• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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