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