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