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