1/*
2 * Property Service contexts backend for labeling Android
3 * property keys
4 */
5
6#include <stdarg.h>
7#include <string.h>
8#include <ctype.h>
9#include <errno.h>
10#include <limits.h>
11#include <sys/types.h>
12#include <sys/stat.h>
13#include "callbacks.h"
14#include "label_internal.h"
15
16/* A property security context specification. */
17typedef struct spec {
18	struct selabel_lookup_rec lr;	/* holds contexts for lookup result */
19	char *property_key;		/* property key string */
20} spec_t;
21
22/* Our stored configuration */
23struct saved_data {
24	/*
25	 * The array of specifications is sorted for longest
26	 * prefix match
27	 */
28	spec_t *spec_arr;
29	unsigned int nspec;	/* total number of specifications */
30};
31
32static int cmp(const void *A, const void *B)
33{
34	const struct spec *sp1 = A, *sp2 = B;
35
36	if (strncmp(sp1->property_key, "*", 1) == 0)
37		return 1;
38	if (strncmp(sp2->property_key, "*", 1) == 0)
39		return -1;
40
41	size_t L1 = strlen(sp1->property_key);
42	size_t L2 = strlen(sp2->property_key);
43
44	return (L1 < L2) - (L1 > L2);
45}
46
47/*
48 * Warn about duplicate specifications.
49 */
50static int nodups_specs(struct saved_data *data, const char *path)
51{
52	int rc = 0;
53	unsigned int ii, jj;
54	struct spec *curr_spec, *spec_arr = data->spec_arr;
55
56	for (ii = 0; ii < data->nspec; ii++) {
57		curr_spec = &spec_arr[ii];
58		for (jj = ii + 1; jj < data->nspec; jj++) {
59			if (!strcmp(spec_arr[jj].property_key,
60					    curr_spec->property_key)) {
61				rc = -1;
62				errno = EINVAL;
63				if (strcmp(spec_arr[jj].lr.ctx_raw,
64						    curr_spec->lr.ctx_raw)) {
65					selinux_log
66						(SELINUX_ERROR,
67						 "%s: Multiple different specifications for %s  (%s and %s).\n",
68						 path, curr_spec->property_key,
69						 spec_arr[jj].lr.ctx_raw,
70						 curr_spec->lr.ctx_raw);
71				} else {
72					selinux_log
73						(SELINUX_ERROR,
74						 "%s: Multiple same specifications for %s.\n",
75						 path, curr_spec->property_key);
76				}
77			}
78		}
79	}
80	return rc;
81}
82
83static int process_line(struct selabel_handle *rec,
84			const char *path, char *line_buf,
85			int pass, unsigned lineno)
86{
87	int items;
88	char *prop = NULL, *context = NULL;
89	struct saved_data *data = (struct saved_data *)rec->data;
90	spec_t *spec_arr = data->spec_arr;
91	unsigned int nspec = data->nspec;
92	const char *errbuf = NULL;
93
94	items = read_spec_entries(line_buf, &errbuf, 2, &prop, &context);
95	if (items < 0) {
96		if (errbuf) {
97			selinux_log(SELINUX_ERROR,
98				    "%s:  line %u error due to: %s\n", path,
99				    lineno, errbuf);
100		} else {
101			selinux_log(SELINUX_ERROR,
102				    "%s:  line %u error due to: %m\n", path,
103				    lineno);
104		}
105		return -1;
106	}
107
108	if (items == 0)
109		return items;
110
111	if (items != 2) {
112		selinux_log(SELINUX_ERROR,
113			    "%s:  line %u is missing fields\n", path,
114			    lineno);
115		free(prop);
116		errno = EINVAL;
117		return -1;
118	}
119
120	if (pass == 0) {
121		free(prop);
122		free(context);
123	} else if (pass == 1) {
124		/* On the second pass, process and store the specification in spec. */
125		spec_arr[nspec].property_key = prop;
126		spec_arr[nspec].lr.ctx_raw = context;
127
128		if (rec->validating) {
129			if (selabel_validate(rec, &spec_arr[nspec].lr) < 0) {
130				selinux_log(SELINUX_ERROR,
131					    "%s:  line %u has invalid context %s\n",
132					    path, lineno, spec_arr[nspec].lr.ctx_raw);
133				errno = EINVAL;
134				return -1;
135			}
136		}
137	}
138
139	data->nspec = ++nspec;
140	return 0;
141}
142
143static int init(struct selabel_handle *rec, const struct selinux_opt *opts,
144		unsigned n)
145{
146	struct saved_data *data = (struct saved_data *)rec->data;
147	const char *path = NULL;
148	FILE *fp;
149	char line_buf[BUFSIZ];
150	unsigned int lineno, maxnspec, pass;
151	int status = -1;
152	struct stat sb;
153
154	/* Process arguments */
155	while (n--)
156		switch (opts[n].type) {
157		case SELABEL_OPT_PATH:
158			path = opts[n].value;
159			break;
160		}
161
162	if (!path)
163		return -1;
164
165	/* Open the specification file. */
166	if ((fp = fopen(path, "re")) == NULL)
167		return -1;
168
169	if (fstat(fileno(fp), &sb) < 0)
170		goto finish;
171	errno = EINVAL;
172	if (!S_ISREG(sb.st_mode))
173		goto finish;
174
175	/*
176	 * Two passes of the specification file. First is to get the size.
177	 * After the first pass, the spec array is malloced to the appropriate
178	 * size. Second pass is to populate the spec array and check for
179	 * dups.
180	 */
181	maxnspec = UINT_MAX / sizeof(spec_t);
182	for (pass = 0; pass < 2; pass++) {
183		data->nspec = 0;
184		lineno = 0;
185
186		while (fgets(line_buf, sizeof(line_buf) - 1, fp)
187		       && data->nspec < maxnspec) {
188			if (process_line(rec, path, line_buf, pass, ++lineno)
189									  != 0)
190				goto finish;
191		}
192
193		if (pass == 1) {
194			status = nodups_specs(data, path);
195
196			if (status)
197				goto finish;
198		}
199
200		if (pass == 0) {
201			if (data->nspec == 0) {
202				status = 0;
203				goto finish;
204			}
205
206			if (NULL == (data->spec_arr =
207				     calloc(data->nspec, sizeof(spec_t))))
208				goto finish;
209
210			maxnspec = data->nspec;
211			rewind(fp);
212		}
213	}
214
215	qsort(data->spec_arr, data->nspec, sizeof(struct spec), cmp);
216
217	status = digest_add_specfile(rec->digest, fp, NULL, sb.st_size, path);
218	if (status)
219		goto finish;
220
221	digest_gen_hash(rec->digest);
222
223finish:
224	fclose(fp);
225	return status;
226}
227
228/*
229 * Backend interface routines
230 */
231static void closef(struct selabel_handle *rec)
232{
233	struct saved_data *data = (struct saved_data *)rec->data;
234	struct spec *spec;
235	unsigned int i;
236
237	for (i = 0; i < data->nspec; i++) {
238		spec = &data->spec_arr[i];
239		free(spec->property_key);
240		free(spec->lr.ctx_raw);
241		free(spec->lr.ctx_trans);
242	}
243
244	if (data->spec_arr)
245		free(data->spec_arr);
246
247	free(data);
248}
249
250static struct selabel_lookup_rec *property_lookup(struct selabel_handle *rec,
251					 const char *key,
252					 int __attribute__((unused)) type)
253{
254	struct saved_data *data = (struct saved_data *)rec->data;
255	spec_t *spec_arr = data->spec_arr;
256	unsigned int i;
257	struct selabel_lookup_rec *ret = NULL;
258
259	if (!data->nspec) {
260		errno = ENOENT;
261		goto finish;
262	}
263
264	for (i = 0; i < data->nspec; i++) {
265		if (strncmp(spec_arr[i].property_key, key,
266			    strlen(spec_arr[i].property_key)) == 0) {
267			break;
268		}
269		if (strncmp(spec_arr[i].property_key, "*", 1) == 0)
270			break;
271	}
272
273	if (i >= data->nspec) {
274		/* No matching specification. */
275		errno = ENOENT;
276		goto finish;
277	}
278
279	ret = &spec_arr[i].lr;
280
281finish:
282	return ret;
283}
284
285static struct selabel_lookup_rec *service_lookup(struct selabel_handle *rec,
286		const char *key, int __attribute__((unused)) type)
287{
288	struct saved_data *data = (struct saved_data *)rec->data;
289	spec_t *spec_arr = data->spec_arr;
290	unsigned int i;
291	struct selabel_lookup_rec *ret = NULL;
292
293	if (!data->nspec) {
294		errno = ENOENT;
295		goto finish;
296	}
297
298	for (i = 0; i < data->nspec; i++) {
299		if (strcmp(spec_arr[i].property_key, key) == 0)
300			break;
301		if (strcmp(spec_arr[i].property_key, "*") == 0)
302			break;
303	}
304
305	if (i >= data->nspec) {
306		/* No matching specification. */
307		errno = ENOENT;
308		goto finish;
309	}
310
311	ret = &spec_arr[i].lr;
312
313finish:
314	return ret;
315}
316
317static void stats(struct selabel_handle __attribute__((unused)) *rec)
318{
319	selinux_log(SELINUX_WARNING, "'stats' functionality not implemented.\n");
320}
321
322int selabel_property_init(struct selabel_handle *rec,
323			  const struct selinux_opt *opts,
324			  unsigned nopts)
325{
326	struct saved_data *data;
327
328	data = (struct saved_data *)calloc(1, sizeof(*data));
329	if (!data)
330		return -1;
331
332	rec->data = data;
333	rec->func_close = &closef;
334	rec->func_stats = &stats;
335	rec->func_lookup = &property_lookup;
336
337	return init(rec, opts, nopts);
338}
339
340int selabel_service_init(struct selabel_handle *rec,
341		const struct selinux_opt *opts, unsigned nopts)
342{
343	struct saved_data *data;
344
345	data = (struct saved_data *)calloc(1, sizeof(*data));
346	if (!data)
347		return -1;
348
349	rec->data = data;
350	rec->func_close = &closef;
351	rec->func_stats = &stats;
352	rec->func_lookup = &service_lookup;
353
354	return init(rec, opts, nopts);
355}
356