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 if (rc < 0) 394 goto out; 395 396 data->nspec++; 397 } 398 399 rc = 0; 400 out: 401 free(stem_map); 402 403 return rc; 404 } 405 406 struct file_details { 407 const char *suffix; 408 struct stat sb; 409 }; 410 411 static char *rolling_append(char *current, const char *suffix, size_t max) 412 { 413 size_t size; 414 size_t suffix_size; 415 size_t current_size; 416 417 if (!suffix) 418 return current; 419 420 current_size = strlen(current); 421 suffix_size = strlen(suffix); 422 423 size = current_size + suffix_size; 424 if (size < current_size || size < suffix_size) 425 return NULL; 426 427 /* ensure space for the '.' and the '\0' characters. */ 428 if (size >= (SIZE_MAX - 2)) 429 return NULL; 430 431 size += 2; 432 433 if (size > max) 434 return NULL; 435 436 /* Append any given suffix */ 437 char *to = current + current_size; 438 *to++ = '.'; 439 strcpy(to, suffix); 440 441 return current; 442 } 443 444 static bool fcontext_is_binary(FILE *fp) 445 { 446 uint32_t magic; 447 448 size_t len = fread(&magic, sizeof(magic), 1, fp); 449 rewind(fp); 450 451 return (len && (magic == SELINUX_MAGIC_COMPILED_FCONTEXT)); 452 } 453 454 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 455 456 static FILE *open_file(const char *path, const char *suffix, 457 char *save_path, size_t len, struct stat *sb, bool open_oldest) 458 { 459 unsigned int i; 460 int rc; 461 char stack_path[len]; 462 struct file_details *found = NULL; 463 464 /* 465 * Rolling append of suffix. Try to open with path.suffix then the 466 * next as path.suffix.suffix and so forth. 467 */ 468 struct file_details fdetails[2] = { 469 { .suffix = suffix }, 470 { .suffix = "bin" } 471 }; 472 473 rc = snprintf(stack_path, sizeof(stack_path), "%s", path); 474 if (rc >= (int) sizeof(stack_path)) { 475 errno = ENAMETOOLONG; 476 return NULL; 477 } 478 479 for (i = 0; i < ARRAY_SIZE(fdetails); i++) { 480 481 /* This handles the case if suffix is null */ 482 path = rolling_append(stack_path, fdetails[i].suffix, 483 sizeof(stack_path)); 484 if (!path) 485 return NULL; 486 487 rc = stat(path, &fdetails[i].sb); 488 if (rc) 489 continue; 490 491 /* first file thing found, just take it */ 492 if (!found) { 493 strcpy(save_path, path); 494 found = &fdetails[i]; 495 continue; 496 } 497 498 /* 499 * Keep picking the newest file found. Where "newest" 500 * includes equality. This provides a precedence on 501 * secondary suffixes even when the timestamp is the 502 * same. Ie choose file_contexts.bin over file_contexts 503 * even if the time stamp is the same. Invert this logic 504 * on open_oldest set to true. The idea is that if the 505 * newest file failed to process, we can attempt to 506 * process the oldest. The logic here is subtle and depends 507 * on the array ordering in fdetails for the case when time 508 * stamps are the same. 509 */ 510 if (open_oldest ^ 511 (fdetails[i].sb.st_mtime >= found->sb.st_mtime)) { 512 found = &fdetails[i]; 513 strcpy(save_path, path); 514 } 515 } 516 517 if (!found) { 518 errno = ENOENT; 519 return NULL; 520 } 521 522 memcpy(sb, &found->sb, sizeof(*sb)); 523 return fopen(save_path, "r"); 524 } 525 526 static int process_file(const char *path, const char *suffix, 527 struct selabel_handle *rec, 528 const char *prefix, struct selabel_digest *digest) 529 { 530 int rc; 531 unsigned int i; 532 struct stat sb; 533 FILE *fp = NULL; 534 char found_path[PATH_MAX]; 535 536 /* 537 * On the first pass open the newest modified file. If it fails to 538 * process, then the second pass shall open the oldest file. If both 539 * passes fail, then it's a fatal error. 540 */ 541 for (i = 0; i < 2; i++) { 542 fp = open_file(path, suffix, found_path, sizeof(found_path), 543 &sb, i > 0); 544 if (fp == NULL) 545 return -1; 546 547 rc = fcontext_is_binary(fp) ? 548 load_mmap(fp, sb.st_size, rec, found_path) : 549 process_text_file(fp, prefix, rec, found_path); 550 if (!rc) 551 rc = digest_add_specfile(digest, fp, NULL, sb.st_size, 552 found_path); 553 554 fclose(fp); 555 556 if (!rc) 557 return 0; 558 } 559 return -1; 560 } 561 562 static void closef(struct selabel_handle *rec); 563 564 static int init(struct selabel_handle *rec, const struct selinux_opt *opts, 565 unsigned n) 566 { 567 struct saved_data *data = (struct saved_data *)rec->data; 568 size_t num_paths = 0; 569 char **path = NULL; 570 const char *prefix = NULL; 571 int status = -1; 572 size_t i; 573 bool baseonly = false; 574 bool path_provided; 575 576 /* Process arguments */ 577 i = n; 578 while (i--) 579 switch(opts[i].type) { 580 case SELABEL_OPT_PATH: 581 num_paths++; 582 break; 583 case SELABEL_OPT_SUBSET: 584 prefix = opts[i].value; 585 break; 586 case SELABEL_OPT_BASEONLY: 587 baseonly = !!opts[i].value; 588 break; 589 } 590 591 if (!num_paths) { 592 num_paths = 1; 593 path_provided = false; 594 } else { 595 path_provided = true; 596 } 597 598 path = calloc(num_paths, sizeof(*path)); 599 if (path == NULL) { 600 goto finish; 601 } 602 rec->spec_files = path; 603 rec->spec_files_len = num_paths; 604 605 if (path_provided) { 606 for (i = 0; i < n; i++) { 607 switch(opts[i].type) { 608 case SELABEL_OPT_PATH: 609 *path = strdup(opts[i].value); 610 if (*path == NULL) 611 goto finish; 612 path++; 613 break; 614 default: 615 break; 616 } 617 } 618 } 619 #if !defined(BUILD_HOST) && !defined(ANDROID) 620 char subs_file[PATH_MAX + 1]; 621 /* Process local and distribution substitution files */ 622 if (!path_provided) { 623 rec->dist_subs = 624 selabel_subs_init(selinux_file_context_subs_dist_path(), 625 rec->dist_subs, rec->digest); 626 rec->subs = selabel_subs_init(selinux_file_context_subs_path(), 627 rec->subs, rec->digest); 628 rec->spec_files[0] = strdup(selinux_file_context_path()); 629 if (rec->spec_files[0] == NULL) 630 goto finish; 631 } else { 632 for (i = 0; i < num_paths; i++) { 633 snprintf(subs_file, sizeof(subs_file), "%s.subs_dist", rec->spec_files[i]); 634 rec->dist_subs = selabel_subs_init(subs_file, rec->dist_subs, rec->digest); 635 snprintf(subs_file, sizeof(subs_file), "%s.subs", rec->spec_files[i]); 636 rec->subs = selabel_subs_init(subs_file, rec->subs, rec->digest); 637 } 638 } 639 #else 640 if (!path_provided) { 641 selinux_log(SELINUX_ERROR, "No path given to file labeling backend\n"); 642 goto finish; 643 } 644 #endif 645 646 /* 647 * Do detailed validation of the input and fill the spec array 648 */ 649 for (i = 0; i < num_paths; i++) { 650 status = process_file(rec->spec_files[i], NULL, rec, prefix, rec->digest); 651 if (status) 652 goto finish; 653 654 if (rec->validating) { 655 status = nodups_specs(data, rec->spec_files[i]); 656 if (status) 657 goto finish; 658 } 659 } 660 661 if (!baseonly) { 662 status = process_file(rec->spec_files[0], "homedirs", rec, prefix, 663 rec->digest); 664 if (status && errno != ENOENT) 665 goto finish; 666 667 status = process_file(rec->spec_files[0], "local", rec, prefix, 668 rec->digest); 669 if (status && errno != ENOENT) 670 goto finish; 671 } 672 673 digest_gen_hash(rec->digest); 674 675 status = sort_specs(data); 676 677 finish: 678 if (status) 679 closef(rec); 680 681 return status; 682 } 683 684 /* 685 * Backend interface routines 686 */ 687 static void closef(struct selabel_handle *rec) 688 { 689 struct saved_data *data = (struct saved_data *)rec->data; 690 struct mmap_area *area, *last_area; 691 struct spec *spec; 692 struct stem *stem; 693 unsigned int i; 694 695 if (!data) 696 return; 697 698 /* make sure successive ->func_close() calls are harmless */ 699 rec->data = NULL; 700 701 for (i = 0; i < data->nspec; i++) { 702 spec = &data->spec_arr[i]; 703 free(spec->lr.ctx_trans); 704 free(spec->lr.ctx_raw); 705 regex_data_free(spec->regex); 706 if (spec->from_mmap) 707 continue; 708 free(spec->regex_str); 709 free(spec->type_str); 710 } 711 712 for (i = 0; i < (unsigned int)data->num_stems; i++) { 713 stem = &data->stem_arr[i]; 714 if (stem->from_mmap) 715 continue; 716 free(stem->buf); 717 } 718 719 if (data->spec_arr) 720 free(data->spec_arr); 721 if (data->stem_arr) 722 free(data->stem_arr); 723 724 area = data->mmap_areas; 725 while (area) { 726 munmap(area->addr, area->len); 727 last_area = area; 728 area = area->next; 729 free(last_area); 730 } 731 free(data); 732 } 733 734 static struct spec *lookup_common(struct selabel_handle *rec, 735 const char *key, 736 int type, 737 bool partial) 738 { 739 struct saved_data *data = (struct saved_data *)rec->data; 740 struct spec *spec_arr = data->spec_arr; 741 int i, rc, file_stem; 742 mode_t mode = (mode_t)type; 743 const char *buf; 744 struct spec *ret = NULL; 745 char *clean_key = NULL; 746 const char *prev_slash, *next_slash; 747 unsigned int sofar = 0; 748 749 if (!data->nspec) { 750 errno = ENOENT; 751 goto finish; 752 } 753 754 /* Remove duplicate slashes */ 755 if ((next_slash = strstr(key, "//"))) { 756 clean_key = (char *) malloc(strlen(key) + 1); 757 if (!clean_key) 758 goto finish; 759 prev_slash = key; 760 while (next_slash) { 761 memcpy(clean_key + sofar, prev_slash, next_slash - prev_slash); 762 sofar += next_slash - prev_slash; 763 prev_slash = next_slash + 1; 764 next_slash = strstr(prev_slash, "//"); 765 } 766 strcpy(clean_key + sofar, prev_slash); 767 key = clean_key; 768 } 769 770 buf = key; 771 file_stem = find_stem_from_file(data, &buf); 772 mode &= S_IFMT; 773 774 /* 775 * Check for matching specifications in reverse order, so that 776 * the last matching specification is used. 777 */ 778 for (i = data->nspec - 1; i >= 0; i--) { 779 struct spec *spec = &spec_arr[i]; 780 /* if the spec in question matches no stem or has the same 781 * stem as the file AND if the spec in question has no mode 782 * specified or if the mode matches the file mode then we do 783 * a regex check */ 784 if ((spec->stem_id == -1 || spec->stem_id == file_stem) && 785 (!mode || !spec->mode || mode == spec->mode)) { 786 if (compile_regex(data, spec, NULL) < 0) 787 goto finish; 788 if (spec->stem_id == -1) 789 rc = regex_match(spec->regex, key, partial); 790 else 791 rc = regex_match(spec->regex, buf, partial); 792 if (rc == REGEX_MATCH) { 793 spec->matches++; 794 break; 795 } else if (partial && rc == REGEX_MATCH_PARTIAL) 796 break; 797 798 if (rc == REGEX_NO_MATCH) 799 continue; 800 801 errno = ENOENT; 802 /* else it's an error */ 803 goto finish; 804 } 805 } 806 807 if (i < 0 || strcmp(spec_arr[i].lr.ctx_raw, "<<none>>") == 0) { 808 /* No matching specification. */ 809 errno = ENOENT; 810 goto finish; 811 } 812 813 errno = 0; 814 ret = &spec_arr[i]; 815 816 finish: 817 free(clean_key); 818 return ret; 819 } 820 821 static struct selabel_lookup_rec *lookup(struct selabel_handle *rec, 822 const char *key, int type) 823 { 824 struct spec *spec; 825 826 spec = lookup_common(rec, key, type, false); 827 if (spec) 828 return &spec->lr; 829 return NULL; 830 } 831 832 static bool partial_match(struct selabel_handle *rec, const char *key) 833 { 834 return lookup_common(rec, key, 0, true) ? true : false; 835 } 836 837 static struct selabel_lookup_rec *lookup_best_match(struct selabel_handle *rec, 838 const char *key, 839 const char **aliases, 840 int type) 841 { 842 size_t n, i; 843 int best = -1; 844 struct spec **specs; 845 size_t prefix_len = 0; 846 struct selabel_lookup_rec *lr = NULL; 847 848 if (!aliases || !aliases[0]) 849 return lookup(rec, key, type); 850 851 for (n = 0; aliases[n]; n++) 852 ; 853 854 specs = calloc(n+1, sizeof(struct spec *)); 855 if (!specs) 856 return NULL; 857 specs[0] = lookup_common(rec, key, type, false); 858 if (specs[0]) { 859 if (!specs[0]->hasMetaChars) { 860 /* exact match on key */ 861 lr = &specs[0]->lr; 862 goto out; 863 } 864 best = 0; 865 prefix_len = specs[0]->prefix_len; 866 } 867 for (i = 1; i <= n; i++) { 868 specs[i] = lookup_common(rec, aliases[i-1], type, false); 869 if (specs[i]) { 870 if (!specs[i]->hasMetaChars) { 871 /* exact match on alias */ 872 lr = &specs[i]->lr; 873 goto out; 874 } 875 if (specs[i]->prefix_len > prefix_len) { 876 best = i; 877 prefix_len = specs[i]->prefix_len; 878 } 879 } 880 } 881 882 if (best >= 0) { 883 /* longest fixed prefix match on key or alias */ 884 lr = &specs[best]->lr; 885 } else { 886 errno = ENOENT; 887 } 888 889 out: 890 free(specs); 891 return lr; 892 } 893 894 static enum selabel_cmp_result incomp(struct spec *spec1, struct spec *spec2, const char *reason, int i, int j) 895 { 896 selinux_log(SELINUX_INFO, 897 "selabel_cmp: mismatched %s on entry %d: (%s, %x, %s) vs entry %d: (%s, %x, %s)\n", 898 reason, 899 i, spec1->regex_str, spec1->mode, spec1->lr.ctx_raw, 900 j, spec2->regex_str, spec2->mode, spec2->lr.ctx_raw); 901 return SELABEL_INCOMPARABLE; 902 } 903 904 static enum selabel_cmp_result cmp(struct selabel_handle *h1, 905 struct selabel_handle *h2) 906 { 907 struct saved_data *data1 = (struct saved_data *)h1->data; 908 struct saved_data *data2 = (struct saved_data *)h2->data; 909 unsigned int i, nspec1 = data1->nspec, j, nspec2 = data2->nspec; 910 struct spec *spec_arr1 = data1->spec_arr, *spec_arr2 = data2->spec_arr; 911 struct stem *stem_arr1 = data1->stem_arr, *stem_arr2 = data2->stem_arr; 912 bool skipped1 = false, skipped2 = false; 913 914 i = 0; 915 j = 0; 916 while (i < nspec1 && j < nspec2) { 917 struct spec *spec1 = &spec_arr1[i]; 918 struct spec *spec2 = &spec_arr2[j]; 919 920 /* 921 * Because sort_specs() moves exact pathnames to the 922 * end, we might need to skip over additional regex 923 * entries that only exist in one of the configurations. 924 */ 925 if (!spec1->hasMetaChars && spec2->hasMetaChars) { 926 j++; 927 skipped2 = true; 928 continue; 929 } 930 931 if (spec1->hasMetaChars && !spec2->hasMetaChars) { 932 i++; 933 skipped1 = true; 934 continue; 935 } 936 937 if (spec1->regex && spec2->regex) { 938 if (regex_cmp(spec1->regex, spec2->regex) == SELABEL_INCOMPARABLE){ 939 return incomp(spec1, spec2, "regex", i, j); 940 } 941 } else { 942 if (strcmp(spec1->regex_str, spec2->regex_str)) 943 return incomp(spec1, spec2, "regex_str", i, j); 944 } 945 946 if (spec1->mode != spec2->mode) 947 return incomp(spec1, spec2, "mode", i, j); 948 949 if (spec1->stem_id == -1 && spec2->stem_id != -1) 950 return incomp(spec1, spec2, "stem_id", i, j); 951 if (spec2->stem_id == -1 && spec1->stem_id != -1) 952 return incomp(spec1, spec2, "stem_id", i, j); 953 if (spec1->stem_id != -1 && spec2->stem_id != -1) { 954 struct stem *stem1 = &stem_arr1[spec1->stem_id]; 955 struct stem *stem2 = &stem_arr2[spec2->stem_id]; 956 if (stem1->len != stem2->len || 957 strncmp(stem1->buf, stem2->buf, stem1->len)) 958 return incomp(spec1, spec2, "stem", i, j); 959 } 960 961 if (strcmp(spec1->lr.ctx_raw, spec2->lr.ctx_raw)) 962 return incomp(spec1, spec2, "ctx_raw", i, j); 963 964 i++; 965 j++; 966 } 967 968 if ((skipped1 || i < nspec1) && !skipped2) 969 return SELABEL_SUPERSET; 970 if ((skipped2 || j < nspec2) && !skipped1) 971 return SELABEL_SUBSET; 972 if (skipped1 && skipped2) 973 return SELABEL_INCOMPARABLE; 974 return SELABEL_EQUAL; 975 } 976 977 978 static void stats(struct selabel_handle *rec) 979 { 980 struct saved_data *data = (struct saved_data *)rec->data; 981 unsigned int i, nspec = data->nspec; 982 struct spec *spec_arr = data->spec_arr; 983 984 for (i = 0; i < nspec; i++) { 985 if (spec_arr[i].matches == 0) { 986 if (spec_arr[i].type_str) { 987 COMPAT_LOG(SELINUX_WARNING, 988 "Warning! No matches for (%s, %s, %s)\n", 989 spec_arr[i].regex_str, 990 spec_arr[i].type_str, 991 spec_arr[i].lr.ctx_raw); 992 } else { 993 COMPAT_LOG(SELINUX_WARNING, 994 "Warning! No matches for (%s, %s)\n", 995 spec_arr[i].regex_str, 996 spec_arr[i].lr.ctx_raw); 997 } 998 } 999 } 1000 } 1001 1002 int selabel_file_init(struct selabel_handle *rec, 1003 const struct selinux_opt *opts, 1004 unsigned nopts) 1005 { 1006 struct saved_data *data; 1007 1008 data = (struct saved_data *)malloc(sizeof(*data)); 1009 if (!data) 1010 return -1; 1011 memset(data, 0, sizeof(*data)); 1012 1013 rec->data = data; 1014 rec->func_close = &closef; 1015 rec->func_stats = &stats; 1016 rec->func_lookup = &lookup; 1017 rec->func_partial_match = &partial_match; 1018 rec->func_lookup_best_match = &lookup_best_match; 1019 rec->func_cmp = &cmp; 1020 1021 return init(rec, opts, nopts); 1022 } 1023