2012-05-31 21:09:29 +00:00
|
|
|
/*
|
|
|
|
* 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 {
|
2012-05-31 21:16:16 +00:00
|
|
|
struct selabel_lookup_rec lr; /* holds contexts for lookup result */
|
|
|
|
char *property_key; /* property key string */
|
2012-05-31 21:09:29 +00:00
|
|
|
} spec_t;
|
|
|
|
|
|
|
|
/* Our stored configuration */
|
|
|
|
struct saved_data {
|
|
|
|
/*
|
|
|
|
* The array of specifications is sorted for longest
|
|
|
|
* prefix match
|
|
|
|
*/
|
|
|
|
spec_t *spec_arr;
|
2012-05-31 21:16:16 +00:00
|
|
|
unsigned int nspec; /* total number of specifications */
|
2012-05-31 21:09:29 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static int cmp(const void *A, const void *B)
|
|
|
|
{
|
|
|
|
const struct spec *sp1 = A, *sp2 = B;
|
|
|
|
|
2012-05-31 21:16:16 +00:00
|
|
|
if (strncmp(sp1->property_key, "*", 1) == 0)
|
2012-05-31 21:09:29 +00:00
|
|
|
return 1;
|
2012-05-31 21:16:16 +00:00
|
|
|
if (strncmp(sp2->property_key, "*", 1) == 0)
|
2012-05-31 21:09:29 +00:00
|
|
|
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++) {
|
2012-05-31 21:16:16 +00:00
|
|
|
if (!strcmp(spec_arr[jj].property_key, curr_spec->property_key)) {
|
2012-05-31 21:09:29 +00:00
|
|
|
rc = -1;
|
|
|
|
errno = EINVAL;
|
2012-05-31 21:16:16 +00:00
|
|
|
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);
|
2012-05-31 21:09:29 +00:00
|
|
|
} else {
|
2012-05-31 21:16:16 +00:00
|
|
|
selinux_log(SELINUX_ERROR,
|
|
|
|
"%s: Multiple same specifications for %s.\n",
|
|
|
|
path,
|
|
|
|
curr_spec->property_key);
|
2012-05-31 21:09:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int process_line(struct selabel_handle *rec,
|
2012-05-31 21:16:16 +00:00
|
|
|
const char *path, char *line_buf,
|
2012-05-31 21:09:29 +00:00
|
|
|
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);
|
2012-05-31 21:16:16 +00:00
|
|
|
return -1;
|
|
|
|
|
2012-05-31 21:09:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2012-05-31 21:16:16 +00:00
|
|
|
return -1;
|
2012-05-31 21:09:29 +00:00
|
|
|
}
|
2012-05-31 21:16:16 +00:00
|
|
|
}
|
2012-05-31 21:09:29 +00:00
|
|
|
|
|
|
|
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) {
|
2012-05-31 21:16:16 +00:00
|
|
|
if (process_line(rec, path, line_buf, pass, ++lineno) != 0)
|
2012-05-31 21:09:29 +00:00
|
|
|
goto finish;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pass == 1) {
|
|
|
|
status = nodups_specs(data, path);
|
2012-05-31 21:16:16 +00:00
|
|
|
|
2012-05-31 21:09:29 +00:00
|
|
|
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;
|
|
|
|
|
2012-05-31 21:16:16 +00:00
|
|
|
memset(data->spec_arr, 0, sizeof(spec_t) * data->nspec);
|
2012-05-31 21:09:29 +00:00
|
|
|
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);
|
2012-05-31 21:16:16 +00:00
|
|
|
|
2012-05-31 21:09:29 +00:00
|
|
|
free(data);
|
|
|
|
}
|
|
|
|
|
2012-05-31 21:16:16 +00:00
|
|
|
static struct selabel_lookup_rec *lookup(struct selabel_handle *rec,
|
|
|
|
const char *key,
|
|
|
|
int __attribute__ ((unused)) type)
|
2012-05-31 21:09:29 +00:00
|
|
|
{
|
|
|
|
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++) {
|
2012-05-31 21:16:16 +00:00
|
|
|
if (strncmp(spec_arr[i].property_key, key,
|
|
|
|
strlen(spec_arr[i].property_key)) == 0) {
|
2012-05-31 21:09:29 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-05-31 21:16:16 +00:00
|
|
|
static void stats(struct selabel_handle __attribute__ ((unused)) * rec)
|
2012-05-31 21:09:29 +00:00
|
|
|
{
|
|
|
|
selinux_log(SELINUX_WARNING, "'stats' functionality not implemented.\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
int selabel_property_init(struct selabel_handle *rec, struct selinux_opt *opts,
|
2012-05-31 21:16:16 +00:00
|
|
|
unsigned nopts)
|
2012-05-31 21:09:29 +00:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|