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. Return error on different specifications.
     49  * TODO: Remove duplicate specifications. Move duplicate check to after sort
     50  * to improve performance.
     51  */
     52 static int nodups_specs(struct saved_data *data)
     53 {
     54 	int rc = 0;
     55 	unsigned int ii, jj;
     56 	struct spec *curr_spec, *spec_arr = data->spec_arr;
     57 
     58 	for (ii = 0; ii < data->nspec; ii++) {
     59 		curr_spec = &spec_arr[ii];
     60 		for (jj = ii + 1; jj < data->nspec; jj++) {
     61 			if (!strcmp(spec_arr[jj].property_key,
     62 					    curr_spec->property_key)) {
     63 				if (strcmp(spec_arr[jj].lr.ctx_raw,
     64 						    curr_spec->lr.ctx_raw)) {
     65 					rc = -1;
     66 					errno = EINVAL;
     67 					selinux_log
     68 						(SELINUX_ERROR,
     69 						 "Multiple different specifications for %s  (%s and %s).\n",
     70 						 curr_spec->property_key,
     71 						 spec_arr[jj].lr.ctx_raw,
     72 						 curr_spec->lr.ctx_raw);
     73 				} else {
     74 					selinux_log
     75 						(SELINUX_WARNING,
     76 						 "Multiple same specifications for %s.\n",
     77 						 curr_spec->property_key);
     78 				}
     79 			}
     80 		}
     81 	}
     82 	return rc;
     83 }
     84 
     85 static int process_line(struct selabel_handle *rec,
     86 			const char *path, char *line_buf,
     87 			int pass, unsigned lineno)
     88 {
     89 	int items;
     90 	char *prop = NULL, *context = NULL;
     91 	struct saved_data *data = (struct saved_data *)rec->data;
     92 	spec_t *spec_arr = data->spec_arr;
     93 	unsigned int nspec = data->nspec;
     94 	const char *errbuf = NULL;
     95 
     96 	items = read_spec_entries(line_buf, &errbuf, 2, &prop, &context);
     97 	if (items < 0) {
     98 		items = errno;
     99 		selinux_log(SELINUX_ERROR,
    100 			"%s:  line %u error due to: %s\n", path,
    101 			lineno, errbuf ?: strerror(errno));
    102 		errno = items;
    103 		return -1;
    104 	}
    105 
    106 	if (items == 0)
    107 		return items;
    108 
    109 	if (items != 2) {
    110 		selinux_log(SELINUX_ERROR,
    111 			    "%s:  line %u is missing fields\n", path,
    112 			    lineno);
    113 		free(prop);
    114 		errno = EINVAL;
    115 		return -1;
    116 	}
    117 
    118 	if (pass == 0) {
    119 		free(prop);
    120 		free(context);
    121 	} else if (pass == 1) {
    122 		/* On the second pass, process and store the specification in spec. */
    123 		spec_arr[nspec].property_key = prop;
    124 		spec_arr[nspec].lr.ctx_raw = context;
    125 
    126 		if (rec->validating) {
    127 			if (selabel_validate(rec, &spec_arr[nspec].lr) < 0) {
    128 				selinux_log(SELINUX_ERROR,
    129 					    "%s:  line %u has invalid context %s\n",
    130 					    path, lineno, spec_arr[nspec].lr.ctx_raw);
    131 				errno = EINVAL;
    132 				return -1;
    133 			}
    134 		}
    135 
    136 		data->nspec = ++nspec;
    137 	}
    138 
    139 	return 0;
    140 }
    141 
    142 static int process_file(struct selabel_handle *rec, const char *path)
    143 {
    144 	struct saved_data *data = (struct saved_data *)rec->data;
    145 	char line_buf[BUFSIZ];
    146 	unsigned int lineno, maxnspec, pass;
    147 	struct stat sb;
    148 	FILE *fp;
    149 	int status = -1;
    150 	unsigned int nspec;
    151 	spec_t *spec_arr;
    152 
    153 	/* Open the specification file. */
    154 	if ((fp = fopen(path, "re")) == NULL)
    155 		return -1;
    156 
    157 	if (fstat(fileno(fp), &sb) < 0)
    158 		goto finish;
    159 
    160 	errno = EINVAL;
    161 
    162 	if (!S_ISREG(sb.st_mode))
    163 		goto finish;
    164 
    165 	/*
    166 	 * Two passes per specification file. First is to get the size.
    167 	 * After the first pass, the spec array is malloced / realloced to
    168 	 * the appropriate size. Second pass is to populate the spec array.
    169 	 */
    170 	maxnspec = UINT_MAX / sizeof(spec_t);
    171 	for (pass = 0; pass < 2; pass++) {
    172 		nspec = 0;
    173 		lineno = 0;
    174 
    175 		while (fgets(line_buf, sizeof(line_buf) - 1, fp) &&
    176 			nspec < maxnspec) {
    177 			if (process_line(rec, path, line_buf, pass, ++lineno))
    178 				goto finish;
    179 			nspec++;
    180 		}
    181 
    182 		if (pass == 0) {
    183 			if (nspec == 0) {
    184 				status = 0;
    185 				goto finish;
    186 			}
    187 
    188 			/* grow spec array if required */
    189 			spec_arr = realloc(data->spec_arr,
    190 					(data->nspec + nspec) * sizeof(spec_t));
    191 			if (spec_arr == NULL)
    192 				goto finish;
    193 
    194 			memset(&spec_arr[data->nspec], 0, nspec * sizeof(spec_t));
    195 			data->spec_arr = spec_arr;
    196 			maxnspec = nspec;
    197 			rewind(fp);
    198 		}
    199 	}
    200 
    201 	status = digest_add_specfile(rec->digest, fp, NULL, sb.st_size, path);
    202 
    203 finish:
    204 	fclose(fp);
    205 	return status;
    206 }
    207 
    208 static void closef(struct selabel_handle *rec);
    209 
    210 static int init(struct selabel_handle *rec, const struct selinux_opt *opts,
    211 		unsigned n)
    212 {
    213 	struct saved_data *data = (struct saved_data *)rec->data;
    214 	char **paths = NULL;
    215 	size_t num_paths = 0;
    216 	int status = -1;
    217 	size_t i;
    218 
    219 	/* Process arguments */
    220 	i = n;
    221 	while (i--) {
    222 		switch (opts[i].type) {
    223 		case SELABEL_OPT_PATH:
    224 			num_paths++;
    225 			break;
    226 		}
    227 	}
    228 
    229 	if (!num_paths)
    230 		return -1;
    231 
    232 	paths = calloc(num_paths, sizeof(*paths));
    233 	if (!paths)
    234 		return -1;
    235 
    236 	rec->spec_files = paths;
    237 	rec->spec_files_len = num_paths;
    238 
    239 	i = n;
    240 	while (i--) {
    241 		switch(opts[i].type) {
    242 		case SELABEL_OPT_PATH:
    243 			*paths = strdup(opts[i].value);
    244 			if (*paths == NULL)
    245 				goto finish;
    246 			paths++;
    247 		}
    248 	}
    249 
    250 	for (i = 0; i < num_paths; i++) {
    251 		status = process_file(rec, rec->spec_files[i]);
    252 		if (status)
    253 			goto finish;
    254 	}
    255 
    256 	/* warn about duplicates after all files have been processed. */
    257 	status = nodups_specs(data);
    258 	if (status)
    259 		goto finish;
    260 
    261 	qsort(data->spec_arr, data->nspec, sizeof(struct spec), cmp);
    262 
    263 	digest_gen_hash(rec->digest);
    264 
    265 finish:
    266 	if (status)
    267 		closef(rec);
    268 
    269 	return status;
    270 }
    271 
    272 /*
    273  * Backend interface routines
    274  */
    275 static void closef(struct selabel_handle *rec)
    276 {
    277 	struct saved_data *data = (struct saved_data *)rec->data;
    278 	struct spec *spec;
    279 	unsigned int i;
    280 
    281 	if (data->spec_arr) {
    282 		for (i = 0; i < data->nspec; i++) {
    283 			spec = &data->spec_arr[i];
    284 			free(spec->property_key);
    285 			free(spec->lr.ctx_raw);
    286 			free(spec->lr.ctx_trans);
    287 		}
    288 
    289 		free(data->spec_arr);
    290 	}
    291 
    292 	free(data);
    293 }
    294 
    295 static struct selabel_lookup_rec *property_lookup(struct selabel_handle *rec,
    296 					 const char *key,
    297 					 int __attribute__((unused)) type)
    298 {
    299 	struct saved_data *data = (struct saved_data *)rec->data;
    300 	spec_t *spec_arr = data->spec_arr;
    301 	unsigned int i;
    302 	struct selabel_lookup_rec *ret = NULL;
    303 
    304 	if (!data->nspec) {
    305 		errno = ENOENT;
    306 		goto finish;
    307 	}
    308 
    309 	for (i = 0; i < data->nspec; i++) {
    310 		if (strncmp(spec_arr[i].property_key, key,
    311 			    strlen(spec_arr[i].property_key)) == 0) {
    312 			break;
    313 		}
    314 		if (strncmp(spec_arr[i].property_key, "*", 1) == 0)
    315 			break;
    316 	}
    317 
    318 	if (i >= data->nspec) {
    319 		/* No matching specification. */
    320 		errno = ENOENT;
    321 		goto finish;
    322 	}
    323 
    324 	ret = &spec_arr[i].lr;
    325 
    326 finish:
    327 	return ret;
    328 }
    329 
    330 static struct selabel_lookup_rec *service_lookup(struct selabel_handle *rec,
    331 		const char *key, int __attribute__((unused)) type)
    332 {
    333 	struct saved_data *data = (struct saved_data *)rec->data;
    334 	spec_t *spec_arr = data->spec_arr;
    335 	unsigned int i;
    336 	struct selabel_lookup_rec *ret = NULL;
    337 
    338 	if (!data->nspec) {
    339 		errno = ENOENT;
    340 		goto finish;
    341 	}
    342 
    343 	for (i = 0; i < data->nspec; i++) {
    344 		if (strcmp(spec_arr[i].property_key, key) == 0)
    345 			break;
    346 		if (strcmp(spec_arr[i].property_key, "*") == 0)
    347 			break;
    348 	}
    349 
    350 	if (i >= data->nspec) {
    351 		/* No matching specification. */
    352 		errno = ENOENT;
    353 		goto finish;
    354 	}
    355 
    356 	ret = &spec_arr[i].lr;
    357 
    358 finish:
    359 	return ret;
    360 }
    361 
    362 static void stats(struct selabel_handle __attribute__((unused)) *rec)
    363 {
    364 	selinux_log(SELINUX_WARNING, "'stats' functionality not implemented.\n");
    365 }
    366 
    367 int selabel_property_init(struct selabel_handle *rec,
    368 			  const struct selinux_opt *opts,
    369 			  unsigned nopts)
    370 {
    371 	struct saved_data *data;
    372 
    373 	data = (struct saved_data *)calloc(1, sizeof(*data));
    374 	if (!data)
    375 		return -1;
    376 
    377 	rec->data = data;
    378 	rec->func_close = &closef;
    379 	rec->func_stats = &stats;
    380 	rec->func_lookup = &property_lookup;
    381 
    382 	return init(rec, opts, nopts);
    383 }
    384 
    385 int selabel_service_init(struct selabel_handle *rec,
    386 		const struct selinux_opt *opts, unsigned nopts)
    387 {
    388 	struct saved_data *data;
    389 
    390 	data = (struct saved_data *)calloc(1, sizeof(*data));
    391 	if (!data)
    392 		return -1;
    393 
    394 	rec->data = data;
    395 	rec->func_close = &closef;
    396 	rec->func_stats = &stats;
    397 	rec->func_lookup = &service_lookup;
    398 
    399 	return init(rec, opts, nopts);
    400 }
    401