blob: 83ba7b75bede6eddd80f977ab7d6bed47ab6b322 [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
Stephen Smalleyedfaad82012-07-12 11:30:24 -0400256int selinux_android_setfilecon2(const char *pkgdir,
257 const char *pkgname,
258 const char *seinfo,
259 uid_t uid)
Stephen Smalleyf0740362012-01-04 12:30:47 -0500260{
Stephen Smalleyba70ee42012-07-10 15:49:04 -0400261 const char *username;
262 char *orig_ctx_str = NULL, *ctx_str, *end = NULL;
Stephen Smalleyf0740362012-01-04 12:30:47 -0500263 context_t ctx = NULL;
264 struct passwd *pw;
265 struct seapp_context *cur;
266 int i, rc;
Stephen Smalleyba70ee42012-07-10 15:49:04 -0400267 unsigned long id = 0;
Stephen Smalleyf0740362012-01-04 12:30:47 -0500268
269 if (is_selinux_enabled() <= 0)
270 return 0;
271
272 __selinux_once(once, seapp_context_init);
273
274 rc = getfilecon(pkgdir, &ctx_str);
275 if (rc < 0)
276 goto err;
277
278 ctx = context_new(ctx_str);
279 orig_ctx_str = ctx_str;
280 if (!ctx)
281 goto oom;
282
283 pw = getpwuid(uid);
284 if (!pw)
285 goto err;
Stephen Smalleyba70ee42012-07-10 15:49:04 -0400286 username = pw->pw_name;
287
288 if (!strncmp(username, "app_", 4)) {
289 id = strtoul(username + 4, NULL, 10);
290 if (id >= MLS_CATS)
291 goto err;
292 } else if (username[0] == 'u' && isdigit(username[1])) {
293 unsigned long unused;
294 unused = strtoul(username+1, &end, 10);
295 if (end[0] != '_')
296 goto err;
297 id = strtoul(end + 2, NULL, 10);
Stephen Smalleyc9726ab2012-07-11 16:10:13 -0400298 if (id >= MLS_CATS/2)
Stephen Smalleyba70ee42012-07-10 15:49:04 -0400299 goto err;
300 if (end[1] == 'i')
301 id += MLS_CATS/2;
302 else if (end[1] != 'a')
303 goto err;
304 /* use app_ for matching on the user= field */
305 username = "app_";
306 }
Stephen Smalleyf0740362012-01-04 12:30:47 -0500307
308 for (i = 0; i < nspec; i++) {
309 cur = seapp_contexts[i];
310
311 /* isSystemServer=true is only for app process labeling. */
312 if (cur->isSystemServer)
313 continue;
314
315 if (cur->user) {
316 if (cur->prefix) {
Stephen Smalleyba70ee42012-07-10 15:49:04 -0400317 if (strncasecmp(username, cur->user, cur->len-1))
Stephen Smalleyf0740362012-01-04 12:30:47 -0500318 continue;
319 } else {
Stephen Smalleyba70ee42012-07-10 15:49:04 -0400320 if (strcasecmp(username, cur->user))
Stephen Smalleyf0740362012-01-04 12:30:47 -0500321 continue;
322 }
323 }
324
Stephen Smalleyedfaad82012-07-12 11:30:24 -0400325 if (cur->seinfo) {
326 if (!seinfo || strcasecmp(seinfo, cur->seinfo))
327 continue;
328 }
Stephen Smalleyf0740362012-01-04 12:30:47 -0500329
330 if (cur->name) {
Stephen Smalleyba70ee42012-07-10 15:49:04 -0400331 if (!pkgname || strcasecmp(pkgname, cur->name))
Stephen Smalleyf0740362012-01-04 12:30:47 -0500332 continue;
333 }
334
335 if (!cur->type)
336 continue;
337
338 if (context_type_set(ctx, cur->type))
339 goto oom;
340
Stephen Smalleyc9726ab2012-07-11 16:10:13 -0400341 if (cur->levelFromUid) {
Stephen Smalleyf0740362012-01-04 12:30:47 -0500342 char level[255];
Stephen Smalleyf0740362012-01-04 12:30:47 -0500343 snprintf(level, sizeof level, "%s:c%lu",
344 context_range_get(ctx), id);
345 if (context_range_set(ctx, level))
346 goto oom;
347 } else if (cur->level) {
348 if (context_range_set(ctx, cur->level))
349 goto oom;
350 }
351
352 break;
353 }
354
355 ctx_str = context_str(ctx);
356 if (!ctx_str)
357 goto oom;
358
359 rc = security_check_context(ctx_str);
360 if (rc < 0)
361 goto err;
362
363 if (strcmp(ctx_str, orig_ctx_str)) {
364 rc = setfilecon(pkgdir, ctx_str);
365 if (rc < 0)
366 goto err;
367 }
368
369 rc = 0;
370out:
371 freecon(orig_ctx_str);
372 context_free(ctx);
373 return rc;
374err:
375 selinux_log(SELINUX_ERROR, "%s: Error setting context for pkgdir %s, uid %d: %s\n",
376 __FUNCTION__, pkgdir, uid, strerror(errno));
377 rc = -1;
378 goto out;
379oom:
380 selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __FUNCTION__);
381 rc = -1;
382 goto out;
383}
384
Stephen Smalleyedfaad82012-07-12 11:30:24 -0400385int selinux_android_setfilecon(const char *pkgdir,
386 const char *pkgname,
387 uid_t uid)
388{
389 return selinux_android_setfilecon2(pkgdir, pkgname, NULL, uid);
390}
391
Stephen Smalleyf0740362012-01-04 12:30:47 -0500392int selinux_android_setcontext(uid_t uid,
393 int isSystemServer,
394 const char *seinfo,
Stephen Smalleyba70ee42012-07-10 15:49:04 -0400395 const char *pkgname)
Stephen Smalleyf0740362012-01-04 12:30:47 -0500396{
Stephen Smalleyba70ee42012-07-10 15:49:04 -0400397 const char *username;
398 char *orig_ctx_str = NULL, *ctx_str, *end = NULL;
Stephen Smalleyf0740362012-01-04 12:30:47 -0500399 context_t ctx = NULL;
Stephen Smalleyba70ee42012-07-10 15:49:04 -0400400 unsigned long id = 0;
Stephen Smalleyf0740362012-01-04 12:30:47 -0500401 struct passwd *pw;
402 struct seapp_context *cur;
403 int i, rc;
404
405 if (is_selinux_enabled() <= 0)
406 return 0;
407
408 __selinux_once(once, seapp_context_init);
409
410 rc = getcon(&ctx_str);
411 if (rc)
412 goto err;
413
414 ctx = context_new(ctx_str);
415 orig_ctx_str = ctx_str;
416 if (!ctx)
417 goto oom;
418
419 pw = getpwuid(uid);
420 if (!pw)
421 goto err;
Stephen Smalleyba70ee42012-07-10 15:49:04 -0400422 username = pw->pw_name;
423
424 if (!strncmp(username, "app_", 4)) {
425 id = strtoul(username + 4, NULL, 10);
426 if (id >= MLS_CATS)
427 goto err;
428 } else if (username[0] == 'u' && isdigit(username[1])) {
429 unsigned long unused;
430 unused = strtoul(username+1, &end, 10);
431 if (end[0] != '_')
432 goto err;
433 id = strtoul(end + 2, NULL, 10);
Stephen Smalleyc9726ab2012-07-11 16:10:13 -0400434 if (id >= MLS_CATS/2)
Stephen Smalleyba70ee42012-07-10 15:49:04 -0400435 goto err;
436 if (end[1] == 'i')
437 id += MLS_CATS/2;
438 else if (end[1] != 'a')
439 goto err;
440 /* use app_ for matching on the user= field */
441 username = "app_";
442 }
Stephen Smalleyf0740362012-01-04 12:30:47 -0500443
444 for (i = 0; i < nspec; i++) {
445 cur = seapp_contexts[i];
446 if (cur->isSystemServer != isSystemServer)
447 continue;
448 if (cur->user) {
449 if (cur->prefix) {
Stephen Smalleyba70ee42012-07-10 15:49:04 -0400450 if (strncasecmp(username, cur->user, cur->len-1))
Stephen Smalleyf0740362012-01-04 12:30:47 -0500451 continue;
452 } else {
Stephen Smalleyba70ee42012-07-10 15:49:04 -0400453 if (strcasecmp(username, cur->user))
Stephen Smalleyf0740362012-01-04 12:30:47 -0500454 continue;
455 }
456 }
457 if (cur->seinfo) {
458 if (!seinfo || strcasecmp(seinfo, cur->seinfo))
459 continue;
460 }
461 if (cur->name) {
Stephen Smalleyba70ee42012-07-10 15:49:04 -0400462 if (!pkgname || strcasecmp(pkgname, cur->name))
Stephen Smalleyf0740362012-01-04 12:30:47 -0500463 continue;
464 }
465
466 if (!cur->domain)
467 continue;
468
469 if (context_type_set(ctx, cur->domain))
470 goto oom;
471
Stephen Smalleyc9726ab2012-07-11 16:10:13 -0400472 if (cur->levelFromUid) {
Stephen Smalleyf0740362012-01-04 12:30:47 -0500473 char level[255];
Stephen Smalleyf0740362012-01-04 12:30:47 -0500474 snprintf(level, sizeof level, "%s:c%lu",
475 context_range_get(ctx), id);
476 if (context_range_set(ctx, level))
477 goto oom;
478 } else if (cur->level) {
479 if (context_range_set(ctx, cur->level))
480 goto oom;
481 }
482
483 break;
484 }
485
486 if (i == nspec) {
487 /*
488 * No match.
489 * Fail to prevent staying in the zygote's context.
490 */
491 selinux_log(SELINUX_ERROR,
492 "%s: No match for app with uid %d, seinfo %s, name %s\n",
Stephen Smalleyba70ee42012-07-10 15:49:04 -0400493 __FUNCTION__, uid, seinfo, pkgname);
Stephen Smalleyf0740362012-01-04 12:30:47 -0500494 rc = -1;
495 goto out;
496 }
497
498 ctx_str = context_str(ctx);
499 if (!ctx_str)
500 goto oom;
501
502 rc = security_check_context(ctx_str);
503 if (rc < 0)
504 goto err;
505
506 if (strcmp(ctx_str, orig_ctx_str)) {
507 rc = setcon(ctx_str);
508 if (rc < 0)
509 goto err;
510 }
511
512 rc = 0;
513out:
514 freecon(orig_ctx_str);
515 context_free(ctx);
516 return rc;
517err:
518 if (isSystemServer)
519 selinux_log(SELINUX_ERROR,
520 "%s: Error setting context for system server: %s\n",
521 __FUNCTION__, strerror(errno));
522 else
523 selinux_log(SELINUX_ERROR,
524 "%s: Error setting context for app with uid %d, seinfo %s: %s\n",
525 __FUNCTION__, uid, seinfo, strerror(errno));
526
527 rc = -1;
528 goto out;
529oom:
530 selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __FUNCTION__);
531 rc = -1;
532 goto out;
533}
534
Stephen Smalley0ca91b32012-03-19 10:25:53 -0400535static struct selabel_handle *sehandle = NULL;
536
537static void file_context_init(void)
538{
Stephen Smalley32ebfe82012-03-20 13:05:37 -0400539 int i = 0;
Stephen Smalley0ca91b32012-03-19 10:25:53 -0400540
Stephen Smalley32ebfe82012-03-20 13:05:37 -0400541 sehandle = NULL;
542 while ((sehandle == NULL) && seopts[i].value) {
543 sehandle = selabel_open(SELABEL_CTX_FILE, &seopts[i], 1);
544 i++;
545 }
Stephen Smalley0ca91b32012-03-19 10:25:53 -0400546
Stephen Smalley0ca91b32012-03-19 10:25:53 -0400547 if (!sehandle)
548 selinux_log(SELINUX_ERROR,"%s: Error getting sehandle label (%s)\n",
549 __FUNCTION__, strerror(errno));
550}
551
552static pthread_once_t fc_once = PTHREAD_ONCE_INIT;
553
554int selinux_android_restorecon(const char *pathname)
555{
556
557 __selinux_once(fc_once, file_context_init);
558
559 int ret;
560
561 if (!sehandle)
562 goto bail;
563
564 struct stat sb;
565
566 if (lstat(pathname, &sb) < 0)
567 goto err;
568
569 char *oldcontext, *newcontext;
570
571 if (lgetfilecon(pathname, &oldcontext) < 0)
572 goto err;
573
574 if (selabel_lookup(sehandle, &newcontext, pathname, sb.st_mode) < 0)
575 goto err;
576
577 if (strcmp(newcontext, "<<none>>") && strcmp(oldcontext, newcontext))
578 if (lsetfilecon(pathname, newcontext) < 0)
579 goto err;
580
581 ret = 0;
582out:
583 if (oldcontext)
584 freecon(oldcontext);
585 if (newcontext)
586 freecon(newcontext);
587
588 return ret;
589
590err:
591 selinux_log(SELINUX_ERROR,
592 "%s: Error restoring context for %s (%s)\n",
593 __FUNCTION__, pathname, strerror(errno));
594
595bail:
596 ret = -1;
597 goto out;
598}