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