Home | History | Annotate | Download | only in src
      1 /*
      2  * Property Service contexts backend for labeling Android
      3  * property keys
      4  */
      5 
      6 #include <stdarg.h>
      7 #include <string.h>
      8 #include <ctype.h>
      9 #include <errno.h>
     10 #include <limits.h>
     11 #include <sys/types.h>
     12 #include <sys/stat.h>
     13 #include "callbacks.h"
     14 #include "label_internal.h"
     15 
     16 /* A property security context specification. */
     17 typedef struct spec {
     18 	struct selabel_lookup_rec lr;	 /* holds contexts for lookup result */
     19 	char *property_key;	         /* property key string */
     20 } spec_t;
     21 
     22 /* Our stored configuration */
     23 struct saved_data {
     24 	/*
     25 	 * The array of specifications is sorted for longest
     26 	 * prefix match
     27 	 */
     28 	spec_t *spec_arr;
     29 	unsigned int nspec; /* total number of specifications */
     30 };
     31 
     32 static int cmp(const void *A, const void *B)
     33 {
     34 	const struct spec *sp1 = A, *sp2 = B;
     35 
     36 	if (strncmp(sp1->property_key,"*",1) == 0)
     37 		return 1;
     38 	if (strncmp(sp2->property_key,"*",1) == 0)
     39 		return -1;
     40 
     41 	size_t L1 = strlen(sp1->property_key);
     42 	size_t L2 = strlen(sp2->property_key);
     43 
     44 	return (L1 < L2) - (L1 > L2);
     45 }
     46 
     47 /*
     48  * Warn about duplicate specifications.
     49  */
     50 static int nodups_specs(struct saved_data *data, const char *path)
     51 {
     52 	int rc = 0;
     53 	unsigned int ii, jj;
     54 	struct spec *curr_spec, *spec_arr = data->spec_arr;
     55 
     56 	for (ii = 0; ii < data->nspec; ii++) {
     57 		curr_spec = &spec_arr[ii];
     58 		for (jj = ii + 1; jj < data->nspec; jj++) {
     59 			if ((!strcmp(spec_arr[jj].property_key, curr_spec->property_key))) {
     60 				rc = -1;
     61 				errno = EINVAL;
     62 				if (strcmp
     63 				    (spec_arr[jj].lr.ctx_raw,
     64 				     curr_spec->lr.ctx_raw)) {
     65 					selinux_log
     66 						(SELINUX_ERROR,
     67 						 "%s: Multiple different specifications for %s  (%s and %s).\n",
     68 						 path, curr_spec->property_key,
     69 						 spec_arr[jj].lr.ctx_raw,
     70 						 curr_spec->lr.ctx_raw);
     71 				} else {
     72 					selinux_log
     73 						(SELINUX_ERROR,
     74 						 "%s: Multiple same specifications for %s.\n",
     75 						 path, curr_spec->property_key);
     76 				}
     77 			}
     78 		}
     79 	}
     80 	return rc;
     81 }
     82 
     83 static int process_line(struct selabel_handle *rec,
     84 			const char *path, char *line_buf,
     85 			int pass, unsigned lineno)
     86 {
     87 	int items, len;
     88 	char buf1[BUFSIZ], buf2[BUFSIZ];
     89 	char *buf_p, *prop = buf1, *context = buf2;
     90 	struct saved_data *data = (struct saved_data *)rec->data;
     91 	spec_t *spec_arr = data->spec_arr;
     92 	unsigned int nspec = data->nspec;
     93 
     94 	len = strlen(line_buf);
     95 	if (line_buf[len - 1] == '\n')
     96 		line_buf[len - 1] = 0;
     97 	buf_p = line_buf;
     98 	while (isspace(*buf_p))
     99 		buf_p++;
    100 	/* Skip comment lines and empty lines. */
    101 	if (*buf_p == '#' || *buf_p == 0)
    102 		return 0;
    103 	items = sscanf(line_buf, "%255s %255s", prop, context);
    104 	if (items != 2) {
    105 		selinux_log(SELINUX_WARNING,
    106 			    "%s:  line %d is missing fields, skipping\n", path,
    107 			    lineno);
    108 		return 0;
    109 	}
    110 
    111 	if (pass == 1) {
    112 		/* On the second pass, process and store the specification in spec. */
    113 		spec_arr[nspec].property_key = strdup(prop);
    114 		if (!spec_arr[nspec].property_key) {
    115 			selinux_log(SELINUX_WARNING,
    116 				    "%s:  out of memory at line %d on prop %s\n",
    117 				    path, lineno, prop);
    118 		return -1;
    119 
    120 		}
    121 
    122 		spec_arr[nspec].lr.ctx_raw = strdup(context);
    123 		if (!spec_arr[nspec].lr.ctx_raw) {
    124 			selinux_log(SELINUX_WARNING,
    125 				    "%s:  out of memory at line %d on context %s\n",
    126 				    path, lineno, context);
    127 		return -1;
    128 		}
    129 
    130 		if (rec->validating) {
    131 		        if (selabel_validate(rec, &spec_arr[nspec].lr) < 0) {
    132 			        selinux_log(SELINUX_WARNING,
    133 					    "%s:  line %d has invalid context %s\n",
    134 					    path, lineno, spec_arr[nspec].lr.ctx_raw);
    135 			}
    136 		}
    137      	}
    138 
    139 	data->nspec = ++nspec;
    140 	return 0;
    141 }
    142 
    143 static int init(struct selabel_handle *rec, const struct selinux_opt *opts,
    144 		unsigned n)
    145 {
    146 	struct saved_data *data = (struct saved_data *)rec->data;
    147 	const char *path = NULL;
    148 	FILE *fp;
    149 	char line_buf[BUFSIZ];
    150 	unsigned int lineno = 0, maxnspec, pass;
    151 	int status = -1;
    152 	struct stat sb;
    153 
    154 	/* Process arguments */
    155 	while (n--)
    156 		switch (opts[n].type) {
    157 		case SELABEL_OPT_PATH:
    158 			path = opts[n].value;
    159 			break;
    160 		}
    161 
    162 	/* Open the specification file. */
    163 	if ((fp = fopen(path, "r")) == NULL)
    164 		return -1;
    165 
    166 	if (fstat(fileno(fp), &sb) < 0)
    167 		return -1;
    168 	if (!S_ISREG(sb.st_mode)) {
    169 		errno = EINVAL;
    170 		return -1;
    171 	}
    172 
    173 	/*
    174 	 * Two passes of the specification file. First is to get the size.
    175 	 * After the first pass, the spec array is malloced to the appropriate
    176 	 * size. Second pass is to populate the spec array and check for
    177 	 * dups.
    178 	 */
    179 	maxnspec = UINT_MAX / sizeof(spec_t);
    180 	for (pass = 0; pass < 2; pass++) {
    181 		data->nspec = 0;
    182 
    183 		while (fgets(line_buf, sizeof line_buf - 1, fp)
    184 		       && data->nspec < maxnspec) {
    185 			if (process_line(rec, path, line_buf, pass, ++lineno) != 0) {
    186 				goto finish;
    187 			}
    188 		}
    189 
    190 		if (pass == 1) {
    191 			status = nodups_specs(data, path);
    192 
    193 			if (status)
    194 				goto finish;
    195 		}
    196 
    197 		if (pass == 0) {
    198 
    199 			if (data->nspec == 0) {
    200 				status = 0;
    201 				goto finish;
    202 			}
    203 
    204 			if (NULL == (data->spec_arr =
    205 				     malloc(sizeof(spec_t) * data->nspec)))
    206 				goto finish;
    207 
    208 			memset(data->spec_arr, 0, sizeof(spec_t)*data->nspec);
    209 			maxnspec = data->nspec;
    210 			rewind(fp);
    211 		}
    212 	}
    213 
    214 	qsort(data->spec_arr, data->nspec, sizeof(struct spec), cmp);
    215 
    216 	status = 0;
    217 finish:
    218 	fclose(fp);
    219 	return status;
    220 }
    221 
    222 /*
    223  * Backend interface routines
    224  */
    225 static void closef(struct selabel_handle *rec)
    226 {
    227 	struct saved_data *data = (struct saved_data *)rec->data;
    228 	struct spec *spec;
    229 	unsigned int i;
    230 
    231 	for (i = 0; i < data->nspec; i++) {
    232 		spec = &data->spec_arr[i];
    233 		free(spec->property_key);
    234 		free(spec->lr.ctx_raw);
    235 		free(spec->lr.ctx_trans);
    236 	}
    237 
    238 	if (data->spec_arr)
    239 		free(data->spec_arr);
    240 
    241 	free(data);
    242 }
    243 
    244 static struct selabel_lookup_rec *lookup(struct selabel_handle *rec,
    245 					 const char *key,
    246 					 int __attribute__((unused)) type)
    247 {
    248 	struct saved_data *data = (struct saved_data *)rec->data;
    249 	spec_t *spec_arr = data->spec_arr;
    250 	unsigned int i;
    251 	struct selabel_lookup_rec *ret = NULL;
    252 
    253 	if (!data->nspec) {
    254 		errno = ENOENT;
    255 		goto finish;
    256 	}
    257 
    258 	for (i = 0; i < data->nspec; i++) {
    259 		if (strncmp(spec_arr[i].property_key, key,
    260 		    strlen(spec_arr[i].property_key)) == 0) {
    261 			break;
    262 		}
    263 		if (strncmp(spec_arr[i].property_key, "*", 1) == 0)
    264 			break;
    265 	}
    266 
    267 	if (i >= data->nspec) {
    268 		/* No matching specification. */
    269 		errno = ENOENT;
    270 		goto finish;
    271 	}
    272 
    273 	ret = &spec_arr[i].lr;
    274 
    275 finish:
    276 	return ret;
    277 }
    278 
    279 static void stats(struct selabel_handle __attribute__((unused)) *rec)
    280 {
    281 	selinux_log(SELINUX_WARNING, "'stats' functionality not implemented.\n");
    282 }
    283 
    284 int selabel_property_init(struct selabel_handle *rec,
    285 			  const struct selinux_opt *opts,
    286 			  unsigned nopts)
    287 {
    288 	struct saved_data *data;
    289 
    290 	data = (struct saved_data *)malloc(sizeof(*data));
    291 	if (!data)
    292 		return -1;
    293 	memset(data, 0, sizeof(*data));
    294 
    295 	rec->data = data;
    296 	rec->func_close = &closef;
    297 	rec->func_stats = &stats;
    298 	rec->func_lookup = &lookup;
    299 
    300 	return init(rec, opts, nopts);
    301 }
    302