LSM: Multiple concurrent LSMs
Subsystems (6)
Casey Schaufler
maintainerPATCH v5 1/1 LSM: Multiple concurrent LSMs
Subject: [PATCH v5] LSM: Multiple concurrent LSMs
Here's my post vacation update. I am considering moving away from the array of LSM vectors to lists within each hook. The primary advantage would be that multiple sparse LSMs would spend less time checking on NULL hooks. Thoughts?
It would also make security_reset_ops
considerably less pleasant, but that is code that gets ifdefed out except for one configuration of one LSM.
Version 5 of the patch addresses:
1. Tetsuo Handa pointed out that handling of failures alloc hooks was still not correct in v4. The code now only calls the free hook for LSMs that have had their alloc hook successfully called. The alloc hooks have been de-macro-ized, too.
2. Removed the Yama special case. It is no longer necessary.
Version 4 of the patch addresses:
1. Removed the conditional CONFIG_SECURITY_COMPOSER. This removes the option for LSMs to opt out of the multiple concurrent LSM mechanism. It also prevents breaking the change into meaningful subsets.
2. Pulled the trivial hooks out of the capability LSM as the security_hook calls make calling them unnecessary.
3. Removed register_security as it has nothing to do.
4. Changed the way security_hook calls the LSM specific hooks so that the capability hook is only called if no other LSM has supplied a hook, rather than looking to see if there is a capability hook.
5. Removed security_fixup_ops. The capability LSM only contains "real" hooks. Removed security_fixdown_ops as well, for the same reason.
6. Simplified security_reset_ops and made it reasonably safe.
Version 3 of the patchset addresses:
1. Improvements to allocations in lsm_read for the securityfs lsm interface.
2. A repair to the ordering of an NULL check in security_module_enable.
3. Sharing of the inode_getsecurity, inode_setsecurity and inode_listsecurity hooks, even thougth there is no current contention for them.
Version 2 of the patchset addresses:
1. The lsm_set functions did not handle error cases. The on demand allocation has been replaced with a more robust scheme within the security_alloc hooks. This requires more change in security/security.c but has the advantage of being more likely to be correct.
2. reset_security_ops() didn't work, causing a panic on invocation. That's been fixed. The change is somewhat invasive relative to the functionality.
3. Add registration time detection of LSMs that are going to use hooks that can't (currently) be shared.
Provide a mechanism for using multiple LSMs on the same running kernel. This mechanism is not backward compatible. All LSMs must conform to it.
As David Howells suggested some time back, making Smack and SELinux available at that same time has proven quite a challenge. That work has been deferred and that particular configuration disallowed.
Performance measurement is in the early stages. The Smack tests run within the noise with AppArmor, TOMOYO and Yama enabled in addition to Smack.
The Smack LSM behavior has been tested. AppArmor, TOMOYO, Yama and SELinux have been shown to boot, but have not been functionally tested beyond the lack of obvious error messages and complaints from kernel debugging facilities. The kernels have been tested with Ubuntu 12.04 and Fedora 17.
---
include/linux/lsm.h | 192 +++++ include/linux/security.h | 15 +- security/Kconfig | 75 +- security/apparmor/context.c | 10 +- security/apparmor/domain.c | 8 +- security/apparmor/include/context.h | 13 +- security/apparmor/lsm.c | 45 +- security/capability.c | 1056 ++------------------------ security/inode.c | 49 +- security/security.c | 1399 ++++++++++++++++++++++++++++------- security/selinux/hooks.c | 355 +++++---- security/selinux/include/objsec.h | 2 + security/selinux/include/xfrm.h | 2 +- security/selinux/netlabel.c | 13 +- security/selinux/selinuxfs.c | 6 +- security/selinux/xfrm.c | 9 +- security/smack/Kconfig | 1 - security/smack/smack.h | 14 +- security/smack/smack_access.c | 2 +- security/smack/smack_lsm.c | 331 +++++---- security/smack/smackfs.c | 92 ++- security/tomoyo/common.h | 6 +- security/tomoyo/domain.c | 2 +- security/tomoyo/securityfs_if.c | 9 +- security/tomoyo/tomoyo.c | 41 +- security/yama/Kconfig | 7 - security/yama/yama_lsm.c | 9 - 27 files changed, 2048 insertions(+), 1715 deletions(-)
include/linux/lsm.h new file +192 -0
@@ -0,0 +1,192 @@ +/* +* +* Copyright (C) 2012 Casey Schaufler <[email protected]> +* Copyright (C) 2012 Intel Corporation +* +* 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, version 2. +* +* Author: +* Casey Schaufler <[email protected]> +* +*/ +#ifndef _LINUX_LSM_H +#define _LINUX_LSM_H + +#include <linux/cred.h> +#include <linux/fs.h> +#include <linux/msg.h> +#include <linux/key.h> +#include <net/sock.h> + +/* +* Maximum number of LSMs that can be used at a time. +*/ +#define COMPOSER_MAX CONFIG_SECURITY_COMPOSER_MAX +#include <linux/security.h> + +/* +* Just a set of slots for each LSM to keep its blob in. +*/ +struct lsm_blob { +int lsm_setcount;/* Number of blobs set */ +void *lsm_blobs[COMPOSER_MAX ];/* LSM specific blobs */ +}; + +static inline struct lsm_blob *lsm_alloc_blob (gfp_t gfp) +{ +return kzalloc (sizeof (struct lsm_blob ), gfp); +} + +static inline void *lsm_get_blob (const struct lsm_blob *bp,const int lsm) +{ +if (bp ==NULL ) +return NULL ; +return bp->lsm_blobs[lsm]; +} + +static inline void lsm_set_blob (void **vpp,void *value,const int lsm) +{ +struct lsm_blob *bp = *vpp; + +if (value ==NULL && bp->lsm_blobs[lsm] !=NULL ) + bp->lsm_setcount--; +if (value !=NULL && bp->lsm_blobs[lsm] ==NULL ) + bp->lsm_setcount++; + + bp->lsm_blobs[lsm] = value; +} + +static inline void *lsm_get_cred (const struct cred *cred, +const struct security_operations *sop) +{ +#ifdef CONFIG_SECURITY_COMPOSER_DEBUG +if (cred ==NULL ) { +pr_warn ("%s: cred is NULL, LSM is %s.\n" , __func__, sop->name); +return NULL ; + } +if (cred->security ==NULL ) { +pr_warn ("%s: cred blob NULL, LSM is %s.\n" , + __func__, sop->name); +return NULL ; + } +#endif /* CONFIG_SECURITY_COMPOSER_DEBUG */ +return lsm_get_blob (cred->security, sop->order); +} + +static inline void lsm_set_cred (struct cred *cred,void *value, +const struct security_operations *sop) +{ +lsm_set_blob (&cred->security, value, sop->order); +} + +static inline int lsm_set_init_cred (struct cred *cred,void *value, +const struct security_operations *sop) +{ +if (cred->security ==NULL ) { + cred->security =lsm_alloc_blob (GFP_KERNEL ); +if (cred->security ==NULL ) +return -ENOMEM ; + } + +lsm_set_blob (&cred->security, value, sop->order); +return 0 ; +} + +static inline void *lsm_get_file (const struct file *file, +const struct security_operations *sop) +{ +return lsm_get_blob (file->f_security, sop->order); +} + +static inline void lsm_set_file (struct file *file,void *value, +const struct security_operations *sop) +{ +lsm_set_blob (&file->f_security, value, sop->order); +} + +static inline void *lsm_get_inode (const struct inode *inode, +const struct security_operations *sop) +{ +return lsm_get_blob (inode->i_security, sop->order); +} + +static inline void lsm_set_inode (struct inode *inode,void *value, +const struct security_operations *sop) +{ +#ifdef CONFIG_SECURITY_COMPOSER_DEBUG +if (inode ==NULL ) { +pr_warn ("%s: inode is NULL, LSM is %s.\n" , __func__, sop->name); +return ; + } +if (inode->i_security ==NULL ) { +pr_warn ("%s: inode blob is NULL, LSM is %s.\n" , + __func__, sop->name); + inode->i_security =lsm_alloc_blob (GFP_KERNEL ); + } +#endif /* CONFIG_SECURITY_COMPOSER_DEBUG */ +lsm_set_blob (&inode->i_security, value, sop->order); +} + +static inline void *lsm_get_super (const struct super_block *super, +const struct security_operations *sop) +{ +return lsm_get_blob (super->s_security, sop->order); +} + +static inline void lsm_set_super (struct super_block *super,void *value, +const struct security_operations *sop) +{ +lsm_set_blob (&super->s_security, value, sop->order); +} + +static inline void *lsm_get_ipc (const struct kern_ipc_perm *ipc, +const struct security_operations *sop) +{ +return lsm_get_blob (ipc->security, sop->order); +} + +static inline void lsm_set_ipc (struct kern_ipc_perm *ipc,void *value, +const struct security_operations *sop) +{ +lsm_set_blob (&ipc->security, value, sop->order); +} + +static inline void *lsm_get_msg (const struct msg_msg *msg, +const struct security_operations *sop) +{ +return lsm_get_blob (msg->security, sop->order); +} + +static inline void lsm_set_msg (struct msg_msg *msg,void *value, +const struct security_operations *sop) +{ +lsm_set_blob (&msg->security, value, sop->order); +} + +static inline void *lsm_get_key (const struct key *key, +const struct security_operations *sop) +{ +return lsm_get_blob (key->security, sop->order); +} + +static inline void lsm_set_key (struct key *key,void *value, +const struct security_operations *sop) +{ +lsm_set_blob (&key->security, value, sop->order); +} + +static inline void *lsm_get_sock (const struct sock *sock, +const struct security_operations *sop) +{ +return lsm_get_blob (sock->sk_security, sop->order); +} + +static inline void lsm_set_sock (struct sock *sock,void *value, +const struct security_operations *sop) +{ +lsm_set_blob (&sock->sk_security, value, sop->order); +} + +#endif /* ! _LINUX_LSM_H */
include/linux/security.h +12 -3
@@ -112,7 +112,9 @@ struct seq_file;extern int cap_netlink_send (struct sock *sk,struct sk_buff *skb); -void reset_security_ops (void ); +extern int lsm_count; +extern struct security_operations *composer_ops[]; +extern struct security_operations capability_ops;#ifdef CONFIG_MMU extern unsigned long mmap_min_addr;
@@ -193,6 +195,11 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)* A string that acts as a unique identifier for the LSM with max number * of characters = SECURITY_NAME_MAX. * +* @order: +* The numeric order in which this LSM will be invoked. +* Set during LSM initialization. Used to identify +* which security blob to use when there is more than one LSM. +* * Security hooks for program execution operations. * * @bprm_set_creds:
@@ -1379,6 +1386,7 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)*/ struct security_operations {char name[SECURITY_NAME_MAX +1 ]; +int order;int (*ptrace_access_check) (struct task_struct *child,unsigned int mode);int (*ptrace_traceme) (struct task_struct *parent);
@@ -1658,9 +1666,10 @@ struct security_operations {/* prototypes */ extern int security_init (void );extern int security_module_enable (struct security_operations *ops); -extern int register_security (struct security_operations *ops); -extern void __init security_fixup_ops (struct security_operations *ops); +#ifdef CONFIG_SECURITY_SELINUX_DISABLE +extern int reset_security_ops (struct security_operations *ops); +#endif /* CONFIG_SECURITY_SELINUX_DISABLE */ /* Security operations */ int security_ptrace_access_check (struct task_struct *child,unsigned int mode);
security/Kconfig +46 -29
95 lines not on this page · raw message
security/apparmor/context.c +5 -5
@@ -76,7 +76,7 @@ void aa_dup_task_context(struct aa_task_cxt *new, const struct aa_task_cxt *old)*/ int aa_replace_current_profile (struct aa_profile *profile) { -struct aa_task_cxt *cxt =current_cred ()->security; +struct aa_task_cxt *cxt =lsm_get_cred (current_cred (), &apparmor_ops);struct cred *new;BUG_ON (!profile);
@@ -87,7 +87,7 @@ int aa_replace_current_profile(struct aa_profile *profile)if (!new)return -ENOMEM ; - cxt = new->security; + cxt =lsm_get_cred (new, &apparmor_ops);if (unconfined (profile) || (cxt->profile->ns != profile->ns)) {/* if switching to unconfined or a different profile namespace * clear out context state
@@ -123,7 +123,7 @@ int aa_set_current_onexec(struct aa_profile *profile)if (!new)return -ENOMEM ; - cxt = new->security; + cxt =lsm_get_cred (new, &apparmor_ops);aa_get_profile (profile);aa_put_profile (cxt->onexec); cxt->onexec = profile;
@@ -150,7 +150,7 @@ int aa_set_current_hat(struct aa_profile *profile, u64 token)return -ENOMEM ;BUG_ON (!profile); - cxt = new->security; + cxt =lsm_get_cred (new, &apparmor_ops);if (!cxt->previous) {/* transfer refcount */ cxt->previous = cxt->profile;
@@ -187,7 +187,7 @@ int aa_restore_previous_profile(u64 token)if (!new)return -ENOMEM ; - cxt = new->security; + cxt =lsm_get_cred (new, &apparmor_ops);if (cxt->token != token) {abort_creds (new);return -EACCES ;
security/apparmor/domain.c +4 -4
@@ -360,7 +360,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)if (bprm->cred_prepared)return 0 ; - cxt = bprm->cred->security; + cxt =lsm_get_cred (bprm->cred, &apparmor_ops);BUG_ON (!cxt); profile =aa_get_profile (aa_newest_version (cxt->profile));
@@ -557,7 +557,7 @@ int apparmor_bprm_secureexec(struct linux_binprm *bprm)void apparmor_bprm_committing_creds (struct linux_binprm *bprm) {struct aa_profile *profile =__aa_current_profile (); -struct aa_task_cxt *new_cxt = bprm->cred->security; +struct aa_task_cxt *new_cxt =lsm_get_cred (bprm->cred, &apparmor_ops);/* bail out if unconfined or not changing profile */ if ((new_cxt->profile == profile) ||
@@ -634,7 +634,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)/* released below */ cred =get_current_cred (); - cxt = cred->security; + cxt =lsm_get_cred (cred, &apparmor_ops); profile =aa_cred_profile (cred); previous_profile = cxt->previous;
@@ -770,7 +770,7 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec, } cred =get_current_cred (); - cxt = cred->security; + cxt =lsm_get_cred (cred, &apparmor_ops); profile =aa_cred_profile (cred);/*
security/apparmor/include/context.h +10 -3
@@ -18,6 +18,7 @@#include <linux/cred.h> #include <linux/slab.h> #include <linux/sched.h> +#include <linux/lsm.h> #include "policy.h"
@@ -81,6 +82,8 @@ int aa_set_current_onexec(struct aa_profile *profile);int aa_set_current_hat (struct aa_profile *profile,u64 token);int aa_restore_previous_profile (u64 cookie); +extern struct security_operations apparmor_ops; +/** * __aa_task_is_confined - determine if @task has any confinement * @task: task to check confinement of (NOT NULL)
@@ -89,7 +92,9 @@ int aa_restore_previous_profile(u64 cookie);*/ static inline bool __aa_task_is_confined (struct task_struct *task) { -struct aa_task_cxt *cxt =__task_cred (task)->security; +struct aa_task_cxt *cxt; + + cxt =lsm_get_cred (__task_cred (task), &apparmor_ops);BUG_ON (!cxt || !cxt->profile);if (unconfined (aa_newest_version (cxt->profile)))
@@ -108,7 +113,7 @@ static inline bool __aa_task_is_confined(struct task_struct *task)*/ static inline struct aa_profile *aa_cred_profile (const struct cred *cred) { -struct aa_task_cxt *cxt = cred->security; +struct aa_task_cxt *cxt =lsm_get_cred (cred, &apparmor_ops);BUG_ON (!cxt || !cxt->profile);return aa_newest_version (cxt->profile); }
@@ -136,8 +141,10 @@ static inline struct aa_profile *__aa_current_profile(void)*/ static inline struct aa_profile *aa_current_profile (void ) { -const struct aa_task_cxt *cxt =current_cred ()->security; +const struct aa_task_cxt *cxt;struct aa_profile *profile; + + cxt =lsm_get_cred (current_cred (), &apparmor_ops);BUG_ON (!cxt || !cxt->profile); profile =aa_newest_version (cxt->profile);
security/apparmor/lsm.c +19 -26
136 lines not on this page · raw message
security/capability.c +58 -998
1,113 lines not on this page · raw message
security/inode.c +47 -2
68 lines not on this page · raw message
security/security.c +1131 -268
1,240 lines not on this page · raw message
security/selinux/hooks.c +202 -153
355 lines not on this page · raw message
security/smack/smack_lsm.c +170 -161
331 lines not on this page · raw message
security/smack/smackfs.c +85 -7
92 lines not on this page · raw message
security/tomoyo/tomoyo.c +27 -14
41 lines not on this page · raw message
The rest of this diff is not shown. View the raw message.
--
This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to [email protected] with the words "unsubscribe selinux" without quotes as the message.
Part of the diff is not shown here. View the raw message.
A message that is not in the archive (sent off-list or not yet received): CAGXu5jJKWZMJbX+kQZb1Qe64EjuZMEubCgtiV4COXWo9=hqPiQ@mail.gmail.com
Casey Schaufler
maintainerQuoted 49 lines · Kees Cook
Quoted 5 lines · Casey Schaufler
Here's my post vacation update. I am considering moving away from the array of LSM vectors to lists within each hook. The primary advantage would be that multiple sparse LSMs would spend less time checking on NULL hooks. Thoughts?
I had actually been working up to suggesting this, so yes, I'm in favor of it. Much nicer for the likely sparse condition.
Quoted 4 lines
It would also make
security_reset_opsconsiderably less pleasant, but that is code that gets ifdefed out except for one configuration of one LSM.That seems fine to me.
Quoted 6 lines
Version 5 of the patch addresses:
1. Tetsuo Handa pointed out that handling of failures alloc hooks was still not correct in v4. The code now only calls the free hook for LSMs that have had their alloc hook successfully called. The alloc hooks have been de-macro-ized, too.Excellent.
Quoted 2 lines
2. Removed the Yama special case. It is no longer necessary.
:)
Quoted 1 line
Signed-off-by Casey Schaufler <[email protected]>Reviewed-by Kees Cook <[email protected]>Quoted 19 lines
security/security.c +17
-1+/* +* Only SELinux calls reset_security_ops. +*/ +#ifdef CONFIG_SECURITY_SELINUX_DISABLE +int reset_security_ops (struct security_operations *ops) { - security_ops = &default_security_ops; +struct security_operations *empty; + +/* +* This LSM is configured to own /proc/.../attr. +* Don't leave that important interface unattended. +*/ +if (lsm_present == ops->order) +return -EBUSY ; + + empty =kzalloc (sizeof (struct security_operations ),GFP_KERNEL ); +if (empty ==NULL ) +return -ENOMEM ;
Quoted 8 lines
security/security.c static void __init do_security_initcalls(void) · line 99
Quoted 7 lines
security/security.c +6
-0+ +strcpy (empty->name, ops->name); + empty->order = ops->order; + composer_ops[ops->order] = empty; + return 0; } +#endif /* CONFIG_SECURITY_SELINUX_DISABLE */ Won't this leak if called more than once?
Sure will. It leaks if called once for that matter. SELinux blobs that have been allocated but not freed never will be after reset_security_ops is called. The calling code does what it can to minimize the impact, but it will be there. I could add a flags field to security_operations that indicates the LSM has been reset to prevent this problem, but it seems wasteful.
Quoted 42 lines
Quoted 26 lines
security/security.c +7
-18-/** -* register_security - registers a security framework with the kernel -* @ops: a pointer to the struct security_options that is to be registered -* -* This function allows a security module to register itself with the -* kernel security subsystem. Some rudimentary checking is done on the @ops -* value passed to this function. You'll need to check first if your LSM -* is allowed to register its @ops by calling security_module_enable(@ops). -* -* If there is already a security module registered with the kernel, -* an error will be returned. Otherwise %0 is returned on success. -*/ -int __init register_security (struct security_operations *ops) -{ -if (verify (ops)) { -printk (KERN_DEBUG "%s could not verify " -"security_operations structure.\n" , __func__); -return -EINVAL ; +if (lsm_count >=COMPOSER_MAX ) { +pr_warn ("Too many security modules. %s not loaded.\n" , + ops->name); +return 0 ; + } +/* +* Set up the operation vector early, but only once. security/security.c __setup("security=", choose_lsm); · line 142
Quoted 10 lines
security/security.c +9
-0+* This allows LSM specific file systems to check to see if they +* should come on line. +*/ +if (ops ==NULL ) { +pr_debug ("%s could not verify security_operations.\n" , + __func__); +return 0 ; } +if (allowed_lsms[0 ] !='\0' &&strstr (allowed_lsms, ops->name) ==NULL ) +return 0 ;This means we must always avoid LSMs that could be substrings of another LSM. Should this check maybe be improved to test for start/end and commas?
Off the top of my head, and seems horrible. Maybe there's a cleaner way:
Quoted 13 lines
if (allowed_lsms[0] != '\0') {
char * found;
int len;
found = strstr(allowed_lsms, ops->name);
if (!found)
return 0;
if (found != allowed_lsms && found[-1] != ',')
return 0;
len = strlen(ops->name);
if (strlen(found) < len || (found[len] != '\0' && found[len] != ','))
return 0;
}Yes, and I agree it's worth doing, if ugly.
Quoted 51 lines
Quoted 22 lines
security/security.c +18
-0+#define call_int_hook (RC ,FUNC , ...) \ + do { \ + int called = 0; \ + int thisrc; \ + int i; \ + \ + RC = 0; \ + for (i = 1; i < lsm_count; i++) { \ + if (!composer_ops[i]->FUNC) \ + continue; \ + thisrc = composer_ops[i]->FUNC(__VA_ARGS__); \ + if (thisrc) \ + RC = thisrc; \ + called = 1; \ + } \ + if (!called && composer_ops[0]->FUNC) \ + RC = composer_ops[0]->FUNC(__VA_ARGS__); \ + } while (0)int security_task_prctl (int option,unsigned long arg2,unsigned long arg3,unsigned long arg4,unsigned long arg5) {security/security.c __setup("security=", choose_lsm); · line 267
Quoted 10 lines
security/security.c +2
-6-#ifdef CONFIG_SECURITY_YAMA_STACKED int rc; - rc =yama_task_prctl (option, arg2, arg3, arg4, arg5); -if (rc != -ENOSYS ) -return rc; -#endif -return security_ops->task_prctl (option, arg2, arg3, arg4, arg5); +call_int_hook (rc, task_prctl, option, arg2, arg3, arg4, arg5); +return rc; }prctl control needs special handling. When an option is unhandled, it'll return
-ENOSYS. Also, some prctls return non-zero results, so they could mask each other. I think it would make sense to walk the composer list as long as the return value is-ENOSYS. As soon as it isn't, then stop the call chain and return the value. Ultimately, this means that the LSM order defines who get access to a given prctl option (though with caps last).int i, rc, called = 0;
for (i = 1; i < lsm_count; i++) { if (!composer_ops[i]->task_prctl) continue; rc = composer_ops[i]->task_prctl(option, arg2, arg3, arg4, arg5); called = 1; if (rc != -ENOSYS) return rc; } if (!called and composer_ops[0]->task_prctl) return composer_ops[0]->task_prctl(option, arg2, arg3, arg4, arg5);
Quoted 1 line
return rc;
I'll investigate further and revise.
Quoted 4 lines
-Kees
--
This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to [email protected] with the words "unsubscribe selinux" without quotes as the message.
A message that is not in the archive (sent off-list or not yet received): [email protected]
Casey Schaufler
maintainerQuoted 8 lines · Tetsuo Handa
Quoted 7 lines · Kees Cook
prctl control needs special handling. When an option is unhandled, it'll return
-ENOSYS. Also, some prctls return non-zero results, so they could mask each other. I think it would make sense to walk the composer list as long as the return value is-ENOSYS. As soon as it isn't, then stop the call chain and return the value. Ultimately, this means that the LSM order defines who get access to a given prctl option (though with caps last).
I don't see why you think prctl is a special case. For starters, only Yama does anything with it, and Yama does the expected "call cap if you care" thingy. Yes, the -ENOSYS return is there, but the current implementation will handle it correctly because it's in cap_task_prctl.
What am I missing?
Quoted 69 lines
Quoted 12 lines
int i, rc, called = 0;
for (i = 1; i < lsm_count; i++) { if (!composer_ops[i]->task_prctl) continue; rc = composer_ops[i]->task_prctl(option, arg2, arg3, arg4, arg5); called = 1; if (rc != -ENOSYS) return rc; } if (!called and composer_ops[0]->task_prctl) return composer_ops[0]->task_prctl(option, arg2, arg3, arg4, arg5);Quoted 1 line
return rc;
With optimization,
security_task_prctl()would look like below? I removed "int called" usage so that LSM modules which implement ->task_prctl need not to callcap_task_prctl(). Also, I assume "for (i = 1; i <lsm_count; i++)" will be changed to "for (i = 0; i <lsm_count; i++)" if this optimization is accepted.int security_task_prctl(int option, unsigned long arg2, unsigned long arg3,
unsigned long arg4, unsigned long arg5){ int i; for (i = 1; i < lsm_count; i++) { int rc; if (!composer_ops[i]->task_prctl) continue; rc = composer_ops[i]->task_prctl(option, arg2, arg3, arg4, arg5); if (rc != -ENOSYS) return rc; } return cap_task_prctl(option, arg2, arg3, arg4, arg5); }Same thing applies to
security_bprm_set_creds()etc. so that LSM modules which implement ->bprm_set_creds need not to callcap_bprm_set_creds()?int
security_bprm_set_creds(struct linux_binprm *bprm){ int i; for (i = 1; i < lsm_count; i++) { int rc; if (!composer_ops[i]->bprm_set_creds) continue; rc = composer_ops[i]->bprm_set_creds(bprm); if (rc) return rc; } return cap_bprm_set_creds(bprm); }Quoted 1 line · Casey Schaufler
I still think that we want to "call all" rather than "bail on fail".
If
prctl()is "bail out upon handled" andsecurity_bprm_set_creds()is "bail out upon fail", wouldn't it look natural to do like below?int
security_inode_permission(struct inode *inode, int mask){(quoted diff) +0
-0if (unlikely(IS_PRIVATE(inode))) return 0;for (i = 1; i < lsm_count; i++) { int rc; if (!composer_ops[i]->inode_permission) continue; rc = composer_ops[i]->inode_permission(inode, mask); if (rc) return rc; } return 0; }
Quoted 5 lines
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
--
This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to [email protected] with the words "unsubscribe selinux" without quotes as the message.
A message that is not in the archive (sent off-list or not yet received): [email protected]
A message that is not in the archive (sent off-list or not yet received): CAGXu5jKdLnDCpDzteY1v3aStAxAezdOykKUN1gDt69BZcBvwEA@mail.gmail.com
A message that is not in the archive (sent off-list or not yet received): [email protected]
A message that is not in the archive (sent off-list or not yet received): [email protected]
A message that is not in the archive (sent off-list or not yet received): [email protected]
Casey Schaufler
maintainerQuoted 1 line · Tetsuo Handa
This is what I think we can optimize.
I think that I have worked out a list based scheme that will address the performance concerns. I hope to have a version ready in the next few days. There is a lot of typing involved.
Quoted 6 lines
Only compile tested. This may not boot.
Calls to common cap functions (e.g.
cap_bprm_set_creds()) are not yet eliminated from each LSM modules. Common cap functions can be now eliminated from each LSM modules because these common cap functions are called fromsecurity/security.c(though I think I've made several mistakes while optimizing).
I don't know that we can do that in every case, but I'll look.
Quoted 10 lines
Revived
register_security()so that individual LSM modules can determine whether that module is listed on the activation list or not; and can take appropriate action (probably callpanic()) if registration failed when that module is listed on the activation list.Updated
register_security()to allow control of LSM hook call ordering. RevivedCONFIG_DEFAULT_SECURITYso that Linux distributors can specify list of LSM modules which should be enabled by default (e.g. "selinux", "apparmor,yama") while compiling other LSM modules which are not enabled unless explicitly specified by security= kernel boot parameter.
I will definitely try to incorporate this.
Quoted 1 line
What do you think?
I am going to hold off on specific comments until I've decided on the merits of my list based scheme, which will eliminate the composer_ops array.
--
This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to [email protected] with the words "unsubscribe selinux" without quotes as the message.
A message that is not in the archive (sent off-list or not yet received): [email protected]
Casey Schaufler
maintainerQuoted 12 lines · Tetsuo Handa
Quoted 11 lines · Casey Schaufler
security/security.c +11
-0+#define call_int_hook (RC ,FUNC , ...) \ + do { \ + int called = 0; \ + int thisrc; \ + int i; \ + \ + RC = 0; \ + for (i = 1; i < lsm_count; i++) { \ + if (!composer_ops[i]->FUNC) \ + continue; \ + thisrc = composer_ops[i]->FUNC(__VA_ARGS__); \
Quoted 8 lines
security/security.c __setup("security=", choose_lsm); · line 228
Quoted 7 lines
security/security.c +7
-0+if (thisrc) \ +RC = thisrc; \ + called =1 ; \ + } \ +if (!called && composer_ops[0 ]->FUNC) \ +RC = composer_ops[0 ]->FUNC (__VA_ARGS__); \ + }while (0 )Why can't we simplify like below? What is special with composer_ops[0] ?
composer_ops[0] is the capability operations. They get called if and only if no LSM supplies something for a particular hook. LSMs are allowed to call the capability hook themselves or not as they see fit. Thus the special case for composer_ops[0].
Quoted 34 lines
#define call_int_hook(RC, FUNC, ...) \ do { \ int i; \ \ RC = 0; \ for (i = 0; i < lsm_count; i++) { \ if (!composer_ops[i]->FUNC) \ continue; \ RC = composer_ops[i]->FUNC(__VA_ARGS__); \ if (RC) \ break; \ } \ } while (0)Quoted 17 lines
int
security_inode_alloc(struct inode *inode)
{security/security.c +13
-2- inode->i_security =NULL ; -return security_ops->inode_alloc_security (inode); +int i; +int rc; +struct lsm_blob tblob; +struct lsm_blob *bp =NULL ; + +memset (&tblob,0 ,sizeof (tblob)); + inode->i_security = &tblob; + +for (rc =0 , i =1 ; i < lsm_count && rc ==0 ; i++) +if (composer_ops[i]->inode_alloc_security) + rc = composer_ops[i]->inode_alloc_security (inode); + +if (tblob.lsm_setcount !=0 ) {
Quoted 8 lines
security/security.c int security_bprm_check(struct linux_binprm *bprm) · line 520
Quoted 7 lines
security/security.c +7
-0+if (rc ==0 ) + bp =kmemdup (&tblob,sizeof (tblob),GFP_KERNEL ); +if (bp ==NULL ) { +if (rc ==0 ) + rc = -ENOMEM ; +for (i--; i >=1 ; i--) { +if (composer_ops[i]->inode_free_security)if (!composer_ops[i]->inode_free_security)
Yes, indeed.
Quoted 15 lines
security/security.c int security_bprm_check(struct linux_binprm *bprm) · line 529
Quoted 10 lines
security/security.c +8
-0+continue ; + composer_ops[i]->inode_free_security (inode); + } +lsm_blob_cleanup (rc, &tblob, __func__); + } + } + inode->i_security = bp; +return rc; }--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
--
This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to [email protected] with the words "unsubscribe selinux" without quotes as the message.
A message that is not in the archive (sent off-list or not yet received): [email protected]
Casey Schaufler
maintainerQuoted 2 lines · Tetsuo Handa
What about adding "struct
security_operations*next;" to "structsecurity_operations" like below?
This would address the memory leak introduced by my version of security_reset_ops.
What is doesn't address is all of the checks to see if a particular LSM provides a hook. For that there needs to be a list in each hook. A list per hook would complicate registration and reset. It would allow cleaner handling of the capability hooks.
My inclination is to have a shot at making the changes in security.c to do the lists at that level. It's more work, but I think it would be a better result.
Quoted 14 lines
struct
security_operations{(quoted diff) +2
-0char name[SECURITY_NAME_MAX + 1]; + int index; /* Index number used by lsm_get_blob()/lsm_set_blob(). */ + struct security_operations *next; /* Next LSM to call if not NULL. */ int foo(); int bar_alloc(); void bar_free(); };I think the basic change looks like
int
security_foo()
{
Review
Quoted 9 lines
(quoted diff) +8
-1- return security_ops->foo(); + struct security_operations *ops = security_ops; + for (ops = security_ops; ops; ops = ops->next) + if (ops->foo) { + int rc = ops->foo(); + if (rc) + return rc; + } + return 0;
Standard list macros should be used. Not that I think they add all that much value in this case, but you never know.
I still think that we want to "call all" rather than "bail on fail".
Quoted 10 lines
}
int
security_bar_alloc()
{(quoted diff) +5
-1- return security_ops->bar_alloc(); + int rc; + struct security_operations *ops; + struct security_operations *tmp; + for (ops = security_ops; ops; ops = ops->next) + if (ops->bar_alloc) {
Review
Quoted 10 lines
(quoted diff) +10
-0+ rc = ops->bar_alloc(); + if (rc) + goto undo; + } + return 0; +undo: + for (tmp = security_ops; tmp != ops; tmp = tmp->next) + if (tmp->bar_free) + tmp->bar_free(); + return rc;
Looks about right.
Review
Quoted 10 lines
}
void
security_bar_free()
{(quoted diff) +4
-1- security_ops->bar_free(); + struct security_operations *ops; + for (ops = security_ops; ops; ops = ops->next) + if (ops->bar_free) + ops->bar_free(); }
Same for any "void" hook.
Quoted 7 lines
and unregistration will look like
int
remove_security_ops(struct security_operations *lsm){(quoted diff) +0
-0struct security_operations *ops = security_ops; struct security_operations *prev = ops;
Review
Quoted 10 lines
(quoted diff) +0
-0do { if (ops == lsm) { prev->next = lsm->next; return 0; } prev = ops; ops = ops->next; } while (ops); return -ENOENT; }
Very clean, but it's not important that this be clean.
Quoted 10 lines
and registration will look like
int
add_security_ops(struct security_operations *lsm){(quoted diff) +0
-0static int index; struct security_operations *ops = security_ops; while (1) { if (ops == lsm) return 0;
Review
Quoted 10 lines
(quoted diff) +0
-0if (!ops->next) break; ops = ops->next; } if (index == COMPOSER_MAX - 1) return -ENOSPC; lsm->index = index++; ops->next = lsm; return 0; }
Plus conflict checking. Again, it's not really important how clean this operation is.
Quoted 7 lines
.
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
--
This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to [email protected] with the words "unsubscribe selinux" without quotes as the message.