Home | History | Annotate | Download | only in src
      1 #include <sys/types.h>
      2 #include <unistd.h>
      3 #include <string.h>
      4 #include <stdio.h>
      5 #include <stdlib.h>
      6 #include <ctype.h>
      7 #include <errno.h>
      8 #include <pwd.h>
      9 #include <grp.h>
     10 #include <sys/mman.h>
     11 #include <sys/mount.h>
     12 #include <sys/types.h>
     13 #include <sys/stat.h>
     14 #include <fcntl.h>
     15 #include <selinux/selinux.h>
     16 #include <selinux/context.h>
     17 #include <selinux/android.h>
     18 #include <selinux/label.h>
     19 #include <selinux/avc.h>
     20 #include <private/android_filesystem_config.h>
     21 #include "callbacks.h"
     22 #include "selinux_internal.h"
     23 
     24 /*
     25  * XXX Where should this configuration file be located?
     26  * Needs to be accessible by zygote and installd when
     27  * setting credentials for app processes and setting permissions
     28  * on app data directories.
     29  */
     30 static char const * const seapp_contexts_file[] = {
     31 	"/data/system/seapp_contexts",
     32 	"/seapp_contexts",
     33 	0 };
     34 
     35 static const struct selinux_opt seopts[] = {
     36 	{ SELABEL_OPT_PATH, "/data/system/file_contexts" },
     37 	{ SELABEL_OPT_PATH, "/file_contexts" },
     38 	{ 0, NULL } };
     39 
     40 static const char *const sepolicy_file[] = {
     41         "/data/system/sepolicy",
     42         "/sepolicy",
     43         0 };
     44 
     45 struct seapp_context {
     46 	/* input selectors */
     47 	char isSystemServer;
     48 	char *user;
     49 	size_t len;
     50 	char prefix;
     51 	char *seinfo;
     52 	char *name;
     53 	/* outputs */
     54 	char *domain;
     55 	char *type;
     56 	char *level;
     57 	char *sebool;
     58 	char levelFromUid;
     59 };
     60 
     61 static int seapp_context_cmp(const void *A, const void *B)
     62 {
     63 	const struct seapp_context **sp1 = A, **sp2 = B;
     64 	const struct seapp_context *s1 = *sp1, *s2 = *sp2;
     65 
     66 	/* Give precedence to isSystemServer=true. */
     67 	if (s1->isSystemServer != s2->isSystemServer)
     68 		return (s1->isSystemServer ? -1 : 1);
     69 
     70 	/* Give precedence to a specified user= over an unspecified user=. */
     71 	if (s1->user && !s2->user)
     72 		return -1;
     73 	if (!s1->user && s2->user)
     74 		return 1;
     75 
     76 	if (s1->user) {
     77 		/* Give precedence to a fixed user= string over a prefix. */
     78 		if (s1->prefix != s2->prefix)
     79 			return (s2->prefix ? -1 : 1);
     80 
     81 		/* Give precedence to a longer prefix over a shorter prefix. */
     82 		if (s1->prefix && s1->len != s2->len)
     83 			return (s1->len > s2->len) ? -1 : 1;
     84 	}
     85 
     86 	/* Give precedence to a specified seinfo= over an unspecified seinfo=. */
     87 	if (s1->seinfo && !s2->seinfo)
     88 		return -1;
     89 	if (!s1->seinfo && s2->seinfo)
     90 		return 1;
     91 
     92 	/* Give precedence to a specified name= over an unspecified name=. */
     93 	if (s1->name && !s2->name)
     94 		return -1;
     95 	if (!s1->name && s2->name)
     96 		return 1;
     97 
     98         /* Give precedence to a specified sebool= over an unspecified sebool=. */
     99         if (s1->sebool && !s2->sebool)
    100                 return -1;
    101         if (!s1->sebool && s2->sebool)
    102                 return 1;
    103 
    104 	/* Anything else has equal precedence. */
    105 	return 0;
    106 }
    107 
    108 static struct seapp_context **seapp_contexts = NULL;
    109 static int nspec = 0;
    110 
    111 int selinux_android_seapp_context_reload(void)
    112 {
    113 	FILE *fp = NULL;
    114 	char line_buf[BUFSIZ];
    115 	char *token;
    116 	unsigned lineno;
    117 	struct seapp_context *cur;
    118 	char *p, *name = NULL, *value = NULL, *saveptr;
    119 	size_t len;
    120 	int i = 0, ret;
    121 
    122 	while ((fp==NULL) && seapp_contexts_file[i])
    123 		fp = fopen(seapp_contexts_file[i++], "r");
    124 
    125 	if (!fp) {
    126 		selinux_log(SELINUX_ERROR, "%s:  could not open any seapp_contexts file", __FUNCTION__);
    127 		return -1;
    128 	}
    129 
    130 	nspec = 0;
    131 	while (fgets(line_buf, sizeof line_buf - 1, fp)) {
    132 		p = line_buf;
    133 		while (isspace(*p))
    134 			p++;
    135 		if (*p == '#' || *p == 0)
    136 			continue;
    137 		nspec++;
    138 	}
    139 
    140 	seapp_contexts = calloc(nspec, sizeof(struct seapp_context *));
    141 	if (!seapp_contexts)
    142 		goto oom;
    143 
    144 	rewind(fp);
    145 	nspec = 0;
    146 	lineno = 1;
    147 	while (fgets(line_buf, sizeof line_buf - 1, fp)) {
    148 		len = strlen(line_buf);
    149 		if (line_buf[len - 1] == '\n')
    150 			line_buf[len - 1] = 0;
    151 		p = line_buf;
    152 		while (isspace(*p))
    153 			p++;
    154 		if (*p == '#' || *p == 0)
    155 			continue;
    156 
    157 		cur = calloc(1, sizeof(struct seapp_context));
    158 		if (!cur)
    159 			goto oom;
    160 
    161 		token = strtok_r(p, " \t", &saveptr);
    162 		if (!token)
    163 			goto err;
    164 
    165 		while (1) {
    166 			name = token;
    167 			value = strchr(name, '=');
    168 			if (!value)
    169 				goto err;
    170 			*value++ = 0;
    171 
    172 			if (!strcasecmp(name, "isSystemServer")) {
    173 				if (!strcasecmp(value, "true"))
    174 					cur->isSystemServer = 1;
    175 				else if (!strcasecmp(value, "false"))
    176 					cur->isSystemServer = 0;
    177 				else {
    178 					goto err;
    179 				}
    180 			} else if (!strcasecmp(name, "user")) {
    181 				cur->user = strdup(value);
    182 				if (!cur->user)
    183 					goto oom;
    184 				cur->len = strlen(cur->user);
    185 				if (cur->user[cur->len-1] == '*')
    186 					cur->prefix = 1;
    187 			} else if (!strcasecmp(name, "seinfo")) {
    188 				cur->seinfo = strdup(value);
    189 				if (!cur->seinfo)
    190 					goto oom;
    191 			} else if (!strcasecmp(name, "name")) {
    192 				cur->name = strdup(value);
    193 				if (!cur->name)
    194 					goto oom;
    195 			} else if (!strcasecmp(name, "domain")) {
    196 				cur->domain = strdup(value);
    197 				if (!cur->domain)
    198 					goto oom;
    199 			} else if (!strcasecmp(name, "type")) {
    200 				cur->type = strdup(value);
    201 				if (!cur->type)
    202 					goto oom;
    203 			} else if (!strcasecmp(name, "levelFromUid")) {
    204 				if (!strcasecmp(value, "true"))
    205 					cur->levelFromUid = 1;
    206 				else if (!strcasecmp(value, "false"))
    207 					cur->levelFromUid = 0;
    208 				else {
    209 					goto err;
    210 				}
    211 			} else if (!strcasecmp(name, "level")) {
    212 				cur->level = strdup(value);
    213 				if (!cur->level)
    214 					goto oom;
    215 			} else if (!strcasecmp(name, "sebool")) {
    216 				cur->sebool = strdup(value);
    217 				if (!cur->sebool)
    218 					goto oom;
    219 			} else
    220 				goto err;
    221 
    222 			token = strtok_r(NULL, " \t", &saveptr);
    223 			if (!token)
    224 				break;
    225 		}
    226 
    227 		seapp_contexts[nspec] = cur;
    228 		nspec++;
    229 		lineno++;
    230 	}
    231 
    232 	qsort(seapp_contexts, nspec, sizeof(struct seapp_context *),
    233 	      seapp_context_cmp);
    234 
    235 #if DEBUG
    236 	{
    237 		int i;
    238 		for (i = 0; i < nspec; i++) {
    239 			cur = seapp_contexts[i];
    240 			selinux_log(SELINUX_INFO, "%s:  isSystemServer=%s user=%s seinfo=%s name=%s sebool=%s -> domain=%s type=%s level=%s levelFromUid=%s",
    241 			__FUNCTION__,
    242 			cur->isSystemServer ? "true" : "false", cur->user,
    243 			cur->seinfo, cur->name, cur->sebool, cur->domain,
    244 			cur->type, cur->level,
    245 			cur->levelFromUid ? "true" : "false");
    246 		}
    247 	}
    248 #endif
    249 
    250 	ret = 0;
    251 
    252 out:
    253 	fclose(fp);
    254 	return ret;
    255 
    256 err:
    257 	selinux_log(SELINUX_ERROR, "%s:  Error reading %s, line %u, name %s, value %s\n",
    258 		    __FUNCTION__, seapp_contexts_file[i - 1], lineno, name, value);
    259 	ret = -1;
    260 	goto out;
    261 oom:
    262 	selinux_log(SELINUX_ERROR,
    263 		    "%s:  Out of memory\n", __FUNCTION__);
    264 	ret = -1;
    265 	goto out;
    266 }
    267 
    268 
    269 static void seapp_context_init(void)
    270 {
    271         selinux_android_seapp_context_reload();
    272 }
    273 
    274 static pthread_once_t once = PTHREAD_ONCE_INIT;
    275 
    276 #define SEAPP_TYPE 1
    277 #define SEAPP_DOMAIN 2
    278 static int seapp_context_lookup(int kind,
    279 				uid_t uid,
    280 				int isSystemServer,
    281 				const char *seinfo,
    282 				const char *pkgname,
    283 				context_t ctx)
    284 {
    285 	const char *username = NULL;
    286 	char *end = NULL;
    287 	struct passwd *pw;
    288 	struct seapp_context *cur;
    289 	int i;
    290 	size_t n;
    291 	uid_t appid = 0;
    292 
    293 	appid = uid % AID_USER;
    294 	if (appid < AID_APP) {
    295 		for (n = 0; n < android_id_count; n++) {
    296 			if (android_ids[n].aid == appid) {
    297 				username = android_ids[n].name;
    298 				break;
    299 			}
    300 		}
    301 		if (!username)
    302 			goto err;
    303 	} else if (appid < AID_ISOLATED_START) {
    304 		username = "app_";
    305 		appid -= AID_APP;
    306 	} else {
    307 		username = "isolated";
    308 		appid -= AID_ISOLATED_START;
    309 	}
    310 
    311 	if (appid >= MLS_CATS)
    312 		goto err;
    313 
    314 	for (i = 0; i < nspec; i++) {
    315 		cur = seapp_contexts[i];
    316 
    317 		if (cur->isSystemServer != isSystemServer)
    318 			continue;
    319 
    320 		if (cur->user) {
    321 			if (cur->prefix) {
    322 				if (strncasecmp(username, cur->user, cur->len-1))
    323 					continue;
    324 			} else {
    325 				if (strcasecmp(username, cur->user))
    326 					continue;
    327 			}
    328 		}
    329 
    330 		if (cur->seinfo) {
    331 			if (!seinfo || strcasecmp(seinfo, cur->seinfo))
    332 				continue;
    333 		}
    334 
    335 		if (cur->name) {
    336 			if (!pkgname || strcasecmp(pkgname, cur->name))
    337 				continue;
    338 		}
    339 
    340 		if (kind == SEAPP_TYPE && !cur->type)
    341 			continue;
    342 		else if (kind == SEAPP_DOMAIN && !cur->domain)
    343 			continue;
    344 
    345 		if (cur->sebool) {
    346 			int value = security_get_boolean_active(cur->sebool);
    347 			if (value == 0)
    348 				continue;
    349 			else if (value == -1) {
    350 				selinux_log(SELINUX_ERROR, \
    351 				"Could not find boolean: %s ", cur->sebool);
    352 				goto err;
    353 			}
    354 		}
    355 
    356 		if (kind == SEAPP_TYPE) {
    357 			if (context_type_set(ctx, cur->type))
    358 				goto oom;
    359 		} else if (kind == SEAPP_DOMAIN) {
    360 			if (context_type_set(ctx, cur->domain))
    361 				goto oom;
    362 		}
    363 
    364 		if (cur->levelFromUid) {
    365 			char level[255];
    366 			snprintf(level, sizeof level, "%s:c%lu",
    367 				 context_range_get(ctx), appid);
    368 			if (context_range_set(ctx, level))
    369 				goto oom;
    370 		} else if (cur->level) {
    371 			if (context_range_set(ctx, cur->level))
    372 				goto oom;
    373 		}
    374 
    375 		break;
    376 	}
    377 
    378 	if (kind == SEAPP_DOMAIN && i == nspec) {
    379 		/*
    380 		 * No match.
    381 		 * Fail to prevent staying in the zygote's context.
    382 		 */
    383 		selinux_log(SELINUX_ERROR,
    384 			    "%s:  No match for app with uid %d, seinfo %s, name %s\n",
    385 			    __FUNCTION__, uid, seinfo, pkgname);
    386 
    387 		if (security_getenforce() == 1)
    388 			goto err;
    389 	}
    390 
    391 	return 0;
    392 err:
    393 	return -1;
    394 oom:
    395 	return -2;
    396 }
    397 
    398 int selinux_android_setfilecon2(const char *pkgdir,
    399 				const char *pkgname,
    400 				const char *seinfo,
    401 				uid_t uid)
    402 {
    403 	char *orig_ctx_str = NULL, *ctx_str;
    404 	context_t ctx = NULL;
    405 	int rc;
    406 
    407 	if (is_selinux_enabled() <= 0)
    408 		return 0;
    409 
    410 	__selinux_once(once, seapp_context_init);
    411 
    412 	rc = getfilecon(pkgdir, &ctx_str);
    413 	if (rc < 0)
    414 		goto err;
    415 
    416 	ctx = context_new(ctx_str);
    417 	orig_ctx_str = ctx_str;
    418 	if (!ctx)
    419 		goto oom;
    420 
    421 	rc = seapp_context_lookup(SEAPP_TYPE, uid, 0, seinfo, pkgname, ctx);
    422 	if (rc == -1)
    423 		goto err;
    424 	else if (rc == -2)
    425 		goto oom;
    426 
    427 	ctx_str = context_str(ctx);
    428 	if (!ctx_str)
    429 		goto oom;
    430 
    431 	rc = security_check_context(ctx_str);
    432 	if (rc < 0)
    433 		goto err;
    434 
    435 	if (strcmp(ctx_str, orig_ctx_str)) {
    436 		rc = setfilecon(pkgdir, ctx_str);
    437 		if (rc < 0)
    438 			goto err;
    439 	}
    440 
    441 	rc = 0;
    442 out:
    443 	freecon(orig_ctx_str);
    444 	context_free(ctx);
    445 	return rc;
    446 err:
    447 	selinux_log(SELINUX_ERROR, "%s:  Error setting context for pkgdir %s, uid %d: %s\n",
    448 		    __FUNCTION__, pkgdir, uid, strerror(errno));
    449 	rc = -1;
    450 	goto out;
    451 oom:
    452 	selinux_log(SELINUX_ERROR, "%s:  Out of memory\n", __FUNCTION__);
    453 	rc = -1;
    454 	goto out;
    455 }
    456 
    457 int selinux_android_setfilecon(const char *pkgdir,
    458 			       const char *pkgname,
    459 			       uid_t uid)
    460 {
    461 	return selinux_android_setfilecon2(pkgdir, pkgname, NULL, uid);
    462 }
    463 
    464 int selinux_android_setcontext(uid_t uid,
    465 			       int isSystemServer,
    466 			       const char *seinfo,
    467 			       const char *pkgname)
    468 {
    469 	char *orig_ctx_str = NULL, *ctx_str;
    470 	context_t ctx = NULL;
    471 	int rc;
    472 
    473 	if (is_selinux_enabled() <= 0)
    474 		return 0;
    475 
    476 	__selinux_once(once, seapp_context_init);
    477 
    478 	rc = getcon(&ctx_str);
    479 	if (rc)
    480 		goto err;
    481 
    482 	ctx = context_new(ctx_str);
    483 	orig_ctx_str = ctx_str;
    484 	if (!ctx)
    485 		goto oom;
    486 
    487 	rc = seapp_context_lookup(SEAPP_DOMAIN, uid, isSystemServer, seinfo, pkgname, ctx);
    488 	if (rc == -1)
    489 		goto err;
    490 	else if (rc == -2)
    491 		goto oom;
    492 
    493 	ctx_str = context_str(ctx);
    494 	if (!ctx_str)
    495 		goto oom;
    496 
    497 	rc = security_check_context(ctx_str);
    498 	if (rc < 0)
    499 		goto err;
    500 
    501 	if (strcmp(ctx_str, orig_ctx_str)) {
    502 		rc = setcon(ctx_str);
    503 		if (rc < 0)
    504 			goto err;
    505 	}
    506 
    507 	rc = 0;
    508 out:
    509 	freecon(orig_ctx_str);
    510 	context_free(ctx);
    511 	avc_netlink_close();
    512 	return rc;
    513 err:
    514 	if (isSystemServer)
    515 		selinux_log(SELINUX_ERROR,
    516 			    "%s:  Error setting context for system server: %s\n",
    517 			    __FUNCTION__, strerror(errno));
    518 	else
    519 		selinux_log(SELINUX_ERROR,
    520 			    "%s:  Error setting context for app with uid %d, seinfo %s: %s\n",
    521 			    __FUNCTION__, uid, seinfo, strerror(errno));
    522 
    523 	rc = -1;
    524 	goto out;
    525 oom:
    526 	selinux_log(SELINUX_ERROR, "%s:  Out of memory\n", __FUNCTION__);
    527 	rc = -1;
    528 	goto out;
    529 }
    530 
    531 static struct selabel_handle *sehandle = NULL;
    532 
    533 static struct selabel_handle *file_context_open(void)
    534 {
    535 	struct selabel_handle *h;
    536 	int i = 0;
    537 
    538 	h = NULL;
    539 	while ((h == NULL) && seopts[i].value) {
    540 		h = selabel_open(SELABEL_CTX_FILE, &seopts[i], 1);
    541 		i++;
    542 	}
    543 
    544 	if (!h)
    545 		selinux_log(SELINUX_ERROR, "%s: Error getting sehandle label (%s)\n",
    546 			    __FUNCTION__, strerror(errno));
    547 	return h;
    548 }
    549 
    550 static void file_context_init(void)
    551 {
    552 	sehandle = file_context_open();
    553 }
    554 
    555 static pthread_once_t fc_once = PTHREAD_ONCE_INIT;
    556 
    557 int selinux_android_restorecon(const char *pathname)
    558 {
    559 
    560 	__selinux_once(fc_once, file_context_init);
    561 
    562 	int ret;
    563 
    564 	if (!sehandle)
    565 		goto bail;
    566 
    567 	struct stat sb;
    568 
    569 	if (lstat(pathname, &sb) < 0)
    570 		goto err;
    571 
    572 	char *oldcontext, *newcontext;
    573 
    574 	if (lgetfilecon(pathname, &oldcontext) < 0)
    575 		goto err;
    576 
    577 	if (selabel_lookup(sehandle, &newcontext, pathname, sb.st_mode) < 0)
    578 		goto err;
    579 
    580 	if (strcmp(newcontext, "<<none>>") && strcmp(oldcontext, newcontext))
    581 		if (lsetfilecon(pathname, newcontext) < 0)
    582 			goto err;
    583 
    584 	ret = 0;
    585 out:
    586 	if (oldcontext)
    587 		freecon(oldcontext);
    588 	if (newcontext)
    589 		freecon(newcontext);
    590 
    591 	return ret;
    592 
    593 err:
    594 	selinux_log(SELINUX_ERROR,
    595 		    "%s:  Error restoring context for %s (%s)\n",
    596 		    __FUNCTION__, pathname, strerror(errno));
    597 
    598 bail:
    599 	ret = -1;
    600 	goto out;
    601 }
    602 
    603 
    604 struct selabel_handle* selinux_android_file_context_handle(void)
    605 {
    606         return file_context_open();
    607 }
    608 
    609 int selinux_android_reload_policy(void)
    610 {
    611 	char path[PATH_MAX];
    612 	int fd = -1, rc;
    613 	struct stat sb;
    614 	void *map = NULL;
    615 	int i = 0;
    616 
    617 	while (fd < 0 && sepolicy_file[i]) {
    618 		snprintf(path, sizeof(path), "%s",
    619 			sepolicy_file[i]);
    620 		fd = open(path, O_RDONLY);
    621 		i++;
    622 	}
    623 	if (fd < 0) {
    624 		selinux_log(SELINUX_ERROR, "SELinux:  Could not open sepolicy:  %s\n",
    625 				strerror(errno));
    626 		return -1;
    627 	}
    628 	if (fstat(fd, &sb) < 0) {
    629 		selinux_log(SELINUX_ERROR, "SELinux:  Could not stat %s:  %s\n",
    630 				path, strerror(errno));
    631 		close(fd);
    632 		return -1;
    633 	}
    634 	map = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    635 	if (map == MAP_FAILED) {
    636 		selinux_log(SELINUX_ERROR, "SELinux:  Could not map %s:  %s\n",
    637 			path, strerror(errno));
    638 		close(fd);
    639 		return -1;
    640 	}
    641 
    642 	rc = security_load_policy(map, sb.st_size);
    643 	if (rc < 0) {
    644 		selinux_log(SELINUX_ERROR, "SELinux:  Could not load policy:  %s\n",
    645 			strerror(errno));
    646 		munmap(map, sb.st_size);
    647 		close(fd);
    648 		return -1;
    649 	}
    650 
    651 	munmap(map, sb.st_size);
    652 	close(fd);
    653 	selinux_log(SELINUX_INFO, "SELinux: Loaded policy from %s\n", path);
    654 
    655 	return 0;
    656 }
    657 
    658 int selinux_android_load_policy(void)
    659 {
    660 	mkdir(SELINUXMNT, 0755);
    661 	if (mount("selinuxfs", SELINUXMNT, "selinuxfs", 0, NULL)) {
    662 		if (errno == ENODEV) {
    663 			/* SELinux not enabled in kernel */
    664 			return -1;
    665 		}
    666 		selinux_log(SELINUX_ERROR,"SELinux:  Could not mount selinuxfs:  %s\n",
    667 				strerror(errno));
    668 		return -1;
    669 	}
    670 	set_selinuxmnt(SELINUXMNT);
    671 
    672 	return selinux_android_reload_policy();
    673 }
    674