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