16cd6a6acSopenharmony_ci#ifndef _SELABEL_FILE_H_
26cd6a6acSopenharmony_ci#define _SELABEL_FILE_H_
36cd6a6acSopenharmony_ci
46cd6a6acSopenharmony_ci#include <errno.h>
56cd6a6acSopenharmony_ci#include <pthread.h>
66cd6a6acSopenharmony_ci#include <string.h>
76cd6a6acSopenharmony_ci
86cd6a6acSopenharmony_ci#include <sys/stat.h>
96cd6a6acSopenharmony_ci#include <sys/xattr.h>
106cd6a6acSopenharmony_ci
116cd6a6acSopenharmony_ci/*
126cd6a6acSopenharmony_ci * regex.h/c were introduced to hold all dependencies on the regular
136cd6a6acSopenharmony_ci * expression back-end when we started supporting PCRE2. regex.h defines a
146cd6a6acSopenharmony_ci * minimal interface required by libselinux, so that the remaining code
156cd6a6acSopenharmony_ci * can be agnostic about the underlying implementation.
166cd6a6acSopenharmony_ci */
176cd6a6acSopenharmony_ci#include "regex.h"
186cd6a6acSopenharmony_ci
196cd6a6acSopenharmony_ci#include "callbacks.h"
206cd6a6acSopenharmony_ci#include "label_internal.h"
216cd6a6acSopenharmony_ci#include "selinux_internal.h"
226cd6a6acSopenharmony_ci
236cd6a6acSopenharmony_ci#define SELINUX_MAGIC_COMPILED_FCONTEXT	0xf97cff8a
246cd6a6acSopenharmony_ci
256cd6a6acSopenharmony_ci/* Version specific changes */
266cd6a6acSopenharmony_ci#define SELINUX_COMPILED_FCONTEXT_NOPCRE_VERS	1
276cd6a6acSopenharmony_ci#define SELINUX_COMPILED_FCONTEXT_PCRE_VERS	2
286cd6a6acSopenharmony_ci#define SELINUX_COMPILED_FCONTEXT_MODE		3
296cd6a6acSopenharmony_ci#define SELINUX_COMPILED_FCONTEXT_PREFIX_LEN	4
306cd6a6acSopenharmony_ci#define SELINUX_COMPILED_FCONTEXT_REGEX_ARCH	5
316cd6a6acSopenharmony_ci
326cd6a6acSopenharmony_ci#define SELINUX_COMPILED_FCONTEXT_MAX_VERS \
336cd6a6acSopenharmony_ci	SELINUX_COMPILED_FCONTEXT_REGEX_ARCH
346cd6a6acSopenharmony_ci
356cd6a6acSopenharmony_ci/* Required selinux_restorecon and selabel_get_digests_all_partial_matches() */
366cd6a6acSopenharmony_ci#define RESTORECON_PARTIAL_MATCH_DIGEST  "security.sehash"
376cd6a6acSopenharmony_ci
386cd6a6acSopenharmony_cistruct selabel_sub {
396cd6a6acSopenharmony_ci	char *src;
406cd6a6acSopenharmony_ci	int slen;
416cd6a6acSopenharmony_ci	char *dst;
426cd6a6acSopenharmony_ci	struct selabel_sub *next;
436cd6a6acSopenharmony_ci};
446cd6a6acSopenharmony_ci
456cd6a6acSopenharmony_ci/* A file security context specification. */
466cd6a6acSopenharmony_cistruct spec {
476cd6a6acSopenharmony_ci	struct selabel_lookup_rec lr;	/* holds contexts for lookup result */
486cd6a6acSopenharmony_ci	char *regex_str;	/* regular expression string for diagnostics */
496cd6a6acSopenharmony_ci	char *type_str;		/* type string for diagnostic messages */
506cd6a6acSopenharmony_ci	struct regex_data * regex; /* backend dependent regular expression data */
516cd6a6acSopenharmony_ci	bool regex_compiled; /* bool to indicate if the regex is compiled */
526cd6a6acSopenharmony_ci	pthread_mutex_t regex_lock; /* lock for lazy compilation of regex */
536cd6a6acSopenharmony_ci	mode_t mode;		/* mode format value */
546cd6a6acSopenharmony_ci	bool any_matches;	/* did any pathname match? */
556cd6a6acSopenharmony_ci	int stem_id;		/* indicates which stem-compression item */
566cd6a6acSopenharmony_ci	char hasMetaChars;	/* regular expression has meta-chars */
576cd6a6acSopenharmony_ci	char from_mmap;		/* this spec is from an mmap of the data */
586cd6a6acSopenharmony_ci	size_t prefix_len;      /* length of fixed path prefix */
596cd6a6acSopenharmony_ci};
606cd6a6acSopenharmony_ci
616cd6a6acSopenharmony_ci/* A regular expression stem */
626cd6a6acSopenharmony_cistruct stem {
636cd6a6acSopenharmony_ci	char *buf;
646cd6a6acSopenharmony_ci	int len;
656cd6a6acSopenharmony_ci	char from_mmap;
666cd6a6acSopenharmony_ci};
676cd6a6acSopenharmony_ci
686cd6a6acSopenharmony_ci/* Where we map the file in during selabel_open() */
696cd6a6acSopenharmony_cistruct mmap_area {
706cd6a6acSopenharmony_ci	void *addr;	/* Start addr + len used to release memory at close */
716cd6a6acSopenharmony_ci	size_t len;
726cd6a6acSopenharmony_ci	void *next_addr;	/* Incremented by next_entry() */
736cd6a6acSopenharmony_ci	size_t next_len;	/* Decremented by next_entry() */
746cd6a6acSopenharmony_ci	struct mmap_area *next;
756cd6a6acSopenharmony_ci};
766cd6a6acSopenharmony_ci
776cd6a6acSopenharmony_ci/* Our stored configuration */
786cd6a6acSopenharmony_cistruct saved_data {
796cd6a6acSopenharmony_ci	/*
806cd6a6acSopenharmony_ci	 * The array of specifications, initially in the same order as in
816cd6a6acSopenharmony_ci	 * the specification file. Sorting occurs based on hasMetaChars.
826cd6a6acSopenharmony_ci	 */
836cd6a6acSopenharmony_ci	struct spec *spec_arr;
846cd6a6acSopenharmony_ci	unsigned int nspec;
856cd6a6acSopenharmony_ci	unsigned int alloc_specs;
866cd6a6acSopenharmony_ci
876cd6a6acSopenharmony_ci	/*
886cd6a6acSopenharmony_ci	 * The array of regular expression stems.
896cd6a6acSopenharmony_ci	 */
906cd6a6acSopenharmony_ci	struct stem *stem_arr;
916cd6a6acSopenharmony_ci	int num_stems;
926cd6a6acSopenharmony_ci	int alloc_stems;
936cd6a6acSopenharmony_ci	struct mmap_area *mmap_areas;
946cd6a6acSopenharmony_ci
956cd6a6acSopenharmony_ci	/* substitution support */
966cd6a6acSopenharmony_ci	struct selabel_sub *dist_subs;
976cd6a6acSopenharmony_ci	struct selabel_sub *subs;
986cd6a6acSopenharmony_ci};
996cd6a6acSopenharmony_ci
1006cd6a6acSopenharmony_cistatic inline mode_t string_to_mode(char *mode)
1016cd6a6acSopenharmony_ci{
1026cd6a6acSopenharmony_ci	size_t len;
1036cd6a6acSopenharmony_ci
1046cd6a6acSopenharmony_ci	if (!mode)
1056cd6a6acSopenharmony_ci		return 0;
1066cd6a6acSopenharmony_ci	len = strlen(mode);
1076cd6a6acSopenharmony_ci	if (mode[0] != '-' || len != 2)
1086cd6a6acSopenharmony_ci		return -1;
1096cd6a6acSopenharmony_ci	switch (mode[1]) {
1106cd6a6acSopenharmony_ci	case 'b':
1116cd6a6acSopenharmony_ci		return S_IFBLK;
1126cd6a6acSopenharmony_ci	case 'c':
1136cd6a6acSopenharmony_ci		return S_IFCHR;
1146cd6a6acSopenharmony_ci	case 'd':
1156cd6a6acSopenharmony_ci		return S_IFDIR;
1166cd6a6acSopenharmony_ci	case 'p':
1176cd6a6acSopenharmony_ci		return S_IFIFO;
1186cd6a6acSopenharmony_ci	case 'l':
1196cd6a6acSopenharmony_ci		return S_IFLNK;
1206cd6a6acSopenharmony_ci	case 's':
1216cd6a6acSopenharmony_ci		return S_IFSOCK;
1226cd6a6acSopenharmony_ci	case '-':
1236cd6a6acSopenharmony_ci		return S_IFREG;
1246cd6a6acSopenharmony_ci	default:
1256cd6a6acSopenharmony_ci		return -1;
1266cd6a6acSopenharmony_ci	}
1276cd6a6acSopenharmony_ci	/* impossible to get here */
1286cd6a6acSopenharmony_ci	return 0;
1296cd6a6acSopenharmony_ci}
1306cd6a6acSopenharmony_ci
1316cd6a6acSopenharmony_cistatic inline int grow_specs(struct saved_data *data)
1326cd6a6acSopenharmony_ci{
1336cd6a6acSopenharmony_ci	struct spec *specs;
1346cd6a6acSopenharmony_ci	size_t new_specs, total_specs;
1356cd6a6acSopenharmony_ci
1366cd6a6acSopenharmony_ci	if (data->nspec < data->alloc_specs)
1376cd6a6acSopenharmony_ci		return 0;
1386cd6a6acSopenharmony_ci
1396cd6a6acSopenharmony_ci	new_specs = data->nspec + 16;
1406cd6a6acSopenharmony_ci	total_specs = data->nspec + new_specs;
1416cd6a6acSopenharmony_ci
1426cd6a6acSopenharmony_ci	specs = realloc(data->spec_arr, total_specs * sizeof(*specs));
1436cd6a6acSopenharmony_ci	if (!specs) {
1446cd6a6acSopenharmony_ci		perror("realloc");
1456cd6a6acSopenharmony_ci		return -1;
1466cd6a6acSopenharmony_ci	}
1476cd6a6acSopenharmony_ci
1486cd6a6acSopenharmony_ci	/* blank the new entries */
1496cd6a6acSopenharmony_ci	memset(&specs[data->nspec], 0, new_specs * sizeof(*specs));
1506cd6a6acSopenharmony_ci
1516cd6a6acSopenharmony_ci	data->spec_arr = specs;
1526cd6a6acSopenharmony_ci	data->alloc_specs = total_specs;
1536cd6a6acSopenharmony_ci	return 0;
1546cd6a6acSopenharmony_ci}
1556cd6a6acSopenharmony_ci
1566cd6a6acSopenharmony_ci/* Determine if the regular expression specification has any meta characters. */
1576cd6a6acSopenharmony_cistatic inline void spec_hasMetaChars(struct spec *spec)
1586cd6a6acSopenharmony_ci{
1596cd6a6acSopenharmony_ci	char *c;
1606cd6a6acSopenharmony_ci	int len;
1616cd6a6acSopenharmony_ci	char *end;
1626cd6a6acSopenharmony_ci
1636cd6a6acSopenharmony_ci	c = spec->regex_str;
1646cd6a6acSopenharmony_ci	len = strlen(spec->regex_str);
1656cd6a6acSopenharmony_ci	end = c + len;
1666cd6a6acSopenharmony_ci
1676cd6a6acSopenharmony_ci	spec->hasMetaChars = 0;
1686cd6a6acSopenharmony_ci	spec->prefix_len = len;
1696cd6a6acSopenharmony_ci
1706cd6a6acSopenharmony_ci	/* Look at each character in the RE specification string for a
1716cd6a6acSopenharmony_ci	 * meta character. Return when any meta character reached. */
1726cd6a6acSopenharmony_ci	while (c < end) {
1736cd6a6acSopenharmony_ci		switch (*c) {
1746cd6a6acSopenharmony_ci		case '.':
1756cd6a6acSopenharmony_ci		case '^':
1766cd6a6acSopenharmony_ci		case '$':
1776cd6a6acSopenharmony_ci		case '?':
1786cd6a6acSopenharmony_ci		case '*':
1796cd6a6acSopenharmony_ci		case '+':
1806cd6a6acSopenharmony_ci		case '|':
1816cd6a6acSopenharmony_ci		case '[':
1826cd6a6acSopenharmony_ci		case '(':
1836cd6a6acSopenharmony_ci		case '{':
1846cd6a6acSopenharmony_ci			spec->hasMetaChars = 1;
1856cd6a6acSopenharmony_ci			spec->prefix_len = c - spec->regex_str;
1866cd6a6acSopenharmony_ci			return;
1876cd6a6acSopenharmony_ci		case '\\':	/* skip the next character */
1886cd6a6acSopenharmony_ci			c++;
1896cd6a6acSopenharmony_ci			break;
1906cd6a6acSopenharmony_ci		default:
1916cd6a6acSopenharmony_ci			break;
1926cd6a6acSopenharmony_ci
1936cd6a6acSopenharmony_ci		}
1946cd6a6acSopenharmony_ci		c++;
1956cd6a6acSopenharmony_ci	}
1966cd6a6acSopenharmony_ci}
1976cd6a6acSopenharmony_ci
1986cd6a6acSopenharmony_ci/* Move exact pathname specifications to the end. */
1996cd6a6acSopenharmony_cistatic inline int sort_specs(struct saved_data *data)
2006cd6a6acSopenharmony_ci{
2016cd6a6acSopenharmony_ci	struct spec *spec_copy;
2026cd6a6acSopenharmony_ci	struct spec spec;
2036cd6a6acSopenharmony_ci	unsigned int i;
2046cd6a6acSopenharmony_ci	int front, back;
2056cd6a6acSopenharmony_ci	size_t len = sizeof(*spec_copy);
2066cd6a6acSopenharmony_ci
2076cd6a6acSopenharmony_ci	spec_copy = malloc(len * data->nspec);
2086cd6a6acSopenharmony_ci	if (!spec_copy)
2096cd6a6acSopenharmony_ci		return -1;
2106cd6a6acSopenharmony_ci
2116cd6a6acSopenharmony_ci	/* first move the exact pathnames to the back */
2126cd6a6acSopenharmony_ci	front = 0;
2136cd6a6acSopenharmony_ci	back = data->nspec - 1;
2146cd6a6acSopenharmony_ci	for (i = 0; i < data->nspec; i++) {
2156cd6a6acSopenharmony_ci		if (data->spec_arr[i].hasMetaChars)
2166cd6a6acSopenharmony_ci			memcpy(&spec_copy[front++], &data->spec_arr[i], len);
2176cd6a6acSopenharmony_ci		else
2186cd6a6acSopenharmony_ci			memcpy(&spec_copy[back--], &data->spec_arr[i], len);
2196cd6a6acSopenharmony_ci	}
2206cd6a6acSopenharmony_ci
2216cd6a6acSopenharmony_ci	/*
2226cd6a6acSopenharmony_ci	 * now the exact pathnames are at the end, but they are in the reverse
2236cd6a6acSopenharmony_ci	 * order. Since 'front' is now the first of the 'exact' we can run
2246cd6a6acSopenharmony_ci	 * that part of the array switching the front and back element.
2256cd6a6acSopenharmony_ci	 */
2266cd6a6acSopenharmony_ci	back = data->nspec - 1;
2276cd6a6acSopenharmony_ci	while (front < back) {
2286cd6a6acSopenharmony_ci		/* save the front */
2296cd6a6acSopenharmony_ci		memcpy(&spec, &spec_copy[front], len);
2306cd6a6acSopenharmony_ci		/* move the back to the front */
2316cd6a6acSopenharmony_ci		memcpy(&spec_copy[front], &spec_copy[back], len);
2326cd6a6acSopenharmony_ci		/* put the old front in the back */
2336cd6a6acSopenharmony_ci		memcpy(&spec_copy[back], &spec, len);
2346cd6a6acSopenharmony_ci		front++;
2356cd6a6acSopenharmony_ci		back--;
2366cd6a6acSopenharmony_ci	}
2376cd6a6acSopenharmony_ci
2386cd6a6acSopenharmony_ci	free(data->spec_arr);
2396cd6a6acSopenharmony_ci	data->spec_arr = spec_copy;
2406cd6a6acSopenharmony_ci
2416cd6a6acSopenharmony_ci	return 0;
2426cd6a6acSopenharmony_ci}
2436cd6a6acSopenharmony_ci
2446cd6a6acSopenharmony_ci/* Return the length of the text that can be considered the stem, returns 0
2456cd6a6acSopenharmony_ci * if there is no identifiable stem */
2466cd6a6acSopenharmony_cistatic inline int get_stem_from_spec(const char *const buf)
2476cd6a6acSopenharmony_ci{
2486cd6a6acSopenharmony_ci	const char *tmp = strchr(buf + 1, '/');
2496cd6a6acSopenharmony_ci	const char *ind;
2506cd6a6acSopenharmony_ci
2516cd6a6acSopenharmony_ci	if (!tmp)
2526cd6a6acSopenharmony_ci		return 0;
2536cd6a6acSopenharmony_ci
2546cd6a6acSopenharmony_ci	for (ind = buf; ind < tmp; ind++) {
2556cd6a6acSopenharmony_ci		if (strchr(".^$?*+|[({", (int)*ind))
2566cd6a6acSopenharmony_ci			return 0;
2576cd6a6acSopenharmony_ci	}
2586cd6a6acSopenharmony_ci	return tmp - buf;
2596cd6a6acSopenharmony_ci}
2606cd6a6acSopenharmony_ci
2616cd6a6acSopenharmony_ci/*
2626cd6a6acSopenharmony_ci * return the stemid given a string and a length
2636cd6a6acSopenharmony_ci */
2646cd6a6acSopenharmony_cistatic inline int find_stem(struct saved_data *data, const char *buf,
2656cd6a6acSopenharmony_ci						    int stem_len)
2666cd6a6acSopenharmony_ci{
2676cd6a6acSopenharmony_ci	int i;
2686cd6a6acSopenharmony_ci
2696cd6a6acSopenharmony_ci	for (i = 0; i < data->num_stems; i++) {
2706cd6a6acSopenharmony_ci		if (stem_len == data->stem_arr[i].len &&
2716cd6a6acSopenharmony_ci		    !strncmp(buf, data->stem_arr[i].buf, stem_len))
2726cd6a6acSopenharmony_ci			return i;
2736cd6a6acSopenharmony_ci	}
2746cd6a6acSopenharmony_ci
2756cd6a6acSopenharmony_ci	return -1;
2766cd6a6acSopenharmony_ci}
2776cd6a6acSopenharmony_ci
2786cd6a6acSopenharmony_ci/* returns the index of the new stored object */
2796cd6a6acSopenharmony_cistatic inline int store_stem(struct saved_data *data, char *buf, int stem_len)
2806cd6a6acSopenharmony_ci{
2816cd6a6acSopenharmony_ci	int num = data->num_stems;
2826cd6a6acSopenharmony_ci
2836cd6a6acSopenharmony_ci	if (data->alloc_stems == num) {
2846cd6a6acSopenharmony_ci		struct stem *tmp_arr;
2856cd6a6acSopenharmony_ci		int alloc_stems = data->alloc_stems * 2 + 16;
2866cd6a6acSopenharmony_ci		tmp_arr = realloc(data->stem_arr,
2876cd6a6acSopenharmony_ci				  sizeof(*tmp_arr) * alloc_stems);
2886cd6a6acSopenharmony_ci		if (!tmp_arr) {
2896cd6a6acSopenharmony_ci			return -1;
2906cd6a6acSopenharmony_ci		}
2916cd6a6acSopenharmony_ci		data->alloc_stems = alloc_stems;
2926cd6a6acSopenharmony_ci		data->stem_arr = tmp_arr;
2936cd6a6acSopenharmony_ci	}
2946cd6a6acSopenharmony_ci	data->stem_arr[num].len = stem_len;
2956cd6a6acSopenharmony_ci	data->stem_arr[num].buf = buf;
2966cd6a6acSopenharmony_ci	data->stem_arr[num].from_mmap = 0;
2976cd6a6acSopenharmony_ci	data->num_stems++;
2986cd6a6acSopenharmony_ci
2996cd6a6acSopenharmony_ci	return num;
3006cd6a6acSopenharmony_ci}
3016cd6a6acSopenharmony_ci
3026cd6a6acSopenharmony_ci/* find the stem of a file spec, returns the index into stem_arr for a new
3036cd6a6acSopenharmony_ci * or existing stem, (or -1 if there is no possible stem - IE for a file in
3046cd6a6acSopenharmony_ci * the root directory or a regex that is too complex for us). */
3056cd6a6acSopenharmony_cistatic inline int find_stem_from_spec(struct saved_data *data, const char *buf)
3066cd6a6acSopenharmony_ci{
3076cd6a6acSopenharmony_ci	int stem_len = get_stem_from_spec(buf);
3086cd6a6acSopenharmony_ci	int stemid;
3096cd6a6acSopenharmony_ci	char *stem;
3106cd6a6acSopenharmony_ci	int r;
3116cd6a6acSopenharmony_ci
3126cd6a6acSopenharmony_ci	if (!stem_len)
3136cd6a6acSopenharmony_ci		return -1;
3146cd6a6acSopenharmony_ci
3156cd6a6acSopenharmony_ci	stemid = find_stem(data, buf, stem_len);
3166cd6a6acSopenharmony_ci	if (stemid >= 0)
3176cd6a6acSopenharmony_ci		return stemid;
3186cd6a6acSopenharmony_ci
3196cd6a6acSopenharmony_ci	/* not found, allocate a new one */
3206cd6a6acSopenharmony_ci	stem = strndup(buf, stem_len);
3216cd6a6acSopenharmony_ci	if (!stem)
3226cd6a6acSopenharmony_ci		return -1;
3236cd6a6acSopenharmony_ci
3246cd6a6acSopenharmony_ci	r = store_stem(data, stem, stem_len);
3256cd6a6acSopenharmony_ci	if (r < 0)
3266cd6a6acSopenharmony_ci		free(stem);
3276cd6a6acSopenharmony_ci
3286cd6a6acSopenharmony_ci	return r;
3296cd6a6acSopenharmony_ci}
3306cd6a6acSopenharmony_ci
3316cd6a6acSopenharmony_ci/* This will always check for buffer over-runs and either read the next entry
3326cd6a6acSopenharmony_ci * if buf != NULL or skip over the entry (as these areas are mapped in the
3336cd6a6acSopenharmony_ci * current buffer). */
3346cd6a6acSopenharmony_cistatic inline int next_entry(void *buf, struct mmap_area *fp, size_t bytes)
3356cd6a6acSopenharmony_ci{
3366cd6a6acSopenharmony_ci	if (bytes > fp->next_len)
3376cd6a6acSopenharmony_ci		return -1;
3386cd6a6acSopenharmony_ci
3396cd6a6acSopenharmony_ci	if (buf)
3406cd6a6acSopenharmony_ci		memcpy(buf, fp->next_addr, bytes);
3416cd6a6acSopenharmony_ci
3426cd6a6acSopenharmony_ci	fp->next_addr = (char *)fp->next_addr + bytes;
3436cd6a6acSopenharmony_ci	fp->next_len -= bytes;
3446cd6a6acSopenharmony_ci	return 0;
3456cd6a6acSopenharmony_ci}
3466cd6a6acSopenharmony_ci
3476cd6a6acSopenharmony_cistatic inline int compile_regex(struct spec *spec, const char **errbuf)
3486cd6a6acSopenharmony_ci{
3496cd6a6acSopenharmony_ci	char *reg_buf, *anchored_regex, *cp;
3506cd6a6acSopenharmony_ci	struct regex_error_data error_data;
3516cd6a6acSopenharmony_ci	static char regex_error_format_buffer[256];
3526cd6a6acSopenharmony_ci	size_t len;
3536cd6a6acSopenharmony_ci	int rc;
3546cd6a6acSopenharmony_ci	bool regex_compiled;
3556cd6a6acSopenharmony_ci
3566cd6a6acSopenharmony_ci	/* We really want pthread_once() here, but since its
3576cd6a6acSopenharmony_ci	 * init_routine does not take a parameter, it's not possible
3586cd6a6acSopenharmony_ci	 * to use, so we generate the same effect with atomics and a
3596cd6a6acSopenharmony_ci	 * mutex */
3606cd6a6acSopenharmony_ci#ifdef __ATOMIC_RELAXED
3616cd6a6acSopenharmony_ci	regex_compiled =
3626cd6a6acSopenharmony_ci		__atomic_load_n(&spec->regex_compiled, __ATOMIC_ACQUIRE);
3636cd6a6acSopenharmony_ci#else
3646cd6a6acSopenharmony_ci	/* GCC <4.7 */
3656cd6a6acSopenharmony_ci	__sync_synchronize();
3666cd6a6acSopenharmony_ci	regex_compiled = spec->regex_compiled;
3676cd6a6acSopenharmony_ci#endif
3686cd6a6acSopenharmony_ci	if (regex_compiled) {
3696cd6a6acSopenharmony_ci		return 0; /* already done */
3706cd6a6acSopenharmony_ci	}
3716cd6a6acSopenharmony_ci
3726cd6a6acSopenharmony_ci	__pthread_mutex_lock(&spec->regex_lock);
3736cd6a6acSopenharmony_ci	/* Check if another thread compiled the regex while we waited
3746cd6a6acSopenharmony_ci	 * on the mutex */
3756cd6a6acSopenharmony_ci#ifdef __ATOMIC_RELAXED
3766cd6a6acSopenharmony_ci	regex_compiled =
3776cd6a6acSopenharmony_ci		__atomic_load_n(&spec->regex_compiled, __ATOMIC_ACQUIRE);
3786cd6a6acSopenharmony_ci#else
3796cd6a6acSopenharmony_ci	/* GCC <4.7 */
3806cd6a6acSopenharmony_ci	__sync_synchronize();
3816cd6a6acSopenharmony_ci	regex_compiled = spec->regex_compiled;
3826cd6a6acSopenharmony_ci#endif
3836cd6a6acSopenharmony_ci	if (regex_compiled) {
3846cd6a6acSopenharmony_ci		__pthread_mutex_unlock(&spec->regex_lock);
3856cd6a6acSopenharmony_ci		return 0;
3866cd6a6acSopenharmony_ci	}
3876cd6a6acSopenharmony_ci
3886cd6a6acSopenharmony_ci	reg_buf = spec->regex_str;
3896cd6a6acSopenharmony_ci	/* Anchor the regular expression. */
3906cd6a6acSopenharmony_ci	len = strlen(reg_buf);
3916cd6a6acSopenharmony_ci	cp = anchored_regex = malloc(len + 3);
3926cd6a6acSopenharmony_ci	if (!anchored_regex) {
3936cd6a6acSopenharmony_ci		if (errbuf)
3946cd6a6acSopenharmony_ci			*errbuf = "out of memory";
3956cd6a6acSopenharmony_ci		__pthread_mutex_unlock(&spec->regex_lock);
3966cd6a6acSopenharmony_ci		return -1;
3976cd6a6acSopenharmony_ci	}
3986cd6a6acSopenharmony_ci
3996cd6a6acSopenharmony_ci	/* Create ^...$ regexp.  */
4006cd6a6acSopenharmony_ci	*cp++ = '^';
4016cd6a6acSopenharmony_ci	memcpy(cp, reg_buf, len);
4026cd6a6acSopenharmony_ci	cp += len;
4036cd6a6acSopenharmony_ci	*cp++ = '$';
4046cd6a6acSopenharmony_ci	*cp = '\0';
4056cd6a6acSopenharmony_ci
4066cd6a6acSopenharmony_ci	/* Compile the regular expression. */
4076cd6a6acSopenharmony_ci	rc = regex_prepare_data(&spec->regex, anchored_regex, &error_data);
4086cd6a6acSopenharmony_ci	free(anchored_regex);
4096cd6a6acSopenharmony_ci	if (rc < 0) {
4106cd6a6acSopenharmony_ci		if (errbuf) {
4116cd6a6acSopenharmony_ci			regex_format_error(&error_data,
4126cd6a6acSopenharmony_ci					regex_error_format_buffer,
4136cd6a6acSopenharmony_ci					sizeof(regex_error_format_buffer));
4146cd6a6acSopenharmony_ci			*errbuf = &regex_error_format_buffer[0];
4156cd6a6acSopenharmony_ci		}
4166cd6a6acSopenharmony_ci		__pthread_mutex_unlock(&spec->regex_lock);
4176cd6a6acSopenharmony_ci		return -1;
4186cd6a6acSopenharmony_ci	}
4196cd6a6acSopenharmony_ci
4206cd6a6acSopenharmony_ci	/* Done. */
4216cd6a6acSopenharmony_ci#ifdef __ATOMIC_RELAXED
4226cd6a6acSopenharmony_ci	__atomic_store_n(&spec->regex_compiled, true, __ATOMIC_RELEASE);
4236cd6a6acSopenharmony_ci#else
4246cd6a6acSopenharmony_ci	/* GCC <4.7 */
4256cd6a6acSopenharmony_ci	spec->regex_compiled = true;
4266cd6a6acSopenharmony_ci	__sync_synchronize();
4276cd6a6acSopenharmony_ci#endif
4286cd6a6acSopenharmony_ci	__pthread_mutex_unlock(&spec->regex_lock);
4296cd6a6acSopenharmony_ci	return 0;
4306cd6a6acSopenharmony_ci}
4316cd6a6acSopenharmony_ci
4326cd6a6acSopenharmony_ci/* This service is used by label_file.c process_file() and
4336cd6a6acSopenharmony_ci * utils/sefcontext_compile.c */
4346cd6a6acSopenharmony_cistatic inline int process_line(struct selabel_handle *rec,
4356cd6a6acSopenharmony_ci			const char *path, const char *prefix,
4366cd6a6acSopenharmony_ci			char *line_buf, unsigned lineno)
4376cd6a6acSopenharmony_ci{
4386cd6a6acSopenharmony_ci	int items, len, rc;
4396cd6a6acSopenharmony_ci	char *regex = NULL, *type = NULL, *context = NULL;
4406cd6a6acSopenharmony_ci	struct saved_data *data = (struct saved_data *)rec->data;
4416cd6a6acSopenharmony_ci	struct spec *spec_arr;
4426cd6a6acSopenharmony_ci	unsigned int nspec = data->nspec;
4436cd6a6acSopenharmony_ci	const char *errbuf = NULL;
4446cd6a6acSopenharmony_ci
4456cd6a6acSopenharmony_ci	items = read_spec_entries(line_buf, &errbuf, 3, &regex, &type, &context);
4466cd6a6acSopenharmony_ci	if (items < 0) {
4476cd6a6acSopenharmony_ci		if (errbuf) {
4486cd6a6acSopenharmony_ci			selinux_log(SELINUX_ERROR,
4496cd6a6acSopenharmony_ci				    "%s:  line %u error due to: %s\n", path,
4506cd6a6acSopenharmony_ci				    lineno, errbuf);
4516cd6a6acSopenharmony_ci		} else {
4526cd6a6acSopenharmony_ci			selinux_log(SELINUX_ERROR,
4536cd6a6acSopenharmony_ci				    "%s:  line %u error due to: %m\n", path,
4546cd6a6acSopenharmony_ci				    lineno);
4556cd6a6acSopenharmony_ci		}
4566cd6a6acSopenharmony_ci		return -1;
4576cd6a6acSopenharmony_ci	}
4586cd6a6acSopenharmony_ci
4596cd6a6acSopenharmony_ci	if (items == 0)
4606cd6a6acSopenharmony_ci		return items;
4616cd6a6acSopenharmony_ci
4626cd6a6acSopenharmony_ci	if (items < 2) {
4636cd6a6acSopenharmony_ci		COMPAT_LOG(SELINUX_ERROR,
4646cd6a6acSopenharmony_ci			    "%s:  line %u is missing fields\n", path,
4656cd6a6acSopenharmony_ci			    lineno);
4666cd6a6acSopenharmony_ci		if (items == 1)
4676cd6a6acSopenharmony_ci			free(regex);
4686cd6a6acSopenharmony_ci		errno = EINVAL;
4696cd6a6acSopenharmony_ci		return -1;
4706cd6a6acSopenharmony_ci	} else if (items == 2) {
4716cd6a6acSopenharmony_ci		/* The type field is optional. */
4726cd6a6acSopenharmony_ci		context = type;
4736cd6a6acSopenharmony_ci		type = 0;
4746cd6a6acSopenharmony_ci	}
4756cd6a6acSopenharmony_ci
4766cd6a6acSopenharmony_ci	len = get_stem_from_spec(regex);
4776cd6a6acSopenharmony_ci	if (len && prefix && strncmp(prefix, regex, len)) {
4786cd6a6acSopenharmony_ci		/* Stem of regex does not match requested prefix, discard. */
4796cd6a6acSopenharmony_ci		free(regex);
4806cd6a6acSopenharmony_ci		free(type);
4816cd6a6acSopenharmony_ci		free(context);
4826cd6a6acSopenharmony_ci		return 0;
4836cd6a6acSopenharmony_ci	}
4846cd6a6acSopenharmony_ci
4856cd6a6acSopenharmony_ci	rc = grow_specs(data);
4866cd6a6acSopenharmony_ci	if (rc)
4876cd6a6acSopenharmony_ci		return rc;
4886cd6a6acSopenharmony_ci
4896cd6a6acSopenharmony_ci	spec_arr = data->spec_arr;
4906cd6a6acSopenharmony_ci
4916cd6a6acSopenharmony_ci	/* process and store the specification in spec. */
4926cd6a6acSopenharmony_ci	spec_arr[nspec].stem_id = find_stem_from_spec(data, regex);
4936cd6a6acSopenharmony_ci	spec_arr[nspec].regex_str = regex;
4946cd6a6acSopenharmony_ci	__pthread_mutex_init(&spec_arr[nspec].regex_lock, NULL);
4956cd6a6acSopenharmony_ci	spec_arr[nspec].regex_compiled = false;
4966cd6a6acSopenharmony_ci
4976cd6a6acSopenharmony_ci	spec_arr[nspec].type_str = type;
4986cd6a6acSopenharmony_ci	spec_arr[nspec].mode = 0;
4996cd6a6acSopenharmony_ci
5006cd6a6acSopenharmony_ci	spec_arr[nspec].lr.ctx_raw = context;
5016cd6a6acSopenharmony_ci	spec_arr[nspec].lr.lineno = lineno;
5026cd6a6acSopenharmony_ci
5036cd6a6acSopenharmony_ci	/*
5046cd6a6acSopenharmony_ci	 * bump data->nspecs to cause closef() to cover it in its free
5056cd6a6acSopenharmony_ci	 * but do not bump nspec since it's used below.
5066cd6a6acSopenharmony_ci	 */
5076cd6a6acSopenharmony_ci	data->nspec++;
5086cd6a6acSopenharmony_ci
5096cd6a6acSopenharmony_ci	if (rec->validating
5106cd6a6acSopenharmony_ci			&& compile_regex(&spec_arr[nspec], &errbuf)) {
5116cd6a6acSopenharmony_ci		COMPAT_LOG(SELINUX_ERROR,
5126cd6a6acSopenharmony_ci			   "%s:  line %u has invalid regex %s:  %s\n",
5136cd6a6acSopenharmony_ci			   path, lineno, regex, errbuf);
5146cd6a6acSopenharmony_ci		errno = EINVAL;
5156cd6a6acSopenharmony_ci		return -1;
5166cd6a6acSopenharmony_ci	}
5176cd6a6acSopenharmony_ci
5186cd6a6acSopenharmony_ci	if (type) {
5196cd6a6acSopenharmony_ci		mode_t mode = string_to_mode(type);
5206cd6a6acSopenharmony_ci
5216cd6a6acSopenharmony_ci		if (mode == (mode_t)-1) {
5226cd6a6acSopenharmony_ci			COMPAT_LOG(SELINUX_ERROR,
5236cd6a6acSopenharmony_ci				   "%s:  line %u has invalid file type %s\n",
5246cd6a6acSopenharmony_ci				   path, lineno, type);
5256cd6a6acSopenharmony_ci			errno = EINVAL;
5266cd6a6acSopenharmony_ci			return -1;
5276cd6a6acSopenharmony_ci		}
5286cd6a6acSopenharmony_ci		spec_arr[nspec].mode = mode;
5296cd6a6acSopenharmony_ci	}
5306cd6a6acSopenharmony_ci
5316cd6a6acSopenharmony_ci	/* Determine if specification has
5326cd6a6acSopenharmony_ci	 * any meta characters in the RE */
5336cd6a6acSopenharmony_ci	spec_hasMetaChars(&spec_arr[nspec]);
5346cd6a6acSopenharmony_ci
5356cd6a6acSopenharmony_ci	if (strcmp(context, "<<none>>") && rec->validating)
5366cd6a6acSopenharmony_ci		return compat_validate(rec, &spec_arr[nspec].lr, path, lineno);
5376cd6a6acSopenharmony_ci
5386cd6a6acSopenharmony_ci	return 0;
5396cd6a6acSopenharmony_ci}
5406cd6a6acSopenharmony_ci
5416cd6a6acSopenharmony_ci#endif /* _SELABEL_FILE_H_ */
542