android.c revision 0beab96891a9ee1808b113479f167148cab5c998
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 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 95int selinux_android_seapp_context_reload(void) 96{ 97 FILE *fp = NULL; 98 char line_buf[BUFSIZ]; 99 char *token; 100 unsigned lineno; 101 struct seapp_context *cur; 102 char *p, *name = NULL, *value = NULL, *saveptr; 103 size_t len; 104 int i = 0, ret; 105 106 while ((fp==NULL) && seapp_contexts_file[i]) 107 fp = fopen(seapp_contexts_file[i++], "r"); 108 109 if (!fp) { 110 selinux_log(SELINUX_ERROR, "%s: could not open any seapp_contexts file", __FUNCTION__); 111 return -1; 112 } 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 230 ret = 0; 231 232out: 233 fclose(fp); 234 return ret; 235 236err: 237 selinux_log(SELINUX_ERROR, "%s: Error reading %s, line %u, name %s, value %s\n", 238 __FUNCTION__, seapp_contexts_file[i - 1], lineno, name, value); 239 ret = -1; 240 goto out; 241oom: 242 selinux_log(SELINUX_ERROR, 243 "%s: Out of memory\n", __FUNCTION__); 244 ret = -1; 245 goto out; 246} 247 248 249static void seapp_context_init(void) 250{ 251 selinux_android_seapp_context_reload(); 252} 253 254static pthread_once_t once = PTHREAD_ONCE_INIT; 255 256int selinux_android_setfilecon2(const char *pkgdir, 257 const char *pkgname, 258 const char *seinfo, 259 uid_t uid) 260{ 261 const char *username; 262 char *orig_ctx_str = NULL, *ctx_str, *end = NULL; 263 context_t ctx = NULL; 264 struct passwd *pw; 265 struct seapp_context *cur; 266 int i, rc; 267 unsigned long id = 0; 268 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; 286 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); 298 if (id >= MLS_CATS/2) 299 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 } 307 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) { 317 if (strncasecmp(username, cur->user, cur->len-1)) 318 continue; 319 } else { 320 if (strcasecmp(username, cur->user)) 321 continue; 322 } 323 } 324 325 if (cur->seinfo) { 326 if (!seinfo || strcasecmp(seinfo, cur->seinfo)) 327 continue; 328 } 329 330 if (cur->name) { 331 if (!pkgname || strcasecmp(pkgname, cur->name)) 332 continue; 333 } 334 335 if (!cur->type) 336 continue; 337 338 if (context_type_set(ctx, cur->type)) 339 goto oom; 340 341 if (cur->levelFromUid) { 342 char level[255]; 343 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 385int 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 392int selinux_android_setcontext(uid_t uid, 393 int isSystemServer, 394 const char *seinfo, 395 const char *pkgname) 396{ 397 const char *username; 398 char *orig_ctx_str = NULL, *ctx_str, *end = NULL; 399 context_t ctx = NULL; 400 unsigned long id = 0; 401 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; 422 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); 434 if (id >= MLS_CATS/2) 435 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 } 443 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) { 450 if (strncasecmp(username, cur->user, cur->len-1)) 451 continue; 452 } else { 453 if (strcasecmp(username, cur->user)) 454 continue; 455 } 456 } 457 if (cur->seinfo) { 458 if (!seinfo || strcasecmp(seinfo, cur->seinfo)) 459 continue; 460 } 461 if (cur->name) { 462 if (!pkgname || strcasecmp(pkgname, cur->name)) 463 continue; 464 } 465 466 if (!cur->domain) 467 continue; 468 469 if (context_type_set(ctx, cur->domain)) 470 goto oom; 471 472 if (cur->levelFromUid) { 473 char level[255]; 474 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", 493 __FUNCTION__, uid, seinfo, pkgname); 494 goto err; 495 } 496 497 ctx_str = context_str(ctx); 498 if (!ctx_str) 499 goto oom; 500 501 rc = security_check_context(ctx_str); 502 if (rc < 0) 503 goto err; 504 505 if (strcmp(ctx_str, orig_ctx_str)) { 506 rc = setcon(ctx_str); 507 if (rc < 0) 508 goto err; 509 } 510 511 rc = 0; 512out: 513 freecon(orig_ctx_str); 514 context_free(ctx); 515 return rc; 516err: 517 if (isSystemServer) 518 selinux_log(SELINUX_ERROR, 519 "%s: Error setting context for system server: %s\n", 520 __FUNCTION__, strerror(errno)); 521 else 522 selinux_log(SELINUX_ERROR, 523 "%s: Error setting context for app with uid %d, seinfo %s: %s\n", 524 __FUNCTION__, uid, seinfo, strerror(errno)); 525 526 rc = (security_getenforce() == 0) ? 0 : -1; 527 goto out; 528oom: 529 selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __FUNCTION__); 530 rc = -1; 531 goto out; 532} 533 534static struct selabel_handle *sehandle = NULL; 535 536static void file_context_init(void) 537{ 538 int i = 0; 539 540 sehandle = NULL; 541 while ((sehandle == NULL) && seopts[i].value) { 542 sehandle = selabel_open(SELABEL_CTX_FILE, &seopts[i], 1); 543 i++; 544 } 545 546 if (!sehandle) 547 selinux_log(SELINUX_ERROR,"%s: Error getting sehandle label (%s)\n", 548 __FUNCTION__, strerror(errno)); 549} 550 551static pthread_once_t fc_once = PTHREAD_ONCE_INIT; 552 553int selinux_android_restorecon(const char *pathname) 554{ 555 556 __selinux_once(fc_once, file_context_init); 557 558 int ret; 559 560 if (!sehandle) 561 goto bail; 562 563 struct stat sb; 564 565 if (lstat(pathname, &sb) < 0) 566 goto err; 567 568 char *oldcontext, *newcontext; 569 570 if (lgetfilecon(pathname, &oldcontext) < 0) 571 goto err; 572 573 if (selabel_lookup(sehandle, &newcontext, pathname, sb.st_mode) < 0) 574 goto err; 575 576 if (strcmp(newcontext, "<<none>>") && strcmp(oldcontext, newcontext)) 577 if (lsetfilecon(pathname, newcontext) < 0) 578 goto err; 579 580 ret = 0; 581out: 582 if (oldcontext) 583 freecon(oldcontext); 584 if (newcontext) 585 freecon(newcontext); 586 587 return ret; 588 589err: 590 selinux_log(SELINUX_ERROR, 591 "%s: Error restoring context for %s (%s)\n", 592 __FUNCTION__, pathname, strerror(errno)); 593 594bail: 595 ret = -1; 596 goto out; 597} 598 599 600struct selabel_handle* selinux_android_file_context_handle(void) { 601 602 __selinux_once(fc_once, file_context_init); 603 604 return sehandle; 605} 606