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