]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - security/security.c
LSM: Introduce CONFIG_LSM
[linux.git] / security / security.c
index d670136dda2c9066306c7974eccb3df6fbf58ca4..11a42cd313c5aba51ae59c40e30f28b3b1013e6d 100644 (file)
@@ -37,6 +37,9 @@
 /* Maximum number of letters for an LSM name string */
 #define SECURITY_NAME_MAX      10
 
+/* How many LSMs were built into the kernel? */
+#define LSM_COUNT (__end_lsm_info - __start_lsm_info)
+
 struct security_hook_heads security_hook_heads __lsm_ro_after_init;
 static ATOMIC_NOTIFIER_HEAD(lsm_notifier_chain);
 
@@ -45,6 +48,11 @@ char *lsm_names;
 static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =
        CONFIG_DEFAULT_SECURITY;
 
+static __initconst const char * const builtin_lsm_order = CONFIG_LSM;
+
+/* Ordered list of LSMs to initialize. */
+static __initdata struct lsm_info **ordered_lsms;
+
 static __initdata bool debug;
 #define init_debug(...)                                                \
        do {                                                    \
@@ -52,18 +60,156 @@ static __initdata bool debug;
                        pr_info(__VA_ARGS__);                   \
        } while (0)
 
-static void __init major_lsm_init(void)
+static bool __init is_enabled(struct lsm_info *lsm)
 {
-       struct lsm_info *lsm;
-       int ret;
+       if (!lsm->enabled || *lsm->enabled)
+               return true;
+
+       return false;
+}
+
+/* Mark an LSM's enabled flag. */
+static int lsm_enabled_true __initdata = 1;
+static int lsm_enabled_false __initdata = 0;
+static void __init set_enabled(struct lsm_info *lsm, bool enabled)
+{
+       /*
+        * When an LSM hasn't configured an enable variable, we can use
+        * a hard-coded location for storing the default enabled state.
+        */
+       if (!lsm->enabled) {
+               if (enabled)
+                       lsm->enabled = &lsm_enabled_true;
+               else
+                       lsm->enabled = &lsm_enabled_false;
+       } else if (lsm->enabled == &lsm_enabled_true) {
+               if (!enabled)
+                       lsm->enabled = &lsm_enabled_false;
+       } else if (lsm->enabled == &lsm_enabled_false) {
+               if (enabled)
+                       lsm->enabled = &lsm_enabled_true;
+       } else {
+               *lsm->enabled = enabled;
+       }
+}
+
+/* Is an LSM already listed in the ordered LSMs list? */
+static bool __init exists_ordered_lsm(struct lsm_info *lsm)
+{
+       struct lsm_info **check;
+
+       for (check = ordered_lsms; *check; check++)
+               if (*check == lsm)
+                       return true;
+
+       return false;
+}
+
+/* Append an LSM to the list of ordered LSMs to initialize. */
+static int last_lsm __initdata;
+static void __init append_ordered_lsm(struct lsm_info *lsm, const char *from)
+{
+       /* Ignore duplicate selections. */
+       if (exists_ordered_lsm(lsm))
+               return;
+
+       if (WARN(last_lsm == LSM_COUNT, "%s: out of LSM slots!?\n", from))
+               return;
+
+       ordered_lsms[last_lsm++] = lsm;
+       init_debug("%s ordering: %s (%sabled)\n", from, lsm->name,
+                  is_enabled(lsm) ? "en" : "dis");
+}
+
+/* Is an LSM allowed to be initialized? */
+static bool __init lsm_allowed(struct lsm_info *lsm)
+{
+       /* Skip if the LSM is disabled. */
+       if (!is_enabled(lsm))
+               return false;
+
+       /* Skip major-specific checks if not a major LSM. */
+       if ((lsm->flags & LSM_FLAG_LEGACY_MAJOR) == 0)
+               return true;
+
+       /* Disabled if this LSM isn't the chosen one. */
+       if (strcmp(lsm->name, chosen_lsm) != 0)
+               return false;
+
+       return true;
+}
+
+/* Check if LSM should be initialized. */
+static void __init maybe_initialize_lsm(struct lsm_info *lsm)
+{
+       int enabled = lsm_allowed(lsm);
+
+       /* Record enablement (to handle any following exclusive LSMs). */
+       set_enabled(lsm, enabled);
+
+       /* If selected, initialize the LSM. */
+       if (enabled) {
+               int ret;
 
-       for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
                init_debug("initializing %s\n", lsm->name);
                ret = lsm->init();
                WARN(ret, "%s failed to initialize: %d\n", lsm->name, ret);
        }
 }
 
+/* Populate ordered LSMs list from comma-separated LSM name list. */
+static void __init ordered_lsm_parse(const char *order, const char *origin)
+{
+       struct lsm_info *lsm;
+       char *sep, *name, *next;
+
+       sep = kstrdup(order, GFP_KERNEL);
+       next = sep;
+       /* Walk the list, looking for matching LSMs. */
+       while ((name = strsep(&next, ",")) != NULL) {
+               bool found = false;
+
+               for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
+                       if ((lsm->flags & LSM_FLAG_LEGACY_MAJOR) == 0 &&
+                           strcmp(lsm->name, name) == 0) {
+                               append_ordered_lsm(lsm, origin);
+                               found = true;
+                       }
+               }
+
+               if (!found)
+                       init_debug("%s ignored: %s\n", origin, name);
+       }
+       kfree(sep);
+}
+
+static void __init ordered_lsm_init(void)
+{
+       struct lsm_info **lsm;
+
+       ordered_lsms = kcalloc(LSM_COUNT + 1, sizeof(*ordered_lsms),
+                               GFP_KERNEL);
+
+       ordered_lsm_parse(builtin_lsm_order, "builtin");
+
+       for (lsm = ordered_lsms; *lsm; lsm++)
+               maybe_initialize_lsm(*lsm);
+
+       kfree(ordered_lsms);
+}
+
+static void __init major_lsm_init(void)
+{
+       struct lsm_info *lsm;
+
+       for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
+               if ((lsm->flags & LSM_FLAG_LEGACY_MAJOR) == 0)
+                       continue;
+
+               maybe_initialize_lsm(lsm);
+       }
+}
+
 /**
  * security_init - initializes the security framework
  *
@@ -87,6 +233,9 @@ int __init security_init(void)
        yama_add_hooks();
        loadpin_add_hooks();
 
+       /* Load LSMs in specified order. */
+       ordered_lsm_init();
+
        /*
         * Load all the remaining security modules.
         */
@@ -147,29 +296,6 @@ static int lsm_append(char *new, char **result)
        return 0;
 }
 
-/**
- * security_module_enable - Load given security module on boot ?
- * @module: the name of the module
- *
- * Each LSM must pass this method before registering its own operations
- * to avoid security registration races. This method may also be used
- * to check if your LSM is currently loaded during kernel initialization.
- *
- * Returns:
- *
- * true if:
- *
- * - The passed LSM is the one chosen by user at boot time,
- * - or the passed LSM is configured as the default and the user did not
- *   choose an alternate LSM at boot time.
- *
- * Otherwise, return false.
- */
-int __init security_module_enable(const char *module)
-{
-       return !strcmp(module, chosen_lsm);
-}
-
 /**
  * security_add_hooks - Add a modules hooks to the hook lists.
  * @hooks: the hooks to add
@@ -384,20 +510,31 @@ void security_sb_free(struct super_block *sb)
        call_void_hook(sb_free_security, sb);
 }
 
-int security_sb_copy_data(char *orig, char *copy)
+void security_free_mnt_opts(void **mnt_opts)
+{
+       if (!*mnt_opts)
+               return;
+       call_void_hook(sb_free_mnt_opts, *mnt_opts);
+       *mnt_opts = NULL;
+}
+EXPORT_SYMBOL(security_free_mnt_opts);
+
+int security_sb_eat_lsm_opts(char *options, void **mnt_opts)
 {
-       return call_int_hook(sb_copy_data, 0, orig, copy);
+       return call_int_hook(sb_eat_lsm_opts, 0, options, mnt_opts);
 }
-EXPORT_SYMBOL(security_sb_copy_data);
+EXPORT_SYMBOL(security_sb_eat_lsm_opts);
 
-int security_sb_remount(struct super_block *sb, void *data)
+int security_sb_remount(struct super_block *sb,
+                       void *mnt_opts)
 {
-       return call_int_hook(sb_remount, 0, sb, data);
+       return call_int_hook(sb_remount, 0, sb, mnt_opts);
 }
+EXPORT_SYMBOL(security_sb_remount);
 
-int security_sb_kern_mount(struct super_block *sb, int flags, void *data)
+int security_sb_kern_mount(struct super_block *sb)
 {
-       return call_int_hook(sb_kern_mount, 0, sb, flags, data);
+       return call_int_hook(sb_kern_mount, 0, sb);
 }
 
 int security_sb_show_options(struct seq_file *m, struct super_block *sb)
@@ -427,13 +564,13 @@ int security_sb_pivotroot(const struct path *old_path, const struct path *new_pa
 }
 
 int security_sb_set_mnt_opts(struct super_block *sb,
-                               struct security_mnt_opts *opts,
+                               void *mnt_opts,
                                unsigned long kern_flags,
                                unsigned long *set_kern_flags)
 {
        return call_int_hook(sb_set_mnt_opts,
-                               opts->num_mnt_opts ? -EOPNOTSUPP : 0, sb,
-                               opts, kern_flags, set_kern_flags);
+                               mnt_opts ? -EOPNOTSUPP : 0, sb,
+                               mnt_opts, kern_flags, set_kern_flags);
 }
 EXPORT_SYMBOL(security_sb_set_mnt_opts);
 
@@ -447,11 +584,13 @@ int security_sb_clone_mnt_opts(const struct super_block *oldsb,
 }
 EXPORT_SYMBOL(security_sb_clone_mnt_opts);
 
-int security_sb_parse_opts_str(char *options, struct security_mnt_opts *opts)
+int security_add_mnt_opt(const char *option, const char *val, int len,
+                        void **mnt_opts)
 {
-       return call_int_hook(sb_parse_opts_str, 0, options, opts);
+       return call_int_hook(sb_add_mnt_opt, -EINVAL,
+                                       option, val, len, mnt_opts);
 }
-EXPORT_SYMBOL(security_sb_parse_opts_str);
+EXPORT_SYMBOL(security_add_mnt_opt);
 
 int security_inode_alloc(struct inode *inode)
 {