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