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