Home | History | Annotate | Download | only in src
      1 /*
      2  * File contexts backend for labeling system
      3  *
      4  * Author : Eamon Walsh <ewalsh (at) tycho.nsa.gov>
      5  * Author : Stephen Smalley <sds (at) tycho.nsa.gov>
      6  */
      7 
      8 #include <assert.h>
      9 #include <fcntl.h>
     10 #include <stdarg.h>
     11 #include <string.h>
     12 #include <stdio.h>
     13 #include <ctype.h>
     14 #include <errno.h>
     15 #include <limits.h>
     16 #include <stdint.h>
     17 #include <unistd.h>
     18 #include <sys/mman.h>
     19 #include <sys/types.h>
     20 #include <sys/stat.h>
     21 
     22 #include "callbacks.h"
     23 #include "label_internal.h"
     24 #include "label_file.h"
     25 
     26 /*
     27  * Internals, mostly moved over from matchpathcon.c
     28  */
     29 
     30 /* return the length of the text that is the stem of a file name */
     31 static int get_stem_from_file_name(const char *const buf)
     32 {
     33 	const char *tmp = strchr(buf + 1, '/');
     34 
     35 	if (!tmp)
     36 		return 0;
     37 	return tmp - buf;
     38 }
     39 
     40 /* find the stem of a file name, returns the index into stem_arr (or -1 if
     41  * there is no match - IE for a file in the root directory or a regex that is
     42  * too complex for us).  Makes buf point to the text AFTER the stem. */
     43 static int find_stem_from_file(struct saved_data *data, const char **buf)
     44 {
     45 	int i;
     46 	int stem_len = get_stem_from_file_name(*buf);
     47 
     48 	if (!stem_len)
     49 		return -1;
     50 	for (i = 0; i < data->num_stems; i++) {
     51 		if (stem_len == data->stem_arr[i].len
     52 		    && !strncmp(*buf, data->stem_arr[i].buf, stem_len)) {
     53 			*buf += stem_len;
     54 			return i;
     55 		}
     56 	}
     57 	return -1;
     58 }
     59 
     60 /*
     61  * Warn about duplicate specifications.
     62  */
     63 static int nodups_specs(struct saved_data *data, const char *path)
     64 {
     65 	int rc = 0;
     66 	unsigned int ii, jj;
     67 	struct spec *curr_spec, *spec_arr = data->spec_arr;
     68 
     69 	for (ii = 0; ii < data->nspec; ii++) {
     70 		curr_spec = &spec_arr[ii];
     71 		for (jj = ii + 1; jj < data->nspec; jj++) {
     72 			if ((!strcmp(spec_arr[jj].regex_str,
     73 				curr_spec->regex_str))
     74 			    && (!spec_arr[jj].mode || !curr_spec->mode
     75 				|| spec_arr[jj].mode == curr_spec->mode)) {
     76 				rc = -1;
     77 				errno = EINVAL;
     78 				if (strcmp(spec_arr[jj].lr.ctx_raw,
     79 					    curr_spec->lr.ctx_raw)) {
     80 					COMPAT_LOG
     81 						(SELINUX_ERROR,
     82 						 "%s: Multiple different specifications for %s  (%s and %s).\n",
     83 						 path, curr_spec->regex_str,
     84 						 spec_arr[jj].lr.ctx_raw,
     85 						 curr_spec->lr.ctx_raw);
     86 				} else {
     87 					COMPAT_LOG
     88 						(SELINUX_ERROR,
     89 						 "%s: Multiple same specifications for %s.\n",
     90 						 path, curr_spec->regex_str);
     91 				}
     92 			}
     93 		}
     94 	}
     95 	return rc;
     96 }
     97 
     98 static int process_text_file(FILE *fp, const char *prefix,
     99 			     struct selabel_handle *rec, const char *path)
    100 {
    101 	int rc;
    102 	size_t line_len;
    103 	unsigned int lineno = 0;
    104 	char *line_buf = NULL;
    105 
    106 	while (getline(&line_buf, &line_len, fp) > 0) {
    107 		rc = process_line(rec, path, prefix, line_buf, ++lineno);
    108 		if (rc)
    109 			goto out;
    110 	}
    111 	rc = 0;
    112 out:
    113 	free(line_buf);
    114 	return rc;
    115 }
    116 
    117 static int load_mmap(FILE *fp, size_t len, struct selabel_handle *rec,
    118 		     const char *path)
    119 {
    120 	struct saved_data *data = (struct saved_data *)rec->data;
    121 	int rc;
    122 	char *addr, *str_buf;
    123 	int *stem_map;
    124 	struct mmap_area *mmap_area;
    125 	uint32_t i, magic, version;
    126 	uint32_t entry_len, stem_map_len, regex_array_len;
    127 	const char *reg_version;
    128 	const char *reg_arch;
    129 	char reg_arch_matches = 0;
    130 
    131 	mmap_area = malloc(sizeof(*mmap_area));
    132 	if (!mmap_area) {
    133 		return -1;
    134 	}
    135 
    136 	addr = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fileno(fp), 0);
    137 	if (addr == MAP_FAILED) {
    138 		free(mmap_area);
    139 		perror("mmap");
    140 		return -1;
    141 	}
    142 
    143 	/* save where we mmap'd the file to cleanup on close() */
    144 	mmap_area->addr = mmap_area->next_addr = addr;
    145 	mmap_area->len = mmap_area->next_len = len;
    146 	mmap_area->next = data->mmap_areas;
    147 	data->mmap_areas = mmap_area;
    148 
    149 	/* check if this looks like an fcontext file */
    150 	rc = next_entry(&magic, mmap_area, sizeof(uint32_t));
    151 	if (rc < 0 || magic != SELINUX_MAGIC_COMPILED_FCONTEXT)
    152 		return -1;
    153 
    154 	/* check if this version is higher than we understand */
    155 	rc = next_entry(&version, mmap_area, sizeof(uint32_t));
    156 	if (rc < 0 || version > SELINUX_COMPILED_FCONTEXT_MAX_VERS)
    157 		return -1;
    158 
    159 	reg_version = regex_version();
    160 	if (!reg_version)
    161 		return -1;
    162 
    163 	reg_arch = regex_arch_string();
    164 	if (!reg_arch)
    165 		return -1;
    166 
    167 	if (version >= SELINUX_COMPILED_FCONTEXT_PCRE_VERS) {
    168 
    169 		len = strlen(reg_version);
    170 
    171 		rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t));
    172 		if (rc < 0)
    173 			return -1;
    174 
    175 		/* Check version lengths */
    176 		if (len != entry_len)
    177 			return -1;
    178 
    179 		/* Check if regex version mismatch */
    180 		str_buf = malloc(entry_len + 1);
    181 		if (!str_buf)
    182 			return -1;
    183 
    184 		rc = next_entry(str_buf, mmap_area, entry_len);
    185 		if (rc < 0) {
    186 			free(str_buf);
    187 			return -1;
    188 		}
    189 
    190 		str_buf[entry_len] = '\0';
    191 		if ((strcmp(str_buf, reg_version) != 0)) {
    192 			free(str_buf);
    193 			return -1;
    194 		}
    195 		free(str_buf);
    196 
    197 		if (version >= SELINUX_COMPILED_FCONTEXT_REGEX_ARCH) {
    198 			len = strlen(reg_arch);
    199 
    200 			rc = next_entry(&entry_len, mmap_area,
    201 					sizeof(uint32_t));
    202 			if (rc < 0)
    203 				return -1;
    204 
    205 			/* Check arch string lengths */
    206 			if (len != entry_len) {
    207 				/*
    208 				 * Skip the entry and conclude that we have
    209 				 * a mismatch, which is not fatal.
    210 				 */
    211 				next_entry(NULL, mmap_area, entry_len);
    212 				goto end_arch_check;
    213 			}
    214 
    215 			/* Check if arch string mismatch */
    216 			str_buf = malloc(entry_len + 1);
    217 			if (!str_buf)
    218 				return -1;
    219 
    220 			rc = next_entry(str_buf, mmap_area, entry_len);
    221 			if (rc < 0) {
    222 				free(str_buf);
    223 				return -1;
    224 			}
    225 
    226 			str_buf[entry_len] = '\0';
    227 			reg_arch_matches = strcmp(str_buf, reg_arch) == 0;
    228 			free(str_buf);
    229 		}
    230 	}
    231 end_arch_check:
    232 
    233 	/* allocate the stems_data array */
    234 	rc = next_entry(&stem_map_len, mmap_area, sizeof(uint32_t));
    235 	if (rc < 0 || !stem_map_len)
    236 		return -1;
    237 
    238 	/*
    239 	 * map indexed by the stem # in the mmap file and contains the stem
    240 	 * number in the data stem_arr
    241 	 */
    242 	stem_map = calloc(stem_map_len, sizeof(*stem_map));
    243 	if (!stem_map)
    244 		return -1;
    245 
    246 	for (i = 0; i < stem_map_len; i++) {
    247 		char *buf;
    248 		uint32_t stem_len;
    249 		int newid;
    250 
    251 		/* the length does not inlude the nul */
    252 		rc = next_entry(&stem_len, mmap_area, sizeof(uint32_t));
    253 		if (rc < 0 || !stem_len) {
    254 			rc = -1;
    255 			goto out;
    256 		}
    257 
    258 		/* Check for stem_len wrap around. */
    259 		if (stem_len < UINT32_MAX) {
    260 			buf = (char *)mmap_area->next_addr;
    261 			/* Check if over-run before null check. */
    262 			rc = next_entry(NULL, mmap_area, (stem_len + 1));
    263 			if (rc < 0)
    264 				goto out;
    265 
    266 			if (buf[stem_len] != '\0') {
    267 				rc = -1;
    268 				goto out;
    269 			}
    270 		} else {
    271 			rc = -1;
    272 			goto out;
    273 		}
    274 
    275 		/* store the mapping between old and new */
    276 		newid = find_stem(data, buf, stem_len);
    277 		if (newid < 0) {
    278 			newid = store_stem(data, buf, stem_len);
    279 			if (newid < 0) {
    280 				rc = newid;
    281 				goto out;
    282 			}
    283 			data->stem_arr[newid].from_mmap = 1;
    284 		}
    285 		stem_map[i] = newid;
    286 	}
    287 
    288 	/* allocate the regex array */
    289 	rc = next_entry(&regex_array_len, mmap_area, sizeof(uint32_t));
    290 	if (rc < 0 || !regex_array_len) {
    291 		rc = -1;
    292 		goto out;
    293 	}
    294 
    295 	for (i = 0; i < regex_array_len; i++) {
    296 		struct spec *spec;
    297 		int32_t stem_id, meta_chars;
    298 		uint32_t mode = 0, prefix_len = 0;
    299 
    300 		rc = grow_specs(data);
    301 		if (rc < 0)
    302 			goto out;
    303 
    304 		spec = &data->spec_arr[data->nspec];
    305 		spec->from_mmap = 1;
    306 
    307 		/* Process context */
    308 		rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t));
    309 		if (rc < 0 || !entry_len) {
    310 			rc = -1;
    311 			goto out;
    312 		}
    313 
    314 		str_buf = malloc(entry_len);
    315 		if (!str_buf) {
    316 			rc = -1;
    317 			goto out;
    318 		}
    319 		rc = next_entry(str_buf, mmap_area, entry_len);
    320 		if (rc < 0)
    321 			goto out;
    322 
    323 		if (str_buf[entry_len - 1] != '\0') {
    324 			free(str_buf);
    325 			rc = -1;
    326 			goto out;
    327 		}
    328 		spec->lr.ctx_raw = str_buf;
    329 
    330 		if (strcmp(spec->lr.ctx_raw, "<<none>>") && rec->validating) {
    331 			if (selabel_validate(rec, &spec->lr) < 0) {
    332 				selinux_log(SELINUX_ERROR,
    333 					    "%s: context %s is invalid\n",
    334 					    path, spec->lr.ctx_raw);
    335 				goto out;
    336 			}
    337 		}
    338 
    339 		/* Process regex string */
    340 		rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t));
    341 		if (rc < 0 || !entry_len) {
    342 			rc = -1;
    343 			goto out;
    344 		}
    345 
    346 		spec->regex_str = (char *)mmap_area->next_addr;
    347 		rc = next_entry(NULL, mmap_area, entry_len);
    348 		if (rc < 0)
    349 			goto out;
    350 
    351 		if (spec->regex_str[entry_len - 1] != '\0') {
    352 			rc = -1;
    353 			goto out;
    354 		}
    355 
    356 		/* Process mode */
    357 		if (version >= SELINUX_COMPILED_FCONTEXT_MODE)
    358 			rc = next_entry(&mode, mmap_area, sizeof(uint32_t));
    359 		else
    360 			rc = next_entry(&mode, mmap_area, sizeof(mode_t));
    361 		if (rc < 0)
    362 			goto out;
    363 
    364 		spec->mode = mode;
    365 
    366 		/* map the stem id from the mmap file to the data->stem_arr */
    367 		rc = next_entry(&stem_id, mmap_area, sizeof(int32_t));
    368 		if (rc < 0)
    369 			goto out;
    370 
    371 		if (stem_id < 0 || stem_id >= (int32_t)stem_map_len)
    372 			spec->stem_id = -1;
    373 		 else
    374 			spec->stem_id = stem_map[stem_id];
    375 
    376 		/* retrieve the hasMetaChars bit */
    377 		rc = next_entry(&meta_chars, mmap_area, sizeof(uint32_t));
    378 		if (rc < 0)
    379 			goto out;
    380 
    381 		spec->hasMetaChars = meta_chars;
    382 		/* and prefix length for use by selabel_lookup_best_match */
    383 		if (version >= SELINUX_COMPILED_FCONTEXT_PREFIX_LEN) {
    384 			rc = next_entry(&prefix_len, mmap_area,
    385 					    sizeof(uint32_t));
    386 			if (rc < 0)
    387 				goto out;
    388 
    389 			spec->prefix_len = prefix_len;
    390 		}
    391 
    392 		rc = regex_load_mmap(mmap_area, &spec->regex, reg_arch_matches,
    393 				     &spec->regex_compiled);
    394 		if (rc < 0)
    395 			goto out;
    396 
    397 		__pthread_mutex_init(&spec->regex_lock, NULL);
    398 		data->nspec++;
    399 	}
    400 
    401 	rc = 0;
    402 out:
    403 	free(stem_map);
    404 
    405 	return rc;
    406 }
    407 
    408 struct file_details {
    409 	const char *suffix;
    410 	struct stat sb;
    411 };
    412 
    413 static char *rolling_append(char *current, const char *suffix, size_t max)
    414 {
    415 	size_t size;
    416 	size_t suffix_size;
    417 	size_t current_size;
    418 
    419 	if (!suffix)
    420 		return current;
    421 
    422 	current_size = strlen(current);
    423 	suffix_size = strlen(suffix);
    424 
    425 	size = current_size + suffix_size;
    426 	if (size < current_size || size < suffix_size)
    427 		return NULL;
    428 
    429 	/* ensure space for the '.' and the '\0' characters. */
    430 	if (size >= (SIZE_MAX - 2))
    431 		return NULL;
    432 
    433 	size += 2;
    434 
    435 	if (size > max)
    436 		return NULL;
    437 
    438 	/* Append any given suffix */
    439 	char *to = current + current_size;
    440 	*to++ = '.';
    441 	strcpy(to, suffix);
    442 
    443 	return current;
    444 }
    445 
    446 static bool fcontext_is_binary(FILE *fp)
    447 {
    448 	uint32_t magic;
    449 
    450 	size_t len = fread(&magic, sizeof(magic), 1, fp);
    451 	rewind(fp);
    452 
    453 	return (len && (magic == SELINUX_MAGIC_COMPILED_FCONTEXT));
    454 }
    455 
    456 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
    457 
    458 static FILE *open_file(const char *path, const char *suffix,
    459 	       char *save_path, size_t len, struct stat *sb, bool open_oldest)
    460 {
    461 	unsigned int i;
    462 	int rc;
    463 	char stack_path[len];
    464 	struct file_details *found = NULL;
    465 
    466 	/*
    467 	 * Rolling append of suffix. Try to open with path.suffix then the
    468 	 * next as path.suffix.suffix and so forth.
    469 	 */
    470 	struct file_details fdetails[2] = {
    471 			{ .suffix = suffix },
    472 			{ .suffix = "bin" }
    473 	};
    474 
    475 	rc = snprintf(stack_path, sizeof(stack_path), "%s", path);
    476 	if (rc >= (int) sizeof(stack_path)) {
    477 		errno = ENAMETOOLONG;
    478 		return NULL;
    479 	}
    480 
    481 	for (i = 0; i < ARRAY_SIZE(fdetails); i++) {
    482 
    483 		/* This handles the case if suffix is null */
    484 		path = rolling_append(stack_path, fdetails[i].suffix,
    485 				      sizeof(stack_path));
    486 		if (!path)
    487 			return NULL;
    488 
    489 		rc = stat(path, &fdetails[i].sb);
    490 		if (rc)
    491 			continue;
    492 
    493 		/* first file thing found, just take it */
    494 		if (!found) {
    495 			strcpy(save_path, path);
    496 			found = &fdetails[i];
    497 			continue;
    498 		}
    499 
    500 		/*
    501 		 * Keep picking the newest file found. Where "newest"
    502 		 * includes equality. This provides a precedence on
    503 		 * secondary suffixes even when the timestamp is the
    504 		 * same. Ie choose file_contexts.bin over file_contexts
    505 		 * even if the time stamp is the same. Invert this logic
    506 		 * on open_oldest set to true. The idea is that if the
    507 		 * newest file failed to process, we can attempt to
    508 		 * process the oldest. The logic here is subtle and depends
    509 		 * on the array ordering in fdetails for the case when time
    510 		 * stamps are the same.
    511 		 */
    512 		if (open_oldest ^
    513 			(fdetails[i].sb.st_mtime >= found->sb.st_mtime)) {
    514 			found = &fdetails[i];
    515 			strcpy(save_path, path);
    516 		}
    517 	}
    518 
    519 	if (!found) {
    520 		errno = ENOENT;
    521 		return NULL;
    522 	}
    523 
    524 	memcpy(sb, &found->sb, sizeof(*sb));
    525 	return fopen(save_path, "re");
    526 }
    527 
    528 static int process_file(const char *path, const char *suffix,
    529 			  struct selabel_handle *rec,
    530 			  const char *prefix, struct selabel_digest *digest)
    531 {
    532 	int rc;
    533 	unsigned int i;
    534 	struct stat sb;
    535 	FILE *fp = NULL;
    536 	char found_path[PATH_MAX];
    537 
    538 	/*
    539 	 * On the first pass open the newest modified file. If it fails to
    540 	 * process, then the second pass shall open the oldest file. If both
    541 	 * passes fail, then it's a fatal error.
    542 	 */
    543 	for (i = 0; i < 2; i++) {
    544 		fp = open_file(path, suffix, found_path, sizeof(found_path),
    545 			&sb, i > 0);
    546 		if (fp == NULL)
    547 			return -1;
    548 
    549 		rc = fcontext_is_binary(fp) ?
    550 				load_mmap(fp, sb.st_size, rec, found_path) :
    551 				process_text_file(fp, prefix, rec, found_path);
    552 		if (!rc)
    553 			rc = digest_add_specfile(digest, fp, NULL, sb.st_size,
    554 				found_path);
    555 
    556 		fclose(fp);
    557 
    558 		if (!rc)
    559 			return 0;
    560 	}
    561 	return -1;
    562 }
    563 
    564 static void selabel_subs_fini(struct selabel_sub *ptr)
    565 {
    566 	struct selabel_sub *next;
    567 
    568 	while (ptr) {
    569 		next = ptr->next;
    570 		free(ptr->src);
    571 		free(ptr->dst);
    572 		free(ptr);
    573 		ptr = next;
    574 	}
    575 }
    576 
    577 static char *selabel_sub(struct selabel_sub *ptr, const char *src)
    578 {
    579 	char *dst = NULL;
    580 	int len;
    581 
    582 	while (ptr) {
    583 		if (strncmp(src, ptr->src, ptr->slen) == 0 ) {
    584 			if (src[ptr->slen] == '/' ||
    585 			    src[ptr->slen] == 0) {
    586 				if ((src[ptr->slen] == '/') &&
    587 				    (strcmp(ptr->dst, "/") == 0))
    588 					len = ptr->slen + 1;
    589 				else
    590 					len = ptr->slen;
    591 				if (asprintf(&dst, "%s%s", ptr->dst, &src[len]) < 0)
    592 					return NULL;
    593 				return dst;
    594 			}
    595 		}
    596 		ptr = ptr->next;
    597 	}
    598 	return NULL;
    599 }
    600 
    601 #if !defined(BUILD_HOST) && !defined(ANDROID)
    602 static int selabel_subs_init(const char *path, struct selabel_digest *digest,
    603 		       struct selabel_sub **out_subs)
    604 {
    605 	char buf[1024];
    606 	FILE *cfg = fopen(path, "re");
    607 	struct selabel_sub *list = NULL, *sub = NULL;
    608 	struct stat sb;
    609 	int status = -1;
    610 
    611 	*out_subs = NULL;
    612 	if (!cfg) {
    613 		/* If the file does not exist, it is not fatal */
    614 		return (errno == ENOENT) ? 0 : -1;
    615 	}
    616 
    617 	if (fstat(fileno(cfg), &sb) < 0)
    618 		goto out;
    619 
    620 	while (fgets_unlocked(buf, sizeof(buf) - 1, cfg)) {
    621 		char *ptr = NULL;
    622 		char *src = buf;
    623 		char *dst = NULL;
    624 
    625 		while (*src && isspace(*src))
    626 			src++;
    627 		if (src[0] == '#') continue;
    628 		ptr = src;
    629 		while (*ptr && ! isspace(*ptr))
    630 			ptr++;
    631 		*ptr++ = '\0';
    632 		if (! *src) continue;
    633 
    634 		dst = ptr;
    635 		while (*dst && isspace(*dst))
    636 			dst++;
    637 		ptr=dst;
    638 		while (*ptr && ! isspace(*ptr))
    639 			ptr++;
    640 		*ptr='\0';
    641 		if (! *dst)
    642 			continue;
    643 
    644 		sub = malloc(sizeof(*sub));
    645 		if (! sub)
    646 			goto err;
    647 		memset(sub, 0, sizeof(*sub));
    648 
    649 		sub->src=strdup(src);
    650 		if (! sub->src)
    651 			goto err;
    652 
    653 		sub->dst=strdup(dst);
    654 		if (! sub->dst)
    655 			goto err;
    656 
    657 		sub->slen = strlen(src);
    658 		sub->next = list;
    659 		list = sub;
    660 		sub = NULL;
    661 	}
    662 
    663 	if (digest_add_specfile(digest, cfg, NULL, sb.st_size, path) < 0)
    664 		goto err;
    665 
    666 	*out_subs = list;
    667 	status = 0;
    668 
    669 out:
    670 	fclose(cfg);
    671 	return status;
    672 err:
    673 	if (sub)
    674 		free(sub->src);
    675 	free(sub);
    676 	while (list) {
    677 		sub = list->next;
    678 		free(list->src);
    679 		free(list->dst);
    680 		free(list);
    681 		list = sub;
    682 	}
    683 	goto out;
    684 }
    685 #endif
    686 
    687 static char *selabel_sub_key(struct saved_data *data, const char *key)
    688 {
    689 	char *ptr = NULL;
    690 	char *dptr = NULL;
    691 
    692 	ptr = selabel_sub(data->subs, key);
    693 	if (ptr) {
    694 		dptr = selabel_sub(data->dist_subs, ptr);
    695 		if (dptr) {
    696 			free(ptr);
    697 			ptr = dptr;
    698 		}
    699 	} else {
    700 		ptr = selabel_sub(data->dist_subs, key);
    701 	}
    702 	if (ptr)
    703 		return ptr;
    704 
    705 	return NULL;
    706 }
    707 
    708 static void closef(struct selabel_handle *rec);
    709 
    710 static int init(struct selabel_handle *rec, const struct selinux_opt *opts,
    711 		unsigned n)
    712 {
    713 	struct saved_data *data = (struct saved_data *)rec->data;
    714 	size_t num_paths = 0;
    715 	char **path = NULL;
    716 	const char *prefix = NULL;
    717 	int status = -1;
    718 	size_t i;
    719 	bool baseonly = false;
    720 	bool path_provided;
    721 
    722 	/* Process arguments */
    723 	i = n;
    724 	while (i--)
    725 		switch(opts[i].type) {
    726 		case SELABEL_OPT_PATH:
    727 			num_paths++;
    728 			break;
    729 		case SELABEL_OPT_SUBSET:
    730 			prefix = opts[i].value;
    731 			break;
    732 		case SELABEL_OPT_BASEONLY:
    733 			baseonly = !!opts[i].value;
    734 			break;
    735 		}
    736 
    737 	if (!num_paths) {
    738 		num_paths = 1;
    739 		path_provided = false;
    740 	} else {
    741 		path_provided = true;
    742 	}
    743 
    744 	path = calloc(num_paths, sizeof(*path));
    745 	if (path == NULL) {
    746 		goto finish;
    747 	}
    748 	rec->spec_files = path;
    749 	rec->spec_files_len = num_paths;
    750 
    751 	if (path_provided) {
    752 		for (i = 0; i < n; i++) {
    753 			switch(opts[i].type) {
    754 			case SELABEL_OPT_PATH:
    755 				*path = strdup(opts[i].value);
    756 				if (*path == NULL)
    757 					goto finish;
    758 				path++;
    759 				break;
    760 			default:
    761 				break;
    762 			}
    763 		}
    764 	}
    765 #if !defined(BUILD_HOST) && !defined(ANDROID)
    766 	char subs_file[PATH_MAX + 1];
    767 	/* Process local and distribution substitution files */
    768 	if (!path_provided) {
    769 		status = selabel_subs_init(
    770 			selinux_file_context_subs_dist_path(),
    771 			rec->digest, &data->dist_subs);
    772 		if (status)
    773 			goto finish;
    774 		status = selabel_subs_init(selinux_file_context_subs_path(),
    775 			rec->digest, &data->subs);
    776 		if (status)
    777 			goto finish;
    778 		rec->spec_files[0] = strdup(selinux_file_context_path());
    779 		if (rec->spec_files[0] == NULL)
    780 			goto finish;
    781 	} else {
    782 		for (i = 0; i < num_paths; i++) {
    783 			snprintf(subs_file, sizeof(subs_file), "%s.subs_dist", rec->spec_files[i]);
    784 			status = selabel_subs_init(subs_file, rec->digest,
    785 					   &data->dist_subs);
    786 			if (status)
    787 				goto finish;
    788 			snprintf(subs_file, sizeof(subs_file), "%s.subs", rec->spec_files[i]);
    789 			status = selabel_subs_init(subs_file, rec->digest,
    790 					   &data->subs);
    791 			if (status)
    792 				goto finish;
    793 		}
    794 	}
    795 #else
    796 	if (!path_provided) {
    797 		selinux_log(SELINUX_ERROR, "No path given to file labeling backend\n");
    798 		goto finish;
    799 	}
    800 #endif
    801 
    802 	/*
    803 	 * Do detailed validation of the input and fill the spec array
    804 	 */
    805 	for (i = 0; i < num_paths; i++) {
    806 		status = process_file(rec->spec_files[i], NULL, rec, prefix, rec->digest);
    807 		if (status)
    808 			goto finish;
    809 
    810 		if (rec->validating) {
    811 			status = nodups_specs(data, rec->spec_files[i]);
    812 			if (status)
    813 				goto finish;
    814 		}
    815 	}
    816 
    817 	if (!baseonly) {
    818 		status = process_file(rec->spec_files[0], "homedirs", rec, prefix,
    819 							    rec->digest);
    820 		if (status && errno != ENOENT)
    821 			goto finish;
    822 
    823 		status = process_file(rec->spec_files[0], "local", rec, prefix,
    824 							    rec->digest);
    825 		if (status && errno != ENOENT)
    826 			goto finish;
    827 	}
    828 
    829 	digest_gen_hash(rec->digest);
    830 
    831 	status = sort_specs(data);
    832 
    833 finish:
    834 	if (status)
    835 		closef(rec);
    836 
    837 	return status;
    838 }
    839 
    840 /*
    841  * Backend interface routines
    842  */
    843 static void closef(struct selabel_handle *rec)
    844 {
    845 	struct saved_data *data = (struct saved_data *)rec->data;
    846 	struct mmap_area *area, *last_area;
    847 	struct spec *spec;
    848 	struct stem *stem;
    849 	unsigned int i;
    850 
    851 	if (!data)
    852 		return;
    853 
    854 	/* make sure successive ->func_close() calls are harmless */
    855 	rec->data = NULL;
    856 
    857 	selabel_subs_fini(data->subs);
    858 	selabel_subs_fini(data->dist_subs);
    859 
    860 	for (i = 0; i < data->nspec; i++) {
    861 		spec = &data->spec_arr[i];
    862 		free(spec->lr.ctx_trans);
    863 		free(spec->lr.ctx_raw);
    864 		regex_data_free(spec->regex);
    865 		__pthread_mutex_destroy(&spec->regex_lock);
    866 		if (spec->from_mmap)
    867 			continue;
    868 		free(spec->regex_str);
    869 		free(spec->type_str);
    870 	}
    871 
    872 	for (i = 0; i < (unsigned int)data->num_stems; i++) {
    873 		stem = &data->stem_arr[i];
    874 		if (stem->from_mmap)
    875 			continue;
    876 		free(stem->buf);
    877 	}
    878 
    879 	if (data->spec_arr)
    880 		free(data->spec_arr);
    881 	if (data->stem_arr)
    882 		free(data->stem_arr);
    883 
    884 	area = data->mmap_areas;
    885 	while (area) {
    886 		munmap(area->addr, area->len);
    887 		last_area = area;
    888 		area = area->next;
    889 		free(last_area);
    890 	}
    891 	free(data);
    892 }
    893 
    894 static struct spec *lookup_common(struct selabel_handle *rec,
    895 					     const char *key,
    896 					     int type,
    897 					     bool partial)
    898 {
    899 	struct saved_data *data = (struct saved_data *)rec->data;
    900 	struct spec *spec_arr = data->spec_arr;
    901 	int i, rc, file_stem;
    902 	mode_t mode = (mode_t)type;
    903 	const char *buf;
    904 	struct spec *ret = NULL;
    905 	char *clean_key = NULL;
    906 	const char *prev_slash, *next_slash;
    907 	unsigned int sofar = 0;
    908 	char *sub = NULL;
    909 
    910 	if (!data->nspec) {
    911 		errno = ENOENT;
    912 		goto finish;
    913 	}
    914 
    915 	/* Remove duplicate slashes */
    916 	if ((next_slash = strstr(key, "//"))) {
    917 		clean_key = (char *) malloc(strlen(key) + 1);
    918 		if (!clean_key)
    919 			goto finish;
    920 		prev_slash = key;
    921 		while (next_slash) {
    922 			memcpy(clean_key + sofar, prev_slash, next_slash - prev_slash);
    923 			sofar += next_slash - prev_slash;
    924 			prev_slash = next_slash + 1;
    925 			next_slash = strstr(prev_slash, "//");
    926 		}
    927 		strcpy(clean_key + sofar, prev_slash);
    928 		key = clean_key;
    929 	}
    930 
    931 	sub = selabel_sub_key(data, key);
    932 	if (sub)
    933 		key = sub;
    934 
    935 	buf = key;
    936 	file_stem = find_stem_from_file(data, &buf);
    937 	mode &= S_IFMT;
    938 
    939 	/*
    940 	 * Check for matching specifications in reverse order, so that
    941 	 * the last matching specification is used.
    942 	 */
    943 	for (i = data->nspec - 1; i >= 0; i--) {
    944 		struct spec *spec = &spec_arr[i];
    945 		/* if the spec in question matches no stem or has the same
    946 		 * stem as the file AND if the spec in question has no mode
    947 		 * specified or if the mode matches the file mode then we do
    948 		 * a regex check        */
    949 		if ((spec->stem_id == -1 || spec->stem_id == file_stem) &&
    950 		    (!mode || !spec->mode || mode == spec->mode)) {
    951 			if (compile_regex(data, spec, NULL) < 0)
    952 				goto finish;
    953 			if (spec->stem_id == -1)
    954 				rc = regex_match(spec->regex, key, partial);
    955 			else
    956 				rc = regex_match(spec->regex, buf, partial);
    957 			if (rc == REGEX_MATCH) {
    958 				spec->matches++;
    959 				break;
    960 			} else if (partial && rc == REGEX_MATCH_PARTIAL)
    961 				break;
    962 
    963 			if (rc == REGEX_NO_MATCH)
    964 				continue;
    965 
    966 			errno = ENOENT;
    967 			/* else it's an error */
    968 			goto finish;
    969 		}
    970 	}
    971 
    972 	if (i < 0 || strcmp(spec_arr[i].lr.ctx_raw, "<<none>>") == 0) {
    973 		/* No matching specification. */
    974 		errno = ENOENT;
    975 		goto finish;
    976 	}
    977 
    978 	errno = 0;
    979 	ret = &spec_arr[i];
    980 
    981 finish:
    982 	free(clean_key);
    983 	free(sub);
    984 	return ret;
    985 }
    986 
    987 static struct selabel_lookup_rec *lookup(struct selabel_handle *rec,
    988 					 const char *key, int type)
    989 {
    990 	struct spec *spec;
    991 
    992 	spec = lookup_common(rec, key, type, false);
    993 	if (spec)
    994 		return &spec->lr;
    995 	return NULL;
    996 }
    997 
    998 static bool partial_match(struct selabel_handle *rec, const char *key)
    999 {
   1000 	return lookup_common(rec, key, 0, true) ? true : false;
   1001 }
   1002 
   1003 static struct selabel_lookup_rec *lookup_best_match(struct selabel_handle *rec,
   1004 						    const char *key,
   1005 						    const char **aliases,
   1006 						    int type)
   1007 {
   1008 	size_t n, i;
   1009 	int best = -1;
   1010 	struct spec **specs;
   1011 	size_t prefix_len = 0;
   1012 	struct selabel_lookup_rec *lr = NULL;
   1013 
   1014 	if (!aliases || !aliases[0])
   1015 		return lookup(rec, key, type);
   1016 
   1017 	for (n = 0; aliases[n]; n++)
   1018 		;
   1019 
   1020 	specs = calloc(n+1, sizeof(struct spec *));
   1021 	if (!specs)
   1022 		return NULL;
   1023 	specs[0] = lookup_common(rec, key, type, false);
   1024 	if (specs[0]) {
   1025 		if (!specs[0]->hasMetaChars) {
   1026 			/* exact match on key */
   1027 			lr = &specs[0]->lr;
   1028 			goto out;
   1029 		}
   1030 		best = 0;
   1031 		prefix_len = specs[0]->prefix_len;
   1032 	}
   1033 	for (i = 1; i <= n; i++) {
   1034 		specs[i] = lookup_common(rec, aliases[i-1], type, false);
   1035 		if (specs[i]) {
   1036 			if (!specs[i]->hasMetaChars) {
   1037 				/* exact match on alias */
   1038 				lr = &specs[i]->lr;
   1039 				goto out;
   1040 			}
   1041 			if (specs[i]->prefix_len > prefix_len) {
   1042 				best = i;
   1043 				prefix_len = specs[i]->prefix_len;
   1044 			}
   1045 		}
   1046 	}
   1047 
   1048 	if (best >= 0) {
   1049 		/* longest fixed prefix match on key or alias */
   1050 		lr = &specs[best]->lr;
   1051 	} else {
   1052 		errno = ENOENT;
   1053 	}
   1054 
   1055 out:
   1056 	free(specs);
   1057 	return lr;
   1058 }
   1059 
   1060 static enum selabel_cmp_result incomp(struct spec *spec1, struct spec *spec2, const char *reason, int i, int j)
   1061 {
   1062 	selinux_log(SELINUX_INFO,
   1063 		    "selabel_cmp: mismatched %s on entry %d: (%s, %x, %s) vs entry %d: (%s, %x, %s)\n",
   1064 		    reason,
   1065 		    i, spec1->regex_str, spec1->mode, spec1->lr.ctx_raw,
   1066 		    j, spec2->regex_str, spec2->mode, spec2->lr.ctx_raw);
   1067 	return SELABEL_INCOMPARABLE;
   1068 }
   1069 
   1070 static enum selabel_cmp_result cmp(struct selabel_handle *h1,
   1071 				   struct selabel_handle *h2)
   1072 {
   1073 	struct saved_data *data1 = (struct saved_data *)h1->data;
   1074 	struct saved_data *data2 = (struct saved_data *)h2->data;
   1075 	unsigned int i, nspec1 = data1->nspec, j, nspec2 = data2->nspec;
   1076 	struct spec *spec_arr1 = data1->spec_arr, *spec_arr2 = data2->spec_arr;
   1077 	struct stem *stem_arr1 = data1->stem_arr, *stem_arr2 = data2->stem_arr;
   1078 	bool skipped1 = false, skipped2 = false;
   1079 
   1080 	i = 0;
   1081 	j = 0;
   1082 	while (i < nspec1 && j < nspec2) {
   1083 		struct spec *spec1 = &spec_arr1[i];
   1084 		struct spec *spec2 = &spec_arr2[j];
   1085 
   1086 		/*
   1087 		 * Because sort_specs() moves exact pathnames to the
   1088 		 * end, we might need to skip over additional regex
   1089 		 * entries that only exist in one of the configurations.
   1090 		 */
   1091 		if (!spec1->hasMetaChars && spec2->hasMetaChars) {
   1092 			j++;
   1093 			skipped2 = true;
   1094 			continue;
   1095 		}
   1096 
   1097 		if (spec1->hasMetaChars && !spec2->hasMetaChars) {
   1098 			i++;
   1099 			skipped1 = true;
   1100 			continue;
   1101 		}
   1102 
   1103 		if (spec1->regex && spec2->regex) {
   1104 			if (regex_cmp(spec1->regex, spec2->regex) == SELABEL_INCOMPARABLE){
   1105 				return incomp(spec1, spec2, "regex", i, j);
   1106 			}
   1107 		} else {
   1108 			if (strcmp(spec1->regex_str, spec2->regex_str))
   1109 				return incomp(spec1, spec2, "regex_str", i, j);
   1110 		}
   1111 
   1112 		if (spec1->mode != spec2->mode)
   1113 			return incomp(spec1, spec2, "mode", i, j);
   1114 
   1115 		if (spec1->stem_id == -1 && spec2->stem_id != -1)
   1116 			return incomp(spec1, spec2, "stem_id", i, j);
   1117 		if (spec2->stem_id == -1 && spec1->stem_id != -1)
   1118 			return incomp(spec1, spec2, "stem_id", i, j);
   1119 		if (spec1->stem_id != -1 && spec2->stem_id != -1) {
   1120 			struct stem *stem1 = &stem_arr1[spec1->stem_id];
   1121 			struct stem *stem2 = &stem_arr2[spec2->stem_id];
   1122 			if (stem1->len != stem2->len ||
   1123 			    strncmp(stem1->buf, stem2->buf, stem1->len))
   1124 				return incomp(spec1, spec2, "stem", i, j);
   1125 		}
   1126 
   1127 		if (strcmp(spec1->lr.ctx_raw, spec2->lr.ctx_raw))
   1128 			return incomp(spec1, spec2, "ctx_raw", i, j);
   1129 
   1130 		i++;
   1131 		j++;
   1132 	}
   1133 
   1134 	if ((skipped1 || i < nspec1) && !skipped2)
   1135 		return SELABEL_SUPERSET;
   1136 	if ((skipped2 || j < nspec2) && !skipped1)
   1137 		return SELABEL_SUBSET;
   1138 	if (skipped1 && skipped2)
   1139 		return SELABEL_INCOMPARABLE;
   1140 	return SELABEL_EQUAL;
   1141 }
   1142 
   1143 
   1144 static void stats(struct selabel_handle *rec)
   1145 {
   1146 	struct saved_data *data = (struct saved_data *)rec->data;
   1147 	unsigned int i, nspec = data->nspec;
   1148 	struct spec *spec_arr = data->spec_arr;
   1149 
   1150 	for (i = 0; i < nspec; i++) {
   1151 		if (spec_arr[i].matches == 0) {
   1152 			if (spec_arr[i].type_str) {
   1153 				COMPAT_LOG(SELINUX_WARNING,
   1154 				    "Warning!  No matches for (%s, %s, %s)\n",
   1155 				    spec_arr[i].regex_str,
   1156 				    spec_arr[i].type_str,
   1157 				    spec_arr[i].lr.ctx_raw);
   1158 			} else {
   1159 				COMPAT_LOG(SELINUX_WARNING,
   1160 				    "Warning!  No matches for (%s, %s)\n",
   1161 				    spec_arr[i].regex_str,
   1162 				    spec_arr[i].lr.ctx_raw);
   1163 			}
   1164 		}
   1165 	}
   1166 }
   1167 
   1168 int selabel_file_init(struct selabel_handle *rec,
   1169 				    const struct selinux_opt *opts,
   1170 				    unsigned nopts)
   1171 {
   1172 	struct saved_data *data;
   1173 
   1174 	data = (struct saved_data *)malloc(sizeof(*data));
   1175 	if (!data)
   1176 		return -1;
   1177 	memset(data, 0, sizeof(*data));
   1178 
   1179 	rec->data = data;
   1180 	rec->func_close = &closef;
   1181 	rec->func_stats = &stats;
   1182 	rec->func_lookup = &lookup;
   1183 	rec->func_partial_match = &partial_match;
   1184 	rec->func_lookup_best_match = &lookup_best_match;
   1185 	rec->func_cmp = &cmp;
   1186 
   1187 	return init(rec, opts, nopts);
   1188 }
   1189