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