apparmor: refactor prepare_ns() and make usable from different views
authorJohn Johansen <john.johansen@canonical.com>
Mon, 16 Jan 2017 08:42:34 +0000 (00:42 -0800)
committerJohn Johansen <john.johansen@canonical.com>
Mon, 16 Jan 2017 09:18:28 +0000 (01:18 -0800)
prepare_ns() will need to be called from alternate views, and namespaces
will need to be created via different interfaces. So refactor and
allow specifying the view ns.

Signed-off-by: John Johansen <john.johansen@canonical.com>
security/apparmor/apparmorfs.c
security/apparmor/include/policy.h
security/apparmor/include/policy_ns.h
security/apparmor/policy.c
security/apparmor/policy_ns.c

index 96a02ee..2b48be2 100644 (file)
@@ -125,7 +125,8 @@ static ssize_t profile_load(struct file *f, const char __user *buf, size_t size,
 
        error = PTR_ERR(data);
        if (!IS_ERR(data)) {
-               error = aa_replace_profiles(data, size, PROF_ADD);
+               error = aa_replace_profiles(__aa_current_profile()->ns, data,
+                                           size, PROF_ADD);
                kvfree(data);
        }
 
@@ -147,7 +148,8 @@ static ssize_t profile_replace(struct file *f, const char __user *buf,
        data = aa_simple_write_to_buffer(OP_PROF_REPL, buf, size, size, pos);
        error = PTR_ERR(data);
        if (!IS_ERR(data)) {
-               error = aa_replace_profiles(data, size, PROF_REPLACE);
+               error = aa_replace_profiles(__aa_current_profile()->ns, data,
+                                           size, PROF_REPLACE);
                kvfree(data);
        }
 
index da62d29..1573cad 100644 (file)
@@ -184,7 +184,8 @@ struct aa_profile *aa_fqlookupn_profile(struct aa_profile *base,
                                        const char *fqname, size_t n);
 struct aa_profile *aa_match_profile(struct aa_ns *ns, const char *name);
 
-ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace);
+ssize_t aa_replace_profiles(struct aa_ns *view, void *udata, size_t size,
+                           bool noreplace);
 ssize_t aa_remove_profiles(char *name, size_t size);
 void __aa_profile_list_release(struct list_head *head);
 
index e4c8765..820d86d 100644 (file)
@@ -83,7 +83,9 @@ void aa_free_ns_kref(struct kref *kref);
 
 struct aa_ns *aa_find_ns(struct aa_ns *root, const char *name);
 struct aa_ns *aa_findn_ns(struct aa_ns *root, const char *name, size_t n);
-struct aa_ns *aa_prepare_ns(const char *name);
+struct aa_ns *__aa_find_or_create_ns(struct aa_ns *parent, const char *name,
+                                    struct dentry *dir);
+struct aa_ns *aa_prepare_ns(struct aa_ns *root, const char *name);
 void __aa_remove_ns(struct aa_ns *ns);
 
 static inline struct aa_profile *aa_deref_parent(struct aa_profile *p)
index 5d99fb7..e02ab20 100644 (file)
@@ -731,6 +731,7 @@ static int __lookup_replace(struct aa_ns *ns, const char *hname,
 
 /**
  * aa_replace_profiles - replace profile(s) on the profile list
+ * @view: namespace load is viewed from
  * @udata: serialized data stream  (NOT NULL)
  * @size: size of the serialized data stream
  * @noreplace: true if only doing addition, no replacement allowed
@@ -741,7 +742,8 @@ static int __lookup_replace(struct aa_ns *ns, const char *hname,
  *
  * Returns: size of data consumed else error code on failure.
  */
-ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace)
+ssize_t aa_replace_profiles(struct aa_ns *view, void *udata, size_t size,
+                           bool noreplace)
 {
        const char *ns_name, *info = NULL;
        struct aa_ns *ns = NULL;
@@ -756,7 +758,7 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace)
                goto out;
 
        /* released below */
-       ns = aa_prepare_ns(ns_name);
+       ns = aa_prepare_ns(view, ns_name);
        if (!ns) {
                error = audit_policy(op, GFP_KERNEL, ns_name,
                                     "failed to prepare namespace", -ENOMEM);
index 8a5632f..f6cdc73 100644 (file)
@@ -181,48 +181,82 @@ struct aa_ns *aa_find_ns(struct aa_ns *root, const char *name)
        return aa_findn_ns(root, name, strlen(name));
 }
 
+static struct aa_ns *__aa_create_ns(struct aa_ns *parent, const char *name,
+                                   struct dentry *dir)
+{
+       struct aa_ns *ns;
+       int error;
+
+       AA_BUG(!parent);
+       AA_BUG(!name);
+       AA_BUG(!mutex_is_locked(&parent->lock));
+
+       ns = alloc_ns(parent->base.hname, name);
+       if (!ns)
+               return NULL;
+       mutex_lock(&ns->lock);
+       error = __aa_fs_ns_mkdir(ns, ns_subns_dir(parent), name);
+       if (error) {
+               AA_ERROR("Failed to create interface for ns %s\n",
+                        ns->base.name);
+               mutex_unlock(&ns->lock);
+               aa_free_ns(ns);
+               return ERR_PTR(error);
+       }
+       ns->parent = aa_get_ns(parent);
+       list_add_rcu(&ns->base.list, &parent->sub_ns);
+       /* add list ref */
+       aa_get_ns(ns);
+       mutex_unlock(&ns->lock);
+
+       return ns;
+}
+
 /**
- * aa_prepare_ns - find an existing or create a new namespace of @name
- * @name: the namespace to find or add  (MAYBE NULL)
+ * aa_create_ns - create an ns, fail if it already exists
+ * @parent: the parent of the namespace being created
+ * @name: the name of the namespace
+ * @dir: if not null the dir to put the ns entries in
  *
- * Returns: refcounted ns or NULL if failed to create one
+ * Returns: the a refcounted ns that has been add or an ERR_PTR
  */
-struct aa_ns *aa_prepare_ns(const char *name)
+struct aa_ns *__aa_find_or_create_ns(struct aa_ns *parent, const char *name,
+                                    struct dentry *dir)
 {
-       struct aa_ns *ns, *root;
+       struct aa_ns *ns;
 
-       root = aa_current_profile()->ns;
+       AA_BUG(!mutex_is_locked(&parent->lock));
 
-       mutex_lock(&root->lock);
+       /* try and find the specified ns */
+       /* released by caller */
+       ns = aa_get_ns(__aa_find_ns(&parent->sub_ns, name));
+       if (!ns)
+               ns = __aa_create_ns(parent, name, dir);
+       else
+               ns = ERR_PTR(-EEXIST);
 
-       /* if name isn't specified the profile is loaded to the current ns */
-       if (!name) {
-               /* released by caller */
-               ns = aa_get_ns(root);
-               goto out;
-       }
+       /* return ref */
+       return ns;
+}
 
+/**
+ * aa_prepare_ns - find an existing or create a new namespace of @name
+ * @parent: ns to treat as parent
+ * @name: the namespace to find or add  (NOT NULL)
+ *
+ * Returns: refcounted namespace or PTR_ERR if failed to create one
+ */
+struct aa_ns *aa_prepare_ns(struct aa_ns *parent, const char *name)
+{
+       struct aa_ns *ns;
+
+       mutex_lock(&parent->lock);
        /* try and find the specified ns and if it doesn't exist create it */
        /* released by caller */
-       ns = aa_get_ns(__aa_find_ns(&root->sub_ns, name));
-       if (!ns) {
-               ns = alloc_ns(root->base.hname, name);
-               if (!ns)
-                       goto out;
-               if (__aa_fs_ns_mkdir(ns, ns_subns_dir(root), name)) {
-                       AA_ERROR("Failed to create interface for ns %s\n",
-                                ns->base.name);
-                       aa_free_ns(ns);
-                       ns = NULL;
-                       goto out;
-               }
-               ns->parent = aa_get_ns(root);
-               list_add_rcu(&ns->base.list, &root->sub_ns);
-               /* add list ref */
-               aa_get_ns(ns);
-       }
-out:
-       mutex_unlock(&root->lock);
+       ns = aa_get_ns(__aa_find_ns(&parent->sub_ns, name));
+       if (!ns)
+               ns = __aa_create_ns(parent, name, NULL);
+       mutex_unlock(&parent->lock);
 
        /* return ref */
        return ns;