android.c revision 09f69843a9991d35888b35f0bfa8de0b11a824b2
1#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 <selinux/label.h> 17#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 */ 26static char const * const seapp_contexts_file[] = { 27 "/data/system/seapp_contexts", 28 "/seapp_contexts", 29 0 }; 30 31static const struct selinux_opt seopts[] = { 32 { SELABEL_OPT_PATH, "/data/system/file_contexts" }, 33 { SELABEL_OPT_PATH, "/file_contexts" }, 34 { 0, NULL } }; 35 36struct 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 *sebool; 49 char levelFromUid; 50}; 51 52static int seapp_context_cmp(const void *A, const void *B) 53{ 54 const struct seapp_context **sp1 = A, **sp2 = B; 55 const struct seapp_context *s1 = *sp1, *s2 = *sp2; 56 57 /* Give precedence to isSystemServer=true. */ 58 if (s1->isSystemServer != s2->isSystemServer) 59 return (s1->isSystemServer ? -1 : 1); 60 61 /* Give precedence to a specified user= over an unspecified user=. */ 62 if (s1->user && !s2->user) 63 return -1; 64 if (!s1->user && s2->user) 65 return 1; 66 67 if (s1->user) { 68 /* Give precedence to a fixed user= string over a prefix. */ 69 if (s1->prefix != s2->prefix) 70 return (s2->prefix ? -1 : 1); 71 72 /* Give precedence to a longer prefix over a shorter prefix. */ 73 if (s1->prefix && s1->len != s2->len) 74 return (s1->len > s2->len) ? -1 : 1; 75 } 76 77 /* Give precedence to a specified seinfo= over an unspecified seinfo=. */ 78 if (s1->seinfo && !s2->seinfo) 79 return -1; 80 if (!s1->seinfo && s2->seinfo) 81 return 1; 82 83 /* Give precedence to a specified name= over an unspecified name=. */ 84 if (s1->name && !s2->name) 85 return -1; 86 if (!s1->name && s2->name) 87 return 1; 88 89 /* Give precedence to a specified sebool= over an unspecified sebool=. */ 90 if (s1->sebool && !s2->sebool) 91 return -1; 92 if (!s1->sebool && s2->sebool) 93 return 1; 94 95 /* Anything else has equal precedence. */ 96 return 0; 97} 98 99static struct seapp_context **seapp_contexts = NULL; 100static int nspec = 0; 101 102int selinux_android_seapp_context_reload(void) 103{ 104 FILE *fp = NULL; 105 char line_buf[BUFSIZ]; 106 char *token; 107 unsigned lineno; 108 struct seapp_context *cur; 109 char *p, *name = NULL, *value = NULL, *saveptr; 110 size_t len; 111 int i = 0, ret; 112 113 while ((fp==NULL) && seapp_contexts_file[i]) 114 fp = fopen(seapp_contexts_file[i++], "r"); 115 116 if (!fp) { 117 selinux_log(SELINUX_ERROR, "%s: could not open any seapp_contexts file", __FUNCTION__); 118 return -1; 119 } 120 121 nspec = 0; 122 while (fgets(line_buf, sizeof line_buf - 1, fp)) { 123 p = line_buf; 124 while (isspace(*p)) 125 p++; 126 if (*p == '#' || *p == 0) 127 continue; 128 nspec++; 129 } 130 131 seapp_contexts = calloc(nspec, sizeof(struct seapp_context *)); 132 if (!seapp_contexts) 133 goto oom; 134 135 rewind(fp); 136 nspec = 0; 137 lineno = 1; 138 while (fgets(line_buf, sizeof line_buf - 1, fp)) { 139 len = strlen(line_buf); 140 if (line_buf[len - 1] == '\n') 141 line_buf[len - 1] = 0; 142 p = line_buf; 143 while (isspace(*p)) 144 p++; 145 if (*p == '#' || *p == 0) 146 continue; 147 148 cur = calloc(1, sizeof(struct seapp_context)); 149 if (!cur) 150 goto oom; 151 152 token = strtok_r(p, " \t", &saveptr); 153 if (!token) 154 goto err; 155 156 while (1) { 157 name = token; 158 value = strchr(name, '='); 159 if (!value) 160 goto err; 161 *value++ = 0; 162 163 if (!strcasecmp(name, "isSystemServer")) { 164 if (!strcasecmp(value, "true")) 165 cur->isSystemServer = 1; 166 else if (!strcasecmp(value, "false")) 167 cur->isSystemServer = 0; 168 else { 169 goto err; 170 } 171 } else if (!strcasecmp(name, "user")) { 172 cur->user = strdup(value); 173 if (!cur->user) 174 goto oom; 175 cur->len = strlen(cur->user); 176 if (cur->user[cur->len-1] == '*') 177 cur->prefix = 1; 178 } else if (!strcasecmp(name, "seinfo")) { 179 cur->seinfo = strdup(value); 180 if (!cur->seinfo) 181 goto oom; 182 } else if (!strcasecmp(name, "name")) { 183 cur->name = strdup(value); 184 if (!cur->name) 185 goto oom; 186 } else if (!strcasecmp(name, "domain")) { 187 cur->domain = strdup(value); 188 if (!cur->domain) 189 goto oom; 190 } else if (!strcasecmp(name, "type")) { 191 cur->type = strdup(value); 192 if (!cur->type) 193 goto oom; 194 } else if (!strcasecmp(name, "levelFromUid")) { 195 if (!strcasecmp(value, "true")) 196 cur->levelFromUid = 1; 197 else if (!strcasecmp(value, "false")) 198 cur->levelFromUid = 0; 199 else { 200 goto err; 201 } 202 } else if (!strcasecmp(name, "level")) { 203 cur->level = strdup(value); 204 if (!cur->level) 205 goto oom; 206 } else if (!strcasecmp(name, "sebool")) { 207 cur->sebool = strdup(value); 208 if (!cur->sebool) 209 goto oom; 210 } else 211 goto err; 212 213 token = strtok_r(NULL, " \t", &saveptr); 214 if (!token) 215 break; 216 } 217 218 seapp_contexts[nspec] = cur; 219 nspec++; 220 lineno++; 221 } 222 223 qsort(seapp_contexts, nspec, sizeof(struct seapp_context *), 224 seapp_context_cmp); 225 226#if DEBUG 227 { 228 int i; 229 for (i = 0; i < nspec; i++) { 230 cur = seapp_contexts[i]; 231 selinux_log(SELINUX_INFO, "%s: isSystemServer=%s user=%s seinfo=%s name=%s sebool=%s -> domain=%s type=%s level=%s levelFromUid=%s", 232 __FUNCTION__, 233 cur->isSystemServer ? "true" : "false", cur->user, 234 cur->seinfo, cur->name, cur->sebool, cur->domain, 235 cur->type, cur->level, 236 cur->levelFromUid ? "true" : "false"); 237 } 238 } 239#endif 240 241 ret = 0; 242 243out: 244 fclose(fp); 245 return ret; 246 247err: 248 selinux_log(SELINUX_ERROR, "%s: Error reading %s, line %u, name %s, value %s\n", 249 __FUNCTION__, seapp_contexts_file[i - 1], lineno, name, value); 250 ret = -1; 251 goto out; 252oom: 253 selinux_log(SELINUX_ERROR, 254 "%s: Out of memory\n", __FUNCTION__); 255 ret = -1; 256 goto out; 257} 258 259 260static void seapp_context_init(void) 261{ 262 selinux_android_seapp_context_reload(); 263} 264 265static pthread_once_t once = PTHREAD_ONCE_INIT; 266 267int selinux_android_setfilecon2(const char *pkgdir, 268 const char *pkgname, 269 const char *seinfo, 270 uid_t uid) 271{ 272 const char *username; 273 char *orig_ctx_str = NULL, *ctx_str, *end = NULL; 274 context_t ctx = NULL; 275 struct passwd *pw; 276 struct seapp_context *cur; 277 int i, rc; 278 unsigned long id = 0; 279 280 if (is_selinux_enabled() <= 0) 281 return 0; 282 283 __selinux_once(once, seapp_context_init); 284 285 rc = getfilecon(pkgdir, &ctx_str); 286 if (rc < 0) 287 goto err; 288 289 ctx = context_new(ctx_str); 290 orig_ctx_str = ctx_str; 291 if (!ctx) 292 goto oom; 293 294 pw = getpwuid(uid); 295 if (!pw) 296 goto err; 297 username = pw->pw_name; 298 299 if (!strncmp(username, "app_", 4)) { 300 id = strtoul(username + 4, NULL, 10); 301 if (id >= MLS_CATS) 302 goto err; 303 } else if (username[0] == 'u' && isdigit(username[1])) { 304 unsigned long unused; 305 unused = strtoul(username+1, &end, 10); 306 if (end[0] != '_') 307 goto err; 308 id = strtoul(end + 2, NULL, 10); 309 if (id >= MLS_CATS/2) 310 goto err; 311 if (end[1] == 'i') 312 id += MLS_CATS/2; 313 else if (end[1] != 'a') 314 goto err; 315 /* use app_ for matching on the user= field */ 316 username = "app_"; 317 } 318 319 for (i = 0; i < nspec; i++) { 320 cur = seapp_contexts[i]; 321 322 /* isSystemServer=true is only for app process labeling. */ 323 if (cur->isSystemServer) 324 continue; 325 326 if (cur->user) { 327 if (cur->prefix) { 328 if (strncasecmp(username, cur->user, cur->len-1)) 329 continue; 330 } else { 331 if (strcasecmp(username, cur->user)) 332 continue; 333 } 334 } 335 336 if (cur->seinfo) { 337 if (!seinfo || strcasecmp(seinfo, cur->seinfo)) 338 continue; 339 } 340 341 if (cur->name) { 342 if (!pkgname || strcasecmp(pkgname, cur->name)) 343 continue; 344 } 345 346 if (!cur->type) 347 continue; 348 349 if (cur->sebool) { 350 int value = security_get_boolean_active(cur->sebool); 351 if (value == 0) 352 continue; 353 else if (value == -1) { 354 selinux_log(SELINUX_ERROR, \ 355 "Could not find boolean: %s ", cur->sebool); 356 goto err; 357 } 358 } 359 360 if (context_type_set(ctx, cur->type)) 361 goto oom; 362 363 if (cur->levelFromUid) { 364 char level[255]; 365 snprintf(level, sizeof level, "%s:c%lu", 366 context_range_get(ctx), id); 367 if (context_range_set(ctx, level)) 368 goto oom; 369 } else if (cur->level) { 370 if (context_range_set(ctx, cur->level)) 371 goto oom; 372 } 373 374 break; 375 } 376 377 ctx_str = context_str(ctx); 378 if (!ctx_str) 379 goto oom; 380 381 rc = security_check_context(ctx_str); 382 if (rc < 0) 383 goto err; 384 385 if (strcmp(ctx_str, orig_ctx_str)) { 386 rc = setfilecon(pkgdir, ctx_str); 387 if (rc < 0) 388 goto err; 389 } 390 391 rc = 0; 392out: 393 freecon(orig_ctx_str); 394 context_free(ctx); 395 return rc; 396err: 397 selinux_log(SELINUX_ERROR, "%s: Error setting context for pkgdir %s, uid %d: %s\n", 398 __FUNCTION__, pkgdir, uid, strerror(errno)); 399 rc = -1; 400 goto out; 401oom: 402 selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __FUNCTION__); 403 rc = -1; 404 goto out; 405} 406 407int selinux_android_setfilecon(const char *pkgdir, 408 const char *pkgname, 409 uid_t uid) 410{ 411 return selinux_android_setfilecon2(pkgdir, pkgname, NULL, uid); 412} 413 414int selinux_android_setcontext(uid_t uid, 415 int isSystemServer, 416 const char *seinfo, 417 const char *pkgname) 418{ 419 const char *username; 420 char *orig_ctx_str = NULL, *ctx_str, *end = NULL; 421 context_t ctx = NULL; 422 unsigned long id = 0; 423 struct passwd *pw; 424 struct seapp_context *cur; 425 int i, rc; 426 427 if (is_selinux_enabled() <= 0) 428 return 0; 429 430 __selinux_once(once, seapp_context_init); 431 432 rc = getcon(&ctx_str); 433 if (rc) 434 goto err; 435 436 ctx = context_new(ctx_str); 437 orig_ctx_str = ctx_str; 438 if (!ctx) 439 goto oom; 440 441 pw = getpwuid(uid); 442 if (!pw) 443 goto err; 444 username = pw->pw_name; 445 446 if (!strncmp(username, "app_", 4)) { 447 id = strtoul(username + 4, NULL, 10); 448 if (id >= MLS_CATS) 449 goto err; 450 } else if (username[0] == 'u' && isdigit(username[1])) { 451 unsigned long unused; 452 unused = strtoul(username+1, &end, 10); 453 if (end[0] != '_') 454 goto err; 455 id = strtoul(end + 2, NULL, 10); 456 if (id >= MLS_CATS/2) 457 goto err; 458 if (end[1] == 'i') 459 id += MLS_CATS/2; 460 else if (end[1] != 'a') 461 goto err; 462 /* use app_ for matching on the user= field */ 463 username = "app_"; 464 } 465 466 for (i = 0; i < nspec; i++) { 467 cur = seapp_contexts[i]; 468 469 if (cur->isSystemServer != isSystemServer) 470 continue; 471 if (cur->user) { 472 if (cur->prefix) { 473 if (strncasecmp(username, cur->user, cur->len-1)) 474 continue; 475 } else { 476 if (strcasecmp(username, cur->user)) 477 continue; 478 } 479 } 480 if (cur->seinfo) { 481 if (!seinfo || strcasecmp(seinfo, cur->seinfo)) 482 continue; 483 } 484 if (cur->name) { 485 if (!pkgname || strcasecmp(pkgname, cur->name)) 486 continue; 487 } 488 489 if (!cur->domain) 490 continue; 491 492 if (cur->sebool) { 493 int value = security_get_boolean_active(cur->sebool); 494 if (value == 0) 495 continue; 496 else if (value == -1) { 497 selinux_log(SELINUX_ERROR, \ 498 "Could not find boolean: %s ", cur->sebool); 499 goto err; 500 } 501 } 502 503 if (context_type_set(ctx, cur->domain)) 504 goto oom; 505 506 if (cur->levelFromUid) { 507 char level[255]; 508 snprintf(level, sizeof level, "%s:c%lu", 509 context_range_get(ctx), id); 510 if (context_range_set(ctx, level)) 511 goto oom; 512 } else if (cur->level) { 513 if (context_range_set(ctx, cur->level)) 514 goto oom; 515 } 516 517 break; 518 } 519 520 if (i == nspec) { 521 /* 522 * No match. 523 * Fail to prevent staying in the zygote's context. 524 */ 525 selinux_log(SELINUX_ERROR, 526 "%s: No match for app with uid %d, seinfo %s, name %s\n", 527 __FUNCTION__, uid, seinfo, pkgname); 528 529 rc = (security_getenforce() == 0) ? 0 : -1; 530 goto out; 531 } 532 533 ctx_str = context_str(ctx); 534 if (!ctx_str) 535 goto oom; 536 537 rc = security_check_context(ctx_str); 538 if (rc < 0) 539 goto err; 540 541 if (strcmp(ctx_str, orig_ctx_str)) { 542 rc = setcon(ctx_str); 543 if (rc < 0) 544 goto err; 545 } 546 547 rc = 0; 548out: 549 freecon(orig_ctx_str); 550 context_free(ctx); 551 return rc; 552err: 553 if (isSystemServer) 554 selinux_log(SELINUX_ERROR, 555 "%s: Error setting context for system server: %s\n", 556 __FUNCTION__, strerror(errno)); 557 else 558 selinux_log(SELINUX_ERROR, 559 "%s: Error setting context for app with uid %d, seinfo %s: %s\n", 560 __FUNCTION__, uid, seinfo, strerror(errno)); 561 562 rc = -1; 563 goto out; 564oom: 565 selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __FUNCTION__); 566 rc = -1; 567 goto out; 568} 569 570static struct selabel_handle *sehandle = NULL; 571 572static void file_context_init(void) 573{ 574 int i = 0; 575 576 sehandle = NULL; 577 while ((sehandle == NULL) && seopts[i].value) { 578 sehandle = selabel_open(SELABEL_CTX_FILE, &seopts[i], 1); 579 i++; 580 } 581 582 if (!sehandle) 583 selinux_log(SELINUX_ERROR,"%s: Error getting sehandle label (%s)\n", 584 __FUNCTION__, strerror(errno)); 585} 586 587static pthread_once_t fc_once = PTHREAD_ONCE_INIT; 588 589int selinux_android_restorecon(const char *pathname) 590{ 591 592 __selinux_once(fc_once, file_context_init); 593 594 int ret; 595 596 if (!sehandle) 597 goto bail; 598 599 struct stat sb; 600 601 if (lstat(pathname, &sb) < 0) 602 goto err; 603 604 char *oldcontext, *newcontext; 605 606 if (lgetfilecon(pathname, &oldcontext) < 0) 607 goto err; 608 609 if (selabel_lookup(sehandle, &newcontext, pathname, sb.st_mode) < 0) 610 goto err; 611 612 if (strcmp(newcontext, "<<none>>") && strcmp(oldcontext, newcontext)) 613 if (lsetfilecon(pathname, newcontext) < 0) 614 goto err; 615 616 ret = 0; 617out: 618 if (oldcontext) 619 freecon(oldcontext); 620 if (newcontext) 621 freecon(newcontext); 622 623 return ret; 624 625err: 626 selinux_log(SELINUX_ERROR, 627 "%s: Error restoring context for %s (%s)\n", 628 __FUNCTION__, pathname, strerror(errno)); 629 630bail: 631 ret = -1; 632 goto out; 633} 634 635 636struct selabel_handle* selinux_android_file_context_handle(void) { 637 638 __selinux_once(fc_once, file_context_init); 639 640 return sehandle; 641} 642