apparmor: audit policy ns specified in policy load
authorJohn Johansen <john.johansen@canonical.com>
Mon, 16 Jan 2017 08:42:56 +0000 (00:42 -0800)
committerJohn Johansen <john.johansen@canonical.com>
Mon, 16 Jan 2017 09:18:43 +0000 (01:18 -0800)
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 <john.johansen@canonical.com>
security/apparmor/include/policy_unpack.h
security/apparmor/policy.c
security/apparmor/policy_unpack.c

index 7b675b6..4c1319e 100644 (file)
@@ -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);
index ff29b60..eb1ccd1 100644 (file)
@@ -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 */
@@ -827,14 +827,40 @@ ssize_t aa_replace_profiles(struct aa_ns *view, bool noreplace,
        if (error)
                goto out;
 
-       /* released below */
-       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;
+       /* 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 (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 */
@@ -964,7 +990,8 @@ fail_lock:
 
        /* 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";
@@ -975,10 +1002,9 @@ fail_lock:
                        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);
@@ -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) {
@@ -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);
@@ -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;
@@ -1061,6 +1087,6 @@ fail_ns_lock:
 
 fail:
        (void) audit_policy(__aa_current_profile(), OP_PROF_RM, GFP_KERNEL,
-                           NULL, name, info, error);
+                           ns_name, name, info, error);
        return error;
 }
index fb4ef84..38c148f 100644 (file)
@@ -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
@@ -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;
@@ -486,19 +489,32 @@ fail:
  *
  * 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)
@@ -646,7 +662,8 @@ fail:
                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);
@@ -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;
                }
@@ -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;
        }
@@ -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;
        }
@@ -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);
        }
 }
@@ -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;
@@ -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;