LWN.net Logo

stacking get/setprocattr support patches

From:  serue@us.ibm.com
To:  linux-security-module@wirex.com
Subject:  stacking get/setprocattr support patches
Date:  Tue, 31 May 2005 12:56:13 -0500
Cc:  Stephen Smalley <sds@epoch.ncsc.mil>, zohar@us.ibm.com
Archive-link:  Article, Thread

Attached are two patches to make procattr shareable by >1
LSM.  The first patch applies on top of the current set of
stacker patches from sf.net/projects/lsm-stacker, and
modifies stacker.c.  The second patch is to the sf.net
libselinux sources.

Since I took Mimi's idea of switching from
	selinux: user:role:type
to
	user:role:type (selinux)

for both get and setprocattr, no patch is needed for
ps so long as it's ok to output the data from only the
first LSM to output data.

These patches have been tested

	with stacker and an unpatched libselinux
	with stacker and a patched libselinux
	without stacker and a patched libselinux

and all worked as expected.

Any comments are much appreciated.

thanks,
-serge

Index: linux-2.6.12-rc2-stack/security/stacker.c
===================================================================
--- linux-2.6.12-rc2-stack.orig/security/stacker.c	2005-05-25 16:01:52.000000000 -0500
+++ linux-2.6.12-rc2-stack/security/stacker.c	2005-05-31 15:22:39.000000000 -0500
@@ -956,24 +956,111 @@ static void stacker_d_instantiate (struc
 	CALL_ALL(d_instantiate,d_instantiate(dentry,inode));
 }
 
+/*
+ * Query all LSMs.
+ * If none returns a non-error, return -EINVAL.  SO if all were to return
+ * -EPERM, we lose that info...
+ */
 static int
 stacker_getprocattr(struct task_struct *p, char *name, void *value, size_t size)
 {
-	if (!selinux_module)
-		return -EINVAL;
-	if (!selinux_module->module_operations.getprocattr)
+	struct module_entry *m;
+	int len = 0, o_len, ret=0;
+	int found_nonerr = 0;
+
+
+	if (list_empty(&stacked_modules))
 		return -EINVAL;
-	return selinux_module->module_operations.getprocattr(p, name, value, size);
+
+	rcu_read_lock();
+	stack_for_each_entry(m, &stacked_modules, lsm_list) {
+		if (!m->module_operations.getprocattr)
+			continue;
+		o_len = len;
+		rcu_read_unlock();
+		ret = m->module_operations.getprocattr(p, name,
+					value+len, size-len);
+		rcu_read_lock();
+		if (ret < 0)
+			continue;
+		found_nonerr = 1;
+		if (ret == 0)
+			continue;
+		len += ret;
+		if (len+m->namelen+4 < size) {
+			char *v = value;
+			if (v[len-1]=='\n')
+				len--;
+			len += sprintf(value+len, " (%s)\n", m->module_name);
+		}
+	}
+	rcu_read_unlock();
+
+	return found_nonerr ? len : -EINVAL;
+}
+
+static struct module_entry *
+find_active_lsm(const char *name, int len)
+{
+	struct module_entry *m, *ret = NULL;
+
+	rcu_read_lock();
+	stack_for_each_entry(m, &stacked_modules, lsm_list) {
+		if (m->namelen == len && !strncmp(m->module_name, name, len)) {
+			ret = m;
+			break;
+		}
+	}
+
+	rcu_read_unlock();
+	return ret;
 }
 
-static int stacker_setprocattr(struct task_struct *p, char *name, void *value, size_t size)
+/*
+ * We assume input will be either
+ * "data" - in which case it goes to selinux, or
+ * "data (mod_name)" in which case the data goes to module mod_name.
+ */
+static int
+stacker_setprocattr(struct task_struct *p, char *name, void *value, size_t size)
 {
+	struct module_entry *callm = selinux_module;
+	char *realv = (char *)value;
+	size_t dsize = size;
+	int loc = 0, end_data = size;
 
-	if (!selinux_module)
+	if (list_empty(&stacked_modules))
 		return -EINVAL;
-	if (!selinux_module->module_operations.setprocattr)
+
+	if (dsize && realv[dsize-1] == '\n')
+		dsize--;
+
+	if (!dsize || realv[dsize-1]!=')')
+		goto call;
+
+	dsize--;
+	loc = dsize-1;
+	while (loc && realv[loc]!='(')
+		loc--;
+	if (!loc)
+		goto call;
+
+	callm = find_active_lsm(realv+loc+1, dsize-loc-1);
+	if (!callm)
+		goto call;
+
+
+	loc--;
+	while (loc && realv[loc]==' ')
+		loc--;
+
+	end_data = loc+1;
+call:
+	if (!callm || !callm->module_operations.setprocattr)
 		return -EINVAL;
-	return selinux_module->module_operations.setprocattr(p, name, value, size);
+
+	return callm->module_operations.setprocattr(p, name, value, end_data) +
+			(size-end_data);
 }
 
 /*
@@ -1031,23 +1118,6 @@ out:
 	return ret;
 }
 
-static struct module_entry *
-find_active_lsm(const char *name, int len)
-{
-	struct module_entry *m, *ret = NULL;
-
-	rcu_read_lock();
-	stack_for_each_entry(m, &stacked_modules, lsm_list) {
-		if (m->namelen == len && !strncmp(m->module_name, name, len)) {
-			ret = m;
-			break;
-		}
-	}
-
-	rcu_read_unlock();
-	return ret;
-}
-
 /*
  * Currently this version of stacker does not allow for module
  * unregistering.

Index: src/getcon.c
===================================================================
--- src.orig/getcon.c	2005-05-31 15:18:45.000000000 -0500
+++ src/getcon.c	2005-05-31 15:23:56.000000000 -0500
@@ -7,9 +7,11 @@
 #include <asm/page.h>
 #include "policy.h"
 
+extern char *find_context(char *ctx, int len);
+
 int getcon(security_context_t *context)
 {
-	char *buf;
+	char *buf, *c;
 	size_t size;
 	int fd;
 	ssize_t ret;
@@ -30,11 +32,13 @@ int getcon(security_context_t *context)
 	if (ret < 0)
 		goto out2;
 
-	*context = strdup(buf);
+	c = find_context(buf, ret);
+	*context = strdup(c);
 	if (!(*context)) {
 		ret = -1;
 		goto out2;
 	}
+
 	ret = 0;
 out2:			
 	free(buf);
Index: src/getexeccon.c
===================================================================
--- src.orig/getexeccon.c	2005-05-31 15:18:45.000000000 -0500
+++ src/getexeccon.c	2005-05-31 15:23:56.000000000 -0500
@@ -7,9 +7,11 @@
 #include <asm/page.h>
 #include "policy.h"
 
+extern char *find_context(char *ctx, ssize_t len);
+
 int getexeccon(security_context_t *context)
 {
-	char *buf;
+	char *buf, *c;
 	size_t size;
 	int fd;
 	ssize_t ret;
@@ -35,11 +37,13 @@ int getexeccon(security_context_t *conte
 		goto out2;
 	}
 
-	*context = strdup(buf);
+	c = find_context(buf, ret);
+	*context = strdup(c);
 	if (!(*context)) {
 		ret = -1;
 		goto out2;
 	}
+
 	ret = 0;
 out2:			
 	free(buf);
Index: src/getfscreatecon.c
===================================================================
--- src.orig/getfscreatecon.c	2005-05-31 15:18:45.000000000 -0500
+++ src/getfscreatecon.c	2005-05-31 15:23:56.000000000 -0500
@@ -7,9 +7,11 @@
 #include <asm/page.h>
 #include "policy.h"
 
+extern char *find_context(char *ctx, ssize_t len);
+
 int getfscreatecon(security_context_t *context)
 {
-	char *buf;
+	char *buf, *c;
 	size_t size;
 	int fd;
 	ssize_t ret;
@@ -35,11 +37,13 @@ int getfscreatecon(security_context_t *c
 		goto out2;
 	}
 
-	*context = strdup(buf);
+	c = find_context(buf, ret);
+	*context = strdup(c);
 	if (!(*context)) {
 		ret = -1;
 		goto out2;
 	}
+
 	ret = 0;
 out2:			
 	free(buf);
Index: src/getpidcon.c
===================================================================
--- src.orig/getpidcon.c	2005-05-31 15:18:45.000000000 -0500
+++ src/getpidcon.c	2005-05-31 15:23:56.000000000 -0500
@@ -8,10 +8,12 @@
 #include <asm/page.h>
 #include "policy.h"
 
+extern char *find_context(char *ctx, ssize_t len);
+
 int getpidcon(pid_t pid, security_context_t *context)
 {
 	char path[40];
-	char *buf;
+	char *buf, *c;
 	size_t size;
 	int fd;
 	ssize_t ret;
@@ -34,11 +36,13 @@ int getpidcon(pid_t pid, security_contex
 	if (ret < 0)
 		goto out2;
 
-	*context = strdup(buf);
+	c = find_context(buf, ret);
+	*context = strdup(c);
 	if (!(*context)) {
 		ret = -1;
 		goto out2;
 	}
+
 	ret = 0;
 out2:			
 	free(buf);
Index: src/parse_attr.c
===================================================================
--- src.orig/parse_attr.c	2005-05-31 10:27:56.348471792 -0500
+++ src/parse_attr.c	2005-05-31 15:40:46.000000000 -0500
@@ -0,0 +1,59 @@
+#include "selinux_internal.h"
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+/*
+ * We get either one line which is the selinux output, in which
+ * case we return the line itself, or a set of \n-terminated lines
+ * each ending in ' (mod_name)', in which case we return the line
+ * ending in ' (selinux)'.
+ */
+char *find_context(char *ctx, ssize_t len)
+{
+	char *ec, *sc, *sm, *em;
+
+	sc = ctx;
+
+	do {
+		ec = strstr(sc, " (");
+		if (!ec || ec-ctx > len)
+			return sc;
+		sm = ec+2;
+		em = index(sm, ')');
+		if (!em || em-ctx > len)
+			return sc;
+		if (em-sm==7 && strncmp(sm, "selinux", 7)==0) {
+			*ec = '\0';
+			return sc;
+		}
+		sc = em+2;
+	} while (sc-ctx < len);
+
+	if (sc-ctx >= len)
+		/* No line had ' (selinux)' nor was any
+		 * without a trailing ' (modname)'.  No
+		 * selinux output at all, then? */
+		return ctx;
+
+	return sc;
+}
+
+#define PROCATTR_SELINUX " (selinux)"
+#define STACKSYS "/sys/stacker"
+
+/*
+ * Check existance of /sys/stacker to determine whether the kernel is
+ * using the stacker module.  If so, we must output a trailing
+ * ' (selinux)' to our procattr data.
+ */
+char *selinux_identifier(void)
+{
+	struct stat stat_obj;
+
+	if (stat(STACKSYS, &stat_obj)==0)
+		return PROCATTR_SELINUX;
+	else
+		return "";
+}
Index: src/setcon.c
===================================================================
--- src.orig/setcon.c	2005-05-31 15:18:45.000000000 -0500
+++ src/setcon.c	2005-05-31 15:23:56.000000000 -0500
@@ -6,6 +6,9 @@
 #include <fcntl.h>
 #include <string.h>
 #include <selinux/selinux.h>
+#include <stdio.h>
+
+extern char *selinux_identifier(void);
 
 int setcon(security_context_t context)
 {
@@ -15,8 +18,8 @@ int setcon(security_context_t context)
 	fd = open("/proc/self/attr/current", O_RDWR);
 	if (fd < 0)
 		return -1;
-	if (context) 
-		ret = write(fd, context, strlen(context)+1);
+	if (context)
+		ret = dprintf(fd, "%s%s", context, selinux_identifier());
 	else
 		ret = -1; /* we can not clear this one */
 	close(fd);
Index: src/setexeccon.c
===================================================================
--- src.orig/setexeccon.c	2005-05-31 15:18:45.000000000 -0500
+++ src/setexeccon.c	2005-05-31 15:23:56.000000000 -0500
@@ -2,6 +2,9 @@
 #include <fcntl.h>
 #include <string.h>
 #include "selinux_internal.h"
+#include <stdio.h>
+
+extern char *selinux_identifier(void);
 
 int setexeccon(security_context_t context)
 {
@@ -12,9 +15,9 @@ int setexeccon(security_context_t contex
 	if (fd < 0)
 		return -1;
 	if (context) 
-		ret = write(fd, context, strlen(context)+1);
+		ret = dprintf(fd, "%s%s", context, selinux_identifier());
 	else
-		ret = write(fd, NULL, 0); /* clear */
+		ret = dprintf(fd, "%s", selinux_identifier());  /* clear */
 	close(fd);
 	if (ret < 0)
 		return -1;
Index: src/setfscreatecon.c
===================================================================
--- src.orig/setfscreatecon.c	2005-05-31 15:18:45.000000000 -0500
+++ src/setfscreatecon.c	2005-05-31 15:23:56.000000000 -0500
@@ -2,6 +2,9 @@
 #include <string.h>
 #include <fcntl.h>
 #include <selinux/selinux.h>
+#include <stdio.h>
+
+extern char *selinux_identifier(void);
 
 int setfscreatecon(char *context)
 {
@@ -12,9 +15,9 @@ int setfscreatecon(char *context)
 	if (fd < 0)
 		return -1;
 	if (context) 
-		ret = write(fd, context, strlen(context)+1);
+		ret = dprintf(fd, "%s%s", context, selinux_identifier());
 	else
-		ret = write(fd, NULL, 0); /* clear */
+		ret = dprintf(fd, "%s", selinux_identifier());  /* clear */
 	close(fd);
 	if (ret < 0)
 		return -1;


Copyright © 2005, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds