Home | History | Annotate | Download | only in src
      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 
     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  */
    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 
    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 
    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 
    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  */
    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. */
    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  */
    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
    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
    512 static void filespec_eval(void)
    513 {
    514 }
    515 #endif
    516 
    517 /*
    518  * Destroy the association hash table.
    519  */
    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  */
    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 
    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 */
    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 */
   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  */
   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  */
   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. */
   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. */
   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