16cd6a6acSopenharmony_ci/*
26cd6a6acSopenharmony_ci * File contexts backend for labeling system
36cd6a6acSopenharmony_ci *
46cd6a6acSopenharmony_ci * Author : Eamon Walsh <ewalsh@tycho.nsa.gov>
56cd6a6acSopenharmony_ci * Author : Stephen Smalley <sds@tycho.nsa.gov>
66cd6a6acSopenharmony_ci */
76cd6a6acSopenharmony_ci
86cd6a6acSopenharmony_ci#include <assert.h>
96cd6a6acSopenharmony_ci#include <fcntl.h>
106cd6a6acSopenharmony_ci#include <stdarg.h>
116cd6a6acSopenharmony_ci#include <string.h>
126cd6a6acSopenharmony_ci#include <stdio.h>
136cd6a6acSopenharmony_ci#include <ctype.h>
146cd6a6acSopenharmony_ci#include <errno.h>
156cd6a6acSopenharmony_ci#include <limits.h>
166cd6a6acSopenharmony_ci#include <stdint.h>
176cd6a6acSopenharmony_ci#include <unistd.h>
186cd6a6acSopenharmony_ci#include <sys/mman.h>
196cd6a6acSopenharmony_ci#include <sys/types.h>
206cd6a6acSopenharmony_ci#include <sys/stat.h>
216cd6a6acSopenharmony_ci
226cd6a6acSopenharmony_ci#include "callbacks.h"
236cd6a6acSopenharmony_ci#include "label_internal.h"
246cd6a6acSopenharmony_ci#include "label_file.h"
256cd6a6acSopenharmony_ci
266cd6a6acSopenharmony_ci/*
276cd6a6acSopenharmony_ci * Internals, mostly moved over from matchpathcon.c
286cd6a6acSopenharmony_ci */
296cd6a6acSopenharmony_ci
306cd6a6acSopenharmony_ci/* return the length of the text that is the stem of a file name */
316cd6a6acSopenharmony_cistatic int get_stem_from_file_name(const char *const buf)
326cd6a6acSopenharmony_ci{
336cd6a6acSopenharmony_ci	const char *tmp = strchr(buf + 1, '/');
346cd6a6acSopenharmony_ci
356cd6a6acSopenharmony_ci	if (!tmp)
366cd6a6acSopenharmony_ci		return 0;
376cd6a6acSopenharmony_ci	return tmp - buf;
386cd6a6acSopenharmony_ci}
396cd6a6acSopenharmony_ci
406cd6a6acSopenharmony_ci/* find the stem of a file name, returns the index into stem_arr (or -1 if
416cd6a6acSopenharmony_ci * there is no match - IE for a file in the root directory or a regex that is
426cd6a6acSopenharmony_ci * too complex for us). */
436cd6a6acSopenharmony_cistatic int find_stem_from_file(struct saved_data *data, const char *key)
446cd6a6acSopenharmony_ci{
456cd6a6acSopenharmony_ci	int i;
466cd6a6acSopenharmony_ci	int stem_len = get_stem_from_file_name(key);
476cd6a6acSopenharmony_ci
486cd6a6acSopenharmony_ci	if (!stem_len)
496cd6a6acSopenharmony_ci		return -1;
506cd6a6acSopenharmony_ci	for (i = 0; i < data->num_stems; i++) {
516cd6a6acSopenharmony_ci		if (stem_len == data->stem_arr[i].len
526cd6a6acSopenharmony_ci		    && !strncmp(key, data->stem_arr[i].buf, stem_len)) {
536cd6a6acSopenharmony_ci			return i;
546cd6a6acSopenharmony_ci		}
556cd6a6acSopenharmony_ci	}
566cd6a6acSopenharmony_ci	return -1;
576cd6a6acSopenharmony_ci}
586cd6a6acSopenharmony_ci
596cd6a6acSopenharmony_ci/*
606cd6a6acSopenharmony_ci * Warn about duplicate specifications.
616cd6a6acSopenharmony_ci */
626cd6a6acSopenharmony_cistatic int nodups_specs(struct saved_data *data, const char *path)
636cd6a6acSopenharmony_ci{
646cd6a6acSopenharmony_ci	int rc = 0;
656cd6a6acSopenharmony_ci	unsigned int ii, jj;
666cd6a6acSopenharmony_ci	struct spec *curr_spec, *spec_arr = data->spec_arr;
676cd6a6acSopenharmony_ci
686cd6a6acSopenharmony_ci	for (ii = 0; ii < data->nspec; ii++) {
696cd6a6acSopenharmony_ci		curr_spec = &spec_arr[ii];
706cd6a6acSopenharmony_ci		for (jj = ii + 1; jj < data->nspec; jj++) {
716cd6a6acSopenharmony_ci			if ((!strcmp(spec_arr[jj].regex_str,
726cd6a6acSopenharmony_ci				curr_spec->regex_str))
736cd6a6acSopenharmony_ci			    && (!spec_arr[jj].mode || !curr_spec->mode
746cd6a6acSopenharmony_ci				|| spec_arr[jj].mode == curr_spec->mode)) {
756cd6a6acSopenharmony_ci				rc = -1;
766cd6a6acSopenharmony_ci				errno = EINVAL;
776cd6a6acSopenharmony_ci				if (strcmp(spec_arr[jj].lr.ctx_raw,
786cd6a6acSopenharmony_ci					    curr_spec->lr.ctx_raw)) {
796cd6a6acSopenharmony_ci					COMPAT_LOG
806cd6a6acSopenharmony_ci						(SELINUX_ERROR,
816cd6a6acSopenharmony_ci						 "%s: Multiple different specifications for %s  (%s and %s).\n",
826cd6a6acSopenharmony_ci						 path, curr_spec->regex_str,
836cd6a6acSopenharmony_ci						 spec_arr[jj].lr.ctx_raw,
846cd6a6acSopenharmony_ci						 curr_spec->lr.ctx_raw);
856cd6a6acSopenharmony_ci				} else {
866cd6a6acSopenharmony_ci					COMPAT_LOG
876cd6a6acSopenharmony_ci						(SELINUX_ERROR,
886cd6a6acSopenharmony_ci						 "%s: Multiple same specifications for %s.\n",
896cd6a6acSopenharmony_ci						 path, curr_spec->regex_str);
906cd6a6acSopenharmony_ci				}
916cd6a6acSopenharmony_ci			}
926cd6a6acSopenharmony_ci		}
936cd6a6acSopenharmony_ci	}
946cd6a6acSopenharmony_ci	return rc;
956cd6a6acSopenharmony_ci}
966cd6a6acSopenharmony_ci
976cd6a6acSopenharmony_cistatic int process_text_file(FILE *fp, const char *prefix,
986cd6a6acSopenharmony_ci			     struct selabel_handle *rec, const char *path)
996cd6a6acSopenharmony_ci{
1006cd6a6acSopenharmony_ci	int rc;
1016cd6a6acSopenharmony_ci	size_t line_len;
1026cd6a6acSopenharmony_ci	unsigned int lineno = 0;
1036cd6a6acSopenharmony_ci	char *line_buf = NULL;
1046cd6a6acSopenharmony_ci
1056cd6a6acSopenharmony_ci	while (getline(&line_buf, &line_len, fp) > 0) {
1066cd6a6acSopenharmony_ci		rc = process_line(rec, path, prefix, line_buf, ++lineno);
1076cd6a6acSopenharmony_ci		if (rc)
1086cd6a6acSopenharmony_ci			goto out;
1096cd6a6acSopenharmony_ci	}
1106cd6a6acSopenharmony_ci	rc = 0;
1116cd6a6acSopenharmony_ciout:
1126cd6a6acSopenharmony_ci	free(line_buf);
1136cd6a6acSopenharmony_ci	return rc;
1146cd6a6acSopenharmony_ci}
1156cd6a6acSopenharmony_ci
1166cd6a6acSopenharmony_cistatic int load_mmap(FILE *fp, size_t len, struct selabel_handle *rec,
1176cd6a6acSopenharmony_ci		     const char *path)
1186cd6a6acSopenharmony_ci{
1196cd6a6acSopenharmony_ci	struct saved_data *data = (struct saved_data *)rec->data;
1206cd6a6acSopenharmony_ci	int rc;
1216cd6a6acSopenharmony_ci	char *addr, *str_buf;
1226cd6a6acSopenharmony_ci	int *stem_map;
1236cd6a6acSopenharmony_ci	struct mmap_area *mmap_area;
1246cd6a6acSopenharmony_ci	uint32_t i, magic, version;
1256cd6a6acSopenharmony_ci	uint32_t entry_len, stem_map_len, regex_array_len;
1266cd6a6acSopenharmony_ci	const char *reg_version;
1276cd6a6acSopenharmony_ci	const char *reg_arch;
1286cd6a6acSopenharmony_ci	char reg_arch_matches = 0;
1296cd6a6acSopenharmony_ci
1306cd6a6acSopenharmony_ci	mmap_area = malloc(sizeof(*mmap_area));
1316cd6a6acSopenharmony_ci	if (!mmap_area) {
1326cd6a6acSopenharmony_ci		return -1;
1336cd6a6acSopenharmony_ci	}
1346cd6a6acSopenharmony_ci
1356cd6a6acSopenharmony_ci	addr = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fileno(fp), 0);
1366cd6a6acSopenharmony_ci	if (addr == MAP_FAILED) {
1376cd6a6acSopenharmony_ci		free(mmap_area);
1386cd6a6acSopenharmony_ci		perror("mmap");
1396cd6a6acSopenharmony_ci		return -1;
1406cd6a6acSopenharmony_ci	}
1416cd6a6acSopenharmony_ci
1426cd6a6acSopenharmony_ci	/* save where we mmap'd the file to cleanup on close() */
1436cd6a6acSopenharmony_ci	mmap_area->addr = mmap_area->next_addr = addr;
1446cd6a6acSopenharmony_ci	mmap_area->len = mmap_area->next_len = len;
1456cd6a6acSopenharmony_ci	mmap_area->next = data->mmap_areas;
1466cd6a6acSopenharmony_ci	data->mmap_areas = mmap_area;
1476cd6a6acSopenharmony_ci
1486cd6a6acSopenharmony_ci	/* check if this looks like an fcontext file */
1496cd6a6acSopenharmony_ci	rc = next_entry(&magic, mmap_area, sizeof(uint32_t));
1506cd6a6acSopenharmony_ci	if (rc < 0 || magic != SELINUX_MAGIC_COMPILED_FCONTEXT)
1516cd6a6acSopenharmony_ci		return -1;
1526cd6a6acSopenharmony_ci
1536cd6a6acSopenharmony_ci	/* check if this version is higher than we understand */
1546cd6a6acSopenharmony_ci	rc = next_entry(&version, mmap_area, sizeof(uint32_t));
1556cd6a6acSopenharmony_ci	if (rc < 0 || version > SELINUX_COMPILED_FCONTEXT_MAX_VERS)
1566cd6a6acSopenharmony_ci		return -1;
1576cd6a6acSopenharmony_ci
1586cd6a6acSopenharmony_ci	reg_version = regex_version();
1596cd6a6acSopenharmony_ci	if (!reg_version)
1606cd6a6acSopenharmony_ci		return -1;
1616cd6a6acSopenharmony_ci
1626cd6a6acSopenharmony_ci	reg_arch = regex_arch_string();
1636cd6a6acSopenharmony_ci	if (!reg_arch)
1646cd6a6acSopenharmony_ci		return -1;
1656cd6a6acSopenharmony_ci
1666cd6a6acSopenharmony_ci	if (version >= SELINUX_COMPILED_FCONTEXT_PCRE_VERS) {
1676cd6a6acSopenharmony_ci
1686cd6a6acSopenharmony_ci		len = strlen(reg_version);
1696cd6a6acSopenharmony_ci
1706cd6a6acSopenharmony_ci		rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t));
1716cd6a6acSopenharmony_ci		if (rc < 0)
1726cd6a6acSopenharmony_ci			return -1;
1736cd6a6acSopenharmony_ci
1746cd6a6acSopenharmony_ci		/* Check version lengths */
1756cd6a6acSopenharmony_ci		if (len != entry_len)
1766cd6a6acSopenharmony_ci			return -1;
1776cd6a6acSopenharmony_ci
1786cd6a6acSopenharmony_ci		/* Check if regex version mismatch */
1796cd6a6acSopenharmony_ci		str_buf = malloc(entry_len + 1);
1806cd6a6acSopenharmony_ci		if (!str_buf)
1816cd6a6acSopenharmony_ci			return -1;
1826cd6a6acSopenharmony_ci
1836cd6a6acSopenharmony_ci		rc = next_entry(str_buf, mmap_area, entry_len);
1846cd6a6acSopenharmony_ci		if (rc < 0) {
1856cd6a6acSopenharmony_ci			free(str_buf);
1866cd6a6acSopenharmony_ci			return -1;
1876cd6a6acSopenharmony_ci		}
1886cd6a6acSopenharmony_ci
1896cd6a6acSopenharmony_ci		str_buf[entry_len] = '\0';
1906cd6a6acSopenharmony_ci		if ((strcmp(str_buf, reg_version) != 0)) {
1916cd6a6acSopenharmony_ci			COMPAT_LOG(SELINUX_ERROR,
1926cd6a6acSopenharmony_ci				"Regex version mismatch, expected: %s actual: %s\n",
1936cd6a6acSopenharmony_ci				reg_version, str_buf);
1946cd6a6acSopenharmony_ci			free(str_buf);
1956cd6a6acSopenharmony_ci			return -1;
1966cd6a6acSopenharmony_ci		}
1976cd6a6acSopenharmony_ci		free(str_buf);
1986cd6a6acSopenharmony_ci
1996cd6a6acSopenharmony_ci		if (version >= SELINUX_COMPILED_FCONTEXT_REGEX_ARCH) {
2006cd6a6acSopenharmony_ci			len = strlen(reg_arch);
2016cd6a6acSopenharmony_ci
2026cd6a6acSopenharmony_ci			rc = next_entry(&entry_len, mmap_area,
2036cd6a6acSopenharmony_ci					sizeof(uint32_t));
2046cd6a6acSopenharmony_ci			if (rc < 0)
2056cd6a6acSopenharmony_ci				return -1;
2066cd6a6acSopenharmony_ci
2076cd6a6acSopenharmony_ci			/* Check arch string lengths */
2086cd6a6acSopenharmony_ci			if (len != entry_len) {
2096cd6a6acSopenharmony_ci				/*
2106cd6a6acSopenharmony_ci				 * Skip the entry and conclude that we have
2116cd6a6acSopenharmony_ci				 * a mismatch, which is not fatal.
2126cd6a6acSopenharmony_ci				 */
2136cd6a6acSopenharmony_ci				next_entry(NULL, mmap_area, entry_len);
2146cd6a6acSopenharmony_ci				goto end_arch_check;
2156cd6a6acSopenharmony_ci			}
2166cd6a6acSopenharmony_ci
2176cd6a6acSopenharmony_ci			/* Check if arch string mismatch */
2186cd6a6acSopenharmony_ci			str_buf = malloc(entry_len + 1);
2196cd6a6acSopenharmony_ci			if (!str_buf)
2206cd6a6acSopenharmony_ci				return -1;
2216cd6a6acSopenharmony_ci
2226cd6a6acSopenharmony_ci			rc = next_entry(str_buf, mmap_area, entry_len);
2236cd6a6acSopenharmony_ci			if (rc < 0) {
2246cd6a6acSopenharmony_ci				free(str_buf);
2256cd6a6acSopenharmony_ci				return -1;
2266cd6a6acSopenharmony_ci			}
2276cd6a6acSopenharmony_ci
2286cd6a6acSopenharmony_ci			str_buf[entry_len] = '\0';
2296cd6a6acSopenharmony_ci			reg_arch_matches = strcmp(str_buf, reg_arch) == 0;
2306cd6a6acSopenharmony_ci			free(str_buf);
2316cd6a6acSopenharmony_ci		}
2326cd6a6acSopenharmony_ci	}
2336cd6a6acSopenharmony_ciend_arch_check:
2346cd6a6acSopenharmony_ci
2356cd6a6acSopenharmony_ci	/* allocate the stems_data array */
2366cd6a6acSopenharmony_ci	rc = next_entry(&stem_map_len, mmap_area, sizeof(uint32_t));
2376cd6a6acSopenharmony_ci	if (rc < 0)
2386cd6a6acSopenharmony_ci		return -1;
2396cd6a6acSopenharmony_ci
2406cd6a6acSopenharmony_ci	/*
2416cd6a6acSopenharmony_ci	 * map indexed by the stem # in the mmap file and contains the stem
2426cd6a6acSopenharmony_ci	 * number in the data stem_arr
2436cd6a6acSopenharmony_ci	 */
2446cd6a6acSopenharmony_ci	stem_map = calloc(stem_map_len, sizeof(*stem_map));
2456cd6a6acSopenharmony_ci	if (!stem_map)
2466cd6a6acSopenharmony_ci		return -1;
2476cd6a6acSopenharmony_ci
2486cd6a6acSopenharmony_ci	for (i = 0; i < stem_map_len; i++) {
2496cd6a6acSopenharmony_ci		char *buf;
2506cd6a6acSopenharmony_ci		uint32_t stem_len;
2516cd6a6acSopenharmony_ci		int newid;
2526cd6a6acSopenharmony_ci
2536cd6a6acSopenharmony_ci		/* the length does not include the nul */
2546cd6a6acSopenharmony_ci		rc = next_entry(&stem_len, mmap_area, sizeof(uint32_t));
2556cd6a6acSopenharmony_ci		if (rc < 0 || !stem_len) {
2566cd6a6acSopenharmony_ci			rc = -1;
2576cd6a6acSopenharmony_ci			goto out;
2586cd6a6acSopenharmony_ci		}
2596cd6a6acSopenharmony_ci
2606cd6a6acSopenharmony_ci		/* Check for stem_len wrap around. */
2616cd6a6acSopenharmony_ci		if (stem_len < UINT32_MAX) {
2626cd6a6acSopenharmony_ci			buf = (char *)mmap_area->next_addr;
2636cd6a6acSopenharmony_ci			/* Check if over-run before null check. */
2646cd6a6acSopenharmony_ci			rc = next_entry(NULL, mmap_area, (stem_len + 1));
2656cd6a6acSopenharmony_ci			if (rc < 0)
2666cd6a6acSopenharmony_ci				goto out;
2676cd6a6acSopenharmony_ci
2686cd6a6acSopenharmony_ci			if (buf[stem_len] != '\0') {
2696cd6a6acSopenharmony_ci				rc = -1;
2706cd6a6acSopenharmony_ci				goto out;
2716cd6a6acSopenharmony_ci			}
2726cd6a6acSopenharmony_ci		} else {
2736cd6a6acSopenharmony_ci			rc = -1;
2746cd6a6acSopenharmony_ci			goto out;
2756cd6a6acSopenharmony_ci		}
2766cd6a6acSopenharmony_ci
2776cd6a6acSopenharmony_ci		/* store the mapping between old and new */
2786cd6a6acSopenharmony_ci		newid = find_stem(data, buf, stem_len);
2796cd6a6acSopenharmony_ci		if (newid < 0) {
2806cd6a6acSopenharmony_ci			newid = store_stem(data, buf, stem_len);
2816cd6a6acSopenharmony_ci			if (newid < 0) {
2826cd6a6acSopenharmony_ci				rc = newid;
2836cd6a6acSopenharmony_ci				goto out;
2846cd6a6acSopenharmony_ci			}
2856cd6a6acSopenharmony_ci			data->stem_arr[newid].from_mmap = 1;
2866cd6a6acSopenharmony_ci		}
2876cd6a6acSopenharmony_ci		stem_map[i] = newid;
2886cd6a6acSopenharmony_ci	}
2896cd6a6acSopenharmony_ci
2906cd6a6acSopenharmony_ci	/* allocate the regex array */
2916cd6a6acSopenharmony_ci	rc = next_entry(&regex_array_len, mmap_area, sizeof(uint32_t));
2926cd6a6acSopenharmony_ci	if (rc < 0 || !regex_array_len) {
2936cd6a6acSopenharmony_ci		rc = -1;
2946cd6a6acSopenharmony_ci		goto out;
2956cd6a6acSopenharmony_ci	}
2966cd6a6acSopenharmony_ci
2976cd6a6acSopenharmony_ci	for (i = 0; i < regex_array_len; i++) {
2986cd6a6acSopenharmony_ci		struct spec *spec;
2996cd6a6acSopenharmony_ci		int32_t stem_id, meta_chars;
3006cd6a6acSopenharmony_ci		uint32_t mode = 0, prefix_len = 0;
3016cd6a6acSopenharmony_ci
3026cd6a6acSopenharmony_ci		rc = grow_specs(data);
3036cd6a6acSopenharmony_ci		if (rc < 0)
3046cd6a6acSopenharmony_ci			goto out;
3056cd6a6acSopenharmony_ci
3066cd6a6acSopenharmony_ci		spec = &data->spec_arr[data->nspec];
3076cd6a6acSopenharmony_ci		spec->from_mmap = 1;
3086cd6a6acSopenharmony_ci
3096cd6a6acSopenharmony_ci		/* Process context */
3106cd6a6acSopenharmony_ci		rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t));
3116cd6a6acSopenharmony_ci		if (rc < 0 || !entry_len) {
3126cd6a6acSopenharmony_ci			rc = -1;
3136cd6a6acSopenharmony_ci			goto out;
3146cd6a6acSopenharmony_ci		}
3156cd6a6acSopenharmony_ci
3166cd6a6acSopenharmony_ci		str_buf = malloc(entry_len);
3176cd6a6acSopenharmony_ci		if (!str_buf) {
3186cd6a6acSopenharmony_ci			rc = -1;
3196cd6a6acSopenharmony_ci			goto out;
3206cd6a6acSopenharmony_ci		}
3216cd6a6acSopenharmony_ci		rc = next_entry(str_buf, mmap_area, entry_len);
3226cd6a6acSopenharmony_ci		if (rc < 0) {
3236cd6a6acSopenharmony_ci			free(str_buf);
3246cd6a6acSopenharmony_ci			goto out;
3256cd6a6acSopenharmony_ci		}
3266cd6a6acSopenharmony_ci
3276cd6a6acSopenharmony_ci		if (str_buf[entry_len - 1] != '\0') {
3286cd6a6acSopenharmony_ci			free(str_buf);
3296cd6a6acSopenharmony_ci			rc = -1;
3306cd6a6acSopenharmony_ci			goto out;
3316cd6a6acSopenharmony_ci		}
3326cd6a6acSopenharmony_ci		spec->lr.ctx_raw = str_buf;
3336cd6a6acSopenharmony_ci
3346cd6a6acSopenharmony_ci		if (strcmp(spec->lr.ctx_raw, "<<none>>") && rec->validating) {
3356cd6a6acSopenharmony_ci			if (selabel_validate(rec, &spec->lr) < 0) {
3366cd6a6acSopenharmony_ci				selinux_log(SELINUX_ERROR,
3376cd6a6acSopenharmony_ci					    "%s: context %s is invalid\n",
3386cd6a6acSopenharmony_ci					    path, spec->lr.ctx_raw);
3396cd6a6acSopenharmony_ci				goto out;
3406cd6a6acSopenharmony_ci			}
3416cd6a6acSopenharmony_ci		}
3426cd6a6acSopenharmony_ci
3436cd6a6acSopenharmony_ci		/* Process regex string */
3446cd6a6acSopenharmony_ci		rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t));
3456cd6a6acSopenharmony_ci		if (rc < 0 || !entry_len) {
3466cd6a6acSopenharmony_ci			rc = -1;
3476cd6a6acSopenharmony_ci			goto out;
3486cd6a6acSopenharmony_ci		}
3496cd6a6acSopenharmony_ci
3506cd6a6acSopenharmony_ci		spec->regex_str = (char *)mmap_area->next_addr;
3516cd6a6acSopenharmony_ci		rc = next_entry(NULL, mmap_area, entry_len);
3526cd6a6acSopenharmony_ci		if (rc < 0)
3536cd6a6acSopenharmony_ci			goto out;
3546cd6a6acSopenharmony_ci
3556cd6a6acSopenharmony_ci		if (spec->regex_str[entry_len - 1] != '\0') {
3566cd6a6acSopenharmony_ci			rc = -1;
3576cd6a6acSopenharmony_ci			goto out;
3586cd6a6acSopenharmony_ci		}
3596cd6a6acSopenharmony_ci
3606cd6a6acSopenharmony_ci		/* Process mode */
3616cd6a6acSopenharmony_ci		if (version >= SELINUX_COMPILED_FCONTEXT_MODE)
3626cd6a6acSopenharmony_ci			rc = next_entry(&mode, mmap_area, sizeof(uint32_t));
3636cd6a6acSopenharmony_ci		else
3646cd6a6acSopenharmony_ci			rc = next_entry(&mode, mmap_area, sizeof(mode_t));
3656cd6a6acSopenharmony_ci		if (rc < 0)
3666cd6a6acSopenharmony_ci			goto out;
3676cd6a6acSopenharmony_ci
3686cd6a6acSopenharmony_ci		spec->mode = mode;
3696cd6a6acSopenharmony_ci
3706cd6a6acSopenharmony_ci		/* map the stem id from the mmap file to the data->stem_arr */
3716cd6a6acSopenharmony_ci		rc = next_entry(&stem_id, mmap_area, sizeof(int32_t));
3726cd6a6acSopenharmony_ci		if (rc < 0)
3736cd6a6acSopenharmony_ci			goto out;
3746cd6a6acSopenharmony_ci
3756cd6a6acSopenharmony_ci		if (stem_id < 0 || stem_id >= (int32_t)stem_map_len)
3766cd6a6acSopenharmony_ci			spec->stem_id = -1;
3776cd6a6acSopenharmony_ci		else
3786cd6a6acSopenharmony_ci			spec->stem_id = stem_map[stem_id];
3796cd6a6acSopenharmony_ci
3806cd6a6acSopenharmony_ci		/* retrieve the hasMetaChars bit */
3816cd6a6acSopenharmony_ci		rc = next_entry(&meta_chars, mmap_area, sizeof(uint32_t));
3826cd6a6acSopenharmony_ci		if (rc < 0)
3836cd6a6acSopenharmony_ci			goto out;
3846cd6a6acSopenharmony_ci
3856cd6a6acSopenharmony_ci		spec->hasMetaChars = meta_chars;
3866cd6a6acSopenharmony_ci		/* and prefix length for use by selabel_lookup_best_match */
3876cd6a6acSopenharmony_ci		if (version >= SELINUX_COMPILED_FCONTEXT_PREFIX_LEN) {
3886cd6a6acSopenharmony_ci			rc = next_entry(&prefix_len, mmap_area,
3896cd6a6acSopenharmony_ci					    sizeof(uint32_t));
3906cd6a6acSopenharmony_ci			if (rc < 0)
3916cd6a6acSopenharmony_ci				goto out;
3926cd6a6acSopenharmony_ci
3936cd6a6acSopenharmony_ci			spec->prefix_len = prefix_len;
3946cd6a6acSopenharmony_ci		}
3956cd6a6acSopenharmony_ci
3966cd6a6acSopenharmony_ci		rc = regex_load_mmap(mmap_area, &spec->regex, reg_arch_matches,
3976cd6a6acSopenharmony_ci				     &spec->regex_compiled);
3986cd6a6acSopenharmony_ci		if (rc < 0)
3996cd6a6acSopenharmony_ci			goto out;
4006cd6a6acSopenharmony_ci
4016cd6a6acSopenharmony_ci		__pthread_mutex_init(&spec->regex_lock, NULL);
4026cd6a6acSopenharmony_ci		data->nspec++;
4036cd6a6acSopenharmony_ci	}
4046cd6a6acSopenharmony_ci
4056cd6a6acSopenharmony_ci	rc = 0;
4066cd6a6acSopenharmony_ciout:
4076cd6a6acSopenharmony_ci	free(stem_map);
4086cd6a6acSopenharmony_ci
4096cd6a6acSopenharmony_ci	return rc;
4106cd6a6acSopenharmony_ci}
4116cd6a6acSopenharmony_ci
4126cd6a6acSopenharmony_cistruct file_details {
4136cd6a6acSopenharmony_ci	const char *suffix;
4146cd6a6acSopenharmony_ci	struct stat sb;
4156cd6a6acSopenharmony_ci};
4166cd6a6acSopenharmony_ci
4176cd6a6acSopenharmony_cistatic char *rolling_append(char *current, const char *suffix, size_t max)
4186cd6a6acSopenharmony_ci{
4196cd6a6acSopenharmony_ci	size_t size;
4206cd6a6acSopenharmony_ci	size_t suffix_size;
4216cd6a6acSopenharmony_ci	size_t current_size;
4226cd6a6acSopenharmony_ci
4236cd6a6acSopenharmony_ci	if (!suffix)
4246cd6a6acSopenharmony_ci		return current;
4256cd6a6acSopenharmony_ci
4266cd6a6acSopenharmony_ci	current_size = strlen(current);
4276cd6a6acSopenharmony_ci	suffix_size = strlen(suffix);
4286cd6a6acSopenharmony_ci
4296cd6a6acSopenharmony_ci	size = current_size + suffix_size;
4306cd6a6acSopenharmony_ci	if (size < current_size || size < suffix_size)
4316cd6a6acSopenharmony_ci		return NULL;
4326cd6a6acSopenharmony_ci
4336cd6a6acSopenharmony_ci	/* ensure space for the '.' and the '\0' characters. */
4346cd6a6acSopenharmony_ci	if (size >= (SIZE_MAX - 2))
4356cd6a6acSopenharmony_ci		return NULL;
4366cd6a6acSopenharmony_ci
4376cd6a6acSopenharmony_ci	size += 2;
4386cd6a6acSopenharmony_ci
4396cd6a6acSopenharmony_ci	if (size > max)
4406cd6a6acSopenharmony_ci		return NULL;
4416cd6a6acSopenharmony_ci
4426cd6a6acSopenharmony_ci	/* Append any given suffix */
4436cd6a6acSopenharmony_ci	char *to = current + current_size;
4446cd6a6acSopenharmony_ci	*to++ = '.';
4456cd6a6acSopenharmony_ci	strcpy(to, suffix);
4466cd6a6acSopenharmony_ci
4476cd6a6acSopenharmony_ci	return current;
4486cd6a6acSopenharmony_ci}
4496cd6a6acSopenharmony_ci
4506cd6a6acSopenharmony_cistatic bool fcontext_is_binary(FILE *fp)
4516cd6a6acSopenharmony_ci{
4526cd6a6acSopenharmony_ci	uint32_t magic;
4536cd6a6acSopenharmony_ci
4546cd6a6acSopenharmony_ci	size_t len = fread(&magic, sizeof(magic), 1, fp);
4556cd6a6acSopenharmony_ci	rewind(fp);
4566cd6a6acSopenharmony_ci
4576cd6a6acSopenharmony_ci	return (len && (magic == SELINUX_MAGIC_COMPILED_FCONTEXT));
4586cd6a6acSopenharmony_ci}
4596cd6a6acSopenharmony_ci
4606cd6a6acSopenharmony_ci#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
4616cd6a6acSopenharmony_ci
4626cd6a6acSopenharmony_cistatic FILE *open_file(const char *path, const char *suffix,
4636cd6a6acSopenharmony_ci	       char *save_path, size_t len, struct stat *sb, bool open_oldest)
4646cd6a6acSopenharmony_ci{
4656cd6a6acSopenharmony_ci	unsigned int i;
4666cd6a6acSopenharmony_ci	int rc;
4676cd6a6acSopenharmony_ci	char stack_path[len];
4686cd6a6acSopenharmony_ci	struct file_details *found = NULL;
4696cd6a6acSopenharmony_ci
4706cd6a6acSopenharmony_ci	/*
4716cd6a6acSopenharmony_ci	 * Rolling append of suffix. Try to open with path.suffix then the
4726cd6a6acSopenharmony_ci	 * next as path.suffix.suffix and so forth.
4736cd6a6acSopenharmony_ci	 */
4746cd6a6acSopenharmony_ci	struct file_details fdetails[2] = {
4756cd6a6acSopenharmony_ci			{ .suffix = suffix },
4766cd6a6acSopenharmony_ci			{ .suffix = "bin" }
4776cd6a6acSopenharmony_ci	};
4786cd6a6acSopenharmony_ci
4796cd6a6acSopenharmony_ci	rc = snprintf(stack_path, sizeof(stack_path), "%s", path);
4806cd6a6acSopenharmony_ci	if (rc >= (int) sizeof(stack_path)) {
4816cd6a6acSopenharmony_ci		errno = ENAMETOOLONG;
4826cd6a6acSopenharmony_ci		return NULL;
4836cd6a6acSopenharmony_ci	}
4846cd6a6acSopenharmony_ci
4856cd6a6acSopenharmony_ci	for (i = 0; i < ARRAY_SIZE(fdetails); i++) {
4866cd6a6acSopenharmony_ci
4876cd6a6acSopenharmony_ci		/* This handles the case if suffix is null */
4886cd6a6acSopenharmony_ci		path = rolling_append(stack_path, fdetails[i].suffix,
4896cd6a6acSopenharmony_ci				      sizeof(stack_path));
4906cd6a6acSopenharmony_ci		if (!path)
4916cd6a6acSopenharmony_ci			return NULL;
4926cd6a6acSopenharmony_ci
4936cd6a6acSopenharmony_ci		rc = stat(path, &fdetails[i].sb);
4946cd6a6acSopenharmony_ci		if (rc)
4956cd6a6acSopenharmony_ci			continue;
4966cd6a6acSopenharmony_ci
4976cd6a6acSopenharmony_ci		/* first file thing found, just take it */
4986cd6a6acSopenharmony_ci		if (!found) {
4996cd6a6acSopenharmony_ci			strcpy(save_path, path);
5006cd6a6acSopenharmony_ci			found = &fdetails[i];
5016cd6a6acSopenharmony_ci			continue;
5026cd6a6acSopenharmony_ci		}
5036cd6a6acSopenharmony_ci
5046cd6a6acSopenharmony_ci		/*
5056cd6a6acSopenharmony_ci		 * Keep picking the newest file found. Where "newest"
5066cd6a6acSopenharmony_ci		 * includes equality. This provides a precedence on
5076cd6a6acSopenharmony_ci		 * secondary suffixes even when the timestamp is the
5086cd6a6acSopenharmony_ci		 * same. Ie choose file_contexts.bin over file_contexts
5096cd6a6acSopenharmony_ci		 * even if the time stamp is the same. Invert this logic
5106cd6a6acSopenharmony_ci		 * on open_oldest set to true. The idea is that if the
5116cd6a6acSopenharmony_ci		 * newest file failed to process, we can attempt to
5126cd6a6acSopenharmony_ci		 * process the oldest. The logic here is subtle and depends
5136cd6a6acSopenharmony_ci		 * on the array ordering in fdetails for the case when time
5146cd6a6acSopenharmony_ci		 * stamps are the same.
5156cd6a6acSopenharmony_ci		 */
5166cd6a6acSopenharmony_ci		if (open_oldest ^
5176cd6a6acSopenharmony_ci			(fdetails[i].sb.st_mtime >= found->sb.st_mtime)) {
5186cd6a6acSopenharmony_ci			found = &fdetails[i];
5196cd6a6acSopenharmony_ci			strcpy(save_path, path);
5206cd6a6acSopenharmony_ci		}
5216cd6a6acSopenharmony_ci	}
5226cd6a6acSopenharmony_ci
5236cd6a6acSopenharmony_ci	if (!found) {
5246cd6a6acSopenharmony_ci		errno = ENOENT;
5256cd6a6acSopenharmony_ci		return NULL;
5266cd6a6acSopenharmony_ci	}
5276cd6a6acSopenharmony_ci
5286cd6a6acSopenharmony_ci	memcpy(sb, &found->sb, sizeof(*sb));
5296cd6a6acSopenharmony_ci	return fopen(save_path, "re");
5306cd6a6acSopenharmony_ci}
5316cd6a6acSopenharmony_ci
5326cd6a6acSopenharmony_cistatic int process_file(const char *path, const char *suffix,
5336cd6a6acSopenharmony_ci			  struct selabel_handle *rec,
5346cd6a6acSopenharmony_ci			  const char *prefix, struct selabel_digest *digest)
5356cd6a6acSopenharmony_ci{
5366cd6a6acSopenharmony_ci	int rc;
5376cd6a6acSopenharmony_ci	unsigned int i;
5386cd6a6acSopenharmony_ci	struct stat sb;
5396cd6a6acSopenharmony_ci	FILE *fp = NULL;
5406cd6a6acSopenharmony_ci	char found_path[PATH_MAX];
5416cd6a6acSopenharmony_ci
5426cd6a6acSopenharmony_ci	/*
5436cd6a6acSopenharmony_ci	 * On the first pass open the newest modified file. If it fails to
5446cd6a6acSopenharmony_ci	 * process, then the second pass shall open the oldest file. If both
5456cd6a6acSopenharmony_ci	 * passes fail, then it's a fatal error.
5466cd6a6acSopenharmony_ci	 */
5476cd6a6acSopenharmony_ci	for (i = 0; i < 2; i++) {
5486cd6a6acSopenharmony_ci		fp = open_file(path, suffix, found_path, sizeof(found_path),
5496cd6a6acSopenharmony_ci			&sb, i > 0);
5506cd6a6acSopenharmony_ci		if (fp == NULL)
5516cd6a6acSopenharmony_ci			return -1;
5526cd6a6acSopenharmony_ci
5536cd6a6acSopenharmony_ci		rc = fcontext_is_binary(fp) ?
5546cd6a6acSopenharmony_ci				load_mmap(fp, sb.st_size, rec, found_path) :
5556cd6a6acSopenharmony_ci				process_text_file(fp, prefix, rec, found_path);
5566cd6a6acSopenharmony_ci		if (!rc)
5576cd6a6acSopenharmony_ci			rc = digest_add_specfile(digest, fp, NULL, sb.st_size,
5586cd6a6acSopenharmony_ci				found_path);
5596cd6a6acSopenharmony_ci
5606cd6a6acSopenharmony_ci		fclose(fp);
5616cd6a6acSopenharmony_ci
5626cd6a6acSopenharmony_ci		if (!rc)
5636cd6a6acSopenharmony_ci			return 0;
5646cd6a6acSopenharmony_ci	}
5656cd6a6acSopenharmony_ci	return -1;
5666cd6a6acSopenharmony_ci}
5676cd6a6acSopenharmony_ci
5686cd6a6acSopenharmony_cistatic void selabel_subs_fini(struct selabel_sub *ptr)
5696cd6a6acSopenharmony_ci{
5706cd6a6acSopenharmony_ci	struct selabel_sub *next;
5716cd6a6acSopenharmony_ci
5726cd6a6acSopenharmony_ci	while (ptr) {
5736cd6a6acSopenharmony_ci		next = ptr->next;
5746cd6a6acSopenharmony_ci		free(ptr->src);
5756cd6a6acSopenharmony_ci		free(ptr->dst);
5766cd6a6acSopenharmony_ci		free(ptr);
5776cd6a6acSopenharmony_ci		ptr = next;
5786cd6a6acSopenharmony_ci	}
5796cd6a6acSopenharmony_ci}
5806cd6a6acSopenharmony_ci
5816cd6a6acSopenharmony_cistatic char *selabel_sub(struct selabel_sub *ptr, const char *src)
5826cd6a6acSopenharmony_ci{
5836cd6a6acSopenharmony_ci	char *dst = NULL;
5846cd6a6acSopenharmony_ci	int len;
5856cd6a6acSopenharmony_ci
5866cd6a6acSopenharmony_ci	while (ptr) {
5876cd6a6acSopenharmony_ci		if (strncmp(src, ptr->src, ptr->slen) == 0 ) {
5886cd6a6acSopenharmony_ci			if (src[ptr->slen] == '/' ||
5896cd6a6acSopenharmony_ci			    src[ptr->slen] == 0) {
5906cd6a6acSopenharmony_ci				if ((src[ptr->slen] == '/') &&
5916cd6a6acSopenharmony_ci				    (strcmp(ptr->dst, "/") == 0))
5926cd6a6acSopenharmony_ci					len = ptr->slen + 1;
5936cd6a6acSopenharmony_ci				else
5946cd6a6acSopenharmony_ci					len = ptr->slen;
5956cd6a6acSopenharmony_ci				if (asprintf(&dst, "%s%s", ptr->dst, &src[len]) < 0)
5966cd6a6acSopenharmony_ci					return NULL;
5976cd6a6acSopenharmony_ci				return dst;
5986cd6a6acSopenharmony_ci			}
5996cd6a6acSopenharmony_ci		}
6006cd6a6acSopenharmony_ci		ptr = ptr->next;
6016cd6a6acSopenharmony_ci	}
6026cd6a6acSopenharmony_ci	return NULL;
6036cd6a6acSopenharmony_ci}
6046cd6a6acSopenharmony_ci
6056cd6a6acSopenharmony_cistatic int selabel_subs_init(const char *path, struct selabel_digest *digest,
6066cd6a6acSopenharmony_ci		       struct selabel_sub **out_subs)
6076cd6a6acSopenharmony_ci{
6086cd6a6acSopenharmony_ci	char buf[1024];
6096cd6a6acSopenharmony_ci	FILE *cfg = fopen(path, "re");
6106cd6a6acSopenharmony_ci	struct selabel_sub *list = NULL, *sub = NULL;
6116cd6a6acSopenharmony_ci	struct stat sb;
6126cd6a6acSopenharmony_ci	int status = -1;
6136cd6a6acSopenharmony_ci
6146cd6a6acSopenharmony_ci	*out_subs = NULL;
6156cd6a6acSopenharmony_ci	if (!cfg) {
6166cd6a6acSopenharmony_ci		/* If the file does not exist, it is not fatal */
6176cd6a6acSopenharmony_ci		return (errno == ENOENT) ? 0 : -1;
6186cd6a6acSopenharmony_ci	}
6196cd6a6acSopenharmony_ci
6206cd6a6acSopenharmony_ci	if (fstat(fileno(cfg), &sb) < 0)
6216cd6a6acSopenharmony_ci		goto out;
6226cd6a6acSopenharmony_ci
6236cd6a6acSopenharmony_ci	while (fgets_unlocked(buf, sizeof(buf) - 1, cfg)) {
6246cd6a6acSopenharmony_ci		char *ptr = NULL;
6256cd6a6acSopenharmony_ci		char *src = buf;
6266cd6a6acSopenharmony_ci		char *dst = NULL;
6276cd6a6acSopenharmony_ci
6286cd6a6acSopenharmony_ci		while (*src && isspace(*src))
6296cd6a6acSopenharmony_ci			src++;
6306cd6a6acSopenharmony_ci		if (src[0] == '#') continue;
6316cd6a6acSopenharmony_ci		ptr = src;
6326cd6a6acSopenharmony_ci		while (*ptr && ! isspace(*ptr))
6336cd6a6acSopenharmony_ci			ptr++;
6346cd6a6acSopenharmony_ci		*ptr++ = '\0';
6356cd6a6acSopenharmony_ci		if (! *src) continue;
6366cd6a6acSopenharmony_ci
6376cd6a6acSopenharmony_ci		dst = ptr;
6386cd6a6acSopenharmony_ci		while (*dst && isspace(*dst))
6396cd6a6acSopenharmony_ci			dst++;
6406cd6a6acSopenharmony_ci		ptr = dst;
6416cd6a6acSopenharmony_ci		while (*ptr && ! isspace(*ptr))
6426cd6a6acSopenharmony_ci			ptr++;
6436cd6a6acSopenharmony_ci		*ptr = '\0';
6446cd6a6acSopenharmony_ci		if (! *dst)
6456cd6a6acSopenharmony_ci			continue;
6466cd6a6acSopenharmony_ci
6476cd6a6acSopenharmony_ci		sub = malloc(sizeof(*sub));
6486cd6a6acSopenharmony_ci		if (! sub)
6496cd6a6acSopenharmony_ci			goto err;
6506cd6a6acSopenharmony_ci		memset(sub, 0, sizeof(*sub));
6516cd6a6acSopenharmony_ci
6526cd6a6acSopenharmony_ci		sub->src = strdup(src);
6536cd6a6acSopenharmony_ci		if (! sub->src)
6546cd6a6acSopenharmony_ci			goto err;
6556cd6a6acSopenharmony_ci
6566cd6a6acSopenharmony_ci		sub->dst = strdup(dst);
6576cd6a6acSopenharmony_ci		if (! sub->dst)
6586cd6a6acSopenharmony_ci			goto err;
6596cd6a6acSopenharmony_ci
6606cd6a6acSopenharmony_ci		sub->slen = strlen(src);
6616cd6a6acSopenharmony_ci		sub->next = list;
6626cd6a6acSopenharmony_ci		list = sub;
6636cd6a6acSopenharmony_ci		sub = NULL;
6646cd6a6acSopenharmony_ci	}
6656cd6a6acSopenharmony_ci
6666cd6a6acSopenharmony_ci	if (digest_add_specfile(digest, cfg, NULL, sb.st_size, path) < 0)
6676cd6a6acSopenharmony_ci		goto err;
6686cd6a6acSopenharmony_ci
6696cd6a6acSopenharmony_ci	*out_subs = list;
6706cd6a6acSopenharmony_ci	status = 0;
6716cd6a6acSopenharmony_ci
6726cd6a6acSopenharmony_ciout:
6736cd6a6acSopenharmony_ci	fclose(cfg);
6746cd6a6acSopenharmony_ci	return status;
6756cd6a6acSopenharmony_cierr:
6766cd6a6acSopenharmony_ci	if (sub)
6776cd6a6acSopenharmony_ci		free(sub->src);
6786cd6a6acSopenharmony_ci	free(sub);
6796cd6a6acSopenharmony_ci	while (list) {
6806cd6a6acSopenharmony_ci		sub = list->next;
6816cd6a6acSopenharmony_ci		free(list->src);
6826cd6a6acSopenharmony_ci		free(list->dst);
6836cd6a6acSopenharmony_ci		free(list);
6846cd6a6acSopenharmony_ci		list = sub;
6856cd6a6acSopenharmony_ci	}
6866cd6a6acSopenharmony_ci	goto out;
6876cd6a6acSopenharmony_ci}
6886cd6a6acSopenharmony_ci
6896cd6a6acSopenharmony_cistatic char *selabel_sub_key(struct saved_data *data, const char *key)
6906cd6a6acSopenharmony_ci{
6916cd6a6acSopenharmony_ci	char *ptr = NULL;
6926cd6a6acSopenharmony_ci	char *dptr = NULL;
6936cd6a6acSopenharmony_ci
6946cd6a6acSopenharmony_ci	ptr = selabel_sub(data->subs, key);
6956cd6a6acSopenharmony_ci	if (ptr) {
6966cd6a6acSopenharmony_ci		dptr = selabel_sub(data->dist_subs, ptr);
6976cd6a6acSopenharmony_ci		if (dptr) {
6986cd6a6acSopenharmony_ci			free(ptr);
6996cd6a6acSopenharmony_ci			ptr = dptr;
7006cd6a6acSopenharmony_ci		}
7016cd6a6acSopenharmony_ci	} else {
7026cd6a6acSopenharmony_ci		ptr = selabel_sub(data->dist_subs, key);
7036cd6a6acSopenharmony_ci	}
7046cd6a6acSopenharmony_ci	if (ptr)
7056cd6a6acSopenharmony_ci		return ptr;
7066cd6a6acSopenharmony_ci
7076cd6a6acSopenharmony_ci	return NULL;
7086cd6a6acSopenharmony_ci}
7096cd6a6acSopenharmony_ci
7106cd6a6acSopenharmony_cistatic void closef(struct selabel_handle *rec);
7116cd6a6acSopenharmony_ci
7126cd6a6acSopenharmony_ci#ifdef OHOS_FC_INIT
7136cd6a6acSopenharmony_cistatic int init(struct selabel_handle *rec, const struct selinux_opt *opts, unsigned n)
7146cd6a6acSopenharmony_ci{
7156cd6a6acSopenharmony_ci	struct saved_data *data = (struct saved_data *)rec->data;
7166cd6a6acSopenharmony_ci	const char *prefix = NULL;
7176cd6a6acSopenharmony_ci	int status = -1;
7186cd6a6acSopenharmony_ci	size_t path_nums = 0;
7196cd6a6acSopenharmony_ci	size_t opt_nums = n;
7206cd6a6acSopenharmony_ci
7216cd6a6acSopenharmony_ci	while (n--) {
7226cd6a6acSopenharmony_ci		switch (opts[n].type) {
7236cd6a6acSopenharmony_ci			case SELABEL_OPT_PATH:
7246cd6a6acSopenharmony_ci				path_nums++;
7256cd6a6acSopenharmony_ci				break;
7266cd6a6acSopenharmony_ci			default:
7276cd6a6acSopenharmony_ci				break;
7286cd6a6acSopenharmony_ci		}
7296cd6a6acSopenharmony_ci	}
7306cd6a6acSopenharmony_ci
7316cd6a6acSopenharmony_ci	if (path_nums == 0) {
7326cd6a6acSopenharmony_ci		selinux_log(SELINUX_ERROR, "No specific file_contexts provided\n");
7336cd6a6acSopenharmony_ci		goto finish;
7346cd6a6acSopenharmony_ci	}
7356cd6a6acSopenharmony_ci
7366cd6a6acSopenharmony_ci	rec->spec_file = (char **)calloc(path_nums, sizeof(char *));
7376cd6a6acSopenharmony_ci	if (rec->spec_file == NULL) {
7386cd6a6acSopenharmony_ci		goto finish;
7396cd6a6acSopenharmony_ci	}
7406cd6a6acSopenharmony_ci	rec->spec_file_nums = path_nums;
7416cd6a6acSopenharmony_ci	size_t i = 0;
7426cd6a6acSopenharmony_ci	n = opt_nums;
7436cd6a6acSopenharmony_ci	while (n--) {
7446cd6a6acSopenharmony_ci		if (opts[n].type == SELABEL_OPT_PATH) {
7456cd6a6acSopenharmony_ci			rec->spec_file[i] = strdup(opts[n].value);
7466cd6a6acSopenharmony_ci			if (rec->spec_file[i] == NULL) {
7476cd6a6acSopenharmony_ci				goto finish;
7486cd6a6acSopenharmony_ci			}
7496cd6a6acSopenharmony_ci			i++;
7506cd6a6acSopenharmony_ci		}
7516cd6a6acSopenharmony_ci	}
7526cd6a6acSopenharmony_ci
7536cd6a6acSopenharmony_ci	for (int path_index = 0; path_index < rec->spec_file_nums; path_index++) {
7546cd6a6acSopenharmony_ci		status = process_file(rec->spec_file[path_index], NULL, rec, prefix, rec->digest);
7556cd6a6acSopenharmony_ci		if (status) {
7566cd6a6acSopenharmony_ci			goto finish;
7576cd6a6acSopenharmony_ci		}
7586cd6a6acSopenharmony_ci
7596cd6a6acSopenharmony_ci		if (rec->validating) {
7606cd6a6acSopenharmony_ci			status = nodups_specs(data, rec->spec_file[path_index]);
7616cd6a6acSopenharmony_ci			if (status) {
7626cd6a6acSopenharmony_ci				goto finish;
7636cd6a6acSopenharmony_ci			}
7646cd6a6acSopenharmony_ci		}
7656cd6a6acSopenharmony_ci	}
7666cd6a6acSopenharmony_ci
7676cd6a6acSopenharmony_ci	digest_gen_hash(rec->digest);
7686cd6a6acSopenharmony_ci
7696cd6a6acSopenharmony_ci	status = sort_specs(data);
7706cd6a6acSopenharmony_ci
7716cd6a6acSopenharmony_cifinish:
7726cd6a6acSopenharmony_ci	if (status)
7736cd6a6acSopenharmony_ci		closef(rec);
7746cd6a6acSopenharmony_ci
7756cd6a6acSopenharmony_ci	return status;
7766cd6a6acSopenharmony_ci}
7776cd6a6acSopenharmony_ci#else
7786cd6a6acSopenharmony_cistatic int init(struct selabel_handle *rec, const struct selinux_opt *opts,
7796cd6a6acSopenharmony_ci		unsigned n)
7806cd6a6acSopenharmony_ci{
7816cd6a6acSopenharmony_ci	struct saved_data *data = (struct saved_data *)rec->data;
7826cd6a6acSopenharmony_ci	const char *path = NULL;
7836cd6a6acSopenharmony_ci	const char *prefix = NULL;
7846cd6a6acSopenharmony_ci	int status = -1, baseonly = 0;
7856cd6a6acSopenharmony_ci
7866cd6a6acSopenharmony_ci	/* Process arguments */
7876cd6a6acSopenharmony_ci	while (n--)
7886cd6a6acSopenharmony_ci		switch(opts[n].type) {
7896cd6a6acSopenharmony_ci		case SELABEL_OPT_PATH:
7906cd6a6acSopenharmony_ci			path = opts[n].value;
7916cd6a6acSopenharmony_ci			break;
7926cd6a6acSopenharmony_ci		case SELABEL_OPT_SUBSET:
7936cd6a6acSopenharmony_ci			prefix = opts[n].value;
7946cd6a6acSopenharmony_ci			break;
7956cd6a6acSopenharmony_ci		case SELABEL_OPT_BASEONLY:
7966cd6a6acSopenharmony_ci			baseonly = !!opts[n].value;
7976cd6a6acSopenharmony_ci			break;
7986cd6a6acSopenharmony_ci		}
7996cd6a6acSopenharmony_ci
8006cd6a6acSopenharmony_ci#if !defined(BUILD_HOST) && !defined(ANDROID)
8016cd6a6acSopenharmony_ci	char subs_file[PATH_MAX + 1];
8026cd6a6acSopenharmony_ci	/* Process local and distribution substitution files */
8036cd6a6acSopenharmony_ci	if (!path) {
8046cd6a6acSopenharmony_ci		status = selabel_subs_init(
8056cd6a6acSopenharmony_ci			selinux_file_context_subs_dist_path(),
8066cd6a6acSopenharmony_ci			rec->digest, &data->dist_subs);
8076cd6a6acSopenharmony_ci		if (status)
8086cd6a6acSopenharmony_ci			goto finish;
8096cd6a6acSopenharmony_ci		status = selabel_subs_init(selinux_file_context_subs_path(),
8106cd6a6acSopenharmony_ci			rec->digest, &data->subs);
8116cd6a6acSopenharmony_ci		if (status)
8126cd6a6acSopenharmony_ci			goto finish;
8136cd6a6acSopenharmony_ci		path = selinux_file_context_path();
8146cd6a6acSopenharmony_ci	} else {
8156cd6a6acSopenharmony_ci		snprintf(subs_file, sizeof(subs_file), "%s.subs_dist", path);
8166cd6a6acSopenharmony_ci		status = selabel_subs_init(subs_file, rec->digest,
8176cd6a6acSopenharmony_ci					   &data->dist_subs);
8186cd6a6acSopenharmony_ci		if (status)
8196cd6a6acSopenharmony_ci			goto finish;
8206cd6a6acSopenharmony_ci		snprintf(subs_file, sizeof(subs_file), "%s.subs", path);
8216cd6a6acSopenharmony_ci		status = selabel_subs_init(subs_file, rec->digest,
8226cd6a6acSopenharmony_ci					   &data->subs);
8236cd6a6acSopenharmony_ci		if (status)
8246cd6a6acSopenharmony_ci			goto finish;
8256cd6a6acSopenharmony_ci	}
8266cd6a6acSopenharmony_ci
8276cd6a6acSopenharmony_ci#endif
8286cd6a6acSopenharmony_ci
8296cd6a6acSopenharmony_ci	if (!path)
8306cd6a6acSopenharmony_ci		goto finish;
8316cd6a6acSopenharmony_ci
8326cd6a6acSopenharmony_ci	rec->spec_file = strdup(path);
8336cd6a6acSopenharmony_ci
8346cd6a6acSopenharmony_ci	/*
8356cd6a6acSopenharmony_ci	 * The do detailed validation of the input and fill the spec array
8366cd6a6acSopenharmony_ci	 */
8376cd6a6acSopenharmony_ci	status = process_file(path, NULL, rec, prefix, rec->digest);
8386cd6a6acSopenharmony_ci	if (status)
8396cd6a6acSopenharmony_ci		goto finish;
8406cd6a6acSopenharmony_ci
8416cd6a6acSopenharmony_ci	if (rec->validating) {
8426cd6a6acSopenharmony_ci		status = nodups_specs(data, path);
8436cd6a6acSopenharmony_ci		if (status)
8446cd6a6acSopenharmony_ci			goto finish;
8456cd6a6acSopenharmony_ci	}
8466cd6a6acSopenharmony_ci
8476cd6a6acSopenharmony_ci	if (!baseonly) {
8486cd6a6acSopenharmony_ci		status = process_file(path, "homedirs", rec, prefix,
8496cd6a6acSopenharmony_ci							    rec->digest);
8506cd6a6acSopenharmony_ci		if (status && errno != ENOENT)
8516cd6a6acSopenharmony_ci			goto finish;
8526cd6a6acSopenharmony_ci
8536cd6a6acSopenharmony_ci		status = process_file(path, "local", rec, prefix,
8546cd6a6acSopenharmony_ci							    rec->digest);
8556cd6a6acSopenharmony_ci		if (status && errno != ENOENT)
8566cd6a6acSopenharmony_ci			goto finish;
8576cd6a6acSopenharmony_ci	}
8586cd6a6acSopenharmony_ci
8596cd6a6acSopenharmony_ci	digest_gen_hash(rec->digest);
8606cd6a6acSopenharmony_ci
8616cd6a6acSopenharmony_ci	status = sort_specs(data);
8626cd6a6acSopenharmony_ci
8636cd6a6acSopenharmony_cifinish:
8646cd6a6acSopenharmony_ci	if (status)
8656cd6a6acSopenharmony_ci		closef(rec);
8666cd6a6acSopenharmony_ci
8676cd6a6acSopenharmony_ci	return status;
8686cd6a6acSopenharmony_ci}
8696cd6a6acSopenharmony_ci#endif
8706cd6a6acSopenharmony_ci
8716cd6a6acSopenharmony_ci/*
8726cd6a6acSopenharmony_ci * Backend interface routines
8736cd6a6acSopenharmony_ci */
8746cd6a6acSopenharmony_cistatic void closef(struct selabel_handle *rec)
8756cd6a6acSopenharmony_ci{
8766cd6a6acSopenharmony_ci	struct saved_data *data = (struct saved_data *)rec->data;
8776cd6a6acSopenharmony_ci	struct mmap_area *area, *last_area;
8786cd6a6acSopenharmony_ci	struct spec *spec;
8796cd6a6acSopenharmony_ci	struct stem *stem;
8806cd6a6acSopenharmony_ci	unsigned int i;
8816cd6a6acSopenharmony_ci
8826cd6a6acSopenharmony_ci	selabel_subs_fini(data->subs);
8836cd6a6acSopenharmony_ci	selabel_subs_fini(data->dist_subs);
8846cd6a6acSopenharmony_ci
8856cd6a6acSopenharmony_ci	for (i = 0; i < data->nspec; i++) {
8866cd6a6acSopenharmony_ci		spec = &data->spec_arr[i];
8876cd6a6acSopenharmony_ci		free(spec->lr.ctx_trans);
8886cd6a6acSopenharmony_ci		free(spec->lr.ctx_raw);
8896cd6a6acSopenharmony_ci		regex_data_free(spec->regex);
8906cd6a6acSopenharmony_ci		__pthread_mutex_destroy(&spec->regex_lock);
8916cd6a6acSopenharmony_ci		if (spec->from_mmap)
8926cd6a6acSopenharmony_ci			continue;
8936cd6a6acSopenharmony_ci		free(spec->regex_str);
8946cd6a6acSopenharmony_ci		free(spec->type_str);
8956cd6a6acSopenharmony_ci	}
8966cd6a6acSopenharmony_ci
8976cd6a6acSopenharmony_ci	for (i = 0; i < (unsigned int)data->num_stems; i++) {
8986cd6a6acSopenharmony_ci		stem = &data->stem_arr[i];
8996cd6a6acSopenharmony_ci		if (stem->from_mmap)
9006cd6a6acSopenharmony_ci			continue;
9016cd6a6acSopenharmony_ci		free(stem->buf);
9026cd6a6acSopenharmony_ci	}
9036cd6a6acSopenharmony_ci
9046cd6a6acSopenharmony_ci	if (data->spec_arr)
9056cd6a6acSopenharmony_ci		free(data->spec_arr);
9066cd6a6acSopenharmony_ci	if (data->stem_arr)
9076cd6a6acSopenharmony_ci		free(data->stem_arr);
9086cd6a6acSopenharmony_ci
9096cd6a6acSopenharmony_ci	area = data->mmap_areas;
9106cd6a6acSopenharmony_ci	while (area) {
9116cd6a6acSopenharmony_ci		munmap(area->addr, area->len);
9126cd6a6acSopenharmony_ci		last_area = area;
9136cd6a6acSopenharmony_ci		area = area->next;
9146cd6a6acSopenharmony_ci		free(last_area);
9156cd6a6acSopenharmony_ci	}
9166cd6a6acSopenharmony_ci	free(data);
9176cd6a6acSopenharmony_ci}
9186cd6a6acSopenharmony_ci
9196cd6a6acSopenharmony_ci// Finds all the matches of |key| in the given context. Returns the result in
9206cd6a6acSopenharmony_ci// the allocated array and updates the match count. If match_count is NULL,
9216cd6a6acSopenharmony_ci// stops early once the 1st match is found.
9226cd6a6acSopenharmony_cistatic struct spec **lookup_all(struct selabel_handle *rec,
9236cd6a6acSopenharmony_ci                                      const char *key,
9246cd6a6acSopenharmony_ci                                      int type,
9256cd6a6acSopenharmony_ci                                      bool partial,
9266cd6a6acSopenharmony_ci                                      size_t *match_count)
9276cd6a6acSopenharmony_ci{
9286cd6a6acSopenharmony_ci	struct saved_data *data = (struct saved_data *)rec->data;
9296cd6a6acSopenharmony_ci	struct spec *spec_arr = data->spec_arr;
9306cd6a6acSopenharmony_ci	int i, rc, file_stem;
9316cd6a6acSopenharmony_ci	size_t len;
9326cd6a6acSopenharmony_ci	mode_t mode = (mode_t)type;
9336cd6a6acSopenharmony_ci	char *clean_key = NULL;
9346cd6a6acSopenharmony_ci	const char *prev_slash, *next_slash;
9356cd6a6acSopenharmony_ci	unsigned int sofar = 0;
9366cd6a6acSopenharmony_ci	char *sub = NULL;
9376cd6a6acSopenharmony_ci
9386cd6a6acSopenharmony_ci	struct spec **result = NULL;
9396cd6a6acSopenharmony_ci	if (match_count) {
9406cd6a6acSopenharmony_ci		*match_count = 0;
9416cd6a6acSopenharmony_ci		result = calloc(data->nspec, sizeof(struct spec*));
9426cd6a6acSopenharmony_ci	} else {
9436cd6a6acSopenharmony_ci		result = calloc(1, sizeof(struct spec*));
9446cd6a6acSopenharmony_ci	}
9456cd6a6acSopenharmony_ci	if (!result) {
9466cd6a6acSopenharmony_ci		selinux_log(SELINUX_ERROR, "Failed to allocate %zu bytes of data\n",
9476cd6a6acSopenharmony_ci			    data->nspec * sizeof(struct spec*));
9486cd6a6acSopenharmony_ci		goto finish;
9496cd6a6acSopenharmony_ci	}
9506cd6a6acSopenharmony_ci
9516cd6a6acSopenharmony_ci	if (!data->nspec) {
9526cd6a6acSopenharmony_ci		errno = ENOENT;
9536cd6a6acSopenharmony_ci		goto finish;
9546cd6a6acSopenharmony_ci	}
9556cd6a6acSopenharmony_ci
9566cd6a6acSopenharmony_ci	/* Remove duplicate slashes */
9576cd6a6acSopenharmony_ci	if ((next_slash = strstr(key, "//"))) {
9586cd6a6acSopenharmony_ci		clean_key = (char *) malloc(strlen(key) + 1);
9596cd6a6acSopenharmony_ci		if (!clean_key)
9606cd6a6acSopenharmony_ci			goto finish;
9616cd6a6acSopenharmony_ci		prev_slash = key;
9626cd6a6acSopenharmony_ci		while (next_slash) {
9636cd6a6acSopenharmony_ci			memcpy(clean_key + sofar, prev_slash, next_slash - prev_slash);
9646cd6a6acSopenharmony_ci			sofar += next_slash - prev_slash;
9656cd6a6acSopenharmony_ci			prev_slash = next_slash + 1;
9666cd6a6acSopenharmony_ci			next_slash = strstr(prev_slash, "//");
9676cd6a6acSopenharmony_ci		}
9686cd6a6acSopenharmony_ci		strcpy(clean_key + sofar, prev_slash);
9696cd6a6acSopenharmony_ci		key = clean_key;
9706cd6a6acSopenharmony_ci	}
9716cd6a6acSopenharmony_ci
9726cd6a6acSopenharmony_ci	/* remove trailing slash */
9736cd6a6acSopenharmony_ci	len = strlen(key);
9746cd6a6acSopenharmony_ci	if (len == 0) {
9756cd6a6acSopenharmony_ci		errno = EINVAL;
9766cd6a6acSopenharmony_ci		goto finish;
9776cd6a6acSopenharmony_ci	}
9786cd6a6acSopenharmony_ci
9796cd6a6acSopenharmony_ci	if (len > 1 && key[len - 1] == '/') {
9806cd6a6acSopenharmony_ci		/* reuse clean_key from above if available */
9816cd6a6acSopenharmony_ci		if (!clean_key) {
9826cd6a6acSopenharmony_ci			clean_key = (char *) malloc(len);
9836cd6a6acSopenharmony_ci			if (!clean_key)
9846cd6a6acSopenharmony_ci				goto finish;
9856cd6a6acSopenharmony_ci
9866cd6a6acSopenharmony_ci			memcpy(clean_key, key, len - 1);
9876cd6a6acSopenharmony_ci		}
9886cd6a6acSopenharmony_ci
9896cd6a6acSopenharmony_ci		clean_key[len - 1] = '\0';
9906cd6a6acSopenharmony_ci		key = clean_key;
9916cd6a6acSopenharmony_ci	}
9926cd6a6acSopenharmony_ci
9936cd6a6acSopenharmony_ci	sub = selabel_sub_key(data, key);
9946cd6a6acSopenharmony_ci	if (sub)
9956cd6a6acSopenharmony_ci		key = sub;
9966cd6a6acSopenharmony_ci
9976cd6a6acSopenharmony_ci	file_stem = find_stem_from_file(data, key);
9986cd6a6acSopenharmony_ci	mode &= S_IFMT;
9996cd6a6acSopenharmony_ci
10006cd6a6acSopenharmony_ci	/*
10016cd6a6acSopenharmony_ci	 * Check for matching specifications in reverse order, so that
10026cd6a6acSopenharmony_ci	 * the last matching specification is used.
10036cd6a6acSopenharmony_ci	 */
10046cd6a6acSopenharmony_ci	for (i = data->nspec - 1; i >= 0; i--) {
10056cd6a6acSopenharmony_ci		struct spec *spec = &spec_arr[i];
10066cd6a6acSopenharmony_ci		/* if the spec in question matches no stem or has the same
10076cd6a6acSopenharmony_ci		 * stem as the file AND if the spec in question has no mode
10086cd6a6acSopenharmony_ci		 * specified or if the mode matches the file mode then we do
10096cd6a6acSopenharmony_ci		 * a regex check        */
10106cd6a6acSopenharmony_ci		bool stem_matches = spec->stem_id == -1 || spec->stem_id == file_stem;
10116cd6a6acSopenharmony_ci		// Don't check the stem if we want to find partial matches.
10126cd6a6acSopenharmony_ci                // Otherwise the case "/abc/efg/(/.*)?" will be considered
10136cd6a6acSopenharmony_ci                //a miss for "/abc".
10146cd6a6acSopenharmony_ci		if ((partial || stem_matches) &&
10156cd6a6acSopenharmony_ci				(!mode || !spec->mode || mode == spec->mode)) {
10166cd6a6acSopenharmony_ci			if (compile_regex(spec, NULL) < 0)
10176cd6a6acSopenharmony_ci				goto finish;
10186cd6a6acSopenharmony_ci			rc = regex_match(spec->regex, key, partial);
10196cd6a6acSopenharmony_ci			if (rc == REGEX_MATCH || (partial && rc == REGEX_MATCH_PARTIAL)) {
10206cd6a6acSopenharmony_ci				if (rc == REGEX_MATCH) {
10216cd6a6acSopenharmony_ci#ifdef __ATOMIC_RELAXED
10226cd6a6acSopenharmony_ci					__atomic_store_n(&spec->any_matches,
10236cd6a6acSopenharmony_ci							 true, __ATOMIC_RELAXED);
10246cd6a6acSopenharmony_ci#else
10256cd6a6acSopenharmony_ci#error "Please use a compiler that supports __atomic builtins"
10266cd6a6acSopenharmony_ci#endif
10276cd6a6acSopenharmony_ci				}
10286cd6a6acSopenharmony_ci
10296cd6a6acSopenharmony_ci				if (strcmp(spec_arr[i].lr.ctx_raw, "<<none>>") == 0) {
10306cd6a6acSopenharmony_ci					errno = ENOENT;
10316cd6a6acSopenharmony_ci					goto finish;
10326cd6a6acSopenharmony_ci				}
10336cd6a6acSopenharmony_ci
10346cd6a6acSopenharmony_ci				if (match_count) {
10356cd6a6acSopenharmony_ci					result[*match_count] = spec;
10366cd6a6acSopenharmony_ci					*match_count += 1;
10376cd6a6acSopenharmony_ci					// Continue to find all the matches.
10386cd6a6acSopenharmony_ci					continue;
10396cd6a6acSopenharmony_ci				}
10406cd6a6acSopenharmony_ci				result[0] = spec;
10416cd6a6acSopenharmony_ci				break;
10426cd6a6acSopenharmony_ci			}
10436cd6a6acSopenharmony_ci
10446cd6a6acSopenharmony_ci			if (rc == REGEX_NO_MATCH)
10456cd6a6acSopenharmony_ci				continue;
10466cd6a6acSopenharmony_ci
10476cd6a6acSopenharmony_ci			errno = ENOENT;
10486cd6a6acSopenharmony_ci			/* else it's an error */
10496cd6a6acSopenharmony_ci			goto finish;
10506cd6a6acSopenharmony_ci		}
10516cd6a6acSopenharmony_ci	}
10526cd6a6acSopenharmony_ci	if (!result[0])
10536cd6a6acSopenharmony_ci		errno = ENOENT;
10546cd6a6acSopenharmony_ci
10556cd6a6acSopenharmony_cifinish:
10566cd6a6acSopenharmony_ci	free(clean_key);
10576cd6a6acSopenharmony_ci	free(sub);
10586cd6a6acSopenharmony_ci	if (result && !result[0]) {
10596cd6a6acSopenharmony_ci		free(result);
10606cd6a6acSopenharmony_ci		result = NULL;
10616cd6a6acSopenharmony_ci	}
10626cd6a6acSopenharmony_ci	return result;
10636cd6a6acSopenharmony_ci}
10646cd6a6acSopenharmony_ci
10656cd6a6acSopenharmony_cistatic struct spec *lookup_common(struct selabel_handle *rec,
10666cd6a6acSopenharmony_ci                                  const char *key,
10676cd6a6acSopenharmony_ci                                  int type,
10686cd6a6acSopenharmony_ci                                  bool partial) {
10696cd6a6acSopenharmony_ci	struct spec **matches = lookup_all(rec, key, type, partial, NULL);
10706cd6a6acSopenharmony_ci	if (!matches) {
10716cd6a6acSopenharmony_ci		return NULL;
10726cd6a6acSopenharmony_ci	}
10736cd6a6acSopenharmony_ci	struct spec *result = matches[0];
10746cd6a6acSopenharmony_ci	free(matches);
10756cd6a6acSopenharmony_ci	return result;
10766cd6a6acSopenharmony_ci}
10776cd6a6acSopenharmony_ci
10786cd6a6acSopenharmony_ci/*
10796cd6a6acSopenharmony_ci * Returns true if the digest of all partial matched contexts is the same as
10806cd6a6acSopenharmony_ci * the one saved by setxattr, otherwise returns false. The length of the SHA1
10816cd6a6acSopenharmony_ci * digest will always be returned. The caller must free any returned digests.
10826cd6a6acSopenharmony_ci */
10836cd6a6acSopenharmony_cistatic bool get_digests_all_partial_matches(struct selabel_handle *rec,
10846cd6a6acSopenharmony_ci					    const char *pathname,
10856cd6a6acSopenharmony_ci					    uint8_t **calculated_digest,
10866cd6a6acSopenharmony_ci					    uint8_t **xattr_digest,
10876cd6a6acSopenharmony_ci					    size_t *digest_len)
10886cd6a6acSopenharmony_ci{
10896cd6a6acSopenharmony_ci	uint8_t read_digest[SHA1_HASH_SIZE];
10906cd6a6acSopenharmony_ci	ssize_t read_size = getxattr(pathname, RESTORECON_PARTIAL_MATCH_DIGEST,
10916cd6a6acSopenharmony_ci				     read_digest, SHA1_HASH_SIZE
10926cd6a6acSopenharmony_ci#ifdef __APPLE__
10936cd6a6acSopenharmony_ci				     , 0, 0
10946cd6a6acSopenharmony_ci#endif /* __APPLE __ */
10956cd6a6acSopenharmony_ci				    );
10966cd6a6acSopenharmony_ci	uint8_t hash_digest[SHA1_HASH_SIZE];
10976cd6a6acSopenharmony_ci	bool status = selabel_hash_all_partial_matches(rec, pathname,
10986cd6a6acSopenharmony_ci						       hash_digest);
10996cd6a6acSopenharmony_ci
11006cd6a6acSopenharmony_ci	*xattr_digest = NULL;
11016cd6a6acSopenharmony_ci	*calculated_digest = NULL;
11026cd6a6acSopenharmony_ci	*digest_len = SHA1_HASH_SIZE;
11036cd6a6acSopenharmony_ci
11046cd6a6acSopenharmony_ci	if (read_size == SHA1_HASH_SIZE) {
11056cd6a6acSopenharmony_ci		*xattr_digest = calloc(1, SHA1_HASH_SIZE + 1);
11066cd6a6acSopenharmony_ci		if (!*xattr_digest)
11076cd6a6acSopenharmony_ci			goto oom;
11086cd6a6acSopenharmony_ci
11096cd6a6acSopenharmony_ci		memcpy(*xattr_digest, read_digest, SHA1_HASH_SIZE);
11106cd6a6acSopenharmony_ci	}
11116cd6a6acSopenharmony_ci
11126cd6a6acSopenharmony_ci	if (status) {
11136cd6a6acSopenharmony_ci		*calculated_digest = calloc(1, SHA1_HASH_SIZE + 1);
11146cd6a6acSopenharmony_ci		if (!*calculated_digest)
11156cd6a6acSopenharmony_ci			goto oom;
11166cd6a6acSopenharmony_ci
11176cd6a6acSopenharmony_ci		memcpy(*calculated_digest, hash_digest, SHA1_HASH_SIZE);
11186cd6a6acSopenharmony_ci	}
11196cd6a6acSopenharmony_ci
11206cd6a6acSopenharmony_ci	if (status && read_size == SHA1_HASH_SIZE &&
11216cd6a6acSopenharmony_ci	    memcmp(read_digest, hash_digest, SHA1_HASH_SIZE) == 0)
11226cd6a6acSopenharmony_ci		return true;
11236cd6a6acSopenharmony_ci
11246cd6a6acSopenharmony_ci	return false;
11256cd6a6acSopenharmony_ci
11266cd6a6acSopenharmony_cioom:
11276cd6a6acSopenharmony_ci	selinux_log(SELINUX_ERROR, "SELinux: %s: Out of memory\n", __func__);
11286cd6a6acSopenharmony_ci	return false;
11296cd6a6acSopenharmony_ci}
11306cd6a6acSopenharmony_ci
11316cd6a6acSopenharmony_cistatic bool hash_all_partial_matches(struct selabel_handle *rec, const char *key, uint8_t *digest)
11326cd6a6acSopenharmony_ci{
11336cd6a6acSopenharmony_ci	assert(digest);
11346cd6a6acSopenharmony_ci
11356cd6a6acSopenharmony_ci	size_t total_matches;
11366cd6a6acSopenharmony_ci	struct spec **matches = lookup_all(rec, key, 0, true, &total_matches);
11376cd6a6acSopenharmony_ci	if (!matches) {
11386cd6a6acSopenharmony_ci		return false;
11396cd6a6acSopenharmony_ci	}
11406cd6a6acSopenharmony_ci
11416cd6a6acSopenharmony_ci	Sha1Context context;
11426cd6a6acSopenharmony_ci	Sha1Initialise(&context);
11436cd6a6acSopenharmony_ci	size_t i;
11446cd6a6acSopenharmony_ci	for (i = 0; i < total_matches; i++) {
11456cd6a6acSopenharmony_ci		char* regex_str = matches[i]->regex_str;
11466cd6a6acSopenharmony_ci		mode_t mode = matches[i]->mode;
11476cd6a6acSopenharmony_ci		char* ctx_raw = matches[i]->lr.ctx_raw;
11486cd6a6acSopenharmony_ci
11496cd6a6acSopenharmony_ci		Sha1Update(&context, regex_str, strlen(regex_str) + 1);
11506cd6a6acSopenharmony_ci		Sha1Update(&context, &mode, sizeof(mode_t));
11516cd6a6acSopenharmony_ci		Sha1Update(&context, ctx_raw, strlen(ctx_raw) + 1);
11526cd6a6acSopenharmony_ci	}
11536cd6a6acSopenharmony_ci
11546cd6a6acSopenharmony_ci	SHA1_HASH sha1_hash;
11556cd6a6acSopenharmony_ci	Sha1Finalise(&context, &sha1_hash);
11566cd6a6acSopenharmony_ci	memcpy(digest, sha1_hash.bytes, SHA1_HASH_SIZE);
11576cd6a6acSopenharmony_ci
11586cd6a6acSopenharmony_ci	free(matches);
11596cd6a6acSopenharmony_ci	return true;
11606cd6a6acSopenharmony_ci}
11616cd6a6acSopenharmony_ci
11626cd6a6acSopenharmony_cistatic struct selabel_lookup_rec *lookup(struct selabel_handle *rec,
11636cd6a6acSopenharmony_ci					 const char *key, int type)
11646cd6a6acSopenharmony_ci{
11656cd6a6acSopenharmony_ci	struct spec *spec;
11666cd6a6acSopenharmony_ci
11676cd6a6acSopenharmony_ci	spec = lookup_common(rec, key, type, false);
11686cd6a6acSopenharmony_ci	if (spec)
11696cd6a6acSopenharmony_ci		return &spec->lr;
11706cd6a6acSopenharmony_ci	return NULL;
11716cd6a6acSopenharmony_ci}
11726cd6a6acSopenharmony_ci
11736cd6a6acSopenharmony_cistatic bool partial_match(struct selabel_handle *rec, const char *key)
11746cd6a6acSopenharmony_ci{
11756cd6a6acSopenharmony_ci	return lookup_common(rec, key, 0, true) ? true : false;
11766cd6a6acSopenharmony_ci}
11776cd6a6acSopenharmony_ci
11786cd6a6acSopenharmony_cistatic struct selabel_lookup_rec *lookup_best_match(struct selabel_handle *rec,
11796cd6a6acSopenharmony_ci						    const char *key,
11806cd6a6acSopenharmony_ci						    const char **aliases,
11816cd6a6acSopenharmony_ci						    int type)
11826cd6a6acSopenharmony_ci{
11836cd6a6acSopenharmony_ci	size_t n, i;
11846cd6a6acSopenharmony_ci	int best = -1;
11856cd6a6acSopenharmony_ci	struct spec **specs;
11866cd6a6acSopenharmony_ci	size_t prefix_len = 0;
11876cd6a6acSopenharmony_ci	struct selabel_lookup_rec *lr = NULL;
11886cd6a6acSopenharmony_ci
11896cd6a6acSopenharmony_ci	if (!aliases || !aliases[0])
11906cd6a6acSopenharmony_ci		return lookup(rec, key, type);
11916cd6a6acSopenharmony_ci
11926cd6a6acSopenharmony_ci	for (n = 0; aliases[n]; n++)
11936cd6a6acSopenharmony_ci		;
11946cd6a6acSopenharmony_ci
11956cd6a6acSopenharmony_ci	specs = calloc(n+1, sizeof(struct spec *));
11966cd6a6acSopenharmony_ci	if (!specs)
11976cd6a6acSopenharmony_ci		return NULL;
11986cd6a6acSopenharmony_ci	specs[0] = lookup_common(rec, key, type, false);
11996cd6a6acSopenharmony_ci	if (specs[0]) {
12006cd6a6acSopenharmony_ci		if (!specs[0]->hasMetaChars) {
12016cd6a6acSopenharmony_ci			/* exact match on key */
12026cd6a6acSopenharmony_ci			lr = &specs[0]->lr;
12036cd6a6acSopenharmony_ci			goto out;
12046cd6a6acSopenharmony_ci		}
12056cd6a6acSopenharmony_ci		best = 0;
12066cd6a6acSopenharmony_ci		prefix_len = specs[0]->prefix_len;
12076cd6a6acSopenharmony_ci	}
12086cd6a6acSopenharmony_ci	for (i = 1; i <= n; i++) {
12096cd6a6acSopenharmony_ci		specs[i] = lookup_common(rec, aliases[i-1], type, false);
12106cd6a6acSopenharmony_ci		if (specs[i]) {
12116cd6a6acSopenharmony_ci			if (!specs[i]->hasMetaChars) {
12126cd6a6acSopenharmony_ci				/* exact match on alias */
12136cd6a6acSopenharmony_ci				lr = &specs[i]->lr;
12146cd6a6acSopenharmony_ci				goto out;
12156cd6a6acSopenharmony_ci			}
12166cd6a6acSopenharmony_ci			if (specs[i]->prefix_len > prefix_len) {
12176cd6a6acSopenharmony_ci				best = i;
12186cd6a6acSopenharmony_ci				prefix_len = specs[i]->prefix_len;
12196cd6a6acSopenharmony_ci			}
12206cd6a6acSopenharmony_ci		}
12216cd6a6acSopenharmony_ci	}
12226cd6a6acSopenharmony_ci
12236cd6a6acSopenharmony_ci	if (best >= 0) {
12246cd6a6acSopenharmony_ci		/* longest fixed prefix match on key or alias */
12256cd6a6acSopenharmony_ci		lr = &specs[best]->lr;
12266cd6a6acSopenharmony_ci	} else {
12276cd6a6acSopenharmony_ci		errno = ENOENT;
12286cd6a6acSopenharmony_ci	}
12296cd6a6acSopenharmony_ci
12306cd6a6acSopenharmony_ciout:
12316cd6a6acSopenharmony_ci	free(specs);
12326cd6a6acSopenharmony_ci	return lr;
12336cd6a6acSopenharmony_ci}
12346cd6a6acSopenharmony_ci
12356cd6a6acSopenharmony_cistatic enum selabel_cmp_result incomp(struct spec *spec1, struct spec *spec2, const char *reason, int i, int j)
12366cd6a6acSopenharmony_ci{
12376cd6a6acSopenharmony_ci	selinux_log(SELINUX_INFO,
12386cd6a6acSopenharmony_ci		    "selabel_cmp: mismatched %s on entry %d: (%s, %x, %s) vs entry %d: (%s, %x, %s)\n",
12396cd6a6acSopenharmony_ci		    reason,
12406cd6a6acSopenharmony_ci		    i, spec1->regex_str, spec1->mode, spec1->lr.ctx_raw,
12416cd6a6acSopenharmony_ci		    j, spec2->regex_str, spec2->mode, spec2->lr.ctx_raw);
12426cd6a6acSopenharmony_ci	return SELABEL_INCOMPARABLE;
12436cd6a6acSopenharmony_ci}
12446cd6a6acSopenharmony_ci
12456cd6a6acSopenharmony_cistatic enum selabel_cmp_result cmp(struct selabel_handle *h1,
12466cd6a6acSopenharmony_ci				   struct selabel_handle *h2)
12476cd6a6acSopenharmony_ci{
12486cd6a6acSopenharmony_ci	struct saved_data *data1 = (struct saved_data *)h1->data;
12496cd6a6acSopenharmony_ci	struct saved_data *data2 = (struct saved_data *)h2->data;
12506cd6a6acSopenharmony_ci	unsigned int i, nspec1 = data1->nspec, j, nspec2 = data2->nspec;
12516cd6a6acSopenharmony_ci	struct spec *spec_arr1 = data1->spec_arr, *spec_arr2 = data2->spec_arr;
12526cd6a6acSopenharmony_ci	struct stem *stem_arr1 = data1->stem_arr, *stem_arr2 = data2->stem_arr;
12536cd6a6acSopenharmony_ci	bool skipped1 = false, skipped2 = false;
12546cd6a6acSopenharmony_ci
12556cd6a6acSopenharmony_ci	i = 0;
12566cd6a6acSopenharmony_ci	j = 0;
12576cd6a6acSopenharmony_ci	while (i < nspec1 && j < nspec2) {
12586cd6a6acSopenharmony_ci		struct spec *spec1 = &spec_arr1[i];
12596cd6a6acSopenharmony_ci		struct spec *spec2 = &spec_arr2[j];
12606cd6a6acSopenharmony_ci
12616cd6a6acSopenharmony_ci		/*
12626cd6a6acSopenharmony_ci		 * Because sort_specs() moves exact pathnames to the
12636cd6a6acSopenharmony_ci		 * end, we might need to skip over additional regex
12646cd6a6acSopenharmony_ci		 * entries that only exist in one of the configurations.
12656cd6a6acSopenharmony_ci		 */
12666cd6a6acSopenharmony_ci		if (!spec1->hasMetaChars && spec2->hasMetaChars) {
12676cd6a6acSopenharmony_ci			j++;
12686cd6a6acSopenharmony_ci			skipped2 = true;
12696cd6a6acSopenharmony_ci			continue;
12706cd6a6acSopenharmony_ci		}
12716cd6a6acSopenharmony_ci
12726cd6a6acSopenharmony_ci		if (spec1->hasMetaChars && !spec2->hasMetaChars) {
12736cd6a6acSopenharmony_ci			i++;
12746cd6a6acSopenharmony_ci			skipped1 = true;
12756cd6a6acSopenharmony_ci			continue;
12766cd6a6acSopenharmony_ci		}
12776cd6a6acSopenharmony_ci
12786cd6a6acSopenharmony_ci		if (spec1->regex && spec2->regex) {
12796cd6a6acSopenharmony_ci			if (regex_cmp(spec1->regex, spec2->regex) == SELABEL_INCOMPARABLE){
12806cd6a6acSopenharmony_ci				return incomp(spec1, spec2, "regex", i, j);
12816cd6a6acSopenharmony_ci			}
12826cd6a6acSopenharmony_ci		} else {
12836cd6a6acSopenharmony_ci			if (strcmp(spec1->regex_str, spec2->regex_str))
12846cd6a6acSopenharmony_ci				return incomp(spec1, spec2, "regex_str", i, j);
12856cd6a6acSopenharmony_ci		}
12866cd6a6acSopenharmony_ci
12876cd6a6acSopenharmony_ci		if (spec1->mode != spec2->mode)
12886cd6a6acSopenharmony_ci			return incomp(spec1, spec2, "mode", i, j);
12896cd6a6acSopenharmony_ci
12906cd6a6acSopenharmony_ci		if (spec1->stem_id == -1 && spec2->stem_id != -1)
12916cd6a6acSopenharmony_ci			return incomp(spec1, spec2, "stem_id", i, j);
12926cd6a6acSopenharmony_ci		if (spec2->stem_id == -1 && spec1->stem_id != -1)
12936cd6a6acSopenharmony_ci			return incomp(spec1, spec2, "stem_id", i, j);
12946cd6a6acSopenharmony_ci		if (spec1->stem_id != -1 && spec2->stem_id != -1) {
12956cd6a6acSopenharmony_ci			struct stem *stem1 = &stem_arr1[spec1->stem_id];
12966cd6a6acSopenharmony_ci			struct stem *stem2 = &stem_arr2[spec2->stem_id];
12976cd6a6acSopenharmony_ci			if (stem1->len != stem2->len ||
12986cd6a6acSopenharmony_ci			    strncmp(stem1->buf, stem2->buf, stem1->len))
12996cd6a6acSopenharmony_ci				return incomp(spec1, spec2, "stem", i, j);
13006cd6a6acSopenharmony_ci		}
13016cd6a6acSopenharmony_ci
13026cd6a6acSopenharmony_ci		if (strcmp(spec1->lr.ctx_raw, spec2->lr.ctx_raw))
13036cd6a6acSopenharmony_ci			return incomp(spec1, spec2, "ctx_raw", i, j);
13046cd6a6acSopenharmony_ci
13056cd6a6acSopenharmony_ci		i++;
13066cd6a6acSopenharmony_ci		j++;
13076cd6a6acSopenharmony_ci	}
13086cd6a6acSopenharmony_ci
13096cd6a6acSopenharmony_ci	if ((skipped1 || i < nspec1) && !skipped2)
13106cd6a6acSopenharmony_ci		return SELABEL_SUPERSET;
13116cd6a6acSopenharmony_ci	if ((skipped2 || j < nspec2) && !skipped1)
13126cd6a6acSopenharmony_ci		return SELABEL_SUBSET;
13136cd6a6acSopenharmony_ci	if (skipped1 && skipped2)
13146cd6a6acSopenharmony_ci		return SELABEL_INCOMPARABLE;
13156cd6a6acSopenharmony_ci	return SELABEL_EQUAL;
13166cd6a6acSopenharmony_ci}
13176cd6a6acSopenharmony_ci
13186cd6a6acSopenharmony_ci
13196cd6a6acSopenharmony_cistatic void stats(struct selabel_handle *rec)
13206cd6a6acSopenharmony_ci{
13216cd6a6acSopenharmony_ci	struct saved_data *data = (struct saved_data *)rec->data;
13226cd6a6acSopenharmony_ci	unsigned int i, nspec = data->nspec;
13236cd6a6acSopenharmony_ci	struct spec *spec_arr = data->spec_arr;
13246cd6a6acSopenharmony_ci	bool any_matches;
13256cd6a6acSopenharmony_ci
13266cd6a6acSopenharmony_ci	for (i = 0; i < nspec; i++) {
13276cd6a6acSopenharmony_ci#ifdef __ATOMIC_RELAXED
13286cd6a6acSopenharmony_ci		any_matches = __atomic_load_n(&spec_arr[i].any_matches, __ATOMIC_RELAXED);
13296cd6a6acSopenharmony_ci#else
13306cd6a6acSopenharmony_ci#error "Please use a compiler that supports __atomic builtins"
13316cd6a6acSopenharmony_ci#endif
13326cd6a6acSopenharmony_ci		if (!any_matches) {
13336cd6a6acSopenharmony_ci			if (spec_arr[i].type_str) {
13346cd6a6acSopenharmony_ci				COMPAT_LOG(SELINUX_WARNING,
13356cd6a6acSopenharmony_ci				    "Warning!  No matches for (%s, %s, %s)\n",
13366cd6a6acSopenharmony_ci				    spec_arr[i].regex_str,
13376cd6a6acSopenharmony_ci				    spec_arr[i].type_str,
13386cd6a6acSopenharmony_ci				    spec_arr[i].lr.ctx_raw);
13396cd6a6acSopenharmony_ci			} else {
13406cd6a6acSopenharmony_ci				COMPAT_LOG(SELINUX_WARNING,
13416cd6a6acSopenharmony_ci				    "Warning!  No matches for (%s, %s)\n",
13426cd6a6acSopenharmony_ci				    spec_arr[i].regex_str,
13436cd6a6acSopenharmony_ci				    spec_arr[i].lr.ctx_raw);
13446cd6a6acSopenharmony_ci			}
13456cd6a6acSopenharmony_ci		}
13466cd6a6acSopenharmony_ci	}
13476cd6a6acSopenharmony_ci}
13486cd6a6acSopenharmony_ci
13496cd6a6acSopenharmony_ciint selabel_file_init(struct selabel_handle *rec,
13506cd6a6acSopenharmony_ci				    const struct selinux_opt *opts,
13516cd6a6acSopenharmony_ci				    unsigned nopts)
13526cd6a6acSopenharmony_ci{
13536cd6a6acSopenharmony_ci	struct saved_data *data;
13546cd6a6acSopenharmony_ci
13556cd6a6acSopenharmony_ci	data = (struct saved_data *)malloc(sizeof(*data));
13566cd6a6acSopenharmony_ci	if (!data)
13576cd6a6acSopenharmony_ci		return -1;
13586cd6a6acSopenharmony_ci	memset(data, 0, sizeof(*data));
13596cd6a6acSopenharmony_ci
13606cd6a6acSopenharmony_ci	rec->data = data;
13616cd6a6acSopenharmony_ci	rec->func_close = &closef;
13626cd6a6acSopenharmony_ci	rec->func_stats = &stats;
13636cd6a6acSopenharmony_ci	rec->func_lookup = &lookup;
13646cd6a6acSopenharmony_ci	rec->func_partial_match = &partial_match;
13656cd6a6acSopenharmony_ci	rec->func_get_digests_all_partial_matches =
13666cd6a6acSopenharmony_ci					&get_digests_all_partial_matches;
13676cd6a6acSopenharmony_ci	rec->func_hash_all_partial_matches = &hash_all_partial_matches;
13686cd6a6acSopenharmony_ci	rec->func_lookup_best_match = &lookup_best_match;
13696cd6a6acSopenharmony_ci	rec->func_cmp = &cmp;
13706cd6a6acSopenharmony_ci
13716cd6a6acSopenharmony_ci	return init(rec, opts, nopts);
13726cd6a6acSopenharmony_ci}
1373