]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - security/apparmor/lsm.c
Merge branch 'for-4.11' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/percpu
[karo-tx-linux.git] / security / apparmor / lsm.c
index c751b033420c5856310c124f749e475f3e69e89d..709eacd23909acfa5438e9173085d91eb1780991 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/sysctl.h>
 #include <linux/audit.h>
 #include <linux/user_namespace.h>
+#include <linux/kmemleak.h>
 #include <net/sock.h>
 
 #include "include/apparmor.h"
@@ -40,6 +41,9 @@
 /* Flag indicating whether initialization completed */
 int apparmor_initialized __initdata;
 
+DEFINE_PER_CPU(struct aa_buffers, aa_buffers);
+
+
 /*
  * LSM hook functions
  */
@@ -166,42 +170,42 @@ static int common_perm(const char *op, const struct path *path, u32 mask,
 }
 
 /**
- * common_perm_dir_dentry - common permission wrapper when path is dir, dentry
+ * common_perm_cond - common permission wrapper around inode cond
  * @op: operation being checked
- * @dir: directory of the dentry  (NOT NULL)
- * @dentry: dentry to check  (NOT NULL)
+ * @path: location to check (NOT NULL)
  * @mask: requested permissions mask
- * @cond: conditional info for the permission request  (NOT NULL)
  *
  * Returns: %0 else error code if error or permission denied
  */
-static int common_perm_dir_dentry(const char *op, const struct path *dir,
-                                 struct dentry *dentry, u32 mask,
-                                 struct path_cond *cond)
+static int common_perm_cond(const char *op, const struct path *path, u32 mask)
 {
-       struct path path = { .mnt = dir->mnt, .dentry = dentry };
+       struct path_cond cond = { d_backing_inode(path->dentry)->i_uid,
+                                 d_backing_inode(path->dentry)->i_mode
+       };
 
-       return common_perm(op, &path, mask, cond);
+       if (!path_mediated_fs(path->dentry))
+               return 0;
+
+       return common_perm(op, path, mask, &cond);
 }
 
 /**
- * common_perm_path - common permission wrapper when mnt, dentry
+ * common_perm_dir_dentry - common permission wrapper when path is dir, dentry
  * @op: operation being checked
- * @path: location to check (NOT NULL)
+ * @dir: directory of the dentry  (NOT NULL)
+ * @dentry: dentry to check  (NOT NULL)
  * @mask: requested permissions mask
+ * @cond: conditional info for the permission request  (NOT NULL)
  *
  * Returns: %0 else error code if error or permission denied
  */
-static inline int common_perm_path(const char *op, const struct path *path,
-                                  u32 mask)
+static int common_perm_dir_dentry(const char *op, const struct path *dir,
+                                 struct dentry *dentry, u32 mask,
+                                 struct path_cond *cond)
 {
-       struct path_cond cond = { d_backing_inode(path->dentry)->i_uid,
-                                 d_backing_inode(path->dentry)->i_mode
-       };
-       if (!path_mediated_fs(path->dentry))
-               return 0;
+       struct path path = { .mnt = dir->mnt, .dentry = dentry };
 
-       return common_perm(op, path, mask, &cond);
+       return common_perm(op, &path, mask, cond);
 }
 
 /**
@@ -274,7 +278,7 @@ static int apparmor_path_mknod(const struct path *dir, struct dentry *dentry,
 
 static int apparmor_path_truncate(const struct path *path)
 {
-       return common_perm_path(OP_TRUNC, path, MAY_WRITE | AA_MAY_META_WRITE);
+       return common_perm_cond(OP_TRUNC, path, MAY_WRITE | AA_MAY_META_WRITE);
 }
 
 static int apparmor_path_symlink(const struct path *dir, struct dentry *dentry,
@@ -333,17 +337,17 @@ static int apparmor_path_rename(const struct path *old_dir, struct dentry *old_d
 
 static int apparmor_path_chmod(const struct path *path, umode_t mode)
 {
-       return common_perm_path(OP_CHMOD, path, AA_MAY_CHMOD);
+       return common_perm_cond(OP_CHMOD, path, AA_MAY_CHMOD);
 }
 
 static int apparmor_path_chown(const struct path *path, kuid_t uid, kgid_t gid)
 {
-       return common_perm_path(OP_CHOWN, path, AA_MAY_CHOWN);
+       return common_perm_cond(OP_CHOWN, path, AA_MAY_CHOWN);
 }
 
 static int apparmor_inode_getattr(const struct path *path)
 {
-       return common_perm_path(OP_GETATTR, path, AA_MAY_META_READ);
+       return common_perm_cond(OP_GETATTR, path, AA_MAY_META_READ);
 }
 
 static int apparmor_file_open(struct file *file, const struct cred *cred)
@@ -402,7 +406,7 @@ static int common_file_perm(const char *op, struct file *file, u32 mask)
        struct aa_profile *profile, *fprofile = aa_cred_profile(file->f_cred);
        int error = 0;
 
-       BUG_ON(!fprofile);
+       AA_BUG(!fprofile);
 
        if (!file->f_path.mnt ||
            !path_mediated_fs(file->f_path.dentry))
@@ -501,20 +505,16 @@ static int apparmor_getprocattr(struct task_struct *task, char *name,
        return error;
 }
 
-static int apparmor_setprocattr(struct task_struct *task, char *name,
-                               void *value, size_t size)
+static int apparmor_setprocattr(const char *name, void *value,
+                               size_t size)
 {
-       struct common_audit_data sa;
-       struct apparmor_audit_data aad = {0,};
        char *command, *largs = NULL, *args = value;
        size_t arg_size;
        int error;
+       DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SETPROCATTR);
 
        if (size == 0)
                return -EINVAL;
-       /* task can only write its own attributes */
-       if (current != task)
-               return -EACCES;
 
        /* AppArmor requires that the buffer must be null terminated atm */
        if (args[size - 1] != '\0') {
@@ -544,17 +544,17 @@ static int apparmor_setprocattr(struct task_struct *task, char *name,
                        error = aa_setprocattr_changehat(args, arg_size,
                                                         AA_DO_TEST);
                } else if (strcmp(command, "changeprofile") == 0) {
-                       error = aa_setprocattr_changeprofile(args, !AA_ONEXEC,
-                                                            !AA_DO_TEST);
+                       error = aa_change_profile(args, !AA_ONEXEC,
+                                                 !AA_DO_TEST, false);
                } else if (strcmp(command, "permprofile") == 0) {
-                       error = aa_setprocattr_changeprofile(args, !AA_ONEXEC,
-                                                            AA_DO_TEST);
+                       error = aa_change_profile(args, !AA_ONEXEC, AA_DO_TEST,
+                                                 false);
                } else
                        goto fail;
        } else if (strcmp(name, "exec") == 0) {
                if (strcmp(command, "exec") == 0)
-                       error = aa_setprocattr_changeprofile(args, AA_ONEXEC,
-                                                            !AA_DO_TEST);
+                       error = aa_change_profile(args, AA_ONEXEC, !AA_DO_TEST,
+                                                 false);
                else
                        goto fail;
        } else
@@ -568,12 +568,9 @@ out:
        return error;
 
 fail:
-       sa.type = LSM_AUDIT_DATA_NONE;
-       sa.aad = &aad;
-       aad.profile = aa_current_profile();
-       aad.op = OP_SETPROCATTR;
-       aad.info = name;
-       aad.error = error = -EINVAL;
+       aad(&sa)->profile = aa_current_profile();
+       aad(&sa)->info = name;
+       aad(&sa)->error = error = -EINVAL;
        aa_audit_msg(AUDIT_APPARMOR_DENIED, &sa, NULL);
        goto out;
 }
@@ -677,9 +674,9 @@ enum profile_mode aa_g_profile_mode = APPARMOR_ENFORCE;
 module_param_call(mode, param_set_mode, param_get_mode,
                  &aa_g_profile_mode, S_IRUSR | S_IWUSR);
 
-#ifdef CONFIG_SECURITY_APPARMOR_HASH
 /* whether policy verification hashing is enabled */
 bool aa_g_hash_policy = IS_ENABLED(CONFIG_SECURITY_APPARMOR_HASH_DEFAULT);
+#ifdef CONFIG_SECURITY_APPARMOR_HASH
 module_param_named(hash_policy, aa_g_hash_policy, aabool, S_IRUSR | S_IWUSR);
 #endif
 
@@ -750,6 +747,8 @@ static int param_get_aalockpolicy(char *buffer, const struct kernel_param *kp)
 {
        if (!policy_view_capable(NULL))
                return -EPERM;
+       if (!apparmor_enabled)
+               return -EINVAL;
        return param_get_bool(buffer, kp);
 }
 
@@ -757,6 +756,8 @@ static int param_set_aabool(const char *val, const struct kernel_param *kp)
 {
        if (!policy_admin_capable(NULL))
                return -EPERM;
+       if (!apparmor_enabled)
+               return -EINVAL;
        return param_set_bool(val, kp);
 }
 
@@ -764,6 +765,8 @@ static int param_get_aabool(char *buffer, const struct kernel_param *kp)
 {
        if (!policy_view_capable(NULL))
                return -EPERM;
+       if (!apparmor_enabled)
+               return -EINVAL;
        return param_get_bool(buffer, kp);
 }
 
@@ -771,6 +774,8 @@ static int param_set_aauint(const char *val, const struct kernel_param *kp)
 {
        if (!policy_admin_capable(NULL))
                return -EPERM;
+       if (!apparmor_enabled)
+               return -EINVAL;
        return param_set_uint(val, kp);
 }
 
@@ -778,6 +783,8 @@ static int param_get_aauint(char *buffer, const struct kernel_param *kp)
 {
        if (!policy_view_capable(NULL))
                return -EPERM;
+       if (!apparmor_enabled)
+               return -EINVAL;
        return param_get_uint(buffer, kp);
 }
 
@@ -871,6 +878,83 @@ static int __init set_init_ctx(void)
        return 0;
 }
 
+static void destroy_buffers(void)
+{
+       u32 i, j;
+
+       for_each_possible_cpu(i) {
+               for_each_cpu_buffer(j) {
+                       kfree(per_cpu(aa_buffers, i).buf[j]);
+                       per_cpu(aa_buffers, i).buf[j] = NULL;
+               }
+       }
+}
+
+static int __init alloc_buffers(void)
+{
+       u32 i, j;
+
+       for_each_possible_cpu(i) {
+               for_each_cpu_buffer(j) {
+                       char *buffer;
+
+                       if (cpu_to_node(i) > num_online_nodes())
+                               /* fallback to kmalloc for offline nodes */
+                               buffer = kmalloc(aa_g_path_max, GFP_KERNEL);
+                       else
+                               buffer = kmalloc_node(aa_g_path_max, GFP_KERNEL,
+                                                     cpu_to_node(i));
+                       if (!buffer) {
+                               destroy_buffers();
+                               return -ENOMEM;
+                       }
+                       per_cpu(aa_buffers, i).buf[j] = buffer;
+               }
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_SYSCTL
+static int apparmor_dointvec(struct ctl_table *table, int write,
+                            void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+       if (!policy_admin_capable(NULL))
+               return -EPERM;
+       if (!apparmor_enabled)
+               return -EINVAL;
+
+       return proc_dointvec(table, write, buffer, lenp, ppos);
+}
+
+static struct ctl_path apparmor_sysctl_path[] = {
+       { .procname = "kernel", },
+       { }
+};
+
+static struct ctl_table apparmor_sysctl_table[] = {
+       {
+               .procname       = "unprivileged_userns_apparmor_policy",
+               .data           = &unprivileged_userns_apparmor_policy,
+               .maxlen         = sizeof(int),
+               .mode           = 0600,
+               .proc_handler   = apparmor_dointvec,
+       },
+       { }
+};
+
+static int __init apparmor_init_sysctl(void)
+{
+       return register_sysctl_paths(apparmor_sysctl_path,
+                                    apparmor_sysctl_table) ? 0 : -ENOMEM;
+}
+#else
+static inline int apparmor_init_sysctl(void)
+{
+       return 0;
+}
+#endif /* CONFIG_SYSCTL */
+
 static int __init apparmor_init(void)
 {
        int error;
@@ -893,13 +977,27 @@ static int __init apparmor_init(void)
                goto alloc_out;
        }
 
+       error = apparmor_init_sysctl();
+       if (error) {
+               AA_ERROR("Unable to register sysctls\n");
+               goto alloc_out;
+
+       }
+
+       error = alloc_buffers();
+       if (error) {
+               AA_ERROR("Unable to allocate work buffers\n");
+               goto buffers_out;
+       }
+
        error = set_init_ctx();
        if (error) {
                AA_ERROR("Failed to set context on init task\n");
                aa_free_root_ns();
-               goto alloc_out;
+               goto buffers_out;
        }
-       security_add_hooks(apparmor_hooks, ARRAY_SIZE(apparmor_hooks));
+       security_add_hooks(apparmor_hooks, ARRAY_SIZE(apparmor_hooks),
+                               "apparmor");
 
        /* Report that AppArmor successfully initialized */
        apparmor_initialized = 1;
@@ -912,6 +1010,9 @@ static int __init apparmor_init(void)
 
        return error;
 
+buffers_out:
+       destroy_buffers();
+
 alloc_out:
        aa_destroy_aafs();
        aa_teardown_dfa_engine();