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