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