Merge from upstream libselinux

Change-Id: I1fd35714001e3fcf9022756334cbb89611ce5c66
diff --git a/Android.mk b/Android.mk
index b530040..14c9aab 100644
--- a/Android.mk
+++ b/Android.mk
@@ -34,7 +34,8 @@
 	src/freecon.c \
 	src/init.c \
 	src/label.c \
-	src/label_file.c
+	src/label_file.c \
+	src/label_android_property.c
 
 
 common_COPY_HEADERS_TO := selinux
diff --git a/include/selinux/android.h b/include/selinux/android.h
index 883cff3..df3abc3 100644
--- a/include/selinux/android.h
+++ b/include/selinux/android.h
@@ -17,6 +17,10 @@
 				      const char *name,
 				      uid_t uid);
 
+extern int selinux_android_restorecon(const char *file);
+
+extern int selinux_android_seapp_context_reload(void);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/selinux/label.h b/include/selinux/label.h
index 1a54307..8263f32 100644
--- a/include/selinux/label.h
+++ b/include/selinux/label.h
@@ -31,6 +31,8 @@
 #define SELABEL_CTX_X		2
 /* db objects */
 #define SELABEL_CTX_DB		3
+/* Android property service contexts */
+#define SELABEL_CTX_ANDROID_PROP 4
 
 /*
  * Available options
diff --git a/src/android.c b/src/android.c
index 4768475..ad7a018 100644
--- a/src/android.c
+++ b/src/android.c
@@ -13,6 +13,7 @@
 #include <selinux/selinux.h>
 #include <selinux/context.h>
 #include <selinux/android.h>
+#include <selinux/label.h>
 #include "callbacks.h"
 #include "selinux_internal.h"
 
@@ -22,7 +23,15 @@
  * setting credentials for app processes and setting permissions
  * on app data directories.
  */
-#define SEAPP_CONTEXTS "/seapp_contexts"
+static char const * const seapp_contexts_file[] = {
+	"/data/system/seapp_contexts",
+	"/seapp_contexts",
+	0 };
+
+static const struct selinux_opt seopts[] = {
+	{ SELABEL_OPT_PATH, "/data/system/file_contexts" },
+	{ SELABEL_OPT_PATH, "/file_contexts" },
+	{ 0, NULL } };
 
 struct seapp_context {
 	/* input selectors */
@@ -83,21 +92,23 @@
 static struct seapp_context **seapp_contexts = NULL;
 static int nspec = 0;
 
-static void seapp_context_init(void)
+int selinux_android_seapp_context_reload(void)
 {
-	FILE *fp;
+	FILE *fp = NULL;
 	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;
+	int i = 0, ret;
 
-	fp = fopen(path, "r");
+	while ((fp==NULL) && seapp_contexts_file[i])
+		fp = fopen(seapp_contexts_file[i++], "r");
+
 	if (!fp) {
-		selinux_log(SELINUX_ERROR, "%s:  could not open %s", __FUNCTION__, path);
-		return;
+		selinux_log(SELINUX_ERROR, "%s:  could not open any seapp_contexts file", __FUNCTION__);
+		return -1;
 	}
 
 	nspec = 0;
@@ -216,20 +227,30 @@
 	}
 #endif
 
+	ret = 0;
+
 out:
 	fclose(fp);
-	return;
+	return ret;
 
 err:
 	selinux_log(SELINUX_ERROR, "%s:  Error reading %s, line %u, name %s, value %s\n",
-		    __FUNCTION__, path, lineno, name, value);
+		    __FUNCTION__, seapp_contexts_file[i - 1], lineno, name, value);
+	ret = -1;
 	goto out;
 oom:
 	selinux_log(SELINUX_ERROR, 
 		    "%s:  Out of memory\n", __FUNCTION__);
+	ret = -1;
 	goto out;
 }
 
+
+static void seapp_context_init(void)
+{
+        selinux_android_seapp_context_reload();
+}
+
 static pthread_once_t once = PTHREAD_ONCE_INIT;
 
 int selinux_android_setfilecon(const char *pkgdir,
@@ -463,3 +484,67 @@
 	goto out;
 }
 
+static struct selabel_handle *sehandle = NULL;
+
+static void file_context_init(void)
+{
+	int i = 0;
+
+	sehandle = NULL;
+	while ((sehandle == NULL) && seopts[i].value) {
+		sehandle = selabel_open(SELABEL_CTX_FILE, &seopts[i], 1);
+		i++;
+	}
+
+	if (!sehandle)
+		selinux_log(SELINUX_ERROR,"%s: Error getting sehandle label (%s)\n",
+			    __FUNCTION__, strerror(errno));
+}
+
+static pthread_once_t fc_once = PTHREAD_ONCE_INIT;
+
+int selinux_android_restorecon(const char *pathname)
+{
+
+	__selinux_once(fc_once, file_context_init);
+
+	int ret;
+
+	if (!sehandle)
+		goto bail;
+
+	struct stat sb;
+
+	if (lstat(pathname, &sb) < 0)
+		goto err;
+
+	char *oldcontext, *newcontext;
+
+	if (lgetfilecon(pathname, &oldcontext) < 0)
+		goto err;
+
+	if (selabel_lookup(sehandle, &newcontext, pathname, sb.st_mode) < 0)
+		goto err;
+
+	if (strcmp(newcontext, "<<none>>") && strcmp(oldcontext, newcontext))
+		if (lsetfilecon(pathname, newcontext) < 0)
+			goto err;
+
+	ret = 0;
+out:
+	if (oldcontext)
+		freecon(oldcontext);
+	if (newcontext)
+		freecon(newcontext);
+
+	return ret;
+
+err:
+	selinux_log(SELINUX_ERROR,
+		    "%s:  Error restoring context for %s (%s)\n",
+		    __FUNCTION__, pathname, strerror(errno));
+
+bail:
+	ret = -1;
+	goto out;
+}
diff --git a/src/label.c b/src/label.c
index c448e3d..490d832 100644
--- a/src/label.c
+++ b/src/label.c
@@ -21,6 +21,10 @@
 
 static selabel_initfunc initfuncs[] = {
 	&selabel_file_init,
+	NULL,
+	NULL,
+	NULL,
+	&selabel_property_init,
 };
 
 /*
@@ -67,6 +71,9 @@
 		goto out;
 	}
 
+	if (initfuncs[backend] == NULL)
+		goto out;
+
 	rec = (struct selabel_handle *)malloc(sizeof(*rec));
 	if (!rec)
 		goto out;
diff --git a/src/label_android_property.c b/src/label_android_property.c
new file mode 100644
index 0000000..cf73ec1
--- /dev/null
+++ b/src/label_android_property.c
@@ -0,0 +1,296 @@
+/*
+ * Property Service contexts backend for labeling Android 
+ * property keys
+ */
+
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "callbacks.h"
+#include "label_internal.h"
+
+/* A property security context specification. */
+typedef struct spec {
+	struct selabel_lookup_rec lr;	 /* holds contexts for lookup result */
+	char *property_key;	         /* property key string */
+} spec_t;
+
+/* Our stored configuration */
+struct saved_data {
+	/*
+	 * The array of specifications is sorted for longest
+	 * prefix match
+	 */
+	spec_t *spec_arr;
+	unsigned int nspec; /* total number of specifications */
+};
+
+static int cmp(const void *A, const void *B)
+{
+	const struct spec *sp1 = A, *sp2 = B;
+
+	if (strncmp(sp1->property_key,"*",1) == 0)
+		return 1;
+	if (strncmp(sp2->property_key,"*",1) == 0)
+		return -1;
+
+	size_t L1 = strlen(sp1->property_key);
+	size_t L2 = strlen(sp2->property_key);
+
+	return (L1 < L2) - (L1 > L2);
+}
+
+/*
+ * 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].property_key, curr_spec->property_key))) {
+				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->property_key,
+						 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->property_key);
+				}
+			}
+		}
+	}
+	return rc;
+}
+
+static int process_line(struct selabel_handle *rec,
+			const char *path, char *line_buf, 
+			int pass, unsigned lineno)
+{
+	int items, len;
+	char buf1[BUFSIZ], buf2[BUFSIZ];
+	char *buf_p, *prop = buf1, *context = buf2;
+	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", prop, context);
+	if (items != 2) {
+		selinux_log(SELINUX_WARNING,
+			    "%s:  line %d is missing fields, skipping\n", path,
+			    lineno);
+		return 0;
+	}
+
+	if (pass == 1) {
+		/* On the second pass, process and store the specification in spec. */
+		spec_arr[nspec].property_key = strdup(prop);
+		if (!spec_arr[nspec].property_key) {
+			selinux_log(SELINUX_WARNING,
+				    "%s:  out of memory at line %d on prop %s\n",
+				    path, lineno, prop);
+		return -1;
+	    
+		}
+
+		spec_arr[nspec].lr.ctx_raw = strdup(context);
+		if (!spec_arr[nspec].lr.ctx_raw) {
+			selinux_log(SELINUX_WARNING,
+				    "%s:  out of memory at line %d on context %s\n",
+				    path, lineno, context);
+		return -1;
+		}
+     	}
+
+	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;
+	FILE *fp;
+	char line_buf[BUFSIZ];
+	unsigned int lineno = 0, maxnspec, pass;
+	int status = -1;
+	struct stat sb;
+
+	/* Process arguments */
+	while (n--)
+		switch (opts[n].type) {
+		case SELABEL_OPT_PATH:
+			path = opts[n].value;
+			break;
+		default:
+			selinux_log(SELINUX_WARNING,
+				    "Argument type (%d) not recognized. Skipping\n", opts[n].type);
+			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;
+	}
+
+	/*
+	 * Two passes of the specification file. First is to get the size.
+	 * After the first pass, the spec array is malloced to the appropriate 
+	 * size. Second pass is to populate the spec array and check for 
+	 * dups.
+	 */
+	maxnspec = UINT_MAX / sizeof(spec_t);
+	for (pass = 0; pass < 2; pass++) {
+		data->nspec = 0;
+
+		while (fgets(line_buf, sizeof line_buf - 1, fp)
+		       && data->nspec < maxnspec) {
+			if (process_line(rec, path, line_buf, pass, ++lineno) != 0) {
+				goto finish;
+			}
+		}
+
+		if (pass == 1) {
+			status = nodups_specs(data, path);
+	    
+			if (status)
+				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);
+		}
+	}
+
+	qsort(data->spec_arr, data->nspec, sizeof(struct spec), cmp);
+
+	status = 0;
+finish:
+	fclose(fp);
+	return status;
+}
+
+/*
+ * Backend interface routines
+ */
+static void closef(struct selabel_handle *rec)
+{
+	struct saved_data *data = (struct saved_data *)rec->data;
+	struct spec *spec;
+	unsigned int i;
+
+	for (i = 0; i < data->nspec; i++) {
+		spec = &data->spec_arr[i];
+		free(spec->property_key);
+		free(spec->lr.ctx_raw);
+		free(spec->lr.ctx_trans);
+	}
+
+	if (data->spec_arr)
+		free(data->spec_arr);
+	
+	free(data);
+}
+
+static struct selabel_lookup_rec *lookup(struct selabel_handle *rec, 
+					 const char *key, 
+					 int __attribute__((unused)) type)
+{
+	struct saved_data *data = (struct saved_data *)rec->data;
+	spec_t *spec_arr = data->spec_arr;
+	unsigned int i;
+	struct selabel_lookup_rec *ret = NULL;
+
+	if (!data->nspec) {
+		errno = ENOENT;
+		goto finish;
+	}
+
+	for (i = 0; i < data->nspec; i++) {
+		if (strncmp(spec_arr[i].property_key, key, 
+		    strlen(spec_arr[i].property_key)) == 0) {
+			break;
+		}
+		if (strncmp(spec_arr[i].property_key, "*", 1) == 0)
+			break;
+	}
+
+	if (i >= data->nspec) {
+		/* No matching specification. */
+		errno = ENOENT;
+		goto finish;
+	}
+
+	ret = &spec_arr[i].lr;
+
+finish:
+	return ret;
+}
+
+static void stats(struct selabel_handle __attribute__((unused)) *rec)
+{
+	selinux_log(SELINUX_WARNING, "'stats' functionality not implemented.\n");
+}
+
+int selabel_property_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
index 37a21db..5192d4d 100644
--- a/src/label_internal.h
+++ b/src/label_internal.h
@@ -25,6 +25,8 @@
 		   unsigned nopts) hidden;
 int selabel_db_init(struct selabel_handle *rec,
 		    struct selinux_opt *opts, unsigned nopts) hidden;
+int selabel_property_init(struct selabel_handle *rec,
+		    struct selinux_opt *opts, unsigned nopts) hidden;
 
 /*
  * Labeling internal structures