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