• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * File contexts backend for labeling system
3  *
4  * Author : Eamon Walsh <ewalsh@tycho.nsa.gov>
5  * Author : Stephen Smalley <sds@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 */
get_stem_from_file_name(const char * const buf)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. */
find_stem_from_file(struct saved_data * data,const char ** buf)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  */
nodups_specs(struct saved_data * data,const char * path)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 
compile_regex(struct saved_data * data,struct spec * spec,const char ** errbuf)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 
process_line(struct selabel_handle * rec,const char * path,const char * prefix,char * line_buf,unsigned lineno)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 
load_mmap(struct selabel_handle * rec,const char * path,struct stat * sb)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 
process_file(const char * path,const char * suffix,struct selabel_handle * rec,const char * prefix)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 
init(struct selabel_handle * rec,struct selinux_opt * opts,unsigned n)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  */
closef(struct selabel_handle * rec)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 
lookup(struct selabel_handle * rec,const char * key,int type)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 
stats(struct selabel_handle * rec)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 
selabel_file_init(struct selabel_handle * rec,struct selinux_opt * opts,unsigned nopts)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