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