blob: 2bebaf174aef8cf95e2621db3f80ee006d40916d [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>
Stephen Smalley0ca91b32012-03-19 10:25:53 -040016#include <selinux/label.h>
Stephen Smalleyf0740362012-01-04 12:30:47 -050017#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 */
Stephen Smalley7446c912012-03-19 10:37:05 -040026static char const * const seapp_contexts_file[] = {
27 "/data/system/seapp_contexts",
28 "/seapp_contexts",
29 0 };
Stephen Smalleyf0740362012-01-04 12:30:47 -050030
Stephen Smalley32ebfe82012-03-20 13:05:37 -040031static const struct selinux_opt seopts[] = {
32 { SELABEL_OPT_PATH, "/data/system/file_contexts" },
33 { SELABEL_OPT_PATH, "/file_contexts" },
34 { 0, NULL } };
Stephen Smalley7446c912012-03-19 10:37:05 -040035
Stephen Smalleyf0740362012-01-04 12:30:47 -050036struct 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
Stephen Smalley7446c912012-03-19 10:37:05 -040095int selinux_android_seapp_context_reload(void)
Stephen Smalleyf0740362012-01-04 12:30:47 -050096{
Stephen Smalley7446c912012-03-19 10:37:05 -040097 FILE *fp = NULL;
Stephen Smalleyf0740362012-01-04 12:30:47 -050098 char line_buf[BUFSIZ];
Stephen Smalleyf0740362012-01-04 12:30:47 -050099 char *token;
100 unsigned lineno;
101 struct seapp_context *cur;
102 char *p, *name = NULL, *value = NULL, *saveptr;
103 size_t len;
Stephen Smalley7446c912012-03-19 10:37:05 -0400104 int i = 0, ret;
Stephen Smalleyf0740362012-01-04 12:30:47 -0500105
Stephen Smalley7446c912012-03-19 10:37:05 -0400106 while ((fp==NULL) && seapp_contexts_file[i])
107 fp = fopen(seapp_contexts_file[i++], "r");
108
Stephen Smalleyf0740362012-01-04 12:30:47 -0500109 if (!fp) {
Stephen Smalley7446c912012-03-19 10:37:05 -0400110 selinux_log(SELINUX_ERROR, "%s: could not open any seapp_contexts file", __FUNCTION__);
111 return -1;
Stephen Smalleyf0740362012-01-04 12:30:47 -0500112 }
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
Stephen Smalley7446c912012-03-19 10:37:05 -0400230 ret = 0;
231
Stephen Smalleyf0740362012-01-04 12:30:47 -0500232out:
233 fclose(fp);
Stephen Smalley7446c912012-03-19 10:37:05 -0400234 return ret;
Stephen Smalleyf0740362012-01-04 12:30:47 -0500235
236err:
237 selinux_log(SELINUX_ERROR, "%s: Error reading %s, line %u, name %s, value %s\n",
Stephen Smalley7446c912012-03-19 10:37:05 -0400238 __FUNCTION__, seapp_contexts_file[i - 1], lineno, name, value);
239 ret = -1;
Stephen Smalleyf0740362012-01-04 12:30:47 -0500240 goto out;
241oom:
242 selinux_log(SELINUX_ERROR,
243 "%s: Out of memory\n", __FUNCTION__);
Stephen Smalley7446c912012-03-19 10:37:05 -0400244 ret = -1;
Stephen Smalleyf0740362012-01-04 12:30:47 -0500245 goto out;
246}
247
Stephen Smalley7446c912012-03-19 10:37:05 -0400248
249static void seapp_context_init(void)
250{
251 selinux_android_seapp_context_reload();
252}
253
Stephen Smalleyf0740362012-01-04 12:30:47 -0500254static pthread_once_t once = PTHREAD_ONCE_INIT;
255
256int selinux_android_setfilecon(const char *pkgdir,
Stephen Smalleyba70ee42012-07-10 15:49:04 -0400257 const char *pkgname,
Stephen Smalleyf0740362012-01-04 12:30:47 -0500258 uid_t uid)
259{
Stephen Smalleyba70ee42012-07-10 15:49:04 -0400260 const char *username;
261 char *orig_ctx_str = NULL, *ctx_str, *end = NULL;
Stephen Smalleyf0740362012-01-04 12:30:47 -0500262 context_t ctx = NULL;
263 struct passwd *pw;
264 struct seapp_context *cur;
265 int i, rc;
Stephen Smalleyba70ee42012-07-10 15:49:04 -0400266 unsigned long id = 0;
Stephen Smalleyf0740362012-01-04 12:30:47 -0500267
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;
Stephen Smalleyba70ee42012-07-10 15:49:04 -0400285 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 }
Stephen Smalleyf0740362012-01-04 12:30:47 -0500306
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) {
Stephen Smalleyba70ee42012-07-10 15:49:04 -0400316 if (strncasecmp(username, cur->user, cur->len-1))
Stephen Smalleyf0740362012-01-04 12:30:47 -0500317 continue;
318 } else {
Stephen Smalleyba70ee42012-07-10 15:49:04 -0400319 if (strcasecmp(username, cur->user))
Stephen Smalleyf0740362012-01-04 12:30:47 -0500320 continue;
321 }
322 }
323
324 /* seinfo= is ignored / not available for file labeling. */
325
326 if (cur->name) {
Stephen Smalleyba70ee42012-07-10 15:49:04 -0400327 if (!pkgname || strcasecmp(pkgname, cur->name))
Stephen Smalleyf0740362012-01-04 12:30:47 -0500328 continue;
329 }
330
331 if (!cur->type)
332 continue;
333
334 if (context_type_set(ctx, cur->type))
335 goto oom;
336
Stephen Smalleyba70ee42012-07-10 15:49:04 -0400337 if (cur->levelFromUid && id) {
Stephen Smalleyf0740362012-01-04 12:30:47 -0500338 char level[255];
Stephen Smalleyf0740362012-01-04 12:30:47 -0500339 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,
Stephen Smalleyba70ee42012-07-10 15:49:04 -0400384 const char *pkgname)
Stephen Smalleyf0740362012-01-04 12:30:47 -0500385{
Stephen Smalleyba70ee42012-07-10 15:49:04 -0400386 const char *username;
387 char *orig_ctx_str = NULL, *ctx_str, *end = NULL;
Stephen Smalleyf0740362012-01-04 12:30:47 -0500388 context_t ctx = NULL;
Stephen Smalleyba70ee42012-07-10 15:49:04 -0400389 unsigned long id = 0;
Stephen Smalleyf0740362012-01-04 12:30:47 -0500390 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;
Stephen Smalleyba70ee42012-07-10 15:49:04 -0400411 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 }
Stephen Smalleyf0740362012-01-04 12:30:47 -0500432
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) {
Stephen Smalleyba70ee42012-07-10 15:49:04 -0400439 if (strncasecmp(username, cur->user, cur->len-1))
Stephen Smalleyf0740362012-01-04 12:30:47 -0500440 continue;
441 } else {
Stephen Smalleyba70ee42012-07-10 15:49:04 -0400442 if (strcasecmp(username, cur->user))
Stephen Smalleyf0740362012-01-04 12:30:47 -0500443 continue;
444 }
445 }
446 if (cur->seinfo) {
447 if (!seinfo || strcasecmp(seinfo, cur->seinfo))
448 continue;
449 }
450 if (cur->name) {
Stephen Smalleyba70ee42012-07-10 15:49:04 -0400451 if (!pkgname || strcasecmp(pkgname, cur->name))
Stephen Smalleyf0740362012-01-04 12:30:47 -0500452 continue;
453 }
454
455 if (!cur->domain)
456 continue;
457
458 if (context_type_set(ctx, cur->domain))
459 goto oom;
460
Stephen Smalleyba70ee42012-07-10 15:49:04 -0400461 if (cur->levelFromUid && id) {
Stephen Smalleyf0740362012-01-04 12:30:47 -0500462 char level[255];
Stephen Smalleyf0740362012-01-04 12:30:47 -0500463 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",
Stephen Smalleyba70ee42012-07-10 15:49:04 -0400482 __FUNCTION__, uid, seinfo, pkgname);
Stephen Smalleyf0740362012-01-04 12:30:47 -0500483 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
Stephen Smalley0ca91b32012-03-19 10:25:53 -0400524static struct selabel_handle *sehandle = NULL;
525
526static void file_context_init(void)
527{
Stephen Smalley32ebfe82012-03-20 13:05:37 -0400528 int i = 0;
Stephen Smalley0ca91b32012-03-19 10:25:53 -0400529
Stephen Smalley32ebfe82012-03-20 13:05:37 -0400530 sehandle = NULL;
531 while ((sehandle == NULL) && seopts[i].value) {
532 sehandle = selabel_open(SELABEL_CTX_FILE, &seopts[i], 1);
533 i++;
534 }
Stephen Smalley0ca91b32012-03-19 10:25:53 -0400535
Stephen Smalley0ca91b32012-03-19 10:25:53 -0400536 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}