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