1 /*
2  * File contexts backend for labeling system
3  *
4  * Author : Eamon Walsh <ewalsh@tycho.nsa.gov>
5  */
6 
7 #include <fcntl.h>
8 #include <stdarg.h>
9 #include <string.h>
10 #include <stdio.h>
11 #include <ctype.h>
12 #include <errno.h>
13 #include <limits.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <unistd.h>
17 #include "callbacks.h"
18 #include "label_internal.h"
19 #include <pcre.h>
20 
21 /*
22  * Internals, mostly moved over from matchpathcon.c
23  */
24 
25 /* A file security context specification. */
26 typedef struct spec {
27 	struct selabel_lookup_rec lr;	/* holds contexts for lookup result */
28 	char *regex_str;	/* regular expession string for diagnostics */
29 	char *type_str;		/* type string for diagnostic messages */
30 	pcre *regex;		/* compiled regular expression */
31 	pcre_extra *sd;         /* pcre study result */
32 	char regcomp;           /* regex_str has been compiled to regex */
33 	mode_t mode;		/* mode format value */
34 	int matches;		/* number of matching pathnames */
35 	int hasMetaChars;	/* regular expression has meta-chars */
36 	int stem_id;		/* indicates which stem-compression item */
37 	size_t prefix_len;      /* length of fixed path prefix */
38 } spec_t;
39 
40 /* A regular expression stem */
41 typedef struct stem {
42 	char *buf;
43 	int len;
44 } stem_t;
45 
46 /* Our stored configuration */
47 struct saved_data {
48 	/*
49 	 * The array of specifications, initially in the same order as in
50 	 * the specification file. Sorting occurs based on hasMetaChars.
51 	 */
52 	spec_t *spec_arr;
53 	unsigned int nspec;
54 	unsigned int ncomp;
55 
56 	/*
57 	 * The array of regular expression stems.
58 	 */
59 	stem_t *stem_arr;
60 	int num_stems;
61 	int alloc_stems;
62 };
63 
64 /* Return the length of the text that can be considered the stem, returns 0
65  * if there is no identifiable stem */
get_stem_from_spec(const char * const buf)66 static int get_stem_from_spec(const char *const buf)
67 {
68 	const char *tmp = strchr(buf + 1, '/');
69 	const char *ind;
70 
71 	if (!tmp)
72 		return 0;
73 
74 	for (ind = buf; ind < tmp; ind++) {
75 		if (strchr(".^$?*+|[({", (int)*ind))
76 			return 0;
77 	}
78 	return tmp - buf;
79 }
80 
81 /* return the length of the text that is the stem of a file name */
get_stem_from_file_name(const char * const buf)82 static int get_stem_from_file_name(const char *const buf)
83 {
84 	const char *tmp = strchr(buf + 1, '/');
85 
86 	if (!tmp)
87 		return 0;
88 	return tmp - buf;
89 }
90 
91 /* find the stem of a file spec, returns the index into stem_arr for a new
92  * or existing stem, (or -1 if there is no possible stem - IE for a file in
93  * the root directory or a regex that is too complex for us). */
find_stem_from_spec(struct saved_data * data,const char * buf)94 static int find_stem_from_spec(struct saved_data *data, const char *buf)
95 {
96 	int i, num = data->num_stems;
97 	int stem_len = get_stem_from_spec(buf);
98 
99 	if (!stem_len)
100 		return -1;
101 	for (i = 0; i < num; i++) {
102 		if (stem_len == data->stem_arr[i].len
103 		    && !strncmp(buf, data->stem_arr[i].buf, stem_len))
104 			return i;
105 	}
106 	if (data->alloc_stems == num) {
107 		stem_t *tmp_arr;
108 		data->alloc_stems = data->alloc_stems * 2 + 16;
109 		tmp_arr = (stem_t *) realloc(data->stem_arr,
110 				  sizeof(stem_t) * data->alloc_stems);
111 		if (!tmp_arr)
112 			return -1;
113 		data->stem_arr = tmp_arr;
114 	}
115 	data->stem_arr[num].len = stem_len;
116 	data->stem_arr[num].buf = (char *) malloc(stem_len + 1);
117 	if (!data->stem_arr[num].buf)
118 		return -1;
119 	memcpy(data->stem_arr[num].buf, buf, stem_len);
120 	data->stem_arr[num].buf[stem_len] = '\0';
121 	data->num_stems++;
122 	buf += stem_len;
123 	return num;
124 }
125 
126 /* find the stem of a file name, returns the index into stem_arr (or -1 if
127  * there is no match - IE for a file in the root directory or a regex that is
128  * too complex for us).  Makes buf point to the text AFTER the stem. */
find_stem_from_file(struct saved_data * data,const char ** buf)129 static int find_stem_from_file(struct saved_data *data, const char **buf)
130 {
131 	int i;
132 	int stem_len = get_stem_from_file_name(*buf);
133 
134 	if (!stem_len)
135 		return -1;
136 	for (i = 0; i < data->num_stems; i++) {
137 		if (stem_len == data->stem_arr[i].len
138 		    && !strncmp(*buf, data->stem_arr[i].buf, stem_len)) {
139 			*buf += stem_len;
140 			return i;
141 		}
142 	}
143 	return -1;
144 }
145 
146 /*
147  * Warn about duplicate specifications.
148  */
nodups_specs(struct saved_data * data,const char * path)149 static int nodups_specs(struct saved_data *data, const char *path)
150 {
151 	int rc = 0;
152 	unsigned int ii, jj;
153 	struct spec *curr_spec, *spec_arr = data->spec_arr;
154 
155 	for (ii = 0; ii < data->nspec; ii++) {
156 		curr_spec = &spec_arr[ii];
157 		for (jj = ii + 1; jj < data->nspec; jj++) {
158 			if ((!strcmp
159 			     (spec_arr[jj].regex_str, curr_spec->regex_str))
160 			    && (!spec_arr[jj].mode || !curr_spec->mode
161 				|| spec_arr[jj].mode == curr_spec->mode)) {
162 				rc = -1;
163 				errno = EINVAL;
164 				if (strcmp
165 				    (spec_arr[jj].lr.ctx_raw,
166 				     curr_spec->lr.ctx_raw)) {
167 					selinux_log
168 						(SELINUX_ERROR,
169 						 "%s: Multiple different specifications for %s  (%s and %s).\n",
170 						 path, curr_spec->regex_str,
171 						 spec_arr[jj].lr.ctx_raw,
172 						 curr_spec->lr.ctx_raw);
173 				} else {
174 					selinux_log
175 						(SELINUX_ERROR,
176 						 "%s: Multiple same specifications for %s.\n",
177 						 path, curr_spec->regex_str);
178 				}
179 			}
180 		}
181 	}
182 	return rc;
183 }
184 
185 /* Determine if the regular expression specification has any meta characters. */
spec_hasMetaChars(struct spec * spec)186 static void spec_hasMetaChars(struct spec *spec)
187 {
188 	char *c;
189 	size_t len;
190 	char *end;
191 
192 	c = spec->regex_str;
193 	len = strlen(spec->regex_str);
194 	end = c + len;
195 
196 	spec->hasMetaChars = 0;
197 	spec->prefix_len = len;
198 
199 	/* Look at each character in the RE specification string for a
200 	 * meta character. Return when any meta character reached. */
201 	while (c != end) {
202 		switch (*c) {
203 		case '.':
204 		case '^':
205 		case '$':
206 		case '?':
207 		case '*':
208 		case '+':
209 		case '|':
210 		case '[':
211 		case '(':
212 		case '{':
213 			spec->hasMetaChars = 1;
214 			spec->prefix_len = c - spec->regex_str;
215 			return;
216 		case '\\':	/* skip the next character */
217 			c++;
218 			break;
219 		default:
220 			break;
221 
222 		}
223 		c++;
224 	}
225 	return;
226 }
227 
compile_regex(struct saved_data * data,spec_t * spec,const char ** errbuf)228 static int compile_regex(struct saved_data *data, spec_t *spec, const char **errbuf)
229 {
230 	const char *tmperrbuf;
231 	char *reg_buf, *anchored_regex, *cp;
232 	stem_t *stem_arr = data->stem_arr;
233 	size_t len;
234 	int erroff;
235 
236 	if (spec->regcomp)
237 		return 0; /* already done */
238 
239 	data->ncomp++; /* how many compiled regexes required */
240 
241 	/* Skip the fixed stem. */
242 	reg_buf = spec->regex_str;
243 	if (spec->stem_id >= 0)
244 		reg_buf += stem_arr[spec->stem_id].len;
245 
246 	/* Anchor the regular expression. */
247 	len = strlen(reg_buf);
248 	cp = anchored_regex = (char *) malloc(len + 3);
249 	if (!anchored_regex)
250 		return -1;
251 	/* Create ^...$ regexp.  */
252 	*cp++ = '^';
253 	memcpy(cp, reg_buf, len);
254 	cp += len;
255 	*cp++ = '$';
256 	*cp = '\0';
257 
258 	/* Compile the regular expression. */
259 	spec->regex = pcre_compile(anchored_regex, PCRE_DOTALL, &tmperrbuf, &erroff, NULL);
260 	free(anchored_regex);
261 	if (!spec->regex) {
262 		if (errbuf)
263 			*errbuf=tmperrbuf;
264 		return -1;
265 	}
266 
267 	spec->sd = pcre_study(spec->regex, 0, &tmperrbuf);
268 	if (!spec->sd && tmperrbuf) {
269 		if (errbuf)
270 			*errbuf=tmperrbuf;
271 		return -1;
272 	}
273 
274 	/* Done. */
275 	spec->regcomp = 1;
276 
277 	return 0;
278 }
279 
280 
process_line(struct selabel_handle * rec,const char * path,const char * prefix,char * line_buf,int pass,unsigned lineno)281 static int process_line(struct selabel_handle *rec,
282 			const char *path, const char *prefix,
283 			char *line_buf, int pass, unsigned lineno)
284 {
285 	int items, len;
286 	char buf1[BUFSIZ], buf2[BUFSIZ], buf3[BUFSIZ];
287 	char *buf_p, *regex = buf1, *type = buf2, *context = buf3;
288 	struct saved_data *data = (struct saved_data *)rec->data;
289 	spec_t *spec_arr = data->spec_arr;
290 	unsigned int nspec = data->nspec;
291 
292 	len = strlen(line_buf);
293 	if (line_buf[len - 1] == '\n')
294 		line_buf[len - 1] = 0;
295 	buf_p = line_buf;
296 	while (isspace(*buf_p))
297 		buf_p++;
298 	/* Skip comment lines and empty lines. */
299 	if (*buf_p == '#' || *buf_p == 0)
300 		return 0;
301 	items = sscanf(line_buf, "%255s %255s %255s", regex, type, context);
302 	if (items < 2) {
303 		selinux_log(SELINUX_WARNING,
304 			    "%s:  line %d is missing fields, skipping\n", path,
305 			    lineno);
306 		return 0;
307 	} else if (items == 2) {
308 		/* The type field is optional. */
309 		context = type;
310 		type = NULL;
311 	}
312 
313 	len = get_stem_from_spec(regex);
314 	if (len && prefix && strncmp(prefix, regex, len)) {
315 		/* Stem of regex does not match requested prefix, discard. */
316 		return 0;
317 	}
318 
319 	if (pass == 1) {
320 		/* On the second pass, process and store the specification in spec. */
321 		const char *errbuf = NULL;
322 		spec_arr[nspec].stem_id = find_stem_from_spec(data, regex);
323 		spec_arr[nspec].regex_str = strdup(regex);
324 		if (!spec_arr[nspec].regex_str) {
325 			selinux_log(SELINUX_WARNING,
326 				   "%s:  out of memory at line %d on regex %s\n",
327 				   path, lineno, regex);
328 			return -1;
329 
330 		}
331 		if (rec->validating && compile_regex(data, &spec_arr[nspec], &errbuf)) {
332 			selinux_log(SELINUX_WARNING,
333 				   "%s:  line %d has invalid regex %s:  %s\n",
334 				   path, lineno, regex,
335 				   (errbuf ? errbuf : "out of memory"));
336 		}
337 
338 		/* Convert the type string to a mode format */
339 		spec_arr[nspec].mode = 0;
340 		if (!type)
341 			goto skip_type;
342 		spec_arr[nspec].type_str = strdup(type);
343 		len = strlen(type);
344 		if (type[0] != '-' || len != 2) {
345 			selinux_log(SELINUX_WARNING,
346 				    "%s:  line %d has invalid file type %s\n",
347 				    path, lineno, type);
348 			return 0;
349 		}
350 		switch (type[1]) {
351 		case 'b':
352 			spec_arr[nspec].mode = S_IFBLK;
353 			break;
354 		case 'c':
355 			spec_arr[nspec].mode = S_IFCHR;
356 			break;
357 		case 'd':
358 			spec_arr[nspec].mode = S_IFDIR;
359 			break;
360 		case 'p':
361 			spec_arr[nspec].mode = S_IFIFO;
362 			break;
363 		case 'l':
364 			spec_arr[nspec].mode = S_IFLNK;
365 			break;
366 		case 's':
367 			spec_arr[nspec].mode = S_IFSOCK;
368 			break;
369 		case '-':
370 			spec_arr[nspec].mode = S_IFREG;
371 			break;
372 		default:
373 			selinux_log(SELINUX_WARNING,
374 				    "%s:  line %d has invalid file type %s\n",
375 				    path, lineno, type);
376 			return 0;
377 		}
378 
379 	skip_type:
380 		spec_arr[nspec].lr.ctx_raw = strdup(context);
381 
382 		if (strcmp(context, "<<none>>") && rec->validating) {
383 			if (selabel_validate(rec, &spec_arr[nspec].lr) < 0) {
384 				selinux_log(SELINUX_WARNING,
385 					    "%s:  line %d has invalid context %s\n",
386 					    path, lineno, spec_arr[nspec].lr.ctx_raw);
387 			}
388 		}
389 
390 		/* Determine if specification has
391 		 * any meta characters in the RE */
392 		spec_hasMetaChars(&spec_arr[nspec]);
393 	}
394 
395 	data->nspec = ++nspec;
396 	return 0;
397 }
398 
init(struct selabel_handle * rec,const struct selinux_opt * opts,unsigned n)399 static int init(struct selabel_handle *rec, const struct selinux_opt *opts,
400 		unsigned n)
401 {
402 	struct saved_data *data = (struct saved_data *)rec->data;
403 	const char *path = NULL;
404 	const char *prefix = NULL;
405 	FILE *fp;
406 	FILE *localfp = NULL;
407 	FILE *homedirfp = NULL;
408 	char local_path[PATH_MAX + 1];
409 	char homedir_path[PATH_MAX + 1];
410 	char line_buf[BUFSIZ];
411 	unsigned int lineno, pass, i, j, maxnspec;
412 	spec_t *spec_copy = NULL;
413 	int status = -1, baseonly = 0;
414 	struct stat sb;
415 
416 	/* Process arguments */
417 	while (n--)
418 		switch(opts[n].type) {
419 		case SELABEL_OPT_PATH:
420 			path = opts[n].value;
421 			break;
422 		case SELABEL_OPT_SUBSET:
423 			prefix = opts[n].value;
424 			break;
425 		case SELABEL_OPT_BASEONLY:
426 			baseonly = !!opts[n].value;
427 			break;
428 		}
429 
430 	/* Open the specification file. */
431 	if ((fp = fopen(path, "r")) == NULL)
432 		return -1;
433 
434 	if (fstat(fileno(fp), &sb) < 0)
435 		return -1;
436 	if (!S_ISREG(sb.st_mode)) {
437 		errno = EINVAL;
438 		return -1;
439 	}
440 
441 	if (!baseonly) {
442 		snprintf(homedir_path, sizeof(homedir_path), "%s.homedirs",
443 			 path);
444 		homedirfp = fopen(homedir_path, "r");
445 
446 		snprintf(local_path, sizeof(local_path), "%s.local", path);
447 		localfp = fopen(local_path, "r");
448 	}
449 
450 	/*
451 	 * Perform two passes over the specification file.
452 	 * The first pass counts the number of specifications and
453 	 * performs simple validation of the input.  At the end
454 	 * of the first pass, the spec array is allocated.
455 	 * The second pass performs detailed validation of the input
456 	 * and fills in the spec array.
457 	 */
458 	maxnspec = UINT_MAX / sizeof(spec_t);
459 	for (pass = 0; pass < 2; pass++) {
460 		lineno = 0;
461 		data->nspec = 0;
462 		data->ncomp = 0;
463 		while (fgets(line_buf, sizeof line_buf - 1, fp)
464 		       && data->nspec < maxnspec) {
465 			if (process_line(rec, path, prefix, line_buf,
466 					 pass, ++lineno) != 0)
467 				goto finish;
468 		}
469 		if (pass == 1) {
470 			status = nodups_specs(data, path);
471 			if (status)
472 				goto finish;
473 		}
474 		lineno = 0;
475 		if (homedirfp)
476 			while (fgets(line_buf, sizeof line_buf - 1, homedirfp)
477 			       && data->nspec < maxnspec) {
478 				if (process_line
479 				    (rec, homedir_path, prefix,
480 				     line_buf, pass, ++lineno) != 0)
481 					goto finish;
482 			}
483 
484 		lineno = 0;
485 		if (localfp)
486 			while (fgets(line_buf, sizeof line_buf - 1, localfp)
487 			       && data->nspec < maxnspec) {
488 				if (process_line
489 				    (rec, local_path, prefix, line_buf,
490 				     pass, ++lineno) != 0)
491 					goto finish;
492 			}
493 
494 		if (pass == 0) {
495 			if (data->nspec == 0) {
496 				status = 0;
497 				goto finish;
498 			}
499 			if (NULL == (data->spec_arr =
500 				     (spec_t *) malloc(sizeof(spec_t) * data->nspec)))
501 				goto finish;
502 			memset(data->spec_arr, 0, sizeof(spec_t)*data->nspec);
503 			maxnspec = data->nspec;
504 			rewind(fp);
505 			if (homedirfp)
506 				rewind(homedirfp);
507 			if (localfp)
508 				rewind(localfp);
509 		}
510 	}
511 
512 	/* Move exact pathname specifications to the end. */
513 	spec_copy = (spec_t *) malloc(sizeof(spec_t) * data->nspec);
514 	if (!spec_copy)
515 		goto finish;
516 	j = 0;
517 	for (i = 0; i < data->nspec; i++)
518 		if (data->spec_arr[i].hasMetaChars)
519 			memcpy(&spec_copy[j++],
520 			       &data->spec_arr[i], sizeof(spec_t));
521 	for (i = 0; i < data->nspec; i++)
522 		if (!data->spec_arr[i].hasMetaChars)
523 			memcpy(&spec_copy[j++],
524 			       &data->spec_arr[i], sizeof(spec_t));
525 	free(data->spec_arr);
526 	data->spec_arr = spec_copy;
527 
528 	status = 0;
529 finish:
530 	fclose(fp);
531 	if (data->spec_arr != spec_copy)
532 		free(data->spec_arr);
533 	if (homedirfp)
534 		fclose(homedirfp);
535 	if (localfp)
536 		fclose(localfp);
537 	return status;
538 }
539 
540 /*
541  * Backend interface routines
542  */
closef(struct selabel_handle * rec)543 static void closef(struct selabel_handle *rec)
544 {
545 	struct saved_data *data = (struct saved_data *)rec->data;
546 	struct spec *spec;
547 	struct stem *stem;
548 	unsigned int i;
549 
550 	for (i = 0; i < data->nspec; i++) {
551 		spec = &data->spec_arr[i];
552 		free(spec->regex_str);
553 		free(spec->type_str);
554 		free(spec->lr.ctx_raw);
555 		free(spec->lr.ctx_trans);
556 		if (spec->regcomp) {
557 			pcre_free(spec->regex);
558 			pcre_free_study(spec->sd);
559 		}
560 	}
561 
562 	for (i = 0; i < (unsigned int)data->num_stems; i++) {
563 		stem = &data->stem_arr[i];
564 		free(stem->buf);
565 	}
566 
567 	if (data->spec_arr)
568 		free(data->spec_arr);
569 	if (data->stem_arr)
570 		free(data->stem_arr);
571 
572 	free(data);
573 }
574 
lookup_common(struct selabel_handle * rec,const char * key,int type,bool partial)575 static spec_t *lookup_common(struct selabel_handle *rec,
576 			     const char *key,
577 			     int type,
578 			     bool partial)
579 {
580 	struct saved_data *data = (struct saved_data *)rec->data;
581 	spec_t *spec_arr = data->spec_arr;
582 	int i, rc, file_stem, pcre_options = 0;
583 	mode_t mode = (mode_t)type;
584 	const char *buf;
585 	spec_t *ret = NULL;
586 	char *clean_key = NULL;
587 	const char *prev_slash, *next_slash;
588 	unsigned int sofar = 0;
589 
590 	if (!data->nspec) {
591 		errno = ENOENT;
592 		goto finish;
593 	}
594 
595 	/* Remove duplicate slashes */
596 	if ((next_slash = strstr(key, "//"))) {
597 		clean_key = (char *) malloc(strlen(key) + 1);
598 		if (!clean_key)
599 			goto finish;
600 		prev_slash = key;
601 		while (next_slash) {
602 			memcpy(clean_key + sofar, prev_slash, next_slash - prev_slash);
603 			sofar += next_slash - prev_slash;
604 			prev_slash = next_slash + 1;
605 			next_slash = strstr(prev_slash, "//");
606 		}
607 		strcpy(clean_key + sofar, prev_slash);
608 		key = clean_key;
609 	}
610 
611 	buf = key;
612 	file_stem = find_stem_from_file(data, &buf);
613 	mode &= S_IFMT;
614 
615 	if (partial)
616 		pcre_options |= PCRE_PARTIAL_SOFT;
617 
618 	/*
619 	 * Check for matching specifications in reverse order, so that
620 	 * the last matching specification is used.
621 	 */
622 	for (i = data->nspec - 1; i >= 0; i--) {
623 		/* if the spec in question matches no stem or has the same
624 		 * stem as the file AND if the spec in question has no mode
625 		 * specified or if the mode matches the file mode then we do
626 		 * a regex check        */
627 		if ((spec_arr[i].stem_id == -1
628 		     || spec_arr[i].stem_id == file_stem)
629 		    && (!mode || !spec_arr[i].mode
630 			|| mode == spec_arr[i].mode)) {
631 			if (compile_regex(data, &spec_arr[i], NULL) < 0)
632 				goto finish;
633 			if (spec_arr[i].stem_id == -1)
634 				rc = pcre_exec(spec_arr[i].regex, spec_arr[i].sd, key, strlen(key), 0, pcre_options, NULL, 0);
635 			else
636 				rc = pcre_exec(spec_arr[i].regex, spec_arr[i].sd, buf, strlen(buf), 0, pcre_options, NULL, 0);
637 
638 			if (rc == 0) {
639 				spec_arr[i].matches++;
640 				break;
641 			} else if (partial && rc == PCRE_ERROR_PARTIAL)
642 				break;
643 
644 			if (rc == PCRE_ERROR_NOMATCH)
645 				continue;
646 			/* else it's an error */
647 			goto finish;
648 		}
649 	}
650 
651 	if (i < 0 || strcmp(spec_arr[i].lr.ctx_raw, "<<none>>") == 0) {
652 		/* No matching specification. */
653 		errno = ENOENT;
654 		goto finish;
655 	}
656 
657 	ret = &spec_arr[i];
658 
659 finish:
660 	free(clean_key);
661 	return ret;
662 }
663 
lookup(struct selabel_handle * rec,const char * key,int type)664 static struct selabel_lookup_rec *lookup(struct selabel_handle *rec,
665 					 const char *key, int type)
666 {
667 	spec_t *spec;
668 	spec = lookup_common(rec, key, type, false);
669 	if (spec)
670 		return &spec->lr;
671 	return NULL;
672 }
673 
partial_match(struct selabel_handle * rec,const char * key)674 static bool partial_match(struct selabel_handle *rec, const char *key)
675 {
676 	return lookup_common(rec, key, 0, true) ? true : false;
677 }
678 
lookup_best_match(struct selabel_handle * rec,const char * key,const char ** aliases,int type)679 static struct selabel_lookup_rec *lookup_best_match(struct selabel_handle *rec,
680 						    const char *key,
681 						    const char **aliases,
682 						    int type)
683 {
684 	size_t n, i;
685 	int best = -1;
686 	spec_t **specs;
687 	size_t prefix_len = 0;
688 	struct selabel_lookup_rec *lr = NULL;
689 
690 	if (!aliases || !aliases[0])
691 		return lookup(rec, key, type);
692 
693 	for (n = 0; aliases[n]; n++)
694 		;
695 
696 	specs = calloc(n+1, sizeof(spec_t *));
697 	if (!specs)
698 		return NULL;
699 	specs[0] = lookup_common(rec, key, type, false);
700 	if (specs[0]) {
701 		if (!specs[0]->hasMetaChars) {
702 			/* exact match on key */
703 			lr = &specs[0]->lr;
704 			goto out;
705 		}
706 		best = 0;
707 		prefix_len = specs[0]->prefix_len;
708 	}
709 	for (i = 1; i <= n; i++) {
710 		specs[i] = lookup_common(rec, aliases[i-1], type, false);
711 		if (specs[i]) {
712 			if (!specs[i]->hasMetaChars) {
713 				/* exact match on alias */
714 				lr = &specs[i]->lr;
715 				goto out;
716 			}
717 			if (specs[i]->prefix_len > prefix_len) {
718 				best = i;
719 				prefix_len = specs[i]->prefix_len;
720 			}
721 		}
722 	}
723 
724 	if (best >= 0) {
725 		/* longest fixed prefix match on key or alias */
726 		lr = &specs[best]->lr;
727 	}
728 
729 out:
730 	free(specs);
731 	return lr;
732 }
733 
stats(struct selabel_handle * rec)734 static void stats(struct selabel_handle *rec)
735 {
736 	struct saved_data *data = (struct saved_data *)rec->data;
737 	unsigned int i, nspec = data->nspec;
738 	spec_t *spec_arr = data->spec_arr;
739 
740 	for (i = 0; i < nspec; i++) {
741 		if (spec_arr[i].matches == 0) {
742 			if (spec_arr[i].type_str) {
743 				selinux_log(SELINUX_WARNING,
744 				    "Warning!  No matches for (%s, %s, %s)\n",
745 				    spec_arr[i].regex_str,
746 				    spec_arr[i].type_str,
747 				    spec_arr[i].lr.ctx_raw);
748 			} else {
749 				selinux_log(SELINUX_WARNING,
750 				    "Warning!  No matches for (%s, %s)\n",
751 				    spec_arr[i].regex_str,
752 				    spec_arr[i].lr.ctx_raw);
753 			}
754 		}
755 	}
756 }
757 
selabel_file_init(struct selabel_handle * rec,const struct selinux_opt * opts,unsigned nopts)758 int selabel_file_init(struct selabel_handle *rec, const struct selinux_opt *opts,
759 		      unsigned nopts)
760 {
761 	struct saved_data *data;
762 
763 	data = (struct saved_data *)malloc(sizeof(*data));
764 	if (!data)
765 		return -1;
766 	memset(data, 0, sizeof(*data));
767 
768 	rec->data = data;
769 	rec->func_close = &closef;
770 	rec->func_stats = &stats;
771 	rec->func_lookup = &lookup;
772 	rec->func_partial_match = &partial_match;
773 	rec->func_lookup_best_match = &lookup_best_match;
774 
775 	return init(rec, opts, nopts);
776 }
777