xref: /aosp_15_r20/external/selinux/libselinux/src/android/android_seapp.c (revision 2d543d20722ada2425b5bdab9d0d1d29470e7bba)
1 #include <ctype.h>
2 #include <limits.h>
3 #include <linux/magic.h>
4 #include <pwd.h>
5 #include <stdbool.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <unistd.h>
10 
11 #include <private/android_filesystem_config.h>
12 #include <selinux/android.h>
13 #include <selinux/context.h>
14 #include <selinux/selinux.h>
15 
16 #include "android_internal.h"
17 #include "callbacks.h"
18 #include "selinux_internal.h"
19 
20 /* Locations for the file_contexts files. For each partition, only the first
21  * existing entry will be used (for example, if
22  * /system/etc/selinux/plat_file_contexts exists, /plat_file_contexts will be
23  * ignored).
24  */
25 static const path_alts_t file_context_paths = { .paths = {
26 	{
27 		"/system/etc/selinux/plat_file_contexts",
28 		"/plat_file_contexts"
29 	},
30 	{
31 		"/system_ext/etc/selinux/system_ext_file_contexts",
32 		"/system_ext_file_contexts"
33 	},
34 	{
35 		"/product/etc/selinux/product_file_contexts",
36 		"/product_file_contexts"
37 	},
38 	{
39 		"/vendor/etc/selinux/vendor_file_contexts",
40 		"/vendor_file_contexts"
41 	},
42 	{
43 		"/odm/etc/selinux/odm_file_contexts",
44 		"/odm_file_contexts"
45 	}
46 }};
47 
48 /* Locations for the seapp_contexts files, and corresponding partitions. For
49  * each partition, only the first existing entry will be used (for example, if
50  * /system/etc/selinux/plat_seapp_contexts exists, /plat_seapp_contexts will be
51  * ignored).
52  *
53  * PLEASE KEEP IN SYNC WITH:
54  * hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
55  */
56 static const path_alts_t seapp_context_paths = { .paths = {
57 	{
58 		"/system/etc/selinux/plat_seapp_contexts",
59 		"/plat_seapp_contexts"
60 	},
61 	{
62 		"/system_ext/etc/selinux/system_ext_seapp_contexts",
63 		"/system_ext_seapp_contexts"
64 	},
65 	{
66 		"/product/etc/selinux/product_seapp_contexts",
67 		"/product_seapp_contexts"
68 	},
69 	{
70 		"/vendor/etc/selinux/vendor_seapp_contexts",
71 		"/vendor_seapp_contexts"
72 	},
73 	{
74 		"/odm/etc/selinux/odm_seapp_contexts",
75 		"/odm_seapp_contexts"
76 	}
77 }, .partitions= {
78 	"system",
79 	"system_ext",
80 	"product",
81 	"vendor",
82 	"odm"
83 }};
84 
85 static pthread_once_t fc_once = PTHREAD_ONCE_INIT;
86 static struct selabel_handle* seapp_fc_sehandle = NULL;
87 
selinux_android_file_context_handle_init(void)88 void selinux_android_file_context_handle_init(void)
89 {
90 	const char* file_contexts[MAX_CONTEXT_PATHS];
91 	struct selinux_opt opts[MAX_CONTEXT_PATHS + 1];
92 	int npaths, nopts;
93 
94 	npaths = find_existing_files(&file_context_paths, file_contexts);
95 	paths_to_opts(file_contexts, npaths, opts);
96 
97 	opts[npaths].type = SELABEL_OPT_BASEONLY;
98 	opts[npaths].value = (char *) 1;
99 	nopts = npaths + 1;
100 
101 	seapp_fc_sehandle = initialize_backend(SELABEL_CTX_FILE, "file", opts, nopts);
102 }
103 
104 /* Returns a handle for the file contexts backend, initialized with the Android
105  * configuration */
selinux_android_file_context_handle(void)106 struct selabel_handle* selinux_android_file_context_handle(void)
107 {
108 	__selinux_once(fc_once, selinux_android_file_context_handle_init);
109 	return seapp_fc_sehandle;
110 }
111 
112 #if DEBUG
113 static char const * const levelFromName[] = {
114 	"none",
115 	"app",
116 	"user",
117 	"all"
118 };
119 #endif
120 
121 struct prefix_str {
122 	size_t len;
123 	char *str;
124 	char is_prefix;
125 };
126 
free_prefix_str(struct prefix_str * p)127 static void free_prefix_str(struct prefix_str *p)
128 {
129 	if (!p)
130 		return;
131 	free(p->str);
132 }
133 
134 /* For a set of selectors, represents the contexts that should be applied to an
135  * app and its data. Each instance is based on a line in a seapp_contexts file.
136  * */
137 struct seapp_context {
138 	/* input selectors */
139 	bool isSystemServer;
140 	bool isEphemeralAppSet;
141 	bool isEphemeralApp;
142 	struct prefix_str user;
143 	char *seinfo;
144 	struct prefix_str name;
145 	bool isPrivAppSet;
146 	bool isPrivApp;
147 	int32_t minTargetSdkVersion;
148 	bool fromRunAs;
149 	bool isIsolatedComputeApp;
150 	bool isSdkSandboxAudit;
151 	bool isSdkSandboxNext;
152 	/* outputs */
153 	char *domain;
154 	char *type;
155 	char *level;
156 	enum levelFrom levelFrom;
157 	const char* partition;
158 };
159 
free_seapp_context(struct seapp_context * s)160 static void free_seapp_context(struct seapp_context *s)
161 {
162 	if (!s)
163 		return;
164 
165 	free_prefix_str(&s->user);
166 	free(s->seinfo);
167 	free_prefix_str(&s->name);
168 	free(s->domain);
169 	free(s->type);
170 	free(s->level);
171 }
172 
is_platform(const char * partition)173 static bool is_platform(const char *partition) {
174 	// system, system_ext, product are regarded as "platform", whereas vendor
175 	// and odm are regarded as vendor.
176 	if (strcmp(partition, "system") == 0) return true;
177 	if (strcmp(partition, "system_ext") == 0) return true;
178 	if (strcmp(partition, "product") == 0) return true;
179 	return false;
180 }
181 
182 /* Compare two seapp_context. Used to sort all the entries found. */
seapp_context_cmp(const void * A,const void * B)183 static int seapp_context_cmp(const void *A, const void *B)
184 {
185 	const struct seapp_context *const *sp1 = (const struct seapp_context *const *) A;
186 	const struct seapp_context *const *sp2 = (const struct seapp_context *const *) B;
187 	const struct seapp_context *s1 = *sp1, *s2 = *sp2;
188 
189 	/* Give precedence to isSystemServer=true. */
190 	if (s1->isSystemServer != s2->isSystemServer)
191 		return (s1->isSystemServer ? -1 : 1);
192 
193 	/* Give precedence to a specified isEphemeral= over an
194 	 * unspecified isEphemeral=. */
195 	if (s1->isEphemeralAppSet != s2->isEphemeralAppSet)
196 		return (s1->isEphemeralAppSet ? -1 : 1);
197 
198 	/* Give precedence to a specified user= over an unspecified user=. */
199 	if (s1->user.str && !s2->user.str)
200 		return -1;
201 	if (!s1->user.str && s2->user.str)
202 		return 1;
203 
204 	if (s1->user.str) {
205 		/* Give precedence to a fixed user= string over a prefix. */
206 		if (s1->user.is_prefix != s2->user.is_prefix)
207 			return (s2->user.is_prefix ? -1 : 1);
208 
209 		/* Give precedence to a longer prefix over a shorter prefix. */
210 		if (s1->user.is_prefix && s1->user.len != s2->user.len)
211 			return (s1->user.len > s2->user.len) ? -1 : 1;
212 	}
213 
214 	/* Give precedence to a specified seinfo= over an unspecified seinfo=. */
215 	if (s1->seinfo && !s2->seinfo)
216 		return -1;
217 	if (!s1->seinfo && s2->seinfo)
218 		return 1;
219 
220 	/* Give precedence to a specified name= over an unspecified name=. */
221 	if (s1->name.str && !s2->name.str)
222 		return -1;
223 	if (!s1->name.str && s2->name.str)
224 		return 1;
225 
226 	if (s1->name.str) {
227 		/* Give precedence to a fixed name= string over a prefix. */
228 		if (s1->name.is_prefix != s2->name.is_prefix)
229 			return (s2->name.is_prefix ? -1 : 1);
230 
231 		/* Give precedence to a longer prefix over a shorter prefix. */
232 		if (s1->name.is_prefix && s1->name.len != s2->name.len)
233 			return (s1->name.len > s2->name.len) ? -1 : 1;
234 	}
235 
236 	/* Give precedence to a specified isPrivApp= over an unspecified isPrivApp=. */
237 	if (s1->isPrivAppSet != s2->isPrivAppSet)
238 		return (s1->isPrivAppSet ? -1 : 1);
239 
240 	/* Give precedence to a higher minTargetSdkVersion= over a lower minTargetSdkVersion=.
241 	 * If unspecified, minTargetSdkVersion has a default value of 0.
242 	 */
243 	if (s1->minTargetSdkVersion > s2->minTargetSdkVersion)
244 		return -1;
245 	else if (s1->minTargetSdkVersion < s2->minTargetSdkVersion)
246 		return 1;
247 
248 	/* Give precedence to fromRunAs=true. */
249 	if (s1->fromRunAs != s2->fromRunAs)
250 		return (s1->fromRunAs ? -1 : 1);
251 
252 	/* Give precedence to platform side contexts */
253 	bool isS1Platform = is_platform(s1->partition);
254 	bool isS2Platform = is_platform(s2->partition);
255 	if (isS1Platform != isS2Platform)
256 		return (isS1Platform ? -1 : 1);
257 
258 	/* Anything else has equal precedence. */
259 	return 0;
260 }
261 
262 /* Array of all the seapp_context entries configured. */
263 static struct seapp_context **seapp_contexts = NULL;
264 /* Size of seapp_contexts */
265 static int nspec = 0;
266 
free_seapp_contexts(void)267 static void free_seapp_contexts(void)
268 {
269 	int n;
270 
271 	if (!seapp_contexts)
272 		return;
273 
274 	for (n = 0; n < nspec; n++)
275 		free_seapp_context(seapp_contexts[n]);
276 
277 	free(seapp_contexts);
278 	seapp_contexts = NULL;
279 	nspec = 0;
280 }
281 
get_minTargetSdkVersion(const char * value)282 static int32_t get_minTargetSdkVersion(const char *value)
283 {
284 	char *endptr;
285 	long minTargetSdkVersion;
286 	minTargetSdkVersion = strtol(value, &endptr, 10);
287 	if (('\0' != *endptr) || (minTargetSdkVersion < 0) || (minTargetSdkVersion > INT32_MAX)) {
288 		return -1; /* error parsing minTargetSdkVersion */
289 	} else {
290 		return (int32_t) minTargetSdkVersion;
291 	}
292 }
293 
seapp_context_reload_internal(const path_alts_t * context_paths)294 int seapp_context_reload_internal(const path_alts_t *context_paths)
295 {
296 	FILE *fp = NULL;
297 	char line_buf[BUFSIZ];
298 	char *token;
299 	unsigned lineno;
300 	struct seapp_context *cur;
301 	char *p, *name = NULL, *value = NULL, *saveptr;
302 	size_t i, len, files_len = 0;
303 	int ret;
304 	const char* seapp_contexts_files[MAX_CONTEXT_PATHS];
305 	const char* seapp_contexts_partitions[MAX_CONTEXT_PATHS];
306 
307 	files_len = find_existing_files_with_partitions(context_paths, seapp_contexts_files, seapp_contexts_partitions);
308 
309 	/* Reset the current entries */
310 	free_seapp_contexts();
311 
312 	nspec = 0;
313 	for (i = 0; i < files_len; i++) {
314 		fp = fopen(seapp_contexts_files[i], "re");
315 		if (!fp) {
316 			selinux_log(SELINUX_ERROR, "%s:  could not open seapp_contexts file: %s",
317 				    __FUNCTION__, seapp_contexts_files[i]);
318 			return -1;
319 		}
320 		while (fgets(line_buf, sizeof line_buf - 1, fp)) {
321 			p = line_buf;
322 			while (isspace(*p))
323 				p++;
324 			if (*p == '#' || *p == 0)
325 				continue;
326 			nspec++;
327 		}
328 		fclose(fp);
329 	}
330 
331 	seapp_contexts = (struct seapp_context **) calloc(nspec, sizeof(struct seapp_context *));
332 	if (!seapp_contexts)
333 		goto oom;
334 
335 	nspec = 0;
336 	for (i = 0; i < files_len; i++) {
337 		lineno = 1;
338 		fp = fopen(seapp_contexts_files[i], "re");
339 		if (!fp) {
340 			selinux_log(SELINUX_ERROR, "%s:  could not open seapp_contexts file: %s",
341 				    __FUNCTION__, seapp_contexts_files[i]);
342 			free_seapp_contexts();
343 			return -1;
344 		}
345 		while (fgets(line_buf, sizeof line_buf - 1, fp)) {
346 			len = strlen(line_buf);
347 			if (len == 0) {
348 				// line contains a NUL byte as its first entry
349 				goto err;
350 			}
351 			if (line_buf[len - 1] == '\n')
352 				line_buf[len - 1] = 0;
353 			p = line_buf;
354 			while (isspace(*p))
355 				p++;
356 			if (*p == '#' || *p == 0)
357 				continue;
358 
359 			cur = (struct seapp_context *) calloc(1, sizeof(struct seapp_context));
360 			if (!cur)
361 				goto oom;
362 
363 			token = strtok_r(p, " \t", &saveptr);
364 			if (!token) {
365 				free_seapp_context(cur);
366 				goto err;
367 			}
368 
369 			while (1) {
370 				name = token;
371 				value = strchr(name, '=');
372 				if (!value) {
373 					free_seapp_context(cur);
374 					goto err;
375 				}
376 				*value++ = 0;
377 
378 				if (!strcasecmp(name, "isSystemServer")) {
379 					if (!strcasecmp(value, "true"))
380 						cur->isSystemServer = true;
381 					else if (!strcasecmp(value, "false"))
382 						cur->isSystemServer = false;
383 					else {
384 						free_seapp_context(cur);
385 						goto err;
386 					}
387 				} else if (!strcasecmp(name, "isEphemeralApp")) {
388 					cur->isEphemeralAppSet = true;
389 					if (!strcasecmp(value, "true"))
390 						cur->isEphemeralApp = true;
391 					else if (!strcasecmp(value, "false"))
392 						cur->isEphemeralApp = false;
393 					else {
394 						free_seapp_context(cur);
395 						goto err;
396 					}
397 				} else if (!strcasecmp(name, "user")) {
398 					if (cur->user.str) {
399 						free_seapp_context(cur);
400 						goto err;
401 					}
402 					cur->user.str = strdup(value);
403 					if (!cur->user.str) {
404 						free_seapp_context(cur);
405 						goto oom;
406 					}
407 					cur->user.len = strlen(cur->user.str);
408 					if (cur->user.str[cur->user.len-1] == '*')
409 						cur->user.is_prefix = 1;
410 				} else if (!strcasecmp(name, "seinfo")) {
411 					if (cur->seinfo) {
412 						free_seapp_context(cur);
413 						goto err;
414 					}
415 					cur->seinfo = strdup(value);
416 					if (!cur->seinfo) {
417 						free_seapp_context(cur);
418 						goto oom;
419 					}
420 					if (strstr(value, ":")) {
421 						free_seapp_context(cur);
422 						goto err;
423 					}
424 				} else if (!strcasecmp(name, "name")) {
425 					if (cur->name.str) {
426 						free_seapp_context(cur);
427 						goto err;
428 					}
429 					cur->name.str = strdup(value);
430 					if (!cur->name.str) {
431 						free_seapp_context(cur);
432 						goto oom;
433 					}
434 					cur->name.len = strlen(cur->name.str);
435 					if (cur->name.str[cur->name.len-1] == '*')
436 						cur->name.is_prefix = 1;
437 				} else if (!strcasecmp(name, "domain")) {
438 					if (cur->domain) {
439 						free_seapp_context(cur);
440 						goto err;
441 					}
442 					cur->domain = strdup(value);
443 					if (!cur->domain) {
444 						free_seapp_context(cur);
445 						goto oom;
446 					}
447 				} else if (!strcasecmp(name, "type")) {
448 					if (cur->type) {
449 						free_seapp_context(cur);
450 						goto err;
451 					}
452 					cur->type = strdup(value);
453 					if (!cur->type) {
454 						free_seapp_context(cur);
455 						goto oom;
456 					}
457 				} else if (!strcasecmp(name, "levelFrom")) {
458 					if (cur->levelFrom) {
459 						free_seapp_context(cur);
460 						goto err;
461 					}
462 					if (!strcasecmp(value, "none"))
463 						cur->levelFrom = LEVELFROM_NONE;
464 					else if (!strcasecmp(value, "app"))
465 						cur->levelFrom = LEVELFROM_APP;
466 					else if (!strcasecmp(value, "user"))
467 						cur->levelFrom = LEVELFROM_USER;
468 					else if (!strcasecmp(value, "all"))
469 						cur->levelFrom = LEVELFROM_ALL;
470 					else {
471 						free_seapp_context(cur);
472 						goto err;
473 					}
474 				} else if (!strcasecmp(name, "level")) {
475 					if (cur->level) {
476 						free_seapp_context(cur);
477 						goto err;
478 					}
479 					cur->level = strdup(value);
480 					if (!cur->level) {
481 						free_seapp_context(cur);
482 						goto oom;
483 					}
484 				} else if (!strcasecmp(name, "isPrivApp")) {
485 					cur->isPrivAppSet = true;
486 					if (!strcasecmp(value, "true"))
487 						cur->isPrivApp = true;
488 					else if (!strcasecmp(value, "false"))
489 						cur->isPrivApp = false;
490 					else {
491 						free_seapp_context(cur);
492 						goto err;
493 					}
494 				} else if (!strcasecmp(name, "minTargetSdkVersion")) {
495 					cur->minTargetSdkVersion = get_minTargetSdkVersion(value);
496 					if (cur->minTargetSdkVersion < 0) {
497 						free_seapp_context(cur);
498 						goto err;
499 					}
500 				} else if (!strcasecmp(name, "fromRunAs")) {
501 					if (!strcasecmp(value, "true"))
502 						cur->fromRunAs = true;
503 					else if (!strcasecmp(value, "false"))
504 						cur->fromRunAs = false;
505 					else {
506 						free_seapp_context(cur);
507 						goto err;
508 					}
509 				} else if (!strcasecmp(name, "isIsolatedComputeApp")) {
510 					if (!strcasecmp(value, "true"))
511 						cur->isIsolatedComputeApp = true;
512 					else if (!strcasecmp(value, "false"))
513 						cur->isIsolatedComputeApp = false;
514 					else {
515 						free_seapp_context(cur);
516 						goto err;
517 					}
518 				} else if (!strcasecmp(name, "isSdkSandboxAudit")) {
519 					if (!strcasecmp(value, "true"))
520 						cur->isSdkSandboxAudit = true;
521 					else if (!strcasecmp(value, "false"))
522 						cur->isSdkSandboxAudit = false;
523 					else {
524 						free_seapp_context(cur);
525 						goto err;
526 					}
527 				} else if (!strcasecmp(name, "isSdkSandboxNext")) {
528 					if (!strcasecmp(value, "true"))
529 						cur->isSdkSandboxNext = true;
530 					else if (!strcasecmp(value, "false"))
531 						cur->isSdkSandboxNext = false;
532 					else {
533 						free_seapp_context(cur);
534 						goto err;
535 					}
536 				} else {
537 					free_seapp_context(cur);
538 					goto err;
539 				}
540 
541 				token = strtok_r(NULL, " \t", &saveptr);
542 				if (!token)
543 					break;
544 			}
545 
546 			if (!cur->isPrivApp && cur->name.str &&
547 			    (!cur->seinfo || !strcmp(cur->seinfo, "default"))) {
548 				selinux_log(SELINUX_ERROR, "%s:  No specific seinfo value specified with name=\"%s\", on line %u:  insecure configuration!\n",
549 					    seapp_contexts_files[i], cur->name.str, lineno);
550 				free_seapp_context(cur);
551 				goto err;
552 			}
553 
554 			cur->partition = seapp_contexts_partitions[i];
555 			seapp_contexts[nspec] = cur;
556 			nspec++;
557 			lineno++;
558 		}
559 		fclose(fp);
560 		fp = NULL;
561 	}
562 
563 	qsort(seapp_contexts, nspec, sizeof(struct seapp_context *),
564 	      seapp_context_cmp);
565 
566 	for (int i = 0; i < nspec; i++) {
567 		const struct seapp_context *s1 = seapp_contexts[i];
568 		for (int j = i + 1; j < nspec; j++) {
569 			const struct seapp_context *s2 = seapp_contexts[j];
570 			if (seapp_context_cmp(&s1, &s2) != 0)
571 				break;
572 			/*
573 			* Check for a duplicated entry on the input selectors.
574 			* We already compared isSystemServer with seapp_context_cmp.
575 			* We also have already checked that both entries specify the same
576 			* string fields, so if s1 has a non-NULL string, then so does s2.
577 			*/
578 			bool dup = (!s1->user.str || !strcmp(s1->user.str, s2->user.str)) &&
579 				(!s1->seinfo || !strcmp(s1->seinfo, s2->seinfo)) &&
580 				(!s1->name.str || !strcmp(s1->name.str, s2->name.str)) &&
581 				(!s1->isPrivAppSet || s1->isPrivApp == s2->isPrivApp) &&
582 				(!s1->isEphemeralAppSet || s1->isEphemeralApp == s2->isEphemeralApp) &&
583 				(s1->isIsolatedComputeApp == s2->isIsolatedComputeApp) &&
584 				(s1->isSdkSandboxAudit == s2->isSdkSandboxAudit) &&
585 				(s1->isSdkSandboxNext == s2->isSdkSandboxNext);
586 
587 			if (dup) {
588 				selinux_log(SELINUX_ERROR, "seapp_contexts:  Duplicated entry\n");
589 				if (s1->user.str)
590 					selinux_log(SELINUX_ERROR, " user=%s\n", s1->user.str);
591 				if (s1->seinfo)
592 					selinux_log(SELINUX_ERROR, " seinfo=%s\n", s1->seinfo);
593 				if (s1->name.str)
594 					selinux_log(SELINUX_ERROR, " name=%s\n", s1->name.str);
595 				if (s1->partition)
596 					selinux_log(SELINUX_ERROR, " partition=%s\n", s1->partition);
597 				goto err_no_log;
598 			}
599 		}
600 	}
601 
602 #if DEBUG
603 	{
604 		int i;
605 		for (i = 0; i < nspec; i++) {
606 			cur = seapp_contexts[i];
607 			selinux_log(SELINUX_INFO, "%s:  isSystemServer=%s isEphemeralApp=%s "
608 				"isIsolatedComputeApp=%s isSdkSandboxAudit=%s isSdkSandboxNext=%s "
609 				"user=%s seinfo=%s name=%s isPrivApp=%s minTargetSdkVersion=%d "
610 				"fromRunAs=%s -> domain=%s type=%s level=%s levelFrom=%s",
611 				__FUNCTION__,
612 				cur->isSystemServer ? "true" : "false",
613 				cur->isEphemeralAppSet ? (cur->isEphemeralApp ? "true" : "false") : "null",
614 				cur->isIsolatedComputeApp ? "true" : "false",
615 				cur->isSdkSandboxAudit ? "true" : "false",
616 				cur->isSdkSandboxNext ? "true" : "false",
617 				cur->user.str,
618 				cur->seinfo, cur->name.str,
619 				cur->isPrivAppSet ? (cur->isPrivApp ? "true" : "false") : "null",
620 				cur->minTargetSdkVersion,
621 				cur->fromRunAs ? "true" : "false",
622 				cur->domain, cur->type, cur->level,
623 				levelFromName[cur->levelFrom]);
624 		}
625 	}
626 #endif
627 
628 	ret = 0;
629 
630 out:
631 	if (fp) {
632 		fclose(fp);
633 	}
634 	return ret;
635 
636 err:
637 	selinux_log(SELINUX_ERROR, "%s:  Invalid entry on line %u\n",
638 		    seapp_contexts_files[i], lineno);
639 err_no_log:
640 	free_seapp_contexts();
641 	ret = -1;
642 	goto out;
643 oom:
644 	selinux_log(SELINUX_ERROR,
645 		    "%s:  Out of memory\n", __FUNCTION__);
646 	free_seapp_contexts();
647 	ret = -1;
648 	goto out;
649 }
650 
selinux_android_seapp_context_reload(void)651 int selinux_android_seapp_context_reload(void)
652 {
653 	return seapp_context_reload_internal(&seapp_context_paths);
654 }
655 
656 /* indirection to support pthread_once */
seapp_context_init(void)657 static void seapp_context_init(void)
658 {
659 	selinux_android_seapp_context_reload();
660 }
661 
662 static pthread_once_t seapp_once = PTHREAD_ONCE_INIT;
663 
selinux_android_seapp_context_init(void)664 void selinux_android_seapp_context_init(void) {
665 	__selinux_once(seapp_once, seapp_context_init);
666 }
667 
668 /*
669  * Max id that can be mapped to category set uniquely
670  * using the current scheme.
671  */
672 #define CAT_MAPPING_MAX_ID (0x1<<16)
673 
674 #define PRIVILEGED_APP_STR "privapp"
675 #define ISOLATED_COMPUTE_APP_STR "isolatedComputeApp"
676 #define APPLY_SDK_SANDBOX_AUDIT_RESTRICTIONS_STR "isSdkSandboxAudit"
677 #define APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS_STR "isSdkSandboxNext"
678 #define EPHEMERAL_APP_STR "ephemeralapp"
679 #define TARGETSDKVERSION_STR "targetSdkVersion"
680 #define PARTITION_STR "partition"
681 #define FROM_RUNAS_STR "fromRunAs"
682 #define COMPLETE_STR "complete"
683 
is_preinstalled_app_partition_valid(const char * app_policy,const char * app_partition)684 static bool is_preinstalled_app_partition_valid(const char *app_policy, const char *app_partition) {
685 	// We forbid system/system_ext/product installed apps from being labeled with vendor sepolicy.
686 	// So, either the app shouldn't be platform, or the spec should be platform.
687 	return !(is_platform(app_partition) && !is_platform(app_policy));
688 }
689 
690 /* Sets the categories of ctx based on the level request */
set_range_from_level(context_t ctx,enum levelFrom levelFrom,uid_t userid,uid_t appid)691 int set_range_from_level(context_t ctx, enum levelFrom levelFrom, uid_t userid, uid_t appid)
692 {
693 	char level[255];
694 	switch (levelFrom) {
695 	case LEVELFROM_NONE:
696 		strncpy(level, "s0", sizeof level);
697 		break;
698 	case LEVELFROM_APP:
699 		snprintf(level, sizeof level, "s0:c%u,c%u",
700 			 appid & 0xff,
701 			 256 + (appid>>8 & 0xff));
702 		break;
703 	case LEVELFROM_USER:
704 		snprintf(level, sizeof level, "s0:c%u,c%u",
705 			 512 + (userid & 0xff),
706 			 768 + (userid>>8 & 0xff));
707 		break;
708 	case LEVELFROM_ALL:
709 		snprintf(level, sizeof level, "s0:c%u,c%u,c%u,c%u",
710 			 appid & 0xff,
711 			 256 + (appid>>8 & 0xff),
712 			 512 + (userid & 0xff),
713 			 768 + (userid>>8 & 0xff));
714 		break;
715 	default:
716 		return -1;
717 	}
718 	if (context_range_set(ctx, level)) {
719 		return -2;
720 	}
721 	return 0;
722 }
723 
parse_seinfo(const char * seinfo,struct parsed_seinfo * info)724 int parse_seinfo(const char* seinfo, struct parsed_seinfo* info) {
725 	char local_seinfo[SEINFO_BUFSIZ];
726 
727 	memset(info, 0, sizeof(*info));
728 
729 	if (strlen(seinfo) >= SEINFO_BUFSIZ) {
730 		selinux_log(SELINUX_ERROR, "%s:  seinfo is too large to be parsed: %zu\n",
731 				__FUNCTION__, strlen(seinfo));
732 		return -1;
733 	}
734 	strncpy(local_seinfo, seinfo, SEINFO_BUFSIZ);
735 
736 	char *token;
737 	char *saved_colon_ptr = NULL;
738 	char *saved_equal_ptr;
739 	bool first = true;
740 	for (token = strtok_r(local_seinfo, ":", &saved_colon_ptr); token; token = strtok_r(NULL, ":", &saved_colon_ptr)) {
741 		if (first) {
742 			strncpy(info->base, token, SEINFO_BUFSIZ);
743 			first = false;
744 			continue;
745 		}
746 		if (!strcmp(token, PRIVILEGED_APP_STR)) {
747 			info->is |= IS_PRIV_APP;
748 			continue;
749 		}
750 		if (!strcmp(token, EPHEMERAL_APP_STR)) {
751 			info->is |= IS_EPHEMERAL_APP;
752 			continue;
753 		}
754 		if (!strcmp(token, ISOLATED_COMPUTE_APP_STR)) {
755 			info->is |= IS_ISOLATED_COMPUTE_APP;
756 			continue;
757 		}
758 		if (!strcmp(token, APPLY_SDK_SANDBOX_AUDIT_RESTRICTIONS_STR)) {
759 			info->is |= IS_SDK_SANDBOX_AUDIT;
760 			continue;
761 		}
762 		if (!strcmp(token, APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS_STR)) {
763 			info->is |= IS_SDK_SANDBOX_NEXT;
764 			continue;
765 		}
766 		if (!strcmp(token, FROM_RUNAS_STR)) {
767 			info->is |= IS_FROM_RUN_AS;
768 			continue;
769 		}
770 		if (!strncmp(token, TARGETSDKVERSION_STR, strlen(TARGETSDKVERSION_STR))) {
771 			saved_equal_ptr = NULL;
772 			char *subtoken = strtok_r(token, "=", &saved_equal_ptr);
773 			subtoken = strtok_r(NULL, "=", &saved_equal_ptr);
774 			if (!subtoken) {
775 				selinux_log(SELINUX_ERROR, "%s:  Invalid targetSdkVersion: %s in %s\n",
776 						__FUNCTION__, token, seinfo);
777 				return -1;
778 			}
779 			info->targetSdkVersion = strtol(subtoken, NULL, 10);
780 			continue;
781 		}
782 		if (!strncmp(token, PARTITION_STR, strlen(PARTITION_STR))) {
783 			saved_equal_ptr = NULL;
784 			char *subtoken = strtok_r(token, "=", &saved_equal_ptr);
785 			subtoken = strtok_r(NULL, "=", &saved_equal_ptr);
786 			if (!subtoken) {
787 				selinux_log(SELINUX_ERROR, "%s:  Invalid partition: %s in %s\n",
788 						__FUNCTION__, token, seinfo);
789 				return -1;
790 			}
791 			info->isPreinstalledApp = true;
792 			strncpy(info->partition, subtoken, strlen(subtoken));
793 			continue;
794 		}
795 		if (!strcmp(token, COMPLETE_STR)) {
796 			break;
797 		}
798 		selinux_log(SELINUX_WARNING, "%s:  Ignoring unknown seinfo field: %s in %s\n",
799 				__FUNCTION__, token, seinfo);
800 	}
801 	return 0;
802 }
803 
804 /*
805  * This code is Android specific, bionic guarantees that
806  * calls to non-reentrant getpwuid() are thread safe.
807  */
808 struct passwd *(*seapp_getpwuid)(uid_t uid) = getpwuid;
809 
seapp_context_lookup_internal(enum seapp_kind kind,uid_t uid,bool isSystemServer,const char * seinfo,const char * pkgname,context_t ctx)810 int seapp_context_lookup_internal(enum seapp_kind kind,
811 				uid_t uid,
812 				bool isSystemServer,
813 				const char *seinfo,
814 				const char *pkgname,
815 				context_t ctx)
816 {
817 	struct passwd *pwd;
818 	const char *username = NULL;
819 	struct seapp_context *cur = NULL;
820 	int i;
821 	uid_t userid;
822 	uid_t appid;
823 	struct parsed_seinfo info;
824 	memset(&info, 0, sizeof(info));
825 
826 	if (seinfo) {
827 		int ret = parse_seinfo(seinfo, &info);
828 		if (ret) {
829 			selinux_log(SELINUX_ERROR, "%s:  Invalid seinfo: %s\n", __FUNCTION__, seinfo);
830 			goto err;
831 		}
832 		if (info.targetSdkVersion < 0) {
833 			selinux_log(SELINUX_ERROR,
834 					"%s:  Invalid targetSdkVersion passed for app with uid %d, seinfo %s, name %s\n",
835 					__FUNCTION__, uid, seinfo, pkgname);
836 			goto err;
837 		}
838 	}
839 
840 	userid = uid / AID_USER_OFFSET;
841 	appid = uid % AID_USER_OFFSET;
842 	if (appid < AID_APP_START) {
843 		pwd = seapp_getpwuid(appid);
844 		if (!pwd)
845 			goto err;
846 		username = pwd->pw_name;
847 	} else if (appid < AID_SDK_SANDBOX_PROCESS_START) {
848 		username = "_app";
849 		appid -= AID_APP_START;
850 	} else if (appid < AID_ISOLATED_START) {
851 		username = "_sdksandbox";
852 		appid -= AID_SDK_SANDBOX_PROCESS_START;
853 	} else {
854 		username = "_isolated";
855 		appid -= AID_ISOLATED_START;
856 	}
857 
858 	if (appid >= CAT_MAPPING_MAX_ID || userid >= CAT_MAPPING_MAX_ID)
859 		goto err;
860 
861 	for (i = 0; i < nspec; i++) {
862 		cur = seapp_contexts[i];
863 
864 		if (cur->isSystemServer != isSystemServer)
865 			continue;
866 
867 		if (cur->isEphemeralAppSet && cur->isEphemeralApp != ((info.is & IS_EPHEMERAL_APP) != 0))
868 			continue;
869 
870 		if (cur->user.str) {
871 			if (cur->user.is_prefix) {
872 				if (strncasecmp(username, cur->user.str, cur->user.len-1))
873 					continue;
874 			} else {
875 				if (strcasecmp(username, cur->user.str))
876 					continue;
877 			}
878 		}
879 
880 		if (cur->seinfo) {
881 			if (!seinfo || strcasecmp(info.base, cur->seinfo))
882 				continue;
883 		}
884 
885 		if (cur->name.str) {
886 			if(!pkgname)
887 				continue;
888 
889 			if (cur->name.is_prefix) {
890 				if (strncasecmp(pkgname, cur->name.str, cur->name.len-1))
891 					continue;
892 			} else {
893 				if (strcasecmp(pkgname, cur->name.str))
894 					continue;
895 			}
896 		}
897 
898 		if (cur->isPrivAppSet && cur->isPrivApp != ((info.is & IS_PRIV_APP) != 0))
899 			continue;
900 
901 		if (cur->minTargetSdkVersion > info.targetSdkVersion)
902 			continue;
903 
904 		if (cur->fromRunAs != ((info.is & IS_FROM_RUN_AS) != 0))
905 			continue;
906 
907 		if (cur->isIsolatedComputeApp != ((info.is & IS_ISOLATED_COMPUTE_APP) != 0))
908 			continue;
909 
910 		if (cur->isSdkSandboxAudit != ((info.is & IS_SDK_SANDBOX_AUDIT) != 0))
911 			continue;
912 
913 		if (cur->isSdkSandboxNext != ((info.is & IS_SDK_SANDBOX_NEXT) != 0))
914 			continue;
915 
916 		if (kind == SEAPP_TYPE && !cur->type)
917 			continue;
918 		else if (kind == SEAPP_DOMAIN && !cur->domain)
919 			continue;
920 
921 		if (kind == SEAPP_TYPE) {
922 			if (context_type_set(ctx, cur->type))
923 				goto oom;
924 		} else if (kind == SEAPP_DOMAIN) {
925 			if (context_type_set(ctx, cur->domain))
926 				goto oom;
927 		}
928 
929 		if (cur->levelFrom != LEVELFROM_NONE) {
930 			int res = set_range_from_level(ctx, cur->levelFrom, userid, appid);
931 			if (res != 0) {
932 				return res;
933 			}
934 		} else if (cur->level) {
935 			if (context_range_set(ctx, cur->level))
936 				goto oom;
937 		}
938 
939 		if (info.isPreinstalledApp
940 				&& !is_preinstalled_app_partition_valid(cur->partition, info.partition)) {
941 			// TODO(b/280547417): make this an error after fixing violations
942 			selinux_log(SELINUX_WARNING,
943 				"%s:  App %s preinstalled to %s can't be labeled with %s sepolicy",
944 				__FUNCTION__, pkgname, info.partition, cur->partition);
945 		}
946 
947 		break;
948 	}
949 
950 	if (kind == SEAPP_DOMAIN && i == nspec) {
951 		/*
952 		 * No match.
953 		 * Fail to prevent staying in the zygote's context.
954 		 */
955 		selinux_log(SELINUX_ERROR,
956 			    "%s:  No match for app with uid %d, seinfo %s, name %s\n",
957 			    __FUNCTION__, uid, seinfo, pkgname);
958 
959 		if (security_getenforce() == 1)
960 			goto err;
961 	}
962 
963 	return 0;
964 err:
965 	return -1;
966 oom:
967 	return -2;
968 }
969 
seapp_context_lookup(enum seapp_kind kind,uid_t uid,bool isSystemServer,const char * seinfo,const char * pkgname,context_t ctx)970 int seapp_context_lookup(enum seapp_kind kind,
971 				uid_t uid,
972 				bool isSystemServer,
973 				const char *seinfo,
974 				const char *pkgname,
975 				context_t ctx)
976 {
977 	// Ensure the default context files are loaded.
978 	selinux_android_seapp_context_init();
979 	return seapp_context_lookup_internal(kind, uid, isSystemServer, seinfo, pkgname, ctx);
980 }
981