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, curr_spec->property_key))) {
60				rc = -1;
61				errno = EINVAL;
62				if (strcmp
63				    (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, len;
88	char buf1[BUFSIZ], buf2[BUFSIZ];
89	char *buf_p, *prop = buf1, *context = buf2;
90	struct saved_data *data = (struct saved_data *)rec->data;
91	spec_t *spec_arr = data->spec_arr;
92	unsigned int nspec = data->nspec;
93
94	len = strlen(line_buf);
95	if (line_buf[len - 1] == '\n')
96		line_buf[len - 1] = 0;
97	buf_p = line_buf;
98	while (isspace(*buf_p))
99		buf_p++;
100	/* Skip comment lines and empty lines. */
101	if (*buf_p == '#' || *buf_p == 0)
102		return 0;
103	items = sscanf(line_buf, "%255s %255s", prop, context);
104	if (items != 2) {
105		selinux_log(SELINUX_WARNING,
106			    "%s:  line %d is missing fields, skipping\n", path,
107			    lineno);
108		return 0;
109	}
110
111	if (pass == 1) {
112		/* On the second pass, process and store the specification in spec. */
113		spec_arr[nspec].property_key = strdup(prop);
114		if (!spec_arr[nspec].property_key) {
115			selinux_log(SELINUX_WARNING,
116				    "%s:  out of memory at line %d on prop %s\n",
117				    path, lineno, prop);
118		return -1;
119
120		}
121
122		spec_arr[nspec].lr.ctx_raw = strdup(context);
123		if (!spec_arr[nspec].lr.ctx_raw) {
124			selinux_log(SELINUX_WARNING,
125				    "%s:  out of memory at line %d on context %s\n",
126				    path, lineno, context);
127		return -1;
128		}
129
130		if (rec->validating) {
131		        if (selabel_validate(rec, &spec_arr[nspec].lr) < 0) {
132			        selinux_log(SELINUX_WARNING,
133					    "%s:  line %d has invalid context %s\n",
134					    path, lineno, spec_arr[nspec].lr.ctx_raw);
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 = 0, 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	/* Open the specification file. */
163	if ((fp = fopen(path, "r")) == NULL)
164		return -1;
165
166	if (fstat(fileno(fp), &sb) < 0)
167		return -1;
168	if (!S_ISREG(sb.st_mode)) {
169		errno = EINVAL;
170		return -1;
171	}
172
173	/*
174	 * Two passes of the specification file. First is to get the size.
175	 * After the first pass, the spec array is malloced to the appropriate
176	 * size. Second pass is to populate the spec array and check for
177	 * dups.
178	 */
179	maxnspec = UINT_MAX / sizeof(spec_t);
180	for (pass = 0; pass < 2; pass++) {
181		data->nspec = 0;
182
183		while (fgets(line_buf, sizeof line_buf - 1, fp)
184		       && data->nspec < maxnspec) {
185			if (process_line(rec, path, line_buf, pass, ++lineno) != 0) {
186				goto finish;
187			}
188		}
189
190		if (pass == 1) {
191			status = nodups_specs(data, path);
192
193			if (status)
194				goto finish;
195		}
196
197		if (pass == 0) {
198
199			if (data->nspec == 0) {
200				status = 0;
201				goto finish;
202			}
203
204			if (NULL == (data->spec_arr =
205				     malloc(sizeof(spec_t) * data->nspec)))
206				goto finish;
207
208			memset(data->spec_arr, 0, sizeof(spec_t)*data->nspec);
209			maxnspec = data->nspec;
210			rewind(fp);
211		}
212	}
213
214	qsort(data->spec_arr, data->nspec, sizeof(struct spec), cmp);
215
216	status = 0;
217finish:
218	fclose(fp);
219	return status;
220}
221
222/*
223 * Backend interface routines
224 */
225static void closef(struct selabel_handle *rec)
226{
227	struct saved_data *data = (struct saved_data *)rec->data;
228	struct spec *spec;
229	unsigned int i;
230
231	for (i = 0; i < data->nspec; i++) {
232		spec = &data->spec_arr[i];
233		free(spec->property_key);
234		free(spec->lr.ctx_raw);
235		free(spec->lr.ctx_trans);
236	}
237
238	if (data->spec_arr)
239		free(data->spec_arr);
240
241	free(data);
242}
243
244static struct selabel_lookup_rec *lookup(struct selabel_handle *rec,
245					 const char *key,
246					 int __attribute__((unused)) type)
247{
248	struct saved_data *data = (struct saved_data *)rec->data;
249	spec_t *spec_arr = data->spec_arr;
250	unsigned int i;
251	struct selabel_lookup_rec *ret = NULL;
252
253	if (!data->nspec) {
254		errno = ENOENT;
255		goto finish;
256	}
257
258	for (i = 0; i < data->nspec; i++) {
259		if (strncmp(spec_arr[i].property_key, key,
260		    strlen(spec_arr[i].property_key)) == 0) {
261			break;
262		}
263		if (strncmp(spec_arr[i].property_key, "*", 1) == 0)
264			break;
265	}
266
267	if (i >= data->nspec) {
268		/* No matching specification. */
269		errno = ENOENT;
270		goto finish;
271	}
272
273	ret = &spec_arr[i].lr;
274
275finish:
276	return ret;
277}
278
279static void stats(struct selabel_handle __attribute__((unused)) *rec)
280{
281	selinux_log(SELINUX_WARNING, "'stats' functionality not implemented.\n");
282}
283
284int selabel_property_init(struct selabel_handle *rec,
285			  const struct selinux_opt *opts,
286			  unsigned nopts)
287{
288	struct saved_data *data;
289
290	data = (struct saved_data *)malloc(sizeof(*data));
291	if (!data)
292		return -1;
293	memset(data, 0, sizeof(*data));
294
295	rec->data = data;
296	rec->func_close = &closef;
297	rec->func_stats = &stats;
298	rec->func_lookup = &lookup;
299
300	return init(rec, opts, nopts);
301}
302