Add basic interface files to access namespace and profile information.
The interface files are created when a profile is loaded and removed
when the profile or namespace is removed.

Signed-off-by: John Johansen <[email protected]>
---
 security/apparmor/apparmorfs.c         |  281 ++++++++++++++++++++++++++++++--
 security/apparmor/audit.c              |    6 +
 security/apparmor/include/apparmorfs.h |   24 +++
 security/apparmor/include/audit.h      |    2 +
 security/apparmor/include/policy.h     |   11 ++
 security/apparmor/policy.c             |   51 +++++-
 6 files changed, 351 insertions(+), 24 deletions(-)

diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index b73e59b..7919735 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -12,6 +12,7 @@
  * License.
  */
 
+#include <linux/ctype.h>
 #include <linux/security.h>
 #include <linux/vmalloc.h>
 #include <linux/module.h>
@@ -28,6 +29,49 @@
 #include "include/resource.h"
 
 /**
+ * aa_mangle_name - mangle a profile name to std profile layout form
+ * @name: profile name to mangle  (NOT NULL)
+ * @target: buffer to store mangled name, same length as @name (MAYBE NULL)
+ *
+ * Returns: length of mangled name
+ */
+static int mangle_name(char *name, char *target)
+{
+       char *t = target;
+
+       while (*name == '/' || *name == '.')
+               name++;
+
+       if (target) {
+               for (; *name; name++) {
+                       if (strchr("\"\'(){}[]", *name))
+                               continue;
+                       else if (*name == '/')
+                               *(t)++ = '.';
+                       else if (isspace(*name))
+                               *(t)++ = '_';
+                       else if (strchr("*?^$\\", *name))
+                               *(t)++ = 'X';
+                       else if (isgraph(*name))
+                               *(t)++ = *name;
+               }
+
+               *t = 0;
+       } else {
+               int len = 0;
+               for (; *name; name++) {
+                       if (strchr("\"\'(){}[]", *name))
+                               continue;
+                       len++;
+               }
+
+               return len;
+       }
+
+       return t - target;
+}
+
+/**
  * aa_simple_write_to_buffer - common routine for getting policy from user
  * @op: operation doing the user buffer copy
  * @userbuf: user buffer to copy data from  (NOT NULL)
@@ -182,8 +226,206 @@ const struct file_operations aa_fs_seq_file_ops = {
        .release        = single_release,
 };
 
-/** Base file system setup **/
+static int aa_fs_seq_string_show(struct seq_file *seq, void *v)
+{
+       char *string = seq->private;
+
+       if (string)
+               seq_printf(seq, "%s\n", string);
+
+       return 0;
+}
+
+static int aa_fs_seq_string_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, aa_fs_seq_string_show, inode->i_private);
+}
+
+const struct file_operations aa_fs_seq_string_fops = {
+       .owner          = THIS_MODULE,
+       .open           = aa_fs_seq_string_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int aa_fs_seq_mode_show(struct seq_file *seq, void *v)
+{
+       enum profile_mode *mode = seq->private;
+
+       seq_printf(seq, "%s\n", aa_profile_mode_names[*mode]);
+
+       return 0;
+}
+
+static int aa_fs_seq_mode_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, aa_fs_seq_mode_show, inode->i_private);
+}
+
+const struct file_operations aa_fs_seq_mode_fops = {
+       .owner          = THIS_MODULE,
+       .open           = aa_fs_seq_mode_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+/** fns to setup dynamic per profile/namespace files **/
+void __aa_fs_profile_rmdir(struct aa_profile *profile)
+{
+       struct aa_profile *child;
+       int i;
+
+       if (!profile)
+               return;
+
+       list_for_each_entry(child, &profile->base.profiles, base.list)
+               __aa_fs_profile_rmdir(child);
+
+       for (i = AAFS_PROF_SIZE - 1; i >= 0; --i) {
+               securityfs_remove(profile->dents[i]);
+               profile->dents[i] = NULL;
+       }
+}
+
+/* requires lock be held */
+int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
+{
+       struct aa_profile *child;
+       struct dentry *dent = NULL, *dir;
+       int error;
+
+       if (!parent) {
+               dent = profile->parent->dents[AAFS_PROF_DIR];
+               /* adding to parent that previously didn't have children */
+               dent = securityfs_create_dir("profiles", dent);
+               if (!dent)
+                       goto fail;
+               profile->parent->dents[AAFS_PROF_PROFS] = parent = dent;
+       }
+
+       if (!profile->dirname) {
+               int len, id_len;
+               len = mangle_name(profile->base.name, NULL);
+               id_len = snprintf(NULL, 0, ".%ld", profile->ns->uniq_id);
+
+               profile->dirname = kmalloc(len + id_len + 1, GFP_KERNEL);
+               if (!profile->dirname)
+                       goto fail;
 
+               mangle_name(profile->base.name, profile->dirname);
+               sprintf(profile->dirname + len, ".%ld", profile->ns->uniq_id++);
+       }
+
+       dent = securityfs_create_dir(profile->dirname, parent);
+       if (IS_ERR(dent))
+               goto fail;
+       profile->dents[AAFS_PROF_DIR] = dir = dent;
+
+       dent = securityfs_create_file("name", S_IFREG | 0444, dir,
+                                     profile->base.name,
+                                     &aa_fs_seq_string_fops);
+       if (IS_ERR(dent))
+               goto fail;
+       profile->dents[AAFS_PROF_NAME] = dent;
+
+       dent = securityfs_create_file("mode", S_IFREG | 0444, dir,
+                                     &profile->mode, &aa_fs_seq_mode_fops);
+       if (IS_ERR(dent))
+               goto fail;
+       profile->dents[AAFS_PROF_MODE] = dent;
+
+       list_for_each_entry(child, &profile->base.profiles, base.list) {
+               error = __aa_fs_profile_mkdir(child, 
profile->dents[AAFS_PROF_PROFS]);
+               if (error)
+                       goto fail2;
+       }
+
+       return 0;
+
+fail:
+       error = PTR_ERR(dent);
+
+fail2:
+       __aa_fs_profile_rmdir(profile);
+
+       return error;
+}
+
+void __aa_fs_namespace_rmdir(struct aa_namespace *ns)
+{
+       struct aa_namespace *sub;
+       struct aa_profile *child;
+       int i;
+
+       if (!ns)
+               return;
+
+       list_for_each_entry(child, &ns->base.profiles, base.list)
+               __aa_fs_profile_rmdir(child);
+
+       list_for_each_entry(sub, &ns->sub_ns, base.list)
+               __aa_fs_namespace_rmdir(sub);
+
+       for (i = AAFS_NS_SIZE - 1; i >= 0 ; --i) {
+               securityfs_remove(ns->dents[i]);
+               ns->dents[i] = NULL;
+       }
+}
+
+int __aa_fs_namespace_mkdir(struct aa_namespace *ns, struct dentry *parent,
+                           const char *name)
+{
+       struct aa_namespace *sub;
+       struct aa_profile *child;
+       struct dentry *dent, *dir;
+       int error;
+
+       if (!name)
+               name = ns->base.name;
+
+       dent = securityfs_create_dir(name, parent);
+       if (IS_ERR(dent))
+               goto fail;
+       ns->dents[AAFS_NS_DIR] = dir = dent;
+
+       dent = securityfs_create_dir("profiles", dir);
+       if (IS_ERR(dent))
+               goto fail;
+       ns->dents[AAFS_NS_PROFS] = dent;
+
+       dent = securityfs_create_dir("namespaces", dir);
+       if (IS_ERR(dent))
+               goto fail;
+       ns->dents[AAFS_NS_NS] = dent;
+
+       list_for_each_entry(child, &ns->base.profiles, base.list) {
+               error = __aa_fs_profile_mkdir(child, ns->dents[AAFS_NS_PROFS]);
+               if (error)
+                       goto fail2;
+       }
+
+       list_for_each_entry(sub, &ns->sub_ns, base.list) {
+               error = __aa_fs_namespace_mkdir(sub, ns->dents[AAFS_NS_NS],
+                                               NULL);
+               if (error)
+                       goto fail2;
+       }
+
+       return 0;
+
+fail:
+       error = PTR_ERR(dent);
+
+fail2:
+       __aa_fs_namespace_rmdir(ns);
+
+       return error;
+}
+
+
+/** Base file system setup **/
 static struct aa_fs_entry aa_fs_entry_file[] = {
        AA_FS_FILE_STRING("mask", "create read write exec append mmap_exec " \
                                  "link lock"),
@@ -221,8 +463,10 @@ static struct aa_fs_entry aa_fs_entry_apparmor[] = {
        { }
 };
 
-static struct aa_fs_entry aa_fs_entry =
-       AA_FS_DIR("apparmor", aa_fs_entry_apparmor);
+static struct aa_fs_entry aa_fs_entry[] = {
+       AA_FS_DIR("apparmor", aa_fs_entry_apparmor),
+       { }
+};
 
 /**
  * aafs_create_file - create a file entry in the apparmor securityfs
@@ -247,6 +491,7 @@ static int __init aafs_create_file(struct aa_fs_entry 
*fs_file,
        return error;
 }
 
+static void __init aafs_remove_dir(struct aa_fs_entry *fs_dir);
 /**
  * aafs_create_dir - recursively create a directory entry in the securityfs
  * @fs_dir: aa_fs_entry (and all child entries) to build (NOT NULL)
@@ -257,17 +502,16 @@ static int __init aafs_create_file(struct aa_fs_entry 
*fs_file,
 static int __init aafs_create_dir(struct aa_fs_entry *fs_dir,
                                  struct dentry *parent)
 {
-       int error;
        struct aa_fs_entry *fs_file;
+       struct dentry *dir;
+       int error;
 
-       fs_dir->dentry = securityfs_create_dir(fs_dir->name, parent);
-       if (IS_ERR(fs_dir->dentry)) {
-               error = PTR_ERR(fs_dir->dentry);
-               fs_dir->dentry = NULL;
-               goto failed;
-       }
+       dir = securityfs_create_dir(fs_dir->name, parent);
+       if (IS_ERR(dir))
+               return PTR_ERR(dir);
+       fs_dir->dentry = dir;
 
-       for (fs_file = fs_dir->v.files; fs_file->name; ++fs_file) {
+       for (fs_file = fs_dir->v.files; fs_file && fs_file->name; ++fs_file) {
                if (fs_file->v_type == AA_FS_TYPE_DIR)
                        error = aafs_create_dir(fs_file, fs_dir->dentry);
                else
@@ -279,6 +523,8 @@ static int __init aafs_create_dir(struct aa_fs_entry 
*fs_dir,
        return 0;
 
 failed:
+       aafs_remove_dir(fs_dir);
+
        return error;
 }
 
@@ -303,7 +549,7 @@ static void __init aafs_remove_dir(struct aa_fs_entry 
*fs_dir)
 {
        struct aa_fs_entry *fs_file;
 
-       for (fs_file = fs_dir->v.files; fs_file->name; ++fs_file) {
+       for (fs_file = fs_dir->v.files; fs_file && fs_file->name; ++fs_file) {
                if (fs_file->v_type == AA_FS_TYPE_DIR)
                        aafs_remove_dir(fs_file);
                else
@@ -320,7 +566,7 @@ static void __init aafs_remove_dir(struct aa_fs_entry 
*fs_dir)
  */
 void __init aa_destroy_aafs(void)
 {
-       aafs_remove_dir(&aa_fs_entry);
+       aafs_remove_dir(aa_fs_entry);
 }
 
 /**
@@ -337,13 +583,18 @@ static int __init aa_create_aafs(void)
        if (!apparmor_initialized)
                return 0;
 
-       if (aa_fs_entry.dentry) {
+       if (aa_fs_entry[0].dentry) {
                AA_ERROR("%s: AppArmor securityfs already exists\n", __func__);
                return -EEXIST;
        }
 
        /* Populate fs tree. */
-       error = aafs_create_dir(&aa_fs_entry, NULL);
+       error = aafs_create_dir(aa_fs_entry, NULL);
+       if (error)
+               goto error;
+
+       error = __aa_fs_namespace_mkdir(root_ns, aa_fs_entry[0].dentry,
+                                       "policy");
        if (error)
                goto error;
 
diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c
index 031d2d9..3f221c7 100644
--- a/security/apparmor/audit.c
+++ b/security/apparmor/audit.c
@@ -73,6 +73,12 @@ const char *const op_table[] = {
        "profile_remove"
 };
 
+const char *const aa_profile_mode_names[] = {
+       "enforce",
+       "complain",
+       "kill"
+};
+
 const char *const audit_mode_names[] = {
        "normal",
        "quiet_denied",
diff --git a/security/apparmor/include/apparmorfs.h 
b/security/apparmor/include/apparmorfs.h
index 7ea4769..1ab7efd 100644
--- a/security/apparmor/include/apparmorfs.h
+++ b/security/apparmor/include/apparmorfs.h
@@ -61,4 +61,28 @@ extern const struct file_operations aa_fs_seq_file_ops;
 
 extern void __init aa_destroy_aafs(void);
 
+struct aa_profile;
+struct aa_namespace;
+
+enum aafs_ns_type {
+       AAFS_NS_DIR,
+       AAFS_NS_PROFS,
+       AAFS_NS_NS,
+       AAFS_NS_SIZE,
+};
+
+enum aafs_prof_type {
+       AAFS_PROF_DIR,
+       AAFS_PROF_PROFS,
+       AAFS_PROF_NAME,
+       AAFS_PROF_MODE,
+       AAFS_PROF_SIZE,
+};
+
+void __aa_fs_profile_rmdir(struct aa_profile *profile);
+int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent);
+void __aa_fs_namespace_rmdir(struct aa_namespace *ns);
+int __aa_fs_namespace_mkdir(struct aa_namespace *ns, struct dentry *parent,
+                           const char *name);
+
 #endif /* __AA_APPARMORFS_H */
diff --git a/security/apparmor/include/audit.h 
b/security/apparmor/include/audit.h
index 69d8cae..6539ab3 100644
--- a/security/apparmor/include/audit.h
+++ b/security/apparmor/include/audit.h
@@ -28,6 +28,8 @@ struct aa_profile;
 extern const char *const audit_mode_names[];
 #define AUDIT_MAX_INDEX 5
 
+extern const char *const aa_profile_mode_names[];
+
 enum audit_mode {
        AUDIT_NORMAL,           /* follow normal auditing of accesses */
        AUDIT_QUIET_DENIED,     /* quiet all denied access messages */
diff --git a/security/apparmor/include/policy.h 
b/security/apparmor/include/policy.h
index 0d16d91..07fc7b0 100644
--- a/security/apparmor/include/policy.h
+++ b/security/apparmor/include/policy.h
@@ -108,6 +108,8 @@ struct aa_ns_acct {
  * @acct: accounting for the namespace
  * @unconfined: special unconfined profile for the namespace
  * @sub_ns: list of namespaces under the current namespace.
+ * @uniq_id: a unique id count for the profiles in the namespace
+ * @dents: dentries for the namespaces file entries in apparmorfs
  *
  * An aa_namespace defines the set profiles that are searched to determine
  * which profile to attach to a task.  Profiles can not be shared between
@@ -132,6 +134,9 @@ struct aa_namespace {
        struct list_head sub_ns;
 
        atomic_t uniq_null;
+       long uniq_id;
+
+       struct dentry *dents[AAFS_NS_SIZE];
 };
 
 /* struct aa_policydb - match engine for a policy
@@ -163,6 +168,9 @@ struct aa_policydb {
  * @caps: capabilities for the profile
  * @rlimits: rlimits for the profile
  *
+ * @dents: dentries for the profiles file entries in apparmorfs
+ * @dirname: name of the profile dir in apparmorfs
+ *
  * The AppArmor profile contains the basic confinement data.  Each profile
  * has a name, and exists in a namespace.  The @name and @exec_match are
  * used to determine profile attachment against unconfined tasks.  All other
@@ -198,6 +206,9 @@ struct aa_profile {
        struct aa_file_rules file;
        struct aa_caps caps;
        struct aa_rlimit rlimits;
+
+       char *dirname;
+       struct dentry *dents[AAFS_PROF_SIZE];
 };
 
 extern struct aa_namespace *root_ns;
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
index 4d3f8ba..4297f1d 100644
--- a/security/apparmor/policy.c
+++ b/security/apparmor/policy.c
@@ -388,7 +388,7 @@ struct aa_namespace *aa_find_namespace(struct aa_namespace 
*root,
  */
 static struct aa_namespace *aa_prepare_namespace(const char *name)
 {
-       struct aa_namespace *ns, *root;
+       struct aa_namespace *ns, *new_ns = NULL, *root;
 
        root = aa_current_profile()->ns;
 
@@ -405,11 +405,23 @@ static struct aa_namespace *aa_prepare_namespace(const 
char *name)
        /* released by caller */
        ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name));
        if (!ns)
-               ns = alloc_namespace(root->base.hname, name);
+               new_ns = ns = alloc_namespace(root->base.hname, name);
 
        if (ns) {
                /* add parent ref */
                ns->parent = aa_get_namespace(root);
+
+               if (new_ns && __aa_fs_namespace_mkdir(new_ns,
+                                                    root->dents[AAFS_NS_PROFS],
+                                                     NULL)) {
+                       free_namespace(new_ns);
+                       AA_ERROR("Added namespace %s but failed to "
+                                "add interface files.\n",
+                                new_ns->base.name);
+                       ns = NULL;
+                       goto out;
+               }
+
                list_add_rcu(&ns->base.list, &root->sub_ns);
                /* add list ref */
                aa_get_namespace(ns);
@@ -670,6 +682,7 @@ static void free_profile(struct aa_profile *profile)
        aa_free_cap_rules(&profile->caps);
        aa_free_rlimit_rules(&profile->rlimits);
 
+       kzfree(profile->dirname);
        aa_put_dfa(profile->xmatch);
        aa_put_dfa(profile->policy.dfa);
 
@@ -1013,11 +1026,11 @@ bool aa_may_manage_policy(int op)
  * @ns - namespace the lookup occurs in
  * @new - profile to lookup who it is replacing
  * @noreplace - true if not replacing an existing profile
- * @old - Returns: pointer to profile to replace (NO REFCOUNT)
- * @rename - Returns: pointer to profile to rename (NO REFCOUNT)
+ * @old - Returns: pointer to profile to replace (ref counted)
+ * @rename - Returns: pointer to profile to rename (ref counted)
  * @info - Returns: info string on why lookup failed
  *
- * Returns: policy (no ref) profile is in on success else ptr error
+ * Returns: policy (no ref count) profile is in on success else ptr error
  */
 static struct aa_policy *__lookup_replace(struct aa_namespace *ns,
                                          struct aa_profile *new,
@@ -1108,10 +1121,9 @@ ssize_t aa_replace_profiles(void *udata, size_t size, 
bool noreplace)
                policy = __lookup_replace(ns, new, noreplace, NULL, NULL,
                                          &info);
                if (IS_ERR(policy)) {
-                       mutex_unlock(&ns->lock);
                        error = PTR_ERR(policy);
                        name = new->base.hname;
-                       goto fail;
+                       goto fail_lock;
                }
                /* released when @new is freed */
                new->ns = aa_get_namespace(ns);
@@ -1120,6 +1132,17 @@ ssize_t aa_replace_profiles(void *udata, size_t size, 
bool noreplace)
                        new->parent = aa_get_profile((struct aa_profile *) 
policy);
        }
 
+       list_for_each_entry(new, &lh, base.list) {
+               /* make new profiles available for introspection */
+               error = __aa_fs_profile_mkdir(new,
+                       new->parent ? new->parent->dents[AAFS_PROF_PROFS] :
+                                     new->ns->dents[AAFS_NS_PROFS]);
+               if (error) {
+                       info = "failed to create ";
+                       goto fail_lock;
+               }
+       }
+
        /* do actual replacement */
        list_for_each_entry_safe(new, tmp, &lh, base.list) {
                struct aa_profile *old, *rename;
@@ -1132,10 +1155,14 @@ ssize_t aa_replace_profiles(void *udata, size_t size, 
bool noreplace)
 
                audit_policy(op, GFP_ATOMIC, new->base.name, NULL, error);
 
-               if (rename)
+               if (rename) {
                        __replace_profile(rename, new);
-               if (old)
+                       __aa_fs_profile_rmdir(rename);
+               }
+               if (old) {
                        __replace_profile(old, new);
+                       __aa_fs_profile_rmdir(old);
+               }
                if (!(old || rename))
                        __list_add_profile(&policy->profiles, new);
 
@@ -1143,6 +1170,7 @@ ssize_t aa_replace_profiles(void *udata, size_t size, 
bool noreplace)
                aa_put_profile(old);
                aa_put_profile(new);
        }
+
        mutex_unlock(&ns->lock);
 
 out:
@@ -1152,6 +1180,9 @@ out:
                return error;
        return size;
 
+fail_lock:
+       mutex_unlock(&ns->lock);
+
 fail:
        error = audit_policy(op, GFP_KERNEL, name, info, error);
 
@@ -1211,6 +1242,7 @@ ssize_t aa_remove_profiles(char *fqname, size_t size)
                /* remove namespace - can only happen if fqname[0] == ':' */
                mutex_lock(&ns->parent->lock);
                __remove_namespace(ns);
+               __aa_fs_namespace_rmdir(ns);
                mutex_unlock(&ns->parent->lock);
        } else {
                /* remove profile */
@@ -1223,6 +1255,7 @@ ssize_t aa_remove_profiles(char *fqname, size_t size)
                }
                name = profile->base.hname;
                __remove_profile(profile);
+               __aa_fs_profile_rmdir(profile);
                mutex_unlock(&ns->lock);
        }
 
-- 
1.7.10.4


-- 
AppArmor mailing list
[email protected]
Modify settings or unsubscribe at: 
https://lists.ubuntu.com/mailman/listinfo/apparmor

Reply via email to