android.c revision f074036424618c130dacb3464465a8b40bffef58
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 "callbacks.h"
17#include "selinux_internal.h"
18
19/*
20 * XXX Where should this configuration file be located?
21 * Needs to be accessible by zygote and installd when
22 * setting credentials for app processes and setting permissions
23 * on app data directories.
24 */
25#define SEAPP_CONTEXTS "/seapp_contexts"
26
27struct seapp_context {
28	/* input selectors */
29	char isSystemServer;
30	char *user;
31	size_t len;
32	char prefix;
33	char *seinfo;
34	char *name;
35	/* outputs */
36	char *domain;
37	char *type;
38	char *level;
39	char levelFromUid;
40};
41
42static int seapp_context_cmp(const void *A, const void *B)
43{
44	const struct seapp_context **sp1 = A, **sp2 = B;
45	const struct seapp_context *s1 = *sp1, *s2 = *sp2;
46
47	/* Give precedence to isSystemServer=true. */
48	if (s1->isSystemServer != s2->isSystemServer)
49		return (s1->isSystemServer ? -1 : 1);
50
51	/* Give precedence to a specified user= over an unspecified user=. */
52	if (s1->user && !s2->user)
53		return -1;
54	if (!s1->user && s2->user)
55		return 1;
56
57	if (s1->user) {
58		/* Give precedence to a fixed user= string over a prefix. */
59		if (s1->prefix != s2->prefix)
60			return (s2->prefix ? -1 : 1);
61
62		/* Give precedence to a longer prefix over a shorter prefix. */
63		if (s1->prefix && s1->len != s2->len)
64			return (s1->len > s2->len) ? -1 : 1;
65	}
66
67	/* Give precedence to a specified seinfo= over an unspecified seinfo=. */
68	if (s1->seinfo && !s2->seinfo)
69		return -1;
70	if (!s1->seinfo && s2->seinfo)
71		return 1;
72
73	/* Give precedence to a specified name= over an unspecified name=. */
74	if (s1->name && !s2->name)
75		return -1;
76	if (!s1->name && s2->name)
77		return 1;
78
79	/* Anything else has equal precedence. */
80	return 0;
81}
82
83static struct seapp_context **seapp_contexts = NULL;
84static int nspec = 0;
85
86static void seapp_context_init(void)
87{
88	FILE *fp;
89	char line_buf[BUFSIZ];
90	const char *path = SEAPP_CONTEXTS;
91	char *token;
92	unsigned lineno;
93	struct seapp_context *cur;
94	char *p, *name = NULL, *value = NULL, *saveptr;
95	size_t len;
96
97	fp = fopen(path, "r");
98	if (!fp) {
99		selinux_log(SELINUX_ERROR, "%s:  could not open %s", __FUNCTION__, path);
100		return;
101	}
102
103	nspec = 0;
104	while (fgets(line_buf, sizeof line_buf - 1, fp)) {
105		p = line_buf;
106		while (isspace(*p))
107			p++;
108		if (*p == '#' || *p == 0)
109			continue;
110		nspec++;
111	}
112
113	seapp_contexts = calloc(nspec, sizeof(struct seapp_context *));
114	if (!seapp_contexts)
115		goto oom;
116
117	rewind(fp);
118	nspec = 0;
119	lineno = 1;
120	while (fgets(line_buf, sizeof line_buf - 1, fp)) {
121		len = strlen(line_buf);
122		if (line_buf[len - 1] == '\n')
123			line_buf[len - 1] = 0;
124		p = line_buf;
125		while (isspace(*p))
126			p++;
127		if (*p == '#' || *p == 0)
128			continue;
129
130		cur = calloc(1, sizeof(struct seapp_context));
131		if (!cur)
132			goto oom;
133
134		token = strtok_r(p, " \t", &saveptr);
135		if (!token)
136			goto err;
137
138		while (1) {
139			name = token;
140			value = strchr(name, '=');
141			if (!value)
142				goto err;
143			*value++ = 0;
144
145			if (!strcasecmp(name, "isSystemServer")) {
146				if (!strcasecmp(value, "true"))
147					cur->isSystemServer = 1;
148				else if (!strcasecmp(value, "false"))
149					cur->isSystemServer = 0;
150				else {
151					goto err;
152				}
153			} else if (!strcasecmp(name, "user")) {
154				cur->user = strdup(value);
155				if (!cur->user)
156					goto oom;
157				cur->len = strlen(cur->user);
158				if (cur->user[cur->len-1] == '*')
159					cur->prefix = 1;
160			} else if (!strcasecmp(name, "seinfo")) {
161				cur->seinfo = strdup(value);
162				if (!cur->seinfo)
163					goto oom;
164			} else if (!strcasecmp(name, "name")) {
165				cur->name = strdup(value);
166				if (!cur->name)
167					goto oom;
168			} else if (!strcasecmp(name, "domain")) {
169				cur->domain = strdup(value);
170				if (!cur->domain)
171					goto oom;
172			} else if (!strcasecmp(name, "type")) {
173				cur->type = strdup(value);
174				if (!cur->type)
175					goto oom;
176			} else if (!strcasecmp(name, "levelFromUid")) {
177				if (!strcasecmp(value, "true"))
178					cur->levelFromUid = 1;
179				else if (!strcasecmp(value, "false"))
180					cur->levelFromUid = 0;
181				else {
182					goto err;
183				}
184			} else if (!strcasecmp(name, "level")) {
185				cur->level = strdup(value);
186				if (!cur->level)
187					goto oom;
188			} else
189				goto err;
190
191			token = strtok_r(NULL, " \t", &saveptr);
192			if (!token)
193				break;
194		}
195
196		seapp_contexts[nspec] = cur;
197		nspec++;
198		lineno++;
199	}
200
201	qsort(seapp_contexts, nspec, sizeof(struct seapp_context *),
202	      seapp_context_cmp);
203
204#if DEBUG
205	{
206		int i;
207		for (i = 0; i < nspec; i++) {
208			cur = seapp_contexts[i];
209			selinux_log(SELINUX_INFO, "%s:  isSystemServer=%s user=%s seinfo=%s name=%s -> domain=%s type=%s level=%s levelFromUid=%s",
210				    __FUNCTION__,
211				    cur->isSystemServer ? "true" : "false",
212				    cur->user, cur->seinfo, cur->name,
213				    cur->domain, cur->type, cur->level,
214				    cur->levelFromUid ? "true" : "false");
215		}
216	}
217#endif
218
219out:
220	fclose(fp);
221	return;
222
223err:
224	selinux_log(SELINUX_ERROR, "%s:  Error reading %s, line %u, name %s, value %s\n",
225		    __FUNCTION__, path, lineno, name, value);
226	goto out;
227oom:
228	selinux_log(SELINUX_ERROR,
229		    "%s:  Out of memory\n", __FUNCTION__);
230	goto out;
231}
232
233static pthread_once_t once = PTHREAD_ONCE_INIT;
234
235int selinux_android_setfilecon(const char *pkgdir,
236			       const char *name,
237			       uid_t uid)
238{
239	char *orig_ctx_str = NULL, *ctx_str;
240	context_t ctx = NULL;
241	struct passwd *pw;
242	struct seapp_context *cur;
243	int i, rc;
244
245	if (is_selinux_enabled() <= 0)
246		return 0;
247
248	__selinux_once(once, seapp_context_init);
249
250	rc = getfilecon(pkgdir, &ctx_str);
251	if (rc < 0)
252		goto err;
253
254	ctx = context_new(ctx_str);
255	orig_ctx_str = ctx_str;
256	if (!ctx)
257		goto oom;
258
259	pw = getpwuid(uid);
260	if (!pw)
261		goto err;
262
263	for (i = 0; i < nspec; i++) {
264		cur = seapp_contexts[i];
265
266		/* isSystemServer=true is only for app process labeling. */
267		if (cur->isSystemServer)
268			continue;
269
270		if (cur->user) {
271			if (cur->prefix) {
272				if (strncasecmp(pw->pw_name, cur->user, cur->len-1))
273					continue;
274			} else {
275				if (strcasecmp(pw->pw_name, cur->user))
276					continue;
277			}
278		}
279
280		/* seinfo= is ignored / not available for file labeling. */
281
282		if (cur->name) {
283			if (!name || strcasecmp(name, cur->name))
284				continue;
285		}
286
287		if (!cur->type)
288			continue;
289
290		if (context_type_set(ctx, cur->type))
291			goto oom;
292
293		if (cur->levelFromUid && !strncmp(pw->pw_name, "app_", 4)) {
294			char level[255];
295			unsigned long id;
296
297			/* Only supported for app UIDs. */
298			id = strtoul(pw->pw_name + 4, NULL, 10);
299			snprintf(level, sizeof level, "%s:c%lu",
300				 context_range_get(ctx), id);
301			if (context_range_set(ctx, level))
302				goto oom;
303		} else if (cur->level) {
304			if (context_range_set(ctx, cur->level))
305				goto oom;
306		}
307
308		break;
309	}
310
311	ctx_str = context_str(ctx);
312	if (!ctx_str)
313		goto oom;
314
315	rc = security_check_context(ctx_str);
316	if (rc < 0)
317		goto err;
318
319	if (strcmp(ctx_str, orig_ctx_str)) {
320		rc = setfilecon(pkgdir, ctx_str);
321		if (rc < 0)
322			goto err;
323	}
324
325	rc = 0;
326out:
327	freecon(orig_ctx_str);
328	context_free(ctx);
329	return rc;
330err:
331	selinux_log(SELINUX_ERROR, "%s:  Error setting context for pkgdir %s, uid %d: %s\n",
332		    __FUNCTION__, pkgdir, uid, strerror(errno));
333	rc = -1;
334	goto out;
335oom:
336	selinux_log(SELINUX_ERROR, "%s:  Out of memory\n", __FUNCTION__);
337	rc = -1;
338	goto out;
339}
340
341int selinux_android_setcontext(uid_t uid,
342			       int isSystemServer,
343			       const char *seinfo,
344			       const char *name)
345{
346	char *orig_ctx_str = NULL, *ctx_str;
347	context_t ctx = NULL;
348	unsigned long id;
349	struct passwd *pw;
350	struct seapp_context *cur;
351	int i, rc;
352
353	if (is_selinux_enabled() <= 0)
354		return 0;
355
356	__selinux_once(once, seapp_context_init);
357
358	rc = getcon(&ctx_str);
359	if (rc)
360		goto err;
361
362	ctx = context_new(ctx_str);
363	orig_ctx_str = ctx_str;
364	if (!ctx)
365		goto oom;
366
367	pw = getpwuid(uid);
368	if (!pw)
369		goto err;
370
371	for (i = 0; i < nspec; i++) {
372		cur = seapp_contexts[i];
373		if (cur->isSystemServer != isSystemServer)
374			continue;
375		if (cur->user) {
376			if (cur->prefix) {
377				if (strncasecmp(pw->pw_name, cur->user, cur->len-1))
378					continue;
379			} else {
380				if (strcasecmp(pw->pw_name, cur->user))
381					continue;
382			}
383		}
384		if (cur->seinfo) {
385			if (!seinfo || strcasecmp(seinfo, cur->seinfo))
386				continue;
387		}
388		if (cur->name) {
389			if (!name || strcasecmp(name, cur->name))
390				continue;
391		}
392
393		if (!cur->domain)
394			continue;
395
396		if (context_type_set(ctx, cur->domain))
397			goto oom;
398
399		if (cur->levelFromUid && !strncmp(pw->pw_name, "app_", 4)) {
400			char level[255];
401			unsigned long id;
402
403			/* Only supported for app UIDs. */
404			id = strtoul(pw->pw_name + 4, NULL, 10);
405			snprintf(level, sizeof level, "%s:c%lu",
406				 context_range_get(ctx), id);
407			if (context_range_set(ctx, level))
408				goto oom;
409		} else if (cur->level) {
410			if (context_range_set(ctx, cur->level))
411				goto oom;
412		}
413
414		break;
415	}
416
417	if (i == nspec) {
418		/*
419		 * No match.
420		 * Fail to prevent staying in the zygote's context.
421		 */
422		selinux_log(SELINUX_ERROR,
423			    "%s:  No match for app with uid %d, seinfo %s, name %s\n",
424			    __FUNCTION__, uid, seinfo, name);
425		rc = -1;
426		goto out;
427	}
428
429	ctx_str = context_str(ctx);
430	if (!ctx_str)
431		goto oom;
432
433	rc = security_check_context(ctx_str);
434	if (rc < 0)
435		goto err;
436
437	if (strcmp(ctx_str, orig_ctx_str)) {
438		rc = setcon(ctx_str);
439		if (rc < 0)
440			goto err;
441	}
442
443	rc = 0;
444out:
445	freecon(orig_ctx_str);
446	context_free(ctx);
447	return rc;
448err:
449	if (isSystemServer)
450		selinux_log(SELINUX_ERROR,
451			    "%s:  Error setting context for system server: %s\n",
452			    __FUNCTION__, strerror(errno));
453	else
454		selinux_log(SELINUX_ERROR,
455			    "%s:  Error setting context for app with uid %d, seinfo %s: %s\n",
456			    __FUNCTION__, uid, seinfo, strerror(errno));
457
458	rc = -1;
459	goto out;
460oom:
461	selinux_log(SELINUX_ERROR, "%s:  Out of memory\n", __FUNCTION__);
462	rc = -1;
463	goto out;
464}
465
466