Message ID | 3fb59e68-4448-84fd-dcbd-ed0b1731096a@schaufler-ca.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Hi,
[auto build test ERROR on security/next]
[also build test ERROR on v4.7-rc2 next-20160608]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Casey-Schaufler/LSM-module-hierarchy-in-proc-attr/20160609-055140
base: https://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security.git next
config: x86_64-allmodconfig (attached as .config)
compiler: gcc-6 (Debian 6.1.1-1) 6.1.1 20160430
reproduce:
# save the attached .config to linux build tree
make ARCH=x86_64
All errors (new ones prefixed by >>):
security/loadpin/loadpin.c: In function 'loadpin_add_hooks':
>> security/loadpin/loadpin.c:185:2: error: too few arguments to function 'security_add_hooks'
security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks));
^~~~~~~~~~~~~~~~~~
In file included from security/loadpin/loadpin.c:23:0:
include/linux/lsm_hooks.h:1860:13: note: declared here
extern void security_add_hooks(struct security_hook_list *hooks, int count,
^~~~~~~~~~~~~~~~~~
vim +/security_add_hooks +185 security/loadpin/loadpin.c
9b091556 Kees Cook 2016-04-20 179 LSM_HOOK_INIT(kernel_read_file, loadpin_read_file),
9b091556 Kees Cook 2016-04-20 180 };
9b091556 Kees Cook 2016-04-20 181
9b091556 Kees Cook 2016-04-20 182 void __init loadpin_add_hooks(void)
9b091556 Kees Cook 2016-04-20 183 {
9b091556 Kees Cook 2016-04-20 184 pr_info("ready to pin (currently %sabled)", enabled ? "en" : "dis");
9b091556 Kees Cook 2016-04-20 @185 security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks));
9b091556 Kees Cook 2016-04-20 186 }
9b091556 Kees Cook 2016-04-20 187
9b091556 Kees Cook 2016-04-20 188 /* Should not be mutable after boot, so not listed in sysfs (perm == 0). */
:::::: The code at line 185 was first introduced by commit
:::::: 9b091556a073a9f5f93e2ad23d118f45c4796a84 LSM: LoadPin for kernel file loading restrictions
:::::: TO: Kees Cook <keescook@chromium.org>
:::::: CC: James Morris <james.l.morris@oracle.com>
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
Hi, [auto build test WARNING on security/next] [also build test WARNING on v4.7-rc2 next-20160608] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system] url: https://github.com/0day-ci/linux/commits/Casey-Schaufler/LSM-module-hierarchy-in-proc-attr/20160609-055140 base: https://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security.git next reproduce: make htmldocs All warnings (new ones prefixed by >>): lib/crc32.c:148: warning: No description found for parameter 'tab)[256]' lib/crc32.c:148: warning: Excess function parameter 'tab' description in 'crc32_le_generic' lib/crc32.c:293: warning: No description found for parameter 'tab)[256]' lib/crc32.c:293: warning: Excess function parameter 'tab' description in 'crc32_be_generic' lib/crc32.c:1: warning: no structured comments found mm/memory.c:2881: warning: No description found for parameter 'old' >> security/security.c:127: warning: No description found for parameter 'hooks' >> security/security.c:127: warning: No description found for parameter 'count' >> security/security.c:127: warning: No description found for parameter 'lsm' vim +/hooks +127 security/security.c 111 * Otherwise, return false. 112 */ 113 int __init security_module_enable(const char *module) 114 { 115 return !strcmp(module, chosen_lsm); 116 } 117 118 /** 119 * security_add_hooks - Add a modules hooks to the hook lists. 120 * @hooks - the hooks to add 121 * @count - the number of hooks to add 122 * 123 * Each LSM has to register its hooks with the infrastructure. 124 */ 125 void __init security_add_hooks(struct security_hook_list *hooks, int count, 126 char *lsm) > 127 { 128 int i; 129 130 for (i = 0; i < count; i++) { 131 hooks[i].lsm = lsm; 132 list_add_tail_rcu(&hooks[i].list, hooks[i].head); 133 } 134 if (lsm_append(lsm, &lsm_names) < 0) 135 panic("%s - Cannot get early memory.\n", __func__); --- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation
On Wed, Jun 8, 2016 at 2:49 PM, Casey Schaufler <casey@schaufler-ca.com> wrote: > Subject: [PATCH] LSM: module hierarchy in /proc/.../attr > > Back in 2007 I made what turned out to be a rather serious > mistake in the implementation of the Smack security module. > The SELinux module used an interface in /proc to manipulate > the security context on processes. Rather than use a similar > interface, I used the same interface. The AppArmor team did > likewise. Now /proc/.../attr/current will tell you the > security "context" of the process, but it will be different > depending on the security module you're using. That hasn't > been a problem to date, as you can only have one module > that supports process attributes at a time. We are coming > up on a change to that, where multiple modules with process > attributes can be supported. (Not included here) > > This patch provides a subdirectory in /proc/.../attr for > each of the security modules that use the LSM hooks > getprocattr() and setprocattr(). Each of the interfaces > used by a module are presented in the subdirectory. The > old interfaces remain and work the same as before. > User space code can begin migrating to the subdirectory > interfaces in anticipation of the time when what comes > from /proc/self/attr/current might not be what a runtime > wants. > > Also looking ahead, the attr directory and each of its > subdirectories provides a "context" interface. The data > provided is the same as the "current" interface in each > of the subdirectories, except that it is formatted to > identify the module it comes from. The format is: > > lsmname='context-value' > > When multiple concurrent modules are supported the > /proc/.../attr/context interface will include the data > for all of the active modules. > > Finally, I got tired of having to find indirect ways to > determine what security modules are active on a system. > I have added /sys/kernel/security/lsm, which contains a > comma separated list of the active security modules. No > more groping around in /proc/filesystems, which won't > help if the module doesn't support its own filesystem. I think this might be more readable if the patch was split into the lsm name support first, then the attr dir support, and then maybe the "context" file support. Each feel like logically distinct changes to me. > The original implementation is by Kees Cook. The code Wow, it is? I forgot a lot in 9 years. :) > has been changed a bit to reflect changes in the direction > of the multiple concurrent module work, to be independent > of it, and to bring it up to date with the current tree. > > Signed-off-by: Casey Schaufler <casey@schaufler-ca.com> > > --- > Documentation/security/LSM.txt | 26 +++++-- > fs/proc/base.c | 95 ++++++++++++++++++++--- > fs/proc/internal.h | 1 + > include/linux/lsm_hooks.h | 19 ++--- > include/linux/security.h | 15 ++-- > security/apparmor/lsm.c | 45 ++++++++--- > security/commoncap.c | 3 +- > security/inode.c | 26 ++++++- > security/security.c | 166 ++++++++++++++++++++++++++++++++++++++++- > security/selinux/hooks.c | 28 +++++-- > security/smack/smack_lsm.c | 28 ++++--- > security/tomoyo/tomoyo.c | 2 +- > security/yama/yama_lsm.c | 2 +- > 13 files changed, 391 insertions(+), 65 deletions(-) > > diff --git a/Documentation/security/LSM.txt b/Documentation/security/LSM.txt > index 3db7e67..125c489 100644 > --- a/Documentation/security/LSM.txt > +++ b/Documentation/security/LSM.txt > @@ -16,11 +16,25 @@ MAC extensions, other extensions can be built using the LSM to provide > specific changes to system operation when these tweaks are not available > in the core functionality of Linux itself. > > -Without a specific LSM built into the kernel, the default LSM will be the > -Linux capabilities system. Most LSMs choose to extend the capabilities > -system, building their checks on top of the defined capability hooks. > -For more details on capabilities, see capabilities(7) in the Linux > -man-pages project. > +The Linux capabilities modules will always be included. For more details > +on capabilities, see capabilities(7) in the Linux man-pages project. > +This may be followed by any number of "minor" modules and at most one > +"major" module. > + > +A list of the active security modules can be found by reading > +/sys/kernel/security/lsm. This is a comma separated list, and > +will always include the capability module. The list reflects the > +order in which checks are made. The capability module will always > +be first, followed by any "minor" modules (e.g. Yama) and then > +the one "major" module (e.g. SELinux) if there is one configured. > + > +Process attributes associated with "major" security modules should > +be accessed and maintained using the special files in the module > +specific subdirectories in /proc/.../attr. The attributes related > +to Smack would be found in /proc/.../attr/smack while the attributes > +for SELinux would be in /proc/.../attr/selinux. Using the files > +found directly in /proc/.../attr (e.g. current) should be avoided. > +These files remain as legacy interfaces. > > Based on https://lkml.org/lkml/2007/10/26/215, > a new LSM is accepted into the kernel when its intent (a description of > @@ -31,4 +45,4 @@ that end users and distros can make a more informed decision about which > LSMs suit their requirements. > > For extensive documentation on the available LSM hook interfaces, please > -see include/linux/security.h. > +see include/linux/lsm_hooks.h. > diff --git a/fs/proc/base.c b/fs/proc/base.c > index a11eb71..df94f26 100644 > --- a/fs/proc/base.c > +++ b/fs/proc/base.c > @@ -131,9 +131,13 @@ struct pid_entry { > #define REG(NAME, MODE, fops) \ > NOD(NAME, (S_IFREG|(MODE)), NULL, &fops, {}) > #define ONE(NAME, MODE, show) \ > - NOD(NAME, (S_IFREG|(MODE)), \ > + NOD(NAME, (S_IFREG|(MODE)), \ Accidental whitespace change? > NULL, &proc_single_file_operations, \ > { .proc_show = show } ) > +#define ATTR(LSM, NAME, MODE) \ > + NOD(NAME, (S_IFREG|(MODE)), \ > + NULL, &proc_pid_attr_operations, \ > + { .lsm = LSM }) > > /* > * Count the number of hardlinks for the pid_entry table, excluding the . > @@ -2433,7 +2437,7 @@ static ssize_t proc_pid_attr_read(struct file * file, char __user * buf, > if (!task) > return -ESRCH; > > - length = security_getprocattr(task, > + length = security_getprocattr(task, PROC_I(inode)->op.lsm, > (char*)file->f_path.dentry->d_name.name, > &p); > put_task_struct(task); > @@ -2473,7 +2477,7 @@ static ssize_t proc_pid_attr_write(struct file * file, const char __user * buf, > if (length < 0) > goto out_free; > > - length = security_setprocattr(task, > + length = security_setprocattr(task, PROC_I(inode)->op.lsm, > (char*)file->f_path.dentry->d_name.name, > page, count); > mutex_unlock(&task->signal->cred_guard_mutex); > @@ -2491,13 +2495,86 @@ static const struct file_operations proc_pid_attr_operations = { > .llseek = generic_file_llseek, > }; > > +#define LSM_DIR_OPS(LSM) \ > +static int proc_##LSM##_attr_dir_iterate(struct file *filp, \ > + struct dir_context *ctx) \ > +{ \ > + return proc_pident_readdir(filp, ctx, \ > + LSM##_attr_dir_stuff, \ > + ARRAY_SIZE(LSM##_attr_dir_stuff)); \ > +} \ > +\ > +static const struct file_operations proc_##LSM##_attr_dir_ops = { \ > + .read = generic_read_dir, \ > + .iterate = proc_##LSM##_attr_dir_iterate, \ > + .llseek = default_llseek, \ > +}; \ > +\ > +static struct dentry *proc_##LSM##_attr_dir_lookup(struct inode *dir, \ > + struct dentry *dentry, unsigned int flags) \ > +{ \ > + return proc_pident_lookup(dir, dentry, \ > + LSM##_attr_dir_stuff, \ > + ARRAY_SIZE(LSM##_attr_dir_stuff)); \ > +} \ > +\ > +static const struct inode_operations proc_##LSM##_attr_dir_inode_ops = { \ > + .lookup = proc_##LSM##_attr_dir_lookup, \ > + .getattr = pid_getattr, \ > + .setattr = proc_setattr, \ > +} > + > +#ifdef CONFIG_SECURITY_SELINUX > +static const struct pid_entry selinux_attr_dir_stuff[] = { > + ATTR("selinux", "current", S_IRUGO|S_IWUGO), > + ATTR("selinux", "prev", S_IRUGO), > + ATTR("selinux", "exec", S_IRUGO|S_IWUGO), > + ATTR("selinux", "fscreate", S_IRUGO|S_IWUGO), > + ATTR("selinux", "keycreate", S_IRUGO|S_IWUGO), > + ATTR("selinux", "sockcreate", S_IRUGO|S_IWUGO), > + ATTR("selinux", "context", S_IRUGO|S_IWUGO), > +}; > +LSM_DIR_OPS(selinux); > +#endif > + > +#ifdef CONFIG_SECURITY_SMACK > +static const struct pid_entry smack_attr_dir_stuff[] = { > + ATTR("smack", "current", S_IRUGO|S_IWUGO), > + ATTR("smack", "context", S_IRUGO|S_IWUGO), > +}; > +LSM_DIR_OPS(smack); > +#endif > + > +#ifdef CONFIG_SECURITY_APPARMOR > +static const struct pid_entry apparmor_attr_dir_stuff[] = { > + ATTR("apparmor", "current", S_IRUGO|S_IWUGO), > + ATTR("apparmor", "prev", S_IRUGO), > + ATTR("apparmor", "exec", S_IRUGO|S_IWUGO), > + ATTR("apparmor", "context", S_IRUGO|S_IWUGO), > +}; > +LSM_DIR_OPS(apparmor); > +#endif > + > static const struct pid_entry attr_dir_stuff[] = { > - REG("current", S_IRUGO|S_IWUGO, proc_pid_attr_operations), > - REG("prev", S_IRUGO, proc_pid_attr_operations), > - REG("exec", S_IRUGO|S_IWUGO, proc_pid_attr_operations), > - REG("fscreate", S_IRUGO|S_IWUGO, proc_pid_attr_operations), > - REG("keycreate", S_IRUGO|S_IWUGO, proc_pid_attr_operations), > - REG("sockcreate", S_IRUGO|S_IWUGO, proc_pid_attr_operations), > + ATTR(NULL, "current", S_IRUGO|S_IWUGO), > + ATTR(NULL, "prev", S_IRUGO), > + ATTR(NULL, "exec", S_IRUGO|S_IWUGO), > + ATTR(NULL, "fscreate", S_IRUGO|S_IWUGO), > + ATTR(NULL, "keycreate", S_IRUGO|S_IWUGO), > + ATTR(NULL, "sockcreate", S_IRUGO|S_IWUGO), > + ATTR(NULL, "context", S_IRUGO|S_IWUGO), > +#ifdef CONFIG_SECURITY_SELINUX > + DIR("selinux", S_IRUGO|S_IXUGO, > + proc_selinux_attr_dir_inode_ops, proc_selinux_attr_dir_ops), > +#endif > +#ifdef CONFIG_SECURITY_SMACK > + DIR("smack", S_IRUGO|S_IXUGO, > + proc_smack_attr_dir_inode_ops, proc_smack_attr_dir_ops), > +#endif > +#ifdef CONFIG_SECURITY_APPARMOR > + DIR("apparmor", S_IRUGO|S_IXUGO, > + proc_apparmor_attr_dir_inode_ops, proc_apparmor_attr_dir_ops), > +#endif > }; I wonder if there might be a way to more dynamically add these instead of #ifdefing each LSM into this file? Something like "register_lsm_attr("apparmor", "current", S_IRUGO|S_IWUGO);" and leave it to the LSMs to do that work at init time? > static int proc_attr_dir_readdir(struct file *file, struct dir_context *ctx) > diff --git a/fs/proc/internal.h b/fs/proc/internal.h > index aa27810..b607cd5 100644 > --- a/fs/proc/internal.h > +++ b/fs/proc/internal.h > @@ -56,6 +56,7 @@ union proc_op { > int (*proc_show)(struct seq_file *m, > struct pid_namespace *ns, struct pid *pid, > struct task_struct *task); > + const char *lsm; > }; > > struct proc_inode { > diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h > index 7ae3976..5b796db 100644 > --- a/include/linux/lsm_hooks.h > +++ b/include/linux/lsm_hooks.h > @@ -1514,9 +1514,10 @@ union security_list_options { > > void (*d_instantiate)(struct dentry *dentry, struct inode *inode); > > - int (*getprocattr)(struct task_struct *p, char *name, char **value); > - int (*setprocattr)(struct task_struct *p, char *name, void *value, > - size_t size); > + int (*getprocattr)(struct task_struct *p, const char *lsm, char *name, > + char **value); > + int (*setprocattr)(struct task_struct *p, const char *lsm, char *name, > + void *value, size_t size); > int (*ismaclabel)(const char *name); > int (*secid_to_secctx)(u32 secid, char **secdata, u32 *seclen); > int (*secctx_to_secid)(const char *secdata, u32 seclen, u32 *secid); > @@ -1841,6 +1842,7 @@ struct security_hook_list { > struct list_head list; > struct list_head *head; > union security_list_options hook; > + char *lsm; > }; > > /* > @@ -1853,15 +1855,10 @@ struct security_hook_list { > { .head = &security_hook_heads.HEAD, .hook = { .HEAD = HOOK } } > > extern struct security_hook_heads security_hook_heads; > +extern char *lsm_names; > > -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); > -} > +extern void security_add_hooks(struct security_hook_list *hooks, int count, > + char *lsm); > > #ifdef CONFIG_SECURITY_SELINUX_DISABLE > /* > diff --git a/include/linux/security.h b/include/linux/security.h > index 14df373..383fcb0 100644 > --- a/include/linux/security.h > +++ b/include/linux/security.h > @@ -355,8 +355,10 @@ int security_sem_semctl(struct sem_array *sma, int cmd); > int security_sem_semop(struct sem_array *sma, struct sembuf *sops, > unsigned nsops, int alter); > void security_d_instantiate(struct dentry *dentry, struct inode *inode); > -int security_getprocattr(struct task_struct *p, char *name, char **value); > -int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size); > +int security_getprocattr(struct task_struct *p, const char *lsm, char *name, > + char **value); > +int security_setprocattr(struct task_struct *p, const char *lsm, char *name, > + void *value, size_t size); > int security_netlink_send(struct sock *sk, struct sk_buff *skb); > int security_ismaclabel(const char *name); > int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen); > @@ -1075,15 +1077,18 @@ static inline int security_sem_semop(struct sem_array *sma, > return 0; > } > > -static inline void security_d_instantiate(struct dentry *dentry, struct inode *inode) > +static inline void security_d_instantiate(struct dentry *dentry, > + struct inode *inode) > { } > > -static inline int security_getprocattr(struct task_struct *p, char *name, char **value) > +static inline int security_getprocattr(struct task_struct *p, const char *lsm, > + char *name, char **value) > { > return -EINVAL; > } > > -static inline int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size) > +static inline int security_setprocattr(struct task_struct *p, const char *lsm, > + char *name, void *value, size_t size) > { > return -EINVAL; > } > diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c > index 2660fbc..7e54292 100644 > --- a/security/apparmor/lsm.c > +++ b/security/apparmor/lsm.c > @@ -468,8 +468,8 @@ static int apparmor_file_mprotect(struct vm_area_struct *vma, > !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0); > } > > -static int apparmor_getprocattr(struct task_struct *task, char *name, > - char **value) > +static int apparmor_getprocattr(struct task_struct *task, const char *lsm, > + char *name, char **value) > { > int error = -ENOENT; > /* released below */ > @@ -479,6 +479,8 @@ static int apparmor_getprocattr(struct task_struct *task, char *name, > > if (strcmp(name, "current") == 0) > profile = aa_get_newest_profile(cxt->profile); > + else if (strcmp(name, "context") == 0) > + profile = aa_get_newest_profile(cxt->profile); > else if (strcmp(name, "prev") == 0 && cxt->previous) > profile = aa_get_newest_profile(cxt->previous); > else if (strcmp(name, "exec") == 0 && cxt->onexec) > @@ -486,8 +488,29 @@ static int apparmor_getprocattr(struct task_struct *task, char *name, > else > error = -EINVAL; > > - if (profile) > - error = aa_getprocattr(profile, value); > + if (profile) { > + if (strcmp(name, "context") == 0) { > + char *vp; > + char *np; > + > + error = aa_getprocattr(profile, &vp); > + if (error > 0) { > + error += 12; > + *value = kzalloc(error, GFP_KERNEL); > + if (*value == NULL) > + error = -ENOMEM; > + else { > + sprintf(*value, "apparmor='%s'", vp); > + np = strchr(*value, '\n'); > + if (np != NULL) { > + np[0] = '\''; > + np[1] = '\0'; > + } > + } > + } > + } else > + error = aa_getprocattr(profile, value); > + } > > aa_put_profile(profile); > put_cred(cred); > @@ -495,8 +518,8 @@ static int apparmor_getprocattr(struct task_struct *task, char *name, > return error; > } > > -static int apparmor_setprocattr(struct task_struct *task, char *name, > - void *value, size_t size) > +static int apparmor_setprocattr(struct task_struct *task, const char *lsm, > + char *name, void *value, size_t size) > { > struct common_audit_data sa; > struct apparmor_audit_data aad = {0,}; > @@ -530,7 +553,7 @@ static int apparmor_setprocattr(struct task_struct *task, char *name, > return -EINVAL; > > arg_size = size - (args - (char *) value); > - if (strcmp(name, "current") == 0) { > + if (strcmp(name, "current") == 0 || strcmp(name, "context") == 0) { > if (strcmp(command, "changehat") == 0) { > error = aa_setprocattr_changehat(args, arg_size, > !AA_DO_TEST); > @@ -552,7 +575,10 @@ static int apparmor_setprocattr(struct task_struct *task, char *name, > else > goto fail; > } else > - /* only support the "current" and "exec" process attributes */ > + /* > + * only support the "context", "current" and > + * "exec" process attributes > + */ > return -EINVAL; > > if (!error) > @@ -880,7 +906,8 @@ static int __init apparmor_init(void) > aa_free_root_ns(); > goto alloc_out; > } > - security_add_hooks(apparmor_hooks, ARRAY_SIZE(apparmor_hooks)); > + security_add_hooks(apparmor_hooks, ARRAY_SIZE(apparmor_hooks), > + "apparmor"); > > /* Report that AppArmor successfully initialized */ > apparmor_initialized = 1; > diff --git a/security/commoncap.c b/security/commoncap.c > index e7fadde..d101734 100644 > --- a/security/commoncap.c > +++ b/security/commoncap.c > @@ -1090,7 +1090,8 @@ struct security_hook_list capability_hooks[] = { > > void __init capability_add_hooks(void) > { > - security_add_hooks(capability_hooks, ARRAY_SIZE(capability_hooks)); > + security_add_hooks(capability_hooks, ARRAY_SIZE(capability_hooks), > + "capability"); > } > > #endif /* CONFIG_SECURITY */ > diff --git a/security/inode.c b/security/inode.c > index 28414b0..235ad82 100644 > --- a/security/inode.c > +++ b/security/inode.c > @@ -20,6 +20,7 @@ > #include <linux/init.h> > #include <linux/namei.h> > #include <linux/security.h> > +#include <linux/lsm_hooks.h> > #include <linux/magic.h> > > static struct vfsmount *mount; > @@ -208,6 +209,21 @@ void securityfs_remove(struct dentry *dentry) > } > EXPORT_SYMBOL_GPL(securityfs_remove); > > +#ifdef CONFIG_SECURITY > +static struct dentry *lsm_dentry; > +static ssize_t lsm_read(struct file *filp, char __user *buf, size_t count, > + loff_t *ppos) > +{ > + return simple_read_from_buffer(buf, count, ppos, lsm_names, > + strlen(lsm_names)); > +} > + > +static const struct file_operations lsm_ops = { > + .read = lsm_read, > + .llseek = generic_file_llseek, > +}; > +#endif > + > static int __init securityfs_init(void) > { > int retval; > @@ -217,9 +233,15 @@ static int __init securityfs_init(void) > return retval; > > retval = register_filesystem(&fs_type); > - if (retval) > + if (retval) { > sysfs_remove_mount_point(kernel_kobj, "security"); > - return retval; > + return retval; > + } > +#ifdef CONFIG_SECURITY > + lsm_dentry = securityfs_create_file("lsm", S_IRUGO, NULL, NULL, > + &lsm_ops); > +#endif > + return 0; > } > > core_initcall(securityfs_init); > diff --git a/security/security.c b/security/security.c > index 7095693..6ed098e 100644 > --- a/security/security.c > +++ b/security/security.c > @@ -32,6 +32,7 @@ > /* Maximum number of letters for an LSM name string */ > #define SECURITY_NAME_MAX 10 > > +char *lsm_names; > /* Boot-time LSM user choice */ > static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] = > CONFIG_DEFAULT_SECURITY; > @@ -78,6 +79,23 @@ static int __init choose_lsm(char *str) > } > __setup("security=", choose_lsm); > > +static int lsm_append(char *new, char **result) > +{ > + char *cp; > + > + if (*result == NULL) { > + *result = kstrdup(new, GFP_KERNEL); > + } else { > + cp = kzalloc(strlen(*result) + strlen(new) + 2, GFP_KERNEL); > + if (cp == NULL) > + return -ENOMEM; > + sprintf(cp, "%s,%s", *result, new); > + kfree(*result); > + *result = cp; > + } > + return 0; > +} > + > /** > * security_module_enable - Load given security module on boot ? > * @module: the name of the module > @@ -97,6 +115,26 @@ int __init security_module_enable(const char *module) > return !strcmp(module, chosen_lsm); > } > > +/** > + * security_add_hooks - Add a modules hooks to the hook lists. > + * @hooks - the hooks to add > + * @count - the number of hooks to add > + * > + * Each LSM has to register its hooks with the infrastructure. > + */ > +void __init security_add_hooks(struct security_hook_list *hooks, int count, > + char *lsm) > +{ > + int i; > + > + for (i = 0; i < count; i++) { > + hooks[i].lsm = lsm; > + list_add_tail_rcu(&hooks[i].list, hooks[i].head); > + } > + if (lsm_append(lsm, &lsm_names) < 0) > + panic("%s - Cannot get early memory.\n", __func__); > +} > + > /* > * Hook list operation macros. > * > @@ -1123,14 +1161,134 @@ void security_d_instantiate(struct dentry *dentry, struct inode *inode) > } > EXPORT_SYMBOL(security_d_instantiate); > > -int security_getprocattr(struct task_struct *p, char *name, char **value) > +int security_getprocattr(struct task_struct *p, const char *lsm, char *name, > + char **value) > { > - return call_int_hook(getprocattr, -EINVAL, p, name, value); > + struct security_hook_list *hp; > + char *vp; > + char *cp = NULL; > + int rc = -EINVAL; > + int trc; > + > + /* > + * "context" requires work here in addition to what > + * the modules provide. > + */ > + if (strcmp(name, "context") == 0) { > + *value = NULL; > + list_for_each_entry(hp, > + &security_hook_heads.getprocattr, list) { > + if (lsm != NULL && strcmp(lsm, hp->lsm)) > + continue; > + trc = hp->hook.getprocattr(p, lsm, "context", &vp); > + if (trc == -ENOENT) > + continue; > + if (trc <= 0) { > + kfree(*value); > + return trc; > + } > + rc = trc; > + if (*value == NULL) { > + *value = vp; > + } else { > + cp = kzalloc(strlen(*value) + strlen(vp) + 1, > + GFP_KERNEL); > + if (cp == NULL) { > + kfree(*value); > + kfree(vp); > + return -ENOMEM; > + } > + sprintf(cp, "%s%s", *value, vp); > + kfree(*value); > + kfree(vp); > + *value = cp; > + } > + } > + if (rc > 0) > + return strlen(*value); > + return rc; > + } > + > + list_for_each_entry(hp, &security_hook_heads.getprocattr, list) { > + if (lsm != NULL && strcmp(lsm, hp->lsm)) > + continue; > + rc = hp->hook.getprocattr(p, lsm, name, value); > + if (rc != -ENOENT) > + return rc; > + } > + return -EINVAL; > } > > -int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size) > +int security_setprocattr(struct task_struct *p, const char *lsm, char *name, > + void *value, size_t size) > { > - return call_int_hook(setprocattr, -EINVAL, p, name, value, size); > + struct security_hook_list *hp; > + int rc = -EINVAL; > + char *local; > + char *cp; > + int slen; > + int failed = 0; > + > + /* > + * "context" is handled directly here. > + */ > + if (strcmp(name, "context") == 0) { > + /* > + * First verify that the input is acceptable. > + * lsm1='v1'lsm2='v2'lsm3='v3' > + * > + * A note on the use of strncmp() below. > + * The check is for the substring at the beginning of cp. > + */ > + local = kzalloc(size + 1, GFP_KERNEL); > + memcpy(local, value, size); > + cp = local; > + list_for_each_entry(hp, &security_hook_heads.setprocattr, > + list) { > + if (lsm != NULL && strcmp(lsm, hp->lsm)) > + continue; > + slen = strlen(hp->lsm); > + if (strncmp(cp, hp->lsm, slen)) > + goto free_out; > + cp += slen; > + if (cp[0] != '=' || cp[1] != '\'' || cp[2] == '\'') > + goto free_out; > + for (cp += 2; cp[0] != '\''; cp++) > + if (cp[0] == '\0') > + goto free_out; > + cp++; > + } > + cp = local; > + list_for_each_entry(hp, &security_hook_heads.setprocattr, > + list) { > + if (lsm != NULL && strcmp(lsm, hp->lsm)) > + continue; > + cp += strlen(hp->lsm) + 2; > + for (slen = 0; cp[slen] != '\''; slen++) > + ; > + cp[slen] = '\0'; > + > + rc = hp->hook.setprocattr(p, NULL, "context", cp, slen); > + if (rc < 0) > + failed = rc; > + cp += slen + 1; > + } > + if (failed != 0) > + rc = failed; > + else > + rc = size; > +free_out: > + kfree(local); > + return rc; > + } > + list_for_each_entry(hp, &security_hook_heads.setprocattr, list) { > + if (lsm != NULL && strcmp(lsm, hp->lsm)) > + continue; > + rc = hp->hook.setprocattr(p, lsm, name, value, size); > + if (rc != -ENOENT) > + break; > + } > + return rc; > } > > int security_netlink_send(struct sock *sk, struct sk_buff *skb) > diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c > index a86d537..bb5a809 100644 > --- a/security/selinux/hooks.c > +++ b/security/selinux/hooks.c > @@ -5692,7 +5692,7 @@ static void selinux_d_instantiate(struct dentry *dentry, struct inode *inode) > inode_doinit_with_dentry(inode, dentry); > } > > -static int selinux_getprocattr(struct task_struct *p, > +static int selinux_getprocattr(struct task_struct *p, const char *lsm, > char *name, char **value) > { > const struct task_security_struct *__tsec; > @@ -5711,6 +5711,8 @@ static int selinux_getprocattr(struct task_struct *p, > > if (!strcmp(name, "current")) > sid = __tsec->sid; > + else if (!strcmp(name, "context")) > + sid = __tsec->sid; > else if (!strcmp(name, "prev")) > sid = __tsec->osid; > else if (!strcmp(name, "exec")) > @@ -5728,7 +5730,21 @@ static int selinux_getprocattr(struct task_struct *p, > if (!sid) > return 0; > > - error = security_sid_to_context(sid, value, &len); > + if (strcmp(name, "context")) { > + error = security_sid_to_context(sid, value, &len); > + } else { > + char *vp; > + > + error = security_sid_to_context(sid, &vp, &len); > + if (!error) { > + *value = kzalloc(len + 10, GFP_KERNEL); > + if (*value == NULL) > + error = -ENOMEM; > + else > + sprintf(*value, "selinux='%s'", vp); > + } > + } > + > if (error) > return error; > return len; > @@ -5738,7 +5754,7 @@ invalid: > return -EINVAL; > } > > -static int selinux_setprocattr(struct task_struct *p, > +static int selinux_setprocattr(struct task_struct *p, const char *lsm, > char *name, void *value, size_t size) > { > struct task_security_struct *tsec; > @@ -5768,6 +5784,8 @@ static int selinux_setprocattr(struct task_struct *p, > error = current_has_perm(p, PROCESS__SETSOCKCREATE); > else if (!strcmp(name, "current")) > error = current_has_perm(p, PROCESS__SETCURRENT); > + else if (!strcmp(name, "context")) > + error = current_has_perm(p, PROCESS__SETCURRENT); > else > error = -EINVAL; > if (error) > @@ -5827,7 +5845,7 @@ static int selinux_setprocattr(struct task_struct *p, > tsec->keycreate_sid = sid; > } else if (!strcmp(name, "sockcreate")) { > tsec->sockcreate_sid = sid; > - } else if (!strcmp(name, "current")) { > + } else if (!strcmp(name, "current") || !strcmp(name, "context")) { > error = -EINVAL; > if (sid == 0) > goto abort_change; > @@ -6233,7 +6251,7 @@ static __init int selinux_init(void) > 0, SLAB_PANIC, NULL); > avc_init(); > > - security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks)); > + security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks), "selinux"); > > if (avc_add_callback(selinux_netcache_avc_callback, AVC_CALLBACK_RESET)) > panic("SELinux: Unable to register AVC netcache callback\n"); > diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c > index 6777295..03a297b 100644 > --- a/security/smack/smack_lsm.c > +++ b/security/smack/smack_lsm.c > @@ -3570,22 +3570,28 @@ unlockandout: > * > * Returns the length of the smack label or an error code > */ > -static int smack_getprocattr(struct task_struct *p, char *name, char **value) > +static int smack_getprocattr(struct task_struct *p, const char *lsm, char *name, > + char **value) > { > struct smack_known *skp = smk_of_task_struct(p); > char *cp; > int slen; > > - if (strcmp(name, "current") != 0) > + if (strcmp(name, "current") == 0) { > + cp = kstrdup(skp->smk_known, GFP_KERNEL); > + if (cp == NULL) > + return -ENOMEM; > + } else if (strcmp(name, "context") == 0) { > + slen = strlen(skp->smk_known) + 9; > + cp = kzalloc(slen, GFP_KERNEL); > + if (cp == NULL) > + return -ENOMEM; > + sprintf(cp, "smack='%s'", skp->smk_known); > + } else > return -EINVAL; > > - cp = kstrdup(skp->smk_known, GFP_KERNEL); > - if (cp == NULL) > - return -ENOMEM; > - > - slen = strlen(cp); > *value = cp; > - return slen; > + return strlen(cp); > } > > /** > @@ -3600,7 +3606,7 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value) > * > * Returns the length of the smack label or an error code > */ > -static int smack_setprocattr(struct task_struct *p, char *name, > +static int smack_setprocattr(struct task_struct *p, const char *lsm, char *name, > void *value, size_t size) > { > struct task_smack *tsp = current_security(); > @@ -3622,7 +3628,7 @@ static int smack_setprocattr(struct task_struct *p, char *name, > if (value == NULL || size == 0 || size >= SMK_LONGLABEL) > return -EINVAL; > > - if (strcmp(name, "current") != 0) > + if (strcmp(name, "current") != 0 && strcmp(name, "context") != 0) > return -EINVAL; > > skp = smk_import_entry(value, size); > @@ -4801,7 +4807,7 @@ static __init int smack_init(void) > /* > * Register with LSM > */ > - security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks)); > + security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks), "smack"); > > return 0; > } > diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c > index 75c9987..edc52d6 100644 > --- a/security/tomoyo/tomoyo.c > +++ b/security/tomoyo/tomoyo.c > @@ -542,7 +542,7 @@ static int __init tomoyo_init(void) > if (!security_module_enable("tomoyo")) > return 0; > /* register ourselves with the security framework */ > - security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks)); > + security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks), "tomoyo"); > 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 0309f21..f8ee60e 100644 > --- a/security/yama/yama_lsm.c > +++ b/security/yama/yama_lsm.c > @@ -471,6 +471,6 @@ static inline void yama_init_sysctl(void) { } > void __init yama_add_hooks(void) > { > pr_info("Yama: becoming mindful.\n"); > - security_add_hooks(yama_hooks, ARRAY_SIZE(yama_hooks)); > + security_add_hooks(yama_hooks, ARRAY_SIZE(yama_hooks), "yama"); > yama_init_sysctl(); > } > Excepting the notes above, I'm glad to see this return! I really like the idea of having these interfaces split up into subdirectories. -Kees
diff --git a/Documentation/security/LSM.txt b/Documentation/security/LSM.txt index 3db7e67..125c489 100644 --- a/Documentation/security/LSM.txt +++ b/Documentation/security/LSM.txt @@ -16,11 +16,25 @@ MAC extensions, other extensions can be built using the LSM to provide specific changes to system operation when these tweaks are not available in the core functionality of Linux itself. -Without a specific LSM built into the kernel, the default LSM will be the -Linux capabilities system. Most LSMs choose to extend the capabilities -system, building their checks on top of the defined capability hooks. -For more details on capabilities, see capabilities(7) in the Linux -man-pages project. +The Linux capabilities modules will always be included. For more details +on capabilities, see capabilities(7) in the Linux man-pages project. +This may be followed by any number of "minor" modules and at most one +"major" module. + +A list of the active security modules can be found by reading +/sys/kernel/security/lsm. This is a comma separated list, and +will always include the capability module. The list reflects the +order in which checks are made. The capability module will always +be first, followed by any "minor" modules (e.g. Yama) and then +the one "major" module (e.g. SELinux) if there is one configured. + +Process attributes associated with "major" security modules should +be accessed and maintained using the special files in the module +specific subdirectories in /proc/.../attr. The attributes related +to Smack would be found in /proc/.../attr/smack while the attributes +for SELinux would be in /proc/.../attr/selinux. Using the files +found directly in /proc/.../attr (e.g. current) should be avoided. +These files remain as legacy interfaces. Based on https://lkml.org/lkml/2007/10/26/215, a new LSM is accepted into the kernel when its intent (a description of @@ -31,4 +45,4 @@ that end users and distros can make a more informed decision about which LSMs suit their requirements. For extensive documentation on the available LSM hook interfaces, please -see include/linux/security.h. +see include/linux/lsm_hooks.h. diff --git a/fs/proc/base.c b/fs/proc/base.c index a11eb71..df94f26 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -131,9 +131,13 @@ struct pid_entry { #define REG(NAME, MODE, fops) \ NOD(NAME, (S_IFREG|(MODE)), NULL, &fops, {}) #define ONE(NAME, MODE, show) \ - NOD(NAME, (S_IFREG|(MODE)), \ + NOD(NAME, (S_IFREG|(MODE)), \ NULL, &proc_single_file_operations, \ { .proc_show = show } ) +#define ATTR(LSM, NAME, MODE) \ + NOD(NAME, (S_IFREG|(MODE)), \ + NULL, &proc_pid_attr_operations, \ + { .lsm = LSM }) /* * Count the number of hardlinks for the pid_entry table, excluding the . @@ -2433,7 +2437,7 @@ static ssize_t proc_pid_attr_read(struct file * file, char __user * buf, if (!task) return -ESRCH; - length = security_getprocattr(task, + length = security_getprocattr(task, PROC_I(inode)->op.lsm, (char*)file->f_path.dentry->d_name.name, &p); put_task_struct(task); @@ -2473,7 +2477,7 @@ static ssize_t proc_pid_attr_write(struct file * file, const char __user * buf, if (length < 0) goto out_free; - length = security_setprocattr(task, + length = security_setprocattr(task, PROC_I(inode)->op.lsm, (char*)file->f_path.dentry->d_name.name, page, count); mutex_unlock(&task->signal->cred_guard_mutex); @@ -2491,13 +2495,86 @@ static const struct file_operations proc_pid_attr_operations = { .llseek = generic_file_llseek, }; +#define LSM_DIR_OPS(LSM) \ +static int proc_##LSM##_attr_dir_iterate(struct file *filp, \ + struct dir_context *ctx) \ +{ \ + return proc_pident_readdir(filp, ctx, \ + LSM##_attr_dir_stuff, \ + ARRAY_SIZE(LSM##_attr_dir_stuff)); \ +} \ +\ +static const struct file_operations proc_##LSM##_attr_dir_ops = { \ + .read = generic_read_dir, \ + .iterate = proc_##LSM##_attr_dir_iterate, \ + .llseek = default_llseek, \ +}; \ +\ +static struct dentry *proc_##LSM##_attr_dir_lookup(struct inode *dir, \ + struct dentry *dentry, unsigned int flags) \ +{ \ + return proc_pident_lookup(dir, dentry, \ + LSM##_attr_dir_stuff, \ + ARRAY_SIZE(LSM##_attr_dir_stuff)); \ +} \ +\ +static const struct inode_operations proc_##LSM##_attr_dir_inode_ops = { \ + .lookup = proc_##LSM##_attr_dir_lookup, \ + .getattr = pid_getattr, \ + .setattr = proc_setattr, \ +} + +#ifdef CONFIG_SECURITY_SELINUX +static const struct pid_entry selinux_attr_dir_stuff[] = { + ATTR("selinux", "current", S_IRUGO|S_IWUGO), + ATTR("selinux", "prev", S_IRUGO), + ATTR("selinux", "exec", S_IRUGO|S_IWUGO), + ATTR("selinux", "fscreate", S_IRUGO|S_IWUGO), + ATTR("selinux", "keycreate", S_IRUGO|S_IWUGO), + ATTR("selinux", "sockcreate", S_IRUGO|S_IWUGO), + ATTR("selinux", "context", S_IRUGO|S_IWUGO), +}; +LSM_DIR_OPS(selinux); +#endif + +#ifdef CONFIG_SECURITY_SMACK +static const struct pid_entry smack_attr_dir_stuff[] = { + ATTR("smack", "current", S_IRUGO|S_IWUGO), + ATTR("smack", "context", S_IRUGO|S_IWUGO), +}; +LSM_DIR_OPS(smack); +#endif + +#ifdef CONFIG_SECURITY_APPARMOR +static const struct pid_entry apparmor_attr_dir_stuff[] = { + ATTR("apparmor", "current", S_IRUGO|S_IWUGO), + ATTR("apparmor", "prev", S_IRUGO), + ATTR("apparmor", "exec", S_IRUGO|S_IWUGO), + ATTR("apparmor", "context", S_IRUGO|S_IWUGO), +}; +LSM_DIR_OPS(apparmor); +#endif + static const struct pid_entry attr_dir_stuff[] = { - REG("current", S_IRUGO|S_IWUGO, proc_pid_attr_operations), - REG("prev", S_IRUGO, proc_pid_attr_operations), - REG("exec", S_IRUGO|S_IWUGO, proc_pid_attr_operations), - REG("fscreate", S_IRUGO|S_IWUGO, proc_pid_attr_operations), - REG("keycreate", S_IRUGO|S_IWUGO, proc_pid_attr_operations), - REG("sockcreate", S_IRUGO|S_IWUGO, proc_pid_attr_operations), + ATTR(NULL, "current", S_IRUGO|S_IWUGO), + ATTR(NULL, "prev", S_IRUGO), + ATTR(NULL, "exec", S_IRUGO|S_IWUGO), + ATTR(NULL, "fscreate", S_IRUGO|S_IWUGO), + ATTR(NULL, "keycreate", S_IRUGO|S_IWUGO), + ATTR(NULL, "sockcreate", S_IRUGO|S_IWUGO), + ATTR(NULL, "context", S_IRUGO|S_IWUGO), +#ifdef CONFIG_SECURITY_SELINUX + DIR("selinux", S_IRUGO|S_IXUGO, + proc_selinux_attr_dir_inode_ops, proc_selinux_attr_dir_ops), +#endif +#ifdef CONFIG_SECURITY_SMACK + DIR("smack", S_IRUGO|S_IXUGO, + proc_smack_attr_dir_inode_ops, proc_smack_attr_dir_ops), +#endif +#ifdef CONFIG_SECURITY_APPARMOR + DIR("apparmor", S_IRUGO|S_IXUGO, + proc_apparmor_attr_dir_inode_ops, proc_apparmor_attr_dir_ops), +#endif }; static int proc_attr_dir_readdir(struct file *file, struct dir_context *ctx) diff --git a/fs/proc/internal.h b/fs/proc/internal.h index aa27810..b607cd5 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h @@ -56,6 +56,7 @@ union proc_op { int (*proc_show)(struct seq_file *m, struct pid_namespace *ns, struct pid *pid, struct task_struct *task); + const char *lsm; }; struct proc_inode { diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 7ae3976..5b796db 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -1514,9 +1514,10 @@ union security_list_options { void (*d_instantiate)(struct dentry *dentry, struct inode *inode); - int (*getprocattr)(struct task_struct *p, char *name, char **value); - int (*setprocattr)(struct task_struct *p, char *name, void *value, - size_t size); + int (*getprocattr)(struct task_struct *p, const char *lsm, char *name, + char **value); + int (*setprocattr)(struct task_struct *p, const char *lsm, char *name, + void *value, size_t size); int (*ismaclabel)(const char *name); int (*secid_to_secctx)(u32 secid, char **secdata, u32 *seclen); int (*secctx_to_secid)(const char *secdata, u32 seclen, u32 *secid); @@ -1841,6 +1842,7 @@ struct security_hook_list { struct list_head list; struct list_head *head; union security_list_options hook; + char *lsm; }; /* @@ -1853,15 +1855,10 @@ struct security_hook_list { { .head = &security_hook_heads.HEAD, .hook = { .HEAD = HOOK } } extern struct security_hook_heads security_hook_heads; +extern char *lsm_names; -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); -} +extern void security_add_hooks(struct security_hook_list *hooks, int count, + char *lsm); #ifdef CONFIG_SECURITY_SELINUX_DISABLE /* diff --git a/include/linux/security.h b/include/linux/security.h index 14df373..383fcb0 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -355,8 +355,10 @@ int security_sem_semctl(struct sem_array *sma, int cmd); int security_sem_semop(struct sem_array *sma, struct sembuf *sops, unsigned nsops, int alter); void security_d_instantiate(struct dentry *dentry, struct inode *inode); -int security_getprocattr(struct task_struct *p, char *name, char **value); -int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size); +int security_getprocattr(struct task_struct *p, const char *lsm, char *name, + char **value); +int security_setprocattr(struct task_struct *p, const char *lsm, char *name, + void *value, size_t size); int security_netlink_send(struct sock *sk, struct sk_buff *skb); int security_ismaclabel(const char *name); int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen); @@ -1075,15 +1077,18 @@ static inline int security_sem_semop(struct sem_array *sma, return 0; } -static inline void security_d_instantiate(struct dentry *dentry, struct inode *inode) +static inline void security_d_instantiate(struct dentry *dentry, + struct inode *inode) { } -static inline int security_getprocattr(struct task_struct *p, char *name, char **value) +static inline int security_getprocattr(struct task_struct *p, const char *lsm, + char *name, char **value) { return -EINVAL; } -static inline int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size) +static inline int security_setprocattr(struct task_struct *p, const char *lsm, + char *name, void *value, size_t size) { return -EINVAL; } diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 2660fbc..7e54292 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -468,8 +468,8 @@ static int apparmor_file_mprotect(struct vm_area_struct *vma, !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0); } -static int apparmor_getprocattr(struct task_struct *task, char *name, - char **value) +static int apparmor_getprocattr(struct task_struct *task, const char *lsm, + char *name, char **value) { int error = -ENOENT; /* released below */ @@ -479,6 +479,8 @@ static int apparmor_getprocattr(struct task_struct *task, char *name, if (strcmp(name, "current") == 0) profile = aa_get_newest_profile(cxt->profile); + else if (strcmp(name, "context") == 0) + profile = aa_get_newest_profile(cxt->profile); else if (strcmp(name, "prev") == 0 && cxt->previous) profile = aa_get_newest_profile(cxt->previous); else if (strcmp(name, "exec") == 0 && cxt->onexec) @@ -486,8 +488,29 @@ static int apparmor_getprocattr(struct task_struct *task, char *name, else error = -EINVAL; - if (profile) - error = aa_getprocattr(profile, value); + if (profile) { + if (strcmp(name, "context") == 0) { + char *vp; + char *np; + + error = aa_getprocattr(profile, &vp); + if (error > 0) { + error += 12; + *value = kzalloc(error, GFP_KERNEL); + if (*value == NULL) + error = -ENOMEM; + else { + sprintf(*value, "apparmor='%s'", vp); + np = strchr(*value, '\n'); + if (np != NULL) { + np[0] = '\''; + np[1] = '\0'; + } + } + } + } else + error = aa_getprocattr(profile, value); + } aa_put_profile(profile); put_cred(cred); @@ -495,8 +518,8 @@ static int apparmor_getprocattr(struct task_struct *task, char *name, return error; } -static int apparmor_setprocattr(struct task_struct *task, char *name, - void *value, size_t size) +static int apparmor_setprocattr(struct task_struct *task, const char *lsm, + char *name, void *value, size_t size) { struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; @@ -530,7 +553,7 @@ static int apparmor_setprocattr(struct task_struct *task, char *name, return -EINVAL; arg_size = size - (args - (char *) value); - if (strcmp(name, "current") == 0) { + if (strcmp(name, "current") == 0 || strcmp(name, "context") == 0) { if (strcmp(command, "changehat") == 0) { error = aa_setprocattr_changehat(args, arg_size, !AA_DO_TEST); @@ -552,7 +575,10 @@ static int apparmor_setprocattr(struct task_struct *task, char *name, else goto fail; } else - /* only support the "current" and "exec" process attributes */ + /* + * only support the "context", "current" and + * "exec" process attributes + */ return -EINVAL; if (!error) @@ -880,7 +906,8 @@ static int __init apparmor_init(void) aa_free_root_ns(); goto alloc_out; } - security_add_hooks(apparmor_hooks, ARRAY_SIZE(apparmor_hooks)); + security_add_hooks(apparmor_hooks, ARRAY_SIZE(apparmor_hooks), + "apparmor"); /* Report that AppArmor successfully initialized */ apparmor_initialized = 1; diff --git a/security/commoncap.c b/security/commoncap.c index e7fadde..d101734 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -1090,7 +1090,8 @@ struct security_hook_list capability_hooks[] = { void __init capability_add_hooks(void) { - security_add_hooks(capability_hooks, ARRAY_SIZE(capability_hooks)); + security_add_hooks(capability_hooks, ARRAY_SIZE(capability_hooks), + "capability"); } #endif /* CONFIG_SECURITY */ diff --git a/security/inode.c b/security/inode.c index 28414b0..235ad82 100644 --- a/security/inode.c +++ b/security/inode.c @@ -20,6 +20,7 @@ #include <linux/init.h> #include <linux/namei.h> #include <linux/security.h> +#include <linux/lsm_hooks.h> #include <linux/magic.h> static struct vfsmount *mount; @@ -208,6 +209,21 @@ void securityfs_remove(struct dentry *dentry) } EXPORT_SYMBOL_GPL(securityfs_remove); +#ifdef CONFIG_SECURITY +static struct dentry *lsm_dentry; +static ssize_t lsm_read(struct file *filp, char __user *buf, size_t count, + loff_t *ppos) +{ + return simple_read_from_buffer(buf, count, ppos, lsm_names, + strlen(lsm_names)); +} + +static const struct file_operations lsm_ops = { + .read = lsm_read, + .llseek = generic_file_llseek, +}; +#endif + static int __init securityfs_init(void) { int retval; @@ -217,9 +233,15 @@ static int __init securityfs_init(void) return retval; retval = register_filesystem(&fs_type); - if (retval) + if (retval) { sysfs_remove_mount_point(kernel_kobj, "security"); - return retval; + return retval; + } +#ifdef CONFIG_SECURITY + lsm_dentry = securityfs_create_file("lsm", S_IRUGO, NULL, NULL, + &lsm_ops); +#endif + return 0; } core_initcall(securityfs_init); diff --git a/security/security.c b/security/security.c index 7095693..6ed098e 100644 --- a/security/security.c +++ b/security/security.c @@ -32,6 +32,7 @@ /* Maximum number of letters for an LSM name string */ #define SECURITY_NAME_MAX 10 +char *lsm_names; /* Boot-time LSM user choice */ static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] = CONFIG_DEFAULT_SECURITY; @@ -78,6 +79,23 @@ static int __init choose_lsm(char *str) } __setup("security=", choose_lsm); +static int lsm_append(char *new, char **result) +{ + char *cp; + + if (*result == NULL) { + *result = kstrdup(new, GFP_KERNEL); + } else { + cp = kzalloc(strlen(*result) + strlen(new) + 2, GFP_KERNEL); + if (cp == NULL) + return -ENOMEM; + sprintf(cp, "%s,%s", *result, new); + kfree(*result); + *result = cp; + } + return 0; +} + /** * security_module_enable - Load given security module on boot ? * @module: the name of the module @@ -97,6 +115,26 @@ int __init security_module_enable(const char *module) return !strcmp(module, chosen_lsm); } +/** + * security_add_hooks - Add a modules hooks to the hook lists. + * @hooks - the hooks to add + * @count - the number of hooks to add + * + * Each LSM has to register its hooks with the infrastructure. + */ +void __init security_add_hooks(struct security_hook_list *hooks, int count, + char *lsm) +{ + int i; + + for (i = 0; i < count; i++) { + hooks[i].lsm = lsm; + list_add_tail_rcu(&hooks[i].list, hooks[i].head); + } + if (lsm_append(lsm, &lsm_names) < 0) + panic("%s - Cannot get early memory.\n", __func__); +} + /* * Hook list operation macros. * @@ -1123,14 +1161,134 @@ void security_d_instantiate(struct dentry *dentry, struct inode *inode) } EXPORT_SYMBOL(security_d_instantiate); -int security_getprocattr(struct task_struct *p, char *name, char **value) +int security_getprocattr(struct task_struct *p, const char *lsm, char *name, + char **value) { - return call_int_hook(getprocattr, -EINVAL, p, name, value); + struct security_hook_list *hp; + char *vp; + char *cp = NULL; + int rc = -EINVAL; + int trc; + + /* + * "context" requires work here in addition to what + * the modules provide. + */ + if (strcmp(name, "context") == 0) { + *value = NULL; + list_for_each_entry(hp, + &security_hook_heads.getprocattr, list) { + if (lsm != NULL && strcmp(lsm, hp->lsm)) + continue; + trc = hp->hook.getprocattr(p, lsm, "context", &vp); + if (trc == -ENOENT) + continue; + if (trc <= 0) { + kfree(*value); + return trc; + } + rc = trc; + if (*value == NULL) { + *value = vp; + } else { + cp = kzalloc(strlen(*value) + strlen(vp) + 1, + GFP_KERNEL); + if (cp == NULL) { + kfree(*value); + kfree(vp); + return -ENOMEM; + } + sprintf(cp, "%s%s", *value, vp); + kfree(*value); + kfree(vp); + *value = cp; + } + } + if (rc > 0) + return strlen(*value); + return rc; + } + + list_for_each_entry(hp, &security_hook_heads.getprocattr, list) { + if (lsm != NULL && strcmp(lsm, hp->lsm)) + continue; + rc = hp->hook.getprocattr(p, lsm, name, value); + if (rc != -ENOENT) + return rc; + } + return -EINVAL; } -int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size) +int security_setprocattr(struct task_struct *p, const char *lsm, char *name, + void *value, size_t size) { - return call_int_hook(setprocattr, -EINVAL, p, name, value, size); + struct security_hook_list *hp; + int rc = -EINVAL; + char *local; + char *cp; + int slen; + int failed = 0; + + /* + * "context" is handled directly here. + */ + if (strcmp(name, "context") == 0) { + /* + * First verify that the input is acceptable. + * lsm1='v1'lsm2='v2'lsm3='v3' + * + * A note on the use of strncmp() below. + * The check is for the substring at the beginning of cp. + */ + local = kzalloc(size + 1, GFP_KERNEL); + memcpy(local, value, size); + cp = local; + list_for_each_entry(hp, &security_hook_heads.setprocattr, + list) { + if (lsm != NULL && strcmp(lsm, hp->lsm)) + continue; + slen = strlen(hp->lsm); + if (strncmp(cp, hp->lsm, slen)) + goto free_out; + cp += slen; + if (cp[0] != '=' || cp[1] != '\'' || cp[2] == '\'') + goto free_out; + for (cp += 2; cp[0] != '\''; cp++) + if (cp[0] == '\0') + goto free_out; + cp++; + } + cp = local; + list_for_each_entry(hp, &security_hook_heads.setprocattr, + list) { + if (lsm != NULL && strcmp(lsm, hp->lsm)) + continue; + cp += strlen(hp->lsm) + 2; + for (slen = 0; cp[slen] != '\''; slen++) + ; + cp[slen] = '\0'; + + rc = hp->hook.setprocattr(p, NULL, "context", cp, slen); + if (rc < 0) + failed = rc; + cp += slen + 1; + } + if (failed != 0) + rc = failed; + else + rc = size; +free_out: + kfree(local); + return rc; + } + list_for_each_entry(hp, &security_hook_heads.setprocattr, list) { + if (lsm != NULL && strcmp(lsm, hp->lsm)) + continue; + rc = hp->hook.setprocattr(p, lsm, name, value, size); + if (rc != -ENOENT) + break; + } + return rc; } int security_netlink_send(struct sock *sk, struct sk_buff *skb) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index a86d537..bb5a809 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -5692,7 +5692,7 @@ static void selinux_d_instantiate(struct dentry *dentry, struct inode *inode) inode_doinit_with_dentry(inode, dentry); } -static int selinux_getprocattr(struct task_struct *p, +static int selinux_getprocattr(struct task_struct *p, const char *lsm, char *name, char **value) { const struct task_security_struct *__tsec; @@ -5711,6 +5711,8 @@ static int selinux_getprocattr(struct task_struct *p, if (!strcmp(name, "current")) sid = __tsec->sid; + else if (!strcmp(name, "context")) + sid = __tsec->sid; else if (!strcmp(name, "prev")) sid = __tsec->osid; else if (!strcmp(name, "exec")) @@ -5728,7 +5730,21 @@ static int selinux_getprocattr(struct task_struct *p, if (!sid) return 0; - error = security_sid_to_context(sid, value, &len); + if (strcmp(name, "context")) { + error = security_sid_to_context(sid, value, &len); + } else { + char *vp; + + error = security_sid_to_context(sid, &vp, &len); + if (!error) { + *value = kzalloc(len + 10, GFP_KERNEL); + if (*value == NULL) + error = -ENOMEM; + else + sprintf(*value, "selinux='%s'", vp); + } + } + if (error) return error; return len; @@ -5738,7 +5754,7 @@ invalid: return -EINVAL; } -static int selinux_setprocattr(struct task_struct *p, +static int selinux_setprocattr(struct task_struct *p, const char *lsm, char *name, void *value, size_t size) { struct task_security_struct *tsec; @@ -5768,6 +5784,8 @@ static int selinux_setprocattr(struct task_struct *p, error = current_has_perm(p, PROCESS__SETSOCKCREATE); else if (!strcmp(name, "current")) error = current_has_perm(p, PROCESS__SETCURRENT); + else if (!strcmp(name, "context")) + error = current_has_perm(p, PROCESS__SETCURRENT); else error = -EINVAL; if (error) @@ -5827,7 +5845,7 @@ static int selinux_setprocattr(struct task_struct *p, tsec->keycreate_sid = sid; } else if (!strcmp(name, "sockcreate")) { tsec->sockcreate_sid = sid; - } else if (!strcmp(name, "current")) { + } else if (!strcmp(name, "current") || !strcmp(name, "context")) { error = -EINVAL; if (sid == 0) goto abort_change; @@ -6233,7 +6251,7 @@ static __init int selinux_init(void) 0, SLAB_PANIC, NULL); avc_init(); - security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks)); + security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks), "selinux"); if (avc_add_callback(selinux_netcache_avc_callback, AVC_CALLBACK_RESET)) panic("SELinux: Unable to register AVC netcache callback\n"); diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 6777295..03a297b 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -3570,22 +3570,28 @@ unlockandout: * * Returns the length of the smack label or an error code */ -static int smack_getprocattr(struct task_struct *p, char *name, char **value) +static int smack_getprocattr(struct task_struct *p, const char *lsm, char *name, + char **value) { struct smack_known *skp = smk_of_task_struct(p); char *cp; int slen; - if (strcmp(name, "current") != 0) + if (strcmp(name, "current") == 0) { + cp = kstrdup(skp->smk_known, GFP_KERNEL); + if (cp == NULL) + return -ENOMEM; + } else if (strcmp(name, "context") == 0) { + slen = strlen(skp->smk_known) + 9; + cp = kzalloc(slen, GFP_KERNEL); + if (cp == NULL) + return -ENOMEM; + sprintf(cp, "smack='%s'", skp->smk_known); + } else return -EINVAL; - cp = kstrdup(skp->smk_known, GFP_KERNEL); - if (cp == NULL) - return -ENOMEM; - - slen = strlen(cp); *value = cp; - return slen; + return strlen(cp); } /** @@ -3600,7 +3606,7 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value) * * Returns the length of the smack label or an error code */ -static int smack_setprocattr(struct task_struct *p, char *name, +static int smack_setprocattr(struct task_struct *p, const char *lsm, char *name, void *value, size_t size) { struct task_smack *tsp = current_security(); @@ -3622,7 +3628,7 @@ static int smack_setprocattr(struct task_struct *p, char *name, if (value == NULL || size == 0 || size >= SMK_LONGLABEL) return -EINVAL; - if (strcmp(name, "current") != 0) + if (strcmp(name, "current") != 0 && strcmp(name, "context") != 0) return -EINVAL; skp = smk_import_entry(value, size); @@ -4801,7 +4807,7 @@ static __init int smack_init(void) /* * Register with LSM */ - security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks)); + security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks), "smack"); return 0; } diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index 75c9987..edc52d6 100644 --- a/security/tomoyo/tomoyo.c +++ b/security/tomoyo/tomoyo.c @@ -542,7 +542,7 @@ static int __init tomoyo_init(void) if (!security_module_enable("tomoyo")) return 0; /* register ourselves with the security framework */ - security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks)); + security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks), "tomoyo"); 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 0309f21..f8ee60e 100644 --- a/security/yama/yama_lsm.c +++ b/security/yama/yama_lsm.c @@ -471,6 +471,6 @@ static inline void yama_init_sysctl(void) { } void __init yama_add_hooks(void) { pr_info("Yama: becoming mindful.\n"); - security_add_hooks(yama_hooks, ARRAY_SIZE(yama_hooks)); + security_add_hooks(yama_hooks, ARRAY_SIZE(yama_hooks), "yama"); yama_init_sysctl(); }
Subject: [PATCH] LSM: module hierarchy in /proc/.../attr Back in 2007 I made what turned out to be a rather serious mistake in the implementation of the Smack security module. The SELinux module used an interface in /proc to manipulate the security context on processes. Rather than use a similar interface, I used the same interface. The AppArmor team did likewise. Now /proc/.../attr/current will tell you the security "context" of the process, but it will be different depending on the security module you're using. That hasn't been a problem to date, as you can only have one module that supports process attributes at a time. We are coming up on a change to that, where multiple modules with process attributes can be supported. (Not included here) This patch provides a subdirectory in /proc/.../attr for each of the security modules that use the LSM hooks getprocattr() and setprocattr(). Each of the interfaces used by a module are presented in the subdirectory. The old interfaces remain and work the same as before. User space code can begin migrating to the subdirectory interfaces in anticipation of the time when what comes from /proc/self/attr/current might not be what a runtime wants. Also looking ahead, the attr directory and each of its subdirectories provides a "context" interface. The data provided is the same as the "current" interface in each of the subdirectories, except that it is formatted to identify the module it comes from. The format is: lsmname='context-value' When multiple concurrent modules are supported the /proc/.../attr/context interface will include the data for all of the active modules. Finally, I got tired of having to find indirect ways to determine what security modules are active on a system. I have added /sys/kernel/security/lsm, which contains a comma separated list of the active security modules. No more groping around in /proc/filesystems, which won't help if the module doesn't support its own filesystem. The original implementation is by Kees Cook. The code has been changed a bit to reflect changes in the direction of the multiple concurrent module work, to be independent of it, and to bring it up to date with the current tree. Signed-off-by: Casey Schaufler <casey@schaufler-ca.com> --- Documentation/security/LSM.txt | 26 +++++-- fs/proc/base.c | 95 ++++++++++++++++++++--- fs/proc/internal.h | 1 + include/linux/lsm_hooks.h | 19 ++--- include/linux/security.h | 15 ++-- security/apparmor/lsm.c | 45 ++++++++--- security/commoncap.c | 3 +- security/inode.c | 26 ++++++- security/security.c | 166 ++++++++++++++++++++++++++++++++++++++++- security/selinux/hooks.c | 28 +++++-- security/smack/smack_lsm.c | 28 ++++--- security/tomoyo/tomoyo.c | 2 +- security/yama/yama_lsm.c | 2 +- 13 files changed, 391 insertions(+), 65 deletions(-) -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html