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