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