blob: 4768475d2043932c293602ff3e5500046687453a [file] [log] [blame]
Stephen Smalleyf0740362012-01-04 12:30:47 -05001#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