]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
Infrastructure management of the cred security blob
authorCasey Schaufler <casey@schaufler-ca.com>
Mon, 12 Nov 2018 17:30:56 +0000 (09:30 -0800)
committerKees Cook <keescook@chromium.org>
Tue, 8 Jan 2019 21:18:44 +0000 (13:18 -0800)
Move management of the cred security blob out of the
security modules and into the security infrastructre.
Instead of allocating and freeing space the security
modules tell the infrastructure how much space they
require.

Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
[kees: adjusted for ordered init series]
Signed-off-by: Kees Cook <keescook@chromium.org>
include/linux/lsm_hooks.h
security/apparmor/include/cred.h
security/apparmor/include/lib.h
security/apparmor/lsm.c
security/security.c
security/selinux/hooks.c
security/selinux/include/objsec.h
security/smack/smack.h
security/smack/smack_lsm.c
security/tomoyo/common.h
security/tomoyo/tomoyo.c

index 0c908c091a03139cc04f1d3934e552530fb51429..dd33666567bc93ef3f9473e84e19baae699fbee3 100644 (file)
@@ -2027,6 +2027,13 @@ struct security_hook_list {
        char                            *lsm;
 } __randomize_layout;
 
+/*
+ * Security blob size or offset data.
+ */
+struct lsm_blob_sizes {
+       int     lbs_cred;
+};
+
 /*
  * Initializing a security_hook_list structure takes
  * up a lot of space in a source file. This macro takes
@@ -2056,6 +2063,7 @@ struct lsm_info {
        unsigned long flags;    /* Optional: flags describing LSM */
        int *enabled;           /* Optional: controlled by CONFIG_LSM */
        int (*init)(void);      /* Required. */
+       struct lsm_blob_sizes *blobs; /* Optional: for blob sharing. */
 };
 
 extern struct lsm_info __start_lsm_info[], __end_lsm_info[];
@@ -2095,4 +2103,8 @@ static inline void security_delete_hooks(struct security_hook_list *hooks,
 #define __lsm_ro_after_init    __ro_after_init
 #endif /* CONFIG_SECURITY_WRITABLE_HOOKS */
 
+#ifdef CONFIG_SECURITY
+void __init lsm_early_cred(struct cred *cred);
+#endif
+
 #endif /* ! __LINUX_LSM_HOOKS_H */
index a757370f2a0cdc9b8803c4fc98143822d4d53e2b..b9504a05fddcbbc14561431fa0d1c8c5c4cc7b5f 100644 (file)
@@ -25,7 +25,7 @@
 
 static inline struct aa_label *cred_label(const struct cred *cred)
 {
-       struct aa_label **blob = cred->security;
+       struct aa_label **blob = cred->security + apparmor_blob_sizes.lbs_cred;
 
        AA_BUG(!blob);
        return *blob;
@@ -34,7 +34,7 @@ static inline struct aa_label *cred_label(const struct cred *cred)
 static inline void set_cred_label(const struct cred *cred,
                                  struct aa_label *label)
 {
-       struct aa_label **blob = cred->security;
+       struct aa_label **blob = cred->security + apparmor_blob_sizes.lbs_cred;
 
        AA_BUG(!blob);
        *blob = label;
index 6505e1ad9e230605885f20f1e6f8df2029866cf2..bbe9b384d71d19684e5de3e041b3596346a922ff 100644 (file)
@@ -16,6 +16,7 @@
 
 #include <linux/slab.h>
 #include <linux/fs.h>
+#include <linux/lsm_hooks.h>
 
 #include "match.h"
 
@@ -55,6 +56,9 @@ const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name,
                             size_t *ns_len);
 void aa_info_message(const char *str);
 
+/* Security blob offsets */
+extern struct lsm_blob_sizes apparmor_blob_sizes;
+
 /**
  * aa_strneq - compare null terminated @str to a non null terminated substring
  * @str: a null terminated string
index 8c2cb4b1a6c36b62ece3c52bd9615f122266c7f8..d5e4a384f2050ff9c9487996e54d24bbf51ac266 100644 (file)
@@ -1151,6 +1151,13 @@ static int apparmor_inet_conn_request(struct sock *sk, struct sk_buff *skb,
 }
 #endif
 
+/*
+ * The cred blob is a pointer to, not an instance of, an aa_task_ctx.
+ */
+struct lsm_blob_sizes apparmor_blob_sizes __lsm_ro_after_init = {
+       .lbs_cred = sizeof(struct aa_task_ctx *),
+};
+
 static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
        LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check),
        LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme),
@@ -1485,6 +1492,7 @@ static int __init set_init_ctx(void)
        if (!ctx)
                return -ENOMEM;
 
+       lsm_early_cred(cred);
        set_cred_label(cred, aa_get_label(ns_unconfined(root_ns)));
        task_ctx(current) = ctx;
 
@@ -1725,5 +1733,6 @@ DEFINE_LSM(apparmor) = {
        .name = "apparmor",
        .flags = LSM_FLAG_LEGACY_MAJOR | LSM_FLAG_EXCLUSIVE,
        .enabled = &apparmor_enabled,
+       .blobs = &apparmor_blob_sizes,
        .init = apparmor_init,
 };
index 60b39db95c2f19e62ae99829ec2400c7f57cf15c..09be8ce007a25438bca3616eccce4f388a3318bd 100644 (file)
@@ -41,6 +41,8 @@ struct security_hook_heads security_hook_heads __lsm_ro_after_init;
 static ATOMIC_NOTIFIER_HEAD(lsm_notifier_chain);
 
 char *lsm_names;
+static struct lsm_blob_sizes blob_sizes __lsm_ro_after_init;
+
 /* Boot-time LSM user choice */
 static __initdata const char *chosen_lsm_order;
 static __initdata const char *chosen_major_lsm;
@@ -139,6 +141,25 @@ static bool __init lsm_allowed(struct lsm_info *lsm)
        return true;
 }
 
+static void __init lsm_set_blob_size(int *need, int *lbs)
+{
+       int offset;
+
+       if (*need > 0) {
+               offset = *lbs;
+               *lbs += *need;
+               *need = offset;
+       }
+}
+
+static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed)
+{
+       if (!needed)
+               return;
+
+       lsm_set_blob_size(&needed->lbs_cred, &blob_sizes.lbs_cred);
+}
+
 /* Prepare LSM for initialization. */
 static void __init prepare_lsm(struct lsm_info *lsm)
 {
@@ -153,6 +174,8 @@ static void __init prepare_lsm(struct lsm_info *lsm)
                        exclusive = lsm;
                        init_debug("exclusive chosen: %s\n", lsm->name);
                }
+
+               lsm_set_blob_sizes(lsm->blobs);
        }
 }
 
@@ -255,6 +278,8 @@ static void __init ordered_lsm_init(void)
        for (lsm = ordered_lsms; *lsm; lsm++)
                prepare_lsm(*lsm);
 
+       init_debug("cred blob size     = %d\n", blob_sizes.lbs_cred);
+
        for (lsm = ordered_lsms; *lsm; lsm++)
                initialize_lsm(*lsm);
 
@@ -382,6 +407,47 @@ int unregister_lsm_notifier(struct notifier_block *nb)
 }
 EXPORT_SYMBOL(unregister_lsm_notifier);
 
+/**
+ * lsm_cred_alloc - allocate a composite cred blob
+ * @cred: the cred that needs a blob
+ * @gfp: allocation type
+ *
+ * Allocate the cred blob for all the modules
+ *
+ * Returns 0, or -ENOMEM if memory can't be allocated.
+ */
+static int lsm_cred_alloc(struct cred *cred, gfp_t gfp)
+{
+       if (blob_sizes.lbs_cred == 0) {
+               cred->security = NULL;
+               return 0;
+       }
+
+       cred->security = kzalloc(blob_sizes.lbs_cred, gfp);
+       if (cred->security == NULL)
+               return -ENOMEM;
+       return 0;
+}
+
+/**
+ * lsm_early_cred - during initialization allocate a composite cred blob
+ * @cred: the cred that needs a blob
+ *
+ * Allocate the cred blob for all the modules if it's not already there
+ */
+void __init lsm_early_cred(struct cred *cred)
+{
+       int rc;
+
+       if (cred == NULL)
+               panic("%s: NULL cred.\n", __func__);
+       if (cred->security != NULL)
+               return;
+       rc = lsm_cred_alloc(cred, GFP_KERNEL);
+       if (rc)
+               panic("%s: Early cred alloc failed.\n", __func__);
+}
+
 /*
  * Hook list operation macros.
  *
@@ -1195,17 +1261,36 @@ void security_task_free(struct task_struct *task)
 
 int security_cred_alloc_blank(struct cred *cred, gfp_t gfp)
 {
-       return call_int_hook(cred_alloc_blank, 0, cred, gfp);
+       int rc = lsm_cred_alloc(cred, gfp);
+
+       if (rc)
+               return rc;
+
+       rc = call_int_hook(cred_alloc_blank, 0, cred, gfp);
+       if (rc)
+               security_cred_free(cred);
+       return rc;
 }
 
 void security_cred_free(struct cred *cred)
 {
        call_void_hook(cred_free, cred);
+
+       kfree(cred->security);
+       cred->security = NULL;
 }
 
 int security_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp)
 {
-       return call_int_hook(cred_prepare, 0, new, old, gfp);
+       int rc = lsm_cred_alloc(new, gfp);
+
+       if (rc)
+               return rc;
+
+       rc = call_int_hook(cred_prepare, 0, new, old, gfp);
+       if (rc)
+               security_cred_free(new);
+       return rc;
 }
 
 void security_transfer_creds(struct cred *new, const struct cred *old)
index 169cf5b3334bef7822c0d7756207266ccb1c031c..239b13b442e773b95e7c672bfe9af458806bb2ef 100644 (file)
@@ -210,12 +210,9 @@ static void cred_init_security(void)
        struct cred *cred = (struct cred *) current->real_cred;
        struct task_security_struct *tsec;
 
-       tsec = kzalloc(sizeof(struct task_security_struct), GFP_KERNEL);
-       if (!tsec)
-               panic("SELinux:  Failed to initialize initial task.\n");
-
+       lsm_early_cred(cred);
+       tsec = selinux_cred(cred);
        tsec->osid = tsec->sid = SECINITSID_KERNEL;
-       cred->security = tsec;
 }
 
 /*
@@ -3685,47 +3682,16 @@ static int selinux_task_alloc(struct task_struct *task,
                            sid, sid, SECCLASS_PROCESS, PROCESS__FORK, NULL);
 }
 
-/*
- * allocate the SELinux part of blank credentials
- */
-static int selinux_cred_alloc_blank(struct cred *cred, gfp_t gfp)
-{
-       struct task_security_struct *tsec;
-
-       tsec = kzalloc(sizeof(struct task_security_struct), gfp);
-       if (!tsec)
-               return -ENOMEM;
-
-       cred->security = tsec;
-       return 0;
-}
-
-/*
- * detach and free the LSM part of a set of credentials
- */
-static void selinux_cred_free(struct cred *cred)
-{
-       struct task_security_struct *tsec = selinux_cred(cred);
-
-       kfree(tsec);
-}
-
 /*
  * prepare a new set of credentials for modification
  */
 static int selinux_cred_prepare(struct cred *new, const struct cred *old,
                                gfp_t gfp)
 {
-       const struct task_security_struct *old_tsec;
-       struct task_security_struct *tsec;
-
-       old_tsec = selinux_cred(old);
-
-       tsec = kmemdup(old_tsec, sizeof(struct task_security_struct), gfp);
-       if (!tsec)
-               return -ENOMEM;
+       const struct task_security_struct *old_tsec = selinux_cred(old);
+       struct task_security_struct *tsec = selinux_cred(new);
 
-       new->security = tsec;
+       *tsec = *old_tsec;
        return 0;
 }
 
@@ -6678,6 +6644,10 @@ static void selinux_bpf_prog_free(struct bpf_prog_aux *aux)
 }
 #endif
 
+struct lsm_blob_sizes selinux_blob_sizes __lsm_ro_after_init = {
+       .lbs_cred = sizeof(struct task_security_struct),
+};
+
 static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
        LSM_HOOK_INIT(binder_set_context_mgr, selinux_binder_set_context_mgr),
        LSM_HOOK_INIT(binder_transaction, selinux_binder_transaction),
@@ -6761,8 +6731,6 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
        LSM_HOOK_INIT(file_open, selinux_file_open),
 
        LSM_HOOK_INIT(task_alloc, selinux_task_alloc),
-       LSM_HOOK_INIT(cred_alloc_blank, selinux_cred_alloc_blank),
-       LSM_HOOK_INIT(cred_free, selinux_cred_free),
        LSM_HOOK_INIT(cred_prepare, selinux_cred_prepare),
        LSM_HOOK_INIT(cred_transfer, selinux_cred_transfer),
        LSM_HOOK_INIT(cred_getsecid, selinux_cred_getsecid),
@@ -6981,6 +6949,7 @@ DEFINE_LSM(selinux) = {
        .name = "selinux",
        .flags = LSM_FLAG_LEGACY_MAJOR | LSM_FLAG_EXCLUSIVE,
        .enabled = &selinux_enabled,
+       .blobs = &selinux_blob_sizes,
        .init = selinux_init,
 };
 
index 734b6833bdff0a08c4a0c5137f15ee756ce96ecd..c2974b031d05d1a54de7481c5a5608847e42ea4b 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/binfmts.h>
 #include <linux/in.h>
 #include <linux/spinlock.h>
+#include <linux/lsm_hooks.h>
 #include <net/net_namespace.h>
 #include "flask.h"
 #include "avc.h"
@@ -158,9 +159,10 @@ struct bpf_security_struct {
        u32 sid;  /*SID of bpf obj creater*/
 };
 
+extern struct lsm_blob_sizes selinux_blob_sizes;
 static inline struct task_security_struct *selinux_cred(const struct cred *cred)
 {
-       return cred->security;
+       return cred->security + selinux_blob_sizes.lbs_cred;
 }
 
 #endif /* _SELINUX_OBJSEC_H_ */
index 01a922856eba07816abcf4c1423e519f05f5f1e1..b27eb252e953c437a40187f23eadab6b19fd677a 100644 (file)
@@ -336,6 +336,7 @@ extern struct smack_known *smack_syslog_label;
 extern struct smack_known *smack_unconfined;
 #endif
 extern int smack_ptrace_rule;
+extern struct lsm_blob_sizes smack_blob_sizes;
 
 extern struct smack_known smack_known_floor;
 extern struct smack_known smack_known_hat;
@@ -358,7 +359,7 @@ extern struct hlist_head smack_known_hash[SMACK_HASH_SLOTS];
 
 static inline struct task_smack *smack_cred(const struct cred *cred)
 {
-       return cred->security;
+       return cred->security + smack_blob_sizes.lbs_cred;
 }
 
 /*
index 9a050ca17296abcdc6404ff095e8eb9c0e0b8c52..bad27a8e16318897d3f64cb46382441ab11e40ea 100644 (file)
@@ -326,29 +326,20 @@ static struct inode_smack *new_inode_smack(struct smack_known *skp)
 }
 
 /**
- * new_task_smack - allocate a task security blob
+ * init_task_smack - initialize a task security blob
+ * @tsp: blob to initialize
  * @task: a pointer to the Smack label for the running task
  * @forked: a pointer to the Smack label for the forked task
- * @gfp: type of the memory for the allocation
  *
- * Returns the new blob or NULL if there's no memory available
  */
-static struct task_smack *new_task_smack(struct smack_known *task,
-                                       struct smack_known *forked, gfp_t gfp)
+static void init_task_smack(struct task_smack *tsp, struct smack_known *task,
+                                       struct smack_known *forked)
 {
-       struct task_smack *tsp;
-
-       tsp = kzalloc(sizeof(struct task_smack), gfp);
-       if (tsp == NULL)
-               return NULL;
-
        tsp->smk_task = task;
        tsp->smk_forked = forked;
        INIT_LIST_HEAD(&tsp->smk_rules);
        INIT_LIST_HEAD(&tsp->smk_relabel);
        mutex_init(&tsp->smk_rules_lock);
-
-       return tsp;
 }
 
 /**
@@ -1881,14 +1872,7 @@ static int smack_file_open(struct file *file)
  */
 static int smack_cred_alloc_blank(struct cred *cred, gfp_t gfp)
 {
-       struct task_smack *tsp;
-
-       tsp = new_task_smack(NULL, NULL, gfp);
-       if (tsp == NULL)
-               return -ENOMEM;
-
-       cred->security = tsp;
-
+       init_task_smack(smack_cred(cred), NULL, NULL);
        return 0;
 }
 
@@ -1905,10 +1889,6 @@ static void smack_cred_free(struct cred *cred)
        struct list_head *l;
        struct list_head *n;
 
-       if (tsp == NULL)
-               return;
-       cred->security = NULL;
-
        smk_destroy_label_list(&tsp->smk_relabel);
 
        list_for_each_safe(l, n, &tsp->smk_rules) {
@@ -1916,7 +1896,6 @@ static void smack_cred_free(struct cred *cred)
                list_del(&rp->list);
                kfree(rp);
        }
-       kfree(tsp);
 }
 
 /**
@@ -1931,14 +1910,10 @@ static int smack_cred_prepare(struct cred *new, const struct cred *old,
                              gfp_t gfp)
 {
        struct task_smack *old_tsp = smack_cred(old);
-       struct task_smack *new_tsp;
+       struct task_smack *new_tsp = smack_cred(new);
        int rc;
 
-       new_tsp = new_task_smack(old_tsp->smk_task, old_tsp->smk_task, gfp);
-       if (new_tsp == NULL)
-               return -ENOMEM;
-
-       new->security = new_tsp;
+       init_task_smack(new_tsp, old_tsp->smk_task, old_tsp->smk_task);
 
        rc = smk_copy_rules(&new_tsp->smk_rules, &old_tsp->smk_rules, gfp);
        if (rc != 0)
@@ -1946,10 +1921,7 @@ static int smack_cred_prepare(struct cred *new, const struct cred *old,
 
        rc = smk_copy_relabel(&new_tsp->smk_relabel, &old_tsp->smk_relabel,
                                gfp);
-       if (rc != 0)
-               return rc;
-
-       return 0;
+       return rc;
 }
 
 /**
@@ -4581,6 +4553,10 @@ static int smack_dentry_create_files_as(struct dentry *dentry, int mode,
        return 0;
 }
 
+struct lsm_blob_sizes smack_blob_sizes __lsm_ro_after_init = {
+       .lbs_cred = sizeof(struct task_smack),
+};
+
 static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
        LSM_HOOK_INIT(ptrace_access_check, smack_ptrace_access_check),
        LSM_HOOK_INIT(ptrace_traceme, smack_ptrace_traceme),
@@ -4758,20 +4734,25 @@ static __init void init_smack_known_list(void)
  */
 static __init int smack_init(void)
 {
-       struct cred *cred;
+       struct cred *cred = (struct cred *) current->cred;
        struct task_smack *tsp;
 
        smack_inode_cache = KMEM_CACHE(inode_smack, 0);
        if (!smack_inode_cache)
                return -ENOMEM;
 
-       tsp = new_task_smack(&smack_known_floor, &smack_known_floor,
-                               GFP_KERNEL);
-       if (tsp == NULL) {
-               kmem_cache_destroy(smack_inode_cache);
-               return -ENOMEM;
-       }
+       lsm_early_cred(cred);
 
+       /*
+        * Set the security state for the initial task.
+        */
+       tsp = smack_cred(cred);
+       init_task_smack(tsp, &smack_known_floor, &smack_known_floor);
+
+       /*
+        * Register with LSM
+        */
+       security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks), "smack");
        smack_enabled = 1;
 
        pr_info("Smack:  Initializing.\n");
@@ -4785,20 +4766,9 @@ static __init int smack_init(void)
        pr_info("Smack:  IPv6 Netfilter enabled.\n");
 #endif
 
-       /*
-        * Set the security state for the initial task.
-        */
-       cred = (struct cred *) current->cred;
-       cred->security = tsp;
-
        /* initialize the smack_known_list */
        init_smack_known_list();
 
-       /*
-        * Register with LSM
-        */
-       security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks), "smack");
-
        return 0;
 }
 
@@ -4809,5 +4779,6 @@ static __init int smack_init(void)
 DEFINE_LSM(smack) = {
        .name = "smack",
        .flags = LSM_FLAG_LEGACY_MAJOR | LSM_FLAG_EXCLUSIVE,
+       .blobs = &smack_blob_sizes,
        .init = smack_init,
 };
index 41898613d93b5fda8b30b1da722acba64c6f6d6d..4fc17294a12dce1959ea81250282c21178ba3764 100644 (file)
@@ -1087,6 +1087,7 @@ extern struct tomoyo_domain_info tomoyo_kernel_domain;
 extern struct tomoyo_policy_namespace tomoyo_kernel_namespace;
 extern unsigned int tomoyo_memory_quota[TOMOYO_MAX_MEMORY_STAT];
 extern unsigned int tomoyo_memory_used[TOMOYO_MAX_MEMORY_STAT];
+extern struct lsm_blob_sizes tomoyo_blob_sizes;
 
 /********** Inlined functions. **********/
 
@@ -1206,7 +1207,7 @@ static inline void tomoyo_put_group(struct tomoyo_group *group)
  */
 static inline struct tomoyo_domain_info **tomoyo_cred(const struct cred *cred)
 {
-       return (struct tomoyo_domain_info **)&cred->security;
+       return cred->security + tomoyo_blob_sizes.lbs_cred;
 }
 
 /**
index 15864307925d817c27185c0ddbd178ee03324cbc..9094cf41a2478c360baf458d6ea213578fe211d0 100644 (file)
@@ -509,6 +509,10 @@ static int tomoyo_socket_sendmsg(struct socket *sock, struct msghdr *msg,
        return tomoyo_socket_sendmsg_permission(sock, msg, size);
 }
 
+struct lsm_blob_sizes tomoyo_blob_sizes __lsm_ro_after_init = {
+       .lbs_cred = sizeof(struct tomoyo_domain_info *),
+};
+
 /*
  * tomoyo_security_ops is a "struct security_operations" which is used for
  * registering TOMOYO.
@@ -562,6 +566,7 @@ static int __init tomoyo_init(void)
        /* register ourselves with the security framework */
        security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks), "tomoyo");
        printk(KERN_INFO "TOMOYO Linux initialized\n");
+       lsm_early_cred(cred);
        blob = tomoyo_cred(cred);
        *blob = &tomoyo_kernel_domain;
        tomoyo_mm_init();
@@ -573,5 +578,6 @@ DEFINE_LSM(tomoyo) = {
        .name = "tomoyo",
        .enabled = &tomoyo_enabled,
        .flags = LSM_FLAG_LEGACY_MAJOR | LSM_FLAG_EXCLUSIVE,
+       .blobs = &tomoyo_blob_sizes,
        .init = tomoyo_init,
 };