1 /*
2 * The majority of this code is from Android's
3 * external/libselinux/src/android.c and upstream
4 * selinux/policycoreutils/setfiles/restore.c
5 *
6 * See selinux_restorecon(3) for details.
7 */
8
9 #include <unistd.h>
10 #include <string.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <stdbool.h>
14 #include <ctype.h>
15 #include <errno.h>
16 #include <fcntl.h>
17 #include <fts.h>
18 #include <limits.h>
19 #include <stdint.h>
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <sys/xattr.h>
23 #include <sys/vfs.h>
24 #include <sys/statvfs.h>
25 #include <sys/utsname.h>
26 #include <linux/magic.h>
27 #include <libgen.h>
28 #include <syslog.h>
29 #include <assert.h>
30
31 #include <selinux/selinux.h>
32 #include <selinux/context.h>
33 #include <selinux/label.h>
34 #include <selinux/restorecon.h>
35
36 #include "callbacks.h"
37 #include "selinux_internal.h"
38
39 #define RESTORECON_LAST "security.restorecon_last"
40
41 #define SYS_PATH "/sys"
42 #define SYS_PREFIX SYS_PATH "/"
43
44 #define STAR_COUNT 1024
45
46 static struct selabel_handle *fc_sehandle = NULL;
47 static unsigned char *fc_digest = NULL;
48 static size_t fc_digest_len = 0;
49 static char *rootpath = NULL;
50 static int rootpathlen;
51
52 /* Information on excluded fs and directories. */
53 struct edir {
54 char *directory;
55 size_t size;
56 /* True if excluded by selinux_restorecon_set_exclude_list(3). */
57 bool caller_excluded;
58 };
59 #define CALLER_EXCLUDED true
60 static bool ignore_mounts;
61 static int exclude_non_seclabel_mounts(void);
62 static int exclude_count = 0;
63 static struct edir *exclude_lst = NULL;
64 static uint64_t fc_count = 0; /* Number of files processed so far */
65 static uint64_t efile_count; /* Estimated total number of files */
66
67 /* Store information on directories with xattr's. */
68 struct dir_xattr *dir_xattr_list;
69 static struct dir_xattr *dir_xattr_last;
70
71 /* restorecon_flags for passing to restorecon_sb() */
72 struct rest_flags {
73 bool nochange;
74 bool verbose;
75 bool progress;
76 bool mass_relabel;
77 bool set_specctx;
78 bool add_assoc;
79 bool ignore_digest;
80 bool recurse;
81 bool userealpath;
82 bool set_xdev;
83 bool abort_on_error;
84 bool syslog_changes;
85 bool log_matches;
86 bool ignore_noent;
87 bool warnonnomatch;
88 };
89
restorecon_init(void)90 static void restorecon_init(void)
91 {
92 struct selabel_handle *sehandle = NULL;
93
94 if (!fc_sehandle) {
95 sehandle = selinux_restorecon_default_handle();
96 selinux_restorecon_set_sehandle(sehandle);
97 }
98
99 efile_count = 0;
100 if (!ignore_mounts)
101 efile_count = exclude_non_seclabel_mounts();
102 }
103
104 static pthread_once_t fc_once = PTHREAD_ONCE_INIT;
105
106 /*
107 * Manage excluded directories:
108 * remove_exclude() - This removes any conflicting entries as there could be
109 * a case where a non-seclabel fs is mounted on /foo and
110 * then a seclabel fs is mounted on top of it.
111 * However if an entry has been added via
112 * selinux_restorecon_set_exclude_list(3) do not remove.
113 *
114 * add_exclude() - Add a directory/fs to be excluded from labeling. If it
115 * has already been added, then ignore.
116 *
117 * check_excluded() - Check if directory/fs is to be excluded when relabeling.
118 *
119 * file_system_count() - Calculates the the number of files to be processed.
120 * The count is only used if SELINUX_RESTORECON_PROGRESS
121 * is set and a mass relabel is requested.
122 *
123 * exclude_non_seclabel_mounts() - Reads /proc/mounts to determine what
124 * non-seclabel mounts to exclude from
125 * relabeling. restorecon_init() will not
126 * call this function if the
127 * SELINUX_RESTORECON_IGNORE_MOUNTS
128 * flag is set.
129 * Setting SELINUX_RESTORECON_IGNORE_MOUNTS
130 * is useful where there is a non-seclabel fs
131 * mounted on /foo and then a seclabel fs is
132 * mounted on a directory below this.
133 */
remove_exclude(const char * directory)134 static void remove_exclude(const char *directory)
135 {
136 int i;
137
138 for (i = 0; i < exclude_count; i++) {
139 if (strcmp(directory, exclude_lst[i].directory) == 0 &&
140 !exclude_lst[i].caller_excluded) {
141 free(exclude_lst[i].directory);
142 if (i != exclude_count - 1)
143 exclude_lst[i] = exclude_lst[exclude_count - 1];
144 exclude_count--;
145 return;
146 }
147 }
148 }
149
add_exclude(const char * directory,bool who)150 static int add_exclude(const char *directory, bool who)
151 {
152 struct edir *tmp_list, *current;
153 size_t len = 0;
154 int i;
155
156 /* Check if already present. */
157 for (i = 0; i < exclude_count; i++) {
158 if (strcmp(directory, exclude_lst[i].directory) == 0)
159 return 0;
160 }
161
162 if (directory == NULL || directory[0] != '/') {
163 selinux_log(SELINUX_ERROR,
164 "Full path required for exclude: %s.\n",
165 directory);
166 errno = EINVAL;
167 return -1;
168 }
169
170 tmp_list = realloc(exclude_lst,
171 sizeof(struct edir) * (exclude_count + 1));
172 if (!tmp_list)
173 goto oom;
174
175 exclude_lst = tmp_list;
176
177 len = strlen(directory);
178 while (len > 1 && directory[len - 1] == '/')
179 len--;
180
181 current = (exclude_lst + exclude_count);
182
183 current->directory = strndup(directory, len);
184 if (!current->directory)
185 goto oom;
186
187 current->size = len;
188 current->caller_excluded = who;
189 exclude_count++;
190 return 0;
191
192 oom:
193 selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __func__);
194 return -1;
195 }
196
check_excluded(const char * file)197 static int check_excluded(const char *file)
198 {
199 int i;
200
201 for (i = 0; i < exclude_count; i++) {
202 if (strncmp(file, exclude_lst[i].directory,
203 exclude_lst[i].size) == 0) {
204 if (file[exclude_lst[i].size] == 0 ||
205 file[exclude_lst[i].size] == '/')
206 return 1;
207 }
208 }
209 return 0;
210 }
211
file_system_count(char * name)212 static int file_system_count(char *name)
213 {
214 struct statvfs statvfs_buf;
215 int nfile = 0;
216
217 memset(&statvfs_buf, 0, sizeof(statvfs_buf));
218 if (!statvfs(name, &statvfs_buf))
219 nfile = statvfs_buf.f_files - statvfs_buf.f_ffree;
220
221 return nfile;
222 }
223
224 /*
225 * This is called once when selinux_restorecon() is first called.
226 * Searches /proc/mounts for all file systems that do not support extended
227 * attributes and adds them to the exclude directory table. File systems
228 * that support security labels have the seclabel option, return
229 * approximate total file count.
230 */
exclude_non_seclabel_mounts(void)231 static int exclude_non_seclabel_mounts(void)
232 {
233 struct utsname uts;
234 FILE *fp;
235 size_t len;
236 ssize_t num;
237 int index = 0, found = 0, nfile = 0;
238 char *mount_info[4];
239 char *buf = NULL, *item;
240
241 /* Check to see if the kernel supports seclabel */
242 if (uname(&uts) == 0 && strverscmp(uts.release, "2.6.30") < 0)
243 return 0;
244
245 fp = fopen("/proc/mounts", "re");
246 if (!fp)
247 return 0;
248
249 while ((num = getline(&buf, &len, fp)) != -1) {
250 found = 0;
251 index = 0;
252 item = strtok(buf, " ");
253 while (item != NULL) {
254 mount_info[index] = item;
255 index++;
256 if (index == 4)
257 break;
258 item = strtok(NULL, " ");
259 }
260 if (index < 4) {
261 selinux_log(SELINUX_ERROR,
262 "/proc/mounts record \"%s\" has incorrect format.\n",
263 buf);
264 continue;
265 }
266
267 /* Remove pre-existing entry */
268 remove_exclude(mount_info[1]);
269
270 item = strtok(mount_info[3], ",");
271 while (item != NULL) {
272 if (strcmp(item, "seclabel") == 0) {
273 found = 1;
274 nfile += file_system_count(mount_info[1]);
275 break;
276 }
277 item = strtok(NULL, ",");
278 }
279
280 /* Exclude mount points without the seclabel option */
281 if (!found) {
282 if (add_exclude(mount_info[1], !CALLER_EXCLUDED) &&
283 errno == ENOMEM)
284 assert(0);
285 }
286 }
287
288 free(buf);
289 fclose(fp);
290 /* return estimated #Files + 5% for directories and hard links */
291 return nfile * 1.05;
292 }
293
294 /* Called by selinux_restorecon_xattr(3) to build a linked list of entries. */
add_xattr_entry(const char * directory,bool delete_nonmatch,bool delete_all)295 static int add_xattr_entry(const char *directory, bool delete_nonmatch,
296 bool delete_all)
297 {
298 char *sha1_buf = NULL;
299 unsigned char *xattr_value = NULL;
300 ssize_t xattr_size;
301 size_t i;
302 int rc, digest_result;
303 struct dir_xattr *new_entry;
304
305 if (!directory) {
306 errno = EINVAL;
307 return -1;
308 }
309
310 xattr_value = malloc(fc_digest_len);
311 if (!xattr_value)
312 goto oom;
313
314 xattr_size = getxattr(directory, RESTORECON_LAST, xattr_value,
315 fc_digest_len);
316 if (xattr_size < 0) {
317 free(xattr_value);
318 return 1;
319 }
320
321 /* Convert entry to a hex encoded string. */
322 sha1_buf = malloc(xattr_size * 2 + 1);
323 if (!sha1_buf) {
324 free(xattr_value);
325 goto oom;
326 }
327
328 for (i = 0; i < (size_t)xattr_size; i++)
329 sprintf((&sha1_buf[i * 2]), "%02x", xattr_value[i]);
330
331 rc = memcmp(fc_digest, xattr_value, fc_digest_len);
332 digest_result = rc ? NOMATCH : MATCH;
333
334 if ((delete_nonmatch && rc != 0) || delete_all) {
335 digest_result = rc ? DELETED_NOMATCH : DELETED_MATCH;
336 rc = removexattr(directory, RESTORECON_LAST);
337 if (rc) {
338 selinux_log(SELINUX_ERROR,
339 "Error: %s removing xattr \"%s\" from: %s\n",
340 strerror(errno), RESTORECON_LAST, directory);
341 digest_result = ERROR;
342 }
343 }
344 free(xattr_value);
345
346 /* Now add entries to link list. */
347 new_entry = malloc(sizeof(struct dir_xattr));
348 if (!new_entry)
349 goto oom;
350 new_entry->next = NULL;
351
352 new_entry->directory = strdup(directory);
353 if (!new_entry->directory)
354 goto oom;
355
356 new_entry->digest = strdup(sha1_buf);
357 if (!new_entry->digest)
358 goto oom;
359
360 new_entry->result = digest_result;
361
362 if (!dir_xattr_list) {
363 dir_xattr_list = new_entry;
364 dir_xattr_last = new_entry;
365 } else {
366 dir_xattr_last->next = new_entry;
367 dir_xattr_last = new_entry;
368 }
369
370 free(sha1_buf);
371 return 0;
372
373 oom:
374 selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __func__);
375 return -1;
376 }
377
378 /*
379 * Support filespec services filespec_add(), filespec_eval() and
380 * filespec_destroy().
381 *
382 * selinux_restorecon(3) uses filespec services when the
383 * SELINUX_RESTORECON_ADD_ASSOC flag is set for adding associations between
384 * an inode and a specification.
385 */
386
387 /*
388 * The hash table of associations, hashed by inode number. Chaining is used
389 * for collisions, with elements ordered by inode number in each bucket.
390 * Each hash bucket has a dummy header.
391 */
392 #define HASH_BITS 16
393 #define HASH_BUCKETS (1 << HASH_BITS)
394 #define HASH_MASK (HASH_BUCKETS-1)
395
396 /*
397 * An association between an inode and a context.
398 */
399 typedef struct file_spec {
400 ino_t ino; /* inode number */
401 char *con; /* matched context */
402 char *file; /* full pathname */
403 struct file_spec *next; /* next association in hash bucket chain */
404 } file_spec_t;
405
406 static file_spec_t *fl_head;
407
408 /*
409 * Try to add an association between an inode and a context. If there is a
410 * different context that matched the inode, then use the first context
411 * that matched.
412 */
filespec_add(ino_t ino,const char * con,const char * file)413 static int filespec_add(ino_t ino, const char *con, const char *file)
414 {
415 file_spec_t *prevfl, *fl;
416 int h, ret;
417 struct stat64 sb;
418
419 if (!fl_head) {
420 fl_head = malloc(sizeof(file_spec_t) * HASH_BUCKETS);
421 if (!fl_head)
422 goto oom;
423 memset(fl_head, 0, sizeof(file_spec_t) * HASH_BUCKETS);
424 }
425
426 h = (ino + (ino >> HASH_BITS)) & HASH_MASK;
427 for (prevfl = &fl_head[h], fl = fl_head[h].next; fl;
428 prevfl = fl, fl = fl->next) {
429 if (ino == fl->ino) {
430 ret = lstat64(fl->file, &sb);
431 if (ret < 0 || sb.st_ino != ino) {
432 freecon(fl->con);
433 free(fl->file);
434 fl->file = strdup(file);
435 if (!fl->file)
436 goto oom;
437 fl->con = strdup(con);
438 if (!fl->con)
439 goto oom;
440 return 1;
441 }
442
443 if (strcmp(fl->con, con) == 0)
444 return 1;
445
446 selinux_log(SELINUX_ERROR,
447 "conflicting specifications for %s and %s, using %s.\n",
448 file, fl->file, fl->con);
449 free(fl->file);
450 fl->file = strdup(file);
451 if (!fl->file)
452 goto oom;
453 return 1;
454 }
455
456 if (ino > fl->ino)
457 break;
458 }
459
460 fl = malloc(sizeof(file_spec_t));
461 if (!fl)
462 goto oom;
463 fl->ino = ino;
464 fl->con = strdup(con);
465 if (!fl->con)
466 goto oom_freefl;
467 fl->file = strdup(file);
468 if (!fl->file)
469 goto oom_freefl;
470 fl->next = prevfl->next;
471 prevfl->next = fl;
472 return 0;
473
474 oom_freefl:
475 free(fl);
476 oom:
477 selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __func__);
478 return -1;
479 }
480
481 /*
482 * Evaluate the association hash table distribution.
483 */
484 #ifdef DEBUG
filespec_eval(void)485 static void filespec_eval(void)
486 {
487 file_spec_t *fl;
488 int h, used, nel, len, longest;
489
490 if (!fl_head)
491 return;
492
493 used = 0;
494 longest = 0;
495 nel = 0;
496 for (h = 0; h < HASH_BUCKETS; h++) {
497 len = 0;
498 for (fl = fl_head[h].next; fl; fl = fl->next)
499 len++;
500 if (len)
501 used++;
502 if (len > longest)
503 longest = len;
504 nel += len;
505 }
506
507 selinux_log(SELINUX_INFO,
508 "filespec hash table stats: %d elements, %d/%d buckets used, longest chain length %d\n",
509 nel, used, HASH_BUCKETS, longest);
510 }
511 #else
filespec_eval(void)512 static void filespec_eval(void)
513 {
514 }
515 #endif
516
517 /*
518 * Destroy the association hash table.
519 */
filespec_destroy(void)520 static void filespec_destroy(void)
521 {
522 file_spec_t *fl, *tmp;
523 int h;
524
525 if (!fl_head)
526 return;
527
528 for (h = 0; h < HASH_BUCKETS; h++) {
529 fl = fl_head[h].next;
530 while (fl) {
531 tmp = fl;
532 fl = fl->next;
533 freecon(tmp->con);
534 free(tmp->file);
535 free(tmp);
536 }
537 fl_head[h].next = NULL;
538 }
539 free(fl_head);
540 fl_head = NULL;
541 }
542
543 /*
544 * Called if SELINUX_RESTORECON_SET_SPECFILE_CTX is not set to check if
545 * the type components differ, updating newtypecon if so.
546 */
compare_types(char * curcon,char * newcon,char ** newtypecon)547 static int compare_types(char *curcon, char *newcon, char **newtypecon)
548 {
549 int types_differ = 0;
550 context_t cona;
551 context_t conb;
552 int rc = 0;
553
554 cona = context_new(curcon);
555 if (!cona) {
556 rc = -1;
557 goto out;
558 }
559 conb = context_new(newcon);
560 if (!conb) {
561 context_free(cona);
562 rc = -1;
563 goto out;
564 }
565
566 types_differ = strcmp(context_type_get(cona), context_type_get(conb));
567 if (types_differ) {
568 rc |= context_user_set(conb, context_user_get(cona));
569 rc |= context_role_set(conb, context_role_get(cona));
570 rc |= context_range_set(conb, context_range_get(cona));
571 if (!rc) {
572 *newtypecon = strdup(context_str(conb));
573 if (!*newtypecon) {
574 rc = -1;
575 goto err;
576 }
577 }
578 }
579
580 err:
581 context_free(cona);
582 context_free(conb);
583 out:
584 return rc;
585 }
586
restorecon_sb(const char * pathname,const struct stat * sb,struct rest_flags * flags)587 static int restorecon_sb(const char *pathname, const struct stat *sb,
588 struct rest_flags *flags)
589 {
590 char *newcon = NULL;
591 char *curcon = NULL;
592 char *newtypecon = NULL;
593 int rc;
594 bool updated = false;
595 const char *lookup_path = pathname;
596 float pc;
597
598 if (rootpath) {
599 if (strncmp(rootpath, lookup_path, rootpathlen) != 0) {
600 selinux_log(SELINUX_ERROR,
601 "%s is not located in alt_rootpath %s\n",
602 lookup_path, rootpath);
603 return -1;
604 }
605 lookup_path += rootpathlen;
606 }
607
608 if (rootpath != NULL && lookup_path[0] == '\0')
609 /* this is actually the root dir of the alt root. */
610 rc = selabel_lookup_raw(fc_sehandle, &newcon, "/",
611 sb->st_mode);
612 else
613 rc = selabel_lookup_raw(fc_sehandle, &newcon, lookup_path,
614 sb->st_mode);
615
616 if (rc < 0) {
617 if (errno == ENOENT && flags->warnonnomatch)
618 selinux_log(SELINUX_INFO,
619 "Warning no default label for %s\n",
620 lookup_path);
621
622 return 0; /* no match, but not an error */
623 }
624
625 if (flags->progress) {
626 fc_count++;
627 if (fc_count % STAR_COUNT == 0) {
628 if (flags->mass_relabel && efile_count > 0) {
629 pc = (fc_count < efile_count) ? (100.0 *
630 fc_count / efile_count) : 100;
631 fprintf(stdout, "\r%-.1f%%", (double)pc);
632 } else {
633 fprintf(stdout, "\r%luk", fc_count / STAR_COUNT);
634 }
635 fflush(stdout);
636 }
637 }
638
639 if (flags->add_assoc) {
640 rc = filespec_add(sb->st_ino, newcon, pathname);
641
642 if (rc < 0) {
643 selinux_log(SELINUX_ERROR,
644 "filespec_add error: %s\n", pathname);
645 freecon(newcon);
646 return -1;
647 }
648
649 if (rc > 0) {
650 /* Already an association and it took precedence. */
651 freecon(newcon);
652 return 0;
653 }
654 }
655
656 if (flags->log_matches)
657 selinux_log(SELINUX_INFO, "%s matched by %s\n",
658 pathname, newcon);
659
660 if (lgetfilecon_raw(pathname, &curcon) < 0) {
661 if (errno != ENODATA)
662 goto err;
663
664 curcon = NULL;
665 }
666
667 if (curcon == NULL || strcmp(curcon, newcon) != 0) {
668 if (!flags->set_specctx && curcon &&
669 (is_context_customizable(curcon) > 0)) {
670 if (flags->verbose) {
671 selinux_log(SELINUX_INFO,
672 "%s not reset as customized by admin to %s\n",
673 pathname, curcon);
674 goto out;
675 }
676 }
677
678 if (!flags->set_specctx && curcon) {
679 /* If types different then update newcon. */
680 rc = compare_types(curcon, newcon, &newtypecon);
681 if (rc)
682 goto err;
683
684 if (newtypecon) {
685 freecon(newcon);
686 newcon = newtypecon;
687 } else {
688 goto out;
689 }
690 }
691
692 if (!flags->nochange) {
693 if (lsetfilecon(pathname, newcon) < 0)
694 goto err;
695 updated = true;
696 }
697
698 if (flags->verbose)
699 selinux_log(SELINUX_INFO,
700 "%s %s from %s to %s\n",
701 updated ? "Relabeled" : "Would relabel",
702 pathname, curcon, newcon);
703
704 if (flags->syslog_changes && !flags->nochange) {
705 if (curcon)
706 syslog(LOG_INFO,
707 "relabeling %s from %s to %s\n",
708 pathname, curcon, newcon);
709 else
710 syslog(LOG_INFO, "labeling %s to %s\n",
711 pathname, newcon);
712 }
713 }
714
715 out:
716 rc = 0;
717 out1:
718 freecon(curcon);
719 freecon(newcon);
720 return rc;
721 err:
722 selinux_log(SELINUX_ERROR,
723 "Could not set context for %s: %s\n",
724 pathname, strerror(errno));
725 rc = -1;
726 goto out1;
727 }
728
729 /*
730 * Public API
731 */
732
733 /* selinux_restorecon(3) - Main function that is responsible for labeling */
selinux_restorecon(const char * pathname_orig,unsigned int restorecon_flags)734 int selinux_restorecon(const char *pathname_orig,
735 unsigned int restorecon_flags)
736 {
737 struct rest_flags flags;
738
739 flags.ignore_digest = (restorecon_flags &
740 SELINUX_RESTORECON_IGNORE_DIGEST) ? true : false;
741 flags.nochange = (restorecon_flags &
742 SELINUX_RESTORECON_NOCHANGE) ? true : false;
743 flags.verbose = (restorecon_flags &
744 SELINUX_RESTORECON_VERBOSE) ? true : false;
745 flags.progress = (restorecon_flags &
746 SELINUX_RESTORECON_PROGRESS) ? true : false;
747 flags.mass_relabel = (restorecon_flags &
748 SELINUX_RESTORECON_MASS_RELABEL) ? true : false;
749 flags.recurse = (restorecon_flags &
750 SELINUX_RESTORECON_RECURSE) ? true : false;
751 flags.set_specctx = (restorecon_flags &
752 SELINUX_RESTORECON_SET_SPECFILE_CTX) ? true : false;
753 flags.userealpath = (restorecon_flags &
754 SELINUX_RESTORECON_REALPATH) ? true : false;
755 flags.set_xdev = (restorecon_flags &
756 SELINUX_RESTORECON_XDEV) ? true : false;
757 flags.add_assoc = (restorecon_flags &
758 SELINUX_RESTORECON_ADD_ASSOC) ? true : false;
759 flags.abort_on_error = (restorecon_flags &
760 SELINUX_RESTORECON_ABORT_ON_ERROR) ? true : false;
761 flags.syslog_changes = (restorecon_flags &
762 SELINUX_RESTORECON_SYSLOG_CHANGES) ? true : false;
763 flags.log_matches = (restorecon_flags &
764 SELINUX_RESTORECON_LOG_MATCHES) ? true : false;
765 flags.ignore_noent = (restorecon_flags &
766 SELINUX_RESTORECON_IGNORE_NOENTRY) ? true : false;
767 flags.warnonnomatch = true;
768 ignore_mounts = (restorecon_flags &
769 SELINUX_RESTORECON_IGNORE_MOUNTS) ? true : false;
770
771 bool issys;
772 bool setrestoreconlast = true; /* TRUE = set xattr RESTORECON_LAST
773 * FALSE = don't use xattr */
774 struct stat sb;
775 struct statfs sfsb;
776 FTS *fts;
777 FTSENT *ftsent;
778 char *pathname = NULL, *pathdnamer = NULL, *pathdname, *pathbname;
779 char *paths[2] = { NULL, NULL };
780 int fts_flags, error, sverrno;
781 char *xattr_value = NULL;
782 ssize_t size;
783 dev_t dev_num = 0;
784
785 if (flags.verbose && flags.progress)
786 flags.verbose = false;
787
788 __selinux_once(fc_once, restorecon_init);
789
790 if (!fc_sehandle)
791 return -1;
792
793 if (fc_digest_len) {
794 xattr_value = malloc(fc_digest_len);
795 if (!xattr_value)
796 return -1;
797 }
798
799 /*
800 * Convert passed-in pathname to canonical pathname by resolving
801 * realpath of containing dir, then appending last component name.
802 */
803 if (flags.userealpath) {
804 char *basename_cpy = strdup(pathname_orig);
805 if (!basename_cpy)
806 goto realpatherr;
807 pathbname = basename(basename_cpy);
808 if (!strcmp(pathbname, "/") || !strcmp(pathbname, ".") ||
809 !strcmp(pathbname, "..")) {
810 pathname = realpath(pathname_orig, NULL);
811 if (!pathname) {
812 free(basename_cpy);
813 goto realpatherr;
814 }
815 } else {
816 char *dirname_cpy = strdup(pathname_orig);
817 if (!dirname_cpy) {
818 free(basename_cpy);
819 goto realpatherr;
820 }
821 pathdname = dirname(dirname_cpy);
822 pathdnamer = realpath(pathdname, NULL);
823 free(dirname_cpy);
824 if (!pathdnamer) {
825 free(basename_cpy);
826 goto realpatherr;
827 }
828 if (!strcmp(pathdnamer, "/"))
829 error = asprintf(&pathname, "/%s", pathbname);
830 else
831 error = asprintf(&pathname, "%s/%s",
832 pathdnamer, pathbname);
833 if (error < 0) {
834 free(basename_cpy);
835 goto oom;
836 }
837 }
838 free(basename_cpy);
839 } else {
840 pathname = strdup(pathname_orig);
841 if (!pathname)
842 goto oom;
843 }
844
845 paths[0] = pathname;
846 issys = (!strcmp(pathname, SYS_PATH) ||
847 !strncmp(pathname, SYS_PREFIX,
848 sizeof(SYS_PREFIX) - 1)) ? true : false;
849
850 if (lstat(pathname, &sb) < 0) {
851 if (flags.ignore_noent && errno == ENOENT) {
852 free(pathdnamer);
853 free(pathname);
854 return 0;
855 } else {
856 selinux_log(SELINUX_ERROR,
857 "lstat(%s) failed: %s\n",
858 pathname, strerror(errno));
859 error = -1;
860 goto cleanup;
861 }
862 }
863
864 /* Ignore restoreconlast if not a directory */
865 if ((sb.st_mode & S_IFDIR) != S_IFDIR)
866 setrestoreconlast = false;
867
868 if (!flags.recurse) {
869 if (check_excluded(pathname)) {
870 error = 0;
871 goto cleanup;
872 }
873
874 error = restorecon_sb(pathname, &sb, &flags);
875 goto cleanup;
876 }
877
878 /* Ignore restoreconlast on /sys */
879 if (issys)
880 setrestoreconlast = false;
881
882 /* Ignore restoreconlast on in-memory filesystems */
883 if (statfs(pathname, &sfsb) == 0) {
884 if (sfsb.f_type == RAMFS_MAGIC || sfsb.f_type == TMPFS_MAGIC)
885 setrestoreconlast = false;
886 }
887
888 if (setrestoreconlast) {
889 size = getxattr(pathname, RESTORECON_LAST, xattr_value,
890 fc_digest_len);
891
892 if (!flags.ignore_digest && (size_t)size == fc_digest_len &&
893 memcmp(fc_digest, xattr_value, fc_digest_len)
894 == 0) {
895 selinux_log(SELINUX_INFO,
896 "Skipping restorecon as matching digest on: %s\n",
897 pathname);
898 error = 0;
899 goto cleanup;
900 }
901 }
902
903 if (flags.set_xdev)
904 fts_flags = FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV;
905 else
906 fts_flags = FTS_PHYSICAL | FTS_NOCHDIR;
907
908 fts = fts_open(paths, fts_flags, NULL);
909 if (!fts)
910 goto fts_err;
911
912 ftsent = fts_read(fts);
913 if (!ftsent)
914 goto fts_err;
915
916 /*
917 * Keep the inode of the first device. This is because the FTS_XDEV
918 * flag tells fts not to descend into directories with different
919 * device numbers, but fts will still give back the actual directory.
920 * By saving the device number of the directory that was passed to
921 * selinux_restorecon() and then skipping all actions on any
922 * directories with a different device number when the FTS_XDEV flag
923 * is set (from http://marc.info/?l=selinux&m=124688830500777&w=2).
924 */
925 dev_num = ftsent->fts_statp->st_dev;
926
927 error = 0;
928 do {
929 /* If the FTS_XDEV flag is set and the device is different */
930 if (flags.set_xdev && ftsent->fts_statp->st_dev != dev_num)
931 continue;
932
933 switch (ftsent->fts_info) {
934 case FTS_DC:
935 selinux_log(SELINUX_ERROR,
936 "Directory cycle on %s.\n",
937 ftsent->fts_path);
938 errno = ELOOP;
939 error = -1;
940 goto out;
941 case FTS_DP:
942 continue;
943 case FTS_DNR:
944 selinux_log(SELINUX_ERROR,
945 "Could not read %s: %s.\n",
946 ftsent->fts_path,
947 strerror(ftsent->fts_errno));
948 fts_set(fts, ftsent, FTS_SKIP);
949 continue;
950 case FTS_NS:
951 selinux_log(SELINUX_ERROR,
952 "Could not stat %s: %s.\n",
953 ftsent->fts_path,
954 strerror(ftsent->fts_errno));
955 fts_set(fts, ftsent, FTS_SKIP);
956 continue;
957 case FTS_ERR:
958 selinux_log(SELINUX_ERROR,
959 "Error on %s: %s.\n",
960 ftsent->fts_path,
961 strerror(ftsent->fts_errno));
962 fts_set(fts, ftsent, FTS_SKIP);
963 continue;
964 case FTS_D:
965 if (issys && !selabel_partial_match(fc_sehandle,
966 ftsent->fts_path)) {
967 fts_set(fts, ftsent, FTS_SKIP);
968 continue;
969 }
970
971 if (check_excluded(ftsent->fts_path)) {
972 fts_set(fts, ftsent, FTS_SKIP);
973 continue;
974 }
975 /* fall through */
976 default:
977 error |= restorecon_sb(ftsent->fts_path,
978 ftsent->fts_statp, &flags);
979 if (flags.warnonnomatch)
980 flags.warnonnomatch = false;
981 if (error && flags.abort_on_error)
982 goto out;
983 break;
984 }
985 } while ((ftsent = fts_read(fts)) != NULL);
986
987 /* Labeling successful. Mark the top level directory as completed. */
988 if (setrestoreconlast && !flags.nochange && !error && fc_digest) {
989 error = setxattr(pathname, RESTORECON_LAST, fc_digest,
990 fc_digest_len, 0);
991 if (!error && flags.verbose)
992 selinux_log(SELINUX_INFO,
993 "Updated digest for: %s\n", pathname);
994 }
995
996 out:
997 if (flags.progress && flags.mass_relabel)
998 fprintf(stdout, "\r%s 100.0%%\n", pathname);
999
1000 sverrno = errno;
1001 (void) fts_close(fts);
1002 errno = sverrno;
1003 cleanup:
1004 if (flags.add_assoc) {
1005 if (flags.verbose)
1006 filespec_eval();
1007 filespec_destroy();
1008 }
1009 free(pathdnamer);
1010 free(pathname);
1011 free(xattr_value);
1012 return error;
1013
1014 oom:
1015 sverrno = errno;
1016 selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __func__);
1017 errno = sverrno;
1018 error = -1;
1019 goto cleanup;
1020
1021 realpatherr:
1022 sverrno = errno;
1023 selinux_log(SELINUX_ERROR,
1024 "SELinux: Could not get canonical path for %s restorecon: %s.\n",
1025 pathname_orig, strerror(errno));
1026 errno = sverrno;
1027 error = -1;
1028 goto cleanup;
1029
1030 fts_err:
1031 selinux_log(SELINUX_ERROR,
1032 "fts error while labeling %s: %s\n",
1033 paths[0], strerror(errno));
1034 error = -1;
1035 goto cleanup;
1036 }
1037
1038 /* selinux_restorecon_set_sehandle(3) is called to set the global fc handle */
selinux_restorecon_set_sehandle(struct selabel_handle * hndl)1039 void selinux_restorecon_set_sehandle(struct selabel_handle *hndl)
1040 {
1041 char **specfiles;
1042 size_t num_specfiles;
1043
1044 fc_sehandle = (struct selabel_handle *) hndl;
1045
1046 /*
1047 * Read digest if requested in selabel_open(3) and set global params.
1048 */
1049 if (selabel_digest(fc_sehandle, &fc_digest, &fc_digest_len,
1050 &specfiles, &num_specfiles) < 0) {
1051 fc_digest = NULL;
1052 fc_digest_len = 0;
1053 }
1054 }
1055
1056 /*
1057 * selinux_restorecon_default_handle(3) is called to set the global restorecon
1058 * handle by a process if the default params are required.
1059 */
selinux_restorecon_default_handle(void)1060 struct selabel_handle *selinux_restorecon_default_handle(void)
1061 {
1062 struct selabel_handle *sehandle;
1063
1064 struct selinux_opt fc_opts[] = {
1065 { SELABEL_OPT_DIGEST, (char *)1 }
1066 };
1067
1068 sehandle = selabel_open(SELABEL_CTX_FILE, fc_opts, 1);
1069
1070 if (!sehandle) {
1071 selinux_log(SELINUX_ERROR,
1072 "Error obtaining file context handle: %s\n",
1073 strerror(errno));
1074 return NULL;
1075 }
1076
1077 return sehandle;
1078 }
1079
1080 /*
1081 * selinux_restorecon_set_exclude_list(3) is called to add additional entries
1082 * to be excluded from labeling checks.
1083 */
selinux_restorecon_set_exclude_list(const char ** exclude_list)1084 void selinux_restorecon_set_exclude_list(const char **exclude_list)
1085 {
1086 int i;
1087 struct stat sb;
1088
1089 for (i = 0; exclude_list[i]; i++) {
1090 if (lstat(exclude_list[i], &sb) < 0 && errno != EACCES) {
1091 selinux_log(SELINUX_ERROR,
1092 "lstat error on exclude path \"%s\", %s - ignoring.\n",
1093 exclude_list[i], strerror(errno));
1094 break;
1095 }
1096 if (add_exclude(exclude_list[i], CALLER_EXCLUDED) &&
1097 errno == ENOMEM)
1098 assert(0);
1099 }
1100 }
1101
1102 /* selinux_restorecon_set_alt_rootpath(3) sets an alternate rootpath. */
selinux_restorecon_set_alt_rootpath(const char * alt_rootpath)1103 int selinux_restorecon_set_alt_rootpath(const char *alt_rootpath)
1104 {
1105 int len;
1106
1107 /* This should be NULL on first use */
1108 if (rootpath)
1109 free(rootpath);
1110
1111 rootpath = strdup(alt_rootpath);
1112 if (!rootpath) {
1113 selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __func__);
1114 return -1;
1115 }
1116
1117 /* trim trailing /, if present */
1118 len = strlen(rootpath);
1119 while (len && (rootpath[len - 1] == '/'))
1120 rootpath[--len] = '\0';
1121 rootpathlen = len;
1122
1123 return 0;
1124 }
1125
1126 /* selinux_restorecon_xattr(3) - Find RESTORECON_LAST entries. */
selinux_restorecon_xattr(const char * pathname,unsigned int xattr_flags,struct dir_xattr *** xattr_list)1127 int selinux_restorecon_xattr(const char *pathname, unsigned int xattr_flags,
1128 struct dir_xattr ***xattr_list)
1129 {
1130 bool recurse = (xattr_flags &
1131 SELINUX_RESTORECON_XATTR_RECURSE) ? true : false;
1132 bool delete_nonmatch = (xattr_flags &
1133 SELINUX_RESTORECON_XATTR_DELETE_NONMATCH_DIGESTS) ? true : false;
1134 bool delete_all = (xattr_flags &
1135 SELINUX_RESTORECON_XATTR_DELETE_ALL_DIGESTS) ? true : false;
1136 ignore_mounts = (xattr_flags &
1137 SELINUX_RESTORECON_XATTR_IGNORE_MOUNTS) ? true : false;
1138
1139 int rc, fts_flags;
1140 struct stat sb;
1141 struct statfs sfsb;
1142 struct dir_xattr *current, *next;
1143 FTS *fts;
1144 FTSENT *ftsent;
1145 char *paths[2] = { NULL, NULL };
1146
1147 __selinux_once(fc_once, restorecon_init);
1148
1149 if (!fc_sehandle || !fc_digest_len)
1150 return -1;
1151
1152 if (lstat(pathname, &sb) < 0) {
1153 if (errno == ENOENT)
1154 return 0;
1155
1156 selinux_log(SELINUX_ERROR,
1157 "lstat(%s) failed: %s\n",
1158 pathname, strerror(errno));
1159 return -1;
1160 }
1161
1162 if (!recurse) {
1163 if (statfs(pathname, &sfsb) == 0) {
1164 if (sfsb.f_type == RAMFS_MAGIC ||
1165 sfsb.f_type == TMPFS_MAGIC)
1166 return 0;
1167 }
1168
1169 if (check_excluded(pathname))
1170 return 0;
1171
1172 rc = add_xattr_entry(pathname, delete_nonmatch, delete_all);
1173
1174 if (!rc && dir_xattr_list)
1175 *xattr_list = &dir_xattr_list;
1176 else if (rc == -1)
1177 return rc;
1178
1179 return 0;
1180 }
1181
1182 paths[0] = (char *)pathname;
1183 fts_flags = FTS_PHYSICAL | FTS_NOCHDIR;
1184
1185 fts = fts_open(paths, fts_flags, NULL);
1186 if (!fts) {
1187 selinux_log(SELINUX_ERROR,
1188 "fts error on %s: %s\n",
1189 paths[0], strerror(errno));
1190 return -1;
1191 }
1192
1193 while ((ftsent = fts_read(fts)) != NULL) {
1194 switch (ftsent->fts_info) {
1195 case FTS_DP:
1196 continue;
1197 case FTS_D:
1198 if (statfs(ftsent->fts_path, &sfsb) == 0) {
1199 if (sfsb.f_type == RAMFS_MAGIC ||
1200 sfsb.f_type == TMPFS_MAGIC)
1201 continue;
1202 }
1203 if (check_excluded(ftsent->fts_path)) {
1204 fts_set(fts, ftsent, FTS_SKIP);
1205 continue;
1206 }
1207
1208 rc = add_xattr_entry(ftsent->fts_path,
1209 delete_nonmatch, delete_all);
1210 if (rc == 1)
1211 continue;
1212 else if (rc == -1)
1213 goto cleanup;
1214 break;
1215 default:
1216 break;
1217 }
1218 }
1219
1220 if (dir_xattr_list)
1221 *xattr_list = &dir_xattr_list;
1222
1223 (void) fts_close(fts);
1224 return 0;
1225
1226 cleanup:
1227 rc = errno;
1228 (void) fts_close(fts);
1229 errno = rc;
1230
1231 if (dir_xattr_list) {
1232 /* Free any used memory */
1233 current = dir_xattr_list;
1234 while (current) {
1235 next = current->next;
1236 free(current->directory);
1237 free(current->digest);
1238 free(current);
1239 current = next;
1240 }
1241 }
1242 return -1;
1243 }
1244