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