Blob Blame History Raw
diff --git a/libselinux/include/selinux/selinux.h b/libselinux/include/selinux/selinux.h
index 6b9089d..85b0cfc 100644
--- a/libselinux/include/selinux/selinux.h
+++ b/libselinux/include/selinux/selinux.h
@@ -496,7 +496,9 @@ extern const char *selinux_policy_root(void);
 
 /* These functions return the paths to specific files under the 
    policy root directory. */
+extern const char *selinux_current_policy_path(void);
 extern const char *selinux_binary_policy_path(void);
+extern char *selinux_binary_policy_path_min_max(int min, int *max);
 extern const char *selinux_failsafe_context_path(void);
 extern const char *selinux_removable_context_path(void);
 extern const char *selinux_default_context_path(void);
diff --git a/libselinux/man/man3/selinux_binary_policy_path.3 b/libselinux/man/man3/selinux_binary_policy_path.3
index 8ead1a4..c68ace5 100644
--- a/libselinux/man/man3/selinux_binary_policy_path.3
+++ b/libselinux/man/man3/selinux_binary_policy_path.3
@@ -17,6 +17,8 @@ extern const char *selinux_policy_root(void);
 
 extern const char *selinux_binary_policy_path(void);
 
+extern const char *selinux_current_policy_path(void);
+
 extern const char *selinux_failsafe_context_path(void);
 
 extern const char *selinux_removable_context_path(void);
@@ -52,7 +54,9 @@ selinux_path() - top-level SELinux configuration directory
 .sp
 selinux_policy_root() - top-level policy directory 
 .sp
-selinux_binary_policy_path() - binary policy file loaded into kernel
+selinux_current_policy_path() - binary policy file loaded into kernel
+.sp
+selinux_binary_policy_path() - binary policy path on disk
 .sp
 selinux_default_type_path - context file mapping roles to default types.
 .sp
diff --git a/libselinux/man/man8/selinux.8 b/libselinux/man/man8/selinux.8
index 9f16f77..4835f2f 100644
--- a/libselinux/man/man8/selinux.8
+++ b/libselinux/man/man8/selinux.8
@@ -69,14 +69,27 @@ Many domains that are protected by SELinux also include SELinux man pages explai
 All files, directories, devices ... have a security context/label associated with them.  These context are stored in the extended attributes of the file system.
 Problems with SELinux often arise from the file system being mislabeled. This can be caused by booting the machine with a non SELinux kernel.  If you see an error message containing file_t, that is usually a good indicator that you have a serious problem with file system labeling.  
 
-The best way to relabel the file system is to create the flag file /.autorelabel and reboot.  system-config-securitylevel, also has this capability.  The restorcon/fixfiles commands are also available for relabeling files. 
+The best way to relabel the file system is to create the flag file /.autorelabel and reboot.  system-config-selinux, also has this capability.  The restorcon/fixfiles commands are also available for relabeling files. 
   
 .SH AUTHOR	
 This manual page was written by Dan Walsh <dwalsh@redhat.com>.
 
 .SH "SEE ALSO"
-booleans(8), setsebool(8), selinuxenabled(8), togglesebool(8), restorecon(8), setfiles(8), ftpd_selinux(8), named_selinux(8), rsync_selinux(8), httpd_selinux(8), nfs_selinux(8), samba_selinux(8), kerberos_selinux(8), nis_selinux(8), ypbind_selinux(8)
+booleans(8), setsebool(8), selinuxenabled(8), restorecon(8), setfiles(8), semanage(8), sepolicy(8)
+.br
 
+Every confined service on the system has a man page in the following format:
+.br
+
+.B <servicename>_selinux(8)
+
+For example, httpd has the 
+.B httpd_selinux(8) 
+man page.
+
+.B man -k selinux 
+
+Will list all SELinux man pages.
 
 .SH FILES
 /etc/selinux/config
diff --git a/libselinux/src/audit2why.c b/libselinux/src/audit2why.c
index 02483a3..b309671 100644
--- a/libselinux/src/audit2why.c
+++ b/libselinux/src/audit2why.c
@@ -164,6 +164,9 @@ static PyObject *finish(PyObject *self __attribute__((unused)), PyObject *args)
   
 	if (PyArg_ParseTuple(args,(char *)":finish")) {
 		int i = 0;
+		if (! avc)
+			Py_RETURN_NONE;
+
 		for (i = 0; i < boolcnt; i++) {
 			free(boollist[i]->name);
 			free(boollist[i]);
@@ -177,7 +180,7 @@ static PyObject *finish(PyObject *self __attribute__((unused)), PyObject *args)
 		avc = NULL;
 		boollist = NULL;
 		boolcnt = 0;
-	  
+
 		/* Boilerplate to return "None" */
 		Py_RETURN_NONE;
 	}
@@ -206,27 +209,12 @@ static int __policy_init(const char *init_path)
 			return 1;
 		}
 	} else {
-		vers = sepol_policy_kern_vers_max();
-		if (vers < 0) {
-			snprintf(errormsg, sizeof(errormsg), 
-				 "Could not get policy version:  %s\n",
-				 strerror(errno));
-			PyErr_SetString( PyExc_ValueError, errormsg);
-			return 1;
-		}
-		snprintf(path, PATH_MAX, "%s.%d",
-			 selinux_binary_policy_path(), vers);
-		fp = fopen(path, "r");
-		while (!fp && errno == ENOENT && --vers) {
-			snprintf(path, PATH_MAX, "%s.%d",
-				 selinux_binary_policy_path(), vers);
-			fp = fopen(path, "r");
-		}
+		fp = fopen(selinux_current_policy_path(), "r");
 		if (!fp) {
 			snprintf(errormsg, sizeof(errormsg), 
-				 "unable to open %s.%d:  %s\n",
-				 selinux_binary_policy_path(),
-				 security_policyvers(), strerror(errno));
+				 "unable to open %s:  %s\n",
+				 selinux_current_policy_path(),
+				 strerror(errno));
 			PyErr_SetString( PyExc_ValueError, errormsg);
 			return 1;
 		}
@@ -295,9 +283,16 @@ static int __policy_init(const char *init_path)
 static PyObject *init(PyObject *self __attribute__((unused)), PyObject *args) {
   int result;
   char *init_path=NULL;
+  if (avc) {
+	  PyErr_SetString( PyExc_ValueError, "init called multiple times");
+	  return NULL;
+  }
+
   if (!PyArg_ParseTuple(args,(char *)"|s:policy_init",&init_path))
     return NULL;
+
   result = __policy_init(init_path);
+
   return Py_BuildValue("i", result);
 }
 
diff --git a/libselinux/src/avc.c b/libselinux/src/avc.c
index 802a07f..6ff83a7 100644
--- a/libselinux/src/avc.c
+++ b/libselinux/src/avc.c
@@ -827,6 +827,7 @@ int avc_has_perm(security_id_t ssid, security_id_t tsid,
 	errsave = errno;
 	avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata);
 	errno = errsave;
+	if (!avc_enforcing) return 0;
 	return rc;
 }
 
diff --git a/libselinux/src/file_path_suffixes.h b/libselinux/src/file_path_suffixes.h
index 825f295..d11c8dc 100644
--- a/libselinux/src/file_path_suffixes.h
+++ b/libselinux/src/file_path_suffixes.h
@@ -26,4 +26,4 @@ S_(BINPOLICY, "/policy/policy")
     S_(FILE_CONTEXT_SUBS, "/contexts/files/file_contexts.subs")
     S_(FILE_CONTEXT_SUBS_DIST, "/contexts/files/file_contexts.subs_dist")
     S_(SEPGSQL_CONTEXTS, "/contexts/sepgsql_contexts")
-    S_(BOOLEAN_SUBS, "/booleans.subs")
+    S_(BOOLEAN_SUBS, "/booleans.subs_dist")
diff --git a/libselinux/src/label_file.c b/libselinux/src/label_file.c
index 02b3cd2..fad8bbd 100644
--- a/libselinux/src/label_file.c
+++ b/libselinux/src/label_file.c
@@ -8,6 +8,7 @@
  * developed by Secure Computing Corporation.
  */
 
+#include <assert.h>
 #include <fcntl.h>
 #include <stdarg.h>
 #include <string.h>
@@ -16,7 +17,12 @@
 #include <ctype.h>
 #include <errno.h>
 #include <limits.h>
+#include <stdint.h>
 #include <pcre.h>
+
+#include <linux/limits.h>
+
+#include <sys/mman.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
@@ -229,6 +235,167 @@ static int process_line(struct selabel_handle *rec,
 	return 0;
 }
 
+static int load_mmap(struct selabel_handle *rec, const char *path, struct stat *stat)
+{
+	struct saved_data *data = (struct saved_data *)rec->data;
+	char mmap_path[PATH_MAX + 1];
+	int mmapfd;
+	int rc, i;
+	struct stat mmap_stat;
+	char *addr;
+	size_t len;
+	int stem_map_len, *stem_map;
+
+	uint32_t *magic;
+	uint32_t *section_len;
+	uint32_t *plen;
+
+	rc = snprintf(mmap_path, sizeof(mmap_path), "%s.bin", path);
+	if (rc >= sizeof(mmap_path))
+		return -1;
+
+	mmapfd = open(mmap_path, O_RDONLY);
+	if (!mmapfd)
+		return -1;
+
+	rc = fstat(mmapfd, &mmap_stat);
+	if (rc < 0)
+		return -1;
+
+	/* if mmap is old, ignore it */
+	if (mmap_stat.st_mtime < stat->st_mtime)
+		return -1;
+
+	if (mmap_stat.st_mtime == stat->st_mtime &&
+	    mmap_stat.st_mtim.tv_nsec < stat->st_mtim.tv_nsec)
+		return -1;
+
+	/* ok, read it in... */
+	len = mmap_stat.st_size;
+	len += (sysconf(_SC_PAGE_SIZE) - 1);
+	len &= ~(sysconf(_SC_PAGE_SIZE) - 1);
+
+	addr = mmap(NULL, len, PROT_READ, MAP_PRIVATE, mmapfd, 0);
+	close(mmapfd);
+	if (addr == MAP_FAILED) {
+		perror("mmap");
+		return -1;
+	}
+
+	/* check if this looks like an fcontext file */
+	magic = (uint32_t *)addr;
+	if (*magic != SELINUX_MAGIC_COMPILED_FCONTEXT)
+		return -1;
+	addr += sizeof(uint32_t);
+
+	/* check if this version is higher than we understand */
+	section_len = (uint32_t *)addr;
+	if (*section_len > SELINUX_COMPILED_FCONTEXT_MAX_VERS)
+		return -1;
+	addr += sizeof(uint32_t);
+
+	/* allocate the stems_data array */
+	section_len = (uint32_t *)addr;
+	addr += sizeof(uint32_t);
+
+	/*
+	 * map indexed by the stem # in the mmap file and contains the stem
+	 * number in the data stem_arr
+	 */
+	stem_map_len = *section_len;
+	stem_map = calloc(stem_map_len, sizeof(*stem_map));
+	if (!stem_map)
+		return -1;
+
+	for (i = 0; i < *section_len; i++) {
+		char *buf;
+		uint32_t stem_len;
+		int newid;
+
+		/* the length does not inlude the nul */
+		plen = (uint32_t *)addr;
+		addr += sizeof(uint32_t);
+
+		stem_len = *plen;
+		buf = (char *)addr;
+		addr += (stem_len + 1); // +1 is the nul
+
+		/* store the mapping between old and new */
+		newid = find_stem(data, buf, stem_len);
+		if (newid < 0) {
+			newid = store_stem(data, buf, stem_len);
+			if (newid < 0)
+				return newid;
+			data->stem_arr[newid].from_mmap = 1;
+		}
+		stem_map[i] = newid;
+	}
+
+	/* allocate the regex array */
+	section_len = (uint32_t *)addr;
+	addr += sizeof(*section_len);
+
+	for (i = 0; i < *section_len; i++) {
+		struct spec *spec;
+		int32_t stem_id;
+
+		rc = grow_specs(data);
+		if (rc < 0)
+			return rc;
+
+		spec = &data->spec_arr[data->nspec];
+		spec->from_mmap = 1;
+		spec->regcomp = 1;
+
+		plen = (uint32_t *)addr;
+		addr += sizeof(uint32_t);
+		spec->lr.ctx_raw = strdup((char *)addr);
+		if (!spec->lr.ctx_raw)
+			return -1;
+		addr += *plen;
+
+		plen = (uint32_t *)addr;
+		addr += sizeof(uint32_t);
+		spec->regex_str = (char *)addr;
+		addr += *plen;
+
+		spec->mode = *(mode_t *)addr;
+		addr += sizeof(mode_t);
+
+		/* map the stem id from the mmap file to the data->stem_arr */
+		stem_id = *(int32_t *)addr;
+		if (stem_id == -1) {
+			spec->stem_id = -1;
+		} else {
+			assert(stem_id <= stem_map_len);
+			spec->stem_id = stem_map[stem_id];
+		}
+		addr += sizeof(int32_t);
+
+		/* retrieve the hasMetaChars bit */
+		spec->hasMetaChars = *(uint32_t *)addr;
+		addr += sizeof(uint32_t);
+
+		plen = (uint32_t *)addr;
+		addr += sizeof(uint32_t);
+		spec->regex = (pcre *)addr;
+		addr += *plen;
+
+		plen = (uint32_t *)addr;
+		addr += sizeof(uint32_t);
+		spec->lsd.study_data = (void *)addr;
+		spec->lsd.flags |= PCRE_EXTRA_STUDY_DATA;
+		addr += *plen;
+
+		data->nspec++;
+	}
+
+	free(stem_map);
+
+	/* win */
+	return 0;
+}
+
 static int process_file(const char *path, const char *suffix, struct selabel_handle *rec, const char *prefix)
 {
 	FILE *fp;
@@ -261,6 +428,10 @@ static int process_file(const char *path, const char *suffix, struct selabel_han
 		return -1;
 	}
 
+	rc = load_mmap(rec, path, &sb);
+	if (rc == 0)
+		goto out;
+
 	/*
 	 * The do detailed validation of the input and fill the spec array
 	 */
@@ -270,6 +441,7 @@ static int process_file(const char *path, const char *suffix, struct selabel_han
 		if (rc)
 			return rc;
 	}
+out:
 	free(line_buf);
 	fclose(fp);
 
@@ -357,6 +529,8 @@ static void closef(struct selabel_handle *rec)
 
 	for (i = 0; i < data->nspec; i++) {
 		spec = &data->spec_arr[i];
+		if (spec->from_mmap)
+			continue;
 		free(spec->regex_str);
 		free(spec->type_str);
 		free(spec->lr.ctx_raw);
@@ -369,6 +543,8 @@ static void closef(struct selabel_handle *rec)
 
 	for (i = 0; i < (unsigned int)data->num_stems; i++) {
 		stem = &data->stem_arr[i];
+		if (stem->from_mmap)
+			continue;
 		free(stem->buf);
 	}
 
diff --git a/libselinux/src/label_file.h b/libselinux/src/label_file.h
index cb5633b..9799bbb 100644
--- a/libselinux/src/label_file.h
+++ b/libselinux/src/label_file.h
@@ -5,24 +5,32 @@
 
 #include "label_internal.h"
 
+#define SELINUX_MAGIC_COMPILED_FCONTEXT	0xf97cff8a
+#define SELINUX_COMPILED_FCONTEXT_MAX_VERS	1
+
 /* A file security context specification. */
 struct spec {
 	struct selabel_lookup_rec lr;	/* holds contexts for lookup result */
 	char *regex_str;	/* regular expession string for diagnostics */
 	char *type_str;		/* type string for diagnostic messages */
 	pcre *regex;		/* compiled regular expression */
-	pcre_extra *sd;		/* extra compiled stuff */
+	union {
+		pcre_extra *sd;	/* pointer to extra compiled stuff */
+		pcre_extra lsd;	/* used to hold the mmap'd version */
+	};
 	mode_t mode;		/* mode format value */
 	int matches;		/* number of matching pathnames */
 	int stem_id;		/* indicates which stem-compression item */
 	char hasMetaChars;	/* regular expression has meta-chars */
 	char regcomp;		/* regex_str has been compiled to regex */
+	char from_mmap;		/* this spec is from an mmap of the data */
 };
 
 /* A regular expression stem */
 struct stem {
 	char *buf;
 	int len;
+	char from_mmap;
 };
 
 /* Our stored configuration */
@@ -45,7 +53,10 @@ struct saved_data {
 
 static inline pcre_extra *get_pcre_extra(struct spec *spec)
 {
-	return spec->sd;
+	if (spec->from_mmap)
+		return &spec->lsd;
+	else
+		return spec->sd;
 }
 
 static inline mode_t string_to_mode(char *mode)
diff --git a/libselinux/src/load_policy.c b/libselinux/src/load_policy.c
index 10e29b9..888dab5 100644
--- a/libselinux/src/load_policy.c
+++ b/libselinux/src/load_policy.c
@@ -49,8 +49,9 @@ int load_setlocaldefs hidden = 1;
 int selinux_mkload_policy(int preservebools)
 {	
 	int kernvers = security_policyvers();
-	int maxvers = kernvers, minvers = DEFAULT_POLICY_VERSION, vers;
+	int maxvers = kernvers, minvers = DEFAULT_POLICY_VERSION;
 	int setlocaldefs = load_setlocaldefs;
+	char *pol_path = NULL;
 	char path[PATH_MAX];
 	struct stat sb;
 	struct utsname uts;
@@ -162,29 +163,24 @@ checkbool:
 			maxvers = max(kernvers, maxvers);
 	}
 
-	vers = maxvers;
-      search:
-	snprintf(path, sizeof(path), "%s.%d",
-		 selinux_binary_policy_path(), vers);
-	fd = open(path, O_RDONLY);
-	while (fd < 0 && errno == ENOENT
-	       && --vers >= minvers) {
-		/* Check prior versions to see if old policy is available */
-		snprintf(path, sizeof(path), "%s.%d",
-			 selinux_binary_policy_path(), vers);
-		fd = open(path, O_RDONLY);
+search:
+	pol_path = selinux_binary_policy_path_min_max(minvers, &maxvers);
+	if (!pol_path) {
+		fprintf(stderr, "SELinux: unable to find usable policy file:  %s\n",
+			strerror(errno));
+		goto dlclose;
 	}
+
+	fd = open(pol_path, O_RDONLY);
 	if (fd < 0) {
-		fprintf(stderr,
-			"SELinux:  Could not open policy file <= %s.%d:  %s\n",
-			selinux_binary_policy_path(), maxvers, strerror(errno));
+		fprintf(stderr, "SELinux:  Could not open policy file %s:  %s\n",
+			pol_path, strerror(errno));
 		goto dlclose;
 	}
 
 	if (fstat(fd, &sb) < 0) {
-		fprintf(stderr,
-			"SELinux:  Could not stat policy file %s:  %s\n",
-			path, strerror(errno));
+		fprintf(stderr, "SELinux:  Could not stat policy file %s:  %s\n",
+			pol_path, strerror(errno));
 		goto close;
 	}
 
@@ -195,13 +191,12 @@ checkbool:
 	size = sb.st_size;
 	data = map = mmap(NULL, size, prot, MAP_PRIVATE, fd, 0);
 	if (map == MAP_FAILED) {
-		fprintf(stderr,
-			"SELinux:  Could not map policy file %s:  %s\n",
-			path, strerror(errno));
+		fprintf(stderr, "SELinux:  Could not map policy file %s:  %s\n",
+			pol_path, strerror(errno));
 		goto close;
 	}
 
-	if (vers > kernvers && usesepol) {
+	if (maxvers > kernvers && usesepol) {
 		/* Need to downgrade to kernel-supported version. */
 		if (policy_file_create(&pf))
 			goto unmap;
@@ -220,12 +215,12 @@ checkbool:
 			/* Downgrade failed, keep searching. */
 			fprintf(stderr,
 				"SELinux:  Could not downgrade policy file %s, searching for an older version.\n",
-				path);
+				pol_path);
 			policy_file_free(pf);
 			policydb_free(policydb);
 			munmap(map, sb.st_size);
 			close(fd);
-			vers--;
+			maxvers--;
 			goto search;
 		}
 		policy_file_free(pf);
@@ -281,7 +276,7 @@ checkbool:
 	if (rc)
 		fprintf(stderr,
 			"SELinux:  Could not load policy file %s:  %s\n",
-			path, strerror(errno));
+			pol_path, strerror(errno));
 
       unmap:
 	if (data != map)
@@ -296,6 +291,7 @@ checkbool:
 	if (libsepolh)
 		dlclose(libsepolh);
 #endif
+	free(pol_path);
 	return rc;
 }
 
diff --git a/libselinux/src/matchpathcon.c b/libselinux/src/matchpathcon.c
index 2d7369e..2a00807 100644
--- a/libselinux/src/matchpathcon.c
+++ b/libselinux/src/matchpathcon.c
@@ -2,6 +2,7 @@
 #include <string.h>
 #include <errno.h>
 #include <stdio.h>
+#include <syslog.h>
 #include "selinux_internal.h"
 #include "label_internal.h"
 #include "callbacks.h"
@@ -62,7 +63,7 @@ static void
 {
 	va_list ap;
 	va_start(ap, fmt);
-	vfprintf(stderr, fmt, ap);
+	vsyslog(LOG_ERR, fmt, ap);
 	va_end(ap);
 }
 
diff --git a/libselinux/src/selinux_config.c b/libselinux/src/selinux_config.c
index 296f357..cb65666 100644
--- a/libselinux/src/selinux_config.c
+++ b/libselinux/src/selinux_config.c
@@ -9,6 +9,7 @@
 #include <unistd.h>
 #include <pthread.h>
 #include "selinux_internal.h"
+#include "policy.h"
 #include "get_default_type_internal.h"
 
 #define SELINUXDIR "/etc/selinux/"
@@ -296,13 +297,57 @@ const char *selinux_removable_context_path(void)
 
 hidden_def(selinux_removable_context_path)
 
+char *selinux_binary_policy_path_min_max(int min, int *max)
+{
+	int ret;
+	char *path = NULL;
+
+	while(*max >= min) {
+		ret = asprintf(&path, "%s.%d", get_path(BINPOLICY), *max);
+		if (ret < 0)
+			goto err;
+		ret = access(path, R_OK);
+		if (!ret)
+			return path;
+		free(path);
+		path = NULL;
+		*max = *max - 1;
+	}
+err:
+	free(path);
+	return NULL;
+}
+hidden_def(selinux_binary_policy_path_min_max)
+
 const char *selinux_binary_policy_path(void)
 {
 	return get_path(BINPOLICY);
 }
-
 hidden_def(selinux_binary_policy_path)
 
+const char *selinux_current_policy_path(void)
+{
+	int rc = 0;
+	int vers = 0;
+	static char policy_path[PATH_MAX];
+
+	snprintf(policy_path, sizeof(policy_path), "%s/policy", selinux_mnt);
+	if (access(policy_path, F_OK) != 0 ) {
+		vers = security_policyvers();
+		do {
+			/* Check prior versions to see if old policy is available */
+			snprintf(policy_path, sizeof(policy_path), "%s.%d",
+				 selinux_binary_policy_path(), vers);
+		} while ((rc = access(policy_path, F_OK)) && --vers > 0);
+
+		if (rc) return NULL;
+	}
+
+	return policy_path;
+}
+
+hidden_def(selinux_current_policy_path)
+
 const char *selinux_file_context_path(void)
 {
 	return get_path(FILE_CONTEXTS);
diff --git a/libselinux/src/selinux_internal.h b/libselinux/src/selinux_internal.h
index 2c7c85c..75ebc88 100644
--- a/libselinux/src/selinux_internal.h
+++ b/libselinux/src/selinux_internal.h
@@ -61,7 +61,9 @@ hidden_proto(selinux_mkload_policy)
     hidden_proto(security_deny_unknown)
     hidden_proto(selinux_boolean_sub)
     hidden_proto(selinux_binary_policy_path)
+    hidden_proto(selinux_binary_policy_path_min_max);
     hidden_proto(selinux_booleans_subs_path)
+    hidden_proto(selinux_current_policy_path)
     hidden_proto(selinux_default_context_path)
     hidden_proto(selinux_securetty_types_path)
     hidden_proto(selinux_failsafe_context_path)
diff --git a/libselinux/src/seusers.c b/libselinux/src/seusers.c
index cfea186..8b1eba6 100644
--- a/libselinux/src/seusers.c
+++ b/libselinux/src/seusers.c
@@ -125,7 +125,7 @@ static int check_group(const char *group, const char *name, const gid_t gid) {
 		rbuf = malloc(rbuflen);
 		if (rbuf == NULL)
 			return 0;
-		int retval = getgrnam_r(group, &gbuf, rbuf, 
+		int retval = getgrnam_r(group, &gbuf, rbuf,
 				rbuflen, &grent);
 		if ( retval == ERANGE )
 		{
@@ -198,13 +198,13 @@ int getseuserbyname(const char *name, char **r_seuser, char **r_level)
 		if (!strcmp(username, name))
 			break;
 
-		if (username[0] == '%' && 
-		    !groupseuser && 
+		if (username[0] == '%' &&
+		    !groupseuser &&
 		    check_group(&username[1], name, gid)) {
 				groupseuser = seuser;
 				grouplevel = level;
 		} else {
-			if (!defaultseuser && 
+			if (!defaultseuser &&
 			    !strcmp(username, "__default__")) {
 				defaultseuser = seuser;
 				defaultlevel = level;
@@ -258,7 +258,7 @@ int getseuserbyname(const char *name, char **r_seuser, char **r_level)
 	return 0;
 }
 
-int getseuser(const char *username, const char *service, 
+int getseuser(const char *username, const char *service,
 	      char **r_seuser, char **r_level) {
 	int ret = -1;
 	int len = 0;
diff --git a/libselinux/utils/.gitignore b/libselinux/utils/.gitignore
index 8b9294d..060eaab 100644
--- a/libselinux/utils/.gitignore
+++ b/libselinux/utils/.gitignore
@@ -13,6 +13,7 @@ getsebool
 getseuser
 matchpathcon
 policyvers
+sefcontext_compile
 selinux_check_securetty_context
 selinuxenabled
 selinuxexeccon
diff --git a/libselinux/utils/Makefile b/libselinux/utils/Makefile
index 5f3e047..f469924 100644
--- a/libselinux/utils/Makefile
+++ b/libselinux/utils/Makefile
@@ -28,6 +28,7 @@ LDLIBS += -L../src -lselinux -L$(LIBDIR)
 
 TARGETS=$(patsubst %.c,%,$(wildcard *.c))
 
+sefcontext_compile: LDLIBS += -lpcre
 
 ifeq ($(DISABLE_AVC),y)
 	UNUSED_TARGETS+=compute_av compute_create compute_member compute_relabel
diff --git a/libselinux/utils/sefcontext_compile.c b/libselinux/utils/sefcontext_compile.c
new file mode 100644
index 0000000..15cc836
--- /dev/null
+++ b/libselinux/utils/sefcontext_compile.c
@@ -0,0 +1,350 @@
+#include <ctype.h>
+#include <errno.h>
+#include <pcre.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <linux/limits.h>
+
+#include "../src/label_file.h"
+
+static int process_file(struct saved_data *data, const char *filename)
+{
+	struct spec *spec;
+	unsigned int line_num;
+	char *line_buf = NULL;
+	size_t line_len;
+	ssize_t len;
+	FILE *context_file;
+
+	context_file = fopen(filename, "r");
+	if (!context_file) {
+		fprintf(stderr, "Error opening %s: %s\n", filename, strerror(errno));
+		return -1;
+	}
+
+	line_num = 0;
+	while ((len = getline(&line_buf, &line_len, context_file)) != -1) {
+		char *context;
+		char *mode;
+		char *regex;
+		char *cp, *anchored_regex;
+		char *buf_p;
+		pcre *re;
+		pcre_extra *sd;
+		const char *err;
+		int items, erroff, rc;
+		size_t regex_len;
+		int32_t stem_id;
+
+		len = strlen(line_buf);
+		if (line_buf[len - 1] == '\n')
+			line_buf[len - 1] = 0;
+		buf_p = line_buf;
+		while (isspace(*buf_p))
+			buf_p++;
+		/* Skip comment lines and empty lines. */
+		if (*buf_p == '#' || *buf_p == 0)
+			continue;
+
+		items = sscanf(line_buf, "%ms %ms %ms", &regex, &mode, &context);
+		if (items < 2 || items > 3) {
+			fprintf(stderr, "invalid entry, skipping:%s", line_buf);
+			continue;
+		}
+
+		if (items == 2) {
+			context = mode;
+			mode = NULL;
+		}
+
+		rc = grow_specs(data);
+		if (rc) {
+			fprintf(stderr, "grow_specs failed: %s\n", strerror(errno));
+			return rc;
+		}
+
+		spec = &data->spec_arr[data->nspec];
+
+		spec->lr.ctx_raw = context;
+		spec->mode = string_to_mode(mode);
+		if (spec->mode == -1) {
+			fprintf(stderr, "%s: line %d has invalid file type %s\n",
+				regex, line_num + 1, mode);
+			spec->mode = 0;
+		}
+		free(mode);
+		spec->regex_str = regex;
+
+		stem_id = find_stem_from_spec(data, regex);
+		spec->stem_id = stem_id;
+		/* skip past the fixed stem part */
+		if (stem_id != -1)
+			regex += data->stem_arr[stem_id].len;
+
+		regex_len = strlen(regex);
+		cp = anchored_regex = malloc(regex_len + 3);
+		if (!cp) {
+			fprintf(stderr, "Malloc Failed: %s\n", strerror(errno));
+			return -1;
+		}
+		*cp++ = '^';
+		memcpy(cp, regex, regex_len);
+		cp += regex_len;
+		*cp++ = '$';
+		*cp = '\0';
+
+		spec_hasMetaChars(spec);
+
+		re = pcre_compile(anchored_regex, 0, &err, &erroff, NULL);
+		if (!re) {
+			fprintf(stderr, "PCRE compilation failed for %s at offset %d: %s\n", anchored_regex, erroff, err);
+			return -1;
+		}
+		spec->regex = re;
+
+		sd = pcre_study(re, 0, &err);
+		if (!sd) {
+			fprintf(stderr, "PCRE study failed for %s: %s\n", anchored_regex, err);
+			return -1;
+		}
+		free(anchored_regex);
+		spec->sd = sd;
+
+		line_num++;
+		data->nspec++;
+	}
+
+	free(line_buf);
+	fclose(context_file);
+
+	return 0;
+}
+
+/*
+ * File Format
+ *
+ * u32 - magic number
+ * u32 - version
+ * u32 - number of stems
+ * ** Stems
+ * 	u32  - length of stem EXCLUDING nul
+ * 	char - stem char array INCLUDING nul
+ * u32 - number of regexs
+ * ** Regexes
+ * 	u32  - length of upcoming context INCLUDING nul
+ * 	char - char array of the raw context
+ *	u32  - length of the upcoming regex_str
+ *	char - char array of the original regex string including the stem.
+ *	mode_t - mode bits
+ *	s32  - stemid associated with the regex
+ *	u32  - spec has meta characters
+ *	u32  - data length of the pcre regex
+ *	char - a bufer holding the raw pcre regex info
+ *	u32  - data length of the pcre regex study daya
+ *	char - a buffer holding the raw pcre regex study data
+ */
+static int write_binary_file(struct saved_data *data, char *filename)
+{
+	struct spec *specs = data->spec_arr;
+	FILE *bin_file;
+	size_t len;
+	uint32_t magic = SELINUX_MAGIC_COMPILED_FCONTEXT;
+	uint32_t section_len;
+	uint32_t i;
+
+	bin_file = fopen(filename, "w");
+	if (!bin_file) {
+		perror("fopen output_file");
+		exit(EXIT_FAILURE);
+	}
+
+	/* write some magic number */
+	len = fwrite(&magic, sizeof(uint32_t), 1, bin_file);
+	if (len != 1)
+		return -1;
+
+	/* write the version */
+	section_len = SELINUX_COMPILED_FCONTEXT_MAX_VERS;
+	len = fwrite(&section_len, sizeof(uint32_t), 1, bin_file);
+	if (len != 1)
+		return -1;
+
+	/* write the number of stems coming */
+	section_len = data->num_stems;
+	len = fwrite(&section_len, sizeof(uint32_t), 1, bin_file);
+	if (len != 1)
+		return -1;
+
+	for (i = 0; i < section_len; i++) {
+		char *stem = data->stem_arr[i].buf;
+		uint32_t stem_len = data->stem_arr[i].len;
+
+		/* write the strlen (aka no nul) */
+		len = fwrite(&stem_len, sizeof(uint32_t), 1, bin_file);
+		if (len != 1)
+			return -1;
+
+		/* include the nul in the file */
+		stem_len += 1;
+		len = fwrite(stem, sizeof(char), stem_len, bin_file);
+		if (len != stem_len)
+			return -1;
+	}
+
+	/* write the number of regexes coming */
+	section_len = data->nspec;
+	len = fwrite(&section_len, sizeof(uint32_t), 1, bin_file);
+	if (len != 1)
+		return -1;
+
+	for (i = 0; i < section_len; i++) {
+		char *context = specs[i].lr.ctx_raw;
+		char *regex_str = specs[i].regex_str;
+		mode_t mode = specs[i].mode;
+		int32_t stem_id = specs[i].stem_id;
+		pcre *re = specs[i].regex;
+		pcre_extra *sd = get_pcre_extra(&specs[i]);
+		uint32_t to_write;
+		size_t size;
+		int rc;
+
+		/* length of the context string (including nul) */
+		to_write = strlen(context) + 1;
+		len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file);
+		if (len != 1)
+			return -1;
+
+		/* original context strin (including nul) */
+		len = fwrite(context, sizeof(char), to_write, bin_file);
+		if (len != to_write)
+			return -1;
+
+		/* length of the original regex string (including nul) */
+		to_write = strlen(regex_str) + 1;
+		len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file);
+		if (len != 1)
+			return -1;
+
+		/* original regex string */
+		len = fwrite(regex_str, sizeof(char), to_write, bin_file);
+		if (len != to_write)
+			return -1;
+
+		/* binary F_MODE bits */
+		len = fwrite(&mode, sizeof(mode), 1, bin_file);
+		if (len != 1)
+			return -1;
+
+		/* stem for this regex (could be -1) */
+		len = fwrite(&stem_id, sizeof(stem_id), 1, bin_file);
+		if (len != 1)
+			return -1;
+
+		/* does this spec have a metaChar? */
+		to_write = specs[i].hasMetaChars;
+		len = fwrite(&to_write, sizeof(to_write), 1, bin_file);
+		if (len != 1)
+			return -1;
+
+		/* determine the size of the pcre data in bytes */
+		rc = pcre_fullinfo(re, NULL, PCRE_INFO_SIZE, &size);
+		if (rc < 0)
+			return -1;
+
+		/* write the number of bytes in the pcre data */
+		to_write = size;
+		len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file);
+		if (len != 1)
+			return -1;
+
+		/* write the actual pcre data as a char array */
+		len = fwrite(re, 1, to_write, bin_file);
+		if (len != to_write)
+			return -1;
+
+		/* determine the size of the pcre study info */
+		rc = pcre_fullinfo(re, sd, PCRE_INFO_STUDYSIZE, &size);
+		if (rc < 0)
+			return -1;
+
+		/* write the number of bytes in the pcre study data */
+		to_write = size;
+		len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file);
+		if (len != 1)
+			return -1;
+
+		/* write the actual pcre study data as a char array */
+		len = fwrite(sd->study_data, 1, to_write, bin_file);
+		if (len != to_write)
+			return -1;
+	}
+
+	fclose(bin_file);
+
+	return 0;
+}
+
+static int free_specs(struct saved_data *data)
+{
+	struct spec *specs = data->spec_arr;
+	unsigned int num_entries = data->nspec;
+	unsigned int i;
+
+	for (i = 0; i < num_entries; i++) {
+		free(specs[i].lr.ctx_raw);
+		free(specs[i].lr.ctx_trans);
+		free(specs[i].regex_str);
+		pcre_free(specs[i].regex);
+		pcre_free_study(specs[i].sd);
+	}
+	free(specs);
+
+	num_entries = data->num_stems;
+	for (i = 0; i < num_entries; i++) {
+		free(data->stem_arr[i].buf);
+	}
+	free(data->stem_arr);
+
+	memset(data, 0, sizeof(*data));
+	return 0;
+}
+
+int main(int argc, char *argv[])
+{
+	struct saved_data data;
+	const char *path;
+	char stack_path[PATH_MAX + 1];
+	int rc;
+
+	if (argc != 2) {
+		fprintf(stderr, "usage: %s input_file\n", argv[0]);
+		exit(EXIT_FAILURE);
+	}
+
+	memset(&data, 0, sizeof(data));
+
+	path = argv[1];
+
+	rc = process_file(&data, path);
+	if (rc < 0)
+		return rc;
+
+	rc = sort_specs(&data);
+	if (rc)
+		return rc;
+
+	rc = snprintf(stack_path, sizeof(stack_path), "%s.bin", path);
+	if (rc < 0 || rc >= sizeof(stack_path))
+		return rc;
+	rc = write_binary_file(&data, stack_path);
+	if (rc < 0)
+		return rc;
+
+	rc = free_specs(&data);
+	if (rc < 0)
+		return rc;
+
+	return 0;
+}