LSM: Module stacking in support of S.A.R.A and Landlock
v2: Reduce the patchset to what is required to support
the proposed S.A.R.A. and LandLock security modules
The S.A.R.A. security module is intended to be used
in conjunction with other security modules. It requires
state to be maintained for the credential, which
in turn requires a mechanism for sharing the credential
security blob. The module also requires mechanism for
user space manipulation of the credential information,
hence an additional subdirectory in /proc/.../attr.
The LandLock security module provides user configurable
policy in the secmark mechanism. It requires data in
the credential, file and inode security blobs. For this
to be used along side the existing "major" security
modules mechanism for sharing these blobs is provided.
A side effect of providing sharing of the crendential
security blob is that the TOMOYO module can be used at
the same time as the other "major" modules.
The mechanism for configuring which security modules are
enabled has to change when stacking in enabled. Any
module that uses just the security blobs that are shared
can be selected. Additionally, one other "major" module
can be selected.
The security module stacking issues around networking and
IPC are not addressed here as they are beyond what is
required for TOMOYO, S.A.R.A and LandLock.
git://github.com/cschaufler/lsm-stacking.git#stacking-4.19-rc2-saralock
Signed-off-by: Casey Schaufler <[email protected]>
---
Documentation/admin-guide/LSM/index.rst | 23 ++-
fs/proc/base.c | 64 ++++++-
fs/proc/internal.h | 1 +
include/linux/lsm_hooks.h | 20 ++-
include/linux/security.h | 15 +-
kernel/cred.c | 13 --
security/Kconfig | 92 ++++++++++
security/apparmor/domain.c | 2 +-
security/apparmor/include/cred.h | 24 ++-
security/apparmor/include/file.h | 9 +-
security/apparmor/include/lib.h | 4 +
security/apparmor/lsm.c | 53 ++++--
security/apparmor/task.c | 6 +-
security/security.c | 293 ++++++++++++++++++++++++++++++--
security/selinux/hooks.c | 215 ++++++++---------------
security/selinux/include/objsec.h | 37 +++-
security/selinux/selinuxfs.c | 5 +-
security/selinux/xfrm.c | 4 +-
security/smack/smack.h | 42 ++++-
security/smack/smack_access.c | 4 +-
security/smack/smack_lsm.c | 283 +++++++++++-------------------
security/smack/smackfs.c | 18 +-
security/tomoyo/common.h | 31 +++-
security/tomoyo/domain.c | 4 +-
security/tomoyo/securityfs_if.c | 15 +-
security/tomoyo/tomoyo.c | 57 +++++--
26 files changed, 899 insertions(+), 435 deletions(-)
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.
This patch provides a subdirectory in /proc/.../attr for
Smack. Smack user space can use the "current" file in
this subdirectory and never have to worry about getting
SELinux attributes by mistake. Programs that use the
old interface will continue to work (or fail, as the case
may be) as before.
The proposed S.A.R.A security module is dependent on
the mechanism to create its own attr subdirectory.
The original implementation is by Kees Cook.
Signed-off-by: Casey Schaufler <[email protected]>
---
Documentation/admin-guide/LSM/index.rst | 13 +++--
fs/proc/base.c | 64 +++++++++++++++++++++----
fs/proc/internal.h | 1 +
include/linux/security.h | 15 ++++--
security/security.c | 24 ++++++++--
5 files changed, 96 insertions(+), 21 deletions(-)
diff --git a/Documentation/admin-guide/LSM/index.rst b/Documentation/admin-guide/LSM/index.rst
index c980dfe9abf1..9842e21afd4a 100644
--- a/Documentation/admin-guide/LSM/index.rst
+++ b/Documentation/admin-guide/LSM/index.rst
@@ -17,9 +17,8 @@ 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.
+The Linux capabilities modules will always be included. This may be
+followed by any number of "minor" modules and at most one "major" module.
For more details on capabilities, see ``capabilities(7)`` in the Linux
man-pages project.
@@ -30,6 +29,14 @@ 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 ``/proc/.../attr``.
+A security module may maintain a module specific subdirectory there,
+named after the module. ``/proc/.../attr/smack`` is provided by the Smack
+security module and contains all its special files. The files directly
+in ``/proc/.../attr`` remain as legacy interfaces for modules that provide
+subdirectories.
+
.. toctree::
:maxdepth: 1
diff --git a/fs/proc/base.c b/fs/proc/base.c
index ccf86f16d9f0..bd2dd85310fe 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -140,9 +140,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 .
@@ -2503,7 +2507,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);
@@ -2552,7 +2556,9 @@ static ssize_t proc_pid_attr_write(struct file * file, const char __user * buf,
if (rv < 0)
goto out_free;
- rv = security_setprocattr(file->f_path.dentry->d_name.name, page, count);
+ rv = security_setprocattr(PROC_I(inode)->op.lsm,
+ file->f_path.dentry->d_name.name, page,
+ count);
mutex_unlock(¤t->signal->cred_guard_mutex);
out_free:
kfree(page);
@@ -2566,13 +2572,53 @@ 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_SMACK
+static const struct pid_entry smack_attr_dir_stuff[] = {
+ ATTR("smack", "current", 0666),
+};
+LSM_DIR_OPS(smack);
+#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", 0666),
+ ATTR(NULL, "prev", 0444),
+ ATTR(NULL, "exec", 0666),
+ ATTR(NULL, "fscreate", 0666),
+ ATTR(NULL, "keycreate", 0666),
+ ATTR(NULL, "sockcreate", 0666),
+#ifdef CONFIG_SECURITY_SMACK
+ DIR("smack", 0555,
+ proc_smack_attr_dir_inode_ops, proc_smack_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 5185d7f6a51e..d4f9989063d0 100644
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -81,6 +81,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/security.h b/include/linux/security.h
index 75f4156c84d7..418de5d20ffb 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -390,8 +390,10 @@ int security_sem_semctl(struct kern_ipc_perm *sma, int cmd);
int security_sem_semop(struct kern_ipc_perm *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(const char *name, void *value, size_t size);
+int security_getprocattr(struct task_struct *p, const char *lsm, char *name,
+ char **value);
+int security_setprocattr(const char *lsm, const 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);
@@ -1139,15 +1141,18 @@ static inline int security_sem_semop(struct kern_ipc_perm *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(char *name, void *value, size_t size)
+static inline int security_setprocattr(const char *lsm, char *name,
+ void *value, size_t size)
{
return -EINVAL;
}
diff --git a/security/security.c b/security/security.c
index 736e78da1ab9..3dfe75d0d373 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1288,14 +1288,30 @@ 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;
+
+ hlist_for_each_entry(hp, &security_hook_heads.getprocattr, list) {
+ if (lsm != NULL && strcmp(lsm, hp->lsm))
+ continue;
+ return hp->hook.getprocattr(p, name, value);
+ }
+ return -EINVAL;
}
-int security_setprocattr(const char *name, void *value, size_t size)
+int security_setprocattr(const char *lsm, const char *name, void *value,
+ size_t size)
{
- return call_int_hook(setprocattr, -EINVAL, name, value, size);
+ struct security_hook_list *hp;
+
+ hlist_for_each_entry(hp, &security_hook_heads.setprocattr, list) {
+ if (lsm != NULL && strcmp(lsm, hp->lsm))
+ continue;
+ return hp->hook.setprocattr(name, value, size);
+ }
+ return -EINVAL;
}
int security_netlink_send(struct sock *sk, struct sk_buff *skb)
--
2.17.1
Don't use the cred->security pointer directly.
Provide a helper function that provides the security blob pointer.
Signed-off-by: Casey Schaufler <[email protected]>
---
security/smack/smack.h | 14 +++++++--
security/smack/smack_access.c | 4 +--
security/smack/smack_lsm.c | 57 +++++++++++++++++------------------
security/smack/smackfs.c | 18 +++++------
4 files changed, 50 insertions(+), 43 deletions(-)
diff --git a/security/smack/smack.h b/security/smack/smack.h
index f7db791fb566..0b55d6a55b26 100644
--- a/security/smack/smack.h
+++ b/security/smack/smack.h
@@ -356,6 +356,11 @@ extern struct list_head smack_onlycap_list;
#define SMACK_HASH_SLOTS 16
extern struct hlist_head smack_known_hash[SMACK_HASH_SLOTS];
+static inline struct task_smack *smack_cred(const struct cred *cred)
+{
+ return cred->security;
+}
+
/*
* Is the directory transmuting?
*/
@@ -382,13 +387,16 @@ static inline struct smack_known *smk_of_task(const struct task_smack *tsp)
return tsp->smk_task;
}
-static inline struct smack_known *smk_of_task_struct(const struct task_struct *t)
+static inline struct smack_known *smk_of_task_struct(
+ const struct task_struct *t)
{
struct smack_known *skp;
+ const struct cred *cred;
rcu_read_lock();
- skp = smk_of_task(__task_cred(t)->security);
+ cred = __task_cred(t);
rcu_read_unlock();
+ skp = smk_of_task(smack_cred(cred));
return skp;
}
@@ -405,7 +413,7 @@ static inline struct smack_known *smk_of_forked(const struct task_smack *tsp)
*/
static inline struct smack_known *smk_of_current(void)
{
- return smk_of_task(current_security());
+ return smk_of_task(smack_cred(current_cred()));
}
/*
diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c
index 9a4c0ad46518..489d49a20b47 100644
--- a/security/smack/smack_access.c
+++ b/security/smack/smack_access.c
@@ -275,7 +275,7 @@ int smk_tskacc(struct task_smack *tsp, struct smack_known *obj_known,
int smk_curacc(struct smack_known *obj_known,
u32 mode, struct smk_audit_info *a)
{
- struct task_smack *tsp = current_security();
+ struct task_smack *tsp = smack_cred(current_cred());
return smk_tskacc(tsp, obj_known, mode, a);
}
@@ -635,7 +635,7 @@ DEFINE_MUTEX(smack_onlycap_lock);
*/
bool smack_privileged_cred(int cap, const struct cred *cred)
{
- struct task_smack *tsp = cred->security;
+ struct task_smack *tsp = smack_cred(cred);
struct smack_known *skp = tsp->smk_task;
struct smack_known_list_elem *sklep;
int rc;
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 340fc30ad85d..68ee3ae8f25c 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -122,7 +122,7 @@ static int smk_bu_note(char *note, struct smack_known *sskp,
static int smk_bu_current(char *note, struct smack_known *oskp,
int mode, int rc)
{
- struct task_smack *tsp = current_security();
+ struct task_smack *tsp = smack_cred(current_cred());
char acc[SMK_NUM_ACCESS_TYPE + 1];
if (rc <= 0)
@@ -143,7 +143,7 @@ static int smk_bu_current(char *note, struct smack_known *oskp,
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
static int smk_bu_task(struct task_struct *otp, int mode, int rc)
{
- struct task_smack *tsp = current_security();
+ struct task_smack *tsp = smack_cred(current_cred());
struct smack_known *smk_task = smk_of_task_struct(otp);
char acc[SMK_NUM_ACCESS_TYPE + 1];
@@ -165,7 +165,7 @@ static int smk_bu_task(struct task_struct *otp, int mode, int rc)
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
static int smk_bu_inode(struct inode *inode, int mode, int rc)
{
- struct task_smack *tsp = current_security();
+ struct task_smack *tsp = smack_cred(current_cred());
struct inode_smack *isp = inode->i_security;
char acc[SMK_NUM_ACCESS_TYPE + 1];
@@ -195,7 +195,7 @@ static int smk_bu_inode(struct inode *inode, int mode, int rc)
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
static int smk_bu_file(struct file *file, int mode, int rc)
{
- struct task_smack *tsp = current_security();
+ struct task_smack *tsp = smack_cred(current_cred());
struct smack_known *sskp = tsp->smk_task;
struct inode *inode = file_inode(file);
struct inode_smack *isp = inode->i_security;
@@ -225,7 +225,7 @@ static int smk_bu_file(struct file *file, int mode, int rc)
static int smk_bu_credfile(const struct cred *cred, struct file *file,
int mode, int rc)
{
- struct task_smack *tsp = cred->security;
+ struct task_smack *tsp = smack_cred(cred);
struct smack_known *sskp = tsp->smk_task;
struct inode *inode = file_inode(file);
struct inode_smack *isp = inode->i_security;
@@ -429,7 +429,7 @@ static int smk_ptrace_rule_check(struct task_struct *tracer,
}
rcu_read_lock();
- tsp = __task_cred(tracer)->security;
+ tsp = smack_cred(__task_cred(tracer));
tracer_known = smk_of_task(tsp);
if ((mode & PTRACE_MODE_ATTACH) &&
@@ -496,7 +496,7 @@ static int smack_ptrace_traceme(struct task_struct *ptp)
int rc;
struct smack_known *skp;
- skp = smk_of_task(current_security());
+ skp = smk_of_task(smack_cred(current_cred()));
rc = smk_ptrace_rule_check(ptp, skp, PTRACE_MODE_ATTACH, __func__);
return rc;
@@ -913,7 +913,7 @@ static int smack_sb_statfs(struct dentry *dentry)
static int smack_bprm_set_creds(struct linux_binprm *bprm)
{
struct inode *inode = file_inode(bprm->file);
- struct task_smack *bsp = bprm->cred->security;
+ struct task_smack *bsp = smack_cred(bprm->cred);
struct inode_smack *isp;
struct superblock_smack *sbsp;
int rc;
@@ -1744,7 +1744,7 @@ static int smack_mmap_file(struct file *file,
return -EACCES;
mkp = isp->smk_mmap;
- tsp = current_security();
+ tsp = smack_cred(current_cred());
skp = smk_of_current();
rc = 0;
@@ -1840,7 +1840,7 @@ static int smack_file_send_sigiotask(struct task_struct *tsk,
struct fown_struct *fown, int signum)
{
struct smack_known *skp;
- struct smack_known *tkp = smk_of_task(tsk->cred->security);
+ struct smack_known *tkp = smk_of_task(smack_cred(tsk->cred));
struct file *file;
int rc;
struct smk_audit_info ad;
@@ -1888,7 +1888,7 @@ static int smack_file_receive(struct file *file)
if (inode->i_sb->s_magic == SOCKFS_MAGIC) {
sock = SOCKET_I(inode);
ssp = sock->sk->sk_security;
- tsp = current_security();
+ tsp = smack_cred(current_cred());
/*
* If the receiving process can't write to the
* passed socket or if the passed socket can't
@@ -1930,7 +1930,7 @@ static int smack_file_receive(struct file *file)
*/
static int smack_file_open(struct file *file)
{
- struct task_smack *tsp = file->f_cred->security;
+ struct task_smack *tsp = smack_cred(file->f_cred);
struct inode *inode = file_inode(file);
struct smk_audit_info ad;
int rc;
@@ -1977,7 +1977,7 @@ static int smack_cred_alloc_blank(struct cred *cred, gfp_t gfp)
*/
static void smack_cred_free(struct cred *cred)
{
- struct task_smack *tsp = cred->security;
+ struct task_smack *tsp = smack_cred(cred);
struct smack_rule *rp;
struct list_head *l;
struct list_head *n;
@@ -2007,7 +2007,7 @@ static void smack_cred_free(struct cred *cred)
static int smack_cred_prepare(struct cred *new, const struct cred *old,
gfp_t gfp)
{
- struct task_smack *old_tsp = old->security;
+ struct task_smack *old_tsp = smack_cred(old);
struct task_smack *new_tsp;
int rc;
@@ -2038,15 +2038,14 @@ static int smack_cred_prepare(struct cred *new, const struct cred *old,
*/
static void smack_cred_transfer(struct cred *new, const struct cred *old)
{
- struct task_smack *old_tsp = old->security;
- struct task_smack *new_tsp = new->security;
+ struct task_smack *old_tsp = smack_cred(old);
+ struct task_smack *new_tsp = smack_cred(new);
new_tsp->smk_task = old_tsp->smk_task;
new_tsp->smk_forked = old_tsp->smk_task;
mutex_init(&new_tsp->smk_rules_lock);
INIT_LIST_HEAD(&new_tsp->smk_rules);
-
/* cbs copy rule list */
}
@@ -2057,12 +2056,12 @@ static void smack_cred_transfer(struct cred *new, const struct cred *old)
*
* Sets the secid to contain a u32 version of the smack label.
*/
-static void smack_cred_getsecid(const struct cred *c, u32 *secid)
+static void smack_cred_getsecid(const struct cred *cred, u32 *secid)
{
struct smack_known *skp;
rcu_read_lock();
- skp = smk_of_task(c->security);
+ skp = smk_of_task(smack_cred(cred));
*secid = skp->smk_secid;
rcu_read_unlock();
}
@@ -2076,7 +2075,7 @@ static void smack_cred_getsecid(const struct cred *c, u32 *secid)
*/
static int smack_kernel_act_as(struct cred *new, u32 secid)
{
- struct task_smack *new_tsp = new->security;
+ struct task_smack *new_tsp = smack_cred(new);
new_tsp->smk_task = smack_from_secid(secid);
return 0;
@@ -2094,7 +2093,7 @@ static int smack_kernel_create_files_as(struct cred *new,
struct inode *inode)
{
struct inode_smack *isp = inode->i_security;
- struct task_smack *tsp = new->security;
+ struct task_smack *tsp = smack_cred(new);
tsp->smk_forked = isp->smk_inode;
tsp->smk_task = tsp->smk_forked;
@@ -2278,7 +2277,7 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info,
* specific behavior. This is not clean. For one thing
* we can't take privilege into account.
*/
- skp = smk_of_task(cred->security);
+ skp = smk_of_task(smack_cred(cred));
rc = smk_access(skp, tkp, MAY_DELIVER, &ad);
rc = smk_bu_note("USB signal", skp, tkp, MAY_DELIVER, rc);
return rc;
@@ -3605,7 +3604,7 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value)
*/
static int smack_setprocattr(const char *name, void *value, size_t size)
{
- struct task_smack *tsp = current_security();
+ struct task_smack *tsp = smack_cred(current_cred());
struct cred *new;
struct smack_known *skp;
struct smack_known_list_elem *sklep;
@@ -3646,7 +3645,7 @@ static int smack_setprocattr(const char *name, void *value, size_t size)
if (new == NULL)
return -ENOMEM;
- tsp = new->security;
+ tsp = smack_cred(new);
tsp->smk_task = skp;
/*
* process can change its label only once
@@ -4291,7 +4290,7 @@ static void smack_inet_csk_clone(struct sock *sk,
static int smack_key_alloc(struct key *key, const struct cred *cred,
unsigned long flags)
{
- struct smack_known *skp = smk_of_task(cred->security);
+ struct smack_known *skp = smk_of_task(smack_cred(cred));
key->security = skp;
return 0;
@@ -4322,7 +4321,7 @@ static int smack_key_permission(key_ref_t key_ref,
{
struct key *keyp;
struct smk_audit_info ad;
- struct smack_known *tkp = smk_of_task(cred->security);
+ struct smack_known *tkp = smk_of_task(smack_cred(cred));
int request = 0;
int rc;
@@ -4591,7 +4590,7 @@ static int smack_inode_copy_up(struct dentry *dentry, struct cred **new)
return -ENOMEM;
}
- tsp = new_creds->security;
+ tsp = smack_cred(new_creds);
/*
* Get label from overlay inode and set it in create_sid
@@ -4619,8 +4618,8 @@ static int smack_dentry_create_files_as(struct dentry *dentry, int mode,
const struct cred *old,
struct cred *new)
{
- struct task_smack *otsp = old->security;
- struct task_smack *ntsp = new->security;
+ struct task_smack *otsp = smack_cred(old);
+ struct task_smack *ntsp = smack_cred(new);
struct inode_smack *isp;
int may;
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c
index f6482e53d55a..9d2dde608298 100644
--- a/security/smack/smackfs.c
+++ b/security/smack/smackfs.c
@@ -2208,14 +2208,14 @@ static const struct file_operations smk_logging_ops = {
static void *load_self_seq_start(struct seq_file *s, loff_t *pos)
{
- struct task_smack *tsp = current_security();
+ struct task_smack *tsp = smack_cred(current_cred());
return smk_seq_start(s, pos, &tsp->smk_rules);
}
static void *load_self_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
- struct task_smack *tsp = current_security();
+ struct task_smack *tsp = smack_cred(current_cred());
return smk_seq_next(s, v, pos, &tsp->smk_rules);
}
@@ -2262,7 +2262,7 @@ static int smk_open_load_self(struct inode *inode, struct file *file)
static ssize_t smk_write_load_self(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
- struct task_smack *tsp = current_security();
+ struct task_smack *tsp = smack_cred(current_cred());
return smk_write_rules_list(file, buf, count, ppos, &tsp->smk_rules,
&tsp->smk_rules_lock, SMK_FIXED24_FMT);
@@ -2414,14 +2414,14 @@ static const struct file_operations smk_load2_ops = {
static void *load_self2_seq_start(struct seq_file *s, loff_t *pos)
{
- struct task_smack *tsp = current_security();
+ struct task_smack *tsp = smack_cred(current_cred());
return smk_seq_start(s, pos, &tsp->smk_rules);
}
static void *load_self2_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
- struct task_smack *tsp = current_security();
+ struct task_smack *tsp = smack_cred(current_cred());
return smk_seq_next(s, v, pos, &tsp->smk_rules);
}
@@ -2467,7 +2467,7 @@ static int smk_open_load_self2(struct inode *inode, struct file *file)
static ssize_t smk_write_load_self2(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
- struct task_smack *tsp = current_security();
+ struct task_smack *tsp = smack_cred(current_cred());
return smk_write_rules_list(file, buf, count, ppos, &tsp->smk_rules,
&tsp->smk_rules_lock, SMK_LONG_FMT);
@@ -2681,14 +2681,14 @@ static const struct file_operations smk_syslog_ops = {
static void *relabel_self_seq_start(struct seq_file *s, loff_t *pos)
{
- struct task_smack *tsp = current_security();
+ struct task_smack *tsp = smack_cred(current_cred());
return smk_seq_start(s, pos, &tsp->smk_relabel);
}
static void *relabel_self_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
- struct task_smack *tsp = current_security();
+ struct task_smack *tsp = smack_cred(current_cred());
return smk_seq_next(s, v, pos, &tsp->smk_relabel);
}
@@ -2736,7 +2736,7 @@ static int smk_open_relabel_self(struct inode *inode, struct file *file)
static ssize_t smk_write_relabel_self(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
- struct task_smack *tsp = current_security();
+ struct task_smack *tsp = smack_cred(current_cred());
char *data;
int rc;
LIST_HEAD(list_tmp);
--
2.17.1
Don't use the cred->security pointer directly.
Provide a helper function that provides the security blob pointer.
Signed-off-by: Casey Schaufler <[email protected]>
---
security/selinux/hooks.c | 54 +++++++++++++++----------------
security/selinux/include/objsec.h | 5 +++
security/selinux/xfrm.c | 4 +--
3 files changed, 34 insertions(+), 29 deletions(-)
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index ad9a9b8e9979..9d6cdd21acb6 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -228,7 +228,7 @@ static inline u32 cred_sid(const struct cred *cred)
{
const struct task_security_struct *tsec;
- tsec = cred->security;
+ tsec = selinux_cred(cred);
return tsec->sid;
}
@@ -464,7 +464,7 @@ static int may_context_mount_sb_relabel(u32 sid,
struct superblock_security_struct *sbsec,
const struct cred *cred)
{
- const struct task_security_struct *tsec = cred->security;
+ const struct task_security_struct *tsec = selinux_cred(cred);
int rc;
rc = avc_has_perm(&selinux_state,
@@ -483,7 +483,7 @@ static int may_context_mount_inode_relabel(u32 sid,
struct superblock_security_struct *sbsec,
const struct cred *cred)
{
- const struct task_security_struct *tsec = cred->security;
+ const struct task_security_struct *tsec = selinux_cred(cred);
int rc;
rc = avc_has_perm(&selinux_state,
tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
@@ -1949,7 +1949,7 @@ static int may_create(struct inode *dir,
struct dentry *dentry,
u16 tclass)
{
- const struct task_security_struct *tsec = current_security();
+ const struct task_security_struct *tsec = selinux_cred(current_cred());
struct inode_security_struct *dsec;
struct superblock_security_struct *sbsec;
u32 sid, newsid;
@@ -1971,7 +1971,7 @@ static int may_create(struct inode *dir,
if (rc)
return rc;
- rc = selinux_determine_inode_label(current_security(), dir,
+ rc = selinux_determine_inode_label(selinux_cred(current_cred()), dir,
&dentry->d_name, tclass, &newsid);
if (rc)
return rc;
@@ -2478,8 +2478,8 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
if (bprm->called_set_creds)
return 0;
- old_tsec = current_security();
- new_tsec = bprm->cred->security;
+ old_tsec = selinux_cred(current_cred());
+ new_tsec = selinux_cred(bprm->cred);
isec = inode_security(inode);
/* Default to the current task SID. */
@@ -2643,7 +2643,7 @@ static void selinux_bprm_committing_creds(struct linux_binprm *bprm)
struct rlimit *rlim, *initrlim;
int rc, i;
- new_tsec = bprm->cred->security;
+ new_tsec = selinux_cred(bprm->cred);
if (new_tsec->sid == new_tsec->osid)
return;
@@ -2686,7 +2686,7 @@ static void selinux_bprm_committing_creds(struct linux_binprm *bprm)
*/
static void selinux_bprm_committed_creds(struct linux_binprm *bprm)
{
- const struct task_security_struct *tsec = current_security();
+ const struct task_security_struct *tsec = selinux_cred(current_cred());
struct itimerval itimer;
u32 osid, sid;
int rc, i;
@@ -2989,7 +2989,7 @@ static int selinux_dentry_init_security(struct dentry *dentry, int mode,
u32 newsid;
int rc;
- rc = selinux_determine_inode_label(current_security(),
+ rc = selinux_determine_inode_label(selinux_cred(current_cred()),
d_inode(dentry->d_parent), name,
inode_mode_to_security_class(mode),
&newsid);
@@ -3009,14 +3009,14 @@ static int selinux_dentry_create_files_as(struct dentry *dentry, int mode,
int rc;
struct task_security_struct *tsec;
- rc = selinux_determine_inode_label(old->security,
+ rc = selinux_determine_inode_label(selinux_cred(old),
d_inode(dentry->d_parent), name,
inode_mode_to_security_class(mode),
&newsid);
if (rc)
return rc;
- tsec = new->security;
+ tsec = selinux_cred(new);
tsec->create_sid = newsid;
return 0;
}
@@ -3026,7 +3026,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
const char **name,
void **value, size_t *len)
{
- const struct task_security_struct *tsec = current_security();
+ const struct task_security_struct *tsec = selinux_cred(current_cred());
struct superblock_security_struct *sbsec;
u32 newsid, clen;
int rc;
@@ -3036,7 +3036,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
newsid = tsec->create_sid;
- rc = selinux_determine_inode_label(current_security(),
+ rc = selinux_determine_inode_label(selinux_cred(current_cred()),
dir, qstr,
inode_mode_to_security_class(inode->i_mode),
&newsid);
@@ -3498,7 +3498,7 @@ static int selinux_inode_copy_up(struct dentry *src, struct cred **new)
return -ENOMEM;
}
- tsec = new_creds->security;
+ tsec = selinux_cred(new_creds);
/* Get label from overlay inode and set it in create_sid */
selinux_inode_getsecid(d_inode(src), &sid);
tsec->create_sid = sid;
@@ -3918,7 +3918,7 @@ static int selinux_cred_alloc_blank(struct cred *cred, gfp_t gfp)
*/
static void selinux_cred_free(struct cred *cred)
{
- struct task_security_struct *tsec = cred->security;
+ struct task_security_struct *tsec = selinux_cred(cred);
/*
* cred->security == NULL if security_cred_alloc_blank() or
@@ -3938,7 +3938,7 @@ static int selinux_cred_prepare(struct cred *new, const struct cred *old,
const struct task_security_struct *old_tsec;
struct task_security_struct *tsec;
- old_tsec = old->security;
+ old_tsec = selinux_cred(old);
tsec = kmemdup(old_tsec, sizeof(struct task_security_struct), gfp);
if (!tsec)
@@ -3953,8 +3953,8 @@ static int selinux_cred_prepare(struct cred *new, const struct cred *old,
*/
static void selinux_cred_transfer(struct cred *new, const struct cred *old)
{
- const struct task_security_struct *old_tsec = old->security;
- struct task_security_struct *tsec = new->security;
+ const struct task_security_struct *old_tsec = selinux_cred(old);
+ struct task_security_struct *tsec = selinux_cred(new);
*tsec = *old_tsec;
}
@@ -3970,7 +3970,7 @@ static void selinux_cred_getsecid(const struct cred *c, u32 *secid)
*/
static int selinux_kernel_act_as(struct cred *new, u32 secid)
{
- struct task_security_struct *tsec = new->security;
+ struct task_security_struct *tsec = selinux_cred(new);
u32 sid = current_sid();
int ret;
@@ -3995,7 +3995,7 @@ static int selinux_kernel_act_as(struct cred *new, u32 secid)
static int selinux_kernel_create_files_as(struct cred *new, struct inode *inode)
{
struct inode_security_struct *isec = inode_security(inode);
- struct task_security_struct *tsec = new->security;
+ struct task_security_struct *tsec = selinux_cred(new);
u32 sid = current_sid();
int ret;
@@ -4544,7 +4544,7 @@ static int sock_has_perm(struct sock *sk, u32 perms)
static int selinux_socket_create(int family, int type,
int protocol, int kern)
{
- const struct task_security_struct *tsec = current_security();
+ const struct task_security_struct *tsec = selinux_cred(current_cred());
u32 newsid;
u16 secclass;
int rc;
@@ -4564,7 +4564,7 @@ static int selinux_socket_create(int family, int type,
static int selinux_socket_post_create(struct socket *sock, int family,
int type, int protocol, int kern)
{
- const struct task_security_struct *tsec = current_security();
+ const struct task_security_struct *tsec = selinux_cred(current_cred());
struct inode_security_struct *isec = inode_security_novalidate(SOCK_INODE(sock));
struct sk_security_struct *sksec;
u16 sclass = socket_type_to_security_class(family, type, protocol);
@@ -5442,7 +5442,7 @@ static int selinux_secmark_relabel_packet(u32 sid)
const struct task_security_struct *__tsec;
u32 tsid;
- __tsec = current_security();
+ __tsec = selinux_cred(current_cred());
tsid = __tsec->sid;
return avc_has_perm(&selinux_state,
@@ -6379,7 +6379,7 @@ static int selinux_getprocattr(struct task_struct *p,
unsigned len;
rcu_read_lock();
- __tsec = __task_cred(p)->security;
+ __tsec = selinux_cred(__task_cred(p));
if (current != p) {
error = avc_has_perm(&selinux_state,
@@ -6502,7 +6502,7 @@ static int selinux_setprocattr(const char *name, void *value, size_t size)
operation. See selinux_bprm_set_creds for the execve
checks and may_create for the file creation checks. The
operation will then fail if the context is not permitted. */
- tsec = new->security;
+ tsec = selinux_cred(new);
if (!strcmp(name, "exec")) {
tsec->exec_sid = sid;
} else if (!strcmp(name, "fscreate")) {
@@ -6631,7 +6631,7 @@ static int selinux_key_alloc(struct key *k, const struct cred *cred,
if (!ksec)
return -ENOMEM;
- tsec = cred->security;
+ tsec = selinux_cred(cred);
if (tsec->keycreate_sid)
ksec->sid = tsec->keycreate_sid;
else
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index cc5e26b0161b..734b6833bdff 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -158,4 +158,9 @@ struct bpf_security_struct {
u32 sid; /*SID of bpf obj creater*/
};
+static inline struct task_security_struct *selinux_cred(const struct cred *cred)
+{
+ return cred->security;
+}
+
#endif /* _SELINUX_OBJSEC_H_ */
diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c
index 91dc3783ed94..8ffe7e1053c4 100644
--- a/security/selinux/xfrm.c
+++ b/security/selinux/xfrm.c
@@ -79,7 +79,7 @@ static int selinux_xfrm_alloc_user(struct xfrm_sec_ctx **ctxp,
gfp_t gfp)
{
int rc;
- const struct task_security_struct *tsec = current_security();
+ const struct task_security_struct *tsec = selinux_cred(current_cred());
struct xfrm_sec_ctx *ctx = NULL;
u32 str_len;
@@ -138,7 +138,7 @@ static void selinux_xfrm_free(struct xfrm_sec_ctx *ctx)
*/
static int selinux_xfrm_delete(struct xfrm_sec_ctx *ctx)
{
- const struct task_security_struct *tsec = current_security();
+ const struct task_security_struct *tsec = selinux_cred(current_cred());
if (!ctx)
return 0;
--
2.17.1
Don't use the inode->i_security pointer directly.
Provide a helper function that provides the security blob pointer.
Signed-off-by: Casey Schaufler <[email protected]>
---
security/selinux/hooks.c | 26 +++++++++++++-------------
security/selinux/include/objsec.h | 6 ++++++
security/selinux/selinuxfs.c | 4 ++--
3 files changed, 21 insertions(+), 15 deletions(-)
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 3468b4592036..2720fe3ebf5f 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -276,7 +276,7 @@ static int __inode_security_revalidate(struct inode *inode,
struct dentry *dentry,
bool may_sleep)
{
- struct inode_security_struct *isec = inode->i_security;
+ struct inode_security_struct *isec = selinux_inode(inode);
might_sleep_if(may_sleep);
@@ -297,7 +297,7 @@ static int __inode_security_revalidate(struct inode *inode,
static struct inode_security_struct *inode_security_novalidate(struct inode *inode)
{
- return inode->i_security;
+ return selinux_inode(inode);
}
static struct inode_security_struct *inode_security_rcu(struct inode *inode, bool rcu)
@@ -307,7 +307,7 @@ static struct inode_security_struct *inode_security_rcu(struct inode *inode, boo
error = __inode_security_revalidate(inode, NULL, !rcu);
if (error)
return ERR_PTR(error);
- return inode->i_security;
+ return selinux_inode(inode);
}
/*
@@ -316,14 +316,14 @@ static struct inode_security_struct *inode_security_rcu(struct inode *inode, boo
static struct inode_security_struct *inode_security(struct inode *inode)
{
__inode_security_revalidate(inode, NULL, true);
- return inode->i_security;
+ return selinux_inode(inode);
}
static struct inode_security_struct *backing_inode_security_novalidate(struct dentry *dentry)
{
struct inode *inode = d_backing_inode(dentry);
- return inode->i_security;
+ return selinux_inode(inode);
}
/*
@@ -334,7 +334,7 @@ static struct inode_security_struct *backing_inode_security(struct dentry *dentr
struct inode *inode = d_backing_inode(dentry);
__inode_security_revalidate(inode, dentry, true);
- return inode->i_security;
+ return selinux_inode(inode);
}
static void inode_free_rcu(struct rcu_head *head)
@@ -347,7 +347,7 @@ static void inode_free_rcu(struct rcu_head *head)
static void inode_free_security(struct inode *inode)
{
- struct inode_security_struct *isec = inode->i_security;
+ struct inode_security_struct *isec = selinux_inode(inode);
struct superblock_security_struct *sbsec = inode->i_sb->s_security;
/*
@@ -1501,7 +1501,7 @@ static int selinux_genfs_get_sid(struct dentry *dentry,
static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry)
{
struct superblock_security_struct *sbsec = NULL;
- struct inode_security_struct *isec = inode->i_security;
+ struct inode_security_struct *isec = selinux_inode(inode);
u32 task_sid, sid = 0;
u16 sclass;
struct dentry *dentry;
@@ -1801,7 +1801,7 @@ static int inode_has_perm(const struct cred *cred,
return 0;
sid = cred_sid(cred);
- isec = inode->i_security;
+ isec = selinux_inode(inode);
return avc_has_perm(&selinux_state,
sid, isec->sid, isec->sclass, perms, adp);
@@ -3029,7 +3029,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
/* Possibly defer initialization to selinux_complete_init. */
if (sbsec->flags & SE_SBINITIALIZED) {
- struct inode_security_struct *isec = inode->i_security;
+ struct inode_security_struct *isec = selinux_inode(inode);
isec->sclass = inode_mode_to_security_class(inode->i_mode);
isec->sid = newsid;
isec->initialized = LABEL_INITIALIZED;
@@ -3129,7 +3129,7 @@ static noinline int audit_inode_permission(struct inode *inode,
unsigned flags)
{
struct common_audit_data ad;
- struct inode_security_struct *isec = inode->i_security;
+ struct inode_security_struct *isec = selinux_inode(inode);
int rc;
ad.type = LSM_AUDIT_DATA_INODE;
@@ -4150,7 +4150,7 @@ static int selinux_task_kill(struct task_struct *p, struct siginfo *info,
static void selinux_task_to_inode(struct task_struct *p,
struct inode *inode)
{
- struct inode_security_struct *isec = inode->i_security;
+ struct inode_security_struct *isec = selinux_inode(inode);
u32 sid = task_sid(p);
spin_lock(&isec->lock);
@@ -6529,7 +6529,7 @@ static void selinux_release_secctx(char *secdata, u32 seclen)
static void selinux_inode_invalidate_secctx(struct inode *inode)
{
- struct inode_security_struct *isec = inode->i_security;
+ struct inode_security_struct *isec = selinux_inode(inode);
spin_lock(&isec->lock);
isec->initialized = LABEL_INVALID;
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index 2586fbc7e38c..3304a1ee58a4 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -172,4 +172,10 @@ static inline struct file_security_struct *selinux_file(const struct file *file)
return file->f_security;
}
+static inline struct inode_security_struct *selinux_inode(
+ const struct inode *inode)
+{
+ return inode->i_security;
+}
+
#endif /* _SELINUX_OBJSEC_H_ */
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index b5665bdc29fc..cc434e45eaae 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -1379,7 +1379,7 @@ static int sel_make_bools(struct selinux_fs_info *fsi)
goto out;
}
- isec = (struct inode_security_struct *)inode->i_security;
+ isec = selinux_inode(inode);
ret = security_genfs_sid(fsi->state, "selinuxfs", page,
SECCLASS_FILE, &sid);
if (ret) {
@@ -1954,7 +1954,7 @@ static int sel_fill_super(struct super_block *sb, void *data, int silent)
}
inode->i_ino = ++fsi->last_ino;
- isec = (struct inode_security_struct *)inode->i_security;
+ isec = selinux_inode(inode);
isec->sid = SECINITSID_DEVNULL;
isec->sclass = SECCLASS_CHR_FILE;
isec->initialized = LABEL_INITIALIZED;
--
2.17.1
Move management of the inode->i_security blob out
of the individual security modules and into the security
infrastructure. Instead of allocating the blobs from within
the modules the modules tell the infrastructure how much
space is required, and the space is allocated there.
Signed-off-by: Casey Schaufler <[email protected]>
---
include/linux/lsm_hooks.h | 3 ++
security/security.c | 83 ++++++++++++++++++++++++++++++-
security/selinux/hooks.c | 32 +-----------
security/selinux/include/objsec.h | 5 +-
security/smack/smack_lsm.c | 70 ++++----------------------
5 files changed, 98 insertions(+), 95 deletions(-)
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 167ffbd4d0c0..416b20c3795b 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -2030,6 +2030,7 @@ struct security_hook_list {
struct lsm_blob_sizes {
int lbs_cred;
int lbs_file;
+ int lbs_inode;
};
/*
@@ -2092,9 +2093,11 @@ static inline void loadpin_add_hooks(void) { };
#endif
extern int lsm_cred_alloc(struct cred *cred, gfp_t gfp);
+extern int lsm_inode_alloc(struct inode *inode);
#ifdef CONFIG_SECURITY
void lsm_early_cred(struct cred *cred);
+void lsm_early_inode(struct inode *inode);
#endif
#endif /* ! __LINUX_LSM_HOOKS_H */
diff --git a/security/security.c b/security/security.c
index 5430cae73cf6..2501cdcbebff 100644
--- a/security/security.c
+++ b/security/security.c
@@ -41,6 +41,7 @@ struct security_hook_heads security_hook_heads __lsm_ro_after_init;
static ATOMIC_NOTIFIER_HEAD(lsm_notifier_chain);
static struct kmem_cache *lsm_file_cache;
+static struct kmem_cache *lsm_inode_cache;
char *lsm_names;
static struct lsm_blob_sizes blob_sizes;
@@ -101,6 +102,10 @@ int __init security_init(void)
lsm_file_cache = kmem_cache_create("lsm_file_cache",
blob_sizes.lbs_file, 0,
SLAB_PANIC, NULL);
+ if (blob_sizes.lbs_inode)
+ lsm_inode_cache = kmem_cache_create("lsm_inode_cache",
+ blob_sizes.lbs_inode, 0,
+ SLAB_PANIC, NULL);
/*
* The second call to a module specific init function
* adds hooks to the hook lists and does any other early
@@ -111,6 +116,7 @@ int __init security_init(void)
#ifdef CONFIG_SECURITY_LSM_DEBUG
pr_info("LSM: cred blob size = %d\n", blob_sizes.lbs_cred);
pr_info("LSM: file blob size = %d\n", blob_sizes.lbs_file);
+ pr_info("LSM: inode blob size = %d\n", blob_sizes.lbs_inode);
#endif
return 0;
@@ -288,6 +294,13 @@ void __init security_add_blobs(struct lsm_blob_sizes *needed)
{
lsm_set_size(&needed->lbs_cred, &blob_sizes.lbs_cred);
lsm_set_size(&needed->lbs_file, &blob_sizes.lbs_file);
+ /*
+ * The inode blob gets an rcu_head in addition to
+ * what the modules might need.
+ */
+ if (needed->lbs_inode && blob_sizes.lbs_inode == 0)
+ blob_sizes.lbs_inode = sizeof(struct rcu_head);
+ lsm_set_size(&needed->lbs_inode, &blob_sizes.lbs_inode);
}
/**
@@ -311,6 +324,46 @@ int lsm_file_alloc(struct file *file)
return 0;
}
+/**
+ * lsm_inode_alloc - allocate a composite inode blob
+ * @inode: the inode that needs a blob
+ *
+ * Allocate the inode blob for all the modules
+ *
+ * Returns 0, or -ENOMEM if memory can't be allocated.
+ */
+int lsm_inode_alloc(struct inode *inode)
+{
+ if (!lsm_inode_cache) {
+ inode->i_security = NULL;
+ return 0;
+ }
+
+ inode->i_security = kmem_cache_zalloc(lsm_inode_cache, GFP_NOFS);
+ if (inode->i_security == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+/**
+ * lsm_early_inode - during initialization allocate a composite inode blob
+ * @inode: the inode that needs a blob
+ *
+ * Allocate the inode blob for all the modules if it's not already there
+ */
+void lsm_early_inode(struct inode *inode)
+{
+ int rc;
+
+ if (inode == NULL)
+ panic("%s: NULL inode.\n", __func__);
+ if (inode->i_security != NULL)
+ return;
+ rc = lsm_inode_alloc(inode);
+ if (rc)
+ panic("%s: Early inode alloc failed.\n", __func__);
+}
+
/*
* Hook list operation macros.
*
@@ -557,14 +610,40 @@ EXPORT_SYMBOL(security_sb_parse_opts_str);
int security_inode_alloc(struct inode *inode)
{
- inode->i_security = NULL;
- return call_int_hook(inode_alloc_security, 0, inode);
+ int rc = lsm_inode_alloc(inode);
+
+ if (unlikely(rc))
+ return rc;
+ rc = call_int_hook(inode_alloc_security, 0, inode);
+ if (unlikely(rc))
+ security_inode_free(inode);
+ return rc;
+}
+
+static void inode_free_by_rcu(struct rcu_head *head)
+{
+ /*
+ * The rcu head is at the start of the inode blob
+ */
+ kmem_cache_free(lsm_inode_cache, head);
}
void security_inode_free(struct inode *inode)
{
integrity_inode_free(inode);
call_void_hook(inode_free_security, inode);
+ /*
+ * The inode may still be referenced in a path walk and
+ * a call to security_inode_permission() can be made
+ * after inode_free_security() is called. Ideally, the VFS
+ * wouldn't do this, but fixing that is a much harder
+ * job. For now, simply free the i_security via RCU, and
+ * leave the current inode->i_security pointer intact.
+ * The inode will be freed after the RCU grace period too.
+ */
+ if (inode->i_security)
+ call_rcu((struct rcu_head *)inode->i_security,
+ inode_free_by_rcu);
}
int security_dentry_init_security(struct dentry *dentry, int mode,
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 2720fe3ebf5f..0b593030d9f2 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -148,8 +148,6 @@ static int __init checkreqprot_setup(char *str)
}
__setup("checkreqprot=", checkreqprot_setup);
-static struct kmem_cache *sel_inode_cache;
-
/**
* selinux_secmark_enabled - Check to see if SECMARK is currently enabled
*
@@ -245,13 +243,9 @@ static inline u32 task_sid(const struct task_struct *task)
static int inode_alloc_security(struct inode *inode)
{
- struct inode_security_struct *isec;
+ struct inode_security_struct *isec = selinux_inode(inode);
u32 sid = current_sid();
- isec = kmem_cache_zalloc(sel_inode_cache, GFP_NOFS);
- if (!isec)
- return -ENOMEM;
-
spin_lock_init(&isec->lock);
INIT_LIST_HEAD(&isec->list);
isec->inode = inode;
@@ -259,7 +253,6 @@ static int inode_alloc_security(struct inode *inode)
isec->sclass = SECCLASS_FILE;
isec->task_sid = sid;
isec->initialized = LABEL_INVALID;
- inode->i_security = isec;
return 0;
}
@@ -337,14 +330,6 @@ static struct inode_security_struct *backing_inode_security(struct dentry *dentr
return selinux_inode(inode);
}
-static void inode_free_rcu(struct rcu_head *head)
-{
- struct inode_security_struct *isec;
-
- isec = container_of(head, struct inode_security_struct, rcu);
- kmem_cache_free(sel_inode_cache, isec);
-}
-
static void inode_free_security(struct inode *inode)
{
struct inode_security_struct *isec = selinux_inode(inode);
@@ -365,17 +350,6 @@ static void inode_free_security(struct inode *inode)
list_del_init(&isec->list);
spin_unlock(&sbsec->isec_lock);
}
-
- /*
- * The inode may still be referenced in a path walk and
- * a call to selinux_inode_permission() can be made
- * after inode_free_security() is called. Ideally, the VFS
- * wouldn't do this, but fixing that is a much harder
- * job. For now, simply free the i_security via RCU, and
- * leave the current inode->i_security pointer intact.
- * The inode will be freed after the RCU grace period too.
- */
- call_rcu(&isec->rcu, inode_free_rcu);
}
static int file_alloc_security(struct file *file)
@@ -6840,6 +6814,7 @@ static void selinux_bpf_prog_free(struct bpf_prog_aux *aux)
struct lsm_blob_sizes selinux_blob_sizes = {
.lbs_cred = sizeof(struct task_security_struct),
.lbs_file = sizeof(struct file_security_struct),
+ .lbs_inode = sizeof(struct inode_security_struct),
};
static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
@@ -7109,9 +7084,6 @@ static __init int selinux_init(void)
default_noexec = !(VM_DATA_DEFAULT_FLAGS & VM_EXEC);
- sel_inode_cache = kmem_cache_create("selinux_inode_security",
- sizeof(struct inode_security_struct),
- 0, SLAB_PANIC, NULL);
avc_init();
avtab_cache_init();
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index 3304a1ee58a4..7a3d18fa9b13 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -59,10 +59,7 @@ enum label_initialized {
struct inode_security_struct {
struct inode *inode; /* back pointer to inode object */
- union {
- struct list_head list; /* list of inode_security_struct */
- struct rcu_head rcu; /* for freeing the inode_security_struct */
- };
+ struct list_head list; /* list of inode_security_struct */
u32 task_sid; /* SID of creating task */
u32 sid; /* SID of this object */
u16 sclass; /* security class of this object */
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 364699ad55b9..6617abb51732 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -288,24 +288,18 @@ static struct smack_known *smk_fetch(const char *name, struct inode *ip,
}
/**
- * new_inode_smack - allocate an inode security blob
+ * init_inode_smack - initialize an inode security blob
+ * @isp: the blob to initialize
* @skp: a pointer to the Smack label entry to use in the blob
*
- * Returns the new blob or NULL if there's no memory available
*/
-static struct inode_smack *new_inode_smack(struct smack_known *skp)
+static void init_inode_smack(struct inode *inode, struct smack_known *skp)
{
- struct inode_smack *isp;
-
- isp = kmem_cache_zalloc(smack_inode_cache, GFP_NOFS);
- if (isp == NULL)
- return NULL;
+ struct inode_smack *isp = smack_inode(inode);
isp->smk_inode = skp;
isp->smk_flags = 0;
mutex_init(&isp->smk_lock);
-
- return isp;
}
/**
@@ -824,17 +818,13 @@ static int smack_set_mnt_opts(struct super_block *sb,
/*
* Initialize the root inode.
*/
- isp = smack_inode(inode);
- if (isp == NULL) {
- isp = new_inode_smack(sp->smk_root);
- if (isp == NULL)
- return -ENOMEM;
- inode->i_security = isp;
- } else
- isp->smk_inode = sp->smk_root;
+ lsm_early_inode(inode);
+ init_inode_smack(inode, sp->smk_root);
- if (transmute)
+ if (transmute) {
+ isp = smack_inode(inode);
isp->smk_flags |= SMK_INODE_TRANSMUTE;
+ }
return 0;
}
@@ -963,48 +953,10 @@ static int smack_inode_alloc_security(struct inode *inode)
{
struct smack_known *skp = smk_of_current();
- inode->i_security = new_inode_smack(skp);
- if (inode->i_security == NULL)
- return -ENOMEM;
+ init_inode_smack(inode, skp);
return 0;
}
-/**
- * smack_inode_free_rcu - Free inode_smack blob from cache
- * @head: the rcu_head for getting inode_smack pointer
- *
- * Call back function called from call_rcu() to free
- * the i_security blob pointer in inode
- */
-static void smack_inode_free_rcu(struct rcu_head *head)
-{
- struct inode_smack *issp;
-
- issp = container_of(head, struct inode_smack, smk_rcu);
- kmem_cache_free(smack_inode_cache, issp);
-}
-
-/**
- * smack_inode_free_security - free an inode blob using call_rcu()
- * @inode: the inode with a blob
- *
- * Clears the blob pointer in inode using RCU
- */
-static void smack_inode_free_security(struct inode *inode)
-{
- struct inode_smack *issp = smack_inode(inode);
-
- /*
- * The inode may still be referenced in a path walk and
- * a call to smack_inode_permission() can be made
- * after smack_inode_free_security() is called.
- * To avoid race condition free the i_security via RCU
- * and leave the current inode->i_security pointer intact.
- * The inode will be freed after the RCU grace period too.
- */
- call_rcu(&issp->smk_rcu, smack_inode_free_rcu);
-}
-
/**
* smack_inode_init_security - copy out the smack from an inode
* @inode: the newly created inode
@@ -4619,6 +4571,7 @@ static int smack_dentry_create_files_as(struct dentry *dentry, int mode,
struct lsm_blob_sizes smack_blob_sizes = {
.lbs_cred = sizeof(struct task_smack),
.lbs_file = sizeof(struct smack_known *),
+ .lbs_inode = sizeof(struct inode_smack),
};
static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
@@ -4637,7 +4590,6 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(bprm_set_creds, smack_bprm_set_creds),
LSM_HOOK_INIT(inode_alloc_security, smack_inode_alloc_security),
- LSM_HOOK_INIT(inode_free_security, smack_inode_free_security),
LSM_HOOK_INIT(inode_init_security, smack_inode_init_security),
LSM_HOOK_INIT(inode_link, smack_inode_link),
LSM_HOOK_INIT(inode_unlink, smack_inode_unlink),
--
2.17.1
Two proposed security modules require the ability to
share security blobs with existing "major" security modules.
These modules, S.A.R.A and LandLock, provide significantly
different services than SELinux, Smack or AppArmor. Using
either in conjunction with the existing modules is quite
reasonable. S.A.R.A requires access to the cred blob, while
LandLock uses the cred, file and inode blobs.
The use of the cred, file and inode blobs has been
abstracted in preceding patches in the series. This
patch teaches the affected security modules how to access
the part of the blob set aside for their use in the case
where blobs are shared. The configuration option
CONFIG_SECURITY_STACKING identifies systems where the
blobs may be shared.
The mechanism for selecting which security modules are
active has been changed to allow non-conflicting "major"
security modules to be used together. At this time the
TOMOYO module can safely be used with any of the others.
The two new modules would be non-conflicting as well.
Signed-off-by: Casey Schaufler <[email protected]>
---
Documentation/admin-guide/LSM/index.rst | 14 +++--
include/linux/lsm_hooks.h | 2 +-
security/Kconfig | 81 +++++++++++++++++++++++++
security/apparmor/include/cred.h | 8 +++
security/apparmor/include/file.h | 9 ++-
security/apparmor/include/lib.h | 4 ++
security/apparmor/lsm.c | 8 ++-
security/security.c | 30 ++++++++-
security/selinux/hooks.c | 3 +-
security/selinux/include/objsec.h | 18 +++++-
security/smack/smack.h | 19 +++++-
security/smack/smack_lsm.c | 17 +++---
security/tomoyo/common.h | 12 +++-
security/tomoyo/tomoyo.c | 3 +-
14 files changed, 200 insertions(+), 28 deletions(-)
diff --git a/Documentation/admin-guide/LSM/index.rst b/Documentation/admin-guide/LSM/index.rst
index 9842e21afd4a..d3d8af174042 100644
--- a/Documentation/admin-guide/LSM/index.rst
+++ b/Documentation/admin-guide/LSM/index.rst
@@ -17,10 +17,16 @@ 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.
-The Linux capabilities modules will always be included. This may be
-followed by any number of "minor" modules and at most one "major" module.
-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.
+
+Security modules that do not use the security data blobs maintained
+by the LSM infrastructure are considered "minor" modules. These may be
+included at compile time and stacked explicitly. Security modules that
+use the LSM maintained security blobs are considered "major" modules.
+These may only be stacked if the CONFIG_LSM_STACKED configuration
+option is used. If this is chosen all of the security modules selected
+will be used.
A list of the active security modules can be found by reading
``/sys/kernel/security/lsm``. This is a comma separated list, and
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 416b20c3795b..dddcced54fea 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -2079,7 +2079,7 @@ static inline void security_delete_hooks(struct security_hook_list *hooks,
#define __lsm_ro_after_init __ro_after_init
#endif /* CONFIG_SECURITY_WRITABLE_HOOKS */
-extern int __init security_module_enable(const char *module);
+extern bool __init security_module_enable(const char *lsm, const bool stacked);
extern void __init capability_add_hooks(void);
#ifdef CONFIG_SECURITY_YAMA
extern void __init yama_add_hooks(void);
diff --git a/security/Kconfig b/security/Kconfig
index 22f7664c4977..ed48025ae9e0 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -36,6 +36,28 @@ config SECURITY_WRITABLE_HOOKS
bool
default n
+config SECURITY_STACKING
+ bool "Security module stacking"
+ depends on SECURITY
+ help
+ Allows multiple major security modules to be stacked.
+ Modules are invoked in the order registered with a
+ "bail on fail" policy, in which the infrastructure
+ will stop processing once a denial is detected. Not
+ all modules can be stacked. SELinux, Smack and AppArmor are
+ known to be incompatible. User space components may
+ have trouble identifying the security module providing
+ data in some cases.
+
+ If you select this option you will have to select which
+ of the stackable modules you wish to be active. The
+ "Default security module" will be ignored. The boot line
+ "security=" option can be used to specify that one of
+ the modules identifed for stacking should be used instead
+ of the entire stack.
+
+ If you are unsure how to answer this question, answer N.
+
config SECURITY_LSM_DEBUG
bool "Enable debugging of the LSM infrastructure"
depends on SECURITY
@@ -250,6 +272,9 @@ source security/yama/Kconfig
source security/integrity/Kconfig
+menu "Security Module Selection"
+ visible if !SECURITY_STACKING
+
choice
prompt "Default security module"
default DEFAULT_SECURITY_SELINUX if SECURITY_SELINUX
@@ -289,3 +314,59 @@ config DEFAULT_SECURITY
endmenu
+menu "Security Module Stack"
+ visible if SECURITY_STACKING
+
+choice
+ prompt "Stacked 'extreme' security module"
+ default SECURITY_SELINUX_STACKED if SECURITY_SELINUX
+ default SECURITY_SMACK_STACKED if SECURITY_SMACK
+ default SECURITY_APPARMOR_STACKED if SECURITY_APPARMOR
+
+ help
+ Enable an extreme security module. These modules cannot
+ be used at the same time.
+
+ config SECURITY_SELINUX_STACKED
+ bool "SELinux" if SECURITY_SELINUX=y
+ help
+ This option instructs the system to use the SELinux checks.
+ At this time the Smack security module is incompatible with this
+ module.
+ At this time the AppArmor security module is incompatible with this
+ module.
+
+ config SECURITY_SMACK_STACKED
+ bool "Simplified Mandatory Access Control" if SECURITY_SMACK=y
+ help
+ This option instructs the system to use the Smack checks.
+ At this time the SELinux security module is incompatible with this
+ module.
+ At this time the AppArmor security module is incompatible with this
+ module.
+
+ config SECURITY_APPARMOR_STACKED
+ bool "AppArmor" if SECURITY_APPARMOR=y
+ help
+ This option instructs the system to use the AppArmor checks.
+ At this time the SELinux security module is incompatible with this
+ module.
+ At this time the Smack security module is incompatible with this
+ module.
+
+endchoice
+
+config SECURITY_TOMOYO_STACKED
+ bool "TOMOYO support is enabled by default"
+ depends on SECURITY_TOMOYO && SECURITY_STACKING
+ default n
+ help
+ This option instructs the system to use the TOMOYO checks.
+ If not selected the module will not be invoked.
+ Stacked security modules may interact in unexpected ways.
+
+ If you are unsure how to answer this question, answer N.
+
+endmenu
+
+endmenu
diff --git a/security/apparmor/include/cred.h b/security/apparmor/include/cred.h
index a90eae76d7c1..be7575adf6f0 100644
--- a/security/apparmor/include/cred.h
+++ b/security/apparmor/include/cred.h
@@ -25,7 +25,11 @@
static inline struct aa_label *cred_label(const struct cred *cred)
{
+#ifdef CONFIG_SECURITY_STACKING
+ struct aa_label **blob = cred->security + apparmor_blob_sizes.lbs_cred;
+#else
struct aa_label **blob = cred->security;
+#endif
AA_BUG(!blob);
return *blob;
@@ -34,7 +38,11 @@ static inline struct aa_label *cred_label(const struct cred *cred)
static inline void set_cred_label(const struct cred *cred,
struct aa_label *label)
{
+#ifdef CONFIG_SECURITY_STACKING
+ struct aa_label **blob = cred->security + apparmor_blob_sizes.lbs_cred;
+#else
struct aa_label **blob = cred->security;
+#endif
AA_BUG(!blob);
*blob = label;
diff --git a/security/apparmor/include/file.h b/security/apparmor/include/file.h
index 4c2c8ac8842f..aeb757471cc0 100644
--- a/security/apparmor/include/file.h
+++ b/security/apparmor/include/file.h
@@ -32,7 +32,14 @@ struct path;
AA_MAY_CHMOD | AA_MAY_CHOWN | AA_MAY_LOCK | \
AA_EXEC_MMAP | AA_MAY_LINK)
-#define file_ctx(X) ((struct aa_file_ctx *)(X)->f_security)
+static inline struct aa_file_ctx *file_ctx(struct file *file)
+{
+#ifdef CONFIG_SECURITY_STACKING
+ return file->f_security + apparmor_blob_sizes.lbs_file;
+#else
+ return file->f_security;
+#endif
+}
/* struct aa_file_ctx - the AppArmor context the file was opened in
* @lock: lock to update the ctx
diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h
index 6505e1ad9e23..bbe9b384d71d 100644
--- a/security/apparmor/include/lib.h
+++ b/security/apparmor/include/lib.h
@@ -16,6 +16,7 @@
#include <linux/slab.h>
#include <linux/fs.h>
+#include <linux/lsm_hooks.h>
#include "match.h"
@@ -55,6 +56,9 @@ const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name,
size_t *ns_len);
void aa_info_message(const char *str);
+/* Security blob offsets */
+extern struct lsm_blob_sizes apparmor_blob_sizes;
+
/**
* aa_strneq - compare null terminated @str to a non null terminated substring
* @str: a null terminated string
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 15716b6ff860..36d8386170e8 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -1553,7 +1553,9 @@ static int __init apparmor_init(void)
int error;
if (!finish) {
- if (apparmor_enabled && security_module_enable("apparmor"))
+ if (apparmor_enabled &&
+ security_module_enable("apparmor",
+ IS_ENABLED(CONFIG_SECURITY_APPARMOR_STACKED)))
security_add_blobs(&apparmor_blob_sizes);
else
apparmor_enabled = false;
@@ -1561,7 +1563,9 @@ static int __init apparmor_init(void)
return 0;
}
- if (!apparmor_enabled || !security_module_enable("apparmor")) {
+ if (!apparmor_enabled ||
+ !security_module_enable("apparmor",
+ IS_ENABLED(CONFIG_SECURITY_APPARMOR_STACKED))) {
aa_info_message("AppArmor disabled by boot time parameter");
apparmor_enabled = false;
return 0;
diff --git a/security/security.c b/security/security.c
index 2501cdcbebff..06bed74d1ed0 100644
--- a/security/security.c
+++ b/security/security.c
@@ -36,6 +36,7 @@
/* Maximum number of letters for an LSM name string */
#define SECURITY_NAME_MAX 10
+#define MODULE_STACK "(stacking)"
struct security_hook_heads security_hook_heads __lsm_ro_after_init;
static ATOMIC_NOTIFIER_HEAD(lsm_notifier_chain);
@@ -48,7 +49,11 @@ static struct lsm_blob_sizes blob_sizes;
/* Boot-time LSM user choice */
static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =
+#ifdef CONFIG_SECURITY_STACKING
+ MODULE_STACK;
+#else
CONFIG_DEFAULT_SECURITY;
+#endif
static void __init do_security_initcalls(void)
{
@@ -169,6 +174,7 @@ static int lsm_append(char *new, char **result)
/**
* security_module_enable - Load given security module on boot ?
* @module: the name of the module
+ * @stacked: indicates that the module wants to be stacked
*
* Each LSM must pass this method before registering its own operations
* to avoid security registration races. This method may also be used
@@ -184,9 +190,29 @@ static int lsm_append(char *new, char **result)
*
* Otherwise, return false.
*/
-int __init security_module_enable(const char *module)
+bool __init security_module_enable(const char *lsm, const bool stacked)
{
- return !strcmp(module, chosen_lsm);
+#ifdef CONFIG_SECURITY_STACKING
+ /*
+ * Module defined on the command line security=XXXX
+ */
+ if (strcmp(chosen_lsm, MODULE_STACK)) {
+ if (!strcmp(lsm, chosen_lsm)) {
+ pr_info("Command line sets the %s security module.\n",
+ lsm);
+ return true;
+ }
+ return false;
+ }
+ /*
+ * Module configured as stacked.
+ */
+ return stacked;
+#else
+ if (strcmp(lsm, chosen_lsm) == 0)
+ return true;
+ return false;
+#endif
}
/**
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 0b593030d9f2..1de307286bcc 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -7055,7 +7055,8 @@ static __init int selinux_init(void)
{
static int finish;
- if (!security_module_enable("selinux")) {
+ if (!security_module_enable("selinux",
+ IS_ENABLED(CONFIG_SECURITY_SELINUX_STACKED))) {
selinux_enabled = 0;
return 0;
}
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index 7a3d18fa9b13..24e14560a765 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -161,18 +161,30 @@ struct bpf_security_struct {
extern struct lsm_blob_sizes selinux_blob_sizes;
static inline struct task_security_struct *selinux_cred(const struct cred *cred)
{
- return cred->security;
+#ifdef CONFIG_SECURITY_STACKING
+ return cred->security + selinux_blob_sizes.lbs_cred;
+#else
+ return cred->security;
+#endif
}
static inline struct file_security_struct *selinux_file(const struct file *file)
{
- return file->f_security;
+#ifdef CONFIG_SECURITY_STACKING
+ return file->f_security + selinux_blob_sizes.lbs_file;
+#else
+ return file->f_security;
+#endif
}
static inline struct inode_security_struct *selinux_inode(
const struct inode *inode)
{
- return inode->i_security;
+#ifdef CONFIG_SECURITY_STACKING
+ return inode->i_security + selinux_blob_sizes.lbs_inode;
+#else
+ return inode->i_security;
+#endif
}
#endif /* _SELINUX_OBJSEC_H_ */
diff --git a/security/smack/smack.h b/security/smack/smack.h
index 5da5bd1b9b47..6513a71c5ca1 100644
--- a/security/smack/smack.h
+++ b/security/smack/smack.h
@@ -337,6 +337,7 @@ extern struct smack_known *smack_syslog_label;
extern struct smack_known *smack_unconfined;
#endif
extern int smack_ptrace_rule;
+extern struct lsm_blob_sizes smack_blob_sizes;
extern struct smack_known smack_known_floor;
extern struct smack_known smack_known_hat;
@@ -359,17 +360,29 @@ extern struct hlist_head smack_known_hash[SMACK_HASH_SLOTS];
static inline struct task_smack *smack_cred(const struct cred *cred)
{
- return cred->security;
+#ifdef CONFIG_SECURITY_STACKING
+ return cred->security + smack_blob_sizes.lbs_cred;
+#else
+ return cred->security;
+#endif
}
static inline struct smack_known **smack_file(const struct file *file)
{
- return file->f_security;
+#ifdef CONFIG_SECURITY_STACKING
+ return file->f_security + smack_blob_sizes.lbs_file;
+#else
+ return file->f_security;
+#endif
}
static inline struct inode_smack *smack_inode(const struct inode *inode)
{
- return inode->i_security;
+#ifdef CONFIG_SECURITY_STACKING
+ return inode->i_security + smack_blob_sizes.lbs_inode;
+#else
+ return inode->i_security;
+#endif
}
/*
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 6617abb51732..be14540ce09c 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -3493,18 +3493,16 @@ static int smack_getprocattr(struct task_struct *p, 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
return -EINVAL;
- cp = kstrdup(skp->smk_known, GFP_KERNEL);
- if (cp == NULL)
- return -ENOMEM;
-
- slen = strlen(cp);
*value = cp;
- return slen;
+ return strlen(cp);
}
/**
@@ -4754,7 +4752,8 @@ static __init int smack_init(void)
struct cred *cred = (struct cred *) current->cred;
struct task_smack *tsp;
- if (!security_module_enable("smack"))
+ if (!security_module_enable("smack",
+ IS_ENABLED(CONFIG_SECURITY_SMACK_STACKED)))
return 0;
if (!finish) {
diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h
index 0110bebe86e2..f386f92c57c5 100644
--- a/security/tomoyo/common.h
+++ b/security/tomoyo/common.h
@@ -1087,6 +1087,7 @@ extern struct tomoyo_domain_info tomoyo_kernel_domain;
extern struct tomoyo_policy_namespace tomoyo_kernel_namespace;
extern unsigned int tomoyo_memory_quota[TOMOYO_MAX_MEMORY_STAT];
extern unsigned int tomoyo_memory_used[TOMOYO_MAX_MEMORY_STAT];
+extern struct lsm_blob_sizes tomoyo_blob_sizes;
/********** Inlined functions. **********/
@@ -1206,7 +1207,11 @@ static inline void tomoyo_put_group(struct tomoyo_group *group)
*/
static inline struct tomoyo_domain_info **tomoyo_cred(const struct cred *cred)
{
+#ifdef CONFIG_SECURITY_STACKING
+ return cred->security + tomoyo_blob_sizes.lbs_cred;
+#else
return cred->security;
+#endif
}
/**
@@ -1216,8 +1221,13 @@ static inline struct tomoyo_domain_info **tomoyo_cred(const struct cred *cred)
*/
static inline struct tomoyo_domain_info *tomoyo_domain(void)
{
- struct tomoyo_domain_info **blob = tomoyo_cred(current_cred());
+ const struct cred *cred = current_cred();
+ struct tomoyo_domain_info **blob;
+
+ if (cred->security == NULL)
+ return NULL;
+ blob = tomoyo_cred(cred);
return *blob;
}
diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
index bb84e6ec3886..fa121ad8534a 100644
--- a/security/tomoyo/tomoyo.c
+++ b/security/tomoyo/tomoyo.c
@@ -564,7 +564,8 @@ static int __init tomoyo_init(void)
struct cred *cred = (struct cred *) current_cred();
struct tomoyo_domain_info **blob;
- if (!security_module_enable("tomoyo")) {
+ if (!security_module_enable("tomoyo",
+ IS_ENABLED(CONFIG_SECURITY_TOMOYO_STACKED))) {
tomoyo_enabled = false;
return 0;
}
--
2.17.1
Move management of the file->f_security blob out of the
individual security modules and into the infrastructure.
The modules no longer allocate or free the data, instead
they tell the infrastructure how much space they require.
Signed-off-by: Casey Schaufler <[email protected]>
---
include/linux/lsm_hooks.h | 1 +
security/apparmor/lsm.c | 19 +++++++-------
security/security.c | 54 +++++++++++++++++++++++++++++++++++---
security/selinux/hooks.c | 25 ++----------------
security/smack/smack.h | 5 ++++
security/smack/smack_lsm.c | 26 +++++++-----------
6 files changed, 78 insertions(+), 52 deletions(-)
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 0bef312efd45..167ffbd4d0c0 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -2029,6 +2029,7 @@ struct security_hook_list {
*/
struct lsm_blob_sizes {
int lbs_cred;
+ int lbs_file;
};
/*
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index c2566aaa138e..15716b6ff860 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -431,21 +431,21 @@ static int apparmor_file_open(struct file *file)
static int apparmor_file_alloc_security(struct file *file)
{
- int error = 0;
-
- /* freed by apparmor_file_free_security */
+ struct aa_file_ctx *ctx = file_ctx(file);
struct aa_label *label = begin_current_label_crit_section();
- file->f_security = aa_alloc_file_ctx(label, GFP_KERNEL);
- if (!file_ctx(file))
- error = -ENOMEM;
- end_current_label_crit_section(label);
- return error;
+ spin_lock_init(&ctx->lock);
+ rcu_assign_pointer(ctx->label, aa_get_label(label));
+ end_current_label_crit_section(label);
+ return 0;
}
static void apparmor_file_free_security(struct file *file)
{
- aa_free_file_ctx(file_ctx(file));
+ struct aa_file_ctx *ctx = file_ctx(file);
+
+ if (ctx)
+ aa_put_label(rcu_access_pointer(ctx->label));
}
static int common_file_perm(const char *op, struct file *file, u32 mask)
@@ -1131,6 +1131,7 @@ static void apparmor_sock_graft(struct sock *sk, struct socket *parent)
*/
struct lsm_blob_sizes apparmor_blob_sizes = {
.lbs_cred = sizeof(struct aa_task_ctx *),
+ .lbs_file = sizeof(struct aa_file_ctx),
};
static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
diff --git a/security/security.c b/security/security.c
index ff7df14f6db1..5430cae73cf6 100644
--- a/security/security.c
+++ b/security/security.c
@@ -40,6 +40,8 @@
struct security_hook_heads security_hook_heads __lsm_ro_after_init;
static ATOMIC_NOTIFIER_HEAD(lsm_notifier_chain);
+static struct kmem_cache *lsm_file_cache;
+
char *lsm_names;
static struct lsm_blob_sizes blob_sizes;
@@ -92,6 +94,13 @@ int __init security_init(void)
*/
do_security_initcalls();
+ /*
+ * Create any kmem_caches needed for blobs
+ */
+ if (blob_sizes.lbs_file)
+ lsm_file_cache = kmem_cache_create("lsm_file_cache",
+ blob_sizes.lbs_file, 0,
+ SLAB_PANIC, NULL);
/*
* The second call to a module specific init function
* adds hooks to the hook lists and does any other early
@@ -101,6 +110,7 @@ int __init security_init(void)
#ifdef CONFIG_SECURITY_LSM_DEBUG
pr_info("LSM: cred blob size = %d\n", blob_sizes.lbs_cred);
+ pr_info("LSM: file blob size = %d\n", blob_sizes.lbs_file);
#endif
return 0;
@@ -277,6 +287,28 @@ static void __init lsm_set_size(int *need, int *lbs)
void __init security_add_blobs(struct lsm_blob_sizes *needed)
{
lsm_set_size(&needed->lbs_cred, &blob_sizes.lbs_cred);
+ lsm_set_size(&needed->lbs_file, &blob_sizes.lbs_file);
+}
+
+/**
+ * lsm_file_alloc - allocate a composite file blob
+ * @file: the file that needs a blob
+ *
+ * Allocate the file blob for all the modules
+ *
+ * Returns 0, or -ENOMEM if memory can't be allocated.
+ */
+int lsm_file_alloc(struct file *file)
+{
+ if (!lsm_file_cache) {
+ file->f_security = NULL;
+ return 0;
+ }
+
+ file->f_security = kmem_cache_zalloc(lsm_file_cache, GFP_KERNEL);
+ if (file->f_security == NULL)
+ return -ENOMEM;
+ return 0;
}
/*
@@ -962,12 +994,28 @@ int security_file_permission(struct file *file, int mask)
int security_file_alloc(struct file *file)
{
- return call_int_hook(file_alloc_security, 0, file);
+ int rc = lsm_file_alloc(file);
+
+ if (rc)
+ return rc;
+ rc = call_int_hook(file_alloc_security, 0, file);
+ if (unlikely(rc))
+ security_file_free(file);
+ return rc;
}
void security_file_free(struct file *file)
{
+ void *blob;
+
+ if (!lsm_file_cache)
+ return;
+
call_void_hook(file_free_security, file);
+
+ blob = file->f_security;
+ file->f_security = NULL;
+ kmem_cache_free(lsm_file_cache, blob);
}
int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
@@ -1085,7 +1133,7 @@ int security_cred_alloc_blank(struct cred *cred, gfp_t gfp)
return rc;
rc = call_int_hook(cred_alloc_blank, 0, cred, gfp);
- if (rc)
+ if (unlikely(rc))
security_cred_free(cred);
return rc;
}
@@ -1106,7 +1154,7 @@ int security_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp)
return rc;
rc = call_int_hook(cred_prepare, 0, new, old, gfp);
- if (rc)
+ if (unlikely(rc))
security_cred_free(new);
return rc;
}
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 94b3123c237b..3468b4592036 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -149,7 +149,6 @@ static int __init checkreqprot_setup(char *str)
__setup("checkreqprot=", checkreqprot_setup);
static struct kmem_cache *sel_inode_cache;
-static struct kmem_cache *file_security_cache;
/**
* selinux_secmark_enabled - Check to see if SECMARK is currently enabled
@@ -381,27 +380,15 @@ static void inode_free_security(struct inode *inode)
static int file_alloc_security(struct file *file)
{
- struct file_security_struct *fsec;
+ struct file_security_struct *fsec = selinux_file(file);
u32 sid = current_sid();
- fsec = kmem_cache_zalloc(file_security_cache, GFP_KERNEL);
- if (!fsec)
- return -ENOMEM;
-
fsec->sid = sid;
fsec->fown_sid = sid;
- file->f_security = fsec;
return 0;
}
-static void file_free_security(struct file *file)
-{
- struct file_security_struct *fsec = selinux_file(file);
- file->f_security = NULL;
- kmem_cache_free(file_security_cache, fsec);
-}
-
static int superblock_alloc_security(struct super_block *sb)
{
struct superblock_security_struct *sbsec;
@@ -3558,11 +3545,6 @@ static int selinux_file_alloc_security(struct file *file)
return file_alloc_security(file);
}
-static void selinux_file_free_security(struct file *file)
-{
- file_free_security(file);
-}
-
/*
* Check whether a task has the ioctl permission and cmd
* operation to an inode.
@@ -6857,6 +6839,7 @@ static void selinux_bpf_prog_free(struct bpf_prog_aux *aux)
struct lsm_blob_sizes selinux_blob_sizes = {
.lbs_cred = sizeof(struct task_security_struct),
+ .lbs_file = sizeof(struct file_security_struct),
};
static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
@@ -6927,7 +6910,6 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(file_permission, selinux_file_permission),
LSM_HOOK_INIT(file_alloc_security, selinux_file_alloc_security),
- LSM_HOOK_INIT(file_free_security, selinux_file_free_security),
LSM_HOOK_INIT(file_ioctl, selinux_file_ioctl),
LSM_HOOK_INIT(mmap_file, selinux_mmap_file),
LSM_HOOK_INIT(mmap_addr, selinux_mmap_addr),
@@ -7130,9 +7112,6 @@ static __init int selinux_init(void)
sel_inode_cache = kmem_cache_create("selinux_inode_security",
sizeof(struct inode_security_struct),
0, SLAB_PANIC, NULL);
- file_security_cache = kmem_cache_create("selinux_file_security",
- sizeof(struct file_security_struct),
- 0, SLAB_PANIC, NULL);
avc_init();
avtab_cache_init();
diff --git a/security/smack/smack.h b/security/smack/smack.h
index 0c6dce446825..043525a52e94 100644
--- a/security/smack/smack.h
+++ b/security/smack/smack.h
@@ -362,6 +362,11 @@ static inline struct task_smack *smack_cred(const struct cred *cred)
return cred->security;
}
+static inline struct smack_known **smack_file(const struct file *file)
+{
+ return file->f_security;
+}
+
/*
* Is the directory transmuting?
*/
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index a06ea8aa89c4..d1430341798f 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -1571,24 +1571,12 @@ static void smack_inode_getsecid(struct inode *inode, u32 *secid)
*/
static int smack_file_alloc_security(struct file *file)
{
- struct smack_known *skp = smk_of_current();
+ struct smack_known **blob = smack_file(file);
- file->f_security = skp;
+ *blob = smk_of_current();
return 0;
}
-/**
- * smack_file_free_security - clear a file security blob
- * @file: the object
- *
- * The security blob for a file is a pointer to the master
- * label list, so no memory is freed.
- */
-static void smack_file_free_security(struct file *file)
-{
- file->f_security = NULL;
-}
-
/**
* smack_file_ioctl - Smack check on ioctls
* @file: the object
@@ -1813,7 +1801,9 @@ static int smack_mmap_file(struct file *file,
*/
static void smack_file_set_fowner(struct file *file)
{
- file->f_security = smk_of_current();
+ struct smack_known **blob = smack_file(file);
+
+ *blob = smk_of_current();
}
/**
@@ -1830,6 +1820,7 @@ static void smack_file_set_fowner(struct file *file)
static int smack_file_send_sigiotask(struct task_struct *tsk,
struct fown_struct *fown, int signum)
{
+ struct smack_known **blob;
struct smack_known *skp;
struct smack_known *tkp = smk_of_task(smack_cred(tsk->cred));
struct file *file;
@@ -1842,7 +1833,8 @@ static int smack_file_send_sigiotask(struct task_struct *tsk,
file = container_of(fown, struct file, f_owner);
/* we don't log here as rc can be overriden */
- skp = file->f_security;
+ blob = smack_file(file);
+ skp = *blob;
rc = smk_access(skp, tkp, MAY_DELIVER, NULL);
rc = smk_bu_note("sigiotask", skp, tkp, MAY_DELIVER, rc);
if (rc != 0 && has_capability(tsk, CAP_MAC_OVERRIDE))
@@ -4626,6 +4618,7 @@ static int smack_dentry_create_files_as(struct dentry *dentry, int mode,
struct lsm_blob_sizes smack_blob_sizes = {
.lbs_cred = sizeof(struct task_smack),
+ .lbs_file = sizeof(struct smack_known *),
};
static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
@@ -4663,7 +4656,6 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(inode_getsecid, smack_inode_getsecid),
LSM_HOOK_INIT(file_alloc_security, smack_file_alloc_security),
- LSM_HOOK_INIT(file_free_security, smack_file_free_security),
LSM_HOOK_INIT(file_ioctl, smack_file_ioctl),
LSM_HOOK_INIT(file_lock, smack_file_lock),
LSM_HOOK_INIT(file_fcntl, smack_file_fcntl),
--
2.17.1
Don't use the file->f_security pointer directly.
Provide a helper function that provides the security blob pointer.
Signed-off-by: Casey Schaufler <[email protected]>
---
security/selinux/hooks.c | 18 +++++++++---------
security/selinux/include/objsec.h | 5 +++++
2 files changed, 14 insertions(+), 9 deletions(-)
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 9b49698754a7..94b3123c237b 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -397,7 +397,7 @@ static int file_alloc_security(struct file *file)
static void file_free_security(struct file *file)
{
- struct file_security_struct *fsec = file->f_security;
+ struct file_security_struct *fsec = selinux_file(file);
file->f_security = NULL;
kmem_cache_free(file_security_cache, fsec);
}
@@ -1880,7 +1880,7 @@ static int file_has_perm(const struct cred *cred,
struct file *file,
u32 av)
{
- struct file_security_struct *fsec = file->f_security;
+ struct file_security_struct *fsec = selinux_file(file);
struct inode *inode = file_inode(file);
struct common_audit_data ad;
u32 sid = cred_sid(cred);
@@ -2224,7 +2224,7 @@ static int selinux_binder_transfer_file(struct task_struct *from,
struct file *file)
{
u32 sid = task_sid(to);
- struct file_security_struct *fsec = file->f_security;
+ struct file_security_struct *fsec = selinux_file(file);
struct dentry *dentry = file->f_path.dentry;
struct inode_security_struct *isec;
struct common_audit_data ad;
@@ -3536,7 +3536,7 @@ static int selinux_revalidate_file_permission(struct file *file, int mask)
static int selinux_file_permission(struct file *file, int mask)
{
struct inode *inode = file_inode(file);
- struct file_security_struct *fsec = file->f_security;
+ struct file_security_struct *fsec = selinux_file(file);
struct inode_security_struct *isec;
u32 sid = current_sid();
@@ -3571,7 +3571,7 @@ static int ioctl_has_perm(const struct cred *cred, struct file *file,
u32 requested, u16 cmd)
{
struct common_audit_data ad;
- struct file_security_struct *fsec = file->f_security;
+ struct file_security_struct *fsec = selinux_file(file);
struct inode *inode = file_inode(file);
struct inode_security_struct *isec;
struct lsm_ioctlop_audit ioctl;
@@ -3823,7 +3823,7 @@ static void selinux_file_set_fowner(struct file *file)
{
struct file_security_struct *fsec;
- fsec = file->f_security;
+ fsec = selinux_file(file);
fsec->fown_sid = current_sid();
}
@@ -3838,7 +3838,7 @@ static int selinux_file_send_sigiotask(struct task_struct *tsk,
/* struct fown_struct is never outside the context of a struct file */
file = container_of(fown, struct file, f_owner);
- fsec = file->f_security;
+ fsec = selinux_file(file);
if (!signum)
perm = signal_to_av(SIGIO); /* as per send_sigio_to_task */
@@ -3862,7 +3862,7 @@ static int selinux_file_open(struct file *file)
struct file_security_struct *fsec;
struct inode_security_struct *isec;
- fsec = file->f_security;
+ fsec = selinux_file(file);
isec = inode_security(file_inode(file));
/*
* Save inode label and policy sequence number
@@ -4002,7 +4002,7 @@ static int selinux_kernel_module_from_file(struct file *file)
ad.type = LSM_AUDIT_DATA_FILE;
ad.u.file = file;
- fsec = file->f_security;
+ fsec = selinux_file(file);
if (sid != fsec->sid) {
rc = avc_has_perm(&selinux_state,
sid, fsec->sid, SECCLASS_FD, FD__USE, &ad);
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index db1c7000ada3..2586fbc7e38c 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -167,4 +167,9 @@ static inline struct task_security_struct *selinux_cred(const struct cred *cred)
return cred->security;
}
+static inline struct file_security_struct *selinux_file(const struct file *file)
+{
+ return file->f_security;
+}
+
#endif /* _SELINUX_OBJSEC_H_ */
--
2.17.1
Move management of the cred security blob out of the
security modules and into the security infrastructure.
Instead of allocating and freeing space the security
modules tell the infrastructure how much space they
require.
Some SELinux memory management debug code has been removed.
Signed-off-by: Casey Schaufler <[email protected]>
---
include/linux/lsm_hooks.h | 14 ++++
kernel/cred.c | 13 ----
security/Kconfig | 11 ++++
security/apparmor/domain.c | 2 +-
security/apparmor/include/cred.h | 16 ++++-
security/apparmor/lsm.c | 28 ++++++--
security/apparmor/task.c | 6 +-
security/security.c | 106 +++++++++++++++++++++++++++++-
security/selinux/hooks.c | 63 +++++-------------
security/selinux/include/objsec.h | 4 ++
security/selinux/selinuxfs.c | 1 +
security/smack/smack.h | 1 +
security/smack/smack_lsm.c | 85 +++++++++---------------
security/tomoyo/common.h | 21 +++++-
security/tomoyo/domain.c | 4 +-
security/tomoyo/securityfs_if.c | 15 +++--
security/tomoyo/tomoyo.c | 56 +++++++++++++---
17 files changed, 303 insertions(+), 143 deletions(-)
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 97a020c616ad..0bef312efd45 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -2024,6 +2024,13 @@ struct security_hook_list {
char *lsm;
} __randomize_layout;
+/*
+ * Security blob size or offset data.
+ */
+struct lsm_blob_sizes {
+ int lbs_cred;
+};
+
/*
* Initializing a security_hook_list structure takes
* up a lot of space in a source file. This macro takes
@@ -2036,6 +2043,7 @@ struct security_hook_list {
extern struct security_hook_heads security_hook_heads;
extern char *lsm_names;
+extern void security_add_blobs(struct lsm_blob_sizes *needed);
extern void security_add_hooks(struct security_hook_list *hooks, int count,
char *lsm);
@@ -2082,4 +2090,10 @@ void __init loadpin_add_hooks(void);
static inline void loadpin_add_hooks(void) { };
#endif
+extern int lsm_cred_alloc(struct cred *cred, gfp_t gfp);
+
+#ifdef CONFIG_SECURITY
+void lsm_early_cred(struct cred *cred);
+#endif
+
#endif /* ! __LINUX_LSM_HOOKS_H */
diff --git a/kernel/cred.c b/kernel/cred.c
index ecf03657e71c..fa2061ee4955 100644
--- a/kernel/cred.c
+++ b/kernel/cred.c
@@ -704,19 +704,6 @@ bool creds_are_invalid(const struct cred *cred)
{
if (cred->magic != CRED_MAGIC)
return true;
-#ifdef CONFIG_SECURITY_SELINUX
- /*
- * cred->security == NULL if security_cred_alloc_blank() or
- * security_prepare_creds() returned an error.
- */
- if (selinux_is_enabled() && cred->security) {
- if ((unsigned long) cred->security < PAGE_SIZE)
- return true;
- if ((*(u32 *)cred->security & 0xffffff00) ==
- (POISON_FREE << 24 | POISON_FREE << 16 | POISON_FREE << 8))
- return true;
- }
-#endif
return false;
}
EXPORT_SYMBOL(creds_are_invalid);
diff --git a/security/Kconfig b/security/Kconfig
index 27d8b2688f75..22f7664c4977 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -36,6 +36,17 @@ config SECURITY_WRITABLE_HOOKS
bool
default n
+config SECURITY_LSM_DEBUG
+ bool "Enable debugging of the LSM infrastructure"
+ depends on SECURITY
+ help
+ This allows you to choose debug messages related to
+ security modules configured into your kernel. These
+ messages may be helpful in determining how a security
+ module is using security blobs.
+
+ If you are unsure how to answer this question, answer N.
+
config SECURITYFS
bool "Enable the securityfs filesystem"
help
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
index 08c88de0ffda..726910bba84b 100644
--- a/security/apparmor/domain.c
+++ b/security/apparmor/domain.c
@@ -975,7 +975,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
}
aa_put_label(cred_label(bprm->cred));
/* transfer reference, released when cred is freed */
- cred_label(bprm->cred) = new;
+ set_cred_label(bprm->cred, new);
done:
aa_put_label(label);
diff --git a/security/apparmor/include/cred.h b/security/apparmor/include/cred.h
index e287b7d0d4be..a90eae76d7c1 100644
--- a/security/apparmor/include/cred.h
+++ b/security/apparmor/include/cred.h
@@ -23,8 +23,22 @@
#include "policy_ns.h"
#include "task.h"
-#define cred_label(X) ((X)->security)
+static inline struct aa_label *cred_label(const struct cred *cred)
+{
+ struct aa_label **blob = cred->security;
+
+ AA_BUG(!blob);
+ return *blob;
+}
+static inline void set_cred_label(const struct cred *cred,
+ struct aa_label *label)
+{
+ struct aa_label **blob = cred->security;
+
+ AA_BUG(!blob);
+ *blob = label;
+}
/**
* aa_cred_raw_label - obtain cred's label
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 8b8b70620bbe..c2566aaa138e 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -57,7 +57,7 @@ DEFINE_PER_CPU(struct aa_buffers, aa_buffers);
static void apparmor_cred_free(struct cred *cred)
{
aa_put_label(cred_label(cred));
- cred_label(cred) = NULL;
+ set_cred_label(cred, NULL);
}
/*
@@ -65,7 +65,7 @@ static void apparmor_cred_free(struct cred *cred)
*/
static int apparmor_cred_alloc_blank(struct cred *cred, gfp_t gfp)
{
- cred_label(cred) = NULL;
+ set_cred_label(cred, NULL);
return 0;
}
@@ -75,7 +75,7 @@ static int apparmor_cred_alloc_blank(struct cred *cred, gfp_t gfp)
static int apparmor_cred_prepare(struct cred *new, const struct cred *old,
gfp_t gfp)
{
- cred_label(new) = aa_get_newest_label(cred_label(old));
+ set_cred_label(new, aa_get_newest_label(cred_label(old)));
return 0;
}
@@ -84,7 +84,7 @@ static int apparmor_cred_prepare(struct cred *new, const struct cred *old,
*/
static void apparmor_cred_transfer(struct cred *new, const struct cred *old)
{
- cred_label(new) = aa_get_newest_label(cred_label(old));
+ set_cred_label(new, aa_get_newest_label(cred_label(old)));
}
static void apparmor_task_free(struct task_struct *task)
@@ -1126,6 +1126,13 @@ static void apparmor_sock_graft(struct sock *sk, struct socket *parent)
ctx->label = aa_get_current_label();
}
+/*
+ * The cred blob is a pointer to, not an instance of, an aa_task_ctx.
+ */
+struct lsm_blob_sizes apparmor_blob_sizes = {
+ .lbs_cred = sizeof(struct aa_task_ctx *),
+};
+
static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check),
LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme),
@@ -1455,7 +1462,8 @@ static int __init set_init_ctx(void)
if (!ctx)
return -ENOMEM;
- cred_label(cred) = aa_get_label(ns_unconfined(root_ns));
+ lsm_early_cred(cred);
+ set_cred_label(cred, aa_get_label(ns_unconfined(root_ns)));
task_ctx(current) = ctx;
return 0;
@@ -1540,8 +1548,18 @@ static inline int apparmor_init_sysctl(void)
static int __init apparmor_init(void)
{
+ static int finish;
int error;
+ if (!finish) {
+ if (apparmor_enabled && security_module_enable("apparmor"))
+ security_add_blobs(&apparmor_blob_sizes);
+ else
+ apparmor_enabled = false;
+ finish = 1;
+ return 0;
+ }
+
if (!apparmor_enabled || !security_module_enable("apparmor")) {
aa_info_message("AppArmor disabled by boot time parameter");
apparmor_enabled = false;
diff --git a/security/apparmor/task.c b/security/apparmor/task.c
index c6b78a14da91..4551110f0496 100644
--- a/security/apparmor/task.c
+++ b/security/apparmor/task.c
@@ -81,7 +81,7 @@ int aa_replace_current_label(struct aa_label *label)
*/
aa_get_label(label);
aa_put_label(cred_label(new));
- cred_label(new) = label;
+ set_cred_label(new, label);
commit_creds(new);
return 0;
@@ -138,7 +138,7 @@ int aa_set_current_hat(struct aa_label *label, u64 token)
return -EACCES;
}
- cred_label(new) = aa_get_newest_label(label);
+ set_cred_label(new, aa_get_newest_label(label));
/* clear exec on switching context */
aa_put_label(ctx->onexec);
ctx->onexec = NULL;
@@ -172,7 +172,7 @@ int aa_restore_previous_label(u64 token)
return -ENOMEM;
aa_put_label(cred_label(new));
- cred_label(new) = aa_get_newest_label(ctx->previous);
+ set_cred_label(new, aa_get_newest_label(ctx->previous));
AA_BUG(!cred_label(new));
/* clear exec && prev information when restoring to previous context */
aa_clear_task_ctx_trans(ctx);
diff --git a/security/security.c b/security/security.c
index 3dfe75d0d373..ff7df14f6db1 100644
--- a/security/security.c
+++ b/security/security.c
@@ -41,6 +41,8 @@ struct security_hook_heads security_hook_heads __lsm_ro_after_init;
static ATOMIC_NOTIFIER_HEAD(lsm_notifier_chain);
char *lsm_names;
+static struct lsm_blob_sizes blob_sizes;
+
/* Boot-time LSM user choice */
static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =
CONFIG_DEFAULT_SECURITY;
@@ -85,10 +87,22 @@ int __init security_init(void)
loadpin_add_hooks();
/*
- * Load all the remaining security modules.
+ * The first call to a module specific init function
+ * updates the blob size requirements.
+ */
+ do_security_initcalls();
+
+ /*
+ * The second call to a module specific init function
+ * adds hooks to the hook lists and does any other early
+ * initializations required.
*/
do_security_initcalls();
+#ifdef CONFIG_SECURITY_LSM_DEBUG
+ pr_info("LSM: cred blob size = %d\n", blob_sizes.lbs_cred);
+#endif
+
return 0;
}
@@ -198,6 +212,73 @@ int unregister_lsm_notifier(struct notifier_block *nb)
}
EXPORT_SYMBOL(unregister_lsm_notifier);
+/**
+ * lsm_cred_alloc - allocate a composite cred blob
+ * @cred: the cred that needs a blob
+ * @gfp: allocation type
+ *
+ * Allocate the cred blob for all the modules
+ *
+ * Returns 0, or -ENOMEM if memory can't be allocated.
+ */
+int lsm_cred_alloc(struct cred *cred, gfp_t gfp)
+{
+ if (blob_sizes.lbs_cred == 0) {
+ cred->security = NULL;
+ return 0;
+ }
+
+ cred->security = kzalloc(blob_sizes.lbs_cred, gfp);
+ if (cred->security == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+/**
+ * lsm_early_cred - during initialization allocate a composite cred blob
+ * @cred: the cred that needs a blob
+ *
+ * Allocate the cred blob for all the modules if it's not already there
+ */
+void lsm_early_cred(struct cred *cred)
+{
+ int rc;
+
+ if (cred == NULL)
+ panic("%s: NULL cred.\n", __func__);
+ if (cred->security != NULL)
+ return;
+ rc = lsm_cred_alloc(cred, GFP_KERNEL);
+ if (rc)
+ panic("%s: Early cred alloc failed.\n", __func__);
+}
+
+static void __init lsm_set_size(int *need, int *lbs)
+{
+ int offset;
+
+ if (*need > 0) {
+ offset = *lbs;
+ *lbs += *need;
+ *need = offset;
+ }
+}
+
+/**
+ * security_add_blobs - Report blob sizes
+ * @needed: the size of blobs needed by the module
+ *
+ * Each LSM has to register its blobs with the infrastructure.
+ * The "needed" data tells the infrastructure how much memory
+ * the module requires for each of its blobs. On return the
+ * structure is filled with the offset that module should use
+ * from the blob pointer.
+ */
+void __init security_add_blobs(struct lsm_blob_sizes *needed)
+{
+ lsm_set_size(&needed->lbs_cred, &blob_sizes.lbs_cred);
+}
+
/*
* Hook list operation macros.
*
@@ -998,17 +1079,36 @@ void security_task_free(struct task_struct *task)
int security_cred_alloc_blank(struct cred *cred, gfp_t gfp)
{
- return call_int_hook(cred_alloc_blank, 0, cred, gfp);
+ int rc = lsm_cred_alloc(cred, gfp);
+
+ if (rc)
+ return rc;
+
+ rc = call_int_hook(cred_alloc_blank, 0, cred, gfp);
+ if (rc)
+ security_cred_free(cred);
+ return rc;
}
void security_cred_free(struct cred *cred)
{
call_void_hook(cred_free, cred);
+
+ kfree(cred->security);
+ cred->security = NULL;
}
int security_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp)
{
- return call_int_hook(cred_prepare, 0, new, old, gfp);
+ int rc = lsm_cred_alloc(new, gfp);
+
+ if (rc)
+ return rc;
+
+ rc = call_int_hook(cred_prepare, 0, new, old, gfp);
+ if (rc)
+ security_cred_free(new);
+ return rc;
}
void security_transfer_creds(struct cred *new, const struct cred *old)
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 9d6cdd21acb6..9b49698754a7 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -213,12 +213,9 @@ static void cred_init_security(void)
struct cred *cred = (struct cred *) current->real_cred;
struct task_security_struct *tsec;
- tsec = kzalloc(sizeof(struct task_security_struct), GFP_KERNEL);
- if (!tsec)
- panic("SELinux: Failed to initialize initial task.\n");
-
+ lsm_early_cred(cred);
+ tsec = selinux_cred(cred);
tsec->osid = tsec->sid = SECINITSID_KERNEL;
- cred->security = tsec;
}
/*
@@ -3898,53 +3895,17 @@ static int selinux_task_alloc(struct task_struct *task,
sid, sid, SECCLASS_PROCESS, PROCESS__FORK, NULL);
}
-/*
- * allocate the SELinux part of blank credentials
- */
-static int selinux_cred_alloc_blank(struct cred *cred, gfp_t gfp)
-{
- struct task_security_struct *tsec;
-
- tsec = kzalloc(sizeof(struct task_security_struct), gfp);
- if (!tsec)
- return -ENOMEM;
-
- cred->security = tsec;
- return 0;
-}
-
-/*
- * detach and free the LSM part of a set of credentials
- */
-static void selinux_cred_free(struct cred *cred)
-{
- struct task_security_struct *tsec = selinux_cred(cred);
-
- /*
- * cred->security == NULL if security_cred_alloc_blank() or
- * security_prepare_creds() returned an error.
- */
- BUG_ON(cred->security && (unsigned long) cred->security < PAGE_SIZE);
- cred->security = (void *) 0x7UL;
- kfree(tsec);
-}
-
/*
* prepare a new set of credentials for modification
*/
static int selinux_cred_prepare(struct cred *new, const struct cred *old,
gfp_t gfp)
{
- const struct task_security_struct *old_tsec;
- struct task_security_struct *tsec;
-
- old_tsec = selinux_cred(old);
+ const struct task_security_struct *old_tsec = selinux_cred(old);
+ struct task_security_struct *tsec = selinux_cred(new);
- tsec = kmemdup(old_tsec, sizeof(struct task_security_struct), gfp);
- if (!tsec)
- return -ENOMEM;
+ *tsec = *old_tsec;
- new->security = tsec;
return 0;
}
@@ -6894,6 +6855,10 @@ static void selinux_bpf_prog_free(struct bpf_prog_aux *aux)
}
#endif
+struct lsm_blob_sizes selinux_blob_sizes = {
+ .lbs_cred = sizeof(struct task_security_struct),
+};
+
static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(binder_set_context_mgr, selinux_binder_set_context_mgr),
LSM_HOOK_INIT(binder_transaction, selinux_binder_transaction),
@@ -6976,8 +6941,6 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(file_open, selinux_file_open),
LSM_HOOK_INIT(task_alloc, selinux_task_alloc),
- LSM_HOOK_INIT(cred_alloc_blank, selinux_cred_alloc_blank),
- LSM_HOOK_INIT(cred_free, selinux_cred_free),
LSM_HOOK_INIT(cred_prepare, selinux_cred_prepare),
LSM_HOOK_INIT(cred_transfer, selinux_cred_transfer),
LSM_HOOK_INIT(cred_getsecid, selinux_cred_getsecid),
@@ -7133,11 +7096,19 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
static __init int selinux_init(void)
{
+ static int finish;
+
if (!security_module_enable("selinux")) {
selinux_enabled = 0;
return 0;
}
+ if (!finish) {
+ security_add_blobs(&selinux_blob_sizes);
+ finish = 1;
+ return 0;
+ }
+
if (!selinux_enabled) {
pr_info("SELinux: Disabled at boot.\n");
return 0;
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index 734b6833bdff..db1c7000ada3 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -25,6 +25,9 @@
#include <linux/binfmts.h>
#include <linux/in.h>
#include <linux/spinlock.h>
+#include <linux/lsm_hooks.h>
+#include <linux/msg.h>
+#include <net/sock.h>
#include <net/net_namespace.h>
#include "flask.h"
#include "avc.h"
@@ -158,6 +161,7 @@ struct bpf_security_struct {
u32 sid; /*SID of bpf obj creater*/
};
+extern struct lsm_blob_sizes selinux_blob_sizes;
static inline struct task_security_struct *selinux_cred(const struct cred *cred)
{
return cred->security;
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index f3a5a138a096..b5665bdc29fc 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -31,6 +31,7 @@
#include <linux/uaccess.h>
#include <linux/kobject.h>
#include <linux/ctype.h>
+#include <linux/lsm_hooks.h>
/* selinuxfs pseudo filesystem for exporting the security policy API.
Based on the proc code and the fs/nfsd/nfsctl.c code. */
diff --git a/security/smack/smack.h b/security/smack/smack.h
index 0b55d6a55b26..0c6dce446825 100644
--- a/security/smack/smack.h
+++ b/security/smack/smack.h
@@ -24,6 +24,7 @@
#include <linux/list.h>
#include <linux/rculist.h>
#include <linux/lsm_audit.h>
+#include <linux/msg.h>
/*
* Use IPv6 port labeling if IPv6 is enabled and secmarks
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 68ee3ae8f25c..a06ea8aa89c4 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -309,29 +309,20 @@ static struct inode_smack *new_inode_smack(struct smack_known *skp)
}
/**
- * new_task_smack - allocate a task security blob
+ * init_task_smack - initialize a task security blob
+ * @tsp: blob to initialize
* @task: a pointer to the Smack label for the running task
* @forked: a pointer to the Smack label for the forked task
- * @gfp: type of the memory for the allocation
*
- * Returns the new blob or NULL if there's no memory available
*/
-static struct task_smack *new_task_smack(struct smack_known *task,
- struct smack_known *forked, gfp_t gfp)
+static void init_task_smack(struct task_smack *tsp, struct smack_known *task,
+ struct smack_known *forked)
{
- struct task_smack *tsp;
-
- tsp = kzalloc(sizeof(struct task_smack), gfp);
- if (tsp == NULL)
- return NULL;
-
tsp->smk_task = task;
tsp->smk_forked = forked;
INIT_LIST_HEAD(&tsp->smk_rules);
INIT_LIST_HEAD(&tsp->smk_relabel);
mutex_init(&tsp->smk_rules_lock);
-
- return tsp;
}
/**
@@ -1958,14 +1949,7 @@ static int smack_file_open(struct file *file)
*/
static int smack_cred_alloc_blank(struct cred *cred, gfp_t gfp)
{
- struct task_smack *tsp;
-
- tsp = new_task_smack(NULL, NULL, gfp);
- if (tsp == NULL)
- return -ENOMEM;
-
- cred->security = tsp;
-
+ init_task_smack(smack_cred(cred), NULL, NULL);
return 0;
}
@@ -1982,10 +1966,6 @@ static void smack_cred_free(struct cred *cred)
struct list_head *l;
struct list_head *n;
- if (tsp == NULL)
- return;
- cred->security = NULL;
-
smk_destroy_label_list(&tsp->smk_relabel);
list_for_each_safe(l, n, &tsp->smk_rules) {
@@ -1993,7 +1973,6 @@ static void smack_cred_free(struct cred *cred)
list_del(&rp->list);
kfree(rp);
}
- kfree(tsp);
}
/**
@@ -2008,14 +1987,10 @@ static int smack_cred_prepare(struct cred *new, const struct cred *old,
gfp_t gfp)
{
struct task_smack *old_tsp = smack_cred(old);
- struct task_smack *new_tsp;
+ struct task_smack *new_tsp = smack_cred(new);
int rc;
- new_tsp = new_task_smack(old_tsp->smk_task, old_tsp->smk_task, gfp);
- if (new_tsp == NULL)
- return -ENOMEM;
-
- new->security = new_tsp;
+ init_task_smack(new_tsp, old_tsp->smk_task, old_tsp->smk_task);
rc = smk_copy_rules(&new_tsp->smk_rules, &old_tsp->smk_rules, gfp);
if (rc != 0)
@@ -2023,10 +1998,7 @@ static int smack_cred_prepare(struct cred *new, const struct cred *old,
rc = smk_copy_relabel(&new_tsp->smk_relabel, &old_tsp->smk_relabel,
gfp);
- if (rc != 0)
- return rc;
-
- return 0;
+ return rc;
}
/**
@@ -4652,6 +4624,10 @@ static int smack_dentry_create_files_as(struct dentry *dentry, int mode,
return 0;
}
+struct lsm_blob_sizes smack_blob_sizes = {
+ .lbs_cred = sizeof(struct task_smack),
+};
+
static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(ptrace_access_check, smack_ptrace_access_check),
LSM_HOOK_INIT(ptrace_traceme, smack_ptrace_traceme),
@@ -4830,23 +4806,35 @@ static __init void init_smack_known_list(void)
*/
static __init int smack_init(void)
{
- struct cred *cred;
+ static int finish;
+ struct cred *cred = (struct cred *) current->cred;
struct task_smack *tsp;
if (!security_module_enable("smack"))
return 0;
+ if (!finish) {
+ security_add_blobs(&smack_blob_sizes);
+ finish = 1;
+ return 0;
+ }
+
smack_inode_cache = KMEM_CACHE(inode_smack, 0);
if (!smack_inode_cache)
return -ENOMEM;
- tsp = new_task_smack(&smack_known_floor, &smack_known_floor,
- GFP_KERNEL);
- if (tsp == NULL) {
- kmem_cache_destroy(smack_inode_cache);
- return -ENOMEM;
- }
+ lsm_early_cred(cred);
+ /*
+ * Set the security state for the initial task.
+ */
+ tsp = smack_cred(cred);
+ init_task_smack(tsp, &smack_known_floor, &smack_known_floor);
+
+ /*
+ * Register with LSM
+ */
+ security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks), "smack");
smack_enabled = 1;
pr_info("Smack: Initializing.\n");
@@ -4860,20 +4848,9 @@ static __init int smack_init(void)
pr_info("Smack: IPv6 Netfilter enabled.\n");
#endif
- /*
- * Set the security state for the initial task.
- */
- cred = (struct cred *) current->cred;
- cred->security = tsp;
-
/* initialize the smack_known_list */
init_smack_known_list();
- /*
- * Register with LSM
- */
- security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks), "smack");
-
return 0;
}
diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h
index 539bcdd30bb8..0110bebe86e2 100644
--- a/security/tomoyo/common.h
+++ b/security/tomoyo/common.h
@@ -29,6 +29,7 @@
#include <linux/in.h>
#include <linux/in6.h>
#include <linux/un.h>
+#include <linux/lsm_hooks.h>
#include <net/sock.h>
#include <net/af_unix.h>
#include <net/ip.h>
@@ -1062,6 +1063,7 @@ void tomoyo_write_log2(struct tomoyo_request_info *r, int len, const char *fmt,
/********** External variable definitions. **********/
extern bool tomoyo_policy_loaded;
+extern bool tomoyo_enabled;
extern const char * const tomoyo_condition_keyword
[TOMOYO_MAX_CONDITION_KEYWORD];
extern const char * const tomoyo_dif[TOMOYO_MAX_DOMAIN_INFO_FLAGS];
@@ -1196,6 +1198,17 @@ static inline void tomoyo_put_group(struct tomoyo_group *group)
atomic_dec(&group->head.users);
}
+/**
+ * tomoyo_cred - Get a pointer to the tomoyo cred security blob
+ * @cred - the relevant cred
+ *
+ * Returns pointer to the tomoyo cred blob.
+ */
+static inline struct tomoyo_domain_info **tomoyo_cred(const struct cred *cred)
+{
+ return cred->security;
+}
+
/**
* tomoyo_domain - Get "struct tomoyo_domain_info" for current thread.
*
@@ -1203,7 +1216,9 @@ static inline void tomoyo_put_group(struct tomoyo_group *group)
*/
static inline struct tomoyo_domain_info *tomoyo_domain(void)
{
- return current_cred()->security;
+ struct tomoyo_domain_info **blob = tomoyo_cred(current_cred());
+
+ return *blob;
}
/**
@@ -1216,7 +1231,9 @@ static inline struct tomoyo_domain_info *tomoyo_domain(void)
static inline struct tomoyo_domain_info *tomoyo_real_domain(struct task_struct
*task)
{
- return task_cred_xxx(task, security);
+ struct tomoyo_domain_info **blob = tomoyo_cred(get_task_cred(task));
+
+ return *blob;
}
/**
diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c
index f6758dad981f..b7469fdbff01 100644
--- a/security/tomoyo/domain.c
+++ b/security/tomoyo/domain.c
@@ -678,6 +678,7 @@ static int tomoyo_environ(struct tomoyo_execve *ee)
*/
int tomoyo_find_next_domain(struct linux_binprm *bprm)
{
+ struct tomoyo_domain_info **blob;
struct tomoyo_domain_info *old_domain = tomoyo_domain();
struct tomoyo_domain_info *domain = NULL;
const char *original_name = bprm->filename;
@@ -843,7 +844,8 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
domain = old_domain;
/* Update reference count on "struct tomoyo_domain_info". */
atomic_inc(&domain->users);
- bprm->cred->security = domain;
+ blob = tomoyo_cred(bprm->cred);
+ *blob = domain;
kfree(exename.name);
if (!retval) {
ee->r.domain = domain;
diff --git a/security/tomoyo/securityfs_if.c b/security/tomoyo/securityfs_if.c
index 1d3d7e7a1f05..768dff9608b1 100644
--- a/security/tomoyo/securityfs_if.c
+++ b/security/tomoyo/securityfs_if.c
@@ -71,9 +71,12 @@ static ssize_t tomoyo_write_self(struct file *file, const char __user *buf,
if (!cred) {
error = -ENOMEM;
} else {
- struct tomoyo_domain_info *old_domain =
- cred->security;
- cred->security = new_domain;
+ struct tomoyo_domain_info **blob;
+ struct tomoyo_domain_info *old_domain;
+
+ blob = tomoyo_cred(cred);
+ old_domain = *blob;
+ *blob = new_domain;
atomic_inc(&new_domain->users);
atomic_dec(&old_domain->users);
commit_creds(cred);
@@ -234,10 +237,14 @@ static void __init tomoyo_create_entry(const char *name, const umode_t mode,
*/
static int __init tomoyo_initerface_init(void)
{
+ struct tomoyo_domain_info *domain;
struct dentry *tomoyo_dir;
+ if (!tomoyo_enabled)
+ return 0;
+ domain = tomoyo_domain();
/* Don't create securityfs entries unless registered. */
- if (current_cred()->security != &tomoyo_kernel_domain)
+ if (domain != &tomoyo_kernel_domain)
return 0;
tomoyo_dir = securityfs_create_dir("tomoyo", NULL);
diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
index 9f932e2d6852..bb84e6ec3886 100644
--- a/security/tomoyo/tomoyo.c
+++ b/security/tomoyo/tomoyo.c
@@ -18,7 +18,9 @@
*/
static int tomoyo_cred_alloc_blank(struct cred *new, gfp_t gfp)
{
- new->security = NULL;
+ struct tomoyo_domain_info **blob = tomoyo_cred(new);
+
+ *blob = NULL;
return 0;
}
@@ -34,8 +36,13 @@ static int tomoyo_cred_alloc_blank(struct cred *new, gfp_t gfp)
static int tomoyo_cred_prepare(struct cred *new, const struct cred *old,
gfp_t gfp)
{
- struct tomoyo_domain_info *domain = old->security;
- new->security = domain;
+ struct tomoyo_domain_info **old_blob = tomoyo_cred(old);
+ struct tomoyo_domain_info **new_blob = tomoyo_cred(new);
+ struct tomoyo_domain_info *domain;
+
+ domain = *old_blob;
+ *new_blob = domain;
+
if (domain)
atomic_inc(&domain->users);
return 0;
@@ -59,7 +66,9 @@ static void tomoyo_cred_transfer(struct cred *new, const struct cred *old)
*/
static void tomoyo_cred_free(struct cred *cred)
{
- struct tomoyo_domain_info *domain = cred->security;
+ struct tomoyo_domain_info **blob = tomoyo_cred(cred);
+ struct tomoyo_domain_info *domain = *blob;
+
if (domain)
atomic_dec(&domain->users);
}
@@ -73,6 +82,9 @@ static void tomoyo_cred_free(struct cred *cred)
*/
static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
{
+ struct tomoyo_domain_info **blob;
+ struct tomoyo_domain_info *domain;
+
/*
* Do only if this function is called for the first time of an execve
* operation.
@@ -93,13 +105,14 @@ static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
* stored inside "bprm->cred->security" will be acquired later inside
* tomoyo_find_next_domain().
*/
- atomic_dec(&((struct tomoyo_domain_info *)
- bprm->cred->security)->users);
+ blob = tomoyo_cred(bprm->cred);
+ domain = *blob;
+ atomic_dec(&domain->users);
/*
* Tell tomoyo_bprm_check_security() is called for the first time of an
* execve operation.
*/
- bprm->cred->security = NULL;
+ *blob = NULL;
return 0;
}
@@ -112,8 +125,11 @@ static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
*/
static int tomoyo_bprm_check_security(struct linux_binprm *bprm)
{
- struct tomoyo_domain_info *domain = bprm->cred->security;
+ struct tomoyo_domain_info **blob;
+ struct tomoyo_domain_info *domain;
+ blob = tomoyo_cred(bprm->cred);
+ domain = *blob;
/*
* Execute permission is checked against pathname passed to do_execve()
* using current domain.
@@ -493,6 +509,10 @@ static int tomoyo_socket_sendmsg(struct socket *sock, struct msghdr *msg,
return tomoyo_socket_sendmsg_permission(sock, msg, size);
}
+struct lsm_blob_sizes tomoyo_blob_sizes = {
+ .lbs_cred = sizeof(struct tomoyo_domain_info *),
+};
+
/*
* tomoyo_security_ops is a "struct security_operations" which is used for
* registering TOMOYO.
@@ -531,6 +551,8 @@ static struct security_hook_list tomoyo_hooks[] __lsm_ro_after_init = {
/* Lock for GC. */
DEFINE_SRCU(tomoyo_ss);
+bool tomoyo_enabled;
+
/**
* tomoyo_init - Register TOMOYO Linux as a LSM module.
*
@@ -538,14 +560,28 @@ DEFINE_SRCU(tomoyo_ss);
*/
static int __init tomoyo_init(void)
{
+ static int finish;
struct cred *cred = (struct cred *) current_cred();
+ struct tomoyo_domain_info **blob;
+
+ if (!security_module_enable("tomoyo")) {
+ tomoyo_enabled = false;
+ return 0;
+ }
+ tomoyo_enabled = true;
- if (!security_module_enable("tomoyo"))
+ if (!finish) {
+ security_add_blobs(&tomoyo_blob_sizes);
+ finish = 1;
return 0;
+ }
+
/* register ourselves with the security framework */
security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks), "tomoyo");
printk(KERN_INFO "TOMOYO Linux initialized\n");
- cred->security = &tomoyo_kernel_domain;
+ lsm_early_cred(cred);
+ blob = tomoyo_cred(cred);
+ *blob = &tomoyo_kernel_domain;
tomoyo_mm_init();
return 0;
}
--
2.17.1
Don't use the inode->i_security pointer directly.
Provide a helper function that provides the security blob pointer.
Signed-off-by: Casey Schaufler <[email protected]>
---
security/smack/smack.h | 9 +++++++--
security/smack/smack_lsm.c | 32 ++++++++++++++++----------------
2 files changed, 23 insertions(+), 18 deletions(-)
diff --git a/security/smack/smack.h b/security/smack/smack.h
index 043525a52e94..5da5bd1b9b47 100644
--- a/security/smack/smack.h
+++ b/security/smack/smack.h
@@ -367,12 +367,17 @@ static inline struct smack_known **smack_file(const struct file *file)
return file->f_security;
}
+static inline struct inode_smack *smack_inode(const struct inode *inode)
+{
+ return inode->i_security;
+}
+
/*
* Is the directory transmuting?
*/
static inline int smk_inode_transmutable(const struct inode *isp)
{
- struct inode_smack *sip = isp->i_security;
+ struct inode_smack *sip = smack_inode(isp);
return (sip->smk_flags & SMK_INODE_TRANSMUTE) != 0;
}
@@ -381,7 +386,7 @@ static inline int smk_inode_transmutable(const struct inode *isp)
*/
static inline struct smack_known *smk_of_inode(const struct inode *isp)
{
- struct inode_smack *sip = isp->i_security;
+ struct inode_smack *sip = smack_inode(isp);
return sip->smk_inode;
}
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index d1430341798f..364699ad55b9 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -166,7 +166,7 @@ static int smk_bu_task(struct task_struct *otp, int mode, int rc)
static int smk_bu_inode(struct inode *inode, int mode, int rc)
{
struct task_smack *tsp = smack_cred(current_cred());
- struct inode_smack *isp = inode->i_security;
+ struct inode_smack *isp = smack_inode(inode);
char acc[SMK_NUM_ACCESS_TYPE + 1];
if (isp->smk_flags & SMK_INODE_IMPURE)
@@ -198,7 +198,7 @@ static int smk_bu_file(struct file *file, int mode, int rc)
struct task_smack *tsp = smack_cred(current_cred());
struct smack_known *sskp = tsp->smk_task;
struct inode *inode = file_inode(file);
- struct inode_smack *isp = inode->i_security;
+ struct inode_smack *isp = smack_inode(inode);
char acc[SMK_NUM_ACCESS_TYPE + 1];
if (isp->smk_flags & SMK_INODE_IMPURE)
@@ -228,7 +228,7 @@ static int smk_bu_credfile(const struct cred *cred, struct file *file,
struct task_smack *tsp = smack_cred(cred);
struct smack_known *sskp = tsp->smk_task;
struct inode *inode = file_inode(file);
- struct inode_smack *isp = inode->i_security;
+ struct inode_smack *isp = smack_inode(inode);
char acc[SMK_NUM_ACCESS_TYPE + 1];
if (isp->smk_flags & SMK_INODE_IMPURE)
@@ -824,7 +824,7 @@ static int smack_set_mnt_opts(struct super_block *sb,
/*
* Initialize the root inode.
*/
- isp = inode->i_security;
+ isp = smack_inode(inode);
if (isp == NULL) {
isp = new_inode_smack(sp->smk_root);
if (isp == NULL)
@@ -912,7 +912,7 @@ static int smack_bprm_set_creds(struct linux_binprm *bprm)
if (bprm->called_set_creds)
return 0;
- isp = inode->i_security;
+ isp = smack_inode(inode);
if (isp->smk_task == NULL || isp->smk_task == bsp->smk_task)
return 0;
@@ -992,7 +992,7 @@ static void smack_inode_free_rcu(struct rcu_head *head)
*/
static void smack_inode_free_security(struct inode *inode)
{
- struct inode_smack *issp = inode->i_security;
+ struct inode_smack *issp = smack_inode(inode);
/*
* The inode may still be referenced in a path walk and
@@ -1020,7 +1020,7 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir,
const struct qstr *qstr, const char **name,
void **value, size_t *len)
{
- struct inode_smack *issp = inode->i_security;
+ struct inode_smack *issp = smack_inode(inode);
struct smack_known *skp = smk_of_current();
struct smack_known *isp = smk_of_inode(inode);
struct smack_known *dsp = smk_of_inode(dir);
@@ -1358,7 +1358,7 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name,
const void *value, size_t size, int flags)
{
struct smack_known *skp;
- struct inode_smack *isp = d_backing_inode(dentry)->i_security;
+ struct inode_smack *isp = smack_inode(d_backing_inode(dentry));
if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) {
isp->smk_flags |= SMK_INODE_TRANSMUTE;
@@ -1439,7 +1439,7 @@ static int smack_inode_removexattr(struct dentry *dentry, const char *name)
if (rc != 0)
return rc;
- isp = d_backing_inode(dentry)->i_security;
+ isp = smack_inode(d_backing_inode(dentry));
/*
* Don't do anything special for these.
* XATTR_NAME_SMACKIPIN
@@ -1714,7 +1714,7 @@ static int smack_mmap_file(struct file *file,
if (unlikely(IS_PRIVATE(file_inode(file))))
return 0;
- isp = file_inode(file)->i_security;
+ isp = smack_inode(file_inode(file));
if (isp->smk_mmap == NULL)
return 0;
sbsp = file_inode(file)->i_sb->s_security;
@@ -2056,7 +2056,7 @@ static int smack_kernel_act_as(struct cred *new, u32 secid)
static int smack_kernel_create_files_as(struct cred *new,
struct inode *inode)
{
- struct inode_smack *isp = inode->i_security;
+ struct inode_smack *isp = smack_inode(inode);
struct task_smack *tsp = smack_cred(new);
tsp->smk_forked = isp->smk_inode;
@@ -2256,7 +2256,7 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info,
*/
static void smack_task_to_inode(struct task_struct *p, struct inode *inode)
{
- struct inode_smack *isp = inode->i_security;
+ struct inode_smack *isp = smack_inode(inode);
struct smack_known *skp = smk_of_task_struct(p);
isp->smk_inode = skp;
@@ -2719,7 +2719,7 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
const void *value, size_t size, int flags)
{
struct smack_known *skp;
- struct inode_smack *nsp = inode->i_security;
+ struct inode_smack *nsp = smack_inode(inode);
struct socket_smack *ssp;
struct socket *sock;
int rc = 0;
@@ -3327,7 +3327,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
if (inode == NULL)
return;
- isp = inode->i_security;
+ isp = smack_inode(inode);
mutex_lock(&isp->smk_lock);
/*
@@ -4559,7 +4559,7 @@ static int smack_inode_copy_up(struct dentry *dentry, struct cred **new)
/*
* Get label from overlay inode and set it in create_sid
*/
- isp = d_inode(dentry->d_parent)->i_security;
+ isp = smack_inode(d_inode(dentry->d_parent));
skp = isp->smk_inode;
tsp->smk_task = skp;
*new = new_creds;
@@ -4596,7 +4596,7 @@ static int smack_dentry_create_files_as(struct dentry *dentry, int mode,
/*
* the attribute of the containing directory
*/
- isp = d_inode(dentry->d_parent)->i_security;
+ isp = smack_inode(d_inode(dentry->d_parent));
if (isp->smk_flags & SMK_INODE_TRANSMUTE) {
rcu_read_lock();
--
2.17.1
On Tue, 11 Sep 2018, Casey Schaufler wrote:
> LSM: Module stacking in support of S.A.R.A and Landlock
Please help prevent RSI and shorten this to SARA.
--
James Morris
<[email protected]>
On Tue, Sep 11, 2018 at 09:41:32AM -0700, Casey Schaufler wrote:
> 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.
>
> This patch provides a subdirectory in /proc/.../attr for
> Smack. Smack user space can use the "current" file in
> this subdirectory and never have to worry about getting
> SELinux attributes by mistake. Programs that use the
> old interface will continue to work (or fail, as the case
> may be) as before.
>
Did downstream distributions already merge the stacking patches on
their own?
Got a little-bit confused after reading the log above; I already see
this in in Ubuntu 18.04.1 LTS, v4.15.0-33-generic:
$ tree /proc/self/attr/
/proc/self/attr/
├── apparmor
│ ├── current
│ ├── exec
│ └── prev
├── current
├── display_lsm
├── exec
├── fscreate
├── keycreate
├── prev
├── selinux
│ ├── current
│ ├── exec
│ ├── fscreate
│ ├── keycreate
│ ├── prev
│ └── sockcreate
├── smack
│ └── current
└── sockcreate
Thanks,
--
Darwi
http://darwish.chasingpointers.com
On 9/11/2018 4:45 PM, Ahmed S. Darwish wrote:
> On Tue, Sep 11, 2018 at 09:41:32AM -0700, Casey Schaufler wrote:
>> 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.
>>
>> This patch provides a subdirectory in /proc/.../attr for
>> Smack. Smack user space can use the "current" file in
>> this subdirectory and never have to worry about getting
>> SELinux attributes by mistake. Programs that use the
>> old interface will continue to work (or fail, as the case
>> may be) as before.
>>
> Did downstream distributions already merge the stacking patches on
> their own?
Ubuntu is leading the way with adopting the stacking patches.
Adding the SARA and LandLock authors for review & comment.
Salvatore & Mickaël: does this patchset meet your needs for merging to
mainline?
On Tue, 11 Sep 2018, Casey Schaufler wrote:
> LSM: Module stacking in support of S.A.R.A and Landlock
>
> v2: Reduce the patchset to what is required to support
> the proposed S.A.R.A. and LandLock security modules
>
> The S.A.R.A. security module is intended to be used
> in conjunction with other security modules. It requires
> state to be maintained for the credential, which
> in turn requires a mechanism for sharing the credential
> security blob. The module also requires mechanism for
> user space manipulation of the credential information,
> hence an additional subdirectory in /proc/.../attr.
>
> The LandLock security module provides user configurable
> policy in the secmark mechanism. It requires data in
> the credential, file and inode security blobs. For this
> to be used along side the existing "major" security
> modules mechanism for sharing these blobs is provided.
>
> A side effect of providing sharing of the crendential
> security blob is that the TOMOYO module can be used at
> the same time as the other "major" modules.
>
> The mechanism for configuring which security modules are
> enabled has to change when stacking in enabled. Any
> module that uses just the security blobs that are shared
> can be selected. Additionally, one other "major" module
> can be selected.
>
> The security module stacking issues around networking and
> IPC are not addressed here as they are beyond what is
> required for TOMOYO, S.A.R.A and LandLock.
>
> git://github.com/cschaufler/lsm-stacking.git#stacking-4.19-rc2-saralock
>
> Signed-off-by: Casey Schaufler <[email protected]>
> ---
> Documentation/admin-guide/LSM/index.rst | 23 ++-
> fs/proc/base.c | 64 ++++++-
> fs/proc/internal.h | 1 +
> include/linux/lsm_hooks.h | 20 ++-
> include/linux/security.h | 15 +-
> kernel/cred.c | 13 --
> security/Kconfig | 92 ++++++++++
> security/apparmor/domain.c | 2 +-
> security/apparmor/include/cred.h | 24 ++-
> security/apparmor/include/file.h | 9 +-
> security/apparmor/include/lib.h | 4 +
> security/apparmor/lsm.c | 53 ++++--
> security/apparmor/task.c | 6 +-
> security/security.c | 293 ++++++++++++++++++++++++++++++--
> security/selinux/hooks.c | 215 ++++++++---------------
> security/selinux/include/objsec.h | 37 +++-
> security/selinux/selinuxfs.c | 5 +-
> security/selinux/xfrm.c | 4 +-
> security/smack/smack.h | 42 ++++-
> security/smack/smack_access.c | 4 +-
> security/smack/smack_lsm.c | 283 +++++++++++-------------------
> security/smack/smackfs.c | 18 +-
> security/tomoyo/common.h | 31 +++-
> security/tomoyo/domain.c | 4 +-
> security/tomoyo/securityfs_if.c | 15 +-
> security/tomoyo/tomoyo.c | 57 +++++--
> 26 files changed, 899 insertions(+), 435 deletions(-)
>
--
James Morris
<[email protected]>
On Tue, Sep 11, 2018 at 9:41 AM, Casey Schaufler <[email protected]> wrote:
> 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.
>
> This patch provides a subdirectory in /proc/.../attr for
> Smack. Smack user space can use the "current" file in
> this subdirectory and never have to worry about getting
> SELinux attributes by mistake. Programs that use the
> old interface will continue to work (or fail, as the case
> may be) as before.
>
> The proposed S.A.R.A security module is dependent on
> the mechanism to create its own attr subdirectory.
>
> The original implementation is by Kees Cook.
>
> Signed-off-by: Casey Schaufler <[email protected]>
> ---
> Documentation/admin-guide/LSM/index.rst | 13 +++--
> fs/proc/base.c | 64 +++++++++++++++++++++----
> fs/proc/internal.h | 1 +
> include/linux/security.h | 15 ++++--
> security/security.c | 24 ++++++++--
> 5 files changed, 96 insertions(+), 21 deletions(-)
>
> diff --git a/Documentation/admin-guide/LSM/index.rst b/Documentation/admin-guide/LSM/index.rst
> index c980dfe9abf1..9842e21afd4a 100644
> --- a/Documentation/admin-guide/LSM/index.rst
> +++ b/Documentation/admin-guide/LSM/index.rst
> @@ -17,9 +17,8 @@ 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.
> +The Linux capabilities modules will always be included. This may be
> +followed by any number of "minor" modules and at most one "major" module.
> For more details on capabilities, see ``capabilities(7)`` in the Linux
> man-pages project.
>
> @@ -30,6 +29,14 @@ 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 ``/proc/.../attr``.
> +A security module may maintain a module specific subdirectory there,
> +named after the module. ``/proc/.../attr/smack`` is provided by the Smack
> +security module and contains all its special files. The files directly
> +in ``/proc/.../attr`` remain as legacy interfaces for modules that provide
> +subdirectories.
> +
> .. toctree::
> :maxdepth: 1
>
> diff --git a/fs/proc/base.c b/fs/proc/base.c
> index ccf86f16d9f0..bd2dd85310fe 100644
> --- a/fs/proc/base.c
> +++ b/fs/proc/base.c
> @@ -140,9 +140,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 .
> @@ -2503,7 +2507,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);
> @@ -2552,7 +2556,9 @@ static ssize_t proc_pid_attr_write(struct file * file, const char __user * buf,
> if (rv < 0)
> goto out_free;
>
> - rv = security_setprocattr(file->f_path.dentry->d_name.name, page, count);
> + rv = security_setprocattr(PROC_I(inode)->op.lsm,
> + file->f_path.dentry->d_name.name, page,
> + count);
> mutex_unlock(¤t->signal->cred_guard_mutex);
> out_free:
> kfree(page);
> @@ -2566,13 +2572,53 @@ 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_SMACK
> +static const struct pid_entry smack_attr_dir_stuff[] = {
> + ATTR("smack", "current", 0666),
> +};
> +LSM_DIR_OPS(smack);
> +#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", 0666),
> + ATTR(NULL, "prev", 0444),
> + ATTR(NULL, "exec", 0666),
> + ATTR(NULL, "fscreate", 0666),
> + ATTR(NULL, "keycreate", 0666),
> + ATTR(NULL, "sockcreate", 0666),
> +#ifdef CONFIG_SECURITY_SMACK
> + DIR("smack", 0555,
> + proc_smack_attr_dir_inode_ops, proc_smack_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 5185d7f6a51e..d4f9989063d0 100644
> --- a/fs/proc/internal.h
> +++ b/fs/proc/internal.h
> @@ -81,6 +81,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/security.h b/include/linux/security.h
> index 75f4156c84d7..418de5d20ffb 100644
> --- a/include/linux/security.h
> +++ b/include/linux/security.h
> @@ -390,8 +390,10 @@ int security_sem_semctl(struct kern_ipc_perm *sma, int cmd);
> int security_sem_semop(struct kern_ipc_perm *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(const char *name, void *value, size_t size);
> +int security_getprocattr(struct task_struct *p, const char *lsm, char *name,
> + char **value);
> +int security_setprocattr(const char *lsm, const 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);
> @@ -1139,15 +1141,18 @@ static inline int security_sem_semop(struct kern_ipc_perm *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(char *name, void *value, size_t size)
> +static inline int security_setprocattr(const char *lsm, char *name,
> + void *value, size_t size)
> {
> return -EINVAL;
> }
> diff --git a/security/security.c b/security/security.c
> index 736e78da1ab9..3dfe75d0d373 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -1288,14 +1288,30 @@ 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;
> +
> + hlist_for_each_entry(hp, &security_hook_heads.getprocattr, list) {
> + if (lsm != NULL && strcmp(lsm, hp->lsm))
> + continue;
> + return hp->hook.getprocattr(p, name, value);
> + }
Walking this list to do strcmp() makes my eye twitch. ;) I see that
procfs doesn't let us do a late evaluation and attr_dir_stuff is
const. We already have security_initcall() exporting a per-LSM
function, why not expand this slightly to include enough information
that we could resolve all this at build time?
Anyway, I view that as a potential improvement, and not something that
should block this. So:
Reviewed-by: Kees Cook <[email protected]>
> + return -EINVAL;
> }
>
> -int security_setprocattr(const char *name, void *value, size_t size)
> +int security_setprocattr(const char *lsm, const char *name, void *value,
> + size_t size)
> {
> - return call_int_hook(setprocattr, -EINVAL, name, value, size);
> + struct security_hook_list *hp;
> +
> + hlist_for_each_entry(hp, &security_hook_heads.setprocattr, list) {
> + if (lsm != NULL && strcmp(lsm, hp->lsm))
> + continue;
> + return hp->hook.setprocattr(name, value, size);
> + }
> + return -EINVAL;
> }
>
> int security_netlink_send(struct sock *sk, struct sk_buff *skb)
> --
> 2.17.1
>
>
--
Kees Cook
Pixel Security
On Tue, Sep 11, 2018 at 9:41 AM, Casey Schaufler <[email protected]> wrote:
> Don't use the cred->security pointer directly.
> Provide a helper function that provides the security blob pointer.
>
> Signed-off-by: Casey Schaufler <[email protected]>
> ---
> security/smack/smack.h | 14 +++++++--
> security/smack/smack_access.c | 4 +--
> security/smack/smack_lsm.c | 57 +++++++++++++++++------------------
> security/smack/smackfs.c | 18 +++++------
> 4 files changed, 50 insertions(+), 43 deletions(-)
>
> diff --git a/security/smack/smack.h b/security/smack/smack.h
> index f7db791fb566..0b55d6a55b26 100644
> --- a/security/smack/smack.h
> +++ b/security/smack/smack.h
> @@ -356,6 +356,11 @@ extern struct list_head smack_onlycap_list;
> #define SMACK_HASH_SLOTS 16
> extern struct hlist_head smack_known_hash[SMACK_HASH_SLOTS];
>
> +static inline struct task_smack *smack_cred(const struct cred *cred)
> +{
> + return cred->security;
> +}
> +
> /*
> * Is the directory transmuting?
> */
> @@ -382,13 +387,16 @@ static inline struct smack_known *smk_of_task(const struct task_smack *tsp)
> return tsp->smk_task;
> }
>
> -static inline struct smack_known *smk_of_task_struct(const struct task_struct *t)
> +static inline struct smack_known *smk_of_task_struct(
> + const struct task_struct *t)
> {
> struct smack_known *skp;
> + const struct cred *cred;
>
> rcu_read_lock();
> - skp = smk_of_task(__task_cred(t)->security);
> + cred = __task_cred(t);
> rcu_read_unlock();
> + skp = smk_of_task(smack_cred(cred));
Hm, why is this safe? (i.e. what is pinning the cred?) I would expect
get_cred()/put_cred() since this is not for "current"? And then what
controls the skp lifetime?
Everything else looks to be mechanical replacement, so that's fine.
Did you use some tooling to do the mechanical replacement or was it
done by hand?
-Kees
--
Kees Cook
Pixel Security
On Tue, Sep 11, 2018 at 9:41 AM, Casey Schaufler <[email protected]> wrote:
> Don't use the cred->security pointer directly.
> Provide a helper function that provides the security blob pointer.
>
> Signed-off-by: Casey Schaufler <[email protected]>
Like smack, this seems to be largely:
s/$identifier->security/selinux_cred($identifier)/
s/current_security()/selinux_cred(current_cred())/
Is that right? The one __task_cred() use seemed to be fully contained
under rcu read lock.
Reviewed-by: Kees Cook <[email protected]>
-Kees
--
Kees Cook
Pixel Security
On Tue, Sep 11, 2018 at 9:41 AM, Casey Schaufler <[email protected]> wrote:
> Move management of the cred security blob out of the
> security modules and into the security infrastructure.
> Instead of allocating and freeing space the security
> modules tell the infrastructure how much space they
> require.
There's a lot of changes here that I think deserve some longer
discussion in the changelog. Notably, now we run _two_ cycles of init
calls? That seems... odd. Notes below...
> Some SELinux memory management debug code has been removed.
>
> Signed-off-by: Casey Schaufler <[email protected]>
> ---
> include/linux/lsm_hooks.h | 14 ++++
> kernel/cred.c | 13 ----
> security/Kconfig | 11 ++++
> security/apparmor/domain.c | 2 +-
> security/apparmor/include/cred.h | 16 ++++-
> security/apparmor/lsm.c | 28 ++++++--
> security/apparmor/task.c | 6 +-
> security/security.c | 106 +++++++++++++++++++++++++++++-
> security/selinux/hooks.c | 63 +++++-------------
> security/selinux/include/objsec.h | 4 ++
> security/selinux/selinuxfs.c | 1 +
> security/smack/smack.h | 1 +
> security/smack/smack_lsm.c | 85 +++++++++---------------
> security/tomoyo/common.h | 21 +++++-
> security/tomoyo/domain.c | 4 +-
> security/tomoyo/securityfs_if.c | 15 +++--
> security/tomoyo/tomoyo.c | 56 +++++++++++++---
> 17 files changed, 303 insertions(+), 143 deletions(-)
>
> diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
> index 97a020c616ad..0bef312efd45 100644
> --- a/include/linux/lsm_hooks.h
> +++ b/include/linux/lsm_hooks.h
> @@ -2024,6 +2024,13 @@ struct security_hook_list {
> char *lsm;
> } __randomize_layout;
>
> +/*
> + * Security blob size or offset data.
> + */
> +struct lsm_blob_sizes {
> + int lbs_cred;
> +};
> +
> /*
> * Initializing a security_hook_list structure takes
> * up a lot of space in a source file. This macro takes
> @@ -2036,6 +2043,7 @@ struct security_hook_list {
> extern struct security_hook_heads security_hook_heads;
> extern char *lsm_names;
>
> +extern void security_add_blobs(struct lsm_blob_sizes *needed);
> extern void security_add_hooks(struct security_hook_list *hooks, int count,
> char *lsm);
>
> @@ -2082,4 +2090,10 @@ void __init loadpin_add_hooks(void);
> static inline void loadpin_add_hooks(void) { };
> #endif
>
> +extern int lsm_cred_alloc(struct cred *cred, gfp_t gfp);
> +
> +#ifdef CONFIG_SECURITY
> +void lsm_early_cred(struct cred *cred);
> +#endif
> +
> #endif /* ! __LINUX_LSM_HOOKS_H */
> diff --git a/kernel/cred.c b/kernel/cred.c
> index ecf03657e71c..fa2061ee4955 100644
> --- a/kernel/cred.c
> +++ b/kernel/cred.c
> @@ -704,19 +704,6 @@ bool creds_are_invalid(const struct cred *cred)
> {
> if (cred->magic != CRED_MAGIC)
> return true;
> -#ifdef CONFIG_SECURITY_SELINUX
> - /*
> - * cred->security == NULL if security_cred_alloc_blank() or
> - * security_prepare_creds() returned an error.
> - */
> - if (selinux_is_enabled() && cred->security) {
> - if ((unsigned long) cred->security < PAGE_SIZE)
> - return true;
> - if ((*(u32 *)cred->security & 0xffffff00) ==
> - (POISON_FREE << 24 | POISON_FREE << 16 | POISON_FREE << 8))
> - return true;
These aren't unreasonable checks -- can we add them back in later?
(They don't need to be selinux specific, in fact: the LSM could do the
poison...)
> [...]
> diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
> index 08c88de0ffda..726910bba84b 100644
> --- a/security/apparmor/domain.c
> +++ b/security/apparmor/domain.c
> @@ -975,7 +975,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
> }
> aa_put_label(cred_label(bprm->cred));
> /* transfer reference, released when cred is freed */
> - cred_label(bprm->cred) = new;
> + set_cred_label(bprm->cred, new);
>
> done:
> aa_put_label(label);
> diff --git a/security/apparmor/include/cred.h b/security/apparmor/include/cred.h
> index e287b7d0d4be..a90eae76d7c1 100644
> --- a/security/apparmor/include/cred.h
> +++ b/security/apparmor/include/cred.h
> @@ -23,8 +23,22 @@
> #include "policy_ns.h"
> #include "task.h"
>
> -#define cred_label(X) ((X)->security)
> +static inline struct aa_label *cred_label(const struct cred *cred)
> +{
> + struct aa_label **blob = cred->security;
> +
> + AA_BUG(!blob);
> + return *blob;
> +}
>
> +static inline void set_cred_label(const struct cred *cred,
> + struct aa_label *label)
> +{
> + struct aa_label **blob = cred->security;
> +
> + AA_BUG(!blob);
> + *blob = label;
> +}
This feels like it should be a separate patch? Shouldn't AA not be
playing with cred->security directly before we get to this "sizing and
allocation" patch?
> [...]
> diff --git a/security/security.c b/security/security.c
> index 3dfe75d0d373..ff7df14f6db1 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -41,6 +41,8 @@ struct security_hook_heads security_hook_heads __lsm_ro_after_init;
> static ATOMIC_NOTIFIER_HEAD(lsm_notifier_chain);
>
> char *lsm_names;
> +static struct lsm_blob_sizes blob_sizes;
This needs to be __lsm_ro_after_init.
> +
> /* Boot-time LSM user choice */
> static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =
> CONFIG_DEFAULT_SECURITY;
> @@ -85,10 +87,22 @@ int __init security_init(void)
> loadpin_add_hooks();
>
> /*
> - * Load all the remaining security modules.
> + * The first call to a module specific init function
> + * updates the blob size requirements.
> + */
> + do_security_initcalls();
> +
> + /*
> + * The second call to a module specific init function
> + * adds hooks to the hook lists and does any other early
> + * initializations required.
> */
> do_security_initcalls();
Tracking init state internally to each LSM seems not great. What about
having the state be a global the LSMs can query?
enum security_initcall_state_t {
LSM_PRE_INIT,
LSM_INIT,
};
static __initdata enum security_initcall_state_t security_initcall_state;
static void __init __do_security_initcalls(enum security_initcall_state state)
{
int ret;
initcall_t call;
initcall_entry_t *ce;
security_initcall_state = state;
ce = __security_initcall_start;
trace_initcall_level("security");
while (ce < __security_initcall_end) {
call = initcall_from_entry(ce);
trace_initcall_start(call);
ret = call();
trace_initcall_finish(call, ret);
ce++;
}
}
static void __init do_security_initcalls(void)
{
__do_security_initcalls(LSM_PRE_INIT);
__do_security_initcalls(LSM_INIT);
}
>
> +#ifdef CONFIG_SECURITY_LSM_DEBUG
> + pr_info("LSM: cred blob size = %d\n", blob_sizes.lbs_cred);
> +#endif
> +
> return 0;
> }
>
> @@ -198,6 +212,73 @@ int unregister_lsm_notifier(struct notifier_block *nb)
> }
> EXPORT_SYMBOL(unregister_lsm_notifier);
>
> +/**
> + * lsm_cred_alloc - allocate a composite cred blob
> + * @cred: the cred that needs a blob
> + * @gfp: allocation type
> + *
> + * Allocate the cred blob for all the modules
> + *
> + * Returns 0, or -ENOMEM if memory can't be allocated.
> + */
> +int lsm_cred_alloc(struct cred *cred, gfp_t gfp)
> +{
> + if (blob_sizes.lbs_cred == 0) {
> + cred->security = NULL;
> + return 0;
> + }
> +
> + cred->security = kzalloc(blob_sizes.lbs_cred, gfp);
> + if (cred->security == NULL)
> + return -ENOMEM;
> + return 0;
> +}
> +
> +/**
> + * lsm_early_cred - during initialization allocate a composite cred blob
> + * @cred: the cred that needs a blob
> + *
> + * Allocate the cred blob for all the modules if it's not already there
Perhaps mention this is mainly for retroactively attaching a cred blob
to "init"?
> + */
> +void lsm_early_cred(struct cred *cred)
> +{
> + int rc;
> +
> + if (cred == NULL)
> + panic("%s: NULL cred.\n", __func__);
I have been given strongly worded advice to never BUG nor panic. I would:
if (WARN_ON(!cred || !cred->security))
return;
> + if (cred->security != NULL)
> + return;
> + rc = lsm_cred_alloc(cred, GFP_KERNEL);
> + if (rc)
> + panic("%s: Early cred alloc failed.\n", __func__);
And:
WARN_ON(lsm_cred_alloc(cred, GFP_KERNEL));
> +}
> +
> +static void __init lsm_set_size(int *need, int *lbs)
> +{
> + int offset;
> +
> + if (*need > 0) {
> + offset = *lbs;
> + *lbs += *need;
> + *need = offset;
> + }
> +}
> +
> +/**
> + * security_add_blobs - Report blob sizes
> + * @needed: the size of blobs needed by the module
> + *
> + * Each LSM has to register its blobs with the infrastructure.
> + * The "needed" data tells the infrastructure how much memory
> + * the module requires for each of its blobs. On return the
> + * structure is filled with the offset that module should use
> + * from the blob pointer.
> + */
> +void __init security_add_blobs(struct lsm_blob_sizes *needed)
> +{
> + lsm_set_size(&needed->lbs_cred, &blob_sizes.lbs_cred);
> +}
> +
> /*
> * Hook list operation macros.
> *
> @@ -998,17 +1079,36 @@ void security_task_free(struct task_struct *task)
>
> int security_cred_alloc_blank(struct cred *cred, gfp_t gfp)
> {
> - return call_int_hook(cred_alloc_blank, 0, cred, gfp);
> + int rc = lsm_cred_alloc(cred, gfp);
> +
> + if (rc)
> + return rc;
> +
> + rc = call_int_hook(cred_alloc_blank, 0, cred, gfp);
> + if (rc)
> + security_cred_free(cred);
> + return rc;
I spent some time looking at this, but I think this is fine. I thought
it might be a double-free via the put_cred_rcu() path, but
cred->security gets set to NULL below, and we really don't want
allocated memory hanging around in the call_int_hook fails, so ...
yes. All good.
> }
>
> void security_cred_free(struct cred *cred)
> {
> call_void_hook(cred_free, cred);
> +
> + kfree(cred->security);
> + cred->security = NULL;
> }
>
> int security_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp)
> {
> - return call_int_hook(cred_prepare, 0, new, old, gfp);
> + int rc = lsm_cred_alloc(new, gfp);
> +
> + if (rc)
> + return rc;
> +
> + rc = call_int_hook(cred_prepare, 0, new, old, gfp);
> + if (rc)
> + security_cred_free(new);
> + return rc;
> }
>
> void security_transfer_creds(struct cred *new, const struct cred *old)
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index 9d6cdd21acb6..9b49698754a7 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -213,12 +213,9 @@ static void cred_init_security(void)
> struct cred *cred = (struct cred *) current->real_cred;
> struct task_security_struct *tsec;
>
> - tsec = kzalloc(sizeof(struct task_security_struct), GFP_KERNEL);
> - if (!tsec)
> - panic("SELinux: Failed to initialize initial task.\n");
> -
> + lsm_early_cred(cred);
> + tsec = selinux_cred(cred);
Perhaps leave the existing panic() as-is?
> tsec->osid = tsec->sid = SECINITSID_KERNEL;
> - cred->security = tsec;
> }
>
> /*
> @@ -3898,53 +3895,17 @@ static int selinux_task_alloc(struct task_struct *task,
> sid, sid, SECCLASS_PROCESS, PROCESS__FORK, NULL);
> }
>
> -/*
> - * allocate the SELinux part of blank credentials
> - */
> -static int selinux_cred_alloc_blank(struct cred *cred, gfp_t gfp)
> -{
> - struct task_security_struct *tsec;
> -
> - tsec = kzalloc(sizeof(struct task_security_struct), gfp);
> - if (!tsec)
> - return -ENOMEM;
> -
> - cred->security = tsec;
> - return 0;
> -}
> -
> -/*
> - * detach and free the LSM part of a set of credentials
> - */
> -static void selinux_cred_free(struct cred *cred)
> -{
> - struct task_security_struct *tsec = selinux_cred(cred);
> -
> - /*
> - * cred->security == NULL if security_cred_alloc_blank() or
> - * security_prepare_creds() returned an error.
> - */
> - BUG_ON(cred->security && (unsigned long) cred->security < PAGE_SIZE);
> - cred->security = (void *) 0x7UL;
What is the world was this? Is this a "< PAGE_SIZE" poison of 0x7?
> - kfree(tsec);
> -}
> -
> /*
> * prepare a new set of credentials for modification
> */
> static int selinux_cred_prepare(struct cred *new, const struct cred *old,
> gfp_t gfp)
> {
> - const struct task_security_struct *old_tsec;
> - struct task_security_struct *tsec;
> -
> - old_tsec = selinux_cred(old);
> + const struct task_security_struct *old_tsec = selinux_cred(old);
> + struct task_security_struct *tsec = selinux_cred(new);
>
> - tsec = kmemdup(old_tsec, sizeof(struct task_security_struct), gfp);
> - if (!tsec)
> - return -ENOMEM;
> + *tsec = *old_tsec;
>
> - new->security = tsec;
> return 0;
> }
>
> @@ -6894,6 +6855,10 @@ static void selinux_bpf_prog_free(struct bpf_prog_aux *aux)
> }
> #endif
>
> +struct lsm_blob_sizes selinux_blob_sizes = {
> + .lbs_cred = sizeof(struct task_security_struct),
> +};
> +
> static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
> LSM_HOOK_INIT(binder_set_context_mgr, selinux_binder_set_context_mgr),
> LSM_HOOK_INIT(binder_transaction, selinux_binder_transaction),
> @@ -6976,8 +6941,6 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
> LSM_HOOK_INIT(file_open, selinux_file_open),
>
> LSM_HOOK_INIT(task_alloc, selinux_task_alloc),
> - LSM_HOOK_INIT(cred_alloc_blank, selinux_cred_alloc_blank),
> - LSM_HOOK_INIT(cred_free, selinux_cred_free),
> LSM_HOOK_INIT(cred_prepare, selinux_cred_prepare),
> LSM_HOOK_INIT(cred_transfer, selinux_cred_transfer),
> LSM_HOOK_INIT(cred_getsecid, selinux_cred_getsecid),
> @@ -7133,11 +7096,19 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
>
> static __init int selinux_init(void)
> {
> + static int finish;
> +
> if (!security_module_enable("selinux")) {
> selinux_enabled = 0;
> return 0;
> }
>
> + if (!finish) {
> + security_add_blobs(&selinux_blob_sizes);
> + finish = 1;
> + return 0;
> + }
> +
> if (!selinux_enabled) {
> pr_info("SELinux: Disabled at boot.\n");
> return 0;
> diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
> index 734b6833bdff..db1c7000ada3 100644
> --- a/security/selinux/include/objsec.h
> +++ b/security/selinux/include/objsec.h
> @@ -25,6 +25,9 @@
> #include <linux/binfmts.h>
> #include <linux/in.h>
> #include <linux/spinlock.h>
> +#include <linux/lsm_hooks.h>
> +#include <linux/msg.h>
> +#include <net/sock.h>
That's a surprising number of new headers?
> #include <net/net_namespace.h>
> #include "flask.h"
> #include "avc.h"
> @@ -158,6 +161,7 @@ struct bpf_security_struct {
> u32 sid; /*SID of bpf obj creater*/
> };
>
> +extern struct lsm_blob_sizes selinux_blob_sizes;
> static inline struct task_security_struct *selinux_cred(const struct cred *cred)
> {
> return cred->security;
> diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
> index f3a5a138a096..b5665bdc29fc 100644
> --- a/security/selinux/selinuxfs.c
> +++ b/security/selinux/selinuxfs.c
> @@ -31,6 +31,7 @@
> #include <linux/uaccess.h>
> #include <linux/kobject.h>
> #include <linux/ctype.h>
> +#include <linux/lsm_hooks.h>
>
> /* selinuxfs pseudo filesystem for exporting the security policy API.
> Based on the proc code and the fs/nfsd/nfsctl.c code. */
> diff --git a/security/smack/smack.h b/security/smack/smack.h
> index 0b55d6a55b26..0c6dce446825 100644
> --- a/security/smack/smack.h
> +++ b/security/smack/smack.h
> @@ -24,6 +24,7 @@
> #include <linux/list.h>
> #include <linux/rculist.h>
> #include <linux/lsm_audit.h>
> +#include <linux/msg.h>
>
> /*
> * Use IPv6 port labeling if IPv6 is enabled and secmarks
> diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
> index 68ee3ae8f25c..a06ea8aa89c4 100644
> --- a/security/smack/smack_lsm.c
> +++ b/security/smack/smack_lsm.c
> @@ -309,29 +309,20 @@ static struct inode_smack *new_inode_smack(struct smack_known *skp)
> }
>
> /**
> - * new_task_smack - allocate a task security blob
> + * init_task_smack - initialize a task security blob
> + * @tsp: blob to initialize
> * @task: a pointer to the Smack label for the running task
> * @forked: a pointer to the Smack label for the forked task
> - * @gfp: type of the memory for the allocation
> *
> - * Returns the new blob or NULL if there's no memory available
> */
> -static struct task_smack *new_task_smack(struct smack_known *task,
> - struct smack_known *forked, gfp_t gfp)
> +static void init_task_smack(struct task_smack *tsp, struct smack_known *task,
> + struct smack_known *forked)
> {
> - struct task_smack *tsp;
> -
> - tsp = kzalloc(sizeof(struct task_smack), gfp);
> - if (tsp == NULL)
> - return NULL;
> -
> tsp->smk_task = task;
> tsp->smk_forked = forked;
> INIT_LIST_HEAD(&tsp->smk_rules);
> INIT_LIST_HEAD(&tsp->smk_relabel);
> mutex_init(&tsp->smk_rules_lock);
> -
> - return tsp;
> }
>
> /**
> @@ -1958,14 +1949,7 @@ static int smack_file_open(struct file *file)
> */
> static int smack_cred_alloc_blank(struct cred *cred, gfp_t gfp)
> {
> - struct task_smack *tsp;
> -
> - tsp = new_task_smack(NULL, NULL, gfp);
> - if (tsp == NULL)
> - return -ENOMEM;
> -
> - cred->security = tsp;
> -
> + init_task_smack(smack_cred(cred), NULL, NULL);
> return 0;
> }
>
> @@ -1982,10 +1966,6 @@ static void smack_cred_free(struct cred *cred)
> struct list_head *l;
> struct list_head *n;
>
> - if (tsp == NULL)
> - return;
> - cred->security = NULL;
> -
> smk_destroy_label_list(&tsp->smk_relabel);
>
> list_for_each_safe(l, n, &tsp->smk_rules) {
> @@ -1993,7 +1973,6 @@ static void smack_cred_free(struct cred *cred)
> list_del(&rp->list);
> kfree(rp);
> }
> - kfree(tsp);
> }
>
> /**
> @@ -2008,14 +1987,10 @@ static int smack_cred_prepare(struct cred *new, const struct cred *old,
> gfp_t gfp)
> {
> struct task_smack *old_tsp = smack_cred(old);
> - struct task_smack *new_tsp;
> + struct task_smack *new_tsp = smack_cred(new);
> int rc;
>
> - new_tsp = new_task_smack(old_tsp->smk_task, old_tsp->smk_task, gfp);
> - if (new_tsp == NULL)
> - return -ENOMEM;
> -
> - new->security = new_tsp;
> + init_task_smack(new_tsp, old_tsp->smk_task, old_tsp->smk_task);
>
> rc = smk_copy_rules(&new_tsp->smk_rules, &old_tsp->smk_rules, gfp);
> if (rc != 0)
> @@ -2023,10 +1998,7 @@ static int smack_cred_prepare(struct cred *new, const struct cred *old,
>
> rc = smk_copy_relabel(&new_tsp->smk_relabel, &old_tsp->smk_relabel,
> gfp);
> - if (rc != 0)
> - return rc;
> -
> - return 0;
> + return rc;
> }
>
> /**
> @@ -4652,6 +4624,10 @@ static int smack_dentry_create_files_as(struct dentry *dentry, int mode,
> return 0;
> }
>
> +struct lsm_blob_sizes smack_blob_sizes = {
> + .lbs_cred = sizeof(struct task_smack),
> +};
> +
> static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
> LSM_HOOK_INIT(ptrace_access_check, smack_ptrace_access_check),
> LSM_HOOK_INIT(ptrace_traceme, smack_ptrace_traceme),
> @@ -4830,23 +4806,35 @@ static __init void init_smack_known_list(void)
> */
> static __init int smack_init(void)
> {
> - struct cred *cred;
> + static int finish;
> + struct cred *cred = (struct cred *) current->cred;
> struct task_smack *tsp;
>
> if (!security_module_enable("smack"))
> return 0;
>
> + if (!finish) {
> + security_add_blobs(&smack_blob_sizes);
> + finish = 1;
> + return 0;
> + }
> +
> smack_inode_cache = KMEM_CACHE(inode_smack, 0);
> if (!smack_inode_cache)
> return -ENOMEM;
>
> - tsp = new_task_smack(&smack_known_floor, &smack_known_floor,
> - GFP_KERNEL);
> - if (tsp == NULL) {
> - kmem_cache_destroy(smack_inode_cache);
> - return -ENOMEM;
> - }
> + lsm_early_cred(cred);
>
> + /*
> + * Set the security state for the initial task.
> + */
> + tsp = smack_cred(cred);
> + init_task_smack(tsp, &smack_known_floor, &smack_known_floor);
> +
> + /*
> + * Register with LSM
> + */
> + security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks), "smack");
> smack_enabled = 1;
>
> pr_info("Smack: Initializing.\n");
> @@ -4860,20 +4848,9 @@ static __init int smack_init(void)
> pr_info("Smack: IPv6 Netfilter enabled.\n");
> #endif
>
> - /*
> - * Set the security state for the initial task.
> - */
> - cred = (struct cred *) current->cred;
> - cred->security = tsp;
> -
> /* initialize the smack_known_list */
> init_smack_known_list();
>
> - /*
> - * Register with LSM
> - */
> - security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks), "smack");
> -
> return 0;
> }
I feel like some of this Smack refactoring could be split out to ease review...
>
> diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h
> index 539bcdd30bb8..0110bebe86e2 100644
> --- a/security/tomoyo/common.h
> +++ b/security/tomoyo/common.h
> @@ -29,6 +29,7 @@
> #include <linux/in.h>
> #include <linux/in6.h>
> #include <linux/un.h>
> +#include <linux/lsm_hooks.h>
> #include <net/sock.h>
> #include <net/af_unix.h>
> #include <net/ip.h>
> @@ -1062,6 +1063,7 @@ void tomoyo_write_log2(struct tomoyo_request_info *r, int len, const char *fmt,
> /********** External variable definitions. **********/
>
> extern bool tomoyo_policy_loaded;
> +extern bool tomoyo_enabled;
> extern const char * const tomoyo_condition_keyword
> [TOMOYO_MAX_CONDITION_KEYWORD];
> extern const char * const tomoyo_dif[TOMOYO_MAX_DOMAIN_INFO_FLAGS];
> @@ -1196,6 +1198,17 @@ static inline void tomoyo_put_group(struct tomoyo_group *group)
> atomic_dec(&group->head.users);
> }
>
> +/**
> + * tomoyo_cred - Get a pointer to the tomoyo cred security blob
> + * @cred - the relevant cred
> + *
> + * Returns pointer to the tomoyo cred blob.
> + */
> +static inline struct tomoyo_domain_info **tomoyo_cred(const struct cred *cred)
> +{
> + return cred->security;
> +}
> +
> /**
> * tomoyo_domain - Get "struct tomoyo_domain_info" for current thread.
> *
> @@ -1203,7 +1216,9 @@ static inline void tomoyo_put_group(struct tomoyo_group *group)
> */
> static inline struct tomoyo_domain_info *tomoyo_domain(void)
> {
> - return current_cred()->security;
> + struct tomoyo_domain_info **blob = tomoyo_cred(current_cred());
> +
> + return *blob;
> }
>
> /**
> @@ -1216,7 +1231,9 @@ static inline struct tomoyo_domain_info *tomoyo_domain(void)
> static inline struct tomoyo_domain_info *tomoyo_real_domain(struct task_struct
> *task)
> {
> - return task_cred_xxx(task, security);
> + struct tomoyo_domain_info **blob = tomoyo_cred(get_task_cred(task));
> +
> + return *blob;
> }
>
> /**
Shouldn't this Tomoyo cred handling get split out?
> diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c
> index f6758dad981f..b7469fdbff01 100644
> --- a/security/tomoyo/domain.c
> +++ b/security/tomoyo/domain.c
> @@ -678,6 +678,7 @@ static int tomoyo_environ(struct tomoyo_execve *ee)
> */
> int tomoyo_find_next_domain(struct linux_binprm *bprm)
> {
> + struct tomoyo_domain_info **blob;
> struct tomoyo_domain_info *old_domain = tomoyo_domain();
> struct tomoyo_domain_info *domain = NULL;
> const char *original_name = bprm->filename;
> @@ -843,7 +844,8 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
> domain = old_domain;
> /* Update reference count on "struct tomoyo_domain_info". */
> atomic_inc(&domain->users);
> - bprm->cred->security = domain;
> + blob = tomoyo_cred(bprm->cred);
> + *blob = domain;
> kfree(exename.name);
> if (!retval) {
> ee->r.domain = domain;
> diff --git a/security/tomoyo/securityfs_if.c b/security/tomoyo/securityfs_if.c
> index 1d3d7e7a1f05..768dff9608b1 100644
> --- a/security/tomoyo/securityfs_if.c
> +++ b/security/tomoyo/securityfs_if.c
> @@ -71,9 +71,12 @@ static ssize_t tomoyo_write_self(struct file *file, const char __user *buf,
> if (!cred) {
> error = -ENOMEM;
> } else {
> - struct tomoyo_domain_info *old_domain =
> - cred->security;
> - cred->security = new_domain;
> + struct tomoyo_domain_info **blob;
> + struct tomoyo_domain_info *old_domain;
> +
> + blob = tomoyo_cred(cred);
> + old_domain = *blob;
> + *blob = new_domain;
> atomic_inc(&new_domain->users);
> atomic_dec(&old_domain->users);
> commit_creds(cred);
> @@ -234,10 +237,14 @@ static void __init tomoyo_create_entry(const char *name, const umode_t mode,
> */
> static int __init tomoyo_initerface_init(void)
> {
> + struct tomoyo_domain_info *domain;
> struct dentry *tomoyo_dir;
>
> + if (!tomoyo_enabled)
> + return 0;
> + domain = tomoyo_domain();
> /* Don't create securityfs entries unless registered. */
> - if (current_cred()->security != &tomoyo_kernel_domain)
> + if (domain != &tomoyo_kernel_domain)
> return 0;
>
> tomoyo_dir = securityfs_create_dir("tomoyo", NULL);
> diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
> index 9f932e2d6852..bb84e6ec3886 100644
> --- a/security/tomoyo/tomoyo.c
> +++ b/security/tomoyo/tomoyo.c
> @@ -18,7 +18,9 @@
> */
> static int tomoyo_cred_alloc_blank(struct cred *new, gfp_t gfp)
> {
> - new->security = NULL;
> + struct tomoyo_domain_info **blob = tomoyo_cred(new);
> +
> + *blob = NULL;
> return 0;
> }
>
> @@ -34,8 +36,13 @@ static int tomoyo_cred_alloc_blank(struct cred *new, gfp_t gfp)
> static int tomoyo_cred_prepare(struct cred *new, const struct cred *old,
> gfp_t gfp)
> {
> - struct tomoyo_domain_info *domain = old->security;
> - new->security = domain;
> + struct tomoyo_domain_info **old_blob = tomoyo_cred(old);
> + struct tomoyo_domain_info **new_blob = tomoyo_cred(new);
> + struct tomoyo_domain_info *domain;
> +
> + domain = *old_blob;
> + *new_blob = domain;
> +
> if (domain)
> atomic_inc(&domain->users);
> return 0;
> @@ -59,7 +66,9 @@ static void tomoyo_cred_transfer(struct cred *new, const struct cred *old)
> */
> static void tomoyo_cred_free(struct cred *cred)
> {
> - struct tomoyo_domain_info *domain = cred->security;
> + struct tomoyo_domain_info **blob = tomoyo_cred(cred);
> + struct tomoyo_domain_info *domain = *blob;
> +
> if (domain)
> atomic_dec(&domain->users);
> }
> @@ -73,6 +82,9 @@ static void tomoyo_cred_free(struct cred *cred)
> */
> static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
> {
> + struct tomoyo_domain_info **blob;
> + struct tomoyo_domain_info *domain;
> +
> /*
> * Do only if this function is called for the first time of an execve
> * operation.
> @@ -93,13 +105,14 @@ static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
> * stored inside "bprm->cred->security" will be acquired later inside
> * tomoyo_find_next_domain().
> */
> - atomic_dec(&((struct tomoyo_domain_info *)
> - bprm->cred->security)->users);
> + blob = tomoyo_cred(bprm->cred);
> + domain = *blob;
> + atomic_dec(&domain->users);
> /*
> * Tell tomoyo_bprm_check_security() is called for the first time of an
> * execve operation.
> */
> - bprm->cred->security = NULL;
> + *blob = NULL;
> return 0;
> }
>
> @@ -112,8 +125,11 @@ static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
> */
> static int tomoyo_bprm_check_security(struct linux_binprm *bprm)
> {
> - struct tomoyo_domain_info *domain = bprm->cred->security;
> + struct tomoyo_domain_info **blob;
> + struct tomoyo_domain_info *domain;
>
> + blob = tomoyo_cred(bprm->cred);
> + domain = *blob;
> /*
> * Execute permission is checked against pathname passed to do_execve()
> * using current domain.
> @@ -493,6 +509,10 @@ static int tomoyo_socket_sendmsg(struct socket *sock, struct msghdr *msg,
> return tomoyo_socket_sendmsg_permission(sock, msg, size);
> }
>
> +struct lsm_blob_sizes tomoyo_blob_sizes = {
> + .lbs_cred = sizeof(struct tomoyo_domain_info *),
> +};
> +
> /*
> * tomoyo_security_ops is a "struct security_operations" which is used for
> * registering TOMOYO.
> @@ -531,6 +551,8 @@ static struct security_hook_list tomoyo_hooks[] __lsm_ro_after_init = {
> /* Lock for GC. */
> DEFINE_SRCU(tomoyo_ss);
>
> +bool tomoyo_enabled;
> +
> /**
> * tomoyo_init - Register TOMOYO Linux as a LSM module.
> *
> @@ -538,14 +560,28 @@ DEFINE_SRCU(tomoyo_ss);
> */
> static int __init tomoyo_init(void)
> {
> + static int finish;
> struct cred *cred = (struct cred *) current_cred();
> + struct tomoyo_domain_info **blob;
> +
> + if (!security_module_enable("tomoyo")) {
> + tomoyo_enabled = false;
> + return 0;
> + }
> + tomoyo_enabled = true;
>
> - if (!security_module_enable("tomoyo"))
> + if (!finish) {
> + security_add_blobs(&tomoyo_blob_sizes);
> + finish = 1;
> return 0;
> + }
> +
> /* register ourselves with the security framework */
> security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks), "tomoyo");
> printk(KERN_INFO "TOMOYO Linux initialized\n");
> - cred->security = &tomoyo_kernel_domain;
> + lsm_early_cred(cred);
> + blob = tomoyo_cred(cred);
> + *blob = &tomoyo_kernel_domain;
> tomoyo_mm_init();
> return 0;
> }
> --
> 2.17.1
>
>
-Kees
--
Kees Cook
Pixel Security
On Tue, Sep 11, 2018 at 9:41 AM, Casey Schaufler <[email protected]> wrote:
> Don't use the file->f_security pointer directly.
> Provide a helper function that provides the security blob pointer.
>
> Signed-off-by: Casey Schaufler <[email protected]>
Seems delightfully mechanical.
Reviewed-by: Kees Cook <[email protected]>
-Kees
--
Kees Cook
Pixel Security
On Tue, Sep 11, 2018 at 9:42 AM, Casey Schaufler <[email protected]> wrote:
> Move management of the file->f_security blob out of the
> individual security modules and into the infrastructure.
> The modules no longer allocate or free the data, instead
> they tell the infrastructure how much space they require.
>
> Signed-off-by: Casey Schaufler <[email protected]>
> ---
> include/linux/lsm_hooks.h | 1 +
> security/apparmor/lsm.c | 19 +++++++-------
> security/security.c | 54 +++++++++++++++++++++++++++++++++++---
> security/selinux/hooks.c | 25 ++----------------
> security/smack/smack.h | 5 ++++
> security/smack/smack_lsm.c | 26 +++++++-----------
> 6 files changed, 78 insertions(+), 52 deletions(-)
>
> diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
> index 0bef312efd45..167ffbd4d0c0 100644
> --- a/include/linux/lsm_hooks.h
> +++ b/include/linux/lsm_hooks.h
> @@ -2029,6 +2029,7 @@ struct security_hook_list {
> */
> struct lsm_blob_sizes {
> int lbs_cred;
> + int lbs_file;
> };
>
> /*
> diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
> index c2566aaa138e..15716b6ff860 100644
> --- a/security/apparmor/lsm.c
> +++ b/security/apparmor/lsm.c
> @@ -431,21 +431,21 @@ static int apparmor_file_open(struct file *file)
>
> static int apparmor_file_alloc_security(struct file *file)
> {
> - int error = 0;
> -
> - /* freed by apparmor_file_free_security */
> + struct aa_file_ctx *ctx = file_ctx(file);
> struct aa_label *label = begin_current_label_crit_section();
> - file->f_security = aa_alloc_file_ctx(label, GFP_KERNEL);
> - if (!file_ctx(file))
> - error = -ENOMEM;
> - end_current_label_crit_section(label);
>
> - return error;
> + spin_lock_init(&ctx->lock);
> + rcu_assign_pointer(ctx->label, aa_get_label(label));
> + end_current_label_crit_section(label);
> + return 0;
> }
>
> static void apparmor_file_free_security(struct file *file)
> {
> - aa_free_file_ctx(file_ctx(file));
> + struct aa_file_ctx *ctx = file_ctx(file);
> +
> + if (ctx)
> + aa_put_label(rcu_access_pointer(ctx->label));
> }
>
> static int common_file_perm(const char *op, struct file *file, u32 mask)
> @@ -1131,6 +1131,7 @@ static void apparmor_sock_graft(struct sock *sk, struct socket *parent)
> */
> struct lsm_blob_sizes apparmor_blob_sizes = {
> .lbs_cred = sizeof(struct aa_task_ctx *),
> + .lbs_file = sizeof(struct aa_file_ctx),
> };
>
> static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
> diff --git a/security/security.c b/security/security.c
> index ff7df14f6db1..5430cae73cf6 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -40,6 +40,8 @@
> struct security_hook_heads security_hook_heads __lsm_ro_after_init;
> static ATOMIC_NOTIFIER_HEAD(lsm_notifier_chain);
>
> +static struct kmem_cache *lsm_file_cache;
> +
> char *lsm_names;
> static struct lsm_blob_sizes blob_sizes;
>
> @@ -92,6 +94,13 @@ int __init security_init(void)
> */
> do_security_initcalls();
>
> + /*
> + * Create any kmem_caches needed for blobs
> + */
> + if (blob_sizes.lbs_file)
> + lsm_file_cache = kmem_cache_create("lsm_file_cache",
> + blob_sizes.lbs_file, 0,
> + SLAB_PANIC, NULL);
> /*
> * The second call to a module specific init function
> * adds hooks to the hook lists and does any other early
> @@ -101,6 +110,7 @@ int __init security_init(void)
>
> #ifdef CONFIG_SECURITY_LSM_DEBUG
> pr_info("LSM: cred blob size = %d\n", blob_sizes.lbs_cred);
> + pr_info("LSM: file blob size = %d\n", blob_sizes.lbs_file);
> #endif
>
> return 0;
> @@ -277,6 +287,28 @@ static void __init lsm_set_size(int *need, int *lbs)
> void __init security_add_blobs(struct lsm_blob_sizes *needed)
> {
> lsm_set_size(&needed->lbs_cred, &blob_sizes.lbs_cred);
> + lsm_set_size(&needed->lbs_file, &blob_sizes.lbs_file);
> +}
> +
> +/**
> + * lsm_file_alloc - allocate a composite file blob
> + * @file: the file that needs a blob
> + *
> + * Allocate the file blob for all the modules
> + *
> + * Returns 0, or -ENOMEM if memory can't be allocated.
> + */
> +int lsm_file_alloc(struct file *file)
> +{
> + if (!lsm_file_cache) {
> + file->f_security = NULL;
> + return 0;
> + }
> +
> + file->f_security = kmem_cache_zalloc(lsm_file_cache, GFP_KERNEL);
> + if (file->f_security == NULL)
> + return -ENOMEM;
> + return 0;
> }
>
> /*
> @@ -962,12 +994,28 @@ int security_file_permission(struct file *file, int mask)
>
> int security_file_alloc(struct file *file)
> {
> - return call_int_hook(file_alloc_security, 0, file);
> + int rc = lsm_file_alloc(file);
> +
> + if (rc)
> + return rc;
> + rc = call_int_hook(file_alloc_security, 0, file);
> + if (unlikely(rc))
> + security_file_free(file);
> + return rc;
> }
>
> void security_file_free(struct file *file)
> {
> + void *blob;
> +
> + if (!lsm_file_cache)
> + return;
Should this be WARN_ON?
> +
> call_void_hook(file_free_security, file);
> +
> + blob = file->f_security;
> + file->f_security = NULL;
> + kmem_cache_free(lsm_file_cache, blob);
> }
>
> int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
> @@ -1085,7 +1133,7 @@ int security_cred_alloc_blank(struct cred *cred, gfp_t gfp)
> return rc;
>
> rc = call_int_hook(cred_alloc_blank, 0, cred, gfp);
> - if (rc)
> + if (unlikely(rc))
> security_cred_free(cred);
> return rc;
> }
> @@ -1106,7 +1154,7 @@ int security_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp)
> return rc;
>
> rc = call_int_hook(cred_prepare, 0, new, old, gfp);
> - if (rc)
> + if (unlikely(rc))
> security_cred_free(new);
> return rc;
> }
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index 94b3123c237b..3468b4592036 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -149,7 +149,6 @@ static int __init checkreqprot_setup(char *str)
> __setup("checkreqprot=", checkreqprot_setup);
>
> static struct kmem_cache *sel_inode_cache;
> -static struct kmem_cache *file_security_cache;
>
> /**
> * selinux_secmark_enabled - Check to see if SECMARK is currently enabled
> @@ -381,27 +380,15 @@ static void inode_free_security(struct inode *inode)
>
> static int file_alloc_security(struct file *file)
> {
> - struct file_security_struct *fsec;
> + struct file_security_struct *fsec = selinux_file(file);
> u32 sid = current_sid();
>
> - fsec = kmem_cache_zalloc(file_security_cache, GFP_KERNEL);
> - if (!fsec)
> - return -ENOMEM;
> -
> fsec->sid = sid;
> fsec->fown_sid = sid;
> - file->f_security = fsec;
>
> return 0;
> }
>
> -static void file_free_security(struct file *file)
> -{
> - struct file_security_struct *fsec = selinux_file(file);
> - file->f_security = NULL;
> - kmem_cache_free(file_security_cache, fsec);
> -}
> -
> static int superblock_alloc_security(struct super_block *sb)
> {
> struct superblock_security_struct *sbsec;
> @@ -3558,11 +3545,6 @@ static int selinux_file_alloc_security(struct file *file)
> return file_alloc_security(file);
> }
>
> -static void selinux_file_free_security(struct file *file)
> -{
> - file_free_security(file);
> -}
> -
> /*
> * Check whether a task has the ioctl permission and cmd
> * operation to an inode.
> @@ -6857,6 +6839,7 @@ static void selinux_bpf_prog_free(struct bpf_prog_aux *aux)
>
> struct lsm_blob_sizes selinux_blob_sizes = {
> .lbs_cred = sizeof(struct task_security_struct),
> + .lbs_file = sizeof(struct file_security_struct),
> };
>
> static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
> @@ -6927,7 +6910,6 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
>
> LSM_HOOK_INIT(file_permission, selinux_file_permission),
> LSM_HOOK_INIT(file_alloc_security, selinux_file_alloc_security),
> - LSM_HOOK_INIT(file_free_security, selinux_file_free_security),
> LSM_HOOK_INIT(file_ioctl, selinux_file_ioctl),
> LSM_HOOK_INIT(mmap_file, selinux_mmap_file),
> LSM_HOOK_INIT(mmap_addr, selinux_mmap_addr),
> @@ -7130,9 +7112,6 @@ static __init int selinux_init(void)
> sel_inode_cache = kmem_cache_create("selinux_inode_security",
> sizeof(struct inode_security_struct),
> 0, SLAB_PANIC, NULL);
> - file_security_cache = kmem_cache_create("selinux_file_security",
> - sizeof(struct file_security_struct),
> - 0, SLAB_PANIC, NULL);
> avc_init();
>
> avtab_cache_init();
> diff --git a/security/smack/smack.h b/security/smack/smack.h
> index 0c6dce446825..043525a52e94 100644
> --- a/security/smack/smack.h
> +++ b/security/smack/smack.h
> @@ -362,6 +362,11 @@ static inline struct task_smack *smack_cred(const struct cred *cred)
> return cred->security;
> }
>
> +static inline struct smack_known **smack_file(const struct file *file)
> +{
> + return file->f_security;
> +}
> +
> /*
> * Is the directory transmuting?
> */
> diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
> index a06ea8aa89c4..d1430341798f 100644
> --- a/security/smack/smack_lsm.c
> +++ b/security/smack/smack_lsm.c
> @@ -1571,24 +1571,12 @@ static void smack_inode_getsecid(struct inode *inode, u32 *secid)
> */
> static int smack_file_alloc_security(struct file *file)
> {
> - struct smack_known *skp = smk_of_current();
> + struct smack_known **blob = smack_file(file);
>
> - file->f_security = skp;
> + *blob = smk_of_current();
> return 0;
> }
>
> -/**
> - * smack_file_free_security - clear a file security blob
> - * @file: the object
> - *
> - * The security blob for a file is a pointer to the master
> - * label list, so no memory is freed.
> - */
> -static void smack_file_free_security(struct file *file)
> -{
> - file->f_security = NULL;
> -}
> -
> /**
> * smack_file_ioctl - Smack check on ioctls
> * @file: the object
> @@ -1813,7 +1801,9 @@ static int smack_mmap_file(struct file *file,
> */
> static void smack_file_set_fowner(struct file *file)
> {
> - file->f_security = smk_of_current();
> + struct smack_known **blob = smack_file(file);
> +
> + *blob = smk_of_current();
> }
Is this supposed to be part of a separate patch to prepare smack for
the infrastructure change?
Otherwise, seems good.
-Kees
>
> /**
> @@ -1830,6 +1820,7 @@ static void smack_file_set_fowner(struct file *file)
> static int smack_file_send_sigiotask(struct task_struct *tsk,
> struct fown_struct *fown, int signum)
> {
> + struct smack_known **blob;
> struct smack_known *skp;
> struct smack_known *tkp = smk_of_task(smack_cred(tsk->cred));
> struct file *file;
> @@ -1842,7 +1833,8 @@ static int smack_file_send_sigiotask(struct task_struct *tsk,
> file = container_of(fown, struct file, f_owner);
>
> /* we don't log here as rc can be overriden */
> - skp = file->f_security;
> + blob = smack_file(file);
> + skp = *blob;
> rc = smk_access(skp, tkp, MAY_DELIVER, NULL);
> rc = smk_bu_note("sigiotask", skp, tkp, MAY_DELIVER, rc);
> if (rc != 0 && has_capability(tsk, CAP_MAC_OVERRIDE))
> @@ -4626,6 +4618,7 @@ static int smack_dentry_create_files_as(struct dentry *dentry, int mode,
>
> struct lsm_blob_sizes smack_blob_sizes = {
> .lbs_cred = sizeof(struct task_smack),
> + .lbs_file = sizeof(struct smack_known *),
> };
>
> static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
> @@ -4663,7 +4656,6 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
> LSM_HOOK_INIT(inode_getsecid, smack_inode_getsecid),
>
> LSM_HOOK_INIT(file_alloc_security, smack_file_alloc_security),
> - LSM_HOOK_INIT(file_free_security, smack_file_free_security),
> LSM_HOOK_INIT(file_ioctl, smack_file_ioctl),
> LSM_HOOK_INIT(file_lock, smack_file_lock),
> LSM_HOOK_INIT(file_fcntl, smack_file_fcntl),
> --
> 2.17.1
>
>
--
Kees Cook
Pixel Security
On Tue, Sep 11, 2018 at 9:42 AM, Casey Schaufler <[email protected]> wrote:
> Don't use the inode->i_security pointer directly.
> Provide a helper function that provides the security blob pointer.
>
> Signed-off-by: Casey Schaufler <[email protected]>
Happily mechanical! :)
Reviewed-by: Kees Cook <[email protected]>
-Kees
--
Kees Cook
Pixel Security
On Tue, Sep 11, 2018 at 9:42 AM, Casey Schaufler <[email protected]> wrote:
> Don't use the inode->i_security pointer directly.
> Provide a helper function that provides the security blob pointer.
>
> Signed-off-by: Casey Schaufler <[email protected]>
Reviewed-by: Kees Cook <[email protected]>
-Kees
--
Kees Cook
Pixel Security
On Tue, Sep 11, 2018 at 9:42 AM, Casey Schaufler <[email protected]> wrote:
> Move management of the inode->i_security blob out
> of the individual security modules and into the security
> infrastructure. Instead of allocating the blobs from within
> the modules the modules tell the infrastructure how much
> space is required, and the space is allocated there.
>
> Signed-off-by: Casey Schaufler <[email protected]>
> ---
> include/linux/lsm_hooks.h | 3 ++
> security/security.c | 83 ++++++++++++++++++++++++++++++-
> security/selinux/hooks.c | 32 +-----------
> security/selinux/include/objsec.h | 5 +-
> security/smack/smack_lsm.c | 70 ++++----------------------
> 5 files changed, 98 insertions(+), 95 deletions(-)
>
> diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
> index 167ffbd4d0c0..416b20c3795b 100644
> --- a/include/linux/lsm_hooks.h
> +++ b/include/linux/lsm_hooks.h
> @@ -2030,6 +2030,7 @@ struct security_hook_list {
> struct lsm_blob_sizes {
> int lbs_cred;
> int lbs_file;
> + int lbs_inode;
> };
>
> /*
> @@ -2092,9 +2093,11 @@ static inline void loadpin_add_hooks(void) { };
> #endif
>
> extern int lsm_cred_alloc(struct cred *cred, gfp_t gfp);
> +extern int lsm_inode_alloc(struct inode *inode);
>
> #ifdef CONFIG_SECURITY
> void lsm_early_cred(struct cred *cred);
> +void lsm_early_inode(struct inode *inode);
> #endif
>
> #endif /* ! __LINUX_LSM_HOOKS_H */
> diff --git a/security/security.c b/security/security.c
> index 5430cae73cf6..2501cdcbebff 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -41,6 +41,7 @@ struct security_hook_heads security_hook_heads __lsm_ro_after_init;
> static ATOMIC_NOTIFIER_HEAD(lsm_notifier_chain);
>
> static struct kmem_cache *lsm_file_cache;
> +static struct kmem_cache *lsm_inode_cache;
>
> char *lsm_names;
> static struct lsm_blob_sizes blob_sizes;
> @@ -101,6 +102,10 @@ int __init security_init(void)
> lsm_file_cache = kmem_cache_create("lsm_file_cache",
> blob_sizes.lbs_file, 0,
> SLAB_PANIC, NULL);
> + if (blob_sizes.lbs_inode)
> + lsm_inode_cache = kmem_cache_create("lsm_inode_cache",
> + blob_sizes.lbs_inode, 0,
> + SLAB_PANIC, NULL);
> /*
> * The second call to a module specific init function
> * adds hooks to the hook lists and does any other early
> @@ -111,6 +116,7 @@ int __init security_init(void)
> #ifdef CONFIG_SECURITY_LSM_DEBUG
> pr_info("LSM: cred blob size = %d\n", blob_sizes.lbs_cred);
> pr_info("LSM: file blob size = %d\n", blob_sizes.lbs_file);
> + pr_info("LSM: inode blob size = %d\n", blob_sizes.lbs_inode);
> #endif
>
> return 0;
> @@ -288,6 +294,13 @@ void __init security_add_blobs(struct lsm_blob_sizes *needed)
> {
> lsm_set_size(&needed->lbs_cred, &blob_sizes.lbs_cred);
> lsm_set_size(&needed->lbs_file, &blob_sizes.lbs_file);
> + /*
> + * The inode blob gets an rcu_head in addition to
> + * what the modules might need.
> + */
> + if (needed->lbs_inode && blob_sizes.lbs_inode == 0)
> + blob_sizes.lbs_inode = sizeof(struct rcu_head);
> + lsm_set_size(&needed->lbs_inode, &blob_sizes.lbs_inode);
> }
>
> /**
> @@ -311,6 +324,46 @@ int lsm_file_alloc(struct file *file)
> return 0;
> }
>
> +/**
> + * lsm_inode_alloc - allocate a composite inode blob
> + * @inode: the inode that needs a blob
> + *
> + * Allocate the inode blob for all the modules
> + *
> + * Returns 0, or -ENOMEM if memory can't be allocated.
> + */
> +int lsm_inode_alloc(struct inode *inode)
> +{
> + if (!lsm_inode_cache) {
WARN_ON?
> + inode->i_security = NULL;
The other patch didn't set to NULL. Probably should do that in the
other one too.
> + return 0;
> + }
> +
> + inode->i_security = kmem_cache_zalloc(lsm_inode_cache, GFP_NOFS);
> + if (inode->i_security == NULL)
> + return -ENOMEM;
> + return 0;
> +}
> +
> +/**
> + * lsm_early_inode - during initialization allocate a composite inode blob
> + * @inode: the inode that needs a blob
> + *
> + * Allocate the inode blob for all the modules if it's not already there
> + */
> +void lsm_early_inode(struct inode *inode)
> +{
> + int rc;
> +
> + if (inode == NULL)
> + panic("%s: NULL inode.\n", __func__);
> + if (inode->i_security != NULL)
> + return;
> + rc = lsm_inode_alloc(inode);
> + if (rc)
> + panic("%s: Early inode alloc failed.\n", __func__);
> +}
Same thoughts on the use of panic() as the other patch...
> +
> /*
> * Hook list operation macros.
> *
> @@ -557,14 +610,40 @@ EXPORT_SYMBOL(security_sb_parse_opts_str);
>
> int security_inode_alloc(struct inode *inode)
> {
> - inode->i_security = NULL;
> - return call_int_hook(inode_alloc_security, 0, inode);
> + int rc = lsm_inode_alloc(inode);
> +
> + if (unlikely(rc))
> + return rc;
> + rc = call_int_hook(inode_alloc_security, 0, inode);
> + if (unlikely(rc))
> + security_inode_free(inode);
> + return rc;
> +}
> +
> +static void inode_free_by_rcu(struct rcu_head *head)
> +{
> + /*
> + * The rcu head is at the start of the inode blob
> + */
> + kmem_cache_free(lsm_inode_cache, head);
> }
>
> void security_inode_free(struct inode *inode)
> {
> integrity_inode_free(inode);
> call_void_hook(inode_free_security, inode);
> + /*
> + * The inode may still be referenced in a path walk and
> + * a call to security_inode_permission() can be made
> + * after inode_free_security() is called. Ideally, the VFS
> + * wouldn't do this, but fixing that is a much harder
> + * job. For now, simply free the i_security via RCU, and
> + * leave the current inode->i_security pointer intact.
> + * The inode will be freed after the RCU grace period too.
> + */
> + if (inode->i_security)
> + call_rcu((struct rcu_head *)inode->i_security,
> + inode_free_by_rcu);
> }
>
> int security_dentry_init_security(struct dentry *dentry, int mode,
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index 2720fe3ebf5f..0b593030d9f2 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -148,8 +148,6 @@ static int __init checkreqprot_setup(char *str)
> }
> __setup("checkreqprot=", checkreqprot_setup);
>
> -static struct kmem_cache *sel_inode_cache;
> -
> /**
> * selinux_secmark_enabled - Check to see if SECMARK is currently enabled
> *
> @@ -245,13 +243,9 @@ static inline u32 task_sid(const struct task_struct *task)
>
> static int inode_alloc_security(struct inode *inode)
> {
> - struct inode_security_struct *isec;
> + struct inode_security_struct *isec = selinux_inode(inode);
> u32 sid = current_sid();
>
> - isec = kmem_cache_zalloc(sel_inode_cache, GFP_NOFS);
> - if (!isec)
> - return -ENOMEM;
> -
> spin_lock_init(&isec->lock);
> INIT_LIST_HEAD(&isec->list);
> isec->inode = inode;
> @@ -259,7 +253,6 @@ static int inode_alloc_security(struct inode *inode)
> isec->sclass = SECCLASS_FILE;
> isec->task_sid = sid;
> isec->initialized = LABEL_INVALID;
> - inode->i_security = isec;
>
> return 0;
> }
> @@ -337,14 +330,6 @@ static struct inode_security_struct *backing_inode_security(struct dentry *dentr
> return selinux_inode(inode);
> }
>
> -static void inode_free_rcu(struct rcu_head *head)
> -{
> - struct inode_security_struct *isec;
> -
> - isec = container_of(head, struct inode_security_struct, rcu);
> - kmem_cache_free(sel_inode_cache, isec);
> -}
> -
> static void inode_free_security(struct inode *inode)
> {
> struct inode_security_struct *isec = selinux_inode(inode);
> @@ -365,17 +350,6 @@ static void inode_free_security(struct inode *inode)
> list_del_init(&isec->list);
> spin_unlock(&sbsec->isec_lock);
> }
> -
> - /*
> - * The inode may still be referenced in a path walk and
> - * a call to selinux_inode_permission() can be made
> - * after inode_free_security() is called. Ideally, the VFS
> - * wouldn't do this, but fixing that is a much harder
> - * job. For now, simply free the i_security via RCU, and
> - * leave the current inode->i_security pointer intact.
> - * The inode will be freed after the RCU grace period too.
> - */
> - call_rcu(&isec->rcu, inode_free_rcu);
> }
>
> static int file_alloc_security(struct file *file)
> @@ -6840,6 +6814,7 @@ static void selinux_bpf_prog_free(struct bpf_prog_aux *aux)
> struct lsm_blob_sizes selinux_blob_sizes = {
> .lbs_cred = sizeof(struct task_security_struct),
> .lbs_file = sizeof(struct file_security_struct),
> + .lbs_inode = sizeof(struct inode_security_struct),
> };
>
> static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
> @@ -7109,9 +7084,6 @@ static __init int selinux_init(void)
>
> default_noexec = !(VM_DATA_DEFAULT_FLAGS & VM_EXEC);
>
> - sel_inode_cache = kmem_cache_create("selinux_inode_security",
> - sizeof(struct inode_security_struct),
> - 0, SLAB_PANIC, NULL);
> avc_init();
>
> avtab_cache_init();
> diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
> index 3304a1ee58a4..7a3d18fa9b13 100644
> --- a/security/selinux/include/objsec.h
> +++ b/security/selinux/include/objsec.h
> @@ -59,10 +59,7 @@ enum label_initialized {
>
> struct inode_security_struct {
> struct inode *inode; /* back pointer to inode object */
> - union {
> - struct list_head list; /* list of inode_security_struct */
> - struct rcu_head rcu; /* for freeing the inode_security_struct */
> - };
> + struct list_head list; /* list of inode_security_struct */
> u32 task_sid; /* SID of creating task */
> u32 sid; /* SID of this object */
> u16 sclass; /* security class of this object */
> diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
> index 364699ad55b9..6617abb51732 100644
> --- a/security/smack/smack_lsm.c
> +++ b/security/smack/smack_lsm.c
> @@ -288,24 +288,18 @@ static struct smack_known *smk_fetch(const char *name, struct inode *ip,
> }
>
> /**
> - * new_inode_smack - allocate an inode security blob
> + * init_inode_smack - initialize an inode security blob
> + * @isp: the blob to initialize
> * @skp: a pointer to the Smack label entry to use in the blob
> *
> - * Returns the new blob or NULL if there's no memory available
> */
> -static struct inode_smack *new_inode_smack(struct smack_known *skp)
> +static void init_inode_smack(struct inode *inode, struct smack_known *skp)
> {
> - struct inode_smack *isp;
> -
> - isp = kmem_cache_zalloc(smack_inode_cache, GFP_NOFS);
> - if (isp == NULL)
> - return NULL;
> + struct inode_smack *isp = smack_inode(inode);
>
> isp->smk_inode = skp;
> isp->smk_flags = 0;
> mutex_init(&isp->smk_lock);
> -
> - return isp;
> }
>
> /**
> @@ -824,17 +818,13 @@ static int smack_set_mnt_opts(struct super_block *sb,
> /*
> * Initialize the root inode.
> */
> - isp = smack_inode(inode);
> - if (isp == NULL) {
> - isp = new_inode_smack(sp->smk_root);
> - if (isp == NULL)
> - return -ENOMEM;
> - inode->i_security = isp;
> - } else
> - isp->smk_inode = sp->smk_root;
> + lsm_early_inode(inode);
> + init_inode_smack(inode, sp->smk_root);
>
> - if (transmute)
> + if (transmute) {
> + isp = smack_inode(inode);
> isp->smk_flags |= SMK_INODE_TRANSMUTE;
> + }
>
> return 0;
> }
> @@ -963,48 +953,10 @@ static int smack_inode_alloc_security(struct inode *inode)
> {
> struct smack_known *skp = smk_of_current();
>
> - inode->i_security = new_inode_smack(skp);
> - if (inode->i_security == NULL)
> - return -ENOMEM;
> + init_inode_smack(inode, skp);
> return 0;
> }
>
> -/**
> - * smack_inode_free_rcu - Free inode_smack blob from cache
> - * @head: the rcu_head for getting inode_smack pointer
> - *
> - * Call back function called from call_rcu() to free
> - * the i_security blob pointer in inode
> - */
> -static void smack_inode_free_rcu(struct rcu_head *head)
> -{
> - struct inode_smack *issp;
> -
> - issp = container_of(head, struct inode_smack, smk_rcu);
> - kmem_cache_free(smack_inode_cache, issp);
> -}
> -
> -/**
> - * smack_inode_free_security - free an inode blob using call_rcu()
> - * @inode: the inode with a blob
> - *
> - * Clears the blob pointer in inode using RCU
> - */
> -static void smack_inode_free_security(struct inode *inode)
> -{
> - struct inode_smack *issp = smack_inode(inode);
> -
> - /*
> - * The inode may still be referenced in a path walk and
> - * a call to smack_inode_permission() can be made
> - * after smack_inode_free_security() is called.
> - * To avoid race condition free the i_security via RCU
> - * and leave the current inode->i_security pointer intact.
> - * The inode will be freed after the RCU grace period too.
> - */
> - call_rcu(&issp->smk_rcu, smack_inode_free_rcu);
> -}
> -
> /**
> * smack_inode_init_security - copy out the smack from an inode
> * @inode: the newly created inode
> @@ -4619,6 +4571,7 @@ static int smack_dentry_create_files_as(struct dentry *dentry, int mode,
> struct lsm_blob_sizes smack_blob_sizes = {
> .lbs_cred = sizeof(struct task_smack),
> .lbs_file = sizeof(struct smack_known *),
> + .lbs_inode = sizeof(struct inode_smack),
> };
>
> static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
> @@ -4637,7 +4590,6 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
> LSM_HOOK_INIT(bprm_set_creds, smack_bprm_set_creds),
>
> LSM_HOOK_INIT(inode_alloc_security, smack_inode_alloc_security),
> - LSM_HOOK_INIT(inode_free_security, smack_inode_free_security),
> LSM_HOOK_INIT(inode_init_security, smack_inode_init_security),
> LSM_HOOK_INIT(inode_link, smack_inode_link),
> LSM_HOOK_INIT(inode_unlink, smack_inode_unlink),
> --
> 2.17.1
>
>
I continue to really like this series: I think pulling the memory
management up into the core LSM makes things much cleaner.
-Kees
--
Kees Cook
Pixel Security
On Tue, Sep 11, 2018 at 9:42 AM, Casey Schaufler <[email protected]> wrote:
> Two proposed security modules require the ability to
> share security blobs with existing "major" security modules.
> These modules, S.A.R.A and LandLock, provide significantly
> different services than SELinux, Smack or AppArmor. Using
> either in conjunction with the existing modules is quite
> reasonable. S.A.R.A requires access to the cred blob, while
> LandLock uses the cred, file and inode blobs.
>
> The use of the cred, file and inode blobs has been
> abstracted in preceding patches in the series. This
> patch teaches the affected security modules how to access
> the part of the blob set aside for their use in the case
> where blobs are shared. The configuration option
> CONFIG_SECURITY_STACKING identifies systems where the
> blobs may be shared.
>
> The mechanism for selecting which security modules are
> active has been changed to allow non-conflicting "major"
> security modules to be used together. At this time the
> TOMOYO module can safely be used with any of the others.
> The two new modules would be non-conflicting as well.
>
> Signed-off-by: Casey Schaufler <[email protected]>
> ---
> Documentation/admin-guide/LSM/index.rst | 14 +++--
> include/linux/lsm_hooks.h | 2 +-
> security/Kconfig | 81 +++++++++++++++++++++++++
> security/apparmor/include/cred.h | 8 +++
> security/apparmor/include/file.h | 9 ++-
> security/apparmor/include/lib.h | 4 ++
> security/apparmor/lsm.c | 8 ++-
> security/security.c | 30 ++++++++-
> security/selinux/hooks.c | 3 +-
> security/selinux/include/objsec.h | 18 +++++-
> security/smack/smack.h | 19 +++++-
> security/smack/smack_lsm.c | 17 +++---
> security/tomoyo/common.h | 12 +++-
> security/tomoyo/tomoyo.c | 3 +-
> 14 files changed, 200 insertions(+), 28 deletions(-)
>
> diff --git a/Documentation/admin-guide/LSM/index.rst b/Documentation/admin-guide/LSM/index.rst
> index 9842e21afd4a..d3d8af174042 100644
> --- a/Documentation/admin-guide/LSM/index.rst
> +++ b/Documentation/admin-guide/LSM/index.rst
> @@ -17,10 +17,16 @@ 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.
>
> -The Linux capabilities modules will always be included. This may be
> -followed by any number of "minor" modules and at most one "major" module.
> -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.
> +
> +Security modules that do not use the security data blobs maintained
> +by the LSM infrastructure are considered "minor" modules. These may be
> +included at compile time and stacked explicitly. Security modules that
> +use the LSM maintained security blobs are considered "major" modules.
> +These may only be stacked if the CONFIG_LSM_STACKED configuration
> +option is used. If this is chosen all of the security modules selected
> +will be used.
>
> A list of the active security modules can be found by reading
> ``/sys/kernel/security/lsm``. This is a comma separated list, and
> diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
> index 416b20c3795b..dddcced54fea 100644
> --- a/include/linux/lsm_hooks.h
> +++ b/include/linux/lsm_hooks.h
> @@ -2079,7 +2079,7 @@ static inline void security_delete_hooks(struct security_hook_list *hooks,
> #define __lsm_ro_after_init __ro_after_init
> #endif /* CONFIG_SECURITY_WRITABLE_HOOKS */
>
> -extern int __init security_module_enable(const char *module);
> +extern bool __init security_module_enable(const char *lsm, const bool stacked);
> extern void __init capability_add_hooks(void);
> #ifdef CONFIG_SECURITY_YAMA
> extern void __init yama_add_hooks(void);
> diff --git a/security/Kconfig b/security/Kconfig
> index 22f7664c4977..ed48025ae9e0 100644
> --- a/security/Kconfig
> +++ b/security/Kconfig
> @@ -36,6 +36,28 @@ config SECURITY_WRITABLE_HOOKS
> bool
> default n
>
> +config SECURITY_STACKING
> + bool "Security module stacking"
> + depends on SECURITY
> + help
> + Allows multiple major security modules to be stacked.
> + Modules are invoked in the order registered with a
> + "bail on fail" policy, in which the infrastructure
> + will stop processing once a denial is detected. Not
> + all modules can be stacked. SELinux, Smack and AppArmor are
> + known to be incompatible. User space components may
> + have trouble identifying the security module providing
> + data in some cases.
> +
> + If you select this option you will have to select which
> + of the stackable modules you wish to be active. The
> + "Default security module" will be ignored. The boot line
> + "security=" option can be used to specify that one of
> + the modules identifed for stacking should be used instead
> + of the entire stack.
> +
> + If you are unsure how to answer this question, answer N.
I don't see a good reason to make this a config. Why shouldn't this
always be enabled?
> +
> config SECURITY_LSM_DEBUG
> bool "Enable debugging of the LSM infrastructure"
> depends on SECURITY
> @@ -250,6 +272,9 @@ source security/yama/Kconfig
>
> source security/integrity/Kconfig
>
> +menu "Security Module Selection"
> + visible if !SECURITY_STACKING
> +
> choice
> prompt "Default security module"
> default DEFAULT_SECURITY_SELINUX if SECURITY_SELINUX
> @@ -289,3 +314,59 @@ config DEFAULT_SECURITY
>
> endmenu
>
> +menu "Security Module Stack"
> + visible if SECURITY_STACKING
> +
> +choice
> + prompt "Stacked 'extreme' security module"
> + default SECURITY_SELINUX_STACKED if SECURITY_SELINUX
> + default SECURITY_SMACK_STACKED if SECURITY_SMACK
> + default SECURITY_APPARMOR_STACKED if SECURITY_APPARMOR
I don't think any of this is needed either: we already have the
CONFIG_DEFAULT_SECURITY_* choice.
> [...]
> diff --git a/security/apparmor/include/cred.h b/security/apparmor/include/cred.h
> index a90eae76d7c1..be7575adf6f0 100644
> --- a/security/apparmor/include/cred.h
> +++ b/security/apparmor/include/cred.h
> @@ -25,7 +25,11 @@
>
> static inline struct aa_label *cred_label(const struct cred *cred)
> {
> +#ifdef CONFIG_SECURITY_STACKING
> + struct aa_label **blob = cred->security + apparmor_blob_sizes.lbs_cred;
> +#else
> struct aa_label **blob = cred->security;
> +#endif
Without the config, then there's no need for all these #ifdefs.
> -#define file_ctx(X) ((struct aa_file_ctx *)(X)->f_security)
> +static inline struct aa_file_ctx *file_ctx(struct file *file)
> +{
> +#ifdef CONFIG_SECURITY_STACKING
> + return file->f_security + apparmor_blob_sizes.lbs_file;
> +#else
> + return file->f_security;
> +#endif
> +}
This define->inline should have been part of an earlier patch.
> /* struct aa_file_ctx - the AppArmor context the file was opened in
> * @lock: lock to update the ctx
> diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h
> index 6505e1ad9e23..bbe9b384d71d 100644
> --- a/security/apparmor/include/lib.h
> +++ b/security/apparmor/include/lib.h
> @@ -16,6 +16,7 @@
>
> #include <linux/slab.h>
> #include <linux/fs.h>
> +#include <linux/lsm_hooks.h>
>
> #include "match.h"
>
> @@ -55,6 +56,9 @@ const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name,
> size_t *ns_len);
> void aa_info_message(const char *str);
>
> +/* Security blob offsets */
> +extern struct lsm_blob_sizes apparmor_blob_sizes;
> +
> /**
> * aa_strneq - compare null terminated @str to a non null terminated substring
> * @str: a null terminated string
> diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
> index 15716b6ff860..36d8386170e8 100644
> --- a/security/apparmor/lsm.c
> +++ b/security/apparmor/lsm.c
> @@ -1553,7 +1553,9 @@ static int __init apparmor_init(void)
> int error;
>
> if (!finish) {
> - if (apparmor_enabled && security_module_enable("apparmor"))
> + if (apparmor_enabled &&
> + security_module_enable("apparmor",
> + IS_ENABLED(CONFIG_SECURITY_APPARMOR_STACKED)))
> security_add_blobs(&apparmor_blob_sizes);
> else
> apparmor_enabled = false;
> @@ -1561,7 +1563,9 @@ static int __init apparmor_init(void)
> return 0;
> }
>
> - if (!apparmor_enabled || !security_module_enable("apparmor")) {
> + if (!apparmor_enabled ||
> + !security_module_enable("apparmor",
> + IS_ENABLED(CONFIG_SECURITY_APPARMOR_STACKED))) {
> aa_info_message("AppArmor disabled by boot time parameter");
> apparmor_enabled = false;
> return 0;
I don't think any of these changes are needed either with the loss of
the config.
> diff --git a/security/security.c b/security/security.c
> index 2501cdcbebff..06bed74d1ed0 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -36,6 +36,7 @@
>
> /* Maximum number of letters for an LSM name string */
> #define SECURITY_NAME_MAX 10
> +#define MODULE_STACK "(stacking)"
>
> struct security_hook_heads security_hook_heads __lsm_ro_after_init;
> static ATOMIC_NOTIFIER_HEAD(lsm_notifier_chain);
> @@ -48,7 +49,11 @@ static struct lsm_blob_sizes blob_sizes;
>
> /* Boot-time LSM user choice */
> static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =
> +#ifdef CONFIG_SECURITY_STACKING
> + MODULE_STACK;
> +#else
> CONFIG_DEFAULT_SECURITY;
> +#endif
>
> static void __init do_security_initcalls(void)
> {
> @@ -169,6 +174,7 @@ static int lsm_append(char *new, char **result)
> /**
> * security_module_enable - Load given security module on boot ?
> * @module: the name of the module
> + * @stacked: indicates that the module wants to be stacked
> *
> * Each LSM must pass this method before registering its own operations
> * to avoid security registration races. This method may also be used
> @@ -184,9 +190,29 @@ static int lsm_append(char *new, char **result)
> *
> * Otherwise, return false.
> */
> -int __init security_module_enable(const char *module)
> +bool __init security_module_enable(const char *lsm, const bool stacked)
> {
> - return !strcmp(module, chosen_lsm);
> +#ifdef CONFIG_SECURITY_STACKING
> + /*
> + * Module defined on the command line security=XXXX
> + */
> + if (strcmp(chosen_lsm, MODULE_STACK)) {
> + if (!strcmp(lsm, chosen_lsm)) {
> + pr_info("Command line sets the %s security module.\n",
> + lsm);
> + return true;
> + }
> + return false;
> + }
> + /*
> + * Module configured as stacked.
> + */
> + return stacked;
> +#else
> + if (strcmp(lsm, chosen_lsm) == 0)
> + return true;
> + return false;
> +#endif
> }
I don't see the need for this? security_module_enable() will already
specify if it's been selected as the "primary" LSM.
The only change I think is needed here is to treat tomoyo differently.
It can now operate independently, like the "minor" LSMs. So we have
three types of LSMs now: "conflicting", "non-conflicting", and
"minor".
The only differences are how they get initialized. Major use
security_initcall() and minor use explicit calls to $lsm_add_hooks().
Yama is always enabled if built in. Loadpin uses a module parameter
("loadpin.enabled") and checks it when loadpin_add_hooks() is called.
To split tomoyo away from the other security modules, we need to track
its enablement _separately_ from the conflicting LSMs.
i.e. choose_lsm() needs to change, or tomoyo needs to use a module
parameter ("tomoyo.enabled"), or a __setup() call like apparmor and
selinux do for enablement. (Note that apparmor and selinux check
_both_ their __setup() and security_module_enable() values...)
For example (untested, likely whitespace damaged):
diff --git a/security/tomoyo/Kconfig b/security/tomoyo/Kconfig
index 404dce66952a..4edc8e733245 100644
--- a/security/tomoyo/Kconfig
+++ b/security/tomoyo/Kconfig
@@ -14,6 +14,14 @@ config SECURITY_TOMOYO
found at <http://tomoyo.sourceforge.jp/>.
If you are unsure how to answer this question, answer N.
+config SECURITY_TOMOYO_ENABLED
+ bool "Enable TOMOYO at boot"
+ depends on SECURITY_TOMOYO
+ default SECURITY_TOMOYO
+ help
+ If selected, TOMOYO will be enabled at boot. If not selected, it
+ can be enabled at boot with the kernel parameter "tomoyo.enabled=1".
+
config SECURITY_TOMOYO_MAX_ACCEPT_ENTRY
int "Default maximal count for learning mode"
default 2048
diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
index 9f932e2d6852..8dc9ef2096ab 100644
--- a/security/tomoyo/tomoyo.c
+++ b/security/tomoyo/tomoyo.c
@@ -531,6 +531,8 @@ static struct security_hook_list tomoyo_hooks[]
__lsm_ro_after_init = {
/* Lock for GC. */
DEFINE_SRCU(tomoyo_ss);
+static int enabled = IS_ENABLED(CONFIG_SECURITY_TOMOYO_ENABLED);
+
/**
* tomoyo_init - Register TOMOYO Linux as a LSM module.
*
@@ -540,7 +542,7 @@ static int __init tomoyo_init(void)
{
struct cred *cred = (struct cred *) current_cred();
- if (!security_module_enable("tomoyo"))
+ if (!enabled)
return 0;
/* register ourselves with the security framework */
security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks), "tomoyo");
@@ -550,4 +552,8 @@ static int __init tomoyo_init(void)
return 0;
}
+/* Should not be mutable after boot, so not listed in sysfs (perm == 0). */
+module_param(enabled, int, 0);
+MODULE_PARM_DESC(enabled, "Tomoyo enabled at boot");
+
security_initcall(tomoyo_init);
(I prefer LSMs do enablement with module params so that they leave
their namespace open for other runtime configuration. I think
"apparmor=" and "selinux=" for enable/disable isn't good to
replicate.)
We will quickly encounter "LSM ordering" as an issue, but not in this
case yet: there's no new LSM. Ordering is preserved even with my
suggestion: major order is controlled by Makefile link ordering, and
minor is controlled by explicit ordering in security/security.c's
add_hooks() calls.
> diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
> index 6617abb51732..be14540ce09c 100644
> --- a/security/smack/smack_lsm.c
> +++ b/security/smack/smack_lsm.c
> @@ -3493,18 +3493,16 @@ static int smack_getprocattr(struct task_struct *p, 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
> return -EINVAL;
>
> - cp = kstrdup(skp->smk_known, GFP_KERNEL);
> - if (cp == NULL)
> - return -ENOMEM;
> -
> - slen = strlen(cp);
> *value = cp;
> - return slen;
> + return strlen(cp);
> }
This refactoring seems out of place?
-Kees
--
Kees Cook
Pixel Security
On Thu, Sep 13, 2018 at 12:19 AM Kees Cook <[email protected]> wrote:
> On Tue, Sep 11, 2018 at 9:42 AM, Casey Schaufler <[email protected]> wrote:
> > Two proposed security modules require the ability to
> > share security blobs with existing "major" security modules.
> > These modules, S.A.R.A and LandLock, provide significantly
> > different services than SELinux, Smack or AppArmor. Using
> > either in conjunction with the existing modules is quite
> > reasonable. S.A.R.A requires access to the cred blob, while
> > LandLock uses the cred, file and inode blobs.
> >
> > The use of the cred, file and inode blobs has been
> > abstracted in preceding patches in the series. This
> > patch teaches the affected security modules how to access
> > the part of the blob set aside for their use in the case
> > where blobs are shared. The configuration option
> > CONFIG_SECURITY_STACKING identifies systems where the
> > blobs may be shared.
> >
> > The mechanism for selecting which security modules are
> > active has been changed to allow non-conflicting "major"
> > security modules to be used together. At this time the
> > TOMOYO module can safely be used with any of the others.
> > The two new modules would be non-conflicting as well.
> >
> > Signed-off-by: Casey Schaufler <[email protected]>
> > ---
> > Documentation/admin-guide/LSM/index.rst | 14 +++--
> > include/linux/lsm_hooks.h | 2 +-
> > security/Kconfig | 81 +++++++++++++++++++++++++
> > security/apparmor/include/cred.h | 8 +++
> > security/apparmor/include/file.h | 9 ++-
> > security/apparmor/include/lib.h | 4 ++
> > security/apparmor/lsm.c | 8 ++-
> > security/security.c | 30 ++++++++-
> > security/selinux/hooks.c | 3 +-
> > security/selinux/include/objsec.h | 18 +++++-
> > security/smack/smack.h | 19 +++++-
> > security/smack/smack_lsm.c | 17 +++---
> > security/tomoyo/common.h | 12 +++-
> > security/tomoyo/tomoyo.c | 3 +-
> > 14 files changed, 200 insertions(+), 28 deletions(-)
...
> > diff --git a/security/Kconfig b/security/Kconfig
> > index 22f7664c4977..ed48025ae9e0 100644
> > --- a/security/Kconfig
> > +++ b/security/Kconfig
> > @@ -36,6 +36,28 @@ config SECURITY_WRITABLE_HOOKS
> > bool
> > default n
> >
> > +config SECURITY_STACKING
> > + bool "Security module stacking"
> > + depends on SECURITY
> > + help
> > + Allows multiple major security modules to be stacked.
> > + Modules are invoked in the order registered with a
> > + "bail on fail" policy, in which the infrastructure
> > + will stop processing once a denial is detected. Not
> > + all modules can be stacked. SELinux, Smack and AppArmor are
> > + known to be incompatible. User space components may
> > + have trouble identifying the security module providing
> > + data in some cases.
> > +
> > + If you select this option you will have to select which
> > + of the stackable modules you wish to be active. The
> > + "Default security module" will be ignored. The boot line
> > + "security=" option can be used to specify that one of
> > + the modules identifed for stacking should be used instead
> > + of the entire stack.
> > +
> > + If you are unsure how to answer this question, answer N.
>
> I don't see a good reason to make this a config. Why shouldn't this
> always be enabled?
I do. From a user perspective it is sometimes difficult to determine
the reason behind a failed operation; its is a DAC based denial, the
LSM, or some other failure? Stacking additional LSMs has the
potential to make this worse. The boot time configuration adds to the
complexity.
I think we should leave this decision to the individual distros so
that they can make their own decision on LSM stacking based on the
savviness of their user base and the quality of their support
infrastructure.
--
paul moore
http://www.paul-moore.com
On Thu, Sep 13, 2018 at 6:16 AM, Paul Moore <[email protected]> wrote:
> On Thu, Sep 13, 2018 at 12:19 AM Kees Cook <[email protected]> wrote:
>> On Tue, Sep 11, 2018 at 9:42 AM, Casey Schaufler <[email protected]> wrote:
>> > Two proposed security modules require the ability to
>> > share security blobs with existing "major" security modules.
>> > These modules, S.A.R.A and LandLock, provide significantly
>> > different services than SELinux, Smack or AppArmor. Using
>> > either in conjunction with the existing modules is quite
>> > reasonable. S.A.R.A requires access to the cred blob, while
>> > LandLock uses the cred, file and inode blobs.
>> >
>> > The use of the cred, file and inode blobs has been
>> > abstracted in preceding patches in the series. This
>> > patch teaches the affected security modules how to access
>> > the part of the blob set aside for their use in the case
>> > where blobs are shared. The configuration option
>> > CONFIG_SECURITY_STACKING identifies systems where the
>> > blobs may be shared.
>> >
>> > The mechanism for selecting which security modules are
>> > active has been changed to allow non-conflicting "major"
>> > security modules to be used together. At this time the
>> > TOMOYO module can safely be used with any of the others.
>> > The two new modules would be non-conflicting as well.
>> >
>> > Signed-off-by: Casey Schaufler <[email protected]>
>> > ---
>> > Documentation/admin-guide/LSM/index.rst | 14 +++--
>> > include/linux/lsm_hooks.h | 2 +-
>> > security/Kconfig | 81 +++++++++++++++++++++++++
>> > security/apparmor/include/cred.h | 8 +++
>> > security/apparmor/include/file.h | 9 ++-
>> > security/apparmor/include/lib.h | 4 ++
>> > security/apparmor/lsm.c | 8 ++-
>> > security/security.c | 30 ++++++++-
>> > security/selinux/hooks.c | 3 +-
>> > security/selinux/include/objsec.h | 18 +++++-
>> > security/smack/smack.h | 19 +++++-
>> > security/smack/smack_lsm.c | 17 +++---
>> > security/tomoyo/common.h | 12 +++-
>> > security/tomoyo/tomoyo.c | 3 +-
>> > 14 files changed, 200 insertions(+), 28 deletions(-)
>
> ...
>
>> > diff --git a/security/Kconfig b/security/Kconfig
>> > index 22f7664c4977..ed48025ae9e0 100644
>> > --- a/security/Kconfig
>> > +++ b/security/Kconfig
>> > @@ -36,6 +36,28 @@ config SECURITY_WRITABLE_HOOKS
>> > bool
>> > default n
>> >
>> > +config SECURITY_STACKING
>> > + bool "Security module stacking"
>> > + depends on SECURITY
>> > + help
>> > + Allows multiple major security modules to be stacked.
>> > + Modules are invoked in the order registered with a
>> > + "bail on fail" policy, in which the infrastructure
>> > + will stop processing once a denial is detected. Not
>> > + all modules can be stacked. SELinux, Smack and AppArmor are
>> > + known to be incompatible. User space components may
>> > + have trouble identifying the security module providing
>> > + data in some cases.
>> > +
>> > + If you select this option you will have to select which
>> > + of the stackable modules you wish to be active. The
>> > + "Default security module" will be ignored. The boot line
>> > + "security=" option can be used to specify that one of
>> > + the modules identifed for stacking should be used instead
>> > + of the entire stack.
>> > +
>> > + If you are unsure how to answer this question, answer N.
>>
>> I don't see a good reason to make this a config. Why shouldn't this
>> always be enabled?
>
> I do. From a user perspective it is sometimes difficult to determine
> the reason behind a failed operation; its is a DAC based denial, the
> LSM, or some other failure? Stacking additional LSMs has the
> potential to make this worse. The boot time configuration adds to the
> complexity.
Let me try to convince you otherwise. :) The reason I think there's no
need for this is because the only functional change here is how
_TOMOYO_ gets stacked. And in my proposal, we can convert TOMOYO to be
enabled/disabled like LoadPin. Given the configs I showed, stacking
TOMOYO with the other major LSMs becomes a config (and/or boottime)
option.
The changes for TOMOYO are still needed even _with_ SECURITY_STACKING,
and I argue that the other major LSMs remain the same. It's only
infrastructure that has changed. So, I think having SECURITY_STACKING
actually makes things more complex internally (all the ifdefs, weird
enable logic) and for distros ("what's this stacking option", etc?)
> I think we should leave this decision to the individual distros so
> that they can make their own decision on LSM stacking based on the
> savviness of their user base and the quality of their support
> infrastructure.
If we reach the "extreme" stacking case, then yes, I want to make sure
we've got something that makes sense. But this first step doesn't get
us there, so I'd prefer we avoid making it overly complex. I think
everything else in this series _reduces_ complexity, except for this
new config.
-Kees
--
Kees Cook
Pixel Security
On 9/12/2018 4:53 PM, Kees Cook wrote:
> On Tue, Sep 11, 2018 at 9:41 AM, Casey Schaufler <[email protected]> wrote:
>> Move management of the cred security blob out of the
>> security modules and into the security infrastructure.
>> Instead of allocating and freeing space the security
>> modules tell the infrastructure how much space they
>> require.
> There's a lot of changes here that I think deserve some longer
> discussion in the changelog. Notably, now we run _two_ cycles of init
> calls? That seems... odd. Notes below...
The first pass adds up the blob sizes. This has to be done because
some modules allocate blobs during the init phase. I have investigated
alternatives, including blobs that include information about what they
contain, but they're all significantly more complicated.
>> Some SELinux memory management debug code has been removed.
>>
>> Signed-off-by: Casey Schaufler <[email protected]>
>> ---
>> include/linux/lsm_hooks.h | 14 ++++
>> kernel/cred.c | 13 ----
>> security/Kconfig | 11 ++++
>> security/apparmor/domain.c | 2 +-
>> security/apparmor/include/cred.h | 16 ++++-
>> security/apparmor/lsm.c | 28 ++++++--
>> security/apparmor/task.c | 6 +-
>> security/security.c | 106 +++++++++++++++++++++++++++++-
>> security/selinux/hooks.c | 63 +++++-------------
>> security/selinux/include/objsec.h | 4 ++
>> security/selinux/selinuxfs.c | 1 +
>> security/smack/smack.h | 1 +
>> security/smack/smack_lsm.c | 85 +++++++++---------------
>> security/tomoyo/common.h | 21 +++++-
>> security/tomoyo/domain.c | 4 +-
>> security/tomoyo/securityfs_if.c | 15 +++--
>> security/tomoyo/tomoyo.c | 56 +++++++++++++---
>> 17 files changed, 303 insertions(+), 143 deletions(-)
>>
>> diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
>> index 97a020c616ad..0bef312efd45 100644
>> --- a/include/linux/lsm_hooks.h
>> +++ b/include/linux/lsm_hooks.h
>> @@ -2024,6 +2024,13 @@ struct security_hook_list {
>> char *lsm;
>> } __randomize_layout;
>>
>> +/*
>> + * Security blob size or offset data.
>> + */
>> +struct lsm_blob_sizes {
>> + int lbs_cred;
>> +};
>> +
>> /*
>> * Initializing a security_hook_list structure takes
>> * up a lot of space in a source file. This macro takes
>> @@ -2036,6 +2043,7 @@ struct security_hook_list {
>> extern struct security_hook_heads security_hook_heads;
>> extern char *lsm_names;
>>
>> +extern void security_add_blobs(struct lsm_blob_sizes *needed);
>> extern void security_add_hooks(struct security_hook_list *hooks, int count,
>> char *lsm);
>>
>> @@ -2082,4 +2090,10 @@ void __init loadpin_add_hooks(void);
>> static inline void loadpin_add_hooks(void) { };
>> #endif
>>
>> +extern int lsm_cred_alloc(struct cred *cred, gfp_t gfp);
>> +
>> +#ifdef CONFIG_SECURITY
>> +void lsm_early_cred(struct cred *cred);
>> +#endif
>> +
>> #endif /* ! __LINUX_LSM_HOOKS_H */
>> diff --git a/kernel/cred.c b/kernel/cred.c
>> index ecf03657e71c..fa2061ee4955 100644
>> --- a/kernel/cred.c
>> +++ b/kernel/cred.c
>> @@ -704,19 +704,6 @@ bool creds_are_invalid(const struct cred *cred)
>> {
>> if (cred->magic != CRED_MAGIC)
>> return true;
>> -#ifdef CONFIG_SECURITY_SELINUX
>> - /*
>> - * cred->security == NULL if security_cred_alloc_blank() or
>> - * security_prepare_creds() returned an error.
>> - */
>> - if (selinux_is_enabled() && cred->security) {
>> - if ((unsigned long) cred->security < PAGE_SIZE)
>> - return true;
>> - if ((*(u32 *)cred->security & 0xffffff00) ==
>> - (POISON_FREE << 24 | POISON_FREE << 16 | POISON_FREE << 8))
>> - return true;
> These aren't unreasonable checks -- can we add them back in later?
> (They don't need to be selinux specific, in fact: the LSM could do the
> poison...)
I had asked the maintainers about the value of these checks, and
they said that they were primarily there for debugging during the
original cred breakout development. I'd have not problem making them
infrastructure managed if there's a strong desire to keep them.
>> [...]
>> diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
>> index 08c88de0ffda..726910bba84b 100644
>> --- a/security/apparmor/domain.c
>> +++ b/security/apparmor/domain.c
>> @@ -975,7 +975,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
>> }
>> aa_put_label(cred_label(bprm->cred));
>> /* transfer reference, released when cred is freed */
>> - cred_label(bprm->cred) = new;
>> + set_cred_label(bprm->cred, new);
>>
>> done:
>> aa_put_label(label);
>> diff --git a/security/apparmor/include/cred.h b/security/apparmor/include/cred.h
>> index e287b7d0d4be..a90eae76d7c1 100644
>> --- a/security/apparmor/include/cred.h
>> +++ b/security/apparmor/include/cred.h
>> @@ -23,8 +23,22 @@
>> #include "policy_ns.h"
>> #include "task.h"
>>
>> -#define cred_label(X) ((X)->security)
>> +static inline struct aa_label *cred_label(const struct cred *cred)
>> +{
>> + struct aa_label **blob = cred->security;
>> +
>> + AA_BUG(!blob);
>> + return *blob;
>> +}
>>
>> +static inline void set_cred_label(const struct cred *cred,
>> + struct aa_label *label)
>> +{
>> + struct aa_label **blob = cred->security;
>> +
>> + AA_BUG(!blob);
>> + *blob = label;
>> +}
> This feels like it should be a separate patch? Shouldn't AA not be
> playing with cred->security directly before we get to this "sizing and
> allocation" patch?
You're correct. This is a harmless patch break-up mistake. The end
result of the set is correct, and the interim state works as intended,
albeit with unnecessary code.
>> [...]
>> diff --git a/security/security.c b/security/security.c
>> index 3dfe75d0d373..ff7df14f6db1 100644
>> --- a/security/security.c
>> +++ b/security/security.c
>> @@ -41,6 +41,8 @@ struct security_hook_heads security_hook_heads __lsm_ro_after_init;
>> static ATOMIC_NOTIFIER_HEAD(lsm_notifier_chain);
>>
>> char *lsm_names;
>> +static struct lsm_blob_sizes blob_sizes;
> This needs to be __lsm_ro_after_init.
Point.
>> +
>> /* Boot-time LSM user choice */
>> static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =
>> CONFIG_DEFAULT_SECURITY;
>> @@ -85,10 +87,22 @@ int __init security_init(void)
>> loadpin_add_hooks();
>>
>> /*
>> - * Load all the remaining security modules.
>> + * The first call to a module specific init function
>> + * updates the blob size requirements.
>> + */
>> + do_security_initcalls();
>> +
>> + /*
>> + * The second call to a module specific init function
>> + * adds hooks to the hook lists and does any other early
>> + * initializations required.
>> */
>> do_security_initcalls();
> Tracking init state internally to each LSM seems not great. What about
> having the state be a global the LSMs can query?
It looks like six of one, half dozen of the other to me.
I don't feel strongly either way. I did it the way I did
because it was easy. If you feel strongly I can certainly
change it.
>
> enum security_initcall_state_t {
> LSM_PRE_INIT,
> LSM_INIT,
> };
>
> static __initdata enum security_initcall_state_t security_initcall_state;
>
> static void __init __do_security_initcalls(enum security_initcall_state state)
> {
> int ret;
> initcall_t call;
> initcall_entry_t *ce;
>
> security_initcall_state = state;
>
> ce = __security_initcall_start;
> trace_initcall_level("security");
> while (ce < __security_initcall_end) {
> call = initcall_from_entry(ce);
> trace_initcall_start(call);
> ret = call();
> trace_initcall_finish(call, ret);
> ce++;
> }
> }
>
> static void __init do_security_initcalls(void)
> {
> __do_security_initcalls(LSM_PRE_INIT);
> __do_security_initcalls(LSM_INIT);
> }
>
>> +#ifdef CONFIG_SECURITY_LSM_DEBUG
>> + pr_info("LSM: cred blob size = %d\n", blob_sizes.lbs_cred);
>> +#endif
>> +
>> return 0;
>> }
>>
>> @@ -198,6 +212,73 @@ int unregister_lsm_notifier(struct notifier_block *nb)
>> }
>> EXPORT_SYMBOL(unregister_lsm_notifier);
>>
>> +/**
>> + * lsm_cred_alloc - allocate a composite cred blob
>> + * @cred: the cred that needs a blob
>> + * @gfp: allocation type
>> + *
>> + * Allocate the cred blob for all the modules
>> + *
>> + * Returns 0, or -ENOMEM if memory can't be allocated.
>> + */
>> +int lsm_cred_alloc(struct cred *cred, gfp_t gfp)
>> +{
>> + if (blob_sizes.lbs_cred == 0) {
>> + cred->security = NULL;
>> + return 0;
>> + }
>> +
>> + cred->security = kzalloc(blob_sizes.lbs_cred, gfp);
>> + if (cred->security == NULL)
>> + return -ENOMEM;
>> + return 0;
>> +}
>> +
>> +/**
>> + * lsm_early_cred - during initialization allocate a composite cred blob
>> + * @cred: the cred that needs a blob
>> + *
>> + * Allocate the cred blob for all the modules if it's not already there
> Perhaps mention this is mainly for retroactively attaching a cred blob
> to "init"?
Strictly, in fact.
>> + */
>> +void lsm_early_cred(struct cred *cred)
>> +{
>> + int rc;
>> +
>> + if (cred == NULL)
>> + panic("%s: NULL cred.\n", __func__);
> I have been given strongly worded advice to never BUG nor panic. I would:
>
> if (WARN_ON(!cred || !cred->security))
> return;
>
>> + if (cred->security != NULL)
>> + return;
>> + rc = lsm_cred_alloc(cred, GFP_KERNEL);
>> + if (rc)
>> + panic("%s: Early cred alloc failed.\n", __func__);
> And:
>
> WARN_ON(lsm_cred_alloc(cred, GFP_KERNEL));
This duplicates the previous behavior from the SELinux and Smack code.
A WARN_ON will result in an immediate panic when the calling code tries
to dereference the blob.
>
>> +}
>> +
>> +static void __init lsm_set_size(int *need, int *lbs)
>> +{
>> + int offset;
>> +
>> + if (*need > 0) {
>> + offset = *lbs;
>> + *lbs += *need;
>> + *need = offset;
>> + }
>> +}
>> +
>> +/**
>> + * security_add_blobs - Report blob sizes
>> + * @needed: the size of blobs needed by the module
>> + *
>> + * Each LSM has to register its blobs with the infrastructure.
>> + * The "needed" data tells the infrastructure how much memory
>> + * the module requires for each of its blobs. On return the
>> + * structure is filled with the offset that module should use
>> + * from the blob pointer.
>> + */
>> +void __init security_add_blobs(struct lsm_blob_sizes *needed)
>> +{
>> + lsm_set_size(&needed->lbs_cred, &blob_sizes.lbs_cred);
>> +}
>> +
>> /*
>> * Hook list operation macros.
>> *
>> @@ -998,17 +1079,36 @@ void security_task_free(struct task_struct *task)
>>
>> int security_cred_alloc_blank(struct cred *cred, gfp_t gfp)
>> {
>> - return call_int_hook(cred_alloc_blank, 0, cred, gfp);
>> + int rc = lsm_cred_alloc(cred, gfp);
>> +
>> + if (rc)
>> + return rc;
>> +
>> + rc = call_int_hook(cred_alloc_blank, 0, cred, gfp);
>> + if (rc)
>> + security_cred_free(cred);
>> + return rc;
> I spent some time looking at this, but I think this is fine. I thought
> it might be a double-free via the put_cred_rcu() path, but
> cred->security gets set to NULL below, and we really don't want
> allocated memory hanging around in the call_int_hook fails, so ...
> yes. All good.
>
>> }
>>
>> void security_cred_free(struct cred *cred)
>> {
>> call_void_hook(cred_free, cred);
>> +
>> + kfree(cred->security);
>> + cred->security = NULL;
>> }
>>
>> int security_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp)
>> {
>> - return call_int_hook(cred_prepare, 0, new, old, gfp);
>> + int rc = lsm_cred_alloc(new, gfp);
>> +
>> + if (rc)
>> + return rc;
>> +
>> + rc = call_int_hook(cred_prepare, 0, new, old, gfp);
>> + if (rc)
>> + security_cred_free(new);
>> + return rc;
>> }
>>
>> void security_transfer_creds(struct cred *new, const struct cred *old)
>> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
>> index 9d6cdd21acb6..9b49698754a7 100644
>> --- a/security/selinux/hooks.c
>> +++ b/security/selinux/hooks.c
>> @@ -213,12 +213,9 @@ static void cred_init_security(void)
>> struct cred *cred = (struct cred *) current->real_cred;
>> struct task_security_struct *tsec;
>>
>> - tsec = kzalloc(sizeof(struct task_security_struct), GFP_KERNEL);
>> - if (!tsec)
>> - panic("SELinux: Failed to initialize initial task.\n");
>> -
>> + lsm_early_cred(cred);
>> + tsec = selinux_cred(cred);
> Perhaps leave the existing panic() as-is?
Moving the panic into lsm_early_cred() reduces the panic count
and avoids code duplication here and in the Smack equivalent.
>> tsec->osid = tsec->sid = SECINITSID_KERNEL;
>> - cred->security = tsec;
>> }
>>
>> /*
>> @@ -3898,53 +3895,17 @@ static int selinux_task_alloc(struct task_struct *task,
>> sid, sid, SECCLASS_PROCESS, PROCESS__FORK, NULL);
>> }
>>
>> -/*
>> - * allocate the SELinux part of blank credentials
>> - */
>> -static int selinux_cred_alloc_blank(struct cred *cred, gfp_t gfp)
>> -{
>> - struct task_security_struct *tsec;
>> -
>> - tsec = kzalloc(sizeof(struct task_security_struct), gfp);
>> - if (!tsec)
>> - return -ENOMEM;
>> -
>> - cred->security = tsec;
>> - return 0;
>> -}
>> -
>> -/*
>> - * detach and free the LSM part of a set of credentials
>> - */
>> -static void selinux_cred_free(struct cred *cred)
>> -{
>> - struct task_security_struct *tsec = selinux_cred(cred);
>> -
>> - /*
>> - * cred->security == NULL if security_cred_alloc_blank() or
>> - * security_prepare_creds() returned an error.
>> - */
>> - BUG_ON(cred->security && (unsigned long) cred->security < PAGE_SIZE);
>> - cred->security = (void *) 0x7UL;
> What is the world was this? Is this a "< PAGE_SIZE" poison of 0x7?
It's the other side of the creds_are_invalid() check you commented
on above.
>> - kfree(tsec);
>> -}
>> -
>> /*
>> * prepare a new set of credentials for modification
>> */
>> static int selinux_cred_prepare(struct cred *new, const struct cred *old,
>> gfp_t gfp)
>> {
>> - const struct task_security_struct *old_tsec;
>> - struct task_security_struct *tsec;
>> -
>> - old_tsec = selinux_cred(old);
>> + const struct task_security_struct *old_tsec = selinux_cred(old);
>> + struct task_security_struct *tsec = selinux_cred(new);
>>
>> - tsec = kmemdup(old_tsec, sizeof(struct task_security_struct), gfp);
>> - if (!tsec)
>> - return -ENOMEM;
>> + *tsec = *old_tsec;
>>
>> - new->security = tsec;
>> return 0;
>> }
>>
>> @@ -6894,6 +6855,10 @@ static void selinux_bpf_prog_free(struct bpf_prog_aux *aux)
>> }
>> #endif
>>
>> +struct lsm_blob_sizes selinux_blob_sizes = {
>> + .lbs_cred = sizeof(struct task_security_struct),
>> +};
>> +
>> static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
>> LSM_HOOK_INIT(binder_set_context_mgr, selinux_binder_set_context_mgr),
>> LSM_HOOK_INIT(binder_transaction, selinux_binder_transaction),
>> @@ -6976,8 +6941,6 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
>> LSM_HOOK_INIT(file_open, selinux_file_open),
>>
>> LSM_HOOK_INIT(task_alloc, selinux_task_alloc),
>> - LSM_HOOK_INIT(cred_alloc_blank, selinux_cred_alloc_blank),
>> - LSM_HOOK_INIT(cred_free, selinux_cred_free),
>> LSM_HOOK_INIT(cred_prepare, selinux_cred_prepare),
>> LSM_HOOK_INIT(cred_transfer, selinux_cred_transfer),
>> LSM_HOOK_INIT(cred_getsecid, selinux_cred_getsecid),
>> @@ -7133,11 +7096,19 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
>>
>> static __init int selinux_init(void)
>> {
>> + static int finish;
>> +
>> if (!security_module_enable("selinux")) {
>> selinux_enabled = 0;
>> return 0;
>> }
>>
>> + if (!finish) {
>> + security_add_blobs(&selinux_blob_sizes);
>> + finish = 1;
>> + return 0;
>> + }
>> +
>> if (!selinux_enabled) {
>> pr_info("SELinux: Disabled at boot.\n");
>> return 0;
>> diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
>> index 734b6833bdff..db1c7000ada3 100644
>> --- a/security/selinux/include/objsec.h
>> +++ b/security/selinux/include/objsec.h
>> @@ -25,6 +25,9 @@
>> #include <linux/binfmts.h>
>> #include <linux/in.h>
>> #include <linux/spinlock.h>
>> +#include <linux/lsm_hooks.h>
>> +#include <linux/msg.h>
>> +#include <net/sock.h>
> That's a surprising number of new headers?
Possibly not required until later.
>> #include <net/net_namespace.h>
>> #include "flask.h"
>> #include "avc.h"
>> @@ -158,6 +161,7 @@ struct bpf_security_struct {
>> u32 sid; /*SID of bpf obj creater*/
>> };
>>
>> +extern struct lsm_blob_sizes selinux_blob_sizes;
>> static inline struct task_security_struct *selinux_cred(const struct cred *cred)
>> {
>> return cred->security;
>> diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
>> index f3a5a138a096..b5665bdc29fc 100644
>> --- a/security/selinux/selinuxfs.c
>> +++ b/security/selinux/selinuxfs.c
>> @@ -31,6 +31,7 @@
>> #include <linux/uaccess.h>
>> #include <linux/kobject.h>
>> #include <linux/ctype.h>
>> +#include <linux/lsm_hooks.h>
>>
>> /* selinuxfs pseudo filesystem for exporting the security policy API.
>> Based on the proc code and the fs/nfsd/nfsctl.c code. */
>> diff --git a/security/smack/smack.h b/security/smack/smack.h
>> index 0b55d6a55b26..0c6dce446825 100644
>> --- a/security/smack/smack.h
>> +++ b/security/smack/smack.h
>> @@ -24,6 +24,7 @@
>> #include <linux/list.h>
>> #include <linux/rculist.h>
>> #include <linux/lsm_audit.h>
>> +#include <linux/msg.h>
>>
>> /*
>> * Use IPv6 port labeling if IPv6 is enabled and secmarks
>> diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
>> index 68ee3ae8f25c..a06ea8aa89c4 100644
>> --- a/security/smack/smack_lsm.c
>> +++ b/security/smack/smack_lsm.c
>> @@ -309,29 +309,20 @@ static struct inode_smack *new_inode_smack(struct smack_known *skp)
>> }
>>
>> /**
>> - * new_task_smack - allocate a task security blob
>> + * init_task_smack - initialize a task security blob
>> + * @tsp: blob to initialize
>> * @task: a pointer to the Smack label for the running task
>> * @forked: a pointer to the Smack label for the forked task
>> - * @gfp: type of the memory for the allocation
>> *
>> - * Returns the new blob or NULL if there's no memory available
>> */
>> -static struct task_smack *new_task_smack(struct smack_known *task,
>> - struct smack_known *forked, gfp_t gfp)
>> +static void init_task_smack(struct task_smack *tsp, struct smack_known *task,
>> + struct smack_known *forked)
>> {
>> - struct task_smack *tsp;
>> -
>> - tsp = kzalloc(sizeof(struct task_smack), gfp);
>> - if (tsp == NULL)
>> - return NULL;
>> -
>> tsp->smk_task = task;
>> tsp->smk_forked = forked;
>> INIT_LIST_HEAD(&tsp->smk_rules);
>> INIT_LIST_HEAD(&tsp->smk_relabel);
>> mutex_init(&tsp->smk_rules_lock);
>> -
>> - return tsp;
>> }
>>
>> /**
>> @@ -1958,14 +1949,7 @@ static int smack_file_open(struct file *file)
>> */
>> static int smack_cred_alloc_blank(struct cred *cred, gfp_t gfp)
>> {
>> - struct task_smack *tsp;
>> -
>> - tsp = new_task_smack(NULL, NULL, gfp);
>> - if (tsp == NULL)
>> - return -ENOMEM;
>> -
>> - cred->security = tsp;
>> -
>> + init_task_smack(smack_cred(cred), NULL, NULL);
>> return 0;
>> }
>>
>> @@ -1982,10 +1966,6 @@ static void smack_cred_free(struct cred *cred)
>> struct list_head *l;
>> struct list_head *n;
>>
>> - if (tsp == NULL)
>> - return;
>> - cred->security = NULL;
>> -
>> smk_destroy_label_list(&tsp->smk_relabel);
>>
>> list_for_each_safe(l, n, &tsp->smk_rules) {
>> @@ -1993,7 +1973,6 @@ static void smack_cred_free(struct cred *cred)
>> list_del(&rp->list);
>> kfree(rp);
>> }
>> - kfree(tsp);
>> }
>>
>> /**
>> @@ -2008,14 +1987,10 @@ static int smack_cred_prepare(struct cred *new, const struct cred *old,
>> gfp_t gfp)
>> {
>> struct task_smack *old_tsp = smack_cred(old);
>> - struct task_smack *new_tsp;
>> + struct task_smack *new_tsp = smack_cred(new);
>> int rc;
>>
>> - new_tsp = new_task_smack(old_tsp->smk_task, old_tsp->smk_task, gfp);
>> - if (new_tsp == NULL)
>> - return -ENOMEM;
>> -
>> - new->security = new_tsp;
>> + init_task_smack(new_tsp, old_tsp->smk_task, old_tsp->smk_task);
>>
>> rc = smk_copy_rules(&new_tsp->smk_rules, &old_tsp->smk_rules, gfp);
>> if (rc != 0)
>> @@ -2023,10 +1998,7 @@ static int smack_cred_prepare(struct cred *new, const struct cred *old,
>>
>> rc = smk_copy_relabel(&new_tsp->smk_relabel, &old_tsp->smk_relabel,
>> gfp);
>> - if (rc != 0)
>> - return rc;
>> -
>> - return 0;
>> + return rc;
>> }
>>
>> /**
>> @@ -4652,6 +4624,10 @@ static int smack_dentry_create_files_as(struct dentry *dentry, int mode,
>> return 0;
>> }
>>
>> +struct lsm_blob_sizes smack_blob_sizes = {
>> + .lbs_cred = sizeof(struct task_smack),
>> +};
>> +
>> static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
>> LSM_HOOK_INIT(ptrace_access_check, smack_ptrace_access_check),
>> LSM_HOOK_INIT(ptrace_traceme, smack_ptrace_traceme),
>> @@ -4830,23 +4806,35 @@ static __init void init_smack_known_list(void)
>> */
>> static __init int smack_init(void)
>> {
>> - struct cred *cred;
>> + static int finish;
>> + struct cred *cred = (struct cred *) current->cred;
>> struct task_smack *tsp;
>>
>> if (!security_module_enable("smack"))
>> return 0;
>>
>> + if (!finish) {
>> + security_add_blobs(&smack_blob_sizes);
>> + finish = 1;
>> + return 0;
>> + }
>> +
>> smack_inode_cache = KMEM_CACHE(inode_smack, 0);
>> if (!smack_inode_cache)
>> return -ENOMEM;
>>
>> - tsp = new_task_smack(&smack_known_floor, &smack_known_floor,
>> - GFP_KERNEL);
>> - if (tsp == NULL) {
>> - kmem_cache_destroy(smack_inode_cache);
>> - return -ENOMEM;
>> - }
>> + lsm_early_cred(cred);
>>
>> + /*
>> + * Set the security state for the initial task.
>> + */
>> + tsp = smack_cred(cred);
>> + init_task_smack(tsp, &smack_known_floor, &smack_known_floor);
>> +
>> + /*
>> + * Register with LSM
>> + */
>> + security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks), "smack");
>> smack_enabled = 1;
>>
>> pr_info("Smack: Initializing.\n");
>> @@ -4860,20 +4848,9 @@ static __init int smack_init(void)
>> pr_info("Smack: IPv6 Netfilter enabled.\n");
>> #endif
>>
>> - /*
>> - * Set the security state for the initial task.
>> - */
>> - cred = (struct cred *) current->cred;
>> - cred->security = tsp;
>> -
>> /* initialize the smack_known_list */
>> init_smack_known_list();
>>
>> - /*
>> - * Register with LSM
>> - */
>> - security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks), "smack");
>> -
>> return 0;
>> }
> I feel like some of this Smack refactoring could be split out to ease review...
Sure. Always a balance between size of patch and number of patches.
>> diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h
>> index 539bcdd30bb8..0110bebe86e2 100644
>> --- a/security/tomoyo/common.h
>> +++ b/security/tomoyo/common.h
>> @@ -29,6 +29,7 @@
>> #include <linux/in.h>
>> #include <linux/in6.h>
>> #include <linux/un.h>
>> +#include <linux/lsm_hooks.h>
>> #include <net/sock.h>
>> #include <net/af_unix.h>
>> #include <net/ip.h>
>> @@ -1062,6 +1063,7 @@ void tomoyo_write_log2(struct tomoyo_request_info *r, int len, const char *fmt,
>> /********** External variable definitions. **********/
>>
>> extern bool tomoyo_policy_loaded;
>> +extern bool tomoyo_enabled;
>> extern const char * const tomoyo_condition_keyword
>> [TOMOYO_MAX_CONDITION_KEYWORD];
>> extern const char * const tomoyo_dif[TOMOYO_MAX_DOMAIN_INFO_FLAGS];
>> @@ -1196,6 +1198,17 @@ static inline void tomoyo_put_group(struct tomoyo_group *group)
>> atomic_dec(&group->head.users);
>> }
>>
>> +/**
>> + * tomoyo_cred - Get a pointer to the tomoyo cred security blob
>> + * @cred - the relevant cred
>> + *
>> + * Returns pointer to the tomoyo cred blob.
>> + */
>> +static inline struct tomoyo_domain_info **tomoyo_cred(const struct cred *cred)
>> +{
>> + return cred->security;
>> +}
>> +
>> /**
>> * tomoyo_domain - Get "struct tomoyo_domain_info" for current thread.
>> *
>> @@ -1203,7 +1216,9 @@ static inline void tomoyo_put_group(struct tomoyo_group *group)
>> */
>> static inline struct tomoyo_domain_info *tomoyo_domain(void)
>> {
>> - return current_cred()->security;
>> + struct tomoyo_domain_info **blob = tomoyo_cred(current_cred());
>> +
>> + return *blob;
>> }
>>
>> /**
>> @@ -1216,7 +1231,9 @@ static inline struct tomoyo_domain_info *tomoyo_domain(void)
>> static inline struct tomoyo_domain_info *tomoyo_real_domain(struct task_struct
>> *task)
>> {
>> - return task_cred_xxx(task, security);
>> + struct tomoyo_domain_info **blob = tomoyo_cred(get_task_cred(task));
>> +
>> + return *blob;
>> }
>>
>> /**
> Shouldn't this Tomoyo cred handling get split out?
Yeah. Again, it's the patch size/count balance.
>> diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c
>> index f6758dad981f..b7469fdbff01 100644
>> --- a/security/tomoyo/domain.c
>> +++ b/security/tomoyo/domain.c
>> @@ -678,6 +678,7 @@ static int tomoyo_environ(struct tomoyo_execve *ee)
>> */
>> int tomoyo_find_next_domain(struct linux_binprm *bprm)
>> {
>> + struct tomoyo_domain_info **blob;
>> struct tomoyo_domain_info *old_domain = tomoyo_domain();
>> struct tomoyo_domain_info *domain = NULL;
>> const char *original_name = bprm->filename;
>> @@ -843,7 +844,8 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
>> domain = old_domain;
>> /* Update reference count on "struct tomoyo_domain_info". */
>> atomic_inc(&domain->users);
>> - bprm->cred->security = domain;
>> + blob = tomoyo_cred(bprm->cred);
>> + *blob = domain;
>> kfree(exename.name);
>> if (!retval) {
>> ee->r.domain = domain;
>> diff --git a/security/tomoyo/securityfs_if.c b/security/tomoyo/securityfs_if.c
>> index 1d3d7e7a1f05..768dff9608b1 100644
>> --- a/security/tomoyo/securityfs_if.c
>> +++ b/security/tomoyo/securityfs_if.c
>> @@ -71,9 +71,12 @@ static ssize_t tomoyo_write_self(struct file *file, const char __user *buf,
>> if (!cred) {
>> error = -ENOMEM;
>> } else {
>> - struct tomoyo_domain_info *old_domain =
>> - cred->security;
>> - cred->security = new_domain;
>> + struct tomoyo_domain_info **blob;
>> + struct tomoyo_domain_info *old_domain;
>> +
>> + blob = tomoyo_cred(cred);
>> + old_domain = *blob;
>> + *blob = new_domain;
>> atomic_inc(&new_domain->users);
>> atomic_dec(&old_domain->users);
>> commit_creds(cred);
>> @@ -234,10 +237,14 @@ static void __init tomoyo_create_entry(const char *name, const umode_t mode,
>> */
>> static int __init tomoyo_initerface_init(void)
>> {
>> + struct tomoyo_domain_info *domain;
>> struct dentry *tomoyo_dir;
>>
>> + if (!tomoyo_enabled)
>> + return 0;
>> + domain = tomoyo_domain();
>> /* Don't create securityfs entries unless registered. */
>> - if (current_cred()->security != &tomoyo_kernel_domain)
>> + if (domain != &tomoyo_kernel_domain)
>> return 0;
>>
>> tomoyo_dir = securityfs_create_dir("tomoyo", NULL);
>> diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
>> index 9f932e2d6852..bb84e6ec3886 100644
>> --- a/security/tomoyo/tomoyo.c
>> +++ b/security/tomoyo/tomoyo.c
>> @@ -18,7 +18,9 @@
>> */
>> static int tomoyo_cred_alloc_blank(struct cred *new, gfp_t gfp)
>> {
>> - new->security = NULL;
>> + struct tomoyo_domain_info **blob = tomoyo_cred(new);
>> +
>> + *blob = NULL;
>> return 0;
>> }
>>
>> @@ -34,8 +36,13 @@ static int tomoyo_cred_alloc_blank(struct cred *new, gfp_t gfp)
>> static int tomoyo_cred_prepare(struct cred *new, const struct cred *old,
>> gfp_t gfp)
>> {
>> - struct tomoyo_domain_info *domain = old->security;
>> - new->security = domain;
>> + struct tomoyo_domain_info **old_blob = tomoyo_cred(old);
>> + struct tomoyo_domain_info **new_blob = tomoyo_cred(new);
>> + struct tomoyo_domain_info *domain;
>> +
>> + domain = *old_blob;
>> + *new_blob = domain;
>> +
>> if (domain)
>> atomic_inc(&domain->users);
>> return 0;
>> @@ -59,7 +66,9 @@ static void tomoyo_cred_transfer(struct cred *new, const struct cred *old)
>> */
>> static void tomoyo_cred_free(struct cred *cred)
>> {
>> - struct tomoyo_domain_info *domain = cred->security;
>> + struct tomoyo_domain_info **blob = tomoyo_cred(cred);
>> + struct tomoyo_domain_info *domain = *blob;
>> +
>> if (domain)
>> atomic_dec(&domain->users);
>> }
>> @@ -73,6 +82,9 @@ static void tomoyo_cred_free(struct cred *cred)
>> */
>> static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
>> {
>> + struct tomoyo_domain_info **blob;
>> + struct tomoyo_domain_info *domain;
>> +
>> /*
>> * Do only if this function is called for the first time of an execve
>> * operation.
>> @@ -93,13 +105,14 @@ static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
>> * stored inside "bprm->cred->security" will be acquired later inside
>> * tomoyo_find_next_domain().
>> */
>> - atomic_dec(&((struct tomoyo_domain_info *)
>> - bprm->cred->security)->users);
>> + blob = tomoyo_cred(bprm->cred);
>> + domain = *blob;
>> + atomic_dec(&domain->users);
>> /*
>> * Tell tomoyo_bprm_check_security() is called for the first time of an
>> * execve operation.
>> */
>> - bprm->cred->security = NULL;
>> + *blob = NULL;
>> return 0;
>> }
>>
>> @@ -112,8 +125,11 @@ static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
>> */
>> static int tomoyo_bprm_check_security(struct linux_binprm *bprm)
>> {
>> - struct tomoyo_domain_info *domain = bprm->cred->security;
>> + struct tomoyo_domain_info **blob;
>> + struct tomoyo_domain_info *domain;
>>
>> + blob = tomoyo_cred(bprm->cred);
>> + domain = *blob;
>> /*
>> * Execute permission is checked against pathname passed to do_execve()
>> * using current domain.
>> @@ -493,6 +509,10 @@ static int tomoyo_socket_sendmsg(struct socket *sock, struct msghdr *msg,
>> return tomoyo_socket_sendmsg_permission(sock, msg, size);
>> }
>>
>> +struct lsm_blob_sizes tomoyo_blob_sizes = {
>> + .lbs_cred = sizeof(struct tomoyo_domain_info *),
>> +};
>> +
>> /*
>> * tomoyo_security_ops is a "struct security_operations" which is used for
>> * registering TOMOYO.
>> @@ -531,6 +551,8 @@ static struct security_hook_list tomoyo_hooks[] __lsm_ro_after_init = {
>> /* Lock for GC. */
>> DEFINE_SRCU(tomoyo_ss);
>>
>> +bool tomoyo_enabled;
>> +
>> /**
>> * tomoyo_init - Register TOMOYO Linux as a LSM module.
>> *
>> @@ -538,14 +560,28 @@ DEFINE_SRCU(tomoyo_ss);
>> */
>> static int __init tomoyo_init(void)
>> {
>> + static int finish;
>> struct cred *cred = (struct cred *) current_cred();
>> + struct tomoyo_domain_info **blob;
>> +
>> + if (!security_module_enable("tomoyo")) {
>> + tomoyo_enabled = false;
>> + return 0;
>> + }
>> + tomoyo_enabled = true;
>>
>> - if (!security_module_enable("tomoyo"))
>> + if (!finish) {
>> + security_add_blobs(&tomoyo_blob_sizes);
>> + finish = 1;
>> return 0;
>> + }
>> +
>> /* register ourselves with the security framework */
>> security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks), "tomoyo");
>> printk(KERN_INFO "TOMOYO Linux initialized\n");
>> - cred->security = &tomoyo_kernel_domain;
>> + lsm_early_cred(cred);
>> + blob = tomoyo_cred(cred);
>> + *blob = &tomoyo_kernel_domain;
>> tomoyo_mm_init();
>> return 0;
>> }
>> --
>> 2.17.1
>>
>>
> -Kees
>
On Thu, Sep 13, 2018 at 11:19 AM Kees Cook <[email protected]> wrote:
> On Thu, Sep 13, 2018 at 6:16 AM, Paul Moore <[email protected]> wrote:
> > On Thu, Sep 13, 2018 at 12:19 AM Kees Cook <[email protected]> wrote:
> >> On Tue, Sep 11, 2018 at 9:42 AM, Casey Schaufler <[email protected]> wrote:
> >> > Two proposed security modules require the ability to
> >> > share security blobs with existing "major" security modules.
> >> > These modules, S.A.R.A and LandLock, provide significantly
> >> > different services than SELinux, Smack or AppArmor. Using
> >> > either in conjunction with the existing modules is quite
> >> > reasonable. S.A.R.A requires access to the cred blob, while
> >> > LandLock uses the cred, file and inode blobs.
> >> >
> >> > The use of the cred, file and inode blobs has been
> >> > abstracted in preceding patches in the series. This
> >> > patch teaches the affected security modules how to access
> >> > the part of the blob set aside for their use in the case
> >> > where blobs are shared. The configuration option
> >> > CONFIG_SECURITY_STACKING identifies systems where the
> >> > blobs may be shared.
> >> >
> >> > The mechanism for selecting which security modules are
> >> > active has been changed to allow non-conflicting "major"
> >> > security modules to be used together. At this time the
> >> > TOMOYO module can safely be used with any of the others.
> >> > The two new modules would be non-conflicting as well.
> >> >
> >> > Signed-off-by: Casey Schaufler <[email protected]>
> >> > ---
> >> > Documentation/admin-guide/LSM/index.rst | 14 +++--
> >> > include/linux/lsm_hooks.h | 2 +-
> >> > security/Kconfig | 81 +++++++++++++++++++++++++
> >> > security/apparmor/include/cred.h | 8 +++
> >> > security/apparmor/include/file.h | 9 ++-
> >> > security/apparmor/include/lib.h | 4 ++
> >> > security/apparmor/lsm.c | 8 ++-
> >> > security/security.c | 30 ++++++++-
> >> > security/selinux/hooks.c | 3 +-
> >> > security/selinux/include/objsec.h | 18 +++++-
> >> > security/smack/smack.h | 19 +++++-
> >> > security/smack/smack_lsm.c | 17 +++---
> >> > security/tomoyo/common.h | 12 +++-
> >> > security/tomoyo/tomoyo.c | 3 +-
> >> > 14 files changed, 200 insertions(+), 28 deletions(-)
> >
> > ...
> >
> >> > diff --git a/security/Kconfig b/security/Kconfig
> >> > index 22f7664c4977..ed48025ae9e0 100644
> >> > --- a/security/Kconfig
> >> > +++ b/security/Kconfig
> >> > @@ -36,6 +36,28 @@ config SECURITY_WRITABLE_HOOKS
> >> > bool
> >> > default n
> >> >
> >> > +config SECURITY_STACKING
> >> > + bool "Security module stacking"
> >> > + depends on SECURITY
> >> > + help
> >> > + Allows multiple major security modules to be stacked.
> >> > + Modules are invoked in the order registered with a
> >> > + "bail on fail" policy, in which the infrastructure
> >> > + will stop processing once a denial is detected. Not
> >> > + all modules can be stacked. SELinux, Smack and AppArmor are
> >> > + known to be incompatible. User space components may
> >> > + have trouble identifying the security module providing
> >> > + data in some cases.
> >> > +
> >> > + If you select this option you will have to select which
> >> > + of the stackable modules you wish to be active. The
> >> > + "Default security module" will be ignored. The boot line
> >> > + "security=" option can be used to specify that one of
> >> > + the modules identifed for stacking should be used instead
> >> > + of the entire stack.
> >> > +
> >> > + If you are unsure how to answer this question, answer N.
> >>
> >> I don't see a good reason to make this a config. Why shouldn't this
> >> always be enabled?
> >
> > I do. From a user perspective it is sometimes difficult to determine
> > the reason behind a failed operation; its is a DAC based denial, the
> > LSM, or some other failure? Stacking additional LSMs has the
> > potential to make this worse. The boot time configuration adds to the
> > complexity.
>
> Let me try to convince you otherwise. :) The reason I think there's no
> need for this is because the only functional change here is how
> _TOMOYO_ gets stacked. And in my proposal, we can convert TOMOYO to be
> enabled/disabled like LoadPin. Given the configs I showed, stacking
> TOMOYO with the other major LSMs becomes a config (and/or boottime)
> option.
>
> The changes for TOMOYO are still needed even _with_ SECURITY_STACKING,
> and I argue that the other major LSMs remain the same. It's only
> infrastructure that has changed. So, I think having SECURITY_STACKING
> actually makes things more complex internally (all the ifdefs, weird
> enable logic) and for distros ("what's this stacking option", etc?)
None of the above deals with the user experience or support burden a
distro would have by forcing stacking on. If we make it an option the
distros can choose for themselves; picking a kernel build config is
not something new to distros, and I think Casey's text adequately
explains CONFIG_SECURITY_STACKING in terms that would be sufficient.
I currently have a neutral stance on stacking, making it mandatory
pushes me more towards a "no".
As far as the cpp ifdef's, and other conditionals are concerned, I
remain unconvinced this is any worse than any other significant
feature that is a build time option.
--
paul moore
http://www.paul-moore.com
On Thu, Sep 13, 2018 at 12:12 PM, Paul Moore <[email protected]> wrote:
> None of the above deals with the user experience or support burden a
> distro would have by forcing stacking on. If we make it an option the
Just to make sure we're clear here: this series does not provide
"extreme" stacking: SELinux, AppArmor, and SMACK remain boot-exclusive
no matter what the CONFIGs.
> distros can choose for themselves; picking a kernel build config is
> not something new to distros, and I think Casey's text adequately
> explains CONFIG_SECURITY_STACKING in terms that would be sufficient.
I absolutely want stacking to be configurable, but I want to point out
that there is no operational difference between
CONFIG_SECURITY_STACKING=n and CONFIG_SECURITY_STACKING=y in the code
here:
- all the new accessor and allocation code is exercised in both cases
- with stacking enabled: selinux, apparmor, and smack have an offset
of 0 into blobs (and only one can be enabled at a time)
- with stacking disabled: selinux, apparmor, and smack have an offset
of 0 into blobs (and only one can be enabled at a time)
The only behavioral difference is TOMOYO:
1- with stacking disabled and TOMOYO as the only major LSM, it will
have a 0 offset into blobs (like above)
2- with stacking enabled and TOMOYO as the only major LSM, it will
have a 0 offset into blobs (like above)
3- with stacking disabled and another major LSM is enabled, TOMOYO
will be disabled (like always)
4- with stacking enabled and another major LSM is enabled, TOMOYO will
have a non-0 offset into blobs and will run after selinux or smack or
run before apparmor (based on link ordering defined by the Makefile).
Note that cases 1, 2, and 3 are identical in behavior to before this
series. Only case 4 is different, which is why I'm saying that instead
of creating a redundant and needlessly complex config, or reinventing
the "enable" wheel, we should simply drop the no-op
CONFIG_SECURITY_STACKING config and provide TOMOYO with an "enable"
parameter (and CONFIG). And it should be _separate_ from the
"security=" line.
This will be the SAME outcome for distros: if they want stacking, they
choose the "enable TOMOYO by default" CONFIG. If they don't want
stacking, they don't.
> I currently have a neutral stance on stacking, making it mandatory
> pushes me more towards a "no".
This is why I'm trying to explain myself: the infrastructure proposed
here is always exercised, no matter the CONFIG. From that sense it is
"mandatory" no matter what the config is. There isn't a reality where
you could "turn off stacking", because it's not stacking until you
actually stack something, and that will be disabled by default as I've
proposed it.
Let me put this another way: if we simply leave off patch 10, we can
take the other 9 patches (modulo feedback), and we only have to decide
how to expose "stacking"; all the infrastructure work for supporting
it is done.
I'm arguing that "security=" is likely insufficient to describe what
we want, and instead we should focus on individual LSM enablement via
parameters ("tomoyo.enabled=1"). If _ordering_ becomes an issue, we
could either use parameter order, or use "security=" again maybe, but
for now, ordering is already defined by the Makefile (and
security/security.c).
"Stacking" only exists if you try to enable one of [selinux, apparmor,
or smack] AND tomoyo. CONFIG_SECURITY_STACKING is redundant: if you
want to disable stacking, you just disable tomoyo if you have another
LSM (which we can already enforce in the Kconfig).
If you want something more explicit than per-LSM config, then a simple
"security.lsm_stack=1/0" with a CONFIG for the default would be fine.
I'm trying to argue against what appears to be needless complexity
around CONFIG_SECURITY_STACK as it was proposed, since it doesn't
provide a meaningful operational change, since exclusivity of major
LSMs is already handled.
> As far as the cpp ifdef's, and other conditionals are concerned, I
> remain unconvinced this is any worse than any other significant
> feature that is a build time option.
That's fine. That's just a code style issue. What I'm trying to show
is that by lifting the allocation logic up out of the LSMs, we've
actually simplified the logic. The "stacking" part will only become a
distro-choice once they knowingly enable TOMOYO or build in SARA
and/or Landlock in the future.
-Kees
--
Kees Cook
Pixel Security
On Thu, Sep 13, 2018 at 12:01 PM, Casey Schaufler
<[email protected]> wrote:
> On 9/12/2018 4:53 PM, Kees Cook wrote:
>> On Tue, Sep 11, 2018 at 9:41 AM, Casey Schaufler <[email protected]> wrote:
>>> Move management of the cred security blob out of the
>>> security modules and into the security infrastructure.
>>> Instead of allocating and freeing space the security
>>> modules tell the infrastructure how much space they
>>> require.
>> There's a lot of changes here that I think deserve some longer
>> discussion in the changelog. Notably, now we run _two_ cycles of init
>> calls? That seems... odd. Notes below...
>
> The first pass adds up the blob sizes. This has to be done because
> some modules allocate blobs during the init phase. I have investigated
> alternatives, including blobs that include information about what they
> contain, but they're all significantly more complicated.
Agreed: I liked the idea of not burdening each LSM with a state
machine, but I guess it's not much. Note that "finished" then should
be __initdata.
>>> - if (selinux_is_enabled() && cred->security) {
>>> - if ((unsigned long) cred->security < PAGE_SIZE)
>>> - return true;
>>> - if ((*(u32 *)cred->security & 0xffffff00) ==
>>> - (POISON_FREE << 24 | POISON_FREE << 16 | POISON_FREE << 8))
>>> - return true;
>> These aren't unreasonable checks -- can we add them back in later?
>> (They don't need to be selinux specific, in fact: the LSM could do the
>> poison...)
>
> I had asked the maintainers about the value of these checks, and
> they said that they were primarily there for debugging during the
> original cred breakout development. I'd have not problem making them
> infrastructure managed if there's a strong desire to keep them.
Since it was only SELinux doing this, and it was from old debugging,
then I concur: drop it. It would be easy to restore (and for all LSMs)
in the future.
>>> +static inline void set_cred_label(const struct cred *cred,
>>> + struct aa_label *label)
>>> +{
>>> + struct aa_label **blob = cred->security;
>>> +
>>> + AA_BUG(!blob);
>>> + *blob = label;
>>> +}
>> This feels like it should be a separate patch? Shouldn't AA not be
>> playing with cred->security directly before we get to this "sizing and
>> allocation" patch?
>
> You're correct. This is a harmless patch break-up mistake. The end
> result of the set is correct, and the interim state works as intended,
> albeit with unnecessary code.
I much prefer lots of small easy-to-understand patches than trying to
piece together many separate things. Let's please break out each LSM's
changes and any refactoring into separate patches. Complexity is
harder to review than quantity, IMO.
>>> + */
>>> +void lsm_early_cred(struct cred *cred)
>>> +{
>>> + int rc;
>>> +
>>> + if (cred == NULL)
>>> + panic("%s: NULL cred.\n", __func__);
>> I have been given strongly worded advice to never BUG nor panic. I would:
>>
>> if (WARN_ON(!cred || !cred->security))
>> return;
>>
>>> + if (cred->security != NULL)
>>> + return;
>>> + rc = lsm_cred_alloc(cred, GFP_KERNEL);
>>> + if (rc)
>>> + panic("%s: Early cred alloc failed.\n", __func__);
>> And:
>>
>> WARN_ON(lsm_cred_alloc(cred, GFP_KERNEL));
>
> This duplicates the previous behavior from the SELinux and Smack code.
> A WARN_ON will result in an immediate panic when the calling code tries
> to dereference the blob.
I guess what I'm trying to say is, if you have a patch that does:
+ .... panic()
or
+ .... BUG()
and it has "security" anywhere in the topic, you run an extremely high
risk of Linus NAKing the entire series.
>>> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
>>> index 9d6cdd21acb6..9b49698754a7 100644
>>> --- a/security/selinux/hooks.c
>>> +++ b/security/selinux/hooks.c
>>> @@ -213,12 +213,9 @@ static void cred_init_security(void)
>>> struct cred *cred = (struct cred *) current->real_cred;
>>> struct task_security_struct *tsec;
>>>
>>> - tsec = kzalloc(sizeof(struct task_security_struct), GFP_KERNEL);
>>> - if (!tsec)
>>> - panic("SELinux: Failed to initialize initial task.\n");
>>> -
>>> + lsm_early_cred(cred);
>>> + tsec = selinux_cred(cred);
>> Perhaps leave the existing panic() as-is?
>
> Moving the panic into lsm_early_cred() reduces the panic count
> and avoids code duplication here and in the Smack equivalent.
Agreed: but it means intentionally adding a machine-stop condition
which Linus is very opposed to.
>> Shouldn't this Tomoyo cred handling get split out?
>
> Yeah. Again, it's the patch size/count balance.
I really do like lots of little patches. SO much easier. The main
brain-drain on large patches is that things end up out of order due to
filename ordering, etc, so a lot more needs to be kept in your head as
you're reading a long patch. Much prefer lots of small patches.
-Kees
--
Kees Cook
Pixel Security
On Thursday, September 13, 2018 9:12 PM, Paul Moore <[email protected]> wrote:
> On Thu, Sep 13, 2018 at 11:19 AM Kees Cook [email protected] wrote:
>
> > On Thu, Sep 13, 2018 at 6:16 AM, Paul Moore [email protected] wrote:
> >
> > > On Thu, Sep 13, 2018 at 12:19 AM Kees Cook [email protected] wrote:
> > >
> > > > On Tue, Sep 11, 2018 at 9:42 AM, Casey Schaufler [email protected] wrote:
> > > >
> > > > > Two proposed security modules require the ability to
> > > > > share security blobs with existing "major" security modules.
> > > > > These modules, S.A.R.A and LandLock, provide significantly
> > > > > different services than SELinux, Smack or AppArmor. Using
> > > > > either in conjunction with the existing modules is quite
> > > > > reasonable. S.A.R.A requires access to the cred blob, while
> > > > > LandLock uses the cred, file and inode blobs.
> > > > > The use of the cred, file and inode blobs has been
> > > > > abstracted in preceding patches in the series. This
> > > > > patch teaches the affected security modules how to access
> > > > > the part of the blob set aside for their use in the case
> > > > > where blobs are shared. The configuration option
> > > > > CONFIG_SECURITY_STACKING identifies systems where the
> > > > > blobs may be shared.
> > > > > The mechanism for selecting which security modules are
> > > > > active has been changed to allow non-conflicting "major"
> > > > > security modules to be used together. At this time the
> > > > > TOMOYO module can safely be used with any of the others.
> > > > > The two new modules would be non-conflicting as well.
> > > > >
> > > > > Signed-off-by: Casey Schaufler [email protected]
> > > > >
> > > > > ------------------------------------------------------
> > > > >
> > > > > Documentation/admin-guide/LSM/index.rst | 14 +++--
> > > > > include/linux/lsm_hooks.h | 2 +-
> > > > > security/Kconfig | 81 +++++++++++++++++++++++++
> > > > > security/apparmor/include/cred.h | 8 +++
> > > > > security/apparmor/include/file.h | 9 ++-
> > > > > security/apparmor/include/lib.h | 4 ++
> > > > > security/apparmor/lsm.c | 8 ++-
> > > > > security/security.c | 30 ++++++++-
> > > > > security/selinux/hooks.c | 3 +-
> > > > > security/selinux/include/objsec.h | 18 +++++-
> > > > > security/smack/smack.h | 19 +++++-
> > > > > security/smack/smack_lsm.c | 17 +++---
> > > > > security/tomoyo/common.h | 12 +++-
> > > > > security/tomoyo/tomoyo.c | 3 +-
> > > > > 14 files changed, 200 insertions(+), 28 deletions(-)
> > >
> > > ...
> > >
> > > > > diff --git a/security/Kconfig b/security/Kconfig
> > > > > index 22f7664c4977..ed48025ae9e0 100644
> > > > > --- a/security/Kconfig
> > > > > +++ b/security/Kconfig
> > > > > @@ -36,6 +36,28 @@ config SECURITY_WRITABLE_HOOKS
> > > > > bool
> > > > > default n
> > > > > +config SECURITY_STACKING
> > > > >
> > > > > - bool "Security module stacking"
> > > > >
> > > > >
> > > > > - depends on SECURITY
> > > > >
> > > > >
> > > > > - help
> > > > >
> > > > >
> > > > > - Allows multiple major security modules to be stacked.
> > > > >
> > > > >
> > > > > - Modules are invoked in the order registered with a
> > > > >
> > > > >
> > > > > - "bail on fail" policy, in which the infrastructure
> > > > >
> > > > >
> > > > > - will stop processing once a denial is detected. Not
> > > > >
> > > > >
> > > > > - all modules can be stacked. SELinux, Smack and AppArmor are
> > > > >
> > > > >
> > > > > - known to be incompatible. User space components may
> > > > >
> > > > >
> > > > > - have trouble identifying the security module providing
> > > > >
> > > > >
> > > > > - data in some cases.
> > > > >
> > > > >
> > > > > -
> > > > > - If you select this option you will have to select which
> > > > >
> > > > >
> > > > > - of the stackable modules you wish to be active. The
> > > > >
> > > > >
> > > > > - "Default security module" will be ignored. The boot line
> > > > >
> > > > >
> > > > > - "security=" option can be used to specify that one of
> > > > >
> > > > >
> > > > > - the modules identifed for stacking should be used instead
> > > > >
> > > > >
> > > > > - of the entire stack.
> > > > >
> > > > >
> > > > > -
> > > > > - If you are unsure how to answer this question, answer N.
> > > > >
> > > > >
> > > >
> > > > I don't see a good reason to make this a config. Why shouldn't this
> > > > always be enabled?
> > >
> > > I do. From a user perspective it is sometimes difficult to determine
> > > the reason behind a failed operation; its is a DAC based denial, the
> > > LSM, or some other failure? Stacking additional LSMs has the
> > > potential to make this worse. The boot time configuration adds to the
> > > complexity.
> >
> > Let me try to convince you otherwise. :) The reason I think there's no
> > need for this is because the only functional change here is how
> > TOMOYO gets stacked. And in my proposal, we can convert TOMOYO to be
> > enabled/disabled like LoadPin. Given the configs I showed, stacking
> > TOMOYO with the other major LSMs becomes a config (and/or boottime)
> > option.
> > The changes for TOMOYO are still needed even with SECURITY_STACKING,
> > and I argue that the other major LSMs remain the same. It's only
> > infrastructure that has changed. So, I think having SECURITY_STACKING
> > actually makes things more complex internally (all the ifdefs, weird
> > enable logic) and for distros ("what's this stacking option", etc?)
>
> None of the above deals with the user experience or support burden a
> distro would have by forcing stacking on. If we make it an option the
> distros can choose for themselves; picking a kernel build config is
> not something new to distros, and I think Casey's text adequately
> explains CONFIG_SECURITY_STACKING in terms that would be sufficient.
CONFIG_SECURITY_STACKING doesn't make any user visible changes on
itself as it doesn't automatically enable any new LSM. The LSM
specific configs are place where users/distros make decisions. If
there is only one LSM enabled to run then there's nothing to stack.
If someone choose to run two or more LSM in config/boot cmdline
then we can assume having it stacked is what they wanted. As Kees
pointed there is already CONFIG_SECURITY_DEFAULT_XXX. In both cases
CONFIG_SECURITY_STACKING is redundant and only adds burden instead
of removing it.
> I currently have a neutral stance on stacking, making it mandatory
> pushes me more towards a "no".
>
This implies that your real concern is something else than
CONFIG_SECURITY_STACKING which only allows you to ignore the whole
thing. Please reveal it. There are a lot of people waiting for LSM
stacking which is several years late and it would be great to
resolve potential issues earlier rather later.
> As far as the cpp ifdef's, and other conditionals are concerned, I
> remain unconvinced this is any worse than any other significant
> feature that is a build time option.
>
> paul moore
Jordan
On Thu, Sep 13, 2018 at 5:01 PM Kees Cook <[email protected]> wrote:
> On Thu, Sep 13, 2018 at 12:12 PM, Paul Moore <[email protected]> wrote:
> > None of the above deals with the user experience or support burden a
> > distro would have by forcing stacking on. If we make it an option the
>
> Just to make sure we're clear here: this series does not provide
> "extreme" stacking: SELinux, AppArmor, and SMACK remain boot-exclusive
> no matter what the CONFIGs.
>
> > distros can choose for themselves; picking a kernel build config is
> > not something new to distros, and I think Casey's text adequately
> > explains CONFIG_SECURITY_STACKING in terms that would be sufficient.
>
> I absolutely want stacking to be configurable, but I want to point out
> that there is no operational difference between
> CONFIG_SECURITY_STACKING=n and CONFIG_SECURITY_STACKING=y in the code
> here:
>
> - all the new accessor and allocation code is exercised in both cases
>
> - with stacking enabled: selinux, apparmor, and smack have an offset
> of 0 into blobs (and only one can be enabled at a time)
>
> - with stacking disabled: selinux, apparmor, and smack have an offset
> of 0 into blobs (and only one can be enabled at a time)
>
> The only behavioral difference is TOMOYO:
>
> 1- with stacking disabled and TOMOYO as the only major LSM, it will
> have a 0 offset into blobs (like above)
>
> 2- with stacking enabled and TOMOYO as the only major LSM, it will
> have a 0 offset into blobs (like above)
>
> 3- with stacking disabled and another major LSM is enabled, TOMOYO
> will be disabled (like always)
>
> 4- with stacking enabled and another major LSM is enabled, TOMOYO will
> have a non-0 offset into blobs and will run after selinux or smack or
> run before apparmor (based on link ordering defined by the Makefile).
Case #3/#4 is what I'm getting at, and I would argue demonstrates an
operational difference that is user visible/configurable.
Unless something has changed and I missed it, you can currently build
all of the LSMs into a single kernel image, and the admin/user can
choose one at boot time. CONFIG_SECURITY_STACKING=y enables the
admin/user to stack LSMs (albeit with restrictions in the current
iteration) and CONFIG_SECURITY_STACKING=n limits the admin/user to a
single LSM (what we have now). I understand that as of this moment we
are talking only about TOMOYO and AppArmor/Smack/SELinux, but everyone
knows that S.A.R.A/SARA and LandLock are going to follow shortly -
that's the whole point of this latest spin, isn't it?
> > I currently have a neutral stance on stacking, making it mandatory
> > pushes me more towards a "no".
>
> This is why I'm trying to explain myself: the infrastructure proposed
> here is always exercised, no matter the CONFIG. From that sense it is
> "mandatory" no matter what the config is. There isn't a reality where
> you could "turn off stacking", because it's not stacking until you
> actually stack something, and that will be disabled by default as I've
> proposed it.
>
> Let me put this another way: if we simply leave off patch 10, we can
> take the other 9 patches (modulo feedback), and we only have to decide
> how to expose "stacking"; all the infrastructure work for supporting
> it is done.
>
> I'm arguing that "security=" is likely insufficient to describe what
> we want, and instead we should focus on individual LSM enablement via
> parameters ("tomoyo.enabled=1"). If _ordering_ becomes an issue, we
> could either use parameter order, or use "security=" again maybe, but
> for now, ordering is already defined by the Makefile (and
> security/security.c).
The infrastructure bits aren't really my concern; in fact I *like*
that the infrastructure is always exercised, it makes
testing/debugging easier. I also like the ability to limit the
user/admin to one LSM at boot time to make support easier; my goal is
to allow a distro to build support for multiple LSMs without also
requiring that distro to support *stacked* LSMs (see my earlier
comments about the difficulty in determining the source of a failed
operation).
--
paul moore
http://www.paul-moore.com
On Thu, Sep 13, 2018 at 4:58 PM Jordan Glover
<[email protected]> wrote:
>
> On Thursday, September 13, 2018 9:12 PM, Paul Moore <[email protected]> wrote:
>
> > On Thu, Sep 13, 2018 at 11:19 AM Kees Cook [email protected] wrote:
> >
> > > On Thu, Sep 13, 2018 at 6:16 AM, Paul Moore [email protected] wrote:
> > >
> > > > On Thu, Sep 13, 2018 at 12:19 AM Kees Cook [email protected] wrote:
...
> > > > > I don't see a good reason to make this a config. Why shouldn't this
> > > > > always be enabled?
> > > >
> > > > I do. From a user perspective it is sometimes difficult to determine
> > > > the reason behind a failed operation; its is a DAC based denial, the
> > > > LSM, or some other failure? Stacking additional LSMs has the
> > > > potential to make this worse. The boot time configuration adds to the
> > > > complexity.
> > >
> > > Let me try to convince you otherwise. :) The reason I think there's no
> > > need for this is because the only functional change here is how
> > > TOMOYO gets stacked. And in my proposal, we can convert TOMOYO to be
> > > enabled/disabled like LoadPin. Given the configs I showed, stacking
> > > TOMOYO with the other major LSMs becomes a config (and/or boottime)
> > > option.
> > > The changes for TOMOYO are still needed even with SECURITY_STACKING,
> > > and I argue that the other major LSMs remain the same. It's only
> > > infrastructure that has changed. So, I think having SECURITY_STACKING
> > > actually makes things more complex internally (all the ifdefs, weird
> > > enable logic) and for distros ("what's this stacking option", etc?)
> >
> > None of the above deals with the user experience or support burden a
> > distro would have by forcing stacking on. If we make it an option the
> > distros can choose for themselves; picking a kernel build config is
> > not something new to distros, and I think Casey's text adequately
> > explains CONFIG_SECURITY_STACKING in terms that would be sufficient.
>
> CONFIG_SECURITY_STACKING doesn't make any user visible changes on
> itself as it doesn't automatically enable any new LSM. The LSM
> specific configs are place where users/distros make decisions. If
> there is only one LSM enabled to run then there's nothing to stack.
> If someone choose to run two or more LSM in config/boot cmdline
> then we can assume having it stacked is what they wanted. As Kees
> pointed there is already CONFIG_SECURITY_DEFAULT_XXX. In both cases
> CONFIG_SECURITY_STACKING is redundant and only adds burden instead
> of removing it.
See my last response to Kees.
> > I currently have a neutral stance on stacking, making it mandatory
> > pushes me more towards a "no".
>
> This implies that your real concern is something else than
> CONFIG_SECURITY_STACKING which only allows you to ignore the whole
> thing. Please reveal it. There are a lot of people waiting for LSM
> stacking which is several years late and it would be great to
> resolve potential issues earlier rather later.
What? I resent the implication that I'm hiding anything; there are a
lot of fair criticisms you could level at me, but I take offense at
the idea that I'm not being honest here. I've been speaking with
Casey, John, and others about stacking for years, both on-list and
in-person at conferences, and my
neutral-opinion-just-make-it-work-for-everything-and-make-it-optional
stance has been pretty consistent and isn't new.
Also, let's be really clear here: I'm only asking that stacking be
made a build time option (as it is in Casey's patchset). That seems
like a pretty modest ask for something so significant and "several
years late" as you put it.
--
paul moore
http://www.paul-moore.com
On Thu, Sep 13, 2018 at 2:38 PM, Paul Moore <[email protected]> wrote:
> The infrastructure bits aren't really my concern; in fact I *like*
> that the infrastructure is always exercised, it makes
> testing/debugging easier. I also like the ability to limit the
> user/admin to one LSM at boot time to make support easier; my goal is
> to allow a distro to build support for multiple LSMs without also
> requiring that distro to support *stacked* LSMs
I see your point, but as soon as SARA and Landlock appear, they'll have:
depends on SECURITY_STACKING
and then all distros will enable it and there will be no sensible
runtime way to manage it. If, instead, we make it entirely runtime
now, then a CONFIG can control the default state and we can provide
guidance to how SARA and Landlock should expose their "enable"ness.
At the very least, to avoid stacking now (i.e. TOMOYO being enabled
with another major LSM), we just do nothing. The existing code already
makes the existing major LSMs exclusive. Adding a stackable LSM would
need to just have its own "enable" flag (i.e. ignore
security_module_enable()), and then either check a runtime "is
stacking allowed?" flag or have new "depends on SECURITY_STACKING". (I
think the CONFIG will force distros into enabling it without any
runtime opt-out.)
> (see my earlier
> comments about the difficulty in determining the source of a failed
> operation).
Agreed. I would hope that audit could help for that case. *stare at blue sky*
-Kees
--
Kees Cook
Pixel Security
On Thursday, September 13, 2018 11:50 PM, Paul Moore <[email protected]> wrote:
> On Thu, Sep 13, 2018 at 4:58 PM Jordan Glover
> [email protected] wrote:
>
> > On Thursday, September 13, 2018 9:12 PM, Paul Moore [email protected] wrote:
> >
> > > On Thu, Sep 13, 2018 at 11:19 AM Kees Cook [email protected] wrote:
> > >
> > > > On Thu, Sep 13, 2018 at 6:16 AM, Paul Moore [email protected] wrote:
> > > >
> > > > > On Thu, Sep 13, 2018 at 12:19 AM Kees Cook [email protected] wrote:
>
> ...
>
> > > > > > I don't see a good reason to make this a config. Why shouldn't this
> > > > > > always be enabled?
> > > > >
> > > > > I do. From a user perspective it is sometimes difficult to determine
> > > > > the reason behind a failed operation; its is a DAC based denial, the
> > > > > LSM, or some other failure? Stacking additional LSMs has the
> > > > > potential to make this worse. The boot time configuration adds to the
> > > > > complexity.
> > > >
> > > > Let me try to convince you otherwise. :) The reason I think there's no
> > > > need for this is because the only functional change here is how
> > > > TOMOYO gets stacked. And in my proposal, we can convert TOMOYO to be
> > > > enabled/disabled like LoadPin. Given the configs I showed, stacking
> > > > TOMOYO with the other major LSMs becomes a config (and/or boottime)
> > > > option.
> > > > The changes for TOMOYO are still needed even with SECURITY_STACKING,
> > > > and I argue that the other major LSMs remain the same. It's only
> > > > infrastructure that has changed. So, I think having SECURITY_STACKING
> > > > actually makes things more complex internally (all the ifdefs, weird
> > > > enable logic) and for distros ("what's this stacking option", etc?)
> > >
> > > None of the above deals with the user experience or support burden a
> > > distro would have by forcing stacking on. If we make it an option the
> > > distros can choose for themselves; picking a kernel build config is
> > > not something new to distros, and I think Casey's text adequately
> > > explains CONFIG_SECURITY_STACKING in terms that would be sufficient.
> >
> > CONFIG_SECURITY_STACKING doesn't make any user visible changes on
> > itself as it doesn't automatically enable any new LSM. The LSM
> > specific configs are place where users/distros make decisions. If
> > there is only one LSM enabled to run then there's nothing to stack.
> > If someone choose to run two or more LSM in config/boot cmdline
> > then we can assume having it stacked is what they wanted. As Kees
> > pointed there is already CONFIG_SECURITY_DEFAULT_XXX. In both cases
> > CONFIG_SECURITY_STACKING is redundant and only adds burden instead
> > of removing it.
>
> See my last response to Kees.
>
> > > I currently have a neutral stance on stacking, making it mandatory
> > > pushes me more towards a "no".
> >
> > This implies that your real concern is something else than
> > CONFIG_SECURITY_STACKING which only allows you to ignore the whole
> > thing. Please reveal it. There are a lot of people waiting for LSM
> > stacking which is several years late and it would be great to
> > resolve potential issues earlier rather later.
>
> What? I resent the implication that I'm hiding anything; there are a
> lot of fair criticisms you could level at me, but I take offense at
> the idea that I'm not being honest here. I've been speaking with
> Casey, John, and others about stacking for years, both on-list and
> in-person at conferences, and my
> neutral-opinion-just-make-it-work-for-everything-and-make-it-optional
> stance has been pretty consistent and isn't new.
>
> Also, let's be really clear here: I'm only asking that stacking be
> made a build time option (as it is in Casey's patchset). That seems
> like a pretty modest ask for something so significant and "several
> years late" as you put it.
>
> paul moore
Fair enough. I apologize then.
Jordan
On 9/13/2018 2:50 PM, Paul Moore wrote:
> On Thu, Sep 13, 2018 at 4:58 PM Jordan Glover
> <[email protected]> wrote:
>
>> This implies that your real concern is something else than
>> CONFIG_SECURITY_STACKING which only allows you to ignore the whole
>> thing. Please reveal it. There are a lot of people waiting for LSM
>> stacking which is several years late and it would be great to
>> resolve potential issues earlier rather later.
It would be really handy if the "lot of people" were a lot more
vocal about their impatience. Keeping the stacking work on the stove,
much less on a front burner, hasn't always been easy.
> What? I resent the implication that I'm hiding anything; there are a
> lot of fair criticisms you could level at me, but I take offense at
> the idea that I'm not being honest here. I've been speaking with
> Casey, John, and others about stacking for years, both on-list and
> in-person at conferences, and my
> neutral-opinion-just-make-it-work-for-everything-and-make-it-optional
> stance has been pretty consistent and isn't new.
Paul has always been quite upfront about this, and responsive as well.
I won't say that we always agree because we don't, but I don't have a
good argument against either point.
> Also, let's be really clear here: I'm only asking that stacking be
> made a build time option (as it is in Casey's patchset). That seems
> like a pretty modest ask for something so significant and "several
> years late" as you put it.
There's a significant difference between something taking a long time
and something being late. I hope that I haven't given anyone the impression
that I'd have this finished years ago. If so, I owe whoever that was a
beer. This patch set may look deceptively straight forward, but there have
been many heavy branches pruned from the tree. This subset of the total
change for "extreme" stacking represents the easy part. Without a road map
for completing the task (i.e. any/all modules together) Paul's hesitation
to take anything is defensible, and the desire that it be configurable
reasonable.
On Thu, Sep 13, 2018 at 2:51 PM, Kees Cook <[email protected]> wrote:
> At the very least, to avoid stacking now (i.e. TOMOYO being enabled
> with another major LSM), we just do nothing. The existing code already
> makes the existing major LSMs exclusive. Adding a stackable LSM would
> need to just have its own "enable" flag (i.e. ignore
> security_module_enable()), and then either check a runtime "is
> stacking allowed?" flag or have new "depends on SECURITY_STACKING". (I
> think the CONFIG will force distros into enabling it without any
> runtime opt-out.)
Before stacking, we have:
- major LSM, pick one
- all CONFIG minor LSMs, in security.c order
There are two minor LSMs: Yama and LoadPin. If built, Yama is always
on (though it has sysctl knobs). If built, LoadPin is controlled by a
boot param.
Picking the major LSM happens via "security=$LSM" and a per-LSM check
of security_module_enable("$LSM").
Ordering is major, then per security.c for minors.
Under stacking, we have:
The minor LSMs remain unchanged.
We don't have SARA and Landlock yet, but we do have TOMOYO, which we
can use as an example to future "compatible blob-using LSMs".
I see two issues:
- how to determine which set of LSMs are enabled at boot
- how to determine the ORDER of the LSMs
Casey's implementation does this (correct me if I'm wrong):
The minor LSMs remain unchanged.
SECURITY_$lsm_STACKED determines which major is enabled, with
SECURITY_TOMOYO_STACKED allowed in addition. If security= is
specified, all other major LSMs are disabled (i.e. it is not possible
to switch between SELinux/AppArmor/SMACK without also disabling
TOMOYO).
Ordering is per security/Makefile modulo enabled-ness for majors (i.e.
TOMOYO is always _before_ AppArmor if stacked together, otherwise
after SELinux and SMACK), and per security.c for minors.
I don't think this is how we want it to work. For example, Ubuntu
builds in all LSMs, and default-enables AppArmor. If an Ubuntu user
wants TOMOYO, the boot with "security=tomoyo". If Ubuntu wants to make
stacking available to users but off by default, what CONFIGs do they
pick? They could try SECURITY_APPARMOR_STACKED=y and
SECURITY_TOMOYO_STACKED=n, but then how does an end user choose
"apparmor and tomoyo" (or more meaningfully, for the future:
"apparmor, sara, and landlock")? They can still pick
"security=tomoyo", but there isn't a runtime way to opt into stacking.
What about leaving SECURITY_$lsm_DEFAULT as-is, and then...
In the past I'd suggested using "security=" to determine both enabled
and order: "security=tomoyo,smack" would mean stacked LSMs, with
tomoyo going first.
Currently I'm leaning towards "security=" to select ONLY the
incompatible LSM, and per-LSM "enable" flags to determine stacking:
tomoyo.enabled=1 security=smack
This doesn't explicitly address ordering, though. If we made param
_position_ meaningful, then we could get ordering (i.e. above would
mean "tomoyo first").
Note, ordering matters because call_int_hook() will _stop_ on a
non-zero return value: potentially hiding events from later LSMs. Do
we need to revisit this? I seem to remember if being a very dead
horse, and we needed to quick-abort otherwise we ended up in
nonsensical states.
The reason for the new approach is because I can't find a meaningful
way to provide CONFIGs that make sense. We want to provide a few
things:
- is an LSM built into the kernel at all? (CONFIG_SECURITY_$lsm)
- is an LSM enabled by default? (CONFIG_SECURITY_$lsm_ENABLED?)
- has an LSM been enable for this boot? $lsm.enabled=1 or security=$lsm,$lsm ?
- what order should any stacking happen? Makefile? security=?
And for the incompatible-major, do we stick with CONFIG_SECURITY_$lsm_DEFAULT ?
Anyway, if the concern is with exposed behavior for distros, what do
we want? i.e. what should be done for patch 10. Everything else looks
good.
-Kees
--
Kees Cook
Pixel Security
On 09/13/2018 04:06 PM, Kees Cook wrote:
> On Thu, Sep 13, 2018 at 2:51 PM, Kees Cook <[email protected]> wrote:
>> At the very least, to avoid stacking now (i.e. TOMOYO being enabled
>> with another major LSM), we just do nothing. The existing code already
>> makes the existing major LSMs exclusive. Adding a stackable LSM would
>> need to just have its own "enable" flag (i.e. ignore
>> security_module_enable()), and then either check a runtime "is
>> stacking allowed?" flag or have new "depends on SECURITY_STACKING". (I
>> think the CONFIG will force distros into enabling it without any
>> runtime opt-out.)
>
> Before stacking, we have:
>
> - major LSM, pick one
> - all CONFIG minor LSMs, in security.c order
>
> There are two minor LSMs: Yama and LoadPin. If built, Yama is always
> on (though it has sysctl knobs). If built, LoadPin is controlled by a
> boot param.
>
> Picking the major LSM happens via "security=$LSM" and a per-LSM check
> of security_module_enable("$LSM").
>
> Ordering is major, then per security.c for minors.
>
>
> Under stacking, we have:
>
> The minor LSMs remain unchanged.
>
> We don't have SARA and Landlock yet, but we do have TOMOYO, which we
> can use as an example to future "compatible blob-using LSMs".
>
> I see two issues:
>
> - how to determine which set of LSMs are enabled at boot
> - how to determine the ORDER of the LSMs
>
>
> Casey's implementation does this (correct me if I'm wrong):
>
> The minor LSMs remain unchanged.
>
> SECURITY_$lsm_STACKED determines which major is enabled, with
> SECURITY_TOMOYO_STACKED allowed in addition. If security= is
> specified, all other major LSMs are disabled (i.e. it is not possible
> to switch between SELinux/AppArmor/SMACK without also disabling
> TOMOYO).
>
> Ordering is per security/Makefile modulo enabled-ness for majors (i.e.
> TOMOYO is always _before_ AppArmor if stacked together, otherwise
> after SELinux and SMACK), and per security.c for minors.
>
>
> I don't think this is how we want it to work. For example, Ubuntu
> builds in all LSMs, and default-enables AppArmor. If an Ubuntu user
> wants TOMOYO, the boot with "security=tomoyo". If Ubuntu wants to make
> stacking available to users but off by default, what CONFIGs do they
> pick? They could try SECURITY_APPARMOR_STACKED=y and
> SECURITY_TOMOYO_STACKED=n, but then how does an end user choose
> "apparmor and tomoyo" (or more meaningfully, for the future:
> "apparmor, sara, and landlock")? They can still pick
> "security=tomoyo", but there isn't a runtime way to opt into stacking.
>
Ubuntu is carrying an early set of the stacking patches and about a
dozen patches on top of that.
It allows choosing to build the LSMs separate from the what the
default LSM(s) are. There is some config patching here that I need to
update and drop onto this thread.
There is also a patch that allows selecting the set of LSMs at boot.
security=selinux,apparmor
but yama and loadpin are treated differently and I never liked
that. But again another patch to rebase and drop on this thread for
discussion.
>
> What about leaving SECURITY_$lsm_DEFAULT as-is, and then...
>
> In the past I'd suggested using "security=" to determine both enabled
> and order: "security=tomoyo,smack" would mean stacked LSMs, with
> tomoyo going first.
>
> Currently I'm leaning towards "security=" to select ONLY the
> incompatible LSM, and per-LSM "enable" flags to determine stacking:
>
> tomoyo.enabled=1 security=smack
>
I don't like this, it makes it hard for the end user specifying option
at boot time. They have to set security= for the modules they want and
then also individual modules $lsm.enabled= which is very inconvenient
for users.
apparmor.enabled really should only default to enabled and provides a
legacy way to disable apparmor during boot. This option was around
before security= was added.
I need to leave it available as its become part of the api (there are
applications checking /sys/module/apparmor/parameters/enabled but we
can remove it from the Kconfig.
> This doesn't explicitly address ordering, though. If we made param
> _position_ meaningful, then we could get ordering (i.e. above would
> mean "tomoyo first").
>
yes, I think that makes sense
> Note, ordering matters because call_int_hook() will _stop_ on a
> non-zero return value: potentially hiding events from later LSMs. Do
> we need to revisit this? I seem to remember if being a very dead
> horse, and we needed to quick-abort otherwise we ended up in
> nonsensical states.
>
we shouldn't be in nonsensical states but we are doing work that is
thrown away.
Another potential solution is allowing an LSM to register a set of
post hooks that will get called after the regular LSM hooks are
called. This would allow an LSM that cared about whether a hook
succeeded or not to do some processing.
> The reason for the new approach is because I can't find a meaningful
> way to provide CONFIGs that make sense. We want to provide a few
> things:
>
> - is an LSM built into the kernel at all? (CONFIG_SECURITY_$lsm)
> - is an LSM enabled by default? (CONFIG_SECURITY_$lsm_ENABLED?)
> - has an LSM been enable for this boot? $lsm.enabled=1 or security=$lsm,$lsm ?
at least for apparmor it is apparmor.enabled=1 AND security=apparmor or security=$lsm,apparmor ...
> - what order should any stacking happen? Makefile? security=?
>
Preferably not. For the single LSM we have the ability to choose the default LSM, ideally we let the distro decide in the Kconfig and the user with security=...
> And for the incompatible-major, do we stick with CONFIG_SECURITY_$lsm_DEFAULT ?
Ideally Kconfig wouldn't let you choose incompatible LSMs when
selecting what is going to be enabled by default.
>
>
>
> Anyway, if the concern is with exposed behavior for distros, what do
> we want? i.e. what should be done for patch 10. Everything else looks
> good.
>
well speaking for Ubuntu we want to be able to specify the default LSMs
and we want to enable our users to change this.
The current patchset we have is less than ideal partly because of the
whole $lsm.enabled vs security= mess that needs to be resolved.
On 9/13/2018 4:06 PM, Kees Cook wrote:
> On Thu, Sep 13, 2018 at 2:51 PM, Kees Cook <[email protected]> wrote:
>> At the very least, to avoid stacking now (i.e. TOMOYO being enabled
>> with another major LSM), we just do nothing. The existing code already
>> makes the existing major LSMs exclusive. Adding a stackable LSM would
>> need to just have its own "enable" flag (i.e. ignore
>> security_module_enable()), and then either check a runtime "is
>> stacking allowed?" flag or have new "depends on SECURITY_STACKING". (I
>> think the CONFIG will force distros into enabling it without any
>> runtime opt-out.)
> Before stacking, we have:
>
> - major LSM, pick one
- major LSM, pick at most one
> - all CONFIG minor LSMs, in security.c order
>
> There are two minor LSMs: Yama and LoadPin. If built, Yama is always
> on (though it has sysctl knobs). If built, LoadPin is controlled by a
> boot param.
>
> Picking the major LSM happens via "security=$LSM" and a per-LSM check
> of security_module_enable("$LSM").
>
> Ordering is major, then per security.c for minors.
Modules are invoked in the order they are registered.
Minor modules are registered first, so they are invoked before
the major module. This is important because capability comes
first.
> Under stacking, we have:
>
> The minor LSMs remain unchanged.
It wouldn't hurt to use the same registration process for
minor modules as major modules now that you can register more
than one. I haven't changed that because it's unnecessary and
just adds pointless overhead. If we adopt some of the suggestions
below, it may make sense to use the full-up process.
> We don't have SARA and Landlock yet, but we do have TOMOYO, which we
> can use as an example to future "compatible blob-using LSMs".
>
> I see two issues:
>
> - how to determine which set of LSMs are enabled at boot
> - how to determine the ORDER of the LSMs
>
>
> Casey's implementation does this (correct me if I'm wrong):
>
> The minor LSMs remain unchanged.
>
> SECURITY_$lsm_STACKED determines which major is enabled, with
> SECURITY_TOMOYO_STACKED allowed in addition.
Mostly correct. SECURITY_$lsm_STACKED instructs the security module
to register its hooks. Kconfig enforces that you can't specify more
than one of SECURITY_SELINUX_STACKED, SECURITY_SMACK_STACKED, and
SECURITY_APPARMOR_STACKED.
> If security= is
> specified, all other major LSMs are disabled (i.e. it is not possible
> to switch between SELinux/AppArmor/SMACK without also disabling
> TOMOYO).
Correct.
> Ordering is per security/Makefile modulo enabled-ness for majors (i.e.
> TOMOYO is always _before_ AppArmor if stacked together, otherwise
> after SELinux and SMACK), and per security.c for minors.
Correct.
> I don't think this is how we want it to work. For example, Ubuntu
> builds in all LSMs, and default-enables AppArmor. If an Ubuntu user
> wants TOMOYO, the boot with "security=tomoyo". If Ubuntu wants to make
> stacking available to users but off by default, what CONFIGs do they
> pick? They could try SECURITY_APPARMOR_STACKED=y and
> SECURITY_TOMOYO_STACKED=n, but then how does an end user choose
> "apparmor and tomoyo" (or more meaningfully, for the future:
> "apparmor, sara, and landlock")? They can still pick
> "security=tomoyo", but there isn't a runtime way to opt into stacking.
Correct.
Earlier versions of the stacking patchset allowed security=lsm1,lsm2,...,lsmN.
I haven't retrofit that to the current module registration mechanism.
I believe that John Johansen has a patch in Ubuntu for doing this. I don't
have it handy. It's the obvious and backward compatible way to do it.
> What about leaving SECURITY_$lsm_DEFAULT as-is, and then...
That works when you're not stacking or only want one, but isn't
sensible for the general case.
> In the past I'd suggested using "security=" to determine both enabled
> and order: "security=tomoyo,smack" would mean stacked LSMs, with
> tomoyo going first.
>
> Currently I'm leaning towards "security=" to select ONLY the
> incompatible LSM, and per-LSM "enable" flags to determine stacking:
>
> tomoyo.enabled=1 security=smack
I know that I've seen a TOMOYO installation that explicitly sets
security=tomoyo, so I don't see that working.
> This doesn't explicitly address ordering, though. If we made param
> _position_ meaningful, then we could get ordering (i.e. above would
> mean "tomoyo first").
security.tomoyo=1 security.apparmor=2 security.landlock=5
could tell the boot that tomoyo comes 1st, apparmor 2nd, landlock 5th.
The registration code would be smart enough to deal with the fact that
there is no module 3rd or 4th.
> Note, ordering matters because call_int_hook() will _stop_ on a
> non-zero return value: potentially hiding events from later LSMs. Do
> we need to revisit this? I seem to remember if being a very dead
> horse, and we needed to quick-abort otherwise we ended up in
> nonsensical states.
I have done it both ways, and "bail on fail" is far simpler. If
SELinux returns -EACCES and SARA returns -EPERM, which do you tell
the user?
> The reason for the new approach is because I can't find a meaningful
> way to provide CONFIGs that make sense. We want to provide a few
> things:
>
> - is an LSM built into the kernel at all? (CONFIG_SECURITY_$lsm)
That's how it works today.
> - is an LSM enabled by default? (CONFIG_SECURITY_$lsm_ENABLED?)
That's how it's done in this patchset.
> - has an LSM been enable for this boot? $lsm.enabled=1 or security=$lsm,$lsm ?
Currently security=$lsm for one LSM.
> - what order should any stacking happen? Makefile? security=?
Makefile by default.
>
> And for the incompatible-major, do we stick with CONFIG_SECURITY_$lsm_DEFAULT ?
>
>
>
> Anyway, if the concern is with exposed behavior for distros, what do
> we want? i.e. what should be done for patch 10. Everything else looks
> good.
>
> -Kees
>
On Thu, Sep 13, 2018 at 4:32 PM, John Johansen
<[email protected]> wrote:
> On 09/13/2018 04:06 PM, Kees Cook wrote:
>> - what order should any stacking happen? Makefile? security=?
>>
> Preferably not. For the single LSM we have the ability to choose the default LSM, ideally we let the distro decide in the Kconfig and the user with security=...
I can't find a non-crazy way to do this in Kconfig. Right now, if I
threw out all the _DEFAULT stuff, I could do:
config SECURITY_SELINUX_ENABLED
bool "SELinux LSM enabled at boot time"
depends on SECURITY_SELINUX
depends on !SECURITY_APPARMOR_ENABLED && !SECURITY_SMACK_ENABLED
default SECURITY_SELINUX
config SECURITY_SMACK_ENABLED
bool "SMACK LSM enabled at boot time"
depends on SECURITY_SMACK
depends on !SECURITY_APPARMOR_ENABLED && !SECURITY_SELINUX_ENABLED
default SECURITY_SMACK
config SECURITY_APPARMOR_ENABLED
bool "AppArmor LSM enabled at boot time"
depends on SECURITY_APPARMOR
depends on !SECURITY_SMACK_ENABLED && !SECURITY_SELINUX_ENABLED
default SECURITY_APPARMOR
config SECURITY_TOMOYO_ENABLED
bool "TOMOYO LSM enabled at boot time"
depends on SECURITY_TOMOYO
default y if !SECURITY_SELINUX_ENABLED &&
!SECURITY_SMACK_ENABLED && !SECURITY_APPARMOR_ENABLED
config DEFAULT_SECURITY
string
default "selinux" if SECURITY_SELINUX_ENABLED
default "smack" if SECURITY_SMACK_ENABLED
default "apparmor" if SECURITY_APPARMOR_ENABLED
default "tomoyo" if SECURITY_TOMOYO_ENABLED
(As before CONFIG_DEFAULT_SECURITY basically means the effective
"security=" contents. Reminder than Kconfig default are "first match",
so tomoyo would only happen if all others are not enabled by default.)
But this doesn't provide a way for Kconfig to declare the ordering of
TOMOYO followed by SELinux. If we just declare ordering is a function
of the Makefile, then the above would work as expected. The
"conflicting major LSM" could be specified on "security=" and stacked
could be enabled with $lsm.enable=1 (or disabled).
So, before we can really make a decision, I think we have to decide:
should ordering be arbitrary for even this level of stacking?
-Kees
--
Kees Cook
Pixel Security
On Thu, Sep 13, 2018 at 4:51 PM, Casey Schaufler <[email protected]> wrote:
> On 9/13/2018 4:06 PM, Kees Cook wrote:
>> - what order should any stacking happen? Makefile? security=?
> Makefile by default.
Okay, if ordering is by Makefile and everyone dislikes my
$lsm.enabled=0/1 thing, then these mean the same thing:
security=selinux,tomoyo
security=tomoyo,selinux
i.e. order of security= is _ignored_ in favor of the Makefile ordering.
That seems dangerous to accept input that is "out of order", though,
if we ever DO want to make order definable.
-Kees
--
Kees Cook
Pixel Security
On Thu, Sep 13, 2018 at 4:51 PM, Casey Schaufler <[email protected]> wrote:
> On 9/13/2018 4:06 PM, Kees Cook wrote:
>> If security= is
>> specified, all other major LSMs are disabled (i.e. it is not possible
>> to switch between SELinux/AppArmor/SMACK without also disabling
>> TOMOYO).
>
> Correct.
If we assume patch 10 is the way forward, how could we go about fixing
this specific problem?
-Kees
--
Kees Cook
Pixel Security
On 9/13/2018 4:51 PM, Kees Cook wrote:
> On Thu, Sep 13, 2018 at 4:32 PM, John Johansen
> <[email protected]> wrote:
>> On 09/13/2018 04:06 PM, Kees Cook wrote:
>>> - what order should any stacking happen? Makefile? security=?
>>>
>> Preferably not. For the single LSM we have the ability to choose the default LSM, ideally we let the distro decide in the Kconfig and the user with security=...
> I can't find a non-crazy way to do this in Kconfig. Right now, if I
> threw out all the _DEFAULT stuff, I could do:
>
> config SECURITY_SELINUX_ENABLED
> bool "SELinux LSM enabled at boot time"
> depends on SECURITY_SELINUX
> depends on !SECURITY_APPARMOR_ENABLED && !SECURITY_SMACK_ENABLED
> default SECURITY_SELINUX
>
> config SECURITY_SMACK_ENABLED
> bool "SMACK LSM enabled at boot time"
> depends on SECURITY_SMACK
> depends on !SECURITY_APPARMOR_ENABLED && !SECURITY_SELINUX_ENABLED
> default SECURITY_SMACK
>
> config SECURITY_APPARMOR_ENABLED
> bool "AppArmor LSM enabled at boot time"
> depends on SECURITY_APPARMOR
> depends on !SECURITY_SMACK_ENABLED && !SECURITY_SELINUX_ENABLED
> default SECURITY_APPARMOR
>
> config SECURITY_TOMOYO_ENABLED
> bool "TOMOYO LSM enabled at boot time"
> depends on SECURITY_TOMOYO
> default y if !SECURITY_SELINUX_ENABLED &&
> !SECURITY_SMACK_ENABLED && !SECURITY_APPARMOR_ENABLED
>
> config DEFAULT_SECURITY
> string
> default "selinux" if SECURITY_SELINUX_ENABLED
> default "smack" if SECURITY_SMACK_ENABLED
> default "apparmor" if SECURITY_APPARMOR_ENABLED
> default "tomoyo" if SECURITY_TOMOYO_ENABLED
>
> (As before CONFIG_DEFAULT_SECURITY basically means the effective
> "security=" contents. Reminder than Kconfig default are "first match",
> so tomoyo would only happen if all others are not enabled by default.)
>
> But this doesn't provide a way for Kconfig to declare the ordering of
> TOMOYO followed by SELinux. If we just declare ordering is a function
> of the Makefile, then the above would work as expected. The
> "conflicting major LSM" could be specified on "security=" and stacked
> could be enabled with $lsm.enable=1 (or disabled).
>
> So, before we can really make a decision, I think we have to decide:
> should ordering be arbitrary for even this level of stacking?
Do we have a case where it matters? I know that I could write a
module that would have issues if one hook got called and another
didn't because because a precursor module hook failed. I don't
think that any of the existing modules have this problem.
On Thu, Sep 13, 2018 at 5:03 PM, Casey Schaufler <[email protected]> wrote:
> On 9/13/2018 4:51 PM, Kees Cook wrote:
>> So, before we can really make a decision, I think we have to decide:
>> should ordering be arbitrary for even this level of stacking?
>
> Do we have a case where it matters? I know that I could write a
> module that would have issues if one hook got called and another
> didn't because because a precursor module hook failed. I don't
> think that any of the existing modules have this problem.
FWIW, I prefer having explicit ordering that cannot be changed at
runtime. I'm just concerned about painting ourselves (further) into a
corner with security= suddenly gaining ordering semantics, but maybe I
can just ignore this and we can point and laugh at anyone who gets
burned by some future change to making it order-sensitive. :)
-Kees
--
Kees Cook
Pixel Security
On 9/13/2018 4:57 PM, Kees Cook wrote:
> On Thu, Sep 13, 2018 at 4:51 PM, Casey Schaufler <[email protected]> wrote:
>> On 9/13/2018 4:06 PM, Kees Cook wrote:
>>> - what order should any stacking happen? Makefile? security=?
>> Makefile by default.
> Okay, if ordering is by Makefile and everyone dislikes my
> $lsm.enabled=0/1 thing, then these mean the same thing:
>
> security=selinux,tomoyo
> security=tomoyo,selinux
>
> i.e. order of security= is _ignored_ in favor of the Makefile ordering.
No, I think that the two lines above should have a different
execution order. If we really need to specify multiple modules
at boot time that is what makes the most sense.
It's a matter of mechanics and probably another pass during the
init process, but it's doable. If we determine it's necessary for
this stage it is just work.
On Thu, Sep 13, 2018 at 5:08 PM, Casey Schaufler <[email protected]> wrote:
> On 9/13/2018 4:57 PM, Kees Cook wrote:
>> On Thu, Sep 13, 2018 at 4:51 PM, Casey Schaufler <[email protected]> wrote:
>>> On 9/13/2018 4:06 PM, Kees Cook wrote:
>>>> - what order should any stacking happen? Makefile? security=?
>>> Makefile by default.
>> Okay, if ordering is by Makefile and everyone dislikes my
>> $lsm.enabled=0/1 thing, then these mean the same thing:
>>
>> security=selinux,tomoyo
>> security=tomoyo,selinux
>>
>> i.e. order of security= is _ignored_ in favor of the Makefile ordering.
>
> No, I think that the two lines above should have a different
> execution order. If we really need to specify multiple modules
> at boot time that is what makes the most sense.
>
> It's a matter of mechanics and probably another pass during the
> init process, but it's doable. If we determine it's necessary for
> this stage it is just work.
We already have the minor LSMs that cannot change order. They aren't
part of security= parsing either. To enable/disable LoadPin, you do
"loadpin.enabled=1/0" separate from "security=".
Should "blob-sharing" LSMs be like major LSMs or minor LSMs?
If someone is booting with "security=selinux,tomoyo" and then SARA
lands upstream, does that person have to explicitly add "sara" to
their boot args, since they're doing a non-default list of LSMs?
(I actually prefer the answer being "yes" here, FWIW, I just want to
nail down the expectations.)
-Kees
--
Kees Cook
Pixel Security
On Thu, Sep 13, 2018 at 5:52 PM Kees Cook <[email protected]> wrote:
> On Thu, Sep 13, 2018 at 2:38 PM, Paul Moore <[email protected]> wrote:
> > The infrastructure bits aren't really my concern; in fact I *like*
> > that the infrastructure is always exercised, it makes
> > testing/debugging easier. I also like the ability to limit the
> > user/admin to one LSM at boot time to make support easier; my goal is
> > to allow a distro to build support for multiple LSMs without also
> > requiring that distro to support *stacked* LSMs
>
> I see your point, but as soon as SARA and Landlock appear, they'll have:
>
> depends on SECURITY_STACKING
>
> and then all distros will enable it and there will be no sensible
> runtime way to manage it. If, instead, we make it entirely runtime
> now, then a CONFIG can control the default state and we can provide
> guidance to how SARA and Landlock should expose their "enable"ness.
I question why SARA and LandLock require stacking. While some LSMs
may benefit from stacking, e.g. Yama, traditionally each LSM has been
able to stand on its own. I think this is a quality that should be
preserved.
> > (see my earlier
> > comments about the difficulty in determining the source of a failed
> > operation).
>
> Agreed. I would hope that audit could help for that case. *stare at blue sky*
*also staring at blue sky*
Audit can help, but it is independent of the LSMs and not a hard
requirement for all, and even when it is enabled the config might not
be suitable to provide enough information to be helpful in this case.
--
paul moore
http://www.paul-moore.com
On 9/13/2018 5:19 PM, Kees Cook wrote:
> On Thu, Sep 13, 2018 at 5:08 PM, Casey Schaufler <[email protected]> wrote:
>> On 9/13/2018 4:57 PM, Kees Cook wrote:
>>> On Thu, Sep 13, 2018 at 4:51 PM, Casey Schaufler <[email protected]> wrote:
>>>> On 9/13/2018 4:06 PM, Kees Cook wrote:
>>>>> - what order should any stacking happen? Makefile? security=?
>>>> Makefile by default.
>>> Okay, if ordering is by Makefile and everyone dislikes my
>>> $lsm.enabled=0/1 thing, then these mean the same thing:
>>>
>>> security=selinux,tomoyo
>>> security=tomoyo,selinux
>>>
>>> i.e. order of security= is _ignored_ in favor of the Makefile ordering.
>> No, I think that the two lines above should have a different
>> execution order. If we really need to specify multiple modules
>> at boot time that is what makes the most sense.
>>
>> It's a matter of mechanics and probably another pass during the
>> init process, but it's doable. If we determine it's necessary for
>> this stage it is just work.
> We already have the minor LSMs that cannot change order.
Are you saying that we don't have a mechanism to change
the order, or that they wouldn't work right in a different
order? Well, there's the capability module that has to be
first.
> They aren't
> part of security= parsing either.
True, but there's no reason now that we couldn't change that.
Except for capability. Hmm.
> To enable/disable LoadPin, you do
> "loadpin.enabled=1/0" separate from "security=".
SELinux and AppArmor have the same.
> Should "blob-sharing" LSMs be like major LSMs or minor LSMs?
I like the idea of changing the minor modules to do the full
registration process. That would make them all the same.
Except for capability. In any case, the "blob-sharing" LSMs
need to do the full registration process to account for their
blobs sizes, and that brings the "major" behavior along with it.
> If someone is booting with "security=selinux,tomoyo" and then SARA
> lands upstream, does that person have to explicitly add "sara" to
> their boot args, since they're doing a non-default list of LSMs?
Yes. security= is explicit.
> (I actually prefer the answer being "yes" here, FWIW, I just want to
> nail down the expectations.)
For now let's leave the minor (capability, yama, loadpin) as they are,
and require all new modules of any flavor to use full registration.
We could consider something like
security=$lsm # Stack with $lsm at priority 2 - Existing behavior
$lsm.stacked=N # Add $lsm to the stack at priority N. Delete if N == 0
It's OK to specify "selinux.stacked=2" and "sara.stacked=2". Which gets
called first is left up to the system to decide. Whatever the behavior is
gets documented. Capability will always be first and have priority 1.
It's OK to specify "smack.stacked=1".
The default stack is determined by CONFIG_SECURITY_$lsm_STACKED at
build time. CONFIG_SECURITY_$lsm_STACKED changes from a boolean to
an integer value to establish the default hook order.
/sys/kernel/security/lsm reports the modules in hook call order.
/sys/kernel/security/lsm-stack reports the list with the hook call priority
capability:1,yama:1,selinux:1,sara:5,landlack:17
If stacking is not configured $lsm.stacked=0 is treated as security=none.
For other values of N $lsm.stacked=N is treated as security=$lsm.
On Thu, 13 Sep 2018, Casey Schaufler wrote:
> On 9/13/2018 4:57 PM, Kees Cook wrote:
> > On Thu, Sep 13, 2018 at 4:51 PM, Casey Schaufler <[email protected]> wrote:
> >> On 9/13/2018 4:06 PM, Kees Cook wrote:
> >>> - what order should any stacking happen? Makefile? security=?
> >> Makefile by default.
> > Okay, if ordering is by Makefile and everyone dislikes my
> > $lsm.enabled=0/1 thing, then these mean the same thing:
> >
> > security=selinux,tomoyo
> > security=tomoyo,selinux
> >
> > i.e. order of security= is _ignored_ in favor of the Makefile ordering.
>
> No, I think that the two lines above should have a different
> execution order. If we really need to specify multiple modules
> at boot time that is what makes the most sense.
Agreed.
--
James Morris
<[email protected]>
On 09/14/2018 11:18 AM, James Morris wrote:
> On Thu, 13 Sep 2018, Casey Schaufler wrote:
>
>> On 9/13/2018 4:57 PM, Kees Cook wrote:
>>> On Thu, Sep 13, 2018 at 4:51 PM, Casey Schaufler <[email protected]> wrote:
>>>> On 9/13/2018 4:06 PM, Kees Cook wrote:
>>>>> - what order should any stacking happen? Makefile? security=?
>>>> Makefile by default.
>>> Okay, if ordering is by Makefile and everyone dislikes my
>>> $lsm.enabled=0/1 thing, then these mean the same thing:
>>>
>>> security=selinux,tomoyo
>>> security=tomoyo,selinux
>>>
>>> i.e. order of security= is _ignored_ in favor of the Makefile ordering.
>>
>> No, I think that the two lines above should have a different
>> execution order. If we really need to specify multiple modules
>> at boot time that is what makes the most sense.
>
> Agreed.
>
>
+1
partly because if order is ever going to be important, it needs to be
done now. It easy to loosen restrictions (ordering) in the future but
will be problematic to add it in.
On Fri, Sep 14, 2018 at 8:57 AM, Casey Schaufler <[email protected]> wrote:
> On 9/13/2018 5:19 PM, Kees Cook wrote:
>> We already have the minor LSMs that cannot change order.
>
> Are you saying that we don't have a mechanism to change
> the order, or that they wouldn't work right in a different
> order? Well, there's the capability module that has to be
> first.
I just meant their order is explicit in security.c.
>> They aren't
>> part of security= parsing either.
>
> True, but there's no reason now that we couldn't change that.
> Except for capability. Hmm.
Right, we have at least one that MUST be first (and must not be disabled).
>> Should "blob-sharing" LSMs be like major LSMs or minor LSMs?
>
> I like the idea of changing the minor modules to do the full
> registration process. That would make them all the same.
> Except for capability. In any case, the "blob-sharing" LSMs
> need to do the full registration process to account for their
> blobs sizes, and that brings the "major" behavior along with it.
I agree. I'm working on some clean-ups that I'll send out soon, though
I'm worried about some of the various boot-time options...
>> If someone is booting with "security=selinux,tomoyo" and then SARA
>> lands upstream, does that person have to explicitly add "sara" to
>> their boot args, since they're doing a non-default list of LSMs?
>
> Yes. security= is explicit.
>
>> (I actually prefer the answer being "yes" here, FWIW, I just want to
>> nail down the expectations.)
>
> For now let's leave the minor (capability, yama, loadpin) as they are,
> and require all new modules of any flavor to use full registration.
I would even be fine to convert yama and loadpin.
> We could consider something like
>
> security=$lsm # Stack with $lsm at priority 2 - Existing behavior
> $lsm.stacked=N # Add $lsm to the stack at priority N. Delete if N == 0
>
> It's OK to specify "selinux.stacked=2" and "sara.stacked=2". Which gets
> called first is left up to the system to decide. Whatever the behavior is
> gets documented. Capability will always be first and have priority 1.
> It's OK to specify "smack.stacked=1".
I'm less excited about this kind of stacking priority, but, whatever
the case, I think my cleanups may help with whatever we decide.
> The default stack is determined by CONFIG_SECURITY_$lsm_STACKED at
> build time. CONFIG_SECURITY_$lsm_STACKED changes from a boolean to
> an integer value to establish the default hook order.
>
> /sys/kernel/security/lsm reports the modules in hook call order.
Didn't I send a patch to new-line terminate this list? I always get
annoyed when I "cat" it. ;)
> /sys/kernel/security/lsm-stack reports the list with the hook call priority
>
> capability:1,yama:1,selinux:1,sara:5,landlack:17
>
> If stacking is not configured $lsm.stacked=0 is treated as security=none.
> For other values of N $lsm.stacked=N is treated as security=$lsm.
I feel like "order" is bad enough. Can we avoid adding "priority"?
-Kees
--
Kees Cook
Pixel Security
On 9/14/2018 1:05 PM, Kees Cook wrote:
> On Fri, Sep 14, 2018 at 8:57 AM, Casey Schaufler <[email protected]> wrote:
>> On 9/13/2018 5:19 PM, Kees Cook wrote:
>>> We already have the minor LSMs that cannot change order.
>> Are you saying that we don't have a mechanism to change
>> the order, or that they wouldn't work right in a different
>> order? Well, there's the capability module that has to be
>> first.
> I just meant their order is explicit in security.c.
>
>>> They aren't
>>> part of security= parsing either.
>> True, but there's no reason now that we couldn't change that.
>> Except for capability. Hmm.
> Right, we have at least one that MUST be first (and must not be disabled).
>
>>> Should "blob-sharing" LSMs be like major LSMs or minor LSMs?
>> I like the idea of changing the minor modules to do the full
>> registration process. That would make them all the same.
>> Except for capability. In any case, the "blob-sharing" LSMs
>> need to do the full registration process to account for their
>> blobs sizes, and that brings the "major" behavior along with it.
> I agree. I'm working on some clean-ups that I'll send out soon, though
> I'm worried about some of the various boot-time options...
Looking forward to seeing them.
>>> If someone is booting with "security=selinux,tomoyo" and then SARA
>>> lands upstream, does that person have to explicitly add "sara" to
>>> their boot args, since they're doing a non-default list of LSMs?
>> Yes. security= is explicit.
>>
>>> (I actually prefer the answer being "yes" here, FWIW, I just want to
>>> nail down the expectations.)
>> For now let's leave the minor (capability, yama, loadpin) as they are,
>> and require all new modules of any flavor to use full registration.
> I would even be fine to convert yama and loadpin.
That shouldn't be difficult.
>> We could consider something like
>>
>> security=$lsm # Stack with $lsm at priority 2 - Existing behavior
>> $lsm.stacked=N # Add $lsm to the stack at priority N. Delete if N == 0
>>
>> It's OK to specify "selinux.stacked=2" and "sara.stacked=2". Which gets
>> called first is left up to the system to decide. Whatever the behavior is
>> gets documented. Capability will always be first and have priority 1.
>> It's OK to specify "smack.stacked=1".
> I'm less excited about this kind of stacking priority, but, whatever
> the case, I think my cleanups may help with whatever we decide.
OK
>> The default stack is determined by CONFIG_SECURITY_$lsm_STACKED at
>> build time. CONFIG_SECURITY_$lsm_STACKED changes from a boolean to
>> an integer value to establish the default hook order.
>>
>> /sys/kernel/security/lsm reports the modules in hook call order.
> Didn't I send a patch to new-line terminate this list? I always get
> annoyed when I "cat" it. ;)
SELinux set the precedence on that one. Not my fault!
>> /sys/kernel/security/lsm-stack reports the list with the hook call priority
>>
>> capability:1,yama:1,selinux:1,sara:5,landlack:17
>>
>> If stacking is not configured $lsm.stacked=0 is treated as security=none.
>> For other values of N $lsm.stacked=N is treated as security=$lsm.
> I feel like "order" is bad enough. Can we avoid adding "priority"?
Sorry. I changed terminology (order and priority) halfway through the
message. Yes, I like order better. We should stick with that.
On Wed, 12 Sep 2018, James Morris <[email protected]> wrote:
> Adding the SARA and LandLock authors for review & comment.
>
> Salvatore & Mickaël: does this patchset meet your needs for merging to
> mainline?
Since the last time I submitted the patch to the ML, it grew a bit: now it needs
inode's blob stacking (which is already included for Landlock) and
kern_ipc_perm's
blob stacking.
The last one isn't implemented in this patchset, but it isn't
absolutely necessary.
I can merge a version of SARA that doesn't need it and than update it
when possible.
I can provide the same level of protection without using kern_ipc_perm
blob, I'm using it
just to minimize some potential side effects.
>
> On Tue, 11 Sep 2018, Casey Schaufler wrote:
>
> > LSM: Module stacking in support of S.A.R.A and Landlock
> >
> > v2: Reduce the patchset to what is required to support
> > the proposed S.A.R.A. and LandLock security modules
> >
> > The S.A.R.A. security module is intended to be used
> > in conjunction with other security modules. It requires
> > state to be maintained for the credential, which
> > in turn requires a mechanism for sharing the credential
> > security blob. The module also requires mechanism for
> > user space manipulation of the credential information,
> > hence an additional subdirectory in /proc/.../attr.
> >
> > The LandLock security module provides user configurable
> > policy in the secmark mechanism. It requires data in
> > the credential, file and inode security blobs. For this
> > to be used along side the existing "major" security
> > modules mechanism for sharing these blobs is provided.
> >
> > A side effect of providing sharing of the crendential
> > security blob is that the TOMOYO module can be used at
> > the same time as the other "major" modules.
> >
> > The mechanism for configuring which security modules are
> > enabled has to change when stacking in enabled. Any
> > module that uses just the security blobs that are shared
> > can be selected. Additionally, one other "major" module
> > can be selected.
> >
> > The security module stacking issues around networking and
> > IPC are not addressed here as they are beyond what is
> > required for TOMOYO, S.A.R.A and LandLock.
> >
> > git://github.com/cschaufler/lsm-stacking.git#stacking-4.19-rc2-saralock
> >
> > Signed-off-by: Casey Schaufler <[email protected]>
> > ---
> > Documentation/admin-guide/LSM/index.rst | 23 ++-
> > fs/proc/base.c | 64 ++++++-
> > fs/proc/internal.h | 1 +
> > include/linux/lsm_hooks.h | 20 ++-
> > include/linux/security.h | 15 +-
> > kernel/cred.c | 13 --
> > security/Kconfig | 92 ++++++++++
> > security/apparmor/domain.c | 2 +-
> > security/apparmor/include/cred.h | 24 ++-
> > security/apparmor/include/file.h | 9 +-
> > security/apparmor/include/lib.h | 4 +
> > security/apparmor/lsm.c | 53 ++++--
> > security/apparmor/task.c | 6 +-
> > security/security.c | 293 ++++++++++++++++++++++++++++++--
> > security/selinux/hooks.c | 215 ++++++++---------------
> > security/selinux/include/objsec.h | 37 +++-
> > security/selinux/selinuxfs.c | 5 +-
> > security/selinux/xfrm.c | 4 +-
> > security/smack/smack.h | 42 ++++-
> > security/smack/smack_access.c | 4 +-
> > security/smack/smack_lsm.c | 283 +++++++++++-------------------
> > security/smack/smackfs.c | 18 +-
> > security/tomoyo/common.h | 31 +++-
> > security/tomoyo/domain.c | 4 +-
> > security/tomoyo/securityfs_if.c | 15 +-
> > security/tomoyo/tomoyo.c | 57 +++++--
> > 26 files changed, 899 insertions(+), 435 deletions(-)
> >
>
> --
> James Morris
> <[email protected]>
On 9/16/2018 9:54 AM, Salvatore Mesoraca wrote:
> On Wed, 12 Sep 2018, James Morris <[email protected]> wrote:
>> Adding the SARA and LandLock authors for review & comment.
>>
>> Salvatore & Mickaël: does this patchset meet your needs for merging to
>> mainline?
> Since the last time I submitted the patch to the ML, it grew a bit: now it needs
> inode's blob stacking (which is already included for Landlock) and
> kern_ipc_perm's
> blob stacking.
> The last one isn't implemented in this patchset, but it isn't
> absolutely necessary.
> I can merge a version of SARA that doesn't need it and than update it
> when possible.
> I can provide the same level of protection without using kern_ipc_perm
> blob, I'm using it
> just to minimize some potential side effects.
Adding kern_ipc_perm is easy. As it looks like there will need to be
a few revisions I will add it to the next set.
>
>> On Tue, 11 Sep 2018, Casey Schaufler wrote:
>>
>>> LSM: Module stacking in support of S.A.R.A and Landlock
>>>
>>> v2: Reduce the patchset to what is required to support
>>> the proposed S.A.R.A. and LandLock security modules
>>>
>>> The S.A.R.A. security module is intended to be used
>>> in conjunction with other security modules. It requires
>>> state to be maintained for the credential, which
>>> in turn requires a mechanism for sharing the credential
>>> security blob. The module also requires mechanism for
>>> user space manipulation of the credential information,
>>> hence an additional subdirectory in /proc/.../attr.
>>>
>>> The LandLock security module provides user configurable
>>> policy in the secmark mechanism. It requires data in
>>> the credential, file and inode security blobs. For this
>>> to be used along side the existing "major" security
>>> modules mechanism for sharing these blobs is provided.
>>>
>>> A side effect of providing sharing of the crendential
>>> security blob is that the TOMOYO module can be used at
>>> the same time as the other "major" modules.
>>>
>>> The mechanism for configuring which security modules are
>>> enabled has to change when stacking in enabled. Any
>>> module that uses just the security blobs that are shared
>>> can be selected. Additionally, one other "major" module
>>> can be selected.
>>>
>>> The security module stacking issues around networking and
>>> IPC are not addressed here as they are beyond what is
>>> required for TOMOYO, S.A.R.A and LandLock.
>>>
>>> git://github.com/cschaufler/lsm-stacking.git#stacking-4.19-rc2-saralock
>>>
>>> Signed-off-by: Casey Schaufler <[email protected]>
>>> ---
>>> Documentation/admin-guide/LSM/index.rst | 23 ++-
>>> fs/proc/base.c | 64 ++++++-
>>> fs/proc/internal.h | 1 +
>>> include/linux/lsm_hooks.h | 20 ++-
>>> include/linux/security.h | 15 +-
>>> kernel/cred.c | 13 --
>>> security/Kconfig | 92 ++++++++++
>>> security/apparmor/domain.c | 2 +-
>>> security/apparmor/include/cred.h | 24 ++-
>>> security/apparmor/include/file.h | 9 +-
>>> security/apparmor/include/lib.h | 4 +
>>> security/apparmor/lsm.c | 53 ++++--
>>> security/apparmor/task.c | 6 +-
>>> security/security.c | 293 ++++++++++++++++++++++++++++++--
>>> security/selinux/hooks.c | 215 ++++++++---------------
>>> security/selinux/include/objsec.h | 37 +++-
>>> security/selinux/selinuxfs.c | 5 +-
>>> security/selinux/xfrm.c | 4 +-
>>> security/smack/smack.h | 42 ++++-
>>> security/smack/smack_access.c | 4 +-
>>> security/smack/smack_lsm.c | 283 +++++++++++-------------------
>>> security/smack/smackfs.c | 18 +-
>>> security/tomoyo/common.h | 31 +++-
>>> security/tomoyo/domain.c | 4 +-
>>> security/tomoyo/securityfs_if.c | 15 +-
>>> security/tomoyo/tomoyo.c | 57 +++++--
>>> 26 files changed, 899 insertions(+), 435 deletions(-)
>>>
>> --
>> James Morris
>> <[email protected]>
Casey Schaufler <[email protected]> wrote:
>
> On 9/16/2018 9:54 AM, Salvatore Mesoraca wrote:
> > On Wed, 12 Sep 2018, James Morris <[email protected]> wrote:
> >> Adding the SARA and LandLock authors for review & comment.
> >>
> >> Salvatore & Mickaël: does this patchset meet your needs for merging to
> >> mainline?
> > Since the last time I submitted the patch to the ML, it grew a bit: now it needs
> > inode's blob stacking (which is already included for Landlock) and
> > kern_ipc_perm's
> > blob stacking.
> > The last one isn't implemented in this patchset, but it isn't
> > absolutely necessary.
> > I can merge a version of SARA that doesn't need it and than update it
> > when possible.
> > I can provide the same level of protection without using kern_ipc_perm
> > blob, I'm using it
> > just to minimize some potential side effects.
>
> Adding kern_ipc_perm is easy. As it looks like there will need to be
> a few revisions I will add it to the next set.
Great! Thank you very much!
On 9/12/18 23:29, James Morris wrote:
> Adding the SARA and LandLock authors for review & comment.
>
> Salvatore & Mickaël: does this patchset meet your needs for merging to
> mainline?
It almost does! For now, I need the task_struct, cred, inode and file
security blobs. Only the task_struct one is missing.
FYI, the capitalization of Landlock is not consistent in all commit
messages. :)
Thanks,
Mickaël
>
>
>
> On Tue, 11 Sep 2018, Casey Schaufler wrote:
>
>> LSM: Module stacking in support of S.A.R.A and Landlock
>>
>> v2: Reduce the patchset to what is required to support
>> the proposed S.A.R.A. and LandLock security modules
>>
>> The S.A.R.A. security module is intended to be used
>> in conjunction with other security modules. It requires
>> state to be maintained for the credential, which
>> in turn requires a mechanism for sharing the credential
>> security blob. The module also requires mechanism for
>> user space manipulation of the credential information,
>> hence an additional subdirectory in /proc/.../attr.
>>
>> The LandLock security module provides user configurable
>> policy in the secmark mechanism. It requires data in
>> the credential, file and inode security blobs. For this
>> to be used along side the existing "major" security
>> modules mechanism for sharing these blobs is provided.
>>
>> A side effect of providing sharing of the crendential
>> security blob is that the TOMOYO module can be used at
>> the same time as the other "major" modules.
>>
>> The mechanism for configuring which security modules are
>> enabled has to change when stacking in enabled. Any
>> module that uses just the security blobs that are shared
>> can be selected. Additionally, one other "major" module
>> can be selected.
>>
>> The security module stacking issues around networking and
>> IPC are not addressed here as they are beyond what is
>> required for TOMOYO, S.A.R.A and LandLock.
>>
>> git://github.com/cschaufler/lsm-stacking.git#stacking-4.19-rc2-saralock
>>
>> Signed-off-by: Casey Schaufler <[email protected]>
>> ---
>> Documentation/admin-guide/LSM/index.rst | 23 ++-
>> fs/proc/base.c | 64 ++++++-
>> fs/proc/internal.h | 1 +
>> include/linux/lsm_hooks.h | 20 ++-
>> include/linux/security.h | 15 +-
>> kernel/cred.c | 13 --
>> security/Kconfig | 92 ++++++++++
>> security/apparmor/domain.c | 2 +-
>> security/apparmor/include/cred.h | 24 ++-
>> security/apparmor/include/file.h | 9 +-
>> security/apparmor/include/lib.h | 4 +
>> security/apparmor/lsm.c | 53 ++++--
>> security/apparmor/task.c | 6 +-
>> security/security.c | 293 ++++++++++++++++++++++++++++++--
>> security/selinux/hooks.c | 215 ++++++++---------------
>> security/selinux/include/objsec.h | 37 +++-
>> security/selinux/selinuxfs.c | 5 +-
>> security/selinux/xfrm.c | 4 +-
>> security/smack/smack.h | 42 ++++-
>> security/smack/smack_access.c | 4 +-
>> security/smack/smack_lsm.c | 283 +++++++++++-------------------
>> security/smack/smackfs.c | 18 +-
>> security/tomoyo/common.h | 31 +++-
>> security/tomoyo/domain.c | 4 +-
>> security/tomoyo/securityfs_if.c | 15 +-
>> security/tomoyo/tomoyo.c | 57 +++++--
>> 26 files changed, 899 insertions(+), 435 deletions(-)
>>
>
On 9/18/2018 12:44 AM, Mickaël Salaün wrote:
> On 9/12/18 23:29, James Morris wrote:
>> Adding the SARA and LandLock authors for review & comment.
>>
>> Salvatore & Mickaël: does this patchset meet your needs for merging to
>> mainline?
> It almost does! For now, I need the task_struct, cred, inode and file
> security blobs. Only the task_struct one is missing.
I will add the task blob as well as the ipc blob needed by
SARA for v3.
>
> FYI, the capitalization of Landlock is not consistent in all commit
> messages. :)
Sorry. I have been trying to get people to use Smack instead
of SMACK for years. I will strive to use Landlock consistently.