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  * This library derived in part from setfiles and the setfiles.pl script
      8  * developed by Secure Computing Corporation.
      9  */
     10 
     11 #include <assert.h>
     12 #include <fcntl.h>
     13 #include <stdarg.h>
     14 #include <string.h>
     15 #include <stdio.h>
     16 #include <stdio_ext.h>
     17 #include <ctype.h>
     18 #include <errno.h>
     19 #include <limits.h>
     20 #include <stdint.h>
     21 #include <pcre.h>
     22 
     23 #include <linux/limits.h>
     24 
     25 #include <sys/mman.h>
     26 #include <sys/types.h>
     27 #include <sys/stat.h>
     28 #include <unistd.h>
     29 #include "callbacks.h"
     30 #include "label_internal.h"
     31 #include "label_file.h"
     32 
     33 /*
     34  * Internals, mostly moved over from matchpathcon.c
     35  */
     36 
     37 /* return the length of the text that is the stem of a file name */
     38 static int get_stem_from_file_name(const char *const buf)
     39 {
     40 	const char *tmp = strchr(buf + 1, '/');
     41 
     42 	if (!tmp)
     43 		return 0;
     44 	return tmp - buf;
     45 }
     46 
     47 /* find the stem of a file name, returns the index into stem_arr (or -1 if
     48  * there is no match - IE for a file in the root directory or a regex that is
     49  * too complex for us).  Makes buf point to the text AFTER the stem. */
     50 static int find_stem_from_file(struct saved_data *data, const char **buf)
     51 {
     52 	int i;
     53 	int stem_len = get_stem_from_file_name(*buf);
     54 
     55 	if (!stem_len)
     56 		return -1;
     57 	for (i = 0; i < data->num_stems; i++) {
     58 		if (stem_len == data->stem_arr[i].len
     59 		    && !strncmp(*buf, data->stem_arr[i].buf, stem_len)) {
     60 			*buf += stem_len;
     61 			return i;
     62 		}
     63 	}
     64 	return -1;
     65 }
     66 
     67 /*
     68  * Warn about duplicate specifications.
     69  */
     70 static int nodups_specs(struct saved_data *data, const char *path)
     71 {
     72 	int rc = 0;
     73 	unsigned int ii, jj;
     74 	struct spec *curr_spec, *spec_arr = data->spec_arr;
     75 
     76 	for (ii = 0; ii < data->nspec; ii++) {
     77 		curr_spec = &spec_arr[ii];
     78 		for (jj = ii + 1; jj < data->nspec; jj++) {
     79 			if ((!strcmp(spec_arr[jj].regex_str, curr_spec->regex_str))
     80 			    && (!spec_arr[jj].mode || !curr_spec->mode
     81 				|| spec_arr[jj].mode == curr_spec->mode)) {
     82 				rc = -1;
     83 				errno = EINVAL;
     84 				if (strcmp(spec_arr[jj].lr.ctx_raw, curr_spec->lr.ctx_raw)) {
     85 					COMPAT_LOG
     86 						(SELINUX_ERROR,
     87 						 "%s: Multiple different specifications for %s  (%s and %s).\n",
     88 						 path, curr_spec->regex_str,
     89 						 spec_arr[jj].lr.ctx_raw,
     90 						 curr_spec->lr.ctx_raw);
     91 				} else {
     92 					COMPAT_LOG
     93 						(SELINUX_ERROR,
     94 						 "%s: Multiple same specifications for %s.\n",
     95 						 path, curr_spec->regex_str);
     96 				}
     97 			}
     98 		}
     99 	}
    100 	return rc;
    101 }
    102 
    103 static int compile_regex(struct saved_data *data, struct spec *spec, const char **errbuf)
    104 {
    105 	const char *tmperrbuf;
    106 	char *reg_buf, *anchored_regex, *cp;
    107 	struct stem *stem_arr = data->stem_arr;
    108 	size_t len;
    109 	int erroff;
    110 
    111 	if (spec->regcomp)
    112 		return 0; /* already done */
    113 
    114 	/* Skip the fixed stem. */
    115 	reg_buf = spec->regex_str;
    116 	if (spec->stem_id >= 0)
    117 		reg_buf += stem_arr[spec->stem_id].len;
    118 
    119 	/* Anchor the regular expression. */
    120 	len = strlen(reg_buf);
    121 	cp = anchored_regex = malloc(len + 3);
    122 	if (!anchored_regex)
    123 		return -1;
    124 
    125 	/* Create ^...$ regexp.  */
    126 	*cp++ = '^';
    127 	cp = mempcpy(cp, reg_buf, len);
    128 	*cp++ = '$';
    129 	*cp = '\0';
    130 
    131 	/* Compile the regular expression. */
    132 	spec->regex = pcre_compile(anchored_regex, PCRE_DOTALL, &tmperrbuf, &erroff, NULL);
    133 	free(anchored_regex);
    134 	if (!spec->regex) {
    135 		if (errbuf)
    136 			*errbuf=tmperrbuf;
    137 		return -1;
    138 	}
    139 
    140 	spec->sd = pcre_study(spec->regex, 0, &tmperrbuf);
    141 	if (!spec->sd && tmperrbuf) {
    142 		if (errbuf)
    143 			*errbuf=tmperrbuf;
    144 		return -1;
    145 	}
    146 
    147 	/* Done. */
    148 	spec->regcomp = 1;
    149 
    150 	return 0;
    151 }
    152 
    153 static int process_line(struct selabel_handle *rec,
    154 			const char *path, const char *prefix,
    155 			char *line_buf, unsigned lineno)
    156 {
    157 	int items, len, rc;
    158 	char *buf_p, *regex, *type, *context;
    159 	struct saved_data *data = (struct saved_data *)rec->data;
    160 	struct spec *spec_arr;
    161 	unsigned int nspec = data->nspec;
    162 	const char *errbuf = NULL;
    163 
    164 	len = strlen(line_buf);
    165 	if (line_buf[len - 1] == '\n')
    166 		line_buf[len - 1] = 0;
    167 	buf_p = line_buf;
    168 	while (isspace(*buf_p))
    169 		buf_p++;
    170 	/* Skip comment lines and empty lines. */
    171 	if (*buf_p == '#' || *buf_p == 0)
    172 		return 0;
    173 	items = sscanf(line_buf, "%ms %ms %ms", &regex, &type, &context);
    174 	if (items < 2) {
    175 		COMPAT_LOG(SELINUX_WARNING,
    176 			    "%s:  line %u is missing fields, skipping\n", path,
    177 			    lineno);
    178 		if (items == 1)
    179 			free(regex);
    180 		return 0;
    181 	} else if (items == 2) {
    182 		/* The type field is optional. */
    183 		free(context);
    184 		context = type;
    185 		type = 0;
    186 	}
    187 
    188 	len = get_stem_from_spec(regex);
    189 	if (len && prefix && strncmp(prefix, regex, len)) {
    190 		/* Stem of regex does not match requested prefix, discard. */
    191 		free(regex);
    192 		free(type);
    193 		free(context);
    194 		return 0;
    195 	}
    196 
    197 	rc = grow_specs(data);
    198 	if (rc)
    199 		return rc;
    200 
    201 	spec_arr = data->spec_arr;
    202 
    203 	/* process and store the specification in spec. */
    204 	spec_arr[nspec].stem_id = find_stem_from_spec(data, regex);
    205 	spec_arr[nspec].regex_str = regex;
    206 	if (rec->validating && compile_regex(data, &spec_arr[nspec], &errbuf)) {
    207 		COMPAT_LOG(SELINUX_WARNING, "%s:  line %u has invalid regex %s:  %s\n",
    208 			   path, lineno, regex, (errbuf ? errbuf : "out of memory"));
    209 	}
    210 
    211 	/* Convert the type string to a mode format */
    212 	spec_arr[nspec].type_str = type;
    213 	spec_arr[nspec].mode = 0;
    214 	if (type) {
    215 		mode_t mode = string_to_mode(type);
    216 		if (mode == (mode_t)-1) {
    217 			COMPAT_LOG(SELINUX_WARNING, "%s:  line %u has invalid file type %s\n",
    218 				   path, lineno, type);
    219 			mode = 0;
    220 		}
    221 		spec_arr[nspec].mode = mode;
    222 	}
    223 
    224 	spec_arr[nspec].lr.ctx_raw = context;
    225 
    226 	/* Determine if specification has
    227 	 * any meta characters in the RE */
    228 	spec_hasMetaChars(&spec_arr[nspec]);
    229 
    230 	if (strcmp(context, "<<none>>") && rec->validating)
    231 		compat_validate(rec, &spec_arr[nspec].lr, path, lineno);
    232 
    233 	data->nspec = ++nspec;
    234 
    235 	return 0;
    236 }
    237 
    238 static int load_mmap(struct selabel_handle *rec, const char *path, struct stat *sb)
    239 {
    240 	struct saved_data *data = (struct saved_data *)rec->data;
    241 	char mmap_path[PATH_MAX + 1];
    242 	int mmapfd;
    243 	int rc;
    244 	struct stat mmap_stat;
    245 	char *addr;
    246 	size_t len;
    247 	int stem_map_len, *stem_map;
    248 	struct mmap_area *mmap_area;
    249 
    250 	uint32_t i;
    251 	uint32_t *magic;
    252 	uint32_t *section_len;
    253 	uint32_t *plen;
    254 
    255 	rc = snprintf(mmap_path, sizeof(mmap_path), "%s.bin", path);
    256 	if (rc >= (int)sizeof(mmap_path))
    257 		return -1;
    258 
    259 	mmapfd = open(mmap_path, O_RDONLY | O_CLOEXEC);
    260 	if (mmapfd < 0)
    261 		return -1;
    262 
    263 	rc = fstat(mmapfd, &mmap_stat);
    264 	if (rc < 0) {
    265 		close(mmapfd);
    266 		return -1;
    267 	}
    268 
    269 	/* if mmap is old, ignore it */
    270 	if (mmap_stat.st_mtime < sb->st_mtime) {
    271 		close(mmapfd);
    272 		return -1;
    273 	}
    274 
    275 	if (mmap_stat.st_mtime == sb->st_mtime &&
    276 	    mmap_stat.st_mtim.tv_nsec < sb->st_mtim.tv_nsec) {
    277 		close(mmapfd);
    278 		return -1;
    279 	}
    280 
    281 	/* ok, read it in... */
    282 	len = mmap_stat.st_size;
    283 	len += (sysconf(_SC_PAGE_SIZE) - 1);
    284 	len &= ~(sysconf(_SC_PAGE_SIZE) - 1);
    285 
    286 	mmap_area = malloc(sizeof(*mmap_area));
    287 	if (!mmap_area) {
    288 		close(mmapfd);
    289 		return -1;
    290 	}
    291 
    292 	addr = mmap(NULL, len, PROT_READ, MAP_PRIVATE, mmapfd, 0);
    293 	close(mmapfd);
    294 	if (addr == MAP_FAILED) {
    295 		free(mmap_area);
    296 		perror("mmap");
    297 		return -1;
    298 	}
    299 
    300 	/* save where we mmap'd the file to cleanup on close() */
    301 	mmap_area->addr = addr;
    302 	mmap_area->len = len;
    303 	mmap_area->next = data->mmap_areas;
    304 	data->mmap_areas = mmap_area;
    305 
    306 	/* check if this looks like an fcontext file */
    307 	magic = (uint32_t *)addr;
    308 	if (*magic != SELINUX_MAGIC_COMPILED_FCONTEXT)
    309 		return -1;
    310 	addr += sizeof(uint32_t);
    311 
    312 	/* check if this version is higher than we understand */
    313 	section_len = (uint32_t *)addr;
    314 	if (*section_len > SELINUX_COMPILED_FCONTEXT_MAX_VERS)
    315 		return -1;
    316 	addr += sizeof(uint32_t);
    317 
    318 	if (*section_len >= SELINUX_COMPILED_FCONTEXT_PCRE_VERS) {
    319 		len = strlen(pcre_version());
    320 		plen = (uint32_t *)addr;
    321 		if (*plen > mmap_area->len)
    322 			return -1; /* runs off the end of the map */
    323 		if (len != *plen)
    324 			return -1; /* pcre version length mismatch */
    325 		addr += sizeof(uint32_t);
    326 		if (memcmp((char *)addr, pcre_version(), len))
    327 			return -1; /* pcre version content mismatch */
    328 		if (addr + *plen >= (char *)mmap_area->addr + mmap_area->len)
    329 			return -1; /* Buffer over-run */
    330 		addr += *plen;
    331 	}
    332 
    333 	/* allocate the stems_data array */
    334 	section_len = (uint32_t *)addr;
    335 	addr += sizeof(uint32_t);
    336 
    337 	/*
    338 	 * map indexed by the stem # in the mmap file and contains the stem
    339 	 * number in the data stem_arr
    340 	 */
    341 	stem_map_len = *section_len;
    342 	stem_map = calloc(stem_map_len, sizeof(*stem_map));
    343 	if (!stem_map)
    344 		return -1;
    345 
    346 	for (i = 0; i < *section_len; i++) {
    347 		char *buf;
    348 		uint32_t stem_len;
    349 		int newid;
    350 
    351 		/* the length does not inlude the nul */
    352 		plen = (uint32_t *)addr;
    353 		addr += sizeof(uint32_t);
    354 
    355 		stem_len = *plen;
    356 		buf = (char *)addr;
    357 		addr += (stem_len + 1); // +1 is the nul
    358 
    359 		/* store the mapping between old and new */
    360 		newid = find_stem(data, buf, stem_len);
    361 		if (newid < 0) {
    362 			newid = store_stem(data, buf, stem_len);
    363 			if (newid < 0) {
    364 				rc = newid;
    365 				goto err;
    366 			}
    367 			data->stem_arr[newid].from_mmap = 1;
    368 		}
    369 		stem_map[i] = newid;
    370 	}
    371 
    372 	/* allocate the regex array */
    373 	section_len = (uint32_t *)addr;
    374 	addr += sizeof(*section_len);
    375 
    376 	for (i = 0; i < *section_len; i++) {
    377 		struct spec *spec;
    378 		int32_t stem_id;
    379 
    380 		rc = grow_specs(data);
    381 		if (rc < 0)
    382 			goto err;
    383 
    384 		spec = &data->spec_arr[data->nspec];
    385 		spec->from_mmap = 1;
    386 		spec->regcomp = 1;
    387 
    388 		plen = (uint32_t *)addr;
    389 		addr += sizeof(uint32_t);
    390 		rc = -1;
    391 		spec->lr.ctx_raw = strdup((char *)addr);
    392 		if (!spec->lr.ctx_raw)
    393 			goto err;
    394 
    395 		if (addr + *plen >= (char *)mmap_area->addr + mmap_area->len)
    396 			return -1;
    397 		addr += *plen;
    398 
    399 		plen = (uint32_t *)addr;
    400 		addr += sizeof(uint32_t);
    401 		spec->regex_str = (char *)addr;
    402 		if (addr + *plen >= (char *)mmap_area->addr + mmap_area->len)
    403 			return -1;
    404 		addr += *plen;
    405 
    406 		spec->mode = *(mode_t *)addr;
    407 		addr += sizeof(mode_t);
    408 
    409 		/* map the stem id from the mmap file to the data->stem_arr */
    410 		stem_id = *(int32_t *)addr;
    411 		if (stem_id == -1 || stem_id >= stem_map_len)
    412 			spec->stem_id = -1;
    413 		else
    414 			spec->stem_id = stem_map[stem_id];
    415 		addr += sizeof(int32_t);
    416 
    417 		/* retrieve the hasMetaChars bit */
    418 		spec->hasMetaChars = *(uint32_t *)addr;
    419 		addr += sizeof(uint32_t);
    420 
    421 		plen = (uint32_t *)addr;
    422 		addr += sizeof(uint32_t);
    423 		spec->regex = (pcre *)addr;
    424 		if (addr + *plen >= (char *)mmap_area->addr + mmap_area->len)
    425 			return -1;
    426 		addr += *plen;
    427 
    428 		plen = (uint32_t *)addr;
    429 		addr += sizeof(uint32_t);
    430 		spec->lsd.study_data = (void *)addr;
    431 		spec->lsd.flags |= PCRE_EXTRA_STUDY_DATA;
    432 		if (addr + *plen >= (char *)mmap_area->addr + mmap_area->len)
    433 			return -1;
    434 		addr += *plen;
    435 
    436 		data->nspec++;
    437 	}
    438 	/* win */
    439 	rc = 0;
    440 err:
    441 	free(stem_map);
    442 
    443 	return rc;
    444 }
    445 
    446 static int process_file(const char *path, const char *suffix, struct selabel_handle *rec, const char *prefix)
    447 {
    448 	FILE *fp;
    449 	struct stat sb;
    450 	unsigned int lineno;
    451 	size_t line_len;
    452 	char *line_buf = NULL;
    453 	int rc;
    454 	char stack_path[PATH_MAX + 1];
    455 
    456 	/* append the path suffix if we have one */
    457 	if (suffix) {
    458 		rc = snprintf(stack_path, sizeof(stack_path), "%s.%s", path, suffix);
    459 		if (rc >= (int)sizeof(stack_path)) {
    460 			errno = ENAMETOOLONG;
    461 			return -1;
    462 		}
    463 		path = stack_path;
    464 	}
    465 
    466 	/* Open the specification file. */
    467 	if ((fp = fopen(path, "r")) == NULL)
    468 		return -1;
    469 	__fsetlocking(fp, FSETLOCKING_BYCALLER);
    470 
    471 	if (fstat(fileno(fp), &sb) < 0)
    472 		return -1;
    473 	if (!S_ISREG(sb.st_mode)) {
    474 		errno = EINVAL;
    475 		return -1;
    476 	}
    477 
    478 	rc = load_mmap(rec, path, &sb);
    479 	if (rc == 0)
    480 		goto out;
    481 
    482 	/*
    483 	 * The do detailed validation of the input and fill the spec array
    484 	 */
    485 	lineno = 0;
    486 	while (getline(&line_buf, &line_len, fp) > 0) {
    487 		rc = process_line(rec, path, prefix, line_buf, ++lineno);
    488 		if (rc)
    489 			return rc;
    490 	}
    491 out:
    492 	free(line_buf);
    493 	fclose(fp);
    494 
    495 	return 0;
    496 }
    497 
    498 static int init(struct selabel_handle *rec, struct selinux_opt *opts,
    499 		unsigned n)
    500 {
    501 	struct saved_data *data = (struct saved_data *)rec->data;
    502 	const char *path = NULL;
    503 	const char *prefix = NULL;
    504 	char subs_file[PATH_MAX + 1];
    505 	int status = -1, baseonly = 0;
    506 
    507 	/* Process arguments */
    508 	while (n--)
    509 		switch(opts[n].type) {
    510 		case SELABEL_OPT_PATH:
    511 			path = opts[n].value;
    512 			break;
    513 		case SELABEL_OPT_SUBSET:
    514 			prefix = opts[n].value;
    515 			break;
    516 		case SELABEL_OPT_BASEONLY:
    517 			baseonly = !!opts[n].value;
    518 			break;
    519 		}
    520 
    521 	/* Process local and distribution substitution files */
    522 	if (!path) {
    523 		rec->dist_subs = selabel_subs_init(selinux_file_context_subs_dist_path(), rec->dist_subs);
    524 		rec->subs = selabel_subs_init(selinux_file_context_subs_path(), rec->subs);
    525 		path = selinux_file_context_path();
    526 	} else {
    527 		snprintf(subs_file, sizeof(subs_file), "%s.subs_dist", path);
    528 		rec->dist_subs = selabel_subs_init(subs_file, rec->dist_subs);
    529 		snprintf(subs_file, sizeof(subs_file), "%s.subs", path);
    530 		rec->subs = selabel_subs_init(subs_file, rec->subs);
    531 	}
    532 
    533 	rec->spec_file = strdup(path);
    534 
    535 	/*
    536 	 * The do detailed validation of the input and fill the spec array
    537 	 */
    538 	status = process_file(path, NULL, rec, prefix);
    539 	if (status)
    540 		goto finish;
    541 
    542 	if (rec->validating) {
    543 		status = nodups_specs(data, path);
    544 		if (status)
    545 			goto finish;
    546 	}
    547 
    548 	if (!baseonly) {
    549 		status = process_file(path, "homedirs", rec, prefix);
    550 		if (status && errno != ENOENT)
    551 			goto finish;
    552 
    553 		status = process_file(path, "local", rec, prefix);
    554 		if (status && errno != ENOENT)
    555 			goto finish;
    556 	}
    557 
    558 	status = sort_specs(data);
    559 
    560 	status = 0;
    561 finish:
    562 	if (status)
    563 		free(data->spec_arr);
    564 	return status;
    565 }
    566 
    567 /*
    568  * Backend interface routines
    569  */
    570 static void closef(struct selabel_handle *rec)
    571 {
    572 	struct saved_data *data = (struct saved_data *)rec->data;
    573 	struct mmap_area *area, *last_area;
    574 	struct spec *spec;
    575 	struct stem *stem;
    576 	unsigned int i;
    577 
    578 	for (i = 0; i < data->nspec; i++) {
    579 		spec = &data->spec_arr[i];
    580 		free(spec->lr.ctx_trans);
    581 		free(spec->lr.ctx_raw);
    582 		if (spec->from_mmap)
    583 			continue;
    584 		free(spec->regex_str);
    585 		free(spec->type_str);
    586 		if (spec->regcomp) {
    587 			pcre_free(spec->regex);
    588 			pcre_free_study(spec->sd);
    589 		}
    590 	}
    591 
    592 	for (i = 0; i < (unsigned int)data->num_stems; i++) {
    593 		stem = &data->stem_arr[i];
    594 		if (stem->from_mmap)
    595 			continue;
    596 		free(stem->buf);
    597 	}
    598 
    599 	if (data->spec_arr)
    600 		free(data->spec_arr);
    601 	if (data->stem_arr)
    602 		free(data->stem_arr);
    603 
    604 	area = data->mmap_areas;
    605 	while (area) {
    606 		munmap(area->addr, area->len);
    607 		last_area = area;
    608 		area = area->next;
    609 		free(last_area);
    610 	}
    611 	free(data);
    612 }
    613 
    614 static struct selabel_lookup_rec *lookup(struct selabel_handle *rec,
    615 					 const char *key, int type)
    616 {
    617 	struct saved_data *data = (struct saved_data *)rec->data;
    618 	struct spec *spec_arr = data->spec_arr;
    619 	int i, rc, file_stem;
    620 	mode_t mode = (mode_t)type;
    621 	const char *buf;
    622 	struct selabel_lookup_rec *ret = NULL;
    623 	char *clean_key = NULL;
    624 	const char *prev_slash, *next_slash;
    625 	unsigned int sofar = 0;
    626 
    627 	if (!data->nspec) {
    628 		errno = ENOENT;
    629 		goto finish;
    630 	}
    631 
    632 	/* Remove duplicate slashes */
    633 	if ((next_slash = strstr(key, "//"))) {
    634 		clean_key = malloc(strlen(key) + 1);
    635 		if (!clean_key)
    636 			goto finish;
    637 		prev_slash = key;
    638 		while (next_slash) {
    639 			memcpy(clean_key + sofar, prev_slash, next_slash - prev_slash);
    640 			sofar += next_slash - prev_slash;
    641 			prev_slash = next_slash + 1;
    642 			next_slash = strstr(prev_slash, "//");
    643 		}
    644 		strcpy(clean_key + sofar, prev_slash);
    645 		key = clean_key;
    646 	}
    647 
    648 	buf = key;
    649 	file_stem = find_stem_from_file(data, &buf);
    650 	mode &= S_IFMT;
    651 
    652 	/*
    653 	 * Check for matching specifications in reverse order, so that
    654 	 * the last matching specification is used.
    655 	 */
    656 	for (i = data->nspec - 1; i >= 0; i--) {
    657 		struct spec *spec = &spec_arr[i];
    658 		/* if the spec in question matches no stem or has the same
    659 		 * stem as the file AND if the spec in question has no mode
    660 		 * specified or if the mode matches the file mode then we do
    661 		 * a regex check        */
    662 		if ((spec->stem_id == -1 || spec->stem_id == file_stem) &&
    663 		    (!mode || !spec->mode || mode == spec->mode)) {
    664 			if (compile_regex(data, spec, NULL) < 0)
    665 				goto finish;
    666 			if (spec->stem_id == -1)
    667 				rc = pcre_exec(spec->regex, get_pcre_extra(spec), key, strlen(key), 0, 0, NULL, 0);
    668 			else
    669 				rc = pcre_exec(spec->regex, get_pcre_extra(spec), buf, strlen(buf), 0, 0, NULL, 0);
    670 
    671 			if (rc == 0) {
    672 				spec->matches++;
    673 				break;
    674 			} else if (rc == PCRE_ERROR_NOMATCH)
    675 				continue;
    676 
    677 			errno = ENOENT;
    678 			/* else it's an error */
    679 			goto finish;
    680 		}
    681 	}
    682 
    683 	if (i < 0 || strcmp(spec_arr[i].lr.ctx_raw, "<<none>>") == 0) {
    684 		/* No matching specification. */
    685 		errno = ENOENT;
    686 		goto finish;
    687 	}
    688 
    689 	errno = 0;
    690 	ret = &spec_arr[i].lr;
    691 
    692 finish:
    693 	free(clean_key);
    694 	return ret;
    695 }
    696 
    697 static void stats(struct selabel_handle *rec)
    698 {
    699 	struct saved_data *data = (struct saved_data *)rec->data;
    700 	unsigned int i, nspec = data->nspec;
    701 	struct spec *spec_arr = data->spec_arr;
    702 
    703 	for (i = 0; i < nspec; i++) {
    704 		if (spec_arr[i].matches == 0) {
    705 			if (spec_arr[i].type_str) {
    706 				COMPAT_LOG(SELINUX_WARNING,
    707 				    "Warning!  No matches for (%s, %s, %s)\n",
    708 				    spec_arr[i].regex_str,
    709 				    spec_arr[i].type_str,
    710 				    spec_arr[i].lr.ctx_raw);
    711 			} else {
    712 				COMPAT_LOG(SELINUX_WARNING,
    713 				    "Warning!  No matches for (%s, %s)\n",
    714 				    spec_arr[i].regex_str,
    715 				    spec_arr[i].lr.ctx_raw);
    716 			}
    717 		}
    718 	}
    719 }
    720 
    721 int selabel_file_init(struct selabel_handle *rec, struct selinux_opt *opts,
    722 		      unsigned nopts)
    723 {
    724 	struct saved_data *data;
    725 
    726 	data = (struct saved_data *)malloc(sizeof(*data));
    727 	if (!data)
    728 		return -1;
    729 	memset(data, 0, sizeof(*data));
    730 
    731 	rec->data = data;
    732 	rec->func_close = &closef;
    733 	rec->func_stats = &stats;
    734 	rec->func_lookup = &lookup;
    735 
    736 	return init(rec, opts, nopts);
    737 }
    738