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 */
30static char const * const seapp_contexts_file[] = {
31	"/data/system/seapp_contexts",
32	"/seapp_contexts",
33	0 };
34
35static const struct selinux_opt seopts[] = {
36	{ SELABEL_OPT_PATH, "/data/system/file_contexts" },
37	{ SELABEL_OPT_PATH, "/file_contexts" },
38	{ 0, NULL } };
39
40static const char *const sepolicy_file[] = {
41        "/data/system/sepolicy",
42        "/sepolicy",
43        0 };
44
45struct 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
61static 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
108static struct seapp_context **seapp_contexts = NULL;
109static int nspec = 0;
110
111int 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
252out:
253	fclose(fp);
254	return ret;
255
256err:
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;
261oom:
262	selinux_log(SELINUX_ERROR,
263		    "%s:  Out of memory\n", __FUNCTION__);
264	ret = -1;
265	goto out;
266}
267
268
269static void seapp_context_init(void)
270{
271        selinux_android_seapp_context_reload();
272}
273
274static pthread_once_t once = PTHREAD_ONCE_INIT;
275
276#define SEAPP_TYPE 1
277#define SEAPP_DOMAIN 2
278static 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;
392err:
393	return -1;
394oom:
395	return -2;
396}
397
398int 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;
442out:
443	freecon(orig_ctx_str);
444	context_free(ctx);
445	return rc;
446err:
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;
451oom:
452	selinux_log(SELINUX_ERROR, "%s:  Out of memory\n", __FUNCTION__);
453	rc = -1;
454	goto out;
455}
456
457int 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
464int 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;
508out:
509	freecon(orig_ctx_str);
510	context_free(ctx);
511	avc_netlink_close();
512	return rc;
513err:
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;
525oom:
526	selinux_log(SELINUX_ERROR, "%s:  Out of memory\n", __FUNCTION__);
527	rc = -1;
528	goto out;
529}
530
531static struct selabel_handle *sehandle = NULL;
532
533static 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
550static void file_context_init(void)
551{
552	sehandle = file_context_open();
553}
554
555static pthread_once_t fc_once = PTHREAD_ONCE_INIT;
556
557int 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;
585out:
586	if (oldcontext)
587		freecon(oldcontext);
588	if (newcontext)
589		freecon(newcontext);
590
591	return ret;
592
593err:
594	selinux_log(SELINUX_ERROR,
595		    "%s:  Error restoring context for %s (%s)\n",
596		    __FUNCTION__, pathname, strerror(errno));
597
598bail:
599	ret = -1;
600	goto out;
601}
602
603
604struct selabel_handle* selinux_android_file_context_handle(void)
605{
606        return file_context_open();
607}
608
609int 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
658int 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