• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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. */
17 typedef 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 */
23 struct 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 
cmp(const void * A,const void * B)32 static 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. Return error on different specifications.
49  * TODO: Remove duplicate specifications. Move duplicate check to after sort
50  * to improve performance.
51  */
nodups_specs(struct saved_data * data)52 static int nodups_specs(struct saved_data *data)
53 {
54 	int rc = 0;
55 	unsigned int ii, jj;
56 	struct spec *curr_spec, *spec_arr = data->spec_arr;
57 
58 	for (ii = 0; ii < data->nspec; ii++) {
59 		curr_spec = &spec_arr[ii];
60 		for (jj = ii + 1; jj < data->nspec; jj++) {
61 			if (!strcmp(spec_arr[jj].property_key,
62 					    curr_spec->property_key)) {
63 				if (strcmp(spec_arr[jj].lr.ctx_raw,
64 						    curr_spec->lr.ctx_raw)) {
65 					rc = -1;
66 					errno = EINVAL;
67 					selinux_log
68 						(SELINUX_ERROR,
69 						 "Multiple different specifications for %s  (%s and %s).\n",
70 						 curr_spec->property_key,
71 						 spec_arr[jj].lr.ctx_raw,
72 						 curr_spec->lr.ctx_raw);
73 				} else {
74 					selinux_log
75 						(SELINUX_WARNING,
76 						 "Multiple same specifications for %s.\n",
77 						 curr_spec->property_key);
78 				}
79 			}
80 		}
81 	}
82 	return rc;
83 }
84 
process_line(struct selabel_handle * rec,const char * path,char * line_buf,int pass,unsigned lineno)85 static int process_line(struct selabel_handle *rec,
86 			const char *path, char *line_buf,
87 			int pass, unsigned lineno)
88 {
89 	int items;
90 	char *prop = NULL, *context = NULL;
91 	struct saved_data *data = (struct saved_data *)rec->data;
92 	spec_t *spec_arr = data->spec_arr;
93 	unsigned int nspec = data->nspec;
94 	const char *errbuf = NULL;
95 
96 	items = read_spec_entries(line_buf, &errbuf, 2, &prop, &context);
97 	if (items < 0) {
98 		items = errno;
99 		if (errbuf) {
100 			selinux_log(SELINUX_ERROR,
101 				    "%s:  line %u error due to: %s\n", path,
102 				    lineno, errbuf);
103 		} else {
104 			selinux_log(SELINUX_ERROR,
105 				    "%s:  line %u error due to: %m\n", path,
106 				    lineno);
107 		}
108 		errno = items;
109 		return -1;
110 	}
111 
112 	if (items == 0)
113 		return items;
114 
115 	if (items != 2) {
116 		selinux_log(SELINUX_ERROR,
117 			    "%s:  line %u is missing fields\n", path,
118 			    lineno);
119 		free(prop);
120 		errno = EINVAL;
121 		return -1;
122 	}
123 
124 	if (pass == 0) {
125 		free(prop);
126 		free(context);
127 	} else if (pass == 1) {
128 		/* On the second pass, process and store the specification in spec. */
129 		spec_arr[nspec].property_key = prop;
130 		spec_arr[nspec].lr.ctx_raw = context;
131 
132 		if (rec->validating) {
133 			if (selabel_validate(rec, &spec_arr[nspec].lr) < 0) {
134 				selinux_log(SELINUX_ERROR,
135 					    "%s:  line %u has invalid context %s\n",
136 					    path, lineno, spec_arr[nspec].lr.ctx_raw);
137 				errno = EINVAL;
138 				return -1;
139 			}
140 		}
141 
142 		data->nspec = ++nspec;
143 	}
144 
145 	return 0;
146 }
147 
process_file(struct selabel_handle * rec,const char * path)148 static int process_file(struct selabel_handle *rec, const char *path)
149 {
150 	struct saved_data *data = (struct saved_data *)rec->data;
151 	char line_buf[BUFSIZ];
152 	unsigned int lineno, maxnspec, pass;
153 	struct stat sb;
154 	FILE *fp;
155 	int status = -1;
156 	unsigned int nspec;
157 	spec_t *spec_arr;
158 
159 	/* Open the specification file. */
160 	if ((fp = fopen(path, "re")) == NULL)
161 		return -1;
162 
163 	if (fstat(fileno(fp), &sb) < 0)
164 		goto finish;
165 
166 	errno = EINVAL;
167 
168 	if (!S_ISREG(sb.st_mode))
169 		goto finish;
170 
171 	/*
172 	 * Two passes per specification file. First is to get the size.
173 	 * After the first pass, the spec array is malloced / realloced to
174 	 * the appropriate size. Second pass is to populate the spec array.
175 	 */
176 	maxnspec = UINT_MAX / sizeof(spec_t);
177 	for (pass = 0; pass < 2; pass++) {
178 		nspec = 0;
179 		lineno = 0;
180 
181 		while (fgets(line_buf, sizeof(line_buf) - 1, fp) &&
182 			nspec < maxnspec) {
183 			if (process_line(rec, path, line_buf, pass, ++lineno))
184 				goto finish;
185 			nspec++;
186 		}
187 
188 		if (pass == 0) {
189 			if (nspec == 0) {
190 				status = 0;
191 				goto finish;
192 			}
193 
194 			/* grow spec array if required */
195 			spec_arr = realloc(data->spec_arr,
196 					(data->nspec + nspec) * sizeof(spec_t));
197 			if (spec_arr == NULL)
198 				goto finish;
199 
200 			memset(&spec_arr[data->nspec], 0, nspec * sizeof(spec_t));
201 			data->spec_arr = spec_arr;
202 			maxnspec = nspec;
203 			rewind(fp);
204 		}
205 	}
206 
207 	status = digest_add_specfile(rec->digest, fp, NULL, sb.st_size, path);
208 
209 finish:
210 	fclose(fp);
211 	return status;
212 }
213 
214 static void closef(struct selabel_handle *rec);
215 
init(struct selabel_handle * rec,const struct selinux_opt * opts,unsigned n)216 static int init(struct selabel_handle *rec, const struct selinux_opt *opts,
217 		unsigned n)
218 {
219 	struct saved_data *data = (struct saved_data *)rec->data;
220 	char **paths = NULL;
221 	size_t num_paths = 0;
222 	int status = -1;
223 	size_t i;
224 
225 	/* Process arguments */
226 	i = n;
227 	while (i--) {
228 		switch (opts[i].type) {
229 		case SELABEL_OPT_PATH:
230 			num_paths++;
231 			break;
232 		}
233 	}
234 
235 	if (!num_paths)
236 		return -1;
237 
238 	paths = calloc(num_paths, sizeof(*paths));
239 	if (!paths)
240 		return -1;
241 
242 	rec->spec_files = paths;
243 	rec->spec_files_len = num_paths;
244 
245 	i = n;
246 	while (i--) {
247 		switch(opts[i].type) {
248 		case SELABEL_OPT_PATH:
249 			*paths = strdup(opts[i].value);
250 			if (*paths == NULL)
251 				goto finish;
252 			paths++;
253 		}
254 	}
255 
256 	for (i = 0; i < num_paths; i++) {
257 		status = process_file(rec, rec->spec_files[i]);
258 		if (status)
259 			goto finish;
260 	}
261 
262 	/* warn about duplicates after all files have been processed. */
263 	status = nodups_specs(data);
264 	if (status)
265 		goto finish;
266 
267 	qsort(data->spec_arr, data->nspec, sizeof(struct spec), cmp);
268 
269 	digest_gen_hash(rec->digest);
270 
271 finish:
272 	if (status)
273 		closef(rec);
274 
275 	return status;
276 }
277 
278 /*
279  * Backend interface routines
280  */
closef(struct selabel_handle * rec)281 static void closef(struct selabel_handle *rec)
282 {
283 	struct saved_data *data = (struct saved_data *)rec->data;
284 	struct spec *spec;
285 	unsigned int i;
286 
287 	if (!data)
288 		return;
289 
290 	/* make sure successive ->func_close() calls are harmless */
291 	rec->data = NULL;
292 
293 	if (data->spec_arr) {
294 		for (i = 0; i < data->nspec; i++) {
295 			spec = &data->spec_arr[i];
296 			free(spec->property_key);
297 			free(spec->lr.ctx_raw);
298 			free(spec->lr.ctx_trans);
299 		}
300 
301 		free(data->spec_arr);
302 	}
303 
304 	free(data);
305 }
306 
property_lookup(struct selabel_handle * rec,const char * key,int type)307 static struct selabel_lookup_rec *property_lookup(struct selabel_handle *rec,
308 					 const char *key,
309 					 int __attribute__((unused)) type)
310 {
311 	struct saved_data *data = (struct saved_data *)rec->data;
312 	spec_t *spec_arr = data->spec_arr;
313 	unsigned int i;
314 	struct selabel_lookup_rec *ret = NULL;
315 
316 	if (!data->nspec) {
317 		errno = ENOENT;
318 		goto finish;
319 	}
320 
321 	for (i = 0; i < data->nspec; i++) {
322 		if (strncmp(spec_arr[i].property_key, key,
323 			    strlen(spec_arr[i].property_key)) == 0) {
324 			break;
325 		}
326 		if (strncmp(spec_arr[i].property_key, "*", 1) == 0)
327 			break;
328 	}
329 
330 	if (i >= data->nspec) {
331 		/* No matching specification. */
332 		errno = ENOENT;
333 		goto finish;
334 	}
335 
336 	ret = &spec_arr[i].lr;
337 
338 finish:
339 	return ret;
340 }
341 
lookup_exact_match(struct selabel_handle * rec,const char * key,int type)342 static struct selabel_lookup_rec *lookup_exact_match(struct selabel_handle *rec,
343 		const char *key, int __attribute__((unused)) type)
344 {
345 	struct saved_data *data = (struct saved_data *)rec->data;
346 	spec_t *spec_arr = data->spec_arr;
347 	unsigned int i;
348 	struct selabel_lookup_rec *ret = NULL;
349 
350 	if (!data->nspec) {
351 		errno = ENOENT;
352 		goto finish;
353 	}
354 
355 	for (i = 0; i < data->nspec; i++) {
356 		if (strcmp(spec_arr[i].property_key, key) == 0)
357 			break;
358 		if (strcmp(spec_arr[i].property_key, "*") == 0)
359 			break;
360 	}
361 
362 	if (i >= data->nspec) {
363 		/* No matching specification. */
364 		errno = ENOENT;
365 		goto finish;
366 	}
367 
368 	ret = &spec_arr[i].lr;
369 
370 finish:
371 	return ret;
372 }
373 
stats(struct selabel_handle * rec)374 static void stats(struct selabel_handle __attribute__((unused)) *rec)
375 {
376 	selinux_log(SELINUX_WARNING, "'stats' functionality not implemented.\n");
377 }
378 
selabel_property_init(struct selabel_handle * rec,const struct selinux_opt * opts,unsigned nopts)379 int selabel_property_init(struct selabel_handle *rec,
380 			  const struct selinux_opt *opts,
381 			  unsigned nopts)
382 {
383 	struct saved_data *data;
384 
385 	data = (struct saved_data *)calloc(1, sizeof(*data));
386 	if (!data)
387 		return -1;
388 
389 	rec->data = data;
390 	rec->func_close = &closef;
391 	rec->func_stats = &stats;
392 	rec->func_lookup = &property_lookup;
393 
394 	return init(rec, opts, nopts);
395 }
396 
selabel_exact_match_init(struct selabel_handle * rec,const struct selinux_opt * opts,unsigned nopts)397 int selabel_exact_match_init(struct selabel_handle *rec,
398 		const struct selinux_opt *opts, unsigned nopts)
399 {
400 	struct saved_data *data;
401 
402 	data = (struct saved_data *)calloc(1, sizeof(*data));
403 	if (!data)
404 		return -1;
405 
406 	rec->data = data;
407 	rec->func_close = &closef;
408 	rec->func_stats = &stats;
409 	rec->func_lookup = &lookup_exact_match;
410 
411 	return init(rec, opts, nopts);
412 }
413