• 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,const char * path)50 static 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 
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 		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(&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 
init(struct selabel_handle * rec,const struct selinux_opt * opts,unsigned n)143 static 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 		n--;
157 		switch (opts[n].type) {
158 		case SELABEL_OPT_PATH:
159 			path = opts[n].value;
160 			break;
161 		case SELABEL_OPT_UNUSED:
162 		case SELABEL_OPT_VALIDATE:
163 		case SELABEL_OPT_DIGEST:
164 			break;
165 		default:
166 			errno = EINVAL;
167 			return -1;
168 		}
169 	}
170 
171 	if (!path)
172 		return -1;
173 
174 	/* Open the specification file. */
175 	if ((fp = fopen(path, "re")) == NULL)
176 		return -1;
177 
178 	if (fstat(fileno(fp), &sb) < 0)
179 		goto finish;
180 	errno = EINVAL;
181 	if (!S_ISREG(sb.st_mode))
182 		goto finish;
183 
184 	/*
185 	 * Two passes of the specification file. First is to get the size.
186 	 * After the first pass, the spec array is malloced to the appropriate
187 	 * size. Second pass is to populate the spec array and check for
188 	 * dups.
189 	 */
190 	maxnspec = UINT_MAX / sizeof(spec_t);
191 	for (pass = 0; pass < 2; pass++) {
192 		data->nspec = 0;
193 		lineno = 0;
194 
195 		while (fgets(line_buf, sizeof(line_buf) - 1, fp)
196 		       && data->nspec < maxnspec) {
197 			if (process_line(rec, path, line_buf, pass, ++lineno)
198 									  != 0)
199 				goto finish;
200 		}
201 
202 		if (pass == 1) {
203 			status = nodups_specs(data, path);
204 
205 			if (status)
206 				goto finish;
207 		}
208 
209 		if (pass == 0) {
210 			if (data->nspec == 0) {
211 				status = 0;
212 				goto finish;
213 			}
214 
215 			if (NULL == (data->spec_arr =
216 				     calloc(data->nspec, sizeof(spec_t))))
217 				goto finish;
218 
219 			maxnspec = data->nspec;
220 
221 			status = fseek(fp, 0L, SEEK_SET);
222 			if (status == -1)
223 				goto finish;
224 		}
225 	}
226 
227 	qsort(data->spec_arr, data->nspec, sizeof(struct spec), cmp);
228 
229 	status = digest_add_specfile(rec->digest, fp, NULL, sb.st_size, path);
230 	if (status)
231 		goto finish;
232 
233 	digest_gen_hash(rec->digest);
234 
235 finish:
236 	fclose(fp);
237 	return status;
238 }
239 
240 /*
241  * Backend interface routines
242  */
closef(struct selabel_handle * rec)243 static void closef(struct selabel_handle *rec)
244 {
245 	struct saved_data *data = (struct saved_data *)rec->data;
246 	struct spec *spec;
247 	unsigned int i;
248 
249 	if (!data)
250 		return;
251 
252 	for (i = 0; i < data->nspec; i++) {
253 		spec = &data->spec_arr[i];
254 		free(spec->property_key);
255 		free(spec->lr.ctx_raw);
256 		free(spec->lr.ctx_trans);
257 	}
258 
259 	if (data->spec_arr)
260 		free(data->spec_arr);
261 
262 	free(data);
263 }
264 
property_lookup(struct selabel_handle * rec,const char * key,int type)265 static struct selabel_lookup_rec *property_lookup(struct selabel_handle *rec,
266 					 const char *key,
267 					 int __attribute__((unused)) type)
268 {
269 	struct saved_data *data = (struct saved_data *)rec->data;
270 	spec_t *spec_arr = data->spec_arr;
271 	unsigned int i;
272 	struct selabel_lookup_rec *ret = NULL;
273 
274 	if (!data->nspec) {
275 		errno = ENOENT;
276 		goto finish;
277 	}
278 
279 	for (i = 0; i < data->nspec; i++) {
280 		if (strncmp(spec_arr[i].property_key, key,
281 			    strlen(spec_arr[i].property_key)) == 0) {
282 			break;
283 		}
284 		if (strncmp(spec_arr[i].property_key, "*", 1) == 0)
285 			break;
286 	}
287 
288 	if (i >= data->nspec) {
289 		/* No matching specification. */
290 		errno = ENOENT;
291 		goto finish;
292 	}
293 
294 	ret = &spec_arr[i].lr;
295 
296 finish:
297 	return ret;
298 }
299 
service_lookup(struct selabel_handle * rec,const char * key,int type)300 static struct selabel_lookup_rec *service_lookup(struct selabel_handle *rec,
301 		const char *key, int __attribute__((unused)) type)
302 {
303 	struct saved_data *data = (struct saved_data *)rec->data;
304 	spec_t *spec_arr = data->spec_arr;
305 	unsigned int i;
306 	struct selabel_lookup_rec *ret = NULL;
307 
308 	if (!data->nspec) {
309 		errno = ENOENT;
310 		goto finish;
311 	}
312 
313 	for (i = 0; i < data->nspec; i++) {
314 		if (strcmp(spec_arr[i].property_key, key) == 0)
315 			break;
316 		if (strcmp(spec_arr[i].property_key, "*") == 0)
317 			break;
318 	}
319 
320 	if (i >= data->nspec) {
321 		/* No matching specification. */
322 		errno = ENOENT;
323 		goto finish;
324 	}
325 
326 	ret = &spec_arr[i].lr;
327 
328 finish:
329 	return ret;
330 }
331 
stats(struct selabel_handle * rec)332 static void stats(struct selabel_handle __attribute__((unused)) *rec)
333 {
334 	selinux_log(SELINUX_WARNING, "'stats' functionality not implemented.\n");
335 }
336 
selabel_property_init(struct selabel_handle * rec,const struct selinux_opt * opts,unsigned nopts)337 int selabel_property_init(struct selabel_handle *rec,
338 			  const struct selinux_opt *opts,
339 			  unsigned nopts)
340 {
341 	struct saved_data *data;
342 
343 	data = (struct saved_data *)calloc(1, sizeof(*data));
344 	if (!data)
345 		return -1;
346 
347 	rec->data = data;
348 	rec->func_close = &closef;
349 	rec->func_stats = &stats;
350 	rec->func_lookup = &property_lookup;
351 
352 	return init(rec, opts, nopts);
353 }
354 
selabel_service_init(struct selabel_handle * rec,const struct selinux_opt * opts,unsigned nopts)355 int selabel_service_init(struct selabel_handle *rec,
356 		const struct selinux_opt *opts, unsigned nopts)
357 {
358 	struct saved_data *data;
359 
360 	data = (struct saved_data *)calloc(1, sizeof(*data));
361 	if (!data)
362 		return -1;
363 
364 	rec->data = data;
365 	rec->func_close = &closef;
366 	rec->func_stats = &stats;
367 	rec->func_lookup = &service_lookup;
368 
369 	return init(rec, opts, nopts);
370 }
371