16cd6a6acSopenharmony_ci/*
26cd6a6acSopenharmony_ci * Property Service contexts backend for labeling Android
36cd6a6acSopenharmony_ci * property keys
46cd6a6acSopenharmony_ci */
56cd6a6acSopenharmony_ci
66cd6a6acSopenharmony_ci#include <stdarg.h>
76cd6a6acSopenharmony_ci#include <string.h>
86cd6a6acSopenharmony_ci#include <ctype.h>
96cd6a6acSopenharmony_ci#include <errno.h>
106cd6a6acSopenharmony_ci#include <limits.h>
116cd6a6acSopenharmony_ci#include <sys/types.h>
126cd6a6acSopenharmony_ci#include <sys/stat.h>
136cd6a6acSopenharmony_ci#include "callbacks.h"
146cd6a6acSopenharmony_ci#include "label_internal.h"
156cd6a6acSopenharmony_ci
166cd6a6acSopenharmony_ci/* A property security context specification. */
176cd6a6acSopenharmony_citypedef struct spec {
186cd6a6acSopenharmony_ci	struct selabel_lookup_rec lr;	/* holds contexts for lookup result */
196cd6a6acSopenharmony_ci	char *property_key;		/* property key string */
206cd6a6acSopenharmony_ci} spec_t;
216cd6a6acSopenharmony_ci
226cd6a6acSopenharmony_ci/* Our stored configuration */
236cd6a6acSopenharmony_cistruct saved_data {
246cd6a6acSopenharmony_ci	/*
256cd6a6acSopenharmony_ci	 * The array of specifications is sorted for longest
266cd6a6acSopenharmony_ci	 * prefix match
276cd6a6acSopenharmony_ci	 */
286cd6a6acSopenharmony_ci	spec_t *spec_arr;
296cd6a6acSopenharmony_ci	unsigned int nspec;	/* total number of specifications */
306cd6a6acSopenharmony_ci};
316cd6a6acSopenharmony_ci
326cd6a6acSopenharmony_cistatic int cmp(const void *A, const void *B)
336cd6a6acSopenharmony_ci{
346cd6a6acSopenharmony_ci	const struct spec *sp1 = A, *sp2 = B;
356cd6a6acSopenharmony_ci
366cd6a6acSopenharmony_ci	if (strncmp(sp1->property_key, "*", 1) == 0)
376cd6a6acSopenharmony_ci		return 1;
386cd6a6acSopenharmony_ci	if (strncmp(sp2->property_key, "*", 1) == 0)
396cd6a6acSopenharmony_ci		return -1;
406cd6a6acSopenharmony_ci
416cd6a6acSopenharmony_ci	size_t L1 = strlen(sp1->property_key);
426cd6a6acSopenharmony_ci	size_t L2 = strlen(sp2->property_key);
436cd6a6acSopenharmony_ci
446cd6a6acSopenharmony_ci	return (L1 < L2) - (L1 > L2);
456cd6a6acSopenharmony_ci}
466cd6a6acSopenharmony_ci
476cd6a6acSopenharmony_ci/*
486cd6a6acSopenharmony_ci * Warn about duplicate specifications.
496cd6a6acSopenharmony_ci */
506cd6a6acSopenharmony_cistatic int nodups_specs(struct saved_data *data, const char *path)
516cd6a6acSopenharmony_ci{
526cd6a6acSopenharmony_ci	int rc = 0;
536cd6a6acSopenharmony_ci	unsigned int ii, jj;
546cd6a6acSopenharmony_ci	struct spec *curr_spec, *spec_arr = data->spec_arr;
556cd6a6acSopenharmony_ci
566cd6a6acSopenharmony_ci	for (ii = 0; ii < data->nspec; ii++) {
576cd6a6acSopenharmony_ci		curr_spec = &spec_arr[ii];
586cd6a6acSopenharmony_ci		for (jj = ii + 1; jj < data->nspec; jj++) {
596cd6a6acSopenharmony_ci			if (!strcmp(spec_arr[jj].property_key,
606cd6a6acSopenharmony_ci					    curr_spec->property_key)) {
616cd6a6acSopenharmony_ci				rc = -1;
626cd6a6acSopenharmony_ci				errno = EINVAL;
636cd6a6acSopenharmony_ci				if (strcmp(spec_arr[jj].lr.ctx_raw,
646cd6a6acSopenharmony_ci						    curr_spec->lr.ctx_raw)) {
656cd6a6acSopenharmony_ci					selinux_log
666cd6a6acSopenharmony_ci						(SELINUX_ERROR,
676cd6a6acSopenharmony_ci						 "%s: Multiple different specifications for %s  (%s and %s).\n",
686cd6a6acSopenharmony_ci						 path, curr_spec->property_key,
696cd6a6acSopenharmony_ci						 spec_arr[jj].lr.ctx_raw,
706cd6a6acSopenharmony_ci						 curr_spec->lr.ctx_raw);
716cd6a6acSopenharmony_ci				} else {
726cd6a6acSopenharmony_ci					selinux_log
736cd6a6acSopenharmony_ci						(SELINUX_ERROR,
746cd6a6acSopenharmony_ci						 "%s: Multiple same specifications for %s.\n",
756cd6a6acSopenharmony_ci						 path, curr_spec->property_key);
766cd6a6acSopenharmony_ci				}
776cd6a6acSopenharmony_ci			}
786cd6a6acSopenharmony_ci		}
796cd6a6acSopenharmony_ci	}
806cd6a6acSopenharmony_ci	return rc;
816cd6a6acSopenharmony_ci}
826cd6a6acSopenharmony_ci
836cd6a6acSopenharmony_cistatic int process_line(struct selabel_handle *rec,
846cd6a6acSopenharmony_ci			const char *path, char *line_buf,
856cd6a6acSopenharmony_ci			int pass, unsigned lineno)
866cd6a6acSopenharmony_ci{
876cd6a6acSopenharmony_ci	int items;
886cd6a6acSopenharmony_ci	char *prop = NULL, *context = NULL;
896cd6a6acSopenharmony_ci	struct saved_data *data = (struct saved_data *)rec->data;
906cd6a6acSopenharmony_ci	spec_t *spec_arr = data->spec_arr;
916cd6a6acSopenharmony_ci	unsigned int nspec = data->nspec;
926cd6a6acSopenharmony_ci	const char *errbuf = NULL;
936cd6a6acSopenharmony_ci
946cd6a6acSopenharmony_ci	items = read_spec_entries(line_buf, &errbuf, 2, &prop, &context);
956cd6a6acSopenharmony_ci	if (items < 0) {
966cd6a6acSopenharmony_ci		if (errbuf) {
976cd6a6acSopenharmony_ci			selinux_log(SELINUX_ERROR,
986cd6a6acSopenharmony_ci				    "%s:  line %u error due to: %s\n", path,
996cd6a6acSopenharmony_ci				    lineno, errbuf);
1006cd6a6acSopenharmony_ci		} else {
1016cd6a6acSopenharmony_ci			selinux_log(SELINUX_ERROR,
1026cd6a6acSopenharmony_ci				    "%s:  line %u error due to: %m\n", path,
1036cd6a6acSopenharmony_ci				    lineno);
1046cd6a6acSopenharmony_ci		}
1056cd6a6acSopenharmony_ci		return -1;
1066cd6a6acSopenharmony_ci	}
1076cd6a6acSopenharmony_ci
1086cd6a6acSopenharmony_ci	if (items == 0)
1096cd6a6acSopenharmony_ci		return items;
1106cd6a6acSopenharmony_ci
1116cd6a6acSopenharmony_ci	if (items != 2) {
1126cd6a6acSopenharmony_ci		selinux_log(SELINUX_ERROR,
1136cd6a6acSopenharmony_ci			    "%s:  line %u is missing fields\n", path,
1146cd6a6acSopenharmony_ci			    lineno);
1156cd6a6acSopenharmony_ci		free(prop);
1166cd6a6acSopenharmony_ci		errno = EINVAL;
1176cd6a6acSopenharmony_ci		return -1;
1186cd6a6acSopenharmony_ci	}
1196cd6a6acSopenharmony_ci
1206cd6a6acSopenharmony_ci	if (pass == 0) {
1216cd6a6acSopenharmony_ci		free(prop);
1226cd6a6acSopenharmony_ci		free(context);
1236cd6a6acSopenharmony_ci	} else if (pass == 1) {
1246cd6a6acSopenharmony_ci		/* On the second pass, process and store the specification in spec. */
1256cd6a6acSopenharmony_ci		spec_arr[nspec].property_key = prop;
1266cd6a6acSopenharmony_ci		spec_arr[nspec].lr.ctx_raw = context;
1276cd6a6acSopenharmony_ci
1286cd6a6acSopenharmony_ci		if (rec->validating) {
1296cd6a6acSopenharmony_ci			if (selabel_validate(rec, &spec_arr[nspec].lr) < 0) {
1306cd6a6acSopenharmony_ci				selinux_log(SELINUX_ERROR,
1316cd6a6acSopenharmony_ci					    "%s:  line %u has invalid context %s\n",
1326cd6a6acSopenharmony_ci					    path, lineno, spec_arr[nspec].lr.ctx_raw);
1336cd6a6acSopenharmony_ci				errno = EINVAL;
1346cd6a6acSopenharmony_ci				return -1;
1356cd6a6acSopenharmony_ci			}
1366cd6a6acSopenharmony_ci		}
1376cd6a6acSopenharmony_ci	}
1386cd6a6acSopenharmony_ci
1396cd6a6acSopenharmony_ci	data->nspec = ++nspec;
1406cd6a6acSopenharmony_ci	return 0;
1416cd6a6acSopenharmony_ci}
1426cd6a6acSopenharmony_ci
1436cd6a6acSopenharmony_cistatic int init(struct selabel_handle *rec, const struct selinux_opt *opts,
1446cd6a6acSopenharmony_ci		unsigned n)
1456cd6a6acSopenharmony_ci{
1466cd6a6acSopenharmony_ci	struct saved_data *data = (struct saved_data *)rec->data;
1476cd6a6acSopenharmony_ci	const char *path = NULL;
1486cd6a6acSopenharmony_ci	FILE *fp;
1496cd6a6acSopenharmony_ci	char line_buf[BUFSIZ];
1506cd6a6acSopenharmony_ci	unsigned int lineno, maxnspec, pass;
1516cd6a6acSopenharmony_ci	int status = -1;
1526cd6a6acSopenharmony_ci	struct stat sb;
1536cd6a6acSopenharmony_ci
1546cd6a6acSopenharmony_ci	/* Process arguments */
1556cd6a6acSopenharmony_ci	while (n--)
1566cd6a6acSopenharmony_ci		switch (opts[n].type) {
1576cd6a6acSopenharmony_ci		case SELABEL_OPT_PATH:
1586cd6a6acSopenharmony_ci			path = opts[n].value;
1596cd6a6acSopenharmony_ci			break;
1606cd6a6acSopenharmony_ci		}
1616cd6a6acSopenharmony_ci
1626cd6a6acSopenharmony_ci	if (!path)
1636cd6a6acSopenharmony_ci		return -1;
1646cd6a6acSopenharmony_ci
1656cd6a6acSopenharmony_ci	/* Open the specification file. */
1666cd6a6acSopenharmony_ci	if ((fp = fopen(path, "re")) == NULL)
1676cd6a6acSopenharmony_ci		return -1;
1686cd6a6acSopenharmony_ci
1696cd6a6acSopenharmony_ci	if (fstat(fileno(fp), &sb) < 0)
1706cd6a6acSopenharmony_ci		goto finish;
1716cd6a6acSopenharmony_ci	errno = EINVAL;
1726cd6a6acSopenharmony_ci	if (!S_ISREG(sb.st_mode))
1736cd6a6acSopenharmony_ci		goto finish;
1746cd6a6acSopenharmony_ci
1756cd6a6acSopenharmony_ci	/*
1766cd6a6acSopenharmony_ci	 * Two passes of the specification file. First is to get the size.
1776cd6a6acSopenharmony_ci	 * After the first pass, the spec array is malloced to the appropriate
1786cd6a6acSopenharmony_ci	 * size. Second pass is to populate the spec array and check for
1796cd6a6acSopenharmony_ci	 * dups.
1806cd6a6acSopenharmony_ci	 */
1816cd6a6acSopenharmony_ci	maxnspec = UINT_MAX / sizeof(spec_t);
1826cd6a6acSopenharmony_ci	for (pass = 0; pass < 2; pass++) {
1836cd6a6acSopenharmony_ci		data->nspec = 0;
1846cd6a6acSopenharmony_ci		lineno = 0;
1856cd6a6acSopenharmony_ci
1866cd6a6acSopenharmony_ci		while (fgets(line_buf, sizeof(line_buf) - 1, fp)
1876cd6a6acSopenharmony_ci		       && data->nspec < maxnspec) {
1886cd6a6acSopenharmony_ci			if (process_line(rec, path, line_buf, pass, ++lineno)
1896cd6a6acSopenharmony_ci									  != 0)
1906cd6a6acSopenharmony_ci				goto finish;
1916cd6a6acSopenharmony_ci		}
1926cd6a6acSopenharmony_ci
1936cd6a6acSopenharmony_ci		if (pass == 1) {
1946cd6a6acSopenharmony_ci			status = nodups_specs(data, path);
1956cd6a6acSopenharmony_ci
1966cd6a6acSopenharmony_ci			if (status)
1976cd6a6acSopenharmony_ci				goto finish;
1986cd6a6acSopenharmony_ci		}
1996cd6a6acSopenharmony_ci
2006cd6a6acSopenharmony_ci		if (pass == 0) {
2016cd6a6acSopenharmony_ci			if (data->nspec == 0) {
2026cd6a6acSopenharmony_ci				status = 0;
2036cd6a6acSopenharmony_ci				goto finish;
2046cd6a6acSopenharmony_ci			}
2056cd6a6acSopenharmony_ci
2066cd6a6acSopenharmony_ci			if (NULL == (data->spec_arr =
2076cd6a6acSopenharmony_ci				     calloc(data->nspec, sizeof(spec_t))))
2086cd6a6acSopenharmony_ci				goto finish;
2096cd6a6acSopenharmony_ci
2106cd6a6acSopenharmony_ci			maxnspec = data->nspec;
2116cd6a6acSopenharmony_ci			rewind(fp);
2126cd6a6acSopenharmony_ci		}
2136cd6a6acSopenharmony_ci	}
2146cd6a6acSopenharmony_ci
2156cd6a6acSopenharmony_ci	qsort(data->spec_arr, data->nspec, sizeof(struct spec), cmp);
2166cd6a6acSopenharmony_ci
2176cd6a6acSopenharmony_ci	status = digest_add_specfile(rec->digest, fp, NULL, sb.st_size, path);
2186cd6a6acSopenharmony_ci	if (status)
2196cd6a6acSopenharmony_ci		goto finish;
2206cd6a6acSopenharmony_ci
2216cd6a6acSopenharmony_ci	digest_gen_hash(rec->digest);
2226cd6a6acSopenharmony_ci
2236cd6a6acSopenharmony_cifinish:
2246cd6a6acSopenharmony_ci	fclose(fp);
2256cd6a6acSopenharmony_ci	return status;
2266cd6a6acSopenharmony_ci}
2276cd6a6acSopenharmony_ci
2286cd6a6acSopenharmony_ci/*
2296cd6a6acSopenharmony_ci * Backend interface routines
2306cd6a6acSopenharmony_ci */
2316cd6a6acSopenharmony_cistatic void closef(struct selabel_handle *rec)
2326cd6a6acSopenharmony_ci{
2336cd6a6acSopenharmony_ci	struct saved_data *data = (struct saved_data *)rec->data;
2346cd6a6acSopenharmony_ci	struct spec *spec;
2356cd6a6acSopenharmony_ci	unsigned int i;
2366cd6a6acSopenharmony_ci
2376cd6a6acSopenharmony_ci	for (i = 0; i < data->nspec; i++) {
2386cd6a6acSopenharmony_ci		spec = &data->spec_arr[i];
2396cd6a6acSopenharmony_ci		free(spec->property_key);
2406cd6a6acSopenharmony_ci		free(spec->lr.ctx_raw);
2416cd6a6acSopenharmony_ci		free(spec->lr.ctx_trans);
2426cd6a6acSopenharmony_ci	}
2436cd6a6acSopenharmony_ci
2446cd6a6acSopenharmony_ci	if (data->spec_arr)
2456cd6a6acSopenharmony_ci		free(data->spec_arr);
2466cd6a6acSopenharmony_ci
2476cd6a6acSopenharmony_ci	free(data);
2486cd6a6acSopenharmony_ci}
2496cd6a6acSopenharmony_ci
2506cd6a6acSopenharmony_cistatic struct selabel_lookup_rec *property_lookup(struct selabel_handle *rec,
2516cd6a6acSopenharmony_ci					 const char *key,
2526cd6a6acSopenharmony_ci					 int __attribute__((unused)) type)
2536cd6a6acSopenharmony_ci{
2546cd6a6acSopenharmony_ci	struct saved_data *data = (struct saved_data *)rec->data;
2556cd6a6acSopenharmony_ci	spec_t *spec_arr = data->spec_arr;
2566cd6a6acSopenharmony_ci	unsigned int i;
2576cd6a6acSopenharmony_ci	struct selabel_lookup_rec *ret = NULL;
2586cd6a6acSopenharmony_ci
2596cd6a6acSopenharmony_ci	if (!data->nspec) {
2606cd6a6acSopenharmony_ci		errno = ENOENT;
2616cd6a6acSopenharmony_ci		goto finish;
2626cd6a6acSopenharmony_ci	}
2636cd6a6acSopenharmony_ci
2646cd6a6acSopenharmony_ci	for (i = 0; i < data->nspec; i++) {
2656cd6a6acSopenharmony_ci		if (strncmp(spec_arr[i].property_key, key,
2666cd6a6acSopenharmony_ci			    strlen(spec_arr[i].property_key)) == 0) {
2676cd6a6acSopenharmony_ci			break;
2686cd6a6acSopenharmony_ci		}
2696cd6a6acSopenharmony_ci		if (strncmp(spec_arr[i].property_key, "*", 1) == 0)
2706cd6a6acSopenharmony_ci			break;
2716cd6a6acSopenharmony_ci	}
2726cd6a6acSopenharmony_ci
2736cd6a6acSopenharmony_ci	if (i >= data->nspec) {
2746cd6a6acSopenharmony_ci		/* No matching specification. */
2756cd6a6acSopenharmony_ci		errno = ENOENT;
2766cd6a6acSopenharmony_ci		goto finish;
2776cd6a6acSopenharmony_ci	}
2786cd6a6acSopenharmony_ci
2796cd6a6acSopenharmony_ci	ret = &spec_arr[i].lr;
2806cd6a6acSopenharmony_ci
2816cd6a6acSopenharmony_cifinish:
2826cd6a6acSopenharmony_ci	return ret;
2836cd6a6acSopenharmony_ci}
2846cd6a6acSopenharmony_ci
2856cd6a6acSopenharmony_cistatic struct selabel_lookup_rec *service_lookup(struct selabel_handle *rec,
2866cd6a6acSopenharmony_ci		const char *key, int __attribute__((unused)) type)
2876cd6a6acSopenharmony_ci{
2886cd6a6acSopenharmony_ci	struct saved_data *data = (struct saved_data *)rec->data;
2896cd6a6acSopenharmony_ci	spec_t *spec_arr = data->spec_arr;
2906cd6a6acSopenharmony_ci	unsigned int i;
2916cd6a6acSopenharmony_ci	struct selabel_lookup_rec *ret = NULL;
2926cd6a6acSopenharmony_ci
2936cd6a6acSopenharmony_ci	if (!data->nspec) {
2946cd6a6acSopenharmony_ci		errno = ENOENT;
2956cd6a6acSopenharmony_ci		goto finish;
2966cd6a6acSopenharmony_ci	}
2976cd6a6acSopenharmony_ci
2986cd6a6acSopenharmony_ci	for (i = 0; i < data->nspec; i++) {
2996cd6a6acSopenharmony_ci		if (strcmp(spec_arr[i].property_key, key) == 0)
3006cd6a6acSopenharmony_ci			break;
3016cd6a6acSopenharmony_ci		if (strcmp(spec_arr[i].property_key, "*") == 0)
3026cd6a6acSopenharmony_ci			break;
3036cd6a6acSopenharmony_ci	}
3046cd6a6acSopenharmony_ci
3056cd6a6acSopenharmony_ci	if (i >= data->nspec) {
3066cd6a6acSopenharmony_ci		/* No matching specification. */
3076cd6a6acSopenharmony_ci		errno = ENOENT;
3086cd6a6acSopenharmony_ci		goto finish;
3096cd6a6acSopenharmony_ci	}
3106cd6a6acSopenharmony_ci
3116cd6a6acSopenharmony_ci	ret = &spec_arr[i].lr;
3126cd6a6acSopenharmony_ci
3136cd6a6acSopenharmony_cifinish:
3146cd6a6acSopenharmony_ci	return ret;
3156cd6a6acSopenharmony_ci}
3166cd6a6acSopenharmony_ci
3176cd6a6acSopenharmony_cistatic void stats(struct selabel_handle __attribute__((unused)) *rec)
3186cd6a6acSopenharmony_ci{
3196cd6a6acSopenharmony_ci	selinux_log(SELINUX_WARNING, "'stats' functionality not implemented.\n");
3206cd6a6acSopenharmony_ci}
3216cd6a6acSopenharmony_ci
3226cd6a6acSopenharmony_ciint selabel_property_init(struct selabel_handle *rec,
3236cd6a6acSopenharmony_ci			  const struct selinux_opt *opts,
3246cd6a6acSopenharmony_ci			  unsigned nopts)
3256cd6a6acSopenharmony_ci{
3266cd6a6acSopenharmony_ci	struct saved_data *data;
3276cd6a6acSopenharmony_ci
3286cd6a6acSopenharmony_ci	data = (struct saved_data *)calloc(1, sizeof(*data));
3296cd6a6acSopenharmony_ci	if (!data)
3306cd6a6acSopenharmony_ci		return -1;
3316cd6a6acSopenharmony_ci
3326cd6a6acSopenharmony_ci	rec->data = data;
3336cd6a6acSopenharmony_ci	rec->func_close = &closef;
3346cd6a6acSopenharmony_ci	rec->func_stats = &stats;
3356cd6a6acSopenharmony_ci	rec->func_lookup = &property_lookup;
3366cd6a6acSopenharmony_ci
3376cd6a6acSopenharmony_ci	return init(rec, opts, nopts);
3386cd6a6acSopenharmony_ci}
3396cd6a6acSopenharmony_ci
3406cd6a6acSopenharmony_ciint selabel_service_init(struct selabel_handle *rec,
3416cd6a6acSopenharmony_ci		const struct selinux_opt *opts, unsigned nopts)
3426cd6a6acSopenharmony_ci{
3436cd6a6acSopenharmony_ci	struct saved_data *data;
3446cd6a6acSopenharmony_ci
3456cd6a6acSopenharmony_ci	data = (struct saved_data *)calloc(1, sizeof(*data));
3466cd6a6acSopenharmony_ci	if (!data)
3476cd6a6acSopenharmony_ci		return -1;
3486cd6a6acSopenharmony_ci
3496cd6a6acSopenharmony_ci	rec->data = data;
3506cd6a6acSopenharmony_ci	rec->func_close = &closef;
3516cd6a6acSopenharmony_ci	rec->func_stats = &stats;
3526cd6a6acSopenharmony_ci	rec->func_lookup = &service_lookup;
3536cd6a6acSopenharmony_ci
3546cd6a6acSopenharmony_ci	return init(rec, opts, nopts);
3556cd6a6acSopenharmony_ci}
356