Home | History | Annotate | Download | only in setfiles
      1 #include "restore.h"
      2 #include <unistd.h>
      3 #include <fcntl.h>
      4 #include <stdio_ext.h>
      5 #include <ctype.h>
      6 #include <regex.h>
      7 #include <sys/vfs.h>
      8 #include <libgen.h>
      9 #ifdef USE_AUDIT
     10 #include <libaudit.h>
     11 
     12 #ifndef AUDIT_FS_RELABEL
     13 #define AUDIT_FS_RELABEL 2309
     14 #endif
     15 #endif
     16 
     17 static char *policyfile;
     18 static int warn_no_match;
     19 static int null_terminated;
     20 static int request_digest;
     21 static struct restore_opts r_opts;
     22 static int nerr;
     23 
     24 #define STAT_BLOCK_SIZE 1
     25 
     26 /* setfiles will abort its operation after reaching the
     27  * following number of errors (e.g. invalid contexts),
     28  * unless it is used in "debug" mode (-d option).
     29  */
     30 #ifndef ABORT_ON_ERRORS
     31 #define ABORT_ON_ERRORS	10
     32 #endif
     33 
     34 #define SETFILES "setfiles"
     35 #define RESTORECON "restorecon"
     36 static int iamrestorecon;
     37 
     38 /* Behavior flags determined based on setfiles vs. restorecon */
     39 static int ctx_validate; /* Validate contexts */
     40 static const char *altpath; /* Alternate path to file_contexts */
     41 
     42 static __attribute__((__noreturn__)) void usage(const char *const name)
     43 {
     44 	if (iamrestorecon) {
     45 		fprintf(stderr,
     46 			"usage:  %s [-iIDFmnprRv0] [-e excludedir] pathname...\n"
     47 			"usage:  %s [-iIDFmnprRv0] [-e excludedir] -f filename\n",
     48 			name, name);
     49 	} else {
     50 		fprintf(stderr,
     51 			"usage:  %s [-diIDlmnpqvFW] [-e excludedir] [-r alt_root_path] spec_file pathname...\n"
     52 			"usage:  %s [-diIDlmnpqvFW] [-e excludedir] [-r alt_root_path] spec_file -f filename\n"
     53 			"usage:  %s -s [-diIDlmnpqvFW] spec_file\n"
     54 			"usage:  %s -c policyfile spec_file\n",
     55 			name, name, name, name);
     56 	}
     57 	exit(-1);
     58 }
     59 
     60 void inc_err(void)
     61 {
     62 	nerr++;
     63 	if (nerr > ABORT_ON_ERRORS - 1 && !r_opts.debug) {
     64 		fprintf(stderr, "Exiting after %d errors.\n", ABORT_ON_ERRORS);
     65 		exit(-1);
     66 	}
     67 }
     68 
     69 void set_rootpath(const char *arg)
     70 {
     71 	if (strlen(arg) == 1 && strncmp(arg, "/", 1) == 0) {
     72 		fprintf(stderr, "%s:  invalid alt_rootpath: %s\n",
     73 			r_opts.progname, arg);
     74 		exit(-1);
     75 	}
     76 
     77 	r_opts.rootpath = strdup(arg);
     78 	if (!r_opts.rootpath) {
     79 		fprintf(stderr,
     80 			"%s:  insufficient memory for r_opts.rootpath\n",
     81 			r_opts.progname);
     82 		exit(-1);
     83 	}
     84 }
     85 
     86 int canoncon(char **contextp)
     87 {
     88 	char *context = *contextp, *tmpcon;
     89 	int rc = 0;
     90 
     91 	if (policyfile) {
     92 		if (sepol_check_context(context) < 0) {
     93 			fprintf(stderr, "invalid context %s\n", context);
     94 			exit(-1);
     95 		}
     96 	} else if (security_canonicalize_context_raw(context, &tmpcon) == 0) {
     97 		free(context);
     98 		*contextp = tmpcon;
     99 	} else if (errno != ENOENT) {
    100 		rc = -1;
    101 		inc_err();
    102 	}
    103 
    104 	return rc;
    105 }
    106 
    107 #ifndef USE_AUDIT
    108 static void maybe_audit_mass_relabel(int mass_relabel __attribute__((unused)),
    109 				int mass_relabel_errs __attribute__((unused)))
    110 {
    111 #else
    112 static void maybe_audit_mass_relabel(int mass_relabel, int mass_relabel_errs)
    113 {
    114 	int audit_fd = -1;
    115 	int rc = 0;
    116 
    117 	if (!mass_relabel)		/* only audit a forced full relabel */
    118 		return;
    119 
    120 	audit_fd = audit_open();
    121 
    122 	if (audit_fd < 0) {
    123 		fprintf(stderr, "Error connecting to audit system.\n");
    124 		exit(-1);
    125 	}
    126 
    127 	rc = audit_log_user_message(audit_fd, AUDIT_FS_RELABEL,
    128 				    "op=mass relabel",
    129 				    NULL, NULL, NULL, !mass_relabel_errs);
    130 	if (rc <= 0) {
    131 		fprintf(stderr, "Error sending audit message: %s.\n",
    132 			strerror(errno));
    133 		/* exit(-1); -- don't exit atm. as fix for eff_cap isn't
    134 		 * in most kernels.
    135 		 */
    136 	}
    137 	audit_close(audit_fd);
    138 #endif
    139 }
    140 
    141 static int __attribute__ ((format(printf, 2, 3)))
    142 log_callback(int type, const char *fmt, ...)
    143 {
    144 	int rc;
    145 	FILE *out;
    146 	va_list ap;
    147 
    148 	if (type == SELINUX_INFO) {
    149 		out = stdout;
    150 	} else {
    151 		out = stderr;
    152 		fflush(stdout);
    153 		fprintf(out, "%s: ", r_opts.progname);
    154 	}
    155 	va_start(ap, fmt);
    156 	rc = vfprintf(out, fmt, ap);
    157 	va_end(ap);
    158 	return rc;
    159 }
    160 
    161 int main(int argc, char **argv)
    162 {
    163 	struct stat sb;
    164 	int opt, i = 0;
    165 	const char *input_filename = NULL;
    166 	int use_input_file = 0;
    167 	char *buf = NULL;
    168 	size_t buf_len;
    169 	const char *base;
    170 	int errors = 0;
    171 	const char *ropts = "e:f:hiIDlmno:pqrsvFRW0";
    172 	const char *sopts = "c:de:f:hiIDlmno:pqr:svFR:W0";
    173 	const char *opts;
    174 	union selinux_callback cb;
    175 
    176 	/* Initialize variables */
    177 	memset(&r_opts, 0, sizeof(r_opts));
    178 	altpath = NULL;
    179 	null_terminated = 0;
    180 	warn_no_match = 0;
    181 	request_digest = 0;
    182 	policyfile = NULL;
    183 	nerr = 0;
    184 
    185 	r_opts.progname = strdup(argv[0]);
    186 	if (!r_opts.progname) {
    187 		fprintf(stderr, "%s:  Out of memory!\n", argv[0]);
    188 		exit(-1);
    189 	}
    190 	base = basename(r_opts.progname);
    191 
    192 	if (!strcmp(base, SETFILES)) {
    193 		/*
    194 		 * setfiles:
    195 		 * Recursive descent,
    196 		 * Does not expand paths via realpath,
    197 		 * Aborts on errors during the file tree walk,
    198 		 * Try to track inode associations for conflict detection,
    199 		 * Does not follow mounts (sets SELINUX_RESTORECON_XDEV),
    200 		 * Validates all file contexts at init time.
    201 		 */
    202 		iamrestorecon = 0;
    203 		r_opts.recurse = SELINUX_RESTORECON_RECURSE;
    204 		r_opts.userealpath = 0; /* SELINUX_RESTORECON_REALPATH */
    205 		r_opts.abort_on_error = SELINUX_RESTORECON_ABORT_ON_ERROR;
    206 		r_opts.add_assoc = SELINUX_RESTORECON_ADD_ASSOC;
    207 		/* FTS_PHYSICAL and FTS_NOCHDIR are always set by selinux_restorecon(3) */
    208 		r_opts.xdev = SELINUX_RESTORECON_XDEV;
    209 		r_opts.ignore_mounts = 0; /* SELINUX_RESTORECON_IGNORE_MOUNTS */
    210 		ctx_validate = 1;
    211 		opts = sopts;
    212 	} else {
    213 		/*
    214 		 * restorecon:
    215 		 * No recursive descent unless -r/-R,
    216 		 * Expands paths via realpath,
    217 		 * Do not abort on errors during the file tree walk,
    218 		 * Do not try to track inode associations for conflict detection,
    219 		 * Follows mounts,
    220 		 * Does lazy validation of contexts upon use.
    221 		 */
    222 		if (strcmp(base, RESTORECON))
    223 			fprintf(stderr, "Executed with unrecognized name (%s), defaulting to %s behavior.\n",
    224 				base, RESTORECON);
    225 
    226 		iamrestorecon = 1;
    227 		r_opts.recurse = 0;
    228 		r_opts.userealpath = SELINUX_RESTORECON_REALPATH;
    229 		r_opts.abort_on_error = 0;
    230 		r_opts.add_assoc = 0;
    231 		r_opts.xdev = 0;
    232 		r_opts.ignore_mounts = 0;
    233 		ctx_validate = 0;
    234 		opts = ropts;
    235 
    236 		/* restorecon only:  silent exit if no SELinux.
    237 		 * Allows unconditional execution by scripts.
    238 		 */
    239 		if (is_selinux_enabled() <= 0)
    240 			exit(0);
    241 	}
    242 
    243 	/* Process any options. */
    244 	while ((opt = getopt(argc, argv, opts)) > 0) {
    245 		switch (opt) {
    246 		case 'c':
    247 			{
    248 				FILE *policystream;
    249 
    250 				if (iamrestorecon)
    251 					usage(argv[0]);
    252 
    253 				policyfile = optarg;
    254 
    255 				policystream = fopen(policyfile, "r");
    256 				if (!policystream) {
    257 					fprintf(stderr,
    258 						"Error opening %s: %s\n",
    259 						policyfile, strerror(errno));
    260 					exit(-1);
    261 				}
    262 				__fsetlocking(policystream,
    263 					      FSETLOCKING_BYCALLER);
    264 
    265 				if (sepol_set_policydb_from_file(policystream)
    266 									< 0) {
    267 					fprintf(stderr,
    268 						"Error reading policy %s: %s\n",
    269 						policyfile, strerror(errno));
    270 					exit(-1);
    271 				}
    272 				fclose(policystream);
    273 
    274 				ctx_validate = 1;
    275 				break;
    276 			}
    277 		case 'e':
    278 			if (lstat(optarg, &sb) < 0 && errno != EACCES) {
    279 				fprintf(stderr, "Can't stat exclude path \"%s\", %s - ignoring.\n",
    280 					optarg, strerror(errno));
    281 				break;
    282 			}
    283 			add_exclude(optarg);
    284 			break;
    285 		case 'f':
    286 			use_input_file = 1;
    287 			input_filename = optarg;
    288 			break;
    289 		case 'd':
    290 			if (iamrestorecon)
    291 				usage(argv[0]);
    292 			r_opts.debug = 1;
    293 			r_opts.log_matches =
    294 					   SELINUX_RESTORECON_LOG_MATCHES;
    295 			break;
    296 		case 'i':
    297 			r_opts.ignore_noent =
    298 					   SELINUX_RESTORECON_IGNORE_NOENTRY;
    299 			break;
    300 		case 'I': /* Force label check by ignoring directory digest. */
    301 			r_opts.ignore_digest =
    302 					   SELINUX_RESTORECON_IGNORE_DIGEST;
    303 			request_digest = 1;
    304 			break;
    305 		case 'D': /*
    306 			   * Request file_contexts digest in selabel_open
    307 			   * This will effectively enable usage of the
    308 			   * security.restorecon_last extended attribute.
    309 			   */
    310 			request_digest = 1;
    311 			break;
    312 		case 'l':
    313 			r_opts.syslog_changes =
    314 					   SELINUX_RESTORECON_SYSLOG_CHANGES;
    315 			break;
    316 		case 'F':
    317 			r_opts.set_specctx =
    318 					   SELINUX_RESTORECON_SET_SPECFILE_CTX;
    319 			break;
    320 		case 'm':
    321 			r_opts.ignore_mounts =
    322 					   SELINUX_RESTORECON_IGNORE_MOUNTS;
    323 			break;
    324 		case 'n':
    325 			r_opts.nochange = SELINUX_RESTORECON_NOCHANGE;
    326 			break;
    327 		case 'o': /* Deprecated */
    328 			fprintf(stderr, "%s: -o option no longer supported\n",
    329 				r_opts.progname);
    330 			break;
    331 		case 'q':
    332 			/* Deprecated - Was only used to say whether print
    333 			 * filespec_eval() params. Now uses verbose flag.
    334 			 */
    335 			break;
    336 		case 'R':
    337 		case 'r':
    338 			if (iamrestorecon) {
    339 				r_opts.recurse = SELINUX_RESTORECON_RECURSE;
    340 				break;
    341 			}
    342 
    343 			if (lstat(optarg, &sb) < 0 && errno != EACCES) {
    344 				fprintf(stderr,
    345 					"Can't stat alt_root_path \"%s\", %s\n",
    346 					optarg, strerror(errno));
    347 				exit(-1);
    348 			}
    349 
    350 			if (r_opts.rootpath) {
    351 				fprintf(stderr,
    352 					"%s: only one -r can be specified\n",
    353 					argv[0]);
    354 				exit(-1);
    355 			}
    356 			set_rootpath(optarg);
    357 			break;
    358 		case 's':
    359 			use_input_file = 1;
    360 			input_filename = "-";
    361 			r_opts.add_assoc = 0;
    362 			break;
    363 		case 'v':
    364 			if (r_opts.progress) {
    365 				fprintf(stderr,
    366 					"Progress and Verbose mutually exclusive\n");
    367 				usage(argv[0]);
    368 			}
    369 			r_opts.verbose = SELINUX_RESTORECON_VERBOSE;
    370 			break;
    371 		case 'p':
    372 			if (r_opts.verbose) {
    373 				fprintf(stderr,
    374 					"Progress and Verbose mutually exclusive\n");
    375 				usage(argv[0]);
    376 			}
    377 			r_opts.progress = SELINUX_RESTORECON_PROGRESS;
    378 			break;
    379 		case 'W':
    380 			warn_no_match = 1; /* Print selabel_stats() */
    381 			break;
    382 		case '0':
    383 			null_terminated = 1;
    384 			break;
    385 		case 'h':
    386 		case '?':
    387 			usage(argv[0]);
    388 		}
    389 	}
    390 
    391 	for (i = optind; i < argc; i++) {
    392 		if (!strcmp(argv[i], "/"))
    393 			r_opts.mass_relabel = SELINUX_RESTORECON_MASS_RELABEL;
    394 	}
    395 
    396 	cb.func_log = log_callback;
    397 	selinux_set_callback(SELINUX_CB_LOG, cb);
    398 
    399 	if (!iamrestorecon) {
    400 		if (policyfile) {
    401 			if (optind != (argc - 1))
    402 				usage(argv[0]);
    403 		} else if (use_input_file) {
    404 			if (optind != (argc - 1)) {
    405 				/* Cannot mix with pathname arguments. */
    406 				usage(argv[0]);
    407 			}
    408 		} else {
    409 			if (optind > (argc - 2))
    410 				usage(argv[0]);
    411 		}
    412 
    413 		/* Use our own invalid context checking function so that
    414 		 * we can support either checking against the active policy or
    415 		 * checking against a binary policy file.
    416 		 */
    417 		cb.func_validate = canoncon;
    418 		selinux_set_callback(SELINUX_CB_VALIDATE, cb);
    419 
    420 		if (stat(argv[optind], &sb) < 0) {
    421 			perror(argv[optind]);
    422 			exit(-1);
    423 		}
    424 		if (!S_ISREG(sb.st_mode)) {
    425 			fprintf(stderr, "%s:  spec file %s is not a regular file.\n",
    426 				argv[0], argv[optind]);
    427 			exit(-1);
    428 		}
    429 
    430 		altpath = argv[optind];
    431 		optind++;
    432 	} else if (argc == 1)
    433 		usage(argv[0]);
    434 
    435 	/* Set selabel_open options. */
    436 	r_opts.selabel_opt_validate = (ctx_validate ? (char *)1 : NULL);
    437 	r_opts.selabel_opt_digest = (request_digest ? (char *)1 : NULL);
    438 	r_opts.selabel_opt_path = altpath;
    439 
    440 	if (nerr)
    441 		exit(-1);
    442 
    443 	restore_init(&r_opts);
    444 
    445 	if (use_input_file) {
    446 		FILE *f = stdin;
    447 		ssize_t len;
    448 		int delim;
    449 
    450 		if (strcmp(input_filename, "-") != 0)
    451 			f = fopen(input_filename, "r");
    452 
    453 		if (f == NULL) {
    454 			fprintf(stderr, "Unable to open %s: %s\n",
    455 				input_filename,
    456 				strerror(errno));
    457 			usage(argv[0]);
    458 		}
    459 		__fsetlocking(f, FSETLOCKING_BYCALLER);
    460 
    461 		delim = (null_terminated != 0) ? '\0' : '\n';
    462 		while ((len = getdelim(&buf, &buf_len, delim, f)) > 0) {
    463 			buf[len - 1] = 0;
    464 			if (!strcmp(buf, "/"))
    465 				r_opts.mass_relabel = SELINUX_RESTORECON_MASS_RELABEL;
    466 			errors |= process_glob(buf, &r_opts) < 0;
    467 		}
    468 		if (strcmp(input_filename, "-") != 0)
    469 			fclose(f);
    470 	} else {
    471 		for (i = optind; i < argc; i++)
    472 			errors |= process_glob(argv[i], &r_opts) < 0;
    473 	}
    474 
    475 	maybe_audit_mass_relabel(r_opts.mass_relabel, errors);
    476 
    477 	if (warn_no_match)
    478 		selabel_stats(r_opts.hnd);
    479 
    480 	selabel_close(r_opts.hnd);
    481 	restore_finish();
    482 
    483 	if (r_opts.progress)
    484 		fprintf(stdout, "\n");
    485 
    486 	exit(errors ? -1 : 0);
    487 }
    488