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 */
get_stem_from_file_name(const char * const buf)33 static 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. */
find_stem_from_file(struct saved_data * data,const char ** buf)45 static 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 */
nodups_specs(struct saved_data * data,const char * path)65 static 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
load_mmap(struct selabel_handle * rec,const char * path,struct stat * sb,bool isbinary,struct selabel_digest * digest)100 static 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
413 err:
414 free(stem_map);
415
416 return rc;
417 }
418
process_file(const char * path,const char * suffix,struct selabel_handle * rec,const char * prefix,struct selabel_digest * digest)419 static 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
503 out:
504 free(line_buf);
505 if (fp)
506 fclose(fp);
507 return rc;
508 }
509
510 static void closef(struct selabel_handle *rec);
511
init(struct selabel_handle * rec,const struct selinux_opt * opts,unsigned n)512 static 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
583 finish:
584 if (status)
585 closef(rec);
586
587 return status;
588 }
589
590 /*
591 * Backend interface routines
592 */
closef(struct selabel_handle * rec)593 static 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
lookup_common(struct selabel_handle * rec,const char * key,int type,bool partial)637 static 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
728 finish:
729 free(clean_key);
730 return ret;
731 }
732
lookup(struct selabel_handle * rec,const char * key,int type)733 static 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
partial_match(struct selabel_handle * rec,const char * key)744 static bool partial_match(struct selabel_handle *rec, const char *key)
745 {
746 return lookup_common(rec, key, 0, true) ? true : false;
747 }
748
lookup_best_match(struct selabel_handle * rec,const char * key,const char ** aliases,int type)749 static 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
801 out:
802 free(specs);
803 return lr;
804 }
805
incomp(struct spec * spec1,struct spec * spec2,const char * reason,int i,int j)806 static 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
cmp(struct selabel_handle * h1,struct selabel_handle * h2)816 static 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
stats(struct selabel_handle * rec)897 static 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
selabel_file_init(struct selabel_handle * rec,const struct selinux_opt * opts,unsigned nopts)921 int 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