Loading security/apparmor/include/policy_unpack.h +1 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ struct aa_load_ent { struct aa_profile *new; struct aa_profile *old; struct aa_profile *rename; const char *ns_name; }; void aa_load_ent_free(struct aa_load_ent *ent); Loading security/apparmor/policy.c +40 −14 Original line number Diff line number Diff line Loading @@ -819,7 +819,7 @@ ssize_t aa_replace_profiles(struct aa_ns *view, bool noreplace, struct aa_ns *ns = NULL; struct aa_load_ent *ent, *tmp; int op = OP_PROF_REPL; ssize_t error; ssize_t count, error; LIST_HEAD(lh); /* released below */ Loading @@ -827,14 +827,40 @@ ssize_t aa_replace_profiles(struct aa_ns *view, bool noreplace, if (error) goto out; /* released below */ /* ensure that profiles are all for the same ns * TODO: update locking to remove this constaint. All profiles in * the load set must succeed as a set or the load will * fail. Sort ent list and take ns locks in hierarchy order */ count = 0; list_for_each_entry(ent, &lh, list) { if (ns_name) { if (ent->ns_name && strcmp(ent->ns_name, ns_name) != 0) { info = "policy load has mixed namespaces"; error = -EACCES; goto fail; } } else if (ent->ns_name) { if (count) { info = "policy load has mixed namespaces"; error = -EACCES; goto fail; } ns_name = ent->ns_name; } else count++; } if (ns_name) { ns = aa_prepare_ns(view, ns_name); if (!ns) { error = audit_policy(__aa_current_profile(), op, GFP_KERNEL, NULL, ns_name, "failed to prepare namespace", -ENOMEM); goto free; if (IS_ERR(ns)) { info = "failed to prepare namespace"; error = PTR_ERR(ns); ns = NULL; goto fail; } } else ns = aa_get_ns(view); mutex_lock(&ns->lock); /* setup parent and ns info */ Loading Loading @@ -964,7 +990,8 @@ ssize_t aa_replace_profiles(struct aa_ns *view, bool noreplace, /* audit cause of failure */ op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; audit_policy(__aa_current_profile(), op, GFP_KERNEL, NULL, fail: audit_policy(__aa_current_profile(), op, GFP_KERNEL, ns_name, ent->new->base.hname, info, error); /* audit status that rest of profiles in the atomic set failed too */ info = "valid profile in failed atomic policy load"; Loading @@ -975,10 +1002,9 @@ ssize_t aa_replace_profiles(struct aa_ns *view, bool noreplace, continue; } op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; audit_policy(__aa_current_profile(), op, GFP_KERNEL, NULL, audit_policy(__aa_current_profile(), op, GFP_KERNEL, ns_name, tmp->new->base.hname, info, error); } free: list_for_each_entry_safe(ent, tmp, &lh, list) { list_del_init(&ent->list); aa_load_ent_free(ent); Loading @@ -1005,6 +1031,7 @@ ssize_t aa_remove_profiles(struct aa_ns *view, char *fqname, size_t size) struct aa_ns *root = NULL, *ns = NULL; struct aa_profile *profile = NULL; const char *name = fqname, *info = NULL; char *ns_name = NULL; ssize_t error = 0; if (*fqname == 0) { Loading @@ -1016,7 +1043,6 @@ ssize_t aa_remove_profiles(struct aa_ns *view, char *fqname, size_t size) root = view; if (fqname[0] == ':') { char *ns_name; name = aa_split_fqname(fqname, &ns_name); /* released below */ ns = aa_find_ns(root, ns_name); Loading Loading @@ -1050,7 +1076,7 @@ ssize_t aa_remove_profiles(struct aa_ns *view, char *fqname, size_t size) /* don't fail removal if audit fails */ (void) audit_policy(__aa_current_profile(), OP_PROF_RM, GFP_KERNEL, NULL, name, info, error); ns_name, name, info, error); aa_put_ns(ns); aa_put_profile(profile); return size; Loading @@ -1061,6 +1087,6 @@ ssize_t aa_remove_profiles(struct aa_ns *view, char *fqname, size_t size) fail: (void) audit_policy(__aa_current_profile(), OP_PROF_RM, GFP_KERNEL, NULL, name, info, error); ns_name, name, info, error); return error; } security/apparmor/policy_unpack.c +36 −10 Original line number Diff line number Diff line Loading @@ -91,6 +91,7 @@ static void audit_cb(struct audit_buffer *ab, void *va) /** * audit_iface - do audit message for policy unpacking/load/replace/remove * @new: profile if it has been allocated (MAYBE NULL) * @ns_name: name of the ns the profile is to be loaded to (MAY BE NULL) * @name: name of the profile being manipulated (MAYBE NULL) * @info: any extra info about the failure (MAYBE NULL) * @e: buffer position info Loading @@ -98,14 +99,16 @@ static void audit_cb(struct audit_buffer *ab, void *va) * * Returns: %0 or error */ static int audit_iface(struct aa_profile *new, const char *name, const char *info, struct aa_ext *e, int error) static int audit_iface(struct aa_profile *new, const char *ns_name, const char *name, const char *info, struct aa_ext *e, int error) { struct aa_profile *profile = __aa_current_profile(); struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; sa.type = LSM_AUDIT_DATA_NONE; sa.aad = &aad; aad.iface.ns = ns_name; if (e) aad.iface.pos = e->pos - e->start; aad.iface.target = new; Loading Loading @@ -486,19 +489,32 @@ static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile) * * NOTE: unpack profile sets audit struct if there is a failure */ static struct aa_profile *unpack_profile(struct aa_ext *e) static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) { struct aa_profile *profile = NULL; const char *name = NULL; const char *tmpname, *tmpns = NULL, *name = NULL; size_t ns_len; int i, error = -EPROTO; kernel_cap_t tmpcap; u32 tmp; *ns_name = NULL; /* check that we have the right struct being passed */ if (!unpack_nameX(e, AA_STRUCT, "profile")) goto fail; if (!unpack_str(e, &name, NULL)) goto fail; if (*name == '\0') goto fail; tmpname = aa_splitn_fqname(name, strlen(name), &tmpns, &ns_len); if (tmpns) { *ns_name = kstrndup(tmpns, ns_len, GFP_KERNEL); if (!*ns_name) goto fail; name = tmpname; } profile = aa_alloc_profile(name, GFP_KERNEL); if (!profile) Loading Loading @@ -646,7 +662,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) name = NULL; else if (!name) name = "unknown"; audit_iface(profile, name, "failed to unpack profile", e, error); audit_iface(profile, NULL, name, "failed to unpack profile", e, error); aa_free_profile(profile); return ERR_PTR(error); Loading @@ -669,7 +686,7 @@ static int verify_header(struct aa_ext *e, int required, const char **ns) /* get the interface version */ if (!unpack_u32(e, &e->version, "version")) { if (required) { audit_iface(NULL, NULL, "invalid profile format", audit_iface(NULL, NULL, NULL, "invalid profile format", e, error); return error; } Loading @@ -680,15 +697,21 @@ static int verify_header(struct aa_ext *e, int required, const char **ns) * Mask off everything that is not kernel abi version */ if (VERSION_LT(e->version, v5) && VERSION_GT(e->version, v7)) { audit_iface(NULL, NULL, "unsupported interface version", audit_iface(NULL, NULL, NULL, "unsupported interface version", e, error); return error; } /* read the namespace if present */ if (unpack_str(e, &name, "namespace")) { if (*name == '\0') { audit_iface(NULL, NULL, NULL, "invalid namespace name", e, error); return error; } if (*ns && strcmp(*ns, name)) audit_iface(NULL, NULL, "invalid ns change", e, error); audit_iface(NULL, NULL, NULL, "invalid ns change", e, error); else if (!*ns) *ns = name; } Loading Loading @@ -730,7 +753,7 @@ static int verify_profile(struct aa_profile *profile) if (profile->file.dfa && !verify_dfa_xindex(profile->file.dfa, profile->file.trans.size)) { audit_iface(profile, NULL, "Invalid named transition", audit_iface(profile, NULL, NULL, "Invalid named transition", NULL, -EPROTO); return -EPROTO; } Loading @@ -744,6 +767,7 @@ void aa_load_ent_free(struct aa_load_ent *ent) aa_put_profile(ent->rename); aa_put_profile(ent->old); aa_put_profile(ent->new); kfree(ent->ns_name); kzfree(ent); } } Loading Loading @@ -782,13 +806,14 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, *ns = NULL; while (e.pos < e.end) { char *ns_name = NULL; void *start; error = verify_header(&e, e.pos == e.start, ns); if (error) goto fail; start = e.pos; profile = unpack_profile(&e); profile = unpack_profile(&e, &ns_name); if (IS_ERR(profile)) { error = PTR_ERR(profile); goto fail; Loading @@ -810,6 +835,7 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, } ent->new = profile; ent->ns_name = ns_name; list_add_tail(&ent->list, lh); } udata->abi = e.version & K_ABI_MASK; Loading Loading
security/apparmor/include/policy_unpack.h +1 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ struct aa_load_ent { struct aa_profile *new; struct aa_profile *old; struct aa_profile *rename; const char *ns_name; }; void aa_load_ent_free(struct aa_load_ent *ent); Loading
security/apparmor/policy.c +40 −14 Original line number Diff line number Diff line Loading @@ -819,7 +819,7 @@ ssize_t aa_replace_profiles(struct aa_ns *view, bool noreplace, struct aa_ns *ns = NULL; struct aa_load_ent *ent, *tmp; int op = OP_PROF_REPL; ssize_t error; ssize_t count, error; LIST_HEAD(lh); /* released below */ Loading @@ -827,14 +827,40 @@ ssize_t aa_replace_profiles(struct aa_ns *view, bool noreplace, if (error) goto out; /* released below */ /* ensure that profiles are all for the same ns * TODO: update locking to remove this constaint. All profiles in * the load set must succeed as a set or the load will * fail. Sort ent list and take ns locks in hierarchy order */ count = 0; list_for_each_entry(ent, &lh, list) { if (ns_name) { if (ent->ns_name && strcmp(ent->ns_name, ns_name) != 0) { info = "policy load has mixed namespaces"; error = -EACCES; goto fail; } } else if (ent->ns_name) { if (count) { info = "policy load has mixed namespaces"; error = -EACCES; goto fail; } ns_name = ent->ns_name; } else count++; } if (ns_name) { ns = aa_prepare_ns(view, ns_name); if (!ns) { error = audit_policy(__aa_current_profile(), op, GFP_KERNEL, NULL, ns_name, "failed to prepare namespace", -ENOMEM); goto free; if (IS_ERR(ns)) { info = "failed to prepare namespace"; error = PTR_ERR(ns); ns = NULL; goto fail; } } else ns = aa_get_ns(view); mutex_lock(&ns->lock); /* setup parent and ns info */ Loading Loading @@ -964,7 +990,8 @@ ssize_t aa_replace_profiles(struct aa_ns *view, bool noreplace, /* audit cause of failure */ op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; audit_policy(__aa_current_profile(), op, GFP_KERNEL, NULL, fail: audit_policy(__aa_current_profile(), op, GFP_KERNEL, ns_name, ent->new->base.hname, info, error); /* audit status that rest of profiles in the atomic set failed too */ info = "valid profile in failed atomic policy load"; Loading @@ -975,10 +1002,9 @@ ssize_t aa_replace_profiles(struct aa_ns *view, bool noreplace, continue; } op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; audit_policy(__aa_current_profile(), op, GFP_KERNEL, NULL, audit_policy(__aa_current_profile(), op, GFP_KERNEL, ns_name, tmp->new->base.hname, info, error); } free: list_for_each_entry_safe(ent, tmp, &lh, list) { list_del_init(&ent->list); aa_load_ent_free(ent); Loading @@ -1005,6 +1031,7 @@ ssize_t aa_remove_profiles(struct aa_ns *view, char *fqname, size_t size) struct aa_ns *root = NULL, *ns = NULL; struct aa_profile *profile = NULL; const char *name = fqname, *info = NULL; char *ns_name = NULL; ssize_t error = 0; if (*fqname == 0) { Loading @@ -1016,7 +1043,6 @@ ssize_t aa_remove_profiles(struct aa_ns *view, char *fqname, size_t size) root = view; if (fqname[0] == ':') { char *ns_name; name = aa_split_fqname(fqname, &ns_name); /* released below */ ns = aa_find_ns(root, ns_name); Loading Loading @@ -1050,7 +1076,7 @@ ssize_t aa_remove_profiles(struct aa_ns *view, char *fqname, size_t size) /* don't fail removal if audit fails */ (void) audit_policy(__aa_current_profile(), OP_PROF_RM, GFP_KERNEL, NULL, name, info, error); ns_name, name, info, error); aa_put_ns(ns); aa_put_profile(profile); return size; Loading @@ -1061,6 +1087,6 @@ ssize_t aa_remove_profiles(struct aa_ns *view, char *fqname, size_t size) fail: (void) audit_policy(__aa_current_profile(), OP_PROF_RM, GFP_KERNEL, NULL, name, info, error); ns_name, name, info, error); return error; }
security/apparmor/policy_unpack.c +36 −10 Original line number Diff line number Diff line Loading @@ -91,6 +91,7 @@ static void audit_cb(struct audit_buffer *ab, void *va) /** * audit_iface - do audit message for policy unpacking/load/replace/remove * @new: profile if it has been allocated (MAYBE NULL) * @ns_name: name of the ns the profile is to be loaded to (MAY BE NULL) * @name: name of the profile being manipulated (MAYBE NULL) * @info: any extra info about the failure (MAYBE NULL) * @e: buffer position info Loading @@ -98,14 +99,16 @@ static void audit_cb(struct audit_buffer *ab, void *va) * * Returns: %0 or error */ static int audit_iface(struct aa_profile *new, const char *name, const char *info, struct aa_ext *e, int error) static int audit_iface(struct aa_profile *new, const char *ns_name, const char *name, const char *info, struct aa_ext *e, int error) { struct aa_profile *profile = __aa_current_profile(); struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; sa.type = LSM_AUDIT_DATA_NONE; sa.aad = &aad; aad.iface.ns = ns_name; if (e) aad.iface.pos = e->pos - e->start; aad.iface.target = new; Loading Loading @@ -486,19 +489,32 @@ static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile) * * NOTE: unpack profile sets audit struct if there is a failure */ static struct aa_profile *unpack_profile(struct aa_ext *e) static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) { struct aa_profile *profile = NULL; const char *name = NULL; const char *tmpname, *tmpns = NULL, *name = NULL; size_t ns_len; int i, error = -EPROTO; kernel_cap_t tmpcap; u32 tmp; *ns_name = NULL; /* check that we have the right struct being passed */ if (!unpack_nameX(e, AA_STRUCT, "profile")) goto fail; if (!unpack_str(e, &name, NULL)) goto fail; if (*name == '\0') goto fail; tmpname = aa_splitn_fqname(name, strlen(name), &tmpns, &ns_len); if (tmpns) { *ns_name = kstrndup(tmpns, ns_len, GFP_KERNEL); if (!*ns_name) goto fail; name = tmpname; } profile = aa_alloc_profile(name, GFP_KERNEL); if (!profile) Loading Loading @@ -646,7 +662,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) name = NULL; else if (!name) name = "unknown"; audit_iface(profile, name, "failed to unpack profile", e, error); audit_iface(profile, NULL, name, "failed to unpack profile", e, error); aa_free_profile(profile); return ERR_PTR(error); Loading @@ -669,7 +686,7 @@ static int verify_header(struct aa_ext *e, int required, const char **ns) /* get the interface version */ if (!unpack_u32(e, &e->version, "version")) { if (required) { audit_iface(NULL, NULL, "invalid profile format", audit_iface(NULL, NULL, NULL, "invalid profile format", e, error); return error; } Loading @@ -680,15 +697,21 @@ static int verify_header(struct aa_ext *e, int required, const char **ns) * Mask off everything that is not kernel abi version */ if (VERSION_LT(e->version, v5) && VERSION_GT(e->version, v7)) { audit_iface(NULL, NULL, "unsupported interface version", audit_iface(NULL, NULL, NULL, "unsupported interface version", e, error); return error; } /* read the namespace if present */ if (unpack_str(e, &name, "namespace")) { if (*name == '\0') { audit_iface(NULL, NULL, NULL, "invalid namespace name", e, error); return error; } if (*ns && strcmp(*ns, name)) audit_iface(NULL, NULL, "invalid ns change", e, error); audit_iface(NULL, NULL, NULL, "invalid ns change", e, error); else if (!*ns) *ns = name; } Loading Loading @@ -730,7 +753,7 @@ static int verify_profile(struct aa_profile *profile) if (profile->file.dfa && !verify_dfa_xindex(profile->file.dfa, profile->file.trans.size)) { audit_iface(profile, NULL, "Invalid named transition", audit_iface(profile, NULL, NULL, "Invalid named transition", NULL, -EPROTO); return -EPROTO; } Loading @@ -744,6 +767,7 @@ void aa_load_ent_free(struct aa_load_ent *ent) aa_put_profile(ent->rename); aa_put_profile(ent->old); aa_put_profile(ent->new); kfree(ent->ns_name); kzfree(ent); } } Loading Loading @@ -782,13 +806,14 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, *ns = NULL; while (e.pos < e.end) { char *ns_name = NULL; void *start; error = verify_header(&e, e.pos == e.start, ns); if (error) goto fail; start = e.pos; profile = unpack_profile(&e); profile = unpack_profile(&e, &ns_name); if (IS_ERR(profile)) { error = PTR_ERR(profile); goto fail; Loading @@ -810,6 +835,7 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, } ent->new = profile; ent->ns_name = ns_name; list_add_tail(&ent->list, lh); } udata->abi = e.version & K_ABI_MASK; Loading