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