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