Clean up libselinux logic for looking up seapp contexts entries.

Re-factor the logic shared by selinux_android_setfilecon2 and
selinux_android_setcontext into a common helper and replace the
use of getpwuid and username string parsing with direct use of
android_filesystem_config.h definitions.  Also map isolated UIDs
to a separate isolated key so that we can label them differently
in the future if desired.

Change-Id: If2f9def21222588b440a6cedcceec0434f6797fd
Signed-off-by: Stephen Smalley <sds@tycho.nsa.gov>
diff --git a/src/android.c b/src/android.c
index af6c79c..1ed6184 100644
--- a/src/android.c
+++ b/src/android.c
@@ -17,6 +17,7 @@
 #include <selinux/android.h>
 #include <selinux/label.h>
 #include <selinux/avc.h>
+#include <private/android_filesystem_config.h>
 #include "callbacks.h"
 #include "selinux_internal.h"
 
@@ -272,70 +273,48 @@
 
 static pthread_once_t once = PTHREAD_ONCE_INIT;
 
-int selinux_android_setfilecon2(const char *pkgdir,
-				const char *pkgname,
+#define SEAPP_TYPE 1
+#define SEAPP_DOMAIN 2
+static int seapp_context_lookup(int kind,
+				uid_t uid,
+				int isSystemServer,
 				const char *seinfo,
-				uid_t uid)
+				const char *pkgname,
+				context_t ctx)
 {
-	const char *username;
-	char *orig_ctx_str = NULL, *ctx_str, *end = NULL;
-	context_t ctx = NULL;
+	const char *username = NULL;
+	char *end = NULL;
 	struct passwd *pw;
 	struct seapp_context *cur;
-	int i, rc;
-	unsigned long id = 0;
+	int i;
+	size_t n;
+	uid_t appid = 0;
 
-	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;
-	username = pw->pw_name;
-
-	if (!strncmp(username, "app_", 4)) {
-		id = strtoul(username + 4, NULL, 10);
-		if (id >= MLS_CATS)
-			goto err;
-	} else if (username[0] == 'u' && isdigit(username[1])) {
-		unsigned long unused;
-		unused = strtoul(username+1, &end, 10);
-		if (end[0] != '_' || end[1] == 0)
-			goto err;
-		if (end[1] == 'a' && isdigit(end[2])) {
-			id = strtoul(end + 2, NULL, 10);
-			if (id >= MLS_CATS/2)
-				goto err;
-			/* regular app UID */
-			username = "app_";
-		} else if (end[1] == 'i' && isdigit(end[2])) {
-			id = strtoul(end + 2, NULL, 10);
-			if (id >= MLS_CATS/2)
-				goto err;
-			/* isolated service */
-			id += MLS_CATS/2;
-			username = "app_";
-		} else {
-			username = end + 1;
+	appid = uid % AID_USER;
+	if (appid < AID_APP) {
+		for (n = 0; n < android_id_count; n++) {
+			if (android_ids[n].aid == appid) {
+				username = android_ids[n].name;
+				break;
+			}
 		}
+		if (!username)
+			goto err;
+	} else if (appid < AID_ISOLATED_START) {
+		username = "app_";
+		appid -= AID_APP;
+	} else {
+		username = "isolated";
+		appid -= AID_ISOLATED_START;
 	}
 
+	if (appid >= MLS_CATS)
+		goto err;
+
 	for (i = 0; i < nspec; i++) {
 		cur = seapp_contexts[i];
 
-		/* isSystemServer=true is only for app process labeling. */
-		if (cur->isSystemServer)
+		if (cur->isSystemServer != isSystemServer)
 			continue;
 
 		if (cur->user) {
@@ -358,7 +337,9 @@
 				continue;
 		}
 
-		if (!cur->type)
+		if (kind == SEAPP_TYPE && !cur->type)
+			continue;
+		else if (kind == SEAPP_DOMAIN && !cur->domain)
 			continue;
 
 		if (cur->sebool) {
@@ -372,13 +353,18 @@
 			}
 		}
 
-		if (context_type_set(ctx, cur->type))
-			goto oom;
+		if (kind == SEAPP_TYPE) {
+			if (context_type_set(ctx, cur->type))
+				goto oom;
+		} else if (kind == SEAPP_DOMAIN) {
+			if (context_type_set(ctx, cur->domain))
+				goto oom;
+		}
 
 		if (cur->levelFromUid) {
 			char level[255];
 			snprintf(level, sizeof level, "%s:c%lu",
-				 context_range_get(ctx), id);
+				 context_range_get(ctx), appid);
 			if (context_range_set(ctx, level))
 				goto oom;
 		} else if (cur->level) {
@@ -389,6 +375,55 @@
 		break;
 	}
 
+	if (kind == SEAPP_DOMAIN && 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, pkgname);
+
+		if (security_getenforce() == 1)
+			goto err;
+	}
+
+	return 0;
+err:
+	return -1;
+oom:
+	return -2;
+}
+
+int selinux_android_setfilecon2(const char *pkgdir,
+				const char *pkgname,
+				const char *seinfo,
+				uid_t uid)
+{
+	char *orig_ctx_str = NULL, *ctx_str;
+	context_t ctx = NULL;
+	int 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;
+
+	rc = seapp_context_lookup(SEAPP_TYPE, uid, 0, seinfo, pkgname, ctx);
+	if (rc == -1)
+		goto err;
+	else if (rc == -2)
+		goto oom;
+
 	ctx_str = context_str(ctx);
 	if (!ctx_str)
 		goto oom;
@@ -431,13 +466,9 @@
 			       const char *seinfo,
 			       const char *pkgname)
 {
-	const char *username;
-	char *orig_ctx_str = NULL, *ctx_str, *end = NULL;
+	char *orig_ctx_str = NULL, *ctx_str;
 	context_t ctx = NULL;
-	unsigned long id = 0;
-	struct passwd *pw;
-	struct seapp_context *cur;
-	int i, rc;
+	int rc;
 
 	if (is_selinux_enabled() <= 0)
 		return 0;
@@ -453,104 +484,11 @@
 	if (!ctx)
 		goto oom;
 
-	pw = getpwuid(uid);
-	if (!pw)
+	rc = seapp_context_lookup(SEAPP_DOMAIN, uid, isSystemServer, seinfo, pkgname, ctx);
+	if (rc == -1)
 		goto err;
-	username = pw->pw_name;
-
-	if (!strncmp(username, "app_", 4)) {
-		id = strtoul(username + 4, NULL, 10);
-		if (id >= MLS_CATS)
-			goto err;
-	} else if (username[0] == 'u' && isdigit(username[1])) {
-		unsigned long unused;
-		unused = strtoul(username+1, &end, 10);
-		if (end[0] != '_' || end[1] == 0)
-			goto err;
-		if (end[1] == 'a' && isdigit(end[2])) {
-			id = strtoul(end + 2, NULL, 10);
-			if (id >= MLS_CATS/2)
-				goto err;
-			/* regular app UID */
-			username = "app_";
-		} else if (end[1] == 'i' && isdigit(end[2])) {
-			id = strtoul(end + 2, NULL, 10);
-			if (id >= MLS_CATS/2)
-				goto err;
-			/* isolated service */
-			id += MLS_CATS/2;
-			username = "app_";
-		} else {
-			username = end + 1;
-		}
-	}
-
-	for (i = 0; i < nspec; i++) {
-		cur = seapp_contexts[i];
-
-		if (cur->isSystemServer != isSystemServer)
-			continue;
-		if (cur->user) {
-			if (cur->prefix) {
-				if (strncasecmp(username, cur->user, cur->len-1))
-					continue;
-			} else {
-				if (strcasecmp(username, cur->user))
-					continue;
-			}
-		}
-		if (cur->seinfo) {
-			if (!seinfo || strcasecmp(seinfo, cur->seinfo))
-				continue;
-		}
-		if (cur->name) {
-			if (!pkgname || strcasecmp(pkgname, cur->name))
-				continue;
-		}
-
-		if (!cur->domain)
-			continue;
-
-		if (cur->sebool) {
-			int value = security_get_boolean_active(cur->sebool);
-			if (value == 0)
-				continue;
-			else if (value == -1) {
-				selinux_log(SELINUX_ERROR, \
-				"Could not find boolean: %s ", cur->sebool);
-                                goto err;
-                        }
-                }
-
-		if (context_type_set(ctx, cur->domain))
-			goto oom;
-
-		if (cur->levelFromUid) {
-			char level[255];
-			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, pkgname);
-
-		rc = (security_getenforce() == 0) ? 0 : -1;
-		goto out;
-	}
+	else if (rc == -2)
+		goto oom;
 
 	ctx_str = context_str(ctx);
 	if (!ctx_str)