1/* 2 * File contexts backend for labeling system 3 * 4 * Author : Eamon Walsh <ewalsh@tycho.nsa.gov> 5 * Author : Stephen Smalley <sds@tycho.nsa.gov> 6 */ 7 8#include <assert.h> 9#include <fcntl.h> 10#include <stdarg.h> 11#include <string.h> 12#include <stdio.h> 13#include <ctype.h> 14#include <errno.h> 15#include <limits.h> 16#include <stdint.h> 17#include <unistd.h> 18#include <sys/mman.h> 19#include <sys/types.h> 20#include <sys/stat.h> 21 22#include "callbacks.h" 23#include "label_internal.h" 24#include "label_file.h" 25 26/* 27 * Internals, mostly moved over from matchpathcon.c 28 */ 29 30/* return the length of the text that is the stem of a file name */ 31static int get_stem_from_file_name(const char *const buf) 32{ 33 const char *tmp = strchr(buf + 1, '/'); 34 35 if (!tmp) 36 return 0; 37 return tmp - buf; 38} 39 40/* find the stem of a file name, returns the index into stem_arr (or -1 if 41 * there is no match - IE for a file in the root directory or a regex that is 42 * too complex for us). */ 43static int find_stem_from_file(struct saved_data *data, const char *key) 44{ 45 int i; 46 int stem_len = get_stem_from_file_name(key); 47 48 if (!stem_len) 49 return -1; 50 for (i = 0; i < data->num_stems; i++) { 51 if (stem_len == data->stem_arr[i].len 52 && !strncmp(key, data->stem_arr[i].buf, stem_len)) { 53 return i; 54 } 55 } 56 return -1; 57} 58 59/* 60 * Warn about duplicate specifications. 61 */ 62static int nodups_specs(struct saved_data *data, const char *path) 63{ 64 int rc = 0; 65 unsigned int ii, jj; 66 struct spec *curr_spec, *spec_arr = data->spec_arr; 67 68 for (ii = 0; ii < data->nspec; ii++) { 69 curr_spec = &spec_arr[ii]; 70 for (jj = ii + 1; jj < data->nspec; jj++) { 71 if ((!strcmp(spec_arr[jj].regex_str, 72 curr_spec->regex_str)) 73 && (!spec_arr[jj].mode || !curr_spec->mode 74 || spec_arr[jj].mode == curr_spec->mode)) { 75 rc = -1; 76 errno = EINVAL; 77 if (strcmp(spec_arr[jj].lr.ctx_raw, 78 curr_spec->lr.ctx_raw)) { 79 COMPAT_LOG 80 (SELINUX_ERROR, 81 "%s: Multiple different specifications for %s (%s and %s).\n", 82 path, curr_spec->regex_str, 83 spec_arr[jj].lr.ctx_raw, 84 curr_spec->lr.ctx_raw); 85 } else { 86 COMPAT_LOG 87 (SELINUX_ERROR, 88 "%s: Multiple same specifications for %s.\n", 89 path, curr_spec->regex_str); 90 } 91 } 92 } 93 } 94 return rc; 95} 96 97static int process_text_file(FILE *fp, const char *prefix, 98 struct selabel_handle *rec, const char *path) 99{ 100 int rc; 101 size_t line_len; 102 unsigned int lineno = 0; 103 char *line_buf = NULL; 104 105 while (getline(&line_buf, &line_len, fp) > 0) { 106 rc = process_line(rec, path, prefix, line_buf, ++lineno); 107 if (rc) 108 goto out; 109 } 110 rc = 0; 111out: 112 free(line_buf); 113 return rc; 114} 115 116static int load_mmap(FILE *fp, size_t len, struct selabel_handle *rec, 117 const char *path) 118{ 119 struct saved_data *data = (struct saved_data *)rec->data; 120 int rc; 121 char *addr, *str_buf; 122 int *stem_map; 123 struct mmap_area *mmap_area; 124 uint32_t i, magic, version; 125 uint32_t entry_len, stem_map_len, regex_array_len; 126 const char *reg_version; 127 const char *reg_arch; 128 char reg_arch_matches = 0; 129 130 mmap_area = malloc(sizeof(*mmap_area)); 131 if (!mmap_area) { 132 return -1; 133 } 134 135 addr = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fileno(fp), 0); 136 if (addr == MAP_FAILED) { 137 free(mmap_area); 138 perror("mmap"); 139 return -1; 140 } 141 142 /* save where we mmap'd the file to cleanup on close() */ 143 mmap_area->addr = mmap_area->next_addr = addr; 144 mmap_area->len = mmap_area->next_len = len; 145 mmap_area->next = data->mmap_areas; 146 data->mmap_areas = mmap_area; 147 148 /* check if this looks like an fcontext file */ 149 rc = next_entry(&magic, mmap_area, sizeof(uint32_t)); 150 if (rc < 0 || magic != SELINUX_MAGIC_COMPILED_FCONTEXT) 151 return -1; 152 153 /* check if this version is higher than we understand */ 154 rc = next_entry(&version, mmap_area, sizeof(uint32_t)); 155 if (rc < 0 || version > SELINUX_COMPILED_FCONTEXT_MAX_VERS) 156 return -1; 157 158 reg_version = regex_version(); 159 if (!reg_version) 160 return -1; 161 162 reg_arch = regex_arch_string(); 163 if (!reg_arch) 164 return -1; 165 166 if (version >= SELINUX_COMPILED_FCONTEXT_PCRE_VERS) { 167 168 len = strlen(reg_version); 169 170 rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t)); 171 if (rc < 0) 172 return -1; 173 174 /* Check version lengths */ 175 if (len != entry_len) 176 return -1; 177 178 /* Check if regex version mismatch */ 179 str_buf = malloc(entry_len + 1); 180 if (!str_buf) 181 return -1; 182 183 rc = next_entry(str_buf, mmap_area, entry_len); 184 if (rc < 0) { 185 free(str_buf); 186 return -1; 187 } 188 189 str_buf[entry_len] = '\0'; 190 if ((strcmp(str_buf, reg_version) != 0)) { 191 COMPAT_LOG(SELINUX_ERROR, 192 "Regex version mismatch, expected: %s actual: %s\n", 193 reg_version, str_buf); 194 free(str_buf); 195 return -1; 196 } 197 free(str_buf); 198 199 if (version >= SELINUX_COMPILED_FCONTEXT_REGEX_ARCH) { 200 len = strlen(reg_arch); 201 202 rc = next_entry(&entry_len, mmap_area, 203 sizeof(uint32_t)); 204 if (rc < 0) 205 return -1; 206 207 /* Check arch string lengths */ 208 if (len != entry_len) { 209 /* 210 * Skip the entry and conclude that we have 211 * a mismatch, which is not fatal. 212 */ 213 next_entry(NULL, mmap_area, entry_len); 214 goto end_arch_check; 215 } 216 217 /* Check if arch string mismatch */ 218 str_buf = malloc(entry_len + 1); 219 if (!str_buf) 220 return -1; 221 222 rc = next_entry(str_buf, mmap_area, entry_len); 223 if (rc < 0) { 224 free(str_buf); 225 return -1; 226 } 227 228 str_buf[entry_len] = '\0'; 229 reg_arch_matches = strcmp(str_buf, reg_arch) == 0; 230 free(str_buf); 231 } 232 } 233end_arch_check: 234 235 /* allocate the stems_data array */ 236 rc = next_entry(&stem_map_len, mmap_area, sizeof(uint32_t)); 237 if (rc < 0) 238 return -1; 239 240 /* 241 * map indexed by the stem # in the mmap file and contains the stem 242 * number in the data stem_arr 243 */ 244 stem_map = calloc(stem_map_len, sizeof(*stem_map)); 245 if (!stem_map) 246 return -1; 247 248 for (i = 0; i < stem_map_len; i++) { 249 char *buf; 250 uint32_t stem_len; 251 int newid; 252 253 /* the length does not include the nul */ 254 rc = next_entry(&stem_len, mmap_area, sizeof(uint32_t)); 255 if (rc < 0 || !stem_len) { 256 rc = -1; 257 goto out; 258 } 259 260 /* Check for stem_len wrap around. */ 261 if (stem_len < UINT32_MAX) { 262 buf = (char *)mmap_area->next_addr; 263 /* Check if over-run before null check. */ 264 rc = next_entry(NULL, mmap_area, (stem_len + 1)); 265 if (rc < 0) 266 goto out; 267 268 if (buf[stem_len] != '\0') { 269 rc = -1; 270 goto out; 271 } 272 } else { 273 rc = -1; 274 goto out; 275 } 276 277 /* store the mapping between old and new */ 278 newid = find_stem(data, buf, stem_len); 279 if (newid < 0) { 280 newid = store_stem(data, buf, stem_len); 281 if (newid < 0) { 282 rc = newid; 283 goto out; 284 } 285 data->stem_arr[newid].from_mmap = 1; 286 } 287 stem_map[i] = newid; 288 } 289 290 /* allocate the regex array */ 291 rc = next_entry(®ex_array_len, mmap_area, sizeof(uint32_t)); 292 if (rc < 0 || !regex_array_len) { 293 rc = -1; 294 goto out; 295 } 296 297 for (i = 0; i < regex_array_len; i++) { 298 struct spec *spec; 299 int32_t stem_id, meta_chars; 300 uint32_t mode = 0, prefix_len = 0; 301 302 rc = grow_specs(data); 303 if (rc < 0) 304 goto out; 305 306 spec = &data->spec_arr[data->nspec]; 307 spec->from_mmap = 1; 308 309 /* Process context */ 310 rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t)); 311 if (rc < 0 || !entry_len) { 312 rc = -1; 313 goto out; 314 } 315 316 str_buf = malloc(entry_len); 317 if (!str_buf) { 318 rc = -1; 319 goto out; 320 } 321 rc = next_entry(str_buf, mmap_area, entry_len); 322 if (rc < 0) { 323 free(str_buf); 324 goto out; 325 } 326 327 if (str_buf[entry_len - 1] != '\0') { 328 free(str_buf); 329 rc = -1; 330 goto out; 331 } 332 spec->lr.ctx_raw = str_buf; 333 334 if (strcmp(spec->lr.ctx_raw, "<<none>>") && rec->validating) { 335 if (selabel_validate(rec, &spec->lr) < 0) { 336 selinux_log(SELINUX_ERROR, 337 "%s: context %s is invalid\n", 338 path, spec->lr.ctx_raw); 339 goto out; 340 } 341 } 342 343 /* Process regex string */ 344 rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t)); 345 if (rc < 0 || !entry_len) { 346 rc = -1; 347 goto out; 348 } 349 350 spec->regex_str = (char *)mmap_area->next_addr; 351 rc = next_entry(NULL, mmap_area, entry_len); 352 if (rc < 0) 353 goto out; 354 355 if (spec->regex_str[entry_len - 1] != '\0') { 356 rc = -1; 357 goto out; 358 } 359 360 /* Process mode */ 361 if (version >= SELINUX_COMPILED_FCONTEXT_MODE) 362 rc = next_entry(&mode, mmap_area, sizeof(uint32_t)); 363 else 364 rc = next_entry(&mode, mmap_area, sizeof(mode_t)); 365 if (rc < 0) 366 goto out; 367 368 spec->mode = mode; 369 370 /* map the stem id from the mmap file to the data->stem_arr */ 371 rc = next_entry(&stem_id, mmap_area, sizeof(int32_t)); 372 if (rc < 0) 373 goto out; 374 375 if (stem_id < 0 || stem_id >= (int32_t)stem_map_len) 376 spec->stem_id = -1; 377 else 378 spec->stem_id = stem_map[stem_id]; 379 380 /* retrieve the hasMetaChars bit */ 381 rc = next_entry(&meta_chars, mmap_area, sizeof(uint32_t)); 382 if (rc < 0) 383 goto out; 384 385 spec->hasMetaChars = meta_chars; 386 /* and prefix length for use by selabel_lookup_best_match */ 387 if (version >= SELINUX_COMPILED_FCONTEXT_PREFIX_LEN) { 388 rc = next_entry(&prefix_len, mmap_area, 389 sizeof(uint32_t)); 390 if (rc < 0) 391 goto out; 392 393 spec->prefix_len = prefix_len; 394 } 395 396 rc = regex_load_mmap(mmap_area, &spec->regex, reg_arch_matches, 397 &spec->regex_compiled); 398 if (rc < 0) 399 goto out; 400 401 __pthread_mutex_init(&spec->regex_lock, NULL); 402 data->nspec++; 403 } 404 405 rc = 0; 406out: 407 free(stem_map); 408 409 return rc; 410} 411 412struct file_details { 413 const char *suffix; 414 struct stat sb; 415}; 416 417static char *rolling_append(char *current, const char *suffix, size_t max) 418{ 419 size_t size; 420 size_t suffix_size; 421 size_t current_size; 422 423 if (!suffix) 424 return current; 425 426 current_size = strlen(current); 427 suffix_size = strlen(suffix); 428 429 size = current_size + suffix_size; 430 if (size < current_size || size < suffix_size) 431 return NULL; 432 433 /* ensure space for the '.' and the '\0' characters. */ 434 if (size >= (SIZE_MAX - 2)) 435 return NULL; 436 437 size += 2; 438 439 if (size > max) 440 return NULL; 441 442 /* Append any given suffix */ 443 char *to = current + current_size; 444 *to++ = '.'; 445 strcpy(to, suffix); 446 447 return current; 448} 449 450static bool fcontext_is_binary(FILE *fp) 451{ 452 uint32_t magic; 453 454 size_t len = fread(&magic, sizeof(magic), 1, fp); 455 rewind(fp); 456 457 return (len && (magic == SELINUX_MAGIC_COMPILED_FCONTEXT)); 458} 459 460#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 461 462static FILE *open_file(const char *path, const char *suffix, 463 char *save_path, size_t len, struct stat *sb, bool open_oldest) 464{ 465 unsigned int i; 466 int rc; 467 char stack_path[len]; 468 struct file_details *found = NULL; 469 470 /* 471 * Rolling append of suffix. Try to open with path.suffix then the 472 * next as path.suffix.suffix and so forth. 473 */ 474 struct file_details fdetails[2] = { 475 { .suffix = suffix }, 476 { .suffix = "bin" } 477 }; 478 479 rc = snprintf(stack_path, sizeof(stack_path), "%s", path); 480 if (rc >= (int) sizeof(stack_path)) { 481 errno = ENAMETOOLONG; 482 return NULL; 483 } 484 485 for (i = 0; i < ARRAY_SIZE(fdetails); i++) { 486 487 /* This handles the case if suffix is null */ 488 path = rolling_append(stack_path, fdetails[i].suffix, 489 sizeof(stack_path)); 490 if (!path) 491 return NULL; 492 493 rc = stat(path, &fdetails[i].sb); 494 if (rc) 495 continue; 496 497 /* first file thing found, just take it */ 498 if (!found) { 499 strcpy(save_path, path); 500 found = &fdetails[i]; 501 continue; 502 } 503 504 /* 505 * Keep picking the newest file found. Where "newest" 506 * includes equality. This provides a precedence on 507 * secondary suffixes even when the timestamp is the 508 * same. Ie choose file_contexts.bin over file_contexts 509 * even if the time stamp is the same. Invert this logic 510 * on open_oldest set to true. The idea is that if the 511 * newest file failed to process, we can attempt to 512 * process the oldest. The logic here is subtle and depends 513 * on the array ordering in fdetails for the case when time 514 * stamps are the same. 515 */ 516 if (open_oldest ^ 517 (fdetails[i].sb.st_mtime >= found->sb.st_mtime)) { 518 found = &fdetails[i]; 519 strcpy(save_path, path); 520 } 521 } 522 523 if (!found) { 524 errno = ENOENT; 525 return NULL; 526 } 527 528 memcpy(sb, &found->sb, sizeof(*sb)); 529 return fopen(save_path, "re"); 530} 531 532static int process_file(const char *path, const char *suffix, 533 struct selabel_handle *rec, 534 const char *prefix, struct selabel_digest *digest) 535{ 536 int rc; 537 unsigned int i; 538 struct stat sb; 539 FILE *fp = NULL; 540 char found_path[PATH_MAX]; 541 542 /* 543 * On the first pass open the newest modified file. If it fails to 544 * process, then the second pass shall open the oldest file. If both 545 * passes fail, then it's a fatal error. 546 */ 547 for (i = 0; i < 2; i++) { 548 fp = open_file(path, suffix, found_path, sizeof(found_path), 549 &sb, i > 0); 550 if (fp == NULL) 551 return -1; 552 553 rc = fcontext_is_binary(fp) ? 554 load_mmap(fp, sb.st_size, rec, found_path) : 555 process_text_file(fp, prefix, rec, found_path); 556 if (!rc) 557 rc = digest_add_specfile(digest, fp, NULL, sb.st_size, 558 found_path); 559 560 fclose(fp); 561 562 if (!rc) 563 return 0; 564 } 565 return -1; 566} 567 568static void selabel_subs_fini(struct selabel_sub *ptr) 569{ 570 struct selabel_sub *next; 571 572 while (ptr) { 573 next = ptr->next; 574 free(ptr->src); 575 free(ptr->dst); 576 free(ptr); 577 ptr = next; 578 } 579} 580 581static char *selabel_sub(struct selabel_sub *ptr, const char *src) 582{ 583 char *dst = NULL; 584 int len; 585 586 while (ptr) { 587 if (strncmp(src, ptr->src, ptr->slen) == 0 ) { 588 if (src[ptr->slen] == '/' || 589 src[ptr->slen] == 0) { 590 if ((src[ptr->slen] == '/') && 591 (strcmp(ptr->dst, "/") == 0)) 592 len = ptr->slen + 1; 593 else 594 len = ptr->slen; 595 if (asprintf(&dst, "%s%s", ptr->dst, &src[len]) < 0) 596 return NULL; 597 return dst; 598 } 599 } 600 ptr = ptr->next; 601 } 602 return NULL; 603} 604 605static int selabel_subs_init(const char *path, struct selabel_digest *digest, 606 struct selabel_sub **out_subs) 607{ 608 char buf[1024]; 609 FILE *cfg = fopen(path, "re"); 610 struct selabel_sub *list = NULL, *sub = NULL; 611 struct stat sb; 612 int status = -1; 613 614 *out_subs = NULL; 615 if (!cfg) { 616 /* If the file does not exist, it is not fatal */ 617 return (errno == ENOENT) ? 0 : -1; 618 } 619 620 if (fstat(fileno(cfg), &sb) < 0) 621 goto out; 622 623 while (fgets_unlocked(buf, sizeof(buf) - 1, cfg)) { 624 char *ptr = NULL; 625 char *src = buf; 626 char *dst = NULL; 627 628 while (*src && isspace(*src)) 629 src++; 630 if (src[0] == '#') continue; 631 ptr = src; 632 while (*ptr && ! isspace(*ptr)) 633 ptr++; 634 *ptr++ = '\0'; 635 if (! *src) continue; 636 637 dst = ptr; 638 while (*dst && isspace(*dst)) 639 dst++; 640 ptr = dst; 641 while (*ptr && ! isspace(*ptr)) 642 ptr++; 643 *ptr = '\0'; 644 if (! *dst) 645 continue; 646 647 sub = malloc(sizeof(*sub)); 648 if (! sub) 649 goto err; 650 memset(sub, 0, sizeof(*sub)); 651 652 sub->src = strdup(src); 653 if (! sub->src) 654 goto err; 655 656 sub->dst = strdup(dst); 657 if (! sub->dst) 658 goto err; 659 660 sub->slen = strlen(src); 661 sub->next = list; 662 list = sub; 663 sub = NULL; 664 } 665 666 if (digest_add_specfile(digest, cfg, NULL, sb.st_size, path) < 0) 667 goto err; 668 669 *out_subs = list; 670 status = 0; 671 672out: 673 fclose(cfg); 674 return status; 675err: 676 if (sub) 677 free(sub->src); 678 free(sub); 679 while (list) { 680 sub = list->next; 681 free(list->src); 682 free(list->dst); 683 free(list); 684 list = sub; 685 } 686 goto out; 687} 688 689static char *selabel_sub_key(struct saved_data *data, const char *key) 690{ 691 char *ptr = NULL; 692 char *dptr = NULL; 693 694 ptr = selabel_sub(data->subs, key); 695 if (ptr) { 696 dptr = selabel_sub(data->dist_subs, ptr); 697 if (dptr) { 698 free(ptr); 699 ptr = dptr; 700 } 701 } else { 702 ptr = selabel_sub(data->dist_subs, key); 703 } 704 if (ptr) 705 return ptr; 706 707 return NULL; 708} 709 710static void closef(struct selabel_handle *rec); 711 712#ifdef OHOS_FC_INIT 713static int init(struct selabel_handle *rec, const struct selinux_opt *opts, unsigned n) 714{ 715 struct saved_data *data = (struct saved_data *)rec->data; 716 const char *prefix = NULL; 717 int status = -1; 718 size_t path_nums = 0; 719 size_t opt_nums = n; 720 721 while (n--) { 722 switch (opts[n].type) { 723 case SELABEL_OPT_PATH: 724 path_nums++; 725 break; 726 default: 727 break; 728 } 729 } 730 731 if (path_nums == 0) { 732 selinux_log(SELINUX_ERROR, "No specific file_contexts provided\n"); 733 goto finish; 734 } 735 736 rec->spec_file = (char **)calloc(path_nums, sizeof(char *)); 737 if (rec->spec_file == NULL) { 738 goto finish; 739 } 740 rec->spec_file_nums = path_nums; 741 size_t i = 0; 742 n = opt_nums; 743 while (n--) { 744 if (opts[n].type == SELABEL_OPT_PATH) { 745 rec->spec_file[i] = strdup(opts[n].value); 746 if (rec->spec_file[i] == NULL) { 747 goto finish; 748 } 749 i++; 750 } 751 } 752 753 for (int path_index = 0; path_index < rec->spec_file_nums; path_index++) { 754 status = process_file(rec->spec_file[path_index], NULL, rec, prefix, rec->digest); 755 if (status) { 756 goto finish; 757 } 758 759 if (rec->validating) { 760 status = nodups_specs(data, rec->spec_file[path_index]); 761 if (status) { 762 goto finish; 763 } 764 } 765 } 766 767 digest_gen_hash(rec->digest); 768 769 status = sort_specs(data); 770 771finish: 772 if (status) 773 closef(rec); 774 775 return status; 776} 777#else 778static int init(struct selabel_handle *rec, const struct selinux_opt *opts, 779 unsigned n) 780{ 781 struct saved_data *data = (struct saved_data *)rec->data; 782 const char *path = NULL; 783 const char *prefix = NULL; 784 int status = -1, baseonly = 0; 785 786 /* Process arguments */ 787 while (n--) 788 switch(opts[n].type) { 789 case SELABEL_OPT_PATH: 790 path = opts[n].value; 791 break; 792 case SELABEL_OPT_SUBSET: 793 prefix = opts[n].value; 794 break; 795 case SELABEL_OPT_BASEONLY: 796 baseonly = !!opts[n].value; 797 break; 798 } 799 800#if !defined(BUILD_HOST) && !defined(ANDROID) 801 char subs_file[PATH_MAX + 1]; 802 /* Process local and distribution substitution files */ 803 if (!path) { 804 status = selabel_subs_init( 805 selinux_file_context_subs_dist_path(), 806 rec->digest, &data->dist_subs); 807 if (status) 808 goto finish; 809 status = selabel_subs_init(selinux_file_context_subs_path(), 810 rec->digest, &data->subs); 811 if (status) 812 goto finish; 813 path = selinux_file_context_path(); 814 } else { 815 snprintf(subs_file, sizeof(subs_file), "%s.subs_dist", path); 816 status = selabel_subs_init(subs_file, rec->digest, 817 &data->dist_subs); 818 if (status) 819 goto finish; 820 snprintf(subs_file, sizeof(subs_file), "%s.subs", path); 821 status = selabel_subs_init(subs_file, rec->digest, 822 &data->subs); 823 if (status) 824 goto finish; 825 } 826 827#endif 828 829 if (!path) 830 goto finish; 831 832 rec->spec_file = strdup(path); 833 834 /* 835 * The do detailed validation of the input and fill the spec array 836 */ 837 status = process_file(path, NULL, rec, prefix, rec->digest); 838 if (status) 839 goto finish; 840 841 if (rec->validating) { 842 status = nodups_specs(data, path); 843 if (status) 844 goto finish; 845 } 846 847 if (!baseonly) { 848 status = process_file(path, "homedirs", rec, prefix, 849 rec->digest); 850 if (status && errno != ENOENT) 851 goto finish; 852 853 status = process_file(path, "local", rec, prefix, 854 rec->digest); 855 if (status && errno != ENOENT) 856 goto finish; 857 } 858 859 digest_gen_hash(rec->digest); 860 861 status = sort_specs(data); 862 863finish: 864 if (status) 865 closef(rec); 866 867 return status; 868} 869#endif 870 871/* 872 * Backend interface routines 873 */ 874static void closef(struct selabel_handle *rec) 875{ 876 struct saved_data *data = (struct saved_data *)rec->data; 877 struct mmap_area *area, *last_area; 878 struct spec *spec; 879 struct stem *stem; 880 unsigned int i; 881 882 selabel_subs_fini(data->subs); 883 selabel_subs_fini(data->dist_subs); 884 885 for (i = 0; i < data->nspec; i++) { 886 spec = &data->spec_arr[i]; 887 free(spec->lr.ctx_trans); 888 free(spec->lr.ctx_raw); 889 regex_data_free(spec->regex); 890 __pthread_mutex_destroy(&spec->regex_lock); 891 if (spec->from_mmap) 892 continue; 893 free(spec->regex_str); 894 free(spec->type_str); 895 } 896 897 for (i = 0; i < (unsigned int)data->num_stems; i++) { 898 stem = &data->stem_arr[i]; 899 if (stem->from_mmap) 900 continue; 901 free(stem->buf); 902 } 903 904 if (data->spec_arr) 905 free(data->spec_arr); 906 if (data->stem_arr) 907 free(data->stem_arr); 908 909 area = data->mmap_areas; 910 while (area) { 911 munmap(area->addr, area->len); 912 last_area = area; 913 area = area->next; 914 free(last_area); 915 } 916 free(data); 917} 918 919// Finds all the matches of |key| in the given context. Returns the result in 920// the allocated array and updates the match count. If match_count is NULL, 921// stops early once the 1st match is found. 922static struct spec **lookup_all(struct selabel_handle *rec, 923 const char *key, 924 int type, 925 bool partial, 926 size_t *match_count) 927{ 928 struct saved_data *data = (struct saved_data *)rec->data; 929 struct spec *spec_arr = data->spec_arr; 930 int i, rc, file_stem; 931 size_t len; 932 mode_t mode = (mode_t)type; 933 char *clean_key = NULL; 934 const char *prev_slash, *next_slash; 935 unsigned int sofar = 0; 936 char *sub = NULL; 937 938 struct spec **result = NULL; 939 if (match_count) { 940 *match_count = 0; 941 result = calloc(data->nspec, sizeof(struct spec*)); 942 } else { 943 result = calloc(1, sizeof(struct spec*)); 944 } 945 if (!result) { 946 selinux_log(SELINUX_ERROR, "Failed to allocate %zu bytes of data\n", 947 data->nspec * sizeof(struct spec*)); 948 goto finish; 949 } 950 951 if (!data->nspec) { 952 errno = ENOENT; 953 goto finish; 954 } 955 956 /* Remove duplicate slashes */ 957 if ((next_slash = strstr(key, "//"))) { 958 clean_key = (char *) malloc(strlen(key) + 1); 959 if (!clean_key) 960 goto finish; 961 prev_slash = key; 962 while (next_slash) { 963 memcpy(clean_key + sofar, prev_slash, next_slash - prev_slash); 964 sofar += next_slash - prev_slash; 965 prev_slash = next_slash + 1; 966 next_slash = strstr(prev_slash, "//"); 967 } 968 strcpy(clean_key + sofar, prev_slash); 969 key = clean_key; 970 } 971 972 /* remove trailing slash */ 973 len = strlen(key); 974 if (len == 0) { 975 errno = EINVAL; 976 goto finish; 977 } 978 979 if (len > 1 && key[len - 1] == '/') { 980 /* reuse clean_key from above if available */ 981 if (!clean_key) { 982 clean_key = (char *) malloc(len); 983 if (!clean_key) 984 goto finish; 985 986 memcpy(clean_key, key, len - 1); 987 } 988 989 clean_key[len - 1] = '\0'; 990 key = clean_key; 991 } 992 993 sub = selabel_sub_key(data, key); 994 if (sub) 995 key = sub; 996 997 file_stem = find_stem_from_file(data, key); 998 mode &= S_IFMT; 999 1000 /* 1001 * Check for matching specifications in reverse order, so that 1002 * the last matching specification is used. 1003 */ 1004 for (i = data->nspec - 1; i >= 0; i--) { 1005 struct spec *spec = &spec_arr[i]; 1006 /* if the spec in question matches no stem or has the same 1007 * stem as the file AND if the spec in question has no mode 1008 * specified or if the mode matches the file mode then we do 1009 * a regex check */ 1010 bool stem_matches = spec->stem_id == -1 || spec->stem_id == file_stem; 1011 // Don't check the stem if we want to find partial matches. 1012 // Otherwise the case "/abc/efg/(/.*)?" will be considered 1013 //a miss for "/abc". 1014 if ((partial || stem_matches) && 1015 (!mode || !spec->mode || mode == spec->mode)) { 1016 if (compile_regex(spec, NULL) < 0) 1017 goto finish; 1018 rc = regex_match(spec->regex, key, partial); 1019 if (rc == REGEX_MATCH || (partial && rc == REGEX_MATCH_PARTIAL)) { 1020 if (rc == REGEX_MATCH) { 1021#ifdef __ATOMIC_RELAXED 1022 __atomic_store_n(&spec->any_matches, 1023 true, __ATOMIC_RELAXED); 1024#else 1025#error "Please use a compiler that supports __atomic builtins" 1026#endif 1027 } 1028 1029 if (strcmp(spec_arr[i].lr.ctx_raw, "<<none>>") == 0) { 1030 errno = ENOENT; 1031 goto finish; 1032 } 1033 1034 if (match_count) { 1035 result[*match_count] = spec; 1036 *match_count += 1; 1037 // Continue to find all the matches. 1038 continue; 1039 } 1040 result[0] = spec; 1041 break; 1042 } 1043 1044 if (rc == REGEX_NO_MATCH) 1045 continue; 1046 1047 errno = ENOENT; 1048 /* else it's an error */ 1049 goto finish; 1050 } 1051 } 1052 if (!result[0]) 1053 errno = ENOENT; 1054 1055finish: 1056 free(clean_key); 1057 free(sub); 1058 if (result && !result[0]) { 1059 free(result); 1060 result = NULL; 1061 } 1062 return result; 1063} 1064 1065static struct spec *lookup_common(struct selabel_handle *rec, 1066 const char *key, 1067 int type, 1068 bool partial) { 1069 struct spec **matches = lookup_all(rec, key, type, partial, NULL); 1070 if (!matches) { 1071 return NULL; 1072 } 1073 struct spec *result = matches[0]; 1074 free(matches); 1075 return result; 1076} 1077 1078/* 1079 * Returns true if the digest of all partial matched contexts is the same as 1080 * the one saved by setxattr, otherwise returns false. The length of the SHA1 1081 * digest will always be returned. The caller must free any returned digests. 1082 */ 1083static bool get_digests_all_partial_matches(struct selabel_handle *rec, 1084 const char *pathname, 1085 uint8_t **calculated_digest, 1086 uint8_t **xattr_digest, 1087 size_t *digest_len) 1088{ 1089 uint8_t read_digest[SHA1_HASH_SIZE]; 1090 ssize_t read_size = getxattr(pathname, RESTORECON_PARTIAL_MATCH_DIGEST, 1091 read_digest, SHA1_HASH_SIZE 1092#ifdef __APPLE__ 1093 , 0, 0 1094#endif /* __APPLE __ */ 1095 ); 1096 uint8_t hash_digest[SHA1_HASH_SIZE]; 1097 bool status = selabel_hash_all_partial_matches(rec, pathname, 1098 hash_digest); 1099 1100 *xattr_digest = NULL; 1101 *calculated_digest = NULL; 1102 *digest_len = SHA1_HASH_SIZE; 1103 1104 if (read_size == SHA1_HASH_SIZE) { 1105 *xattr_digest = calloc(1, SHA1_HASH_SIZE + 1); 1106 if (!*xattr_digest) 1107 goto oom; 1108 1109 memcpy(*xattr_digest, read_digest, SHA1_HASH_SIZE); 1110 } 1111 1112 if (status) { 1113 *calculated_digest = calloc(1, SHA1_HASH_SIZE + 1); 1114 if (!*calculated_digest) 1115 goto oom; 1116 1117 memcpy(*calculated_digest, hash_digest, SHA1_HASH_SIZE); 1118 } 1119 1120 if (status && read_size == SHA1_HASH_SIZE && 1121 memcmp(read_digest, hash_digest, SHA1_HASH_SIZE) == 0) 1122 return true; 1123 1124 return false; 1125 1126oom: 1127 selinux_log(SELINUX_ERROR, "SELinux: %s: Out of memory\n", __func__); 1128 return false; 1129} 1130 1131static bool hash_all_partial_matches(struct selabel_handle *rec, const char *key, uint8_t *digest) 1132{ 1133 assert(digest); 1134 1135 size_t total_matches; 1136 struct spec **matches = lookup_all(rec, key, 0, true, &total_matches); 1137 if (!matches) { 1138 return false; 1139 } 1140 1141 Sha1Context context; 1142 Sha1Initialise(&context); 1143 size_t i; 1144 for (i = 0; i < total_matches; i++) { 1145 char* regex_str = matches[i]->regex_str; 1146 mode_t mode = matches[i]->mode; 1147 char* ctx_raw = matches[i]->lr.ctx_raw; 1148 1149 Sha1Update(&context, regex_str, strlen(regex_str) + 1); 1150 Sha1Update(&context, &mode, sizeof(mode_t)); 1151 Sha1Update(&context, ctx_raw, strlen(ctx_raw) + 1); 1152 } 1153 1154 SHA1_HASH sha1_hash; 1155 Sha1Finalise(&context, &sha1_hash); 1156 memcpy(digest, sha1_hash.bytes, SHA1_HASH_SIZE); 1157 1158 free(matches); 1159 return true; 1160} 1161 1162static struct selabel_lookup_rec *lookup(struct selabel_handle *rec, 1163 const char *key, int type) 1164{ 1165 struct spec *spec; 1166 1167 spec = lookup_common(rec, key, type, false); 1168 if (spec) 1169 return &spec->lr; 1170 return NULL; 1171} 1172 1173static bool partial_match(struct selabel_handle *rec, const char *key) 1174{ 1175 return lookup_common(rec, key, 0, true) ? true : false; 1176} 1177 1178static struct selabel_lookup_rec *lookup_best_match(struct selabel_handle *rec, 1179 const char *key, 1180 const char **aliases, 1181 int type) 1182{ 1183 size_t n, i; 1184 int best = -1; 1185 struct spec **specs; 1186 size_t prefix_len = 0; 1187 struct selabel_lookup_rec *lr = NULL; 1188 1189 if (!aliases || !aliases[0]) 1190 return lookup(rec, key, type); 1191 1192 for (n = 0; aliases[n]; n++) 1193 ; 1194 1195 specs = calloc(n+1, sizeof(struct spec *)); 1196 if (!specs) 1197 return NULL; 1198 specs[0] = lookup_common(rec, key, type, false); 1199 if (specs[0]) { 1200 if (!specs[0]->hasMetaChars) { 1201 /* exact match on key */ 1202 lr = &specs[0]->lr; 1203 goto out; 1204 } 1205 best = 0; 1206 prefix_len = specs[0]->prefix_len; 1207 } 1208 for (i = 1; i <= n; i++) { 1209 specs[i] = lookup_common(rec, aliases[i-1], type, false); 1210 if (specs[i]) { 1211 if (!specs[i]->hasMetaChars) { 1212 /* exact match on alias */ 1213 lr = &specs[i]->lr; 1214 goto out; 1215 } 1216 if (specs[i]->prefix_len > prefix_len) { 1217 best = i; 1218 prefix_len = specs[i]->prefix_len; 1219 } 1220 } 1221 } 1222 1223 if (best >= 0) { 1224 /* longest fixed prefix match on key or alias */ 1225 lr = &specs[best]->lr; 1226 } else { 1227 errno = ENOENT; 1228 } 1229 1230out: 1231 free(specs); 1232 return lr; 1233} 1234 1235static enum selabel_cmp_result incomp(struct spec *spec1, struct spec *spec2, const char *reason, int i, int j) 1236{ 1237 selinux_log(SELINUX_INFO, 1238 "selabel_cmp: mismatched %s on entry %d: (%s, %x, %s) vs entry %d: (%s, %x, %s)\n", 1239 reason, 1240 i, spec1->regex_str, spec1->mode, spec1->lr.ctx_raw, 1241 j, spec2->regex_str, spec2->mode, spec2->lr.ctx_raw); 1242 return SELABEL_INCOMPARABLE; 1243} 1244 1245static enum selabel_cmp_result cmp(struct selabel_handle *h1, 1246 struct selabel_handle *h2) 1247{ 1248 struct saved_data *data1 = (struct saved_data *)h1->data; 1249 struct saved_data *data2 = (struct saved_data *)h2->data; 1250 unsigned int i, nspec1 = data1->nspec, j, nspec2 = data2->nspec; 1251 struct spec *spec_arr1 = data1->spec_arr, *spec_arr2 = data2->spec_arr; 1252 struct stem *stem_arr1 = data1->stem_arr, *stem_arr2 = data2->stem_arr; 1253 bool skipped1 = false, skipped2 = false; 1254 1255 i = 0; 1256 j = 0; 1257 while (i < nspec1 && j < nspec2) { 1258 struct spec *spec1 = &spec_arr1[i]; 1259 struct spec *spec2 = &spec_arr2[j]; 1260 1261 /* 1262 * Because sort_specs() moves exact pathnames to the 1263 * end, we might need to skip over additional regex 1264 * entries that only exist in one of the configurations. 1265 */ 1266 if (!spec1->hasMetaChars && spec2->hasMetaChars) { 1267 j++; 1268 skipped2 = true; 1269 continue; 1270 } 1271 1272 if (spec1->hasMetaChars && !spec2->hasMetaChars) { 1273 i++; 1274 skipped1 = true; 1275 continue; 1276 } 1277 1278 if (spec1->regex && spec2->regex) { 1279 if (regex_cmp(spec1->regex, spec2->regex) == SELABEL_INCOMPARABLE){ 1280 return incomp(spec1, spec2, "regex", i, j); 1281 } 1282 } else { 1283 if (strcmp(spec1->regex_str, spec2->regex_str)) 1284 return incomp(spec1, spec2, "regex_str", i, j); 1285 } 1286 1287 if (spec1->mode != spec2->mode) 1288 return incomp(spec1, spec2, "mode", i, j); 1289 1290 if (spec1->stem_id == -1 && spec2->stem_id != -1) 1291 return incomp(spec1, spec2, "stem_id", i, j); 1292 if (spec2->stem_id == -1 && spec1->stem_id != -1) 1293 return incomp(spec1, spec2, "stem_id", i, j); 1294 if (spec1->stem_id != -1 && spec2->stem_id != -1) { 1295 struct stem *stem1 = &stem_arr1[spec1->stem_id]; 1296 struct stem *stem2 = &stem_arr2[spec2->stem_id]; 1297 if (stem1->len != stem2->len || 1298 strncmp(stem1->buf, stem2->buf, stem1->len)) 1299 return incomp(spec1, spec2, "stem", i, j); 1300 } 1301 1302 if (strcmp(spec1->lr.ctx_raw, spec2->lr.ctx_raw)) 1303 return incomp(spec1, spec2, "ctx_raw", i, j); 1304 1305 i++; 1306 j++; 1307 } 1308 1309 if ((skipped1 || i < nspec1) && !skipped2) 1310 return SELABEL_SUPERSET; 1311 if ((skipped2 || j < nspec2) && !skipped1) 1312 return SELABEL_SUBSET; 1313 if (skipped1 && skipped2) 1314 return SELABEL_INCOMPARABLE; 1315 return SELABEL_EQUAL; 1316} 1317 1318 1319static void stats(struct selabel_handle *rec) 1320{ 1321 struct saved_data *data = (struct saved_data *)rec->data; 1322 unsigned int i, nspec = data->nspec; 1323 struct spec *spec_arr = data->spec_arr; 1324 bool any_matches; 1325 1326 for (i = 0; i < nspec; i++) { 1327#ifdef __ATOMIC_RELAXED 1328 any_matches = __atomic_load_n(&spec_arr[i].any_matches, __ATOMIC_RELAXED); 1329#else 1330#error "Please use a compiler that supports __atomic builtins" 1331#endif 1332 if (!any_matches) { 1333 if (spec_arr[i].type_str) { 1334 COMPAT_LOG(SELINUX_WARNING, 1335 "Warning! No matches for (%s, %s, %s)\n", 1336 spec_arr[i].regex_str, 1337 spec_arr[i].type_str, 1338 spec_arr[i].lr.ctx_raw); 1339 } else { 1340 COMPAT_LOG(SELINUX_WARNING, 1341 "Warning! No matches for (%s, %s)\n", 1342 spec_arr[i].regex_str, 1343 spec_arr[i].lr.ctx_raw); 1344 } 1345 } 1346 } 1347} 1348 1349int selabel_file_init(struct selabel_handle *rec, 1350 const struct selinux_opt *opts, 1351 unsigned nopts) 1352{ 1353 struct saved_data *data; 1354 1355 data = (struct saved_data *)malloc(sizeof(*data)); 1356 if (!data) 1357 return -1; 1358 memset(data, 0, sizeof(*data)); 1359 1360 rec->data = data; 1361 rec->func_close = &closef; 1362 rec->func_stats = &stats; 1363 rec->func_lookup = &lookup; 1364 rec->func_partial_match = &partial_match; 1365 rec->func_get_digests_all_partial_matches = 1366 &get_digests_all_partial_matches; 1367 rec->func_hash_all_partial_matches = &hash_all_partial_matches; 1368 rec->func_lookup_best_match = &lookup_best_match; 1369 rec->func_cmp = &cmp; 1370 1371 return init(rec, opts, nopts); 1372} 1373