Commit 04dc715e authored by John Johansen's avatar John Johansen

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: default avatarJohn Johansen <john.johansen@canonical.com>
parent 5ac8c355
...@@ -23,6 +23,7 @@ struct aa_load_ent { ...@@ -23,6 +23,7 @@ struct aa_load_ent {
struct aa_profile *new; struct aa_profile *new;
struct aa_profile *old; struct aa_profile *old;
struct aa_profile *rename; struct aa_profile *rename;
const char *ns_name;
}; };
void aa_load_ent_free(struct aa_load_ent *ent); void aa_load_ent_free(struct aa_load_ent *ent);
......
...@@ -819,7 +819,7 @@ ssize_t aa_replace_profiles(struct aa_ns *view, bool noreplace, ...@@ -819,7 +819,7 @@ ssize_t aa_replace_profiles(struct aa_ns *view, bool noreplace,
struct aa_ns *ns = NULL; struct aa_ns *ns = NULL;
struct aa_load_ent *ent, *tmp; struct aa_load_ent *ent, *tmp;
int op = OP_PROF_REPL; int op = OP_PROF_REPL;
ssize_t error; ssize_t count, error;
LIST_HEAD(lh); LIST_HEAD(lh);
/* released below */ /* released below */
...@@ -827,14 +827,40 @@ ssize_t aa_replace_profiles(struct aa_ns *view, bool noreplace, ...@@ -827,14 +827,40 @@ ssize_t aa_replace_profiles(struct aa_ns *view, bool noreplace,
if (error) if (error)
goto out; 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); ns = aa_prepare_ns(view, ns_name);
if (!ns) { if (IS_ERR(ns)) {
error = audit_policy(__aa_current_profile(), op, GFP_KERNEL, info = "failed to prepare namespace";
NULL, ns_name, error = PTR_ERR(ns);
"failed to prepare namespace", -ENOMEM); ns = NULL;
goto free; goto fail;
} }
} else
ns = aa_get_ns(view);
mutex_lock(&ns->lock); mutex_lock(&ns->lock);
/* setup parent and ns info */ /* setup parent and ns info */
...@@ -964,7 +990,8 @@ ssize_t aa_replace_profiles(struct aa_ns *view, bool noreplace, ...@@ -964,7 +990,8 @@ ssize_t aa_replace_profiles(struct aa_ns *view, bool noreplace,
/* audit cause of failure */ /* audit cause of failure */
op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; 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); ent->new->base.hname, info, error);
/* audit status that rest of profiles in the atomic set failed too */ /* audit status that rest of profiles in the atomic set failed too */
info = "valid profile in failed atomic policy load"; info = "valid profile in failed atomic policy load";
...@@ -975,10 +1002,9 @@ ssize_t aa_replace_profiles(struct aa_ns *view, bool noreplace, ...@@ -975,10 +1002,9 @@ ssize_t aa_replace_profiles(struct aa_ns *view, bool noreplace,
continue; continue;
} }
op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; 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); tmp->new->base.hname, info, error);
} }
free:
list_for_each_entry_safe(ent, tmp, &lh, list) { list_for_each_entry_safe(ent, tmp, &lh, list) {
list_del_init(&ent->list); list_del_init(&ent->list);
aa_load_ent_free(ent); aa_load_ent_free(ent);
...@@ -1005,6 +1031,7 @@ ssize_t aa_remove_profiles(struct aa_ns *view, char *fqname, size_t size) ...@@ -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_ns *root = NULL, *ns = NULL;
struct aa_profile *profile = NULL; struct aa_profile *profile = NULL;
const char *name = fqname, *info = NULL; const char *name = fqname, *info = NULL;
char *ns_name = NULL;
ssize_t error = 0; ssize_t error = 0;
if (*fqname == 0) { if (*fqname == 0) {
...@@ -1016,7 +1043,6 @@ ssize_t aa_remove_profiles(struct aa_ns *view, char *fqname, size_t size) ...@@ -1016,7 +1043,6 @@ ssize_t aa_remove_profiles(struct aa_ns *view, char *fqname, size_t size)
root = view; root = view;
if (fqname[0] == ':') { if (fqname[0] == ':') {
char *ns_name;
name = aa_split_fqname(fqname, &ns_name); name = aa_split_fqname(fqname, &ns_name);
/* released below */ /* released below */
ns = aa_find_ns(root, ns_name); 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) ...@@ -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 */ /* don't fail removal if audit fails */
(void) audit_policy(__aa_current_profile(), OP_PROF_RM, GFP_KERNEL, (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_ns(ns);
aa_put_profile(profile); aa_put_profile(profile);
return size; return size;
...@@ -1061,6 +1087,6 @@ ssize_t aa_remove_profiles(struct aa_ns *view, char *fqname, size_t size) ...@@ -1061,6 +1087,6 @@ ssize_t aa_remove_profiles(struct aa_ns *view, char *fqname, size_t size)
fail: fail:
(void) audit_policy(__aa_current_profile(), OP_PROF_RM, GFP_KERNEL, (void) audit_policy(__aa_current_profile(), OP_PROF_RM, GFP_KERNEL,
NULL, name, info, error); ns_name, name, info, error);
return error; return error;
} }
...@@ -91,6 +91,7 @@ static void audit_cb(struct audit_buffer *ab, void *va) ...@@ -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 * audit_iface - do audit message for policy unpacking/load/replace/remove
* @new: profile if it has been allocated (MAYBE NULL) * @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) * @name: name of the profile being manipulated (MAYBE NULL)
* @info: any extra info about the failure (MAYBE NULL) * @info: any extra info about the failure (MAYBE NULL)
* @e: buffer position info * @e: buffer position info
...@@ -98,14 +99,16 @@ static void audit_cb(struct audit_buffer *ab, void *va) ...@@ -98,14 +99,16 @@ static void audit_cb(struct audit_buffer *ab, void *va)
* *
* Returns: %0 or error * Returns: %0 or error
*/ */
static int audit_iface(struct aa_profile *new, const char *name, static int audit_iface(struct aa_profile *new, const char *ns_name,
const char *info, struct aa_ext *e, int error) const char *name, const char *info, struct aa_ext *e,
int error)
{ {
struct aa_profile *profile = __aa_current_profile(); struct aa_profile *profile = __aa_current_profile();
struct common_audit_data sa; struct common_audit_data sa;
struct apparmor_audit_data aad = {0,}; struct apparmor_audit_data aad = {0,};
sa.type = LSM_AUDIT_DATA_NONE; sa.type = LSM_AUDIT_DATA_NONE;
sa.aad = &aad; sa.aad = &aad;
aad.iface.ns = ns_name;
if (e) if (e)
aad.iface.pos = e->pos - e->start; aad.iface.pos = e->pos - e->start;
aad.iface.target = new; aad.iface.target = new;
...@@ -486,19 +489,32 @@ static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile) ...@@ -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 * 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; struct aa_profile *profile = NULL;
const char *name = NULL; const char *tmpname, *tmpns = NULL, *name = NULL;
size_t ns_len;
int i, error = -EPROTO; int i, error = -EPROTO;
kernel_cap_t tmpcap; kernel_cap_t tmpcap;
u32 tmp; u32 tmp;
*ns_name = NULL;
/* check that we have the right struct being passed */ /* check that we have the right struct being passed */
if (!unpack_nameX(e, AA_STRUCT, "profile")) if (!unpack_nameX(e, AA_STRUCT, "profile"))
goto fail; goto fail;
if (!unpack_str(e, &name, NULL)) if (!unpack_str(e, &name, NULL))
goto fail; 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); profile = aa_alloc_profile(name, GFP_KERNEL);
if (!profile) if (!profile)
...@@ -646,7 +662,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) ...@@ -646,7 +662,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
name = NULL; name = NULL;
else if (!name) else if (!name)
name = "unknown"; 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); aa_free_profile(profile);
return ERR_PTR(error); return ERR_PTR(error);
...@@ -669,7 +686,7 @@ static int verify_header(struct aa_ext *e, int required, const char **ns) ...@@ -669,7 +686,7 @@ static int verify_header(struct aa_ext *e, int required, const char **ns)
/* get the interface version */ /* get the interface version */
if (!unpack_u32(e, &e->version, "version")) { if (!unpack_u32(e, &e->version, "version")) {
if (required) { if (required) {
audit_iface(NULL, NULL, "invalid profile format", audit_iface(NULL, NULL, NULL, "invalid profile format",
e, error); e, error);
return error; return error;
} }
...@@ -680,15 +697,21 @@ static int verify_header(struct aa_ext *e, int required, const char **ns) ...@@ -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 * Mask off everything that is not kernel abi version
*/ */
if (VERSION_LT(e->version, v5) && VERSION_GT(e->version, v7)) { 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); e, error);
return error; return error;
} }
/* read the namespace if present */ /* read the namespace if present */
if (unpack_str(e, &name, "namespace")) { 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)) 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) else if (!*ns)
*ns = name; *ns = name;
} }
...@@ -730,7 +753,7 @@ static int verify_profile(struct aa_profile *profile) ...@@ -730,7 +753,7 @@ static int verify_profile(struct aa_profile *profile)
if (profile->file.dfa && if (profile->file.dfa &&
!verify_dfa_xindex(profile->file.dfa, !verify_dfa_xindex(profile->file.dfa,
profile->file.trans.size)) { profile->file.trans.size)) {
audit_iface(profile, NULL, "Invalid named transition", audit_iface(profile, NULL, NULL, "Invalid named transition",
NULL, -EPROTO); NULL, -EPROTO);
return -EPROTO; return -EPROTO;
} }
...@@ -744,6 +767,7 @@ void aa_load_ent_free(struct aa_load_ent *ent) ...@@ -744,6 +767,7 @@ void aa_load_ent_free(struct aa_load_ent *ent)
aa_put_profile(ent->rename); aa_put_profile(ent->rename);
aa_put_profile(ent->old); aa_put_profile(ent->old);
aa_put_profile(ent->new); aa_put_profile(ent->new);
kfree(ent->ns_name);
kzfree(ent); kzfree(ent);
} }
} }
...@@ -782,13 +806,14 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, ...@@ -782,13 +806,14 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh,
*ns = NULL; *ns = NULL;
while (e.pos < e.end) { while (e.pos < e.end) {
char *ns_name = NULL;
void *start; void *start;
error = verify_header(&e, e.pos == e.start, ns); error = verify_header(&e, e.pos == e.start, ns);
if (error) if (error)
goto fail; goto fail;
start = e.pos; start = e.pos;
profile = unpack_profile(&e); profile = unpack_profile(&e, &ns_name);
if (IS_ERR(profile)) { if (IS_ERR(profile)) {
error = PTR_ERR(profile); error = PTR_ERR(profile);
goto fail; goto fail;
...@@ -810,6 +835,7 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, ...@@ -810,6 +835,7 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh,
} }
ent->new = profile; ent->new = profile;
ent->ns_name = ns_name;
list_add_tail(&ent->list, lh); list_add_tail(&ent->list, lh);
} }
udata->abi = e.version & K_ABI_MASK; udata->abi = e.version & K_ABI_MASK;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment