linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v4 0/3] LSM: module hierarchy in /proc/.../attr
@ 2016-06-23 21:07 Casey Schaufler
  2016-06-23 21:10 ` [PATCH v4 1/3] LSM: Add /sys/kernel/security/lsm Casey Schaufler
                   ` (2 more replies)
  0 siblings, 3 replies; 32+ messages in thread
From: Casey Schaufler @ 2016-06-23 21:07 UTC (permalink / raw)
  To: LSM, James Morris
  Cc: John Johansen, Stephen Smalley, Paul Moore, Tetsuo Handa,
	Kees Cook, Casey Schaufler, LKLM

Subject: [PATCH v4 0/3] LSM: module hierarchy in /proc/.../attr

Changes from v3:
	Use kasprintf instead of kzalloc() ... sprintf.

Create interfaces that make it possible to deal with process
attributes in the face of multiple "major" security modules.

Patch 1/3 adds /sys/kernel/security/lsm, which provides
a list of the active security modules on the system.

	$ cat /sys/kernel/security/lsm
	capability,yama,loadpin,smack

Patch 2/3 adds a subdirectory in /proc/.../attr for each
security module that exports process attribute data. This
allows a program in easily differentiate between the "current"
value for Smack and AppArmor.

	$ cat /proc/self/attr/smack/current
	System

	$ cat /proc/self/attr/apparmor/current
	unconfined

Patch 3/3 adds an interface that provides module identified
information that otherwise matches the "current" attr.
This allows a system with multiple modules to provide the
complete security "context" in one place. A (future) system
with both Smack and AppArmor might report:

	$ cat /proc/self/attr/context
	smack='System'apparmor='unconfined'

Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>

---
 Documentation/security/LSM.txt |  26 +++++--
 fs/proc/base.c                 |  95 ++++++++++++++++++++---
 fs/proc/internal.h             |   1 +
 include/linux/lsm_hooks.h      |  12 +--
 include/linux/security.h       |  15 ++--
 security/apparmor/lsm.c        |  37 +++++++--
 security/commoncap.c           |   3 +-
 security/inode.c               |  26 ++++++-
 security/loadpin/loadpin.c     |   2 +-
 security/security.c            | 169 ++++++++++++++++++++++++++++++++++++++++-
 security/selinux/hooks.c       |  24 +++++-
 security/smack/smack_lsm.c     |  23 +++---
 security/tomoyo/tomoyo.c       |   2 +-
 security/yama/yama_lsm.c       |   2 +-
 14 files changed, 382 insertions(+), 55 deletions(-)

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH v4 1/3] LSM: Add /sys/kernel/security/lsm
  2016-06-23 21:07 [PATCH v4 0/3] LSM: module hierarchy in /proc/.../attr Casey Schaufler
@ 2016-06-23 21:10 ` Casey Schaufler
  2016-06-24 18:56   ` Paul Moore
  2016-06-23 21:11 ` [PATCH v4 2/3] LSM: module hierarchy in /proc/.../attr Casey Schaufler
  2016-06-23 21:11 ` [PATCH v4 3/3] LSM: Add context interface for proc attrs Casey Schaufler
  2 siblings, 1 reply; 32+ messages in thread
From: Casey Schaufler @ 2016-06-23 21:10 UTC (permalink / raw)
  To: LSM, James Morris
  Cc: John Johansen, Stephen Smalley, Paul Moore, Tetsuo Handa,
	Kees Cook, LKLM

Subject: [PATCH v4 1/3] LSM: Add /sys/kernel/security/lsm

I got tired of having to find indirect ways to
determine what security modules are active on a system.
I have added /sys/kernel/security/lsm, which contains a
comma separated list of the active security modules. No
more groping around in /proc/filesystems, which won't
help if the module doesn't support its own filesystem.

Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>

---
 include/linux/lsm_hooks.h  | 12 ++++--------
 security/apparmor/lsm.c    |  3 ++-
 security/commoncap.c       |  3 ++-
 security/inode.c           | 26 ++++++++++++++++++++++++--
 security/loadpin/loadpin.c |  2 +-
 security/security.c        | 38 ++++++++++++++++++++++++++++++++++++++
 security/selinux/hooks.c   |  2 +-
 security/smack/smack_lsm.c |  2 +-
 security/tomoyo/tomoyo.c   |  2 +-
 security/yama/yama_lsm.c   |  2 +-
 10 files changed, 75 insertions(+), 17 deletions(-)

diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 7ae3976..0bfd441 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -1841,6 +1841,7 @@ struct security_hook_list {
 	struct list_head		list;
 	struct list_head		*head;
 	union security_list_options	hook;
+	char				*lsm;
 };
 
 /*
@@ -1853,15 +1854,10 @@ struct security_hook_list {
 	{ .head = &security_hook_heads.HEAD, .hook = { .HEAD = HOOK } }
 
 extern struct security_hook_heads security_hook_heads;
+extern char *lsm_names;
 
-static inline void security_add_hooks(struct security_hook_list *hooks,
-				      int count)
-{
-	int i;
-
-	for (i = 0; i < count; i++)
-		list_add_tail_rcu(&hooks[i].list, hooks[i].head);
-}
+extern void security_add_hooks(struct security_hook_list *hooks, int count,
+				char *lsm);
 
 #ifdef CONFIG_SECURITY_SELINUX_DISABLE
 /*
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 2660fbc..fb0fb03 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -880,7 +880,8 @@ static int __init apparmor_init(void)
 		aa_free_root_ns();
 		goto alloc_out;
 	}
-	security_add_hooks(apparmor_hooks, ARRAY_SIZE(apparmor_hooks));
+	security_add_hooks(apparmor_hooks, ARRAY_SIZE(apparmor_hooks),
+				"apparmor");
 
 	/* Report that AppArmor successfully initialized */
 	apparmor_initialized = 1;
diff --git a/security/commoncap.c b/security/commoncap.c
index e7fadde..d101734 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -1090,7 +1090,8 @@ struct security_hook_list capability_hooks[] = {
 
 void __init capability_add_hooks(void)
 {
-	security_add_hooks(capability_hooks, ARRAY_SIZE(capability_hooks));
+	security_add_hooks(capability_hooks, ARRAY_SIZE(capability_hooks),
+				"capability");
 }
 
 #endif /* CONFIG_SECURITY */
diff --git a/security/inode.c b/security/inode.c
index 28414b0..235ad82 100644
--- a/security/inode.c
+++ b/security/inode.c
@@ -20,6 +20,7 @@
 #include <linux/init.h>
 #include <linux/namei.h>
 #include <linux/security.h>
+#include <linux/lsm_hooks.h>
 #include <linux/magic.h>
 
 static struct vfsmount *mount;
@@ -208,6 +209,21 @@ void securityfs_remove(struct dentry *dentry)
 }
 EXPORT_SYMBOL_GPL(securityfs_remove);
 
+#ifdef CONFIG_SECURITY
+static struct dentry *lsm_dentry;
+static ssize_t lsm_read(struct file *filp, char __user *buf, size_t count,
+			loff_t *ppos)
+{
+	return simple_read_from_buffer(buf, count, ppos, lsm_names,
+		strlen(lsm_names));
+}
+
+static const struct file_operations lsm_ops = {
+	.read = lsm_read,
+	.llseek = generic_file_llseek,
+};
+#endif
+
 static int __init securityfs_init(void)
 {
 	int retval;
@@ -217,9 +233,15 @@ static int __init securityfs_init(void)
 		return retval;
 
 	retval = register_filesystem(&fs_type);
-	if (retval)
+	if (retval) {
 		sysfs_remove_mount_point(kernel_kobj, "security");
-	return retval;
+		return retval;
+	}
+#ifdef CONFIG_SECURITY
+	lsm_dentry = securityfs_create_file("lsm", S_IRUGO, NULL, NULL,
+						&lsm_ops);
+#endif
+	return 0;
 }
 
 core_initcall(securityfs_init);
diff --git a/security/loadpin/loadpin.c b/security/loadpin/loadpin.c
index 89a46f1..1d82eae 100644
--- a/security/loadpin/loadpin.c
+++ b/security/loadpin/loadpin.c
@@ -182,7 +182,7 @@ static struct security_hook_list loadpin_hooks[] = {
 void __init loadpin_add_hooks(void)
 {
 	pr_info("ready to pin (currently %sabled)", enabled ? "en" : "dis");
-	security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks));
+	security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks), "loadpin");
 }
 
 /* Should not be mutable after boot, so not listed in sysfs (perm == 0). */
diff --git a/security/security.c b/security/security.c
index c4bb47d..1093f73 100644
--- a/security/security.c
+++ b/security/security.c
@@ -32,6 +32,7 @@
 /* Maximum number of letters for an LSM name string */
 #define SECURITY_NAME_MAX	10
 
+char *lsm_names;
 /* Boot-time LSM user choice */
 static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =
 	CONFIG_DEFAULT_SECURITY;
@@ -78,6 +79,22 @@ static int __init choose_lsm(char *str)
 }
 __setup("security=", choose_lsm);
 
+static int lsm_append(char *new, char **result)
+{
+	char *cp;
+
+	if (*result == NULL) {
+		*result = kstrdup(new, GFP_KERNEL);
+	} else {
+		cp = kasprintf(GFP_KERNEL, "%s,%s", *result, new);
+		if (cp == NULL)
+			return -ENOMEM;
+		kfree(*result);
+		*result = cp;
+	}
+	return 0;
+}
+
 /**
  * security_module_enable - Load given security module on boot ?
  * @module: the name of the module
@@ -97,6 +114,27 @@ int __init security_module_enable(const char *module)
 	return !strcmp(module, chosen_lsm);
 }
 
+/**
+ * security_add_hooks - Add a modules hooks to the hook lists.
+ * @hooks: the hooks to add
+ * @count: the number of hooks to add
+ * @lsm: the name of the security module
+ *
+ * Each LSM has to register its hooks with the infrastructure.
+ */
+void __init security_add_hooks(struct security_hook_list *hooks, int count,
+				char *lsm)
+{
+	int i;
+
+	for (i = 0; i < count; i++) {
+		hooks[i].lsm = lsm;
+		list_add_tail_rcu(&hooks[i].list, hooks[i].head);
+	}
+	if (lsm_append(lsm, &lsm_names) < 0)
+		panic("%s - Cannot get early memory.\n", __func__);
+}
+
 /*
  * Hook list operation macros.
  *
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index a86d537..ed3a757 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -6233,7 +6233,7 @@ static __init int selinux_init(void)
 					    0, SLAB_PANIC, NULL);
 	avc_init();
 
-	security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks));
+	security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks), "selinux");
 
 	if (avc_add_callback(selinux_netcache_avc_callback, AVC_CALLBACK_RESET))
 		panic("SELinux: Unable to register AVC netcache callback\n");
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 6777295..3577009 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -4801,7 +4801,7 @@ static __init int smack_init(void)
 	/*
 	 * Register with LSM
 	 */
-	security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks));
+	security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks), "smack");
 
 	return 0;
 }
diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
index 75c9987..edc52d6 100644
--- a/security/tomoyo/tomoyo.c
+++ b/security/tomoyo/tomoyo.c
@@ -542,7 +542,7 @@ static int __init tomoyo_init(void)
 	if (!security_module_enable("tomoyo"))
 		return 0;
 	/* register ourselves with the security framework */
-	security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks));
+	security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks), "tomoyo");
 	printk(KERN_INFO "TOMOYO Linux initialized\n");
 	cred->security = &tomoyo_kernel_domain;
 	tomoyo_mm_init();
diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c
index 0309f21..f8ee60e 100644
--- a/security/yama/yama_lsm.c
+++ b/security/yama/yama_lsm.c
@@ -471,6 +471,6 @@ static inline void yama_init_sysctl(void) { }
 void __init yama_add_hooks(void)
 {
 	pr_info("Yama: becoming mindful.\n");
-	security_add_hooks(yama_hooks, ARRAY_SIZE(yama_hooks));
+	security_add_hooks(yama_hooks, ARRAY_SIZE(yama_hooks), "yama");
 	yama_init_sysctl();
 }

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v4 2/3] LSM: module hierarchy in /proc/.../attr
  2016-06-23 21:07 [PATCH v4 0/3] LSM: module hierarchy in /proc/.../attr Casey Schaufler
  2016-06-23 21:10 ` [PATCH v4 1/3] LSM: Add /sys/kernel/security/lsm Casey Schaufler
@ 2016-06-23 21:11 ` Casey Schaufler
  2016-06-24 19:11   ` Paul Moore
  2016-06-23 21:11 ` [PATCH v4 3/3] LSM: Add context interface for proc attrs Casey Schaufler
  2 siblings, 1 reply; 32+ messages in thread
From: Casey Schaufler @ 2016-06-23 21:11 UTC (permalink / raw)
  To: LSM, James Morris
  Cc: John Johansen, Stephen Smalley, Paul Moore, Tetsuo Handa,
	Kees Cook, LKLM

Subject: [PATCH v4 2/3] LSM: module hierarchy in /proc/.../attr

Back in 2007 I made what turned out to be a rather serious
mistake in the implementation of the Smack security module.
The SELinux module used an interface in /proc to manipulate
the security context on processes. Rather than use a similar
interface, I used the same interface. The AppArmor team did
likewise. Now /proc/.../attr/current will tell you the
security "context" of the process, but it will be different
depending on the security module you're using. That hasn't
been a problem to date, as you can only have one module
that supports process attributes at a time. We are coming
up on a change to that, where multiple modules with process
attributes can be supported. (Not included here)
 
This patch provides a subdirectory in /proc/.../attr for
each of the security modules that use the LSM hooks
getprocattr() and setprocattr(). Each of the interfaces
used by a module are presented in the subdirectory. The
old interfaces remain and work the same as before.
User space code can begin migrating to the subdirectory
interfaces in anticipation of the time when what comes
from /proc/self/attr/current might not be what a runtime
wants.

The original implementation is by Kees Cook. The code
has been changed a bit to reflect changes in the direction
of the multiple concurrent module work, to be independent
of it, and to bring it up to date with the current tree.

Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
        
---
 Documentation/security/LSM.txt | 26 +++++++++---
 fs/proc/base.c                 | 91 +++++++++++++++++++++++++++++++++++++-----
 fs/proc/internal.h             |  1 +
 include/linux/security.h       | 15 ++++---
 security/security.c            | 31 ++++++++++++--
 5 files changed, 140 insertions(+), 24 deletions(-)

diff --git a/Documentation/security/LSM.txt b/Documentation/security/LSM.txt
index 3db7e67..125c489 100644
--- a/Documentation/security/LSM.txt
+++ b/Documentation/security/LSM.txt
@@ -16,11 +16,25 @@ MAC extensions, other extensions can be built using the LSM to provide
 specific changes to system operation when these tweaks are not available
 in the core functionality of Linux itself.
 
-Without a specific LSM built into the kernel, the default LSM will be the
-Linux capabilities system. Most LSMs choose to extend the capabilities
-system, building their checks on top of the defined capability hooks.
-For more details on capabilities, see capabilities(7) in the Linux
-man-pages project.
+The Linux capabilities modules will always be included. For more details
+on capabilities, see capabilities(7) in the Linux man-pages project.
+This may be followed by any number of "minor" modules and at most one
+"major" module.
+
+A list of the active security modules can be found by reading
+/sys/kernel/security/lsm. This is a comma separated list, and
+will always include the capability module. The list reflects the
+order in which checks are made. The capability module will always
+be first, followed by any "minor" modules (e.g. Yama) and then
+the one "major" module (e.g. SELinux) if there is one configured.
+
+Process attributes associated with "major" security modules should
+be accessed and maintained using the special files in the module
+specific subdirectories in /proc/.../attr. The attributes related
+to Smack would be found in /proc/.../attr/smack while the attributes
+for SELinux would be in /proc/.../attr/selinux. Using the files
+found directly in /proc/.../attr (e.g. current) should be avoided.
+These files remain as legacy interfaces.
 
 Based on https://lkml.org/lkml/2007/10/26/215,
 a new LSM is accepted into the kernel when its intent (a description of
@@ -31,4 +45,4 @@ that end users and distros can make a more informed decision about which
 LSMs suit their requirements.
 
 For extensive documentation on the available LSM hook interfaces, please
-see include/linux/security.h.
+see include/linux/lsm_hooks.h.
diff --git a/fs/proc/base.c b/fs/proc/base.c
index a11eb71..182bc28 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -131,9 +131,13 @@ struct pid_entry {
 #define REG(NAME, MODE, fops)				\
 	NOD(NAME, (S_IFREG|(MODE)), NULL, &fops, {})
 #define ONE(NAME, MODE, show)				\
-	NOD(NAME, (S_IFREG|(MODE)), 			\
+	NOD(NAME, (S_IFREG|(MODE)),			\
 		NULL, &proc_single_file_operations,	\
 		{ .proc_show = show } )
+#define ATTR(LSM, NAME, MODE)				\
+	NOD(NAME, (S_IFREG|(MODE)),			\
+		NULL, &proc_pid_attr_operations,	\
+		{ .lsm = LSM })
 
 /*
  * Count the number of hardlinks for the pid_entry table, excluding the .
@@ -2433,7 +2437,7 @@ static ssize_t proc_pid_attr_read(struct file * file, char __user * buf,
 	if (!task)
 		return -ESRCH;
 
-	length = security_getprocattr(task,
+	length = security_getprocattr(task, PROC_I(inode)->op.lsm,
 				      (char*)file->f_path.dentry->d_name.name,
 				      &p);
 	put_task_struct(task);
@@ -2473,7 +2477,7 @@ static ssize_t proc_pid_attr_write(struct file * file, const char __user * buf,
 	if (length < 0)
 		goto out_free;
 
-	length = security_setprocattr(task,
+	length = security_setprocattr(task, PROC_I(inode)->op.lsm,
 				      (char*)file->f_path.dentry->d_name.name,
 				      page, count);
 	mutex_unlock(&task->signal->cred_guard_mutex);
@@ -2491,13 +2495,82 @@ static const struct file_operations proc_pid_attr_operations = {
 	.llseek		= generic_file_llseek,
 };
 
+#define LSM_DIR_OPS(LSM) \
+static int proc_##LSM##_attr_dir_iterate(struct file *filp, \
+			     struct dir_context *ctx) \
+{ \
+	return proc_pident_readdir(filp, ctx, \
+				   LSM##_attr_dir_stuff, \
+				   ARRAY_SIZE(LSM##_attr_dir_stuff)); \
+} \
+\
+static const struct file_operations proc_##LSM##_attr_dir_ops = { \
+	.read		= generic_read_dir, \
+	.iterate	= proc_##LSM##_attr_dir_iterate, \
+	.llseek		= default_llseek, \
+}; \
+\
+static struct dentry *proc_##LSM##_attr_dir_lookup(struct inode *dir, \
+				struct dentry *dentry, unsigned int flags) \
+{ \
+	return proc_pident_lookup(dir, dentry, \
+				  LSM##_attr_dir_stuff, \
+				  ARRAY_SIZE(LSM##_attr_dir_stuff)); \
+} \
+\
+static const struct inode_operations proc_##LSM##_attr_dir_inode_ops = { \
+	.lookup		= proc_##LSM##_attr_dir_lookup, \
+	.getattr	= pid_getattr, \
+	.setattr	= proc_setattr, \
+}
+
+#ifdef CONFIG_SECURITY_SELINUX
+static const struct pid_entry selinux_attr_dir_stuff[] = {
+	ATTR("selinux", "current",	S_IRUGO|S_IWUGO),
+	ATTR("selinux", "prev",		S_IRUGO),
+	ATTR("selinux", "exec",		S_IRUGO|S_IWUGO),
+	ATTR("selinux", "fscreate",	S_IRUGO|S_IWUGO),
+	ATTR("selinux", "keycreate",	S_IRUGO|S_IWUGO),
+	ATTR("selinux", "sockcreate",	S_IRUGO|S_IWUGO),
+};
+LSM_DIR_OPS(selinux);
+#endif
+
+#ifdef CONFIG_SECURITY_SMACK
+static const struct pid_entry smack_attr_dir_stuff[] = {
+	ATTR("smack", "current",	S_IRUGO|S_IWUGO),
+};
+LSM_DIR_OPS(smack);
+#endif
+
+#ifdef CONFIG_SECURITY_APPARMOR
+static const struct pid_entry apparmor_attr_dir_stuff[] = {
+	ATTR("apparmor", "current",	S_IRUGO|S_IWUGO),
+	ATTR("apparmor", "prev",	S_IRUGO),
+	ATTR("apparmor", "exec",	S_IRUGO|S_IWUGO),
+};
+LSM_DIR_OPS(apparmor);
+#endif
+
 static const struct pid_entry attr_dir_stuff[] = {
-	REG("current",    S_IRUGO|S_IWUGO, proc_pid_attr_operations),
-	REG("prev",       S_IRUGO,	   proc_pid_attr_operations),
-	REG("exec",       S_IRUGO|S_IWUGO, proc_pid_attr_operations),
-	REG("fscreate",   S_IRUGO|S_IWUGO, proc_pid_attr_operations),
-	REG("keycreate",  S_IRUGO|S_IWUGO, proc_pid_attr_operations),
-	REG("sockcreate", S_IRUGO|S_IWUGO, proc_pid_attr_operations),
+	ATTR(NULL, "current",		S_IRUGO|S_IWUGO),
+	ATTR(NULL, "prev",		S_IRUGO),
+	ATTR(NULL, "exec",		S_IRUGO|S_IWUGO),
+	ATTR(NULL, "fscreate",		S_IRUGO|S_IWUGO),
+	ATTR(NULL, "keycreate",		S_IRUGO|S_IWUGO),
+	ATTR(NULL, "sockcreate",	S_IRUGO|S_IWUGO),
+#ifdef CONFIG_SECURITY_SELINUX
+	DIR("selinux",			S_IRUGO|S_IXUGO,
+	    proc_selinux_attr_dir_inode_ops, proc_selinux_attr_dir_ops),
+#endif
+#ifdef CONFIG_SECURITY_SMACK
+	DIR("smack",			S_IRUGO|S_IXUGO,
+	    proc_smack_attr_dir_inode_ops, proc_smack_attr_dir_ops),
+#endif
+#ifdef CONFIG_SECURITY_APPARMOR
+	DIR("apparmor",			S_IRUGO|S_IXUGO,
+	    proc_apparmor_attr_dir_inode_ops, proc_apparmor_attr_dir_ops),
+#endif
 };
 
 static int proc_attr_dir_readdir(struct file *file, struct dir_context *ctx)
diff --git a/fs/proc/internal.h b/fs/proc/internal.h
index aa27810..b607cd5 100644
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -56,6 +56,7 @@ union proc_op {
 	int (*proc_show)(struct seq_file *m,
 		struct pid_namespace *ns, struct pid *pid,
 		struct task_struct *task);
+	const char *lsm;
 };
 
 struct proc_inode {
diff --git a/include/linux/security.h b/include/linux/security.h
index 14df373..383fcb0 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -355,8 +355,10 @@ int security_sem_semctl(struct sem_array *sma, int cmd);
 int security_sem_semop(struct sem_array *sma, struct sembuf *sops,
 			unsigned nsops, int alter);
 void security_d_instantiate(struct dentry *dentry, struct inode *inode);
-int security_getprocattr(struct task_struct *p, char *name, char **value);
-int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size);
+int security_getprocattr(struct task_struct *p, const char *lsm, char *name,
+				char **value);
+int security_setprocattr(struct task_struct *p, const char *lsm, char *name,
+				void *value, size_t size);
 int security_netlink_send(struct sock *sk, struct sk_buff *skb);
 int security_ismaclabel(const char *name);
 int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen);
@@ -1075,15 +1077,18 @@ static inline int security_sem_semop(struct sem_array *sma,
 	return 0;
 }
 
-static inline void security_d_instantiate(struct dentry *dentry, struct inode *inode)
+static inline void security_d_instantiate(struct dentry *dentry,
+						struct inode *inode)
 { }
 
-static inline int security_getprocattr(struct task_struct *p, char *name, char **value)
+static inline int security_getprocattr(struct task_struct *p, const char *lsm,
+					char *name, char **value)
 {
 	return -EINVAL;
 }
 
-static inline int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size)
+static inline int security_setprocattr(struct task_struct *p, const char *lsm,
+					char *name, void *value, size_t size)
 {
 	return -EINVAL;
 }
diff --git a/security/security.c b/security/security.c
index 1093f73..1e9cb55 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1182,14 +1182,37 @@ 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;
+	int rc = -EINVAL;
+
+
+	list_for_each_entry(hp, &security_hook_heads.getprocattr, list) {
+		if (lsm != NULL && strcmp(lsm, hp->lsm))
+			continue;
+		rc = hp->hook.getprocattr(p, name, value);
+		if (rc != -ENOENT)
+			return rc;
+	}
+	return -EINVAL;
 }
 
-int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size)
+int security_setprocattr(struct task_struct *p, const char *lsm, char *name,
+				void *value, size_t size)
 {
-	return call_int_hook(setprocattr, -EINVAL, p, name, value, size);
+	struct security_hook_list *hp;
+	int rc = -EINVAL;
+
+	list_for_each_entry(hp, &security_hook_heads.setprocattr, list) {
+		if (lsm != NULL && strcmp(lsm, hp->lsm))
+			continue;
+		rc = hp->hook.setprocattr(p, name, value, size);
+		if (rc != -ENOENT)
+			break;
+	}
+	return rc;
 }
 
 int security_netlink_send(struct sock *sk, struct sk_buff *skb)

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v4 3/3] LSM: Add context interface for proc attrs
  2016-06-23 21:07 [PATCH v4 0/3] LSM: module hierarchy in /proc/.../attr Casey Schaufler
  2016-06-23 21:10 ` [PATCH v4 1/3] LSM: Add /sys/kernel/security/lsm Casey Schaufler
  2016-06-23 21:11 ` [PATCH v4 2/3] LSM: module hierarchy in /proc/.../attr Casey Schaufler
@ 2016-06-23 21:11 ` Casey Schaufler
  2016-06-23 21:49   ` Kees Cook
  2016-06-24 19:15   ` [PATCH v4 3/3] LSM: Add " Paul Moore
  2 siblings, 2 replies; 32+ messages in thread
From: Casey Schaufler @ 2016-06-23 21:11 UTC (permalink / raw)
  To: LSM, James Morris
  Cc: John Johansen, Stephen Smalley, Paul Moore, Tetsuo Handa,
	Kees Cook, LKLM

Subject: [PATCH v4 3/3] LSM: Add context interface for proc attrs

The /proc/.../attr/current interface is used by all three
Linux security modules (SELinux, Smack and AppArmor) to
report and modify the process security attribute. This is
all fine when there is exactly one of these modules active
and the userspace code knows which it module it is.
It would require a major change to the "current" interface
to provide information about more than one set of process
security attributes. Instead, a "context" attribute is
added, which identifies the security module that the
information applies to. The format is:

        lsmname='context-value'
 
When multiple concurrent modules are supported the
/proc/.../attr/context interface will include the data
for all of the active modules.

        lsmname1='context-value1'lsmname2='context-value2'

The module specific subdirectories under attr contain context
entries that report the information for that specific module
in the same format.

Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>

---
 fs/proc/base.c             |   4 ++
 security/apparmor/lsm.c    |  34 +++++++++++++--
 security/security.c        | 100 +++++++++++++++++++++++++++++++++++++++++++++
 security/selinux/hooks.c   |  22 +++++++++-
 security/smack/smack_lsm.c |  21 ++++++----
 5 files changed, 167 insertions(+), 14 deletions(-)

diff --git a/fs/proc/base.c b/fs/proc/base.c
index 182bc28..df94f26 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -2532,6 +2532,7 @@ static const struct pid_entry selinux_attr_dir_stuff[] = {
 	ATTR("selinux", "fscreate",	S_IRUGO|S_IWUGO),
 	ATTR("selinux", "keycreate",	S_IRUGO|S_IWUGO),
 	ATTR("selinux", "sockcreate",	S_IRUGO|S_IWUGO),
+	ATTR("selinux", "context",	S_IRUGO|S_IWUGO),
 };
 LSM_DIR_OPS(selinux);
 #endif
@@ -2539,6 +2540,7 @@ LSM_DIR_OPS(selinux);
 #ifdef CONFIG_SECURITY_SMACK
 static const struct pid_entry smack_attr_dir_stuff[] = {
 	ATTR("smack", "current",	S_IRUGO|S_IWUGO),
+	ATTR("smack", "context",	S_IRUGO|S_IWUGO),
 };
 LSM_DIR_OPS(smack);
 #endif
@@ -2548,6 +2550,7 @@ static const struct pid_entry apparmor_attr_dir_stuff[] = {
 	ATTR("apparmor", "current",	S_IRUGO|S_IWUGO),
 	ATTR("apparmor", "prev",	S_IRUGO),
 	ATTR("apparmor", "exec",	S_IRUGO|S_IWUGO),
+	ATTR("apparmor", "context",	S_IRUGO|S_IWUGO),
 };
 LSM_DIR_OPS(apparmor);
 #endif
@@ -2559,6 +2562,7 @@ static const struct pid_entry attr_dir_stuff[] = {
 	ATTR(NULL, "fscreate",		S_IRUGO|S_IWUGO),
 	ATTR(NULL, "keycreate",		S_IRUGO|S_IWUGO),
 	ATTR(NULL, "sockcreate",	S_IRUGO|S_IWUGO),
+	ATTR(NULL, "context",		S_IRUGO|S_IWUGO),
 #ifdef CONFIG_SECURITY_SELINUX
 	DIR("selinux",			S_IRUGO|S_IXUGO,
 	    proc_selinux_attr_dir_inode_ops, proc_selinux_attr_dir_ops),
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index fb0fb03..3790a7d 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -479,6 +479,8 @@ static int apparmor_getprocattr(struct task_struct *task, char *name,
 
 	if (strcmp(name, "current") == 0)
 		profile = aa_get_newest_profile(cxt->profile);
+	else if (strcmp(name, "context") == 0)
+		profile = aa_get_newest_profile(cxt->profile);
 	else if (strcmp(name, "prev") == 0  && cxt->previous)
 		profile = aa_get_newest_profile(cxt->previous);
 	else if (strcmp(name, "exec") == 0 && cxt->onexec)
@@ -486,8 +488,29 @@ static int apparmor_getprocattr(struct task_struct *task, char *name,
 	else
 		error = -EINVAL;
 
-	if (profile)
-		error = aa_getprocattr(profile, value);
+	if (profile) {
+		if (strcmp(name, "context") == 0) {
+			char *vp;
+			char *np;
+
+			error = aa_getprocattr(profile, &vp);
+			if (error > 0) {
+				error += 12;
+				*value = kzalloc(error, GFP_KERNEL);
+				if (*value == NULL)
+					error = -ENOMEM;
+				else {
+					sprintf(*value, "apparmor='%s'", vp);
+					np = strchr(*value, '\n');
+					if (np != NULL) {
+						np[0] = '\'';
+						np[1] = '\0';
+					}
+				}
+			}
+		} else
+			error = aa_getprocattr(profile, value);
+	}
 
 	aa_put_profile(profile);
 	put_cred(cred);
@@ -530,7 +553,7 @@ static int apparmor_setprocattr(struct task_struct *task, char *name,
 		return -EINVAL;
 
 	arg_size = size - (args - (char *) value);
-	if (strcmp(name, "current") == 0) {
+	if (strcmp(name, "current") == 0 || strcmp(name, "context") == 0) {
 		if (strcmp(command, "changehat") == 0) {
 			error = aa_setprocattr_changehat(args, arg_size,
 							 !AA_DO_TEST);
@@ -552,7 +575,10 @@ static int apparmor_setprocattr(struct task_struct *task, char *name,
 		else
 			goto fail;
 	} else
-		/* only support the "current" and "exec" process attributes */
+		/*
+		 * only support the "current", context and "exec"
+		 * process attributes
+		 */
 		return -EINVAL;
 
 	if (!error)
diff --git a/security/security.c b/security/security.c
index 1e9cb55..fec70b4 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1186,8 +1186,47 @@ int security_getprocattr(struct task_struct *p, const char *lsm, char *name,
 				char **value)
 {
 	struct security_hook_list *hp;
+	char *vp;
+	char *cp = NULL;
 	int rc = -EINVAL;
+	int trc;
 
+	/*
+	 * "context" requires work here in addition to what
+	 * the modules provide.
+	 */
+	if (strcmp(name, "context") == 0) {
+		*value = NULL;
+		list_for_each_entry(hp,
+				&security_hook_heads.getprocattr, list) {
+			if (lsm != NULL && strcmp(lsm, hp->lsm))
+				continue;
+			trc = hp->hook.getprocattr(p, "context", &vp);
+			if (trc == -ENOENT)
+				continue;
+			if (trc <= 0) {
+				kfree(*value);
+				return trc;
+			}
+			rc = trc;
+			if (*value == NULL) {
+				*value = vp;
+			} else {
+				cp = kasprintf(GFP_KERNEL, "%s%s", *value, vp);
+				if (cp == NULL) {
+					kfree(*value);
+					kfree(vp);
+					return -ENOMEM;
+				}
+				kfree(*value);
+				kfree(vp);
+				*value = cp;
+			}
+		}
+		if (rc > 0)
+			return strlen(*value);
+		return rc;
+	}
 
 	list_for_each_entry(hp, &security_hook_heads.getprocattr, list) {
 		if (lsm != NULL && strcmp(lsm, hp->lsm))
@@ -1204,7 +1243,68 @@ int security_setprocattr(struct task_struct *p, const char *lsm, char *name,
 {
 	struct security_hook_list *hp;
 	int rc = -EINVAL;
+	char *local;
+	char *cp;
+	int slen;
+	int failed = 0;
 
+	/*
+	 * If lsm is NULL look at all the modules to find one
+	 * that processes name. If lsm is not NULL only look at
+	 * that module.
+	 *
+	 * "context" is handled directly here.
+	 */
+	if (strcmp(name, "context") == 0) {
+		/*
+		 * First verify that the input is acceptable.
+		 * lsm1='v1'lsm2='v2'lsm3='v3'
+		 *
+		 * A note on the use of strncmp() below.
+		 * The check is for the substring at the beginning of cp.
+		 * The kzalloc of size + 1 ensures a terminated string.
+		 */
+		local = kzalloc(size + 1, GFP_KERNEL);
+		memcpy(local, value, size);
+		cp = local;
+		list_for_each_entry(hp, &security_hook_heads.setprocattr,
+					list) {
+			if (lsm != NULL && strcmp(lsm, hp->lsm))
+				continue;
+			slen = strlen(hp->lsm);
+			if (strncmp(cp, hp->lsm, slen))
+				goto free_out;
+			cp += slen;
+			if (cp[0] != '=' || cp[1] != '\'' || cp[2] == '\'')
+				goto free_out;
+			for (cp += 2; cp[0] != '\''; cp++)
+				if (cp[0] == '\0')
+					goto free_out;
+			cp++;
+		}
+		cp = local;
+		list_for_each_entry(hp, &security_hook_heads.setprocattr,
+					list) {
+			if (lsm != NULL && strcmp(lsm, hp->lsm))
+				continue;
+			cp += strlen(hp->lsm) + 2;
+			for (slen = 0; cp[slen] != '\''; slen++)
+				;
+			cp[slen] = '\0';
+
+			rc = hp->hook.setprocattr(p, "context", cp, slen);
+			if (rc < 0)
+				failed = rc;
+			cp += slen + 1;
+		}
+		if (failed != 0)
+			rc = failed;
+		else
+			rc = size;
+free_out:
+		kfree(local);
+		return rc;
+	}
 	list_for_each_entry(hp, &security_hook_heads.setprocattr, list) {
 		if (lsm != NULL && strcmp(lsm, hp->lsm))
 			continue;
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index ed3a757..3a21c2b 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -5711,6 +5711,8 @@ static int selinux_getprocattr(struct task_struct *p,
 
 	if (!strcmp(name, "current"))
 		sid = __tsec->sid;
+	else if (!strcmp(name, "context"))
+		sid = __tsec->sid;
 	else if (!strcmp(name, "prev"))
 		sid = __tsec->osid;
 	else if (!strcmp(name, "exec"))
@@ -5728,7 +5730,21 @@ static int selinux_getprocattr(struct task_struct *p,
 	if (!sid)
 		return 0;
 
-	error = security_sid_to_context(sid, value, &len);
+	if (strcmp(name, "context")) {
+		error = security_sid_to_context(sid, value, &len);
+	} else {
+		char *vp;
+
+		error = security_sid_to_context(sid, &vp, &len);
+		if (!error) {
+			*value = kzalloc(len + 10, GFP_KERNEL);
+			if (*value == NULL)
+				error = -ENOMEM;
+			else
+				sprintf(*value, "selinux='%s'", vp);
+		}
+	}
+
 	if (error)
 		return error;
 	return len;
@@ -5768,6 +5784,8 @@ static int selinux_setprocattr(struct task_struct *p,
 		error = current_has_perm(p, PROCESS__SETSOCKCREATE);
 	else if (!strcmp(name, "current"))
 		error = current_has_perm(p, PROCESS__SETCURRENT);
+	else if (!strcmp(name, "context"))
+		error = current_has_perm(p, PROCESS__SETCURRENT);
 	else
 		error = -EINVAL;
 	if (error)
@@ -5827,7 +5845,7 @@ static int selinux_setprocattr(struct task_struct *p,
 		tsec->keycreate_sid = sid;
 	} else if (!strcmp(name, "sockcreate")) {
 		tsec->sockcreate_sid = sid;
-	} else if (!strcmp(name, "current")) {
+	} else if (!strcmp(name, "current") || !strcmp(name, "context")) {
 		error = -EINVAL;
 		if (sid == 0)
 			goto abort_change;
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 3577009..d2d8624 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -3576,16 +3576,21 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value)
 	char *cp;
 	int slen;
 
-	if (strcmp(name, "current") != 0)
+	if (strcmp(name, "current") == 0) {
+		cp = kstrdup(skp->smk_known, GFP_KERNEL);
+		if (cp == NULL)
+			return -ENOMEM;
+	} else if (strcmp(name, "context") == 0) {
+		slen = strlen(skp->smk_known) + 9;
+		cp = kzalloc(slen, GFP_KERNEL);
+		if (cp == NULL)
+			return -ENOMEM;
+		sprintf(cp, "smack='%s'", skp->smk_known);
+	} else
 		return -EINVAL;
 
-	cp = kstrdup(skp->smk_known, GFP_KERNEL);
-	if (cp == NULL)
-		return -ENOMEM;
-
-	slen = strlen(cp);
 	*value = cp;
-	return slen;
+	return strlen(cp);
 }
 
 /**
@@ -3622,7 +3627,7 @@ static int smack_setprocattr(struct task_struct *p, char *name,
 	if (value == NULL || size == 0 || size >= SMK_LONGLABEL)
 		return -EINVAL;
 
-	if (strcmp(name, "current") != 0)
+	if (strcmp(name, "current") != 0 && strcmp(name, "context") != 0)
 		return -EINVAL;
 
 	skp = smk_import_entry(value, size);

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* Re: [PATCH v4 3/3] LSM: Add context interface for proc attrs
  2016-06-23 21:11 ` [PATCH v4 3/3] LSM: Add context interface for proc attrs Casey Schaufler
@ 2016-06-23 21:49   ` Kees Cook
  2016-06-23 22:10     ` Casey Schaufler
  2016-06-24 16:38     ` [PATCH v4 4/3] LSM: Improve " Casey Schaufler
  2016-06-24 19:15   ` [PATCH v4 3/3] LSM: Add " Paul Moore
  1 sibling, 2 replies; 32+ messages in thread
From: Kees Cook @ 2016-06-23 21:49 UTC (permalink / raw)
  To: Casey Schaufler
  Cc: LSM, James Morris, John Johansen, Stephen Smalley, Paul Moore,
	Tetsuo Handa, LKLM

On Thu, Jun 23, 2016 at 2:11 PM, Casey Schaufler <casey@schaufler-ca.com> wrote:
> Subject: [PATCH v4 3/3] LSM: Add context interface for proc attrs
>
> The /proc/.../attr/current interface is used by all three
> Linux security modules (SELinux, Smack and AppArmor) to
> report and modify the process security attribute. This is
> all fine when there is exactly one of these modules active
> and the userspace code knows which it module it is.
> It would require a major change to the "current" interface
> to provide information about more than one set of process
> security attributes. Instead, a "context" attribute is
> added, which identifies the security module that the
> information applies to. The format is:
>
>         lsmname='context-value'
>
> When multiple concurrent modules are supported the
> /proc/.../attr/context interface will include the data
> for all of the active modules.
>
>         lsmname1='context-value1'lsmname2='context-value2'
>
> The module specific subdirectories under attr contain context
> entries that report the information for that specific module
> in the same format.
>
> Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
>
> ---
>  fs/proc/base.c             |   4 ++
>  security/apparmor/lsm.c    |  34 +++++++++++++--
>  security/security.c        | 100 +++++++++++++++++++++++++++++++++++++++++++++
>  security/selinux/hooks.c   |  22 +++++++++-
>  security/smack/smack_lsm.c |  21 ++++++----
>  5 files changed, 167 insertions(+), 14 deletions(-)
>
> diff --git a/fs/proc/base.c b/fs/proc/base.c
> index 182bc28..df94f26 100644
> --- a/fs/proc/base.c
> +++ b/fs/proc/base.c
> @@ -2532,6 +2532,7 @@ static const struct pid_entry selinux_attr_dir_stuff[] = {
>         ATTR("selinux", "fscreate",     S_IRUGO|S_IWUGO),
>         ATTR("selinux", "keycreate",    S_IRUGO|S_IWUGO),
>         ATTR("selinux", "sockcreate",   S_IRUGO|S_IWUGO),
> +       ATTR("selinux", "context",      S_IRUGO|S_IWUGO),
>  };
>  LSM_DIR_OPS(selinux);
>  #endif
> @@ -2539,6 +2540,7 @@ LSM_DIR_OPS(selinux);
>  #ifdef CONFIG_SECURITY_SMACK
>  static const struct pid_entry smack_attr_dir_stuff[] = {
>         ATTR("smack", "current",        S_IRUGO|S_IWUGO),
> +       ATTR("smack", "context",        S_IRUGO|S_IWUGO),
>  };
>  LSM_DIR_OPS(smack);
>  #endif
> @@ -2548,6 +2550,7 @@ static const struct pid_entry apparmor_attr_dir_stuff[] = {
>         ATTR("apparmor", "current",     S_IRUGO|S_IWUGO),
>         ATTR("apparmor", "prev",        S_IRUGO),
>         ATTR("apparmor", "exec",        S_IRUGO|S_IWUGO),
> +       ATTR("apparmor", "context",     S_IRUGO|S_IWUGO),
>  };
>  LSM_DIR_OPS(apparmor);
>  #endif
> @@ -2559,6 +2562,7 @@ static const struct pid_entry attr_dir_stuff[] = {
>         ATTR(NULL, "fscreate",          S_IRUGO|S_IWUGO),
>         ATTR(NULL, "keycreate",         S_IRUGO|S_IWUGO),
>         ATTR(NULL, "sockcreate",        S_IRUGO|S_IWUGO),
> +       ATTR(NULL, "context",           S_IRUGO|S_IWUGO),
>  #ifdef CONFIG_SECURITY_SELINUX
>         DIR("selinux",                  S_IRUGO|S_IXUGO,
>             proc_selinux_attr_dir_inode_ops, proc_selinux_attr_dir_ops),
> diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
> index fb0fb03..3790a7d 100644
> --- a/security/apparmor/lsm.c
> +++ b/security/apparmor/lsm.c
> @@ -479,6 +479,8 @@ static int apparmor_getprocattr(struct task_struct *task, char *name,
>
>         if (strcmp(name, "current") == 0)
>                 profile = aa_get_newest_profile(cxt->profile);
> +       else if (strcmp(name, "context") == 0)
> +               profile = aa_get_newest_profile(cxt->profile);
>         else if (strcmp(name, "prev") == 0  && cxt->previous)
>                 profile = aa_get_newest_profile(cxt->previous);
>         else if (strcmp(name, "exec") == 0 && cxt->onexec)
> @@ -486,8 +488,29 @@ static int apparmor_getprocattr(struct task_struct *task, char *name,
>         else
>                 error = -EINVAL;
>
> -       if (profile)
> -               error = aa_getprocattr(profile, value);
> +       if (profile) {
> +               if (strcmp(name, "context") == 0) {
> +                       char *vp;
> +                       char *np;
> +
> +                       error = aa_getprocattr(profile, &vp);
> +                       if (error > 0) {
> +                               error += 12;
> +                               *value = kzalloc(error, GFP_KERNEL);
> +                               if (*value == NULL)
> +                                       error = -ENOMEM;
> +                               else {
> +                                       sprintf(*value, "apparmor='%s'", vp);

This and the others seem to still not be using kasprintf()?

-Kees

> +                                       np = strchr(*value, '\n');
> +                                       if (np != NULL) {
> +                                               np[0] = '\'';
> +                                               np[1] = '\0';
> +                                       }
> +                               }
> +                       }
> +               } else
> +                       error = aa_getprocattr(profile, value);
> +       }
>
>         aa_put_profile(profile);
>         put_cred(cred);
> @@ -530,7 +553,7 @@ static int apparmor_setprocattr(struct task_struct *task, char *name,
>                 return -EINVAL;
>
>         arg_size = size - (args - (char *) value);
> -       if (strcmp(name, "current") == 0) {
> +       if (strcmp(name, "current") == 0 || strcmp(name, "context") == 0) {
>                 if (strcmp(command, "changehat") == 0) {
>                         error = aa_setprocattr_changehat(args, arg_size,
>                                                          !AA_DO_TEST);
> @@ -552,7 +575,10 @@ static int apparmor_setprocattr(struct task_struct *task, char *name,
>                 else
>                         goto fail;
>         } else
> -               /* only support the "current" and "exec" process attributes */
> +               /*
> +                * only support the "current", context and "exec"
> +                * process attributes
> +                */
>                 return -EINVAL;
>
>         if (!error)
> diff --git a/security/security.c b/security/security.c
> index 1e9cb55..fec70b4 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -1186,8 +1186,47 @@ int security_getprocattr(struct task_struct *p, const char *lsm, char *name,
>                                 char **value)
>  {
>         struct security_hook_list *hp;
> +       char *vp;
> +       char *cp = NULL;
>         int rc = -EINVAL;
> +       int trc;
>
> +       /*
> +        * "context" requires work here in addition to what
> +        * the modules provide.
> +        */
> +       if (strcmp(name, "context") == 0) {
> +               *value = NULL;
> +               list_for_each_entry(hp,
> +                               &security_hook_heads.getprocattr, list) {
> +                       if (lsm != NULL && strcmp(lsm, hp->lsm))
> +                               continue;
> +                       trc = hp->hook.getprocattr(p, "context", &vp);
> +                       if (trc == -ENOENT)
> +                               continue;
> +                       if (trc <= 0) {
> +                               kfree(*value);
> +                               return trc;
> +                       }
> +                       rc = trc;
> +                       if (*value == NULL) {
> +                               *value = vp;
> +                       } else {
> +                               cp = kasprintf(GFP_KERNEL, "%s%s", *value, vp);
> +                               if (cp == NULL) {
> +                                       kfree(*value);
> +                                       kfree(vp);
> +                                       return -ENOMEM;
> +                               }
> +                               kfree(*value);
> +                               kfree(vp);
> +                               *value = cp;
> +                       }
> +               }
> +               if (rc > 0)
> +                       return strlen(*value);
> +               return rc;
> +       }
>
>         list_for_each_entry(hp, &security_hook_heads.getprocattr, list) {
>                 if (lsm != NULL && strcmp(lsm, hp->lsm))
> @@ -1204,7 +1243,68 @@ int security_setprocattr(struct task_struct *p, const char *lsm, char *name,
>  {
>         struct security_hook_list *hp;
>         int rc = -EINVAL;
> +       char *local;
> +       char *cp;
> +       int slen;
> +       int failed = 0;
>
> +       /*
> +        * If lsm is NULL look at all the modules to find one
> +        * that processes name. If lsm is not NULL only look at
> +        * that module.
> +        *
> +        * "context" is handled directly here.
> +        */
> +       if (strcmp(name, "context") == 0) {
> +               /*
> +                * First verify that the input is acceptable.
> +                * lsm1='v1'lsm2='v2'lsm3='v3'
> +                *
> +                * A note on the use of strncmp() below.
> +                * The check is for the substring at the beginning of cp.
> +                * The kzalloc of size + 1 ensures a terminated string.
> +                */
> +               local = kzalloc(size + 1, GFP_KERNEL);
> +               memcpy(local, value, size);
> +               cp = local;
> +               list_for_each_entry(hp, &security_hook_heads.setprocattr,
> +                                       list) {
> +                       if (lsm != NULL && strcmp(lsm, hp->lsm))
> +                               continue;
> +                       slen = strlen(hp->lsm);
> +                       if (strncmp(cp, hp->lsm, slen))
> +                               goto free_out;
> +                       cp += slen;
> +                       if (cp[0] != '=' || cp[1] != '\'' || cp[2] == '\'')
> +                               goto free_out;
> +                       for (cp += 2; cp[0] != '\''; cp++)
> +                               if (cp[0] == '\0')
> +                                       goto free_out;
> +                       cp++;
> +               }
> +               cp = local;
> +               list_for_each_entry(hp, &security_hook_heads.setprocattr,
> +                                       list) {
> +                       if (lsm != NULL && strcmp(lsm, hp->lsm))
> +                               continue;
> +                       cp += strlen(hp->lsm) + 2;
> +                       for (slen = 0; cp[slen] != '\''; slen++)
> +                               ;
> +                       cp[slen] = '\0';
> +
> +                       rc = hp->hook.setprocattr(p, "context", cp, slen);
> +                       if (rc < 0)
> +                               failed = rc;
> +                       cp += slen + 1;
> +               }
> +               if (failed != 0)
> +                       rc = failed;
> +               else
> +                       rc = size;
> +free_out:
> +               kfree(local);
> +               return rc;
> +       }
>         list_for_each_entry(hp, &security_hook_heads.setprocattr, list) {
>                 if (lsm != NULL && strcmp(lsm, hp->lsm))
>                         continue;
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index ed3a757..3a21c2b 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -5711,6 +5711,8 @@ static int selinux_getprocattr(struct task_struct *p,
>
>         if (!strcmp(name, "current"))
>                 sid = __tsec->sid;
> +       else if (!strcmp(name, "context"))
> +               sid = __tsec->sid;
>         else if (!strcmp(name, "prev"))
>                 sid = __tsec->osid;
>         else if (!strcmp(name, "exec"))
> @@ -5728,7 +5730,21 @@ static int selinux_getprocattr(struct task_struct *p,
>         if (!sid)
>                 return 0;
>
> -       error = security_sid_to_context(sid, value, &len);
> +       if (strcmp(name, "context")) {
> +               error = security_sid_to_context(sid, value, &len);
> +       } else {
> +               char *vp;
> +
> +               error = security_sid_to_context(sid, &vp, &len);
> +               if (!error) {
> +                       *value = kzalloc(len + 10, GFP_KERNEL);
> +                       if (*value == NULL)
> +                               error = -ENOMEM;
> +                       else
> +                               sprintf(*value, "selinux='%s'", vp);
> +               }
> +       }
> +
>         if (error)
>                 return error;
>         return len;
> @@ -5768,6 +5784,8 @@ static int selinux_setprocattr(struct task_struct *p,
>                 error = current_has_perm(p, PROCESS__SETSOCKCREATE);
>         else if (!strcmp(name, "current"))
>                 error = current_has_perm(p, PROCESS__SETCURRENT);
> +       else if (!strcmp(name, "context"))
> +               error = current_has_perm(p, PROCESS__SETCURRENT);
>         else
>                 error = -EINVAL;
>         if (error)
> @@ -5827,7 +5845,7 @@ static int selinux_setprocattr(struct task_struct *p,
>                 tsec->keycreate_sid = sid;
>         } else if (!strcmp(name, "sockcreate")) {
>                 tsec->sockcreate_sid = sid;
> -       } else if (!strcmp(name, "current")) {
> +       } else if (!strcmp(name, "current") || !strcmp(name, "context")) {
>                 error = -EINVAL;
>                 if (sid == 0)
>                         goto abort_change;
> diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
> index 3577009..d2d8624 100644
> --- a/security/smack/smack_lsm.c
> +++ b/security/smack/smack_lsm.c
> @@ -3576,16 +3576,21 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value)
>         char *cp;
>         int slen;
>
> -       if (strcmp(name, "current") != 0)
> +       if (strcmp(name, "current") == 0) {
> +               cp = kstrdup(skp->smk_known, GFP_KERNEL);
> +               if (cp == NULL)
> +                       return -ENOMEM;
> +       } else if (strcmp(name, "context") == 0) {
> +               slen = strlen(skp->smk_known) + 9;
> +               cp = kzalloc(slen, GFP_KERNEL);
> +               if (cp == NULL)
> +                       return -ENOMEM;
> +               sprintf(cp, "smack='%s'", skp->smk_known);
> +       } else
>                 return -EINVAL;
>
> -       cp = kstrdup(skp->smk_known, GFP_KERNEL);
> -       if (cp == NULL)
> -               return -ENOMEM;
> -
> -       slen = strlen(cp);
>         *value = cp;
> -       return slen;
> +       return strlen(cp);
>  }
>
>  /**
> @@ -3622,7 +3627,7 @@ static int smack_setprocattr(struct task_struct *p, char *name,
>         if (value == NULL || size == 0 || size >= SMK_LONGLABEL)
>                 return -EINVAL;
>
> -       if (strcmp(name, "current") != 0)
> +       if (strcmp(name, "current") != 0 && strcmp(name, "context") != 0)
>                 return -EINVAL;
>
>         skp = smk_import_entry(value, size);
>



-- 
Kees Cook
Chrome OS & Brillo Security

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v4 3/3] LSM: Add context interface for proc attrs
  2016-06-23 21:49   ` Kees Cook
@ 2016-06-23 22:10     ` Casey Schaufler
  2016-06-24 16:38     ` [PATCH v4 4/3] LSM: Improve " Casey Schaufler
  1 sibling, 0 replies; 32+ messages in thread
From: Casey Schaufler @ 2016-06-23 22:10 UTC (permalink / raw)
  To: Kees Cook
  Cc: LSM, James Morris, John Johansen, Stephen Smalley, Paul Moore,
	Tetsuo Handa, LKLM

On 6/23/2016 2:49 PM, Kees Cook wrote:
> On Thu, Jun 23, 2016 at 2:11 PM, Casey Schaufler <casey@schaufler-ca.com> wrote:
>> Subject: [PATCH v4 3/3] LSM: Add context interface for proc attrs
>>
>> The /proc/.../attr/current interface is used by all three
>> Linux security modules (SELinux, Smack and AppArmor) to
>> report and modify the process security attribute. This is
>> all fine when there is exactly one of these modules active
>> and the userspace code knows which it module it is.
>> It would require a major change to the "current" interface
>> to provide information about more than one set of process
>> security attributes. Instead, a "context" attribute is
>> added, which identifies the security module that the
>> information applies to. The format is:
>>
>>         lsmname='context-value'
>>
>> When multiple concurrent modules are supported the
>> /proc/.../attr/context interface will include the data
>> for all of the active modules.
>>
>>         lsmname1='context-value1'lsmname2='context-value2'
>>
>> The module specific subdirectories under attr contain context
>> entries that report the information for that specific module
>> in the same format.
>>
>> Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
>>
>> ---
>>  fs/proc/base.c             |   4 ++
>>  security/apparmor/lsm.c    |  34 +++++++++++++--
>>  security/security.c        | 100 +++++++++++++++++++++++++++++++++++++++++++++
>>  security/selinux/hooks.c   |  22 +++++++++-
>>  security/smack/smack_lsm.c |  21 ++++++----
>>  5 files changed, 167 insertions(+), 14 deletions(-)
>>
>> diff --git a/fs/proc/base.c b/fs/proc/base.c
>> index 182bc28..df94f26 100644
>> --- a/fs/proc/base.c
>> +++ b/fs/proc/base.c
>> @@ -2532,6 +2532,7 @@ static const struct pid_entry selinux_attr_dir_stuff[] = {
>>         ATTR("selinux", "fscreate",     S_IRUGO|S_IWUGO),
>>         ATTR("selinux", "keycreate",    S_IRUGO|S_IWUGO),
>>         ATTR("selinux", "sockcreate",   S_IRUGO|S_IWUGO),
>> +       ATTR("selinux", "context",      S_IRUGO|S_IWUGO),
>>  };
>>  LSM_DIR_OPS(selinux);
>>  #endif
>> @@ -2539,6 +2540,7 @@ LSM_DIR_OPS(selinux);
>>  #ifdef CONFIG_SECURITY_SMACK
>>  static const struct pid_entry smack_attr_dir_stuff[] = {
>>         ATTR("smack", "current",        S_IRUGO|S_IWUGO),
>> +       ATTR("smack", "context",        S_IRUGO|S_IWUGO),
>>  };
>>  LSM_DIR_OPS(smack);
>>  #endif
>> @@ -2548,6 +2550,7 @@ static const struct pid_entry apparmor_attr_dir_stuff[] = {
>>         ATTR("apparmor", "current",     S_IRUGO|S_IWUGO),
>>         ATTR("apparmor", "prev",        S_IRUGO),
>>         ATTR("apparmor", "exec",        S_IRUGO|S_IWUGO),
>> +       ATTR("apparmor", "context",     S_IRUGO|S_IWUGO),
>>  };
>>  LSM_DIR_OPS(apparmor);
>>  #endif
>> @@ -2559,6 +2562,7 @@ static const struct pid_entry attr_dir_stuff[] = {
>>         ATTR(NULL, "fscreate",          S_IRUGO|S_IWUGO),
>>         ATTR(NULL, "keycreate",         S_IRUGO|S_IWUGO),
>>         ATTR(NULL, "sockcreate",        S_IRUGO|S_IWUGO),
>> +       ATTR(NULL, "context",           S_IRUGO|S_IWUGO),
>>  #ifdef CONFIG_SECURITY_SELINUX
>>         DIR("selinux",                  S_IRUGO|S_IXUGO,
>>             proc_selinux_attr_dir_inode_ops, proc_selinux_attr_dir_ops),
>> diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
>> index fb0fb03..3790a7d 100644
>> --- a/security/apparmor/lsm.c
>> +++ b/security/apparmor/lsm.c
>> @@ -479,6 +479,8 @@ static int apparmor_getprocattr(struct task_struct *task, char *name,
>>
>>         if (strcmp(name, "current") == 0)
>>                 profile = aa_get_newest_profile(cxt->profile);
>> +       else if (strcmp(name, "context") == 0)
>> +               profile = aa_get_newest_profile(cxt->profile);
>>         else if (strcmp(name, "prev") == 0  && cxt->previous)
>>                 profile = aa_get_newest_profile(cxt->previous);
>>         else if (strcmp(name, "exec") == 0 && cxt->onexec)
>> @@ -486,8 +488,29 @@ static int apparmor_getprocattr(struct task_struct *task, char *name,
>>         else
>>                 error = -EINVAL;
>>
>> -       if (profile)
>> -               error = aa_getprocattr(profile, value);
>> +       if (profile) {
>> +               if (strcmp(name, "context") == 0) {
>> +                       char *vp;
>> +                       char *np;
>> +
>> +                       error = aa_getprocattr(profile, &vp);
>> +                       if (error > 0) {
>> +                               error += 12;
>> +                               *value = kzalloc(error, GFP_KERNEL);
>> +                               if (*value == NULL)
>> +                                       error = -ENOMEM;
>> +                               else {
>> +                                       sprintf(*value, "apparmor='%s'", vp);
> This and the others seem to still not be using kasprintf()?

I got the one in security.c but missed the ones in the modules.
Sigh. It'll be in v4. Thank you.

>
> -Kees
>
>> +                                       np = strchr(*value, '\n');
>> +                                       if (np != NULL) {
>> +                                               np[0] = '\'';
>> +                                               np[1] = '\0';
>> +                                       }
>> +                               }
>> +                       }
>> +               } else
>> +                       error = aa_getprocattr(profile, value);
>> +       }
>>
>>         aa_put_profile(profile);
>>         put_cred(cred);
>> @@ -530,7 +553,7 @@ static int apparmor_setprocattr(struct task_struct *task, char *name,
>>                 return -EINVAL;
>>
>>         arg_size = size - (args - (char *) value);
>> -       if (strcmp(name, "current") == 0) {
>> +       if (strcmp(name, "current") == 0 || strcmp(name, "context") == 0) {
>>                 if (strcmp(command, "changehat") == 0) {
>>                         error = aa_setprocattr_changehat(args, arg_size,
>>                                                          !AA_DO_TEST);
>> @@ -552,7 +575,10 @@ static int apparmor_setprocattr(struct task_struct *task, char *name,
>>                 else
>>                         goto fail;
>>         } else
>> -               /* only support the "current" and "exec" process attributes */
>> +               /*
>> +                * only support the "current", context and "exec"
>> +                * process attributes
>> +                */
>>                 return -EINVAL;
>>
>>         if (!error)
>> diff --git a/security/security.c b/security/security.c
>> index 1e9cb55..fec70b4 100644
>> --- a/security/security.c
>> +++ b/security/security.c
>> @@ -1186,8 +1186,47 @@ int security_getprocattr(struct task_struct *p, const char *lsm, char *name,
>>                                 char **value)
>>  {
>>         struct security_hook_list *hp;
>> +       char *vp;
>> +       char *cp = NULL;
>>         int rc = -EINVAL;
>> +       int trc;
>>
>> +       /*
>> +        * "context" requires work here in addition to what
>> +        * the modules provide.
>> +        */
>> +       if (strcmp(name, "context") == 0) {
>> +               *value = NULL;
>> +               list_for_each_entry(hp,
>> +                               &security_hook_heads.getprocattr, list) {
>> +                       if (lsm != NULL && strcmp(lsm, hp->lsm))
>> +                               continue;
>> +                       trc = hp->hook.getprocattr(p, "context", &vp);
>> +                       if (trc == -ENOENT)
>> +                               continue;
>> +                       if (trc <= 0) {
>> +                               kfree(*value);
>> +                               return trc;
>> +                       }
>> +                       rc = trc;
>> +                       if (*value == NULL) {
>> +                               *value = vp;
>> +                       } else {
>> +                               cp = kasprintf(GFP_KERNEL, "%s%s", *value, vp);
>> +                               if (cp == NULL) {
>> +                                       kfree(*value);
>> +                                       kfree(vp);
>> +                                       return -ENOMEM;
>> +                               }
>> +                               kfree(*value);
>> +                               kfree(vp);
>> +                               *value = cp;
>> +                       }
>> +               }
>> +               if (rc > 0)
>> +                       return strlen(*value);
>> +               return rc;
>> +       }
>>
>>         list_for_each_entry(hp, &security_hook_heads.getprocattr, list) {
>>                 if (lsm != NULL && strcmp(lsm, hp->lsm))
>> @@ -1204,7 +1243,68 @@ int security_setprocattr(struct task_struct *p, const char *lsm, char *name,
>>  {
>>         struct security_hook_list *hp;
>>         int rc = -EINVAL;
>> +       char *local;
>> +       char *cp;
>> +       int slen;
>> +       int failed = 0;
>>
>> +       /*
>> +        * If lsm is NULL look at all the modules to find one
>> +        * that processes name. If lsm is not NULL only look at
>> +        * that module.
>> +        *
>> +        * "context" is handled directly here.
>> +        */
>> +       if (strcmp(name, "context") == 0) {
>> +               /*
>> +                * First verify that the input is acceptable.
>> +                * lsm1='v1'lsm2='v2'lsm3='v3'
>> +                *
>> +                * A note on the use of strncmp() below.
>> +                * The check is for the substring at the beginning of cp.
>> +                * The kzalloc of size + 1 ensures a terminated string.
>> +                */
>> +               local = kzalloc(size + 1, GFP_KERNEL);
>> +               memcpy(local, value, size);
>> +               cp = local;
>> +               list_for_each_entry(hp, &security_hook_heads.setprocattr,
>> +                                       list) {
>> +                       if (lsm != NULL && strcmp(lsm, hp->lsm))
>> +                               continue;
>> +                       slen = strlen(hp->lsm);
>> +                       if (strncmp(cp, hp->lsm, slen))
>> +                               goto free_out;
>> +                       cp += slen;
>> +                       if (cp[0] != '=' || cp[1] != '\'' || cp[2] == '\'')
>> +                               goto free_out;
>> +                       for (cp += 2; cp[0] != '\''; cp++)
>> +                               if (cp[0] == '\0')
>> +                                       goto free_out;
>> +                       cp++;
>> +               }
>> +               cp = local;
>> +               list_for_each_entry(hp, &security_hook_heads.setprocattr,
>> +                                       list) {
>> +                       if (lsm != NULL && strcmp(lsm, hp->lsm))
>> +                               continue;
>> +                       cp += strlen(hp->lsm) + 2;
>> +                       for (slen = 0; cp[slen] != '\''; slen++)
>> +                               ;
>> +                       cp[slen] = '\0';
>> +
>> +                       rc = hp->hook.setprocattr(p, "context", cp, slen);
>> +                       if (rc < 0)
>> +                               failed = rc;
>> +                       cp += slen + 1;
>> +               }
>> +               if (failed != 0)
>> +                       rc = failed;
>> +               else
>> +                       rc = size;
>> +free_out:
>> +               kfree(local);
>> +               return rc;
>> +       }
>>         list_for_each_entry(hp, &security_hook_heads.setprocattr, list) {
>>                 if (lsm != NULL && strcmp(lsm, hp->lsm))
>>                         continue;
>> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
>> index ed3a757..3a21c2b 100644
>> --- a/security/selinux/hooks.c
>> +++ b/security/selinux/hooks.c
>> @@ -5711,6 +5711,8 @@ static int selinux_getprocattr(struct task_struct *p,
>>
>>         if (!strcmp(name, "current"))
>>                 sid = __tsec->sid;
>> +       else if (!strcmp(name, "context"))
>> +               sid = __tsec->sid;
>>         else if (!strcmp(name, "prev"))
>>                 sid = __tsec->osid;
>>         else if (!strcmp(name, "exec"))
>> @@ -5728,7 +5730,21 @@ static int selinux_getprocattr(struct task_struct *p,
>>         if (!sid)
>>                 return 0;
>>
>> -       error = security_sid_to_context(sid, value, &len);
>> +       if (strcmp(name, "context")) {
>> +               error = security_sid_to_context(sid, value, &len);
>> +       } else {
>> +               char *vp;
>> +
>> +               error = security_sid_to_context(sid, &vp, &len);
>> +               if (!error) {
>> +                       *value = kzalloc(len + 10, GFP_KERNEL);
>> +                       if (*value == NULL)
>> +                               error = -ENOMEM;
>> +                       else
>> +                               sprintf(*value, "selinux='%s'", vp);
>> +               }
>> +       }
>> +
>>         if (error)
>>                 return error;
>>         return len;
>> @@ -5768,6 +5784,8 @@ static int selinux_setprocattr(struct task_struct *p,
>>                 error = current_has_perm(p, PROCESS__SETSOCKCREATE);
>>         else if (!strcmp(name, "current"))
>>                 error = current_has_perm(p, PROCESS__SETCURRENT);
>> +       else if (!strcmp(name, "context"))
>> +               error = current_has_perm(p, PROCESS__SETCURRENT);
>>         else
>>                 error = -EINVAL;
>>         if (error)
>> @@ -5827,7 +5845,7 @@ static int selinux_setprocattr(struct task_struct *p,
>>                 tsec->keycreate_sid = sid;
>>         } else if (!strcmp(name, "sockcreate")) {
>>                 tsec->sockcreate_sid = sid;
>> -       } else if (!strcmp(name, "current")) {
>> +       } else if (!strcmp(name, "current") || !strcmp(name, "context")) {
>>                 error = -EINVAL;
>>                 if (sid == 0)
>>                         goto abort_change;
>> diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
>> index 3577009..d2d8624 100644
>> --- a/security/smack/smack_lsm.c
>> +++ b/security/smack/smack_lsm.c
>> @@ -3576,16 +3576,21 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value)
>>         char *cp;
>>         int slen;
>>
>> -       if (strcmp(name, "current") != 0)
>> +       if (strcmp(name, "current") == 0) {
>> +               cp = kstrdup(skp->smk_known, GFP_KERNEL);
>> +               if (cp == NULL)
>> +                       return -ENOMEM;
>> +       } else if (strcmp(name, "context") == 0) {
>> +               slen = strlen(skp->smk_known) + 9;
>> +               cp = kzalloc(slen, GFP_KERNEL);
>> +               if (cp == NULL)
>> +                       return -ENOMEM;
>> +               sprintf(cp, "smack='%s'", skp->smk_known);
>> +       } else
>>                 return -EINVAL;
>>
>> -       cp = kstrdup(skp->smk_known, GFP_KERNEL);
>> -       if (cp == NULL)
>> -               return -ENOMEM;
>> -
>> -       slen = strlen(cp);
>>         *value = cp;
>> -       return slen;
>> +       return strlen(cp);
>>  }
>>
>>  /**
>> @@ -3622,7 +3627,7 @@ static int smack_setprocattr(struct task_struct *p, char *name,
>>         if (value == NULL || size == 0 || size >= SMK_LONGLABEL)
>>                 return -EINVAL;
>>
>> -       if (strcmp(name, "current") != 0)
>> +       if (strcmp(name, "current") != 0 && strcmp(name, "context") != 0)
>>                 return -EINVAL;
>>
>>         skp = smk_import_entry(value, size);
>>
>
>

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH v4 4/3] LSM: Improve context interface for proc attrs
  2016-06-23 21:49   ` Kees Cook
  2016-06-23 22:10     ` Casey Schaufler
@ 2016-06-24 16:38     ` Casey Schaufler
  2016-06-24 17:48       ` Kees Cook
  1 sibling, 1 reply; 32+ messages in thread
From: Casey Schaufler @ 2016-06-24 16:38 UTC (permalink / raw)
  To: Kees Cook
  Cc: LSM, James Morris, John Johansen, Stephen Smalley, Paul Moore,
	Tetsuo Handa, LKLM

Subject: [PATCH v4 4/3] LSM: Improve context interface for proc attrs

Replace kzalloc ... sprintf with kasprintf in the
"context" procfs attr code.

Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>

---
 security/apparmor/lsm.c    | 47 +++++++++++++++++++++++-----------------------
 security/selinux/hooks.c   |  4 +---
 security/smack/smack_lsm.c |  5 +----
 3 files changed, 26 insertions(+), 30 deletions(-)

diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 3790a7d..5cac15f 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -476,6 +476,8 @@ static int apparmor_getprocattr(struct task_struct *task, char *name,
 	const struct cred *cred = get_task_cred(task);
 	struct aa_task_cxt *cxt = cred_cxt(cred);
 	struct aa_profile *profile = NULL;
+	char *vp;
+	char *np;
 
 	if (strcmp(name, "current") == 0)
 		profile = aa_get_newest_profile(cxt->profile);
@@ -488,30 +490,29 @@ static int apparmor_getprocattr(struct task_struct *task, char *name,
 	else
 		error = -EINVAL;
 
-	if (profile) {
-		if (strcmp(name, "context") == 0) {
-			char *vp;
-			char *np;
-
-			error = aa_getprocattr(profile, &vp);
-			if (error > 0) {
-				error += 12;
-				*value = kzalloc(error, GFP_KERNEL);
-				if (*value == NULL)
-					error = -ENOMEM;
-				else {
-					sprintf(*value, "apparmor='%s'", vp);
-					np = strchr(*value, '\n');
-					if (np != NULL) {
-						np[0] = '\'';
-						np[1] = '\0';
-					}
-				}
-			}
-		} else
-			error = aa_getprocattr(profile, value);
-	}
+	if (profile == NULL)
+		goto put_out;
+
+	error = aa_getprocattr(profile, &vp);
+	if (error < 0)
+		goto put_out;
+
+	if (strcmp(name, "context") == 0) {
+		*value = kasprintf(GFP_KERNEL, "apparmor='%s'", vp);
+		if (*value == NULL) {
+			error = -ENOMEM;
+			goto put_out;
+		}
+		np = strchr(*value, '\n');
+		if (np != NULL) {
+			np[0] = '\'';
+			np[1] = '\0';
+		}
+		error = strlen(*value);
+	} else
+		*value = vp;
 
+put_out:
 	aa_put_profile(profile);
 	put_cred(cred);
 
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 3a21c2b..6397721 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -5737,11 +5737,9 @@ static int selinux_getprocattr(struct task_struct *p,
 
 		error = security_sid_to_context(sid, &vp, &len);
 		if (!error) {
-			*value = kzalloc(len + 10, GFP_KERNEL);
+			*value = kasprintf(GFP_KERNEL, "selinux='%s'", vp);
 			if (*value == NULL)
 				error = -ENOMEM;
-			else
-				sprintf(*value, "selinux='%s'", vp);
 		}
 	}
 
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index d2d8624..92e66f8 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -3574,18 +3574,15 @@ 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) {
 		cp = kstrdup(skp->smk_known, GFP_KERNEL);
 		if (cp == NULL)
 			return -ENOMEM;
 	} else if (strcmp(name, "context") == 0) {
-		slen = strlen(skp->smk_known) + 9;
-		cp = kzalloc(slen, GFP_KERNEL);
+		cp = kasprintf(GFP_KERNEL, "smack='%s'", skp->smk_known);
 		if (cp == NULL)
 			return -ENOMEM;
-		sprintf(cp, "smack='%s'", skp->smk_known);
 	} else
 		return -EINVAL;
 

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* Re: [PATCH v4 4/3] LSM: Improve context interface for proc attrs
  2016-06-24 16:38     ` [PATCH v4 4/3] LSM: Improve " Casey Schaufler
@ 2016-06-24 17:48       ` Kees Cook
  0 siblings, 0 replies; 32+ messages in thread
From: Kees Cook @ 2016-06-24 17:48 UTC (permalink / raw)
  To: Casey Schaufler
  Cc: LSM, James Morris, John Johansen, Stephen Smalley, Paul Moore,
	Tetsuo Handa, LKLM

On Fri, Jun 24, 2016 at 9:38 AM, Casey Schaufler <casey@schaufler-ca.com> wrote:
> Subject: [PATCH v4 4/3] LSM: Improve context interface for proc attrs
>
> Replace kzalloc ... sprintf with kasprintf in the
> "context" procfs attr code.
>
> Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>

Great! For this and the other 3:

Acked-by: Kees Cook <keescook@chromium.org>

-Kees

-- 
Kees Cook
Chrome OS & Brillo Security

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v4 1/3] LSM: Add /sys/kernel/security/lsm
  2016-06-23 21:10 ` [PATCH v4 1/3] LSM: Add /sys/kernel/security/lsm Casey Schaufler
@ 2016-06-24 18:56   ` Paul Moore
  0 siblings, 0 replies; 32+ messages in thread
From: Paul Moore @ 2016-06-24 18:56 UTC (permalink / raw)
  To: Casey Schaufler
  Cc: LSM, James Morris, John Johansen, Stephen Smalley, Tetsuo Handa,
	Kees Cook, LKLM

On Thu, Jun 23, 2016 at 5:10 PM, Casey Schaufler <casey@schaufler-ca.com> wrote:
> Subject: [PATCH v4 1/3] LSM: Add /sys/kernel/security/lsm
>
> I got tired of having to find indirect ways to
> determine what security modules are active on a system.
> I have added /sys/kernel/security/lsm, which contains a
> comma separated list of the active security modules. No
> more groping around in /proc/filesystems, which won't
> help if the module doesn't support its own filesystem.
>
> Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
>
> ---
>  include/linux/lsm_hooks.h  | 12 ++++--------
>  security/apparmor/lsm.c    |  3 ++-
>  security/commoncap.c       |  3 ++-
>  security/inode.c           | 26 ++++++++++++++++++++++++--
>  security/loadpin/loadpin.c |  2 +-
>  security/security.c        | 38 ++++++++++++++++++++++++++++++++++++++
>  security/selinux/hooks.c   |  2 +-
>  security/smack/smack_lsm.c |  2 +-
>  security/tomoyo/tomoyo.c   |  2 +-
>  security/yama/yama_lsm.c   |  2 +-
>  10 files changed, 75 insertions(+), 17 deletions(-)

Seems reasonable to me.

Acked-by: Paul Moore <paul@paul-moore.com>

> diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
> index 7ae3976..0bfd441 100644
> --- a/include/linux/lsm_hooks.h
> +++ b/include/linux/lsm_hooks.h
> @@ -1841,6 +1841,7 @@ struct security_hook_list {
>         struct list_head                list;
>         struct list_head                *head;
>         union security_list_options     hook;
> +       char                            *lsm;
>  };
>
>  /*
> @@ -1853,15 +1854,10 @@ struct security_hook_list {
>         { .head = &security_hook_heads.HEAD, .hook = { .HEAD = HOOK } }
>
>  extern struct security_hook_heads security_hook_heads;
> +extern char *lsm_names;
>
> -static inline void security_add_hooks(struct security_hook_list *hooks,
> -                                     int count)
> -{
> -       int i;
> -
> -       for (i = 0; i < count; i++)
> -               list_add_tail_rcu(&hooks[i].list, hooks[i].head);
> -}
> +extern void security_add_hooks(struct security_hook_list *hooks, int count,
> +                               char *lsm);
>
>  #ifdef CONFIG_SECURITY_SELINUX_DISABLE
>  /*
> diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
> index 2660fbc..fb0fb03 100644
> --- a/security/apparmor/lsm.c
> +++ b/security/apparmor/lsm.c
> @@ -880,7 +880,8 @@ static int __init apparmor_init(void)
>                 aa_free_root_ns();
>                 goto alloc_out;
>         }
> -       security_add_hooks(apparmor_hooks, ARRAY_SIZE(apparmor_hooks));
> +       security_add_hooks(apparmor_hooks, ARRAY_SIZE(apparmor_hooks),
> +                               "apparmor");
>
>         /* Report that AppArmor successfully initialized */
>         apparmor_initialized = 1;
> diff --git a/security/commoncap.c b/security/commoncap.c
> index e7fadde..d101734 100644
> --- a/security/commoncap.c
> +++ b/security/commoncap.c
> @@ -1090,7 +1090,8 @@ struct security_hook_list capability_hooks[] = {
>
>  void __init capability_add_hooks(void)
>  {
> -       security_add_hooks(capability_hooks, ARRAY_SIZE(capability_hooks));
> +       security_add_hooks(capability_hooks, ARRAY_SIZE(capability_hooks),
> +                               "capability");
>  }
>
>  #endif /* CONFIG_SECURITY */
> diff --git a/security/inode.c b/security/inode.c
> index 28414b0..235ad82 100644
> --- a/security/inode.c
> +++ b/security/inode.c
> @@ -20,6 +20,7 @@
>  #include <linux/init.h>
>  #include <linux/namei.h>
>  #include <linux/security.h>
> +#include <linux/lsm_hooks.h>
>  #include <linux/magic.h>
>
>  static struct vfsmount *mount;
> @@ -208,6 +209,21 @@ void securityfs_remove(struct dentry *dentry)
>  }
>  EXPORT_SYMBOL_GPL(securityfs_remove);
>
> +#ifdef CONFIG_SECURITY
> +static struct dentry *lsm_dentry;
> +static ssize_t lsm_read(struct file *filp, char __user *buf, size_t count,
> +                       loff_t *ppos)
> +{
> +       return simple_read_from_buffer(buf, count, ppos, lsm_names,
> +               strlen(lsm_names));
> +}
> +
> +static const struct file_operations lsm_ops = {
> +       .read = lsm_read,
> +       .llseek = generic_file_llseek,
> +};
> +#endif
> +
>  static int __init securityfs_init(void)
>  {
>         int retval;
> @@ -217,9 +233,15 @@ static int __init securityfs_init(void)
>                 return retval;
>
>         retval = register_filesystem(&fs_type);
> -       if (retval)
> +       if (retval) {
>                 sysfs_remove_mount_point(kernel_kobj, "security");
> -       return retval;
> +               return retval;
> +       }
> +#ifdef CONFIG_SECURITY
> +       lsm_dentry = securityfs_create_file("lsm", S_IRUGO, NULL, NULL,
> +                                               &lsm_ops);
> +#endif
> +       return 0;
>  }
>
>  core_initcall(securityfs_init);
> diff --git a/security/loadpin/loadpin.c b/security/loadpin/loadpin.c
> index 89a46f1..1d82eae 100644
> --- a/security/loadpin/loadpin.c
> +++ b/security/loadpin/loadpin.c
> @@ -182,7 +182,7 @@ static struct security_hook_list loadpin_hooks[] = {
>  void __init loadpin_add_hooks(void)
>  {
>         pr_info("ready to pin (currently %sabled)", enabled ? "en" : "dis");
> -       security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks));
> +       security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks), "loadpin");
>  }
>
>  /* Should not be mutable after boot, so not listed in sysfs (perm == 0). */
> diff --git a/security/security.c b/security/security.c
> index c4bb47d..1093f73 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -32,6 +32,7 @@
>  /* Maximum number of letters for an LSM name string */
>  #define SECURITY_NAME_MAX      10
>
> +char *lsm_names;
>  /* Boot-time LSM user choice */
>  static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =
>         CONFIG_DEFAULT_SECURITY;
> @@ -78,6 +79,22 @@ static int __init choose_lsm(char *str)
>  }
>  __setup("security=", choose_lsm);
>
> +static int lsm_append(char *new, char **result)
> +{
> +       char *cp;
> +
> +       if (*result == NULL) {
> +               *result = kstrdup(new, GFP_KERNEL);
> +       } else {
> +               cp = kasprintf(GFP_KERNEL, "%s,%s", *result, new);
> +               if (cp == NULL)
> +                       return -ENOMEM;
> +               kfree(*result);
> +               *result = cp;
> +       }
> +       return 0;
> +}
> +
>  /**
>   * security_module_enable - Load given security module on boot ?
>   * @module: the name of the module
> @@ -97,6 +114,27 @@ int __init security_module_enable(const char *module)
>         return !strcmp(module, chosen_lsm);
>  }
>
> +/**
> + * security_add_hooks - Add a modules hooks to the hook lists.
> + * @hooks: the hooks to add
> + * @count: the number of hooks to add
> + * @lsm: the name of the security module
> + *
> + * Each LSM has to register its hooks with the infrastructure.
> + */
> +void __init security_add_hooks(struct security_hook_list *hooks, int count,
> +                               char *lsm)
> +{
> +       int i;
> +
> +       for (i = 0; i < count; i++) {
> +               hooks[i].lsm = lsm;
> +               list_add_tail_rcu(&hooks[i].list, hooks[i].head);
> +       }
> +       if (lsm_append(lsm, &lsm_names) < 0)
> +               panic("%s - Cannot get early memory.\n", __func__);
> +}
> +
>  /*
>   * Hook list operation macros.
>   *
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index a86d537..ed3a757 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -6233,7 +6233,7 @@ static __init int selinux_init(void)
>                                             0, SLAB_PANIC, NULL);
>         avc_init();
>
> -       security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks));
> +       security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks), "selinux");
>
>         if (avc_add_callback(selinux_netcache_avc_callback, AVC_CALLBACK_RESET))
>                 panic("SELinux: Unable to register AVC netcache callback\n");
> diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
> index 6777295..3577009 100644
> --- a/security/smack/smack_lsm.c
> +++ b/security/smack/smack_lsm.c
> @@ -4801,7 +4801,7 @@ static __init int smack_init(void)
>         /*
>          * Register with LSM
>          */
> -       security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks));
> +       security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks), "smack");
>
>         return 0;
>  }
> diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
> index 75c9987..edc52d6 100644
> --- a/security/tomoyo/tomoyo.c
> +++ b/security/tomoyo/tomoyo.c
> @@ -542,7 +542,7 @@ static int __init tomoyo_init(void)
>         if (!security_module_enable("tomoyo"))
>                 return 0;
>         /* register ourselves with the security framework */
> -       security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks));
> +       security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks), "tomoyo");
>         printk(KERN_INFO "TOMOYO Linux initialized\n");
>         cred->security = &tomoyo_kernel_domain;
>         tomoyo_mm_init();
> diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c
> index 0309f21..f8ee60e 100644
> --- a/security/yama/yama_lsm.c
> +++ b/security/yama/yama_lsm.c
> @@ -471,6 +471,6 @@ static inline void yama_init_sysctl(void) { }
>  void __init yama_add_hooks(void)
>  {
>         pr_info("Yama: becoming mindful.\n");
> -       security_add_hooks(yama_hooks, ARRAY_SIZE(yama_hooks));
> +       security_add_hooks(yama_hooks, ARRAY_SIZE(yama_hooks), "yama");
>         yama_init_sysctl();
>  }
>



-- 
paul moore
www.paul-moore.com

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v4 2/3] LSM: module hierarchy in /proc/.../attr
  2016-06-23 21:11 ` [PATCH v4 2/3] LSM: module hierarchy in /proc/.../attr Casey Schaufler
@ 2016-06-24 19:11   ` Paul Moore
  2016-06-24 20:05     ` Casey Schaufler
  0 siblings, 1 reply; 32+ messages in thread
From: Paul Moore @ 2016-06-24 19:11 UTC (permalink / raw)
  To: Casey Schaufler
  Cc: LSM, James Morris, John Johansen, Stephen Smalley, Tetsuo Handa,
	Kees Cook, LKLM

On Thu, Jun 23, 2016 at 5:11 PM, Casey Schaufler <casey@schaufler-ca.com> wrote:
> Subject: [PATCH v4 2/3] LSM: module hierarchy in /proc/.../attr
>
> Back in 2007 I made what turned out to be a rather serious
> mistake in the implementation of the Smack security module.
> The SELinux module used an interface in /proc to manipulate
> the security context on processes. Rather than use a similar
> interface, I used the same interface. The AppArmor team did
> likewise. Now /proc/.../attr/current will tell you the
> security "context" of the process, but it will be different
> depending on the security module you're using. That hasn't
> been a problem to date, as you can only have one module
> that supports process attributes at a time. We are coming
> up on a change to that, where multiple modules with process
> attributes can be supported. (Not included here)
>
> This patch provides a subdirectory in /proc/.../attr for
> each of the security modules that use the LSM hooks
> getprocattr() and setprocattr(). Each of the interfaces
> used by a module are presented in the subdirectory. The
> old interfaces remain and work the same as before.
> User space code can begin migrating to the subdirectory
> interfaces in anticipation of the time when what comes
> from /proc/self/attr/current might not be what a runtime
> wants.
>
> The original implementation is by Kees Cook. The code
> has been changed a bit to reflect changes in the direction
> of the multiple concurrent module work, to be independent
> of it, and to bring it up to date with the current tree.
>
> Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
>
> ---
>  Documentation/security/LSM.txt | 26 +++++++++---
>  fs/proc/base.c                 | 91 +++++++++++++++++++++++++++++++++++++-----
>  fs/proc/internal.h             |  1 +
>  include/linux/security.h       | 15 ++++---
>  security/security.c            | 31 ++++++++++++--
>  5 files changed, 140 insertions(+), 24 deletions(-)
>
> diff --git a/Documentation/security/LSM.txt b/Documentation/security/LSM.txt
> index 3db7e67..125c489 100644
> --- a/Documentation/security/LSM.txt
> +++ b/Documentation/security/LSM.txt
> @@ -16,11 +16,25 @@ MAC extensions, other extensions can be built using the LSM to provide
>  specific changes to system operation when these tweaks are not available
>  in the core functionality of Linux itself.
>
> -Without a specific LSM built into the kernel, the default LSM will be the
> -Linux capabilities system. Most LSMs choose to extend the capabilities
> -system, building their checks on top of the defined capability hooks.
> -For more details on capabilities, see capabilities(7) in the Linux
> -man-pages project.
> +The Linux capabilities modules will always be included. For more details
> +on capabilities, see capabilities(7) in the Linux man-pages project.
> +This may be followed by any number of "minor" modules and at most one
> +"major" module.
> +
> +A list of the active security modules can be found by reading
> +/sys/kernel/security/lsm. This is a comma separated list, and
> +will always include the capability module. The list reflects the
> +order in which checks are made. The capability module will always
> +be first, followed by any "minor" modules (e.g. Yama) and then
> +the one "major" module (e.g. SELinux) if there is one configured.

I wouldn't respin it just for this, but it seems like the paragraph
above should really be part of patch 1/3, yes?

> +Process attributes associated with "ma
> diff --git a/fs/proc/base.c b/fs/proc/base.c
> index a11eb71..182bc28 100644
> --- a/fs/proc/base.c
> +++ b/fs/proc/base.c
> @@ -131,9 +131,13 @@ struct pid_entry {
>  #define REG(NAME, MODE, fops)                          \
>         NOD(NAME, (S_IFREG|(MODE)), NULL, &fops, {})
>  #define ONE(NAME, MODE, show)                          \
> -       NOD(NAME, (S_IFREG|(MODE)),                     \
> +       NOD(NAME, (S_IFREG|(MODE)),                     \
>                 NULL, &proc_single_file_operations,     \
>                 { .proc_show = show } )
> +#define ATTR(LSM, NAME, MODE)                          \
> +       NOD(NAME, (S_IFREG|(MODE)),                     \
> +               NULL, &proc_pid_attr_operations,        \
> +               { .lsm = LSM })
>
>  /*
>   * Count the number of hardlinks for the pid_entry table, excluding the .
> @@ -2433,7 +2437,7 @@ static ssize_t proc_pid_attr_read(struct file * file, char __user * buf,
>         if (!task)
>                 return -ESRCH;
>
> -       length = security_getprocattr(task,
> +       length = security_getprocattr(task, PROC_I(inode)->op.lsm,
>                                       (char*)file->f_path.dentry->d_name.name,
>                                       &p);
>         put_task_struct(task);
> @@ -2473,7 +2477,7 @@ static ssize_t proc_pid_attr_write(struct file * file, const char __user * buf,
>         if (length < 0)
>                 goto out_free;
>
> -       length = security_setprocattr(task,
> +       length = security_setprocattr(task, PROC_I(inode)->op.lsm,
>                                       (char*)file->f_path.dentry->d_name.name,
>                                       page, count);
>         mutex_unlock(&task->signal->cred_guard_mutex);
> @@ -2491,13 +2495,82 @@ static const struct file_operations proc_pid_attr_operations = {
>         .llseek         = generic_file_llseek,
>  };
>
> +#define LSM_DIR_OPS(LSM) \
> +static int proc_##LSM##_attr_dir_iterate(struct file *filp, \
> +                            struct dir_context *ctx) \
> +{ \
> +       return proc_pident_readdir(filp, ctx, \
> +                                  LSM##_attr_dir_stuff, \
> +                                  ARRAY_SIZE(LSM##_attr_dir_stuff)); \
> +} \
> +\
> +static const struct file_operations proc_##LSM##_attr_dir_ops = { \
> +       .read           = generic_read_dir, \
> +       .iterate        = proc_##LSM##_attr_dir_iterate, \
> +       .llseek         = default_llseek, \
> +}; \
> +\
> +static struct dentry *proc_##LSM##_attr_dir_lookup(struct inode *dir, \
> +                               struct dentry *dentry, unsigned int flags) \
> +{ \
> +       return proc_pident_lookup(dir, dentry, \
> +                                 LSM##_attr_dir_stuff, \
> +                                 ARRAY_SIZE(LSM##_attr_dir_stuff)); \
> +} \
> +\
> +static const struct inode_operations proc_##LSM##_attr_dir_inode_ops = { \
> +       .lookup         = proc_##LSM##_attr_dir_lookup, \
> +       .getattr        = pid_getattr, \
> +       .setattr        = proc_setattr, \
> +}
> +
> +#ifdef CONFIG_SECURITY_SELINUX
> +static const struct pid_entry selinux_attr_dir_stuff[] = {
> +       ATTR("selinux", "current",      S_IRUGO|S_IWUGO),
> +       ATTR("selinux", "prev",         S_IRUGO),
> +       ATTR("selinux", "exec",         S_IRUGO|S_IWUGO),
> +       ATTR("selinux", "fscreate",     S_IRUGO|S_IWUGO),
> +       ATTR("selinux", "keycreate",    S_IRUGO|S_IWUGO),
> +       ATTR("selinux", "sockcreate",   S_IRUGO|S_IWUGO),
> +};
> +LSM_DIR_OPS(selinux);
> +#endif
> +
> +#ifdef CONFIG_SECURITY_SMACK
> +static const struct pid_entry smack_attr_dir_stuff[] = {
> +       ATTR("smack", "current",        S_IRUGO|S_IWUGO),
> +};
> +LSM_DIR_OPS(smack);
> +#endif
> +
> +#ifdef CONFIG_SECURITY_APPARMOR
> +static const struct pid_entry apparmor_attr_dir_stuff[] = {
> +       ATTR("apparmor", "current",     S_IRUGO|S_IWUGO),
> +       ATTR("apparmor", "prev",        S_IRUGO),
> +       ATTR("apparmor", "exec",        S_IRUGO|S_IWUGO),
> +};
> +LSM_DIR_OPS(apparmor);
> +#endif
> +
>  static const struct pid_entry attr_dir_stuff[] = {
> -       REG("current",    S_IRUGO|S_IWUGO, proc_pid_attr_operations),
> -       REG("prev",       S_IRUGO,         proc_pid_attr_operations),
> -       REG("exec",       S_IRUGO|S_IWUGO, proc_pid_attr_operations),
> -       REG("fscreate",   S_IRUGO|S_IWUGO, proc_pid_attr_operations),
> -       REG("keycreate",  S_IRUGO|S_IWUGO, proc_pid_attr_operations),
> -       REG("sockcreate", S_IRUGO|S_IWUGO, proc_pid_attr_operations),
> +       ATTR(NULL, "current",           S_IRUGO|S_IWUGO),
> +       ATTR(NULL, "prev",              S_IRUGO),
> +       ATTR(NULL, "exec",              S_IRUGO|S_IWUGO),
> +       ATTR(NULL, "fscreate",          S_IRUGO|S_IWUGO),
> +       ATTR(NULL, "keycreate",         S_IRUGO|S_IWUGO),
> +       ATTR(NULL, "sockcreate",        S_IRUGO|S_IWUGO),
> +#ifdef CONFIG_SECURITY_SELINUX
> +       DIR("selinux",                  S_IRUGO|S_IXUGO,
> +           proc_selinux_attr_dir_inode_ops, proc_selinux_attr_dir_ops),
> +#endif
> +#ifdef CONFIG_SECURITY_SMACK
> +       DIR("smack",                    S_IRUGO|S_IXUGO,
> +           proc_smack_attr_dir_inode_ops, proc_smack_attr_dir_ops),
> +#endif
> +#ifdef CONFIG_SECURITY_APPARMOR
> +       DIR("apparmor",                 S_IRUGO|S_IXUGO,
> +           proc_apparmor_attr_dir_inode_ops, proc_apparmor_attr_dir_ops),
> +#endif
>  };

With the number of LSMs set to grow, it seems like it might be a lot
cleaner, and easier to maintain, if we moved the various LSM pid_entry
definitions into the LSMs themselves.  Granted, I say this without
seriously looking at how one would do that, I'm just mentioning it
here; it may prove to be more hassle than it is worth.

-- 
paul moore
www.paul-moore.com

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v4 3/3] LSM: Add context interface for proc attrs
  2016-06-23 21:11 ` [PATCH v4 3/3] LSM: Add context interface for proc attrs Casey Schaufler
  2016-06-23 21:49   ` Kees Cook
@ 2016-06-24 19:15   ` Paul Moore
  2016-06-24 19:56     ` Casey Schaufler
  1 sibling, 1 reply; 32+ messages in thread
From: Paul Moore @ 2016-06-24 19:15 UTC (permalink / raw)
  To: Casey Schaufler
  Cc: LSM, James Morris, John Johansen, Stephen Smalley, Tetsuo Handa,
	Kees Cook, LKLM

On Thu, Jun 23, 2016 at 5:11 PM, Casey Schaufler <casey@schaufler-ca.com> wrote:
> Subject: [PATCH v4 3/3] LSM: Add context interface for proc attrs
>
> The /proc/.../attr/current interface is used by all three
> Linux security modules (SELinux, Smack and AppArmor) to
> report and modify the process security attribute. This is
> all fine when there is exactly one of these modules active
> and the userspace code knows which it module it is.
> It would require a major change to the "current" interface
> to provide information about more than one set of process
> security attributes. Instead, a "context" attribute is
> added, which identifies the security module that the
> information applies to. The format is:
>
>         lsmname='context-value'
>
> When multiple concurrent modules are supported the
> /proc/.../attr/context interface will include the data
> for all of the active modules.
>
>         lsmname1='context-value1'lsmname2='context-value2'
>
> The module specific subdirectories under attr contain context
> entries that report the information for that specific module
> in the same format.

I think a delimiter between the different LSMs would be a good idea.
A comma seems like a safe choice at the moment.

-- 
paul moore
www.paul-moore.com

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v4 3/3] LSM: Add context interface for proc attrs
  2016-06-24 19:15   ` [PATCH v4 3/3] LSM: Add " Paul Moore
@ 2016-06-24 19:56     ` Casey Schaufler
  0 siblings, 0 replies; 32+ messages in thread
From: Casey Schaufler @ 2016-06-24 19:56 UTC (permalink / raw)
  To: Paul Moore
  Cc: LSM, James Morris, John Johansen, Stephen Smalley, Tetsuo Handa,
	Kees Cook, LKLM

On 6/24/2016 12:15 PM, Paul Moore wrote:
> On Thu, Jun 23, 2016 at 5:11 PM, Casey Schaufler <casey@schaufler-ca.com> wrote:
>> Subject: [PATCH v4 3/3] LSM: Add context interface for proc attrs
>>
>> The /proc/.../attr/current interface is used by all three
>> Linux security modules (SELinux, Smack and AppArmor) to
>> report and modify the process security attribute. This is
>> all fine when there is exactly one of these modules active
>> and the userspace code knows which it module it is.
>> It would require a major change to the "current" interface
>> to provide information about more than one set of process
>> security attributes. Instead, a "context" attribute is
>> added, which identifies the security module that the
>> information applies to. The format is:
>>
>>         lsmname='context-value'
>>
>> When multiple concurrent modules are supported the
>> /proc/.../attr/context interface will include the data
>> for all of the active modules.
>>
>>         lsmname1='context-value1'lsmname2='context-value2'
>>
>> The module specific subdirectories under attr contain context
>> entries that report the information for that specific module
>> in the same format.
> I think a delimiter between the different LSMs would be a good idea.
> A comma seems like a safe choice at the moment.

It's unnecessary and makes for more complicated code in
both directions, but everyone seems wedded to a delimiter.
I'll propose a v5 with a delimiter.

>

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v4 2/3] LSM: module hierarchy in /proc/.../attr
  2016-06-24 19:11   ` Paul Moore
@ 2016-06-24 20:05     ` Casey Schaufler
  2016-06-24 20:08       ` Kees Cook
  0 siblings, 1 reply; 32+ messages in thread
From: Casey Schaufler @ 2016-06-24 20:05 UTC (permalink / raw)
  To: Paul Moore
  Cc: LSM, James Morris, John Johansen, Stephen Smalley, Tetsuo Handa,
	Kees Cook, LKLM

On 6/24/2016 12:11 PM, Paul Moore wrote:
> On Thu, Jun 23, 2016 at 5:11 PM, Casey Schaufler <casey@schaufler-ca.com> wrote:
>> Subject: [PATCH v4 2/3] LSM: module hierarchy in /proc/.../attr
>>
>> Back in 2007 I made what turned out to be a rather serious
>> mistake in the implementation of the Smack security module.
>> The SELinux module used an interface in /proc to manipulate
>> the security context on processes. Rather than use a similar
>> interface, I used the same interface. The AppArmor team did
>> likewise. Now /proc/.../attr/current will tell you the
>> security "context" of the process, but it will be different
>> depending on the security module you're using. That hasn't
>> been a problem to date, as you can only have one module
>> that supports process attributes at a time. We are coming
>> up on a change to that, where multiple modules with process
>> attributes can be supported. (Not included here)
>>
>> This patch provides a subdirectory in /proc/.../attr for
>> each of the security modules that use the LSM hooks
>> getprocattr() and setprocattr(). Each of the interfaces
>> used by a module are presented in the subdirectory. The
>> old interfaces remain and work the same as before.
>> User space code can begin migrating to the subdirectory
>> interfaces in anticipation of the time when what comes
>> from /proc/self/attr/current might not be what a runtime
>> wants.
>>
>> The original implementation is by Kees Cook. The code
>> has been changed a bit to reflect changes in the direction
>> of the multiple concurrent module work, to be independent
>> of it, and to bring it up to date with the current tree.
>>
>> Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
>>
>> ---
>>  Documentation/security/LSM.txt | 26 +++++++++---
>>  fs/proc/base.c                 | 91 +++++++++++++++++++++++++++++++++++++-----
>>  fs/proc/internal.h             |  1 +
>>  include/linux/security.h       | 15 ++++---
>>  security/security.c            | 31 ++++++++++++--
>>  5 files changed, 140 insertions(+), 24 deletions(-)
>>
>> diff --git a/Documentation/security/LSM.txt b/Documentation/security/LSM.txt
>> index 3db7e67..125c489 100644
>> --- a/Documentation/security/LSM.txt
>> +++ b/Documentation/security/LSM.txt
>> @@ -16,11 +16,25 @@ MAC extensions, other extensions can be built using the LSM to provide
>>  specific changes to system operation when these tweaks are not available
>>  in the core functionality of Linux itself.
>>
>> -Without a specific LSM built into the kernel, the default LSM will be the
>> -Linux capabilities system. Most LSMs choose to extend the capabilities
>> -system, building their checks on top of the defined capability hooks.
>> -For more details on capabilities, see capabilities(7) in the Linux
>> -man-pages project.
>> +The Linux capabilities modules will always be included. For more details
>> +on capabilities, see capabilities(7) in the Linux man-pages project.
>> +This may be followed by any number of "minor" modules and at most one
>> +"major" module.
>> +
>> +A list of the active security modules can be found by reading
>> +/sys/kernel/security/lsm. This is a comma separated list, and
>> +will always include the capability module. The list reflects the
>> +order in which checks are made. The capability module will always
>> +be first, followed by any "minor" modules (e.g. Yama) and then
>> +the one "major" module (e.g. SELinux) if there is one configured.
> I wouldn't respin it just for this, but it seems like the paragraph
> above should really be part of patch 1/3, yes?

Yes. I can fix that for v5.

>> +Process attributes associated with "ma
>> diff --git a/fs/proc/base.c b/fs/proc/base.c
>> index a11eb71..182bc28 100644
>> --- a/fs/proc/base.c
>> +++ b/fs/proc/base.c
>> @@ -131,9 +131,13 @@ struct pid_entry {
>>  #define REG(NAME, MODE, fops)                          \
>>         NOD(NAME, (S_IFREG|(MODE)), NULL, &fops, {})
>>  #define ONE(NAME, MODE, show)                          \
>> -       NOD(NAME, (S_IFREG|(MODE)),                     \
>> +       NOD(NAME, (S_IFREG|(MODE)),                     \
>>                 NULL, &proc_single_file_operations,     \
>>                 { .proc_show = show } )
>> +#define ATTR(LSM, NAME, MODE)                          \
>> +       NOD(NAME, (S_IFREG|(MODE)),                     \
>> +               NULL, &proc_pid_attr_operations,        \
>> +               { .lsm = LSM })
>>
>>  /*
>>   * Count the number of hardlinks for the pid_entry table, excluding the .
>> @@ -2433,7 +2437,7 @@ static ssize_t proc_pid_attr_read(struct file * file, char __user * buf,
>>         if (!task)
>>                 return -ESRCH;
>>
>> -       length = security_getprocattr(task,
>> +       length = security_getprocattr(task, PROC_I(inode)->op.lsm,
>>                                       (char*)file->f_path.dentry->d_name.name,
>>                                       &p);
>>         put_task_struct(task);
>> @@ -2473,7 +2477,7 @@ static ssize_t proc_pid_attr_write(struct file * file, const char __user * buf,
>>         if (length < 0)
>>                 goto out_free;
>>
>> -       length = security_setprocattr(task,
>> +       length = security_setprocattr(task, PROC_I(inode)->op.lsm,
>>                                       (char*)file->f_path.dentry->d_name.name,
>>                                       page, count);
>>         mutex_unlock(&task->signal->cred_guard_mutex);
>> @@ -2491,13 +2495,82 @@ static const struct file_operations proc_pid_attr_operations = {
>>         .llseek         = generic_file_llseek,
>>  };
>>
>> +#define LSM_DIR_OPS(LSM) \
>> +static int proc_##LSM##_attr_dir_iterate(struct file *filp, \
>> +                            struct dir_context *ctx) \
>> +{ \
>> +       return proc_pident_readdir(filp, ctx, \
>> +                                  LSM##_attr_dir_stuff, \
>> +                                  ARRAY_SIZE(LSM##_attr_dir_stuff)); \
>> +} \
>> +\
>> +static const struct file_operations proc_##LSM##_attr_dir_ops = { \
>> +       .read           = generic_read_dir, \
>> +       .iterate        = proc_##LSM##_attr_dir_iterate, \
>> +       .llseek         = default_llseek, \
>> +}; \
>> +\
>> +static struct dentry *proc_##LSM##_attr_dir_lookup(struct inode *dir, \
>> +                               struct dentry *dentry, unsigned int flags) \
>> +{ \
>> +       return proc_pident_lookup(dir, dentry, \
>> +                                 LSM##_attr_dir_stuff, \
>> +                                 ARRAY_SIZE(LSM##_attr_dir_stuff)); \
>> +} \
>> +\
>> +static const struct inode_operations proc_##LSM##_attr_dir_inode_ops = { \
>> +       .lookup         = proc_##LSM##_attr_dir_lookup, \
>> +       .getattr        = pid_getattr, \
>> +       .setattr        = proc_setattr, \
>> +}
>> +
>> +#ifdef CONFIG_SECURITY_SELINUX
>> +static const struct pid_entry selinux_attr_dir_stuff[] = {
>> +       ATTR("selinux", "current",      S_IRUGO|S_IWUGO),
>> +       ATTR("selinux", "prev",         S_IRUGO),
>> +       ATTR("selinux", "exec",         S_IRUGO|S_IWUGO),
>> +       ATTR("selinux", "fscreate",     S_IRUGO|S_IWUGO),
>> +       ATTR("selinux", "keycreate",    S_IRUGO|S_IWUGO),
>> +       ATTR("selinux", "sockcreate",   S_IRUGO|S_IWUGO),
>> +};
>> +LSM_DIR_OPS(selinux);
>> +#endif
>> +
>> +#ifdef CONFIG_SECURITY_SMACK
>> +static const struct pid_entry smack_attr_dir_stuff[] = {
>> +       ATTR("smack", "current",        S_IRUGO|S_IWUGO),
>> +};
>> +LSM_DIR_OPS(smack);
>> +#endif
>> +
>> +#ifdef CONFIG_SECURITY_APPARMOR
>> +static const struct pid_entry apparmor_attr_dir_stuff[] = {
>> +       ATTR("apparmor", "current",     S_IRUGO|S_IWUGO),
>> +       ATTR("apparmor", "prev",        S_IRUGO),
>> +       ATTR("apparmor", "exec",        S_IRUGO|S_IWUGO),
>> +};
>> +LSM_DIR_OPS(apparmor);
>> +#endif
>> +
>>  static const struct pid_entry attr_dir_stuff[] = {
>> -       REG("current",    S_IRUGO|S_IWUGO, proc_pid_attr_operations),
>> -       REG("prev",       S_IRUGO,         proc_pid_attr_operations),
>> -       REG("exec",       S_IRUGO|S_IWUGO, proc_pid_attr_operations),
>> -       REG("fscreate",   S_IRUGO|S_IWUGO, proc_pid_attr_operations),
>> -       REG("keycreate",  S_IRUGO|S_IWUGO, proc_pid_attr_operations),
>> -       REG("sockcreate", S_IRUGO|S_IWUGO, proc_pid_attr_operations),
>> +       ATTR(NULL, "current",           S_IRUGO|S_IWUGO),
>> +       ATTR(NULL, "prev",              S_IRUGO),
>> +       ATTR(NULL, "exec",              S_IRUGO|S_IWUGO),
>> +       ATTR(NULL, "fscreate",          S_IRUGO|S_IWUGO),
>> +       ATTR(NULL, "keycreate",         S_IRUGO|S_IWUGO),
>> +       ATTR(NULL, "sockcreate",        S_IRUGO|S_IWUGO),
>> +#ifdef CONFIG_SECURITY_SELINUX
>> +       DIR("selinux",                  S_IRUGO|S_IXUGO,
>> +           proc_selinux_attr_dir_inode_ops, proc_selinux_attr_dir_ops),
>> +#endif
>> +#ifdef CONFIG_SECURITY_SMACK
>> +       DIR("smack",                    S_IRUGO|S_IXUGO,
>> +           proc_smack_attr_dir_inode_ops, proc_smack_attr_dir_ops),
>> +#endif
>> +#ifdef CONFIG_SECURITY_APPARMOR
>> +       DIR("apparmor",                 S_IRUGO|S_IXUGO,
>> +           proc_apparmor_attr_dir_inode_ops, proc_apparmor_attr_dir_ops),
>> +#endif
>>  };
> With the number of LSMs set to grow, it seems like it might be a lot
> cleaner, and easier to maintain, if we moved the various LSM pid_entry
> definitions into the LSMs themselves.  Granted, I say this without
> seriously looking at how one would do that, I'm just mentioning it
> here; it may prove to be more hassle than it is worth.
>
I have looked into doing it that way, but have yet to
come up with anything that would work. It seems like a
wonderful challenge for a young, nimble brain. Or maybe
an old wise one. In neither case, mine.

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v4 2/3] LSM: module hierarchy in /proc/.../attr
  2016-06-24 20:05     ` Casey Schaufler
@ 2016-06-24 20:08       ` Kees Cook
  2016-06-24 20:29         ` Paul Moore
  0 siblings, 1 reply; 32+ messages in thread
From: Kees Cook @ 2016-06-24 20:08 UTC (permalink / raw)
  To: Casey Schaufler
  Cc: Paul Moore, LSM, James Morris, John Johansen, Stephen Smalley,
	Tetsuo Handa, LKLM

On Fri, Jun 24, 2016 at 1:05 PM, Casey Schaufler <casey@schaufler-ca.com> wrote:
> On 6/24/2016 12:11 PM, Paul Moore wrote:
>> On Thu, Jun 23, 2016 at 5:11 PM, Casey Schaufler <casey@schaufler-ca.com> wrote:
>>> Subject: [PATCH v4 2/3] LSM: module hierarchy in /proc/.../attr
>>>
>>> Back in 2007 I made what turned out to be a rather serious
>>> mistake in the implementation of the Smack security module.
>>> The SELinux module used an interface in /proc to manipulate
>>> the security context on processes. Rather than use a similar
>>> interface, I used the same interface. The AppArmor team did
>>> likewise. Now /proc/.../attr/current will tell you the
>>> security "context" of the process, but it will be different
>>> depending on the security module you're using. That hasn't
>>> been a problem to date, as you can only have one module
>>> that supports process attributes at a time. We are coming
>>> up on a change to that, where multiple modules with process
>>> attributes can be supported. (Not included here)
>>>
>>> This patch provides a subdirectory in /proc/.../attr for
>>> each of the security modules that use the LSM hooks
>>> getprocattr() and setprocattr(). Each of the interfaces
>>> used by a module are presented in the subdirectory. The
>>> old interfaces remain and work the same as before.
>>> User space code can begin migrating to the subdirectory
>>> interfaces in anticipation of the time when what comes
>>> from /proc/self/attr/current might not be what a runtime
>>> wants.
>>>
>>> The original implementation is by Kees Cook. The code
>>> has been changed a bit to reflect changes in the direction
>>> of the multiple concurrent module work, to be independent
>>> of it, and to bring it up to date with the current tree.
>>>
>>> Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
>>>
>>> ---
>>>  Documentation/security/LSM.txt | 26 +++++++++---
>>>  fs/proc/base.c                 | 91 +++++++++++++++++++++++++++++++++++++-----
>>>  fs/proc/internal.h             |  1 +
>>>  include/linux/security.h       | 15 ++++---
>>>  security/security.c            | 31 ++++++++++++--
>>>  5 files changed, 140 insertions(+), 24 deletions(-)
>>>
>>> diff --git a/Documentation/security/LSM.txt b/Documentation/security/LSM.txt
>>> index 3db7e67..125c489 100644
>>> --- a/Documentation/security/LSM.txt
>>> +++ b/Documentation/security/LSM.txt
>>> @@ -16,11 +16,25 @@ MAC extensions, other extensions can be built using the LSM to provide
>>>  specific changes to system operation when these tweaks are not available
>>>  in the core functionality of Linux itself.
>>>
>>> -Without a specific LSM built into the kernel, the default LSM will be the
>>> -Linux capabilities system. Most LSMs choose to extend the capabilities
>>> -system, building their checks on top of the defined capability hooks.
>>> -For more details on capabilities, see capabilities(7) in the Linux
>>> -man-pages project.
>>> +The Linux capabilities modules will always be included. For more details
>>> +on capabilities, see capabilities(7) in the Linux man-pages project.
>>> +This may be followed by any number of "minor" modules and at most one
>>> +"major" module.
>>> +
>>> +A list of the active security modules can be found by reading
>>> +/sys/kernel/security/lsm. This is a comma separated list, and
>>> +will always include the capability module. The list reflects the
>>> +order in which checks are made. The capability module will always
>>> +be first, followed by any "minor" modules (e.g. Yama) and then
>>> +the one "major" module (e.g. SELinux) if there is one configured.
>> I wouldn't respin it just for this, but it seems like the paragraph
>> above should really be part of patch 1/3, yes?
>
> Yes. I can fix that for v5.
>
>>> +Process attributes associated with "ma
>>> diff --git a/fs/proc/base.c b/fs/proc/base.c
>>> index a11eb71..182bc28 100644
>>> --- a/fs/proc/base.c
>>> +++ b/fs/proc/base.c
>>> @@ -131,9 +131,13 @@ struct pid_entry {
>>>  #define REG(NAME, MODE, fops)                          \
>>>         NOD(NAME, (S_IFREG|(MODE)), NULL, &fops, {})
>>>  #define ONE(NAME, MODE, show)                          \
>>> -       NOD(NAME, (S_IFREG|(MODE)),                     \
>>> +       NOD(NAME, (S_IFREG|(MODE)),                     \
>>>                 NULL, &proc_single_file_operations,     \
>>>                 { .proc_show = show } )
>>> +#define ATTR(LSM, NAME, MODE)                          \
>>> +       NOD(NAME, (S_IFREG|(MODE)),                     \
>>> +               NULL, &proc_pid_attr_operations,        \
>>> +               { .lsm = LSM })
>>>
>>>  /*
>>>   * Count the number of hardlinks for the pid_entry table, excluding the .
>>> @@ -2433,7 +2437,7 @@ static ssize_t proc_pid_attr_read(struct file * file, char __user * buf,
>>>         if (!task)
>>>                 return -ESRCH;
>>>
>>> -       length = security_getprocattr(task,
>>> +       length = security_getprocattr(task, PROC_I(inode)->op.lsm,
>>>                                       (char*)file->f_path.dentry->d_name.name,
>>>                                       &p);
>>>         put_task_struct(task);
>>> @@ -2473,7 +2477,7 @@ static ssize_t proc_pid_attr_write(struct file * file, const char __user * buf,
>>>         if (length < 0)
>>>                 goto out_free;
>>>
>>> -       length = security_setprocattr(task,
>>> +       length = security_setprocattr(task, PROC_I(inode)->op.lsm,
>>>                                       (char*)file->f_path.dentry->d_name.name,
>>>                                       page, count);
>>>         mutex_unlock(&task->signal->cred_guard_mutex);
>>> @@ -2491,13 +2495,82 @@ static const struct file_operations proc_pid_attr_operations = {
>>>         .llseek         = generic_file_llseek,
>>>  };
>>>
>>> +#define LSM_DIR_OPS(LSM) \
>>> +static int proc_##LSM##_attr_dir_iterate(struct file *filp, \
>>> +                            struct dir_context *ctx) \
>>> +{ \
>>> +       return proc_pident_readdir(filp, ctx, \
>>> +                                  LSM##_attr_dir_stuff, \
>>> +                                  ARRAY_SIZE(LSM##_attr_dir_stuff)); \
>>> +} \
>>> +\
>>> +static const struct file_operations proc_##LSM##_attr_dir_ops = { \
>>> +       .read           = generic_read_dir, \
>>> +       .iterate        = proc_##LSM##_attr_dir_iterate, \
>>> +       .llseek         = default_llseek, \
>>> +}; \
>>> +\
>>> +static struct dentry *proc_##LSM##_attr_dir_lookup(struct inode *dir, \
>>> +                               struct dentry *dentry, unsigned int flags) \
>>> +{ \
>>> +       return proc_pident_lookup(dir, dentry, \
>>> +                                 LSM##_attr_dir_stuff, \
>>> +                                 ARRAY_SIZE(LSM##_attr_dir_stuff)); \
>>> +} \
>>> +\
>>> +static const struct inode_operations proc_##LSM##_attr_dir_inode_ops = { \
>>> +       .lookup         = proc_##LSM##_attr_dir_lookup, \
>>> +       .getattr        = pid_getattr, \
>>> +       .setattr        = proc_setattr, \
>>> +}
>>> +
>>> +#ifdef CONFIG_SECURITY_SELINUX
>>> +static const struct pid_entry selinux_attr_dir_stuff[] = {
>>> +       ATTR("selinux", "current",      S_IRUGO|S_IWUGO),
>>> +       ATTR("selinux", "prev",         S_IRUGO),
>>> +       ATTR("selinux", "exec",         S_IRUGO|S_IWUGO),
>>> +       ATTR("selinux", "fscreate",     S_IRUGO|S_IWUGO),
>>> +       ATTR("selinux", "keycreate",    S_IRUGO|S_IWUGO),
>>> +       ATTR("selinux", "sockcreate",   S_IRUGO|S_IWUGO),
>>> +};
>>> +LSM_DIR_OPS(selinux);
>>> +#endif
>>> +
>>> +#ifdef CONFIG_SECURITY_SMACK
>>> +static const struct pid_entry smack_attr_dir_stuff[] = {
>>> +       ATTR("smack", "current",        S_IRUGO|S_IWUGO),
>>> +};
>>> +LSM_DIR_OPS(smack);
>>> +#endif
>>> +
>>> +#ifdef CONFIG_SECURITY_APPARMOR
>>> +static const struct pid_entry apparmor_attr_dir_stuff[] = {
>>> +       ATTR("apparmor", "current",     S_IRUGO|S_IWUGO),
>>> +       ATTR("apparmor", "prev",        S_IRUGO),
>>> +       ATTR("apparmor", "exec",        S_IRUGO|S_IWUGO),
>>> +};
>>> +LSM_DIR_OPS(apparmor);
>>> +#endif
>>> +
>>>  static const struct pid_entry attr_dir_stuff[] = {
>>> -       REG("current",    S_IRUGO|S_IWUGO, proc_pid_attr_operations),
>>> -       REG("prev",       S_IRUGO,         proc_pid_attr_operations),
>>> -       REG("exec",       S_IRUGO|S_IWUGO, proc_pid_attr_operations),
>>> -       REG("fscreate",   S_IRUGO|S_IWUGO, proc_pid_attr_operations),
>>> -       REG("keycreate",  S_IRUGO|S_IWUGO, proc_pid_attr_operations),
>>> -       REG("sockcreate", S_IRUGO|S_IWUGO, proc_pid_attr_operations),
>>> +       ATTR(NULL, "current",           S_IRUGO|S_IWUGO),
>>> +       ATTR(NULL, "prev",              S_IRUGO),
>>> +       ATTR(NULL, "exec",              S_IRUGO|S_IWUGO),
>>> +       ATTR(NULL, "fscreate",          S_IRUGO|S_IWUGO),
>>> +       ATTR(NULL, "keycreate",         S_IRUGO|S_IWUGO),
>>> +       ATTR(NULL, "sockcreate",        S_IRUGO|S_IWUGO),
>>> +#ifdef CONFIG_SECURITY_SELINUX
>>> +       DIR("selinux",                  S_IRUGO|S_IXUGO,
>>> +           proc_selinux_attr_dir_inode_ops, proc_selinux_attr_dir_ops),
>>> +#endif
>>> +#ifdef CONFIG_SECURITY_SMACK
>>> +       DIR("smack",                    S_IRUGO|S_IXUGO,
>>> +           proc_smack_attr_dir_inode_ops, proc_smack_attr_dir_ops),
>>> +#endif
>>> +#ifdef CONFIG_SECURITY_APPARMOR
>>> +       DIR("apparmor",                 S_IRUGO|S_IXUGO,
>>> +           proc_apparmor_attr_dir_inode_ops, proc_apparmor_attr_dir_ops),
>>> +#endif
>>>  };
>> With the number of LSMs set to grow, it seems like it might be a lot
>> cleaner, and easier to maintain, if we moved the various LSM pid_entry
>> definitions into the LSMs themselves.  Granted, I say this without
>> seriously looking at how one would do that, I'm just mentioning it
>> here; it may prove to be more hassle than it is worth.

I had the same suggestion, and when I looked at what it would take, I
decided this was just fine. ;)

> I have looked into doing it that way, but have yet to
> come up with anything that would work. It seems like a
> wonderful challenge for a young, nimble brain. Or maybe
> an old wise one. In neither case, mine.

I think it would require creating a number of new APIs to the proc
interface, and none of it looked fun.

-Kees

-- 
Kees Cook
Chrome OS & Brillo Security

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v4 2/3] LSM: module hierarchy in /proc/.../attr
  2016-06-24 20:08       ` Kees Cook
@ 2016-06-24 20:29         ` Paul Moore
  2016-06-24 23:26           ` [PATCH v5 0/3] LSM: security module information improvements Casey Schaufler
  0 siblings, 1 reply; 32+ messages in thread
From: Paul Moore @ 2016-06-24 20:29 UTC (permalink / raw)
  To: Kees Cook, Casey Schaufler
  Cc: LSM, James Morris, John Johansen, Stephen Smalley, Tetsuo Handa, LKLM

On Fri, Jun 24, 2016 at 4:08 PM, Kees Cook <keescook@chromium.org> wrote:
> On Fri, Jun 24, 2016 at 1:05 PM, Casey Schaufler <casey@schaufler-ca.com> wrote:
>> On 6/24/2016 12:11 PM, Paul Moore wrote:
>>> On Thu, Jun 23, 2016 at 5:11 PM, Casey Schaufler <casey@schaufler-ca.com> wrote:
>>>> Subject: [PATCH v4 2/3] LSM: module hierarchy in /proc/.../attr
>>>>
>>>> Back in 2007 I made what turned out to be a rather serious
>>>> mistake in the implementation of the Smack security module.
>>>> The SELinux module used an interface in /proc to manipulate
>>>> the security context on processes. Rather than use a similar
>>>> interface, I used the same interface. The AppArmor team did
>>>> likewise. Now /proc/.../attr/current will tell you the
>>>> security "context" of the process, but it will be different
>>>> depending on the security module you're using. That hasn't
>>>> been a problem to date, as you can only have one module
>>>> that supports process attributes at a time. We are coming
>>>> up on a change to that, where multiple modules with process
>>>> attributes can be supported. (Not included here)
>>>>
>>>> This patch provides a subdirectory in /proc/.../attr for
>>>> each of the security modules that use the LSM hooks
>>>> getprocattr() and setprocattr(). Each of the interfaces
>>>> used by a module are presented in the subdirectory. The
>>>> old interfaces remain and work the same as before.
>>>> User space code can begin migrating to the subdirectory
>>>> interfaces in anticipation of the time when what comes
>>>> from /proc/self/attr/current might not be what a runtime
>>>> wants.
>>>>
>>>> The original implementation is by Kees Cook. The code
>>>> has been changed a bit to reflect changes in the direction
>>>> of the multiple concurrent module work, to be independent
>>>> of it, and to bring it up to date with the current tree.
>>>>
>>>> Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
>>>>
>>>> ---
>>>>  Documentation/security/LSM.txt | 26 +++++++++---
>>>>  fs/proc/base.c                 | 91 +++++++++++++++++++++++++++++++++++++-----
>>>>  fs/proc/internal.h             |  1 +
>>>>  include/linux/security.h       | 15 ++++---
>>>>  security/security.c            | 31 ++++++++++++--
>>>>  5 files changed, 140 insertions(+), 24 deletions(-)
>>>>
>>>> diff --git a/Documentation/security/LSM.txt b/Documentation/security/LSM.txt
>>>> index 3db7e67..125c489 100644
>>>> --- a/Documentation/security/LSM.txt
>>>> +++ b/Documentation/security/LSM.txt
>>>> @@ -16,11 +16,25 @@ MAC extensions, other extensions can be built using the LSM to provide
>>>>  specific changes to system operation when these tweaks are not available
>>>>  in the core functionality of Linux itself.
>>>>
>>>> -Without a specific LSM built into the kernel, the default LSM will be the
>>>> -Linux capabilities system. Most LSMs choose to extend the capabilities
>>>> -system, building their checks on top of the defined capability hooks.
>>>> -For more details on capabilities, see capabilities(7) in the Linux
>>>> -man-pages project.
>>>> +The Linux capabilities modules will always be included. For more details
>>>> +on capabilities, see capabilities(7) in the Linux man-pages project.
>>>> +This may be followed by any number of "minor" modules and at most one
>>>> +"major" module.
>>>> +
>>>> +A list of the active security modules can be found by reading
>>>> +/sys/kernel/security/lsm. This is a comma separated list, and
>>>> +will always include the capability module. The list reflects the
>>>> +order in which checks are made. The capability module will always
>>>> +be first, followed by any "minor" modules (e.g. Yama) and then
>>>> +the one "major" module (e.g. SELinux) if there is one configured.
>>> I wouldn't respin it just for this, but it seems like the paragraph
>>> above should really be part of patch 1/3, yes?
>>
>> Yes. I can fix that for v5.
>>
>>>> +Process attributes associated with "ma
>>>> diff --git a/fs/proc/base.c b/fs/proc/base.c
>>>> index a11eb71..182bc28 100644
>>>> --- a/fs/proc/base.c
>>>> +++ b/fs/proc/base.c
>>>> @@ -131,9 +131,13 @@ struct pid_entry {
>>>>  #define REG(NAME, MODE, fops)                          \
>>>>         NOD(NAME, (S_IFREG|(MODE)), NULL, &fops, {})
>>>>  #define ONE(NAME, MODE, show)                          \
>>>> -       NOD(NAME, (S_IFREG|(MODE)),                     \
>>>> +       NOD(NAME, (S_IFREG|(MODE)),                     \
>>>>                 NULL, &proc_single_file_operations,     \
>>>>                 { .proc_show = show } )
>>>> +#define ATTR(LSM, NAME, MODE)                          \
>>>> +       NOD(NAME, (S_IFREG|(MODE)),                     \
>>>> +               NULL, &proc_pid_attr_operations,        \
>>>> +               { .lsm = LSM })
>>>>
>>>>  /*
>>>>   * Count the number of hardlinks for the pid_entry table, excluding the .
>>>> @@ -2433,7 +2437,7 @@ static ssize_t proc_pid_attr_read(struct file * file, char __user * buf,
>>>>         if (!task)
>>>>                 return -ESRCH;
>>>>
>>>> -       length = security_getprocattr(task,
>>>> +       length = security_getprocattr(task, PROC_I(inode)->op.lsm,
>>>>                                       (char*)file->f_path.dentry->d_name.name,
>>>>                                       &p);
>>>>         put_task_struct(task);
>>>> @@ -2473,7 +2477,7 @@ static ssize_t proc_pid_attr_write(struct file * file, const char __user * buf,
>>>>         if (length < 0)
>>>>                 goto out_free;
>>>>
>>>> -       length = security_setprocattr(task,
>>>> +       length = security_setprocattr(task, PROC_I(inode)->op.lsm,
>>>>                                       (char*)file->f_path.dentry->d_name.name,
>>>>                                       page, count);
>>>>         mutex_unlock(&task->signal->cred_guard_mutex);
>>>> @@ -2491,13 +2495,82 @@ static const struct file_operations proc_pid_attr_operations = {
>>>>         .llseek         = generic_file_llseek,
>>>>  };
>>>>
>>>> +#define LSM_DIR_OPS(LSM) \
>>>> +static int proc_##LSM##_attr_dir_iterate(struct file *filp, \
>>>> +                            struct dir_context *ctx) \
>>>> +{ \
>>>> +       return proc_pident_readdir(filp, ctx, \
>>>> +                                  LSM##_attr_dir_stuff, \
>>>> +                                  ARRAY_SIZE(LSM##_attr_dir_stuff)); \
>>>> +} \
>>>> +\
>>>> +static const struct file_operations proc_##LSM##_attr_dir_ops = { \
>>>> +       .read           = generic_read_dir, \
>>>> +       .iterate        = proc_##LSM##_attr_dir_iterate, \
>>>> +       .llseek         = default_llseek, \
>>>> +}; \
>>>> +\
>>>> +static struct dentry *proc_##LSM##_attr_dir_lookup(struct inode *dir, \
>>>> +                               struct dentry *dentry, unsigned int flags) \
>>>> +{ \
>>>> +       return proc_pident_lookup(dir, dentry, \
>>>> +                                 LSM##_attr_dir_stuff, \
>>>> +                                 ARRAY_SIZE(LSM##_attr_dir_stuff)); \
>>>> +} \
>>>> +\
>>>> +static const struct inode_operations proc_##LSM##_attr_dir_inode_ops = { \
>>>> +       .lookup         = proc_##LSM##_attr_dir_lookup, \
>>>> +       .getattr        = pid_getattr, \
>>>> +       .setattr        = proc_setattr, \
>>>> +}
>>>> +
>>>> +#ifdef CONFIG_SECURITY_SELINUX
>>>> +static const struct pid_entry selinux_attr_dir_stuff[] = {
>>>> +       ATTR("selinux", "current",      S_IRUGO|S_IWUGO),
>>>> +       ATTR("selinux", "prev",         S_IRUGO),
>>>> +       ATTR("selinux", "exec",         S_IRUGO|S_IWUGO),
>>>> +       ATTR("selinux", "fscreate",     S_IRUGO|S_IWUGO),
>>>> +       ATTR("selinux", "keycreate",    S_IRUGO|S_IWUGO),
>>>> +       ATTR("selinux", "sockcreate",   S_IRUGO|S_IWUGO),
>>>> +};
>>>> +LSM_DIR_OPS(selinux);
>>>> +#endif
>>>> +
>>>> +#ifdef CONFIG_SECURITY_SMACK
>>>> +static const struct pid_entry smack_attr_dir_stuff[] = {
>>>> +       ATTR("smack", "current",        S_IRUGO|S_IWUGO),
>>>> +};
>>>> +LSM_DIR_OPS(smack);
>>>> +#endif
>>>> +
>>>> +#ifdef CONFIG_SECURITY_APPARMOR
>>>> +static const struct pid_entry apparmor_attr_dir_stuff[] = {
>>>> +       ATTR("apparmor", "current",     S_IRUGO|S_IWUGO),
>>>> +       ATTR("apparmor", "prev",        S_IRUGO),
>>>> +       ATTR("apparmor", "exec",        S_IRUGO|S_IWUGO),
>>>> +};
>>>> +LSM_DIR_OPS(apparmor);
>>>> +#endif
>>>> +
>>>>  static const struct pid_entry attr_dir_stuff[] = {
>>>> -       REG("current",    S_IRUGO|S_IWUGO, proc_pid_attr_operations),
>>>> -       REG("prev",       S_IRUGO,         proc_pid_attr_operations),
>>>> -       REG("exec",       S_IRUGO|S_IWUGO, proc_pid_attr_operations),
>>>> -       REG("fscreate",   S_IRUGO|S_IWUGO, proc_pid_attr_operations),
>>>> -       REG("keycreate",  S_IRUGO|S_IWUGO, proc_pid_attr_operations),
>>>> -       REG("sockcreate", S_IRUGO|S_IWUGO, proc_pid_attr_operations),
>>>> +       ATTR(NULL, "current",           S_IRUGO|S_IWUGO),
>>>> +       ATTR(NULL, "prev",              S_IRUGO),
>>>> +       ATTR(NULL, "exec",              S_IRUGO|S_IWUGO),
>>>> +       ATTR(NULL, "fscreate",          S_IRUGO|S_IWUGO),
>>>> +       ATTR(NULL, "keycreate",         S_IRUGO|S_IWUGO),
>>>> +       ATTR(NULL, "sockcreate",        S_IRUGO|S_IWUGO),
>>>> +#ifdef CONFIG_SECURITY_SELINUX
>>>> +       DIR("selinux",                  S_IRUGO|S_IXUGO,
>>>> +           proc_selinux_attr_dir_inode_ops, proc_selinux_attr_dir_ops),
>>>> +#endif
>>>> +#ifdef CONFIG_SECURITY_SMACK
>>>> +       DIR("smack",                    S_IRUGO|S_IXUGO,
>>>> +           proc_smack_attr_dir_inode_ops, proc_smack_attr_dir_ops),
>>>> +#endif
>>>> +#ifdef CONFIG_SECURITY_APPARMOR
>>>> +       DIR("apparmor",                 S_IRUGO|S_IXUGO,
>>>> +           proc_apparmor_attr_dir_inode_ops, proc_apparmor_attr_dir_ops),
>>>> +#endif
>>>>  };
>>> With the number of LSMs set to grow, it seems like it might be a lot
>>> cleaner, and easier to maintain, if we moved the various LSM pid_entry
>>> definitions into the LSMs themselves.  Granted, I say this without
>>> seriously looking at how one would do that, I'm just mentioning it
>>> here; it may prove to be more hassle than it is worth.
>
> I had the same suggestion, and when I looked at what it would take, I
> decided this was just fine. ;)
>
>> I have looked into doing it that way, but have yet to
>> come up with anything that would work. It seems like a
>> wonderful challenge for a young, nimble brain. Or maybe
>> an old wise one. In neither case, mine.
>
> I think it would require creating a number of new APIs to the proc
> interface, and none of it looked fun.

Okay, I can live with that.

-- 
paul moore
www.paul-moore.com

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH v5 0/3] LSM: security module information improvements
  2016-06-24 20:29         ` Paul Moore
@ 2016-06-24 23:26           ` Casey Schaufler
  2016-06-24 23:27             ` [PATCH v5 1/3] LSM: Add /sys/kernel/security/lsm Casey Schaufler
                               ` (2 more replies)
  0 siblings, 3 replies; 32+ messages in thread
From: Casey Schaufler @ 2016-06-24 23:26 UTC (permalink / raw)
  To: Paul Moore, Kees Cook
  Cc: LSM, James Morris, John Johansen, Stephen Smalley, Tetsuo Handa, LKLM

Subject: [PATCH v5 0/3] LSM: security module information improvements

Changes from v4:
	Use kasprintf instead of kzalloc() ... sprintf in more places.
	More in the documentation.
	Separate module information in contexts with ",". (not yet visible)

Changes from v3:
	Use kasprintf instead of kzalloc() ... sprintf.

Create interfaces that make it possible to deal with process
attributes in the face of multiple "major" security modules.

Patch 1/3 adds /sys/kernel/security/lsm, which provides
a list of the active security modules on the system.

	$ cat /sys/kernel/security/lsm
	capability,yama,loadpin,smack

Patch 2/3 adds a subdirectory in /proc/.../attr for each
security module that exports process attribute data. This
allows a program in easily differentiate between the "current"
value for Smack and AppArmor.

	$ cat /proc/self/attr/smack/current
	System

	$ cat /proc/self/attr/apparmor/current
	unconfined

Patch 3/3 adds an interface that provides module identified
information that otherwise matches the "current" attr.
This allows a system with multiple modules to provide the
complete security "context" in one place. A (future) system
with both Smack and AppArmor might report:

	$ cat /proc/self/attr/context
	smack='System',apparmor='unconfined'

Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>

---
 Documentation/security/LSM.txt |  34 ++++++--
 fs/proc/base.c                 |  95 +++++++++++++++++++---
 fs/proc/internal.h             |   1 +
 include/linux/lsm_hooks.h      |  12 +--
 include/linux/security.h       |  15 ++--
 security/apparmor/lsm.c        |  38 +++++++--
 security/commoncap.c           |   3 +-
 security/inode.c               |  26 +++++-
 security/loadpin/loadpin.c     |   2 +-
 security/security.c            | 177 ++++++++++++++++++++++++++++++++++++++++-
 security/selinux/hooks.c       |  22 ++++-
 security/smack/smack_lsm.c     |  22 ++---
 security/tomoyo/tomoyo.c       |   2 +-
 security/yama/yama_lsm.c       |   2 +-
 14 files changed, 395 insertions(+), 56 deletions(-)

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH v5 1/3] LSM: Add /sys/kernel/security/lsm
  2016-06-24 23:26           ` [PATCH v5 0/3] LSM: security module information improvements Casey Schaufler
@ 2016-06-24 23:27             ` Casey Schaufler
  2016-06-29 17:01               ` Paul Moore
  2016-06-24 23:29             ` [PATCH v5 2/3] LSM: module hierarchy in /proc/.../attr Casey Schaufler
  2016-06-24 23:29             ` [PATCH v4 3/3] LSM: Add context interface for proc attrs Casey Schaufler
  2 siblings, 1 reply; 32+ messages in thread
From: Casey Schaufler @ 2016-06-24 23:27 UTC (permalink / raw)
  To: Paul Moore, Kees Cook
  Cc: LSM, James Morris, John Johansen, Stephen Smalley, Tetsuo Handa, LKLM

Subject: [PATCH v5 1/3] LSM: Add /sys/kernel/security/lsm

I got tired of having to find indirect ways to 
determine what security modules are active on a system.
I have added /sys/kernel/security/lsm, which contains a
comma separated list of the active security modules. No
more groping around in /proc/filesystems, which won't
help if the module doesn't support its own filesystem.

Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>

---
 Documentation/security/LSM.txt |  7 +++++++
 include/linux/lsm_hooks.h      | 12 ++++--------
 security/apparmor/lsm.c        |  3 ++-
 security/commoncap.c           |  3 ++-
 security/inode.c               | 26 ++++++++++++++++++++++++--
 security/loadpin/loadpin.c     |  2 +-
 security/security.c            | 38 ++++++++++++++++++++++++++++++++++++++
 security/selinux/hooks.c       |  2 +-
 security/smack/smack_lsm.c     |  2 +-
 security/tomoyo/tomoyo.c       |  2 +-
 security/yama/yama_lsm.c       |  2 +-
 11 files changed, 82 insertions(+), 17 deletions(-)

diff --git a/Documentation/security/LSM.txt b/Documentation/security/LSM.txt
index 3db7e67..c2683f2 100644
--- a/Documentation/security/LSM.txt
+++ b/Documentation/security/LSM.txt
@@ -22,6 +22,13 @@ system, building their checks on top of the defined capability hooks.
 For more details on capabilities, see capabilities(7) in the Linux
 man-pages project.
 
+A list of the active security modules can be found by reading
+/sys/kernel/security/lsm. This is a comma separated list, and
+will always include the capability module. The list reflects the
+order in which checks are made. The capability module will always
+be first, followed by any "minor" modules (e.g. Yama) and then
+the one "major" module (e.g. SELinux) if there is one configured.
+
 Based on https://lkml.org/lkml/2007/10/26/215,
 a new LSM is accepted into the kernel when its intent (a description of
 what it tries to protect against and in what cases one would expect to
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 7ae3976..0bfd441 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -1841,6 +1841,7 @@ struct security_hook_list {
 	struct list_head		list;
 	struct list_head		*head;
 	union security_list_options	hook;
+	char				*lsm;
 };
 
 /*
@@ -1853,15 +1854,10 @@ struct security_hook_list {
 	{ .head = &security_hook_heads.HEAD, .hook = { .HEAD = HOOK } }
 
 extern struct security_hook_heads security_hook_heads;
+extern char *lsm_names;
 
-static inline void security_add_hooks(struct security_hook_list *hooks,
-				      int count)
-{
-	int i;
-
-	for (i = 0; i < count; i++)
-		list_add_tail_rcu(&hooks[i].list, hooks[i].head);
-}
+extern void security_add_hooks(struct security_hook_list *hooks, int count,
+				char *lsm);
 
 #ifdef CONFIG_SECURITY_SELINUX_DISABLE
 /*
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 2660fbc..fb0fb03 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -880,7 +880,8 @@ static int __init apparmor_init(void)
 		aa_free_root_ns();
 		goto alloc_out;
 	}
-	security_add_hooks(apparmor_hooks, ARRAY_SIZE(apparmor_hooks));
+	security_add_hooks(apparmor_hooks, ARRAY_SIZE(apparmor_hooks),
+				"apparmor");
 
 	/* Report that AppArmor successfully initialized */
 	apparmor_initialized = 1;
diff --git a/security/commoncap.c b/security/commoncap.c
index e7fadde..d101734 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -1090,7 +1090,8 @@ struct security_hook_list capability_hooks[] = {
 
 void __init capability_add_hooks(void)
 {
-	security_add_hooks(capability_hooks, ARRAY_SIZE(capability_hooks));
+	security_add_hooks(capability_hooks, ARRAY_SIZE(capability_hooks),
+				"capability");
 }
 
 #endif /* CONFIG_SECURITY */
diff --git a/security/inode.c b/security/inode.c
index 28414b0..235ad82 100644
--- a/security/inode.c
+++ b/security/inode.c
@@ -20,6 +20,7 @@
 #include <linux/init.h>
 #include <linux/namei.h>
 #include <linux/security.h>
+#include <linux/lsm_hooks.h>
 #include <linux/magic.h>
 
 static struct vfsmount *mount;
@@ -208,6 +209,21 @@ void securityfs_remove(struct dentry *dentry)
 }
 EXPORT_SYMBOL_GPL(securityfs_remove);
 
+#ifdef CONFIG_SECURITY
+static struct dentry *lsm_dentry;
+static ssize_t lsm_read(struct file *filp, char __user *buf, size_t count,
+			loff_t *ppos)
+{
+	return simple_read_from_buffer(buf, count, ppos, lsm_names,
+		strlen(lsm_names));
+}
+
+static const struct file_operations lsm_ops = {
+	.read = lsm_read,
+	.llseek = generic_file_llseek,
+};
+#endif
+
 static int __init securityfs_init(void)
 {
 	int retval;
@@ -217,9 +233,15 @@ static int __init securityfs_init(void)
 		return retval;
 
 	retval = register_filesystem(&fs_type);
-	if (retval)
+	if (retval) {
 		sysfs_remove_mount_point(kernel_kobj, "security");
-	return retval;
+		return retval;
+	}
+#ifdef CONFIG_SECURITY
+	lsm_dentry = securityfs_create_file("lsm", S_IRUGO, NULL, NULL,
+						&lsm_ops);
+#endif
+	return 0;
 }
 
 core_initcall(securityfs_init);
diff --git a/security/loadpin/loadpin.c b/security/loadpin/loadpin.c
index 89a46f1..1d82eae 100644
--- a/security/loadpin/loadpin.c
+++ b/security/loadpin/loadpin.c
@@ -182,7 +182,7 @@ static struct security_hook_list loadpin_hooks[] = {
 void __init loadpin_add_hooks(void)
 {
 	pr_info("ready to pin (currently %sabled)", enabled ? "en" : "dis");
-	security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks));
+	security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks), "loadpin");
 }
 
 /* Should not be mutable after boot, so not listed in sysfs (perm == 0). */
diff --git a/security/security.c b/security/security.c
index c4bb47d..1093f73 100644
--- a/security/security.c
+++ b/security/security.c
@@ -32,6 +32,7 @@
 /* Maximum number of letters for an LSM name string */
 #define SECURITY_NAME_MAX	10
 
+char *lsm_names;
 /* Boot-time LSM user choice */
 static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =
 	CONFIG_DEFAULT_SECURITY;
@@ -78,6 +79,22 @@ static int __init choose_lsm(char *str)
 }
 __setup("security=", choose_lsm);
 
+static int lsm_append(char *new, char **result)
+{
+	char *cp;
+
+	if (*result == NULL) {
+		*result = kstrdup(new, GFP_KERNEL);
+	} else {
+		cp = kasprintf(GFP_KERNEL, "%s,%s", *result, new);
+		if (cp == NULL)
+			return -ENOMEM;
+		kfree(*result);
+		*result = cp;
+	}
+	return 0;
+}
+
 /**
  * security_module_enable - Load given security module on boot ?
  * @module: the name of the module
@@ -97,6 +114,27 @@ int __init security_module_enable(const char *module)
 	return !strcmp(module, chosen_lsm);
 }
 
+/**
+ * security_add_hooks - Add a modules hooks to the hook lists.
+ * @hooks: the hooks to add
+ * @count: the number of hooks to add
+ * @lsm: the name of the security module
+ *
+ * Each LSM has to register its hooks with the infrastructure.
+ */
+void __init security_add_hooks(struct security_hook_list *hooks, int count,
+				char *lsm)
+{
+	int i;
+
+	for (i = 0; i < count; i++) {
+		hooks[i].lsm = lsm;
+		list_add_tail_rcu(&hooks[i].list, hooks[i].head);
+	}
+	if (lsm_append(lsm, &lsm_names) < 0)
+		panic("%s - Cannot get early memory.\n", __func__);
+}
+
 /*
  * Hook list operation macros.
  *
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index a86d537..ed3a757 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -6233,7 +6233,7 @@ static __init int selinux_init(void)
 					    0, SLAB_PANIC, NULL);
 	avc_init();
 
-	security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks));
+	security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks), "selinux");
 
 	if (avc_add_callback(selinux_netcache_avc_callback, AVC_CALLBACK_RESET))
 		panic("SELinux: Unable to register AVC netcache callback\n");
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 6777295..3577009 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -4801,7 +4801,7 @@ static __init int smack_init(void)
 	/*
 	 * Register with LSM
 	 */
-	security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks));
+	security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks), "smack");
 
 	return 0;
 }
diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
index 75c9987..edc52d6 100644
--- a/security/tomoyo/tomoyo.c
+++ b/security/tomoyo/tomoyo.c
@@ -542,7 +542,7 @@ static int __init tomoyo_init(void)
 	if (!security_module_enable("tomoyo"))
 		return 0;
 	/* register ourselves with the security framework */
-	security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks));
+	security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks), "tomoyo");
 	printk(KERN_INFO "TOMOYO Linux initialized\n");
 	cred->security = &tomoyo_kernel_domain;
 	tomoyo_mm_init();
diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c
index 0309f21..f8ee60e 100644
--- a/security/yama/yama_lsm.c
+++ b/security/yama/yama_lsm.c
@@ -471,6 +471,6 @@ static inline void yama_init_sysctl(void) { }
 void __init yama_add_hooks(void)
 {
 	pr_info("Yama: becoming mindful.\n");
-	security_add_hooks(yama_hooks, ARRAY_SIZE(yama_hooks));
+	security_add_hooks(yama_hooks, ARRAY_SIZE(yama_hooks), "yama");
 	yama_init_sysctl();
 }

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v5 2/3] LSM: module hierarchy in /proc/.../attr
  2016-06-24 23:26           ` [PATCH v5 0/3] LSM: security module information improvements Casey Schaufler
  2016-06-24 23:27             ` [PATCH v5 1/3] LSM: Add /sys/kernel/security/lsm Casey Schaufler
@ 2016-06-24 23:29             ` Casey Schaufler
  2016-06-29 17:03               ` Paul Moore
  2016-06-24 23:29             ` [PATCH v4 3/3] LSM: Add context interface for proc attrs Casey Schaufler
  2 siblings, 1 reply; 32+ messages in thread
From: Casey Schaufler @ 2016-06-24 23:29 UTC (permalink / raw)
  To: Paul Moore, Kees Cook
  Cc: LSM, James Morris, John Johansen, Stephen Smalley, Tetsuo Handa, LKLM

Subject: [PATCH v5 2/3] LSM: module hierarchy in /proc/.../attr

Back in 2007 I made what turned out to be a rather serious
mistake in the implementation of the Smack security module.
The SELinux module used an interface in /proc to manipulate
the security context on processes. Rather than use a similar
interface, I used the same interface. The AppArmor team did
likewise. Now /proc/.../attr/current will tell you the
security "context" of the process, but it will be different
depending on the security module you're using. That hasn't
been a problem to date, as you can only have one module
that supports process attributes at a time. We are coming
up on a change to that, where multiple modules with process
attributes can be supported. (Not included here)

This patch provides a subdirectory in /proc/.../attr for
each of the security modules that use the LSM hooks
getprocattr() and setprocattr(). Each of the interfaces
used by a module are presented in the subdirectory. The
old interfaces remain and work the same as before.
User space code can begin migrating to the subdirectory
interfaces in anticipation of the time when what comes
from /proc/self/attr/current might not be what a runtime
wants.

The original implementation is by Kees Cook. The code
has been changed a bit to reflect changes in the direction
of the multiple concurrent module work, to be independent
of it, and to bring it up to date with the current tree.

Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>

---
 Documentation/security/LSM.txt | 19 ++++++---
 fs/proc/base.c                 | 91 +++++++++++++++++++++++++++++++++++++-----
 fs/proc/internal.h             |  1 +
 include/linux/security.h       | 15 ++++---
 security/security.c            | 31 ++++++++++++--
 5 files changed, 133 insertions(+), 24 deletions(-)

diff --git a/Documentation/security/LSM.txt b/Documentation/security/LSM.txt
index c2683f2..125c489 100644
--- a/Documentation/security/LSM.txt
+++ b/Documentation/security/LSM.txt
@@ -16,11 +16,10 @@ MAC extensions, other extensions can be built using the LSM to provide
 specific changes to system operation when these tweaks are not available
 in the core functionality of Linux itself.
 
-Without a specific LSM built into the kernel, the default LSM will be the
-Linux capabilities system. Most LSMs choose to extend the capabilities
-system, building their checks on top of the defined capability hooks.
-For more details on capabilities, see capabilities(7) in the Linux
-man-pages project.
+The Linux capabilities modules will always be included. For more details
+on capabilities, see capabilities(7) in the Linux man-pages project.
+This may be followed by any number of "minor" modules and at most one
+"major" module.
 
 A list of the active security modules can be found by reading
 /sys/kernel/security/lsm. This is a comma separated list, and
@@ -29,6 +28,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 the module
+specific subdirectories in /proc/.../attr. The attributes related
+to Smack would be found in /proc/.../attr/smack while the attributes
+for SELinux would be in /proc/.../attr/selinux. Using the files
+found directly in /proc/.../attr (e.g. current) should be avoided.
+These files remain as legacy interfaces.
+
 Based on https://lkml.org/lkml/2007/10/26/215,
 a new LSM is accepted into the kernel when its intent (a description of
 what it tries to protect against and in what cases one would expect to
@@ -38,4 +45,4 @@ that end users and distros can make a more informed decision about which
 LSMs suit their requirements.
 
 For extensive documentation on the available LSM hook interfaces, please
-see include/linux/security.h.
+see include/linux/lsm_hooks.h.
diff --git a/fs/proc/base.c b/fs/proc/base.c
index a11eb71..182bc28 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -131,9 +131,13 @@ struct pid_entry {
 #define REG(NAME, MODE, fops)				\
 	NOD(NAME, (S_IFREG|(MODE)), NULL, &fops, {})
 #define ONE(NAME, MODE, show)				\
-	NOD(NAME, (S_IFREG|(MODE)), 			\
+	NOD(NAME, (S_IFREG|(MODE)),			\
 		NULL, &proc_single_file_operations,	\
 		{ .proc_show = show } )
+#define ATTR(LSM, NAME, MODE)				\
+	NOD(NAME, (S_IFREG|(MODE)),			\
+		NULL, &proc_pid_attr_operations,	\
+		{ .lsm = LSM })
 
 /*
  * Count the number of hardlinks for the pid_entry table, excluding the .
@@ -2433,7 +2437,7 @@ static ssize_t proc_pid_attr_read(struct file * file, char __user * buf,
 	if (!task)
 		return -ESRCH;
 
-	length = security_getprocattr(task,
+	length = security_getprocattr(task, PROC_I(inode)->op.lsm,
 				      (char*)file->f_path.dentry->d_name.name,
 				      &p);
 	put_task_struct(task);
@@ -2473,7 +2477,7 @@ static ssize_t proc_pid_attr_write(struct file * file, const char __user * buf,
 	if (length < 0)
 		goto out_free;
 
-	length = security_setprocattr(task,
+	length = security_setprocattr(task, PROC_I(inode)->op.lsm,
 				      (char*)file->f_path.dentry->d_name.name,
 				      page, count);
 	mutex_unlock(&task->signal->cred_guard_mutex);
@@ -2491,13 +2495,82 @@ static const struct file_operations proc_pid_attr_operations = {
 	.llseek		= generic_file_llseek,
 };
 
+#define LSM_DIR_OPS(LSM) \
+static int proc_##LSM##_attr_dir_iterate(struct file *filp, \
+			     struct dir_context *ctx) \
+{ \
+	return proc_pident_readdir(filp, ctx, \
+				   LSM##_attr_dir_stuff, \
+				   ARRAY_SIZE(LSM##_attr_dir_stuff)); \
+} \
+\
+static const struct file_operations proc_##LSM##_attr_dir_ops = { \
+	.read		= generic_read_dir, \
+	.iterate	= proc_##LSM##_attr_dir_iterate, \
+	.llseek		= default_llseek, \
+}; \
+\
+static struct dentry *proc_##LSM##_attr_dir_lookup(struct inode *dir, \
+				struct dentry *dentry, unsigned int flags) \
+{ \
+	return proc_pident_lookup(dir, dentry, \
+				  LSM##_attr_dir_stuff, \
+				  ARRAY_SIZE(LSM##_attr_dir_stuff)); \
+} \
+\
+static const struct inode_operations proc_##LSM##_attr_dir_inode_ops = { \
+	.lookup		= proc_##LSM##_attr_dir_lookup, \
+	.getattr	= pid_getattr, \
+	.setattr	= proc_setattr, \
+}
+
+#ifdef CONFIG_SECURITY_SELINUX
+static const struct pid_entry selinux_attr_dir_stuff[] = {
+	ATTR("selinux", "current",	S_IRUGO|S_IWUGO),
+	ATTR("selinux", "prev",		S_IRUGO),
+	ATTR("selinux", "exec",		S_IRUGO|S_IWUGO),
+	ATTR("selinux", "fscreate",	S_IRUGO|S_IWUGO),
+	ATTR("selinux", "keycreate",	S_IRUGO|S_IWUGO),
+	ATTR("selinux", "sockcreate",	S_IRUGO|S_IWUGO),
+};
+LSM_DIR_OPS(selinux);
+#endif
+
+#ifdef CONFIG_SECURITY_SMACK
+static const struct pid_entry smack_attr_dir_stuff[] = {
+	ATTR("smack", "current",	S_IRUGO|S_IWUGO),
+};
+LSM_DIR_OPS(smack);
+#endif
+
+#ifdef CONFIG_SECURITY_APPARMOR
+static const struct pid_entry apparmor_attr_dir_stuff[] = {
+	ATTR("apparmor", "current",	S_IRUGO|S_IWUGO),
+	ATTR("apparmor", "prev",	S_IRUGO),
+	ATTR("apparmor", "exec",	S_IRUGO|S_IWUGO),
+};
+LSM_DIR_OPS(apparmor);
+#endif
+
 static const struct pid_entry attr_dir_stuff[] = {
-	REG("current",    S_IRUGO|S_IWUGO, proc_pid_attr_operations),
-	REG("prev",       S_IRUGO,	   proc_pid_attr_operations),
-	REG("exec",       S_IRUGO|S_IWUGO, proc_pid_attr_operations),
-	REG("fscreate",   S_IRUGO|S_IWUGO, proc_pid_attr_operations),
-	REG("keycreate",  S_IRUGO|S_IWUGO, proc_pid_attr_operations),
-	REG("sockcreate", S_IRUGO|S_IWUGO, proc_pid_attr_operations),
+	ATTR(NULL, "current",		S_IRUGO|S_IWUGO),
+	ATTR(NULL, "prev",		S_IRUGO),
+	ATTR(NULL, "exec",		S_IRUGO|S_IWUGO),
+	ATTR(NULL, "fscreate",		S_IRUGO|S_IWUGO),
+	ATTR(NULL, "keycreate",		S_IRUGO|S_IWUGO),
+	ATTR(NULL, "sockcreate",	S_IRUGO|S_IWUGO),
+#ifdef CONFIG_SECURITY_SELINUX
+	DIR("selinux",			S_IRUGO|S_IXUGO,
+	    proc_selinux_attr_dir_inode_ops, proc_selinux_attr_dir_ops),
+#endif
+#ifdef CONFIG_SECURITY_SMACK
+	DIR("smack",			S_IRUGO|S_IXUGO,
+	    proc_smack_attr_dir_inode_ops, proc_smack_attr_dir_ops),
+#endif
+#ifdef CONFIG_SECURITY_APPARMOR
+	DIR("apparmor",			S_IRUGO|S_IXUGO,
+	    proc_apparmor_attr_dir_inode_ops, proc_apparmor_attr_dir_ops),
+#endif
 };
 
 static int proc_attr_dir_readdir(struct file *file, struct dir_context *ctx)
diff --git a/fs/proc/internal.h b/fs/proc/internal.h
index aa27810..b607cd5 100644
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -56,6 +56,7 @@ union proc_op {
 	int (*proc_show)(struct seq_file *m,
 		struct pid_namespace *ns, struct pid *pid,
 		struct task_struct *task);
+	const char *lsm;
 };
 
 struct proc_inode {
diff --git a/include/linux/security.h b/include/linux/security.h
index 14df373..383fcb0 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -355,8 +355,10 @@ int security_sem_semctl(struct sem_array *sma, int cmd);
 int security_sem_semop(struct sem_array *sma, struct sembuf *sops,
 			unsigned nsops, int alter);
 void security_d_instantiate(struct dentry *dentry, struct inode *inode);
-int security_getprocattr(struct task_struct *p, char *name, char **value);
-int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size);
+int security_getprocattr(struct task_struct *p, const char *lsm, char *name,
+				char **value);
+int security_setprocattr(struct task_struct *p, const char *lsm, char *name,
+				void *value, size_t size);
 int security_netlink_send(struct sock *sk, struct sk_buff *skb);
 int security_ismaclabel(const char *name);
 int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen);
@@ -1075,15 +1077,18 @@ static inline int security_sem_semop(struct sem_array *sma,
 	return 0;
 }
 
-static inline void security_d_instantiate(struct dentry *dentry, struct inode *inode)
+static inline void security_d_instantiate(struct dentry *dentry,
+						struct inode *inode)
 { }
 
-static inline int security_getprocattr(struct task_struct *p, char *name, char **value)
+static inline int security_getprocattr(struct task_struct *p, const char *lsm,
+					char *name, char **value)
 {
 	return -EINVAL;
 }
 
-static inline int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size)
+static inline int security_setprocattr(struct task_struct *p, const char *lsm,
+					char *name, void *value, size_t size)
 {
 	return -EINVAL;
 }
diff --git a/security/security.c b/security/security.c
index 1093f73..1e9cb55 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1182,14 +1182,37 @@ 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;
+	int rc = -EINVAL;
+
+
+	list_for_each_entry(hp, &security_hook_heads.getprocattr, list) {
+		if (lsm != NULL && strcmp(lsm, hp->lsm))
+			continue;
+		rc = hp->hook.getprocattr(p, name, value);
+		if (rc != -ENOENT)
+			return rc;
+	}
+	return -EINVAL;
 }
 
-int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size)
+int security_setprocattr(struct task_struct *p, const char *lsm, char *name,
+				void *value, size_t size)
 {
-	return call_int_hook(setprocattr, -EINVAL, p, name, value, size);
+	struct security_hook_list *hp;
+	int rc = -EINVAL;
+
+	list_for_each_entry(hp, &security_hook_heads.setprocattr, list) {
+		if (lsm != NULL && strcmp(lsm, hp->lsm))
+			continue;
+		rc = hp->hook.setprocattr(p, name, value, size);
+		if (rc != -ENOENT)
+			break;
+	}
+	return rc;
 }
 
 int security_netlink_send(struct sock *sk, struct sk_buff *skb)

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v4 3/3] LSM: Add context interface for proc attrs
  2016-06-24 23:26           ` [PATCH v5 0/3] LSM: security module information improvements Casey Schaufler
  2016-06-24 23:27             ` [PATCH v5 1/3] LSM: Add /sys/kernel/security/lsm Casey Schaufler
  2016-06-24 23:29             ` [PATCH v5 2/3] LSM: module hierarchy in /proc/.../attr Casey Schaufler
@ 2016-06-24 23:29             ` Casey Schaufler
  2016-06-24 23:38               ` [PATCH v5 " Casey Schaufler
  2016-06-29 17:04               ` [PATCH v4 " Paul Moore
  2 siblings, 2 replies; 32+ messages in thread
From: Casey Schaufler @ 2016-06-24 23:29 UTC (permalink / raw)
  To: Paul Moore, Kees Cook
  Cc: LSM, James Morris, John Johansen, Stephen Smalley, Tetsuo Handa, LKLM

Subject: [PATCH v4 3/3] LSM: Add context interface for proc attrs

The /proc/.../attr/current interface is used by all three
Linux security modules (SELinux, Smack and AppArmor) to
report and modify the process security attribute. This is
all fine when there is exactly one of these modules active
and the userspace code knows which it module it is.
It would require a major change to the "current" interface
to provide information about more than one set of process
security attributes. Instead, a "context" attribute is
added, which identifies the security module that the
information applies to. The format is:

        lsmname='context-value'

When multiple concurrent modules are supported the
/proc/.../attr/context interface will include the data
for all of the active modules.

	lsmname1='context-value1',lsmname2='context-value2'

The module specific subdirectories under attr contain context
entries that report the information for that specific module
in the same format.

Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>

---
 Documentation/security/LSM.txt |   8 +++
 fs/proc/base.c                 |   4 ++
 security/apparmor/lsm.c        |  35 +++++++++++--
 security/security.c            | 108 +++++++++++++++++++++++++++++++++++++++++
 security/selinux/hooks.c       |  20 +++++++-
 security/smack/smack_lsm.c     |  20 ++++----
 6 files changed, 180 insertions(+), 15 deletions(-)

diff --git a/Documentation/security/LSM.txt b/Documentation/security/LSM.txt
index 125c489..af3eb11 100644
--- a/Documentation/security/LSM.txt
+++ b/Documentation/security/LSM.txt
@@ -36,6 +36,14 @@ for SELinux would be in /proc/.../attr/selinux. Using the files
 found directly in /proc/.../attr (e.g. current) should be avoided.
 These files remain as legacy interfaces.
 
+The files named "context" in the attr directories contain the
+same information as the "current" files, but formatted to
+identify the module it comes from.
+
+if selinux is the active security module:
+	/proc/self/attr/context could contain selinux='unconfined_t'
+	/proc/self/attr/selinux/context could contain selinux='unconfined_t'
+
 Based on https://lkml.org/lkml/2007/10/26/215,
 a new LSM is accepted into the kernel when its intent (a description of
 what it tries to protect against and in what cases one would expect to
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 182bc28..df94f26 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -2532,6 +2532,7 @@ static const struct pid_entry selinux_attr_dir_stuff[] = {
 	ATTR("selinux", "fscreate",	S_IRUGO|S_IWUGO),
 	ATTR("selinux", "keycreate",	S_IRUGO|S_IWUGO),
 	ATTR("selinux", "sockcreate",	S_IRUGO|S_IWUGO),
+	ATTR("selinux", "context",	S_IRUGO|S_IWUGO),
 };
 LSM_DIR_OPS(selinux);
 #endif
@@ -2539,6 +2540,7 @@ LSM_DIR_OPS(selinux);
 #ifdef CONFIG_SECURITY_SMACK
 static const struct pid_entry smack_attr_dir_stuff[] = {
 	ATTR("smack", "current",	S_IRUGO|S_IWUGO),
+	ATTR("smack", "context",	S_IRUGO|S_IWUGO),
 };
 LSM_DIR_OPS(smack);
 #endif
@@ -2548,6 +2550,7 @@ static const struct pid_entry apparmor_attr_dir_stuff[] = {
 	ATTR("apparmor", "current",	S_IRUGO|S_IWUGO),
 	ATTR("apparmor", "prev",	S_IRUGO),
 	ATTR("apparmor", "exec",	S_IRUGO|S_IWUGO),
+	ATTR("apparmor", "context",	S_IRUGO|S_IWUGO),
 };
 LSM_DIR_OPS(apparmor);
 #endif
@@ -2559,6 +2562,7 @@ static const struct pid_entry attr_dir_stuff[] = {
 	ATTR(NULL, "fscreate",		S_IRUGO|S_IWUGO),
 	ATTR(NULL, "keycreate",		S_IRUGO|S_IWUGO),
 	ATTR(NULL, "sockcreate",	S_IRUGO|S_IWUGO),
+	ATTR(NULL, "context",		S_IRUGO|S_IWUGO),
 #ifdef CONFIG_SECURITY_SELINUX
 	DIR("selinux",			S_IRUGO|S_IXUGO,
 	    proc_selinux_attr_dir_inode_ops, proc_selinux_attr_dir_ops),
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index fb0fb03..5cac15f 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -476,9 +476,13 @@ static int apparmor_getprocattr(struct task_struct *task, char *name,
 	const struct cred *cred = get_task_cred(task);
 	struct aa_task_cxt *cxt = cred_cxt(cred);
 	struct aa_profile *profile = NULL;
+	char *vp;
+	char *np;
 
 	if (strcmp(name, "current") == 0)
 		profile = aa_get_newest_profile(cxt->profile);
+	else if (strcmp(name, "context") == 0)
+		profile = aa_get_newest_profile(cxt->profile);
 	else if (strcmp(name, "prev") == 0  && cxt->previous)
 		profile = aa_get_newest_profile(cxt->previous);
 	else if (strcmp(name, "exec") == 0 && cxt->onexec)
@@ -486,9 +490,29 @@ static int apparmor_getprocattr(struct task_struct *task, char *name,
 	else
 		error = -EINVAL;
 
-	if (profile)
-		error = aa_getprocattr(profile, value);
+	if (profile == NULL)
+		goto put_out;
+
+	error = aa_getprocattr(profile, &vp);
+	if (error < 0)
+		goto put_out;
+
+	if (strcmp(name, "context") == 0) {
+		*value = kasprintf(GFP_KERNEL, "apparmor='%s'", vp);
+		if (*value == NULL) {
+			error = -ENOMEM;
+			goto put_out;
+		}
+		np = strchr(*value, '\n');
+		if (np != NULL) {
+			np[0] = '\'';
+			np[1] = '\0';
+		}
+		error = strlen(*value);
+	} else
+		*value = vp;
 
+put_out:
 	aa_put_profile(profile);
 	put_cred(cred);
 
@@ -530,7 +554,7 @@ static int apparmor_setprocattr(struct task_struct *task, char *name,
 		return -EINVAL;
 
 	arg_size = size - (args - (char *) value);
-	if (strcmp(name, "current") == 0) {
+	if (strcmp(name, "current") == 0 || strcmp(name, "context") == 0) {
 		if (strcmp(command, "changehat") == 0) {
 			error = aa_setprocattr_changehat(args, arg_size,
 							 !AA_DO_TEST);
@@ -552,7 +576,10 @@ static int apparmor_setprocattr(struct task_struct *task, char *name,
 		else
 			goto fail;
 	} else
-		/* only support the "current" and "exec" process attributes */
+		/*
+		 * only support the "current", context and "exec"
+		 * process attributes
+		 */
 		return -EINVAL;
 
 	if (!error)
diff --git a/security/security.c b/security/security.c
index 1e9cb55..f97f0d9 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1186,8 +1186,47 @@ int security_getprocattr(struct task_struct *p, const char *lsm, char *name,
 				char **value)
 {
 	struct security_hook_list *hp;
+	char *vp;
+	char *cp = NULL;
 	int rc = -EINVAL;
+	int trc;
 
+	/*
+	 * "context" requires work here in addition to what
+	 * the modules provide.
+	 */
+	if (strcmp(name, "context") == 0) {
+		*value = NULL;
+		list_for_each_entry(hp,
+				&security_hook_heads.getprocattr, list) {
+			if (lsm != NULL && strcmp(lsm, hp->lsm))
+				continue;
+			trc = hp->hook.getprocattr(p, "context", &vp);
+			if (trc == -ENOENT)
+				continue;
+			if (trc <= 0) {
+				kfree(*value);
+				return trc;
+			}
+			rc = trc;
+			if (*value == NULL) {
+				*value = vp;
+			} else {
+				cp = kasprintf(GFP_KERNEL, "%s,%s", *value, vp);
+				if (cp == NULL) {
+					kfree(*value);
+					kfree(vp);
+					return -ENOMEM;
+				}
+				kfree(*value);
+				kfree(vp);
+				*value = cp;
+			}
+		}
+		if (rc > 0)
+			return strlen(*value);
+		return rc;
+	}
 
 	list_for_each_entry(hp, &security_hook_heads.getprocattr, list) {
 		if (lsm != NULL && strcmp(lsm, hp->lsm))
@@ -1204,7 +1243,76 @@ int security_setprocattr(struct task_struct *p, const char *lsm, char *name,
 {
 	struct security_hook_list *hp;
 	int rc = -EINVAL;
+	char *local;
+	char *cp;
+	int slen;
+	int failed = 0;
+
+	/*
+	 * If lsm is NULL look at all the modules to find one
+	 * that processes name. If lsm is not NULL only look at
+	 * that module.
+	 *
+	 * "context" is handled directly here.
+	 */
+	if (strcmp(name, "context") == 0) {
+		/*
+		 * First verify that the input is acceptable.
+		 * lsm1='v1'lsm2='v2'lsm3='v3'
+		 *
+		 * A note on the use of strncmp() below.
+		 * The check is for the substring at the beginning of cp.
+		 * The kzalloc of size + 1 ensures a terminated string.
+		 */
+		local = kzalloc(size + 1, GFP_KERNEL);
+		memcpy(local, value, size);
+		cp = local;
+		list_for_each_entry(hp, &security_hook_heads.setprocattr,
+					list) {
+			if (lsm != NULL && strcmp(lsm, hp->lsm))
+				continue;
+			if (cp[0] == ',') {
+				if (cp == local)
+					goto free_out;
+				cp++;
+			}
+			slen = strlen(hp->lsm);
+			if (strncmp(cp, hp->lsm, slen))
+				goto free_out;
+			cp += slen;
+			if (cp[0] != '=' || cp[1] != '\'' || cp[2] == '\'')
+				goto free_out;
+			for (cp += 2; cp[0] != '\''; cp++)
+				if (cp[0] == '\0')
+					goto free_out;
+			cp++;
+		}
 
+		cp = local;
+		list_for_each_entry(hp, &security_hook_heads.setprocattr,
+					list) {
+			if (lsm != NULL && strcmp(lsm, hp->lsm))
+				continue;
+			if (cp[0] == ',')
+				cp++;
+			cp += strlen(hp->lsm) + 2;
+			for (slen = 0; cp[slen] != '\''; slen++)
+				;
+			cp[slen] = '\0';
+
+			rc = hp->hook.setprocattr(p, "context", cp, slen);
+			if (rc < 0)
+				failed = rc;
+			cp += slen + 1;
+		}
+		if (failed != 0)
+			rc = failed;
+		else
+			rc = size;
+free_out:
+		kfree(local);
+		return rc;
+	}
 	list_for_each_entry(hp, &security_hook_heads.setprocattr, list) {
 		if (lsm != NULL && strcmp(lsm, hp->lsm))
 			continue;
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index ed3a757..6397721 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -5711,6 +5711,8 @@ static int selinux_getprocattr(struct task_struct *p,
 
 	if (!strcmp(name, "current"))
 		sid = __tsec->sid;
+	else if (!strcmp(name, "context"))
+		sid = __tsec->sid;
 	else if (!strcmp(name, "prev"))
 		sid = __tsec->osid;
 	else if (!strcmp(name, "exec"))
@@ -5728,7 +5730,19 @@ static int selinux_getprocattr(struct task_struct *p,
 	if (!sid)
 		return 0;
 
-	error = security_sid_to_context(sid, value, &len);
+	if (strcmp(name, "context")) {
+		error = security_sid_to_context(sid, value, &len);
+	} else {
+		char *vp;
+
+		error = security_sid_to_context(sid, &vp, &len);
+		if (!error) {
+			*value = kasprintf(GFP_KERNEL, "selinux='%s'", vp);
+			if (*value == NULL)
+				error = -ENOMEM;
+		}
+	}
+
 	if (error)
 		return error;
 	return len;
@@ -5768,6 +5782,8 @@ static int selinux_setprocattr(struct task_struct *p,
 		error = current_has_perm(p, PROCESS__SETSOCKCREATE);
 	else if (!strcmp(name, "current"))
 		error = current_has_perm(p, PROCESS__SETCURRENT);
+	else if (!strcmp(name, "context"))
+		error = current_has_perm(p, PROCESS__SETCURRENT);
 	else
 		error = -EINVAL;
 	if (error)
@@ -5827,7 +5843,7 @@ static int selinux_setprocattr(struct task_struct *p,
 		tsec->keycreate_sid = sid;
 	} else if (!strcmp(name, "sockcreate")) {
 		tsec->sockcreate_sid = sid;
-	} else if (!strcmp(name, "current")) {
+	} else if (!strcmp(name, "current") || !strcmp(name, "context")) {
 		error = -EINVAL;
 		if (sid == 0)
 			goto abort_change;
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 3577009..92e66f8 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -3574,18 +3574,20 @@ 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 if (strcmp(name, "context") == 0) {
+		cp = kasprintf(GFP_KERNEL, "smack='%s'", skp->smk_known);
+		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);
 }
 
 /**
@@ -3622,7 +3624,7 @@ static int smack_setprocattr(struct task_struct *p, char *name,
 	if (value == NULL || size == 0 || size >= SMK_LONGLABEL)
 		return -EINVAL;
 
-	if (strcmp(name, "current") != 0)
+	if (strcmp(name, "current") != 0 && strcmp(name, "context") != 0)
 		return -EINVAL;
 
 	skp = smk_import_entry(value, size);

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v5 3/3] LSM: Add context interface for proc attrs
  2016-06-24 23:29             ` [PATCH v4 3/3] LSM: Add context interface for proc attrs Casey Schaufler
@ 2016-06-24 23:38               ` Casey Schaufler
  2016-06-29 17:04               ` [PATCH v4 " Paul Moore
  1 sibling, 0 replies; 32+ messages in thread
From: Casey Schaufler @ 2016-06-24 23:38 UTC (permalink / raw)
  To: Paul Moore, Kees Cook
  Cc: LSM, James Morris, John Johansen, Stephen Smalley, Tetsuo Handa, LKLM

Subject: [PATCH v5 3/3] LSM: Add context interface for proc attrs

(I sent this with the wrong version number. This is the same otherwise)

The /proc/.../attr/current interface is used by all three
Linux security modules (SELinux, Smack and AppArmor) to
report and modify the process security attribute. This is
all fine when there is exactly one of these modules active
and the userspace code knows which it module it is.
It would require a major change to the "current" interface
to provide information about more than one set of process
security attributes. Instead, a "context" attribute is
added, which identifies the security module that the
information applies to. The format is:

        lsmname='context-value'

When multiple concurrent modules are supported the
/proc/.../attr/context interface will include the data
for all of the active modules.

	lsmname1='context-value1',lsmname2='context-value2'

The module specific subdirectories under attr contain context
entries that report the information for that specific module
in the same format.

Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>

---
 Documentation/security/LSM.txt |   8 +++
 fs/proc/base.c                 |   4 ++
 security/apparmor/lsm.c        |  35 +++++++++++--
 security/security.c            | 108 +++++++++++++++++++++++++++++++++++++++++
 security/selinux/hooks.c       |  20 +++++++-
 security/smack/smack_lsm.c     |  20 ++++----
 6 files changed, 180 insertions(+), 15 deletions(-)

diff --git a/Documentation/security/LSM.txt b/Documentation/security/LSM.txt
index 125c489..af3eb11 100644
--- a/Documentation/security/LSM.txt
+++ b/Documentation/security/LSM.txt
@@ -36,6 +36,14 @@ for SELinux would be in /proc/.../attr/selinux. Using the files
 found directly in /proc/.../attr (e.g. current) should be avoided.
 These files remain as legacy interfaces.
 
+The files named "context" in the attr directories contain the
+same information as the "current" files, but formatted to
+identify the module it comes from.
+
+if selinux is the active security module:
+	/proc/self/attr/context could contain selinux='unconfined_t'
+	/proc/self/attr/selinux/context could contain selinux='unconfined_t'
+
 Based on https://lkml.org/lkml/2007/10/26/215,
 a new LSM is accepted into the kernel when its intent (a description of
 what it tries to protect against and in what cases one would expect to
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 182bc28..df94f26 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -2532,6 +2532,7 @@ static const struct pid_entry selinux_attr_dir_stuff[] = {
 	ATTR("selinux", "fscreate",	S_IRUGO|S_IWUGO),
 	ATTR("selinux", "keycreate",	S_IRUGO|S_IWUGO),
 	ATTR("selinux", "sockcreate",	S_IRUGO|S_IWUGO),
+	ATTR("selinux", "context",	S_IRUGO|S_IWUGO),
 };
 LSM_DIR_OPS(selinux);
 #endif
@@ -2539,6 +2540,7 @@ LSM_DIR_OPS(selinux);
 #ifdef CONFIG_SECURITY_SMACK
 static const struct pid_entry smack_attr_dir_stuff[] = {
 	ATTR("smack", "current",	S_IRUGO|S_IWUGO),
+	ATTR("smack", "context",	S_IRUGO|S_IWUGO),
 };
 LSM_DIR_OPS(smack);
 #endif
@@ -2548,6 +2550,7 @@ static const struct pid_entry apparmor_attr_dir_stuff[] = {
 	ATTR("apparmor", "current",	S_IRUGO|S_IWUGO),
 	ATTR("apparmor", "prev",	S_IRUGO),
 	ATTR("apparmor", "exec",	S_IRUGO|S_IWUGO),
+	ATTR("apparmor", "context",	S_IRUGO|S_IWUGO),
 };
 LSM_DIR_OPS(apparmor);
 #endif
@@ -2559,6 +2562,7 @@ static const struct pid_entry attr_dir_stuff[] = {
 	ATTR(NULL, "fscreate",		S_IRUGO|S_IWUGO),
 	ATTR(NULL, "keycreate",		S_IRUGO|S_IWUGO),
 	ATTR(NULL, "sockcreate",	S_IRUGO|S_IWUGO),
+	ATTR(NULL, "context",		S_IRUGO|S_IWUGO),
 #ifdef CONFIG_SECURITY_SELINUX
 	DIR("selinux",			S_IRUGO|S_IXUGO,
 	    proc_selinux_attr_dir_inode_ops, proc_selinux_attr_dir_ops),
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index fb0fb03..5cac15f 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -476,9 +476,13 @@ static int apparmor_getprocattr(struct task_struct *task, char *name,
 	const struct cred *cred = get_task_cred(task);
 	struct aa_task_cxt *cxt = cred_cxt(cred);
 	struct aa_profile *profile = NULL;
+	char *vp;
+	char *np;
 
 	if (strcmp(name, "current") == 0)
 		profile = aa_get_newest_profile(cxt->profile);
+	else if (strcmp(name, "context") == 0)
+		profile = aa_get_newest_profile(cxt->profile);
 	else if (strcmp(name, "prev") == 0  && cxt->previous)
 		profile = aa_get_newest_profile(cxt->previous);
 	else if (strcmp(name, "exec") == 0 && cxt->onexec)
@@ -486,9 +490,29 @@ static int apparmor_getprocattr(struct task_struct *task, char *name,
 	else
 		error = -EINVAL;
 
-	if (profile)
-		error = aa_getprocattr(profile, value);
+	if (profile == NULL)
+		goto put_out;
+
+	error = aa_getprocattr(profile, &vp);
+	if (error < 0)
+		goto put_out;
+
+	if (strcmp(name, "context") == 0) {
+		*value = kasprintf(GFP_KERNEL, "apparmor='%s'", vp);
+		if (*value == NULL) {
+			error = -ENOMEM;
+			goto put_out;
+		}
+		np = strchr(*value, '\n');
+		if (np != NULL) {
+			np[0] = '\'';
+			np[1] = '\0';
+		}
+		error = strlen(*value);
+	} else
+		*value = vp;
 
+put_out:
 	aa_put_profile(profile);
 	put_cred(cred);
 
@@ -530,7 +554,7 @@ static int apparmor_setprocattr(struct task_struct *task, char *name,
 		return -EINVAL;
 
 	arg_size = size - (args - (char *) value);
-	if (strcmp(name, "current") == 0) {
+	if (strcmp(name, "current") == 0 || strcmp(name, "context") == 0) {
 		if (strcmp(command, "changehat") == 0) {
 			error = aa_setprocattr_changehat(args, arg_size,
 							 !AA_DO_TEST);
@@ -552,7 +576,10 @@ static int apparmor_setprocattr(struct task_struct *task, char *name,
 		else
 			goto fail;
 	} else
-		/* only support the "current" and "exec" process attributes */
+		/*
+		 * only support the "current", context and "exec"
+		 * process attributes
+		 */
 		return -EINVAL;
 
 	if (!error)
diff --git a/security/security.c b/security/security.c
index 1e9cb55..f97f0d9 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1186,8 +1186,47 @@ int security_getprocattr(struct task_struct *p, const char *lsm, char *name,
 				char **value)
 {
 	struct security_hook_list *hp;
+	char *vp;
+	char *cp = NULL;
 	int rc = -EINVAL;
+	int trc;
 
+	/*
+	 * "context" requires work here in addition to what
+	 * the modules provide.
+	 */
+	if (strcmp(name, "context") == 0) {
+		*value = NULL;
+		list_for_each_entry(hp,
+				&security_hook_heads.getprocattr, list) {
+			if (lsm != NULL && strcmp(lsm, hp->lsm))
+				continue;
+			trc = hp->hook.getprocattr(p, "context", &vp);
+			if (trc == -ENOENT)
+				continue;
+			if (trc <= 0) {
+				kfree(*value);
+				return trc;
+			}
+			rc = trc;
+			if (*value == NULL) {
+				*value = vp;
+			} else {
+				cp = kasprintf(GFP_KERNEL, "%s,%s", *value, vp);
+				if (cp == NULL) {
+					kfree(*value);
+					kfree(vp);
+					return -ENOMEM;
+				}
+				kfree(*value);
+				kfree(vp);
+				*value = cp;
+			}
+		}
+		if (rc > 0)
+			return strlen(*value);
+		return rc;
+	}
 
 	list_for_each_entry(hp, &security_hook_heads.getprocattr, list) {
 		if (lsm != NULL && strcmp(lsm, hp->lsm))
@@ -1204,7 +1243,76 @@ int security_setprocattr(struct task_struct *p, const char *lsm, char *name,
 {
 	struct security_hook_list *hp;
 	int rc = -EINVAL;
+	char *local;
+	char *cp;
+	int slen;
+	int failed = 0;
+
+	/*
+	 * If lsm is NULL look at all the modules to find one
+	 * that processes name. If lsm is not NULL only look at
+	 * that module.
+	 *
+	 * "context" is handled directly here.
+	 */
+	if (strcmp(name, "context") == 0) {
+		/*
+		 * First verify that the input is acceptable.
+		 * lsm1='v1'lsm2='v2'lsm3='v3'
+		 *
+		 * A note on the use of strncmp() below.
+		 * The check is for the substring at the beginning of cp.
+		 * The kzalloc of size + 1 ensures a terminated string.
+		 */
+		local = kzalloc(size + 1, GFP_KERNEL);
+		memcpy(local, value, size);
+		cp = local;
+		list_for_each_entry(hp, &security_hook_heads.setprocattr,
+					list) {
+			if (lsm != NULL && strcmp(lsm, hp->lsm))
+				continue;
+			if (cp[0] == ',') {
+				if (cp == local)
+					goto free_out;
+				cp++;
+			}
+			slen = strlen(hp->lsm);
+			if (strncmp(cp, hp->lsm, slen))
+				goto free_out;
+			cp += slen;
+			if (cp[0] != '=' || cp[1] != '\'' || cp[2] == '\'')
+				goto free_out;
+			for (cp += 2; cp[0] != '\''; cp++)
+				if (cp[0] == '\0')
+					goto free_out;
+			cp++;
+		}
 
+		cp = local;
+		list_for_each_entry(hp, &security_hook_heads.setprocattr,
+					list) {
+			if (lsm != NULL && strcmp(lsm, hp->lsm))
+				continue;
+			if (cp[0] == ',')
+				cp++;
+			cp += strlen(hp->lsm) + 2;
+			for (slen = 0; cp[slen] != '\''; slen++)
+				;
+			cp[slen] = '\0';
+
+			rc = hp->hook.setprocattr(p, "context", cp, slen);
+			if (rc < 0)
+				failed = rc;
+			cp += slen + 1;
+		}
+		if (failed != 0)
+			rc = failed;
+		else
+			rc = size;
+free_out:
+		kfree(local);
+		return rc;
+	}
 	list_for_each_entry(hp, &security_hook_heads.setprocattr, list) {
 		if (lsm != NULL && strcmp(lsm, hp->lsm))
 			continue;
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index ed3a757..6397721 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -5711,6 +5711,8 @@ static int selinux_getprocattr(struct task_struct *p,
 
 	if (!strcmp(name, "current"))
 		sid = __tsec->sid;
+	else if (!strcmp(name, "context"))
+		sid = __tsec->sid;
 	else if (!strcmp(name, "prev"))
 		sid = __tsec->osid;
 	else if (!strcmp(name, "exec"))
@@ -5728,7 +5730,19 @@ static int selinux_getprocattr(struct task_struct *p,
 	if (!sid)
 		return 0;
 
-	error = security_sid_to_context(sid, value, &len);
+	if (strcmp(name, "context")) {
+		error = security_sid_to_context(sid, value, &len);
+	} else {
+		char *vp;
+
+		error = security_sid_to_context(sid, &vp, &len);
+		if (!error) {
+			*value = kasprintf(GFP_KERNEL, "selinux='%s'", vp);
+			if (*value == NULL)
+				error = -ENOMEM;
+		}
+	}
+
 	if (error)
 		return error;
 	return len;
@@ -5768,6 +5782,8 @@ static int selinux_setprocattr(struct task_struct *p,
 		error = current_has_perm(p, PROCESS__SETSOCKCREATE);
 	else if (!strcmp(name, "current"))
 		error = current_has_perm(p, PROCESS__SETCURRENT);
+	else if (!strcmp(name, "context"))
+		error = current_has_perm(p, PROCESS__SETCURRENT);
 	else
 		error = -EINVAL;
 	if (error)
@@ -5827,7 +5843,7 @@ static int selinux_setprocattr(struct task_struct *p,
 		tsec->keycreate_sid = sid;
 	} else if (!strcmp(name, "sockcreate")) {
 		tsec->sockcreate_sid = sid;
-	} else if (!strcmp(name, "current")) {
+	} else if (!strcmp(name, "current") || !strcmp(name, "context")) {
 		error = -EINVAL;
 		if (sid == 0)
 			goto abort_change;
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 3577009..92e66f8 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -3574,18 +3574,20 @@ 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 if (strcmp(name, "context") == 0) {
+		cp = kasprintf(GFP_KERNEL, "smack='%s'", skp->smk_known);
+		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);
 }
 
 /**
@@ -3622,7 +3624,7 @@ static int smack_setprocattr(struct task_struct *p, char *name,
 	if (value == NULL || size == 0 || size >= SMK_LONGLABEL)
 		return -EINVAL;
 
-	if (strcmp(name, "current") != 0)
+	if (strcmp(name, "current") != 0 && strcmp(name, "context") != 0)
 		return -EINVAL;
 
 	skp = smk_import_entry(value, size);

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* Re: [PATCH v5 1/3] LSM: Add /sys/kernel/security/lsm
  2016-06-24 23:27             ` [PATCH v5 1/3] LSM: Add /sys/kernel/security/lsm Casey Schaufler
@ 2016-06-29 17:01               ` Paul Moore
  2016-07-02 17:21                 ` John Johansen
  0 siblings, 1 reply; 32+ messages in thread
From: Paul Moore @ 2016-06-29 17:01 UTC (permalink / raw)
  To: Casey Schaufler
  Cc: Kees Cook, LSM, James Morris, John Johansen, Stephen Smalley,
	Tetsuo Handa, LKLM

On Fri, Jun 24, 2016 at 7:27 PM, Casey Schaufler <casey@schaufler-ca.com> wrote:
> Subject: [PATCH v5 1/3] LSM: Add /sys/kernel/security/lsm
>
> I got tired of having to find indirect ways to
> determine what security modules are active on a system.
> I have added /sys/kernel/security/lsm, which contains a
> comma separated list of the active security modules. No
> more groping around in /proc/filesystems, which won't
> help if the module doesn't support its own filesystem.
>
> Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
>
> ---
>  Documentation/security/LSM.txt |  7 +++++++
>  include/linux/lsm_hooks.h      | 12 ++++--------
>  security/apparmor/lsm.c        |  3 ++-
>  security/commoncap.c           |  3 ++-
>  security/inode.c               | 26 ++++++++++++++++++++++++--
>  security/loadpin/loadpin.c     |  2 +-
>  security/security.c            | 38 ++++++++++++++++++++++++++++++++++++++
>  security/selinux/hooks.c       |  2 +-
>  security/smack/smack_lsm.c     |  2 +-
>  security/tomoyo/tomoyo.c       |  2 +-
>  security/yama/yama_lsm.c       |  2 +-
>  11 files changed, 82 insertions(+), 17 deletions(-)

Thanks for moving the docs.

Acked-by: Paul Moore <paul@paul-moore.com>

> diff --git a/Documentation/security/LSM.txt b/Documentation/security/LSM.txt
> index 3db7e67..c2683f2 100644
> --- a/Documentation/security/LSM.txt
> +++ b/Documentation/security/LSM.txt
> @@ -22,6 +22,13 @@ system, building their checks on top of the defined capability hooks.
>  For more details on capabilities, see capabilities(7) in the Linux
>  man-pages project.
>
> +A list of the active security modules can be found by reading
> +/sys/kernel/security/lsm. This is a comma separated list, and
> +will always include the capability module. The list reflects the
> +order in which checks are made. The capability module will always
> +be first, followed by any "minor" modules (e.g. Yama) and then
> +the one "major" module (e.g. SELinux) if there is one configured.
> +
>  Based on https://lkml.org/lkml/2007/10/26/215,
>  a new LSM is accepted into the kernel when its intent (a description of
>  what it tries to protect against and in what cases one would expect to
> diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
> index 7ae3976..0bfd441 100644
> --- a/include/linux/lsm_hooks.h
> +++ b/include/linux/lsm_hooks.h
> @@ -1841,6 +1841,7 @@ struct security_hook_list {
>         struct list_head                list;
>         struct list_head                *head;
>         union security_list_options     hook;
> +       char                            *lsm;
>  };
>
>  /*
> @@ -1853,15 +1854,10 @@ struct security_hook_list {
>         { .head = &security_hook_heads.HEAD, .hook = { .HEAD = HOOK } }
>
>  extern struct security_hook_heads security_hook_heads;
> +extern char *lsm_names;
>
> -static inline void security_add_hooks(struct security_hook_list *hooks,
> -                                     int count)
> -{
> -       int i;
> -
> -       for (i = 0; i < count; i++)
> -               list_add_tail_rcu(&hooks[i].list, hooks[i].head);
> -}
> +extern void security_add_hooks(struct security_hook_list *hooks, int count,
> +                               char *lsm);
>
>  #ifdef CONFIG_SECURITY_SELINUX_DISABLE
>  /*
> diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
> index 2660fbc..fb0fb03 100644
> --- a/security/apparmor/lsm.c
> +++ b/security/apparmor/lsm.c
> @@ -880,7 +880,8 @@ static int __init apparmor_init(void)
>                 aa_free_root_ns();
>                 goto alloc_out;
>         }
> -       security_add_hooks(apparmor_hooks, ARRAY_SIZE(apparmor_hooks));
> +       security_add_hooks(apparmor_hooks, ARRAY_SIZE(apparmor_hooks),
> +                               "apparmor");
>
>         /* Report that AppArmor successfully initialized */
>         apparmor_initialized = 1;
> diff --git a/security/commoncap.c b/security/commoncap.c
> index e7fadde..d101734 100644
> --- a/security/commoncap.c
> +++ b/security/commoncap.c
> @@ -1090,7 +1090,8 @@ struct security_hook_list capability_hooks[] = {
>
>  void __init capability_add_hooks(void)
>  {
> -       security_add_hooks(capability_hooks, ARRAY_SIZE(capability_hooks));
> +       security_add_hooks(capability_hooks, ARRAY_SIZE(capability_hooks),
> +                               "capability");
>  }
>
>  #endif /* CONFIG_SECURITY */
> diff --git a/security/inode.c b/security/inode.c
> index 28414b0..235ad82 100644
> --- a/security/inode.c
> +++ b/security/inode.c
> @@ -20,6 +20,7 @@
>  #include <linux/init.h>
>  #include <linux/namei.h>
>  #include <linux/security.h>
> +#include <linux/lsm_hooks.h>
>  #include <linux/magic.h>
>
>  static struct vfsmount *mount;
> @@ -208,6 +209,21 @@ void securityfs_remove(struct dentry *dentry)
>  }
>  EXPORT_SYMBOL_GPL(securityfs_remove);
>
> +#ifdef CONFIG_SECURITY
> +static struct dentry *lsm_dentry;
> +static ssize_t lsm_read(struct file *filp, char __user *buf, size_t count,
> +                       loff_t *ppos)
> +{
> +       return simple_read_from_buffer(buf, count, ppos, lsm_names,
> +               strlen(lsm_names));
> +}
> +
> +static const struct file_operations lsm_ops = {
> +       .read = lsm_read,
> +       .llseek = generic_file_llseek,
> +};
> +#endif
> +
>  static int __init securityfs_init(void)
>  {
>         int retval;
> @@ -217,9 +233,15 @@ static int __init securityfs_init(void)
>                 return retval;
>
>         retval = register_filesystem(&fs_type);
> -       if (retval)
> +       if (retval) {
>                 sysfs_remove_mount_point(kernel_kobj, "security");
> -       return retval;
> +               return retval;
> +       }
> +#ifdef CONFIG_SECURITY
> +       lsm_dentry = securityfs_create_file("lsm", S_IRUGO, NULL, NULL,
> +                                               &lsm_ops);
> +#endif
> +       return 0;
>  }
>
>  core_initcall(securityfs_init);
> diff --git a/security/loadpin/loadpin.c b/security/loadpin/loadpin.c
> index 89a46f1..1d82eae 100644
> --- a/security/loadpin/loadpin.c
> +++ b/security/loadpin/loadpin.c
> @@ -182,7 +182,7 @@ static struct security_hook_list loadpin_hooks[] = {
>  void __init loadpin_add_hooks(void)
>  {
>         pr_info("ready to pin (currently %sabled)", enabled ? "en" : "dis");
> -       security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks));
> +       security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks), "loadpin");
>  }
>
>  /* Should not be mutable after boot, so not listed in sysfs (perm == 0). */
> diff --git a/security/security.c b/security/security.c
> index c4bb47d..1093f73 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -32,6 +32,7 @@
>  /* Maximum number of letters for an LSM name string */
>  #define SECURITY_NAME_MAX      10
>
> +char *lsm_names;
>  /* Boot-time LSM user choice */
>  static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =
>         CONFIG_DEFAULT_SECURITY;
> @@ -78,6 +79,22 @@ static int __init choose_lsm(char *str)
>  }
>  __setup("security=", choose_lsm);
>
> +static int lsm_append(char *new, char **result)
> +{
> +       char *cp;
> +
> +       if (*result == NULL) {
> +               *result = kstrdup(new, GFP_KERNEL);
> +       } else {
> +               cp = kasprintf(GFP_KERNEL, "%s,%s", *result, new);
> +               if (cp == NULL)
> +                       return -ENOMEM;
> +               kfree(*result);
> +               *result = cp;
> +       }
> +       return 0;
> +}
> +
>  /**
>   * security_module_enable - Load given security module on boot ?
>   * @module: the name of the module
> @@ -97,6 +114,27 @@ int __init security_module_enable(const char *module)
>         return !strcmp(module, chosen_lsm);
>  }
>
> +/**
> + * security_add_hooks - Add a modules hooks to the hook lists.
> + * @hooks: the hooks to add
> + * @count: the number of hooks to add
> + * @lsm: the name of the security module
> + *
> + * Each LSM has to register its hooks with the infrastructure.
> + */
> +void __init security_add_hooks(struct security_hook_list *hooks, int count,
> +                               char *lsm)
> +{
> +       int i;
> +
> +       for (i = 0; i < count; i++) {
> +               hooks[i].lsm = lsm;
> +               list_add_tail_rcu(&hooks[i].list, hooks[i].head);
> +       }
> +       if (lsm_append(lsm, &lsm_names) < 0)
> +               panic("%s - Cannot get early memory.\n", __func__);
> +}
> +
>  /*
>   * Hook list operation macros.
>   *
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index a86d537..ed3a757 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -6233,7 +6233,7 @@ static __init int selinux_init(void)
>                                             0, SLAB_PANIC, NULL);
>         avc_init();
>
> -       security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks));
> +       security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks), "selinux");
>
>         if (avc_add_callback(selinux_netcache_avc_callback, AVC_CALLBACK_RESET))
>                 panic("SELinux: Unable to register AVC netcache callback\n");
> diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
> index 6777295..3577009 100644
> --- a/security/smack/smack_lsm.c
> +++ b/security/smack/smack_lsm.c
> @@ -4801,7 +4801,7 @@ static __init int smack_init(void)
>         /*
>          * Register with LSM
>          */
> -       security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks));
> +       security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks), "smack");
>
>         return 0;
>  }
> diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
> index 75c9987..edc52d6 100644
> --- a/security/tomoyo/tomoyo.c
> +++ b/security/tomoyo/tomoyo.c
> @@ -542,7 +542,7 @@ static int __init tomoyo_init(void)
>         if (!security_module_enable("tomoyo"))
>                 return 0;
>         /* register ourselves with the security framework */
> -       security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks));
> +       security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks), "tomoyo");
>         printk(KERN_INFO "TOMOYO Linux initialized\n");
>         cred->security = &tomoyo_kernel_domain;
>         tomoyo_mm_init();
> diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c
> index 0309f21..f8ee60e 100644
> --- a/security/yama/yama_lsm.c
> +++ b/security/yama/yama_lsm.c
> @@ -471,6 +471,6 @@ static inline void yama_init_sysctl(void) { }
>  void __init yama_add_hooks(void)
>  {
>         pr_info("Yama: becoming mindful.\n");
> -       security_add_hooks(yama_hooks, ARRAY_SIZE(yama_hooks));
> +       security_add_hooks(yama_hooks, ARRAY_SIZE(yama_hooks), "yama");
>         yama_init_sysctl();
>  }
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html



-- 
paul moore
www.paul-moore.com

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v5 2/3] LSM: module hierarchy in /proc/.../attr
  2016-06-24 23:29             ` [PATCH v5 2/3] LSM: module hierarchy in /proc/.../attr Casey Schaufler
@ 2016-06-29 17:03               ` Paul Moore
  2016-07-02 17:24                 ` John Johansen
  0 siblings, 1 reply; 32+ messages in thread
From: Paul Moore @ 2016-06-29 17:03 UTC (permalink / raw)
  To: Casey Schaufler
  Cc: Kees Cook, LSM, James Morris, John Johansen, Stephen Smalley,
	Tetsuo Handa, LKLM

On Fri, Jun 24, 2016 at 7:29 PM, Casey Schaufler <casey@schaufler-ca.com> wrote:
> Subject: [PATCH v5 2/3] LSM: module hierarchy in /proc/.../attr
>
> Back in 2007 I made what turned out to be a rather serious
> mistake in the implementation of the Smack security module.
> The SELinux module used an interface in /proc to manipulate
> the security context on processes. Rather than use a similar
> interface, I used the same interface. The AppArmor team did
> likewise. Now /proc/.../attr/current will tell you the
> security "context" of the process, but it will be different
> depending on the security module you're using. That hasn't
> been a problem to date, as you can only have one module
> that supports process attributes at a time. We are coming
> up on a change to that, where multiple modules with process
> attributes can be supported. (Not included here)
>
> This patch provides a subdirectory in /proc/.../attr for
> each of the security modules that use the LSM hooks
> getprocattr() and setprocattr(). Each of the interfaces
> used by a module are presented in the subdirectory. The
> old interfaces remain and work the same as before.
> User space code can begin migrating to the subdirectory
> interfaces in anticipation of the time when what comes
> from /proc/self/attr/current might not be what a runtime
> wants.
>
> The original implementation is by Kees Cook. The code
> has been changed a bit to reflect changes in the direction
> of the multiple concurrent module work, to be independent
> of it, and to bring it up to date with the current tree.
>
> Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
>
> ---
>  Documentation/security/LSM.txt | 19 ++++++---
>  fs/proc/base.c                 | 91 +++++++++++++++++++++++++++++++++++++-----
>  fs/proc/internal.h             |  1 +
>  include/linux/security.h       | 15 ++++---
>  security/security.c            | 31 ++++++++++++--
>  5 files changed, 133 insertions(+), 24 deletions(-)

Acked-by: Paul Moore <paul@paul-moore.com>

> diff --git a/Documentation/security/LSM.txt b/Documentation/security/LSM.txt
> index c2683f2..125c489 100644
> --- a/Documentation/security/LSM.txt
> +++ b/Documentation/security/LSM.txt
> @@ -16,11 +16,10 @@ MAC extensions, other extensions can be built using the LSM to provide
>  specific changes to system operation when these tweaks are not available
>  in the core functionality of Linux itself.
>
> -Without a specific LSM built into the kernel, the default LSM will be the
> -Linux capabilities system. Most LSMs choose to extend the capabilities
> -system, building their checks on top of the defined capability hooks.
> -For more details on capabilities, see capabilities(7) in the Linux
> -man-pages project.
> +The Linux capabilities modules will always be included. For more details
> +on capabilities, see capabilities(7) in the Linux man-pages project.
> +This may be followed by any number of "minor" modules and at most one
> +"major" module.
>
>  A list of the active security modules can be found by reading
>  /sys/kernel/security/lsm. This is a comma separated list, and
> @@ -29,6 +28,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 the module
> +specific subdirectories in /proc/.../attr. The attributes related
> +to Smack would be found in /proc/.../attr/smack while the attributes
> +for SELinux would be in /proc/.../attr/selinux. Using the files
> +found directly in /proc/.../attr (e.g. current) should be avoided.
> +These files remain as legacy interfaces.
> +
>  Based on https://lkml.org/lkml/2007/10/26/215,
>  a new LSM is accepted into the kernel when its intent (a description of
>  what it tries to protect against and in what cases one would expect to
> @@ -38,4 +45,4 @@ that end users and distros can make a more informed decision about which
>  LSMs suit their requirements.
>
>  For extensive documentation on the available LSM hook interfaces, please
> -see include/linux/security.h.
> +see include/linux/lsm_hooks.h.
> diff --git a/fs/proc/base.c b/fs/proc/base.c
> index a11eb71..182bc28 100644
> --- a/fs/proc/base.c
> +++ b/fs/proc/base.c
> @@ -131,9 +131,13 @@ struct pid_entry {
>  #define REG(NAME, MODE, fops)                          \
>         NOD(NAME, (S_IFREG|(MODE)), NULL, &fops, {})
>  #define ONE(NAME, MODE, show)                          \
> -       NOD(NAME, (S_IFREG|(MODE)),                     \
> +       NOD(NAME, (S_IFREG|(MODE)),                     \
>                 NULL, &proc_single_file_operations,     \
>                 { .proc_show = show } )
> +#define ATTR(LSM, NAME, MODE)                          \
> +       NOD(NAME, (S_IFREG|(MODE)),                     \
> +               NULL, &proc_pid_attr_operations,        \
> +               { .lsm = LSM })
>
>  /*
>   * Count the number of hardlinks for the pid_entry table, excluding the .
> @@ -2433,7 +2437,7 @@ static ssize_t proc_pid_attr_read(struct file * file, char __user * buf,
>         if (!task)
>                 return -ESRCH;
>
> -       length = security_getprocattr(task,
> +       length = security_getprocattr(task, PROC_I(inode)->op.lsm,
>                                       (char*)file->f_path.dentry->d_name.name,
>                                       &p);
>         put_task_struct(task);
> @@ -2473,7 +2477,7 @@ static ssize_t proc_pid_attr_write(struct file * file, const char __user * buf,
>         if (length < 0)
>                 goto out_free;
>
> -       length = security_setprocattr(task,
> +       length = security_setprocattr(task, PROC_I(inode)->op.lsm,
>                                       (char*)file->f_path.dentry->d_name.name,
>                                       page, count);
>         mutex_unlock(&task->signal->cred_guard_mutex);
> @@ -2491,13 +2495,82 @@ static const struct file_operations proc_pid_attr_operations = {
>         .llseek         = generic_file_llseek,
>  };
>
> +#define LSM_DIR_OPS(LSM) \
> +static int proc_##LSM##_attr_dir_iterate(struct file *filp, \
> +                            struct dir_context *ctx) \
> +{ \
> +       return proc_pident_readdir(filp, ctx, \
> +                                  LSM##_attr_dir_stuff, \
> +                                  ARRAY_SIZE(LSM##_attr_dir_stuff)); \
> +} \
> +\
> +static const struct file_operations proc_##LSM##_attr_dir_ops = { \
> +       .read           = generic_read_dir, \
> +       .iterate        = proc_##LSM##_attr_dir_iterate, \
> +       .llseek         = default_llseek, \
> +}; \
> +\
> +static struct dentry *proc_##LSM##_attr_dir_lookup(struct inode *dir, \
> +                               struct dentry *dentry, unsigned int flags) \
> +{ \
> +       return proc_pident_lookup(dir, dentry, \
> +                                 LSM##_attr_dir_stuff, \
> +                                 ARRAY_SIZE(LSM##_attr_dir_stuff)); \
> +} \
> +\
> +static const struct inode_operations proc_##LSM##_attr_dir_inode_ops = { \
> +       .lookup         = proc_##LSM##_attr_dir_lookup, \
> +       .getattr        = pid_getattr, \
> +       .setattr        = proc_setattr, \
> +}
> +
> +#ifdef CONFIG_SECURITY_SELINUX
> +static const struct pid_entry selinux_attr_dir_stuff[] = {
> +       ATTR("selinux", "current",      S_IRUGO|S_IWUGO),
> +       ATTR("selinux", "prev",         S_IRUGO),
> +       ATTR("selinux", "exec",         S_IRUGO|S_IWUGO),
> +       ATTR("selinux", "fscreate",     S_IRUGO|S_IWUGO),
> +       ATTR("selinux", "keycreate",    S_IRUGO|S_IWUGO),
> +       ATTR("selinux", "sockcreate",   S_IRUGO|S_IWUGO),
> +};
> +LSM_DIR_OPS(selinux);
> +#endif
> +
> +#ifdef CONFIG_SECURITY_SMACK
> +static const struct pid_entry smack_attr_dir_stuff[] = {
> +       ATTR("smack", "current",        S_IRUGO|S_IWUGO),
> +};
> +LSM_DIR_OPS(smack);
> +#endif
> +
> +#ifdef CONFIG_SECURITY_APPARMOR
> +static const struct pid_entry apparmor_attr_dir_stuff[] = {
> +       ATTR("apparmor", "current",     S_IRUGO|S_IWUGO),
> +       ATTR("apparmor", "prev",        S_IRUGO),
> +       ATTR("apparmor", "exec",        S_IRUGO|S_IWUGO),
> +};
> +LSM_DIR_OPS(apparmor);
> +#endif
> +
>  static const struct pid_entry attr_dir_stuff[] = {
> -       REG("current",    S_IRUGO|S_IWUGO, proc_pid_attr_operations),
> -       REG("prev",       S_IRUGO,         proc_pid_attr_operations),
> -       REG("exec",       S_IRUGO|S_IWUGO, proc_pid_attr_operations),
> -       REG("fscreate",   S_IRUGO|S_IWUGO, proc_pid_attr_operations),
> -       REG("keycreate",  S_IRUGO|S_IWUGO, proc_pid_attr_operations),
> -       REG("sockcreate", S_IRUGO|S_IWUGO, proc_pid_attr_operations),
> +       ATTR(NULL, "current",           S_IRUGO|S_IWUGO),
> +       ATTR(NULL, "prev",              S_IRUGO),
> +       ATTR(NULL, "exec",              S_IRUGO|S_IWUGO),
> +       ATTR(NULL, "fscreate",          S_IRUGO|S_IWUGO),
> +       ATTR(NULL, "keycreate",         S_IRUGO|S_IWUGO),
> +       ATTR(NULL, "sockcreate",        S_IRUGO|S_IWUGO),
> +#ifdef CONFIG_SECURITY_SELINUX
> +       DIR("selinux",                  S_IRUGO|S_IXUGO,
> +           proc_selinux_attr_dir_inode_ops, proc_selinux_attr_dir_ops),
> +#endif
> +#ifdef CONFIG_SECURITY_SMACK
> +       DIR("smack",                    S_IRUGO|S_IXUGO,
> +           proc_smack_attr_dir_inode_ops, proc_smack_attr_dir_ops),
> +#endif
> +#ifdef CONFIG_SECURITY_APPARMOR
> +       DIR("apparmor",                 S_IRUGO|S_IXUGO,
> +           proc_apparmor_attr_dir_inode_ops, proc_apparmor_attr_dir_ops),
> +#endif
>  };
>
>  static int proc_attr_dir_readdir(struct file *file, struct dir_context *ctx)
> diff --git a/fs/proc/internal.h b/fs/proc/internal.h
> index aa27810..b607cd5 100644
> --- a/fs/proc/internal.h
> +++ b/fs/proc/internal.h
> @@ -56,6 +56,7 @@ union proc_op {
>         int (*proc_show)(struct seq_file *m,
>                 struct pid_namespace *ns, struct pid *pid,
>                 struct task_struct *task);
> +       const char *lsm;
>  };
>
>  struct proc_inode {
> diff --git a/include/linux/security.h b/include/linux/security.h
> index 14df373..383fcb0 100644
> --- a/include/linux/security.h
> +++ b/include/linux/security.h
> @@ -355,8 +355,10 @@ int security_sem_semctl(struct sem_array *sma, int cmd);
>  int security_sem_semop(struct sem_array *sma, struct sembuf *sops,
>                         unsigned nsops, int alter);
>  void security_d_instantiate(struct dentry *dentry, struct inode *inode);
> -int security_getprocattr(struct task_struct *p, char *name, char **value);
> -int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size);
> +int security_getprocattr(struct task_struct *p, const char *lsm, char *name,
> +                               char **value);
> +int security_setprocattr(struct task_struct *p, const char *lsm, char *name,
> +                               void *value, size_t size);
>  int security_netlink_send(struct sock *sk, struct sk_buff *skb);
>  int security_ismaclabel(const char *name);
>  int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen);
> @@ -1075,15 +1077,18 @@ static inline int security_sem_semop(struct sem_array *sma,
>         return 0;
>  }
>
> -static inline void security_d_instantiate(struct dentry *dentry, struct inode *inode)
> +static inline void security_d_instantiate(struct dentry *dentry,
> +                                               struct inode *inode)
>  { }
>
> -static inline int security_getprocattr(struct task_struct *p, char *name, char **value)
> +static inline int security_getprocattr(struct task_struct *p, const char *lsm,
> +                                       char *name, char **value)
>  {
>         return -EINVAL;
>  }
>
> -static inline int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size)
> +static inline int security_setprocattr(struct task_struct *p, const char *lsm,
> +                                       char *name, void *value, size_t size)
>  {
>         return -EINVAL;
>  }
> diff --git a/security/security.c b/security/security.c
> index 1093f73..1e9cb55 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -1182,14 +1182,37 @@ 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;
> +       int rc = -EINVAL;
> +
> +
> +       list_for_each_entry(hp, &security_hook_heads.getprocattr, list) {
> +               if (lsm != NULL && strcmp(lsm, hp->lsm))
> +                       continue;
> +               rc = hp->hook.getprocattr(p, name, value);
> +               if (rc != -ENOENT)
> +                       return rc;
> +       }
> +       return -EINVAL;
>  }
>
> -int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size)
> +int security_setprocattr(struct task_struct *p, const char *lsm, char *name,
> +                               void *value, size_t size)
>  {
> -       return call_int_hook(setprocattr, -EINVAL, p, name, value, size);
> +       struct security_hook_list *hp;
> +       int rc = -EINVAL;
> +
> +       list_for_each_entry(hp, &security_hook_heads.setprocattr, list) {
> +               if (lsm != NULL && strcmp(lsm, hp->lsm))
> +                       continue;
> +               rc = hp->hook.setprocattr(p, name, value, size);
> +               if (rc != -ENOENT)
> +                       break;
> +       }
> +       return rc;
>  }
>
>  int security_netlink_send(struct sock *sk, struct sk_buff *skb)
>



-- 
paul moore
www.paul-moore.com

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v4 3/3] LSM: Add context interface for proc attrs
  2016-06-24 23:29             ` [PATCH v4 3/3] LSM: Add context interface for proc attrs Casey Schaufler
  2016-06-24 23:38               ` [PATCH v5 " Casey Schaufler
@ 2016-06-29 17:04               ` Paul Moore
  2016-07-02 17:25                 ` John Johansen
  1 sibling, 1 reply; 32+ messages in thread
From: Paul Moore @ 2016-06-29 17:04 UTC (permalink / raw)
  To: Casey Schaufler
  Cc: Kees Cook, LSM, James Morris, John Johansen, Stephen Smalley,
	Tetsuo Handa, LKLM

On Fri, Jun 24, 2016 at 7:29 PM, Casey Schaufler <casey@schaufler-ca.com> wrote:
> Subject: [PATCH v4 3/3] LSM: Add context interface for proc attrs
>
> The /proc/.../attr/current interface is used by all three
> Linux security modules (SELinux, Smack and AppArmor) to
> report and modify the process security attribute. This is
> all fine when there is exactly one of these modules active
> and the userspace code knows which it module it is.
> It would require a major change to the "current" interface
> to provide information about more than one set of process
> security attributes. Instead, a "context" attribute is
> added, which identifies the security module that the
> information applies to. The format is:
>
>         lsmname='context-value'
>
> When multiple concurrent modules are supported the
> /proc/.../attr/context interface will include the data
> for all of the active modules.
>
>         lsmname1='context-value1',lsmname2='context-value2'
>
> The module specific subdirectories under attr contain context
> entries that report the information for that specific module
> in the same format.
>
> Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
>
> ---
>  Documentation/security/LSM.txt |   8 +++
>  fs/proc/base.c                 |   4 ++
>  security/apparmor/lsm.c        |  35 +++++++++++--
>  security/security.c            | 108 +++++++++++++++++++++++++++++++++++++++++
>  security/selinux/hooks.c       |  20 +++++++-
>  security/smack/smack_lsm.c     |  20 ++++----
>  6 files changed, 180 insertions(+), 15 deletions(-)

Acked-by: Paul Moore <paul@paul-moore.com>

> diff --git a/Documentation/security/LSM.txt b/Documentation/security/LSM.txt
> index 125c489..af3eb11 100644
> --- a/Documentation/security/LSM.txt
> +++ b/Documentation/security/LSM.txt
> @@ -36,6 +36,14 @@ for SELinux would be in /proc/.../attr/selinux. Using the files
>  found directly in /proc/.../attr (e.g. current) should be avoided.
>  These files remain as legacy interfaces.
>
> +The files named "context" in the attr directories contain the
> +same information as the "current" files, but formatted to
> +identify the module it comes from.
> +
> +if selinux is the active security module:
> +       /proc/self/attr/context could contain selinux='unconfined_t'
> +       /proc/self/attr/selinux/context could contain selinux='unconfined_t'
> +
>  Based on https://lkml.org/lkml/2007/10/26/215,
>  a new LSM is accepted into the kernel when its intent (a description of
>  what it tries to protect against and in what cases one would expect to
> diff --git a/fs/proc/base.c b/fs/proc/base.c
> index 182bc28..df94f26 100644
> --- a/fs/proc/base.c
> +++ b/fs/proc/base.c
> @@ -2532,6 +2532,7 @@ static const struct pid_entry selinux_attr_dir_stuff[] = {
>         ATTR("selinux", "fscreate",     S_IRUGO|S_IWUGO),
>         ATTR("selinux", "keycreate",    S_IRUGO|S_IWUGO),
>         ATTR("selinux", "sockcreate",   S_IRUGO|S_IWUGO),
> +       ATTR("selinux", "context",      S_IRUGO|S_IWUGO),
>  };
>  LSM_DIR_OPS(selinux);
>  #endif
> @@ -2539,6 +2540,7 @@ LSM_DIR_OPS(selinux);
>  #ifdef CONFIG_SECURITY_SMACK
>  static const struct pid_entry smack_attr_dir_stuff[] = {
>         ATTR("smack", "current",        S_IRUGO|S_IWUGO),
> +       ATTR("smack", "context",        S_IRUGO|S_IWUGO),
>  };
>  LSM_DIR_OPS(smack);
>  #endif
> @@ -2548,6 +2550,7 @@ static const struct pid_entry apparmor_attr_dir_stuff[] = {
>         ATTR("apparmor", "current",     S_IRUGO|S_IWUGO),
>         ATTR("apparmor", "prev",        S_IRUGO),
>         ATTR("apparmor", "exec",        S_IRUGO|S_IWUGO),
> +       ATTR("apparmor", "context",     S_IRUGO|S_IWUGO),
>  };
>  LSM_DIR_OPS(apparmor);
>  #endif
> @@ -2559,6 +2562,7 @@ static const struct pid_entry attr_dir_stuff[] = {
>         ATTR(NULL, "fscreate",          S_IRUGO|S_IWUGO),
>         ATTR(NULL, "keycreate",         S_IRUGO|S_IWUGO),
>         ATTR(NULL, "sockcreate",        S_IRUGO|S_IWUGO),
> +       ATTR(NULL, "context",           S_IRUGO|S_IWUGO),
>  #ifdef CONFIG_SECURITY_SELINUX
>         DIR("selinux",                  S_IRUGO|S_IXUGO,
>             proc_selinux_attr_dir_inode_ops, proc_selinux_attr_dir_ops),
> diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
> index fb0fb03..5cac15f 100644
> --- a/security/apparmor/lsm.c
> +++ b/security/apparmor/lsm.c
> @@ -476,9 +476,13 @@ static int apparmor_getprocattr(struct task_struct *task, char *name,
>         const struct cred *cred = get_task_cred(task);
>         struct aa_task_cxt *cxt = cred_cxt(cred);
>         struct aa_profile *profile = NULL;
> +       char *vp;
> +       char *np;
>
>         if (strcmp(name, "current") == 0)
>                 profile = aa_get_newest_profile(cxt->profile);
> +       else if (strcmp(name, "context") == 0)
> +               profile = aa_get_newest_profile(cxt->profile);
>         else if (strcmp(name, "prev") == 0  && cxt->previous)
>                 profile = aa_get_newest_profile(cxt->previous);
>         else if (strcmp(name, "exec") == 0 && cxt->onexec)
> @@ -486,9 +490,29 @@ static int apparmor_getprocattr(struct task_struct *task, char *name,
>         else
>                 error = -EINVAL;
>
> -       if (profile)
> -               error = aa_getprocattr(profile, value);
> +       if (profile == NULL)
> +               goto put_out;
> +
> +       error = aa_getprocattr(profile, &vp);
> +       if (error < 0)
> +               goto put_out;
> +
> +       if (strcmp(name, "context") == 0) {
> +               *value = kasprintf(GFP_KERNEL, "apparmor='%s'", vp);
> +               if (*value == NULL) {
> +                       error = -ENOMEM;
> +                       goto put_out;
> +               }
> +               np = strchr(*value, '\n');
> +               if (np != NULL) {
> +                       np[0] = '\'';
> +                       np[1] = '\0';
> +               }
> +               error = strlen(*value);
> +       } else
> +               *value = vp;
>
> +put_out:
>         aa_put_profile(profile);
>         put_cred(cred);
>
> @@ -530,7 +554,7 @@ static int apparmor_setprocattr(struct task_struct *task, char *name,
>                 return -EINVAL;
>
>         arg_size = size - (args - (char *) value);
> -       if (strcmp(name, "current") == 0) {
> +       if (strcmp(name, "current") == 0 || strcmp(name, "context") == 0) {
>                 if (strcmp(command, "changehat") == 0) {
>                         error = aa_setprocattr_changehat(args, arg_size,
>                                                          !AA_DO_TEST);
> @@ -552,7 +576,10 @@ static int apparmor_setprocattr(struct task_struct *task, char *name,
>                 else
>                         goto fail;
>         } else
> -               /* only support the "current" and "exec" process attributes */
> +               /*
> +                * only support the "current", context and "exec"
> +                * process attributes
> +                */
>                 return -EINVAL;
>
>         if (!error)
> diff --git a/security/security.c b/security/security.c
> index 1e9cb55..f97f0d9 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -1186,8 +1186,47 @@ int security_getprocattr(struct task_struct *p, const char *lsm, char *name,
>                                 char **value)
>  {
>         struct security_hook_list *hp;
> +       char *vp;
> +       char *cp = NULL;
>         int rc = -EINVAL;
> +       int trc;
>
> +       /*
> +        * "context" requires work here in addition to what
> +        * the modules provide.
> +        */
> +       if (strcmp(name, "context") == 0) {
> +               *value = NULL;
> +               list_for_each_entry(hp,
> +                               &security_hook_heads.getprocattr, list) {
> +                       if (lsm != NULL && strcmp(lsm, hp->lsm))
> +                               continue;
> +                       trc = hp->hook.getprocattr(p, "context", &vp);
> +                       if (trc == -ENOENT)
> +                               continue;
> +                       if (trc <= 0) {
> +                               kfree(*value);
> +                               return trc;
> +                       }
> +                       rc = trc;
> +                       if (*value == NULL) {
> +                               *value = vp;
> +                       } else {
> +                               cp = kasprintf(GFP_KERNEL, "%s,%s", *value, vp);
> +                               if (cp == NULL) {
> +                                       kfree(*value);
> +                                       kfree(vp);
> +                                       return -ENOMEM;
> +                               }
> +                               kfree(*value);
> +                               kfree(vp);
> +                               *value = cp;
> +                       }
> +               }
> +               if (rc > 0)
> +                       return strlen(*value);
> +               return rc;
> +       }
>
>         list_for_each_entry(hp, &security_hook_heads.getprocattr, list) {
>                 if (lsm != NULL && strcmp(lsm, hp->lsm))
> @@ -1204,7 +1243,76 @@ int security_setprocattr(struct task_struct *p, const char *lsm, char *name,
>  {
>         struct security_hook_list *hp;
>         int rc = -EINVAL;
> +       char *local;
> +       char *cp;
> +       int slen;
> +       int failed = 0;
> +
> +       /*
> +        * If lsm is NULL look at all the modules to find one
> +        * that processes name. If lsm is not NULL only look at
> +        * that module.
> +        *
> +        * "context" is handled directly here.
> +        */
> +       if (strcmp(name, "context") == 0) {
> +               /*
> +                * First verify that the input is acceptable.
> +                * lsm1='v1'lsm2='v2'lsm3='v3'
> +                *
> +                * A note on the use of strncmp() below.
> +                * The check is for the substring at the beginning of cp.
> +                * The kzalloc of size + 1 ensures a terminated string.
> +                */
> +               local = kzalloc(size + 1, GFP_KERNEL);
> +               memcpy(local, value, size);
> +               cp = local;
> +               list_for_each_entry(hp, &security_hook_heads.setprocattr,
> +                                       list) {
> +                       if (lsm != NULL && strcmp(lsm, hp->lsm))
> +                               continue;
> +                       if (cp[0] == ',') {
> +                               if (cp == local)
> +                                       goto free_out;
> +                               cp++;
> +                       }
> +                       slen = strlen(hp->lsm);
> +                       if (strncmp(cp, hp->lsm, slen))
> +                               goto free_out;
> +                       cp += slen;
> +                       if (cp[0] != '=' || cp[1] != '\'' || cp[2] == '\'')
> +                               goto free_out;
> +                       for (cp += 2; cp[0] != '\''; cp++)
> +                               if (cp[0] == '\0')
> +                                       goto free_out;
> +                       cp++;
> +               }
>
> +               cp = local;
> +               list_for_each_entry(hp, &security_hook_heads.setprocattr,
> +                                       list) {
> +                       if (lsm != NULL && strcmp(lsm, hp->lsm))
> +                               continue;
> +                       if (cp[0] == ',')
> +                               cp++;
> +                       cp += strlen(hp->lsm) + 2;
> +                       for (slen = 0; cp[slen] != '\''; slen++)
> +                               ;
> +                       cp[slen] = '\0';
> +
> +                       rc = hp->hook.setprocattr(p, "context", cp, slen);
> +                       if (rc < 0)
> +                               failed = rc;
> +                       cp += slen + 1;
> +               }
> +               if (failed != 0)
> +                       rc = failed;
> +               else
> +                       rc = size;
> +free_out:
> +               kfree(local);
> +               return rc;
> +       }
>         list_for_each_entry(hp, &security_hook_heads.setprocattr, list) {
>                 if (lsm != NULL && strcmp(lsm, hp->lsm))
>                         continue;
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index ed3a757..6397721 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -5711,6 +5711,8 @@ static int selinux_getprocattr(struct task_struct *p,
>
>         if (!strcmp(name, "current"))
>                 sid = __tsec->sid;
> +       else if (!strcmp(name, "context"))
> +               sid = __tsec->sid;
>         else if (!strcmp(name, "prev"))
>                 sid = __tsec->osid;
>         else if (!strcmp(name, "exec"))
> @@ -5728,7 +5730,19 @@ static int selinux_getprocattr(struct task_struct *p,
>         if (!sid)
>                 return 0;
>
> -       error = security_sid_to_context(sid, value, &len);
> +       if (strcmp(name, "context")) {
> +               error = security_sid_to_context(sid, value, &len);
> +       } else {
> +               char *vp;
> +
> +               error = security_sid_to_context(sid, &vp, &len);
> +               if (!error) {
> +                       *value = kasprintf(GFP_KERNEL, "selinux='%s'", vp);
> +                       if (*value == NULL)
> +                               error = -ENOMEM;
> +               }
> +       }
> +
>         if (error)
>                 return error;
>         return len;
> @@ -5768,6 +5782,8 @@ static int selinux_setprocattr(struct task_struct *p,
>                 error = current_has_perm(p, PROCESS__SETSOCKCREATE);
>         else if (!strcmp(name, "current"))
>                 error = current_has_perm(p, PROCESS__SETCURRENT);
> +       else if (!strcmp(name, "context"))
> +               error = current_has_perm(p, PROCESS__SETCURRENT);
>         else
>                 error = -EINVAL;
>         if (error)
> @@ -5827,7 +5843,7 @@ static int selinux_setprocattr(struct task_struct *p,
>                 tsec->keycreate_sid = sid;
>         } else if (!strcmp(name, "sockcreate")) {
>                 tsec->sockcreate_sid = sid;
> -       } else if (!strcmp(name, "current")) {
> +       } else if (!strcmp(name, "current") || !strcmp(name, "context")) {
>                 error = -EINVAL;
>                 if (sid == 0)
>                         goto abort_change;
> diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
> index 3577009..92e66f8 100644
> --- a/security/smack/smack_lsm.c
> +++ b/security/smack/smack_lsm.c
> @@ -3574,18 +3574,20 @@ 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 if (strcmp(name, "context") == 0) {
> +               cp = kasprintf(GFP_KERNEL, "smack='%s'", skp->smk_known);
> +               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);
>  }
>
>  /**
> @@ -3622,7 +3624,7 @@ static int smack_setprocattr(struct task_struct *p, char *name,
>         if (value == NULL || size == 0 || size >= SMK_LONGLABEL)
>                 return -EINVAL;
>
> -       if (strcmp(name, "current") != 0)
> +       if (strcmp(name, "current") != 0 && strcmp(name, "context") != 0)
>                 return -EINVAL;
>
>         skp = smk_import_entry(value, size);
>



-- 
paul moore
www.paul-moore.com

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v5 1/3] LSM: Add /sys/kernel/security/lsm
  2016-06-29 17:01               ` Paul Moore
@ 2016-07-02 17:21                 ` John Johansen
  0 siblings, 0 replies; 32+ messages in thread
From: John Johansen @ 2016-07-02 17:21 UTC (permalink / raw)
  To: Paul Moore, Casey Schaufler
  Cc: Kees Cook, LSM, James Morris, Stephen Smalley, Tetsuo Handa, LKLM

On 06/29/2016 10:01 AM, Paul Moore wrote:
> On Fri, Jun 24, 2016 at 7:27 PM, Casey Schaufler <casey@schaufler-ca.com> wrote:
>> Subject: [PATCH v5 1/3] LSM: Add /sys/kernel/security/lsm
>>
>> I got tired of having to find indirect ways to
>> determine what security modules are active on a system.
>> I have added /sys/kernel/security/lsm, which contains a
>> comma separated list of the active security modules. No
>> more groping around in /proc/filesystems, which won't
>> help if the module doesn't support its own filesystem.
>>
>> Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
>>
>> ---
>>  Documentation/security/LSM.txt |  7 +++++++
>>  include/linux/lsm_hooks.h      | 12 ++++--------
>>  security/apparmor/lsm.c        |  3 ++-
>>  security/commoncap.c           |  3 ++-
>>  security/inode.c               | 26 ++++++++++++++++++++++++--
>>  security/loadpin/loadpin.c     |  2 +-
>>  security/security.c            | 38 ++++++++++++++++++++++++++++++++++++++
>>  security/selinux/hooks.c       |  2 +-
>>  security/smack/smack_lsm.c     |  2 +-
>>  security/tomoyo/tomoyo.c       |  2 +-
>>  security/yama/yama_lsm.c       |  2 +-
>>  11 files changed, 82 insertions(+), 17 deletions(-)
> 
> Thanks for moving the docs.
> 
> Acked-by: Paul Moore <paul@paul-moore.com>

Acked-by: John Johansen <john.johansen@canonical.com>

> 
>> diff --git a/Documentation/security/LSM.txt b/Documentation/security/LSM.txt
>> index 3db7e67..c2683f2 100644
>> --- a/Documentation/security/LSM.txt
>> +++ b/Documentation/security/LSM.txt
>> @@ -22,6 +22,13 @@ system, building their checks on top of the defined capability hooks.
>>  For more details on capabilities, see capabilities(7) in the Linux
>>  man-pages project.
>>
>> +A list of the active security modules can be found by reading
>> +/sys/kernel/security/lsm. This is a comma separated list, and
>> +will always include the capability module. The list reflects the
>> +order in which checks are made. The capability module will always
>> +be first, followed by any "minor" modules (e.g. Yama) and then
>> +the one "major" module (e.g. SELinux) if there is one configured.
>> +
>>  Based on https://lkml.org/lkml/2007/10/26/215,
>>  a new LSM is accepted into the kernel when its intent (a description of
>>  what it tries to protect against and in what cases one would expect to
>> diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
>> index 7ae3976..0bfd441 100644
>> --- a/include/linux/lsm_hooks.h
>> +++ b/include/linux/lsm_hooks.h
>> @@ -1841,6 +1841,7 @@ struct security_hook_list {
>>         struct list_head                list;
>>         struct list_head                *head;
>>         union security_list_options     hook;
>> +       char                            *lsm;
>>  };
>>
>>  /*
>> @@ -1853,15 +1854,10 @@ struct security_hook_list {
>>         { .head = &security_hook_heads.HEAD, .hook = { .HEAD = HOOK } }
>>
>>  extern struct security_hook_heads security_hook_heads;
>> +extern char *lsm_names;
>>
>> -static inline void security_add_hooks(struct security_hook_list *hooks,
>> -                                     int count)
>> -{
>> -       int i;
>> -
>> -       for (i = 0; i < count; i++)
>> -               list_add_tail_rcu(&hooks[i].list, hooks[i].head);
>> -}
>> +extern void security_add_hooks(struct security_hook_list *hooks, int count,
>> +                               char *lsm);
>>
>>  #ifdef CONFIG_SECURITY_SELINUX_DISABLE
>>  /*
>> diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
>> index 2660fbc..fb0fb03 100644
>> --- a/security/apparmor/lsm.c
>> +++ b/security/apparmor/lsm.c
>> @@ -880,7 +880,8 @@ static int __init apparmor_init(void)
>>                 aa_free_root_ns();
>>                 goto alloc_out;
>>         }
>> -       security_add_hooks(apparmor_hooks, ARRAY_SIZE(apparmor_hooks));
>> +       security_add_hooks(apparmor_hooks, ARRAY_SIZE(apparmor_hooks),
>> +                               "apparmor");
>>
>>         /* Report that AppArmor successfully initialized */
>>         apparmor_initialized = 1;
>> diff --git a/security/commoncap.c b/security/commoncap.c
>> index e7fadde..d101734 100644
>> --- a/security/commoncap.c
>> +++ b/security/commoncap.c
>> @@ -1090,7 +1090,8 @@ struct security_hook_list capability_hooks[] = {
>>
>>  void __init capability_add_hooks(void)
>>  {
>> -       security_add_hooks(capability_hooks, ARRAY_SIZE(capability_hooks));
>> +       security_add_hooks(capability_hooks, ARRAY_SIZE(capability_hooks),
>> +                               "capability");
>>  }
>>
>>  #endif /* CONFIG_SECURITY */
>> diff --git a/security/inode.c b/security/inode.c
>> index 28414b0..235ad82 100644
>> --- a/security/inode.c
>> +++ b/security/inode.c
>> @@ -20,6 +20,7 @@
>>  #include <linux/init.h>
>>  #include <linux/namei.h>
>>  #include <linux/security.h>
>> +#include <linux/lsm_hooks.h>
>>  #include <linux/magic.h>
>>
>>  static struct vfsmount *mount;
>> @@ -208,6 +209,21 @@ void securityfs_remove(struct dentry *dentry)
>>  }
>>  EXPORT_SYMBOL_GPL(securityfs_remove);
>>
>> +#ifdef CONFIG_SECURITY
>> +static struct dentry *lsm_dentry;
>> +static ssize_t lsm_read(struct file *filp, char __user *buf, size_t count,
>> +                       loff_t *ppos)
>> +{
>> +       return simple_read_from_buffer(buf, count, ppos, lsm_names,
>> +               strlen(lsm_names));
>> +}
>> +
>> +static const struct file_operations lsm_ops = {
>> +       .read = lsm_read,
>> +       .llseek = generic_file_llseek,
>> +};
>> +#endif
>> +
>>  static int __init securityfs_init(void)
>>  {
>>         int retval;
>> @@ -217,9 +233,15 @@ static int __init securityfs_init(void)
>>                 return retval;
>>
>>         retval = register_filesystem(&fs_type);
>> -       if (retval)
>> +       if (retval) {
>>                 sysfs_remove_mount_point(kernel_kobj, "security");
>> -       return retval;
>> +               return retval;
>> +       }
>> +#ifdef CONFIG_SECURITY
>> +       lsm_dentry = securityfs_create_file("lsm", S_IRUGO, NULL, NULL,
>> +                                               &lsm_ops);
>> +#endif
>> +       return 0;
>>  }
>>
>>  core_initcall(securityfs_init);
>> diff --git a/security/loadpin/loadpin.c b/security/loadpin/loadpin.c
>> index 89a46f1..1d82eae 100644
>> --- a/security/loadpin/loadpin.c
>> +++ b/security/loadpin/loadpin.c
>> @@ -182,7 +182,7 @@ static struct security_hook_list loadpin_hooks[] = {
>>  void __init loadpin_add_hooks(void)
>>  {
>>         pr_info("ready to pin (currently %sabled)", enabled ? "en" : "dis");
>> -       security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks));
>> +       security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks), "loadpin");
>>  }
>>
>>  /* Should not be mutable after boot, so not listed in sysfs (perm == 0). */
>> diff --git a/security/security.c b/security/security.c
>> index c4bb47d..1093f73 100644
>> --- a/security/security.c
>> +++ b/security/security.c
>> @@ -32,6 +32,7 @@
>>  /* Maximum number of letters for an LSM name string */
>>  #define SECURITY_NAME_MAX      10
>>
>> +char *lsm_names;
>>  /* Boot-time LSM user choice */
>>  static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =
>>         CONFIG_DEFAULT_SECURITY;
>> @@ -78,6 +79,22 @@ static int __init choose_lsm(char *str)
>>  }
>>  __setup("security=", choose_lsm);
>>
>> +static int lsm_append(char *new, char **result)
>> +{
>> +       char *cp;
>> +
>> +       if (*result == NULL) {
>> +               *result = kstrdup(new, GFP_KERNEL);
>> +       } else {
>> +               cp = kasprintf(GFP_KERNEL, "%s,%s", *result, new);
>> +               if (cp == NULL)
>> +                       return -ENOMEM;
>> +               kfree(*result);
>> +               *result = cp;
>> +       }
>> +       return 0;
>> +}
>> +
>>  /**
>>   * security_module_enable - Load given security module on boot ?
>>   * @module: the name of the module
>> @@ -97,6 +114,27 @@ int __init security_module_enable(const char *module)
>>         return !strcmp(module, chosen_lsm);
>>  }
>>
>> +/**
>> + * security_add_hooks - Add a modules hooks to the hook lists.
>> + * @hooks: the hooks to add
>> + * @count: the number of hooks to add
>> + * @lsm: the name of the security module
>> + *
>> + * Each LSM has to register its hooks with the infrastructure.
>> + */
>> +void __init security_add_hooks(struct security_hook_list *hooks, int count,
>> +                               char *lsm)
>> +{
>> +       int i;
>> +
>> +       for (i = 0; i < count; i++) {
>> +               hooks[i].lsm = lsm;
>> +               list_add_tail_rcu(&hooks[i].list, hooks[i].head);
>> +       }
>> +       if (lsm_append(lsm, &lsm_names) < 0)
>> +               panic("%s - Cannot get early memory.\n", __func__);
>> +}
>> +
>>  /*
>>   * Hook list operation macros.
>>   *
>> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
>> index a86d537..ed3a757 100644
>> --- a/security/selinux/hooks.c
>> +++ b/security/selinux/hooks.c
>> @@ -6233,7 +6233,7 @@ static __init int selinux_init(void)
>>                                             0, SLAB_PANIC, NULL);
>>         avc_init();
>>
>> -       security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks));
>> +       security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks), "selinux");
>>
>>         if (avc_add_callback(selinux_netcache_avc_callback, AVC_CALLBACK_RESET))
>>                 panic("SELinux: Unable to register AVC netcache callback\n");
>> diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
>> index 6777295..3577009 100644
>> --- a/security/smack/smack_lsm.c
>> +++ b/security/smack/smack_lsm.c
>> @@ -4801,7 +4801,7 @@ static __init int smack_init(void)
>>         /*
>>          * Register with LSM
>>          */
>> -       security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks));
>> +       security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks), "smack");
>>
>>         return 0;
>>  }
>> diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
>> index 75c9987..edc52d6 100644
>> --- a/security/tomoyo/tomoyo.c
>> +++ b/security/tomoyo/tomoyo.c
>> @@ -542,7 +542,7 @@ static int __init tomoyo_init(void)
>>         if (!security_module_enable("tomoyo"))
>>                 return 0;
>>         /* register ourselves with the security framework */
>> -       security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks));
>> +       security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks), "tomoyo");
>>         printk(KERN_INFO "TOMOYO Linux initialized\n");
>>         cred->security = &tomoyo_kernel_domain;
>>         tomoyo_mm_init();
>> diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c
>> index 0309f21..f8ee60e 100644
>> --- a/security/yama/yama_lsm.c
>> +++ b/security/yama/yama_lsm.c
>> @@ -471,6 +471,6 @@ static inline void yama_init_sysctl(void) { }
>>  void __init yama_add_hooks(void)
>>  {
>>         pr_info("Yama: becoming mindful.\n");
>> -       security_add_hooks(yama_hooks, ARRAY_SIZE(yama_hooks));
>> +       security_add_hooks(yama_hooks, ARRAY_SIZE(yama_hooks), "yama");
>>         yama_init_sysctl();
>>  }
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 
> 
> 

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v5 2/3] LSM: module hierarchy in /proc/.../attr
  2016-06-29 17:03               ` Paul Moore
@ 2016-07-02 17:24                 ` John Johansen
  0 siblings, 0 replies; 32+ messages in thread
From: John Johansen @ 2016-07-02 17:24 UTC (permalink / raw)
  To: Paul Moore, Casey Schaufler
  Cc: Kees Cook, LSM, James Morris, Stephen Smalley, Tetsuo Handa, LKLM

On 06/29/2016 10:03 AM, Paul Moore wrote:
> On Fri, Jun 24, 2016 at 7:29 PM, Casey Schaufler <casey@schaufler-ca.com> wrote:
>> Subject: [PATCH v5 2/3] LSM: module hierarchy in /proc/.../attr
>>
>> Back in 2007 I made what turned out to be a rather serious
>> mistake in the implementation of the Smack security module.
>> The SELinux module used an interface in /proc to manipulate
>> the security context on processes. Rather than use a similar
>> interface, I used the same interface. The AppArmor team did
>> likewise. Now /proc/.../attr/current will tell you the
>> security "context" of the process, but it will be different
>> depending on the security module you're using. That hasn't
>> been a problem to date, as you can only have one module
>> that supports process attributes at a time. We are coming
>> up on a change to that, where multiple modules with process
>> attributes can be supported. (Not included here)
>>
>> This patch provides a subdirectory in /proc/.../attr for
>> each of the security modules that use the LSM hooks
>> getprocattr() and setprocattr(). Each of the interfaces
>> used by a module are presented in the subdirectory. The
>> old interfaces remain and work the same as before.
>> User space code can begin migrating to the subdirectory
>> interfaces in anticipation of the time when what comes
>> from /proc/self/attr/current might not be what a runtime
>> wants.
>>
>> The original implementation is by Kees Cook. The code
>> has been changed a bit to reflect changes in the direction
>> of the multiple concurrent module work, to be independent
>> of it, and to bring it up to date with the current tree.
>>
>> Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
>>
>> ---
>>  Documentation/security/LSM.txt | 19 ++++++---
>>  fs/proc/base.c                 | 91 +++++++++++++++++++++++++++++++++++++-----
>>  fs/proc/internal.h             |  1 +
>>  include/linux/security.h       | 15 ++++---
>>  security/security.c            | 31 ++++++++++++--
>>  5 files changed, 133 insertions(+), 24 deletions(-)
> 
> Acked-by: Paul Moore <paul@paul-moore.com>

Acked-by: John Johansen <john.johansen@canonical.com>

> 
>> diff --git a/Documentation/security/LSM.txt b/Documentation/security/LSM.txt
>> index c2683f2..125c489 100644
>> --- a/Documentation/security/LSM.txt
>> +++ b/Documentation/security/LSM.txt
>> @@ -16,11 +16,10 @@ MAC extensions, other extensions can be built using the LSM to provide
>>  specific changes to system operation when these tweaks are not available
>>  in the core functionality of Linux itself.
>>
>> -Without a specific LSM built into the kernel, the default LSM will be the
>> -Linux capabilities system. Most LSMs choose to extend the capabilities
>> -system, building their checks on top of the defined capability hooks.
>> -For more details on capabilities, see capabilities(7) in the Linux
>> -man-pages project.
>> +The Linux capabilities modules will always be included. For more details
>> +on capabilities, see capabilities(7) in the Linux man-pages project.
>> +This may be followed by any number of "minor" modules and at most one
>> +"major" module.
>>
>>  A list of the active security modules can be found by reading
>>  /sys/kernel/security/lsm. This is a comma separated list, and
>> @@ -29,6 +28,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 the module
>> +specific subdirectories in /proc/.../attr. The attributes related
>> +to Smack would be found in /proc/.../attr/smack while the attributes
>> +for SELinux would be in /proc/.../attr/selinux. Using the files
>> +found directly in /proc/.../attr (e.g. current) should be avoided.
>> +These files remain as legacy interfaces.
>> +
>>  Based on https://lkml.org/lkml/2007/10/26/215,
>>  a new LSM is accepted into the kernel when its intent (a description of
>>  what it tries to protect against and in what cases one would expect to
>> @@ -38,4 +45,4 @@ that end users and distros can make a more informed decision about which
>>  LSMs suit their requirements.
>>
>>  For extensive documentation on the available LSM hook interfaces, please
>> -see include/linux/security.h.
>> +see include/linux/lsm_hooks.h.
>> diff --git a/fs/proc/base.c b/fs/proc/base.c
>> index a11eb71..182bc28 100644
>> --- a/fs/proc/base.c
>> +++ b/fs/proc/base.c
>> @@ -131,9 +131,13 @@ struct pid_entry {
>>  #define REG(NAME, MODE, fops)                          \
>>         NOD(NAME, (S_IFREG|(MODE)), NULL, &fops, {})
>>  #define ONE(NAME, MODE, show)                          \
>> -       NOD(NAME, (S_IFREG|(MODE)),                     \
>> +       NOD(NAME, (S_IFREG|(MODE)),                     \
>>                 NULL, &proc_single_file_operations,     \
>>                 { .proc_show = show } )
>> +#define ATTR(LSM, NAME, MODE)                          \
>> +       NOD(NAME, (S_IFREG|(MODE)),                     \
>> +               NULL, &proc_pid_attr_operations,        \
>> +               { .lsm = LSM })
>>
>>  /*
>>   * Count the number of hardlinks for the pid_entry table, excluding the .
>> @@ -2433,7 +2437,7 @@ static ssize_t proc_pid_attr_read(struct file * file, char __user * buf,
>>         if (!task)
>>                 return -ESRCH;
>>
>> -       length = security_getprocattr(task,
>> +       length = security_getprocattr(task, PROC_I(inode)->op.lsm,
>>                                       (char*)file->f_path.dentry->d_name.name,
>>                                       &p);
>>         put_task_struct(task);
>> @@ -2473,7 +2477,7 @@ static ssize_t proc_pid_attr_write(struct file * file, const char __user * buf,
>>         if (length < 0)
>>                 goto out_free;
>>
>> -       length = security_setprocattr(task,
>> +       length = security_setprocattr(task, PROC_I(inode)->op.lsm,
>>                                       (char*)file->f_path.dentry->d_name.name,
>>                                       page, count);
>>         mutex_unlock(&task->signal->cred_guard_mutex);
>> @@ -2491,13 +2495,82 @@ static const struct file_operations proc_pid_attr_operations = {
>>         .llseek         = generic_file_llseek,
>>  };
>>
>> +#define LSM_DIR_OPS(LSM) \
>> +static int proc_##LSM##_attr_dir_iterate(struct file *filp, \
>> +                            struct dir_context *ctx) \
>> +{ \
>> +       return proc_pident_readdir(filp, ctx, \
>> +                                  LSM##_attr_dir_stuff, \
>> +                                  ARRAY_SIZE(LSM##_attr_dir_stuff)); \
>> +} \
>> +\
>> +static const struct file_operations proc_##LSM##_attr_dir_ops = { \
>> +       .read           = generic_read_dir, \
>> +       .iterate        = proc_##LSM##_attr_dir_iterate, \
>> +       .llseek         = default_llseek, \
>> +}; \
>> +\
>> +static struct dentry *proc_##LSM##_attr_dir_lookup(struct inode *dir, \
>> +                               struct dentry *dentry, unsigned int flags) \
>> +{ \
>> +       return proc_pident_lookup(dir, dentry, \
>> +                                 LSM##_attr_dir_stuff, \
>> +                                 ARRAY_SIZE(LSM##_attr_dir_stuff)); \
>> +} \
>> +\
>> +static const struct inode_operations proc_##LSM##_attr_dir_inode_ops = { \
>> +       .lookup         = proc_##LSM##_attr_dir_lookup, \
>> +       .getattr        = pid_getattr, \
>> +       .setattr        = proc_setattr, \
>> +}
>> +
>> +#ifdef CONFIG_SECURITY_SELINUX
>> +static const struct pid_entry selinux_attr_dir_stuff[] = {
>> +       ATTR("selinux", "current",      S_IRUGO|S_IWUGO),
>> +       ATTR("selinux", "prev",         S_IRUGO),
>> +       ATTR("selinux", "exec",         S_IRUGO|S_IWUGO),
>> +       ATTR("selinux", "fscreate",     S_IRUGO|S_IWUGO),
>> +       ATTR("selinux", "keycreate",    S_IRUGO|S_IWUGO),
>> +       ATTR("selinux", "sockcreate",   S_IRUGO|S_IWUGO),
>> +};
>> +LSM_DIR_OPS(selinux);
>> +#endif
>> +
>> +#ifdef CONFIG_SECURITY_SMACK
>> +static const struct pid_entry smack_attr_dir_stuff[] = {
>> +       ATTR("smack", "current",        S_IRUGO|S_IWUGO),
>> +};
>> +LSM_DIR_OPS(smack);
>> +#endif
>> +
>> +#ifdef CONFIG_SECURITY_APPARMOR
>> +static const struct pid_entry apparmor_attr_dir_stuff[] = {
>> +       ATTR("apparmor", "current",     S_IRUGO|S_IWUGO),
>> +       ATTR("apparmor", "prev",        S_IRUGO),
>> +       ATTR("apparmor", "exec",        S_IRUGO|S_IWUGO),
>> +};
>> +LSM_DIR_OPS(apparmor);
>> +#endif
>> +
>>  static const struct pid_entry attr_dir_stuff[] = {
>> -       REG("current",    S_IRUGO|S_IWUGO, proc_pid_attr_operations),
>> -       REG("prev",       S_IRUGO,         proc_pid_attr_operations),
>> -       REG("exec",       S_IRUGO|S_IWUGO, proc_pid_attr_operations),
>> -       REG("fscreate",   S_IRUGO|S_IWUGO, proc_pid_attr_operations),
>> -       REG("keycreate",  S_IRUGO|S_IWUGO, proc_pid_attr_operations),
>> -       REG("sockcreate", S_IRUGO|S_IWUGO, proc_pid_attr_operations),
>> +       ATTR(NULL, "current",           S_IRUGO|S_IWUGO),
>> +       ATTR(NULL, "prev",              S_IRUGO),
>> +       ATTR(NULL, "exec",              S_IRUGO|S_IWUGO),
>> +       ATTR(NULL, "fscreate",          S_IRUGO|S_IWUGO),
>> +       ATTR(NULL, "keycreate",         S_IRUGO|S_IWUGO),
>> +       ATTR(NULL, "sockcreate",        S_IRUGO|S_IWUGO),
>> +#ifdef CONFIG_SECURITY_SELINUX
>> +       DIR("selinux",                  S_IRUGO|S_IXUGO,
>> +           proc_selinux_attr_dir_inode_ops, proc_selinux_attr_dir_ops),
>> +#endif
>> +#ifdef CONFIG_SECURITY_SMACK
>> +       DIR("smack",                    S_IRUGO|S_IXUGO,
>> +           proc_smack_attr_dir_inode_ops, proc_smack_attr_dir_ops),
>> +#endif
>> +#ifdef CONFIG_SECURITY_APPARMOR
>> +       DIR("apparmor",                 S_IRUGO|S_IXUGO,
>> +           proc_apparmor_attr_dir_inode_ops, proc_apparmor_attr_dir_ops),
>> +#endif
>>  };
>>
>>  static int proc_attr_dir_readdir(struct file *file, struct dir_context *ctx)
>> diff --git a/fs/proc/internal.h b/fs/proc/internal.h
>> index aa27810..b607cd5 100644
>> --- a/fs/proc/internal.h
>> +++ b/fs/proc/internal.h
>> @@ -56,6 +56,7 @@ union proc_op {
>>         int (*proc_show)(struct seq_file *m,
>>                 struct pid_namespace *ns, struct pid *pid,
>>                 struct task_struct *task);
>> +       const char *lsm;
>>  };
>>
>>  struct proc_inode {
>> diff --git a/include/linux/security.h b/include/linux/security.h
>> index 14df373..383fcb0 100644
>> --- a/include/linux/security.h
>> +++ b/include/linux/security.h
>> @@ -355,8 +355,10 @@ int security_sem_semctl(struct sem_array *sma, int cmd);
>>  int security_sem_semop(struct sem_array *sma, struct sembuf *sops,
>>                         unsigned nsops, int alter);
>>  void security_d_instantiate(struct dentry *dentry, struct inode *inode);
>> -int security_getprocattr(struct task_struct *p, char *name, char **value);
>> -int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size);
>> +int security_getprocattr(struct task_struct *p, const char *lsm, char *name,
>> +                               char **value);
>> +int security_setprocattr(struct task_struct *p, const char *lsm, char *name,
>> +                               void *value, size_t size);
>>  int security_netlink_send(struct sock *sk, struct sk_buff *skb);
>>  int security_ismaclabel(const char *name);
>>  int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen);
>> @@ -1075,15 +1077,18 @@ static inline int security_sem_semop(struct sem_array *sma,
>>         return 0;
>>  }
>>
>> -static inline void security_d_instantiate(struct dentry *dentry, struct inode *inode)
>> +static inline void security_d_instantiate(struct dentry *dentry,
>> +                                               struct inode *inode)
>>  { }
>>
>> -static inline int security_getprocattr(struct task_struct *p, char *name, char **value)
>> +static inline int security_getprocattr(struct task_struct *p, const char *lsm,
>> +                                       char *name, char **value)
>>  {
>>         return -EINVAL;
>>  }
>>
>> -static inline int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size)
>> +static inline int security_setprocattr(struct task_struct *p, const char *lsm,
>> +                                       char *name, void *value, size_t size)
>>  {
>>         return -EINVAL;
>>  }
>> diff --git a/security/security.c b/security/security.c
>> index 1093f73..1e9cb55 100644
>> --- a/security/security.c
>> +++ b/security/security.c
>> @@ -1182,14 +1182,37 @@ 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;
>> +       int rc = -EINVAL;
>> +
>> +
>> +       list_for_each_entry(hp, &security_hook_heads.getprocattr, list) {
>> +               if (lsm != NULL && strcmp(lsm, hp->lsm))
>> +                       continue;
>> +               rc = hp->hook.getprocattr(p, name, value);
>> +               if (rc != -ENOENT)
>> +                       return rc;
>> +       }
>> +       return -EINVAL;
>>  }
>>
>> -int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size)
>> +int security_setprocattr(struct task_struct *p, const char *lsm, char *name,
>> +                               void *value, size_t size)
>>  {
>> -       return call_int_hook(setprocattr, -EINVAL, p, name, value, size);
>> +       struct security_hook_list *hp;
>> +       int rc = -EINVAL;
>> +
>> +       list_for_each_entry(hp, &security_hook_heads.setprocattr, list) {
>> +               if (lsm != NULL && strcmp(lsm, hp->lsm))
>> +                       continue;
>> +               rc = hp->hook.setprocattr(p, name, value, size);
>> +               if (rc != -ENOENT)
>> +                       break;
>> +       }
>> +       return rc;
>>  }
>>
>>  int security_netlink_send(struct sock *sk, struct sk_buff *skb)
>>
> 
> 
> 

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v4 3/3] LSM: Add context interface for proc attrs
  2016-06-29 17:04               ` [PATCH v4 " Paul Moore
@ 2016-07-02 17:25                 ` John Johansen
  2016-07-05 15:52                   ` [PATCH v5 0/3] LSM: security module information improvements - Acked Casey Schaufler
                                     ` (3 more replies)
  0 siblings, 4 replies; 32+ messages in thread
From: John Johansen @ 2016-07-02 17:25 UTC (permalink / raw)
  To: Paul Moore, Casey Schaufler
  Cc: Kees Cook, LSM, James Morris, Stephen Smalley, Tetsuo Handa, LKLM

On 06/29/2016 10:04 AM, Paul Moore wrote:
> On Fri, Jun 24, 2016 at 7:29 PM, Casey Schaufler <casey@schaufler-ca.com> wrote:
>> Subject: [PATCH v4 3/3] LSM: Add context interface for proc attrs
>>
>> The /proc/.../attr/current interface is used by all three
>> Linux security modules (SELinux, Smack and AppArmor) to
>> report and modify the process security attribute. This is
>> all fine when there is exactly one of these modules active
>> and the userspace code knows which it module it is.
>> It would require a major change to the "current" interface
>> to provide information about more than one set of process
>> security attributes. Instead, a "context" attribute is
>> added, which identifies the security module that the
>> information applies to. The format is:
>>
>>         lsmname='context-value'
>>
>> When multiple concurrent modules are supported the
>> /proc/.../attr/context interface will include the data
>> for all of the active modules.
>>
>>         lsmname1='context-value1',lsmname2='context-value2'
>>
>> The module specific subdirectories under attr contain context
>> entries that report the information for that specific module
>> in the same format.
>>
>> Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>

Acked-by: John Johansen <john.johansen@canonical.com>

>>
>> ---
>>  Documentation/security/LSM.txt |   8 +++
>>  fs/proc/base.c                 |   4 ++
>>  security/apparmor/lsm.c        |  35 +++++++++++--
>>  security/security.c            | 108 +++++++++++++++++++++++++++++++++++++++++
>>  security/selinux/hooks.c       |  20 +++++++-
>>  security/smack/smack_lsm.c     |  20 ++++----
>>  6 files changed, 180 insertions(+), 15 deletions(-)
> 
> Acked-by: Paul Moore <paul@paul-moore.com>
> 
>> diff --git a/Documentation/security/LSM.txt b/Documentation/security/LSM.txt
>> index 125c489..af3eb11 100644
>> --- a/Documentation/security/LSM.txt
>> +++ b/Documentation/security/LSM.txt
>> @@ -36,6 +36,14 @@ for SELinux would be in /proc/.../attr/selinux. Using the files
>>  found directly in /proc/.../attr (e.g. current) should be avoided.
>>  These files remain as legacy interfaces.
>>
>> +The files named "context" in the attr directories contain the
>> +same information as the "current" files, but formatted to
>> +identify the module it comes from.
>> +
>> +if selinux is the active security module:
>> +       /proc/self/attr/context could contain selinux='unconfined_t'
>> +       /proc/self/attr/selinux/context could contain selinux='unconfined_t'
>> +
>>  Based on https://lkml.org/lkml/2007/10/26/215,
>>  a new LSM is accepted into the kernel when its intent (a description of
>>  what it tries to protect against and in what cases one would expect to
>> diff --git a/fs/proc/base.c b/fs/proc/base.c
>> index 182bc28..df94f26 100644
>> --- a/fs/proc/base.c
>> +++ b/fs/proc/base.c
>> @@ -2532,6 +2532,7 @@ static const struct pid_entry selinux_attr_dir_stuff[] = {
>>         ATTR("selinux", "fscreate",     S_IRUGO|S_IWUGO),
>>         ATTR("selinux", "keycreate",    S_IRUGO|S_IWUGO),
>>         ATTR("selinux", "sockcreate",   S_IRUGO|S_IWUGO),
>> +       ATTR("selinux", "context",      S_IRUGO|S_IWUGO),
>>  };
>>  LSM_DIR_OPS(selinux);
>>  #endif
>> @@ -2539,6 +2540,7 @@ LSM_DIR_OPS(selinux);
>>  #ifdef CONFIG_SECURITY_SMACK
>>  static const struct pid_entry smack_attr_dir_stuff[] = {
>>         ATTR("smack", "current",        S_IRUGO|S_IWUGO),
>> +       ATTR("smack", "context",        S_IRUGO|S_IWUGO),
>>  };
>>  LSM_DIR_OPS(smack);
>>  #endif
>> @@ -2548,6 +2550,7 @@ static const struct pid_entry apparmor_attr_dir_stuff[] = {
>>         ATTR("apparmor", "current",     S_IRUGO|S_IWUGO),
>>         ATTR("apparmor", "prev",        S_IRUGO),
>>         ATTR("apparmor", "exec",        S_IRUGO|S_IWUGO),
>> +       ATTR("apparmor", "context",     S_IRUGO|S_IWUGO),
>>  };
>>  LSM_DIR_OPS(apparmor);
>>  #endif
>> @@ -2559,6 +2562,7 @@ static const struct pid_entry attr_dir_stuff[] = {
>>         ATTR(NULL, "fscreate",          S_IRUGO|S_IWUGO),
>>         ATTR(NULL, "keycreate",         S_IRUGO|S_IWUGO),
>>         ATTR(NULL, "sockcreate",        S_IRUGO|S_IWUGO),
>> +       ATTR(NULL, "context",           S_IRUGO|S_IWUGO),
>>  #ifdef CONFIG_SECURITY_SELINUX
>>         DIR("selinux",                  S_IRUGO|S_IXUGO,
>>             proc_selinux_attr_dir_inode_ops, proc_selinux_attr_dir_ops),
>> diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
>> index fb0fb03..5cac15f 100644
>> --- a/security/apparmor/lsm.c
>> +++ b/security/apparmor/lsm.c
>> @@ -476,9 +476,13 @@ static int apparmor_getprocattr(struct task_struct *task, char *name,
>>         const struct cred *cred = get_task_cred(task);
>>         struct aa_task_cxt *cxt = cred_cxt(cred);
>>         struct aa_profile *profile = NULL;
>> +       char *vp;
>> +       char *np;
>>
>>         if (strcmp(name, "current") == 0)
>>                 profile = aa_get_newest_profile(cxt->profile);
>> +       else if (strcmp(name, "context") == 0)
>> +               profile = aa_get_newest_profile(cxt->profile);
>>         else if (strcmp(name, "prev") == 0  && cxt->previous)
>>                 profile = aa_get_newest_profile(cxt->previous);
>>         else if (strcmp(name, "exec") == 0 && cxt->onexec)
>> @@ -486,9 +490,29 @@ static int apparmor_getprocattr(struct task_struct *task, char *name,
>>         else
>>                 error = -EINVAL;
>>
>> -       if (profile)
>> -               error = aa_getprocattr(profile, value);
>> +       if (profile == NULL)
>> +               goto put_out;
>> +
>> +       error = aa_getprocattr(profile, &vp);
>> +       if (error < 0)
>> +               goto put_out;
>> +
>> +       if (strcmp(name, "context") == 0) {
>> +               *value = kasprintf(GFP_KERNEL, "apparmor='%s'", vp);
>> +               if (*value == NULL) {
>> +                       error = -ENOMEM;
>> +                       goto put_out;
>> +               }
>> +               np = strchr(*value, '\n');
>> +               if (np != NULL) {
>> +                       np[0] = '\'';
>> +                       np[1] = '\0';
>> +               }
>> +               error = strlen(*value);
>> +       } else
>> +               *value = vp;
>>
>> +put_out:
>>         aa_put_profile(profile);
>>         put_cred(cred);
>>
>> @@ -530,7 +554,7 @@ static int apparmor_setprocattr(struct task_struct *task, char *name,
>>                 return -EINVAL;
>>
>>         arg_size = size - (args - (char *) value);
>> -       if (strcmp(name, "current") == 0) {
>> +       if (strcmp(name, "current") == 0 || strcmp(name, "context") == 0) {
>>                 if (strcmp(command, "changehat") == 0) {
>>                         error = aa_setprocattr_changehat(args, arg_size,
>>                                                          !AA_DO_TEST);
>> @@ -552,7 +576,10 @@ static int apparmor_setprocattr(struct task_struct *task, char *name,
>>                 else
>>                         goto fail;
>>         } else
>> -               /* only support the "current" and "exec" process attributes */
>> +               /*
>> +                * only support the "current", context and "exec"
>> +                * process attributes
>> +                */
>>                 return -EINVAL;
>>
>>         if (!error)
>> diff --git a/security/security.c b/security/security.c
>> index 1e9cb55..f97f0d9 100644
>> --- a/security/security.c
>> +++ b/security/security.c
>> @@ -1186,8 +1186,47 @@ int security_getprocattr(struct task_struct *p, const char *lsm, char *name,
>>                                 char **value)
>>  {
>>         struct security_hook_list *hp;
>> +       char *vp;
>> +       char *cp = NULL;
>>         int rc = -EINVAL;
>> +       int trc;
>>
>> +       /*
>> +        * "context" requires work here in addition to what
>> +        * the modules provide.
>> +        */
>> +       if (strcmp(name, "context") == 0) {
>> +               *value = NULL;
>> +               list_for_each_entry(hp,
>> +                               &security_hook_heads.getprocattr, list) {
>> +                       if (lsm != NULL && strcmp(lsm, hp->lsm))
>> +                               continue;
>> +                       trc = hp->hook.getprocattr(p, "context", &vp);
>> +                       if (trc == -ENOENT)
>> +                               continue;
>> +                       if (trc <= 0) {
>> +                               kfree(*value);
>> +                               return trc;
>> +                       }
>> +                       rc = trc;
>> +                       if (*value == NULL) {
>> +                               *value = vp;
>> +                       } else {
>> +                               cp = kasprintf(GFP_KERNEL, "%s,%s", *value, vp);
>> +                               if (cp == NULL) {
>> +                                       kfree(*value);
>> +                                       kfree(vp);
>> +                                       return -ENOMEM;
>> +                               }
>> +                               kfree(*value);
>> +                               kfree(vp);
>> +                               *value = cp;
>> +                       }
>> +               }
>> +               if (rc > 0)
>> +                       return strlen(*value);
>> +               return rc;
>> +       }
>>
>>         list_for_each_entry(hp, &security_hook_heads.getprocattr, list) {
>>                 if (lsm != NULL && strcmp(lsm, hp->lsm))
>> @@ -1204,7 +1243,76 @@ int security_setprocattr(struct task_struct *p, const char *lsm, char *name,
>>  {
>>         struct security_hook_list *hp;
>>         int rc = -EINVAL;
>> +       char *local;
>> +       char *cp;
>> +       int slen;
>> +       int failed = 0;
>> +
>> +       /*
>> +        * If lsm is NULL look at all the modules to find one
>> +        * that processes name. If lsm is not NULL only look at
>> +        * that module.
>> +        *
>> +        * "context" is handled directly here.
>> +        */
>> +       if (strcmp(name, "context") == 0) {
>> +               /*
>> +                * First verify that the input is acceptable.
>> +                * lsm1='v1'lsm2='v2'lsm3='v3'
>> +                *
>> +                * A note on the use of strncmp() below.
>> +                * The check is for the substring at the beginning of cp.
>> +                * The kzalloc of size + 1 ensures a terminated string.
>> +                */
>> +               local = kzalloc(size + 1, GFP_KERNEL);
>> +               memcpy(local, value, size);
>> +               cp = local;
>> +               list_for_each_entry(hp, &security_hook_heads.setprocattr,
>> +                                       list) {
>> +                       if (lsm != NULL && strcmp(lsm, hp->lsm))
>> +                               continue;
>> +                       if (cp[0] == ',') {
>> +                               if (cp == local)
>> +                                       goto free_out;
>> +                               cp++;
>> +                       }
>> +                       slen = strlen(hp->lsm);
>> +                       if (strncmp(cp, hp->lsm, slen))
>> +                               goto free_out;
>> +                       cp += slen;
>> +                       if (cp[0] != '=' || cp[1] != '\'' || cp[2] == '\'')
>> +                               goto free_out;
>> +                       for (cp += 2; cp[0] != '\''; cp++)
>> +                               if (cp[0] == '\0')
>> +                                       goto free_out;
>> +                       cp++;
>> +               }
>>
>> +               cp = local;
>> +               list_for_each_entry(hp, &security_hook_heads.setprocattr,
>> +                                       list) {
>> +                       if (lsm != NULL && strcmp(lsm, hp->lsm))
>> +                               continue;
>> +                       if (cp[0] == ',')
>> +                               cp++;
>> +                       cp += strlen(hp->lsm) + 2;
>> +                       for (slen = 0; cp[slen] != '\''; slen++)
>> +                               ;
>> +                       cp[slen] = '\0';
>> +
>> +                       rc = hp->hook.setprocattr(p, "context", cp, slen);
>> +                       if (rc < 0)
>> +                               failed = rc;
>> +                       cp += slen + 1;
>> +               }
>> +               if (failed != 0)
>> +                       rc = failed;
>> +               else
>> +                       rc = size;
>> +free_out:
>> +               kfree(local);
>> +               return rc;
>> +       }
>>         list_for_each_entry(hp, &security_hook_heads.setprocattr, list) {
>>                 if (lsm != NULL && strcmp(lsm, hp->lsm))
>>                         continue;
>> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
>> index ed3a757..6397721 100644
>> --- a/security/selinux/hooks.c
>> +++ b/security/selinux/hooks.c
>> @@ -5711,6 +5711,8 @@ static int selinux_getprocattr(struct task_struct *p,
>>
>>         if (!strcmp(name, "current"))
>>                 sid = __tsec->sid;
>> +       else if (!strcmp(name, "context"))
>> +               sid = __tsec->sid;
>>         else if (!strcmp(name, "prev"))
>>                 sid = __tsec->osid;
>>         else if (!strcmp(name, "exec"))
>> @@ -5728,7 +5730,19 @@ static int selinux_getprocattr(struct task_struct *p,
>>         if (!sid)
>>                 return 0;
>>
>> -       error = security_sid_to_context(sid, value, &len);
>> +       if (strcmp(name, "context")) {
>> +               error = security_sid_to_context(sid, value, &len);
>> +       } else {
>> +               char *vp;
>> +
>> +               error = security_sid_to_context(sid, &vp, &len);
>> +               if (!error) {
>> +                       *value = kasprintf(GFP_KERNEL, "selinux='%s'", vp);
>> +                       if (*value == NULL)
>> +                               error = -ENOMEM;
>> +               }
>> +       }
>> +
>>         if (error)
>>                 return error;
>>         return len;
>> @@ -5768,6 +5782,8 @@ static int selinux_setprocattr(struct task_struct *p,
>>                 error = current_has_perm(p, PROCESS__SETSOCKCREATE);
>>         else if (!strcmp(name, "current"))
>>                 error = current_has_perm(p, PROCESS__SETCURRENT);
>> +       else if (!strcmp(name, "context"))
>> +               error = current_has_perm(p, PROCESS__SETCURRENT);
>>         else
>>                 error = -EINVAL;
>>         if (error)
>> @@ -5827,7 +5843,7 @@ static int selinux_setprocattr(struct task_struct *p,
>>                 tsec->keycreate_sid = sid;
>>         } else if (!strcmp(name, "sockcreate")) {
>>                 tsec->sockcreate_sid = sid;
>> -       } else if (!strcmp(name, "current")) {
>> +       } else if (!strcmp(name, "current") || !strcmp(name, "context")) {
>>                 error = -EINVAL;
>>                 if (sid == 0)
>>                         goto abort_change;
>> diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
>> index 3577009..92e66f8 100644
>> --- a/security/smack/smack_lsm.c
>> +++ b/security/smack/smack_lsm.c
>> @@ -3574,18 +3574,20 @@ 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 if (strcmp(name, "context") == 0) {
>> +               cp = kasprintf(GFP_KERNEL, "smack='%s'", skp->smk_known);
>> +               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);
>>  }
>>
>>  /**
>> @@ -3622,7 +3624,7 @@ static int smack_setprocattr(struct task_struct *p, char *name,
>>         if (value == NULL || size == 0 || size >= SMK_LONGLABEL)
>>                 return -EINVAL;
>>
>> -       if (strcmp(name, "current") != 0)
>> +       if (strcmp(name, "current") != 0 && strcmp(name, "context") != 0)
>>                 return -EINVAL;
>>
>>         skp = smk_import_entry(value, size);
>>
> 
> 
> 

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH v5 0/3] LSM: security module information improvements - Acked
  2016-07-02 17:25                 ` John Johansen
@ 2016-07-05 15:52                   ` Casey Schaufler
  2016-07-08 10:05                     ` James Morris
  2016-07-05 15:52                   ` [PATCH v5 1/3] LSM: Add /sys/kernel/security/lsm " Casey Schaufler
                                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 32+ messages in thread
From: Casey Schaufler @ 2016-07-05 15:52 UTC (permalink / raw)
  To: LSM, James Morris
  Cc: John Johansen, Paul Moore, Kees Cook, Stephen Smalley,
	Tetsuo Handa, LKLM

Subject: [PATCH v5 0/3] LSM: security module information improvements

I have added Acks. James, would you like me to add this to the
Smack tree?

Changes from v4:
	Use kasprintf instead of kzalloc() ... sprintf in more places.
	More in the documentation.
	Separate module information in contexts with ",". (not yet visible)

Changes from v3:
	Use kasprintf instead of kzalloc() ... sprintf.

Create interfaces that make it possible to deal with process
attributes in the face of multiple "major" security modules.

Patch 1/3 adds /sys/kernel/security/lsm, which provides
a list of the active security modules on the system.

	$ cat /sys/kernel/security/lsm
	capability,yama,loadpin,smack

Patch 2/3 adds a subdirectory in /proc/.../attr for each
security module that exports process attribute data. This
allows a program in easily differentiate between the "current"
value for Smack and AppArmor.

	$ cat /proc/self/attr/smack/current
	System

	$ cat /proc/self/attr/apparmor/current
	unconfined

Patch 3/3 adds an interface that provides module identified
information that otherwise matches the "current" attr.
This allows a system with multiple modules to provide the
complete security "context" in one place. A (future) system
with both Smack and AppArmor might report:

	$ cat /proc/self/attr/context
	smack='System',apparmor='unconfined'

Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
Acked-by: John Johansen <john.johansen@canonical.com>
Acked-by: Paul Moore <paul@paul-moore.com>
Acked-by: Kees Cook <keescook@chromium.org>

---
 Documentation/security/LSM.txt |  34 ++++++--
 fs/proc/base.c                 |  95 +++++++++++++++++++---
 fs/proc/internal.h             |   1 +
 include/linux/lsm_hooks.h      |  12 +--
 include/linux/security.h       |  15 ++--
 security/apparmor/lsm.c        |  38 +++++++--
 security/commoncap.c           |   3 +-
 security/inode.c               |  26 +++++-
 security/loadpin/loadpin.c     |   2 +-
 security/security.c            | 177 ++++++++++++++++++++++++++++++++++++++++-
 security/selinux/hooks.c       |  22 ++++-
 security/smack/smack_lsm.c     |  22 ++---
 security/tomoyo/tomoyo.c       |   2 +-
 security/yama/yama_lsm.c       |   2 +-
 14 files changed, 395 insertions(+), 56 deletions(-)

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH v5 1/3] LSM: Add /sys/kernel/security/lsm - Acked
  2016-07-02 17:25                 ` John Johansen
  2016-07-05 15:52                   ` [PATCH v5 0/3] LSM: security module information improvements - Acked Casey Schaufler
@ 2016-07-05 15:52                   ` Casey Schaufler
  2016-07-05 15:52                   ` [PATCH v5 2/3] LSM: module hierarchy in /proc/.../attr " Casey Schaufler
  2016-07-05 15:52                   ` [PATCH v5 3/3] LSM: Add context interface for proc attrs " Casey Schaufler
  3 siblings, 0 replies; 32+ messages in thread
From: Casey Schaufler @ 2016-07-05 15:52 UTC (permalink / raw)
  To: LSM, James Morris
  Cc: John Johansen, Paul Moore, Kees Cook, Stephen Smalley,
	Tetsuo Handa, LKLM

Subject: [PATCH v5 1/3] LSM: Add /sys/kernel/security/lsm

I got tired of having to find indirect ways to 
determine what security modules are active on a system.
I have added /sys/kernel/security/lsm, which contains a
comma separated list of the active security modules. No
more groping around in /proc/filesystems, which won't
help if the module doesn't support its own filesystem.

Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
Acked-by: John Johansen <john.johansen@canonical.com>
Acked-by: Paul Moore <paul@paul-moore.com>
Acked-by: Kees Cook <keescook@chromium.org>

---
 Documentation/security/LSM.txt |  7 +++++++
 include/linux/lsm_hooks.h      | 12 ++++--------
 security/apparmor/lsm.c        |  3 ++-
 security/commoncap.c           |  3 ++-
 security/inode.c               | 26 ++++++++++++++++++++++++--
 security/loadpin/loadpin.c     |  2 +-
 security/security.c            | 38 ++++++++++++++++++++++++++++++++++++++
 security/selinux/hooks.c       |  2 +-
 security/smack/smack_lsm.c     |  2 +-
 security/tomoyo/tomoyo.c       |  2 +-
 security/yama/yama_lsm.c       |  2 +-
 11 files changed, 82 insertions(+), 17 deletions(-)

diff --git a/Documentation/security/LSM.txt b/Documentation/security/LSM.txt
index 3db7e67..c2683f2 100644
--- a/Documentation/security/LSM.txt
+++ b/Documentation/security/LSM.txt
@@ -22,6 +22,13 @@ system, building their checks on top of the defined capability hooks.
 For more details on capabilities, see capabilities(7) in the Linux
 man-pages project.
 
+A list of the active security modules can be found by reading
+/sys/kernel/security/lsm. This is a comma separated list, and
+will always include the capability module. The list reflects the
+order in which checks are made. The capability module will always
+be first, followed by any "minor" modules (e.g. Yama) and then
+the one "major" module (e.g. SELinux) if there is one configured.
+
 Based on https://lkml.org/lkml/2007/10/26/215,
 a new LSM is accepted into the kernel when its intent (a description of
 what it tries to protect against and in what cases one would expect to
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 7ae3976..0bfd441 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -1841,6 +1841,7 @@ struct security_hook_list {
 	struct list_head		list;
 	struct list_head		*head;
 	union security_list_options	hook;
+	char				*lsm;
 };
 
 /*
@@ -1853,15 +1854,10 @@ struct security_hook_list {
 	{ .head = &security_hook_heads.HEAD, .hook = { .HEAD = HOOK } }
 
 extern struct security_hook_heads security_hook_heads;
+extern char *lsm_names;
 
-static inline void security_add_hooks(struct security_hook_list *hooks,
-				      int count)
-{
-	int i;
-
-	for (i = 0; i < count; i++)
-		list_add_tail_rcu(&hooks[i].list, hooks[i].head);
-}
+extern void security_add_hooks(struct security_hook_list *hooks, int count,
+				char *lsm);
 
 #ifdef CONFIG_SECURITY_SELINUX_DISABLE
 /*
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 2660fbc..fb0fb03 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -880,7 +880,8 @@ static int __init apparmor_init(void)
 		aa_free_root_ns();
 		goto alloc_out;
 	}
-	security_add_hooks(apparmor_hooks, ARRAY_SIZE(apparmor_hooks));
+	security_add_hooks(apparmor_hooks, ARRAY_SIZE(apparmor_hooks),
+				"apparmor");
 
 	/* Report that AppArmor successfully initialized */
 	apparmor_initialized = 1;
diff --git a/security/commoncap.c b/security/commoncap.c
index e7fadde..d101734 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -1090,7 +1090,8 @@ struct security_hook_list capability_hooks[] = {
 
 void __init capability_add_hooks(void)
 {
-	security_add_hooks(capability_hooks, ARRAY_SIZE(capability_hooks));
+	security_add_hooks(capability_hooks, ARRAY_SIZE(capability_hooks),
+				"capability");
 }
 
 #endif /* CONFIG_SECURITY */
diff --git a/security/inode.c b/security/inode.c
index 28414b0..235ad82 100644
--- a/security/inode.c
+++ b/security/inode.c
@@ -20,6 +20,7 @@
 #include <linux/init.h>
 #include <linux/namei.h>
 #include <linux/security.h>
+#include <linux/lsm_hooks.h>
 #include <linux/magic.h>
 
 static struct vfsmount *mount;
@@ -208,6 +209,21 @@ void securityfs_remove(struct dentry *dentry)
 }
 EXPORT_SYMBOL_GPL(securityfs_remove);
 
+#ifdef CONFIG_SECURITY
+static struct dentry *lsm_dentry;
+static ssize_t lsm_read(struct file *filp, char __user *buf, size_t count,
+			loff_t *ppos)
+{
+	return simple_read_from_buffer(buf, count, ppos, lsm_names,
+		strlen(lsm_names));
+}
+
+static const struct file_operations lsm_ops = {
+	.read = lsm_read,
+	.llseek = generic_file_llseek,
+};
+#endif
+
 static int __init securityfs_init(void)
 {
 	int retval;
@@ -217,9 +233,15 @@ static int __init securityfs_init(void)
 		return retval;
 
 	retval = register_filesystem(&fs_type);
-	if (retval)
+	if (retval) {
 		sysfs_remove_mount_point(kernel_kobj, "security");
-	return retval;
+		return retval;
+	}
+#ifdef CONFIG_SECURITY
+	lsm_dentry = securityfs_create_file("lsm", S_IRUGO, NULL, NULL,
+						&lsm_ops);
+#endif
+	return 0;
 }
 
 core_initcall(securityfs_init);
diff --git a/security/loadpin/loadpin.c b/security/loadpin/loadpin.c
index 89a46f1..1d82eae 100644
--- a/security/loadpin/loadpin.c
+++ b/security/loadpin/loadpin.c
@@ -182,7 +182,7 @@ static struct security_hook_list loadpin_hooks[] = {
 void __init loadpin_add_hooks(void)
 {
 	pr_info("ready to pin (currently %sabled)", enabled ? "en" : "dis");
-	security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks));
+	security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks), "loadpin");
 }
 
 /* Should not be mutable after boot, so not listed in sysfs (perm == 0). */
diff --git a/security/security.c b/security/security.c
index c4bb47d..1093f73 100644
--- a/security/security.c
+++ b/security/security.c
@@ -32,6 +32,7 @@
 /* Maximum number of letters for an LSM name string */
 #define SECURITY_NAME_MAX	10
 
+char *lsm_names;
 /* Boot-time LSM user choice */
 static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =
 	CONFIG_DEFAULT_SECURITY;
@@ -78,6 +79,22 @@ static int __init choose_lsm(char *str)
 }
 __setup("security=", choose_lsm);
 
+static int lsm_append(char *new, char **result)
+{
+	char *cp;
+
+	if (*result == NULL) {
+		*result = kstrdup(new, GFP_KERNEL);
+	} else {
+		cp = kasprintf(GFP_KERNEL, "%s,%s", *result, new);
+		if (cp == NULL)
+			return -ENOMEM;
+		kfree(*result);
+		*result = cp;
+	}
+	return 0;
+}
+
 /**
  * security_module_enable - Load given security module on boot ?
  * @module: the name of the module
@@ -97,6 +114,27 @@ int __init security_module_enable(const char *module)
 	return !strcmp(module, chosen_lsm);
 }
 
+/**
+ * security_add_hooks - Add a modules hooks to the hook lists.
+ * @hooks: the hooks to add
+ * @count: the number of hooks to add
+ * @lsm: the name of the security module
+ *
+ * Each LSM has to register its hooks with the infrastructure.
+ */
+void __init security_add_hooks(struct security_hook_list *hooks, int count,
+				char *lsm)
+{
+	int i;
+
+	for (i = 0; i < count; i++) {
+		hooks[i].lsm = lsm;
+		list_add_tail_rcu(&hooks[i].list, hooks[i].head);
+	}
+	if (lsm_append(lsm, &lsm_names) < 0)
+		panic("%s - Cannot get early memory.\n", __func__);
+}
+
 /*
  * Hook list operation macros.
  *
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index a86d537..ed3a757 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -6233,7 +6233,7 @@ static __init int selinux_init(void)
 					    0, SLAB_PANIC, NULL);
 	avc_init();
 
-	security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks));
+	security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks), "selinux");
 
 	if (avc_add_callback(selinux_netcache_avc_callback, AVC_CALLBACK_RESET))
 		panic("SELinux: Unable to register AVC netcache callback\n");
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 6777295..3577009 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -4801,7 +4801,7 @@ static __init int smack_init(void)
 	/*
 	 * Register with LSM
 	 */
-	security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks));
+	security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks), "smack");
 
 	return 0;
 }
diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
index 75c9987..edc52d6 100644
--- a/security/tomoyo/tomoyo.c
+++ b/security/tomoyo/tomoyo.c
@@ -542,7 +542,7 @@ static int __init tomoyo_init(void)
 	if (!security_module_enable("tomoyo"))
 		return 0;
 	/* register ourselves with the security framework */
-	security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks));
+	security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks), "tomoyo");
 	printk(KERN_INFO "TOMOYO Linux initialized\n");
 	cred->security = &tomoyo_kernel_domain;
 	tomoyo_mm_init();
diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c
index 0309f21..f8ee60e 100644
--- a/security/yama/yama_lsm.c
+++ b/security/yama/yama_lsm.c
@@ -471,6 +471,6 @@ static inline void yama_init_sysctl(void) { }
 void __init yama_add_hooks(void)
 {
 	pr_info("Yama: becoming mindful.\n");
-	security_add_hooks(yama_hooks, ARRAY_SIZE(yama_hooks));
+	security_add_hooks(yama_hooks, ARRAY_SIZE(yama_hooks), "yama");
 	yama_init_sysctl();
 }

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v5 2/3] LSM: module hierarchy in /proc/.../attr - Acked
  2016-07-02 17:25                 ` John Johansen
  2016-07-05 15:52                   ` [PATCH v5 0/3] LSM: security module information improvements - Acked Casey Schaufler
  2016-07-05 15:52                   ` [PATCH v5 1/3] LSM: Add /sys/kernel/security/lsm " Casey Schaufler
@ 2016-07-05 15:52                   ` Casey Schaufler
  2016-07-05 15:52                   ` [PATCH v5 3/3] LSM: Add context interface for proc attrs " Casey Schaufler
  3 siblings, 0 replies; 32+ messages in thread
From: Casey Schaufler @ 2016-07-05 15:52 UTC (permalink / raw)
  To: LSM, James Morris
  Cc: John Johansen, Paul Moore, Kees Cook, Stephen Smalley,
	Tetsuo Handa, LKLM

Subject: [PATCH v5 2/3] LSM: module hierarchy in /proc/.../attr

Back in 2007 I made what turned out to be a rather serious
mistake in the implementation of the Smack security module.
The SELinux module used an interface in /proc to manipulate
the security context on processes. Rather than use a similar
interface, I used the same interface. The AppArmor team did
likewise. Now /proc/.../attr/current will tell you the
security "context" of the process, but it will be different
depending on the security module you're using. That hasn't
been a problem to date, as you can only have one module
that supports process attributes at a time. We are coming
up on a change to that, where multiple modules with process
attributes can be supported. (Not included here)

This patch provides a subdirectory in /proc/.../attr for
each of the security modules that use the LSM hooks
getprocattr() and setprocattr(). Each of the interfaces
used by a module are presented in the subdirectory. The
old interfaces remain and work the same as before.
User space code can begin migrating to the subdirectory
interfaces in anticipation of the time when what comes
from /proc/self/attr/current might not be what a runtime
wants.

The original implementation is by Kees Cook. The code
has been changed a bit to reflect changes in the direction
of the multiple concurrent module work, to be independent
of it, and to bring it up to date with the current tree.

Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
Acked-by: John Johansen <john.johansen@canonical.com>
Acked-by: Paul Moore <paul@paul-moore.com>
Acked-by: Kees Cook <keescook@chromium.org>

---
 Documentation/security/LSM.txt | 19 ++++++---
 fs/proc/base.c                 | 91 +++++++++++++++++++++++++++++++++++++-----
 fs/proc/internal.h             |  1 +
 include/linux/security.h       | 15 ++++---
 security/security.c            | 31 ++++++++++++--
 5 files changed, 133 insertions(+), 24 deletions(-)

diff --git a/Documentation/security/LSM.txt b/Documentation/security/LSM.txt
index c2683f2..125c489 100644
--- a/Documentation/security/LSM.txt
+++ b/Documentation/security/LSM.txt
@@ -16,11 +16,10 @@ MAC extensions, other extensions can be built using the LSM to provide
 specific changes to system operation when these tweaks are not available
 in the core functionality of Linux itself.
 
-Without a specific LSM built into the kernel, the default LSM will be the
-Linux capabilities system. Most LSMs choose to extend the capabilities
-system, building their checks on top of the defined capability hooks.
-For more details on capabilities, see capabilities(7) in the Linux
-man-pages project.
+The Linux capabilities modules will always be included. For more details
+on capabilities, see capabilities(7) in the Linux man-pages project.
+This may be followed by any number of "minor" modules and at most one
+"major" module.
 
 A list of the active security modules can be found by reading
 /sys/kernel/security/lsm. This is a comma separated list, and
@@ -29,6 +28,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 the module
+specific subdirectories in /proc/.../attr. The attributes related
+to Smack would be found in /proc/.../attr/smack while the attributes
+for SELinux would be in /proc/.../attr/selinux. Using the files
+found directly in /proc/.../attr (e.g. current) should be avoided.
+These files remain as legacy interfaces.
+
 Based on https://lkml.org/lkml/2007/10/26/215,
 a new LSM is accepted into the kernel when its intent (a description of
 what it tries to protect against and in what cases one would expect to
@@ -38,4 +45,4 @@ that end users and distros can make a more informed decision about which
 LSMs suit their requirements.
 
 For extensive documentation on the available LSM hook interfaces, please
-see include/linux/security.h.
+see include/linux/lsm_hooks.h.
diff --git a/fs/proc/base.c b/fs/proc/base.c
index a11eb71..182bc28 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -131,9 +131,13 @@ struct pid_entry {
 #define REG(NAME, MODE, fops)				\
 	NOD(NAME, (S_IFREG|(MODE)), NULL, &fops, {})
 #define ONE(NAME, MODE, show)				\
-	NOD(NAME, (S_IFREG|(MODE)), 			\
+	NOD(NAME, (S_IFREG|(MODE)),			\
 		NULL, &proc_single_file_operations,	\
 		{ .proc_show = show } )
+#define ATTR(LSM, NAME, MODE)				\
+	NOD(NAME, (S_IFREG|(MODE)),			\
+		NULL, &proc_pid_attr_operations,	\
+		{ .lsm = LSM })
 
 /*
  * Count the number of hardlinks for the pid_entry table, excluding the .
@@ -2433,7 +2437,7 @@ static ssize_t proc_pid_attr_read(struct file * file, char __user * buf,
 	if (!task)
 		return -ESRCH;
 
-	length = security_getprocattr(task,
+	length = security_getprocattr(task, PROC_I(inode)->op.lsm,
 				      (char*)file->f_path.dentry->d_name.name,
 				      &p);
 	put_task_struct(task);
@@ -2473,7 +2477,7 @@ static ssize_t proc_pid_attr_write(struct file * file, const char __user * buf,
 	if (length < 0)
 		goto out_free;
 
-	length = security_setprocattr(task,
+	length = security_setprocattr(task, PROC_I(inode)->op.lsm,
 				      (char*)file->f_path.dentry->d_name.name,
 				      page, count);
 	mutex_unlock(&task->signal->cred_guard_mutex);
@@ -2491,13 +2495,82 @@ static const struct file_operations proc_pid_attr_operations = {
 	.llseek		= generic_file_llseek,
 };
 
+#define LSM_DIR_OPS(LSM) \
+static int proc_##LSM##_attr_dir_iterate(struct file *filp, \
+			     struct dir_context *ctx) \
+{ \
+	return proc_pident_readdir(filp, ctx, \
+				   LSM##_attr_dir_stuff, \
+				   ARRAY_SIZE(LSM##_attr_dir_stuff)); \
+} \
+\
+static const struct file_operations proc_##LSM##_attr_dir_ops = { \
+	.read		= generic_read_dir, \
+	.iterate	= proc_##LSM##_attr_dir_iterate, \
+	.llseek		= default_llseek, \
+}; \
+\
+static struct dentry *proc_##LSM##_attr_dir_lookup(struct inode *dir, \
+				struct dentry *dentry, unsigned int flags) \
+{ \
+	return proc_pident_lookup(dir, dentry, \
+				  LSM##_attr_dir_stuff, \
+				  ARRAY_SIZE(LSM##_attr_dir_stuff)); \
+} \
+\
+static const struct inode_operations proc_##LSM##_attr_dir_inode_ops = { \
+	.lookup		= proc_##LSM##_attr_dir_lookup, \
+	.getattr	= pid_getattr, \
+	.setattr	= proc_setattr, \
+}
+
+#ifdef CONFIG_SECURITY_SELINUX
+static const struct pid_entry selinux_attr_dir_stuff[] = {
+	ATTR("selinux", "current",	S_IRUGO|S_IWUGO),
+	ATTR("selinux", "prev",		S_IRUGO),
+	ATTR("selinux", "exec",		S_IRUGO|S_IWUGO),
+	ATTR("selinux", "fscreate",	S_IRUGO|S_IWUGO),
+	ATTR("selinux", "keycreate",	S_IRUGO|S_IWUGO),
+	ATTR("selinux", "sockcreate",	S_IRUGO|S_IWUGO),
+};
+LSM_DIR_OPS(selinux);
+#endif
+
+#ifdef CONFIG_SECURITY_SMACK
+static const struct pid_entry smack_attr_dir_stuff[] = {
+	ATTR("smack", "current",	S_IRUGO|S_IWUGO),
+};
+LSM_DIR_OPS(smack);
+#endif
+
+#ifdef CONFIG_SECURITY_APPARMOR
+static const struct pid_entry apparmor_attr_dir_stuff[] = {
+	ATTR("apparmor", "current",	S_IRUGO|S_IWUGO),
+	ATTR("apparmor", "prev",	S_IRUGO),
+	ATTR("apparmor", "exec",	S_IRUGO|S_IWUGO),
+};
+LSM_DIR_OPS(apparmor);
+#endif
+
 static const struct pid_entry attr_dir_stuff[] = {
-	REG("current",    S_IRUGO|S_IWUGO, proc_pid_attr_operations),
-	REG("prev",       S_IRUGO,	   proc_pid_attr_operations),
-	REG("exec",       S_IRUGO|S_IWUGO, proc_pid_attr_operations),
-	REG("fscreate",   S_IRUGO|S_IWUGO, proc_pid_attr_operations),
-	REG("keycreate",  S_IRUGO|S_IWUGO, proc_pid_attr_operations),
-	REG("sockcreate", S_IRUGO|S_IWUGO, proc_pid_attr_operations),
+	ATTR(NULL, "current",		S_IRUGO|S_IWUGO),
+	ATTR(NULL, "prev",		S_IRUGO),
+	ATTR(NULL, "exec",		S_IRUGO|S_IWUGO),
+	ATTR(NULL, "fscreate",		S_IRUGO|S_IWUGO),
+	ATTR(NULL, "keycreate",		S_IRUGO|S_IWUGO),
+	ATTR(NULL, "sockcreate",	S_IRUGO|S_IWUGO),
+#ifdef CONFIG_SECURITY_SELINUX
+	DIR("selinux",			S_IRUGO|S_IXUGO,
+	    proc_selinux_attr_dir_inode_ops, proc_selinux_attr_dir_ops),
+#endif
+#ifdef CONFIG_SECURITY_SMACK
+	DIR("smack",			S_IRUGO|S_IXUGO,
+	    proc_smack_attr_dir_inode_ops, proc_smack_attr_dir_ops),
+#endif
+#ifdef CONFIG_SECURITY_APPARMOR
+	DIR("apparmor",			S_IRUGO|S_IXUGO,
+	    proc_apparmor_attr_dir_inode_ops, proc_apparmor_attr_dir_ops),
+#endif
 };
 
 static int proc_attr_dir_readdir(struct file *file, struct dir_context *ctx)
diff --git a/fs/proc/internal.h b/fs/proc/internal.h
index aa27810..b607cd5 100644
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -56,6 +56,7 @@ union proc_op {
 	int (*proc_show)(struct seq_file *m,
 		struct pid_namespace *ns, struct pid *pid,
 		struct task_struct *task);
+	const char *lsm;
 };
 
 struct proc_inode {
diff --git a/include/linux/security.h b/include/linux/security.h
index 14df373..383fcb0 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -355,8 +355,10 @@ int security_sem_semctl(struct sem_array *sma, int cmd);
 int security_sem_semop(struct sem_array *sma, struct sembuf *sops,
 			unsigned nsops, int alter);
 void security_d_instantiate(struct dentry *dentry, struct inode *inode);
-int security_getprocattr(struct task_struct *p, char *name, char **value);
-int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size);
+int security_getprocattr(struct task_struct *p, const char *lsm, char *name,
+				char **value);
+int security_setprocattr(struct task_struct *p, const char *lsm, char *name,
+				void *value, size_t size);
 int security_netlink_send(struct sock *sk, struct sk_buff *skb);
 int security_ismaclabel(const char *name);
 int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen);
@@ -1075,15 +1077,18 @@ static inline int security_sem_semop(struct sem_array *sma,
 	return 0;
 }
 
-static inline void security_d_instantiate(struct dentry *dentry, struct inode *inode)
+static inline void security_d_instantiate(struct dentry *dentry,
+						struct inode *inode)
 { }
 
-static inline int security_getprocattr(struct task_struct *p, char *name, char **value)
+static inline int security_getprocattr(struct task_struct *p, const char *lsm,
+					char *name, char **value)
 {
 	return -EINVAL;
 }
 
-static inline int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size)
+static inline int security_setprocattr(struct task_struct *p, const char *lsm,
+					char *name, void *value, size_t size)
 {
 	return -EINVAL;
 }
diff --git a/security/security.c b/security/security.c
index 1093f73..1e9cb55 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1182,14 +1182,37 @@ 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;
+	int rc = -EINVAL;
+
+
+	list_for_each_entry(hp, &security_hook_heads.getprocattr, list) {
+		if (lsm != NULL && strcmp(lsm, hp->lsm))
+			continue;
+		rc = hp->hook.getprocattr(p, name, value);
+		if (rc != -ENOENT)
+			return rc;
+	}
+	return -EINVAL;
 }
 
-int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size)
+int security_setprocattr(struct task_struct *p, const char *lsm, char *name,
+				void *value, size_t size)
 {
-	return call_int_hook(setprocattr, -EINVAL, p, name, value, size);
+	struct security_hook_list *hp;
+	int rc = -EINVAL;
+
+	list_for_each_entry(hp, &security_hook_heads.setprocattr, list) {
+		if (lsm != NULL && strcmp(lsm, hp->lsm))
+			continue;
+		rc = hp->hook.setprocattr(p, name, value, size);
+		if (rc != -ENOENT)
+			break;
+	}
+	return rc;
 }
 
 int security_netlink_send(struct sock *sk, struct sk_buff *skb)

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v5 3/3] LSM: Add context interface for proc attrs - Acked
  2016-07-02 17:25                 ` John Johansen
                                     ` (2 preceding siblings ...)
  2016-07-05 15:52                   ` [PATCH v5 2/3] LSM: module hierarchy in /proc/.../attr " Casey Schaufler
@ 2016-07-05 15:52                   ` Casey Schaufler
  3 siblings, 0 replies; 32+ messages in thread
From: Casey Schaufler @ 2016-07-05 15:52 UTC (permalink / raw)
  To: LSM, James Morris
  Cc: John Johansen, Paul Moore, Kees Cook, Stephen Smalley,
	Tetsuo Handa, LKLM

Subject: [PATCH v5 3/3] LSM: Add context interface for proc attrs

The /proc/.../attr/current interface is used by all three
Linux security modules (SELinux, Smack and AppArmor) to
report and modify the process security attribute. This is
all fine when there is exactly one of these modules active
and the userspace code knows which it module it is.
It would require a major change to the "current" interface
to provide information about more than one set of process
security attributes. Instead, a "context" attribute is
added, which identifies the security module that the
information applies to. The format is:

        lsmname='context-value'

When multiple concurrent modules are supported the
/proc/.../attr/context interface will include the data
for all of the active modules.

	lsmname1='context-value1',lsmname2='context-value2'

The module specific subdirectories under attr contain context
entries that report the information for that specific module
in the same format.

Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
Acked-by: John Johansen <john.johansen@canonical.com>
Acked-by: Paul Moore <paul@paul-moore.com>
Acked-by: Kees Cook <keescook@chromium.org>

---
 Documentation/security/LSM.txt |   8 +++
 fs/proc/base.c                 |   4 ++
 security/apparmor/lsm.c        |  35 +++++++++++--
 security/security.c            | 108 +++++++++++++++++++++++++++++++++++++++++
 security/selinux/hooks.c       |  20 +++++++-
 security/smack/smack_lsm.c     |  20 ++++----
 6 files changed, 180 insertions(+), 15 deletions(-)

diff --git a/Documentation/security/LSM.txt b/Documentation/security/LSM.txt
index 125c489..af3eb11 100644
--- a/Documentation/security/LSM.txt
+++ b/Documentation/security/LSM.txt
@@ -36,6 +36,14 @@ for SELinux would be in /proc/.../attr/selinux. Using the files
 found directly in /proc/.../attr (e.g. current) should be avoided.
 These files remain as legacy interfaces.
 
+The files named "context" in the attr directories contain the
+same information as the "current" files, but formatted to
+identify the module it comes from.
+
+if selinux is the active security module:
+	/proc/self/attr/context could contain selinux='unconfined_t'
+	/proc/self/attr/selinux/context could contain selinux='unconfined_t'
+
 Based on https://lkml.org/lkml/2007/10/26/215,
 a new LSM is accepted into the kernel when its intent (a description of
 what it tries to protect against and in what cases one would expect to
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 182bc28..df94f26 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -2532,6 +2532,7 @@ static const struct pid_entry selinux_attr_dir_stuff[] = {
 	ATTR("selinux", "fscreate",	S_IRUGO|S_IWUGO),
 	ATTR("selinux", "keycreate",	S_IRUGO|S_IWUGO),
 	ATTR("selinux", "sockcreate",	S_IRUGO|S_IWUGO),
+	ATTR("selinux", "context",	S_IRUGO|S_IWUGO),
 };
 LSM_DIR_OPS(selinux);
 #endif
@@ -2539,6 +2540,7 @@ LSM_DIR_OPS(selinux);
 #ifdef CONFIG_SECURITY_SMACK
 static const struct pid_entry smack_attr_dir_stuff[] = {
 	ATTR("smack", "current",	S_IRUGO|S_IWUGO),
+	ATTR("smack", "context",	S_IRUGO|S_IWUGO),
 };
 LSM_DIR_OPS(smack);
 #endif
@@ -2548,6 +2550,7 @@ static const struct pid_entry apparmor_attr_dir_stuff[] = {
 	ATTR("apparmor", "current",	S_IRUGO|S_IWUGO),
 	ATTR("apparmor", "prev",	S_IRUGO),
 	ATTR("apparmor", "exec",	S_IRUGO|S_IWUGO),
+	ATTR("apparmor", "context",	S_IRUGO|S_IWUGO),
 };
 LSM_DIR_OPS(apparmor);
 #endif
@@ -2559,6 +2562,7 @@ static const struct pid_entry attr_dir_stuff[] = {
 	ATTR(NULL, "fscreate",		S_IRUGO|S_IWUGO),
 	ATTR(NULL, "keycreate",		S_IRUGO|S_IWUGO),
 	ATTR(NULL, "sockcreate",	S_IRUGO|S_IWUGO),
+	ATTR(NULL, "context",		S_IRUGO|S_IWUGO),
 #ifdef CONFIG_SECURITY_SELINUX
 	DIR("selinux",			S_IRUGO|S_IXUGO,
 	    proc_selinux_attr_dir_inode_ops, proc_selinux_attr_dir_ops),
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index fb0fb03..5cac15f 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -476,9 +476,13 @@ static int apparmor_getprocattr(struct task_struct *task, char *name,
 	const struct cred *cred = get_task_cred(task);
 	struct aa_task_cxt *cxt = cred_cxt(cred);
 	struct aa_profile *profile = NULL;
+	char *vp;
+	char *np;
 
 	if (strcmp(name, "current") == 0)
 		profile = aa_get_newest_profile(cxt->profile);
+	else if (strcmp(name, "context") == 0)
+		profile = aa_get_newest_profile(cxt->profile);
 	else if (strcmp(name, "prev") == 0  && cxt->previous)
 		profile = aa_get_newest_profile(cxt->previous);
 	else if (strcmp(name, "exec") == 0 && cxt->onexec)
@@ -486,9 +490,29 @@ static int apparmor_getprocattr(struct task_struct *task, char *name,
 	else
 		error = -EINVAL;
 
-	if (profile)
-		error = aa_getprocattr(profile, value);
+	if (profile == NULL)
+		goto put_out;
+
+	error = aa_getprocattr(profile, &vp);
+	if (error < 0)
+		goto put_out;
+
+	if (strcmp(name, "context") == 0) {
+		*value = kasprintf(GFP_KERNEL, "apparmor='%s'", vp);
+		if (*value == NULL) {
+			error = -ENOMEM;
+			goto put_out;
+		}
+		np = strchr(*value, '\n');
+		if (np != NULL) {
+			np[0] = '\'';
+			np[1] = '\0';
+		}
+		error = strlen(*value);
+	} else
+		*value = vp;
 
+put_out:
 	aa_put_profile(profile);
 	put_cred(cred);
 
@@ -530,7 +554,7 @@ static int apparmor_setprocattr(struct task_struct *task, char *name,
 		return -EINVAL;
 
 	arg_size = size - (args - (char *) value);
-	if (strcmp(name, "current") == 0) {
+	if (strcmp(name, "current") == 0 || strcmp(name, "context") == 0) {
 		if (strcmp(command, "changehat") == 0) {
 			error = aa_setprocattr_changehat(args, arg_size,
 							 !AA_DO_TEST);
@@ -552,7 +576,10 @@ static int apparmor_setprocattr(struct task_struct *task, char *name,
 		else
 			goto fail;
 	} else
-		/* only support the "current" and "exec" process attributes */
+		/*
+		 * only support the "current", context and "exec"
+		 * process attributes
+		 */
 		return -EINVAL;
 
 	if (!error)
diff --git a/security/security.c b/security/security.c
index 1e9cb55..f97f0d9 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1186,8 +1186,47 @@ int security_getprocattr(struct task_struct *p, const char *lsm, char *name,
 				char **value)
 {
 	struct security_hook_list *hp;
+	char *vp;
+	char *cp = NULL;
 	int rc = -EINVAL;
+	int trc;
 
+	/*
+	 * "context" requires work here in addition to what
+	 * the modules provide.
+	 */
+	if (strcmp(name, "context") == 0) {
+		*value = NULL;
+		list_for_each_entry(hp,
+				&security_hook_heads.getprocattr, list) {
+			if (lsm != NULL && strcmp(lsm, hp->lsm))
+				continue;
+			trc = hp->hook.getprocattr(p, "context", &vp);
+			if (trc == -ENOENT)
+				continue;
+			if (trc <= 0) {
+				kfree(*value);
+				return trc;
+			}
+			rc = trc;
+			if (*value == NULL) {
+				*value = vp;
+			} else {
+				cp = kasprintf(GFP_KERNEL, "%s,%s", *value, vp);
+				if (cp == NULL) {
+					kfree(*value);
+					kfree(vp);
+					return -ENOMEM;
+				}
+				kfree(*value);
+				kfree(vp);
+				*value = cp;
+			}
+		}
+		if (rc > 0)
+			return strlen(*value);
+		return rc;
+	}
 
 	list_for_each_entry(hp, &security_hook_heads.getprocattr, list) {
 		if (lsm != NULL && strcmp(lsm, hp->lsm))
@@ -1204,7 +1243,76 @@ int security_setprocattr(struct task_struct *p, const char *lsm, char *name,
 {
 	struct security_hook_list *hp;
 	int rc = -EINVAL;
+	char *local;
+	char *cp;
+	int slen;
+	int failed = 0;
+
+	/*
+	 * If lsm is NULL look at all the modules to find one
+	 * that processes name. If lsm is not NULL only look at
+	 * that module.
+	 *
+	 * "context" is handled directly here.
+	 */
+	if (strcmp(name, "context") == 0) {
+		/*
+		 * First verify that the input is acceptable.
+		 * lsm1='v1'lsm2='v2'lsm3='v3'
+		 *
+		 * A note on the use of strncmp() below.
+		 * The check is for the substring at the beginning of cp.
+		 * The kzalloc of size + 1 ensures a terminated string.
+		 */
+		local = kzalloc(size + 1, GFP_KERNEL);
+		memcpy(local, value, size);
+		cp = local;
+		list_for_each_entry(hp, &security_hook_heads.setprocattr,
+					list) {
+			if (lsm != NULL && strcmp(lsm, hp->lsm))
+				continue;
+			if (cp[0] == ',') {
+				if (cp == local)
+					goto free_out;
+				cp++;
+			}
+			slen = strlen(hp->lsm);
+			if (strncmp(cp, hp->lsm, slen))
+				goto free_out;
+			cp += slen;
+			if (cp[0] != '=' || cp[1] != '\'' || cp[2] == '\'')
+				goto free_out;
+			for (cp += 2; cp[0] != '\''; cp++)
+				if (cp[0] == '\0')
+					goto free_out;
+			cp++;
+		}
 
+		cp = local;
+		list_for_each_entry(hp, &security_hook_heads.setprocattr,
+					list) {
+			if (lsm != NULL && strcmp(lsm, hp->lsm))
+				continue;
+			if (cp[0] == ',')
+				cp++;
+			cp += strlen(hp->lsm) + 2;
+			for (slen = 0; cp[slen] != '\''; slen++)
+				;
+			cp[slen] = '\0';
+
+			rc = hp->hook.setprocattr(p, "context", cp, slen);
+			if (rc < 0)
+				failed = rc;
+			cp += slen + 1;
+		}
+		if (failed != 0)
+			rc = failed;
+		else
+			rc = size;
+free_out:
+		kfree(local);
+		return rc;
+	}
 	list_for_each_entry(hp, &security_hook_heads.setprocattr, list) {
 		if (lsm != NULL && strcmp(lsm, hp->lsm))
 			continue;
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index ed3a757..6397721 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -5711,6 +5711,8 @@ static int selinux_getprocattr(struct task_struct *p,
 
 	if (!strcmp(name, "current"))
 		sid = __tsec->sid;
+	else if (!strcmp(name, "context"))
+		sid = __tsec->sid;
 	else if (!strcmp(name, "prev"))
 		sid = __tsec->osid;
 	else if (!strcmp(name, "exec"))
@@ -5728,7 +5730,19 @@ static int selinux_getprocattr(struct task_struct *p,
 	if (!sid)
 		return 0;
 
-	error = security_sid_to_context(sid, value, &len);
+	if (strcmp(name, "context")) {
+		error = security_sid_to_context(sid, value, &len);
+	} else {
+		char *vp;
+
+		error = security_sid_to_context(sid, &vp, &len);
+		if (!error) {
+			*value = kasprintf(GFP_KERNEL, "selinux='%s'", vp);
+			if (*value == NULL)
+				error = -ENOMEM;
+		}
+	}
+
 	if (error)
 		return error;
 	return len;
@@ -5768,6 +5782,8 @@ static int selinux_setprocattr(struct task_struct *p,
 		error = current_has_perm(p, PROCESS__SETSOCKCREATE);
 	else if (!strcmp(name, "current"))
 		error = current_has_perm(p, PROCESS__SETCURRENT);
+	else if (!strcmp(name, "context"))
+		error = current_has_perm(p, PROCESS__SETCURRENT);
 	else
 		error = -EINVAL;
 	if (error)
@@ -5827,7 +5843,7 @@ static int selinux_setprocattr(struct task_struct *p,
 		tsec->keycreate_sid = sid;
 	} else if (!strcmp(name, "sockcreate")) {
 		tsec->sockcreate_sid = sid;
-	} else if (!strcmp(name, "current")) {
+	} else if (!strcmp(name, "current") || !strcmp(name, "context")) {
 		error = -EINVAL;
 		if (sid == 0)
 			goto abort_change;
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 3577009..92e66f8 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -3574,18 +3574,20 @@ 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 if (strcmp(name, "context") == 0) {
+		cp = kasprintf(GFP_KERNEL, "smack='%s'", skp->smk_known);
+		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);
 }
 
 /**
@@ -3622,7 +3624,7 @@ static int smack_setprocattr(struct task_struct *p, char *name,
 	if (value == NULL || size == 0 || size >= SMK_LONGLABEL)
 		return -EINVAL;
 
-	if (strcmp(name, "current") != 0)
+	if (strcmp(name, "current") != 0 && strcmp(name, "context") != 0)
 		return -EINVAL;
 
 	skp = smk_import_entry(value, size);

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* Re: [PATCH v5 0/3] LSM: security module information improvements - Acked
  2016-07-05 15:52                   ` [PATCH v5 0/3] LSM: security module information improvements - Acked Casey Schaufler
@ 2016-07-08 10:05                     ` James Morris
  2016-07-08 15:31                       ` Casey Schaufler
  0 siblings, 1 reply; 32+ messages in thread
From: James Morris @ 2016-07-08 10:05 UTC (permalink / raw)
  To: Casey Schaufler
  Cc: LSM, John Johansen, Paul Moore, Kees Cook, Stephen Smalley,
	Tetsuo Handa, LKLM

On Tue, 5 Jul 2016, Casey Schaufler wrote:

> Subject: [PATCH v5 0/3] LSM: security module information improvements
> 
> I have added Acks. James, would you like me to add this to the
> Smack tree?
> 

I'd like this code to spend longer in -next, so I'll accept it after the 
4.8 merge window closes.


-- 
James Morris
<jmorris@namei.org>

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v5 0/3] LSM: security module information improvements - Acked
  2016-07-08 10:05                     ` James Morris
@ 2016-07-08 15:31                       ` Casey Schaufler
  0 siblings, 0 replies; 32+ messages in thread
From: Casey Schaufler @ 2016-07-08 15:31 UTC (permalink / raw)
  To: James Morris
  Cc: LSM, John Johansen, Paul Moore, Kees Cook, Stephen Smalley,
	Tetsuo Handa, LKLM

On 7/8/2016 3:05 AM, James Morris wrote:
> On Tue, 5 Jul 2016, Casey Schaufler wrote:
>
>> Subject: [PATCH v5 0/3] LSM: security module information improvements
>>
>> I have added Acks. James, would you like me to add this to the
>> Smack tree?
>>
> I'd like this code to spend longer in -next, so I'll accept it after the 
> 4.8 merge window closes.

Good idea. I will plan accordingly. Thank you.

^ permalink raw reply	[flat|nested] 32+ messages in thread

end of thread, other threads:[~2016-07-08 15:31 UTC | newest]

Thread overview: 32+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-06-23 21:07 [PATCH v4 0/3] LSM: module hierarchy in /proc/.../attr Casey Schaufler
2016-06-23 21:10 ` [PATCH v4 1/3] LSM: Add /sys/kernel/security/lsm Casey Schaufler
2016-06-24 18:56   ` Paul Moore
2016-06-23 21:11 ` [PATCH v4 2/3] LSM: module hierarchy in /proc/.../attr Casey Schaufler
2016-06-24 19:11   ` Paul Moore
2016-06-24 20:05     ` Casey Schaufler
2016-06-24 20:08       ` Kees Cook
2016-06-24 20:29         ` Paul Moore
2016-06-24 23:26           ` [PATCH v5 0/3] LSM: security module information improvements Casey Schaufler
2016-06-24 23:27             ` [PATCH v5 1/3] LSM: Add /sys/kernel/security/lsm Casey Schaufler
2016-06-29 17:01               ` Paul Moore
2016-07-02 17:21                 ` John Johansen
2016-06-24 23:29             ` [PATCH v5 2/3] LSM: module hierarchy in /proc/.../attr Casey Schaufler
2016-06-29 17:03               ` Paul Moore
2016-07-02 17:24                 ` John Johansen
2016-06-24 23:29             ` [PATCH v4 3/3] LSM: Add context interface for proc attrs Casey Schaufler
2016-06-24 23:38               ` [PATCH v5 " Casey Schaufler
2016-06-29 17:04               ` [PATCH v4 " Paul Moore
2016-07-02 17:25                 ` John Johansen
2016-07-05 15:52                   ` [PATCH v5 0/3] LSM: security module information improvements - Acked Casey Schaufler
2016-07-08 10:05                     ` James Morris
2016-07-08 15:31                       ` Casey Schaufler
2016-07-05 15:52                   ` [PATCH v5 1/3] LSM: Add /sys/kernel/security/lsm " Casey Schaufler
2016-07-05 15:52                   ` [PATCH v5 2/3] LSM: module hierarchy in /proc/.../attr " Casey Schaufler
2016-07-05 15:52                   ` [PATCH v5 3/3] LSM: Add context interface for proc attrs " Casey Schaufler
2016-06-23 21:11 ` [PATCH v4 3/3] LSM: Add context interface for proc attrs Casey Schaufler
2016-06-23 21:49   ` Kees Cook
2016-06-23 22:10     ` Casey Schaufler
2016-06-24 16:38     ` [PATCH v4 4/3] LSM: Improve " Casey Schaufler
2016-06-24 17:48       ` Kees Cook
2016-06-24 19:15   ` [PATCH v4 3/3] LSM: Add " Paul Moore
2016-06-24 19:56     ` Casey Schaufler

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).