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