[PATCH 04/10] LSM: Infrastructure management of the cred security blob

Kees Cook keescook at chromium.org
Wed Sep 12 23:53:31 UTC 2018


On Tue, Sep 11, 2018 at 9:41 AM, Casey Schaufler <casey at schaufler-ca.com> wrote:
> Move management of the cred security blob out of the
> security modules and into the security infrastructure.
> Instead of allocating and freeing space the security
> modules tell the infrastructure how much space they
> require.

There's a lot of changes here that I think deserve some longer
discussion in the changelog. Notably, now we run _two_ cycles of init
calls? That seems... odd. Notes below...

> Some SELinux memory management debug code has been removed.
>
> Signed-off-by: Casey Schaufler <casey at schaufler-ca.com>
> ---
>  include/linux/lsm_hooks.h         |  14 ++++
>  kernel/cred.c                     |  13 ----
>  security/Kconfig                  |  11 ++++
>  security/apparmor/domain.c        |   2 +-
>  security/apparmor/include/cred.h  |  16 ++++-
>  security/apparmor/lsm.c           |  28 ++++++--
>  security/apparmor/task.c          |   6 +-
>  security/security.c               | 106 +++++++++++++++++++++++++++++-
>  security/selinux/hooks.c          |  63 +++++-------------
>  security/selinux/include/objsec.h |   4 ++
>  security/selinux/selinuxfs.c      |   1 +
>  security/smack/smack.h            |   1 +
>  security/smack/smack_lsm.c        |  85 +++++++++---------------
>  security/tomoyo/common.h          |  21 +++++-
>  security/tomoyo/domain.c          |   4 +-
>  security/tomoyo/securityfs_if.c   |  15 +++--
>  security/tomoyo/tomoyo.c          |  56 +++++++++++++---
>  17 files changed, 303 insertions(+), 143 deletions(-)
>
> diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
> index 97a020c616ad..0bef312efd45 100644
> --- a/include/linux/lsm_hooks.h
> +++ b/include/linux/lsm_hooks.h
> @@ -2024,6 +2024,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
> @@ -2036,6 +2043,7 @@ struct security_hook_list {
>  extern struct security_hook_heads security_hook_heads;
>  extern char *lsm_names;
>
> +extern void security_add_blobs(struct lsm_blob_sizes *needed);
>  extern void security_add_hooks(struct security_hook_list *hooks, int count,
>                                 char *lsm);
>
> @@ -2082,4 +2090,10 @@ void __init loadpin_add_hooks(void);
>  static inline void loadpin_add_hooks(void) { };
>  #endif
>
> +extern int lsm_cred_alloc(struct cred *cred, gfp_t gfp);
> +
> +#ifdef CONFIG_SECURITY
> +void lsm_early_cred(struct cred *cred);
> +#endif
> +
>  #endif /* ! __LINUX_LSM_HOOKS_H */
> diff --git a/kernel/cred.c b/kernel/cred.c
> index ecf03657e71c..fa2061ee4955 100644
> --- a/kernel/cred.c
> +++ b/kernel/cred.c
> @@ -704,19 +704,6 @@ bool creds_are_invalid(const struct cred *cred)
>  {
>         if (cred->magic != CRED_MAGIC)
>                 return true;
> -#ifdef CONFIG_SECURITY_SELINUX
> -       /*
> -        * cred->security == NULL if security_cred_alloc_blank() or
> -        * security_prepare_creds() returned an error.
> -        */
> -       if (selinux_is_enabled() && cred->security) {
> -               if ((unsigned long) cred->security < PAGE_SIZE)
> -                       return true;
> -               if ((*(u32 *)cred->security & 0xffffff00) ==
> -                   (POISON_FREE << 24 | POISON_FREE << 16 | POISON_FREE << 8))
> -                       return true;

These aren't unreasonable checks -- can we add them back in later?
(They don't need to be selinux specific, in fact: the LSM could do the
poison...)

> [...]
> diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
> index 08c88de0ffda..726910bba84b 100644
> --- a/security/apparmor/domain.c
> +++ b/security/apparmor/domain.c
> @@ -975,7 +975,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
>         }
>         aa_put_label(cred_label(bprm->cred));
>         /* transfer reference, released when cred is freed */
> -       cred_label(bprm->cred) = new;
> +       set_cred_label(bprm->cred, new);
>
>  done:
>         aa_put_label(label);
> diff --git a/security/apparmor/include/cred.h b/security/apparmor/include/cred.h
> index e287b7d0d4be..a90eae76d7c1 100644
> --- a/security/apparmor/include/cred.h
> +++ b/security/apparmor/include/cred.h
> @@ -23,8 +23,22 @@
>  #include "policy_ns.h"
>  #include "task.h"
>
> -#define cred_label(X) ((X)->security)
> +static inline struct aa_label *cred_label(const struct cred *cred)
> +{
> +       struct aa_label **blob = cred->security;
> +
> +       AA_BUG(!blob);
> +       return *blob;
> +}
>
> +static inline void set_cred_label(const struct cred *cred,
> +                                 struct aa_label *label)
> +{
> +       struct aa_label **blob = cred->security;
> +
> +       AA_BUG(!blob);
> +       *blob = label;
> +}

This feels like it should be a separate patch? Shouldn't AA not be
playing with cred->security directly before we get to this "sizing and
allocation" patch?

> [...]
> diff --git a/security/security.c b/security/security.c
> index 3dfe75d0d373..ff7df14f6db1 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -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;

This needs to be __lsm_ro_after_init.

> +
>  /* Boot-time LSM user choice */
>  static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =
>         CONFIG_DEFAULT_SECURITY;
> @@ -85,10 +87,22 @@ int __init security_init(void)
>         loadpin_add_hooks();
>
>         /*
> -        * Load all the remaining security modules.
> +        * The first call to a module specific init function
> +        * updates the blob size requirements.
> +        */
> +       do_security_initcalls();
> +
> +       /*
> +        * The second call to a module specific init function
> +        * adds hooks to the hook lists and does any other early
> +        * initializations required.
>          */
>         do_security_initcalls();

Tracking init state internally to each LSM seems not great. What about
having the state be a global the LSMs can query?

enum security_initcall_state_t {
    LSM_PRE_INIT,
    LSM_INIT,
};

static __initdata enum security_initcall_state_t security_initcall_state;

static void __init __do_security_initcalls(enum security_initcall_state state)
{
        int ret;
        initcall_t call;
        initcall_entry_t *ce;

        security_initcall_state = state;

        ce = __security_initcall_start;
        trace_initcall_level("security");
        while (ce < __security_initcall_end) {
                call = initcall_from_entry(ce);
                trace_initcall_start(call);
                ret = call();
                trace_initcall_finish(call, ret);
                ce++;
        }
}

static void __init do_security_initcalls(void)
{
       __do_security_initcalls(LSM_PRE_INIT);
       __do_security_initcalls(LSM_INIT);
}

>
> +#ifdef CONFIG_SECURITY_LSM_DEBUG
> +       pr_info("LSM: cred blob size       = %d\n", blob_sizes.lbs_cred);
> +#endif
> +
>         return 0;
>  }
>
> @@ -198,6 +212,73 @@ 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.
> + */
> +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

Perhaps mention this is mainly for retroactively attaching a cred blob
to "init"?

> + */
> +void lsm_early_cred(struct cred *cred)
> +{
> +       int rc;
> +
> +       if (cred == NULL)
> +               panic("%s: NULL cred.\n", __func__);

I have been given strongly worded advice to never BUG nor panic. I would:

if (WARN_ON(!cred || !cred->security))
       return;

> +       if (cred->security != NULL)
> +               return;
> +       rc = lsm_cred_alloc(cred, GFP_KERNEL);
> +       if (rc)
> +               panic("%s: Early cred alloc failed.\n", __func__);

And:

WARN_ON(lsm_cred_alloc(cred, GFP_KERNEL));

> +}
> +
> +static void __init lsm_set_size(int *need, int *lbs)
> +{
> +       int offset;
> +
> +       if (*need > 0) {
> +               offset = *lbs;
> +               *lbs += *need;
> +               *need = offset;
> +       }
> +}
> +
> +/**
> + * security_add_blobs - Report blob sizes
> + * @needed: the size of blobs needed by the module
> + *
> + * Each LSM has to register its blobs with the infrastructure.
> + * The "needed" data tells the infrastructure how much memory
> + * the module requires for each of its blobs. On return the
> + * structure is filled with the offset that module should use
> + * from the blob pointer.
> + */
> +void __init security_add_blobs(struct lsm_blob_sizes *needed)
> +{
> +       lsm_set_size(&needed->lbs_cred, &blob_sizes.lbs_cred);
> +}
> +
>  /*
>   * Hook list operation macros.
>   *
> @@ -998,17 +1079,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;

I spent some time looking at this, but I think this is fine. I thought
it might be a double-free via the put_cred_rcu() path, but
cred->security gets set to NULL below, and we really don't want
allocated memory hanging around in the call_int_hook fails, so ...
yes. All good.

>  }
>
>  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)
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index 9d6cdd21acb6..9b49698754a7 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -213,12 +213,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);

Perhaps leave the existing panic() as-is?

>         tsec->osid = tsec->sid = SECINITSID_KERNEL;
> -       cred->security = tsec;
>  }
>
>  /*
> @@ -3898,53 +3895,17 @@ 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);
> -
> -       /*
> -        * cred->security == NULL if security_cred_alloc_blank() or
> -        * security_prepare_creds() returned an error.
> -        */
> -       BUG_ON(cred->security && (unsigned long) cred->security < PAGE_SIZE);
> -       cred->security = (void *) 0x7UL;

What is the world was this? Is this a "< PAGE_SIZE" poison of 0x7?

> -       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);
> +       const struct task_security_struct *old_tsec = selinux_cred(old);
> +       struct task_security_struct *tsec = selinux_cred(new);
>
> -       tsec = kmemdup(old_tsec, sizeof(struct task_security_struct), gfp);
> -       if (!tsec)
> -               return -ENOMEM;
> +       *tsec = *old_tsec;
>
> -       new->security = tsec;
>         return 0;
>  }
>
> @@ -6894,6 +6855,10 @@ static void selinux_bpf_prog_free(struct bpf_prog_aux *aux)
>  }
>  #endif
>
> +struct lsm_blob_sizes selinux_blob_sizes = {
> +       .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),
> @@ -6976,8 +6941,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),
> @@ -7133,11 +7096,19 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
>
>  static __init int selinux_init(void)
>  {
> +       static int finish;
> +
>         if (!security_module_enable("selinux")) {
>                 selinux_enabled = 0;
>                 return 0;
>         }
>
> +       if (!finish) {
> +               security_add_blobs(&selinux_blob_sizes);
> +               finish = 1;
> +               return 0;
> +       }
> +
>         if (!selinux_enabled) {
>                 pr_info("SELinux:  Disabled at boot.\n");
>                 return 0;
> diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
> index 734b6833bdff..db1c7000ada3 100644
> --- a/security/selinux/include/objsec.h
> +++ b/security/selinux/include/objsec.h
> @@ -25,6 +25,9 @@
>  #include <linux/binfmts.h>
>  #include <linux/in.h>
>  #include <linux/spinlock.h>
> +#include <linux/lsm_hooks.h>
> +#include <linux/msg.h>
> +#include <net/sock.h>

That's a surprising number of new headers?

>  #include <net/net_namespace.h>
>  #include "flask.h"
>  #include "avc.h"
> @@ -158,6 +161,7 @@ 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;
> diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
> index f3a5a138a096..b5665bdc29fc 100644
> --- a/security/selinux/selinuxfs.c
> +++ b/security/selinux/selinuxfs.c
> @@ -31,6 +31,7 @@
>  #include <linux/uaccess.h>
>  #include <linux/kobject.h>
>  #include <linux/ctype.h>
> +#include <linux/lsm_hooks.h>
>
>  /* selinuxfs pseudo filesystem for exporting the security policy API.
>     Based on the proc code and the fs/nfsd/nfsctl.c code. */
> diff --git a/security/smack/smack.h b/security/smack/smack.h
> index 0b55d6a55b26..0c6dce446825 100644
> --- a/security/smack/smack.h
> +++ b/security/smack/smack.h
> @@ -24,6 +24,7 @@
>  #include <linux/list.h>
>  #include <linux/rculist.h>
>  #include <linux/lsm_audit.h>
> +#include <linux/msg.h>
>
>  /*
>   * Use IPv6 port labeling if IPv6 is enabled and secmarks
> diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
> index 68ee3ae8f25c..a06ea8aa89c4 100644
> --- a/security/smack/smack_lsm.c
> +++ b/security/smack/smack_lsm.c
> @@ -309,29 +309,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;
>  }
>
>  /**
> @@ -1958,14 +1949,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;
>  }
>
> @@ -1982,10 +1966,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) {
> @@ -1993,7 +1973,6 @@ static void smack_cred_free(struct cred *cred)
>                 list_del(&rp->list);
>                 kfree(rp);
>         }
> -       kfree(tsp);
>  }
>
>  /**
> @@ -2008,14 +1987,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)
> @@ -2023,10 +1998,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;
>  }
>
>  /**
> @@ -4652,6 +4624,10 @@ static int smack_dentry_create_files_as(struct dentry *dentry, int mode,
>         return 0;
>  }
>
> +struct lsm_blob_sizes smack_blob_sizes = {
> +       .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),
> @@ -4830,23 +4806,35 @@ static __init void init_smack_known_list(void)
>   */
>  static __init int smack_init(void)
>  {
> -       struct cred *cred;
> +       static int finish;
> +       struct cred *cred = (struct cred *) current->cred;
>         struct task_smack *tsp;
>
>         if (!security_module_enable("smack"))
>                 return 0;
>
> +       if (!finish) {
> +               security_add_blobs(&smack_blob_sizes);
> +               finish = 1;
> +               return 0;
> +       }
> +
>         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");
> @@ -4860,20 +4848,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;
>  }

I feel like some of this Smack refactoring could be split out to ease review...

>
> diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h
> index 539bcdd30bb8..0110bebe86e2 100644
> --- a/security/tomoyo/common.h
> +++ b/security/tomoyo/common.h
> @@ -29,6 +29,7 @@
>  #include <linux/in.h>
>  #include <linux/in6.h>
>  #include <linux/un.h>
> +#include <linux/lsm_hooks.h>
>  #include <net/sock.h>
>  #include <net/af_unix.h>
>  #include <net/ip.h>
> @@ -1062,6 +1063,7 @@ void tomoyo_write_log2(struct tomoyo_request_info *r, int len, const char *fmt,
>  /********** External variable definitions. **********/
>
>  extern bool tomoyo_policy_loaded;
> +extern bool tomoyo_enabled;
>  extern const char * const tomoyo_condition_keyword
>  [TOMOYO_MAX_CONDITION_KEYWORD];
>  extern const char * const tomoyo_dif[TOMOYO_MAX_DOMAIN_INFO_FLAGS];
> @@ -1196,6 +1198,17 @@ static inline void tomoyo_put_group(struct tomoyo_group *group)
>                 atomic_dec(&group->head.users);
>  }
>
> +/**
> + * tomoyo_cred - Get a pointer to the tomoyo cred security blob
> + * @cred - the relevant cred
> + *
> + * Returns pointer to the tomoyo cred blob.
> + */
> +static inline struct tomoyo_domain_info **tomoyo_cred(const struct cred *cred)
> +{
> +       return cred->security;
> +}
> +
>  /**
>   * tomoyo_domain - Get "struct tomoyo_domain_info" for current thread.
>   *
> @@ -1203,7 +1216,9 @@ static inline void tomoyo_put_group(struct tomoyo_group *group)
>   */
>  static inline struct tomoyo_domain_info *tomoyo_domain(void)
>  {
> -       return current_cred()->security;
> +       struct tomoyo_domain_info **blob = tomoyo_cred(current_cred());
> +
> +       return *blob;
>  }
>
>  /**
> @@ -1216,7 +1231,9 @@ static inline struct tomoyo_domain_info *tomoyo_domain(void)
>  static inline struct tomoyo_domain_info *tomoyo_real_domain(struct task_struct
>                                                             *task)
>  {
> -       return task_cred_xxx(task, security);
> +       struct tomoyo_domain_info **blob = tomoyo_cred(get_task_cred(task));
> +
> +       return *blob;
>  }
>
>  /**

Shouldn't this Tomoyo cred handling get split out?

> diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c
> index f6758dad981f..b7469fdbff01 100644
> --- a/security/tomoyo/domain.c
> +++ b/security/tomoyo/domain.c
> @@ -678,6 +678,7 @@ static int tomoyo_environ(struct tomoyo_execve *ee)
>   */
>  int tomoyo_find_next_domain(struct linux_binprm *bprm)
>  {
> +       struct tomoyo_domain_info **blob;
>         struct tomoyo_domain_info *old_domain = tomoyo_domain();
>         struct tomoyo_domain_info *domain = NULL;
>         const char *original_name = bprm->filename;
> @@ -843,7 +844,8 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
>                 domain = old_domain;
>         /* Update reference count on "struct tomoyo_domain_info". */
>         atomic_inc(&domain->users);
> -       bprm->cred->security = domain;
> +       blob = tomoyo_cred(bprm->cred);
> +       *blob = domain;
>         kfree(exename.name);
>         if (!retval) {
>                 ee->r.domain = domain;
> diff --git a/security/tomoyo/securityfs_if.c b/security/tomoyo/securityfs_if.c
> index 1d3d7e7a1f05..768dff9608b1 100644
> --- a/security/tomoyo/securityfs_if.c
> +++ b/security/tomoyo/securityfs_if.c
> @@ -71,9 +71,12 @@ static ssize_t tomoyo_write_self(struct file *file, const char __user *buf,
>                                 if (!cred) {
>                                         error = -ENOMEM;
>                                 } else {
> -                                       struct tomoyo_domain_info *old_domain =
> -                                               cred->security;
> -                                       cred->security = new_domain;
> +                                       struct tomoyo_domain_info **blob;
> +                                       struct tomoyo_domain_info *old_domain;
> +
> +                                       blob = tomoyo_cred(cred);
> +                                       old_domain = *blob;
> +                                       *blob = new_domain;
>                                         atomic_inc(&new_domain->users);
>                                         atomic_dec(&old_domain->users);
>                                         commit_creds(cred);
> @@ -234,10 +237,14 @@ static void __init tomoyo_create_entry(const char *name, const umode_t mode,
>   */
>  static int __init tomoyo_initerface_init(void)
>  {
> +       struct tomoyo_domain_info *domain;
>         struct dentry *tomoyo_dir;
>
> +       if (!tomoyo_enabled)
> +               return 0;
> +       domain = tomoyo_domain();
>         /* Don't create securityfs entries unless registered. */
> -       if (current_cred()->security != &tomoyo_kernel_domain)
> +       if (domain != &tomoyo_kernel_domain)
>                 return 0;
>
>         tomoyo_dir = securityfs_create_dir("tomoyo", NULL);
> diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
> index 9f932e2d6852..bb84e6ec3886 100644
> --- a/security/tomoyo/tomoyo.c
> +++ b/security/tomoyo/tomoyo.c
> @@ -18,7 +18,9 @@
>   */
>  static int tomoyo_cred_alloc_blank(struct cred *new, gfp_t gfp)
>  {
> -       new->security = NULL;
> +       struct tomoyo_domain_info **blob = tomoyo_cred(new);
> +
> +       *blob = NULL;
>         return 0;
>  }
>
> @@ -34,8 +36,13 @@ static int tomoyo_cred_alloc_blank(struct cred *new, gfp_t gfp)
>  static int tomoyo_cred_prepare(struct cred *new, const struct cred *old,
>                                gfp_t gfp)
>  {
> -       struct tomoyo_domain_info *domain = old->security;
> -       new->security = domain;
> +       struct tomoyo_domain_info **old_blob = tomoyo_cred(old);
> +       struct tomoyo_domain_info **new_blob = tomoyo_cred(new);
> +       struct tomoyo_domain_info *domain;
> +
> +       domain = *old_blob;
> +       *new_blob = domain;
> +
>         if (domain)
>                 atomic_inc(&domain->users);
>         return 0;
> @@ -59,7 +66,9 @@ static void tomoyo_cred_transfer(struct cred *new, const struct cred *old)
>   */
>  static void tomoyo_cred_free(struct cred *cred)
>  {
> -       struct tomoyo_domain_info *domain = cred->security;
> +       struct tomoyo_domain_info **blob = tomoyo_cred(cred);
> +       struct tomoyo_domain_info *domain = *blob;
> +
>         if (domain)
>                 atomic_dec(&domain->users);
>  }
> @@ -73,6 +82,9 @@ static void tomoyo_cred_free(struct cred *cred)
>   */
>  static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
>  {
> +       struct tomoyo_domain_info **blob;
> +       struct tomoyo_domain_info *domain;
> +
>         /*
>          * Do only if this function is called for the first time of an execve
>          * operation.
> @@ -93,13 +105,14 @@ static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
>          * stored inside "bprm->cred->security" will be acquired later inside
>          * tomoyo_find_next_domain().
>          */
> -       atomic_dec(&((struct tomoyo_domain_info *)
> -                    bprm->cred->security)->users);
> +       blob = tomoyo_cred(bprm->cred);
> +       domain = *blob;
> +       atomic_dec(&domain->users);
>         /*
>          * Tell tomoyo_bprm_check_security() is called for the first time of an
>          * execve operation.
>          */
> -       bprm->cred->security = NULL;
> +       *blob = NULL;
>         return 0;
>  }
>
> @@ -112,8 +125,11 @@ static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
>   */
>  static int tomoyo_bprm_check_security(struct linux_binprm *bprm)
>  {
> -       struct tomoyo_domain_info *domain = bprm->cred->security;
> +       struct tomoyo_domain_info **blob;
> +       struct tomoyo_domain_info *domain;
>
> +       blob = tomoyo_cred(bprm->cred);
> +       domain = *blob;
>         /*
>          * Execute permission is checked against pathname passed to do_execve()
>          * using current domain.
> @@ -493,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 = {
> +       .lbs_cred = sizeof(struct tomoyo_domain_info *),
> +};
> +
>  /*
>   * tomoyo_security_ops is a "struct security_operations" which is used for
>   * registering TOMOYO.
> @@ -531,6 +551,8 @@ static struct security_hook_list tomoyo_hooks[] __lsm_ro_after_init = {
>  /* Lock for GC. */
>  DEFINE_SRCU(tomoyo_ss);
>
> +bool tomoyo_enabled;
> +
>  /**
>   * tomoyo_init - Register TOMOYO Linux as a LSM module.
>   *
> @@ -538,14 +560,28 @@ DEFINE_SRCU(tomoyo_ss);
>   */
>  static int __init tomoyo_init(void)
>  {
> +       static int finish;
>         struct cred *cred = (struct cred *) current_cred();
> +       struct tomoyo_domain_info **blob;
> +
> +       if (!security_module_enable("tomoyo")) {
> +               tomoyo_enabled = false;
> +               return 0;
> +       }
> +       tomoyo_enabled = true;
>
> -       if (!security_module_enable("tomoyo"))
> +       if (!finish) {
> +               security_add_blobs(&tomoyo_blob_sizes);
> +               finish = 1;
>                 return 0;
> +       }
> +
>         /* register ourselves with the security framework */
>         security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks), "tomoyo");
>         printk(KERN_INFO "TOMOYO Linux initialized\n");
> -       cred->security = &tomoyo_kernel_domain;
> +       lsm_early_cred(cred);
> +       blob = tomoyo_cred(cred);
> +       *blob = &tomoyo_kernel_domain;
>         tomoyo_mm_init();
>         return 0;
>  }
> --
> 2.17.1
>
>

-Kees

-- 
Kees Cook
Pixel Security



More information about the Linux-security-module-archive mailing list