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