xref: /aosp_15_r20/external/selinux/libselinux/src/label_file.c (revision 2d543d20722ada2425b5bdab9d0d1d29470e7bba)
1 /*
2  * File contexts backend for labeling system
3  *
4  * Author : Eamon Walsh <[email protected]>
5  * Author : Stephen Smalley <[email protected]>
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 "hashtab.h"
23 #include "callbacks.h"
24 #include "label_internal.h"
25 #include "label_file.h"
26 
27 /* controls the shrink multiple of the hashtab length */
28 #define SHRINK_MULTIS 1
29 
30 struct chkdups_key {
31 	char *regex;
32 	unsigned int mode;
33 };
34 
35 /*
36  * Internals, mostly moved over from matchpathcon.c
37  */
38 
39 /* return the length of the text that is the stem of a file name */
get_stem_from_file_name(const char * const buf)40 static int get_stem_from_file_name(const char *const buf)
41 {
42 	const char *tmp = strchr(buf + 1, '/');
43 
44 	if (!tmp)
45 		return 0;
46 	return tmp - buf;
47 }
48 
49 /* find the stem of a file name, returns the index into stem_arr (or -1 if
50  * there is no match - IE for a file in the root directory or a regex that is
51  * too complex for us). */
find_stem_from_file(struct saved_data * data,const char * key)52 static int find_stem_from_file(struct saved_data *data, const char *key)
53 {
54 	int i;
55 	int stem_len = get_stem_from_file_name(key);
56 
57 	if (!stem_len)
58 		return -1;
59 	for (i = 0; i < data->num_stems; i++) {
60 		if (stem_len == data->stem_arr[i].len
61 		    && !strncmp(key, data->stem_arr[i].buf, stem_len)) {
62 			return i;
63 		}
64 	}
65 	return -1;
66 }
67 
68 /*
69  * hash calculation and key comparison of hash table
70  */
71 ignore_unsigned_overflow_
symhash(hashtab_t h,const_hashtab_key_t key)72 static unsigned int symhash(hashtab_t h, const_hashtab_key_t key)
73 {
74 	const struct chkdups_key *k = (const struct chkdups_key *)key;
75 	const char *p = NULL;
76 	size_t size;
77 	unsigned int val = 0;
78 
79 	size = strlen(k->regex);
80 	for (p = k->regex; ((size_t) (p - k->regex)) < size; p++)
81 		val =
82 			((val << 4) | (val >> (8 * sizeof(unsigned int) - 4))) ^ (*p);
83 	return val % h->size;
84 }
85 
symcmp(hashtab_t h,const_hashtab_key_t key1,const_hashtab_key_t key2)86 static int symcmp(hashtab_t h
87 		  __attribute__ ((unused)), const_hashtab_key_t key1,
88 		  const_hashtab_key_t key2)
89 {
90 	const struct chkdups_key *a = (const struct chkdups_key *)key1;
91 	const struct chkdups_key *b = (const struct chkdups_key *)key2;
92 
93 	return strcmp(a->regex, b->regex) || (a->mode && b->mode && a->mode != b->mode);
94 }
95 
destroy_chkdups_key(hashtab_key_t key)96 static int destroy_chkdups_key(hashtab_key_t key)
97 {
98 	free(key);
99 
100 	return 0;
101 }
102 
103 /*
104  * Warn about duplicate specifications.
105  */
nodups_specs(struct saved_data * data,const char * path)106 static int nodups_specs(struct saved_data *data, const char *path)
107 {
108 	int rc = 0, ret = 0;
109 	unsigned int ii;
110 	struct spec *curr_spec, *spec_arr = data->spec_arr;
111 	struct chkdups_key *new = NULL;
112 	unsigned int hashtab_len = (data->nspec / SHRINK_MULTIS) ? data->nspec / SHRINK_MULTIS : 1;
113 
114 	hashtab_t hash_table = selinux_hashtab_create(symhash, symcmp, hashtab_len);
115 	if (!hash_table) {
116 		rc = -1;
117 		COMPAT_LOG(SELINUX_ERROR, "%s: hashtab create failed.\n", path);
118 		return rc;
119 	}
120 	for (ii = 0; ii < data->nspec; ii++) {
121 		new = (struct chkdups_key *)malloc(sizeof(struct chkdups_key));
122 		if (!new) {
123 			rc = -1;
124 			selinux_hashtab_destroy_key(hash_table, destroy_chkdups_key);
125 			COMPAT_LOG(SELINUX_ERROR, "%s: hashtab key create failed.\n", path);
126 			return rc;
127 		}
128 		new->regex = spec_arr[ii].regex_str;
129 		new->mode = spec_arr[ii].mode;
130 		ret = selinux_hashtab_insert(hash_table, (hashtab_key_t)new, &spec_arr[ii]);
131 		if (ret == HASHTAB_SUCCESS)
132 			continue;
133 		if (ret == HASHTAB_PRESENT) {
134 			curr_spec =
135 				(struct spec *)selinux_hashtab_search(hash_table, (hashtab_key_t)new);
136 			rc = -1;
137 			errno = EINVAL;
138 			free(new);
139 			if (strcmp(spec_arr[ii].lr.ctx_raw, curr_spec->lr.ctx_raw)) {
140 				COMPAT_LOG
141 					(SELINUX_ERROR,
142 					 "%s: Multiple different specifications for %s  (%s and %s).\n",
143 					 path, curr_spec->regex_str,
144 					 spec_arr[ii].lr.ctx_raw,
145 					 curr_spec->lr.ctx_raw);
146 			} else {
147 				COMPAT_LOG
148 					(SELINUX_ERROR,
149 					 "%s: Multiple same specifications for %s.\n",
150 					 path, curr_spec->regex_str);
151 			}
152 		}
153 		if (ret == HASHTAB_OVERFLOW) {
154 			rc = -1;
155 			free(new);
156 			COMPAT_LOG
157 				(SELINUX_ERROR,
158 				"%s: hashtab happen memory error.\n",
159 				path);
160 			break;
161 		}
162 	}
163 
164 	selinux_hashtab_destroy_key(hash_table, destroy_chkdups_key);
165 
166 	return rc;
167 }
168 
process_text_file(FILE * fp,const char * prefix,struct selabel_handle * rec,const char * path)169 static int process_text_file(FILE *fp, const char *prefix,
170 			     struct selabel_handle *rec, const char *path)
171 {
172 	int rc;
173 	size_t line_len;
174 	unsigned int lineno = 0;
175 	char *line_buf = NULL;
176 
177 	while (getline(&line_buf, &line_len, fp) > 0) {
178 		rc = process_line(rec, path, prefix, line_buf, ++lineno);
179 		if (rc)
180 			goto out;
181 	}
182 	rc = 0;
183 out:
184 	free(line_buf);
185 	return rc;
186 }
187 
load_mmap(FILE * fp,size_t len,struct selabel_handle * rec,const char * path)188 static int load_mmap(FILE *fp, size_t len, struct selabel_handle *rec,
189 		     const char *path)
190 {
191 	struct saved_data *data = (struct saved_data *)rec->data;
192 	int rc;
193 	char *addr, *str_buf;
194 	int *stem_map;
195 	struct mmap_area *mmap_area;
196 	uint32_t i, magic, version;
197 	uint32_t entry_len, stem_map_len, regex_array_len;
198 	const char *reg_version;
199 	const char *reg_arch;
200 	char reg_arch_matches = 0;
201 
202 	mmap_area = malloc(sizeof(*mmap_area));
203 	if (!mmap_area) {
204 		return -1;
205 	}
206 
207 	addr = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fileno(fp), 0);
208 	if (addr == MAP_FAILED) {
209 		free(mmap_area);
210 		perror("mmap");
211 		return -1;
212 	}
213 
214 	/* save where we mmap'd the file to cleanup on close() */
215 	mmap_area->addr = mmap_area->next_addr = addr;
216 	mmap_area->len = mmap_area->next_len = len;
217 	mmap_area->next = data->mmap_areas;
218 	data->mmap_areas = mmap_area;
219 
220 	/* check if this looks like an fcontext file */
221 	rc = next_entry(&magic, mmap_area, sizeof(uint32_t));
222 	if (rc < 0 || magic != SELINUX_MAGIC_COMPILED_FCONTEXT)
223 		return -1;
224 
225 	/* check if this version is higher than we understand */
226 	rc = next_entry(&version, mmap_area, sizeof(uint32_t));
227 	if (rc < 0 || version > SELINUX_COMPILED_FCONTEXT_MAX_VERS)
228 		return -1;
229 
230 	reg_version = regex_version();
231 	if (!reg_version)
232 		return -1;
233 
234 	reg_arch = regex_arch_string();
235 	if (!reg_arch)
236 		return -1;
237 
238 	if (version >= SELINUX_COMPILED_FCONTEXT_PCRE_VERS) {
239 
240 		len = strlen(reg_version);
241 
242 		rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t));
243 		if (rc < 0)
244 			return -1;
245 
246 		/* Check version lengths */
247 		if (len != entry_len)
248 			return -1;
249 
250 		/* Check if regex version mismatch */
251 		str_buf = malloc(entry_len + 1);
252 		if (!str_buf)
253 			return -1;
254 
255 		rc = next_entry(str_buf, mmap_area, entry_len);
256 		if (rc < 0) {
257 			free(str_buf);
258 			return -1;
259 		}
260 
261 		str_buf[entry_len] = '\0';
262 		if ((strcmp(str_buf, reg_version) != 0)) {
263 			COMPAT_LOG(SELINUX_ERROR,
264 				"Regex version mismatch, expected: %s actual: %s\n",
265 				reg_version, str_buf);
266 			free(str_buf);
267 			return -1;
268 		}
269 		free(str_buf);
270 
271 		if (version >= SELINUX_COMPILED_FCONTEXT_REGEX_ARCH) {
272 			len = strlen(reg_arch);
273 
274 			rc = next_entry(&entry_len, mmap_area,
275 					sizeof(uint32_t));
276 			if (rc < 0)
277 				return -1;
278 
279 			/* Check arch string lengths */
280 			if (len != entry_len) {
281 				/*
282 				 * Skip the entry and conclude that we have
283 				 * a mismatch, which is not fatal.
284 				 */
285 				next_entry(NULL, mmap_area, entry_len);
286 				goto end_arch_check;
287 			}
288 
289 			/* Check if arch string mismatch */
290 			str_buf = malloc(entry_len + 1);
291 			if (!str_buf)
292 				return -1;
293 
294 			rc = next_entry(str_buf, mmap_area, entry_len);
295 			if (rc < 0) {
296 				free(str_buf);
297 				return -1;
298 			}
299 
300 			str_buf[entry_len] = '\0';
301 			reg_arch_matches = strcmp(str_buf, reg_arch) == 0;
302 			free(str_buf);
303 		}
304 	}
305 end_arch_check:
306 
307 	/* allocate the stems_data array */
308 	rc = next_entry(&stem_map_len, mmap_area, sizeof(uint32_t));
309 	if (rc < 0)
310 		return -1;
311 
312 	/*
313 	 * map indexed by the stem # in the mmap file and contains the stem
314 	 * number in the data stem_arr
315 	 */
316 	stem_map = calloc(stem_map_len, sizeof(*stem_map));
317 	if (!stem_map)
318 		return -1;
319 
320 	for (i = 0; i < stem_map_len; i++) {
321 		char *buf;
322 		uint32_t stem_len;
323 		int newid;
324 
325 		/* the length does not include the nul */
326 		rc = next_entry(&stem_len, mmap_area, sizeof(uint32_t));
327 		if (rc < 0 || !stem_len) {
328 			rc = -1;
329 			goto out;
330 		}
331 
332 		/* Check for stem_len wrap around. */
333 		if (stem_len < UINT32_MAX) {
334 			buf = (char *)mmap_area->next_addr;
335 			/* Check if over-run before null check. */
336 			rc = next_entry(NULL, mmap_area, (stem_len + 1));
337 			if (rc < 0)
338 				goto out;
339 
340 			if (buf[stem_len] != '\0') {
341 				rc = -1;
342 				goto out;
343 			}
344 		} else {
345 			rc = -1;
346 			goto out;
347 		}
348 
349 		/* store the mapping between old and new */
350 		newid = find_stem(data, buf, stem_len);
351 		if (newid < 0) {
352 			newid = store_stem(data, buf, stem_len);
353 			if (newid < 0) {
354 				rc = newid;
355 				goto out;
356 			}
357 			data->stem_arr[newid].from_mmap = 1;
358 		}
359 		stem_map[i] = newid;
360 	}
361 
362 	/* allocate the regex array */
363 	rc = next_entry(&regex_array_len, mmap_area, sizeof(uint32_t));
364 	if (rc < 0 || !regex_array_len) {
365 		rc = -1;
366 		goto out;
367 	}
368 
369 	for (i = 0; i < regex_array_len; i++) {
370 		struct spec *spec;
371 		int32_t stem_id, meta_chars;
372 		uint32_t mode = 0, prefix_len = 0;
373 
374 		rc = grow_specs(data);
375 		if (rc < 0)
376 			goto out;
377 
378 		spec = &data->spec_arr[data->nspec];
379 		spec->from_mmap = 1;
380 
381 		/* Process context */
382 		rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t));
383 		if (rc < 0 || !entry_len) {
384 			rc = -1;
385 			goto out;
386 		}
387 
388 		str_buf = malloc(entry_len);
389 		if (!str_buf) {
390 			rc = -1;
391 			goto out;
392 		}
393 		rc = next_entry(str_buf, mmap_area, entry_len);
394 		if (rc < 0) {
395 			free(str_buf);
396 			goto out;
397 		}
398 
399 		if (str_buf[entry_len - 1] != '\0') {
400 			free(str_buf);
401 			rc = -1;
402 			goto out;
403 		}
404 		spec->lr.ctx_raw = str_buf;
405 
406 		if (strcmp(spec->lr.ctx_raw, "<<none>>") && rec->validating) {
407 			if (selabel_validate(&spec->lr) < 0) {
408 				selinux_log(SELINUX_ERROR,
409 					    "%s: context %s is invalid\n",
410 					    path, spec->lr.ctx_raw);
411 				goto out;
412 			}
413 		}
414 
415 		/* Process regex string */
416 		rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t));
417 		if (rc < 0 || !entry_len) {
418 			rc = -1;
419 			goto out;
420 		}
421 
422 		spec->regex_str = (char *)mmap_area->next_addr;
423 		rc = next_entry(NULL, mmap_area, entry_len);
424 		if (rc < 0)
425 			goto out;
426 
427 		if (spec->regex_str[entry_len - 1] != '\0') {
428 			rc = -1;
429 			goto out;
430 		}
431 
432 		/* Process mode */
433 		if (version >= SELINUX_COMPILED_FCONTEXT_MODE)
434 			rc = next_entry(&mode, mmap_area, sizeof(uint32_t));
435 		else
436 			rc = next_entry(&mode, mmap_area, sizeof(mode_t));
437 		if (rc < 0)
438 			goto out;
439 
440 		spec->mode = mode;
441 
442 		/* map the stem id from the mmap file to the data->stem_arr */
443 		rc = next_entry(&stem_id, mmap_area, sizeof(int32_t));
444 		if (rc < 0)
445 			goto out;
446 
447 		if (stem_id < 0 || stem_id >= (int32_t)stem_map_len)
448 			spec->stem_id = -1;
449 		else
450 			spec->stem_id = stem_map[stem_id];
451 
452 		/* retrieve the hasMetaChars bit */
453 		rc = next_entry(&meta_chars, mmap_area, sizeof(uint32_t));
454 		if (rc < 0)
455 			goto out;
456 
457 		spec->hasMetaChars = meta_chars;
458 		/* and prefix length for use by selabel_lookup_best_match */
459 		if (version >= SELINUX_COMPILED_FCONTEXT_PREFIX_LEN) {
460 			rc = next_entry(&prefix_len, mmap_area,
461 					    sizeof(uint32_t));
462 			if (rc < 0)
463 				goto out;
464 
465 			spec->prefix_len = prefix_len;
466 		}
467 
468 		rc = regex_load_mmap(mmap_area, &spec->regex, reg_arch_matches,
469 				     &spec->regex_compiled);
470 		if (rc < 0)
471 			goto out;
472 
473 		__pthread_mutex_init(&spec->regex_lock, NULL);
474 		data->nspec++;
475 	}
476 
477 	rc = 0;
478 out:
479 	free(stem_map);
480 
481 	return rc;
482 }
483 
484 struct file_details {
485 	const char *suffix;
486 	struct stat sb;
487 };
488 
rolling_append(char * current,const char * suffix,size_t max)489 static char *rolling_append(char *current, const char *suffix, size_t max)
490 {
491 	size_t size;
492 	size_t suffix_size;
493 	size_t current_size;
494 
495 	if (!suffix)
496 		return current;
497 
498 	current_size = strlen(current);
499 	suffix_size = strlen(suffix);
500 
501 	size = current_size + suffix_size;
502 	if (size < current_size || size < suffix_size)
503 		return NULL;
504 
505 	/* ensure space for the '.' and the '\0' characters. */
506 	if (size >= (SIZE_MAX - 2))
507 		return NULL;
508 
509 	size += 2;
510 
511 	if (size > max)
512 		return NULL;
513 
514 	/* Append any given suffix */
515 	char *to = current + current_size;
516 	*to++ = '.';
517 	strcpy(to, suffix);
518 
519 	return current;
520 }
521 
fcontext_is_binary(FILE * fp)522 static int fcontext_is_binary(FILE *fp)
523 {
524 	uint32_t magic;
525 	int rc;
526 
527 	size_t len = fread(&magic, sizeof(magic), 1, fp);
528 
529 	rc = fseek(fp, 0L, SEEK_SET);
530 	if (rc == -1)
531 		return -1;
532 
533 	return (len && (magic == SELINUX_MAGIC_COMPILED_FCONTEXT));
534 }
535 
536 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
537 
open_file(const char * path,const char * suffix,char * save_path,size_t len,struct stat * sb,bool open_oldest)538 static FILE *open_file(const char *path, const char *suffix,
539 	       char *save_path, size_t len, struct stat *sb, bool open_oldest)
540 {
541 	unsigned int i;
542 	int rc;
543 	char stack_path[len];
544 	struct file_details *found = NULL;
545 
546 	/*
547 	 * Rolling append of suffix. Try to open with path.suffix then the
548 	 * next as path.suffix.suffix and so forth.
549 	 */
550 	struct file_details fdetails[2] = {
551 			{ .suffix = suffix },
552 			{ .suffix = "bin" }
553 	};
554 
555 	rc = snprintf(stack_path, sizeof(stack_path), "%s", path);
556 	if (rc >= (int) sizeof(stack_path)) {
557 		errno = ENAMETOOLONG;
558 		return NULL;
559 	}
560 
561 	for (i = 0; i < ARRAY_SIZE(fdetails); i++) {
562 
563 		/* This handles the case if suffix is null */
564 		path = rolling_append(stack_path, fdetails[i].suffix,
565 				      sizeof(stack_path));
566 		if (!path) {
567 			errno = ENOMEM;
568 			return NULL;
569 		}
570 
571 		rc = stat(path, &fdetails[i].sb);
572 		if (rc)
573 			continue;
574 
575 		/* first file thing found, just take it */
576 		if (!found) {
577 			strcpy(save_path, path);
578 			found = &fdetails[i];
579 			continue;
580 		}
581 
582 		/*
583 		 * Keep picking the newest file found. Where "newest"
584 		 * includes equality. This provides a precedence on
585 		 * secondary suffixes even when the timestamp is the
586 		 * same. Ie choose file_contexts.bin over file_contexts
587 		 * even if the time stamp is the same. Invert this logic
588 		 * on open_oldest set to true. The idea is that if the
589 		 * newest file failed to process, we can attempt to
590 		 * process the oldest. The logic here is subtle and depends
591 		 * on the array ordering in fdetails for the case when time
592 		 * stamps are the same.
593 		 */
594 		if (open_oldest ^
595 			(fdetails[i].sb.st_mtime >= found->sb.st_mtime)) {
596 			found = &fdetails[i];
597 			strcpy(save_path, path);
598 		}
599 	}
600 
601 	if (!found) {
602 		errno = ENOENT;
603 		return NULL;
604 	}
605 
606 	memcpy(sb, &found->sb, sizeof(*sb));
607 	return fopen(save_path, "re");
608 }
609 
process_file(const char * path,const char * suffix,struct selabel_handle * rec,const char * prefix,struct selabel_digest * digest)610 static int process_file(const char *path, const char *suffix,
611 			  struct selabel_handle *rec,
612 			  const char *prefix, struct selabel_digest *digest)
613 {
614 	int rc;
615 	unsigned int i;
616 	struct stat sb;
617 	FILE *fp = NULL;
618 	char found_path[PATH_MAX];
619 
620 	/*
621 	 * On the first pass open the newest modified file. If it fails to
622 	 * process, then the second pass shall open the oldest file. If both
623 	 * passes fail, then it's a fatal error.
624 	 */
625 	for (i = 0; i < 2; i++) {
626 		fp = open_file(path, suffix, found_path, sizeof(found_path),
627 			&sb, i > 0);
628 		if (fp == NULL)
629 			return -1;
630 
631 		rc = fcontext_is_binary(fp);
632 		if (rc < 0) {
633 			fclose_errno_safe(fp);
634 			return -1;
635 		}
636 
637 		rc = rc ?
638 				load_mmap(fp, sb.st_size, rec, found_path) :
639 				process_text_file(fp, prefix, rec, found_path);
640 		if (!rc)
641 			rc = digest_add_specfile(digest, fp, NULL, sb.st_size,
642 				found_path);
643 
644 		fclose_errno_safe(fp);
645 
646 		if (!rc)
647 			return 0;
648 	}
649 	return -1;
650 }
651 
selabel_subs_fini(struct selabel_sub * ptr)652 static void selabel_subs_fini(struct selabel_sub *ptr)
653 {
654 	struct selabel_sub *next;
655 
656 	while (ptr) {
657 		next = ptr->next;
658 		free(ptr->src);
659 		free(ptr->dst);
660 		free(ptr);
661 		ptr = next;
662 	}
663 }
664 
selabel_sub(struct selabel_sub * ptr,const char * src)665 static char *selabel_sub(struct selabel_sub *ptr, const char *src)
666 {
667 	char *dst = NULL;
668 	int len;
669 
670 	while (ptr) {
671 		if (strncmp(src, ptr->src, ptr->slen) == 0 ) {
672 			if (src[ptr->slen] == '/' ||
673 			    src[ptr->slen] == 0) {
674 				if ((src[ptr->slen] == '/') &&
675 				    (strcmp(ptr->dst, "/") == 0))
676 					len = ptr->slen + 1;
677 				else
678 					len = ptr->slen;
679 				if (asprintf(&dst, "%s%s", ptr->dst, &src[len]) < 0)
680 					return NULL;
681 				return dst;
682 			}
683 		}
684 		ptr = ptr->next;
685 	}
686 	return NULL;
687 }
688 
689 #if !defined(BUILD_HOST) && !defined(ANDROID)
selabel_subs_init(const char * path,struct selabel_digest * digest,struct selabel_sub ** out_subs)690 static int selabel_subs_init(const char *path, struct selabel_digest *digest,
691 		       struct selabel_sub **out_subs)
692 {
693 	char buf[1024];
694 	FILE *cfg = fopen(path, "re");
695 	struct selabel_sub *list = NULL, *sub = NULL;
696 	struct stat sb;
697 	int status = -1;
698 
699 	*out_subs = NULL;
700 	if (!cfg) {
701 		/* If the file does not exist, it is not fatal */
702 		return (errno == ENOENT) ? 0 : -1;
703 	}
704 
705 	if (fstat(fileno(cfg), &sb) < 0)
706 		goto out;
707 
708 	while (fgets_unlocked(buf, sizeof(buf) - 1, cfg)) {
709 		char *ptr = NULL;
710 		char *src = buf;
711 		char *dst = NULL;
712 
713 		while (*src && isspace((unsigned char)*src))
714 			src++;
715 		if (src[0] == '#') continue;
716 		ptr = src;
717 		while (*ptr && ! isspace((unsigned char)*ptr))
718 			ptr++;
719 		*ptr++ = '\0';
720 		if (! *src) continue;
721 
722 		dst = ptr;
723 		while (*dst && isspace((unsigned char)*dst))
724 			dst++;
725 		ptr = dst;
726 		while (*ptr && ! isspace((unsigned char)*ptr))
727 			ptr++;
728 		*ptr = '\0';
729 		if (! *dst)
730 			continue;
731 
732 		sub = calloc(1, sizeof(*sub));
733 		if (! sub)
734 			goto err;
735 
736 		sub->src = strdup(src);
737 		if (! sub->src)
738 			goto err;
739 
740 		sub->dst = strdup(dst);
741 		if (! sub->dst)
742 			goto err;
743 
744 		sub->slen = strlen(src);
745 		sub->next = list;
746 		list = sub;
747 		sub = NULL;
748 	}
749 
750 	if (digest_add_specfile(digest, cfg, NULL, sb.st_size, path) < 0)
751 		goto err;
752 
753 	*out_subs = list;
754 	status = 0;
755 
756 out:
757 	fclose(cfg);
758 	return status;
759 err:
760 	if (sub)
761 		free(sub->src);
762 	free(sub);
763 	while (list) {
764 		sub = list->next;
765 		free(list->src);
766 		free(list->dst);
767 		free(list);
768 		list = sub;
769 	}
770 	goto out;
771 }
772 #endif
773 
selabel_sub_key(struct saved_data * data,const char * key)774 static char *selabel_sub_key(struct saved_data *data, const char *key)
775 {
776 	char *ptr = NULL;
777 	char *dptr = NULL;
778 
779 	ptr = selabel_sub(data->subs, key);
780 	if (ptr) {
781 		dptr = selabel_sub(data->dist_subs, ptr);
782 		if (dptr) {
783 			free(ptr);
784 			ptr = dptr;
785 		}
786 	} else {
787 		ptr = selabel_sub(data->dist_subs, key);
788 	}
789 	if (ptr)
790 		return ptr;
791 
792 	return NULL;
793 }
794 
795 static void closef(struct selabel_handle *rec);
796 
init(struct selabel_handle * rec,const struct selinux_opt * opts,unsigned n)797 static int init(struct selabel_handle *rec, const struct selinux_opt *opts,
798 		unsigned n)
799 {
800 	struct saved_data *data = (struct saved_data *)rec->data;
801 	size_t num_paths = 0;
802 	char **path = NULL;
803 	const char *prefix = NULL;
804 	int status = -1;
805 	size_t i;
806 	bool baseonly = false;
807 	bool path_provided;
808 
809 	/* Process arguments */
810 	i = n;
811 	while (i--) {
812 		switch(opts[i].type) {
813 		case SELABEL_OPT_PATH:
814 			num_paths++;
815 			break;
816 		case SELABEL_OPT_SUBSET:
817 			prefix = opts[i].value;
818 			break;
819 		case SELABEL_OPT_BASEONLY:
820 			baseonly = !!opts[i].value;
821 			break;
822 		case SELABEL_OPT_UNUSED:
823 		case SELABEL_OPT_VALIDATE:
824 		case SELABEL_OPT_DIGEST:
825 			break;
826 		default:
827 			errno = EINVAL;
828 			return -1;
829 		}
830 	}
831 
832 	if (!num_paths) {
833 		num_paths = 1;
834 		path_provided = false;
835 	} else {
836 		path_provided = true;
837 	}
838 
839 	path = calloc(num_paths, sizeof(*path));
840 	if (path == NULL) {
841 		goto finish;
842 	}
843 	rec->spec_files = path;
844 	rec->spec_files_len = num_paths;
845 
846 	if (path_provided) {
847 		for (i = 0; i < n; i++) {
848 			switch(opts[i].type) {
849 			case SELABEL_OPT_PATH:
850 				*path = strdup(opts[i].value);
851 				if (*path == NULL)
852 					goto finish;
853 				path++;
854 				break;
855 			default:
856 				break;
857 			}
858 		}
859 	}
860 #if !defined(BUILD_HOST) && !defined(ANDROID)
861 	char subs_file[PATH_MAX + 1];
862 	/* Process local and distribution substitution files */
863 	if (!path_provided) {
864 		status = selabel_subs_init(
865 			selinux_file_context_subs_dist_path(),
866 			rec->digest, &data->dist_subs);
867 		if (status)
868 			goto finish;
869 		status = selabel_subs_init(selinux_file_context_subs_path(),
870 			rec->digest, &data->subs);
871 		if (status)
872 			goto finish;
873 		rec->spec_files[0] = strdup(selinux_file_context_path());
874 		if (rec->spec_files[0] == NULL)
875 			goto finish;
876 	} else {
877 		for (i = 0; i < num_paths; i++) {
878 			snprintf(subs_file, sizeof(subs_file), "%s.subs_dist", rec->spec_files[i]);
879 			status = selabel_subs_init(subs_file, rec->digest,
880 					   &data->dist_subs);
881 			if (status)
882 				goto finish;
883 			snprintf(subs_file, sizeof(subs_file), "%s.subs", rec->spec_files[i]);
884 			status = selabel_subs_init(subs_file, rec->digest,
885 					   &data->subs);
886 			if (status)
887 				goto finish;
888 		}
889 	}
890 #else
891 	if (!path_provided) {
892 		selinux_log(SELINUX_ERROR, "No path given to file labeling backend\n");
893 		goto finish;
894 	}
895 #endif
896 
897 	/*
898 	 * Do detailed validation of the input and fill the spec array
899 	 */
900 	for (i = 0; i < num_paths; i++) {
901 		status = process_file(rec->spec_files[i], NULL, rec, prefix, rec->digest);
902 		if (status)
903 			goto finish;
904 
905 		if (rec->validating) {
906 			status = nodups_specs(data, rec->spec_files[i]);
907 			if (status)
908 				goto finish;
909 		}
910 	}
911 
912 	if (!baseonly) {
913 		status = process_file(rec->spec_files[0], "homedirs", rec, prefix,
914 							    rec->digest);
915 		if (status && errno != ENOENT)
916 			goto finish;
917 
918 		status = process_file(rec->spec_files[0], "local", rec, prefix,
919 							    rec->digest);
920 		if (status && errno != ENOENT)
921 			goto finish;
922 	}
923 
924 	digest_gen_hash(rec->digest);
925 
926 	status = sort_specs(data);
927 
928 finish:
929 	if (status)
930 		closef(rec);
931 
932 	return status;
933 }
934 
935 /*
936  * Backend interface routines
937  */
closef(struct selabel_handle * rec)938 static void closef(struct selabel_handle *rec)
939 {
940 	struct saved_data *data = (struct saved_data *)rec->data;
941 	struct mmap_area *area, *last_area;
942 	struct spec *spec;
943 	struct stem *stem;
944 	unsigned int i;
945 
946 	if (!data)
947 		return;
948 
949 	/* make sure successive ->func_close() calls are harmless */
950 	rec->data = NULL;
951 
952 	selabel_subs_fini(data->subs);
953 	selabel_subs_fini(data->dist_subs);
954 
955 	for (i = 0; i < data->nspec; i++) {
956 		spec = &data->spec_arr[i];
957 		free(spec->lr.ctx_trans);
958 		free(spec->lr.ctx_raw);
959 		regex_data_free(spec->regex);
960 		__pthread_mutex_destroy(&spec->regex_lock);
961 		if (spec->from_mmap)
962 			continue;
963 		free(spec->regex_str);
964 		free(spec->type_str);
965 	}
966 
967 	for (i = 0; i < (unsigned int)data->num_stems; i++) {
968 		stem = &data->stem_arr[i];
969 		if (stem->from_mmap)
970 			continue;
971 		free(stem->buf);
972 	}
973 
974 	if (data->spec_arr)
975 		free(data->spec_arr);
976 	if (data->stem_arr)
977 		free(data->stem_arr);
978 
979 	area = data->mmap_areas;
980 	while (area) {
981 		munmap(area->addr, area->len);
982 		last_area = area;
983 		area = area->next;
984 		free(last_area);
985 	}
986 	free(data);
987 	rec->data = NULL;
988 }
989 
990 // Finds all the matches of |key| in the given context. Returns the result in
991 // the allocated array and updates the match count. If match_count is NULL,
992 // stops early once the 1st match is found.
lookup_all(struct selabel_handle * rec,const char * key,int type,bool partial,size_t * match_count)993 static struct spec **lookup_all(struct selabel_handle *rec,
994                                       const char *key,
995                                       int type,
996                                       bool partial,
997                                       size_t *match_count)
998 {
999 	struct saved_data *data = (struct saved_data *)rec->data;
1000 	struct spec *spec_arr = data->spec_arr;
1001 	int i, rc, file_stem;
1002 	size_t len;
1003 	mode_t mode = (mode_t)type;
1004 	char *clean_key = NULL;
1005 	const char *prev_slash, *next_slash;
1006 	unsigned int sofar = 0;
1007 	char *sub = NULL;
1008 
1009 	struct spec **result = NULL;
1010 	if (match_count) {
1011 		*match_count = 0;
1012 		result = calloc(data->nspec, sizeof(struct spec*));
1013 	} else {
1014 		result = calloc(1, sizeof(struct spec*));
1015 	}
1016 	if (!result) {
1017 		selinux_log(SELINUX_ERROR, "Failed to allocate %zu bytes of data\n",
1018 			    data->nspec * sizeof(struct spec*));
1019 		goto finish;
1020 	}
1021 
1022 	if (!data->nspec) {
1023 		errno = ENOENT;
1024 		goto finish;
1025 	}
1026 
1027 	/* Remove duplicate slashes */
1028 	if ((next_slash = strstr(key, "//"))) {
1029 		clean_key = (char *) malloc(strlen(key) + 1);
1030 		if (!clean_key)
1031 			goto finish;
1032 		prev_slash = key;
1033 		while (next_slash) {
1034 			memcpy(clean_key + sofar, prev_slash, next_slash - prev_slash);
1035 			sofar += next_slash - prev_slash;
1036 			prev_slash = next_slash + 1;
1037 			next_slash = strstr(prev_slash, "//");
1038 		}
1039 		strcpy(clean_key + sofar, prev_slash);
1040 		key = clean_key;
1041 	}
1042 
1043 	/* remove trailing slash */
1044 	len = strlen(key);
1045 	if (len == 0) {
1046 		errno = EINVAL;
1047 		goto finish;
1048 	}
1049 
1050 	if (len > 1 && key[len - 1] == '/') {
1051 		/* reuse clean_key from above if available */
1052 		if (!clean_key) {
1053 			clean_key = (char *) malloc(len);
1054 			if (!clean_key)
1055 				goto finish;
1056 
1057 			memcpy(clean_key, key, len - 1);
1058 		}
1059 
1060 		clean_key[len - 1] = '\0';
1061 		key = clean_key;
1062 	}
1063 
1064 	sub = selabel_sub_key(data, key);
1065 	if (sub)
1066 		key = sub;
1067 
1068 	file_stem = find_stem_from_file(data, key);
1069 	mode &= S_IFMT;
1070 
1071 	/*
1072 	 * Check for matching specifications in reverse order, so that
1073 	 * the last matching specification is used.
1074 	 */
1075 	for (i = data->nspec - 1; i >= 0; i--) {
1076 		struct spec *spec = &spec_arr[i];
1077 		/* if the spec in question matches no stem or has the same
1078 		 * stem as the file AND if the spec in question has no mode
1079 		 * specified or if the mode matches the file mode then we do
1080 		 * a regex check        */
1081 		bool stem_matches = spec->stem_id == -1 || spec->stem_id == file_stem;
1082 		// Don't check the stem if we want to find partial matches.
1083                 // Otherwise the case "/abc/efg/(/.*)?" will be considered
1084                 //a miss for "/abc".
1085 		if ((partial || stem_matches) &&
1086 				(!mode || !spec->mode || mode == spec->mode)) {
1087 			if (compile_regex(spec, NULL) < 0)
1088 				goto finish;
1089 			rc = regex_match(spec->regex, key, partial);
1090 			if (rc == REGEX_MATCH || (partial && rc == REGEX_MATCH_PARTIAL)) {
1091 				if (rc == REGEX_MATCH) {
1092 #ifdef __ATOMIC_RELAXED
1093 					__atomic_store_n(&spec->any_matches,
1094 							 true, __ATOMIC_RELAXED);
1095 #else
1096 #error "Please use a compiler that supports __atomic builtins"
1097 #endif
1098 				}
1099 
1100 				if (strcmp(spec_arr[i].lr.ctx_raw, "<<none>>") == 0) {
1101 					errno = ENOENT;
1102 					goto finish;
1103 				}
1104 
1105 				if (match_count) {
1106 					result[*match_count] = spec;
1107 					*match_count += 1;
1108 					// Continue to find all the matches.
1109 					continue;
1110 				}
1111 				result[0] = spec;
1112 				break;
1113 			}
1114 
1115 			if (rc == REGEX_NO_MATCH)
1116 				continue;
1117 
1118 			errno = ENOENT;
1119 			/* else it's an error */
1120 			goto finish;
1121 		}
1122 	}
1123 	if (!result[0])
1124 		errno = ENOENT;
1125 
1126 finish:
1127 	free(clean_key);
1128 	free(sub);
1129 	if (result && !result[0]) {
1130 		free(result);
1131 		result = NULL;
1132 	}
1133 	return result;
1134 }
1135 
lookup_common(struct selabel_handle * rec,const char * key,int type,bool partial)1136 static struct spec *lookup_common(struct selabel_handle *rec,
1137                                   const char *key,
1138                                   int type,
1139                                   bool partial) {
1140 	struct spec **matches = lookup_all(rec, key, type, partial, NULL);
1141 	if (!matches) {
1142 		return NULL;
1143 	}
1144 	struct spec *result = matches[0];
1145 	free(matches);
1146 	return result;
1147 }
1148 
1149 /*
1150  * Returns true if the digest of all partial matched contexts is the same as
1151  * the one saved by setxattr, otherwise returns false. The length of the SHA1
1152  * digest will always be returned. The caller must free any returned digests.
1153  */
get_digests_all_partial_matches(struct selabel_handle * rec,const char * pathname,uint8_t ** calculated_digest,uint8_t ** xattr_digest,size_t * digest_len)1154 static bool get_digests_all_partial_matches(struct selabel_handle *rec,
1155 					    const char *pathname,
1156 					    uint8_t **calculated_digest,
1157 					    uint8_t **xattr_digest,
1158 					    size_t *digest_len)
1159 {
1160 	uint8_t read_digest[SHA1_HASH_SIZE];
1161 	ssize_t read_size = getxattr(pathname, RESTORECON_PARTIAL_MATCH_DIGEST,
1162 				     read_digest, SHA1_HASH_SIZE
1163 #ifdef __APPLE__
1164 				     , 0, 0
1165 #endif /* __APPLE __ */
1166 				    );
1167 	uint8_t hash_digest[SHA1_HASH_SIZE];
1168 	bool status = selabel_hash_all_partial_matches(rec, pathname,
1169 						       hash_digest);
1170 
1171 	*xattr_digest = NULL;
1172 	*calculated_digest = NULL;
1173 	*digest_len = SHA1_HASH_SIZE;
1174 
1175 	if (read_size == SHA1_HASH_SIZE) {
1176 		*xattr_digest = calloc(1, SHA1_HASH_SIZE + 1);
1177 		if (!*xattr_digest)
1178 			goto oom;
1179 
1180 		memcpy(*xattr_digest, read_digest, SHA1_HASH_SIZE);
1181 	}
1182 
1183 	if (status) {
1184 		*calculated_digest = calloc(1, SHA1_HASH_SIZE + 1);
1185 		if (!*calculated_digest)
1186 			goto oom;
1187 
1188 		memcpy(*calculated_digest, hash_digest, SHA1_HASH_SIZE);
1189 	}
1190 
1191 	if (status && read_size == SHA1_HASH_SIZE &&
1192 	    memcmp(read_digest, hash_digest, SHA1_HASH_SIZE) == 0)
1193 		return true;
1194 
1195 	return false;
1196 
1197 oom:
1198 	selinux_log(SELINUX_ERROR, "SELinux: %s: Out of memory\n", __func__);
1199 	return false;
1200 }
1201 
hash_all_partial_matches(struct selabel_handle * rec,const char * key,uint8_t * digest)1202 static bool hash_all_partial_matches(struct selabel_handle *rec, const char *key, uint8_t *digest)
1203 {
1204 	assert(digest);
1205 
1206 	size_t total_matches;
1207 	struct spec **matches = lookup_all(rec, key, 0, true, &total_matches);
1208 	if (!matches) {
1209 		return false;
1210 	}
1211 
1212 	Sha1Context context;
1213 	Sha1Initialise(&context);
1214 	size_t i;
1215 	for (i = 0; i < total_matches; i++) {
1216 		char* regex_str = matches[i]->regex_str;
1217 		mode_t mode = matches[i]->mode;
1218 		char* ctx_raw = matches[i]->lr.ctx_raw;
1219 
1220 		Sha1Update(&context, regex_str, strlen(regex_str) + 1);
1221 		Sha1Update(&context, &mode, sizeof(mode_t));
1222 		Sha1Update(&context, ctx_raw, strlen(ctx_raw) + 1);
1223 	}
1224 
1225 	SHA1_HASH sha1_hash;
1226 	Sha1Finalise(&context, &sha1_hash);
1227 	memcpy(digest, sha1_hash.bytes, SHA1_HASH_SIZE);
1228 
1229 	free(matches);
1230 	return true;
1231 }
1232 
lookup(struct selabel_handle * rec,const char * key,int type)1233 static struct selabel_lookup_rec *lookup(struct selabel_handle *rec,
1234 					 const char *key, int type)
1235 {
1236 	struct spec *spec;
1237 
1238 	spec = lookup_common(rec, key, type, false);
1239 	if (spec)
1240 		return &spec->lr;
1241 	return NULL;
1242 }
1243 
partial_match(struct selabel_handle * rec,const char * key)1244 static bool partial_match(struct selabel_handle *rec, const char *key)
1245 {
1246 	return lookup_common(rec, key, 0, true) ? true : false;
1247 }
1248 
lookup_best_match(struct selabel_handle * rec,const char * key,const char ** aliases,int type)1249 static struct selabel_lookup_rec *lookup_best_match(struct selabel_handle *rec,
1250 						    const char *key,
1251 						    const char **aliases,
1252 						    int type)
1253 {
1254 	size_t n, i;
1255 	int best = -1;
1256 	struct spec **specs;
1257 	size_t prefix_len = 0;
1258 	struct selabel_lookup_rec *lr = NULL;
1259 
1260 	if (!aliases || !aliases[0])
1261 		return lookup(rec, key, type);
1262 
1263 	for (n = 0; aliases[n]; n++)
1264 		;
1265 
1266 	specs = calloc(n+1, sizeof(struct spec *));
1267 	if (!specs)
1268 		return NULL;
1269 	specs[0] = lookup_common(rec, key, type, false);
1270 	if (specs[0]) {
1271 		if (!specs[0]->hasMetaChars) {
1272 			/* exact match on key */
1273 			lr = &specs[0]->lr;
1274 			goto out;
1275 		}
1276 		best = 0;
1277 		prefix_len = specs[0]->prefix_len;
1278 	}
1279 	for (i = 1; i <= n; i++) {
1280 		specs[i] = lookup_common(rec, aliases[i-1], type, false);
1281 		if (specs[i]) {
1282 			if (!specs[i]->hasMetaChars) {
1283 				/* exact match on alias */
1284 				lr = &specs[i]->lr;
1285 				goto out;
1286 			}
1287 			if (specs[i]->prefix_len > prefix_len) {
1288 				best = i;
1289 				prefix_len = specs[i]->prefix_len;
1290 			}
1291 		}
1292 	}
1293 
1294 	if (best >= 0) {
1295 		/* longest fixed prefix match on key or alias */
1296 		lr = &specs[best]->lr;
1297 	} else {
1298 		errno = ENOENT;
1299 	}
1300 
1301 out:
1302 	free(specs);
1303 	return lr;
1304 }
1305 
incomp(const struct spec * spec1,const struct spec * spec2,const char * reason,int i,int j)1306 static enum selabel_cmp_result incomp(const struct spec *spec1, const struct spec *spec2, const char *reason, int i, int j)
1307 {
1308 	selinux_log(SELINUX_INFO,
1309 		    "selabel_cmp: mismatched %s on entry %d: (%s, %x, %s) vs entry %d: (%s, %x, %s)\n",
1310 		    reason,
1311 		    i, spec1->regex_str, spec1->mode, spec1->lr.ctx_raw,
1312 		    j, spec2->regex_str, spec2->mode, spec2->lr.ctx_raw);
1313 	return SELABEL_INCOMPARABLE;
1314 }
1315 
cmp(const struct selabel_handle * h1,const struct selabel_handle * h2)1316 static enum selabel_cmp_result cmp(const struct selabel_handle *h1,
1317 				   const struct selabel_handle *h2)
1318 {
1319 	const struct saved_data *data1 = (const struct saved_data *)h1->data;
1320 	const struct saved_data *data2 = (const struct saved_data *)h2->data;
1321 	unsigned int i, nspec1 = data1->nspec, j, nspec2 = data2->nspec;
1322 	const struct spec *spec_arr1 = data1->spec_arr, *spec_arr2 = data2->spec_arr;
1323 	const struct stem *stem_arr1 = data1->stem_arr, *stem_arr2 = data2->stem_arr;
1324 	bool skipped1 = false, skipped2 = false;
1325 
1326 	i = 0;
1327 	j = 0;
1328 	while (i < nspec1 && j < nspec2) {
1329 		const struct spec *spec1 = &spec_arr1[i];
1330 		const struct spec *spec2 = &spec_arr2[j];
1331 
1332 		/*
1333 		 * Because sort_specs() moves exact pathnames to the
1334 		 * end, we might need to skip over additional regex
1335 		 * entries that only exist in one of the configurations.
1336 		 */
1337 		if (!spec1->hasMetaChars && spec2->hasMetaChars) {
1338 			j++;
1339 			skipped2 = true;
1340 			continue;
1341 		}
1342 
1343 		if (spec1->hasMetaChars && !spec2->hasMetaChars) {
1344 			i++;
1345 			skipped1 = true;
1346 			continue;
1347 		}
1348 
1349 		if (spec1->regex && spec2->regex) {
1350 			if (regex_cmp(spec1->regex, spec2->regex) == SELABEL_INCOMPARABLE){
1351 				return incomp(spec1, spec2, "regex", i, j);
1352 			}
1353 		} else {
1354 			if (strcmp(spec1->regex_str, spec2->regex_str))
1355 				return incomp(spec1, spec2, "regex_str", i, j);
1356 		}
1357 
1358 		if (spec1->mode != spec2->mode)
1359 			return incomp(spec1, spec2, "mode", i, j);
1360 
1361 		if (spec1->stem_id == -1 && spec2->stem_id != -1)
1362 			return incomp(spec1, spec2, "stem_id", i, j);
1363 		if (spec2->stem_id == -1 && spec1->stem_id != -1)
1364 			return incomp(spec1, spec2, "stem_id", i, j);
1365 		if (spec1->stem_id != -1 && spec2->stem_id != -1) {
1366 			const struct stem *stem1 = &stem_arr1[spec1->stem_id];
1367 			const struct stem *stem2 = &stem_arr2[spec2->stem_id];
1368 			if (stem1->len != stem2->len ||
1369 			    strncmp(stem1->buf, stem2->buf, stem1->len))
1370 				return incomp(spec1, spec2, "stem", i, j);
1371 		}
1372 
1373 		if (strcmp(spec1->lr.ctx_raw, spec2->lr.ctx_raw))
1374 			return incomp(spec1, spec2, "ctx_raw", i, j);
1375 
1376 		i++;
1377 		j++;
1378 	}
1379 
1380 	if ((skipped1 || i < nspec1) && !skipped2)
1381 		return SELABEL_SUPERSET;
1382 	if ((skipped2 || j < nspec2) && !skipped1)
1383 		return SELABEL_SUBSET;
1384 	if (skipped1 && skipped2)
1385 		return SELABEL_INCOMPARABLE;
1386 	return SELABEL_EQUAL;
1387 }
1388 
1389 
stats(struct selabel_handle * rec)1390 static void stats(struct selabel_handle *rec)
1391 {
1392 	struct saved_data *data = (struct saved_data *)rec->data;
1393 	unsigned int i, nspec = data->nspec;
1394 	struct spec *spec_arr = data->spec_arr;
1395 	bool any_matches;
1396 
1397 	for (i = 0; i < nspec; i++) {
1398 #ifdef __ATOMIC_RELAXED
1399 		any_matches = __atomic_load_n(&spec_arr[i].any_matches, __ATOMIC_RELAXED);
1400 #else
1401 #error "Please use a compiler that supports __atomic builtins"
1402 #endif
1403 		if (!any_matches) {
1404 			if (spec_arr[i].type_str) {
1405 				COMPAT_LOG(SELINUX_WARNING,
1406 				    "Warning!  No matches for (%s, %s, %s)\n",
1407 				    spec_arr[i].regex_str,
1408 				    spec_arr[i].type_str,
1409 				    spec_arr[i].lr.ctx_raw);
1410 			} else {
1411 				COMPAT_LOG(SELINUX_WARNING,
1412 				    "Warning!  No matches for (%s, %s)\n",
1413 				    spec_arr[i].regex_str,
1414 				    spec_arr[i].lr.ctx_raw);
1415 			}
1416 		}
1417 	}
1418 }
1419 
selabel_file_init(struct selabel_handle * rec,const struct selinux_opt * opts,unsigned nopts)1420 int selabel_file_init(struct selabel_handle *rec,
1421 				    const struct selinux_opt *opts,
1422 				    unsigned nopts)
1423 {
1424 	struct saved_data *data;
1425 
1426 	data = (struct saved_data *)calloc(1, sizeof(*data));
1427 	if (!data)
1428 		return -1;
1429 
1430 	rec->data = data;
1431 	rec->func_close = &closef;
1432 	rec->func_stats = &stats;
1433 	rec->func_lookup = &lookup;
1434 	rec->func_partial_match = &partial_match;
1435 	rec->func_get_digests_all_partial_matches =
1436 					&get_digests_all_partial_matches;
1437 	rec->func_hash_all_partial_matches = &hash_all_partial_matches;
1438 	rec->func_lookup_best_match = &lookup_best_match;
1439 	rec->func_cmp = &cmp;
1440 
1441 	return init(rec, opts, nopts);
1442 }
1443