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 */
31static 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). */
43static 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 */
62static 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
97static 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;
111out:
112	free(line_buf);
113	return rc;
114}
115
116static 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	}
233end_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;
406out:
407	free(stem_map);
408
409	return rc;
410}
411
412struct file_details {
413	const char *suffix;
414	struct stat sb;
415};
416
417static 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
450static 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
462static 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
532static 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
568static 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
581static 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
605static 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
672out:
673	fclose(cfg);
674	return status;
675err:
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
689static 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
710static void closef(struct selabel_handle *rec);
711
712#ifdef OHOS_FC_INIT
713static 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
771finish:
772	if (status)
773		closef(rec);
774
775	return status;
776}
777#else
778static 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
863finish:
864	if (status)
865		closef(rec);
866
867	return status;
868}
869#endif
870
871/*
872 * Backend interface routines
873 */
874static 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.
922static 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
1055finish:
1056	free(clean_key);
1057	free(sub);
1058	if (result && !result[0]) {
1059		free(result);
1060		result = NULL;
1061	}
1062	return result;
1063}
1064
1065static 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 */
1083static 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
1126oom:
1127	selinux_log(SELINUX_ERROR, "SELinux: %s: Out of memory\n", __func__);
1128	return false;
1129}
1130
1131static 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
1162static 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
1173static bool partial_match(struct selabel_handle *rec, const char *key)
1174{
1175	return lookup_common(rec, key, 0, true) ? true : false;
1176}
1177
1178static 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
1230out:
1231	free(specs);
1232	return lr;
1233}
1234
1235static 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
1245static 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
1319static 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
1349int 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