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

From: Kees Cook
Date: Wed Sep 12 2018 - 19:53:42 EST


On Tue, Sep 11, 2018 at 9:41 AM, Casey Schaufler <casey@xxxxxxxxxxxxxxxx> 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@xxxxxxxxxxxxxxxx>
> ---
> 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