• 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 		"/dev/selinux/apex_file_contexts",
32 	},
33 	{
34 		"/system_ext/etc/selinux/system_ext_file_contexts",
35 		"/system_ext_file_contexts"
36 	},
37 	{
38 		"/product/etc/selinux/product_file_contexts",
39 		"/product_file_contexts"
40 	},
41 	{
42 		"/vendor/etc/selinux/vendor_file_contexts",
43 		"/vendor_file_contexts"
44 	},
45 	{
46 		"/odm/etc/selinux/odm_file_contexts",
47 		"/odm_file_contexts"
48 	}
49 }};
50 
51 /* Locations for the seapp_contexts files. For each partition, only the first
52  * existing entry will be used (for example, if
53  * /system/etc/selinux/plat_seapp_contexts exists, /plat_seapp_contexts will be
54  * ignored).
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 		"/dev/selinux/apex_seapp_contexts",
63 	},
64 	{
65 		"/system_ext/etc/selinux/system_ext_seapp_contexts",
66 		"/system_ext_seapp_contexts"
67 	},
68 	{
69 		"/product/etc/selinux/product_seapp_contexts",
70 		"/product_seapp_contexts"
71 	},
72 	{
73 		"/vendor/etc/selinux/vendor_seapp_contexts",
74 		"/vendor_seapp_contexts"
75 	},
76 	{
77 		"/odm/etc/selinux/odm_seapp_contexts",
78 		"/odm_seapp_contexts"
79 	}
80 }};
81 
82 /* Returns a handle for the file contexts backend, initialized with the Android
83  * configuration */
selinux_android_file_context_handle(void)84 struct selabel_handle* selinux_android_file_context_handle(void)
85 {
86 	const char* file_contexts[MAX_CONTEXT_PATHS];
87 	struct selinux_opt opts[MAX_CONTEXT_PATHS + 1];
88 	int npaths, nopts;
89 
90 	npaths = find_existing_files(&file_context_paths, file_contexts);
91 	paths_to_opts(file_contexts, npaths, opts);
92 
93 	opts[npaths].type = SELABEL_OPT_BASEONLY;
94 	opts[npaths].value = (char *) 1;
95 	nopts = npaths + 1;
96 
97 	return initialize_backend(SELABEL_CTX_FILE, "file", opts, nopts);
98 }
99 
100 #if DEBUG
101 static char const * const levelFromName[] = {
102 	"none",
103 	"app",
104 	"user",
105 	"all"
106 };
107 #endif
108 
109 struct prefix_str {
110 	size_t len;
111 	char *str;
112 	char is_prefix;
113 };
114 
free_prefix_str(struct prefix_str * p)115 static void free_prefix_str(struct prefix_str *p)
116 {
117 	if (!p)
118 		return;
119 	free(p->str);
120 }
121 
122 /* For a set of selectors, represents the contexts that should be applied to an
123  * app and its data. Each instance is based on a line in a seapp_contexts file.
124  * */
125 struct seapp_context {
126 	/* input selectors */
127 	bool isSystemServer;
128 	bool isEphemeralAppSet;
129 	bool isEphemeralApp;
130 	struct prefix_str user;
131 	char *seinfo;
132 	struct prefix_str name;
133 	bool isPrivAppSet;
134 	bool isPrivApp;
135 	int32_t minTargetSdkVersion;
136 	bool fromRunAs;
137 	bool isIsolatedComputeApp;
138 	bool isSdkSandboxNext;
139 	/* outputs */
140 	char *domain;
141 	char *type;
142 	char *level;
143 	enum levelFrom levelFrom;
144 };
145 
free_seapp_context(struct seapp_context * s)146 static void free_seapp_context(struct seapp_context *s)
147 {
148 	if (!s)
149 		return;
150 
151 	free_prefix_str(&s->user);
152 	free(s->seinfo);
153 	free_prefix_str(&s->name);
154 	free(s->domain);
155 	free(s->type);
156 	free(s->level);
157 }
158 
159 /* If any duplicate was found while sorting the entries */
160 static bool seapp_contexts_dup = false;
161 
162 /* Compare two seapp_context. Used to sort all the entries found. */
seapp_context_cmp(const void * A,const void * B)163 static int seapp_context_cmp(const void *A, const void *B)
164 {
165 	const struct seapp_context *const *sp1 = (const struct seapp_context *const *) A;
166 	const struct seapp_context *const *sp2 = (const struct seapp_context *const *) B;
167 	const struct seapp_context *s1 = *sp1, *s2 = *sp2;
168 	bool dup;
169 
170 	/* Give precedence to isSystemServer=true. */
171 	if (s1->isSystemServer != s2->isSystemServer)
172 		return (s1->isSystemServer ? -1 : 1);
173 
174 	/* Give precedence to a specified isEphemeral= over an
175 	 * unspecified isEphemeral=. */
176 	if (s1->isEphemeralAppSet != s2->isEphemeralAppSet)
177 		return (s1->isEphemeralAppSet ? -1 : 1);
178 
179 	/* Give precedence to a specified user= over an unspecified user=. */
180 	if (s1->user.str && !s2->user.str)
181 		return -1;
182 	if (!s1->user.str && s2->user.str)
183 		return 1;
184 
185 	if (s1->user.str) {
186 		/* Give precedence to a fixed user= string over a prefix. */
187 		if (s1->user.is_prefix != s2->user.is_prefix)
188 			return (s2->user.is_prefix ? -1 : 1);
189 
190 		/* Give precedence to a longer prefix over a shorter prefix. */
191 		if (s1->user.is_prefix && s1->user.len != s2->user.len)
192 			return (s1->user.len > s2->user.len) ? -1 : 1;
193 	}
194 
195 	/* Give precedence to a specified seinfo= over an unspecified seinfo=. */
196 	if (s1->seinfo && !s2->seinfo)
197 		return -1;
198 	if (!s1->seinfo && s2->seinfo)
199 		return 1;
200 
201 	/* Give precedence to a specified name= over an unspecified name=. */
202 	if (s1->name.str && !s2->name.str)
203 		return -1;
204 	if (!s1->name.str && s2->name.str)
205 		return 1;
206 
207 	if (s1->name.str) {
208 		/* Give precedence to a fixed name= string over a prefix. */
209 		if (s1->name.is_prefix != s2->name.is_prefix)
210 			return (s2->name.is_prefix ? -1 : 1);
211 
212 		/* Give precedence to a longer prefix over a shorter prefix. */
213 		if (s1->name.is_prefix && s1->name.len != s2->name.len)
214 			return (s1->name.len > s2->name.len) ? -1 : 1;
215 	}
216 
217 	/* Give precedence to a specified isPrivApp= over an unspecified isPrivApp=. */
218 	if (s1->isPrivAppSet != s2->isPrivAppSet)
219 		return (s1->isPrivAppSet ? -1 : 1);
220 
221 	/* Give precedence to a higher minTargetSdkVersion= over a lower minTargetSdkVersion=.
222 	 * If unspecified, minTargetSdkVersion has a default value of 0.
223 	 */
224 	if (s1->minTargetSdkVersion > s2->minTargetSdkVersion)
225 		return -1;
226 	else if (s1->minTargetSdkVersion < s2->minTargetSdkVersion)
227 		return 1;
228 
229 	/* Give precedence to fromRunAs=true. */
230 	if (s1->fromRunAs != s2->fromRunAs)
231 		return (s1->fromRunAs ? -1 : 1);
232 
233 	/*
234 	 * Check for a duplicated entry on the input selectors.
235 	 * We already compared isSystemServer above.
236 	 * We also have already checked that both entries specify the same
237 	 * string fields, so if s1 has a non-NULL string, then so does s2.
238 	 */
239 	dup = (!s1->user.str || !strcmp(s1->user.str, s2->user.str)) &&
240 		(!s1->seinfo || !strcmp(s1->seinfo, s2->seinfo)) &&
241 		(!s1->name.str || !strcmp(s1->name.str, s2->name.str)) &&
242 		(s1->isPrivAppSet && s1->isPrivApp == s2->isPrivApp) &&
243 		(s1->isSystemServer && s1->isSystemServer == s2->isSystemServer) &&
244 		(s1->isEphemeralAppSet && s1->isEphemeralApp == s2->isEphemeralApp) &&
245 		(s1->isIsolatedComputeApp && s1->isIsolatedComputeApp == s2->isIsolatedComputeApp) &&
246 		(s1->isSdkSandboxNext && s1->isSdkSandboxNext == s2->isSdkSandboxNext);
247 
248 	if (dup) {
249 		seapp_contexts_dup = true;
250 		selinux_log(SELINUX_ERROR, "seapp_contexts:  Duplicated entry\n");
251 		if (s1->user.str)
252 			selinux_log(SELINUX_ERROR, " user=%s\n", s1->user.str);
253 		if (s1->seinfo)
254 			selinux_log(SELINUX_ERROR, " seinfo=%s\n", s1->seinfo);
255 		if (s1->name.str)
256 			selinux_log(SELINUX_ERROR, " name=%s\n", s1->name.str);
257 	}
258 
259 	/* Anything else has equal precedence. */
260 	return 0;
261 }
262 
263 /* Array of all the seapp_context entries configured. */
264 static struct seapp_context **seapp_contexts = NULL;
265 /* Size of seapp_contexts */
266 static int nspec = 0;
267 
free_seapp_contexts(void)268 static void free_seapp_contexts(void)
269 {
270 	int n;
271 
272 	if (!seapp_contexts)
273 		return;
274 
275 	for (n = 0; n < nspec; n++)
276 		free_seapp_context(seapp_contexts[n]);
277 
278 	free(seapp_contexts);
279 	seapp_contexts = NULL;
280 	nspec = 0;
281 }
282 
get_minTargetSdkVersion(const char * value)283 static int32_t get_minTargetSdkVersion(const char *value)
284 {
285 	char *endptr;
286 	long minTargetSdkVersion;
287 	minTargetSdkVersion = strtol(value, &endptr, 10);
288 	if (('\0' != *endptr) || (minTargetSdkVersion < 0) || (minTargetSdkVersion > INT32_MAX)) {
289 		return -1; /* error parsing minTargetSdkVersion */
290 	} else {
291 		return (int32_t) minTargetSdkVersion;
292 	}
293 }
294 
seapp_context_reload_internal(const path_alts_t * context_paths)295 int seapp_context_reload_internal(const path_alts_t *context_paths)
296 {
297 	FILE *fp = NULL;
298 	char line_buf[BUFSIZ];
299 	char *token;
300 	unsigned lineno;
301 	struct seapp_context *cur;
302 	char *p, *name = NULL, *value = NULL, *saveptr;
303 	size_t i, len, files_len = 0;
304 	int ret;
305 	const char* seapp_contexts_files[MAX_CONTEXT_PATHS];
306 
307 	files_len = find_existing_files(context_paths, seapp_contexts_files);
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, "levelFromUid")) {
458 					if (cur->levelFrom) {
459 						free_seapp_context(cur);
460 						goto err;
461 					}
462 					if (!strcasecmp(value, "true"))
463 						cur->levelFrom = LEVELFROM_APP;
464 					else if (!strcasecmp(value, "false"))
465 						cur->levelFrom = LEVELFROM_NONE;
466 					else {
467 						free_seapp_context(cur);
468 						goto err;
469 					}
470 				} else if (!strcasecmp(name, "levelFrom")) {
471 					if (cur->levelFrom) {
472 						free_seapp_context(cur);
473 						goto err;
474 					}
475 					if (!strcasecmp(value, "none"))
476 						cur->levelFrom = LEVELFROM_NONE;
477 					else if (!strcasecmp(value, "app"))
478 						cur->levelFrom = LEVELFROM_APP;
479 					else if (!strcasecmp(value, "user"))
480 						cur->levelFrom = LEVELFROM_USER;
481 					else if (!strcasecmp(value, "all"))
482 						cur->levelFrom = LEVELFROM_ALL;
483 					else {
484 						free_seapp_context(cur);
485 						goto err;
486 					}
487 				} else if (!strcasecmp(name, "level")) {
488 					if (cur->level) {
489 						free_seapp_context(cur);
490 						goto err;
491 					}
492 					cur->level = strdup(value);
493 					if (!cur->level) {
494 						free_seapp_context(cur);
495 						goto oom;
496 					}
497 				} else if (!strcasecmp(name, "isPrivApp")) {
498 					cur->isPrivAppSet = true;
499 					if (!strcasecmp(value, "true"))
500 						cur->isPrivApp = true;
501 					else if (!strcasecmp(value, "false"))
502 						cur->isPrivApp = false;
503 					else {
504 						free_seapp_context(cur);
505 						goto err;
506 					}
507 				} else if (!strcasecmp(name, "minTargetSdkVersion")) {
508 					cur->minTargetSdkVersion = get_minTargetSdkVersion(value);
509 					if (cur->minTargetSdkVersion < 0) {
510 						free_seapp_context(cur);
511 						goto err;
512 					}
513 				} else if (!strcasecmp(name, "fromRunAs")) {
514 					if (!strcasecmp(value, "true"))
515 						cur->fromRunAs = true;
516 					else if (!strcasecmp(value, "false"))
517 						cur->fromRunAs = false;
518 					else {
519 						free_seapp_context(cur);
520 						goto err;
521 					}
522 				} else if (!strcasecmp(name, "isIsolatedComputeApp")) {
523 					if (!strcasecmp(value, "true"))
524 						cur->isIsolatedComputeApp = true;
525 					else if (!strcasecmp(value, "false"))
526 						cur->isIsolatedComputeApp = 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 			seapp_contexts[nspec] = cur;
559 			nspec++;
560 			lineno++;
561 		}
562 		fclose(fp);
563 		fp = NULL;
564 	}
565 
566 	qsort(seapp_contexts, nspec, sizeof(struct seapp_context *),
567 	      seapp_context_cmp);
568 
569 	if (seapp_contexts_dup)
570 		goto err_no_log;
571 
572 #if DEBUG
573 	{
574 		int i;
575 		for (i = 0; i < nspec; i++) {
576 			cur = seapp_contexts[i];
577 			selinux_log(SELINUX_INFO, "%s:  isSystemServer=%s isEphemeralApp=%s isIsolatedComputeApp=%s isSdkSandboxNext=%s user=%s seinfo=%s "
578 					"name=%s isPrivApp=%s minTargetSdkVersion=%d fromRunAs=%s -> domain=%s type=%s level=%s levelFrom=%s",
579 				__FUNCTION__,
580 				cur->isSystemServer ? "true" : "false",
581 				cur->isEphemeralAppSet ? (cur->isEphemeralApp ? "true" : "false") : "null",
582 				cur->user.str,
583 				cur->seinfo, cur->name.str,
584 				cur->isPrivAppSet ? (cur->isPrivApp ? "true" : "false") : "null",
585 				cur->minTargetSdkVersion,
586 				cur->fromRunAs ? "true" : "false",
587 				cur->isIsolatedComputeApp ? "true" : "false",
588 				cur->isSdkSandboxNext ? "true" : "false",
589 				cur->domain, cur->type, cur->level,
590 				levelFromName[cur->levelFrom]);
591 		}
592 	}
593 #endif
594 
595 	ret = 0;
596 
597 out:
598 	if (fp) {
599 		fclose(fp);
600 	}
601 	return ret;
602 
603 err:
604 	selinux_log(SELINUX_ERROR, "%s:  Invalid entry on line %u\n",
605 		    seapp_contexts_files[i], lineno);
606 err_no_log:
607 	free_seapp_contexts();
608 	ret = -1;
609 	goto out;
610 oom:
611 	selinux_log(SELINUX_ERROR,
612 		    "%s:  Out of memory\n", __FUNCTION__);
613 	free_seapp_contexts();
614 	ret = -1;
615 	goto out;
616 }
617 
selinux_android_seapp_context_reload(void)618 int selinux_android_seapp_context_reload(void)
619 {
620 	return seapp_context_reload_internal(&seapp_context_paths);
621 }
622 
623 /* indirection to support pthread_once */
seapp_context_init(void)624 static void seapp_context_init(void)
625 {
626 	selinux_android_seapp_context_reload();
627 }
628 
629 static pthread_once_t seapp_once = PTHREAD_ONCE_INIT;
630 
selinux_android_seapp_context_init(void)631 void selinux_android_seapp_context_init(void) {
632 	__selinux_once(seapp_once, seapp_context_init);
633 }
634 
635 /*
636  * Max id that can be mapped to category set uniquely
637  * using the current scheme.
638  */
639 #define CAT_MAPPING_MAX_ID (0x1<<16)
640 
641 #define PRIVILEGED_APP_STR ":privapp"
642 #define ISOLATED_COMPUTE_APP_STR ":isolatedComputeApp"
643 #define APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS_STR ":isSdkSandboxNext"
644 #define EPHEMERAL_APP_STR ":ephemeralapp"
645 #define TARGETSDKVERSION_STR ":targetSdkVersion="
646 #define FROM_RUNAS_STR ":fromRunAs"
get_app_targetSdkVersion(const char * seinfo)647 static int32_t get_app_targetSdkVersion(const char *seinfo)
648 {
649 	char *substr = strstr(seinfo, TARGETSDKVERSION_STR);
650 	long targetSdkVersion;
651 	char *endptr;
652 	if (substr != NULL) {
653 		substr = substr + strlen(TARGETSDKVERSION_STR);
654 		if (substr != NULL) {
655 			targetSdkVersion = strtol(substr, &endptr, 10);
656 			if (('\0' != *endptr && ':' != *endptr)
657 					|| (targetSdkVersion < 0) || (targetSdkVersion > INT32_MAX)) {
658 				return -1; /* malformed targetSdkVersion value in seinfo */
659 			} else {
660 				return (int32_t) targetSdkVersion;
661 			}
662 		}
663 	}
664 	return 0; /* default to 0 when targetSdkVersion= is not present in seinfo */
665 }
666 
seinfo_parse(char * dest,const char * src,size_t size)667 static int seinfo_parse(char *dest, const char *src, size_t size)
668 {
669 	size_t len;
670 	char *p;
671 
672 	if ((p = strchr(src, ':')) != NULL)
673 		len = p - src;
674 	else
675 		len = strlen(src);
676 
677 	if (len > size - 1)
678 		return -1;
679 
680 	strncpy(dest, src, len);
681 	dest[len] = '\0';
682 
683 	return 0;
684 }
685 
686 /* 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)687 int set_range_from_level(context_t ctx, enum levelFrom levelFrom, uid_t userid, uid_t appid)
688 {
689 	char level[255];
690 	switch (levelFrom) {
691 	case LEVELFROM_NONE:
692 		strncpy(level, "s0", sizeof level);
693 		break;
694 	case LEVELFROM_APP:
695 		snprintf(level, sizeof level, "s0:c%u,c%u",
696 			 appid & 0xff,
697 			 256 + (appid>>8 & 0xff));
698 		break;
699 	case LEVELFROM_USER:
700 		snprintf(level, sizeof level, "s0:c%u,c%u",
701 			 512 + (userid & 0xff),
702 			 768 + (userid>>8 & 0xff));
703 		break;
704 	case LEVELFROM_ALL:
705 		snprintf(level, sizeof level, "s0:c%u,c%u,c%u,c%u",
706 			 appid & 0xff,
707 			 256 + (appid>>8 & 0xff),
708 			 512 + (userid & 0xff),
709 			 768 + (userid>>8 & 0xff));
710 		break;
711 	default:
712 		return -1;
713 	}
714 	if (context_range_set(ctx, level)) {
715 		return -2;
716 	}
717 	return 0;
718 }
719 
720 /*
721  * This code is Android specific, bionic guarantees that
722  * calls to non-reentrant getpwuid() are thread safe.
723  */
724 struct passwd *(*seapp_getpwuid)(uid_t uid) = getpwuid;
725 
seapp_context_lookup_internal(enum seapp_kind kind,uid_t uid,bool isSystemServer,const char * seinfo,const char * pkgname,context_t ctx)726 int seapp_context_lookup_internal(enum seapp_kind kind,
727 				uid_t uid,
728 				bool isSystemServer,
729 				const char *seinfo,
730 				const char *pkgname,
731 				context_t ctx)
732 {
733 	struct passwd *pwd;
734 	const char *username = NULL;
735 	struct seapp_context *cur = NULL;
736 	int i;
737 	uid_t userid;
738 	uid_t appid;
739 	bool isPrivApp = false;
740 	bool isEphemeralApp = false;
741 	bool isIsolatedComputeApp = false;
742 	bool isSdkSandboxNext = false;
743 	int32_t targetSdkVersion = 0;
744 	bool fromRunAs = false;
745 	char parsedseinfo[BUFSIZ];
746 
747 	if (seinfo) {
748 		if (seinfo_parse(parsedseinfo, seinfo, BUFSIZ))
749 			goto err;
750 		isPrivApp = strstr(seinfo, PRIVILEGED_APP_STR) ? true : false;
751 		isEphemeralApp = strstr(seinfo, EPHEMERAL_APP_STR) ? true : false;
752 		isIsolatedComputeApp = strstr(seinfo, ISOLATED_COMPUTE_APP_STR) ? true : false;
753 		isSdkSandboxNext = strstr(seinfo, APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS_STR) ? true : false;
754 		fromRunAs = strstr(seinfo, FROM_RUNAS_STR) ? true : false;
755 		targetSdkVersion = get_app_targetSdkVersion(seinfo);
756 		if (targetSdkVersion < 0) {
757 			selinux_log(SELINUX_ERROR,
758 					"%s:  Invalid targetSdkVersion passed for app with uid %d, seinfo %s, name %s\n",
759 					__FUNCTION__, uid, seinfo, pkgname);
760 			goto err;
761 		}
762 		seinfo = parsedseinfo;
763 	}
764 
765 	userid = uid / AID_USER_OFFSET;
766 	appid = uid % AID_USER_OFFSET;
767 	if (appid < AID_APP_START) {
768 		pwd = seapp_getpwuid(appid);
769 		if (!pwd)
770 			goto err;
771 		username = pwd->pw_name;
772 	} else if (appid < AID_SDK_SANDBOX_PROCESS_START) {
773 		username = "_app";
774 		appid -= AID_APP_START;
775 	} else if (appid < AID_ISOLATED_START) {
776 		username = "_sdksandbox";
777 		appid -= AID_SDK_SANDBOX_PROCESS_START;
778 	} else {
779 		username = "_isolated";
780 		appid -= AID_ISOLATED_START;
781 	}
782 
783 	if (appid >= CAT_MAPPING_MAX_ID || userid >= CAT_MAPPING_MAX_ID)
784 		goto err;
785 
786 	for (i = 0; i < nspec; i++) {
787 		cur = seapp_contexts[i];
788 
789 		if (cur->isSystemServer != isSystemServer)
790 			continue;
791 
792 		if (cur->isEphemeralAppSet && cur->isEphemeralApp != isEphemeralApp)
793 			continue;
794 
795 		if (cur->user.str) {
796 			if (cur->user.is_prefix) {
797 				if (strncasecmp(username, cur->user.str, cur->user.len-1))
798 					continue;
799 			} else {
800 				if (strcasecmp(username, cur->user.str))
801 					continue;
802 			}
803 		}
804 
805 		if (cur->seinfo) {
806 			if (!seinfo || strcasecmp(seinfo, cur->seinfo))
807 				continue;
808 		}
809 
810 		if (cur->name.str) {
811 			if(!pkgname)
812 				continue;
813 
814 			if (cur->name.is_prefix) {
815 				if (strncasecmp(pkgname, cur->name.str, cur->name.len-1))
816 					continue;
817 			} else {
818 				if (strcasecmp(pkgname, cur->name.str))
819 					continue;
820 			}
821 		}
822 
823 		if (cur->isPrivAppSet && cur->isPrivApp != isPrivApp)
824 			continue;
825 
826 		if (cur->minTargetSdkVersion > targetSdkVersion)
827 			continue;
828 
829 		if (cur->fromRunAs != fromRunAs)
830 			continue;
831 
832 		if (cur->isIsolatedComputeApp != isIsolatedComputeApp)
833 			continue;
834 
835 		if (cur->isSdkSandboxNext != isSdkSandboxNext)
836 			continue;
837 
838 		if (kind == SEAPP_TYPE && !cur->type)
839 			continue;
840 		else if (kind == SEAPP_DOMAIN && !cur->domain)
841 			continue;
842 
843 		if (kind == SEAPP_TYPE) {
844 			if (context_type_set(ctx, cur->type))
845 				goto oom;
846 		} else if (kind == SEAPP_DOMAIN) {
847 			if (context_type_set(ctx, cur->domain))
848 				goto oom;
849 		}
850 
851 		if (cur->levelFrom != LEVELFROM_NONE) {
852 			int res = set_range_from_level(ctx, cur->levelFrom, userid, appid);
853 			if (res != 0) {
854 				return res;
855 			}
856 		} else if (cur->level) {
857 			if (context_range_set(ctx, cur->level))
858 				goto oom;
859 		}
860 
861 		break;
862 	}
863 
864 	if (kind == SEAPP_DOMAIN && i == nspec) {
865 		/*
866 		 * No match.
867 		 * Fail to prevent staying in the zygote's context.
868 		 */
869 		selinux_log(SELINUX_ERROR,
870 			    "%s:  No match for app with uid %d, seinfo %s, name %s\n",
871 			    __FUNCTION__, uid, seinfo, pkgname);
872 
873 		if (security_getenforce() == 1)
874 			goto err;
875 	}
876 
877 	return 0;
878 err:
879 	return -1;
880 oom:
881 	return -2;
882 }
883 
seapp_context_lookup(enum seapp_kind kind,uid_t uid,bool isSystemServer,const char * seinfo,const char * pkgname,context_t ctx)884 int seapp_context_lookup(enum seapp_kind kind,
885 				uid_t uid,
886 				bool isSystemServer,
887 				const char *seinfo,
888 				const char *pkgname,
889 				context_t ctx)
890 {
891 	// Ensure the default context files are loaded.
892 	selinux_android_seapp_context_init();
893 	return seapp_context_lookup_internal(kind, uid, isSystemServer, seinfo, pkgname, ctx);
894 }
895