Re: [PATCH] libselinux: raw string_to_class/string_to_av_perm variants

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch adds support for remapping classes and permissions on policy reload. This is accomplished by separating the code that computes the "real" kernel class and permission values into a helper function, mapping_compute(). This function is called both from selinux_set_mapping() when the user specifies a new mapping, and from the netlink code when a policyload notification is received. The function now builds up a temporary mapping and swaps it in rather than working on the active mapping in place.

Issue: There is a race condition in which old class and permission values may arrive from userspace after a kernel policyload has taken place. Fixing this would require a string interface to the kernel, or some kind of transaction support.

Signed-off-by: Eamon Walsh <ewalsh@xxxxxxxxxxxxx>
---

 avc_internal.c |   17 +++++++
 mapping.c      |  130 +++++++++++++++++++++++++++++++++++++++++++--------------
 mapping.h      |   10 ++++
 3 files changed, 126 insertions(+), 31 deletions(-)


diff --git a/libselinux/src/avc_internal.c b/libselinux/src/avc_internal.c
index 8372f52..2c54a92 100644
--- a/libselinux/src/avc_internal.c
+++ b/libselinux/src/avc_internal.c
@@ -22,6 +22,7 @@
 #include "callbacks.h"
 #include "selinux_netlink.h"
 #include "avc_internal.h"
+#include "mapping.h"

 #ifndef NETLINK_SELINUX
 #define NETLINK_SELINUX 7
@@ -180,6 +181,8 @@ static int avc_netlink_process(char *buf)
 		avc_log(SELINUX_INFO,
 			"%s:  received policyload notice (seqno=%d)\n",
 			avc_prefix, msg->seqno);
+
+		/* Flush the AVC */
 		rc = avc_ss_reset(msg->seqno);
 		if (rc<  0) {
 			avc_log(SELINUX_ERROR,
@@ -187,6 +190,20 @@ static int avc_netlink_process(char *buf)
 				avc_prefix, rc, errno);
 			return rc;
 		}
+
+		/* Flush any cached class/permission values */
+		flush_class_cache();
+
+		/* Recompute any mapped classes/permissions */
+		rc = mapping_recompute();
+		if (rc<  0) {
+			avc_log(SELINUX_ERROR,
+				"%s:  mapping reset returned %d (errno %d)\n",
+				avc_prefix, rc, errno);
+			return rc;
+		}
+
+		/* Call callback for users */
 		rc = selinux_netlink_policyload(msg->seqno);
 		if (rc<  0)
 			return rc;
diff --git a/libselinux/src/mapping.c b/libselinux/src/mapping.c
index f9858ce..eb99eb3 100644
--- a/libselinux/src/mapping.c
+++ b/libselinux/src/mapping.c
@@ -6,6 +6,7 @@
 #include<stdio.h>
 #include<stdlib.h>
 #include<stdarg.h>
+#include<string.h>
 #include<assert.h>
 #include<selinux/selinux.h>
 #include<selinux/avc.h>
@@ -17,12 +18,85 @@

 struct selinux_mapping {
 	security_class_t value; /* real, kernel value */
+	char *name;
 	unsigned num_perms;
 	access_vector_t perms[sizeof(access_vector_t) * 8];
+	char *names[sizeof(access_vector_t) * 8];
 };

-static struct selinux_mapping *current_mapping = NULL;
-static security_class_t current_mapping_size = 0;
+static struct selinux_mapping *current_mapping;
+static int current_mapping_size;
+
+/*
+ * Mapping recompute functions
+ */
+
+static void
+mapping_free(struct selinux_mapping *map, int size, int free_strings)
+{
+	int i;
+	unsigned j;
+
+	if (free_strings)
+		for (i = 0; i<  size; i++) {
+			free(map[i].name);
+			for (j = 0; j<  map[i].num_perms; j++)
+				free(map[i].names[j]);
+		}
+
+	free(map);
+}
+
+static int
+mapping_compute(struct selinux_mapping *map, int size, int free_strings)
+{
+	struct selinux_mapping *old_mapping;
+	int old_mapping_size, i;
+	unsigned k;
+
+	/* Find the real, kernel values for the names */
+	for (i = 1; i<  size; i++) {
+		map[i].value = string_to_security_class_raw(map[i].name);
+		if (!map[i].value)
+			return -1;
+
+		for (k = 0; k<  map[i].num_perms; k++) {
+			if (!map[i].names[k])
+				continue;
+			map[i].perms[k] = string_to_av_perm_raw(
+				map[i].value, map[i].names[k]);
+			if (!map[i].perms[k])
+				return -1;
+		}
+	}
+
+	/* Switch in the new mapping */
+	old_mapping_size = current_mapping_size;
+	old_mapping = current_mapping;
+	current_mapping_size = size;
+	current_mapping = map;
+	mapping_free(old_mapping, old_mapping_size, free_strings);
+	return 0;
+}
+
+int
+mapping_recompute(void)
+{
+	struct selinux_mapping *copy;
+	size_t size;
+
+	size = current_mapping_size * sizeof(struct selinux_mapping);
+	if (size == 0)
+		return 0;
+
+	/* Not a deep copy - we keep the old class and perm names */
+	copy = malloc(size);
+	if (!copy)
+		return -1;
+	memcpy(copy, current_mapping, size);
+
+	return mapping_compute(copy, current_mapping_size, 0);
+}

 /*
  * Mapping setting function
@@ -31,40 +105,34 @@ static security_class_t current_mapping_size = 0;
 int
 selinux_set_mapping(struct security_class_mapping *map)
 {
-	size_t size = sizeof(struct selinux_mapping);
-	security_class_t i, j;
+	struct selinux_mapping *new_mapping;
+	int new_size, i, j;
 	unsigned k;

-	free(current_mapping);
-	current_mapping = NULL;
-	current_mapping_size = 0;
-
-	if (avc_reset()<  0)
-		goto err;
-
 	/* Find number of classes in the input mapping */
 	if (!map) {
 		errno = EINVAL;
-		goto err;
+		return -1;
 	}
 	i = 0;
 	while (map[i].name)
 		i++;

 	/* Allocate space for the class records, plus one for class zero */
-	current_mapping = (struct selinux_mapping *)calloc(++i, size);
-	if (!current_mapping)
-		goto err;
+	new_size = i + 1;
+	new_mapping = calloc(new_size, sizeof(struct selinux_mapping));
+	if (!new_mapping)
+		return -1;

-	/* Store the raw class and permission values */
+	/* Store the class and permission names */
 	j = 0;
 	while (map[j].name) {
 		struct security_class_mapping *p_in = map + (j++);
-		struct selinux_mapping *p_out = current_mapping + j;
+		struct selinux_mapping *p_out = new_mapping + j;

-		p_out->value = string_to_security_class(p_in->name);
-		if (!p_out->value)
-			goto err2;
+		p_out->name = strdup(p_in->name);
+		if (!p_out->name)
+			goto err;

 		k = 0;
 		while (p_in->perms&&  p_in->perms[k]) {
@@ -73,23 +141,23 @@ selinux_set_mapping(struct security_class_mapping *map)
 				k++;
 				continue;
 			}
-			p_out->perms[k] = string_to_av_perm(p_out->value,
-							    p_in->perms[k]);
-			if (!p_out->perms[k])
-				goto err2;
+			p_out->names[k] = strdup(p_in->perms[k]);
+			if (!p_out->names[k])
+				goto err;
 			k++;
 		}
 		p_out->num_perms = k;
 	}

-	/* Set the mapping size here so the above lookups are "raw" */
-	current_mapping_size = i;
-	return 0;
-err2:
-	free(current_mapping);
-	current_mapping = NULL;
-	current_mapping_size = 0;
+	/* Flush the AVC */
+	if (avc_reset()<  0)
+		goto err;
+
+	/* Set up the mapping */
+	if (mapping_compute(new_mapping, new_size, 1) == 0)
+		return 0;
 err:
+	mapping_free(new_mapping, new_size, 1);
 	return -1;
 }

diff --git a/libselinux/src/mapping.h b/libselinux/src/mapping.h
index b9e9c44..34d94eb 100644
--- a/libselinux/src/mapping.h
+++ b/libselinux/src/mapping.h
@@ -42,6 +42,16 @@ map_perm(security_class_t tclass, access_vector_t kperm);
 extern void
 map_decision(security_class_t tclass, struct av_decision *avd);

+/*
+ * Recompute mapping on policy load
+ */
+
+extern void
+flush_class_cache(void) hidden;
+
+extern int
+mapping_recompute(void) hidden;
+
 /*mapping is not used for embedded build*/
 #ifdef DISABLE_AVC
 #define unmap_perm(x,y) y



--
Eamon Walsh<ewalsh@xxxxxxxxxxxxx>
National Security Agency


--
This message was distributed to subscribers of the selinux mailing list.
If you no longer wish to subscribe, send mail to majordomo@xxxxxxxxxxxxx with
the words "unsubscribe selinux" without quotes as the message.

[Index of Archives]     [Selinux Refpolicy]     [Linux SGX]     [Fedora Users]     [Fedora Desktop]     [Yosemite Photos]     [Yosemite Camping]     [Yosemite Campsites]     [KDE Users]     [Gnome Users]

  Powered by Linux