LSM: Switch to lists of hooks

Instead of using a vector of security operations
with explicit, special case stacking of the capability
and yama hooks use lists of hooks with capability and
yama hooks included as appropriate.

The security_operations structure is no longer required.
Instead, there is a union of the function pointers that
allows all the hooks lists to use a common mechanism for
list management while retaining typing. Each module
supplies an array describing the hooks it provides instead
of a sparsely populated security_operations structure.
The description includes the element that gets put on
the hook list, avoiding the issues surrounding individual
element allocation.

The method for registering security modules is changed to
reflect the information available. The method for removing
a module, currently only used by SELinux, has also changed.
It should be generic now, however if there are potential
race conditions based on ordering of hook removal that needs
to be addressed by the calling module.

The security hooks are called from the lists and the first
failure is returned.

Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
Acked-by: John Johansen <john.johansen@canonical.com>
Acked-by: Kees Cook <keescook@chromium.org>
Acked-by: Paul Moore <paul@paul-moore.com>
Acked-by:  Stephen Smalley <sds@tycho.nsa.gov>
Acked-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: James Morris <james.l.morris@oracle.com>
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 27dd6fc..f014f25 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -25,21 +25,10 @@
 #define __LINUX_LSM_HOOKS_H
 
 #include <linux/security.h>
-
-/* Maximum number of letters for an LSM name string */
-#define SECURITY_NAME_MAX	10
-
-#ifdef CONFIG_SECURITY
+#include <linux/init.h>
+#include <linux/rculist.h>
 
 /**
- * struct security_operations - main security structure
- *
- * Security module identifier.
- *
- * @name:
- *	A string that acts as a unique identifier for the LSM with max number
- *	of characters = SECURITY_NAME_MAX.
- *
  * Security hooks for program execution operations.
  *
  * @bprm_set_creds:
@@ -1310,9 +1299,7 @@
  * This is the main security structure.
  */
 
-struct security_operations {
-	char name[SECURITY_NAME_MAX + 1];
-
+union security_list_options {
 	int (*binder_set_context_mgr)(struct task_struct *mgr);
 	int (*binder_transaction)(struct task_struct *from,
 					struct task_struct *to);
@@ -1838,20 +1825,62 @@
 };
 
 /*
+ * Security module hook list structure.
+ * For use with generic list macros for common operations.
+ */
+struct security_hook_list {
+	struct list_head		list;
+	struct list_head		*head;
+	union security_list_options	hook;
+};
+
+/*
  * 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
  * text involved.
- * Casey says: Comment is true in the next patch.
  */
-#define LSM_HOOK_INIT(HEAD, HOOK)	.HEAD = HOOK
+#define LSM_HOOK_INIT(HEAD, HOOK) \
+	{ .head = &security_hook_heads.HEAD, .hook = { .HEAD = HOOK } }
 
-/* prototypes */
-extern int security_module_enable(struct security_operations *ops);
-extern int register_security(struct security_operations *ops);
-extern void __init security_fixup_ops(struct security_operations *ops);
-extern void reset_security_ops(void);
+extern struct security_hook_heads security_hook_heads;
 
-#endif /* CONFIG_SECURITY */
+static inline void security_add_hooks(struct security_hook_list *hooks,
+				      int count)
+{
+	int i;
+
+	for (i = 0; i < count; i++)
+		list_add_tail_rcu(&hooks[i].list, hooks[i].head);
+}
+
+#ifdef CONFIG_SECURITY_SELINUX_DISABLE
+/*
+ * Assuring the safety of deleting a security module is up to
+ * the security module involved. This may entail ordering the
+ * module's hook list in a particular way, refusing to disable
+ * the module once a policy is loaded or any number of other
+ * actions better imagined than described.
+ *
+ * The name of the configuration option reflects the only module
+ * that currently uses the mechanism. Any developer who thinks
+ * disabling their module is a good idea needs to be at least as
+ * careful as the SELinux team.
+ */
+static inline void security_delete_hooks(struct security_hook_list *hooks,
+						int count)
+{
+	int i;
+
+	for (i = 0; i < count; i++)
+		list_del_rcu(&hooks[i].list);
+}
+#endif /* CONFIG_SECURITY_SELINUX_DISABLE */
+
+extern int __init security_module_enable(const char *module);
+extern void __init capability_add_hooks(void);
+#ifdef CONFIG_SECURITY_YAMA_STACKED
+void __init yama_add_hooks(void);
+#endif
 
 #endif /* ! __LINUX_LSM_HOOKS_H */
diff --git a/include/linux/security.h b/include/linux/security.h
index a2a100e..8c8175d 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -27,6 +27,7 @@
 #include <linux/slab.h>
 #include <linux/err.h>
 #include <linux/string.h>
+#include <linux/mm.h>
 
 struct linux_binprm;
 struct cred;
@@ -54,9 +55,6 @@
 struct xfrm_sec_ctx;
 struct mm_struct;
 
-/* Maximum number of letters for an LSM name string */
-#define SECURITY_NAME_MAX	10
-
 /* If capable should audit the security request */
 #define SECURITY_CAP_NOAUDIT 0
 #define SECURITY_CAP_AUDIT 1
@@ -69,10 +67,7 @@
 struct user_namespace;
 struct timezone;
 
-/*
- * These functions are in security/capability.c and are used
- * as the default capabilities functions
- */
+/* These functions are in security/commoncap.c */
 extern int cap_capable(const struct cred *cred, struct user_namespace *ns,
 		       int cap, int audit);
 extern int cap_settime(const struct timespec *ts, const struct timezone *tz);
@@ -114,8 +109,6 @@
 struct xfrm_user_sec_ctx;
 struct seq_file;
 
-extern int cap_netlink_send(struct sock *sk, struct sk_buff *skb);
-
 #ifdef CONFIG_MMU
 extern unsigned long mmap_min_addr;
 extern unsigned long dac_mmap_min_addr;
@@ -472,7 +465,7 @@
 
 static inline int security_vm_enough_memory_mm(struct mm_struct *mm, long pages)
 {
-	return cap_vm_enough_memory(mm, pages);
+	return __vm_enough_memory(mm, pages, cap_vm_enough_memory(mm, pages));
 }
 
 static inline int security_bprm_set_creds(struct linux_binprm *bprm)
@@ -1075,7 +1068,7 @@
 
 static inline int security_netlink_send(struct sock *sk, struct sk_buff *skb)
 {
-	return cap_netlink_send(sk, skb);
+	return 0;
 }
 
 static inline int security_ismaclabel(const char *name)
@@ -1643,36 +1636,5 @@
 { }
 #endif /* CONFIG_SECURITY */
 
-#ifdef CONFIG_SECURITY_YAMA
-extern int yama_ptrace_access_check(struct task_struct *child,
-				    unsigned int mode);
-extern int yama_ptrace_traceme(struct task_struct *parent);
-extern void yama_task_free(struct task_struct *task);
-extern int yama_task_prctl(int option, unsigned long arg2, unsigned long arg3,
-			   unsigned long arg4, unsigned long arg5);
-#else
-static inline int yama_ptrace_access_check(struct task_struct *child,
-					   unsigned int mode)
-{
-	return 0;
-}
-
-static inline int yama_ptrace_traceme(struct task_struct *parent)
-{
-	return 0;
-}
-
-static inline void yama_task_free(struct task_struct *task)
-{
-}
-
-static inline int yama_task_prctl(int option, unsigned long arg2,
-				  unsigned long arg3, unsigned long arg4,
-				  unsigned long arg5)
-{
-	return -ENOSYS;
-}
-#endif /* CONFIG_SECURITY_YAMA */
-
 #endif /* ! __LINUX_SECURITY_H */
 
diff --git a/security/Makefile b/security/Makefile
index 05f1c93..c9bfbc8 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -14,7 +14,7 @@
 obj-$(CONFIG_MMU)			+= min_addr.o
 
 # Object file lists
-obj-$(CONFIG_SECURITY)			+= security.o capability.o
+obj-$(CONFIG_SECURITY)			+= security.o
 obj-$(CONFIG_SECURITYFS)		+= inode.o
 obj-$(CONFIG_SECURITY_SELINUX)		+= selinux/
 obj-$(CONFIG_SECURITY_SMACK)		+= smack/
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
index d97cba3..dc0027b 100644
--- a/security/apparmor/domain.c
+++ b/security/apparmor/domain.c
@@ -347,9 +347,7 @@
 		file_inode(bprm->file)->i_mode
 	};
 	const char *name = NULL, *target = NULL, *info = NULL;
-	int error = cap_bprm_set_creds(bprm);
-	if (error)
-		return error;
+	int error = 0;
 
 	if (bprm->cred_prepared)
 		return 0;
@@ -531,15 +529,13 @@
  */
 int apparmor_bprm_secureexec(struct linux_binprm *bprm)
 {
-	int ret = cap_bprm_secureexec(bprm);
-
 	/* the decision to use secure exec is computed in set_creds
 	 * and stored in bprm->unsafe.
 	 */
-	if (!ret && (bprm->unsafe & AA_SECURE_X_NEEDED))
-		ret = 1;
+	if (bprm->unsafe & AA_SECURE_X_NEEDED)
+		return 1;
 
-	return ret;
+	return 0;
 }
 
 /**
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index f542532..5696874 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -96,19 +96,11 @@
 static int apparmor_ptrace_access_check(struct task_struct *child,
 					unsigned int mode)
 {
-	int error = cap_ptrace_access_check(child, mode);
-	if (error)
-		return error;
-
 	return aa_ptrace(current, child, mode);
 }
 
 static int apparmor_ptrace_traceme(struct task_struct *parent)
 {
-	int error = cap_ptrace_traceme(parent);
-	if (error)
-		return error;
-
 	return aa_ptrace(parent, current, PTRACE_MODE_ATTACH);
 }
 
@@ -123,10 +115,10 @@
 	cred = __task_cred(target);
 	profile = aa_cred_profile(cred);
 
-	*effective = cred->cap_effective;
-	*inheritable = cred->cap_inheritable;
-	*permitted = cred->cap_permitted;
-
+	/*
+	 * cap_capget is stacked ahead of this and will
+	 * initialize effective and permitted.
+	 */
 	if (!unconfined(profile) && !COMPLAIN_MODE(profile)) {
 		*effective = cap_intersect(*effective, profile->caps.allow);
 		*permitted = cap_intersect(*permitted, profile->caps.allow);
@@ -140,13 +132,11 @@
 			    int cap, int audit)
 {
 	struct aa_profile *profile;
-	/* cap_capable returns 0 on success, else -EPERM */
-	int error = cap_capable(cred, ns, cap, audit);
-	if (!error) {
-		profile = aa_cred_profile(cred);
-		if (!unconfined(profile))
-			error = aa_capable(profile, cap, audit);
-	}
+	int error = 0;
+
+	profile = aa_cred_profile(cred);
+	if (!unconfined(profile))
+		error = aa_capable(profile, cap, audit);
 	return error;
 }
 
@@ -615,9 +605,7 @@
 	return error;
 }
 
-static struct security_operations apparmor_ops = {
-	LSM_HOOK_INIT(name, "apparmor"),
-
+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),
 	LSM_HOOK_INIT(capget, apparmor_capget),
@@ -640,7 +628,6 @@
 	LSM_HOOK_INIT(file_alloc_security, apparmor_file_alloc_security),
 	LSM_HOOK_INIT(file_free_security, apparmor_file_free_security),
 	LSM_HOOK_INIT(mmap_file, apparmor_mmap_file),
-	LSM_HOOK_INIT(mmap_addr, cap_mmap_addr),
 	LSM_HOOK_INIT(file_mprotect, apparmor_file_mprotect),
 	LSM_HOOK_INIT(file_lock, apparmor_file_lock),
 
@@ -898,7 +885,7 @@
 {
 	int error;
 
-	if (!apparmor_enabled || !security_module_enable(&apparmor_ops)) {
+	if (!apparmor_enabled || !security_module_enable("apparmor")) {
 		aa_info_message("AppArmor disabled by boot time parameter");
 		apparmor_enabled = 0;
 		return 0;
@@ -913,17 +900,10 @@
 	error = set_init_cxt();
 	if (error) {
 		AA_ERROR("Failed to set context on init task\n");
-		goto register_security_out;
+		aa_free_root_ns();
+		goto alloc_out;
 	}
-
-	error = register_security(&apparmor_ops);
-	if (error) {
-		struct cred *cred = (struct cred *)current->real_cred;
-		aa_free_task_context(cred_cxt(cred));
-		cred_cxt(cred) = NULL;
-		AA_ERROR("Unable to register AppArmor\n");
-		goto register_security_out;
-	}
+	security_add_hooks(apparmor_hooks, ARRAY_SIZE(apparmor_hooks));
 
 	/* Report that AppArmor successfully initialized */
 	apparmor_initialized = 1;
@@ -936,9 +916,6 @@
 
 	return error;
 
-register_security_out:
-	aa_free_root_ns();
-
 alloc_out:
 	aa_destroy_aafs();
 
diff --git a/security/commoncap.c b/security/commoncap.c
index f2875cd..d103f5a4 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -12,7 +12,7 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
-#include <linux/security.h>
+#include <linux/lsm_hooks.h>
 #include <linux/file.h>
 #include <linux/mm.h>
 #include <linux/mman.h>
@@ -53,11 +53,6 @@
 	}
 }
 
-int cap_netlink_send(struct sock *sk, struct sk_buff *skb)
-{
-	return 0;
-}
-
 /**
  * cap_capable - Determine whether a task has a particular effective capability
  * @cred: The credentials to use
@@ -941,7 +936,7 @@
  * @pages: The size of the mapping
  *
  * Determine whether the allocation of a new virtual mapping by the current
- * task is permitted, returning 0 if permission is granted, -ve if not.
+ * task is permitted, returning 1 if permission is granted, 0 if not.
  */
 int cap_vm_enough_memory(struct mm_struct *mm, long pages)
 {
@@ -950,7 +945,7 @@
 	if (cap_capable(current_cred(), &init_user_ns, CAP_SYS_ADMIN,
 			SECURITY_CAP_NOAUDIT) == 0)
 		cap_sys_admin = 1;
-	return __vm_enough_memory(mm, pages, cap_sys_admin);
+	return cap_sys_admin;
 }
 
 /*
@@ -981,3 +976,33 @@
 {
 	return 0;
 }
+
+#ifdef CONFIG_SECURITY
+
+struct security_hook_list capability_hooks[] = {
+	LSM_HOOK_INIT(capable, cap_capable),
+	LSM_HOOK_INIT(settime, cap_settime),
+	LSM_HOOK_INIT(ptrace_access_check, cap_ptrace_access_check),
+	LSM_HOOK_INIT(ptrace_traceme, cap_ptrace_traceme),
+	LSM_HOOK_INIT(capget, cap_capget),
+	LSM_HOOK_INIT(capset, cap_capset),
+	LSM_HOOK_INIT(bprm_set_creds, cap_bprm_set_creds),
+	LSM_HOOK_INIT(bprm_secureexec, cap_bprm_secureexec),
+	LSM_HOOK_INIT(inode_need_killpriv, cap_inode_need_killpriv),
+	LSM_HOOK_INIT(inode_killpriv, cap_inode_killpriv),
+	LSM_HOOK_INIT(mmap_addr, cap_mmap_addr),
+	LSM_HOOK_INIT(mmap_file, cap_mmap_file),
+	LSM_HOOK_INIT(task_fix_setuid, cap_task_fix_setuid),
+	LSM_HOOK_INIT(task_prctl, cap_task_prctl),
+	LSM_HOOK_INIT(task_setscheduler, cap_task_setscheduler),
+	LSM_HOOK_INIT(task_setioprio, cap_task_setioprio),
+	LSM_HOOK_INIT(task_setnice, cap_task_setnice),
+	LSM_HOOK_INIT(vm_enough_memory, cap_vm_enough_memory),
+};
+
+void __init capability_add_hooks(void)
+{
+	security_add_hooks(capability_hooks, ARRAY_SIZE(capability_hooks));
+}
+
+#endif /* CONFIG_SECURITY */
diff --git a/security/security.c b/security/security.c
index 02dc720..bd4c5f6 100644
--- a/security/security.c
+++ b/security/security.c
@@ -29,24 +29,13 @@
 
 #define MAX_LSM_EVM_XATTR	2
 
+/* Maximum number of letters for an LSM name string */
+#define SECURITY_NAME_MAX	10
+
 /* Boot-time LSM user choice */
 static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =
 	CONFIG_DEFAULT_SECURITY;
 
-static struct security_operations *security_ops;
-static struct security_operations default_security_ops = {
-	.name	= "default",
-};
-
-static inline int __init verify(struct security_operations *ops)
-{
-	/* verify the security_operations structure exists */
-	if (!ops)
-		return -EINVAL;
-	security_fixup_ops(ops);
-	return 0;
-}
-
 static void __init do_security_initcalls(void)
 {
 	initcall_t *call;
@@ -64,20 +53,27 @@
  */
 int __init security_init(void)
 {
-	printk(KERN_INFO "Security Framework initialized\n");
+	pr_info("Security Framework initialized\n");
 
-	security_fixup_ops(&default_security_ops);
-	security_ops = &default_security_ops;
+	/*
+	 * Always load the capability module.
+	 */
+	capability_add_hooks();
+#ifdef CONFIG_SECURITY_YAMA_STACKED
+	/*
+	 * If Yama is configured for stacking load it next.
+	 */
+	yama_add_hooks();
+#endif
+	/*
+	 * Load the chosen module if there is one.
+	 * This will also find yama if it is stacking
+	 */
 	do_security_initcalls();
 
 	return 0;
 }
 
-void reset_security_ops(void)
-{
-	security_ops = &default_security_ops;
-}
-
 /* Save user chosen LSM */
 static int __init choose_lsm(char *str)
 {
@@ -88,7 +84,7 @@
 
 /**
  * security_module_enable - Load given security module on boot ?
- * @ops: a pointer to the struct security_operations that is to be checked.
+ * @module: the name of the module
  *
  * Each LSM must pass this method before registering its own operations
  * to avoid security registration races. This method may also be used
@@ -100,41 +96,13 @@
  *	 choose an alternate LSM at boot time.
  * Otherwise, return false.
  */
-int __init security_module_enable(struct security_operations *ops)
+int __init security_module_enable(const char *module)
 {
-	return !strcmp(ops->name, chosen_lsm);
-}
-
-/**
- * register_security - registers a security framework with the kernel
- * @ops: a pointer to the struct security_options that is to be registered
- *
- * This function allows a security module to register itself with the
- * kernel security subsystem.  Some rudimentary checking is done on the @ops
- * value passed to this function. You'll need to check first if your LSM
- * is allowed to register its @ops by calling security_module_enable(@ops).
- *
- * If there is already a security module registered with the kernel,
- * an error will be returned.  Otherwise %0 is returned on success.
- */
-int __init register_security(struct security_operations *ops)
-{
-	if (verify(ops)) {
-		printk(KERN_DEBUG "%s could not verify "
-		       "security_operations structure.\n", __func__);
-		return -EINVAL;
-	}
-
-	if (security_ops != &default_security_ops)
-		return -EAGAIN;
-
-	security_ops = ops;
-
-	return 0;
+	return !strcmp(module, chosen_lsm);
 }
 
 /*
- * Hook operation macros.
+ * Hook list operation macros.
  *
  * call_void_hook:
  *	This is a hook that does not return a value.
@@ -143,8 +111,27 @@
  *	This is a hook that returns a value.
  */
 
-#define call_void_hook(FUNC, ...)	security_ops->FUNC(__VA_ARGS__)
-#define call_int_hook(FUNC, IRC, ...)	security_ops->FUNC(__VA_ARGS__)
+#define call_void_hook(FUNC, ...)				\
+	do {							\
+		struct security_hook_list *P;			\
+								\
+		list_for_each_entry(P, &security_hook_heads.FUNC, list)	\
+			P->hook.FUNC(__VA_ARGS__);		\
+	} while (0)
+
+#define call_int_hook(FUNC, IRC, ...) ({			\
+	int RC = IRC;						\
+	do {							\
+		struct security_hook_list *P;			\
+								\
+		list_for_each_entry(P, &security_hook_heads.FUNC, list) { \
+			RC = P->hook.FUNC(__VA_ARGS__);		\
+			if (RC != 0)				\
+				break;				\
+		}						\
+	} while (0);						\
+	RC;							\
+})
 
 /* Security operations */
 
@@ -173,23 +160,11 @@
 
 int security_ptrace_access_check(struct task_struct *child, unsigned int mode)
 {
-#ifdef CONFIG_SECURITY_YAMA_STACKED
-	int rc;
-	rc = yama_ptrace_access_check(child, mode);
-	if (rc)
-		return rc;
-#endif
 	return call_int_hook(ptrace_access_check, 0, child, mode);
 }
 
 int security_ptrace_traceme(struct task_struct *parent)
 {
-#ifdef CONFIG_SECURITY_YAMA_STACKED
-	int rc;
-	rc = yama_ptrace_traceme(parent);
-	if (rc)
-		return rc;
-#endif
 	return call_int_hook(ptrace_traceme, 0, parent);
 }
 
@@ -245,7 +220,25 @@
 
 int security_vm_enough_memory_mm(struct mm_struct *mm, long pages)
 {
-	return call_int_hook(vm_enough_memory, 0, mm, pages);
+	struct security_hook_list *hp;
+	int cap_sys_admin = 1;
+	int rc;
+
+	/*
+	 * The module will respond with a positive value if
+	 * it thinks the __vm_enough_memory() call should be
+	 * made with the cap_sys_admin set. If all of the modules
+	 * agree that it should be set it will. If any module
+	 * thinks it should not be set it won't.
+	 */
+	list_for_each_entry(hp, &security_hook_heads.vm_enough_memory, list) {
+		rc = hp->hook.vm_enough_memory(mm, pages);
+		if (rc <= 0) {
+			cap_sys_admin = 0;
+			break;
+		}
+	}
+	return __vm_enough_memory(mm, pages, cap_sys_admin);
 }
 
 int security_bprm_set_creds(struct linux_binprm *bprm)
@@ -335,8 +328,9 @@
 				unsigned long kern_flags,
 				unsigned long *set_kern_flags)
 {
-	return call_int_hook(sb_set_mnt_opts, 0, sb, opts, kern_flags,
-						set_kern_flags);
+	return call_int_hook(sb_set_mnt_opts,
+				opts->num_mnt_opts ? -EOPNOTSUPP : 0, sb,
+				opts, kern_flags, set_kern_flags);
 }
 EXPORT_SYMBOL(security_sb_set_mnt_opts);
 
@@ -369,8 +363,8 @@
 					struct qstr *name, void **ctx,
 					u32 *ctxlen)
 {
-	return call_int_hook(dentry_init_security, 0, dentry, mode, name,
-							ctx, ctxlen);
+	return call_int_hook(dentry_init_security, -EOPNOTSUPP, dentry, mode,
+				name, ctx, ctxlen);
 }
 EXPORT_SYMBOL(security_dentry_init_security);
 
@@ -390,7 +384,7 @@
 							 NULL, NULL, NULL);
 	memset(new_xattrs, 0, sizeof(new_xattrs));
 	lsm_xattr = new_xattrs;
-	ret = call_int_hook(inode_init_security, 0, inode, dir, qstr,
+	ret = call_int_hook(inode_init_security, -EOPNOTSUPP, inode, dir, qstr,
 						&lsm_xattr->name,
 						&lsm_xattr->value,
 						&lsm_xattr->value_len);
@@ -636,8 +630,15 @@
 
 	if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
 		return 0;
-	ret = call_int_hook(inode_setxattr, 0, dentry, name, value, size,
+	/*
+	 * SELinux and Smack integrate the cap call,
+	 * so assume that all LSMs supplying this call do so.
+	 */
+	ret = call_int_hook(inode_setxattr, 1, dentry, name, value, size,
 				flags);
+
+	if (ret == 1)
+		ret = cap_inode_setxattr(dentry, name, value, size, flags);
 	if (ret)
 		return ret;
 	ret = ima_inode_setxattr(dentry, name, value, size);
@@ -675,7 +676,13 @@
 
 	if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
 		return 0;
-	ret = call_int_hook(inode_removexattr, 0, dentry, name);
+	/*
+	 * SELinux and Smack integrate the cap call,
+	 * so assume that all LSMs supplying this call do so.
+	 */
+	ret = call_int_hook(inode_removexattr, 1, dentry, name);
+	if (ret == 1)
+		ret = cap_inode_removexattr(dentry, name);
 	if (ret)
 		return ret;
 	ret = ima_inode_removexattr(dentry, name);
@@ -698,15 +705,16 @@
 {
 	if (unlikely(IS_PRIVATE(inode)))
 		return -EOPNOTSUPP;
-	return call_int_hook(inode_getsecurity, 0, inode, name, buffer, alloc);
+	return call_int_hook(inode_getsecurity, -EOPNOTSUPP, inode, name,
+				buffer, alloc);
 }
 
 int security_inode_setsecurity(struct inode *inode, const char *name, const void *value, size_t size, int flags)
 {
 	if (unlikely(IS_PRIVATE(inode)))
 		return -EOPNOTSUPP;
-	return call_int_hook(inode_setsecurity, 0, inode, name, value, size,
-				flags);
+	return call_int_hook(inode_setsecurity, -EOPNOTSUPP, inode, name,
+				value, size, flags);
 }
 
 int security_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer_size)
@@ -847,9 +855,6 @@
 
 void security_task_free(struct task_struct *task)
 {
-#ifdef CONFIG_SECURITY_YAMA_STACKED
-	yama_task_free(task);
-#endif
 	call_void_hook(task_free, task);
 }
 
@@ -932,6 +937,7 @@
 
 void security_task_getsecid(struct task_struct *p, u32 *secid)
 {
+	*secid = 0;
 	call_void_hook(task_getsecid, p, secid);
 }
 EXPORT_SYMBOL(security_task_getsecid);
@@ -986,13 +992,19 @@
 int security_task_prctl(int option, unsigned long arg2, unsigned long arg3,
 			 unsigned long arg4, unsigned long arg5)
 {
-#ifdef CONFIG_SECURITY_YAMA_STACKED
-	int rc;
-	rc = yama_task_prctl(option, arg2, arg3, arg4, arg5);
-	if (rc != -ENOSYS)
-		return rc;
-#endif
-	return call_int_hook(task_prctl, 0, option, arg2, arg3, arg4, arg5);
+	int thisrc;
+	int rc = -ENOSYS;
+	struct security_hook_list *hp;
+
+	list_for_each_entry(hp, &security_hook_heads.task_prctl, list) {
+		thisrc = hp->hook.task_prctl(option, arg2, arg3, arg4, arg5);
+		if (thisrc != -ENOSYS) {
+			rc = thisrc;
+			if (thisrc != 0)
+				break;
+		}
+	}
+	return rc;
 }
 
 void security_task_to_inode(struct task_struct *p, struct inode *inode)
@@ -1007,6 +1019,7 @@
 
 void security_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid)
 {
+	*secid = 0;
 	call_void_hook(ipc_getsecid, ipcp, secid);
 }
 
@@ -1113,12 +1126,12 @@
 
 int security_getprocattr(struct task_struct *p, char *name, char **value)
 {
-	return call_int_hook(getprocattr, 0, p, name, value);
+	return call_int_hook(getprocattr, -EINVAL, p, name, value);
 }
 
 int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size)
 {
-	return call_int_hook(setprocattr, 0, p, name, value, size);
+	return call_int_hook(setprocattr, -EINVAL, p, name, value, size);
 }
 
 int security_netlink_send(struct sock *sk, struct sk_buff *skb)
@@ -1134,12 +1147,14 @@
 
 int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
 {
-	return call_int_hook(secid_to_secctx, 0, secid, secdata, seclen);
+	return call_int_hook(secid_to_secctx, -EOPNOTSUPP, secid, secdata,
+				seclen);
 }
 EXPORT_SYMBOL(security_secid_to_secctx);
 
 int security_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid)
 {
+	*secid = 0;
 	return call_int_hook(secctx_to_secid, 0, secdata, seclen, secid);
 }
 EXPORT_SYMBOL(security_secctx_to_secid);
@@ -1164,7 +1179,7 @@
 
 int security_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen)
 {
-	return call_int_hook(inode_getsecctx, 0, inode, ctx, ctxlen);
+	return call_int_hook(inode_getsecctx, -EOPNOTSUPP, inode, ctx, ctxlen);
 }
 EXPORT_SYMBOL(security_inode_getsecctx);
 
@@ -1259,8 +1274,8 @@
 int security_socket_getpeersec_stream(struct socket *sock, char __user *optval,
 				      int __user *optlen, unsigned len)
 {
-	return call_int_hook(socket_getpeersec_stream, 0, sock, optval,
-				optlen, len);
+	return call_int_hook(socket_getpeersec_stream, -ENOPROTOOPT, sock,
+				optval, optlen, len);
 }
 
 int security_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *skb, u32 *secid)
@@ -1438,7 +1453,24 @@
 				       struct xfrm_policy *xp,
 				       const struct flowi *fl)
 {
-	return call_int_hook(xfrm_state_pol_flow_match, 0, x, xp, fl);
+	struct security_hook_list *hp;
+	int rc = 1;
+
+	/*
+	 * Since this function is expected to return 0 or 1, the judgment
+	 * becomes difficult if multiple LSMs supply this call. Fortunately,
+	 * we can use the first LSM's judgment because currently only SELinux
+	 * supplies this call.
+	 *
+	 * For speed optimization, we explicitly break the loop rather than
+	 * using the macro
+	 */
+	list_for_each_entry(hp, &security_hook_heads.xfrm_state_pol_flow_match,
+				list) {
+		rc = hp->hook.xfrm_state_pol_flow_match(x, xp, fl);
+		break;
+	}
+	return rc;
 }
 
 int security_xfrm_decode_session(struct sk_buff *skb, u32 *secid)
@@ -1478,6 +1510,7 @@
 
 int security_key_getsecurity(struct key *key, char **_buffer)
 {
+	*_buffer = NULL;
 	return call_int_hook(key_getsecurity, 0, key, _buffer);
 }
 
@@ -1506,5 +1539,350 @@
 	return call_int_hook(audit_rule_match, 0, secid, field, op, lsmrule,
 				actx);
 }
-
 #endif /* CONFIG_AUDIT */
+
+struct security_hook_heads security_hook_heads = {
+	.binder_set_context_mgr =
+		LIST_HEAD_INIT(security_hook_heads.binder_set_context_mgr),
+	.binder_transaction =
+		LIST_HEAD_INIT(security_hook_heads.binder_transaction),
+	.binder_transfer_binder =
+		LIST_HEAD_INIT(security_hook_heads.binder_transfer_binder),
+	.binder_transfer_file =
+		LIST_HEAD_INIT(security_hook_heads.binder_transfer_file),
+
+	.ptrace_access_check =
+		LIST_HEAD_INIT(security_hook_heads.ptrace_access_check),
+	.ptrace_traceme =
+		LIST_HEAD_INIT(security_hook_heads.ptrace_traceme),
+	.capget =	LIST_HEAD_INIT(security_hook_heads.capget),
+	.capset =	LIST_HEAD_INIT(security_hook_heads.capset),
+	.capable =	LIST_HEAD_INIT(security_hook_heads.capable),
+	.quotactl =	LIST_HEAD_INIT(security_hook_heads.quotactl),
+	.quota_on =	LIST_HEAD_INIT(security_hook_heads.quota_on),
+	.syslog =	LIST_HEAD_INIT(security_hook_heads.syslog),
+	.settime =	LIST_HEAD_INIT(security_hook_heads.settime),
+	.vm_enough_memory =
+		LIST_HEAD_INIT(security_hook_heads.vm_enough_memory),
+	.bprm_set_creds =
+		LIST_HEAD_INIT(security_hook_heads.bprm_set_creds),
+	.bprm_check_security =
+		LIST_HEAD_INIT(security_hook_heads.bprm_check_security),
+	.bprm_secureexec =
+		LIST_HEAD_INIT(security_hook_heads.bprm_secureexec),
+	.bprm_committing_creds =
+		LIST_HEAD_INIT(security_hook_heads.bprm_committing_creds),
+	.bprm_committed_creds =
+		LIST_HEAD_INIT(security_hook_heads.bprm_committed_creds),
+	.sb_alloc_security =
+		LIST_HEAD_INIT(security_hook_heads.sb_alloc_security),
+	.sb_free_security =
+		LIST_HEAD_INIT(security_hook_heads.sb_free_security),
+	.sb_copy_data =	LIST_HEAD_INIT(security_hook_heads.sb_copy_data),
+	.sb_remount =	LIST_HEAD_INIT(security_hook_heads.sb_remount),
+	.sb_kern_mount =
+		LIST_HEAD_INIT(security_hook_heads.sb_kern_mount),
+	.sb_show_options =
+		LIST_HEAD_INIT(security_hook_heads.sb_show_options),
+	.sb_statfs =	LIST_HEAD_INIT(security_hook_heads.sb_statfs),
+	.sb_mount =	LIST_HEAD_INIT(security_hook_heads.sb_mount),
+	.sb_umount =	LIST_HEAD_INIT(security_hook_heads.sb_umount),
+	.sb_pivotroot =	LIST_HEAD_INIT(security_hook_heads.sb_pivotroot),
+	.sb_set_mnt_opts =
+		LIST_HEAD_INIT(security_hook_heads.sb_set_mnt_opts),
+	.sb_clone_mnt_opts =
+		LIST_HEAD_INIT(security_hook_heads.sb_clone_mnt_opts),
+	.sb_parse_opts_str =
+		LIST_HEAD_INIT(security_hook_heads.sb_parse_opts_str),
+	.dentry_init_security =
+		LIST_HEAD_INIT(security_hook_heads.dentry_init_security),
+#ifdef CONFIG_SECURITY_PATH
+	.path_unlink =	LIST_HEAD_INIT(security_hook_heads.path_unlink),
+	.path_mkdir =	LIST_HEAD_INIT(security_hook_heads.path_mkdir),
+	.path_rmdir =	LIST_HEAD_INIT(security_hook_heads.path_rmdir),
+	.path_mknod =	LIST_HEAD_INIT(security_hook_heads.path_mknod),
+	.path_truncate =
+		LIST_HEAD_INIT(security_hook_heads.path_truncate),
+	.path_symlink =	LIST_HEAD_INIT(security_hook_heads.path_symlink),
+	.path_link =	LIST_HEAD_INIT(security_hook_heads.path_link),
+	.path_rename =	LIST_HEAD_INIT(security_hook_heads.path_rename),
+	.path_chmod =	LIST_HEAD_INIT(security_hook_heads.path_chmod),
+	.path_chown =	LIST_HEAD_INIT(security_hook_heads.path_chown),
+	.path_chroot =	LIST_HEAD_INIT(security_hook_heads.path_chroot),
+#endif
+	.inode_alloc_security =
+		LIST_HEAD_INIT(security_hook_heads.inode_alloc_security),
+	.inode_free_security =
+		LIST_HEAD_INIT(security_hook_heads.inode_free_security),
+	.inode_init_security =
+		LIST_HEAD_INIT(security_hook_heads.inode_init_security),
+	.inode_create =	LIST_HEAD_INIT(security_hook_heads.inode_create),
+	.inode_link =	LIST_HEAD_INIT(security_hook_heads.inode_link),
+	.inode_unlink =	LIST_HEAD_INIT(security_hook_heads.inode_unlink),
+	.inode_symlink =
+		LIST_HEAD_INIT(security_hook_heads.inode_symlink),
+	.inode_mkdir =	LIST_HEAD_INIT(security_hook_heads.inode_mkdir),
+	.inode_rmdir =	LIST_HEAD_INIT(security_hook_heads.inode_rmdir),
+	.inode_mknod =	LIST_HEAD_INIT(security_hook_heads.inode_mknod),
+	.inode_rename =	LIST_HEAD_INIT(security_hook_heads.inode_rename),
+	.inode_readlink =
+		LIST_HEAD_INIT(security_hook_heads.inode_readlink),
+	.inode_follow_link =
+		LIST_HEAD_INIT(security_hook_heads.inode_follow_link),
+	.inode_permission =
+		LIST_HEAD_INIT(security_hook_heads.inode_permission),
+	.inode_setattr =
+		LIST_HEAD_INIT(security_hook_heads.inode_setattr),
+	.inode_getattr =
+		LIST_HEAD_INIT(security_hook_heads.inode_getattr),
+	.inode_setxattr =
+		LIST_HEAD_INIT(security_hook_heads.inode_setxattr),
+	.inode_post_setxattr =
+		LIST_HEAD_INIT(security_hook_heads.inode_post_setxattr),
+	.inode_getxattr =
+		LIST_HEAD_INIT(security_hook_heads.inode_getxattr),
+	.inode_listxattr =
+		LIST_HEAD_INIT(security_hook_heads.inode_listxattr),
+	.inode_removexattr =
+		LIST_HEAD_INIT(security_hook_heads.inode_removexattr),
+	.inode_need_killpriv =
+		LIST_HEAD_INIT(security_hook_heads.inode_need_killpriv),
+	.inode_killpriv =
+		LIST_HEAD_INIT(security_hook_heads.inode_killpriv),
+	.inode_getsecurity =
+		LIST_HEAD_INIT(security_hook_heads.inode_getsecurity),
+	.inode_setsecurity =
+		LIST_HEAD_INIT(security_hook_heads.inode_setsecurity),
+	.inode_listsecurity =
+		LIST_HEAD_INIT(security_hook_heads.inode_listsecurity),
+	.inode_getsecid =
+		LIST_HEAD_INIT(security_hook_heads.inode_getsecid),
+	.file_permission =
+		LIST_HEAD_INIT(security_hook_heads.file_permission),
+	.file_alloc_security =
+		LIST_HEAD_INIT(security_hook_heads.file_alloc_security),
+	.file_free_security =
+		LIST_HEAD_INIT(security_hook_heads.file_free_security),
+	.file_ioctl =	LIST_HEAD_INIT(security_hook_heads.file_ioctl),
+	.mmap_addr =	LIST_HEAD_INIT(security_hook_heads.mmap_addr),
+	.mmap_file =	LIST_HEAD_INIT(security_hook_heads.mmap_file),
+	.file_mprotect =
+		LIST_HEAD_INIT(security_hook_heads.file_mprotect),
+	.file_lock =	LIST_HEAD_INIT(security_hook_heads.file_lock),
+	.file_fcntl =	LIST_HEAD_INIT(security_hook_heads.file_fcntl),
+	.file_set_fowner =
+		LIST_HEAD_INIT(security_hook_heads.file_set_fowner),
+	.file_send_sigiotask =
+		LIST_HEAD_INIT(security_hook_heads.file_send_sigiotask),
+	.file_receive =	LIST_HEAD_INIT(security_hook_heads.file_receive),
+	.file_open =	LIST_HEAD_INIT(security_hook_heads.file_open),
+	.task_create =	LIST_HEAD_INIT(security_hook_heads.task_create),
+	.task_free =	LIST_HEAD_INIT(security_hook_heads.task_free),
+	.cred_alloc_blank =
+		LIST_HEAD_INIT(security_hook_heads.cred_alloc_blank),
+	.cred_free =	LIST_HEAD_INIT(security_hook_heads.cred_free),
+	.cred_prepare =	LIST_HEAD_INIT(security_hook_heads.cred_prepare),
+	.cred_transfer =
+		LIST_HEAD_INIT(security_hook_heads.cred_transfer),
+	.kernel_act_as =
+		LIST_HEAD_INIT(security_hook_heads.kernel_act_as),
+	.kernel_create_files_as =
+		LIST_HEAD_INIT(security_hook_heads.kernel_create_files_as),
+	.kernel_fw_from_file =
+		LIST_HEAD_INIT(security_hook_heads.kernel_fw_from_file),
+	.kernel_module_request =
+		LIST_HEAD_INIT(security_hook_heads.kernel_module_request),
+	.kernel_module_from_file =
+		LIST_HEAD_INIT(security_hook_heads.kernel_module_from_file),
+	.task_fix_setuid =
+		LIST_HEAD_INIT(security_hook_heads.task_fix_setuid),
+	.task_setpgid =	LIST_HEAD_INIT(security_hook_heads.task_setpgid),
+	.task_getpgid =	LIST_HEAD_INIT(security_hook_heads.task_getpgid),
+	.task_getsid =	LIST_HEAD_INIT(security_hook_heads.task_getsid),
+	.task_getsecid =
+		LIST_HEAD_INIT(security_hook_heads.task_getsecid),
+	.task_setnice =	LIST_HEAD_INIT(security_hook_heads.task_setnice),
+	.task_setioprio =
+		LIST_HEAD_INIT(security_hook_heads.task_setioprio),
+	.task_getioprio =
+		LIST_HEAD_INIT(security_hook_heads.task_getioprio),
+	.task_setrlimit =
+		LIST_HEAD_INIT(security_hook_heads.task_setrlimit),
+	.task_setscheduler =
+		LIST_HEAD_INIT(security_hook_heads.task_setscheduler),
+	.task_getscheduler =
+		LIST_HEAD_INIT(security_hook_heads.task_getscheduler),
+	.task_movememory =
+		LIST_HEAD_INIT(security_hook_heads.task_movememory),
+	.task_kill =	LIST_HEAD_INIT(security_hook_heads.task_kill),
+	.task_wait =	LIST_HEAD_INIT(security_hook_heads.task_wait),
+	.task_prctl =	LIST_HEAD_INIT(security_hook_heads.task_prctl),
+	.task_to_inode =
+		LIST_HEAD_INIT(security_hook_heads.task_to_inode),
+	.ipc_permission =
+		LIST_HEAD_INIT(security_hook_heads.ipc_permission),
+	.ipc_getsecid =	LIST_HEAD_INIT(security_hook_heads.ipc_getsecid),
+	.msg_msg_alloc_security =
+		LIST_HEAD_INIT(security_hook_heads.msg_msg_alloc_security),
+	.msg_msg_free_security =
+		LIST_HEAD_INIT(security_hook_heads.msg_msg_free_security),
+	.msg_queue_alloc_security =
+		LIST_HEAD_INIT(security_hook_heads.msg_queue_alloc_security),
+	.msg_queue_free_security =
+		LIST_HEAD_INIT(security_hook_heads.msg_queue_free_security),
+	.msg_queue_associate =
+		LIST_HEAD_INIT(security_hook_heads.msg_queue_associate),
+	.msg_queue_msgctl =
+		LIST_HEAD_INIT(security_hook_heads.msg_queue_msgctl),
+	.msg_queue_msgsnd =
+		LIST_HEAD_INIT(security_hook_heads.msg_queue_msgsnd),
+	.msg_queue_msgrcv =
+		LIST_HEAD_INIT(security_hook_heads.msg_queue_msgrcv),
+	.shm_alloc_security =
+		LIST_HEAD_INIT(security_hook_heads.shm_alloc_security),
+	.shm_free_security =
+		LIST_HEAD_INIT(security_hook_heads.shm_free_security),
+	.shm_associate =
+		LIST_HEAD_INIT(security_hook_heads.shm_associate),
+	.shm_shmctl =	LIST_HEAD_INIT(security_hook_heads.shm_shmctl),
+	.shm_shmat =	LIST_HEAD_INIT(security_hook_heads.shm_shmat),
+	.sem_alloc_security =
+		LIST_HEAD_INIT(security_hook_heads.sem_alloc_security),
+	.sem_free_security =
+		LIST_HEAD_INIT(security_hook_heads.sem_free_security),
+	.sem_associate =
+		LIST_HEAD_INIT(security_hook_heads.sem_associate),
+	.sem_semctl =	LIST_HEAD_INIT(security_hook_heads.sem_semctl),
+	.sem_semop =	LIST_HEAD_INIT(security_hook_heads.sem_semop),
+	.netlink_send =	LIST_HEAD_INIT(security_hook_heads.netlink_send),
+	.d_instantiate =
+		LIST_HEAD_INIT(security_hook_heads.d_instantiate),
+	.getprocattr =	LIST_HEAD_INIT(security_hook_heads.getprocattr),
+	.setprocattr =	LIST_HEAD_INIT(security_hook_heads.setprocattr),
+	.ismaclabel =	LIST_HEAD_INIT(security_hook_heads.ismaclabel),
+	.secid_to_secctx =
+		LIST_HEAD_INIT(security_hook_heads.secid_to_secctx),
+	.secctx_to_secid =
+		LIST_HEAD_INIT(security_hook_heads.secctx_to_secid),
+	.release_secctx =
+		LIST_HEAD_INIT(security_hook_heads.release_secctx),
+	.inode_notifysecctx =
+		LIST_HEAD_INIT(security_hook_heads.inode_notifysecctx),
+	.inode_setsecctx =
+		LIST_HEAD_INIT(security_hook_heads.inode_setsecctx),
+	.inode_getsecctx =
+		LIST_HEAD_INIT(security_hook_heads.inode_getsecctx),
+#ifdef CONFIG_SECURITY_NETWORK
+	.unix_stream_connect =
+		LIST_HEAD_INIT(security_hook_heads.unix_stream_connect),
+	.unix_may_send =
+		LIST_HEAD_INIT(security_hook_heads.unix_may_send),
+	.socket_create =
+		LIST_HEAD_INIT(security_hook_heads.socket_create),
+	.socket_post_create =
+		LIST_HEAD_INIT(security_hook_heads.socket_post_create),
+	.socket_bind =	LIST_HEAD_INIT(security_hook_heads.socket_bind),
+	.socket_connect =
+		LIST_HEAD_INIT(security_hook_heads.socket_connect),
+	.socket_listen =
+		LIST_HEAD_INIT(security_hook_heads.socket_listen),
+	.socket_accept =
+		LIST_HEAD_INIT(security_hook_heads.socket_accept),
+	.socket_sendmsg =
+		LIST_HEAD_INIT(security_hook_heads.socket_sendmsg),
+	.socket_recvmsg =
+		LIST_HEAD_INIT(security_hook_heads.socket_recvmsg),
+	.socket_getsockname =
+		LIST_HEAD_INIT(security_hook_heads.socket_getsockname),
+	.socket_getpeername =
+		LIST_HEAD_INIT(security_hook_heads.socket_getpeername),
+	.socket_getsockopt =
+		LIST_HEAD_INIT(security_hook_heads.socket_getsockopt),
+	.socket_setsockopt =
+		LIST_HEAD_INIT(security_hook_heads.socket_setsockopt),
+	.socket_shutdown =
+		LIST_HEAD_INIT(security_hook_heads.socket_shutdown),
+	.socket_sock_rcv_skb =
+		LIST_HEAD_INIT(security_hook_heads.socket_sock_rcv_skb),
+	.socket_getpeersec_stream =
+		LIST_HEAD_INIT(security_hook_heads.socket_getpeersec_stream),
+	.socket_getpeersec_dgram =
+		LIST_HEAD_INIT(security_hook_heads.socket_getpeersec_dgram),
+	.sk_alloc_security =
+		LIST_HEAD_INIT(security_hook_heads.sk_alloc_security),
+	.sk_free_security =
+		LIST_HEAD_INIT(security_hook_heads.sk_free_security),
+	.sk_clone_security =
+		LIST_HEAD_INIT(security_hook_heads.sk_clone_security),
+	.sk_getsecid =	LIST_HEAD_INIT(security_hook_heads.sk_getsecid),
+	.sock_graft =	LIST_HEAD_INIT(security_hook_heads.sock_graft),
+	.inet_conn_request =
+		LIST_HEAD_INIT(security_hook_heads.inet_conn_request),
+	.inet_csk_clone =
+		LIST_HEAD_INIT(security_hook_heads.inet_csk_clone),
+	.inet_conn_established =
+		LIST_HEAD_INIT(security_hook_heads.inet_conn_established),
+	.secmark_relabel_packet =
+		LIST_HEAD_INIT(security_hook_heads.secmark_relabel_packet),
+	.secmark_refcount_inc =
+		LIST_HEAD_INIT(security_hook_heads.secmark_refcount_inc),
+	.secmark_refcount_dec =
+		LIST_HEAD_INIT(security_hook_heads.secmark_refcount_dec),
+	.req_classify_flow =
+		LIST_HEAD_INIT(security_hook_heads.req_classify_flow),
+	.tun_dev_alloc_security =
+		LIST_HEAD_INIT(security_hook_heads.tun_dev_alloc_security),
+	.tun_dev_free_security =
+		LIST_HEAD_INIT(security_hook_heads.tun_dev_free_security),
+	.tun_dev_create =
+		LIST_HEAD_INIT(security_hook_heads.tun_dev_create),
+	.tun_dev_attach_queue =
+		LIST_HEAD_INIT(security_hook_heads.tun_dev_attach_queue),
+	.tun_dev_attach =
+		LIST_HEAD_INIT(security_hook_heads.tun_dev_attach),
+	.tun_dev_open =	LIST_HEAD_INIT(security_hook_heads.tun_dev_open),
+	.skb_owned_by =	LIST_HEAD_INIT(security_hook_heads.skb_owned_by),
+#endif	/* CONFIG_SECURITY_NETWORK */
+#ifdef CONFIG_SECURITY_NETWORK_XFRM
+	.xfrm_policy_alloc_security =
+		LIST_HEAD_INIT(security_hook_heads.xfrm_policy_alloc_security),
+	.xfrm_policy_clone_security =
+		LIST_HEAD_INIT(security_hook_heads.xfrm_policy_clone_security),
+	.xfrm_policy_free_security =
+		LIST_HEAD_INIT(security_hook_heads.xfrm_policy_free_security),
+	.xfrm_policy_delete_security =
+		LIST_HEAD_INIT(security_hook_heads.xfrm_policy_delete_security),
+	.xfrm_state_alloc =
+		LIST_HEAD_INIT(security_hook_heads.xfrm_state_alloc),
+	.xfrm_state_alloc_acquire =
+		LIST_HEAD_INIT(security_hook_heads.xfrm_state_alloc_acquire),
+	.xfrm_state_free_security =
+		LIST_HEAD_INIT(security_hook_heads.xfrm_state_free_security),
+	.xfrm_state_delete_security =
+		LIST_HEAD_INIT(security_hook_heads.xfrm_state_delete_security),
+	.xfrm_policy_lookup =
+		LIST_HEAD_INIT(security_hook_heads.xfrm_policy_lookup),
+	.xfrm_state_pol_flow_match =
+		LIST_HEAD_INIT(security_hook_heads.xfrm_state_pol_flow_match),
+	.xfrm_decode_session =
+		LIST_HEAD_INIT(security_hook_heads.xfrm_decode_session),
+#endif	/* CONFIG_SECURITY_NETWORK_XFRM */
+#ifdef CONFIG_KEYS
+	.key_alloc =	LIST_HEAD_INIT(security_hook_heads.key_alloc),
+	.key_free =	LIST_HEAD_INIT(security_hook_heads.key_free),
+	.key_permission =
+		LIST_HEAD_INIT(security_hook_heads.key_permission),
+	.key_getsecurity =
+		LIST_HEAD_INIT(security_hook_heads.key_getsecurity),
+#endif	/* CONFIG_KEYS */
+#ifdef CONFIG_AUDIT
+	.audit_rule_init =
+		LIST_HEAD_INIT(security_hook_heads.audit_rule_init),
+	.audit_rule_known =
+		LIST_HEAD_INIT(security_hook_heads.audit_rule_known),
+	.audit_rule_match =
+		LIST_HEAD_INIT(security_hook_heads.audit_rule_match),
+	.audit_rule_free =
+		LIST_HEAD_INIT(security_hook_heads.audit_rule_free),
+#endif /* CONFIG_AUDIT */
+};
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 0cf105f..06c9dd9 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -1990,12 +1990,6 @@
 static int selinux_ptrace_access_check(struct task_struct *child,
 				     unsigned int mode)
 {
-	int rc;
-
-	rc = cap_ptrace_access_check(child, mode);
-	if (rc)
-		return rc;
-
 	if (mode & PTRACE_MODE_READ) {
 		u32 sid = current_sid();
 		u32 csid = task_sid(child);
@@ -2007,25 +2001,13 @@
 
 static int selinux_ptrace_traceme(struct task_struct *parent)
 {
-	int rc;
-
-	rc = cap_ptrace_traceme(parent);
-	if (rc)
-		return rc;
-
 	return task_has_perm(parent, current, PROCESS__PTRACE);
 }
 
 static int selinux_capget(struct task_struct *target, kernel_cap_t *effective,
 			  kernel_cap_t *inheritable, kernel_cap_t *permitted)
 {
-	int error;
-
-	error = current_has_perm(target, PROCESS__GETCAP);
-	if (error)
-		return error;
-
-	return cap_capget(target, effective, inheritable, permitted);
+	return current_has_perm(target, PROCESS__GETCAP);
 }
 
 static int selinux_capset(struct cred *new, const struct cred *old,
@@ -2033,13 +2015,6 @@
 			  const kernel_cap_t *inheritable,
 			  const kernel_cap_t *permitted)
 {
-	int error;
-
-	error = cap_capset(new, old,
-				      effective, inheritable, permitted);
-	if (error)
-		return error;
-
 	return cred_has_perm(old, new, PROCESS__SETCAP);
 }
 
@@ -2056,12 +2031,6 @@
 static int selinux_capable(const struct cred *cred, struct user_namespace *ns,
 			   int cap, int audit)
 {
-	int rc;
-
-	rc = cap_capable(cred, ns, cap, audit);
-	if (rc)
-		return rc;
-
 	return cred_has_capability(cred, cap, audit);
 }
 
@@ -2139,12 +2108,12 @@
 {
 	int rc, cap_sys_admin = 0;
 
-	rc = selinux_capable(current_cred(), &init_user_ns, CAP_SYS_ADMIN,
-			     SECURITY_CAP_NOAUDIT);
+	rc = cred_has_capability(current_cred(), CAP_SYS_ADMIN,
+					SECURITY_CAP_NOAUDIT);
 	if (rc == 0)
 		cap_sys_admin = 1;
 
-	return __vm_enough_memory(mm, pages, cap_sys_admin);
+	return cap_sys_admin;
 }
 
 /* binprm security operations */
@@ -2193,10 +2162,6 @@
 	struct inode *inode = file_inode(bprm->file);
 	int rc;
 
-	rc = cap_bprm_set_creds(bprm);
-	if (rc)
-		return rc;
-
 	/* SELinux context only depends on initial program or script and not
 	 * the script interpreter */
 	if (bprm->cred_prepared)
@@ -2320,7 +2285,7 @@
 					PROCESS__NOATSECURE, NULL);
 	}
 
-	return (atsecure || cap_bprm_secureexec(bprm));
+	return !!atsecure;
 }
 
 static int match_file(const void *p, struct file *file, unsigned fd)
@@ -3132,8 +3097,11 @@
 	 * and lack of permission just means that we fall back to the
 	 * in-core context value, not a denial.
 	 */
-	error = selinux_capable(current_cred(), &init_user_ns, CAP_MAC_ADMIN,
-				SECURITY_CAP_NOAUDIT);
+	error = cap_capable(current_cred(), &init_user_ns, CAP_MAC_ADMIN,
+			    SECURITY_CAP_NOAUDIT);
+	if (!error)
+		error = cred_has_capability(current_cred(), CAP_MAC_ADMIN,
+					    SECURITY_CAP_NOAUDIT);
 	if (!error)
 		error = security_sid_to_context_force(isec->sid, &context,
 						      &size);
@@ -3318,12 +3286,7 @@
 
 static int selinux_mmap_addr(unsigned long addr)
 {
-	int rc;
-
-	/* do DAC check on address space usage */
-	rc = cap_mmap_addr(addr);
-	if (rc)
-		return rc;
+	int rc = 0;
 
 	if (addr < CONFIG_LSM_MMAP_MIN_ADDR) {
 		u32 sid = current_sid();
@@ -3639,23 +3602,11 @@
 
 static int selinux_task_setnice(struct task_struct *p, int nice)
 {
-	int rc;
-
-	rc = cap_task_setnice(p, nice);
-	if (rc)
-		return rc;
-
 	return current_has_perm(p, PROCESS__SETSCHED);
 }
 
 static int selinux_task_setioprio(struct task_struct *p, int ioprio)
 {
-	int rc;
-
-	rc = cap_task_setioprio(p, ioprio);
-	if (rc)
-		return rc;
-
 	return current_has_perm(p, PROCESS__SETSCHED);
 }
 
@@ -3681,12 +3632,6 @@
 
 static int selinux_task_setscheduler(struct task_struct *p)
 {
-	int rc;
-
-	rc = cap_task_setscheduler(p);
-	if (rc)
-		return rc;
-
 	return current_has_perm(p, PROCESS__SETSCHED);
 }
 
@@ -5097,12 +5042,6 @@
 
 static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb)
 {
-	int err;
-
-	err = cap_netlink_send(sk, skb);
-	if (err)
-		return err;
-
 	return selinux_nlmsg_perm(sk, skb);
 }
 
@@ -5840,9 +5779,7 @@
 
 #endif
 
-static struct security_operations selinux_ops = {
-	LSM_HOOK_INIT(name, "selinux"),
-
+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),
 	LSM_HOOK_INIT(binder_transfer_binder, selinux_binder_transfer_binder),
@@ -6055,7 +5992,7 @@
 
 static __init int selinux_init(void)
 {
-	if (!security_module_enable(&selinux_ops)) {
+	if (!security_module_enable("selinux")) {
 		selinux_enabled = 0;
 		return 0;
 	}
@@ -6077,8 +6014,7 @@
 					    0, SLAB_PANIC, NULL);
 	avc_init();
 
-	if (register_security(&selinux_ops))
-		panic("SELinux: Unable to register with kernel.\n");
+	security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks));
 
 	if (avc_add_callback(selinux_netcache_avc_callback, AVC_CALLBACK_RESET))
 		panic("SELinux: Unable to register AVC netcache callback\n");
@@ -6206,7 +6142,7 @@
 	selinux_disabled = 1;
 	selinux_enabled = 0;
 
-	reset_security_ops();
+	security_delete_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks));
 
 	/* Try to destroy the avc node cache */
 	avc_disable();
diff --git a/security/smack/smack.h b/security/smack/smack.h
index 262dad8..b8c1a86 100644
--- a/security/smack/smack.h
+++ b/security/smack/smack.h
@@ -276,8 +276,6 @@
 extern struct list_head smack_known_list;
 extern struct list_head smk_netlbladdr_list;
 
-extern struct security_operations smack_ops;
-
 #define SMACK_HASH_SLOTS 16
 extern struct hlist_head smack_known_hash[SMACK_HASH_SLOTS];
 
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 4313bf4..5eae42c 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -436,17 +436,11 @@
  */
 static int smack_ptrace_access_check(struct task_struct *ctp, unsigned int mode)
 {
-	int rc;
 	struct smack_known *skp;
 
-	rc = cap_ptrace_access_check(ctp, mode);
-	if (rc != 0)
-		return rc;
-
 	skp = smk_of_task_struct(ctp);
 
-	rc = smk_ptrace_rule_check(current, skp, mode, __func__);
-	return rc;
+	return smk_ptrace_rule_check(current, skp, mode, __func__);
 }
 
 /**
@@ -462,10 +456,6 @@
 	int rc;
 	struct smack_known *skp;
 
-	rc = cap_ptrace_traceme(ptp);
-	if (rc != 0)
-		return rc;
-
 	skp = smk_of_task(current_security());
 
 	rc = smk_ptrace_rule_check(ptp, skp, PTRACE_MODE_ATTACH, __func__);
@@ -721,10 +711,6 @@
 	struct inode_smack *isp;
 	int rc;
 
-	rc = cap_bprm_set_creds(bprm);
-	if (rc != 0)
-		return rc;
-
 	if (bprm->cred_prepared)
 		return 0;
 
@@ -779,12 +765,11 @@
 static int smack_bprm_secureexec(struct linux_binprm *bprm)
 {
 	struct task_smack *tsp = current_security();
-	int ret = cap_bprm_secureexec(bprm);
 
-	if (!ret && (tsp->smk_task != tsp->smk_forked))
-		ret = 1;
+	if (tsp->smk_task != tsp->smk_forked)
+		return 1;
 
-	return ret;
+	return 0;
 }
 
 /*
@@ -1934,12 +1919,7 @@
  */
 static int smack_task_setnice(struct task_struct *p, int nice)
 {
-	int rc;
-
-	rc = cap_task_setnice(p, nice);
-	if (rc == 0)
-		rc = smk_curacc_on_task(p, MAY_WRITE, __func__);
-	return rc;
+	return smk_curacc_on_task(p, MAY_WRITE, __func__);
 }
 
 /**
@@ -1951,12 +1931,7 @@
  */
 static int smack_task_setioprio(struct task_struct *p, int ioprio)
 {
-	int rc;
-
-	rc = cap_task_setioprio(p, ioprio);
-	if (rc == 0)
-		rc = smk_curacc_on_task(p, MAY_WRITE, __func__);
-	return rc;
+	return smk_curacc_on_task(p, MAY_WRITE, __func__);
 }
 
 /**
@@ -1980,12 +1955,7 @@
  */
 static int smack_task_setscheduler(struct task_struct *p)
 {
-	int rc;
-
-	rc = cap_task_setscheduler(p);
-	if (rc == 0)
-		rc = smk_curacc_on_task(p, MAY_WRITE, __func__);
-	return rc;
+	return smk_curacc_on_task(p, MAY_WRITE, __func__);
 }
 
 /**
@@ -4266,9 +4236,7 @@
 	return 0;
 }
 
-struct security_operations smack_ops = {
-	LSM_HOOK_INIT(name, "smack"),
-
+struct security_hook_list smack_hooks[] = {
 	LSM_HOOK_INIT(ptrace_access_check, smack_ptrace_access_check),
 	LSM_HOOK_INIT(ptrace_traceme, smack_ptrace_traceme),
 	LSM_HOOK_INIT(syslog, smack_syslog),
@@ -4451,7 +4419,7 @@
 	struct cred *cred;
 	struct task_smack *tsp;
 
-	if (!security_module_enable(&smack_ops))
+	if (!security_module_enable("smack"))
 		return 0;
 
 	smack_enabled = 1;
@@ -4481,8 +4449,7 @@
 	/*
 	 * Register with LSM
 	 */
-	if (register_security(&smack_ops))
-		panic("smack: Unable to register with kernel.\n");
+	security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks));
 
 	return 0;
 }
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c
index d968298..4aa12c8 100644
--- a/security/smack/smackfs.c
+++ b/security/smack/smackfs.c
@@ -2547,7 +2547,7 @@
 	int err;
 	int rc;
 
-	if (!security_module_enable(&smack_ops))
+	if (!security_module_enable("smack"))
 		return 0;
 
 	err = smk_init_sysfs();
diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
index bce1358..cbf3df4 100644
--- a/security/tomoyo/tomoyo.c
+++ b/security/tomoyo/tomoyo.c
@@ -72,12 +72,6 @@
  */
 static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
 {
-	int rc;
-
-	rc = cap_bprm_set_creds(bprm);
-	if (rc)
-		return rc;
-
 	/*
 	 * Do only if this function is called for the first time of an execve
 	 * operation.
@@ -502,8 +496,7 @@
  * tomoyo_security_ops is a "struct security_operations" which is used for
  * registering TOMOYO.
  */
-static struct security_operations tomoyo_security_ops = {
-	LSM_HOOK_INIT(name, "tomoyo"),
+static struct security_hook_list tomoyo_hooks[] = {
 	LSM_HOOK_INIT(cred_alloc_blank, tomoyo_cred_alloc_blank),
 	LSM_HOOK_INIT(cred_prepare, tomoyo_cred_prepare),
 	LSM_HOOK_INIT(cred_transfer, tomoyo_cred_transfer),
@@ -546,11 +539,10 @@
 {
 	struct cred *cred = (struct cred *) current_cred();
 
-	if (!security_module_enable(&tomoyo_security_ops))
+	if (!security_module_enable("tomoyo"))
 		return 0;
 	/* register ourselves with the security framework */
-	if (register_security(&tomoyo_security_ops))
-		panic("Failure registering TOMOYO Linux");
+	security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks));
 	printk(KERN_INFO "TOMOYO Linux initialized\n");
 	cred->security = &tomoyo_kernel_domain;
 	tomoyo_mm_init();
diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c
index 23dd4c6..9ed3250 100644
--- a/security/yama/yama_lsm.c
+++ b/security/yama/yama_lsm.c
@@ -154,13 +154,9 @@
 int yama_task_prctl(int option, unsigned long arg2, unsigned long arg3,
 			   unsigned long arg4, unsigned long arg5)
 {
-	int rc;
+	int rc = -ENOSYS;
 	struct task_struct *myself = current;
 
-	rc = cap_task_prctl(option, arg2, arg3, arg4, arg5);
-	if (rc != -ENOSYS)
-		return rc;
-
 	switch (option) {
 	case PR_SET_PTRACER:
 		/* Since a thread can call prctl(), find the group leader
@@ -279,17 +275,10 @@
  *
  * Returns 0 if following the ptrace is allowed, -ve on error.
  */
-int yama_ptrace_access_check(struct task_struct *child,
+static int yama_ptrace_access_check(struct task_struct *child,
 				    unsigned int mode)
 {
-	int rc;
-
-	/* If standard caps disallows it, so does Yama.  We should
-	 * only tighten restrictions further.
-	 */
-	rc = cap_ptrace_access_check(child, mode);
-	if (rc)
-		return rc;
+	int rc = 0;
 
 	/* require ptrace target be a child of ptracer on attach */
 	if (mode == PTRACE_MODE_ATTACH) {
@@ -335,14 +324,7 @@
  */
 int yama_ptrace_traceme(struct task_struct *parent)
 {
-	int rc;
-
-	/* If standard caps disallows it, so does Yama.  We should
-	 * only tighten restrictions further.
-	 */
-	rc = cap_ptrace_traceme(parent);
-	if (rc)
-		return rc;
+	int rc = 0;
 
 	/* Only disallow PTRACE_TRACEME on more aggressive settings. */
 	switch (ptrace_scope) {
@@ -364,16 +346,17 @@
 	return rc;
 }
 
-#ifndef CONFIG_SECURITY_YAMA_STACKED
-static struct security_operations yama_ops = {
-	LSM_HOOK_INIT(name, "yama"),
-
+static struct security_hook_list yama_hooks[] = {
 	LSM_HOOK_INIT(ptrace_access_check, yama_ptrace_access_check),
 	LSM_HOOK_INIT(ptrace_traceme, yama_ptrace_traceme),
 	LSM_HOOK_INIT(task_prctl, yama_task_prctl),
 	LSM_HOOK_INIT(task_free, yama_task_free),
 };
-#endif
+
+void __init yama_add_hooks(void)
+{
+	security_add_hooks(yama_hooks, ARRAY_SIZE(yama_hooks));
+}
 
 #ifdef CONFIG_SYSCTL
 static int yama_dointvec_minmax(struct ctl_table *table, int write,
@@ -418,16 +401,13 @@
 static __init int yama_init(void)
 {
 #ifndef CONFIG_SECURITY_YAMA_STACKED
-	if (!security_module_enable(&yama_ops))
+	/*
+	 * If yama is being stacked this is already taken care of.
+	 */
+	if (!security_module_enable("yama"))
 		return 0;
 #endif
-
-	printk(KERN_INFO "Yama: becoming mindful.\n");
-
-#ifndef CONFIG_SECURITY_YAMA_STACKED
-	if (register_security(&yama_ops))
-		panic("Yama: kernel registration failed.\n");
-#endif
+	pr_info("Yama: becoming mindful.\n");
 
 #ifdef CONFIG_SYSCTL
 	if (!register_sysctl_paths(yama_sysctl_path, yama_sysctl_table))