[RFC,05/10] LSM: Infrastructure managed cred security blob
diff mbox

Message ID ec849ab8-867a-3429-7ee3-50e0a1e32e2a@schaufler-ca.com
State New
Headers show

Commit Message

Casey Schaufler July 11, 2016, 7:30 p.m. UTC
Subject: [PATCH RFC 05/10] LSM: Infrastructure managed cred security blob

Move management of the cred security blob from the security
modules to the LSM infrastructure. This requires that the modules
declare the blob size they require, so the module registration
process has to include that. A function lsm_cred_alloc() is
provided so that modules can do early allocation of cred blobs
during module initialization. Module hooks that are no longer
required are removed.

TOMOYO was stuffing a pointer that it manages into cred->security.
Now a blob big enough for the pointer is allocated by the infrastruture
and the pointer is saved there. It might make sense to revise the way
this module manages it's process data.

Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
---
 include/linux/lsm_hooks.h       | 23 ++++++++++++
 security/Kconfig                | 11 ++++++
 security/apparmor/context.c     |  2 -
 security/apparmor/lsm.c         | 45 ++++++++---------------
 security/security.c             | 81 ++++++++++++++++++++++++++++++++++++++++-
 security/selinux/hooks.c        | 66 +++++++++------------------------
 security/smack/smack_lsm.c      | 75 ++++++++++----------------------------
 security/tomoyo/common.h        | 10 +++--
 security/tomoyo/domain.c        |  4 +-
 security/tomoyo/securityfs_if.c |  8 ++--
 security/tomoyo/tomoyo.c        | 42 ++++++++++++++++-----
 11 files changed, 214 insertions(+), 153 deletions(-)


--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Patch
diff mbox

diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 0bfd441..6086045 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -1845,6 +1845,13 @@  struct security_hook_list {
 };
 
 /*
+ * 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
  * care of the common case and reduces the amount of
@@ -1856,6 +1863,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);
 
@@ -1895,4 +1903,19 @@  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);
+
+static inline void 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__);
+}
+
 #endif /* ! __LINUX_LSM_HOOKS_H */
diff --git a/security/Kconfig b/security/Kconfig
index 176758c..9312c78 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -31,6 +31,17 @@  config SECURITY
 
 	  If you are unsure how to answer this question, answer N.
 
+config SECURITY_STACKING_DEBUG
+	bool "Enable debugging of the LSM infrastructure"
+	depends on SECURITY
+	help
+	  This allows you to choose debug messages related to
+	  security modules configured into your kernel. These
+	  messages can be useful for debugging the use of
+	  security blobs.
+
+	  If you are unsure how to answer this question, answer N.
+
 config SECURITYFS
 	bool "Enable the securityfs filesystem"
 	help
diff --git a/security/apparmor/context.c b/security/apparmor/context.c
index 3064c6c..6e54be2 100644
--- a/security/apparmor/context.c
+++ b/security/apparmor/context.c
@@ -50,8 +50,6 @@  void aa_free_task_context(struct aa_task_cxt *cxt)
 		aa_put_profile(cxt->profile);
 		aa_put_profile(cxt->previous);
 		aa_put_profile(cxt->onexec);
-
-		kzfree(cxt);
 	}
 }
 
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 1c022ec..41a0290 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -44,26 +44,11 @@  int apparmor_initialized __initdata;
  */
 
 /*
- * free the associated aa_task_cxt and put its profiles
+ * put the associated aa_task_cxt profiles
  */
 static void apparmor_cred_free(struct cred *cred)
 {
 	aa_free_task_context(cred_cxt(cred));
-	cred->security = NULL;
-}
-
-/*
- * allocate the apparmor part of blank credentials
- */
-static int apparmor_cred_alloc_blank(struct cred *cred, gfp_t gfp)
-{
-	/* freed by apparmor_cred_free */
-	struct aa_task_cxt *cxt = aa_alloc_task_context(gfp);
-	if (!cxt)
-		return -ENOMEM;
-
-	cred->security = cxt;
-	return 0;
 }
 
 /*
@@ -72,14 +57,7 @@  static int apparmor_cred_alloc_blank(struct cred *cred, gfp_t gfp)
 static int apparmor_cred_prepare(struct cred *new, const struct cred *old,
 				 gfp_t gfp)
 {
-	struct aa_task_cxt *cxt;
-	/* freed by apparmor_cred_free */
-	cxt = aa_alloc_task_context(gfp);
-	if (!cxt)
-		return -ENOMEM;
-
-	aa_dup_task_context(cxt, cred_cxt(old));
-	new->security = cxt;
+	aa_dup_task_context(cred_cxt(new), cred_cxt(old));
 	return 0;
 }
 
@@ -610,6 +588,10 @@  static int apparmor_task_setrlimit(struct task_struct *task,
 	return error;
 }
 
+struct lsm_blob_sizes apparmor_blob_sizes = {
+	.lbs_cred = sizeof(struct aa_task_cxt),
+};
+
 static struct security_hook_list apparmor_hooks[] = {
 	LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check),
 	LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme),
@@ -639,7 +621,6 @@  static struct security_hook_list apparmor_hooks[] = {
 	LSM_HOOK_INIT(getprocattr, apparmor_getprocattr),
 	LSM_HOOK_INIT(setprocattr, apparmor_setprocattr),
 
-	LSM_HOOK_INIT(cred_alloc_blank, apparmor_cred_alloc_blank),
 	LSM_HOOK_INIT(cred_free, apparmor_cred_free),
 	LSM_HOOK_INIT(cred_prepare, apparmor_cred_prepare),
 	LSM_HOOK_INIT(cred_transfer, apparmor_cred_transfer),
@@ -876,20 +857,26 @@  static int __init set_init_cxt(void)
 	struct cred *cred = (struct cred *)current->real_cred;
 	struct aa_task_cxt *cxt;
 
-	cxt = aa_alloc_task_context(GFP_KERNEL);
-	if (!cxt)
-		return -ENOMEM;
+	lsm_early_cred(cred);
+	cxt = apparmor_cred(cred);
 
 	cxt->profile = aa_get_profile(root_ns->unconfined);
-	cred->security = cxt;
 
 	return 0;
 }
 
 static int __init apparmor_init(void)
 {
+	static int finish;
 	int error;
 
+	if (!finish) {
+		if (apparmor_enabled && security_module_enable("apparmor"))
+			security_add_blobs(&apparmor_blob_sizes);
+		finish = 1;
+		return 0;
+	}
+
 	if (!apparmor_enabled || !security_module_enable("apparmor")) {
 		aa_info_message(
 			"AppArmor disabled by boot time parameter");
diff --git a/security/security.c b/security/security.c
index f97f0d9..69200ab 100644
--- a/security/security.c
+++ b/security/security.c
@@ -33,6 +33,8 @@ 
 #define SECURITY_NAME_MAX	10
 
 char *lsm_names;
+static struct lsm_blob_sizes blob_sizes;
+
 /* Boot-time LSM user choice */
 static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =
 	CONFIG_DEFAULT_SECURITY;
@@ -64,10 +66,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();
 
+#ifdef CONFIG_SECURITY_STACKING_DEBUG
+	pr_info("LSM: cred blob size       = %d\n", blob_sizes.lbs_cred);
+#endif
+
 	return 0;
 }
 
@@ -135,6 +149,58 @@  void __init security_add_hooks(struct security_hook_list *hooks, int count,
 		panic("%s - Cannot get early memory.\n", __func__);
 }
 
+/**
+ * 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)
+{
+#ifdef CONFIG_SECURITY_STACKING_DEBUG
+	if (cred->security) {
+		pr_info("%s: Inbound cred blob is not NULL.\n", __func__);
+		return 0;
+	}
+#endif
+	if (blob_sizes.lbs_cred == 0)
+		return 0;
+
+	cred->security = kzalloc(blob_sizes.lbs_cred, gfp);
+	if (cred->security == NULL)
+		return -ENOMEM;
+	return 0;
+}
+
+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.
  *
@@ -916,16 +982,29 @@  void security_task_free(struct task_struct *task)
 
 int security_cred_alloc_blank(struct cred *cred, gfp_t gfp)
 {
+	int rc = lsm_cred_alloc(cred, gfp);
+
+	if (rc)
+		return rc;
+
 	return call_int_hook(cred_alloc_blank, 0, cred, gfp);
 }
 
 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)
 {
+	int rc = lsm_cred_alloc(new, gfp);
+
+	if (rc)
+		return rc;
+
 	return call_int_hook(cred_prepare, 0, new, old, gfp);
 }
 
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index cbadbab..d484d7d 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -177,14 +177,8 @@  static void cred_init_security(void)
 {
 	struct cred *cred = (struct cred *) current->real_cred;
 	struct task_security_struct *tsec;
-	void *b;
-	int size;
-
-	size = sizeof(struct task_security_struct);
-	b = kzalloc(size, GFP_KERNEL);
-	if (!b)
-		panic("SELinux:  Failed to initialize initial task.\n");
-	cred->security = b;
+
+	lsm_early_cred(cred);
 	tsec = selinux_cred(cred);
 	tsec->osid = tsec->sid = SECINITSID_KERNEL;
 }
@@ -3623,52 +3617,16 @@  static int selinux_task_create(unsigned long clone_flags)
 }
 
 /*
- * 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;
-	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;
 }
 
@@ -6012,6 +5970,10 @@  static int selinux_key_getsecurity(struct key *key, char **_buffer)
 
 #endif
 
+struct lsm_blob_sizes selinux_blob_sizes = {
+	.lbs_cred = sizeof(struct task_security_struct),
+};
+
 static struct security_hook_list selinux_hooks[] = {
 	LSM_HOOK_INIT(binder_set_context_mgr, selinux_binder_set_context_mgr),
 	LSM_HOOK_INIT(binder_transaction, selinux_binder_transaction),
@@ -6092,8 +6054,6 @@  static struct security_hook_list selinux_hooks[] = {
 	LSM_HOOK_INIT(file_open, selinux_file_open),
 
 	LSM_HOOK_INIT(task_create, selinux_task_create),
-	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(kernel_act_as, selinux_kernel_act_as),
@@ -6227,11 +6187,19 @@  static struct security_hook_list selinux_hooks[] = {
 
 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) {
 		printk(KERN_INFO "SELinux:  Disabled at boot.\n");
 		return 0;
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index d7aaef0..7ab0021 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -322,27 +322,6 @@  static void init_task_smack(struct task_smack *tsp, struct smack_known *task,
 }
 
 /**
- * new_task_smack - allocate a task security blob
- * @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)
-{
-	struct task_smack *tsp;
-
-	tsp = kzalloc(sizeof(struct task_smack), gfp);
-	if (tsp == NULL)
-		return NULL;
-
-	init_task_smack(tsp, task, forked);
-	return tsp;
-}
-
-/**
  * smk_copy_rules - copy a rule set
  * @nhead: new rules header pointer
  * @ohead: old rules header pointer
@@ -1948,13 +1927,7 @@  static int smack_file_open(struct file *file, const struct cred *cred)
  */
 static int smack_cred_alloc_blank(struct cred *cred, gfp_t gfp)
 {
-	struct task_smack *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;
 }
 
@@ -1971,10 +1944,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) {
@@ -1982,7 +1951,6 @@  static void smack_cred_free(struct cred *cred)
 		list_del(&rp->list);
 		kfree(rp);
 	}
-	kfree(tsp);
 }
 
 /**
@@ -1997,12 +1965,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;
+	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)
@@ -2010,11 +1976,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;
-
-	new->security = new_tsp;
-	return 0;
+	return rc;
 }
 
 /**
@@ -2034,7 +1996,6 @@  static void smack_cred_transfer(struct cred *new, const struct cred *old)
 	mutex_init(&new_tsp->smk_rules_lock);
 	INIT_LIST_HEAD(&new_tsp->smk_rules);
 
-
 	/* cbs copy rule list */
 }
 
@@ -4591,6 +4552,10 @@  static int smack_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen)
 	return 0;
 }
 
+struct lsm_blob_sizes smack_blob_sizes = {
+	.lbs_cred = sizeof(struct task_smack),
+};
+
 static struct security_hook_list smack_hooks[] = {
 	LSM_HOOK_INIT(ptrace_access_check, smack_ptrace_access_check),
 	LSM_HOOK_INIT(ptrace_traceme, smack_ptrace_traceme),
@@ -4770,26 +4735,30 @@  static __init void init_smack_known_list(void)
  */
 static __init int smack_init(void)
 {
+	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.
 	 */
-	cred->security = tsp;
+	tsp = smack_cred(cred);
+	init_task_smack(tsp, &smack_known_floor, &smack_known_floor);
 
 	/*
 	 * Register with LSM
@@ -4808,12 +4777,6 @@  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();
 
diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h
index 82be05d..cbcfccc 100644
--- a/security/tomoyo/common.h
+++ b/security/tomoyo/common.h
@@ -1202,7 +1202,7 @@  static inline void tomoyo_put_group(struct tomoyo_group *group)
  *
  * Returns pointer to the tomoyo cred blob.
  */
-static inline struct tomoyo_domain_info *tomoyo_cred(const struct cred *cred)
+static inline struct tomoyo_domain_info **tomoyo_cred(const struct cred *cred)
 {
 	return cred->security;
 }
@@ -1214,7 +1214,9 @@  static inline struct tomoyo_domain_info *tomoyo_cred(const struct cred *cred)
  */
 static inline struct tomoyo_domain_info *tomoyo_domain(void)
 {
-	return tomoyo_cred(current_cred());
+	struct tomoyo_domain_info **blob = tomoyo_cred(current_cred());
+
+	return *blob;
 }
 
 /**
@@ -1227,7 +1229,9 @@  static inline struct tomoyo_domain_info *tomoyo_domain(void)
 static inline struct tomoyo_domain_info *tomoyo_real_domain(struct task_struct
 							    *task)
 {
-	return tomoyo_cred(get_task_cred(task));
+	struct tomoyo_domain_info **blob = tomoyo_cred(get_task_cred(task));
+
+	return *blob;
 }
 
 /**
diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c
index ade7c6c..b7341f0 100644
--- a/security/tomoyo/domain.c
+++ b/security/tomoyo/domain.c
@@ -675,6 +675,7 @@  out:
  */
 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;
@@ -840,7 +841,8 @@  force_jump_domain:
 		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 e22f2c6..9289f2a 100644
--- a/security/tomoyo/securityfs_if.c
+++ b/security/tomoyo/securityfs_if.c
@@ -70,10 +70,12 @@  static ssize_t tomoyo_write_self(struct file *file, const char __user *buf,
 				if (!cred) {
 					error = -ENOMEM;
 				} else {
+					struct tomoyo_domain_info **blob;
 					struct tomoyo_domain_info *old_domain;
 
-					old_domain = tomoyo_cred(cred);
-					cred->security = new_domain;
+					blob = tomoyo_cred(cred);
+					old_domain = *blob;
+					*blob = new_domain;
 					atomic_inc(&new_domain->users);
 					atomic_dec(&old_domain->users);
 					commit_creds(cred);
@@ -237,7 +239,7 @@  static int __init tomoyo_initerface_init(void)
 	struct tomoyo_domain_info *domain;
 	struct dentry *tomoyo_dir;
 
-	domain = tomoyo_cred(current_cred());
+	domain = tomoyo_domain();
 	/* Don't create securityfs entries unless registered. */
 	if (domain != &tomoyo_kernel_domain)
 		return 0;
diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
index 353c935..61285fe 100644
--- a/security/tomoyo/tomoyo.c
+++ b/security/tomoyo/tomoyo.c
@@ -17,7 +17,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;
 }
 
@@ -33,10 +35,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 **old_blob = tomoyo_cred(old);
+	struct tomoyo_domain_info **new_blob = tomoyo_cred(new);
 	struct tomoyo_domain_info *domain;
 
-	domain = tomoyo_cred(old);
-	new->security = domain;
+	domain = *old_blob;
+	*new_blob = domain;
+
 	if (domain)
 		atomic_inc(&domain->users);
 	return 0;
@@ -60,9 +65,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;
+	struct tomoyo_domain_info **blob = tomoyo_cred(cred);
+	struct tomoyo_domain_info *domain = *blob;
 
-	domain = tomoyo_cred(cred);
 	if (domain)
 		atomic_dec(&domain->users);
 }
@@ -76,6 +81,7 @@  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;
 
 	/*
@@ -98,13 +104,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().
 	 */
-	domain = tomoyo_cred(bprm->cred);
+	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;
 }
 
@@ -117,9 +124,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 **blob;
 	struct tomoyo_domain_info *domain;
 
-	domain = tomoyo_cred(bprm->cred);
+	blob = tomoyo_cred(bprm->cred);
+	domain = *blob;
 	/*
 	 * Execute permission is checked against pathname passed to do_execve()
 	 * using current domain.
@@ -499,6 +508,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.
@@ -544,14 +557,25 @@  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"))
 		return 0;
+
+	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;
 }