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