android.c revision ba70ee4c5ab8026e97fce5c2452dfe588dfaac3e
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_setfilecon(const char *pkgdir,
257			       const char *pkgname,
258			       uid_t uid)
259{
260	const char *username;
261	char *orig_ctx_str = NULL, *ctx_str, *end = NULL;
262	context_t ctx = NULL;
263	struct passwd *pw;
264	struct seapp_context *cur;
265	int i, rc;
266	unsigned long id = 0;
267
268	if (is_selinux_enabled() <= 0)
269		return 0;
270
271	__selinux_once(once, seapp_context_init);
272
273	rc = getfilecon(pkgdir, &ctx_str);
274	if (rc < 0)
275		goto err;
276
277	ctx = context_new(ctx_str);
278	orig_ctx_str = ctx_str;
279	if (!ctx)
280		goto oom;
281
282	pw = getpwuid(uid);
283	if (!pw)
284		goto err;
285	username = pw->pw_name;
286
287	if (!strncmp(username, "app_", 4)) {
288		id = strtoul(username + 4, NULL, 10);
289		if (id >= MLS_CATS)
290			goto err;
291	} else if (username[0] == 'u' && isdigit(username[1])) {
292		unsigned long unused;
293		unused = strtoul(username+1, &end, 10);
294		if (end[0] != '_')
295			goto err;
296		id = strtoul(end + 2, NULL, 10);
297		if (!id || id >= MLS_CATS/2)
298			goto err;
299		if (end[1] == 'i')
300			id += MLS_CATS/2;
301		else if (end[1] != 'a')
302			goto err;
303		/* use app_ for matching on the user= field */
304		username = "app_";
305	}
306
307	for (i = 0; i < nspec; i++) {
308		cur = seapp_contexts[i];
309
310		/* isSystemServer=true is only for app process labeling. */
311		if (cur->isSystemServer)
312			continue;
313
314		if (cur->user) {
315			if (cur->prefix) {
316				if (strncasecmp(username, cur->user, cur->len-1))
317					continue;
318			} else {
319				if (strcasecmp(username, cur->user))
320					continue;
321			}
322		}
323
324		/* seinfo= is ignored / not available for file labeling. */
325
326		if (cur->name) {
327			if (!pkgname || strcasecmp(pkgname, cur->name))
328				continue;
329		}
330
331		if (!cur->type)
332			continue;
333
334		if (context_type_set(ctx, cur->type))
335			goto oom;
336
337		if (cur->levelFromUid && id) {
338			char level[255];
339			snprintf(level, sizeof level, "%s:c%lu",
340				 context_range_get(ctx), id);
341			if (context_range_set(ctx, level))
342				goto oom;
343		} else if (cur->level) {
344			if (context_range_set(ctx, cur->level))
345				goto oom;
346		}
347
348		break;
349	}
350
351	ctx_str = context_str(ctx);
352	if (!ctx_str)
353		goto oom;
354
355	rc = security_check_context(ctx_str);
356	if (rc < 0)
357		goto err;
358
359	if (strcmp(ctx_str, orig_ctx_str)) {
360		rc = setfilecon(pkgdir, ctx_str);
361		if (rc < 0)
362			goto err;
363	}
364
365	rc = 0;
366out:
367	freecon(orig_ctx_str);
368	context_free(ctx);
369	return rc;
370err:
371	selinux_log(SELINUX_ERROR, "%s:  Error setting context for pkgdir %s, uid %d: %s\n",
372		    __FUNCTION__, pkgdir, uid, strerror(errno));
373	rc = -1;
374	goto out;
375oom:
376	selinux_log(SELINUX_ERROR, "%s:  Out of memory\n", __FUNCTION__);
377	rc = -1;
378	goto out;
379}
380
381int selinux_android_setcontext(uid_t uid,
382			       int isSystemServer,
383			       const char *seinfo,
384			       const char *pkgname)
385{
386	const char *username;
387	char *orig_ctx_str = NULL, *ctx_str, *end = NULL;
388	context_t ctx = NULL;
389	unsigned long id = 0;
390	struct passwd *pw;
391	struct seapp_context *cur;
392	int i, rc;
393
394	if (is_selinux_enabled() <= 0)
395		return 0;
396
397	__selinux_once(once, seapp_context_init);
398
399	rc = getcon(&ctx_str);
400	if (rc)
401		goto err;
402
403	ctx = context_new(ctx_str);
404	orig_ctx_str = ctx_str;
405	if (!ctx)
406		goto oom;
407
408	pw = getpwuid(uid);
409	if (!pw)
410		goto err;
411	username = pw->pw_name;
412
413	if (!strncmp(username, "app_", 4)) {
414		id = strtoul(username + 4, NULL, 10);
415		if (id >= MLS_CATS)
416			goto err;
417	} else if (username[0] == 'u' && isdigit(username[1])) {
418		unsigned long unused;
419		unused = strtoul(username+1, &end, 10);
420		if (end[0] != '_')
421			goto err;
422		id = strtoul(end + 2, NULL, 10);
423		if (!id || id >= MLS_CATS/2)
424			goto err;
425		if (end[1] == 'i')
426			id += MLS_CATS/2;
427		else if (end[1] != 'a')
428			goto err;
429		/* use app_ for matching on the user= field */
430		username = "app_";
431	}
432
433	for (i = 0; i < nspec; i++) {
434		cur = seapp_contexts[i];
435		if (cur->isSystemServer != isSystemServer)
436			continue;
437		if (cur->user) {
438			if (cur->prefix) {
439				if (strncasecmp(username, cur->user, cur->len-1))
440					continue;
441			} else {
442				if (strcasecmp(username, cur->user))
443					continue;
444			}
445		}
446		if (cur->seinfo) {
447			if (!seinfo || strcasecmp(seinfo, cur->seinfo))
448				continue;
449		}
450		if (cur->name) {
451			if (!pkgname || strcasecmp(pkgname, cur->name))
452				continue;
453		}
454
455		if (!cur->domain)
456			continue;
457
458		if (context_type_set(ctx, cur->domain))
459			goto oom;
460
461		if (cur->levelFromUid && id) {
462			char level[255];
463			snprintf(level, sizeof level, "%s:c%lu",
464				 context_range_get(ctx), id);
465			if (context_range_set(ctx, level))
466				goto oom;
467		} else if (cur->level) {
468			if (context_range_set(ctx, cur->level))
469				goto oom;
470		}
471
472		break;
473	}
474
475	if (i == nspec) {
476		/*
477		 * No match.
478		 * Fail to prevent staying in the zygote's context.
479		 */
480		selinux_log(SELINUX_ERROR,
481			    "%s:  No match for app with uid %d, seinfo %s, name %s\n",
482			    __FUNCTION__, uid, seinfo, pkgname);
483		rc = -1;
484		goto out;
485	}
486
487	ctx_str = context_str(ctx);
488	if (!ctx_str)
489		goto oom;
490
491	rc = security_check_context(ctx_str);
492	if (rc < 0)
493		goto err;
494
495	if (strcmp(ctx_str, orig_ctx_str)) {
496		rc = setcon(ctx_str);
497		if (rc < 0)
498			goto err;
499	}
500
501	rc = 0;
502out:
503	freecon(orig_ctx_str);
504	context_free(ctx);
505	return rc;
506err:
507	if (isSystemServer)
508		selinux_log(SELINUX_ERROR,
509			    "%s:  Error setting context for system server: %s\n",
510			    __FUNCTION__, strerror(errno));
511	else
512		selinux_log(SELINUX_ERROR,
513			    "%s:  Error setting context for app with uid %d, seinfo %s: %s\n",
514			    __FUNCTION__, uid, seinfo, strerror(errno));
515
516	rc = -1;
517	goto out;
518oom:
519	selinux_log(SELINUX_ERROR, "%s:  Out of memory\n", __FUNCTION__);
520	rc = -1;
521	goto out;
522}
523
524static struct selabel_handle *sehandle = NULL;
525
526static void file_context_init(void)
527{
528	int i = 0;
529
530	sehandle = NULL;
531	while ((sehandle == NULL) && seopts[i].value) {
532		sehandle = selabel_open(SELABEL_CTX_FILE, &seopts[i], 1);
533		i++;
534	}
535
536	if (!sehandle)
537		selinux_log(SELINUX_ERROR,"%s: Error getting sehandle label (%s)\n",
538			    __FUNCTION__, strerror(errno));
539}
540
541static pthread_once_t fc_once = PTHREAD_ONCE_INIT;
542
543int selinux_android_restorecon(const char *pathname)
544{
545
546	__selinux_once(fc_once, file_context_init);
547
548	int ret;
549
550	if (!sehandle)
551		goto bail;
552
553	struct stat sb;
554
555	if (lstat(pathname, &sb) < 0)
556		goto err;
557
558	char *oldcontext, *newcontext;
559
560	if (lgetfilecon(pathname, &oldcontext) < 0)
561		goto err;
562
563	if (selabel_lookup(sehandle, &newcontext, pathname, sb.st_mode) < 0)
564		goto err;
565
566	if (strcmp(newcontext, "<<none>>") && strcmp(oldcontext, newcontext))
567		if (lsetfilecon(pathname, newcontext) < 0)
568			goto err;
569
570	ret = 0;
571out:
572	if (oldcontext)
573		freecon(oldcontext);
574	if (newcontext)
575		freecon(newcontext);
576
577	return ret;
578
579err:
580	selinux_log(SELINUX_ERROR,
581		    "%s:  Error restoring context for %s (%s)\n",
582		    __FUNCTION__, pathname, strerror(errno));
583
584bail:
585	ret = -1;
586	goto out;
587}
588