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