Port of libselinux to Android.
diff --git a/src/android.c b/src/android.c
new file mode 100644
index 0000000..4768475
--- /dev/null
+++ b/src/android.c
@@ -0,0 +1,465 @@
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <selinux/selinux.h>
+#include <selinux/context.h>
+#include <selinux/android.h>
+#include "callbacks.h"
+#include "selinux_internal.h"
+
+/*
+ * XXX Where should this configuration file be located?
+ * Needs to be accessible by zygote and installd when
+ * setting credentials for app processes and setting permissions
+ * on app data directories.
+ */
+#define SEAPP_CONTEXTS "/seapp_contexts"
+
+struct seapp_context {
+	/* input selectors */
+	char isSystemServer;
+	char *user;
+	size_t len;
+	char prefix;
+	char *seinfo;
+	char *name;
+	/* outputs */
+	char *domain;
+	char *type;
+	char *level;
+	char levelFromUid;
+};
+
+static int seapp_context_cmp(const void *A, const void *B)
+{
+	const struct seapp_context **sp1 = A, **sp2 = B;
+	const struct seapp_context *s1 = *sp1, *s2 = *sp2;
+
+	/* Give precedence to isSystemServer=true. */
+	if (s1->isSystemServer != s2->isSystemServer)
+		return (s1->isSystemServer ? -1 : 1);
+
+	/* Give precedence to a specified user= over an unspecified user=. */
+	if (s1->user && !s2->user)
+		return -1;
+	if (!s1->user && s2->user)
+		return 1;
+
+	if (s1->user) {
+		/* Give precedence to a fixed user= string over a prefix. */
+		if (s1->prefix != s2->prefix)
+			return (s2->prefix ? -1 : 1);
+
+		/* Give precedence to a longer prefix over a shorter prefix. */
+		if (s1->prefix && s1->len != s2->len)
+			return (s1->len > s2->len) ? -1 : 1;
+	}
+
+	/* Give precedence to a specified seinfo= over an unspecified seinfo=. */
+	if (s1->seinfo && !s2->seinfo)
+		return -1;
+	if (!s1->seinfo && s2->seinfo)
+		return 1;
+
+	/* Give precedence to a specified name= over an unspecified name=. */
+	if (s1->name && !s2->name)
+		return -1;
+	if (!s1->name && s2->name)
+		return 1;
+
+	/* Anything else has equal precedence. */
+	return 0;
+}
+
+static struct seapp_context **seapp_contexts = NULL;
+static int nspec = 0;
+
+static void seapp_context_init(void)
+{
+	FILE *fp;
+	char line_buf[BUFSIZ];
+	const char *path = SEAPP_CONTEXTS;
+	char *token;
+	unsigned lineno;
+	struct seapp_context *cur;
+	char *p, *name = NULL, *value = NULL, *saveptr;
+	size_t len;
+
+	fp = fopen(path, "r");
+	if (!fp) {
+		selinux_log(SELINUX_ERROR, "%s:  could not open %s", __FUNCTION__, path);
+		return;
+	}
+
+	nspec = 0;
+	while (fgets(line_buf, sizeof line_buf - 1, fp)) {
+		p = line_buf;
+		while (isspace(*p))
+			p++;
+		if (*p == '#' || *p == 0)
+			continue;
+		nspec++;
+	}
+
+	seapp_contexts = calloc(nspec, sizeof(struct seapp_context *));
+	if (!seapp_contexts)
+		goto oom;
+
+	rewind(fp);
+	nspec = 0;
+	lineno = 1;
+	while (fgets(line_buf, sizeof line_buf - 1, fp)) {
+		len = strlen(line_buf);
+		if (line_buf[len - 1] == '\n')
+			line_buf[len - 1] = 0;
+		p = line_buf;
+		while (isspace(*p))
+			p++;
+		if (*p == '#' || *p == 0)
+			continue;
+
+		cur = calloc(1, sizeof(struct seapp_context));
+		if (!cur)
+			goto oom;
+
+		token = strtok_r(p, " \t", &saveptr);
+		if (!token)
+			goto err;
+
+		while (1) {
+			name = token;
+			value = strchr(name, '=');
+			if (!value)
+				goto err;
+			*value++ = 0;
+
+			if (!strcasecmp(name, "isSystemServer")) {
+				if (!strcasecmp(value, "true"))
+					cur->isSystemServer = 1;
+				else if (!strcasecmp(value, "false"))
+					cur->isSystemServer = 0;
+				else {
+					goto err;
+				}
+			} else if (!strcasecmp(name, "user")) {
+				cur->user = strdup(value);
+				if (!cur->user)
+					goto oom;
+				cur->len = strlen(cur->user);
+				if (cur->user[cur->len-1] == '*')
+					cur->prefix = 1;
+			} else if (!strcasecmp(name, "seinfo")) {
+				cur->seinfo = strdup(value);
+				if (!cur->seinfo)
+					goto oom;
+			} else if (!strcasecmp(name, "name")) {
+				cur->name = strdup(value);
+				if (!cur->name)
+					goto oom;
+			} else if (!strcasecmp(name, "domain")) {
+				cur->domain = strdup(value);
+				if (!cur->domain)
+					goto oom;
+			} else if (!strcasecmp(name, "type")) {
+				cur->type = strdup(value);
+				if (!cur->type)
+					goto oom;
+			} else if (!strcasecmp(name, "levelFromUid")) {
+				if (!strcasecmp(value, "true"))
+					cur->levelFromUid = 1;
+				else if (!strcasecmp(value, "false"))
+					cur->levelFromUid = 0;
+				else {
+					goto err;
+				}
+			} else if (!strcasecmp(name, "level")) {
+				cur->level = strdup(value);
+				if (!cur->level)
+					goto oom;
+			} else
+				goto err;
+
+			token = strtok_r(NULL, " \t", &saveptr);
+			if (!token)
+				break;
+		}
+
+		seapp_contexts[nspec] = cur;
+		nspec++;
+		lineno++;
+	}
+
+	qsort(seapp_contexts, nspec, sizeof(struct seapp_context *),
+	      seapp_context_cmp);
+
+#if DEBUG
+	{
+		int i;
+		for (i = 0; i < nspec; i++) {
+			cur = seapp_contexts[i];
+			selinux_log(SELINUX_INFO, "%s:  isSystemServer=%s user=%s seinfo=%s name=%s -> domain=%s type=%s level=%s levelFromUid=%s",
+				    __FUNCTION__,
+				    cur->isSystemServer ? "true" : "false",
+				    cur->user, cur->seinfo, cur->name,
+				    cur->domain, cur->type, cur->level,
+				    cur->levelFromUid ? "true" : "false");
+		}
+	}
+#endif
+
+out:
+	fclose(fp);
+	return;
+
+err:
+	selinux_log(SELINUX_ERROR, "%s:  Error reading %s, line %u, name %s, value %s\n",
+		    __FUNCTION__, path, lineno, name, value);
+	goto out;
+oom:
+	selinux_log(SELINUX_ERROR, 
+		    "%s:  Out of memory\n", __FUNCTION__);
+	goto out;
+}
+
+static pthread_once_t once = PTHREAD_ONCE_INIT;
+
+int selinux_android_setfilecon(const char *pkgdir,
+			       const char *name,
+			       uid_t uid)
+{
+	char *orig_ctx_str = NULL, *ctx_str;
+	context_t ctx = NULL;
+	struct passwd *pw;
+	struct seapp_context *cur;
+	int i, rc;
+
+	if (is_selinux_enabled() <= 0)
+		return 0;
+
+	__selinux_once(once, seapp_context_init);
+
+	rc = getfilecon(pkgdir, &ctx_str);
+	if (rc < 0)
+		goto err;
+
+	ctx = context_new(ctx_str);
+	orig_ctx_str = ctx_str;
+	if (!ctx)
+		goto oom;
+
+	pw = getpwuid(uid);
+	if (!pw)
+		goto err;
+
+	for (i = 0; i < nspec; i++) {
+		cur = seapp_contexts[i];
+
+		/* isSystemServer=true is only for app process labeling. */
+		if (cur->isSystemServer)
+			continue;
+
+		if (cur->user) {
+			if (cur->prefix) {
+				if (strncasecmp(pw->pw_name, cur->user, cur->len-1))
+					continue;
+			} else {
+				if (strcasecmp(pw->pw_name, cur->user))
+					continue;
+			}
+		}
+
+		/* seinfo= is ignored / not available for file labeling. */
+
+		if (cur->name) {
+			if (!name || strcasecmp(name, cur->name))
+				continue;
+		}
+
+		if (!cur->type)
+			continue;
+
+		if (context_type_set(ctx, cur->type))
+			goto oom;
+
+		if (cur->levelFromUid && !strncmp(pw->pw_name, "app_", 4)) {
+			char level[255];
+			unsigned long id;
+
+			/* Only supported for app UIDs. */
+			id = strtoul(pw->pw_name + 4, NULL, 10);
+			snprintf(level, sizeof level, "%s:c%lu",
+				 context_range_get(ctx), id);
+			if (context_range_set(ctx, level))
+				goto oom;
+		} else if (cur->level) {
+			if (context_range_set(ctx, cur->level))
+				goto oom;
+		}
+		
+		break;
+	}
+
+	ctx_str = context_str(ctx);
+	if (!ctx_str)
+		goto oom;
+
+	rc = security_check_context(ctx_str);
+	if (rc < 0)
+		goto err;
+
+	if (strcmp(ctx_str, orig_ctx_str)) {
+		rc = setfilecon(pkgdir, ctx_str);
+		if (rc < 0)
+			goto err;
+	}
+
+	rc = 0;
+out:
+	freecon(orig_ctx_str);
+	context_free(ctx);
+	return rc;
+err:
+	selinux_log(SELINUX_ERROR, "%s:  Error setting context for pkgdir %s, uid %d: %s\n",
+		    __FUNCTION__, pkgdir, uid, strerror(errno));
+	rc = -1;
+	goto out;
+oom:
+	selinux_log(SELINUX_ERROR, "%s:  Out of memory\n", __FUNCTION__);
+	rc = -1;
+	goto out;
+}
+
+int selinux_android_setcontext(uid_t uid,
+			       int isSystemServer,
+			       const char *seinfo,
+			       const char *name)
+{
+	char *orig_ctx_str = NULL, *ctx_str;
+	context_t ctx = NULL;
+	unsigned long id;
+	struct passwd *pw;
+	struct seapp_context *cur;
+	int i, rc;
+
+	if (is_selinux_enabled() <= 0)
+		return 0;
+
+	__selinux_once(once, seapp_context_init);
+	
+	rc = getcon(&ctx_str);
+	if (rc)
+		goto err;
+
+	ctx = context_new(ctx_str);
+	orig_ctx_str = ctx_str;
+	if (!ctx)
+		goto oom;
+
+	pw = getpwuid(uid);
+	if (!pw)
+		goto err;
+
+	for (i = 0; i < nspec; i++) {
+		cur = seapp_contexts[i];
+		if (cur->isSystemServer != isSystemServer)
+			continue;
+		if (cur->user) {
+			if (cur->prefix) {
+				if (strncasecmp(pw->pw_name, cur->user, cur->len-1))
+					continue;
+			} else {
+				if (strcasecmp(pw->pw_name, cur->user))
+					continue;
+			}
+		}
+		if (cur->seinfo) {
+			if (!seinfo || strcasecmp(seinfo, cur->seinfo))
+				continue;
+		}
+		if (cur->name) {
+			if (!name || strcasecmp(name, cur->name))
+				continue;
+		}
+
+		if (!cur->domain)
+			continue;
+
+		if (context_type_set(ctx, cur->domain))
+			goto oom;
+
+		if (cur->levelFromUid && !strncmp(pw->pw_name, "app_", 4)) {
+			char level[255];
+			unsigned long id;
+
+			/* Only supported for app UIDs. */
+			id = strtoul(pw->pw_name + 4, NULL, 10);
+			snprintf(level, sizeof level, "%s:c%lu",
+				 context_range_get(ctx), id);
+			if (context_range_set(ctx, level))
+				goto oom;
+		} else if (cur->level) {
+			if (context_range_set(ctx, cur->level))
+				goto oom;
+		}
+
+		break;
+	}
+
+	if (i == nspec) {
+		/*
+		 * No match. 
+		 * Fail to prevent staying in the zygote's context.
+		 */
+		selinux_log(SELINUX_ERROR,
+			    "%s:  No match for app with uid %d, seinfo %s, name %s\n",
+			    __FUNCTION__, uid, seinfo, name);
+		rc = -1;
+		goto out;
+	}
+
+	ctx_str = context_str(ctx);
+	if (!ctx_str)
+		goto oom;
+
+	rc = security_check_context(ctx_str);
+	if (rc < 0)
+		goto err;
+
+	if (strcmp(ctx_str, orig_ctx_str)) {
+		rc = setcon(ctx_str);
+		if (rc < 0)
+			goto err;
+	}
+
+	rc = 0;
+out:
+	freecon(orig_ctx_str);
+	context_free(ctx);
+	return rc;
+err:
+	if (isSystemServer)
+		selinux_log(SELINUX_ERROR,
+			    "%s:  Error setting context for system server: %s\n",
+			    __FUNCTION__, strerror(errno));
+	else 
+		selinux_log(SELINUX_ERROR,
+			    "%s:  Error setting context for app with uid %d, seinfo %s: %s\n",
+			    __FUNCTION__, uid, seinfo, strerror(errno));
+
+	rc = -1;
+	goto out;
+oom:
+	selinux_log(SELINUX_ERROR, "%s:  Out of memory\n", __FUNCTION__);
+	rc = -1;
+	goto out;
+}
+
diff --git a/src/avc.c b/src/avc.c
new file mode 100644
index 0000000..3ce724d
--- /dev/null
+++ b/src/avc.c
@@ -0,0 +1,1098 @@
+/*
+ * Implementation of the userspace access vector cache (AVC).
+ *
+ * Author : Eamon Walsh <ewalsh@epoch.ncsc.mil>
+ *
+ * Derived from the kernel AVC implementation by
+ * Stephen Smalley <sds@epoch.ncsc.mil> and 
+ * James Morris <jmorris@redhat.com>.
+ */
+#include <selinux/avc.h>
+#include "selinux_internal.h"
+#include "avc_sidtab.h"
+#include "avc_internal.h"
+
+#define AVC_CACHE_SLOTS		512
+#define AVC_CACHE_MAXNODES	410
+
+struct avc_entry {
+	security_id_t ssid;
+	security_id_t tsid;
+	security_class_t tclass;
+	struct av_decision avd;
+	security_id_t	create_sid;
+	int used;		/* used recently */
+};
+
+struct avc_node {
+	struct avc_entry ae;
+	struct avc_node *next;
+};
+
+struct avc_cache {
+	struct avc_node *slots[AVC_CACHE_SLOTS];
+	uint32_t lru_hint;	/* LRU hint for reclaim scan */
+	uint32_t active_nodes;
+	uint32_t latest_notif;	/* latest revocation notification */
+};
+
+struct avc_callback_node {
+	int (*callback) (uint32_t event, security_id_t ssid,
+			 security_id_t tsid,
+			 security_class_t tclass, access_vector_t perms,
+			 access_vector_t * out_retained);
+	uint32_t events;
+	security_id_t ssid;
+	security_id_t tsid;
+	security_class_t tclass;
+	access_vector_t perms;
+	struct avc_callback_node *next;
+};
+
+static void *avc_netlink_thread = NULL;
+static void *avc_lock = NULL;
+static void *avc_log_lock = NULL;
+static struct avc_node *avc_node_freelist = NULL;
+static struct avc_cache avc_cache;
+static char *avc_audit_buf = NULL;
+static struct avc_cache_stats cache_stats;
+static struct avc_callback_node *avc_callbacks = NULL;
+static struct sidtab avc_sidtab;
+
+static inline int avc_hash(security_id_t ssid,
+			   security_id_t tsid, security_class_t tclass)
+{
+	return ((uintptr_t) ssid ^ ((uintptr_t) tsid << 2) ^ tclass)
+	    & (AVC_CACHE_SLOTS - 1);
+}
+
+int avc_context_to_sid(const security_context_t ctx, security_id_t * sid)
+{
+	int rc;
+	avc_get_lock(avc_lock);
+	rc = sidtab_context_to_sid(&avc_sidtab, ctx, sid);
+	avc_release_lock(avc_lock);
+	return rc;
+}
+
+int avc_sid_to_context(security_id_t sid, security_context_t * ctx)
+{
+	int rc;
+	*ctx = NULL;
+	avc_get_lock(avc_lock);
+	*ctx = strdup(sid->ctx);	/* caller must free via freecon */
+	rc = *ctx ? 0 : -1;
+	avc_release_lock(avc_lock);
+	return rc;
+}
+
+int avc_get_initial_sid(const char * name, security_id_t * sid)
+{
+	int rc;
+	security_context_t con;
+
+	rc = security_get_initial_context(name, &con);
+	if (rc < 0)
+		return rc;
+	rc = avc_context_to_sid(con, sid);
+
+	freecon(con);
+
+	return rc;
+}
+
+int avc_open(struct selinux_opt *opts, unsigned nopts)
+{
+	avc_setenforce = 0;
+
+	while (nopts--)
+		switch(opts[nopts].type) {
+		case AVC_OPT_SETENFORCE:
+			avc_setenforce = 1;
+			avc_enforcing = !!opts[nopts].value;
+			break;
+		}
+
+	return avc_init("avc", NULL, NULL, NULL, NULL);
+}
+
+int avc_init(const char *prefix,
+	     const struct avc_memory_callback *mem_cb,
+	     const struct avc_log_callback *log_cb,
+	     const struct avc_thread_callback *thread_cb,
+	     const struct avc_lock_callback *lock_cb)
+{
+	struct avc_node *new;
+	int i, rc = 0;
+
+	if (prefix)
+		strncpy(avc_prefix, prefix, AVC_PREFIX_SIZE - 1);
+
+	set_callbacks(mem_cb, log_cb, thread_cb, lock_cb);
+
+	avc_lock = avc_alloc_lock();
+	avc_log_lock = avc_alloc_lock();
+
+	memset(&cache_stats, 0, sizeof(cache_stats));
+
+	for (i = 0; i < AVC_CACHE_SLOTS; i++)
+		avc_cache.slots[i] = 0;
+	avc_cache.lru_hint = 0;
+	avc_cache.active_nodes = 0;
+	avc_cache.latest_notif = 0;
+
+	rc = sidtab_init(&avc_sidtab);
+	if (rc) {
+		avc_log(SELINUX_ERROR,
+			"%s:  unable to initialize SID table\n",
+			avc_prefix);
+		goto out;
+	}
+
+	avc_audit_buf = (char *)avc_malloc(AVC_AUDIT_BUFSIZE);
+	if (!avc_audit_buf) {
+		avc_log(SELINUX_ERROR,
+			"%s:  unable to allocate audit buffer\n",
+			avc_prefix);
+		rc = -1;
+		goto out;
+	}
+
+	for (i = 0; i < AVC_CACHE_MAXNODES; i++) {
+		new = avc_malloc(sizeof(*new));
+		if (!new) {
+			avc_log(SELINUX_WARNING,
+				"%s:  warning: only got %d av entries\n",
+				avc_prefix, i);
+			break;
+		}
+		memset(new, 0, sizeof(*new));
+		new->next = avc_node_freelist;
+		avc_node_freelist = new;
+	}
+
+	if (!avc_setenforce) {
+		rc = security_getenforce();
+		if (rc < 0) {
+			avc_log(SELINUX_ERROR,
+				"%s:  could not determine enforcing mode: %s\n",
+				avc_prefix,
+				strerror(errno));
+			goto out;
+		}
+		avc_enforcing = rc;
+	}
+
+	rc = avc_netlink_open(0);
+	if (rc < 0) {
+		avc_log(SELINUX_ERROR,
+			"%s:  can't open netlink socket: %d (%s)\n",
+			avc_prefix, errno, strerror(errno));
+		goto out;
+	}
+	if (avc_using_threads) {
+		avc_netlink_thread = avc_create_thread(&avc_netlink_loop);
+		avc_netlink_trouble = 0;
+	}
+	avc_running = 1;
+      out:
+	return rc;
+}
+
+void avc_cache_stats(struct avc_cache_stats *p)
+{
+	memcpy(p, &cache_stats, sizeof(cache_stats));
+}
+
+void avc_sid_stats(void)
+{
+	avc_get_lock(avc_log_lock);
+	avc_get_lock(avc_lock);
+	sidtab_sid_stats(&avc_sidtab, avc_audit_buf, AVC_AUDIT_BUFSIZE);
+	avc_release_lock(avc_lock);
+	avc_log(SELINUX_INFO, "%s", avc_audit_buf);
+	avc_release_lock(avc_log_lock);
+}
+
+void avc_av_stats(void)
+{
+	int i, chain_len, max_chain_len, slots_used;
+	struct avc_node *node;
+
+	avc_get_lock(avc_lock);
+
+	slots_used = 0;
+	max_chain_len = 0;
+	for (i = 0; i < AVC_CACHE_SLOTS; i++) {
+		node = avc_cache.slots[i];
+		if (node) {
+			slots_used++;
+			chain_len = 0;
+			while (node) {
+				chain_len++;
+				node = node->next;
+			}
+			if (chain_len > max_chain_len)
+				max_chain_len = chain_len;
+		}
+	}
+
+	avc_release_lock(avc_lock);
+
+	avc_log(SELINUX_INFO, "%s:  %d AV entries and %d/%d buckets used, "
+		"longest chain length %d\n", avc_prefix,
+		avc_cache.active_nodes,
+		slots_used, AVC_CACHE_SLOTS, max_chain_len);
+}
+
+hidden_def(avc_av_stats)
+
+static inline struct avc_node *avc_reclaim_node(void)
+{
+	struct avc_node *prev, *cur;
+	int try;
+	uint32_t hvalue;
+
+	hvalue = avc_cache.lru_hint;
+	for (try = 0; try < 2; try++) {
+		do {
+			prev = NULL;
+			cur = avc_cache.slots[hvalue];
+			while (cur) {
+				if (!cur->ae.used)
+					goto found;
+
+				cur->ae.used = 0;
+
+				prev = cur;
+				cur = cur->next;
+			}
+			hvalue = (hvalue + 1) & (AVC_CACHE_SLOTS - 1);
+		} while (hvalue != avc_cache.lru_hint);
+	}
+
+	errno = ENOMEM;		/* this was a panic in the kernel... */
+	return NULL;
+
+      found:
+	avc_cache.lru_hint = hvalue;
+
+	if (prev == NULL)
+		avc_cache.slots[hvalue] = cur->next;
+	else
+		prev->next = cur->next;
+
+	return cur;
+}
+
+static inline void avc_clear_avc_entry(struct avc_entry *ae)
+{
+	ae->ssid = ae->tsid = ae->create_sid = NULL;
+	ae->tclass = 0;
+	ae->avd.allowed = ae->avd.decided = 0;
+	ae->avd.auditallow = ae->avd.auditdeny = 0;
+	ae->used = 0;
+}
+
+static inline struct avc_node *avc_claim_node(security_id_t ssid,
+					      security_id_t tsid,
+					      security_class_t tclass)
+{
+	struct avc_node *new;
+	int hvalue;
+
+	if (!avc_node_freelist)
+		avc_cleanup();
+
+	if (avc_node_freelist) {
+		new = avc_node_freelist;
+		avc_node_freelist = avc_node_freelist->next;
+		avc_cache.active_nodes++;
+	} else {
+		new = avc_reclaim_node();
+		if (!new)
+			goto out;
+	}
+
+	hvalue = avc_hash(ssid, tsid, tclass);
+	avc_clear_avc_entry(&new->ae);
+	new->ae.used = 1;
+	new->ae.ssid = ssid;
+	new->ae.tsid = tsid;
+	new->ae.tclass = tclass;
+	new->next = avc_cache.slots[hvalue];
+	avc_cache.slots[hvalue] = new;
+
+      out:
+	return new;
+}
+
+static inline struct avc_node *avc_search_node(security_id_t ssid,
+					       security_id_t tsid,
+					       security_class_t tclass,
+					       int *probes)
+{
+	struct avc_node *cur;
+	int hvalue;
+	int tprobes = 1;
+
+	hvalue = avc_hash(ssid, tsid, tclass);
+	cur = avc_cache.slots[hvalue];
+	while (cur != NULL &&
+	       (ssid != cur->ae.ssid ||
+		tclass != cur->ae.tclass || tsid != cur->ae.tsid)) {
+		tprobes++;
+		cur = cur->next;
+	}
+
+	if (cur == NULL) {
+		/* cache miss */
+		goto out;
+	}
+
+	/* cache hit */
+	if (probes)
+		*probes = tprobes;
+
+	cur->ae.used = 1;
+
+      out:
+	return cur;
+}
+
+/**
+ * avc_lookup - Look up an AVC entry.
+ * @ssid: source security identifier
+ * @tsid: target security identifier
+ * @tclass: target security class
+ * @requested: requested permissions, interpreted based on @tclass
+ * @aeref:  AVC entry reference
+ *
+ * Look up an AVC entry that is valid for the
+ * @requested permissions between the SID pair
+ * (@ssid, @tsid), interpreting the permissions
+ * based on @tclass.  If a valid AVC entry exists,
+ * then this function updates @aeref to refer to the
+ * entry and returns %0.  Otherwise, -1 is returned.
+ */
+static int avc_lookup(security_id_t ssid, security_id_t tsid,
+		      security_class_t tclass,
+		      access_vector_t requested, struct avc_entry_ref *aeref)
+{
+	struct avc_node *node;
+	int probes, rc = 0;
+
+	avc_cache_stats_incr(cav_lookups);
+	node = avc_search_node(ssid, tsid, tclass, &probes);
+
+	if (node && ((node->ae.avd.decided & requested) == requested)) {
+		avc_cache_stats_incr(cav_hits);
+		avc_cache_stats_add(cav_probes, probes);
+		aeref->ae = &node->ae;
+		goto out;
+	}
+
+	avc_cache_stats_incr(cav_misses);
+	rc = -1;
+      out:
+	return rc;
+}
+
+/**
+ * avc_insert - Insert an AVC entry.
+ * @ssid: source security identifier
+ * @tsid: target security identifier
+ * @tclass: target security class
+ * @ae: AVC entry
+ * @aeref:  AVC entry reference
+ *
+ * Insert an AVC entry for the SID pair
+ * (@ssid, @tsid) and class @tclass.
+ * The access vectors and the sequence number are
+ * normally provided by the security server in
+ * response to a security_compute_av() call.  If the
+ * sequence number @ae->avd.seqno is not less than the latest
+ * revocation notification, then the function copies
+ * the access vectors into a cache entry, updates
+ * @aeref to refer to the entry, and returns %0.
+ * Otherwise, this function returns -%1 with @errno set to %EAGAIN.
+ */
+static int avc_insert(security_id_t ssid, security_id_t tsid,
+		      security_class_t tclass,
+		      struct avc_entry *ae, struct avc_entry_ref *aeref)
+{
+	struct avc_node *node;
+	int rc = 0;
+
+	if (ae->avd.seqno < avc_cache.latest_notif) {
+		avc_log(SELINUX_WARNING,
+			"%s:  seqno %d < latest_notif %d\n", avc_prefix,
+			ae->avd.seqno, avc_cache.latest_notif);
+		errno = EAGAIN;
+		rc = -1;
+		goto out;
+	}
+
+	node = avc_claim_node(ssid, tsid, tclass);
+	if (!node) {
+		rc = -1;
+		goto out;
+	}
+
+	node->ae.avd.allowed = ae->avd.allowed;
+	node->ae.avd.decided = ae->avd.decided;
+	node->ae.avd.auditallow = ae->avd.auditallow;
+	node->ae.avd.auditdeny = ae->avd.auditdeny;
+	node->ae.avd.seqno = ae->avd.seqno;
+	aeref->ae = &node->ae;
+      out:
+	return rc;
+}
+
+void avc_cleanup(void)
+{
+}
+
+hidden_def(avc_cleanup)
+
+int avc_reset(void)
+{
+	struct avc_callback_node *c;
+	int i, ret, rc = 0, errsave = 0;
+	struct avc_node *node, *tmp;
+	errno = 0;
+
+	if (!avc_running)
+		return 0;
+
+	avc_get_lock(avc_lock);
+
+	for (i = 0; i < AVC_CACHE_SLOTS; i++) {
+		node = avc_cache.slots[i];
+		while (node) {
+			tmp = node;
+			node = node->next;
+			avc_clear_avc_entry(&tmp->ae);
+			tmp->next = avc_node_freelist;
+			avc_node_freelist = tmp;
+			avc_cache.active_nodes--;
+		}
+		avc_cache.slots[i] = 0;
+	}
+	avc_cache.lru_hint = 0;
+
+	avc_release_lock(avc_lock);
+
+	memset(&cache_stats, 0, sizeof(cache_stats));
+
+	for (c = avc_callbacks; c; c = c->next) {
+		if (c->events & AVC_CALLBACK_RESET) {
+			ret = c->callback(AVC_CALLBACK_RESET, 0, 0, 0, 0, 0);
+			if (ret && !rc) {
+				rc = ret;
+				errsave = errno;
+			}
+		}
+	}
+	errno = errsave;
+	return rc;
+}
+
+hidden_def(avc_reset)
+
+void avc_destroy(void)
+{
+	struct avc_callback_node *c;
+	struct avc_node *node, *tmp;
+	int i;
+
+	avc_get_lock(avc_lock);
+
+	if (avc_using_threads)
+		avc_stop_thread(avc_netlink_thread);
+	avc_netlink_close();
+
+	for (i = 0; i < AVC_CACHE_SLOTS; i++) {
+		node = avc_cache.slots[i];
+		while (node) {
+			tmp = node;
+			node = node->next;
+			avc_free(tmp);
+		}
+	}
+	while (avc_node_freelist) {
+		tmp = avc_node_freelist;
+		avc_node_freelist = tmp->next;
+		avc_free(tmp);
+	}
+	avc_release_lock(avc_lock);
+
+	while (avc_callbacks) {
+		c = avc_callbacks;
+		avc_callbacks = c->next;
+		avc_free(c);
+	}
+	sidtab_destroy(&avc_sidtab);
+	avc_free_lock(avc_lock);
+	avc_free_lock(avc_log_lock);
+	avc_free(avc_audit_buf);
+	avc_running = 0;
+}
+
+/* ratelimit stuff put aside for now --EFW */
+#if 0
+/*
+ * Copied from net/core/utils.c:net_ratelimit and modified for
+ * use by the AVC audit facility.
+ */
+#define AVC_MSG_COST	5*HZ
+#define AVC_MSG_BURST	10*5*HZ
+
+/*
+ * This enforces a rate limit: not more than one kernel message
+ * every 5secs to make a denial-of-service attack impossible.
+ */
+static int avc_ratelimit(void)
+{
+	static unsigned long toks = 10 * 5 * HZ;
+	static unsigned long last_msg;
+	static int missed, rc = 0;
+	unsigned long now = jiffies;
+	void *ratelimit_lock = avc_alloc_lock();
+
+	avc_get_lock(ratelimit_lock);
+	toks += now - last_msg;
+	last_msg = now;
+	if (toks > AVC_MSG_BURST)
+		toks = AVC_MSG_BURST;
+	if (toks >= AVC_MSG_COST) {
+		int lost = missed;
+		missed = 0;
+		toks -= AVC_MSG_COST;
+		avc_release_lock(ratelimit_lock);
+		if (lost) {
+			avc_log(SELINUX_WARNING,
+				"%s:  %d messages suppressed.\n", avc_prefix,
+				lost);
+		}
+		rc = 1;
+		goto out;
+	}
+	missed++;
+	avc_release_lock(ratelimit_lock);
+      out:
+	avc_free_lock(ratelimit_lock);
+	return rc;
+}
+
+static inline int check_avc_ratelimit(void)
+{
+	if (avc_enforcing)
+		return avc_ratelimit();
+	else {
+		/* If permissive, then never suppress messages. */
+		return 1;
+	}
+}
+#endif				/* ratelimit stuff */
+
+/**
+ * avc_dump_av - Display an access vector in human-readable form.
+ * @tclass: target security class
+ * @av: access vector
+ */
+static void avc_dump_av(security_class_t tclass, access_vector_t av)
+{
+	const char *permstr;
+	access_vector_t bit = 1;
+
+	if (av == 0) {
+		log_append(avc_audit_buf, " null");
+		return;
+	}
+
+	log_append(avc_audit_buf, " {");
+
+	while (av) {
+		if (av & bit) {
+			permstr = security_av_perm_to_string(tclass, bit);
+			if (!permstr)
+				break;
+			log_append(avc_audit_buf, " %s", permstr);
+			av &= ~bit;
+		}
+		bit <<= 1;
+	}
+
+	if (av)
+		log_append(avc_audit_buf, " 0x%x", av);
+	log_append(avc_audit_buf, " }");
+}
+
+/**
+ * avc_dump_query - Display a SID pair and a class in human-readable form.
+ * @ssid: source security identifier
+ * @tsid: target security identifier
+ * @tclass: target security class
+ */
+static void avc_dump_query(security_id_t ssid, security_id_t tsid,
+			   security_class_t tclass)
+{
+	avc_get_lock(avc_lock);
+
+	log_append(avc_audit_buf, "scontext=%s tcontext=%s",
+		   ssid->ctx, tsid->ctx);
+
+	avc_release_lock(avc_lock);
+	log_append(avc_audit_buf, " tclass=%s",
+		   security_class_to_string(tclass));
+}
+
+void avc_audit(security_id_t ssid, security_id_t tsid,
+	       security_class_t tclass, access_vector_t requested,
+	       struct av_decision *avd, int result, void *a)
+{
+	access_vector_t denied, audited;
+
+	denied = requested & ~avd->allowed;
+	if (denied)
+		audited = denied & avd->auditdeny;
+	else if (!requested || result)
+		audited = denied = requested;
+	else
+		audited = requested & avd->auditallow;
+	if (!audited)
+		return;
+#if 0
+	if (!check_avc_ratelimit())
+		return;
+#endif
+	/* prevent overlapping buffer writes */
+	avc_get_lock(avc_log_lock);
+	snprintf(avc_audit_buf, AVC_AUDIT_BUFSIZE,
+		 "%s:  %s ", avc_prefix, (denied || !requested) ? "denied" : "granted");
+	avc_dump_av(tclass, audited);
+	log_append(avc_audit_buf, " for ");
+
+	/* get any extra information printed by the callback */
+	avc_suppl_audit(a, tclass, avc_audit_buf + strlen(avc_audit_buf),
+			AVC_AUDIT_BUFSIZE - strlen(avc_audit_buf));
+
+	log_append(avc_audit_buf, " ");
+	avc_dump_query(ssid, tsid, tclass);
+	log_append(avc_audit_buf, "\n");
+	avc_log(SELINUX_AVC, "%s", avc_audit_buf);
+
+	avc_release_lock(avc_log_lock);
+}
+
+hidden_def(avc_audit)
+
+int avc_has_perm_noaudit(security_id_t ssid,
+			 security_id_t tsid,
+			 security_class_t tclass,
+			 access_vector_t requested,
+			 struct avc_entry_ref *aeref, struct av_decision *avd)
+{
+	struct avc_entry *ae;
+	int rc = 0;
+	struct avc_entry entry;
+	access_vector_t denied;
+	struct avc_entry_ref ref;
+
+	if (!avc_using_threads && !avc_app_main_loop) {
+		(void)avc_netlink_check_nb();
+	}
+
+	if (!aeref) {
+		avc_entry_ref_init(&ref);
+		aeref = &ref;
+	}
+
+	avc_get_lock(avc_lock);
+	avc_cache_stats_incr(entry_lookups);
+	ae = aeref->ae;
+	if (ae) {
+		if (ae->ssid == ssid &&
+		    ae->tsid == tsid &&
+		    ae->tclass == tclass &&
+		    ((ae->avd.decided & requested) == requested)) {
+			avc_cache_stats_incr(entry_hits);
+			ae->used = 1;
+		} else {
+			avc_cache_stats_incr(entry_discards);
+			ae = 0;
+		}
+	}
+
+	if (!ae) {
+		avc_cache_stats_incr(entry_misses);
+		rc = avc_lookup(ssid, tsid, tclass, requested, aeref);
+		if (rc) {
+			rc = security_compute_av(ssid->ctx, tsid->ctx,
+						 tclass, requested,
+						 &entry.avd);
+			if (rc)
+				goto out;
+			rc = avc_insert(ssid, tsid, tclass, &entry, aeref);
+			if (rc)
+				goto out;
+		}
+		ae = aeref->ae;
+	}
+
+	if (avd)
+		memcpy(avd, &ae->avd, sizeof(*avd));
+
+	denied = requested & ~(ae->avd.allowed);
+
+	if (!requested || denied) {
+		if (!avc_enforcing ||
+		    (ae->avd.flags & SELINUX_AVD_FLAGS_PERMISSIVE))
+			ae->avd.allowed |= requested;
+		else {
+			errno = EACCES;
+			rc = -1;
+		}
+	}
+
+      out:
+	avc_release_lock(avc_lock);
+	return rc;
+}
+
+hidden_def(avc_has_perm_noaudit)
+
+int avc_has_perm(security_id_t ssid, security_id_t tsid,
+		 security_class_t tclass, access_vector_t requested,
+		 struct avc_entry_ref *aeref, void *auditdata)
+{
+	struct av_decision avd;
+	int errsave, rc;
+
+	memset(&avd, 0, sizeof(avd));
+
+	rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, aeref, &avd);
+	errsave = errno;
+	avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata);
+	errno = errsave;
+	return rc;
+}
+
+int avc_compute_create(security_id_t ssid,  security_id_t tsid,
+		       security_class_t tclass, security_id_t *newsid)
+{
+	int rc;
+	struct avc_entry_ref aeref;
+	struct avc_entry entry;
+	security_context_t ctx;
+
+	*newsid = NULL;
+	avc_entry_ref_init(&aeref);
+
+	avc_get_lock(avc_lock);
+
+	/* check for a cached entry */
+	rc = avc_lookup(ssid, tsid, tclass, 0, &aeref);
+	if (rc) {
+		/* need to make a cache entry for this tuple */
+		rc = security_compute_av(ssid->ctx, tsid->ctx,
+					 tclass, 0, &entry.avd);
+		if (rc)
+			goto out;
+		rc = avc_insert(ssid, tsid, tclass, &entry, &aeref);
+		if (rc)
+			goto out;
+	}
+
+	/* check for a saved compute_create value */
+	if (!aeref.ae->create_sid) {
+		/* need to query the kernel policy */
+		rc = security_compute_create(ssid->ctx, tsid->ctx, tclass,
+						 &ctx);
+		if (rc)
+			goto out;
+		rc = sidtab_context_to_sid(&avc_sidtab, ctx, newsid);
+		freecon(ctx);
+		if (rc)
+			goto out;
+
+		aeref.ae->create_sid = *newsid;
+	} else {
+		/* found saved value */
+		*newsid = aeref.ae->create_sid;
+	}
+
+	rc = 0;
+out:
+	avc_release_lock(avc_lock);
+	return rc;
+}
+
+int avc_add_callback(int (*callback) (uint32_t event, security_id_t ssid,
+				      security_id_t tsid,
+				      security_class_t tclass,
+				      access_vector_t perms,
+				      access_vector_t * out_retained),
+		     uint32_t events, security_id_t ssid,
+		     security_id_t tsid,
+		     security_class_t tclass, access_vector_t perms)
+{
+	struct avc_callback_node *c;
+	int rc = 0;
+
+	c = avc_malloc(sizeof(*c));
+	if (!c) {
+		rc = -1;
+		goto out;
+	}
+
+	c->callback = callback;
+	c->events = events;
+	c->ssid = ssid;
+	c->tsid = tsid;
+	c->tclass = tclass;
+	c->perms = perms;
+	c->next = avc_callbacks;
+	avc_callbacks = c;
+      out:
+	return rc;
+}
+
+static inline int avc_sidcmp(security_id_t x, security_id_t y)
+{
+	return (x == y || x == SECSID_WILD || y == SECSID_WILD);
+}
+
+static inline void avc_update_node(uint32_t event, struct avc_node *node,
+				   access_vector_t perms)
+{
+	switch (event) {
+	case AVC_CALLBACK_GRANT:
+		node->ae.avd.allowed |= perms;
+		break;
+	case AVC_CALLBACK_TRY_REVOKE:
+	case AVC_CALLBACK_REVOKE:
+		node->ae.avd.allowed &= ~perms;
+		break;
+	case AVC_CALLBACK_AUDITALLOW_ENABLE:
+		node->ae.avd.auditallow |= perms;
+		break;
+	case AVC_CALLBACK_AUDITALLOW_DISABLE:
+		node->ae.avd.auditallow &= ~perms;
+		break;
+	case AVC_CALLBACK_AUDITDENY_ENABLE:
+		node->ae.avd.auditdeny |= perms;
+		break;
+	case AVC_CALLBACK_AUDITDENY_DISABLE:
+		node->ae.avd.auditdeny &= ~perms;
+		break;
+	}
+}
+
+static int avc_update_cache(uint32_t event, security_id_t ssid,
+			    security_id_t tsid, security_class_t tclass,
+			    access_vector_t perms)
+{
+	struct avc_node *node;
+	int i;
+
+	avc_get_lock(avc_lock);
+
+	if (ssid == SECSID_WILD || tsid == SECSID_WILD) {
+		/* apply to all matching nodes */
+		for (i = 0; i < AVC_CACHE_SLOTS; i++) {
+			for (node = avc_cache.slots[i]; node; node = node->next) {
+				if (avc_sidcmp(ssid, node->ae.ssid) &&
+				    avc_sidcmp(tsid, node->ae.tsid) &&
+				    tclass == node->ae.tclass) {
+					avc_update_node(event, node, perms);
+				}
+			}
+		}
+	} else {
+		/* apply to one node */
+		node = avc_search_node(ssid, tsid, tclass, 0);
+		if (node) {
+			avc_update_node(event, node, perms);
+		}
+	}
+
+	avc_release_lock(avc_lock);
+
+	return 0;
+}
+
+/* avc_control - update cache and call callbacks
+ *
+ * This should not be called directly; use the individual event
+ * functions instead.
+ */
+static int avc_control(uint32_t event, security_id_t ssid,
+		       security_id_t tsid, security_class_t tclass,
+		       access_vector_t perms,
+		       uint32_t seqno, access_vector_t * out_retained)
+{
+	struct avc_callback_node *c;
+	access_vector_t tretained = 0, cretained = 0;
+	int ret, rc = 0, errsave = 0;
+	errno = 0;
+
+	/*
+	 * try_revoke only removes permissions from the cache
+	 * state if they are not retained by the object manager.
+	 * Hence, try_revoke must wait until after the callbacks have
+	 * been invoked to update the cache state.
+	 */
+	if (event != AVC_CALLBACK_TRY_REVOKE)
+		avc_update_cache(event, ssid, tsid, tclass, perms);
+
+	for (c = avc_callbacks; c; c = c->next) {
+		if ((c->events & event) &&
+		    avc_sidcmp(c->ssid, ssid) &&
+		    avc_sidcmp(c->tsid, tsid) &&
+		    c->tclass == tclass && (c->perms & perms)) {
+			cretained = 0;
+			ret = c->callback(event, ssid, tsid, tclass,
+					  (c->perms & perms), &cretained);
+			if (ret && !rc) {
+				rc = ret;
+				errsave = errno;
+			}
+			if (!ret)
+				tretained |= cretained;
+		}
+	}
+
+	if (event == AVC_CALLBACK_TRY_REVOKE) {
+		/* revoke any unretained permissions */
+		perms &= ~tretained;
+		avc_update_cache(event, ssid, tsid, tclass, perms);
+		*out_retained = tretained;
+	}
+
+	avc_get_lock(avc_lock);
+	if (seqno > avc_cache.latest_notif)
+		avc_cache.latest_notif = seqno;
+	avc_release_lock(avc_lock);
+
+	errno = errsave;
+	return rc;
+}
+
+/**
+ * avc_ss_grant - Grant previously denied permissions.
+ * @ssid: source security identifier or %SECSID_WILD
+ * @tsid: target security identifier or %SECSID_WILD
+ * @tclass: target security class
+ * @perms: permissions to grant
+ * @seqno: policy sequence number
+ */
+int avc_ss_grant(security_id_t ssid, security_id_t tsid,
+		 security_class_t tclass, access_vector_t perms,
+		 uint32_t seqno)
+{
+	return avc_control(AVC_CALLBACK_GRANT,
+			   ssid, tsid, tclass, perms, seqno, 0);
+}
+
+/**
+ * avc_ss_try_revoke - Try to revoke previously granted permissions.
+ * @ssid: source security identifier or %SECSID_WILD
+ * @tsid: target security identifier or %SECSID_WILD
+ * @tclass: target security class
+ * @perms: permissions to grant
+ * @seqno: policy sequence number
+ * @out_retained: subset of @perms that are retained
+ *
+ * Try to revoke previously granted permissions, but
+ * only if they are not retained as migrated permissions.
+ * Return the subset of permissions that are retained via @out_retained.
+ */
+int avc_ss_try_revoke(security_id_t ssid, security_id_t tsid,
+		      security_class_t tclass,
+		      access_vector_t perms, uint32_t seqno,
+		      access_vector_t * out_retained)
+{
+	return avc_control(AVC_CALLBACK_TRY_REVOKE,
+			   ssid, tsid, tclass, perms, seqno, out_retained);
+}
+
+/**
+ * avc_ss_revoke - Revoke previously granted permissions.
+ * @ssid: source security identifier or %SECSID_WILD
+ * @tsid: target security identifier or %SECSID_WILD
+ * @tclass: target security class
+ * @perms: permissions to grant
+ * @seqno: policy sequence number
+ *
+ * Revoke previously granted permissions, even if
+ * they are retained as migrated permissions.
+ */
+int avc_ss_revoke(security_id_t ssid, security_id_t tsid,
+		  security_class_t tclass, access_vector_t perms,
+		  uint32_t seqno)
+{
+	return avc_control(AVC_CALLBACK_REVOKE,
+			   ssid, tsid, tclass, perms, seqno, 0);
+}
+
+/**
+ * avc_ss_reset - Flush the cache and revalidate migrated permissions.
+ * @seqno: policy sequence number
+ */
+int avc_ss_reset(uint32_t seqno)
+{
+	int rc;
+
+	rc = avc_reset();
+
+	avc_get_lock(avc_lock);
+	if (seqno > avc_cache.latest_notif)
+		avc_cache.latest_notif = seqno;
+	avc_release_lock(avc_lock);
+
+	return rc;
+}
+
+/**
+ * avc_ss_set_auditallow - Enable or disable auditing of granted permissions.
+ * @ssid: source security identifier or %SECSID_WILD
+ * @tsid: target security identifier or %SECSID_WILD
+ * @tclass: target security class
+ * @perms: permissions to grant
+ * @seqno: policy sequence number
+ * @enable: enable flag.
+ */
+int avc_ss_set_auditallow(security_id_t ssid, security_id_t tsid,
+			  security_class_t tclass, access_vector_t perms,
+			  uint32_t seqno, uint32_t enable)
+{
+	if (enable)
+		return avc_control(AVC_CALLBACK_AUDITALLOW_ENABLE,
+				   ssid, tsid, tclass, perms, seqno, 0);
+	else
+		return avc_control(AVC_CALLBACK_AUDITALLOW_DISABLE,
+				   ssid, tsid, tclass, perms, seqno, 0);
+}
+
+/**
+ * avc_ss_set_auditdeny - Enable or disable auditing of denied permissions.
+ * @ssid: source security identifier or %SECSID_WILD
+ * @tsid: target security identifier or %SECSID_WILD
+ * @tclass: target security class
+ * @perms: permissions to grant
+ * @seqno: policy sequence number
+ * @enable: enable flag.
+ */
+int avc_ss_set_auditdeny(security_id_t ssid, security_id_t tsid,
+			 security_class_t tclass, access_vector_t perms,
+			 uint32_t seqno, uint32_t enable)
+{
+	if (enable)
+		return avc_control(AVC_CALLBACK_AUDITDENY_ENABLE,
+				   ssid, tsid, tclass, perms, seqno, 0);
+	else
+		return avc_control(AVC_CALLBACK_AUDITDENY_DISABLE,
+				   ssid, tsid, tclass, perms, seqno, 0);
+}
diff --git a/src/avc_internal.c b/src/avc_internal.c
new file mode 100644
index 0000000..be4c0a3
--- /dev/null
+++ b/src/avc_internal.c
@@ -0,0 +1,286 @@
+/*
+ * Callbacks for user-supplied memory allocation, supplemental
+ * auditing, and locking routines.
+ *
+ * Author : Eamon Walsh <ewalsh@epoch.ncsc.mil>
+ *
+ * Netlink code derived in part from sample code by
+ * James Morris <jmorris@redhat.com>.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <poll.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <linux/types.h>
+#include <linux/netlink.h>
+#include "callbacks.h"
+#include "selinux_netlink.h"
+#include "avc_internal.h"
+
+#ifndef NETLINK_SELINUX
+#define NETLINK_SELINUX 7
+#endif
+
+/* callback pointers */
+void *(*avc_func_malloc) (size_t) = NULL;
+void (*avc_func_free) (void *) = NULL;
+
+void (*avc_func_log) (const char *, ...) = NULL;
+void (*avc_func_audit) (void *, security_class_t, char *, size_t) = NULL;
+
+int avc_using_threads = 0;
+int avc_app_main_loop = 0;
+void *(*avc_func_create_thread) (void (*)(void)) = NULL;
+void (*avc_func_stop_thread) (void *) = NULL;
+
+void *(*avc_func_alloc_lock) (void) = NULL;
+void (*avc_func_get_lock) (void *) = NULL;
+void (*avc_func_release_lock) (void *) = NULL;
+void (*avc_func_free_lock) (void *) = NULL;
+
+/* message prefix string and avc enforcing mode */
+char avc_prefix[AVC_PREFIX_SIZE] = "uavc";
+int avc_running = 0;
+int avc_enforcing = 1;
+int avc_setenforce = 0;
+int avc_netlink_trouble = 0;
+
+/* netlink socket code */
+static int fd;
+
+int avc_netlink_open(int blocking)
+{
+	int len, rc = 0;
+	struct sockaddr_nl addr;
+
+	fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_SELINUX);
+	if (fd < 0) {
+		rc = fd;
+		goto out;
+	}
+	
+	fcntl(fd, F_SETFD, FD_CLOEXEC);
+	if (!blocking && fcntl(fd, F_SETFL, O_NONBLOCK)) {
+		close(fd);
+		rc = -1;
+		goto out;
+	}
+
+	len = sizeof(addr);
+
+	memset(&addr, 0, len);
+	addr.nl_family = AF_NETLINK;
+	addr.nl_groups = SELNL_GRP_AVC;
+
+	if (bind(fd, (struct sockaddr *)&addr, len) < 0) {
+		close(fd);
+		rc = -1;
+		goto out;
+	}
+      out:
+	return rc;
+}
+
+void avc_netlink_close(void)
+{
+	close(fd);
+}
+
+static int avc_netlink_receive(char *buf, unsigned buflen, int blocking)
+{
+	int rc;
+	struct pollfd pfd = { fd, POLLIN | POLLPRI, 0 };
+	struct sockaddr_nl nladdr;
+	socklen_t nladdrlen = sizeof nladdr;
+	struct nlmsghdr *nlh = (struct nlmsghdr *)buf;
+
+	rc = poll(&pfd, 1, (blocking ? -1 : 0));
+
+	if (rc == 0 && !blocking) {
+		errno = EWOULDBLOCK;
+		return -1;
+	}
+	else if (rc < 1) {
+		avc_log(SELINUX_ERROR, "%s:  netlink poll: error %d\n",
+			avc_prefix, errno);
+		return rc;
+	}
+
+	rc = recvfrom(fd, buf, buflen, 0, (struct sockaddr *)&nladdr,
+		      &nladdrlen);
+	if (rc < 0)
+		return rc;
+
+	if (nladdrlen != sizeof nladdr) {
+		avc_log(SELINUX_WARNING,
+			"%s:  warning: netlink address truncated, len %d?\n",
+			avc_prefix, nladdrlen);
+		return -1;
+	}
+
+	if (nladdr.nl_pid) {
+		avc_log(SELINUX_WARNING,
+			"%s:  warning: received spoofed netlink packet from: %d\n",
+			avc_prefix, nladdr.nl_pid);
+		return -1;
+	}
+
+	if (rc == 0) {
+		avc_log(SELINUX_WARNING,
+			"%s:  warning: received EOF on netlink socket\n",
+			avc_prefix);
+		errno = EBADFD;
+		return -1;
+	}
+
+	if (nlh->nlmsg_flags & MSG_TRUNC || nlh->nlmsg_len > (unsigned)rc) {
+		avc_log(SELINUX_WARNING,
+			"%s:  warning: incomplete netlink message\n",
+			avc_prefix);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int avc_netlink_process(char *buf)
+{
+	int rc;
+	struct nlmsghdr *nlh = (struct nlmsghdr *)buf;
+
+	switch (nlh->nlmsg_type) {
+	case NLMSG_ERROR:{
+		struct nlmsgerr *err = NLMSG_DATA(nlh);
+
+		/* Netlink ack */
+		if (err->error == 0)
+			break;
+
+		errno = -err->error;
+		avc_log(SELINUX_ERROR,
+			"%s:  netlink error: %d\n", avc_prefix, errno);
+		return -1;
+	}
+
+	case SELNL_MSG_SETENFORCE:{
+		struct selnl_msg_setenforce *msg = NLMSG_DATA(nlh);
+		avc_log(SELINUX_INFO,
+			"%s:  received setenforce notice (enforcing=%d)\n",
+			avc_prefix, msg->val);
+		if (avc_setenforce)
+			break;
+		avc_enforcing = msg->val;
+		if (avc_enforcing && (rc = avc_ss_reset(0)) < 0) {
+			avc_log(SELINUX_ERROR,
+				"%s:  cache reset returned %d (errno %d)\n",
+				avc_prefix, rc, errno);
+			return rc;
+		}
+		rc = selinux_netlink_setenforce(msg->val);
+		if (rc < 0)
+			return rc;
+		break;
+	}
+
+	case SELNL_MSG_POLICYLOAD:{
+		struct selnl_msg_policyload *msg = NLMSG_DATA(nlh);
+		avc_log(SELINUX_INFO,
+			"%s:  received policyload notice (seqno=%d)\n",
+			avc_prefix, msg->seqno);
+		rc = avc_ss_reset(msg->seqno);
+		if (rc < 0) {
+			avc_log(SELINUX_ERROR,
+				"%s:  cache reset returned %d (errno %d)\n",
+				avc_prefix, rc, errno);
+			return rc;
+		}
+		rc = selinux_netlink_policyload(msg->seqno);
+		if (rc < 0)
+			return rc;
+		break;
+	}
+
+	default:
+		avc_log(SELINUX_WARNING,
+			"%s:  warning: unknown netlink message %d\n",
+			avc_prefix, nlh->nlmsg_type);
+	}
+	return 0;
+}
+
+int avc_netlink_check_nb(void)
+{
+	int rc;
+	char buf[1024] __attribute__ ((aligned));
+
+	while (1) {
+		errno = 0;
+		rc = avc_netlink_receive(buf, sizeof(buf), 0);
+		if (rc < 0) {
+			if (errno == EWOULDBLOCK)
+				return 0;
+			if (errno == 0 || errno == EINTR)
+				continue;
+			else {
+				avc_log(SELINUX_ERROR,
+					"%s:  netlink recvfrom: error %d\n",
+					avc_prefix, errno);
+				return rc;
+			}
+		}
+
+		(void)avc_netlink_process(buf);
+	}
+	return 0;
+}
+
+/* run routine for the netlink listening thread */
+void avc_netlink_loop(void)
+{
+	int rc;
+	char buf[1024] __attribute__ ((aligned));
+
+	while (1) {
+		errno = 0;
+		rc = avc_netlink_receive(buf, sizeof(buf), 1);
+		if (rc < 0) {
+			if (errno == 0 || errno == EINTR)
+				continue;
+			else {
+				avc_log(SELINUX_ERROR,
+					"%s:  netlink recvfrom: error %d\n",
+					avc_prefix, errno);
+				break;
+			}
+		}
+
+		rc = avc_netlink_process(buf);
+		if (rc < 0)
+			break;
+	}
+
+	close(fd);
+	avc_netlink_trouble = 1;
+	avc_log(SELINUX_ERROR,
+		"%s:  netlink thread: errors encountered, terminating\n",
+		avc_prefix);
+}
+
+int avc_netlink_acquire_fd(void)
+{
+    avc_app_main_loop = 1;
+
+    return fd;
+}
+
+void avc_netlink_release_fd(void)
+{
+    avc_app_main_loop = 0;
+}
diff --git a/src/avc_internal.h b/src/avc_internal.h
new file mode 100644
index 0000000..53610e8
--- /dev/null
+++ b/src/avc_internal.h
@@ -0,0 +1,182 @@
+/*
+ * This file describes the internal interface used by the AVC
+ * for calling the user-supplied memory allocation, supplemental
+ * auditing, and locking routine, as well as incrementing the
+ * statistics fields.
+ *
+ * Author : Eamon Walsh <ewalsh@epoch.ncsc.mil>
+ */
+#ifndef _SELINUX_AVC_INTERNAL_H_
+#define _SELINUX_AVC_INTERNAL_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <selinux/avc.h>
+#include "callbacks.h"
+#include "dso.h"
+
+/* callback pointers */
+extern void *(*avc_func_malloc) (size_t) hidden;
+extern void (*avc_func_free) (void *)hidden;
+
+extern void (*avc_func_log) (const char *, ...)hidden;
+extern void (*avc_func_audit) (void *, security_class_t, char *, size_t)hidden;
+
+extern int avc_using_threads hidden;
+extern int avc_app_main_loop hidden;
+extern void *(*avc_func_create_thread) (void (*)(void))hidden;
+extern void (*avc_func_stop_thread) (void *)hidden;
+
+extern void *(*avc_func_alloc_lock) (void)hidden;
+extern void (*avc_func_get_lock) (void *)hidden;
+extern void (*avc_func_release_lock) (void *)hidden;
+extern void (*avc_func_free_lock) (void *)hidden;
+
+static inline void set_callbacks(const struct avc_memory_callback *mem_cb,
+				 const struct avc_log_callback *log_cb,
+				 const struct avc_thread_callback *thread_cb,
+				 const struct avc_lock_callback *lock_cb)
+{
+	if (mem_cb) {
+		avc_func_malloc = mem_cb->func_malloc;
+		avc_func_free = mem_cb->func_free;
+	}
+	if (log_cb) {
+		avc_func_log = log_cb->func_log;
+		avc_func_audit = log_cb->func_audit;
+	}
+	if (thread_cb) {
+		avc_using_threads = 1;
+		avc_func_create_thread = thread_cb->func_create_thread;
+		avc_func_stop_thread = thread_cb->func_stop_thread;
+	}
+	if (lock_cb) {
+		avc_func_alloc_lock = lock_cb->func_alloc_lock;
+		avc_func_get_lock = lock_cb->func_get_lock;
+		avc_func_release_lock = lock_cb->func_release_lock;
+		avc_func_free_lock = lock_cb->func_free_lock;
+	}
+}
+
+/* message prefix and enforcing mode*/
+#define AVC_PREFIX_SIZE 16
+extern char avc_prefix[AVC_PREFIX_SIZE] hidden;
+extern int avc_running hidden;
+extern int avc_enforcing hidden;
+extern int avc_setenforce hidden;
+
+/* user-supplied callback interface for avc */
+static inline void *avc_malloc(size_t size)
+{
+	return avc_func_malloc ? avc_func_malloc(size) : malloc(size);
+}
+
+static inline void avc_free(void *ptr)
+{
+	if (avc_func_free)
+		avc_func_free(ptr);
+	else
+		free(ptr);
+}
+
+/* this is a macro in order to use the variadic capability. */
+#define avc_log(type, format...) \
+  if (avc_func_log) \
+    avc_func_log(format); \
+  else \
+    selinux_log(type, format);
+
+static inline void avc_suppl_audit(void *ptr, security_class_t class,
+				   char *buf, size_t len)
+{
+	if (avc_func_audit)
+		avc_func_audit(ptr, class, buf, len);
+	else
+		selinux_audit(ptr, class, buf, len);
+}
+
+static inline void *avc_create_thread(void (*run) (void))
+{
+	return avc_func_create_thread ? avc_func_create_thread(run) : NULL;
+}
+
+static inline void avc_stop_thread(void *thread)
+{
+	if (avc_func_stop_thread)
+		avc_func_stop_thread(thread);
+}
+
+static inline void *avc_alloc_lock(void)
+{
+	return avc_func_alloc_lock ? avc_func_alloc_lock() : NULL;
+}
+
+static inline void avc_get_lock(void *lock)
+{
+	if (avc_func_get_lock)
+		avc_func_get_lock(lock);
+}
+
+static inline void avc_release_lock(void *lock)
+{
+	if (avc_func_release_lock)
+		avc_func_release_lock(lock);
+}
+
+static inline void avc_free_lock(void *lock)
+{
+	if (avc_func_free_lock)
+		avc_func_free_lock(lock);
+}
+
+/* statistics helper routines */
+#ifdef AVC_CACHE_STATS
+
+#define avc_cache_stats_incr(field) \
+  cache_stats.field ++;
+#define avc_cache_stats_add(field, num) \
+  cache_stats.field += num;
+
+#else
+
+#define avc_cache_stats_incr(field)
+#define avc_cache_stats_add(field, num)
+
+#endif
+
+/* logging helper routines */
+#define AVC_AUDIT_BUFSIZE 1024
+
+/* again, we need the variadic capability here */
+#define log_append(buf,format...) \
+  snprintf(buf+strlen(buf), AVC_AUDIT_BUFSIZE-strlen(buf), format)
+
+/* internal callbacks */
+int avc_ss_grant(security_id_t ssid, security_id_t tsid,
+		 security_class_t tclass, access_vector_t perms,
+		 uint32_t seqno) hidden;
+int avc_ss_try_revoke(security_id_t ssid, security_id_t tsid,
+		      security_class_t tclass,
+		      access_vector_t perms, uint32_t seqno,
+		      access_vector_t * out_retained) hidden;
+int avc_ss_revoke(security_id_t ssid, security_id_t tsid,
+		  security_class_t tclass, access_vector_t perms,
+		  uint32_t seqno) hidden;
+int avc_ss_reset(uint32_t seqno) hidden;
+int avc_ss_set_auditallow(security_id_t ssid, security_id_t tsid,
+			  security_class_t tclass, access_vector_t perms,
+			  uint32_t seqno, uint32_t enable) hidden;
+int avc_ss_set_auditdeny(security_id_t ssid, security_id_t tsid,
+			 security_class_t tclass, access_vector_t perms,
+			 uint32_t seqno, uint32_t enable) hidden;
+
+/* netlink kernel message code */
+extern int avc_netlink_trouble hidden;
+
+hidden_proto(avc_av_stats)
+    hidden_proto(avc_cleanup)
+    hidden_proto(avc_reset)
+    hidden_proto(avc_audit)
+    hidden_proto(avc_has_perm_noaudit)
+#endif				/* _SELINUX_AVC_INTERNAL_H_ */
diff --git a/src/avc_sidtab.c b/src/avc_sidtab.c
new file mode 100644
index 0000000..0b696bb
--- /dev/null
+++ b/src/avc_sidtab.c
@@ -0,0 +1,152 @@
+/*
+ * Implementation of the userspace SID hashtable.
+ *
+ * Author : Eamon Walsh, <ewalsh@epoch.ncsc.mil>
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include "selinux_internal.h"
+#include <selinux/avc.h>
+#include "avc_sidtab.h"
+#include "avc_internal.h"
+
+static inline unsigned sidtab_hash(security_context_t key)
+{
+	char *p, *keyp;
+	unsigned int size;
+	unsigned int val;
+
+	val = 0;
+	keyp = (char *)key;
+	size = strlen(keyp);
+	for (p = keyp; (unsigned int)(p - keyp) < size; p++)
+		val =
+		    (val << 4 | (val >> (8 * sizeof(unsigned int) - 4))) ^ (*p);
+	return val & (SIDTAB_SIZE - 1);
+}
+
+int sidtab_init(struct sidtab *s)
+{
+	int i, rc = 0;
+
+	s->htable = (struct sidtab_node **)avc_malloc
+	    (sizeof(struct sidtab_node *) * SIDTAB_SIZE);
+
+	if (!s->htable) {
+		rc = -1;
+		goto out;
+	}
+	for (i = 0; i < SIDTAB_SIZE; i++)
+		s->htable[i] = NULL;
+	s->nel = 0;
+      out:
+	return rc;
+}
+
+int sidtab_insert(struct sidtab *s, const security_context_t ctx)
+{
+	int hvalue, rc = 0;
+	struct sidtab_node *newnode;
+	security_context_t newctx;
+
+	newnode = (struct sidtab_node *)avc_malloc(sizeof(*newnode));
+	if (!newnode) {
+		rc = -1;
+		goto out;
+	}
+	newctx = (security_context_t) strdup(ctx);
+	if (!newctx) {
+		rc = -1;
+		avc_free(newnode);
+		goto out;
+	}
+
+	hvalue = sidtab_hash(newctx);
+	newnode->next = s->htable[hvalue];
+	newnode->sid_s.ctx = newctx;
+	newnode->sid_s.refcnt = 1;	/* unused */
+	s->htable[hvalue] = newnode;
+	s->nel++;
+      out:
+	return rc;
+}
+
+int
+sidtab_context_to_sid(struct sidtab *s,
+		      const security_context_t ctx, security_id_t * sid)
+{
+	int hvalue, rc = 0;
+	struct sidtab_node *cur;
+
+	*sid = NULL;
+	hvalue = sidtab_hash(ctx);
+
+      loop:
+	cur = s->htable[hvalue];
+	while (cur != NULL && strcmp(cur->sid_s.ctx, ctx))
+		cur = cur->next;
+
+	if (cur == NULL) {	/* need to make a new entry */
+		rc = sidtab_insert(s, ctx);
+		if (rc)
+			goto out;
+		goto loop;	/* find the newly inserted node */
+	}
+
+	*sid = &cur->sid_s;
+      out:
+	return rc;
+}
+
+void sidtab_sid_stats(struct sidtab *h, char *buf, int buflen)
+{
+	int i, chain_len, slots_used, max_chain_len;
+	struct sidtab_node *cur;
+
+	slots_used = 0;
+	max_chain_len = 0;
+	for (i = 0; i < SIDTAB_SIZE; i++) {
+		cur = h->htable[i];
+		if (cur) {
+			slots_used++;
+			chain_len = 0;
+			while (cur) {
+				chain_len++;
+				cur = cur->next;
+			}
+
+			if (chain_len > max_chain_len)
+				max_chain_len = chain_len;
+		}
+	}
+
+	snprintf(buf, buflen,
+		 "%s:  %d SID entries and %d/%d buckets used, longest "
+		 "chain length %d\n", avc_prefix, h->nel, slots_used,
+		 SIDTAB_SIZE, max_chain_len);
+}
+
+void sidtab_destroy(struct sidtab *s)
+{
+	int i;
+	struct sidtab_node *cur, *temp;
+
+	if (!s)
+		return;
+
+	for (i = 0; i < SIDTAB_SIZE; i++) {
+		cur = s->htable[i];
+		while (cur != NULL) {
+			temp = cur;
+			cur = cur->next;
+			freecon(temp->sid_s.ctx);
+			avc_free(temp);
+		}
+		s->htable[i] = NULL;
+	}
+	avc_free(s->htable);
+	s->htable = NULL;
+}
diff --git a/src/avc_sidtab.h b/src/avc_sidtab.h
new file mode 100644
index 0000000..29b5d8b
--- /dev/null
+++ b/src/avc_sidtab.h
@@ -0,0 +1,36 @@
+/*
+ * A security identifier table (sidtab) is a hash table
+ * of security context structures indexed by SID value.
+ */
+#ifndef _SELINUX_AVC_SIDTAB_H_
+#define _SELINUX_AVC_SIDTAB_H_
+
+#include <selinux/selinux.h>
+#include <selinux/avc.h>
+#include "dso.h"
+
+struct sidtab_node {
+	struct security_id sid_s;
+	struct sidtab_node *next;
+};
+
+#define SIDTAB_HASH_BITS 7
+#define SIDTAB_HASH_BUCKETS (1 << SIDTAB_HASH_BITS)
+#define SIDTAB_HASH_MASK (SIDTAB_HASH_BUCKETS-1)
+#define SIDTAB_SIZE SIDTAB_HASH_BUCKETS
+
+struct sidtab {
+	struct sidtab_node **htable;
+	unsigned nel;
+};
+
+int sidtab_init(struct sidtab *s) hidden;
+int sidtab_insert(struct sidtab *s, security_context_t ctx) hidden;
+
+int sidtab_context_to_sid(struct sidtab *s,
+			  security_context_t ctx, security_id_t * sid) hidden;
+
+void sidtab_sid_stats(struct sidtab *s, char *buf, int buflen) hidden;
+void sidtab_destroy(struct sidtab *s) hidden;
+
+#endif				/* _SELINUX_AVC_SIDTAB_H_ */
diff --git a/src/booleans.c b/src/booleans.c
new file mode 100644
index 0000000..19ce7f0
--- /dev/null
+++ b/src/booleans.c
@@ -0,0 +1,312 @@
+/*
+ * Author: Karl MacMillan <kmacmillan@tresys.com>
+ *
+ * Modified:  
+ *   Dan Walsh <dwalsh@redhat.com> - Added security_load_booleans().
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fnmatch.h>
+#include <limits.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "selinux_internal.h"
+#include "policy.h"
+
+#define SELINUX_BOOL_DIR "/booleans/"
+
+static int filename_select(const struct dirent *d)
+{
+	if (d->d_name[0] == '.'
+	    && (d->d_name[1] == '\0'
+		|| (d->d_name[1] == '.' && d->d_name[2] == '\0')))
+		return 0;
+	return 1;
+}
+
+int security_get_boolean_names(char ***names, int *len)
+{
+	char path[PATH_MAX];
+	int i, rc;
+	struct dirent **namelist;
+	char **n;
+
+	assert(len);
+	if (!selinux_mnt) {
+		errno = ENOENT;
+		return -1;
+	}
+
+	snprintf(path, sizeof path, "%s%s", selinux_mnt, SELINUX_BOOL_DIR);
+	*len = scandir(path, &namelist, &filename_select, alphasort);
+	if (*len <= 0) {
+		return -1;
+	}
+
+	n = (char **)malloc(sizeof(char *) * *len);
+	if (!n) {
+		rc = -1;
+		goto bad;
+	}
+
+	for (i = 0; i < *len; i++) {
+	        n[i] = strdup(namelist[i]->d_name);
+		if (!n[i]) {
+			rc = -1;
+			goto bad_freen;
+		}
+	}
+	rc = 0;
+	*names = n;
+      out:
+	for (i = 0; i < *len; i++) {
+		free(namelist[i]);
+	}
+	free(namelist);
+	return rc;
+      bad_freen:
+	for (--i; i >= 0; --i)
+		free(n[i]);
+	free(n);
+      bad:
+	goto out;
+}
+
+hidden_def(security_get_boolean_names)
+#define STRBUF_SIZE 3
+static int get_bool_value(const char *name, char **buf)
+{
+	int fd, len;
+	char *fname = NULL;
+
+	if (!selinux_mnt) {
+		errno = ENOENT;
+		return -1;
+	}
+
+	*buf = (char *)malloc(sizeof(char) * (STRBUF_SIZE + 1));
+	if (!*buf)
+		goto out;
+	(*buf)[STRBUF_SIZE] = 0;
+
+	len = strlen(name) + strlen(selinux_mnt) + sizeof(SELINUX_BOOL_DIR);
+	fname = (char *)malloc(sizeof(char) * len);
+	if (!fname)
+		goto out;
+	snprintf(fname, len, "%s%s%s", selinux_mnt, SELINUX_BOOL_DIR, name);
+
+	fd = open(fname, O_RDONLY);
+	if (fd < 0)
+		goto out;
+
+	len = read(fd, *buf, STRBUF_SIZE);
+	close(fd);
+	if (len != STRBUF_SIZE)
+		goto out;
+
+	free(fname);
+	return 0;
+      out:
+	if (*buf)
+		free(*buf);
+	if (fname)
+		free(fname);
+	return -1;
+}
+
+int security_get_boolean_pending(const char *name)
+{
+	char *buf;
+	int val;
+
+	if (get_bool_value(name, &buf))
+		return -1;
+
+	if (atoi(&buf[1]))
+		val = 1;
+	else
+		val = 0;
+	free(buf);
+	return val;
+}
+
+int security_get_boolean_active(const char *name)
+{
+	char *buf;
+	int val;
+
+	if (get_bool_value(name, &buf))
+		return -1;
+
+	buf[1] = '\0';
+	if (atoi(buf))
+		val = 1;
+	else
+		val = 0;
+	free(buf);
+	return val;
+}
+
+hidden_def(security_get_boolean_active)
+
+int security_set_boolean(const char *name, int value)
+{
+	int fd, ret, len;
+	char buf[2], *fname;
+
+	if (!selinux_mnt) {
+		errno = ENOENT;
+		return -1;
+	}
+	if (value < 0 || value > 1) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	len = strlen(name) + strlen(selinux_mnt) + sizeof(SELINUX_BOOL_DIR);
+	fname = (char *)malloc(sizeof(char) * len);
+	if (!fname)
+		return -1;
+	snprintf(fname, len, "%s%s%s", selinux_mnt, SELINUX_BOOL_DIR, name);
+
+	fd = open(fname, O_WRONLY);
+	if (fd < 0) {
+		ret = -1;
+		goto out;
+	}
+
+	if (value)
+		buf[0] = '1';
+	else
+		buf[0] = '0';
+	buf[1] = '\0';
+
+	ret = write(fd, buf, 2);
+	close(fd);
+      out:
+	free(fname);
+	if (ret > 0)
+		return 0;
+	else
+		return -1;
+}
+
+hidden_def(security_set_boolean)
+
+int security_commit_booleans(void)
+{
+	int fd, ret;
+	char buf[2];
+	char path[PATH_MAX];
+
+	if (!selinux_mnt) {
+		errno = ENOENT;
+		return -1;
+	}
+
+	snprintf(path, sizeof path, "%s/commit_pending_bools", selinux_mnt);
+	fd = open(path, O_WRONLY);
+	if (fd < 0)
+		return -1;
+
+	buf[0] = '1';
+	buf[1] = '\0';
+
+	ret = write(fd, buf, 2);
+	close(fd);
+
+	if (ret > 0)
+		return 0;
+	else
+		return -1;
+}
+
+hidden_def(security_commit_booleans)
+
+static char *strtrim(char *dest, char *source, int size)
+{
+	int i = 0;
+	char *ptr = source;
+	i = 0;
+	while (isspace(*ptr) && i < size) {
+		ptr++;
+		i++;
+	}
+	strncpy(dest, ptr, size);
+	for (i = strlen(dest) - 1; i > 0; i--) {
+		if (!isspace(dest[i]))
+			break;
+	}
+	dest[i + 1] = '\0';
+	return dest;
+}
+static int process_boolean(char *buffer, char *name, int namesize, int *val)
+{
+	char name1[BUFSIZ];
+	char *ptr;
+	char *tok = strtok_r(buffer, "=", &ptr);
+	if (tok) {
+		strncpy(name1, tok, BUFSIZ - 1);
+		strtrim(name, name1, namesize - 1);
+		if (name[0] == '#')
+			return 0;
+		tok = strtok_r(NULL, "\0", &ptr);
+		if (tok) {
+			while (isspace(*tok))
+				tok++;
+			*val = -1;
+			if (isdigit(tok[0]))
+				*val = atoi(tok);
+			else if (!strncasecmp(tok, "true", sizeof("true") - 1))
+				*val = 1;
+			else if (!strncasecmp
+				 (tok, "false", sizeof("false") - 1))
+				*val = 0;
+			if (*val != 0 && *val != 1) {
+				errno = EINVAL;
+				return -1;
+			}
+
+		}
+	}
+	return 1;
+}
+
+static void rollback(SELboolean * boollist, int end)
+{
+	int i;
+
+	for (i = 0; i < end; i++)
+		security_set_boolean(boollist[i].name,
+				     security_get_boolean_active(boollist[i].
+								 name));
+}
+
+int security_set_boolean_list(size_t boolcnt, SELboolean * boollist,
+			      int permanent __attribute__((unused)))
+{
+
+	size_t i;
+	for (i = 0; i < boolcnt; i++) {
+		if (security_set_boolean(boollist[i].name, boollist[i].value)) {
+			rollback(boollist, i);
+			return -1;
+		}
+	}
+
+	/* OK, let's do the commit */
+	if (security_commit_booleans()) {
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/src/callbacks.c b/src/callbacks.c
new file mode 100644
index 0000000..b245364
--- /dev/null
+++ b/src/callbacks.c
@@ -0,0 +1,124 @@
+/*
+ * User-supplied callbacks and default implementations.
+ * Class and permission mappings.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <selinux/selinux.h>
+#include "callbacks.h"
+
+/* default implementations */
+static int __attribute__ ((format(printf, 2, 3)))
+default_selinux_log(int type __attribute__((unused)), const char *fmt, ...)
+{
+	int rc;
+	va_list ap;
+	va_start(ap, fmt);
+	rc = vfprintf(stderr, fmt, ap);
+	va_end(ap);
+	return rc;
+}
+
+static int
+default_selinux_audit(void *ptr __attribute__((unused)),
+		      security_class_t cls __attribute__((unused)),
+		      char *buf __attribute__((unused)),
+		      size_t len __attribute__((unused)))
+{
+	return 0;
+}
+
+static int
+default_selinux_validate(security_context_t *ctx)
+{
+	return security_check_context(*ctx);
+}
+
+static int
+default_selinux_setenforce(int enforcing __attribute__((unused)))
+{
+	return 0;
+}
+
+static int
+default_selinux_policyload(int seqno __attribute__((unused)))
+{
+	return 0;
+}
+
+/* callback pointers */
+int __attribute__ ((format(printf, 2, 3)))
+(*selinux_log)(int, const char *, ...) =
+	default_selinux_log;
+
+int
+(*selinux_audit) (void *, security_class_t, char *, size_t) =
+	default_selinux_audit;
+
+int
+(*selinux_validate)(security_context_t *ctx) =
+	default_selinux_validate;
+
+int
+(*selinux_netlink_setenforce) (int enforcing) =
+	default_selinux_setenforce;
+
+int
+(*selinux_netlink_policyload) (int seqno) =
+	default_selinux_policyload;
+
+/* callback setting function */
+void
+selinux_set_callback(int type, union selinux_callback cb)
+{
+	switch (type) {
+	case SELINUX_CB_LOG:
+		selinux_log = cb.func_log;
+		break;
+	case SELINUX_CB_AUDIT:
+		selinux_audit = cb.func_audit;
+		break;
+	case SELINUX_CB_VALIDATE:
+		selinux_validate = cb.func_validate;
+		break;
+	case SELINUX_CB_SETENFORCE:
+		selinux_netlink_setenforce = cb.func_setenforce;
+		break;
+	case SELINUX_CB_POLICYLOAD:
+		selinux_netlink_policyload = cb.func_policyload;
+		break;
+	}
+}
+
+/* callback getting function */
+union selinux_callback
+selinux_get_callback(int type)
+{
+	union selinux_callback cb;
+
+	switch (type) {
+	case SELINUX_CB_LOG:
+		cb.func_log = selinux_log;
+		break;
+	case SELINUX_CB_AUDIT:
+		cb.func_audit = selinux_audit;
+		break;
+	case SELINUX_CB_VALIDATE:
+		cb.func_validate = selinux_validate;
+		break;
+	case SELINUX_CB_SETENFORCE:
+		cb.func_setenforce = selinux_netlink_setenforce;
+		break;
+	case SELINUX_CB_POLICYLOAD:
+		cb.func_policyload = selinux_netlink_policyload;
+		break;
+	default:
+		memset(&cb, 0, sizeof(cb));
+		errno = EINVAL;
+		break;
+	}
+	return cb;
+}
diff --git a/src/callbacks.h b/src/callbacks.h
new file mode 100644
index 0000000..52ad555
--- /dev/null
+++ b/src/callbacks.h
@@ -0,0 +1,30 @@
+/*
+ * This file describes the callbacks passed to selinux_init() and available
+ * for use from the library code.  They all have default implementations.
+ */
+#ifndef _SELINUX_CALLBACKS_H_
+#define _SELINUX_CALLBACKS_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <selinux/selinux.h>
+#include "dso.h"
+
+/* callback pointers */
+extern int __attribute__ ((format(printf, 2, 3)))
+(*selinux_log) (int type, const char *, ...) hidden;
+
+extern int
+(*selinux_audit) (void *, security_class_t, char *, size_t) hidden;
+
+extern int
+(*selinux_validate)(security_context_t *ctx) hidden;
+
+extern int
+(*selinux_netlink_setenforce) (int enforcing) hidden;
+
+extern int
+(*selinux_netlink_policyload) (int seqno) hidden;
+
+#endif				/* _SELINUX_CALLBACKS_H_ */
diff --git a/src/canonicalize_context.c b/src/canonicalize_context.c
new file mode 100644
index 0000000..2f5cd41
--- /dev/null
+++ b/src/canonicalize_context.c
@@ -0,0 +1,62 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include "selinux_internal.h"
+#include "policy.h"
+#include <limits.h>
+
+int security_canonicalize_context(const security_context_t con,
+				      security_context_t * canoncon)
+{
+	char path[PATH_MAX];
+	char *buf;
+	size_t size;
+	int fd, ret;
+
+	if (!selinux_mnt) {
+		errno = ENOENT;
+		return -1;
+	}
+
+	snprintf(path, sizeof path, "%s/context", selinux_mnt);
+	fd = open(path, O_RDWR);
+	if (fd < 0)
+		return -1;
+
+	size = selinux_page_size;
+	buf = malloc(size);
+	if (!buf) {
+		ret = -1;
+		goto out;
+	}
+	strncpy(buf, con, size);
+
+	ret = write(fd, buf, strlen(buf) + 1);
+	if (ret < 0)
+		goto out2;
+
+	memset(buf, 0, size);
+	ret = read(fd, buf, size - 1);
+	if (ret < 0 && errno == EINVAL) {
+		/* Fall back to the original context for kernels
+		   that do not support the extended interface. */
+		strncpy(buf, con, size);
+	}
+
+	*canoncon = strdup(buf);
+	if (!(*canoncon)) {
+		ret = -1;
+		goto out2;
+	}
+	ret = 0;
+      out2:
+	free(buf);
+      out:
+	close(fd);
+	return ret;
+}
+
diff --git a/src/checkAccess.c b/src/checkAccess.c
new file mode 100644
index 0000000..f5b3a87
--- /dev/null
+++ b/src/checkAccess.c
@@ -0,0 +1,38 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <errno.h>
+#include "selinux_internal.h"
+#include <selinux/avc.h>
+
+static pthread_once_t once = PTHREAD_ONCE_INIT;
+
+static void avc_init_once(void)
+{
+	avc_open(NULL, 0);
+}
+
+int selinux_check_access(const security_context_t scon, const security_context_t tcon, const char *class, const char *perm, void *aux) {
+	int status = -1;
+	int rc = -1;
+	security_id_t scon_id;
+	security_id_t tcon_id;
+	security_class_t sclass;
+	access_vector_t av;
+
+	if (is_selinux_enabled() == 0)
+		return 0;
+
+	__selinux_once(once, avc_init_once);
+
+	if ((rc = avc_context_to_sid(scon, &scon_id)) < 0)  return rc;
+
+	if ((rc = avc_context_to_sid(tcon, &tcon_id)) < 0)  return rc;
+
+	if ((sclass = string_to_security_class(class)) == 0) return status;
+
+	if ((av = string_to_av_perm(sclass, perm)) == 0) return status;
+
+	return avc_has_perm (scon_id, tcon_id, sclass, av, NULL, aux);
+}
+
diff --git a/src/check_context.c b/src/check_context.c
new file mode 100644
index 0000000..ac4cb40
--- /dev/null
+++ b/src/check_context.c
@@ -0,0 +1,33 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include "selinux_internal.h"
+#include "policy.h"
+#include <limits.h>
+
+int security_check_context(const security_context_t con)
+{
+	char path[PATH_MAX];
+	int fd, ret;
+
+	if (!selinux_mnt) {
+		errno = ENOENT;
+		return -1;
+	}
+
+	snprintf(path, sizeof path, "%s/context", selinux_mnt);
+	fd = open(path, O_RDWR);
+	if (fd < 0)
+		return -1;
+
+	ret = write(fd, con, strlen(con) + 1);
+	close(fd);
+	if (ret < 0)
+		return -1;
+	return 0;
+}
+
diff --git a/src/compute_av.c b/src/compute_av.c
new file mode 100644
index 0000000..8c01fd0
--- /dev/null
+++ b/src/compute_av.c
@@ -0,0 +1,72 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <limits.h>
+#include "selinux_internal.h"
+#include "policy.h"
+#include "mapping.h"
+
+int security_compute_av(const security_context_t scon,
+			const security_context_t tcon,
+			security_class_t tclass,
+			access_vector_t requested,
+			struct av_decision *avd)
+{
+	char path[PATH_MAX];
+	char *buf;
+	size_t len;
+	int fd, ret;
+
+	if (!selinux_mnt) {
+		errno = ENOENT;
+		return -1;
+	}
+
+	snprintf(path, sizeof path, "%s/access", selinux_mnt);
+	fd = open(path, O_RDWR);
+	if (fd < 0)
+		return -1;
+
+	len = selinux_page_size;
+	buf = malloc(len);
+	if (!buf) {
+		ret = -1;
+		goto out;
+	}
+
+	snprintf(buf, len, "%s %s %hu %x", scon, tcon,
+		 unmap_class(tclass), unmap_perm(tclass, requested));
+
+	ret = write(fd, buf, strlen(buf));
+	if (ret < 0)
+		goto out2;
+
+	memset(buf, 0, len);
+	ret = read(fd, buf, len - 1);
+	if (ret < 0)
+		goto out2;
+
+	ret = sscanf(buf, "%x %x %x %x %u %x",
+		     &avd->allowed, &avd->decided,
+		     &avd->auditallow, &avd->auditdeny,
+		     &avd->seqno, &avd->flags);
+	if (ret < 5) {
+		ret = -1;
+		goto out2;
+	} else if (ret < 6)
+		avd->flags = 0;
+
+	map_decision(tclass, avd);
+
+	ret = 0;
+      out2:
+	free(buf);
+      out:
+	close(fd);
+	return ret;
+}
+
diff --git a/src/compute_create.c b/src/compute_create.c
new file mode 100644
index 0000000..19231b5
--- /dev/null
+++ b/src/compute_create.c
@@ -0,0 +1,61 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <limits.h>
+#include "selinux_internal.h"
+#include "policy.h"
+#include "mapping.h"
+
+int security_compute_create(const security_context_t scon,
+				const security_context_t tcon,
+				security_class_t tclass,
+				security_context_t * newcon)
+{
+	char path[PATH_MAX];
+	char *buf;
+	size_t size;
+	int fd, ret;
+
+	if (!selinux_mnt) {
+		errno = ENOENT;
+		return -1;
+	}
+
+	snprintf(path, sizeof path, "%s/create", selinux_mnt);
+	fd = open(path, O_RDWR);
+	if (fd < 0)
+		return -1;
+
+	size = selinux_page_size;
+	buf = malloc(size);
+	if (!buf) {
+		ret = -1;
+		goto out;
+	}
+	snprintf(buf, size, "%s %s %hu", scon, tcon, unmap_class(tclass));
+
+	ret = write(fd, buf, strlen(buf));
+	if (ret < 0)
+		goto out2;
+
+	memset(buf, 0, size);
+	ret = read(fd, buf, size - 1);
+	if (ret < 0)
+		goto out2;
+
+	*newcon = strdup(buf);
+	if (!(*newcon)) {
+		ret = -1;
+		goto out2;
+	}
+	ret = 0;
+      out2:
+	free(buf);
+      out:
+	close(fd);
+	return ret;
+}
diff --git a/src/context.c b/src/context.c
new file mode 100644
index 0000000..66abea1
--- /dev/null
+++ b/src/context.c
@@ -0,0 +1,197 @@
+#include "context_internal.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#define COMP_USER  0
+#define COMP_ROLE  1
+#define COMP_TYPE  2
+#define COMP_RANGE 3
+
+typedef struct {
+	char *current_str;	/* This is made up-to-date only when needed */
+	char *(component[4]);
+} context_private_t;
+
+/*
+ * Allocate a new context, initialized from str.  There must be 3 or
+ * 4 colon-separated components and no whitespace in any component other
+ * than the MLS component.
+ */
+context_t context_new(const char *str)
+{
+	int i, count;
+	errno = 0;
+	context_private_t *n =
+	    (context_private_t *) malloc(sizeof(context_private_t));
+	context_t result = (context_t) malloc(sizeof(context_s_t));
+	const char *p, *tok;
+
+	if (result)
+		result->ptr = n;
+	else
+		free(n);
+	if (n == 0 || result == 0) {
+		goto err;
+	}
+	n->current_str = n->component[0] = n->component[1] = n->component[2] =
+	    n->component[3] = 0;
+	for (i = count = 0, p = str; *p; p++) {
+		switch (*p) {
+		case ':':
+			count++;
+			break;
+		case '\n':
+		case '\t':
+		case '\r':
+			goto err;	/* sanity check */
+		case ' ':
+			if (count < 3)
+				goto err;	/* sanity check */
+		}
+	}
+	/*
+	 * Could be anywhere from 2 - 5
+	 * e.g user:role:type to user:role:type:sens1:cata-sens2:catb
+	 */
+	if (count < 2 || count > 5) {	/* might not have a range */
+		goto err;
+	}
+
+	n->component[3] = 0;
+	for (i = 0, tok = str; *tok; i++) {
+		if (i < 3)
+			for (p = tok; *p && *p != ':'; p++) {	/* empty */
+		} else {
+			/* MLS range is one component */
+			for (p = tok; *p; p++) {	/* empty */
+			}
+		}
+		n->component[i] = (char *)malloc(p - tok + 1);
+		if (n->component[i] == 0)
+			goto err;
+		strncpy(n->component[i], tok, p - tok);
+		n->component[i][p - tok] = '\0';
+		tok = *p ? p + 1 : p;
+	}
+	return result;
+      err:
+	if (errno == 0) errno = EINVAL;
+	context_free(result);
+	return 0;
+}
+
+hidden_def(context_new)
+
+static void conditional_free(char **v)
+{
+	if (*v) {
+		free(*v);
+	}
+	*v = 0;
+}
+
+/*
+ * free all storage used by a context.  Safe to call with
+ * null pointer. 
+ */
+void context_free(context_t context)
+{
+	context_private_t *n;
+	int i;
+	if (context) {
+		n = context->ptr;
+		if (n) {
+			conditional_free(&n->current_str);
+			for (i = 0; i < 4; i++) {
+				conditional_free(&n->component[i]);
+			}
+			free(n);
+		}
+		free(context);
+	}
+}
+
+hidden_def(context_free)
+
+/*
+ * Return a pointer to the string value of the context.
+ */
+char *context_str(context_t context)
+{
+	context_private_t *n = context->ptr;
+	int i;
+	size_t total = 0;
+	conditional_free(&n->current_str);
+	for (i = 0; i < 4; i++) {
+		if (n->component[i]) {
+			total += strlen(n->component[i]) + 1;
+		}
+	}
+	n->current_str = malloc(total);
+	if (n->current_str != 0) {
+		char *cp = n->current_str;
+
+		strcpy(cp, n->component[0]);
+		cp += strlen(cp);
+		for (i = 1; i < 4; i++) {
+			if (n->component[i]) {
+				*cp++ = ':';
+				strcpy(cp, n->component[i]);
+				cp += strlen(cp);
+			}
+		}
+	}
+	return n->current_str;
+}
+
+hidden_def(context_str)
+
+/* Returns nonzero iff failed */
+static int set_comp(context_private_t * n, int idx, const char *str)
+{
+	char *t = NULL;
+	const char *p;
+	if (str) {
+		t = (char *)malloc(strlen(str) + 1);
+		if (!t) {
+			return 1;
+		}
+		for (p = str; *p; p++) {
+			if (*p == '\t' || *p == '\n' || *p == '\r' ||
+			    ((*p == ':' || *p == ' ') && idx != COMP_RANGE)) {
+				free(t);
+				errno = EINVAL;
+				return 1;
+			}
+		}
+		strcpy(t, str);
+	}
+	conditional_free(&n->component[idx]);
+	n->component[idx] = t;
+	return 0;
+}
+
+#define def_get(name,tag) \
+const char * context_ ## name ## _get(context_t context) \
+{ \
+        context_private_t *n = context->ptr; \
+        return n->component[tag]; \
+} \
+hidden_def(context_ ## name ## _get)
+
+def_get(type, COMP_TYPE)
+    def_get(user, COMP_USER)
+    def_get(range, COMP_RANGE)
+    def_get(role, COMP_ROLE)
+#define def_set(name,tag) \
+int context_ ## name ## _set(context_t context, const char* str) \
+{ \
+        return set_comp(context->ptr,tag,str);\
+} \
+hidden_def(context_ ## name ## _set)
+    def_set(type, COMP_TYPE)
+    def_set(role, COMP_ROLE)
+    def_set(user, COMP_USER)
+    def_set(range, COMP_RANGE)
diff --git a/src/context_internal.h b/src/context_internal.h
new file mode 100644
index 0000000..3c71e80
--- /dev/null
+++ b/src/context_internal.h
@@ -0,0 +1,14 @@
+#include <selinux/context.h>
+#include "dso.h"
+
+hidden_proto(context_new)
+    hidden_proto(context_free)
+    hidden_proto(context_str)
+    hidden_proto(context_type_set)
+    hidden_proto(context_type_get)
+    hidden_proto(context_role_set)
+    hidden_proto(context_role_get)
+    hidden_proto(context_user_set)
+    hidden_proto(context_user_get)
+    hidden_proto(context_range_set)
+    hidden_proto(context_range_get)
diff --git a/src/disable.c b/src/disable.c
new file mode 100644
index 0000000..dac0f5b
--- /dev/null
+++ b/src/disable.c
@@ -0,0 +1,38 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include "selinux_internal.h"
+#include "policy.h"
+#include <stdio.h>
+#include <limits.h>
+
+int security_disable(void)
+{
+	int fd, ret;
+	char path[PATH_MAX];
+	char buf[20];
+
+	if (!selinux_mnt) {
+		errno = ENOENT;
+		return -1;
+	}
+
+	snprintf(path, sizeof path, "%s/disable", selinux_mnt);
+	fd = open(path, O_WRONLY);
+	if (fd < 0)
+		return -1;
+
+	buf[0] = '1';
+	buf[1] = '\0';
+	ret = write(fd, buf, strlen(buf));
+	close(fd);
+	if (ret < 0)
+		return -1;
+
+	return 0;
+}
+
+hidden_def(security_disable)
diff --git a/src/dso.h b/src/dso.h
new file mode 100644
index 0000000..12c3d11
--- /dev/null
+++ b/src/dso.h
@@ -0,0 +1,23 @@
+#ifndef _SELINUX_DSO_H
+#define _SELINUX_DSO_H	1
+
+#ifdef SHARED
+# define hidden __attribute__ ((visibility ("hidden")))
+# define hidden_proto(fct) __hidden_proto (fct, fct##_internal)
+# define __hidden_proto(fct, internal)	\
+     extern __typeof (fct) internal;	\
+     extern __typeof (fct) fct __asm (#internal) hidden;
+# if defined(__alpha__) || defined(__mips__)
+#  define hidden_def(fct) \
+     asm (".globl " #fct "\n" #fct " = " #fct "_internal");
+# else
+#  define hidden_def(fct) \
+     asm (".globl " #fct "\n.set " #fct ", " #fct "_internal");
+#endif
+#else
+# define hidden
+# define hidden_proto(fct)
+# define hidden_def(fct)
+#endif
+
+#endif
diff --git a/src/enabled.c b/src/enabled.c
new file mode 100644
index 0000000..5b36d1e
--- /dev/null
+++ b/src/enabled.c
@@ -0,0 +1,101 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include "selinux_internal.h"
+#include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include "policy.h"
+
+int is_selinux_enabled(void)
+{
+	char buf[BUFSIZ];
+	FILE *fp;
+	char *bufp;
+	size_t len;
+	int enabled = 0;
+	security_context_t con;
+
+	/* init_selinuxmnt() gets called before this function. We
+ 	 * will assume that if a selinux file system is mounted, then
+ 	 * selinux is enabled. */
+	if (selinux_mnt) {
+
+		/* Since a file system is mounted, we consider selinux
+		 * enabled. If getcon fails, selinux is still enabled.
+		 * We only consider it disabled if no policy is loaded. */
+		enabled = 1;
+		if (getcon(&con) == 0) {
+			if (!strcmp(con, "kernel"))
+				enabled = 0;
+			freecon(con);
+		}
+		return enabled;
+        }
+
+	/* Drop back to detecting it the long way. */
+	fp = fopen("/proc/filesystems", "r");
+	if (!fp)
+		return -1;
+
+	while ((bufp = fgets(buf, sizeof buf - 1, fp)) != NULL) {
+		if (strstr(buf, "selinuxfs")) {
+			enabled = 1;
+			break;
+		}
+	}
+
+	if (!bufp)
+		goto out;
+
+	/* Since an selinux file system is available, we consider
+	 * selinux enabled. If getcon fails, selinux is still
+	 * enabled. We only consider it disabled if no policy is loaded. */
+	if (getcon(&con) == 0) {
+		if (!strcmp(con, "kernel"))
+			enabled = 0;
+		freecon(con);
+	}
+
+      out:
+	fclose(fp);
+	return enabled;
+}
+
+hidden_def(is_selinux_enabled)
+
+/*
+ * Function: is_selinux_mls_enabled()
+ * Return:   1 on success
+ *	     0 on failure
+ */
+int is_selinux_mls_enabled(void)
+{
+	char buf[20], path[PATH_MAX];
+	int fd, ret, enabled = 0;
+
+	if (!selinux_mnt)
+		return enabled;
+
+	snprintf(path, sizeof path, "%s/mls", selinux_mnt);
+	fd = open(path, O_RDONLY);
+	if (fd < 0)
+		return enabled;
+
+	memset(buf, 0, sizeof buf);
+
+	do {
+		ret = read(fd, buf, sizeof buf - 1);
+	} while (ret < 0 && errno == EINTR);
+	close(fd);
+	if (ret < 0)
+		return enabled;
+
+	if (!strcmp(buf, "1"))
+		enabled = 1;
+
+	return enabled;
+}
+
+hidden_def(is_selinux_mls_enabled)
diff --git a/src/fgetfilecon.c b/src/fgetfilecon.c
new file mode 100644
index 0000000..eb890bd
--- /dev/null
+++ b/src/fgetfilecon.c
@@ -0,0 +1,51 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/xattr.h>
+#include "selinux_internal.h"
+#include "policy.h"
+
+int fgetfilecon(int fd, security_context_t * context)
+{
+	char *buf;
+	ssize_t size;
+	ssize_t ret;
+
+	size = INITCONTEXTLEN + 1;
+	buf = malloc(size);
+	if (!buf)
+		return -1;
+	memset(buf, 0, size);
+
+	ret = fgetxattr(fd, XATTR_NAME_SELINUX, buf, size - 1);
+	if (ret < 0 && errno == ERANGE) {
+		char *newbuf;
+
+		size = fgetxattr(fd, XATTR_NAME_SELINUX, NULL, 0);
+		if (size < 0)
+			goto out;
+
+		size++;
+		newbuf = realloc(buf, size);
+		if (!newbuf)
+			goto out;
+
+		buf = newbuf;
+		memset(buf, 0, size);
+		ret = fgetxattr(fd, XATTR_NAME_SELINUX, buf, size - 1);
+	}
+      out:
+	if (ret == 0) {
+		/* Re-map empty attribute values to errors. */
+		errno = EOPNOTSUPP;
+		ret = -1;
+	}
+	if (ret < 0)
+		free(buf);
+	else
+		*context = buf;
+	return ret;
+}
+
diff --git a/src/freecon.c b/src/freecon.c
new file mode 100644
index 0000000..3ec4fe2
--- /dev/null
+++ b/src/freecon.c
@@ -0,0 +1,11 @@
+#include <unistd.h>
+#include "selinux_internal.h"
+#include <stdlib.h>
+#include <errno.h>
+
+void freecon(security_context_t con)
+{
+	free(con);
+}
+
+hidden_def(freecon)
diff --git a/src/fsetfilecon.c b/src/fsetfilecon.c
new file mode 100644
index 0000000..38eeabd
--- /dev/null
+++ b/src/fsetfilecon.c
@@ -0,0 +1,15 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/xattr.h>
+#include "selinux_internal.h"
+#include "policy.h"
+
+int fsetfilecon(int fd, const security_context_t context)
+{
+	return fsetxattr(fd, XATTR_NAME_SELINUX, context, strlen(context) + 1,
+			 0);
+}
+
diff --git a/src/get_initial_context.c b/src/get_initial_context.c
new file mode 100644
index 0000000..559c100
--- /dev/null
+++ b/src/get_initial_context.c
@@ -0,0 +1,55 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include "selinux_internal.h"
+#include "policy.h"
+#include <limits.h>
+
+#define SELINUX_INITCON_DIR "/initial_contexts/"
+
+int security_get_initial_context(const char * name, security_context_t * con)
+{
+	char path[PATH_MAX];
+	char *buf;
+	size_t size;
+	int fd, ret;
+
+	if (!selinux_mnt) {
+		errno = ENOENT;
+		return -1;
+	}
+
+	snprintf(path, sizeof path, "%s%s%s", 
+		 selinux_mnt, SELINUX_INITCON_DIR, name);
+	fd = open(path, O_RDONLY);
+	if (fd < 0)
+		return -1;
+
+	size = selinux_page_size;
+	buf = malloc(size);
+	if (!buf) {
+		ret = -1;
+		goto out;
+	}
+	memset(buf, 0, size);
+	ret = read(fd, buf, size - 1);
+	if (ret < 0)
+		goto out2;
+
+	*con = strdup(buf);
+	if (!(*con)) {
+		ret = -1;
+		goto out2;
+	}
+	ret = 0;
+      out2:
+	free(buf);
+      out:
+	close(fd);
+	return ret;
+}
+
diff --git a/src/getenforce.c b/src/getenforce.c
new file mode 100644
index 0000000..4fb516a
--- /dev/null
+++ b/src/getenforce.c
@@ -0,0 +1,40 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include "selinux_internal.h"
+#include "policy.h"
+#include <stdio.h>
+#include <limits.h>
+
+int security_getenforce(void)
+{
+	int fd, ret, enforce = 0;
+	char path[PATH_MAX];
+	char buf[20];
+
+	if (!selinux_mnt) {
+		errno = ENOENT;
+		return -1;
+	}
+
+	snprintf(path, sizeof path, "%s/enforce", selinux_mnt);
+	fd = open(path, O_RDONLY);
+	if (fd < 0)
+		return -1;
+
+	memset(buf, 0, sizeof buf);
+	ret = read(fd, buf, sizeof buf - 1);
+	close(fd);
+	if (ret < 0)
+		return -1;
+
+	if (sscanf(buf, "%d", &enforce) != 1)
+		return -1;
+
+	return enforce;
+}
+
+hidden_def(security_getenforce)
diff --git a/src/getfilecon.c b/src/getfilecon.c
new file mode 100644
index 0000000..d8c0d35
--- /dev/null
+++ b/src/getfilecon.c
@@ -0,0 +1,50 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include "selinux_internal.h"
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/xattr.h>
+#include "policy.h"
+
+int getfilecon(const char *path, security_context_t * context)
+{
+	char *buf;
+	ssize_t size;
+	ssize_t ret;
+
+	size = INITCONTEXTLEN + 1;
+	buf = malloc(size);
+	if (!buf)
+		return -1;
+	memset(buf, 0, size);
+
+	ret = getxattr(path, XATTR_NAME_SELINUX, buf, size - 1);
+	if (ret < 0 && errno == ERANGE) {
+		char *newbuf;
+
+		size = getxattr(path, XATTR_NAME_SELINUX, NULL, 0);
+		if (size < 0)
+			goto out;
+
+		size++;
+		newbuf = realloc(buf, size);
+		if (!newbuf)
+			goto out;
+
+		buf = newbuf;
+		memset(buf, 0, size);
+		ret = getxattr(path, XATTR_NAME_SELINUX, buf, size - 1);
+	}
+      out:
+	if (ret == 0) {
+		/* Re-map empty attribute values to errors. */
+		errno = EOPNOTSUPP;
+		ret = -1;
+	}
+	if (ret < 0)
+		free(buf);
+	else
+		*context = buf;
+	return ret;
+}
diff --git a/src/getpeercon.c b/src/getpeercon.c
new file mode 100644
index 0000000..a5624b3
--- /dev/null
+++ b/src/getpeercon.c
@@ -0,0 +1,45 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include "selinux_internal.h"
+#include "policy.h"
+
+#ifndef SO_PEERSEC
+#define SO_PEERSEC 31
+#endif
+
+int getpeercon(int fd, security_context_t * context)
+{
+	char *buf;
+	socklen_t size;
+	ssize_t ret;
+
+	size = INITCONTEXTLEN + 1;
+	buf = malloc(size);
+	if (!buf)
+		return -1;
+	memset(buf, 0, size);
+
+	ret = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, buf, &size);
+	if (ret < 0 && errno == ERANGE) {
+		char *newbuf;
+
+		newbuf = realloc(buf, size);
+		if (!newbuf)
+			goto out;
+
+		buf = newbuf;
+		memset(buf, 0, size);
+		ret = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, buf, &size);
+	}
+      out:
+	if (ret < 0)
+		free(buf);
+	else
+		*context = buf;
+	return ret;
+}
+
diff --git a/src/init.c b/src/init.c
new file mode 100644
index 0000000..35b04e1
--- /dev/null
+++ b/src/init.c
@@ -0,0 +1,116 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <dlfcn.h>
+#include <sys/vfs.h>
+#include <stdint.h>
+#include <limits.h>
+
+#include "dso.h"
+#include "policy.h"
+#include "selinux_internal.h"
+
+char *selinux_mnt = NULL;
+int selinux_page_size = 0;
+
+static void init_selinuxmnt(void)
+{
+	char buf[BUFSIZ], *p;
+	FILE *fp=NULL;
+	struct statfs sfbuf;
+	int rc;
+	char *bufp;
+	int exists = 0;
+
+	if (selinux_mnt)
+		return;
+
+	/* We check to see if the preferred mount point for selinux file
+	 * system has a selinuxfs. */
+	do {
+		rc = statfs(SELINUXMNT, &sfbuf);
+	} while (rc < 0 && errno == EINTR);
+	if (rc == 0) {
+		if ((uint32_t)sfbuf.f_type == (uint32_t)SELINUX_MAGIC) {
+			selinux_mnt = strdup(SELINUXMNT);
+			return;
+		}
+	} 
+
+	/* Drop back to detecting it the long way. */
+	fp = fopen("/proc/filesystems", "r");
+	if (!fp)
+		return;
+
+	while ((bufp = fgets(buf, sizeof buf - 1, fp)) != NULL) {
+		if (strstr(buf, "selinuxfs")) {
+			exists = 1;
+			break;
+		}
+	}
+
+	if (!exists) 
+		goto out;
+
+	fclose(fp);
+
+	/* At this point, the usual spot doesn't have an selinuxfs so
+	 * we look around for it */
+	fp = fopen("/proc/mounts", "r");
+	if (!fp)
+		goto out;
+
+	while ((bufp = fgets(buf, sizeof buf - 1, fp)) != NULL) {
+		char *tmp;
+		p = strchr(buf, ' ');
+		if (!p)
+			goto out;
+		p++;
+		tmp = strchr(p, ' ');
+		if (!tmp)
+			goto out;
+		if (!strncmp(tmp + 1, "selinuxfs ", 10)) {
+			*tmp = '\0';
+			break;
+		}
+	}
+
+	/* If we found something, dup it */
+	if (bufp)
+		selinux_mnt = strdup(p);
+
+      out:
+	if (fp)
+		fclose(fp);
+	return;
+}
+
+void fini_selinuxmnt(void)
+{
+	free(selinux_mnt);
+	selinux_mnt = NULL;
+}
+
+void set_selinuxmnt(char *mnt)
+{
+	selinux_mnt = strdup(mnt);
+}
+
+hidden_def(set_selinuxmnt)
+
+static void init_lib(void) __attribute__ ((constructor));
+static void init_lib(void)
+{
+	selinux_page_size = sysconf(_SC_PAGE_SIZE);
+	init_selinuxmnt();
+}
+
+static void fini_lib(void) __attribute__ ((destructor));
+static void fini_lib(void)
+{
+	fini_selinuxmnt();
+}
diff --git a/src/label.c b/src/label.c
new file mode 100644
index 0000000..c448e3d
--- /dev/null
+++ b/src/label.c
@@ -0,0 +1,121 @@
+/*
+ * Generalized labeling frontend for userspace object managers.
+ *
+ * Author : Eamon Walsh <ewalsh@epoch.ncsc.mil>
+ */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <selinux/selinux.h>
+#include "callbacks.h"
+#include "label_internal.h"
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+typedef int (*selabel_initfunc)(struct selabel_handle *rec,
+				struct selinux_opt *opts, unsigned nopts);
+
+static selabel_initfunc initfuncs[] = {
+	&selabel_file_init,
+};
+
+/*
+ * Validation functions
+ */
+
+static inline int selabel_is_validate_set(struct selinux_opt *opts, unsigned n)
+{
+	while (n--)
+		if (opts[n].type == SELABEL_OPT_VALIDATE)
+			return !!opts[n].value;
+
+	return 0;
+}
+
+int selabel_validate(struct selabel_handle *rec,
+		     struct selabel_lookup_rec *contexts)
+{
+	int rc = 0;
+
+	if (!rec->validating || contexts->validated)
+		goto out;
+
+	rc = selinux_validate(&contexts->ctx_raw);
+	if (rc < 0)
+		goto out;
+
+	contexts->validated = 1;
+out:
+	return rc;
+}
+
+/*
+ * Public API
+ */
+
+struct selabel_handle *selabel_open(unsigned int backend,
+				    struct selinux_opt *opts, unsigned nopts)
+{
+	struct selabel_handle *rec = NULL;
+
+	if (backend >= ARRAY_SIZE(initfuncs)) {
+		errno = EINVAL;
+		goto out;
+	}
+
+	rec = (struct selabel_handle *)malloc(sizeof(*rec));
+	if (!rec)
+		goto out;
+
+	memset(rec, 0, sizeof(*rec));
+	rec->backend = backend;
+	rec->validating = selabel_is_validate_set(opts, nopts);
+
+	if ((*initfuncs[backend])(rec, opts, nopts)) {
+		free(rec);
+		rec = NULL;
+	}
+
+out:
+	return rec;
+}
+
+static struct selabel_lookup_rec *
+selabel_lookup_common(struct selabel_handle *rec, int translating,
+		      const char *key, int type)
+{
+	struct selabel_lookup_rec *lr;
+	lr = rec->func_lookup(rec, key, type); 
+	if (!lr)
+		return NULL;
+
+	return lr;
+}
+
+int selabel_lookup(struct selabel_handle *rec, security_context_t *con,
+		   const char *key, int type)
+{
+	struct selabel_lookup_rec *lr;
+
+	lr = selabel_lookup_common(rec, 1, key, type);
+	if (!lr)
+		return -1;
+
+	*con = strdup(lr->ctx_raw);
+	return *con ? 0 : -1;
+}
+
+void selabel_close(struct selabel_handle *rec)
+{
+	rec->func_close(rec);
+	free(rec);
+}
+
+void selabel_stats(struct selabel_handle *rec)
+{
+	rec->func_stats(rec);
+}
diff --git a/src/label_file.c b/src/label_file.c
new file mode 100644
index 0000000..71f1ef5
--- /dev/null
+++ b/src/label_file.c
@@ -0,0 +1,682 @@
+/*
+ * File contexts backend for labeling system
+ *
+ * Author : Eamon Walsh <ewalsh@tycho.nsa.gov>
+ */
+
+#include <fcntl.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <regex.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "callbacks.h"
+#include "label_internal.h"
+
+/*
+ * Internals, mostly moved over from matchpathcon.c
+ */
+
+/* A file security context specification. */
+typedef 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 */
+	regex_t regex;		/* compiled regular expression */
+	char regcomp;           /* regex_str has been compiled to regex */
+	mode_t mode;		/* mode format value */
+	int matches;		/* number of matching pathnames */
+	int hasMetaChars;	/* regular expression has meta-chars */
+	int stem_id;		/* indicates which stem-compression item */
+} spec_t;
+
+/* A regular expression stem */
+typedef struct stem {
+	char *buf;
+	int len;
+} stem_t;
+
+/* Our stored configuration */
+struct saved_data {
+	/*
+	 * The array of specifications, initially in the same order as in 
+	 * the specification file. Sorting occurs based on hasMetaChars.
+	 */
+	spec_t *spec_arr;
+	unsigned int nspec;
+	unsigned int ncomp;
+
+	/*
+	 * The array of regular expression stems.
+	 */
+	stem_t *stem_arr;
+	int num_stems;
+	int alloc_stems;
+};
+
+/* Return the length of the text that can be considered the stem, returns 0
+ * if there is no identifiable stem */
+static int get_stem_from_spec(const char *const buf)
+{
+	const char *tmp = strchr(buf + 1, '/');
+	const char *ind;
+
+	if (!tmp)
+		return 0;
+
+	for (ind = buf; ind < tmp; ind++) {
+		if (strchr(".^$?*+|[({", (int)*ind))
+			return 0;
+	}
+	return tmp - buf;
+}
+
+/* return the length of the text that is the stem of a file name */
+static int get_stem_from_file_name(const char *const buf)
+{
+	const char *tmp = strchr(buf + 1, '/');
+
+	if (!tmp)
+		return 0;
+	return tmp - buf;
+}
+
+/* find the stem of a file spec, returns the index into stem_arr for a new
+ * or existing stem, (or -1 if there is no possible stem - IE for a file in
+ * the root directory or a regex that is too complex for us). */
+static int find_stem_from_spec(struct saved_data *data, const char *buf)
+{
+	int i, num = data->num_stems;
+	int stem_len = get_stem_from_spec(buf);
+
+	if (!stem_len)
+		return -1;
+	for (i = 0; i < num; i++) {
+		if (stem_len == data->stem_arr[i].len
+		    && !strncmp(buf, data->stem_arr[i].buf, stem_len))
+			return i;
+	}
+	if (data->alloc_stems == num) {
+		stem_t *tmp_arr;
+		data->alloc_stems = data->alloc_stems * 2 + 16;
+		tmp_arr = realloc(data->stem_arr,
+				  sizeof(stem_t) * data->alloc_stems);
+		if (!tmp_arr)
+			return -1;
+		data->stem_arr = tmp_arr;
+	}
+	data->stem_arr[num].len = stem_len;
+	data->stem_arr[num].buf = malloc(stem_len + 1);
+	if (!data->stem_arr[num].buf)
+		return -1;
+	memcpy(data->stem_arr[num].buf, buf, stem_len);
+	data->stem_arr[num].buf[stem_len] = '\0';
+	data->num_stems++;
+	buf += stem_len;
+	return num;
+}
+
+/* find the stem of a file name, returns the index into stem_arr (or -1 if
+ * there is no match - IE for a file in the root directory or a regex that is
+ * too complex for us).  Makes buf point to the text AFTER the stem. */
+static int find_stem_from_file(struct saved_data *data, const char **buf)
+{
+	int i;
+	int stem_len = get_stem_from_file_name(*buf);
+
+	if (!stem_len)
+		return -1;
+	for (i = 0; i < data->num_stems; i++) {
+		if (stem_len == data->stem_arr[i].len
+		    && !strncmp(*buf, data->stem_arr[i].buf, stem_len)) {
+			*buf += stem_len;
+			return i;
+		}
+	}
+	return -1;
+}
+
+/*
+ * Warn about duplicate specifications.
+ */
+static int nodups_specs(struct saved_data *data, const char *path)
+{
+	int rc = 0;
+	unsigned int ii, jj;
+	struct spec *curr_spec, *spec_arr = data->spec_arr;
+
+	for (ii = 0; ii < data->nspec; ii++) {
+		curr_spec = &spec_arr[ii];
+		for (jj = ii + 1; jj < data->nspec; jj++) {
+			if ((!strcmp
+			     (spec_arr[jj].regex_str, curr_spec->regex_str))
+			    && (!spec_arr[jj].mode || !curr_spec->mode
+				|| spec_arr[jj].mode == curr_spec->mode)) {
+				rc = -1;
+				errno = EINVAL;
+				if (strcmp
+				    (spec_arr[jj].lr.ctx_raw,
+				     curr_spec->lr.ctx_raw)) {
+					selinux_log
+						(SELINUX_ERROR,
+						 "%s: Multiple different specifications for %s  (%s and %s).\n",
+						 path, curr_spec->regex_str,
+						 spec_arr[jj].lr.ctx_raw,
+						 curr_spec->lr.ctx_raw);
+				} else {
+					selinux_log
+						(SELINUX_ERROR,
+						 "%s: Multiple same specifications for %s.\n",
+						 path, curr_spec->regex_str);
+				}
+			}
+		}
+	}
+	return rc;
+}
+
+/* Determine if the regular expression specification has any meta characters. */
+static void spec_hasMetaChars(struct spec *spec)
+{
+	char *c;
+	int len;
+	char *end;
+
+	c = spec->regex_str;
+	len = strlen(spec->regex_str);
+	end = c + len;
+
+	spec->hasMetaChars = 0;
+
+	/* Look at each character in the RE specification string for a 
+	 * meta character. Return when any meta character reached. */
+	while (c != end) {
+		switch (*c) {
+		case '.':
+		case '^':
+		case '$':
+		case '?':
+		case '*':
+		case '+':
+		case '|':
+		case '[':
+		case '(':
+		case '{':
+			spec->hasMetaChars = 1;
+			return;
+		case '\\':	/* skip the next character */
+			c++;
+			break;
+		default:
+			break;
+
+		}
+		c++;
+	}
+	return;
+}
+
+static int compile_regex(struct saved_data *data, spec_t *spec, char **errbuf)
+{
+	char *reg_buf, *anchored_regex, *cp;
+	stem_t *stem_arr = data->stem_arr;
+	size_t len;
+	int regerr;
+
+	if (spec->regcomp)
+		return 0; /* already done */
+
+	data->ncomp++; /* how many compiled regexes required */
+
+	/* Skip the fixed stem. */
+	reg_buf = spec->regex_str;
+	if (spec->stem_id >= 0)
+		reg_buf += stem_arr[spec->stem_id].len;
+
+	/* Anchor the regular expression. */
+	len = strlen(reg_buf);
+	cp = anchored_regex = malloc(len + 3);
+	if (!anchored_regex)
+		return -1;
+	/* Create ^...$ regexp.  */
+	*cp++ = '^';
+	memcpy(cp, reg_buf, len);
+	cp += len;
+	*cp++ = '$';
+	*cp = '\0';
+
+	/* Compile the regular expression. */
+	regerr = regcomp(&spec->regex, anchored_regex, 
+			 REG_EXTENDED | REG_NOSUB);
+	if (regerr != 0) {
+		size_t errsz = 0;
+		errsz = regerror(regerr, &spec->regex, NULL, 0);
+		if (errsz && errbuf)
+			*errbuf = malloc(errsz);
+		if (errbuf && *errbuf)
+			(void)regerror(regerr, &spec->regex,
+				       *errbuf, errsz);
+
+		free(anchored_regex);
+		return -1;
+	}
+	free(anchored_regex);
+
+	/* Done. */
+	spec->regcomp = 1;
+
+	return 0;
+}
+
+
+static int process_line(struct selabel_handle *rec,
+			const char *path, const char *prefix,
+			char *line_buf, int pass, unsigned lineno)
+{
+	int items, len;
+	char buf1[BUFSIZ], buf2[BUFSIZ], buf3[BUFSIZ];
+	char *buf_p, *regex = buf1, *type = buf2, *context = buf3;
+	struct saved_data *data = (struct saved_data *)rec->data;
+	spec_t *spec_arr = data->spec_arr;
+	unsigned int nspec = data->nspec;
+
+	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)
+		return 0;
+	items = sscanf(line_buf, "%255s %255s %255s", regex, type, context);
+	if (items < 2) {
+		selinux_log(SELINUX_WARNING,
+			    "%s:  line %d is missing fields, skipping\n", path,
+			    lineno);
+		return 0;
+	} else if (items == 2) {
+		/* The type field is optional. */
+		context = type;
+		type = NULL;
+	}
+
+	len = get_stem_from_spec(regex);
+	if (len && prefix && strncmp(prefix, regex, len)) {
+		/* Stem of regex does not match requested prefix, discard. */
+		return 0;
+	}
+
+	if (pass == 1) {
+		/* On the second pass, process and store the specification in spec. */
+		char *errbuf = NULL;
+		spec_arr[nspec].stem_id = find_stem_from_spec(data, regex);
+		spec_arr[nspec].regex_str = strdup(regex);
+		if (!spec_arr[nspec].regex_str) {
+			selinux_log(SELINUX_WARNING,
+				   "%s:  out of memory at line %d on regex %s\n",
+				   path, lineno, regex);
+			return -1;
+
+		}
+		if (rec->validating && compile_regex(data, &spec_arr[nspec], &errbuf)) {
+			selinux_log(SELINUX_WARNING,
+				   "%s:  line %d has invalid regex %s:  %s\n",
+				   path, lineno, regex,
+				   (errbuf ? errbuf : "out of memory"));
+		}
+
+		/* Convert the type string to a mode format */
+		spec_arr[nspec].mode = 0;
+		if (!type)
+			goto skip_type;
+		spec_arr[nspec].type_str = strdup(type);
+		len = strlen(type);
+		if (type[0] != '-' || len != 2) {
+			selinux_log(SELINUX_WARNING,
+				    "%s:  line %d has invalid file type %s\n",
+				    path, lineno, type);
+			return 0;
+		}
+		switch (type[1]) {
+		case 'b':
+			spec_arr[nspec].mode = S_IFBLK;
+			break;
+		case 'c':
+			spec_arr[nspec].mode = S_IFCHR;
+			break;
+		case 'd':
+			spec_arr[nspec].mode = S_IFDIR;
+			break;
+		case 'p':
+			spec_arr[nspec].mode = S_IFIFO;
+			break;
+		case 'l':
+			spec_arr[nspec].mode = S_IFLNK;
+			break;
+		case 's':
+			spec_arr[nspec].mode = S_IFSOCK;
+			break;
+		case '-':
+			spec_arr[nspec].mode = S_IFREG;
+			break;
+		default:
+			selinux_log(SELINUX_WARNING,
+				    "%s:  line %d has invalid file type %s\n",
+				    path, lineno, type);
+			return 0;
+		}
+
+	skip_type:
+		spec_arr[nspec].lr.ctx_raw = strdup(context);
+
+		/* Determine if specification has 
+		 * any meta characters in the RE */
+		spec_hasMetaChars(&spec_arr[nspec]);
+	}
+
+	data->nspec = ++nspec;
+	return 0;
+}
+
+static int init(struct selabel_handle *rec, struct selinux_opt *opts,
+		unsigned n)
+{
+	struct saved_data *data = (struct saved_data *)rec->data;
+	const char *path = NULL;
+	const char *prefix = NULL;
+	FILE *fp;
+	FILE *localfp = NULL;
+	FILE *homedirfp = NULL;
+	char local_path[PATH_MAX + 1];
+	char homedir_path[PATH_MAX + 1];
+	char line_buf[BUFSIZ];
+	unsigned int lineno, pass, i, j, maxnspec;
+	spec_t *spec_copy = NULL;
+	int status = -1, baseonly = 0;
+	struct stat sb;
+
+	/* Process arguments */
+	while (n--)
+		switch(opts[n].type) {
+		case SELABEL_OPT_PATH:
+			path = opts[n].value;
+			break;
+		case SELABEL_OPT_SUBSET:
+			prefix = opts[n].value;
+			break;
+		case SELABEL_OPT_BASEONLY:
+			baseonly = !!opts[n].value;
+			break;
+		}
+
+	/* Open the specification file. */
+	if ((fp = fopen(path, "r")) == NULL)
+		return -1;
+
+	if (fstat(fileno(fp), &sb) < 0)
+		return -1;
+	if (!S_ISREG(sb.st_mode)) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	if (!baseonly) {
+		snprintf(homedir_path, sizeof(homedir_path), "%s.homedirs",
+			 path);
+		homedirfp = fopen(homedir_path, "r");
+
+		snprintf(local_path, sizeof(local_path), "%s.local", path);
+		localfp = fopen(local_path, "r");
+	}
+
+	/* 
+	 * Perform two passes over the specification file.
+	 * The first pass counts the number of specifications and
+	 * performs simple validation of the input.  At the end
+	 * of the first pass, the spec array is allocated.
+	 * The second pass performs detailed validation of the input
+	 * and fills in the spec array.
+	 */
+	maxnspec = UINT_MAX / sizeof(spec_t);
+	for (pass = 0; pass < 2; pass++) {
+		lineno = 0;
+		data->nspec = 0;
+		data->ncomp = 0;
+		while (fgets(line_buf, sizeof line_buf - 1, fp)
+		       && data->nspec < maxnspec) {
+			if (process_line(rec, path, prefix, line_buf,
+					 pass, ++lineno) != 0)
+				goto finish;
+		}
+		if (pass == 1) {
+			status = nodups_specs(data, path);
+			if (status)
+				goto finish;
+		}
+		lineno = 0;
+		if (homedirfp)
+			while (fgets(line_buf, sizeof line_buf - 1, homedirfp)
+			       && data->nspec < maxnspec) {
+				if (process_line
+				    (rec, homedir_path, prefix,
+				     line_buf, pass, ++lineno) != 0)
+					goto finish;
+			}
+
+		lineno = 0;
+		if (localfp)
+			while (fgets(line_buf, sizeof line_buf - 1, localfp)
+			       && data->nspec < maxnspec) {
+				if (process_line
+				    (rec, local_path, prefix, line_buf,
+				     pass, ++lineno) != 0)
+					goto finish;
+			}
+
+		if (pass == 0) {
+			if (data->nspec == 0) {
+				status = 0;
+				goto finish;
+			}
+			if (NULL == (data->spec_arr =
+				     malloc(sizeof(spec_t) * data->nspec)))
+				goto finish;
+			memset(data->spec_arr, 0, sizeof(spec_t)*data->nspec);
+			maxnspec = data->nspec;
+			rewind(fp);
+			if (homedirfp)
+				rewind(homedirfp);
+			if (localfp)
+				rewind(localfp);
+		}
+	}
+
+	/* Move exact pathname specifications to the end. */
+	spec_copy = malloc(sizeof(spec_t) * data->nspec);
+	if (!spec_copy)
+		goto finish;
+	j = 0;
+	for (i = 0; i < data->nspec; i++)
+		if (data->spec_arr[i].hasMetaChars)
+			memcpy(&spec_copy[j++],
+			       &data->spec_arr[i], sizeof(spec_t));
+	for (i = 0; i < data->nspec; i++)
+		if (!data->spec_arr[i].hasMetaChars)
+			memcpy(&spec_copy[j++],
+			       &data->spec_arr[i], sizeof(spec_t));
+	free(data->spec_arr);
+	data->spec_arr = spec_copy;
+
+	status = 0;
+finish:
+	fclose(fp);
+	if (data->spec_arr != spec_copy)
+		free(data->spec_arr);
+	if (homedirfp)
+		fclose(homedirfp);
+	if (localfp)
+		fclose(localfp);
+	return status;
+}
+
+/*
+ * Backend interface routines
+ */
+static void closef(struct selabel_handle *rec)
+{
+	struct saved_data *data = (struct saved_data *)rec->data;
+	struct spec *spec;
+	struct stem *stem;
+	unsigned int i;
+
+	for (i = 0; i < data->nspec; i++) {
+		spec = &data->spec_arr[i];
+		free(spec->regex_str);
+		free(spec->type_str);
+		free(spec->lr.ctx_raw);
+		free(spec->lr.ctx_trans);
+		regfree(&spec->regex);
+	}
+
+	for (i = 0; i < (unsigned int)data->num_stems; i++) {
+		stem = &data->stem_arr[i];
+		free(stem->buf);
+	}
+
+	if (data->spec_arr)
+		free(data->spec_arr);
+	if (data->stem_arr)
+		free(data->stem_arr);
+	
+	free(data);
+}
+
+static struct selabel_lookup_rec *lookup(struct selabel_handle *rec,
+					 const char *key, int type)
+{
+	struct saved_data *data = (struct saved_data *)rec->data;
+	spec_t *spec_arr = data->spec_arr;
+	int i, rc, file_stem;
+	mode_t mode = (mode_t)type;
+	const char *buf;
+	struct selabel_lookup_rec *ret = NULL;
+	char *clean_key = NULL;
+	const char *prev_slash, *next_slash;
+	unsigned int sofar = 0;
+
+	if (!data->nspec) {
+		errno = ENOENT;
+		goto finish;
+	}
+
+	/* Remove duplicate slashes */
+	if ((next_slash = strstr(key, "//"))) {
+		clean_key = malloc(strlen(key) + 1);
+		if (!clean_key)
+			goto finish;
+		prev_slash = key;
+		while (next_slash) {
+			memcpy(clean_key + sofar, prev_slash, next_slash - prev_slash);
+			sofar += next_slash - prev_slash;
+			prev_slash = next_slash + 1;
+			next_slash = strstr(prev_slash, "//");
+		}
+		strcpy(clean_key + sofar, prev_slash);
+		key = clean_key;
+	}
+
+	buf = key;
+	file_stem = find_stem_from_file(data, &buf);
+	mode &= S_IFMT;
+
+	/* 
+	 * Check for matching specifications in reverse order, so that
+	 * the last matching specification is used.
+	 */
+	for (i = data->nspec - 1; i >= 0; i--) {
+		/* if the spec in question matches no stem or has the same
+		 * stem as the file AND if the spec in question has no mode
+		 * specified or if the mode matches the file mode then we do
+		 * a regex check        */
+		if ((spec_arr[i].stem_id == -1
+		     || spec_arr[i].stem_id == file_stem)
+		    && (!mode || !spec_arr[i].mode
+			|| mode == spec_arr[i].mode)) {
+			if (compile_regex(data, &spec_arr[i], NULL) < 0)
+				goto finish;
+			if (spec_arr[i].stem_id == -1)
+				rc = regexec(&spec_arr[i].regex, key, 0, 0, 0);
+			else
+				rc = regexec(&spec_arr[i].regex, buf, 0, 0, 0);
+
+			if (rc == 0) {
+				spec_arr[i].matches++;
+				break;
+			}
+			if (rc == REG_NOMATCH)
+				continue;
+			/* else it's an error */
+			goto finish;
+		}
+	}
+
+	if (i < 0 || strcmp(spec_arr[i].lr.ctx_raw, "<<none>>") == 0) {
+		/* No matching specification. */
+		errno = ENOENT;
+		goto finish;
+	}
+
+	ret = &spec_arr[i].lr;
+
+finish:
+	free(clean_key);
+	return ret;
+}
+
+static void stats(struct selabel_handle *rec)
+{
+	struct saved_data *data = (struct saved_data *)rec->data;
+	unsigned int i, nspec = data->nspec;
+	spec_t *spec_arr = data->spec_arr;
+
+	for (i = 0; i < nspec; i++) {
+		if (spec_arr[i].matches == 0) {
+			if (spec_arr[i].type_str) {
+				selinux_log(SELINUX_WARNING,
+				    "Warning!  No matches for (%s, %s, %s)\n",
+				    spec_arr[i].regex_str,
+				    spec_arr[i].type_str,
+				    spec_arr[i].lr.ctx_raw);
+			} else {
+				selinux_log(SELINUX_WARNING,
+				    "Warning!  No matches for (%s, %s)\n",
+				    spec_arr[i].regex_str,
+				    spec_arr[i].lr.ctx_raw);
+			}
+		}
+	}
+}
+
+int selabel_file_init(struct selabel_handle *rec, struct selinux_opt *opts,
+		      unsigned nopts)
+{
+	struct saved_data *data;
+
+	data = (struct saved_data *)malloc(sizeof(*data));
+	if (!data)
+		return -1;
+	memset(data, 0, sizeof(*data));
+
+	rec->data = data;
+	rec->func_close = &closef;
+	rec->func_stats = &stats;
+	rec->func_lookup = &lookup;
+
+	return init(rec, opts, nopts);
+}
diff --git a/src/label_internal.h b/src/label_internal.h
new file mode 100644
index 0000000..37a21db
--- /dev/null
+++ b/src/label_internal.h
@@ -0,0 +1,70 @@
+/*
+ * This file describes the internal interface used by the labeler
+ * for calling the user-supplied memory allocation, validation,
+ * and locking routine.
+ *
+ * Author : Eamon Walsh <ewalsh@epoch.ncsc.mil>
+ */
+#ifndef _SELABEL_INTERNAL_H_
+#define _SELABEL_INTERNAL_H_
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+#include "dso.h"
+
+/*
+ * Installed backends
+ */
+int selabel_file_init(struct selabel_handle *rec, struct selinux_opt *opts,
+		      unsigned nopts) hidden;
+int selabel_media_init(struct selabel_handle *rec, struct selinux_opt *opts,
+		      unsigned nopts) hidden;
+int selabel_x_init(struct selabel_handle *rec, struct selinux_opt *opts,
+		   unsigned nopts) hidden;
+int selabel_db_init(struct selabel_handle *rec,
+		    struct selinux_opt *opts, unsigned nopts) hidden;
+
+/*
+ * Labeling internal structures
+ */
+struct selabel_sub {
+	char *src;
+	int slen;
+	char *dst;
+	struct selabel_sub *next;
+};
+
+struct selabel_lookup_rec {
+	security_context_t ctx_raw;
+	security_context_t ctx_trans;
+	int validated;
+};
+
+struct selabel_handle {
+	/* arguments that were passed to selabel_open */
+	unsigned int backend;
+	int validating;
+
+	/* labeling operations */
+	struct selabel_lookup_rec *(*func_lookup) (struct selabel_handle *h,
+						   const char *key, int type);
+	void (*func_close) (struct selabel_handle *h);
+	void (*func_stats) (struct selabel_handle *h);
+
+	/* supports backend-specific state information */
+	void *data;
+
+	/* substitution support */
+	struct selabel_sub *subs;
+};
+
+/*
+ * Validation function
+ */
+extern int
+selabel_validate(struct selabel_handle *rec,
+		 struct selabel_lookup_rec *contexts) hidden;
+
+#endif				/* _SELABEL_INTERNAL_H_ */
diff --git a/src/lgetfilecon.c b/src/lgetfilecon.c
new file mode 100644
index 0000000..dd1fea2
--- /dev/null
+++ b/src/lgetfilecon.c
@@ -0,0 +1,50 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/xattr.h>
+#include "selinux_internal.h"
+#include "policy.h"
+
+int lgetfilecon(const char *path, security_context_t * context)
+{
+	char *buf;
+	ssize_t size;
+	ssize_t ret;
+
+	size = INITCONTEXTLEN + 1;
+	buf = malloc(size);
+	if (!buf)
+		return -1;
+	memset(buf, 0, size);
+
+	ret = lgetxattr(path, XATTR_NAME_SELINUX, buf, size - 1);
+	if (ret < 0 && errno == ERANGE) {
+		char *newbuf;
+
+		size = lgetxattr(path, XATTR_NAME_SELINUX, NULL, 0);
+		if (size < 0)
+			goto out;
+
+		size++;
+		newbuf = realloc(buf, size);
+		if (!newbuf)
+			goto out;
+
+		buf = newbuf;
+		memset(buf, 0, size);
+		ret = lgetxattr(path, XATTR_NAME_SELINUX, buf, size - 1);
+	}
+      out:
+	if (ret == 0) {
+		/* Re-map empty attribute values to errors. */
+		errno = EOPNOTSUPP;
+		ret = -1;
+	}
+	if (ret < 0)
+		free(buf);
+	else
+		*context = buf;
+	return ret;
+}
diff --git a/src/load_policy.c b/src/load_policy.c
new file mode 100644
index 0000000..d103365
--- /dev/null
+++ b/src/load_policy.c
@@ -0,0 +1,41 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/utsname.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <errno.h>
+#include "selinux_internal.h"
+#include <dlfcn.h>
+#include "policy.h"
+#include <limits.h>
+#include "callbacks.h"
+
+int security_load_policy(void *data, size_t len)
+{
+	char path[PATH_MAX];
+	int fd, ret;
+
+	if (!selinux_mnt) {
+		errno = ENOENT;
+		return -1;
+	}
+
+	snprintf(path, sizeof path, "%s/load", selinux_mnt);
+	fd = open(path, O_RDWR);
+	if (fd < 0)
+		return -1;
+
+	ret = write(fd, data, len);
+	close(fd);
+	if (ret < 0)
+		return -1;
+	return 0;
+}
+
+hidden_def(security_load_policy)
diff --git a/src/lsetfilecon.c b/src/lsetfilecon.c
new file mode 100644
index 0000000..45cc2e3
--- /dev/null
+++ b/src/lsetfilecon.c
@@ -0,0 +1,15 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/xattr.h>
+#include "selinux_internal.h"
+#include "policy.h"
+
+int lsetfilecon(const char *path, const security_context_t context)
+{
+	return lsetxattr(path, XATTR_NAME_SELINUX, context, strlen(context) + 1,
+			 0);
+}
+
diff --git a/src/mapping.c b/src/mapping.c
new file mode 100644
index 0000000..f9858ce
--- /dev/null
+++ b/src/mapping.c
@@ -0,0 +1,195 @@
+/*
+ * Class and permission mappings.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <selinux/selinux.h>
+#include <selinux/avc.h>
+#include "mapping.h"
+
+/*
+ * Class and permission mappings
+ */
+
+struct selinux_mapping {
+	security_class_t value; /* real, kernel value */
+	unsigned num_perms;
+	access_vector_t perms[sizeof(access_vector_t) * 8];
+};
+
+static struct selinux_mapping *current_mapping = NULL;
+static security_class_t current_mapping_size = 0;
+
+/*
+ * Mapping setting function
+ */
+
+int
+selinux_set_mapping(struct security_class_mapping *map)
+{
+	size_t size = sizeof(struct selinux_mapping);
+	security_class_t 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;
+	}
+	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;
+
+	/* Store the raw class and permission values */
+	j = 0;
+	while (map[j].name) {
+		struct security_class_mapping *p_in = map + (j++);
+		struct selinux_mapping *p_out = current_mapping + j;
+
+		p_out->value = string_to_security_class(p_in->name);
+		if (!p_out->value)
+			goto err2;
+
+		k = 0;
+		while (p_in->perms && p_in->perms[k]) {
+			/* An empty permission string skips ahead */
+			if (!*p_in->perms[k]) {
+				k++;
+				continue;
+			}
+			p_out->perms[k] = string_to_av_perm(p_out->value,
+							    p_in->perms[k]);
+			if (!p_out->perms[k])
+				goto err2;
+			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;
+err:
+	return -1;
+}
+
+/*
+ * Get real, kernel values from mapped values
+ */
+
+security_class_t
+unmap_class(security_class_t tclass)
+{
+	if (tclass < current_mapping_size)
+		return current_mapping[tclass].value;
+
+	assert(current_mapping_size == 0);
+	return tclass;
+}
+
+access_vector_t
+unmap_perm(security_class_t tclass, access_vector_t tperm)
+{
+	if (tclass < current_mapping_size) {
+		unsigned i;
+		access_vector_t kperm = 0;
+
+		for (i=0; i<current_mapping[tclass].num_perms; i++)
+			if (tperm & (1<<i)) {
+				assert(current_mapping[tclass].perms[i]);
+				kperm |= current_mapping[tclass].perms[i];
+				tperm &= ~(1<<i);
+			}
+		assert(tperm == 0);
+		return kperm;
+	}
+
+	assert(current_mapping_size == 0);
+	return tperm;
+}
+
+/*
+ * Get mapped values from real, kernel values
+ */
+
+security_class_t
+map_class(security_class_t kclass)
+{
+	security_class_t i;
+
+	for (i=0; i<current_mapping_size; i++)
+		if (current_mapping[i].value == kclass)
+			return i;
+
+	assert(current_mapping_size == 0);
+	return kclass;
+}
+
+access_vector_t
+map_perm(security_class_t tclass, access_vector_t kperm)
+{
+	if (tclass < current_mapping_size) {
+		unsigned i;
+		access_vector_t tperm = 0;
+
+		for (i=0; i<current_mapping[tclass].num_perms; i++)
+			if (kperm & current_mapping[tclass].perms[i]) {
+				tperm |= 1<<i;
+				kperm &= ~current_mapping[tclass].perms[i];
+			}
+		assert(kperm == 0);
+		return tperm;
+	}
+
+	assert(current_mapping_size == 0);
+	return kperm;
+}
+
+void
+map_decision(security_class_t tclass, struct av_decision *avd)
+{
+	if (tclass < current_mapping_size) {
+		unsigned i;
+		access_vector_t result;
+
+		for (i=0, result=0; i<current_mapping[tclass].num_perms; i++)
+			if (avd->allowed & current_mapping[tclass].perms[i])
+				result |= 1<<i;
+		avd->allowed = result;
+
+		for (i=0, result=0; i<current_mapping[tclass].num_perms; i++)
+			if (avd->decided & current_mapping[tclass].perms[i])
+				result |= 1<<i;
+		avd->decided = result;
+
+		for (i=0, result=0; i<current_mapping[tclass].num_perms; i++)
+			if (avd->auditallow & current_mapping[tclass].perms[i])
+				result |= 1<<i;
+		avd->auditallow = result;
+
+		for (i=0, result=0; i<current_mapping[tclass].num_perms; i++)
+			if (avd->auditdeny & current_mapping[tclass].perms[i])
+				result |= 1<<i;
+		avd->auditdeny = result;
+	}
+}
diff --git a/src/mapping.h b/src/mapping.h
new file mode 100644
index 0000000..b96756b
--- /dev/null
+++ b/src/mapping.h
@@ -0,0 +1,41 @@
+/*
+ * This file describes the class and permission mappings used to 
+ * hide the kernel numbers from userspace by allowing userspace object
+ * managers to specify a list of classes and permissions.
+ */
+#ifndef _SELINUX_MAPPING_H_
+#define _SELINUX_MAPPING_H_
+
+#include <selinux/selinux.h>
+
+/*
+ * Get real, kernel values from mapped values
+ */
+
+extern security_class_t
+unmap_class(security_class_t tclass);
+
+extern access_vector_t
+unmap_perm(security_class_t tclass, access_vector_t tperm);
+
+/*
+ * Get mapped values from real, kernel values
+ */
+
+extern security_class_t
+map_class(security_class_t kclass);
+
+extern access_vector_t
+map_perm(security_class_t tclass, access_vector_t kperm);
+
+extern void
+map_decision(security_class_t tclass, struct av_decision *avd);
+
+/*mapping is not used for embedded build*/
+#ifdef DISABLE_AVC 
+#define unmap_perm(x,y) y
+#define unmap_class(x) x
+#define map_decision(x,y) 
+#endif
+
+#endif				/* _SELINUX_MAPPING_H_ */
diff --git a/src/policy.h b/src/policy.h
new file mode 100644
index 0000000..10e8712
--- /dev/null
+++ b/src/policy.h
@@ -0,0 +1,25 @@
+#ifndef _POLICY_H_
+#define _POLICY_H_
+
+/* Private definitions used internally by libselinux. */
+
+/* xattr name for SELinux attributes. */
+#define XATTR_NAME_SELINUX "security.selinux"
+
+/* Initial length guess for getting contexts. */
+#define INITCONTEXTLEN 255
+
+/* selinuxfs magic number */
+#define SELINUX_MAGIC 0xf97cff8c
+
+/* Preferred selinux mount location */
+#define SELINUXMNT "/selinux"
+
+/* selinuxfs mount point */
+extern char *selinux_mnt;
+
+#define FILECONTEXTS "/etc/security/selinux/file_contexts"
+
+#define DEFAULT_POLICY_VERSION 15
+
+#endif
diff --git a/src/policyvers.c b/src/policyvers.c
new file mode 100644
index 0000000..284a7f7
--- /dev/null
+++ b/src/policyvers.c
@@ -0,0 +1,45 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include "selinux_internal.h"
+#include <stdio.h>
+#include "policy.h"
+#include "dso.h"
+#include <limits.h>
+
+int security_policyvers(void)
+{
+	int fd, ret;
+	char path[PATH_MAX];
+	char buf[20];
+	unsigned vers = DEFAULT_POLICY_VERSION;
+
+	if (!selinux_mnt) {
+		errno = ENOENT;
+		return -1;
+	}
+
+	snprintf(path, sizeof path, "%s/policyvers", selinux_mnt);
+	fd = open(path, O_RDONLY);
+	if (fd < 0) {
+		if (errno == ENOENT)
+			return vers;
+		else
+			return -1;
+	}
+	memset(buf, 0, sizeof buf);
+	ret = read(fd, buf, sizeof buf - 1);
+	close(fd);
+	if (ret < 0)
+		return -1;
+
+	if (sscanf(buf, "%u", &vers) != 1)
+		return -1;
+
+	return vers;
+}
+
+hidden_def(security_policyvers)
diff --git a/src/procattr.c b/src/procattr.c
new file mode 100644
index 0000000..6c2c5a8
--- /dev/null
+++ b/src/procattr.c
@@ -0,0 +1,163 @@
+#include <sys/syscall.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include "selinux_internal.h"
+#include "policy.h"
+
+#ifdef HOST
+static pid_t gettid(void)
+{
+	return syscall(__NR_gettid);
+}
+#endif
+
+static int getprocattrcon(security_context_t * context,
+			  pid_t pid, const char *attr)
+{
+	char *path, *buf;
+	size_t size;
+	int fd, rc;
+	ssize_t ret;
+	pid_t tid;
+	int errno_hold;
+
+	if (pid > 0)
+		rc = asprintf(&path, "/proc/%d/attr/%s", pid, attr);
+	else {
+		tid = gettid();
+		rc = asprintf(&path, "/proc/self/task/%d/attr/%s", tid, attr);
+	}
+	if (rc < 0)
+		return -1;
+
+	fd = open(path, O_RDONLY);
+	free(path);
+	if (fd < 0)
+		return -1;
+
+	size = selinux_page_size;
+	buf = malloc(size);
+	if (!buf) {
+		ret = -1;
+		goto out;
+	}
+	memset(buf, 0, size);
+
+	do {
+		ret = read(fd, buf, size - 1);
+	} while (ret < 0 && errno == EINTR);
+	if (ret < 0)
+		goto out2;
+
+	if (ret == 0) {
+		*context = NULL;
+		goto out2;
+	}
+
+	*context = strdup(buf);
+	if (!(*context)) {
+		ret = -1;
+		goto out2;
+	}
+	ret = 0;
+      out2:
+	free(buf);
+      out:
+	errno_hold = errno;
+	close(fd);
+	errno = errno_hold;
+	return ret;
+}
+
+static int setprocattrcon(security_context_t context,
+			  pid_t pid, const char *attr)
+{
+	char *path;
+	int fd, rc;
+	pid_t tid;
+	ssize_t ret;
+	int errno_hold;
+
+	if (pid > 0)
+		rc = asprintf(&path, "/proc/%d/attr/%s", pid, attr);
+	else {
+		tid = gettid();
+		rc = asprintf(&path, "/proc/self/task/%d/attr/%s", tid, attr);
+	}
+	if (rc < 0)
+		return -1;
+
+	fd = open(path, O_RDWR);
+	free(path);
+	if (fd < 0)
+		return -1;
+	if (context)
+		do {
+			ret = write(fd, context, strlen(context) + 1);
+		} while (ret < 0 && errno == EINTR);
+	else
+		do {
+			ret = write(fd, NULL, 0);	/* clear */
+		} while (ret < 0 && errno == EINTR);
+	errno_hold = errno;
+	close(fd);
+	errno = errno_hold;
+	if (ret < 0)
+		return -1;
+	else
+		return 0;
+}
+
+#define getselfattr_def(fn, attr) \
+	int get##fn(security_context_t *c) \
+	{ \
+		return getprocattrcon(c, 0, #attr); \
+	}
+
+#define setselfattr_def(fn, attr) \
+	int set##fn(const security_context_t c) \
+	{ \
+		return setprocattrcon(c, 0, #attr); \
+	}
+
+#define all_selfattr_def(fn, attr) \
+	getselfattr_def(fn, attr)	 \
+	setselfattr_def(fn, attr)
+
+#define getpidattr_def(fn, attr) \
+	int get##fn(pid_t pid, security_context_t *c)	\
+	{ \
+		return getprocattrcon(c, pid, #attr); \
+	}
+
+all_selfattr_def(con, current)
+    getpidattr_def(pidcon, current)
+    getselfattr_def(prevcon, prev)
+    all_selfattr_def(execcon, exec)
+    all_selfattr_def(fscreatecon, fscreate)
+    all_selfattr_def(sockcreatecon, sockcreate)
+    all_selfattr_def(keycreatecon, keycreate)
+
+    hidden_def(getcon_raw)
+    hidden_def(getcon)
+    hidden_def(getexeccon_raw)
+    hidden_def(getfilecon_raw)
+    hidden_def(getfilecon)
+    hidden_def(getfscreatecon_raw)
+    hidden_def(getkeycreatecon_raw)
+    hidden_def(getpeercon_raw)
+    hidden_def(getpidcon_raw)
+    hidden_def(getprevcon_raw)
+    hidden_def(getprevcon)
+    hidden_def(getsockcreatecon_raw)
+    hidden_def(setcon_raw)
+    hidden_def(setexeccon_raw)
+    hidden_def(setexeccon)
+    hidden_def(setfilecon_raw)
+    hidden_def(setfscreatecon_raw)
+    hidden_def(setkeycreatecon_raw)
+    hidden_def(setsockcreatecon_raw)
diff --git a/src/selinux_internal.h b/src/selinux_internal.h
new file mode 100644
index 0000000..5087bb6
--- /dev/null
+++ b/src/selinux_internal.h
@@ -0,0 +1,132 @@
+#include <selinux/selinux.h>
+#include <pthread.h>
+#include "dso.h"
+
+hidden_proto(selinux_mkload_policy)
+    hidden_proto(set_selinuxmnt)
+    hidden_proto(security_disable)
+    hidden_proto(security_policyvers)
+    hidden_proto(security_load_policy)
+    hidden_proto(security_get_boolean_active)
+    hidden_proto(security_get_boolean_names)
+    hidden_proto(security_set_boolean)
+    hidden_proto(security_commit_booleans)
+    hidden_proto(security_check_context)
+    hidden_proto(security_check_context_raw)
+    hidden_proto(security_canonicalize_context)
+    hidden_proto(security_canonicalize_context_raw)
+    hidden_proto(security_compute_av)
+    hidden_proto(security_compute_av_raw)
+    hidden_proto(security_compute_av_flags)
+    hidden_proto(security_compute_av_flags_raw)
+    hidden_proto(security_compute_user)
+    hidden_proto(security_compute_user_raw)
+    hidden_proto(security_compute_create)
+    hidden_proto(security_compute_create_raw)
+    hidden_proto(security_compute_member_raw)
+    hidden_proto(security_compute_relabel_raw)
+    hidden_proto(is_selinux_enabled)
+    hidden_proto(is_selinux_mls_enabled)
+    hidden_proto(freecon)
+    hidden_proto(freeconary)
+    hidden_proto(getprevcon)
+    hidden_proto(getprevcon_raw)
+    hidden_proto(getcon)
+    hidden_proto(getcon_raw)
+    hidden_proto(setcon_raw)
+    hidden_proto(getpeercon_raw)
+    hidden_proto(getpidcon_raw)
+    hidden_proto(getexeccon_raw)
+    hidden_proto(getfilecon)
+    hidden_proto(getfilecon_raw)
+    hidden_proto(lgetfilecon_raw)
+    hidden_proto(fgetfilecon_raw)
+    hidden_proto(setfilecon_raw)
+    hidden_proto(lsetfilecon_raw)
+    hidden_proto(fsetfilecon_raw)
+    hidden_proto(setexeccon)
+    hidden_proto(setexeccon_raw)
+    hidden_proto(getfscreatecon_raw)
+    hidden_proto(getkeycreatecon_raw)
+    hidden_proto(getsockcreatecon_raw)
+    hidden_proto(setfscreatecon_raw)
+    hidden_proto(setkeycreatecon_raw)
+    hidden_proto(setsockcreatecon_raw)
+    hidden_proto(security_getenforce)
+    hidden_proto(security_setenforce)
+    hidden_proto(security_deny_unknown)
+    hidden_proto(selinux_binary_policy_path)
+    hidden_proto(selinux_default_context_path)
+    hidden_proto(selinux_securetty_types_path)
+    hidden_proto(selinux_failsafe_context_path)
+    hidden_proto(selinux_removable_context_path)
+    hidden_proto(selinux_virtual_domain_context_path)
+    hidden_proto(selinux_virtual_image_context_path)
+    hidden_proto(selinux_file_context_path)
+    hidden_proto(selinux_file_context_homedir_path)
+    hidden_proto(selinux_file_context_local_path)
+    hidden_proto(selinux_file_context_subs_path)
+    hidden_proto(selinux_netfilter_context_path)
+    hidden_proto(selinux_homedir_context_path)
+    hidden_proto(selinux_user_contexts_path)
+    hidden_proto(selinux_booleans_path)
+    hidden_proto(selinux_customizable_types_path)
+    hidden_proto(selinux_media_context_path)
+    hidden_proto(selinux_x_context_path)
+    hidden_proto(selinux_sepgsql_context_path)
+    hidden_proto(selinux_path)
+    hidden_proto(selinux_check_passwd_access)
+    hidden_proto(selinux_check_securetty_context)
+    hidden_proto(matchpathcon_init_prefix)
+    hidden_proto(selinux_users_path)
+    hidden_proto(selinux_usersconf_path);
+hidden_proto(selinux_translations_path);
+hidden_proto(selinux_colors_path);
+hidden_proto(selinux_getenforcemode);
+hidden_proto(selinux_getpolicytype);
+hidden_proto(selinux_raw_to_trans_context);
+hidden_proto(selinux_trans_to_raw_context);
+    hidden_proto(selinux_raw_context_to_color);
+hidden_proto(security_get_initial_context);
+hidden_proto(security_get_initial_context_raw);
+hidden_proto(selinux_reset_config);
+
+extern int selinux_page_size hidden;
+
+/* Make pthread_once optional */
+#pragma weak pthread_once
+#pragma weak pthread_key_create
+#pragma weak pthread_key_delete
+#pragma weak pthread_setspecific
+
+/* Call handler iff the first call.  */
+#define __selinux_once(ONCE_CONTROL, INIT_FUNCTION)	\
+	do {						\
+		if (pthread_once != NULL)		\
+			pthread_once (&(ONCE_CONTROL), (INIT_FUNCTION));  \
+		else if ((ONCE_CONTROL) == PTHREAD_ONCE_INIT) {		  \
+			INIT_FUNCTION ();		\
+			(ONCE_CONTROL) = 2;		\
+		}					\
+	} while (0)
+
+/* Pthread key macros */
+#define __selinux_key_create(KEY, DESTRUCTOR)			\
+	do {							\
+		if (pthread_key_create != NULL)			\
+			pthread_key_create(KEY, DESTRUCTOR);	\
+	} while (0)
+
+#define __selinux_key_delete(KEY)				\
+	do {							\
+		if (pthread_key_delete != NULL)			\
+			pthread_key_delete(KEY);		\
+	} while (0)
+
+#define __selinux_setspecific(KEY, VALUE)			\
+	do {							\
+		if (pthread_setspecific != NULL)		\
+			pthread_setspecific(KEY, VALUE);	\
+	} while (0)
+
+
diff --git a/src/selinux_netlink.h b/src/selinux_netlink.h
new file mode 100644
index 0000000..88ef551
--- /dev/null
+++ b/src/selinux_netlink.h
@@ -0,0 +1,31 @@
+/*
+ * Netlink event notifications for SELinux.
+ *
+ * Author: James Morris <jmorris@redhat.com>
+ */
+#ifndef _LINUX_SELINUX_NETLINK_H
+#define _LINUX_SELINUX_NETLINK_H
+
+/* Message types. */
+#define SELNL_MSG_BASE 0x10
+enum {
+	SELNL_MSG_SETENFORCE = SELNL_MSG_BASE,
+	SELNL_MSG_POLICYLOAD,
+	SELNL_MSG_MAX
+};
+
+/* Multicast groups */
+#define SELNL_GRP_NONE		0x00000000
+#define SELNL_GRP_AVC		0x00000001	/* AVC notifications */
+#define SELNL_GRP_ALL		0xffffffff
+
+/* Message structures */
+struct selnl_msg_setenforce {
+	int32_t val;
+};
+
+struct selnl_msg_policyload {
+	uint32_t seqno;
+};
+
+#endif				/* _LINUX_SELINUX_NETLINK_H */
diff --git a/src/setenforce.c b/src/setenforce.c
new file mode 100644
index 0000000..e5e7612
--- /dev/null
+++ b/src/setenforce.c
@@ -0,0 +1,37 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include "selinux_internal.h"
+#include "policy.h"
+#include <stdio.h>
+#include <limits.h>
+
+int security_setenforce(int value)
+{
+	int fd, ret;
+	char path[PATH_MAX];
+	char buf[20];
+
+	if (!selinux_mnt) {
+		errno = ENOENT;
+		return -1;
+	}
+
+	snprintf(path, sizeof path, "%s/enforce", selinux_mnt);
+	fd = open(path, O_RDWR);
+	if (fd < 0)
+		return -1;
+
+	snprintf(buf, sizeof buf, "%d", value);
+	ret = write(fd, buf, strlen(buf));
+	close(fd);
+	if (ret < 0)
+		return -1;
+
+	return 0;
+}
+
+hidden_def(security_setenforce)
diff --git a/src/setfilecon.c b/src/setfilecon.c
new file mode 100644
index 0000000..47022df
--- /dev/null
+++ b/src/setfilecon.c
@@ -0,0 +1,15 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/xattr.h>
+#include "selinux_internal.h"
+#include "policy.h"
+
+int setfilecon(const char *path, const security_context_t context)
+{
+	return setxattr(path, XATTR_NAME_SELINUX, context, strlen(context) + 1,
+			0);
+}
+
diff --git a/src/stringrep.c b/src/stringrep.c
new file mode 100644
index 0000000..5b63673
--- /dev/null
+++ b/src/stringrep.c
@@ -0,0 +1,297 @@
+/*
+ * String representation support for classes and permissions.
+ */
+#include <sys/stat.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <ctype.h>
+#include "selinux_internal.h"
+#include "policy.h"
+#include "mapping.h"
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+#define MAXVECTORS 8*sizeof(access_vector_t)
+
+static pthread_once_t once = PTHREAD_ONCE_INIT;
+
+struct discover_class_node {
+	char *name;
+	security_class_t value;
+	char **perms;
+
+	struct discover_class_node *next;
+};
+
+static struct discover_class_node *discover_class_cache = NULL;
+
+static struct discover_class_node * get_class_cache_entry_name(const char *s)
+{
+	struct discover_class_node *node = discover_class_cache;
+
+	for (; node != NULL && strcmp(s,node->name) != 0; node = node->next);
+
+	return node;
+}
+
+static struct discover_class_node * get_class_cache_entry_value(security_class_t c)
+{
+	struct discover_class_node *node = discover_class_cache;
+
+	for (; node != NULL && c != node->value; node = node->next);
+
+	return node;
+}
+
+static struct discover_class_node * discover_class(const char *s)
+{
+	int fd, ret;
+	char path[PATH_MAX];
+	char buf[20];
+	DIR *dir;
+	struct dirent *dentry;
+	size_t i;
+
+	struct discover_class_node *node;
+
+	if (!selinux_mnt) {
+		errno = ENOENT;
+		return NULL;
+	}
+
+	/* allocate a node */
+	node = malloc(sizeof(struct discover_class_node));
+	if (node == NULL)
+		return NULL;
+
+	/* allocate array for perms */
+	node->perms = calloc(MAXVECTORS,sizeof(char*));
+	if (node->perms == NULL)
+		goto err1;
+
+	/* load up the name */
+	node->name = strdup(s);
+	if (node->name == NULL)
+		goto err2;
+
+	/* load up class index */
+	snprintf(path, sizeof path, "%s/class/%s/index", selinux_mnt,s);
+	fd = open(path, O_RDONLY);
+	if (fd < 0)
+		goto err3;
+
+	memset(buf, 0, sizeof(buf));
+	ret = read(fd, buf, sizeof(buf) - 1);
+	close(fd);
+	if (ret < 0)
+		goto err3;
+
+	if (sscanf(buf, "%hu", &node->value) != 1)
+		goto err3;
+
+	/* load up permission indicies */
+	snprintf(path, sizeof path, "%s/class/%s/perms",selinux_mnt,s);
+	dir = opendir(path);
+	if (dir == NULL)
+		goto err3;
+
+	dentry = readdir(dir);
+	while (dentry != NULL) {
+		unsigned int value;
+		struct stat m;
+
+		snprintf(path, sizeof path, "%s/class/%s/perms/%s", selinux_mnt,s,dentry->d_name);
+		if (stat(path,&m) < 0)
+			goto err4;
+
+		if (m.st_mode & S_IFDIR) {
+			dentry = readdir(dir);
+			continue;
+		}
+
+		fd = open(path, O_RDONLY);
+		if (fd < 0)
+			goto err4;
+
+		memset(buf, 0, sizeof(buf));
+		ret = read(fd, buf, sizeof(buf) - 1);
+		close(fd);
+		if (ret < 0)
+			goto err4;
+
+		if (sscanf(buf, "%u", &value) != 1)
+			goto err4;
+
+		node->perms[value-1] = strdup(dentry->d_name);
+		if (node->perms[value-1] == NULL)
+			goto err4;
+
+		dentry = readdir(dir);
+	}
+	closedir(dir);
+
+	node->next = discover_class_cache;
+	discover_class_cache = node;
+
+	return node;
+
+err4:
+	closedir(dir);
+	for (i=0; i<MAXVECTORS; i++)
+		free(node->perms[i]);
+err3:
+	free(node->name);
+err2:
+	free(node->perms);
+err1:
+	free(node);
+	return NULL;
+}
+
+void flush_class_cache(void)
+{
+	struct discover_class_node *cur = discover_class_cache, *prev = NULL;
+	size_t i;
+
+	while (cur != NULL) {
+		free(cur->name);
+
+		for (i=0 ; i<MAXVECTORS ; i++)
+			free(cur->perms[i]);
+
+		free(cur->perms);
+
+		prev = cur;
+		cur = cur->next;
+
+		free(prev);
+	}
+
+	discover_class_cache = NULL;
+}
+
+security_class_t string_to_security_class(const char *s)
+{
+	struct discover_class_node *node;
+
+	node = get_class_cache_entry_name(s);
+	if (node == NULL) {
+		node = discover_class(s);
+
+		if (node == NULL) {
+			errno = EINVAL;
+			return 0;
+		}
+	}
+
+	return map_class(node->value);
+}
+
+access_vector_t string_to_av_perm(security_class_t tclass, const char *s)
+{
+	struct discover_class_node *node;
+	security_class_t kclass = unmap_class(tclass);
+
+	node = get_class_cache_entry_value(kclass);
+	if (node != NULL) {
+		size_t i;
+		for (i=0; i<MAXVECTORS && node->perms[i] != NULL; i++)
+			if (strcmp(node->perms[i],s) == 0)
+				return map_perm(tclass, 1<<i);
+	}
+
+	errno = EINVAL;
+	return 0;
+}
+
+const char *security_class_to_string(security_class_t tclass)
+{
+	struct discover_class_node *node;
+
+	tclass = unmap_class(tclass);
+
+	node = get_class_cache_entry_value(tclass);
+	if (node)
+		return node->name;
+	return NULL;
+}
+
+const char *security_av_perm_to_string(security_class_t tclass,
+				       access_vector_t av)
+{
+	struct discover_class_node *node;
+	size_t i;
+
+	av = unmap_perm(tclass, av);
+	tclass = unmap_class(tclass);
+
+	node = get_class_cache_entry_value(tclass);
+	if (av && node)
+		for (i = 0; i<MAXVECTORS; i++)
+			if ((1<<i) & av)
+				return node->perms[i];
+
+	return NULL;
+}
+
+int security_av_string(security_class_t tclass, access_vector_t av, char **res)
+{
+	unsigned int i = 0;
+	size_t len = 5;
+	access_vector_t tmp = av;
+	int rc = 0;
+	const char *str;
+	char *ptr;
+
+	/* first pass computes the required length */
+	while (tmp) {
+		if (tmp & 1) {
+			str = security_av_perm_to_string(tclass, av & (1<<i));
+			if (str)
+				len += strlen(str) + 1;
+			else {
+				rc = -1;
+				errno = EINVAL;
+				goto out;
+			}
+		}
+		tmp >>= 1;
+		i++;
+	}
+
+	*res = malloc(len);
+	if (!*res) {
+		rc = -1;
+		goto out;
+	}
+
+	/* second pass constructs the string */
+	i = 0;
+	tmp = av;
+	ptr = *res;
+
+	if (!av) {
+		sprintf(ptr, "null");
+		goto out;
+	}
+
+	ptr += sprintf(ptr, "{ ");
+	while (tmp) {
+		if (tmp & 1)
+			ptr += sprintf(ptr, "%s ", security_av_perm_to_string(
+					       tclass, av & (1<<i)));
+		tmp >>= 1;
+		i++;
+	}
+	sprintf(ptr, "}");
+out:
+	return rc;
+}