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