libselinux/libselinux-rhat.patch
rhatdan 01a1f705b5 Update to upstream
* Add support for lxc_contexts_path
	* utils: add service to getdefaultcon
	* libsemanage: do not set soname needlessly
	* libsemanage: remove PYTHONLIBDIR and ruby equivalent
	* boolean name equivalency
	* getsebool: support boolean name substitution
	* Add man page for new selinux_boolean_sub function.
	* expose selinux_boolean_sub
	* matchpathcon: add -m option to force file type check
	* utils: avcstat: clear sa_mask set
	* seusers: Check for strchr failure
	* booleans: initialize pointer to silence coveriety
	* stop messages when SELinux disabled
	* label_file: use PCRE instead of glibc regex functions
	* label_file: remove all typedefs
	* label_file: move definitions to include file
	* label_file: do string to mode_t conversion in a helper function
	* label_file: move error reporting back into caller
	* label_file: move stem/spec handling to header
	* label_file: drop useless ncomp field from label_file data
	* label_file: move spec_hasMetaChars to header
	* label_file: fix potential read past buffer in spec_hasMetaChars
	* label_file: move regex sorting to the header
	* label_file: add accessors for the pcre extra data
	* label_file: only run regex files one time
	* label_file: new process_file function
	* label_file: break up find_stem_from_spec
	* label_file: struct reorg
	* label_file: only run array once when sorting
	* Ensure that we only close the selinux netlink socket once.
	* improve the file_contexts.5 manual page
2012-09-14 05:59:45 -04:00

1003 lines
26 KiB
Diff

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/src/audit2why.c b/libselinux/src/audit2why.c
index 02483a3..89953d7 100644
--- a/libselinux/src/audit2why.c
+++ b/libselinux/src/audit2why.c
@@ -206,27 +206,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;
}
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..f8a5fea
--- /dev/null
+++ b/libselinux/utils/sefcontext_compile.c
@@ -0,0 +1,345 @@
+#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)
+ 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)
+ 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)
+ 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;
+}