Skip to content

Commit 04dc715

Browse files
committed
apparmor: audit policy ns specified in policy load
Verify that profiles in a load set specify the same policy ns and audit the name of the policy ns that policy is being loaded for. Signed-off-by: John Johansen <[email protected]>
1 parent 5ac8c35 commit 04dc715

File tree

3 files changed

+77
-24
lines changed

3 files changed

+77
-24
lines changed

security/apparmor/include/policy_unpack.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ struct aa_load_ent {
2323
struct aa_profile *new;
2424
struct aa_profile *old;
2525
struct aa_profile *rename;
26+
const char *ns_name;
2627
};
2728

2829
void aa_load_ent_free(struct aa_load_ent *ent);

security/apparmor/policy.c

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -819,22 +819,48 @@ ssize_t aa_replace_profiles(struct aa_ns *view, bool noreplace,
819819
struct aa_ns *ns = NULL;
820820
struct aa_load_ent *ent, *tmp;
821821
int op = OP_PROF_REPL;
822-
ssize_t error;
822+
ssize_t count, error;
823823
LIST_HEAD(lh);
824824

825825
/* released below */
826826
error = aa_unpack(udata, &lh, &ns_name);
827827
if (error)
828828
goto out;
829829

830-
/* released below */
831-
ns = aa_prepare_ns(view, ns_name);
832-
if (!ns) {
833-
error = audit_policy(__aa_current_profile(), op, GFP_KERNEL,
834-
NULL, ns_name,
835-
"failed to prepare namespace", -ENOMEM);
836-
goto free;
830+
/* ensure that profiles are all for the same ns
831+
* TODO: update locking to remove this constaint. All profiles in
832+
* the load set must succeed as a set or the load will
833+
* fail. Sort ent list and take ns locks in hierarchy order
834+
*/
835+
count = 0;
836+
list_for_each_entry(ent, &lh, list) {
837+
if (ns_name) {
838+
if (ent->ns_name &&
839+
strcmp(ent->ns_name, ns_name) != 0) {
840+
info = "policy load has mixed namespaces";
841+
error = -EACCES;
842+
goto fail;
843+
}
844+
} else if (ent->ns_name) {
845+
if (count) {
846+
info = "policy load has mixed namespaces";
847+
error = -EACCES;
848+
goto fail;
849+
}
850+
ns_name = ent->ns_name;
851+
} else
852+
count++;
837853
}
854+
if (ns_name) {
855+
ns = aa_prepare_ns(view, ns_name);
856+
if (IS_ERR(ns)) {
857+
info = "failed to prepare namespace";
858+
error = PTR_ERR(ns);
859+
ns = NULL;
860+
goto fail;
861+
}
862+
} else
863+
ns = aa_get_ns(view);
838864

839865
mutex_lock(&ns->lock);
840866
/* setup parent and ns info */
@@ -964,7 +990,8 @@ ssize_t aa_replace_profiles(struct aa_ns *view, bool noreplace,
964990

965991
/* audit cause of failure */
966992
op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL;
967-
audit_policy(__aa_current_profile(), op, GFP_KERNEL, NULL,
993+
fail:
994+
audit_policy(__aa_current_profile(), op, GFP_KERNEL, ns_name,
968995
ent->new->base.hname, info, error);
969996
/* audit status that rest of profiles in the atomic set failed too */
970997
info = "valid profile in failed atomic policy load";
@@ -975,10 +1002,9 @@ ssize_t aa_replace_profiles(struct aa_ns *view, bool noreplace,
9751002
continue;
9761003
}
9771004
op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL;
978-
audit_policy(__aa_current_profile(), op, GFP_KERNEL, NULL,
1005+
audit_policy(__aa_current_profile(), op, GFP_KERNEL, ns_name,
9791006
tmp->new->base.hname, info, error);
9801007
}
981-
free:
9821008
list_for_each_entry_safe(ent, tmp, &lh, list) {
9831009
list_del_init(&ent->list);
9841010
aa_load_ent_free(ent);
@@ -1005,6 +1031,7 @@ ssize_t aa_remove_profiles(struct aa_ns *view, char *fqname, size_t size)
10051031
struct aa_ns *root = NULL, *ns = NULL;
10061032
struct aa_profile *profile = NULL;
10071033
const char *name = fqname, *info = NULL;
1034+
char *ns_name = NULL;
10081035
ssize_t error = 0;
10091036

10101037
if (*fqname == 0) {
@@ -1016,7 +1043,6 @@ ssize_t aa_remove_profiles(struct aa_ns *view, char *fqname, size_t size)
10161043
root = view;
10171044

10181045
if (fqname[0] == ':') {
1019-
char *ns_name;
10201046
name = aa_split_fqname(fqname, &ns_name);
10211047
/* released below */
10221048
ns = aa_find_ns(root, ns_name);
@@ -1050,7 +1076,7 @@ ssize_t aa_remove_profiles(struct aa_ns *view, char *fqname, size_t size)
10501076

10511077
/* don't fail removal if audit fails */
10521078
(void) audit_policy(__aa_current_profile(), OP_PROF_RM, GFP_KERNEL,
1053-
NULL, name, info, error);
1079+
ns_name, name, info, error);
10541080
aa_put_ns(ns);
10551081
aa_put_profile(profile);
10561082
return size;
@@ -1061,6 +1087,6 @@ ssize_t aa_remove_profiles(struct aa_ns *view, char *fqname, size_t size)
10611087

10621088
fail:
10631089
(void) audit_policy(__aa_current_profile(), OP_PROF_RM, GFP_KERNEL,
1064-
NULL, name, info, error);
1090+
ns_name, name, info, error);
10651091
return error;
10661092
}

security/apparmor/policy_unpack.c

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -91,21 +91,24 @@ static void audit_cb(struct audit_buffer *ab, void *va)
9191
/**
9292
* audit_iface - do audit message for policy unpacking/load/replace/remove
9393
* @new: profile if it has been allocated (MAYBE NULL)
94+
* @ns_name: name of the ns the profile is to be loaded to (MAY BE NULL)
9495
* @name: name of the profile being manipulated (MAYBE NULL)
9596
* @info: any extra info about the failure (MAYBE NULL)
9697
* @e: buffer position info
9798
* @error: error code
9899
*
99100
* Returns: %0 or error
100101
*/
101-
static int audit_iface(struct aa_profile *new, const char *name,
102-
const char *info, struct aa_ext *e, int error)
102+
static int audit_iface(struct aa_profile *new, const char *ns_name,
103+
const char *name, const char *info, struct aa_ext *e,
104+
int error)
103105
{
104106
struct aa_profile *profile = __aa_current_profile();
105107
struct common_audit_data sa;
106108
struct apparmor_audit_data aad = {0,};
107109
sa.type = LSM_AUDIT_DATA_NONE;
108110
sa.aad = &aad;
111+
aad.iface.ns = ns_name;
109112
if (e)
110113
aad.iface.pos = e->pos - e->start;
111114
aad.iface.target = new;
@@ -486,19 +489,32 @@ static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile)
486489
*
487490
* NOTE: unpack profile sets audit struct if there is a failure
488491
*/
489-
static struct aa_profile *unpack_profile(struct aa_ext *e)
492+
static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
490493
{
491494
struct aa_profile *profile = NULL;
492-
const char *name = NULL;
495+
const char *tmpname, *tmpns = NULL, *name = NULL;
496+
size_t ns_len;
493497
int i, error = -EPROTO;
494498
kernel_cap_t tmpcap;
495499
u32 tmp;
496500

501+
*ns_name = NULL;
502+
497503
/* check that we have the right struct being passed */
498504
if (!unpack_nameX(e, AA_STRUCT, "profile"))
499505
goto fail;
500506
if (!unpack_str(e, &name, NULL))
501507
goto fail;
508+
if (*name == '\0')
509+
goto fail;
510+
511+
tmpname = aa_splitn_fqname(name, strlen(name), &tmpns, &ns_len);
512+
if (tmpns) {
513+
*ns_name = kstrndup(tmpns, ns_len, GFP_KERNEL);
514+
if (!*ns_name)
515+
goto fail;
516+
name = tmpname;
517+
}
502518

503519
profile = aa_alloc_profile(name, GFP_KERNEL);
504520
if (!profile)
@@ -646,7 +662,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
646662
name = NULL;
647663
else if (!name)
648664
name = "unknown";
649-
audit_iface(profile, name, "failed to unpack profile", e, error);
665+
audit_iface(profile, NULL, name, "failed to unpack profile", e,
666+
error);
650667
aa_free_profile(profile);
651668

652669
return ERR_PTR(error);
@@ -669,7 +686,7 @@ static int verify_header(struct aa_ext *e, int required, const char **ns)
669686
/* get the interface version */
670687
if (!unpack_u32(e, &e->version, "version")) {
671688
if (required) {
672-
audit_iface(NULL, NULL, "invalid profile format",
689+
audit_iface(NULL, NULL, NULL, "invalid profile format",
673690
e, error);
674691
return error;
675692
}
@@ -680,15 +697,21 @@ static int verify_header(struct aa_ext *e, int required, const char **ns)
680697
* Mask off everything that is not kernel abi version
681698
*/
682699
if (VERSION_LT(e->version, v5) && VERSION_GT(e->version, v7)) {
683-
audit_iface(NULL, NULL, "unsupported interface version",
700+
audit_iface(NULL, NULL, NULL, "unsupported interface version",
684701
e, error);
685702
return error;
686703
}
687704

688705
/* read the namespace if present */
689706
if (unpack_str(e, &name, "namespace")) {
707+
if (*name == '\0') {
708+
audit_iface(NULL, NULL, NULL, "invalid namespace name",
709+
e, error);
710+
return error;
711+
}
690712
if (*ns && strcmp(*ns, name))
691-
audit_iface(NULL, NULL, "invalid ns change", e, error);
713+
audit_iface(NULL, NULL, NULL, "invalid ns change", e,
714+
error);
692715
else if (!*ns)
693716
*ns = name;
694717
}
@@ -730,7 +753,7 @@ static int verify_profile(struct aa_profile *profile)
730753
if (profile->file.dfa &&
731754
!verify_dfa_xindex(profile->file.dfa,
732755
profile->file.trans.size)) {
733-
audit_iface(profile, NULL, "Invalid named transition",
756+
audit_iface(profile, NULL, NULL, "Invalid named transition",
734757
NULL, -EPROTO);
735758
return -EPROTO;
736759
}
@@ -744,6 +767,7 @@ void aa_load_ent_free(struct aa_load_ent *ent)
744767
aa_put_profile(ent->rename);
745768
aa_put_profile(ent->old);
746769
aa_put_profile(ent->new);
770+
kfree(ent->ns_name);
747771
kzfree(ent);
748772
}
749773
}
@@ -782,13 +806,14 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh,
782806

783807
*ns = NULL;
784808
while (e.pos < e.end) {
809+
char *ns_name = NULL;
785810
void *start;
786811
error = verify_header(&e, e.pos == e.start, ns);
787812
if (error)
788813
goto fail;
789814

790815
start = e.pos;
791-
profile = unpack_profile(&e);
816+
profile = unpack_profile(&e, &ns_name);
792817
if (IS_ERR(profile)) {
793818
error = PTR_ERR(profile);
794819
goto fail;
@@ -810,6 +835,7 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh,
810835
}
811836

812837
ent->new = profile;
838+
ent->ns_name = ns_name;
813839
list_add_tail(&ent->list, lh);
814840
}
815841
udata->abi = e.version & K_ABI_MASK;

0 commit comments

Comments
 (0)