Index: linux-2.6.9/Documentation/stacker.txt =================================================================== --- linux-2.6.9.orig/Documentation/stacker.txt 2004-12-02 12:15:35.390617408 -0600 +++ linux-2.6.9/Documentation/stacker.txt 2004-12-01 11:00:48.000000000 -0600 @@ -0,0 +1,54 @@ +------------ +LSM stacking +------------ + +This document consists of two parts. The first describes the stacker LSM. +The second describes what is needed from an LSM in order to permit it to +stack with other LSMs. + +-------------------------------------------------------- +stacker LSM - enable stacking multiple security modules. +-------------------------------------------------------- + +Stacker is compiled into the kernel. Find the "Stacker" option under +the Security submenu, and say 'Y'. Now, any security modules which are +loaded or compiled into the kernel will be managed by stacker. + +You may interact with stacker through it's sysfs interface, located +under /sys/stacker/. This consists of the following files: + +/sys/stacker/next_secondary: +In some cases, pairs or sets of LSMs may work together in subtle ways. +For instance, SELinux and the capabilities modules interact such that +it is better to have SELinux load the capabilities module as its own +secondary. To accomplish this, you would + 1. compile stacker and selinux both into the kernel + 2. compile capabilities as a module. + 3. During init, + echo selinux > /sys/stacker/next_secondary + modprobe capability + +/sys/stacker/lockdown: +Once you write to this file, you will no longer be able to load +LSMs. + +/sys/stacker/list_modules: +Reading this file will show which LSMs are being stacked. Note that +LSMs which have been loaded as secondaries of stacked modules will +not be shown. [ This could be done, but seems deceptive - SEH ] + +/sys/stacker/stop_responding: +Unregisters the /sys/stacker directory, so that you can no longer +interact with stacker. + +/sys/stacker/shortcircuit_restrictive: +Toggle whether to query all security modules for permission to proceed +with an action, regardless of whether an early module has denied +permission. By default, all security modules will be queried, as +they may need to update some state to reflect the attempted action, +although an error will still be returned. + +--------------------------------------------- +Readying an LSM for stacking with other LSMs. +--------------------------------------------- + Index: linux-2.6.9/include/linux/security.h =================================================================== --- linux-2.6.9.orig/include/linux/security.h 2004-12-01 11:00:43.000000000 -0600 +++ linux-2.6.9/include/linux/security.h 2004-12-01 11:00:48.000000000 -0600 @@ -1946,9 +1946,8 @@ /* prototypes */ extern int security_init (void); extern int register_security (struct security_operations *ops); -extern int unregister_security (struct security_operations *ops); +extern int unregister_security (const char *name, struct security_operations *ops); extern int mod_reg_security (const char *name, struct security_operations *ops); -extern int mod_unreg_security (const char *name, struct security_operations *ops); #else /* CONFIG_SECURITY */ Index: linux-2.6.9/security/Kconfig =================================================================== --- linux-2.6.9.orig/security/Kconfig 2004-12-01 11:00:11.000000000 -0600 +++ linux-2.6.9/security/Kconfig 2004-12-01 11:00:48.000000000 -0600 @@ -86,5 +86,20 @@ source security/selinux/Kconfig +config SECURITY_STACKER + boolean "LSM Stacking" + depends on SECURITY + help + Stack multiple LSMs. + +config SECURITY_STACKER_SHORTCIRCUIT + int "Short circuit restrictive hooks by default." + range 0 1 + default 1 + depends on SECURITY_STACKER + help + If set, then security modules will be queried for a particular + restrictive hook only until a module denies permission. + endmenu Index: linux-2.6.9/security/Makefile =================================================================== --- linux-2.6.9.orig/security/Makefile 2004-12-01 11:00:11.000000000 -0600 +++ linux-2.6.9/security/Makefile 2004-12-01 11:00:48.000000000 -0600 @@ -2,6 +2,7 @@ # Makefile for the kernel security code # +obj-$(CONFIG_SECURITY_STACKER) += stacker.o obj-$(CONFIG_KEYS) += keys/ subdir-$(CONFIG_SECURITY_SELINUX) += selinux Index: linux-2.6.9/security/capability.c =================================================================== --- linux-2.6.9.orig/security/capability.c 2004-12-01 11:00:11.000000000 -0600 +++ linux-2.6.9/security/capability.c 2004-12-01 11:00:48.000000000 -0600 @@ -83,15 +83,8 @@ { if (capability_disable) return; - /* remove ourselves from the security framework */ - if (secondary) { - if (mod_unreg_security (MY_NAME, &capability_ops)) - printk (KERN_INFO "Failure unregistering capabilities " - "with primary module.\n"); - return; - } - if (unregister_security (&capability_ops)) { + if (unregister_security (MY_NAME, &capability_ops)) { printk (KERN_INFO "Failure unregistering capabilities with the kernel\n"); } Index: linux-2.6.9/security/root_plug.c =================================================================== --- linux-2.6.9.orig/security/root_plug.c 2004-10-18 16:55:28.000000000 -0500 +++ linux-2.6.9/security/root_plug.c 2004-12-01 11:00:48.000000000 -0600 @@ -121,17 +121,10 @@ static void __exit rootplug_exit (void) { /* remove ourselves from the security framework */ - if (secondary) { - if (mod_unreg_security (MY_NAME, &rootplug_security_ops)) - printk (KERN_INFO "Failure unregistering Root Plug " - " module with primary module.\n"); - } else { - if (unregister_security (&rootplug_security_ops)) { - printk (KERN_INFO "Failure unregistering Root Plug " - "module with the kernel\n"); - } - } - printk (KERN_INFO "Root Plug module removed\n"); + if (unregister_security(MY_NAME, &rootplug_security_ops)) + printk (KERN_INFO "Failure unregistering Root Plug\n"); + else + printk (KERN_INFO "Root Plug module removed\n"); } security_initcall (rootplug_init); Index: linux-2.6.9/security/seclvl.c =================================================================== --- linux-2.6.9.orig/security/seclvl.c 2004-12-01 11:00:44.000000000 -0600 +++ linux-2.6.9/security/seclvl.c 2004-12-01 14:06:10.000000000 -0600 @@ -758,13 +758,10 @@ &sysfs_attr_passwd.attr); } subsystem_unregister(&seclvl_subsys); - if (secondary == 1) { - mod_unreg_security(MY_NAME, &seclvl_ops); - } else if (unregister_security(&seclvl_ops)) { - seclvl_printk(0, KERN_INFO, - "seclvl: Failure unregistering with the " - "kernel\n"); - } + if (unregister_security(MY_NAME, &seclvl_ops)) + seclvl_printk(0, KERN_INFO, "seclvl: failure unregistering.\n"); + else + seclvl_printk(0, KERN_INFO, "seclvl unregistered.\n"); } module_init(seclvl_init); Index: linux-2.6.9/security/security.c =================================================================== --- linux-2.6.9.orig/security/security.c 2004-12-01 11:00:43.000000000 -0600 +++ linux-2.6.9/security/security.c 2004-12-01 11:00:48.000000000 -0600 @@ -222,20 +222,13 @@ * @ops: a pointer to the struct security_options that is to be registered * * This function removes a struct security_operations variable that had - * previously been registered with a successful call to register_security(). - * - * If @ops does not match the valued previously passed to register_security() - * an error is returned. Otherwise the default security options is set to the - * the dummy_security_ops structure, and 0 is returned. + * previously been registered with a successful call to register_security() + * or mod_reg_security(). */ -int unregister_security(struct security_operations *ops) +int unregister_security(const char *name, struct security_operations *ops) { - if (ops != security_ops) { - printk(KERN_INFO "%s: trying to unregister " - "a security_opts structure that is not " - "registered, failing.\n", __FUNCTION__); - return -EINVAL; - } + if (ops != security_ops) + return security_ops->unregister_security(name, ops); security_ops = &dummy_security_ops; @@ -272,30 +265,6 @@ } /** - * mod_unreg_security - allows a security module registered with mod_reg_security() to be unloaded - * @name: a pointer to a string with the name of the security_options to be removed - * @ops: a pointer to the struct security_options that is to be removed - * - * This function allows security modules that have been successfully registered - * with a call to mod_reg_security() to be unloaded from the system. - * This calls the currently loaded security module's unregister_security() call - * with the @name and @ops variables. - * - * The return value depends on the currently loaded security module, with 0 as - * success. - */ -int mod_unreg_security(const char *name, struct security_operations *ops) -{ - if (ops == security_ops) { - printk(KERN_INFO "%s invalid attempt to unregister " - " primary security ops.\n", __FUNCTION__); - return -EINVAL; - } - - return security_ops->unregister_security(name, ops); -} - -/** * capable - calls the currently loaded security module's capable() function with the specified capability * @cap: the requested capability level. * @@ -320,6 +289,5 @@ EXPORT_SYMBOL_GPL(register_security); EXPORT_SYMBOL_GPL(unregister_security); EXPORT_SYMBOL_GPL(mod_reg_security); -EXPORT_SYMBOL_GPL(mod_unreg_security); EXPORT_SYMBOL(capable); EXPORT_SYMBOL(security_ops); Index: linux-2.6.9/security/selinux/hooks.c =================================================================== --- linux-2.6.9.orig/security/selinux/hooks.c 2004-12-01 11:00:45.000000000 -0600 +++ linux-2.6.9/security/selinux/hooks.c 2004-12-01 11:00:48.000000000 -0600 @@ -4648,22 +4648,26 @@ selinux_disabled = 1; - if (secondary) { - if (mod_unreg_security(MY_NAME, &selinux_ops)) - printk(KERN_INFO "Failure unregistering selinux.\n"); + if (unregister_security(MY_NAME, &selinux_ops)) + printk(KERN_INFO "Failure unregistering selinux.\n"); + else + printk(KERN_INFO "Unregistered selinux.\n"); + + if (secondary_ops) { + int ret; + + if (secondary) { + ret = mod_reg_security(secondary_name, secondary_ops); + } else + ret = register_security(secondary_ops); + + if (ret) + printk(KERN_INFO + "Failure re-registering secondary (%s).\n", + secondary_name); else - printk(KERN_INFO "Unregistered selinux.\n"); - secondary = 0; - if (secondary_name) - if (mod_reg_security(secondary_name, secondary_ops)) - printk(KERN_INFO "Failure registering secondary.\n"); - } else { - if (unregister_security(&selinux_ops)) - printk(KERN_INFO "Failure unregistering selinux.\n"); - else - printk(KERN_INFO "Unregistered selinux.\n"); - if (secondary_ops) - register_security(secondary_ops); + printk(KERN_INFO "Re-registered secondary (%s).\n", + secondary_name); } /* Unregister netfilter hooks. */ Index: linux-2.6.9/security/stacker.c =================================================================== --- linux-2.6.9.orig/security/stacker.c 2004-12-02 12:15:35.390617408 -0600 +++ linux-2.6.9/security/stacker.c 2004-12-03 11:38:52.969215280 -0600 @@ -0,0 +1,1546 @@ +/* "Stacker" Linux security module (LSM). + * + * version 2004-11-29 + * Load this module first as the primary LSM module, + * and you can then stack (load) multiple additional LSM modules. + * + * Copyright (C) 2002,2003,2004 Serge E. Hallyn + * Copyright (C) 2002 David A. Wheeler . + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* A module entry keeps track of one of the stacked modules + * Note that module_operations is aggregated instead of being pointed to - + * it's one less allocation and one less pointer to follow. */ + +struct module_entry { + struct list_head lsm_list; /* list of stacked lsms */ + char *module_name; + int namelen; + struct security_operations module_operations; + struct rcu_head m_rcu; + atomic_t use; +}; +static struct list_head stacked_modules; /* list of stacked modules */ + +static short sysfsfiles_registered; +static int num_stacked_modules; + +static spinlock_t stacker_lock; +static int short_circuit_restrictive; /* = 0; if 1, short-circuit restrictive + hooks */ +static int forbid_stacker_register; /* = 0; if 1, can't register */ + + +/* + * Workarounds for the fact that get and setprocattr are used only by + * selinux. (Maybe) + */ +static struct module_entry *selinux_module; +static struct module_entry *next_secondary; + +/* Maximum number of characters in a stacked LSM module name */ +#define MAX_MODULE_NAME_LEN 128 + +static int debug = 0; + +module_param(debug, bool, 0600); +MODULE_PARM_DESC(debug, "Debug enabled or not"); +#define MY_NAME "stacker" +#define stacker_dbg(fmt, arg...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG "%s: %s: " fmt , \ + MY_NAME , __FUNCTION__ , \ + ## arg); \ + } while (0) + +static inline void stacker_free_module(struct module_entry *m) +{ + kfree(m->module_name); + kfree(m); +} + +/* + * Delete a module entry + */ +static void stacker_del_module(struct rcu_head *head) +{ + struct module_entry *m = container_of(head, struct module_entry, + m_rcu); + if (atomic_dec_and_test(&m->use)) + stacker_free_module(m); +} + +/* Walk through the list of modules in stacked_modules + * and ask each (in turn) for their results, then return the + * results. If more than one module reports an error, return + * the FIRST error code. + * + * If shortcircuit_restrictive is set, then we return as soon + * as an error is returned. Otherwise, we check all modules. + * This may be necessary if some modules have to update their + * state based on the permission request. + */ + +#define RETURN_ERROR_IF_ANY_ERROR(BASE_FUNC,FUNC_WITH_ARGS) do { \ + int final_result = 0; \ + struct module_entry *m; \ + rcu_read_lock(); \ + list_for_each_entry_rcu(m, &stacked_modules, lsm_list) { \ + int result = 0, stop = 0; \ + atomic_inc(&m->use); \ + rcu_read_unlock(); \ + if (m->module_operations.BASE_FUNC) \ + result = m->module_operations.FUNC_WITH_ARGS; \ + if (result && !final_result) { \ + final_result = result; \ + if (short_circuit_restrictive) \ + stop = 1; \ + } \ + rcu_read_lock(); \ + if (unlikely(atomic_dec_and_test(&m->use))) \ + stacker_free_module(m); \ + if (stop) \ + break; \ + } \ + rcu_read_unlock(); \ + return final_result; \ +} while (0) + +/* Call all modules in stacked_modules' routine */ +#define CALL_ALL(BASE_FUNC,FUNC_WITH_ARGS) do { \ + struct module_entry *m; \ + rcu_read_lock(); \ + list_for_each_entry_rcu(m, &stacked_modules, lsm_list) { \ + atomic_inc(&m->use); \ + rcu_read_unlock(); \ + if (m->module_operations.BASE_FUNC) \ + m->module_operations.FUNC_WITH_ARGS; \ + rcu_read_lock(); \ + if (unlikely(atomic_dec_and_test(&m->use))) \ + stacker_free_module(m); \ + } \ + rcu_read_unlock(); \ +} while (0) + +/* + * The list of functions for stacker_ops + */ +static int stacker_ptrace (struct task_struct *parent, struct task_struct *child) +{ + RETURN_ERROR_IF_ANY_ERROR(ptrace,ptrace(parent, child)); +} + +static int stacker_capget (struct task_struct *target, kernel_cap_t * effective, + kernel_cap_t * inheritable, kernel_cap_t * permitted) +{ + RETURN_ERROR_IF_ANY_ERROR(capget,capget(target, effective, inheritable, permitted)); +} + +static int stacker_capset_check (struct task_struct *target, + kernel_cap_t * effective, + kernel_cap_t * inheritable, + kernel_cap_t * permitted) +{ + RETURN_ERROR_IF_ANY_ERROR(capset_check,capset_check(target, effective, inheritable, permitted)); +} + +static void stacker_capset_set (struct task_struct *target, + kernel_cap_t * effective, + kernel_cap_t * inheritable, + kernel_cap_t * permitted) +{ + CALL_ALL(capset_set,capset_set(target, effective, inheritable, permitted)); +} + +static int stacker_acct (struct file *file) +{ + RETURN_ERROR_IF_ANY_ERROR(acct,acct(file)); +} + +static int stacker_capable (struct task_struct *tsk, int cap) +{ + RETURN_ERROR_IF_ANY_ERROR(capable,capable(tsk,cap)); +} + + +static int stacker_sysctl (struct ctl_table * table, int op) +{ + RETURN_ERROR_IF_ANY_ERROR(sysctl,sysctl(table, op)); +} + +static int stacker_quotactl (int cmds, int type, int id, struct super_block *sb) +{ + RETURN_ERROR_IF_ANY_ERROR(quotactl,quotactl(cmds,type,id,sb)); +} + +static int stacker_quota_on (struct file *f) +{ + RETURN_ERROR_IF_ANY_ERROR(quota_on,quota_on(f)); +} + +static int stacker_syslog (int type) +{ + RETURN_ERROR_IF_ANY_ERROR(syslog,syslog(type)); +} + +static int stacker_vm_enough_memory(long pages) +{ + RETURN_ERROR_IF_ANY_ERROR(vm_enough_memory,vm_enough_memory(pages)); +} + +static int stacker_netlink_send (struct sock *sk, struct sk_buff *skb) +{ + /* NOTE: The dummy module does this: + if (current->euid == 0) + cap_raise (NETLINK_CB (skb).eff_cap, CAP_NET_ADMIN); + else + NETLINK_CB (skb).eff_cap = 0; + * if this would be a problem with your module, then tell + * your administrators what to do. */ + + RETURN_ERROR_IF_ANY_ERROR(netlink_send,netlink_send(sk,skb)); +} + + +static int stacker_netlink_recv (struct sk_buff *skb) +{ + RETURN_ERROR_IF_ANY_ERROR(netlink_recv,netlink_recv(skb)); +} + +static int stacker_bprm_alloc_security (struct linux_binprm *bprm) +{ + RETURN_ERROR_IF_ANY_ERROR(bprm_alloc_security,bprm_alloc_security(bprm)); +} + +static void stacker_bprm_free_security (struct linux_binprm *bprm) +{ + CALL_ALL(bprm_free_security,bprm_free_security(bprm)); +} + +static void stacker_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) +{ + CALL_ALL(bprm_apply_creds,bprm_apply_creds(bprm, unsafe)); +} + +static int stacker_bprm_set_security (struct linux_binprm *bprm) +{ + RETURN_ERROR_IF_ANY_ERROR(bprm_set_security,bprm_set_security(bprm)); +} + +static int stacker_bprm_check_security (struct linux_binprm *bprm) +{ + RETURN_ERROR_IF_ANY_ERROR(bprm_check_security,bprm_check_security(bprm)); +} + +static int stacker_bprm_secureexec (struct linux_binprm *bprm) +{ + RETURN_ERROR_IF_ANY_ERROR(bprm_secureexec,bprm_secureexec(bprm)); +} + +static int stacker_sb_alloc_security (struct super_block *sb) +{ + RETURN_ERROR_IF_ANY_ERROR(sb_alloc_security,sb_alloc_security(sb)); +} + +static void stacker_sb_free_security (struct super_block *sb) +{ + CALL_ALL(sb_free_security,sb_free_security(sb)); +} + +static int stacker_sb_copy_data (struct file_system_type *type, + void *orig, void *copy) +{ + RETURN_ERROR_IF_ANY_ERROR(sb_copy_data,sb_copy_data(type,orig,copy)); +} + +static int stacker_sb_kern_mount (struct super_block *sb, void *data) +{ + RETURN_ERROR_IF_ANY_ERROR(sb_kern_mount,sb_kern_mount(sb, data)); +} + +static int stacker_sb_statfs (struct super_block *sb) +{ + RETURN_ERROR_IF_ANY_ERROR(sb_statfs,sb_statfs(sb)); +} + +static int stacker_mount (char *dev_name, struct nameidata *nd, char *type, + unsigned long flags, void *data) +{ + RETURN_ERROR_IF_ANY_ERROR(sb_mount,sb_mount(dev_name, nd, type, flags, data)); +} + +static int stacker_check_sb (struct vfsmount *mnt, struct nameidata *nd) +{ + RETURN_ERROR_IF_ANY_ERROR(sb_check_sb,sb_check_sb(mnt, nd)); +} + +static int stacker_umount (struct vfsmount *mnt, int flags) +{ + RETURN_ERROR_IF_ANY_ERROR(sb_umount,sb_umount(mnt, flags)); +} + +static void stacker_umount_close (struct vfsmount *mnt) +{ + CALL_ALL(sb_umount_close,sb_umount_close(mnt)); +} + +static void stacker_umount_busy (struct vfsmount *mnt) +{ + CALL_ALL(sb_umount_busy,sb_umount_busy(mnt)); +} + +static void stacker_post_remount (struct vfsmount *mnt, unsigned long flags, + void *data) +{ + CALL_ALL(sb_post_remount,sb_post_remount(mnt, flags, data)); +} + + +static void stacker_post_mountroot (void) +{ + CALL_ALL(sb_post_mountroot,sb_post_mountroot()); +} + +static void stacker_post_addmount (struct vfsmount *mnt, struct nameidata *nd) +{ + CALL_ALL(sb_post_addmount,sb_post_addmount(mnt, nd)); +} + +static int stacker_pivotroot (struct nameidata *old_nd, struct nameidata *new_nd) +{ + RETURN_ERROR_IF_ANY_ERROR(sb_pivotroot,sb_pivotroot(old_nd, new_nd)); +} + +static void stacker_post_pivotroot (struct nameidata *old_nd, struct nameidata *new_nd) +{ + CALL_ALL(sb_post_pivotroot,sb_post_pivotroot(old_nd, new_nd)); +} + +static int stacker_inode_alloc_security (struct inode *inode) +{ + RETURN_ERROR_IF_ANY_ERROR(inode_alloc_security,inode_alloc_security(inode)); +} + +static void stacker_inode_free_security (struct inode *inode) +{ + CALL_ALL(inode_free_security,inode_free_security(inode)); +} + +static int stacker_inode_create (struct inode *inode, struct dentry *dentry, + int mask) +{ + RETURN_ERROR_IF_ANY_ERROR(inode_create,inode_create(inode, dentry, mask)); +} + +static void stacker_inode_post_create (struct inode *inode, + struct dentry *dentry, int mask) +{ + CALL_ALL(inode_post_create,inode_post_create(inode, dentry, mask)); +} + +static int stacker_inode_link (struct dentry *old_dentry, struct inode *inode, + struct dentry *new_dentry) +{ + RETURN_ERROR_IF_ANY_ERROR(inode_link,inode_link(old_dentry, inode, new_dentry)); +} + +static void stacker_inode_post_link (struct dentry *old_dentry, + struct inode *inode, + struct dentry *new_dentry) +{ + CALL_ALL(inode_post_link,inode_post_link(old_dentry, inode, new_dentry)); +} + +static int stacker_inode_unlink (struct inode *inode, struct dentry *dentry) +{ + RETURN_ERROR_IF_ANY_ERROR(inode_unlink,inode_unlink(inode, dentry)); +} + +static int stacker_inode_symlink (struct inode *inode, struct dentry *dentry, + const char *name) +{ + RETURN_ERROR_IF_ANY_ERROR(inode_symlink,inode_symlink(inode, dentry, name)); +} + +static void stacker_inode_post_symlink (struct inode *inode, + struct dentry *dentry, const char *name) +{ + CALL_ALL(inode_post_symlink,inode_post_symlink(inode, dentry, name)); +} + +static int stacker_inode_mkdir (struct inode *inode, struct dentry *dentry, + int mask) +{ + RETURN_ERROR_IF_ANY_ERROR(inode_mkdir,inode_mkdir(inode, dentry, mask)); +} + +static void stacker_inode_post_mkdir (struct inode *inode, + struct dentry *dentry, int mask) +{ + CALL_ALL(inode_post_mkdir,inode_post_mkdir(inode, dentry, mask)); +} + +static int stacker_inode_rmdir (struct inode *inode, struct dentry *dentry) +{ + RETURN_ERROR_IF_ANY_ERROR(inode_rmdir,inode_rmdir(inode, dentry)); +} + +static int stacker_inode_mknod (struct inode *inode, struct dentry *dentry, + int major, dev_t minor) +{ + RETURN_ERROR_IF_ANY_ERROR(inode_mknod,inode_mknod(inode, dentry, major, minor)); +} + +static void stacker_inode_post_mknod (struct inode *inode, + struct dentry *dentry, int major, dev_t minor) +{ + CALL_ALL(inode_post_mknod,inode_post_mknod(inode, dentry, major, minor)); +} + +static int stacker_inode_rename (struct inode *old_inode, + struct dentry *old_dentry, + struct inode *new_inode, + struct dentry *new_dentry) +{ + RETURN_ERROR_IF_ANY_ERROR(inode_rename,inode_rename(old_inode, old_dentry, + new_inode, new_dentry)); +} + +static void stacker_inode_post_rename (struct inode *old_inode, + struct dentry *old_dentry, + struct inode *new_inode, + struct dentry *new_dentry) +{ + CALL_ALL(inode_post_rename,inode_post_rename(old_inode, old_dentry, + new_inode, new_dentry)); +} + +static int stacker_inode_readlink (struct dentry *dentry) +{ + RETURN_ERROR_IF_ANY_ERROR(inode_readlink,inode_readlink(dentry)); +} + +static int stacker_inode_follow_link (struct dentry *dentry, + struct nameidata *nameidata) +{ + RETURN_ERROR_IF_ANY_ERROR(inode_follow_link,inode_follow_link(dentry, nameidata)); +} + +static int stacker_inode_permission (struct inode *inode, int mask, + struct nameidata *nd) +{ + RETURN_ERROR_IF_ANY_ERROR(inode_permission,inode_permission(inode, mask, nd)); +} + +static int stacker_inode_setattr (struct dentry *dentry, struct iattr *iattr) +{ + RETURN_ERROR_IF_ANY_ERROR(inode_setattr,inode_setattr(dentry, iattr)); +} + +static int stacker_inode_getattr (struct vfsmount *mnt, struct dentry *dentry) +{ + RETURN_ERROR_IF_ANY_ERROR(inode_getattr,inode_getattr(mnt,dentry)); +} + +static void stacker_inode_delete (struct inode *ino) +{ + CALL_ALL(inode_delete,inode_delete(ino)); +} + +static int stacker_inode_setxattr (struct dentry *dentry, char *name, + void *value, size_t size, int flags) +{ + RETURN_ERROR_IF_ANY_ERROR(inode_setxattr,inode_setxattr(dentry,name,value,size,flags)); +} + +static void stacker_inode_post_setxattr (struct dentry *dentry, char *name, void *value, + size_t size, int flags) +{ + CALL_ALL(inode_post_setxattr,inode_post_setxattr(dentry,name,value,size,flags)); +} + +static int stacker_inode_getxattr (struct dentry *dentry, char *name) +{ + RETURN_ERROR_IF_ANY_ERROR(inode_getxattr,inode_getxattr(dentry,name)); +} + +static int stacker_inode_listxattr (struct dentry *dentry) +{ + RETURN_ERROR_IF_ANY_ERROR(inode_listxattr,inode_listxattr(dentry)); +} + +static int stacker_inode_removexattr (struct dentry *dentry, char *name) +{ + RETURN_ERROR_IF_ANY_ERROR(inode_removexattr,inode_removexattr(dentry,name)); +} + +static int stacker_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size) +{ + RETURN_ERROR_IF_ANY_ERROR(inode_getsecurity,inode_getsecurity(inode,name,buffer,size)); +} + +static int stacker_inode_setsecurity(struct inode *inode, const char *name, const void *value, size_t size, int flags) +{ + RETURN_ERROR_IF_ANY_ERROR(inode_setsecurity,inode_setsecurity(inode,name,value,size,flags)); +} + +static int stacker_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer_size) +{ + RETURN_ERROR_IF_ANY_ERROR(inode_listsecurity,inode_listsecurity(inode,buffer, buffer_size)); +} + +static int stacker_file_permission (struct file *file, int mask) +{ + RETURN_ERROR_IF_ANY_ERROR(file_permission,file_permission(file,mask)); +} + +static int stacker_file_alloc_security (struct file *file) +{ + RETURN_ERROR_IF_ANY_ERROR(file_alloc_security,file_alloc_security(file)); +} + +static void stacker_file_free_security (struct file *file) +{ + CALL_ALL(file_free_security,file_free_security(file)); +} + +static int stacker_file_ioctl (struct file *file, unsigned int command, + unsigned long arg) +{ + RETURN_ERROR_IF_ANY_ERROR(file_ioctl,file_ioctl(file,command,arg)); +} + +static int stacker_file_mmap (struct file *file, unsigned long prot, + unsigned long flags) +{ + RETURN_ERROR_IF_ANY_ERROR(file_mmap,file_mmap(file, prot, flags)); +} + +static int stacker_file_mprotect (struct vm_area_struct *vma, + unsigned long prot) +{ + RETURN_ERROR_IF_ANY_ERROR(file_mprotect,file_mprotect(vma,prot)); +} + +static int stacker_file_lock (struct file *file, unsigned int cmd) +{ + RETURN_ERROR_IF_ANY_ERROR(file_lock,file_lock(file,cmd)); +} + +static int stacker_file_fcntl (struct file *file, unsigned int cmd, + unsigned long arg) +{ + RETURN_ERROR_IF_ANY_ERROR(file_fcntl,file_fcntl(file,cmd,arg)); +} + +static int stacker_file_set_fowner (struct file *file) +{ + RETURN_ERROR_IF_ANY_ERROR(file_set_fowner,file_set_fowner(file)); +} + +static int stacker_file_send_sigiotask (struct task_struct *tsk, + struct fown_struct *fown, int sig) +{ + RETURN_ERROR_IF_ANY_ERROR(file_send_sigiotask,file_send_sigiotask(tsk,fown,sig)); +} + +static int stacker_file_receive (struct file *file) +{ + RETURN_ERROR_IF_ANY_ERROR(file_receive,file_receive(file)); +} + +static int stacker_task_create (unsigned long clone_flags) +{ + RETURN_ERROR_IF_ANY_ERROR(task_create,task_create(clone_flags)); +} + +static int stacker_task_alloc_security (struct task_struct *p) +{ + RETURN_ERROR_IF_ANY_ERROR(task_alloc_security,task_alloc_security(p)); +} + +static void stacker_task_free_security (struct task_struct *p) +{ + CALL_ALL(task_free_security,task_free_security(p)); +} + +static int stacker_task_setuid (uid_t id0, uid_t id1, uid_t id2, int flags) +{ + RETURN_ERROR_IF_ANY_ERROR(task_setuid,task_setuid(id0,id1,id2,flags)); +} + +static int stacker_task_post_setuid (uid_t id0, uid_t id1, uid_t id2, int flags) +{ + RETURN_ERROR_IF_ANY_ERROR(task_post_setuid,task_post_setuid(id0,id1,id2,flags)); +} + +static int stacker_task_setgid (gid_t id0, gid_t id1, gid_t id2, int flags) +{ + RETURN_ERROR_IF_ANY_ERROR(task_setgid,task_setgid(id0,id1,id2,flags)); +} + +static int stacker_task_setpgid (struct task_struct *p, pid_t pgid) +{ + RETURN_ERROR_IF_ANY_ERROR(task_setpgid,task_setpgid(p,pgid)); +} + +static int stacker_task_getpgid (struct task_struct *p) +{ + RETURN_ERROR_IF_ANY_ERROR(task_getpgid,task_getpgid(p)); +} + +static int stacker_task_getsid (struct task_struct *p) +{ + RETURN_ERROR_IF_ANY_ERROR(task_getsid,task_getsid(p)); +} + +static int stacker_task_setgroups (struct group_info *group_info) +{ + RETURN_ERROR_IF_ANY_ERROR(task_setgroups,task_setgroups(group_info)); +} + +static int stacker_task_setnice (struct task_struct *p, int nice) +{ + RETURN_ERROR_IF_ANY_ERROR(task_setnice,task_setnice(p,nice)); +} + +static int stacker_task_setrlimit (unsigned int resource, struct rlimit *new_rlim) +{ + RETURN_ERROR_IF_ANY_ERROR(task_setrlimit,task_setrlimit(resource,new_rlim)); +} + +static int stacker_task_setscheduler (struct task_struct *p, int policy, + struct sched_param *lp) +{ + RETURN_ERROR_IF_ANY_ERROR(task_setscheduler,task_setscheduler(p,policy,lp)); +} + +static int stacker_task_getscheduler (struct task_struct *p) +{ + RETURN_ERROR_IF_ANY_ERROR(task_getscheduler,task_getscheduler(p)); +} + +static int stacker_task_wait (struct task_struct *p) +{ + RETURN_ERROR_IF_ANY_ERROR(task_wait,task_wait(p)); +} + +static int stacker_task_kill (struct task_struct *p, struct siginfo *info, + int sig) +{ + RETURN_ERROR_IF_ANY_ERROR(task_kill,task_kill(p,info,sig)); +} + +static int stacker_task_prctl (int option, unsigned long arg2, unsigned long arg3, + unsigned long arg4, unsigned long arg5) +{ + RETURN_ERROR_IF_ANY_ERROR(task_prctl,task_prctl(option,arg2,arg3,arg4,arg5)); +} + +static void stacker_task_reparent_to_init (struct task_struct *p) +{ + /* Note that the dummy version of this hook would call: + * p->euid = p->fsuid = 0; */ + + CALL_ALL(task_reparent_to_init,task_reparent_to_init(p)); +} + +static void stacker_task_to_inode(struct task_struct *p, struct inode *inode) +{ + CALL_ALL(task_to_inode,task_to_inode(p, inode)); +} + +#ifdef CONFIG_SECURITY_NETWORK +static int stacker_socket_create (int family, int type, int protocol, int kern) +{ + RETURN_ERROR_IF_ANY_ERROR(socket_create,socket_create(family,type,protocol,kern)); +} + +static void stacker_socket_post_create (struct socket *sock, int family, + int type, int protocol, int kern) +{ + CALL_ALL(socket_post_create,socket_post_create(sock,family,type,protocol,kern)); +} + +static int stacker_socket_bind (struct socket *sock, struct sockaddr *address, + int addrlen) +{ + RETURN_ERROR_IF_ANY_ERROR(socket_bind,socket_bind(sock,address,addrlen)); +} + +static int stacker_socket_connect (struct socket *sock, + struct sockaddr *address, int addrlen) +{ + RETURN_ERROR_IF_ANY_ERROR(socket_connect,socket_connect(sock,address,addrlen)); +} + +static int stacker_socket_listen (struct socket *sock, int backlog) +{ + RETURN_ERROR_IF_ANY_ERROR(socket_listen,socket_listen(sock,backlog)); +} + +static int stacker_socket_accept (struct socket *sock, struct socket *newsock) +{ + RETURN_ERROR_IF_ANY_ERROR(socket_accept,socket_accept(sock,newsock)); +} + +static void stacker_socket_post_accept (struct socket *sock, + struct socket *newsock) +{ + CALL_ALL(socket_post_accept,socket_post_accept(sock,newsock)); +} + +static int stacker_socket_sendmsg (struct socket *sock, struct msghdr *msg, + int size) +{ + RETURN_ERROR_IF_ANY_ERROR(socket_sendmsg,socket_sendmsg(sock,msg,size)); +} + +static int stacker_socket_recvmsg (struct socket *sock, struct msghdr *msg, + int size, int flags) +{ + RETURN_ERROR_IF_ANY_ERROR(socket_recvmsg,socket_recvmsg(sock,msg,size,flags)); +} + +static int stacker_socket_getsockname (struct socket *sock) +{ + RETURN_ERROR_IF_ANY_ERROR(socket_getsockname,socket_getsockname(sock)); +} + +static int stacker_socket_getpeername (struct socket *sock) +{ + RETURN_ERROR_IF_ANY_ERROR(socket_getpeername,socket_getpeername(sock)); +} + +static int stacker_socket_setsockopt (struct socket *sock, int level, int optname) +{ + RETURN_ERROR_IF_ANY_ERROR(socket_setsockopt,socket_setsockopt(sock,level,optname)); +} + +static int stacker_socket_getsockopt (struct socket *sock, int level, int optname) +{ + RETURN_ERROR_IF_ANY_ERROR(socket_getsockopt,socket_getsockopt(sock,level,optname)); +} + +static int stacker_socket_shutdown (struct socket *sock, int how) +{ + RETURN_ERROR_IF_ANY_ERROR(socket_shutdown,socket_shutdown(sock,how)); +} + +static int stacker_socket_sock_rcv_skb (struct sock *sk, struct sk_buff *skb) +{ + RETURN_ERROR_IF_ANY_ERROR(socket_sock_rcv_skb,socket_sock_rcv_skb(sk,skb)); +} + +static int stacker_unix_stream_connect (struct socket *sock, + struct socket *other, struct sock *newsk) +{ + RETURN_ERROR_IF_ANY_ERROR(unix_stream_connect,unix_stream_connect(sock,other,newsk)); +} + +static int stacker_unix_may_send (struct socket *sock, + struct socket *other) +{ + RETURN_ERROR_IF_ANY_ERROR(unix_may_send,unix_may_send(sock,other)); +} + +static int stacker_socket_getpeersec(struct socket *sock, + char __user *optval, int __user *optlen, unsigned len) +{ + RETURN_ERROR_IF_ANY_ERROR(socket_getpeersec,socket_getpeersec(sock,optval,optlen,len)); +} + +static int stacker_sk_alloc_security(struct sock *sk, int family, + int priority) +{ + RETURN_ERROR_IF_ANY_ERROR(sk_alloc_security,sk_alloc_security(sk,family,priority)); +} + +static void stacker_sk_free_security (struct sock *sk) +{ + CALL_ALL(sk_free_security,sk_free_security(sk)); +} + +#endif + +static int stacker_ipc_permission (struct kern_ipc_perm *ipcp, short flag) +{ + RETURN_ERROR_IF_ANY_ERROR(ipc_permission,ipc_permission(ipcp,flag)); +} + +static int stacker_msg_msg_alloc_security (struct msg_msg *msg) +{ + RETURN_ERROR_IF_ANY_ERROR(msg_msg_alloc_security,msg_msg_alloc_security(msg)); +} + +static void stacker_msg_msg_free_security (struct msg_msg *msg) +{ + CALL_ALL(msg_msg_free_security,msg_msg_free_security(msg)); +} + +static int stacker_msg_queue_alloc_security (struct msg_queue *msq) +{ + RETURN_ERROR_IF_ANY_ERROR(msg_queue_alloc_security,msg_queue_alloc_security(msq)); +} + +static void stacker_msg_queue_free_security (struct msg_queue *msq) +{ + CALL_ALL(msg_queue_free_security,msg_queue_free_security(msq)); +} + +static int stacker_msg_queue_associate (struct msg_queue *msq, int msqflg) +{ + RETURN_ERROR_IF_ANY_ERROR(msg_queue_associate,msg_queue_associate(msq,msqflg)); +} + +static int stacker_msg_queue_msgctl (struct msg_queue *msq, int cmd) +{ + RETURN_ERROR_IF_ANY_ERROR(msg_queue_msgctl,msg_queue_msgctl(msq,cmd)); +} + +static int stacker_msg_queue_msgsnd (struct msg_queue *msq, struct msg_msg *msg, + int msgflg) +{ + RETURN_ERROR_IF_ANY_ERROR(msg_queue_msgsnd,msg_queue_msgsnd(msq,msg,msgflg)); +} + +static int stacker_msg_queue_msgrcv (struct msg_queue *msq, struct msg_msg *msg, + struct task_struct *target, long type, + int mode) +{ + RETURN_ERROR_IF_ANY_ERROR(msg_queue_msgrcv,msg_queue_msgrcv(msq,msg,target,type,mode)); +} + +static int stacker_shm_alloc_security (struct shmid_kernel *shp) +{ + RETURN_ERROR_IF_ANY_ERROR(shm_alloc_security,shm_alloc_security(shp)); +} + +static void stacker_shm_free_security (struct shmid_kernel *shp) +{ + CALL_ALL(shm_free_security,shm_free_security(shp)); +} + +static int stacker_shm_associate (struct shmid_kernel *shp, int shmflg) +{ + RETURN_ERROR_IF_ANY_ERROR(shm_associate,shm_associate(shp,shmflg)); +} + +static int stacker_shm_shmctl (struct shmid_kernel *shp, int cmd) +{ + RETURN_ERROR_IF_ANY_ERROR(shm_shmctl,shm_shmctl(shp,cmd)); +} + +static int stacker_shm_shmat (struct shmid_kernel *shp, char *shmaddr, + int shmflg) +{ + RETURN_ERROR_IF_ANY_ERROR(shm_shmat,shm_shmat(shp,shmaddr,shmflg)); +} + +static int stacker_sem_alloc_security (struct sem_array *sma) +{ + RETURN_ERROR_IF_ANY_ERROR(sem_alloc_security,sem_alloc_security(sma)); +} + +static void stacker_sem_free_security (struct sem_array *sma) +{ + CALL_ALL(sem_free_security,sem_free_security(sma)); +} + +static int stacker_sem_associate (struct sem_array *sma, int semflg) +{ + RETURN_ERROR_IF_ANY_ERROR(sem_associate,sem_associate(sma,semflg)); +} + +static int stacker_sem_semctl (struct sem_array *sma, int cmd) +{ + RETURN_ERROR_IF_ANY_ERROR(sem_semctl,sem_semctl(sma,cmd)); +} + +static int stacker_sem_semop (struct sem_array *sma, + struct sembuf *sops, unsigned nsops, int alter) +{ + RETURN_ERROR_IF_ANY_ERROR(sem_semop,sem_semop(sma,sops,nsops,alter)); +} + +static void stacker_d_instantiate (struct dentry *dentry, struct inode *inode) +{ + CALL_ALL(d_instantiate,d_instantiate(dentry,inode)); +} + +static int +stacker_getprocattr(struct task_struct *p, char *name, void *value, size_t size) +{ + if (!selinux_module) + return -EINVAL; + if (!selinux_module->module_operations.getprocattr) + return -EINVAL; + return selinux_module->module_operations.getprocattr(p, name, value, size); +} + +static int stacker_setprocattr(struct task_struct *p, char *name, void *value, size_t size) +{ + + if (!selinux_module) + return -EINVAL; + if (!selinux_module->module_operations.setprocattr) + return -EINVAL; + return selinux_module->module_operations.setprocattr(p, name, value, size); +} + +/* + * Add the stacked module (as specified by name and ops) + * according to the current ordering policy. + */ +static int stacker_register (const char *name, struct security_operations *ops) +{ + char *new_module_name; + struct module_entry *new_module_entry; + int namelen = strnlen(name, MAX_MODULE_NAME_LEN); + int ret = 0; + + spin_lock(&stacker_lock); + if (next_secondary) { + ret = next_secondary->module_operations.register_security(name, + ops); + printk(KERN_INFO "%s: %s loaded %s (returned %d)\n", + __FUNCTION__, next_secondary->module_name, + name, ret); + next_secondary = NULL; + goto out; + } + + if (forbid_stacker_register) { + ret = -EINVAL; + goto out; + } + + new_module_name = kmalloc(namelen+1, GFP_KERNEL); + new_module_entry = kmalloc(sizeof(struct module_entry), GFP_KERNEL); + if (!new_module_name || !new_module_entry) { + printk(KERN_WARNING + "%s: Failure registering module - out of memory\n", + __FUNCTION__); + ret = -ENOMEM; + goto out; + } + + memset(new_module_entry, 0, sizeof(struct module_entry)); + strncpy(new_module_name, name, namelen); + new_module_name[namelen] = '\0'; + memcpy(&new_module_entry->module_operations, ops, + sizeof(struct security_operations)); + + new_module_entry->module_name = new_module_name; + new_module_entry->namelen = namelen; + + atomic_set(&new_module_entry->use, 1); + INIT_LIST_HEAD(&new_module_entry->lsm_list); + list_add_tail_rcu(&new_module_entry->lsm_list, &stacked_modules); + num_stacked_modules++; + if (strcmp(name, "selinux") == 0) + selinux_module = new_module_entry; + + printk(KERN_INFO "%s: registered %s module\n", __FUNCTION__, + new_module_entry->module_name); + +out: + spin_unlock(&stacker_lock); + return ret; +} + +/* + * try to get a stacked module to unregister this module. + * called with spinlock held. + */ +static inline struct module_entry * +try_all_unregister(const char *name, struct security_operations *ops) +{ + struct module_entry *m, *ret = NULL; + + rcu_read_lock(); + list_for_each_entry_rcu(m, &stacked_modules, lsm_list) { + atomic_inc(&m->use); + rcu_read_unlock(); + if (!m->module_operations.unregister_security) + goto setup_next_iter; + if ( !m->module_operations.unregister_security(name, ops)) + ret = m; + +setup_next_iter: + rcu_read_lock(); + if (unlikely(atomic_dec_and_test(&m->use))) + stacker_free_module(m); + if (ret) + break; + } + rcu_read_unlock(); + + return ret; +} + +/* + * find_lsm_module_by_name: + * Find a module by name. Used by next_secondary_write. Called with + * stacker spinlock held. + */ +static struct module_entry * +find_lsm_with_namelen(const char *name, int len) +{ + struct module_entry *m, *ret = NULL; + + rcu_read_lock(); + list_for_each_entry_rcu(m, &stacked_modules, lsm_list) { + atomic_inc(&m->use); + rcu_read_unlock(); + if (m->namelen == len && !strncmp(m->module_name, name, len)) + ret = m; + rcu_read_lock(); + if (unlikely(atomic_dec_and_test(&m->use))) + stacker_free_module(m); + if (ret) + break; + } + rcu_read_unlock(); + + return ret; +} + +/* + * Unregister a registered LSM. + * It would be nice if we could veto this, but we can't... + */ +static int stacker_unregister (const char *name, struct security_operations *ops) +{ + struct module_entry *m; + int len = strnlen(name, MAX_MODULE_NAME_LEN); + int ret = 0; + + spin_lock(&stacker_lock); + m = find_lsm_with_namelen(name, len); + if (!m) { + m = try_all_unregister(name, ops); + if (m) { + printk(KERN_NOTICE "%s: %s unregistered %s\n", + __FUNCTION__, m->module_name, name); + goto out; + } + } + + if (!m) { + printk(KERN_INFO "%s: could not find module %s.\n", + __FUNCTION__, name); + ret = -ENOENT; + goto out; + } + + list_del_rcu(&m->lsm_list); + num_stacked_modules--; + + if (strcmp(m->module_name, "selinux") == 0) + selinux_module = NULL; + if (next_secondary == m) + next_secondary = NULL; + call_rcu(&m->m_rcu, stacker_del_module); + +out: + spin_unlock(&stacker_lock); + + return ret; +} + +static struct security_operations stacker_ops = { + .ptrace = stacker_ptrace, + .capget = stacker_capget, + .capset_check = stacker_capset_check, + .capset_set = stacker_capset_set, + .acct = stacker_acct, + .sysctl = stacker_sysctl, + .capable = stacker_capable, + .quotactl = stacker_quotactl, + .quota_on = stacker_quota_on, + .syslog = stacker_syslog, + .vm_enough_memory = stacker_vm_enough_memory, + + .bprm_alloc_security = stacker_bprm_alloc_security, + .bprm_free_security = stacker_bprm_free_security, + .bprm_apply_creds = stacker_bprm_apply_creds, + .bprm_set_security = stacker_bprm_set_security, + .bprm_check_security = stacker_bprm_check_security, + .bprm_secureexec = stacker_bprm_secureexec, + + .sb_alloc_security = stacker_sb_alloc_security, + .sb_free_security = stacker_sb_free_security, + .sb_copy_data = stacker_sb_copy_data, + .sb_kern_mount = stacker_sb_kern_mount, + .sb_statfs = stacker_sb_statfs, + .sb_mount = stacker_mount, + .sb_check_sb = stacker_check_sb, + .sb_umount = stacker_umount, + .sb_umount_close = stacker_umount_close, + .sb_umount_busy = stacker_umount_busy, + .sb_post_remount = stacker_post_remount, + .sb_post_mountroot = stacker_post_mountroot, + .sb_post_addmount = stacker_post_addmount, + .sb_pivotroot = stacker_pivotroot, + .sb_post_pivotroot = stacker_post_pivotroot, + + .inode_alloc_security = stacker_inode_alloc_security, + .inode_free_security = stacker_inode_free_security, + .inode_create = stacker_inode_create, + .inode_post_create = stacker_inode_post_create, + .inode_link = stacker_inode_link, + .inode_post_link = stacker_inode_post_link, + .inode_unlink = stacker_inode_unlink, + .inode_symlink = stacker_inode_symlink, + .inode_post_symlink = stacker_inode_post_symlink, + .inode_mkdir = stacker_inode_mkdir, + .inode_post_mkdir = stacker_inode_post_mkdir, + .inode_rmdir = stacker_inode_rmdir, + .inode_mknod = stacker_inode_mknod, + .inode_post_mknod = stacker_inode_post_mknod, + .inode_rename = stacker_inode_rename, + .inode_post_rename = stacker_inode_post_rename, + .inode_readlink = stacker_inode_readlink, + .inode_follow_link = stacker_inode_follow_link, + .inode_permission = stacker_inode_permission, + + .inode_setattr = stacker_inode_setattr, + .inode_getattr = stacker_inode_getattr, + .inode_delete = stacker_inode_delete, + .inode_setxattr = stacker_inode_setxattr, + .inode_post_setxattr = stacker_inode_post_setxattr, + .inode_getxattr = stacker_inode_getxattr, + .inode_listxattr = stacker_inode_listxattr, + .inode_removexattr = stacker_inode_removexattr, + .inode_getsecurity = stacker_inode_getsecurity, + .inode_setsecurity = stacker_inode_setsecurity, + .inode_listsecurity = stacker_inode_listsecurity, + + .file_permission = stacker_file_permission, + .file_alloc_security = stacker_file_alloc_security, + .file_free_security = stacker_file_free_security, + .file_ioctl = stacker_file_ioctl, + .file_mmap = stacker_file_mmap, + .file_mprotect = stacker_file_mprotect, + .file_lock = stacker_file_lock, + .file_fcntl = stacker_file_fcntl, + .file_set_fowner = stacker_file_set_fowner, + .file_send_sigiotask = stacker_file_send_sigiotask, + .file_receive = stacker_file_receive, + + .task_create = stacker_task_create, + .task_alloc_security = stacker_task_alloc_security, + .task_free_security = stacker_task_free_security, + .task_setuid = stacker_task_setuid, + .task_post_setuid = stacker_task_post_setuid, + .task_setgid = stacker_task_setgid, + .task_setpgid = stacker_task_setpgid, + .task_getpgid = stacker_task_getpgid, + .task_getsid = stacker_task_getsid, + .task_setgroups = stacker_task_setgroups, + .task_setnice = stacker_task_setnice, + .task_setrlimit = stacker_task_setrlimit, + .task_setscheduler = stacker_task_setscheduler, + .task_getscheduler = stacker_task_getscheduler, + .task_kill = stacker_task_kill, + .task_wait = stacker_task_wait, + .task_prctl = stacker_task_prctl, + .task_reparent_to_init = stacker_task_reparent_to_init, + .task_to_inode = stacker_task_to_inode, + + .ipc_permission = stacker_ipc_permission, + + .msg_msg_alloc_security = stacker_msg_msg_alloc_security, + .msg_msg_free_security = stacker_msg_msg_free_security, + .msg_queue_alloc_security = stacker_msg_queue_alloc_security, + .msg_queue_free_security = stacker_msg_queue_free_security, + .msg_queue_associate = stacker_msg_queue_associate, + .msg_queue_msgctl = stacker_msg_queue_msgctl, + .msg_queue_msgsnd = stacker_msg_queue_msgsnd, + .msg_queue_msgrcv = stacker_msg_queue_msgrcv, + .shm_alloc_security = stacker_shm_alloc_security, + .shm_free_security = stacker_shm_free_security, + .shm_associate = stacker_shm_associate, + .shm_shmctl = stacker_shm_shmctl, + .shm_shmat = stacker_shm_shmat, + + .sem_alloc_security = stacker_sem_alloc_security, + .sem_free_security = stacker_sem_free_security, + .sem_associate = stacker_sem_associate, + .sem_semctl = stacker_sem_semctl, + .sem_semop = stacker_sem_semop, + + .netlink_send = stacker_netlink_send, + .netlink_recv = stacker_netlink_recv, + + .register_security = stacker_register, + .unregister_security = stacker_unregister, + + .d_instantiate = stacker_d_instantiate, + .getprocattr = stacker_getprocattr, + .setprocattr = stacker_setprocattr, + +#ifdef CONFIG_SECURITY_NETWORK + .unix_stream_connect = stacker_unix_stream_connect, + .unix_may_send = stacker_unix_may_send, + .socket_create = stacker_socket_create, + .socket_post_create = stacker_socket_post_create, + .socket_bind = stacker_socket_bind, + .socket_connect = stacker_socket_connect, + .socket_listen = stacker_socket_listen, + .socket_accept = stacker_socket_accept, + .socket_post_accept = stacker_socket_post_accept, + .socket_sendmsg = stacker_socket_sendmsg, + .socket_recvmsg = stacker_socket_recvmsg, + .socket_getsockname = stacker_socket_getsockname, + .socket_getpeername = stacker_socket_getpeername, + .socket_getsockopt = stacker_socket_getsockopt, + .socket_setsockopt = stacker_socket_setsockopt, + .socket_shutdown = stacker_socket_shutdown, + .socket_sock_rcv_skb = stacker_socket_sock_rcv_skb, + .socket_getpeersec = stacker_socket_getpeersec, + .sk_alloc_security = stacker_sk_alloc_security, + .sk_free_security = stacker_sk_free_security, +#endif + +}; + + +/* + * Functions to provide the sysfs interface + */ + +/* A structure to pass into sysfs through kobjects */ +struct stacker_kobj { + struct list_head slot_list; + struct kobject kobj; +}; + +struct stacker_attribute { + struct attribute attr; + ssize_t (*show)(struct stacker_kobj *, char *); + ssize_t (*store)(struct stacker_kobj *, const char *, size_t); +}; + +/* variables to hold kobject/sysfs data */ +static struct subsystem stacker_subsys; + +static void unregister_sysfs_files(void); + +static ssize_t stacker_attr_store(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t len) +{ + struct stacker_kobj *obj = container_of(kobj, + struct stacker_kobj, kobj); + struct stacker_attribute *attribute = container_of(attr, + struct stacker_attribute, attr); + + return attribute->store ? attribute->store(obj, buf, len) : 0; +} + +static ssize_t stacker_attr_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct stacker_kobj *obj = container_of(kobj, + struct stacker_kobj, kobj); + struct stacker_attribute *attribute = container_of(attr, + struct stacker_attribute, attr); + + return attribute->show ? attribute->show(obj, buf) : 0; +} + +static struct sysfs_ops stacker_sysfs_ops = { + .show = stacker_attr_show, + .store = stacker_attr_store, +}; + +static struct kobj_type stacker_ktype = { + .sysfs_ops = &stacker_sysfs_ops +}; + +static decl_subsys(stacker, &stacker_ktype, NULL); + +/* Set lockdown */ +static ssize_t lockdown_read (struct stacker_kobj *obj, char *buff) +{ + ssize_t len; + + if (forbid_stacker_register) + len = sprintf(buff, "Locked down\n"); + else + len = sprintf(buff, "Unlocked\n"); + + return len; +} + +static ssize_t lockdown_write (struct stacker_kobj *obj, const char *buff, size_t count) +{ + if (count>0) + forbid_stacker_register = 1; + + return count; +} + +static struct stacker_attribute stacker_attr_lockdown = { + .attr = {.name = "lockdown", .mode = S_IFREG | S_IRUGO | S_IWUSR}, + .show = lockdown_read, + .store = lockdown_write +}; + +/* list modules */ +static ssize_t listmodules_read (struct stacker_kobj *obj, char *buff) +{ + ssize_t len = 0; + struct module_entry *m; + + rcu_read_lock(); + list_for_each_entry_rcu(m, &stacked_modules, lsm_list) { + atomic_inc(&m->use); + rcu_read_unlock(); + len += snprintf(buff+len, PAGE_SIZE - len, "%s\n", + m->module_name); + rcu_read_lock(); + if (unlikely(atomic_dec_and_test(&m->use))) + stacker_free_module(m); + } + rcu_read_unlock(); + + return len; +} + +static struct stacker_attribute stacker_attr_listmodules = { + .attr = {.name = "list_modules", .mode = S_IFREG | S_IRUGO | S_IWUSR}, + .show = listmodules_read, +}; + +/* stop responding to sysfs */ +static ssize_t stop_responding_read (struct stacker_kobj *obj, char *buff) +{ + ssize_t len; + + len = sprintf(buff, "Obviously not\n"); + + return len; +} + +static ssize_t stop_responding_write (struct stacker_kobj *obj, const char *buff, size_t count) +{ + if (count>0) { + unregister_sysfs_files(); + } + + return count; +} + +static struct stacker_attribute stacker_attr_stop_responding = { + .attr = {.name = "stop_responding", .mode = S_IFREG | S_IRUGO | S_IWUSR}, + .show = stop_responding_read, + .store = stop_responding_write +}; + +/* short-circuit restrictive */ +static ssize_t shortcircuit_restrictive_read (struct stacker_kobj *obj, char *buff) +{ + ssize_t len; + + if (short_circuit_restrictive) + len = sprintf(buff, "Restrictive hooks ARE being short-circuited.\n"); + else + len = sprintf(buff, "Restrictive hooks are NOT being short-circuited.\n"); + + return len; +} + +static ssize_t shortcircuit_restrictive_write (struct stacker_kobj *obj, const char *buff, size_t count) +{ + int ret = count; + + if (!count) + return count; + + if (buff[0]=='0') + short_circuit_restrictive = 0; + else if (buff[0]=='1') + short_circuit_restrictive = 1; + else + ret = -ENOENT; + + return ret; +} + +static struct stacker_attribute stacker_attr_shortcircuit_restr = { + .attr = {.name = "shortcircuit_restrictive", .mode = S_IFREG | S_IRUGO | S_IWUSR}, + .show = shortcircuit_restrictive_read, + .store = shortcircuit_restrictive_write +}; + +static ssize_t next_secondary_read (struct stacker_kobj *obj, char *buff) +{ + if (!next_secondary) + return 0; + return sprintf(buff, next_secondary->module_name); +} + +static ssize_t next_secondary_write (struct stacker_kobj *obj, const char *buff, size_t count) +{ + unsigned long flags; + size_t len = count; + ssize_t ret = count; + + if (!len) + return count; + if (buff[len-1] == '\n') + len--; + spin_lock_irqsave(&stacker_lock, flags); + next_secondary = find_lsm_with_namelen(buff, len); + if (!next_secondary) + ret = -EINVAL; + spin_unlock_irqrestore(&stacker_lock, flags); + + return ret; +} + +/* + * An EXPORTed function to allow stacked LSMs to directly ask to adopt + * the next LSM, without needing to go through userspace + */ +int lsm_adopt_next_secondary(char *name) +{ + unsigned long flags; + size_t len = strnlen(name, MAX_MODULE_NAME_LEN); + int ret = 0; + + spin_lock_irqsave(&stacker_lock, flags); + next_secondary = find_lsm_with_namelen(name, len); + if (!next_secondary) + ret = -EINVAL; + spin_unlock_irqrestore(&stacker_lock, flags); + return ret; +} + +EXPORT_SYMBOL(lsm_adopt_next_secondary); + +static struct stacker_attribute stacker_attr_nextsec = { + .attr = {.name = "next_secondary", .mode = S_IFREG | S_IRUGO | S_IWUSR}, + .show = next_secondary_read, + .store = next_secondary_write +}; + +static void unregister_sysfs_files(void) +{ + struct kobject *kobj; + + if (!sysfsfiles_registered) + return; + + kobj = &stacker_subsys.kset.kobj; + sysfs_remove_file(kobj, &stacker_attr_nextsec.attr); + sysfs_remove_file(kobj, &stacker_attr_lockdown.attr); + sysfs_remove_file(kobj, &stacker_attr_listmodules.attr); + sysfs_remove_file(kobj, &stacker_attr_stop_responding.attr); + sysfs_remove_file(kobj, &stacker_attr_shortcircuit_restr.attr); + + sysfsfiles_registered = 0; +} + +static int register_sysfs_files(void) +{ + int result; + + result = subsystem_register(&stacker_subsys); + if (result) { + printk(KERN_WARNING + "Error (%d) registering stacker sysfs subsystem\n", + result); + return result; + } + + sysfs_create_file(&stacker_subsys.kset.kobj, + &stacker_attr_lockdown.attr); + sysfs_create_file(&stacker_subsys.kset.kobj, + &stacker_attr_listmodules.attr); + sysfs_create_file(&stacker_subsys.kset.kobj, + &stacker_attr_stop_responding.attr); + sysfs_create_file(&stacker_subsys.kset.kobj, + &stacker_attr_shortcircuit_restr.attr); + sysfs_create_file(&stacker_subsys.kset.kobj, + &stacker_attr_nextsec.attr); + sysfsfiles_registered = 1; + stacker_dbg("sysfs files registered\n"); + return 0; +} + +module_init(register_sysfs_files); + +/* + * Structure sent back to security modules for getting and + * setting security blobs + */ + +static int __init stacker_init (void) +{ + forbid_stacker_register = 0; + short_circuit_restrictive = CONFIG_SECURITY_STACKER_SHORTCIRCUIT; + INIT_LIST_HEAD(&stacked_modules); + sysfsfiles_registered = 0; + num_stacked_modules = 0; + spin_lock_init(&stacker_lock); + + if (register_security (&stacker_ops)) { + /* + * stacking stacker is just a stupid idea, so don't ask + * the current module to load us. + */ + printk (KERN_WARNING "Failure registering stacker module " + "as primary security module.\n"); + return -EINVAL; + } + stacker_dbg("LSM stacker initialized\n"); + + return 0; +} + +static void __exit stacker_exit (void) +{ + /* + * Since we have no return value, we can't just say no. + * Should probably force all child modules to exit somehow... + */ + + unregister_sysfs_files(); + if (unregister_security (MY_NAME, &stacker_ops)) + printk (KERN_WARNING + "Error unregistering LSM stacker.\n"); + else + stacker_dbg("LSM stacker removed.\n"); +} + +security_initcall (stacker_init); +module_exit (stacker_exit); + +MODULE_DESCRIPTION("LSM stacker - supports multiple simultaneous LSM modules"); +MODULE_AUTHOR("David A. Wheeler, Serge Hallyn"); +MODULE_LICENSE("GPL");