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