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 <stdio_ext.h> 14#include <ctype.h> 15#include <errno.h> 16#include <limits.h> 17#include <stdint.h> 18#include <pcre.h> 19#include <unistd.h> 20#include <sys/mman.h> 21#include <sys/types.h> 22#include <sys/stat.h> 23 24#include "callbacks.h" 25#include "label_internal.h" 26#include "label_file.h" 27 28/* 29 * Internals, mostly moved over from matchpathcon.c 30 */ 31 32/* return the length of the text that is the stem of a file name */ 33static int get_stem_from_file_name(const char *const buf) 34{ 35 const char *tmp = strchr(buf + 1, '/'); 36 37 if (!tmp) 38 return 0; 39 return tmp - buf; 40} 41 42/* find the stem of a file name, returns the index into stem_arr (or -1 if 43 * there is no match - IE for a file in the root directory or a regex that is 44 * too complex for us). Makes buf point to the text AFTER the stem. */ 45static int find_stem_from_file(struct saved_data *data, const char **buf) 46{ 47 int i; 48 int stem_len = get_stem_from_file_name(*buf); 49 50 if (!stem_len) 51 return -1; 52 for (i = 0; i < data->num_stems; i++) { 53 if (stem_len == data->stem_arr[i].len 54 && !strncmp(*buf, data->stem_arr[i].buf, stem_len)) { 55 *buf += stem_len; 56 return i; 57 } 58 } 59 return -1; 60} 61 62/* 63 * Warn about duplicate specifications. 64 */ 65static int nodups_specs(struct saved_data *data, const char *path) 66{ 67 int rc = 0; 68 unsigned int ii, jj; 69 struct spec *curr_spec, *spec_arr = data->spec_arr; 70 71 for (ii = 0; ii < data->nspec; ii++) { 72 curr_spec = &spec_arr[ii]; 73 for (jj = ii + 1; jj < data->nspec; jj++) { 74 if ((!strcmp(spec_arr[jj].regex_str, 75 curr_spec->regex_str)) 76 && (!spec_arr[jj].mode || !curr_spec->mode 77 || spec_arr[jj].mode == curr_spec->mode)) { 78 rc = -1; 79 errno = EINVAL; 80 if (strcmp(spec_arr[jj].lr.ctx_raw, 81 curr_spec->lr.ctx_raw)) { 82 COMPAT_LOG 83 (SELINUX_ERROR, 84 "%s: Multiple different specifications for %s (%s and %s).\n", 85 path, curr_spec->regex_str, 86 spec_arr[jj].lr.ctx_raw, 87 curr_spec->lr.ctx_raw); 88 } else { 89 COMPAT_LOG 90 (SELINUX_ERROR, 91 "%s: Multiple same specifications for %s.\n", 92 path, curr_spec->regex_str); 93 } 94 } 95 } 96 } 97 return rc; 98} 99 100static int load_mmap(struct selabel_handle *rec, const char *path, 101 struct stat *sb, bool isbinary, 102 struct selabel_digest *digest) 103{ 104 struct saved_data *data = (struct saved_data *)rec->data; 105 char mmap_path[PATH_MAX + 1]; 106 int mmapfd; 107 int rc; 108 struct stat mmap_stat; 109 char *addr, *str_buf; 110 size_t len; 111 int *stem_map; 112 struct mmap_area *mmap_area; 113 uint32_t i, magic, version; 114 uint32_t entry_len, stem_map_len, regex_array_len; 115 116 if (isbinary) { 117 len = strlen(path); 118 if (len >= sizeof(mmap_path)) 119 return -1; 120 strcpy(mmap_path, path); 121 } else { 122 rc = snprintf(mmap_path, sizeof(mmap_path), "%s.bin", path); 123 if (rc >= (int)sizeof(mmap_path)) 124 return -1; 125 } 126 127 mmapfd = open(mmap_path, O_RDONLY | O_CLOEXEC); 128 if (mmapfd < 0) 129 return -1; 130 131 rc = fstat(mmapfd, &mmap_stat); 132 if (rc < 0) { 133 close(mmapfd); 134 return -1; 135 } 136 137 /* if mmap is old, ignore it */ 138 if (mmap_stat.st_mtime < sb->st_mtime) { 139 close(mmapfd); 140 return -1; 141 } 142 143 /* ok, read it in... */ 144 len = mmap_stat.st_size; 145 len += (sysconf(_SC_PAGE_SIZE) - 1); 146 len &= ~(sysconf(_SC_PAGE_SIZE) - 1); 147 148 mmap_area = malloc(sizeof(*mmap_area)); 149 if (!mmap_area) { 150 close(mmapfd); 151 return -1; 152 } 153 154 addr = mmap(NULL, len, PROT_READ, MAP_PRIVATE, mmapfd, 0); 155 close(mmapfd); 156 if (addr == MAP_FAILED) { 157 free(mmap_area); 158 perror("mmap"); 159 return -1; 160 } 161 162 /* save where we mmap'd the file to cleanup on close() */ 163 mmap_area->addr = mmap_area->next_addr = addr; 164 mmap_area->len = mmap_area->next_len = len; 165 mmap_area->next = data->mmap_areas; 166 data->mmap_areas = mmap_area; 167 168 /* check if this looks like an fcontext file */ 169 rc = next_entry(&magic, mmap_area, sizeof(uint32_t)); 170 if (rc < 0 || magic != SELINUX_MAGIC_COMPILED_FCONTEXT) 171 return -1; 172 173 /* check if this version is higher than we understand */ 174 rc = next_entry(&version, mmap_area, sizeof(uint32_t)); 175 if (rc < 0 || version > SELINUX_COMPILED_FCONTEXT_MAX_VERS) 176 return -1; 177 178 if (version >= SELINUX_COMPILED_FCONTEXT_PCRE_VERS) { 179 len = strlen(pcre_version()); 180 181 rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t)); 182 if (rc < 0) 183 return -1; 184 185 /* Check version lengths */ 186 if (len != entry_len) 187 return -1; 188 189 /* Check if pcre version mismatch */ 190 str_buf = malloc(entry_len + 1); 191 if (!str_buf) 192 return -1; 193 194 rc = next_entry(str_buf, mmap_area, entry_len); 195 if (rc < 0) { 196 free(str_buf); 197 return -1; 198 } 199 200 str_buf[entry_len] = '\0'; 201 if ((strcmp(str_buf, pcre_version()) != 0)) { 202 free(str_buf); 203 return -1; 204 } 205 free(str_buf); 206 } 207 208 /* allocate the stems_data array */ 209 rc = next_entry(&stem_map_len, mmap_area, sizeof(uint32_t)); 210 if (rc < 0 || !stem_map_len) 211 return -1; 212 213 /* 214 * map indexed by the stem # in the mmap file and contains the stem 215 * number in the data stem_arr 216 */ 217 stem_map = calloc(stem_map_len, sizeof(*stem_map)); 218 if (!stem_map) 219 return -1; 220 221 for (i = 0; i < stem_map_len; i++) { 222 char *buf; 223 uint32_t stem_len; 224 int newid; 225 226 /* the length does not inlude the nul */ 227 rc = next_entry(&stem_len, mmap_area, sizeof(uint32_t)); 228 if (rc < 0 || !stem_len) { 229 rc = -1; 230 goto err; 231 } 232 233 /* Check for stem_len wrap around. */ 234 if (stem_len < UINT32_MAX) { 235 buf = (char *)mmap_area->next_addr; 236 /* Check if over-run before null check. */ 237 rc = next_entry(NULL, mmap_area, (stem_len + 1)); 238 if (rc < 0) 239 goto err; 240 241 if (buf[stem_len] != '\0') { 242 rc = -1; 243 goto err; 244 } 245 } else { 246 rc = -1; 247 goto err; 248 } 249 250 /* store the mapping between old and new */ 251 newid = find_stem(data, buf, stem_len); 252 if (newid < 0) { 253 newid = store_stem(data, buf, stem_len); 254 if (newid < 0) { 255 rc = newid; 256 goto err; 257 } 258 data->stem_arr[newid].from_mmap = 1; 259 } 260 stem_map[i] = newid; 261 } 262 263 /* allocate the regex array */ 264 rc = next_entry(®ex_array_len, mmap_area, sizeof(uint32_t)); 265 if (rc < 0 || !regex_array_len) { 266 rc = -1; 267 goto err; 268 } 269 270 for (i = 0; i < regex_array_len; i++) { 271 struct spec *spec; 272 int32_t stem_id, meta_chars; 273 uint32_t mode = 0, prefix_len = 0; 274 275 rc = grow_specs(data); 276 if (rc < 0) 277 goto err; 278 279 spec = &data->spec_arr[data->nspec]; 280 spec->from_mmap = 1; 281 spec->regcomp = 1; 282 283 /* Process context */ 284 rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t)); 285 if (rc < 0 || !entry_len) { 286 rc = -1; 287 goto err; 288 } 289 290 str_buf = malloc(entry_len); 291 if (!str_buf) { 292 rc = -1; 293 goto err; 294 } 295 rc = next_entry(str_buf, mmap_area, entry_len); 296 if (rc < 0) 297 goto err; 298 299 if (str_buf[entry_len - 1] != '\0') { 300 free(str_buf); 301 rc = -1; 302 goto err; 303 } 304 spec->lr.ctx_raw = str_buf; 305 306 if (strcmp(spec->lr.ctx_raw, "<<none>>") && rec->validating) { 307 if (selabel_validate(rec, &spec->lr) < 0) { 308 selinux_log(SELINUX_ERROR, 309 "%s: context %s is invalid\n", mmap_path, spec->lr.ctx_raw); 310 goto err; 311 } 312 } 313 314 /* Process regex string */ 315 rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t)); 316 if (rc < 0 || !entry_len) { 317 rc = -1; 318 goto err; 319 } 320 321 spec->regex_str = (char *)mmap_area->next_addr; 322 rc = next_entry(NULL, mmap_area, entry_len); 323 if (rc < 0) 324 goto err; 325 326 if (spec->regex_str[entry_len - 1] != '\0') { 327 rc = -1; 328 goto err; 329 } 330 331 /* Process mode */ 332 if (version >= SELINUX_COMPILED_FCONTEXT_MODE) 333 rc = next_entry(&mode, mmap_area, sizeof(uint32_t)); 334 else 335 rc = next_entry(&mode, mmap_area, sizeof(mode_t)); 336 if (rc < 0) 337 goto err; 338 339 spec->mode = mode; 340 341 /* map the stem id from the mmap file to the data->stem_arr */ 342 rc = next_entry(&stem_id, mmap_area, sizeof(int32_t)); 343 if (rc < 0) 344 goto err; 345 346 if (stem_id < 0 || stem_id >= (int32_t)stem_map_len) 347 spec->stem_id = -1; 348 else 349 spec->stem_id = stem_map[stem_id]; 350 351 /* retrieve the hasMetaChars bit */ 352 rc = next_entry(&meta_chars, mmap_area, sizeof(uint32_t)); 353 if (rc < 0) 354 goto err; 355 356 spec->hasMetaChars = meta_chars; 357 /* and prefix length for use by selabel_lookup_best_match */ 358 if (version >= SELINUX_COMPILED_FCONTEXT_PREFIX_LEN) { 359 rc = next_entry(&prefix_len, mmap_area, 360 sizeof(uint32_t)); 361 if (rc < 0) 362 goto err; 363 364 spec->prefix_len = prefix_len; 365 } 366 367 /* Process regex and study_data entries */ 368 rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t)); 369 if (rc < 0 || !entry_len) { 370 rc = -1; 371 goto err; 372 } 373 spec->regex = (pcre *)mmap_area->next_addr; 374 rc = next_entry(NULL, mmap_area, entry_len); 375 if (rc < 0) 376 goto err; 377 378 /* Check that regex lengths match. pcre_fullinfo() 379 * also validates its magic number. */ 380 rc = pcre_fullinfo(spec->regex, NULL, PCRE_INFO_SIZE, &len); 381 if (rc < 0 || len != entry_len) { 382 rc = -1; 383 goto err; 384 } 385 386 rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t)); 387 if (rc < 0 || !entry_len) { 388 rc = -1; 389 goto err; 390 } 391 spec->lsd.study_data = (void *)mmap_area->next_addr; 392 spec->lsd.flags |= PCRE_EXTRA_STUDY_DATA; 393 rc = next_entry(NULL, mmap_area, entry_len); 394 if (rc < 0) 395 goto err; 396 397 /* Check that study data lengths match. */ 398 rc = pcre_fullinfo(spec->regex, &spec->lsd, 399 PCRE_INFO_STUDYSIZE, &len); 400 if (rc < 0 || len != entry_len) { 401 rc = -1; 402 goto err; 403 } 404 405 data->nspec++; 406 } 407 408 rc = digest_add_specfile(digest, NULL, addr, mmap_stat.st_size, 409 mmap_path); 410 if (rc) 411 goto err; 412 413err: 414 free(stem_map); 415 416 return rc; 417} 418 419static int process_file(const char *path, const char *suffix, 420 struct selabel_handle *rec, 421 const char *prefix, struct selabel_digest *digest) 422{ 423 FILE *fp; 424 struct stat sb; 425 unsigned int lineno; 426 size_t line_len = 0; 427 char *line_buf = NULL; 428 int rc; 429 char stack_path[PATH_MAX + 1]; 430 bool isbinary = false; 431 uint32_t magic; 432 433 /* append the path suffix if we have one */ 434 if (suffix) { 435 rc = snprintf(stack_path, sizeof(stack_path), 436 "%s.%s", path, suffix); 437 if (rc >= (int)sizeof(stack_path)) { 438 errno = ENAMETOOLONG; 439 return -1; 440 } 441 path = stack_path; 442 } 443 444 /* Open the specification file. */ 445 fp = fopen(path, "r"); 446 if (fp) { 447 __fsetlocking(fp, FSETLOCKING_BYCALLER); 448 449 if (fstat(fileno(fp), &sb) < 0) 450 return -1; 451 if (!S_ISREG(sb.st_mode)) { 452 errno = EINVAL; 453 return -1; 454 } 455 456 magic = 0; 457 if (fread(&magic, sizeof magic, 1, fp) != 1) { 458 if (ferror(fp)) { 459 errno = EINVAL; 460 fclose(fp); 461 return -1; 462 } 463 clearerr(fp); 464 } 465 466 if (magic == SELINUX_MAGIC_COMPILED_FCONTEXT) { 467 /* file_contexts.bin format */ 468 fclose(fp); 469 fp = NULL; 470 isbinary = true; 471 } else { 472 rewind(fp); 473 } 474 } else { 475 /* 476 * Text file does not exist, so clear the timestamp 477 * so that we will always pass the timestamp comparison 478 * with the bin file in load_mmap(). 479 */ 480 sb.st_mtime = 0; 481 } 482 483 rc = load_mmap(rec, path, &sb, isbinary, digest); 484 if (rc == 0) 485 goto out; 486 487 if (!fp) 488 return -1; /* no text or bin file */ 489 490 /* 491 * Then do detailed validation of the input and fill the spec array 492 */ 493 lineno = 0; 494 rc = 0; 495 while (getline(&line_buf, &line_len, fp) > 0) { 496 rc = process_line(rec, path, prefix, line_buf, ++lineno); 497 if (rc) 498 goto out; 499 } 500 501 rc = digest_add_specfile(digest, fp, NULL, sb.st_size, path); 502 503out: 504 free(line_buf); 505 if (fp) 506 fclose(fp); 507 return rc; 508} 509 510static void closef(struct selabel_handle *rec); 511 512static int init(struct selabel_handle *rec, const struct selinux_opt *opts, 513 unsigned n) 514{ 515 struct saved_data *data = (struct saved_data *)rec->data; 516 const char *path = NULL; 517 const char *prefix = NULL; 518 char subs_file[PATH_MAX + 1]; 519 int status = -1, baseonly = 0; 520 521 /* Process arguments */ 522 while (n--) 523 switch(opts[n].type) { 524 case SELABEL_OPT_PATH: 525 path = opts[n].value; 526 break; 527 case SELABEL_OPT_SUBSET: 528 prefix = opts[n].value; 529 break; 530 case SELABEL_OPT_BASEONLY: 531 baseonly = !!opts[n].value; 532 break; 533 } 534 535 /* Process local and distribution substitution files */ 536 if (!path) { 537 rec->dist_subs = 538 selabel_subs_init(selinux_file_context_subs_dist_path(), 539 rec->dist_subs, rec->digest); 540 rec->subs = selabel_subs_init(selinux_file_context_subs_path(), 541 rec->subs, rec->digest); 542 path = selinux_file_context_path(); 543 } else { 544 snprintf(subs_file, sizeof(subs_file), "%s.subs_dist", path); 545 rec->dist_subs = selabel_subs_init(subs_file, rec->dist_subs, 546 rec->digest); 547 snprintf(subs_file, sizeof(subs_file), "%s.subs", path); 548 rec->subs = selabel_subs_init(subs_file, rec->subs, 549 rec->digest); 550 } 551 552 rec->spec_file = strdup(path); 553 554 /* 555 * The do detailed validation of the input and fill the spec array 556 */ 557 status = process_file(path, NULL, rec, prefix, rec->digest); 558 if (status) 559 goto finish; 560 561 if (rec->validating) { 562 status = nodups_specs(data, path); 563 if (status) 564 goto finish; 565 } 566 567 if (!baseonly) { 568 status = process_file(path, "homedirs", rec, prefix, 569 rec->digest); 570 if (status && errno != ENOENT) 571 goto finish; 572 573 status = process_file(path, "local", rec, prefix, 574 rec->digest); 575 if (status && errno != ENOENT) 576 goto finish; 577 } 578 579 digest_gen_hash(rec->digest); 580 581 status = sort_specs(data); 582 583finish: 584 if (status) 585 closef(rec); 586 587 return status; 588} 589 590/* 591 * Backend interface routines 592 */ 593static void closef(struct selabel_handle *rec) 594{ 595 struct saved_data *data = (struct saved_data *)rec->data; 596 struct mmap_area *area, *last_area; 597 struct spec *spec; 598 struct stem *stem; 599 unsigned int i; 600 601 for (i = 0; i < data->nspec; i++) { 602 spec = &data->spec_arr[i]; 603 free(spec->lr.ctx_trans); 604 free(spec->lr.ctx_raw); 605 if (spec->from_mmap) 606 continue; 607 free(spec->regex_str); 608 free(spec->type_str); 609 if (spec->regcomp) { 610 pcre_free(spec->regex); 611 pcre_free_study(spec->sd); 612 } 613 } 614 615 for (i = 0; i < (unsigned int)data->num_stems; i++) { 616 stem = &data->stem_arr[i]; 617 if (stem->from_mmap) 618 continue; 619 free(stem->buf); 620 } 621 622 if (data->spec_arr) 623 free(data->spec_arr); 624 if (data->stem_arr) 625 free(data->stem_arr); 626 627 area = data->mmap_areas; 628 while (area) { 629 munmap(area->addr, area->len); 630 last_area = area; 631 area = area->next; 632 free(last_area); 633 } 634 free(data); 635} 636 637static struct spec *lookup_common(struct selabel_handle *rec, 638 const char *key, 639 int type, 640 bool partial) 641{ 642 struct saved_data *data = (struct saved_data *)rec->data; 643 struct spec *spec_arr = data->spec_arr; 644 int i, rc, file_stem, pcre_options = 0; 645 mode_t mode = (mode_t)type; 646 const char *buf; 647 struct spec *ret = NULL; 648 char *clean_key = NULL; 649 const char *prev_slash, *next_slash; 650 unsigned int sofar = 0; 651 652 if (!data->nspec) { 653 errno = ENOENT; 654 goto finish; 655 } 656 657 /* Remove duplicate slashes */ 658 if ((next_slash = strstr(key, "//"))) { 659 clean_key = (char *) malloc(strlen(key) + 1); 660 if (!clean_key) 661 goto finish; 662 prev_slash = key; 663 while (next_slash) { 664 memcpy(clean_key + sofar, prev_slash, next_slash - prev_slash); 665 sofar += next_slash - prev_slash; 666 prev_slash = next_slash + 1; 667 next_slash = strstr(prev_slash, "//"); 668 } 669 strcpy(clean_key + sofar, prev_slash); 670 key = clean_key; 671 } 672 673 buf = key; 674 file_stem = find_stem_from_file(data, &buf); 675 mode &= S_IFMT; 676 677 if (partial) 678 pcre_options |= PCRE_PARTIAL_SOFT; 679 680 /* 681 * Check for matching specifications in reverse order, so that 682 * the last matching specification is used. 683 */ 684 for (i = data->nspec - 1; i >= 0; i--) { 685 struct spec *spec = &spec_arr[i]; 686 /* if the spec in question matches no stem or has the same 687 * stem as the file AND if the spec in question has no mode 688 * specified or if the mode matches the file mode then we do 689 * a regex check */ 690 if ((spec->stem_id == -1 || spec->stem_id == file_stem) && 691 (!mode || !spec->mode || mode == spec->mode)) { 692 if (compile_regex(data, spec, NULL) < 0) 693 goto finish; 694 if (spec->stem_id == -1) 695 rc = pcre_exec(spec->regex, 696 get_pcre_extra(spec), 697 key, strlen(key), 0, 698 pcre_options, NULL, 0); 699 else 700 rc = pcre_exec(spec->regex, 701 get_pcre_extra(spec), 702 buf, strlen(buf), 0, 703 pcre_options, NULL, 0); 704 if (rc == 0) { 705 spec->matches++; 706 break; 707 } else if (partial && rc == PCRE_ERROR_PARTIAL) 708 break; 709 710 if (rc == PCRE_ERROR_NOMATCH) 711 continue; 712 713 errno = ENOENT; 714 /* else it's an error */ 715 goto finish; 716 } 717 } 718 719 if (i < 0 || strcmp(spec_arr[i].lr.ctx_raw, "<<none>>") == 0) { 720 /* No matching specification. */ 721 errno = ENOENT; 722 goto finish; 723 } 724 725 errno = 0; 726 ret = &spec_arr[i]; 727 728finish: 729 free(clean_key); 730 return ret; 731} 732 733static struct selabel_lookup_rec *lookup(struct selabel_handle *rec, 734 const char *key, int type) 735{ 736 struct spec *spec; 737 738 spec = lookup_common(rec, key, type, false); 739 if (spec) 740 return &spec->lr; 741 return NULL; 742} 743 744static bool partial_match(struct selabel_handle *rec, const char *key) 745{ 746 return lookup_common(rec, key, 0, true) ? true : false; 747} 748 749static struct selabel_lookup_rec *lookup_best_match(struct selabel_handle *rec, 750 const char *key, 751 const char **aliases, 752 int type) 753{ 754 size_t n, i; 755 int best = -1; 756 struct spec **specs; 757 size_t prefix_len = 0; 758 struct selabel_lookup_rec *lr = NULL; 759 760 if (!aliases || !aliases[0]) 761 return lookup(rec, key, type); 762 763 for (n = 0; aliases[n]; n++) 764 ; 765 766 specs = calloc(n+1, sizeof(struct spec *)); 767 if (!specs) 768 return NULL; 769 specs[0] = lookup_common(rec, key, type, false); 770 if (specs[0]) { 771 if (!specs[0]->hasMetaChars) { 772 /* exact match on key */ 773 lr = &specs[0]->lr; 774 goto out; 775 } 776 best = 0; 777 prefix_len = specs[0]->prefix_len; 778 } 779 for (i = 1; i <= n; i++) { 780 specs[i] = lookup_common(rec, aliases[i-1], type, false); 781 if (specs[i]) { 782 if (!specs[i]->hasMetaChars) { 783 /* exact match on alias */ 784 lr = &specs[i]->lr; 785 goto out; 786 } 787 if (specs[i]->prefix_len > prefix_len) { 788 best = i; 789 prefix_len = specs[i]->prefix_len; 790 } 791 } 792 } 793 794 if (best >= 0) { 795 /* longest fixed prefix match on key or alias */ 796 lr = &specs[best]->lr; 797 } else { 798 errno = ENOENT; 799 } 800 801out: 802 free(specs); 803 return lr; 804} 805 806static enum selabel_cmp_result incomp(struct spec *spec1, struct spec *spec2, const char *reason, int i, int j) 807{ 808 selinux_log(SELINUX_INFO, 809 "selabel_cmp: mismatched %s on entry %d: (%s, %x, %s) vs entry %d: (%s, %x, %s)\n", 810 reason, 811 i, spec1->regex_str, spec1->mode, spec1->lr.ctx_raw, 812 j, spec2->regex_str, spec2->mode, spec2->lr.ctx_raw); 813 return SELABEL_INCOMPARABLE; 814} 815 816static enum selabel_cmp_result cmp(struct selabel_handle *h1, 817 struct selabel_handle *h2) 818{ 819 struct saved_data *data1 = (struct saved_data *)h1->data; 820 struct saved_data *data2 = (struct saved_data *)h2->data; 821 unsigned int i, nspec1 = data1->nspec, j, nspec2 = data2->nspec; 822 struct spec *spec_arr1 = data1->spec_arr, *spec_arr2 = data2->spec_arr; 823 struct stem *stem_arr1 = data1->stem_arr, *stem_arr2 = data2->stem_arr; 824 bool skipped1 = false, skipped2 = false; 825 826 i = 0; 827 j = 0; 828 while (i < nspec1 && j < nspec2) { 829 struct spec *spec1 = &spec_arr1[i]; 830 struct spec *spec2 = &spec_arr2[j]; 831 832 /* 833 * Because sort_specs() moves exact pathnames to the 834 * end, we might need to skip over additional regex 835 * entries that only exist in one of the configurations. 836 */ 837 if (!spec1->hasMetaChars && spec2->hasMetaChars) { 838 j++; 839 skipped2 = true; 840 continue; 841 } 842 843 if (spec1->hasMetaChars && !spec2->hasMetaChars) { 844 i++; 845 skipped1 = true; 846 continue; 847 } 848 849 if (spec1->regcomp && spec2->regcomp) { 850 size_t len1, len2; 851 int rc; 852 853 rc = pcre_fullinfo(spec1->regex, NULL, PCRE_INFO_SIZE, &len1); 854 assert(rc == 0); 855 rc = pcre_fullinfo(spec2->regex, NULL, PCRE_INFO_SIZE, &len2); 856 assert(rc == 0); 857 if (len1 != len2 || 858 memcmp(spec1->regex, spec2->regex, len1)) 859 return incomp(spec1, spec2, "regex", i, j); 860 } else { 861 if (strcmp(spec1->regex_str, spec2->regex_str)) 862 return incomp(spec1, spec2, "regex_str", i, j); 863 } 864 865 if (spec1->mode != spec2->mode) 866 return incomp(spec1, spec2, "mode", i, j); 867 868 if (spec1->stem_id == -1 && spec2->stem_id != -1) 869 return incomp(spec1, spec2, "stem_id", i, j); 870 if (spec2->stem_id == -1 && spec1->stem_id != -1) 871 return incomp(spec1, spec2, "stem_id", i, j); 872 if (spec1->stem_id != -1 && spec2->stem_id != -1) { 873 struct stem *stem1 = &stem_arr1[spec1->stem_id]; 874 struct stem *stem2 = &stem_arr2[spec2->stem_id]; 875 if (stem1->len != stem2->len || 876 strncmp(stem1->buf, stem2->buf, stem1->len)) 877 return incomp(spec1, spec2, "stem", i, j); 878 } 879 880 if (strcmp(spec1->lr.ctx_raw, spec2->lr.ctx_raw)) 881 return incomp(spec1, spec2, "ctx_raw", i, j); 882 883 i++; 884 j++; 885 } 886 887 if ((skipped1 || i < nspec1) && !skipped2) 888 return SELABEL_SUPERSET; 889 if ((skipped2 || j < nspec2) && !skipped1) 890 return SELABEL_SUBSET; 891 if (skipped1 && skipped2) 892 return SELABEL_INCOMPARABLE; 893 return SELABEL_EQUAL; 894} 895 896 897static void stats(struct selabel_handle *rec) 898{ 899 struct saved_data *data = (struct saved_data *)rec->data; 900 unsigned int i, nspec = data->nspec; 901 struct spec *spec_arr = data->spec_arr; 902 903 for (i = 0; i < nspec; i++) { 904 if (spec_arr[i].matches == 0) { 905 if (spec_arr[i].type_str) { 906 COMPAT_LOG(SELINUX_WARNING, 907 "Warning! No matches for (%s, %s, %s)\n", 908 spec_arr[i].regex_str, 909 spec_arr[i].type_str, 910 spec_arr[i].lr.ctx_raw); 911 } else { 912 COMPAT_LOG(SELINUX_WARNING, 913 "Warning! No matches for (%s, %s)\n", 914 spec_arr[i].regex_str, 915 spec_arr[i].lr.ctx_raw); 916 } 917 } 918 } 919} 920 921int selabel_file_init(struct selabel_handle *rec, 922 const struct selinux_opt *opts, 923 unsigned nopts) 924{ 925 struct saved_data *data; 926 927 data = (struct saved_data *)malloc(sizeof(*data)); 928 if (!data) 929 return -1; 930 memset(data, 0, sizeof(*data)); 931 932 rec->data = data; 933 rec->func_close = &closef; 934 rec->func_stats = &stats; 935 rec->func_lookup = &lookup; 936 rec->func_partial_match = &partial_match; 937 rec->func_lookup_best_match = &lookup_best_match; 938 rec->func_cmp = &cmp; 939 940 return init(rec, opts, nopts); 941} 942