16cd6a6acSopenharmony_ci/*
26cd6a6acSopenharmony_ci * Media contexts backend for X contexts
36cd6a6acSopenharmony_ci *
46cd6a6acSopenharmony_ci * Author : Eamon Walsh <ewalsh@tycho.nsa.gov>
56cd6a6acSopenharmony_ci */
66cd6a6acSopenharmony_ci
76cd6a6acSopenharmony_ci#include <sys/stat.h>
86cd6a6acSopenharmony_ci#include <string.h>
96cd6a6acSopenharmony_ci#include <stdio.h>
106cd6a6acSopenharmony_ci#include <stdio_ext.h>
116cd6a6acSopenharmony_ci#include <ctype.h>
126cd6a6acSopenharmony_ci#include <errno.h>
136cd6a6acSopenharmony_ci#include <limits.h>
146cd6a6acSopenharmony_ci#include <fnmatch.h>
156cd6a6acSopenharmony_ci#include "callbacks.h"
166cd6a6acSopenharmony_ci#include "label_internal.h"
176cd6a6acSopenharmony_ci
186cd6a6acSopenharmony_ci/*
196cd6a6acSopenharmony_ci * Internals
206cd6a6acSopenharmony_ci */
216cd6a6acSopenharmony_ci
226cd6a6acSopenharmony_ci/* A context specification. */
236cd6a6acSopenharmony_citypedef struct spec {
246cd6a6acSopenharmony_ci	struct selabel_lookup_rec lr;	/* holds contexts for lookup result */
256cd6a6acSopenharmony_ci	char *key;		/* key string */
266cd6a6acSopenharmony_ci	int type;		/* type of record (prop, ext, client) */
276cd6a6acSopenharmony_ci	int matches;		/* number of matches made during operation */
286cd6a6acSopenharmony_ci} spec_t;
296cd6a6acSopenharmony_ci
306cd6a6acSopenharmony_cistruct saved_data {
316cd6a6acSopenharmony_ci	unsigned int nspec;
326cd6a6acSopenharmony_ci	spec_t *spec_arr;
336cd6a6acSopenharmony_ci};
346cd6a6acSopenharmony_ci
356cd6a6acSopenharmony_cistatic int process_line(const char *path, char *line_buf, int pass,
366cd6a6acSopenharmony_ci			unsigned lineno, struct selabel_handle *rec)
376cd6a6acSopenharmony_ci{
386cd6a6acSopenharmony_ci	struct saved_data *data = (struct saved_data *)rec->data;
396cd6a6acSopenharmony_ci	int items;
406cd6a6acSopenharmony_ci	char *buf_p;
416cd6a6acSopenharmony_ci	char *type, *key, *context;
426cd6a6acSopenharmony_ci
436cd6a6acSopenharmony_ci	buf_p = line_buf;
446cd6a6acSopenharmony_ci	while (isspace(*buf_p))
456cd6a6acSopenharmony_ci		buf_p++;
466cd6a6acSopenharmony_ci	/* Skip comment lines and empty lines. */
476cd6a6acSopenharmony_ci	if (*buf_p == '#' || *buf_p == 0)
486cd6a6acSopenharmony_ci		return 0;
496cd6a6acSopenharmony_ci	items = sscanf(line_buf, "%ms %ms %ms ", &type, &key, &context);
506cd6a6acSopenharmony_ci	if (items < 3) {
516cd6a6acSopenharmony_ci		selinux_log(SELINUX_WARNING,
526cd6a6acSopenharmony_ci			    "%s:  line %u is missing fields, skipping\n", path,
536cd6a6acSopenharmony_ci			    lineno);
546cd6a6acSopenharmony_ci		if (items > 0)
556cd6a6acSopenharmony_ci			free(type);
566cd6a6acSopenharmony_ci		if (items > 1)
576cd6a6acSopenharmony_ci			free(key);
586cd6a6acSopenharmony_ci		return 0;
596cd6a6acSopenharmony_ci	}
606cd6a6acSopenharmony_ci
616cd6a6acSopenharmony_ci	if (pass == 1) {
626cd6a6acSopenharmony_ci		/* Convert the type string to a mode format */
636cd6a6acSopenharmony_ci		if (!strcmp(type, "property"))
646cd6a6acSopenharmony_ci			data->spec_arr[data->nspec].type = SELABEL_X_PROP;
656cd6a6acSopenharmony_ci		else if (!strcmp(type, "extension"))
666cd6a6acSopenharmony_ci			data->spec_arr[data->nspec].type = SELABEL_X_EXT;
676cd6a6acSopenharmony_ci		else if (!strcmp(type, "client"))
686cd6a6acSopenharmony_ci			data->spec_arr[data->nspec].type = SELABEL_X_CLIENT;
696cd6a6acSopenharmony_ci		else if (!strcmp(type, "event"))
706cd6a6acSopenharmony_ci			data->spec_arr[data->nspec].type = SELABEL_X_EVENT;
716cd6a6acSopenharmony_ci		else if (!strcmp(type, "selection"))
726cd6a6acSopenharmony_ci			data->spec_arr[data->nspec].type = SELABEL_X_SELN;
736cd6a6acSopenharmony_ci		else if (!strcmp(type, "poly_property"))
746cd6a6acSopenharmony_ci			data->spec_arr[data->nspec].type = SELABEL_X_POLYPROP;
756cd6a6acSopenharmony_ci		else if (!strcmp(type, "poly_selection"))
766cd6a6acSopenharmony_ci			data->spec_arr[data->nspec].type = SELABEL_X_POLYSELN;
776cd6a6acSopenharmony_ci		else {
786cd6a6acSopenharmony_ci			selinux_log(SELINUX_WARNING,
796cd6a6acSopenharmony_ci				    "%s:  line %u has invalid object type %s\n",
806cd6a6acSopenharmony_ci				    path, lineno, type);
816cd6a6acSopenharmony_ci			return 0;
826cd6a6acSopenharmony_ci		}
836cd6a6acSopenharmony_ci		data->spec_arr[data->nspec].key = key;
846cd6a6acSopenharmony_ci		data->spec_arr[data->nspec].lr.ctx_raw = context;
856cd6a6acSopenharmony_ci		free(type);
866cd6a6acSopenharmony_ci	}
876cd6a6acSopenharmony_ci
886cd6a6acSopenharmony_ci	data->nspec++;
896cd6a6acSopenharmony_ci	if (pass == 0) {
906cd6a6acSopenharmony_ci		free(type);
916cd6a6acSopenharmony_ci		free(key);
926cd6a6acSopenharmony_ci		free(context);
936cd6a6acSopenharmony_ci	}
946cd6a6acSopenharmony_ci	return 0;
956cd6a6acSopenharmony_ci}
966cd6a6acSopenharmony_ci
976cd6a6acSopenharmony_cistatic int init(struct selabel_handle *rec, const struct selinux_opt *opts,
986cd6a6acSopenharmony_ci		unsigned n)
996cd6a6acSopenharmony_ci{
1006cd6a6acSopenharmony_ci	FILE *fp;
1016cd6a6acSopenharmony_ci	struct saved_data *data = (struct saved_data *)rec->data;
1026cd6a6acSopenharmony_ci	const char *path = NULL;
1036cd6a6acSopenharmony_ci	char *line_buf = NULL;
1046cd6a6acSopenharmony_ci	size_t line_len = 0;
1056cd6a6acSopenharmony_ci	int status = -1;
1066cd6a6acSopenharmony_ci	unsigned int lineno, pass, maxnspec;
1076cd6a6acSopenharmony_ci	struct stat sb;
1086cd6a6acSopenharmony_ci
1096cd6a6acSopenharmony_ci	/* Process arguments */
1106cd6a6acSopenharmony_ci	while (n--)
1116cd6a6acSopenharmony_ci		switch(opts[n].type) {
1126cd6a6acSopenharmony_ci		case SELABEL_OPT_PATH:
1136cd6a6acSopenharmony_ci			path = opts[n].value;
1146cd6a6acSopenharmony_ci			break;
1156cd6a6acSopenharmony_ci		}
1166cd6a6acSopenharmony_ci
1176cd6a6acSopenharmony_ci	/* Open the specification file. */
1186cd6a6acSopenharmony_ci	if (!path)
1196cd6a6acSopenharmony_ci		path = selinux_x_context_path();
1206cd6a6acSopenharmony_ci	if ((fp = fopen(path, "re")) == NULL)
1216cd6a6acSopenharmony_ci		return -1;
1226cd6a6acSopenharmony_ci	__fsetlocking(fp, FSETLOCKING_BYCALLER);
1236cd6a6acSopenharmony_ci
1246cd6a6acSopenharmony_ci	if (fstat(fileno(fp), &sb) < 0)
1256cd6a6acSopenharmony_ci		goto finish;
1266cd6a6acSopenharmony_ci	if (!S_ISREG(sb.st_mode)) {
1276cd6a6acSopenharmony_ci		errno = EINVAL;
1286cd6a6acSopenharmony_ci		goto finish;
1296cd6a6acSopenharmony_ci	}
1306cd6a6acSopenharmony_ci	rec->spec_file = strdup(path);
1316cd6a6acSopenharmony_ci
1326cd6a6acSopenharmony_ci	/*
1336cd6a6acSopenharmony_ci	 * Perform two passes over the specification file.
1346cd6a6acSopenharmony_ci	 * The first pass counts the number of specifications and
1356cd6a6acSopenharmony_ci	 * performs simple validation of the input.  At the end
1366cd6a6acSopenharmony_ci	 * of the first pass, the spec array is allocated.
1376cd6a6acSopenharmony_ci	 * The second pass performs detailed validation of the input
1386cd6a6acSopenharmony_ci	 * and fills in the spec array.
1396cd6a6acSopenharmony_ci	 */
1406cd6a6acSopenharmony_ci	maxnspec = UINT_MAX / sizeof(spec_t);
1416cd6a6acSopenharmony_ci	for (pass = 0; pass < 2; pass++) {
1426cd6a6acSopenharmony_ci		lineno = 0;
1436cd6a6acSopenharmony_ci		data->nspec = 0;
1446cd6a6acSopenharmony_ci		while (getline(&line_buf, &line_len, fp) > 0 &&
1456cd6a6acSopenharmony_ci		       data->nspec < maxnspec) {
1466cd6a6acSopenharmony_ci			if (process_line(path, line_buf, pass, ++lineno, rec))
1476cd6a6acSopenharmony_ci				goto finish;
1486cd6a6acSopenharmony_ci		}
1496cd6a6acSopenharmony_ci
1506cd6a6acSopenharmony_ci		if (pass == 0) {
1516cd6a6acSopenharmony_ci			if (data->nspec == 0) {
1526cd6a6acSopenharmony_ci				status = 0;
1536cd6a6acSopenharmony_ci				goto finish;
1546cd6a6acSopenharmony_ci			}
1556cd6a6acSopenharmony_ci			data->spec_arr = malloc(sizeof(spec_t)*data->nspec);
1566cd6a6acSopenharmony_ci			if (data->spec_arr == NULL)
1576cd6a6acSopenharmony_ci				goto finish;
1586cd6a6acSopenharmony_ci			memset(data->spec_arr, 0, sizeof(spec_t)*data->nspec);
1596cd6a6acSopenharmony_ci			maxnspec = data->nspec;
1606cd6a6acSopenharmony_ci			rewind(fp);
1616cd6a6acSopenharmony_ci		}
1626cd6a6acSopenharmony_ci	}
1636cd6a6acSopenharmony_ci	free(line_buf);
1646cd6a6acSopenharmony_ci
1656cd6a6acSopenharmony_ci	status = digest_add_specfile(rec->digest, fp, NULL, sb.st_size, path);
1666cd6a6acSopenharmony_ci	if (status)
1676cd6a6acSopenharmony_ci		goto finish;
1686cd6a6acSopenharmony_ci
1696cd6a6acSopenharmony_ci	digest_gen_hash(rec->digest);
1706cd6a6acSopenharmony_ci
1716cd6a6acSopenharmony_cifinish:
1726cd6a6acSopenharmony_ci	fclose(fp);
1736cd6a6acSopenharmony_ci	return status;
1746cd6a6acSopenharmony_ci}
1756cd6a6acSopenharmony_ci
1766cd6a6acSopenharmony_ci/*
1776cd6a6acSopenharmony_ci * Backend interface routines
1786cd6a6acSopenharmony_ci */
1796cd6a6acSopenharmony_cistatic void close(struct selabel_handle *rec)
1806cd6a6acSopenharmony_ci{
1816cd6a6acSopenharmony_ci	struct saved_data *data = (struct saved_data *)rec->data;
1826cd6a6acSopenharmony_ci	struct spec *spec, *spec_arr = data->spec_arr;
1836cd6a6acSopenharmony_ci	unsigned int i;
1846cd6a6acSopenharmony_ci
1856cd6a6acSopenharmony_ci	for (i = 0; i < data->nspec; i++) {
1866cd6a6acSopenharmony_ci		spec = &spec_arr[i];
1876cd6a6acSopenharmony_ci		free(spec->key);
1886cd6a6acSopenharmony_ci		free(spec->lr.ctx_raw);
1896cd6a6acSopenharmony_ci		free(spec->lr.ctx_trans);
1906cd6a6acSopenharmony_ci	}
1916cd6a6acSopenharmony_ci
1926cd6a6acSopenharmony_ci	if (spec_arr)
1936cd6a6acSopenharmony_ci	    free(spec_arr);
1946cd6a6acSopenharmony_ci
1956cd6a6acSopenharmony_ci	free(data);
1966cd6a6acSopenharmony_ci}
1976cd6a6acSopenharmony_ci
1986cd6a6acSopenharmony_cistatic struct selabel_lookup_rec *lookup(struct selabel_handle *rec,
1996cd6a6acSopenharmony_ci					 const char *key, int type)
2006cd6a6acSopenharmony_ci{
2016cd6a6acSopenharmony_ci	struct saved_data *data = (struct saved_data *)rec->data;
2026cd6a6acSopenharmony_ci	spec_t *spec_arr = data->spec_arr;
2036cd6a6acSopenharmony_ci	unsigned int i;
2046cd6a6acSopenharmony_ci
2056cd6a6acSopenharmony_ci	for (i = 0; i < data->nspec; i++) {
2066cd6a6acSopenharmony_ci		if (spec_arr[i].type != type)
2076cd6a6acSopenharmony_ci			continue;
2086cd6a6acSopenharmony_ci		if (!fnmatch(spec_arr[i].key, key, 0))
2096cd6a6acSopenharmony_ci			break;
2106cd6a6acSopenharmony_ci	}
2116cd6a6acSopenharmony_ci
2126cd6a6acSopenharmony_ci	if (i >= data->nspec) {
2136cd6a6acSopenharmony_ci		/* No matching specification. */
2146cd6a6acSopenharmony_ci		errno = ENOENT;
2156cd6a6acSopenharmony_ci		return NULL;
2166cd6a6acSopenharmony_ci	}
2176cd6a6acSopenharmony_ci
2186cd6a6acSopenharmony_ci	spec_arr[i].matches++;
2196cd6a6acSopenharmony_ci	return &spec_arr[i].lr;
2206cd6a6acSopenharmony_ci}
2216cd6a6acSopenharmony_ci
2226cd6a6acSopenharmony_cistatic void stats(struct selabel_handle *rec)
2236cd6a6acSopenharmony_ci{
2246cd6a6acSopenharmony_ci	struct saved_data *data = (struct saved_data *)rec->data;
2256cd6a6acSopenharmony_ci	unsigned int i, total = 0;
2266cd6a6acSopenharmony_ci
2276cd6a6acSopenharmony_ci	for (i = 0; i < data->nspec; i++)
2286cd6a6acSopenharmony_ci		total += data->spec_arr[i].matches;
2296cd6a6acSopenharmony_ci
2306cd6a6acSopenharmony_ci	selinux_log(SELINUX_INFO, "%u entries, %u matches made\n",
2316cd6a6acSopenharmony_ci		  data->nspec, total);
2326cd6a6acSopenharmony_ci}
2336cd6a6acSopenharmony_ci
2346cd6a6acSopenharmony_ciint selabel_x_init(struct selabel_handle *rec, const struct selinux_opt *opts,
2356cd6a6acSopenharmony_ci		   unsigned nopts)
2366cd6a6acSopenharmony_ci{
2376cd6a6acSopenharmony_ci	struct saved_data *data;
2386cd6a6acSopenharmony_ci
2396cd6a6acSopenharmony_ci	data = (struct saved_data *)malloc(sizeof(*data));
2406cd6a6acSopenharmony_ci	if (!data)
2416cd6a6acSopenharmony_ci		return -1;
2426cd6a6acSopenharmony_ci	memset(data, 0, sizeof(*data));
2436cd6a6acSopenharmony_ci
2446cd6a6acSopenharmony_ci	rec->data = data;
2456cd6a6acSopenharmony_ci	rec->func_close = &close;
2466cd6a6acSopenharmony_ci	rec->func_lookup = &lookup;
2476cd6a6acSopenharmony_ci	rec->func_stats = &stats;
2486cd6a6acSopenharmony_ci
2496cd6a6acSopenharmony_ci	return init(rec, opts, nopts);
2506cd6a6acSopenharmony_ci}
251