• 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 <stephen.smalley.work@gmail.com>
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 = 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 			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 = 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 *)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 	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 			return NULL;
568 
569 		rc = stat(path, &fdetails[i].sb);
570 		if (rc)
571 			continue;
572 
573 		/* first file thing found, just take it */
574 		if (!found) {
575 			strcpy(save_path, path);
576 			found = &fdetails[i];
577 			continue;
578 		}
579 
580 		/*
581 		 * Keep picking the newest file found. Where "newest"
582 		 * includes equality. This provides a precedence on
583 		 * secondary suffixes even when the timestamp is the
584 		 * same. Ie choose file_contexts.bin over file_contexts
585 		 * even if the time stamp is the same. Invert this logic
586 		 * on open_oldest set to true. The idea is that if the
587 		 * newest file failed to process, we can attempt to
588 		 * process the oldest. The logic here is subtle and depends
589 		 * on the array ordering in fdetails for the case when time
590 		 * stamps are the same.
591 		 */
592 		if (open_oldest ^
593 			(fdetails[i].sb.st_mtime >= found->sb.st_mtime)) {
594 			found = &fdetails[i];
595 			strcpy(save_path, path);
596 		}
597 	}
598 
599 	if (!found) {
600 		errno = ENOENT;
601 		return NULL;
602 	}
603 
604 	memcpy(sb, &found->sb, sizeof(*sb));
605 	return fopen(save_path, "re");
606 }
607 
process_file(const char * path,const char * suffix,struct selabel_handle * rec,const char * prefix,struct selabel_digest * digest)608 static int process_file(const char *path, const char *suffix,
609 			  struct selabel_handle *rec,
610 			  const char *prefix, struct selabel_digest *digest)
611 {
612 	int rc;
613 	unsigned int i;
614 	struct stat sb;
615 	FILE *fp = NULL;
616 	char found_path[PATH_MAX];
617 
618 	/*
619 	 * On the first pass open the newest modified file. If it fails to
620 	 * process, then the second pass shall open the oldest file. If both
621 	 * passes fail, then it's a fatal error.
622 	 */
623 	for (i = 0; i < 2; i++) {
624 		fp = open_file(path, suffix, found_path, sizeof(found_path),
625 			&sb, i > 0);
626 		if (fp == NULL)
627 			return -1;
628 
629 		rc = fcontext_is_binary(fp);
630 		if (rc < 0) {
631 			(void) fclose(fp);
632 			return -1;
633 		}
634 
635 		rc = rc ?
636 				load_mmap(fp, sb.st_size, rec, found_path) :
637 				process_text_file(fp, prefix, rec, found_path);
638 		if (!rc)
639 			rc = digest_add_specfile(digest, fp, NULL, sb.st_size,
640 				found_path);
641 
642 		fclose(fp);
643 
644 		if (!rc)
645 			return 0;
646 	}
647 	return -1;
648 }
649 
selabel_subs_fini(struct selabel_sub * ptr)650 static void selabel_subs_fini(struct selabel_sub *ptr)
651 {
652 	struct selabel_sub *next;
653 
654 	while (ptr) {
655 		next = ptr->next;
656 		free(ptr->src);
657 		free(ptr->dst);
658 		free(ptr);
659 		ptr = next;
660 	}
661 }
662 
selabel_sub(struct selabel_sub * ptr,const char * src)663 static char *selabel_sub(struct selabel_sub *ptr, const char *src)
664 {
665 	char *dst = NULL;
666 	int len;
667 
668 	while (ptr) {
669 		if (strncmp(src, ptr->src, ptr->slen) == 0 ) {
670 			if (src[ptr->slen] == '/' ||
671 			    src[ptr->slen] == 0) {
672 				if ((src[ptr->slen] == '/') &&
673 				    (strcmp(ptr->dst, "/") == 0))
674 					len = ptr->slen + 1;
675 				else
676 					len = ptr->slen;
677 				if (asprintf(&dst, "%s%s", ptr->dst, &src[len]) < 0)
678 					return NULL;
679 				return dst;
680 			}
681 		}
682 		ptr = ptr->next;
683 	}
684 	return NULL;
685 }
686 
687 #if !defined(BUILD_HOST) && !defined(ANDROID)
selabel_subs_init(const char * path,struct selabel_digest * digest,struct selabel_sub ** out_subs)688 static int selabel_subs_init(const char *path, struct selabel_digest *digest,
689 		       struct selabel_sub **out_subs)
690 {
691 	char buf[1024];
692 	FILE *cfg = fopen(path, "re");
693 	struct selabel_sub *list = NULL, *sub = NULL;
694 	struct stat sb;
695 	int status = -1;
696 
697 	*out_subs = NULL;
698 	if (!cfg) {
699 		/* If the file does not exist, it is not fatal */
700 		return (errno == ENOENT) ? 0 : -1;
701 	}
702 
703 	if (fstat(fileno(cfg), &sb) < 0)
704 		goto out;
705 
706 	while (fgets_unlocked(buf, sizeof(buf) - 1, cfg)) {
707 		char *ptr = NULL;
708 		char *src = buf;
709 		char *dst = NULL;
710 
711 		while (*src && isspace((unsigned char)*src))
712 			src++;
713 		if (src[0] == '#') continue;
714 		ptr = src;
715 		while (*ptr && ! isspace((unsigned char)*ptr))
716 			ptr++;
717 		*ptr++ = '\0';
718 		if (! *src) continue;
719 
720 		dst = ptr;
721 		while (*dst && isspace((unsigned char)*dst))
722 			dst++;
723 		ptr = dst;
724 		while (*ptr && ! isspace((unsigned char)*ptr))
725 			ptr++;
726 		*ptr = '\0';
727 		if (! *dst)
728 			continue;
729 
730 		sub = calloc(1, sizeof(*sub));
731 		if (! sub)
732 			goto err;
733 
734 		sub->src = strdup(src);
735 		if (! sub->src)
736 			goto err;
737 
738 		sub->dst = strdup(dst);
739 		if (! sub->dst)
740 			goto err;
741 
742 		sub->slen = strlen(src);
743 		sub->next = list;
744 		list = sub;
745 		sub = NULL;
746 	}
747 
748 	if (digest_add_specfile(digest, cfg, NULL, sb.st_size, path) < 0)
749 		goto err;
750 
751 	*out_subs = list;
752 	status = 0;
753 
754 out:
755 	fclose(cfg);
756 	return status;
757 err:
758 	if (sub)
759 		free(sub->src);
760 	free(sub);
761 	while (list) {
762 		sub = list->next;
763 		free(list->src);
764 		free(list->dst);
765 		free(list);
766 		list = sub;
767 	}
768 	goto out;
769 }
770 #endif
771 
selabel_sub_key(struct saved_data * data,const char * key)772 static char *selabel_sub_key(struct saved_data *data, const char *key)
773 {
774 	char *ptr = NULL;
775 	char *dptr = NULL;
776 
777 	ptr = selabel_sub(data->subs, key);
778 	if (ptr) {
779 		dptr = selabel_sub(data->dist_subs, ptr);
780 		if (dptr) {
781 			free(ptr);
782 			ptr = dptr;
783 		}
784 	} else {
785 		ptr = selabel_sub(data->dist_subs, key);
786 	}
787 	if (ptr)
788 		return ptr;
789 
790 	return NULL;
791 }
792 
793 static void closef(struct selabel_handle *rec);
794 
795 #ifdef OHOS_FC_INIT
init(struct selabel_handle * rec,const struct selinux_opt * opts,unsigned n)796 static int init(struct selabel_handle *rec, const struct selinux_opt *opts, unsigned n)
797 {
798 	struct saved_data *data = (struct saved_data *)rec->data;
799 	const char *prefix = NULL;
800 	int status = -1;
801 	size_t path_nums = 0;
802 	size_t opt_nums = n;
803 
804 	while (n--) {
805 		switch (opts[n].type) {
806 			case SELABEL_OPT_PATH:
807 				path_nums++;
808 				break;
809 			default:
810 				break;
811 		}
812 	}
813 
814 	if (path_nums == 0) {
815 		selinux_log(SELINUX_ERROR, "No specific file_contexts provided\n");
816 		goto finish;
817 	}
818 
819 	rec->spec_file = (char **)calloc(path_nums, sizeof(char *));
820 	if (rec->spec_file == NULL) {
821 		goto finish;
822 	}
823 	rec->spec_file_nums = path_nums;
824 	size_t i = 0;
825 	n = opt_nums;
826 	while (n--) {
827 		if (opts[n].type == SELABEL_OPT_PATH) {
828 			rec->spec_file[i] = strdup(opts[n].value);
829 			if (rec->spec_file[i] == NULL) {
830 				goto finish;
831 			}
832 			i++;
833 		}
834 	}
835 
836 	for (int path_index = 0; path_index < rec->spec_file_nums; path_index++) {
837 		status = process_file(rec->spec_file[path_index], NULL, rec, prefix, rec->digest);
838 		if (status) {
839 			goto finish;
840 		}
841 
842 		if (rec->validating) {
843 			status = nodups_specs(data, rec->spec_file[path_index]);
844 			if (status) {
845 				goto finish;
846 			}
847 		}
848 	}
849 
850 	digest_gen_hash(rec->digest);
851 
852 	status = sort_specs(data);
853 
854 finish:
855 	if (status)
856 		closef(rec);
857 
858 	return status;
859 }
860 #else
init(struct selabel_handle * rec,const struct selinux_opt * opts,unsigned n)861 static int init(struct selabel_handle *rec, const struct selinux_opt *opts,
862 		unsigned n)
863 {
864 	struct saved_data *data = (struct saved_data *)rec->data;
865 	const char *path = NULL;
866 	const char *prefix = NULL;
867 	int status = -1, baseonly = 0;
868 
869 	/* Process arguments */
870 	while (n) {
871 		n--;
872 		switch(opts[n].type) {
873 		case SELABEL_OPT_PATH:
874 			path = opts[n].value;
875 			break;
876 		case SELABEL_OPT_SUBSET:
877 			prefix = opts[n].value;
878 			break;
879 		case SELABEL_OPT_BASEONLY:
880 			baseonly = !!opts[n].value;
881 			break;
882 		case SELABEL_OPT_UNUSED:
883 		case SELABEL_OPT_VALIDATE:
884 		case SELABEL_OPT_DIGEST:
885 			break;
886 		default:
887 			errno = EINVAL;
888 			return -1;
889 		}
890 	}
891 
892 #if !defined(BUILD_HOST) && !defined(ANDROID)
893 	char subs_file[PATH_MAX + 1];
894 	/* Process local and distribution substitution files */
895 	if (!path) {
896 		status = selabel_subs_init(
897 			selinux_file_context_subs_dist_path(),
898 			rec->digest, &data->dist_subs);
899 		if (status)
900 			goto finish;
901 		status = selabel_subs_init(selinux_file_context_subs_path(),
902 			rec->digest, &data->subs);
903 		if (status)
904 			goto finish;
905 		path = selinux_file_context_path();
906 	} else {
907 		snprintf(subs_file, sizeof(subs_file), "%s.subs_dist", path);
908 		status = selabel_subs_init(subs_file, rec->digest,
909 					   &data->dist_subs);
910 		if (status)
911 			goto finish;
912 		snprintf(subs_file, sizeof(subs_file), "%s.subs", path);
913 		status = selabel_subs_init(subs_file, rec->digest,
914 					   &data->subs);
915 		if (status)
916 			goto finish;
917 	}
918 
919 #endif
920 
921 	if (!path)
922 		goto finish;
923 
924 	rec->spec_file = strdup(path);
925 
926 	/*
927 	 * The do detailed validation of the input and fill the spec array
928 	 */
929 	status = process_file(path, NULL, rec, prefix, rec->digest);
930 	if (status)
931 		goto finish;
932 
933 	if (rec->validating) {
934 		status = nodups_specs(data, path);
935 		if (status)
936 			goto finish;
937 	}
938 
939 	if (!baseonly) {
940 		status = process_file(path, "homedirs", rec, prefix,
941 							    rec->digest);
942 		if (status && errno != ENOENT)
943 			goto finish;
944 
945 		status = process_file(path, "local", rec, prefix,
946 							    rec->digest);
947 		if (status && errno != ENOENT)
948 			goto finish;
949 	}
950 
951 	digest_gen_hash(rec->digest);
952 
953 	status = sort_specs(data);
954 
955 finish:
956 	if (status)
957 		closef(rec);
958 
959 	return status;
960 }
961 #endif
962 
963 /*
964  * Backend interface routines
965  */
closef(struct selabel_handle * rec)966 static void closef(struct selabel_handle *rec)
967 {
968 	struct saved_data *data = (struct saved_data *)rec->data;
969 	struct mmap_area *area, *last_area;
970 	struct spec *spec;
971 	struct stem *stem;
972 	unsigned int i;
973 
974 	if (!data)
975 		return;
976 
977 	selabel_subs_fini(data->subs);
978 	selabel_subs_fini(data->dist_subs);
979 
980 	for (i = 0; i < data->nspec; i++) {
981 		spec = &data->spec_arr[i];
982 		free(spec->lr.ctx_trans);
983 		free(spec->lr.ctx_raw);
984 		regex_data_free(spec->regex);
985 		__pthread_mutex_destroy(&spec->regex_lock);
986 		if (spec->from_mmap)
987 			continue;
988 		free(spec->regex_str);
989 		free(spec->type_str);
990 	}
991 
992 	for (i = 0; i < (unsigned int)data->num_stems; i++) {
993 		stem = &data->stem_arr[i];
994 		if (stem->from_mmap)
995 			continue;
996 		free(stem->buf);
997 	}
998 
999 	if (data->spec_arr)
1000 		free(data->spec_arr);
1001 	if (data->stem_arr)
1002 		free(data->stem_arr);
1003 
1004 	area = data->mmap_areas;
1005 	while (area) {
1006 		munmap(area->addr, area->len);
1007 		last_area = area;
1008 		area = area->next;
1009 		free(last_area);
1010 	}
1011 	free(data);
1012 }
1013 
1014 // Finds all the matches of |key| in the given context. Returns the result in
1015 // the allocated array and updates the match count. If match_count is NULL,
1016 // 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)1017 static struct spec **lookup_all(struct selabel_handle *rec,
1018                                       const char *key,
1019                                       int type,
1020                                       bool partial,
1021                                       size_t *match_count)
1022 {
1023 	struct saved_data *data = (struct saved_data *)rec->data;
1024 	struct spec *spec_arr = data->spec_arr;
1025 	int i, rc, file_stem;
1026 	size_t len;
1027 	mode_t mode = (mode_t)type;
1028 	char *clean_key = NULL;
1029 	const char *prev_slash, *next_slash;
1030 	unsigned int sofar = 0;
1031 	char *sub = NULL;
1032 
1033 	struct spec **result = NULL;
1034 	if (match_count) {
1035 		*match_count = 0;
1036 		result = calloc(data->nspec, sizeof(struct spec*));
1037 	} else {
1038 		result = calloc(1, sizeof(struct spec*));
1039 	}
1040 	if (!result) {
1041 		selinux_log(SELINUX_ERROR, "Failed to allocate %zu bytes of data\n",
1042 			    data->nspec * sizeof(struct spec*));
1043 		goto finish;
1044 	}
1045 
1046 	if (!data->nspec) {
1047 		errno = ENOENT;
1048 		goto finish;
1049 	}
1050 
1051 	/* Remove duplicate slashes */
1052 	if ((next_slash = strstr(key, "//"))) {
1053 		clean_key = (char *) malloc(strlen(key) + 1);
1054 		if (!clean_key)
1055 			goto finish;
1056 		prev_slash = key;
1057 		while (next_slash) {
1058 			memcpy(clean_key + sofar, prev_slash, next_slash - prev_slash);
1059 			sofar += next_slash - prev_slash;
1060 			prev_slash = next_slash + 1;
1061 			next_slash = strstr(prev_slash, "//");
1062 		}
1063 		strcpy(clean_key + sofar, prev_slash);
1064 		key = clean_key;
1065 	}
1066 
1067 	/* remove trailing slash */
1068 	len = strlen(key);
1069 	if (len == 0) {
1070 		errno = EINVAL;
1071 		goto finish;
1072 	}
1073 
1074 	if (len > 1 && key[len - 1] == '/') {
1075 		/* reuse clean_key from above if available */
1076 		if (!clean_key) {
1077 			clean_key = (char *) malloc(len);
1078 			if (!clean_key)
1079 				goto finish;
1080 
1081 			memcpy(clean_key, key, len - 1);
1082 		}
1083 
1084 		clean_key[len - 1] = '\0';
1085 		key = clean_key;
1086 	}
1087 
1088 	sub = selabel_sub_key(data, key);
1089 	if (sub)
1090 		key = sub;
1091 
1092 	file_stem = find_stem_from_file(data, key);
1093 	mode &= S_IFMT;
1094 
1095 	/*
1096 	 * Check for matching specifications in reverse order, so that
1097 	 * the last matching specification is used.
1098 	 */
1099 	for (i = data->nspec - 1; i >= 0; i--) {
1100 		struct spec *spec = &spec_arr[i];
1101 		/* if the spec in question matches no stem or has the same
1102 		 * stem as the file AND if the spec in question has no mode
1103 		 * specified or if the mode matches the file mode then we do
1104 		 * a regex check        */
1105 		bool stem_matches = spec->stem_id == -1 || spec->stem_id == file_stem;
1106 		// Don't check the stem if we want to find partial matches.
1107                 // Otherwise the case "/abc/efg/(/.*)?" will be considered
1108                 //a miss for "/abc".
1109 		if ((partial || stem_matches) &&
1110 				(!mode || !spec->mode || mode == spec->mode)) {
1111 			if (compile_regex(spec, NULL) < 0)
1112 				goto finish;
1113 			rc = regex_match(spec->regex, key, partial);
1114 			if (rc == REGEX_MATCH || (partial && rc == REGEX_MATCH_PARTIAL)) {
1115 				if (rc == REGEX_MATCH) {
1116 #ifdef __ATOMIC_RELAXED
1117 					__atomic_store_n(&spec->any_matches,
1118 							 true, __ATOMIC_RELAXED);
1119 #else
1120 #error "Please use a compiler that supports __atomic builtins"
1121 #endif
1122 				}
1123 
1124 				if (strcmp(spec_arr[i].lr.ctx_raw, "<<none>>") == 0) {
1125 					errno = ENOENT;
1126 					goto finish;
1127 				}
1128 
1129 				if (match_count) {
1130 					result[*match_count] = spec;
1131 					*match_count += 1;
1132 					// Continue to find all the matches.
1133 					continue;
1134 				}
1135 				result[0] = spec;
1136 				break;
1137 			}
1138 
1139 			if (rc == REGEX_NO_MATCH)
1140 				continue;
1141 
1142 			errno = ENOENT;
1143 			/* else it's an error */
1144 			goto finish;
1145 		}
1146 	}
1147 	if (!result[0])
1148 		errno = ENOENT;
1149 
1150 finish:
1151 	free(clean_key);
1152 	free(sub);
1153 	if (result && !result[0]) {
1154 		free(result);
1155 		result = NULL;
1156 	}
1157 	return result;
1158 }
1159 
lookup_common(struct selabel_handle * rec,const char * key,int type,bool partial)1160 static struct spec *lookup_common(struct selabel_handle *rec,
1161                                   const char *key,
1162                                   int type,
1163                                   bool partial) {
1164 	struct spec **matches = lookup_all(rec, key, type, partial, NULL);
1165 	if (!matches) {
1166 		return NULL;
1167 	}
1168 	struct spec *result = matches[0];
1169 	free(matches);
1170 	return result;
1171 }
1172 
1173 /*
1174  * Returns true if the digest of all partial matched contexts is the same as
1175  * the one saved by setxattr, otherwise returns false. The length of the SHA1
1176  * digest will always be returned. The caller must free any returned digests.
1177  */
get_digests_all_partial_matches(struct selabel_handle * rec,const char * pathname,uint8_t ** calculated_digest,uint8_t ** xattr_digest,size_t * digest_len)1178 static bool get_digests_all_partial_matches(struct selabel_handle *rec,
1179 					    const char *pathname,
1180 					    uint8_t **calculated_digest,
1181 					    uint8_t **xattr_digest,
1182 					    size_t *digest_len)
1183 {
1184 	uint8_t read_digest[SHA1_HASH_SIZE];
1185 	ssize_t read_size = getxattr(pathname, RESTORECON_PARTIAL_MATCH_DIGEST,
1186 				     read_digest, SHA1_HASH_SIZE
1187 #ifdef __APPLE__
1188 				     , 0, 0
1189 #endif /* __APPLE __ */
1190 				    );
1191 	uint8_t hash_digest[SHA1_HASH_SIZE];
1192 	bool status = selabel_hash_all_partial_matches(rec, pathname,
1193 						       hash_digest);
1194 
1195 	*xattr_digest = NULL;
1196 	*calculated_digest = NULL;
1197 	*digest_len = SHA1_HASH_SIZE;
1198 
1199 	if (read_size == SHA1_HASH_SIZE) {
1200 		*xattr_digest = calloc(1, SHA1_HASH_SIZE + 1);
1201 		if (!*xattr_digest)
1202 			goto oom;
1203 
1204 		memcpy(*xattr_digest, read_digest, SHA1_HASH_SIZE);
1205 	}
1206 
1207 	if (status) {
1208 		*calculated_digest = calloc(1, SHA1_HASH_SIZE + 1);
1209 		if (!*calculated_digest)
1210 			goto oom;
1211 
1212 		memcpy(*calculated_digest, hash_digest, SHA1_HASH_SIZE);
1213 	}
1214 
1215 	if (status && read_size == SHA1_HASH_SIZE &&
1216 	    memcmp(read_digest, hash_digest, SHA1_HASH_SIZE) == 0)
1217 		return true;
1218 
1219 	return false;
1220 
1221 oom:
1222 	selinux_log(SELINUX_ERROR, "SELinux: %s: Out of memory\n", __func__);
1223 	return false;
1224 }
1225 
hash_all_partial_matches(struct selabel_handle * rec,const char * key,uint8_t * digest)1226 static bool hash_all_partial_matches(struct selabel_handle *rec, const char *key, uint8_t *digest)
1227 {
1228 	assert(digest);
1229 
1230 	size_t total_matches;
1231 	struct spec **matches = lookup_all(rec, key, 0, true, &total_matches);
1232 	if (!matches) {
1233 		return false;
1234 	}
1235 
1236 	Sha1Context context;
1237 	Sha1Initialise(&context);
1238 	size_t i;
1239 	for (i = 0; i < total_matches; i++) {
1240 		char* regex_str = matches[i]->regex_str;
1241 		mode_t mode = matches[i]->mode;
1242 		char* ctx_raw = matches[i]->lr.ctx_raw;
1243 
1244 		Sha1Update(&context, regex_str, strlen(regex_str) + 1);
1245 		Sha1Update(&context, &mode, sizeof(mode_t));
1246 		Sha1Update(&context, ctx_raw, strlen(ctx_raw) + 1);
1247 	}
1248 
1249 	SHA1_HASH sha1_hash;
1250 	Sha1Finalise(&context, &sha1_hash);
1251 	memcpy(digest, sha1_hash.bytes, SHA1_HASH_SIZE);
1252 
1253 	free(matches);
1254 	return true;
1255 }
1256 
lookup(struct selabel_handle * rec,const char * key,int type)1257 static struct selabel_lookup_rec *lookup(struct selabel_handle *rec,
1258 					 const char *key, int type)
1259 {
1260 	struct spec *spec;
1261 
1262 	spec = lookup_common(rec, key, type, false);
1263 	if (spec)
1264 		return &spec->lr;
1265 	return NULL;
1266 }
1267 
partial_match(struct selabel_handle * rec,const char * key)1268 static bool partial_match(struct selabel_handle *rec, const char *key)
1269 {
1270 	return lookup_common(rec, key, 0, true) ? true : false;
1271 }
1272 
lookup_best_match(struct selabel_handle * rec,const char * key,const char ** aliases,int type)1273 static struct selabel_lookup_rec *lookup_best_match(struct selabel_handle *rec,
1274 						    const char *key,
1275 						    const char **aliases,
1276 						    int type)
1277 {
1278 	size_t n, i;
1279 	int best = -1;
1280 	struct spec **specs;
1281 	size_t prefix_len = 0;
1282 	struct selabel_lookup_rec *lr = NULL;
1283 
1284 	if (!aliases || !aliases[0])
1285 		return lookup(rec, key, type);
1286 
1287 	for (n = 0; aliases[n]; n++)
1288 		;
1289 
1290 	specs = calloc(n+1, sizeof(struct spec *));
1291 	if (!specs)
1292 		return NULL;
1293 	specs[0] = lookup_common(rec, key, type, false);
1294 	if (specs[0]) {
1295 		if (!specs[0]->hasMetaChars) {
1296 			/* exact match on key */
1297 			lr = &specs[0]->lr;
1298 			goto out;
1299 		}
1300 		best = 0;
1301 		prefix_len = specs[0]->prefix_len;
1302 	}
1303 	for (i = 1; i <= n; i++) {
1304 		specs[i] = lookup_common(rec, aliases[i-1], type, false);
1305 		if (specs[i]) {
1306 			if (!specs[i]->hasMetaChars) {
1307 				/* exact match on alias */
1308 				lr = &specs[i]->lr;
1309 				goto out;
1310 			}
1311 			if (specs[i]->prefix_len > prefix_len) {
1312 				best = i;
1313 				prefix_len = specs[i]->prefix_len;
1314 			}
1315 		}
1316 	}
1317 
1318 	if (best >= 0) {
1319 		/* longest fixed prefix match on key or alias */
1320 		lr = &specs[best]->lr;
1321 	} else {
1322 		errno = ENOENT;
1323 	}
1324 
1325 out:
1326 	free(specs);
1327 	return lr;
1328 }
1329 
incomp(const struct spec * spec1,const struct spec * spec2,const char * reason,int i,int j)1330 static enum selabel_cmp_result incomp(const struct spec *spec1, const struct spec *spec2, const char *reason, int i, int j)
1331 {
1332 	selinux_log(SELINUX_INFO,
1333 		    "selabel_cmp: mismatched %s on entry %d: (%s, %x, %s) vs entry %d: (%s, %x, %s)\n",
1334 		    reason,
1335 		    i, spec1->regex_str, spec1->mode, spec1->lr.ctx_raw,
1336 		    j, spec2->regex_str, spec2->mode, spec2->lr.ctx_raw);
1337 	return SELABEL_INCOMPARABLE;
1338 }
1339 
cmp(const struct selabel_handle * h1,const struct selabel_handle * h2)1340 static enum selabel_cmp_result cmp(const struct selabel_handle *h1,
1341 				   const struct selabel_handle *h2)
1342 {
1343 	const struct saved_data *data1 = (const struct saved_data *)h1->data;
1344 	const struct saved_data *data2 = (const struct saved_data *)h2->data;
1345 	unsigned int i, nspec1 = data1->nspec, j, nspec2 = data2->nspec;
1346 	const struct spec *spec_arr1 = data1->spec_arr, *spec_arr2 = data2->spec_arr;
1347 	const struct stem *stem_arr1 = data1->stem_arr, *stem_arr2 = data2->stem_arr;
1348 	bool skipped1 = false, skipped2 = false;
1349 
1350 	i = 0;
1351 	j = 0;
1352 	while (i < nspec1 && j < nspec2) {
1353 		const struct spec *spec1 = &spec_arr1[i];
1354 		const struct spec *spec2 = &spec_arr2[j];
1355 
1356 		/*
1357 		 * Because sort_specs() moves exact pathnames to the
1358 		 * end, we might need to skip over additional regex
1359 		 * entries that only exist in one of the configurations.
1360 		 */
1361 		if (!spec1->hasMetaChars && spec2->hasMetaChars) {
1362 			j++;
1363 			skipped2 = true;
1364 			continue;
1365 		}
1366 
1367 		if (spec1->hasMetaChars && !spec2->hasMetaChars) {
1368 			i++;
1369 			skipped1 = true;
1370 			continue;
1371 		}
1372 
1373 		if (spec1->regex && spec2->regex) {
1374 			if (regex_cmp(spec1->regex, spec2->regex) == SELABEL_INCOMPARABLE){
1375 				return incomp(spec1, spec2, "regex", i, j);
1376 			}
1377 		} else {
1378 			if (strcmp(spec1->regex_str, spec2->regex_str))
1379 				return incomp(spec1, spec2, "regex_str", i, j);
1380 		}
1381 
1382 		if (spec1->mode != spec2->mode)
1383 			return incomp(spec1, spec2, "mode", i, j);
1384 
1385 		if (spec1->stem_id == -1 && spec2->stem_id != -1)
1386 			return incomp(spec1, spec2, "stem_id", i, j);
1387 		if (spec2->stem_id == -1 && spec1->stem_id != -1)
1388 			return incomp(spec1, spec2, "stem_id", i, j);
1389 		if (spec1->stem_id != -1 && spec2->stem_id != -1) {
1390 			const struct stem *stem1 = &stem_arr1[spec1->stem_id];
1391 			const struct stem *stem2 = &stem_arr2[spec2->stem_id];
1392 			if (stem1->len != stem2->len ||
1393 			    strncmp(stem1->buf, stem2->buf, stem1->len))
1394 				return incomp(spec1, spec2, "stem", i, j);
1395 		}
1396 
1397 		if (strcmp(spec1->lr.ctx_raw, spec2->lr.ctx_raw))
1398 			return incomp(spec1, spec2, "ctx_raw", i, j);
1399 
1400 		i++;
1401 		j++;
1402 	}
1403 
1404 	if ((skipped1 || i < nspec1) && !skipped2)
1405 		return SELABEL_SUPERSET;
1406 	if ((skipped2 || j < nspec2) && !skipped1)
1407 		return SELABEL_SUBSET;
1408 	if (skipped1 && skipped2)
1409 		return SELABEL_INCOMPARABLE;
1410 	return SELABEL_EQUAL;
1411 }
1412 
1413 
stats(struct selabel_handle * rec)1414 static void stats(struct selabel_handle *rec)
1415 {
1416 	struct saved_data *data = (struct saved_data *)rec->data;
1417 	unsigned int i, nspec = data->nspec;
1418 	struct spec *spec_arr = data->spec_arr;
1419 	bool any_matches;
1420 
1421 	for (i = 0; i < nspec; i++) {
1422 #ifdef __ATOMIC_RELAXED
1423 		any_matches = __atomic_load_n(&spec_arr[i].any_matches, __ATOMIC_RELAXED);
1424 #else
1425 #error "Please use a compiler that supports __atomic builtins"
1426 #endif
1427 		if (!any_matches) {
1428 			if (spec_arr[i].type_str) {
1429 				COMPAT_LOG(SELINUX_WARNING,
1430 				    "Warning!  No matches for (%s, %s, %s)\n",
1431 				    spec_arr[i].regex_str,
1432 				    spec_arr[i].type_str,
1433 				    spec_arr[i].lr.ctx_raw);
1434 			} else {
1435 				COMPAT_LOG(SELINUX_WARNING,
1436 				    "Warning!  No matches for (%s, %s)\n",
1437 				    spec_arr[i].regex_str,
1438 				    spec_arr[i].lr.ctx_raw);
1439 			}
1440 		}
1441 	}
1442 }
1443 
selabel_file_init(struct selabel_handle * rec,const struct selinux_opt * opts,unsigned nopts)1444 int selabel_file_init(struct selabel_handle *rec,
1445 				    const struct selinux_opt *opts,
1446 				    unsigned nopts)
1447 {
1448 	struct saved_data *data;
1449 
1450 	data = (struct saved_data *)calloc(1, sizeof(*data));
1451 	if (!data)
1452 		return -1;
1453 
1454 	rec->data = data;
1455 	rec->func_close = &closef;
1456 	rec->func_stats = &stats;
1457 	rec->func_lookup = &lookup;
1458 	rec->func_partial_match = &partial_match;
1459 	rec->func_get_digests_all_partial_matches =
1460 					&get_digests_all_partial_matches;
1461 	rec->func_hash_all_partial_matches = &hash_all_partial_matches;
1462 	rec->func_lookup_best_match = &lookup_best_match;
1463 	rec->func_cmp = &cmp;
1464 
1465 	return init(rec, opts, nopts);
1466 }
1467