1/*
2 * Media contexts backend for X contexts
3 *
4 * Author : Eamon Walsh <ewalsh@tycho.nsa.gov>
5 */
6
7#include <sys/stat.h>
8#include <string.h>
9#include <stdio.h>
10#include <stdio_ext.h>
11#include <ctype.h>
12#include <errno.h>
13#include <limits.h>
14#include <fnmatch.h>
15#include "callbacks.h"
16#include "label_internal.h"
17
18/*
19 * Internals
20 */
21
22/* A context specification. */
23typedef struct spec {
24	struct selabel_lookup_rec lr;	/* holds contexts for lookup result */
25	char *key;		/* key string */
26	int type;		/* type of record (prop, ext, client) */
27	int matches;		/* number of matches made during operation */
28} spec_t;
29
30struct saved_data {
31	unsigned int nspec;
32	spec_t *spec_arr;
33};
34
35static int process_line(const char *path, char *line_buf, int pass,
36			unsigned lineno, struct selabel_handle *rec)
37{
38	struct saved_data *data = (struct saved_data *)rec->data;
39	int items;
40	char *buf_p;
41	char *type, *key, *context;
42
43	buf_p = line_buf;
44	while (isspace(*buf_p))
45		buf_p++;
46	/* Skip comment lines and empty lines. */
47	if (*buf_p == '#' || *buf_p == 0)
48		return 0;
49	items = sscanf(line_buf, "%ms %ms %ms ", &type, &key, &context);
50	if (items < 3) {
51		selinux_log(SELINUX_WARNING,
52			    "%s:  line %u is missing fields, skipping\n", path,
53			    lineno);
54		if (items > 0)
55			free(type);
56		if (items > 1)
57			free(key);
58		return 0;
59	}
60
61	if (pass == 1) {
62		/* Convert the type string to a mode format */
63		if (!strcmp(type, "property"))
64			data->spec_arr[data->nspec].type = SELABEL_X_PROP;
65		else if (!strcmp(type, "extension"))
66			data->spec_arr[data->nspec].type = SELABEL_X_EXT;
67		else if (!strcmp(type, "client"))
68			data->spec_arr[data->nspec].type = SELABEL_X_CLIENT;
69		else if (!strcmp(type, "event"))
70			data->spec_arr[data->nspec].type = SELABEL_X_EVENT;
71		else if (!strcmp(type, "selection"))
72			data->spec_arr[data->nspec].type = SELABEL_X_SELN;
73		else if (!strcmp(type, "poly_property"))
74			data->spec_arr[data->nspec].type = SELABEL_X_POLYPROP;
75		else if (!strcmp(type, "poly_selection"))
76			data->spec_arr[data->nspec].type = SELABEL_X_POLYSELN;
77		else {
78			selinux_log(SELINUX_WARNING,
79				    "%s:  line %u has invalid object type %s\n",
80				    path, lineno, type);
81			return 0;
82		}
83		data->spec_arr[data->nspec].key = key;
84		data->spec_arr[data->nspec].lr.ctx_raw = context;
85		free(type);
86	}
87
88	data->nspec++;
89	if (pass == 0) {
90		free(type);
91		free(key);
92		free(context);
93	}
94	return 0;
95}
96
97static int init(struct selabel_handle *rec, const struct selinux_opt *opts,
98		unsigned n)
99{
100	FILE *fp;
101	struct saved_data *data = (struct saved_data *)rec->data;
102	const char *path = NULL;
103	char *line_buf = NULL;
104	size_t line_len = 0;
105	int status = -1;
106	unsigned int lineno, pass, maxnspec;
107	struct stat sb;
108
109	/* Process arguments */
110	while (n--)
111		switch(opts[n].type) {
112		case SELABEL_OPT_PATH:
113			path = opts[n].value;
114			break;
115		}
116
117	/* Open the specification file. */
118	if (!path)
119		path = selinux_x_context_path();
120	if ((fp = fopen(path, "r")) == NULL)
121		return -1;
122	__fsetlocking(fp, FSETLOCKING_BYCALLER);
123
124	if (fstat(fileno(fp), &sb) < 0)
125		return -1;
126	if (!S_ISREG(sb.st_mode)) {
127		errno = EINVAL;
128		return -1;
129	}
130	rec->spec_file = strdup(path);
131
132	/*
133	 * Perform two passes over the specification file.
134	 * The first pass counts the number of specifications and
135	 * performs simple validation of the input.  At the end
136	 * of the first pass, the spec array is allocated.
137	 * The second pass performs detailed validation of the input
138	 * and fills in the spec array.
139	 */
140	maxnspec = UINT_MAX / sizeof(spec_t);
141	for (pass = 0; pass < 2; pass++) {
142		lineno = 0;
143		data->nspec = 0;
144		while (getline(&line_buf, &line_len, fp) > 0 &&
145		       data->nspec < maxnspec) {
146			if (process_line(path, line_buf, pass, ++lineno, rec))
147				goto finish;
148		}
149		lineno = 0;
150
151		if (pass == 0) {
152			if (data->nspec == 0) {
153				status = 0;
154				goto finish;
155			}
156			data->spec_arr = malloc(sizeof(spec_t)*data->nspec);
157			if (data->spec_arr == NULL)
158				goto finish;
159			memset(data->spec_arr, 0, sizeof(spec_t)*data->nspec);
160			maxnspec = data->nspec;
161			rewind(fp);
162		}
163	}
164	free(line_buf);
165
166	status = digest_add_specfile(rec->digest, fp, NULL, sb.st_size, path);
167	if (status)
168		goto finish;
169
170	digest_gen_hash(rec->digest);
171
172finish:
173	fclose(fp);
174	return status;
175}
176
177/*
178 * Backend interface routines
179 */
180static void close(struct selabel_handle *rec)
181{
182	struct saved_data *data = (struct saved_data *)rec->data;
183	struct spec *spec, *spec_arr = data->spec_arr;
184	unsigned int i;
185
186	for (i = 0; i < data->nspec; i++) {
187		spec = &spec_arr[i];
188		free(spec->key);
189		free(spec->lr.ctx_raw);
190		free(spec->lr.ctx_trans);
191	}
192
193	if (spec_arr)
194	    free(spec_arr);
195
196	free(data);
197}
198
199static struct selabel_lookup_rec *lookup(struct selabel_handle *rec,
200					 const char *key, int type)
201{
202	struct saved_data *data = (struct saved_data *)rec->data;
203	spec_t *spec_arr = data->spec_arr;
204	unsigned int i;
205
206	for (i = 0; i < data->nspec; i++) {
207		if (spec_arr[i].type != type)
208			continue;
209		if (!fnmatch(spec_arr[i].key, key, 0))
210			break;
211	}
212
213	if (i >= data->nspec) {
214		/* No matching specification. */
215		errno = ENOENT;
216		return NULL;
217	}
218
219	spec_arr[i].matches++;
220	return &spec_arr[i].lr;
221}
222
223static void stats(struct selabel_handle *rec)
224{
225	struct saved_data *data = (struct saved_data *)rec->data;
226	unsigned int i, total = 0;
227
228	for (i = 0; i < data->nspec; i++)
229		total += data->spec_arr[i].matches;
230
231	selinux_log(SELINUX_INFO, "%u entries, %u matches made\n",
232		  data->nspec, total);
233}
234
235int selabel_x_init(struct selabel_handle *rec, const struct selinux_opt *opts,
236		   unsigned nopts)
237{
238	struct saved_data *data;
239
240	data = (struct saved_data *)malloc(sizeof(*data));
241	if (!data)
242		return -1;
243	memset(data, 0, sizeof(*data));
244
245	rec->data = data;
246	rec->func_close = &close;
247	rec->func_lookup = &lookup;
248	rec->func_stats = &stats;
249
250	return init(rec, opts, nopts);
251}
252