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, "re")) == NULL)
121		return -1;
122	__fsetlocking(fp, FSETLOCKING_BYCALLER);
123
124	if (fstat(fileno(fp), &sb) < 0)
125		goto finish;
126	if (!S_ISREG(sb.st_mode)) {
127		errno = EINVAL;
128		goto finish;
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
150		if (pass == 0) {
151			if (data->nspec == 0) {
152				status = 0;
153				goto finish;
154			}
155			data->spec_arr = malloc(sizeof(spec_t)*data->nspec);
156			if (data->spec_arr == NULL)
157				goto finish;
158			memset(data->spec_arr, 0, sizeof(spec_t)*data->nspec);
159			maxnspec = data->nspec;
160			rewind(fp);
161		}
162	}
163	free(line_buf);
164
165	status = digest_add_specfile(rec->digest, fp, NULL, sb.st_size, path);
166	if (status)
167		goto finish;
168
169	digest_gen_hash(rec->digest);
170
171finish:
172	fclose(fp);
173	return status;
174}
175
176/*
177 * Backend interface routines
178 */
179static void close(struct selabel_handle *rec)
180{
181	struct saved_data *data = (struct saved_data *)rec->data;
182	struct spec *spec, *spec_arr = data->spec_arr;
183	unsigned int i;
184
185	for (i = 0; i < data->nspec; i++) {
186		spec = &spec_arr[i];
187		free(spec->key);
188		free(spec->lr.ctx_raw);
189		free(spec->lr.ctx_trans);
190	}
191
192	if (spec_arr)
193	    free(spec_arr);
194
195	free(data);
196}
197
198static struct selabel_lookup_rec *lookup(struct selabel_handle *rec,
199					 const char *key, int type)
200{
201	struct saved_data *data = (struct saved_data *)rec->data;
202	spec_t *spec_arr = data->spec_arr;
203	unsigned int i;
204
205	for (i = 0; i < data->nspec; i++) {
206		if (spec_arr[i].type != type)
207			continue;
208		if (!fnmatch(spec_arr[i].key, key, 0))
209			break;
210	}
211
212	if (i >= data->nspec) {
213		/* No matching specification. */
214		errno = ENOENT;
215		return NULL;
216	}
217
218	spec_arr[i].matches++;
219	return &spec_arr[i].lr;
220}
221
222static void stats(struct selabel_handle *rec)
223{
224	struct saved_data *data = (struct saved_data *)rec->data;
225	unsigned int i, total = 0;
226
227	for (i = 0; i < data->nspec; i++)
228		total += data->spec_arr[i].matches;
229
230	selinux_log(SELINUX_INFO, "%u entries, %u matches made\n",
231		  data->nspec, total);
232}
233
234int selabel_x_init(struct selabel_handle *rec, const struct selinux_opt *opts,
235		   unsigned nopts)
236{
237	struct saved_data *data;
238
239	data = (struct saved_data *)malloc(sizeof(*data));
240	if (!data)
241		return -1;
242	memset(data, 0, sizeof(*data));
243
244	rec->data = data;
245	rec->func_close = &close;
246	rec->func_lookup = &lookup;
247	rec->func_stats = &stats;
248
249	return init(rec, opts, nopts);
250}
251