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
usage(const char * const name)42 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
inc_err(void)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
set_rootpath(const char * arg)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
canoncon(char ** contextp)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
maybe_audit_mass_relabel(int mass_relabel,int mass_relabel_errs)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 = (type == SELINUX_INFO) ? stdout : stderr;
146 va_list ap;
147 fprintf(out, "%s: ", r_opts.progname);
148 va_start(ap, fmt);
149 rc = vfprintf(out, fmt, ap);
150 va_end(ap);
151 return rc;
152 }
153
154 int main(int argc, char **argv)
155 {
156 struct stat sb;
157 int opt, i = 0;
158 const char *input_filename = NULL;
159 int use_input_file = 0;
160 char *buf = NULL;
161 size_t buf_len;
162 const char *base;
163 int errors = 0;
164 const char *ropts = "e:f:hiIDlmno:pqrsvFRW0";
165 const char *sopts = "c:de:f:hiIDlmno:pqr:svFR:W0";
166 const char *opts;
167 union selinux_callback cb;
168
169 /* Initialize variables */
170 memset(&r_opts, 0, sizeof(r_opts));
171 altpath = NULL;
172 null_terminated = 0;
173 warn_no_match = 0;
174 request_digest = 0;
175 policyfile = NULL;
176 nerr = 0;
177
178 r_opts.progname = strdup(argv[0]);
179 if (!r_opts.progname) {
180 fprintf(stderr, "%s: Out of memory!\n", argv[0]);
181 exit(-1);
182 }
183 base = basename(r_opts.progname);
184
185 if (!strcmp(base, SETFILES)) {
186 /*
187 * setfiles:
188 * Recursive descent,
189 * Does not expand paths via realpath,
190 * Aborts on errors during the file tree walk,
191 * Try to track inode associations for conflict detection,
192 * Does not follow mounts (sets SELINUX_RESTORECON_XDEV),
193 * Validates all file contexts at init time.
194 */
195 iamrestorecon = 0;
196 r_opts.recurse = SELINUX_RESTORECON_RECURSE;
197 r_opts.userealpath = 0; /* SELINUX_RESTORECON_REALPATH */
198 r_opts.abort_on_error = SELINUX_RESTORECON_ABORT_ON_ERROR;
199 r_opts.add_assoc = SELINUX_RESTORECON_ADD_ASSOC;
200 /* FTS_PHYSICAL and FTS_NOCHDIR are always set by selinux_restorecon(3) */
201 r_opts.xdev = SELINUX_RESTORECON_XDEV;
202 r_opts.ignore_mounts = 0; /* SELINUX_RESTORECON_IGNORE_MOUNTS */
203 ctx_validate = 1;
204 opts = sopts;
205 } else {
206 /*
207 * restorecon:
208 * No recursive descent unless -r/-R,
209 * Expands paths via realpath,
210 * Do not abort on errors during the file tree walk,
211 * Do not try to track inode associations for conflict detection,
212 * Follows mounts,
213 * Does lazy validation of contexts upon use.
214 */
215 if (strcmp(base, RESTORECON))
216 fprintf(stderr, "Executed with unrecognized name (%s), defaulting to %s behavior.\n",
217 base, RESTORECON);
218
219 iamrestorecon = 1;
220 r_opts.recurse = 0;
221 r_opts.userealpath = SELINUX_RESTORECON_REALPATH;
222 r_opts.abort_on_error = 0;
223 r_opts.add_assoc = 0;
224 r_opts.xdev = 0;
225 r_opts.ignore_mounts = 0;
226 ctx_validate = 0;
227 opts = ropts;
228
229 /* restorecon only: silent exit if no SELinux.
230 * Allows unconditional execution by scripts.
231 */
232 if (is_selinux_enabled() <= 0)
233 exit(0);
234 }
235
236 /* Process any options. */
237 while ((opt = getopt(argc, argv, opts)) > 0) {
238 switch (opt) {
239 case 'c':
240 {
241 FILE *policystream;
242
243 if (iamrestorecon)
244 usage(argv[0]);
245
246 policyfile = optarg;
247
248 policystream = fopen(policyfile, "r");
249 if (!policystream) {
250 fprintf(stderr,
251 "Error opening %s: %s\n",
252 policyfile, strerror(errno));
253 exit(-1);
254 }
255 __fsetlocking(policystream,
256 FSETLOCKING_BYCALLER);
257
258 if (sepol_set_policydb_from_file(policystream)
259 < 0) {
260 fprintf(stderr,
261 "Error reading policy %s: %s\n",
262 policyfile, strerror(errno));
263 exit(-1);
264 }
265 fclose(policystream);
266
267 ctx_validate = 1;
268 break;
269 }
270 case 'e':
271 if (lstat(optarg, &sb) < 0 && errno != EACCES) {
272 fprintf(stderr, "Can't stat exclude path \"%s\", %s - ignoring.\n",
273 optarg, strerror(errno));
274 break;
275 }
276 add_exclude(optarg);
277 break;
278 case 'f':
279 use_input_file = 1;
280 input_filename = optarg;
281 break;
282 case 'd':
283 if (iamrestorecon)
284 usage(argv[0]);
285 r_opts.debug = 1;
286 r_opts.log_matches =
287 SELINUX_RESTORECON_LOG_MATCHES;
288 break;
289 case 'i':
290 r_opts.ignore_noent =
291 SELINUX_RESTORECON_IGNORE_NOENTRY;
292 break;
293 case 'I': /* Force label check by ignoring directory digest. */
294 r_opts.ignore_digest =
295 SELINUX_RESTORECON_IGNORE_DIGEST;
296 request_digest = 1;
297 break;
298 case 'D': /*
299 * Request file_contexts digest in selabel_open
300 * This will effectively enable usage of the
301 * security.restorecon_last extended attribute.
302 */
303 request_digest = 1;
304 break;
305 case 'l':
306 r_opts.syslog_changes =
307 SELINUX_RESTORECON_SYSLOG_CHANGES;
308 break;
309 case 'F':
310 r_opts.set_specctx =
311 SELINUX_RESTORECON_SET_SPECFILE_CTX;
312 break;
313 case 'm':
314 r_opts.ignore_mounts =
315 SELINUX_RESTORECON_IGNORE_MOUNTS;
316 break;
317 case 'n':
318 r_opts.nochange = SELINUX_RESTORECON_NOCHANGE;
319 break;
320 case 'o': /* Deprecated */
321 fprintf(stderr, "%s: -o option no longer supported\n",
322 r_opts.progname);
323 break;
324 case 'q':
325 /* Deprecated - Was only used to say whether print
326 * filespec_eval() params. Now uses verbose flag.
327 */
328 break;
329 case 'R':
330 case 'r':
331 if (iamrestorecon) {
332 r_opts.recurse = SELINUX_RESTORECON_RECURSE;
333 break;
334 }
335
336 if (lstat(optarg, &sb) < 0 && errno != EACCES) {
337 fprintf(stderr,
338 "Can't stat alt_root_path \"%s\", %s\n",
339 optarg, strerror(errno));
340 exit(-1);
341 }
342
343 if (r_opts.rootpath) {
344 fprintf(stderr,
345 "%s: only one -r can be specified\n",
346 argv[0]);
347 exit(-1);
348 }
349 set_rootpath(optarg);
350 break;
351 case 's':
352 use_input_file = 1;
353 input_filename = "-";
354 r_opts.add_assoc = 0;
355 break;
356 case 'v':
357 if (r_opts.progress) {
358 fprintf(stderr,
359 "Progress and Verbose mutually exclusive\n");
360 usage(argv[0]);
361 }
362 r_opts.verbose = SELINUX_RESTORECON_VERBOSE;
363 break;
364 case 'p':
365 if (r_opts.verbose) {
366 fprintf(stderr,
367 "Progress and Verbose mutually exclusive\n");
368 usage(argv[0]);
369 }
370 r_opts.progress = SELINUX_RESTORECON_PROGRESS;
371 break;
372 case 'W':
373 warn_no_match = 1; /* Print selabel_stats() */
374 break;
375 case '0':
376 null_terminated = 1;
377 break;
378 case 'h':
379 case '?':
380 usage(argv[0]);
381 }
382 }
383
384 for (i = optind; i < argc; i++) {
385 if (!strcmp(argv[i], "/"))
386 r_opts.mass_relabel = SELINUX_RESTORECON_MASS_RELABEL;
387 }
388
389 cb.func_log = log_callback;
390 selinux_set_callback(SELINUX_CB_LOG, cb);
391
392 if (!iamrestorecon) {
393 if (policyfile) {
394 if (optind != (argc - 1))
395 usage(argv[0]);
396 } else if (use_input_file) {
397 if (optind != (argc - 1)) {
398 /* Cannot mix with pathname arguments. */
399 usage(argv[0]);
400 }
401 } else {
402 if (optind > (argc - 2))
403 usage(argv[0]);
404 }
405
406 /* Use our own invalid context checking function so that
407 * we can support either checking against the active policy or
408 * checking against a binary policy file.
409 */
410 cb.func_validate = canoncon;
411 selinux_set_callback(SELINUX_CB_VALIDATE, cb);
412
413 if (stat(argv[optind], &sb) < 0) {
414 perror(argv[optind]);
415 exit(-1);
416 }
417 if (!S_ISREG(sb.st_mode)) {
418 fprintf(stderr, "%s: spec file %s is not a regular file.\n",
419 argv[0], argv[optind]);
420 exit(-1);
421 }
422
423 altpath = argv[optind];
424 optind++;
425 } else if (argc == 1)
426 usage(argv[0]);
427
428 /* Set selabel_open options. */
429 r_opts.selabel_opt_validate = (ctx_validate ? (char *)1 : NULL);
430 r_opts.selabel_opt_digest = (request_digest ? (char *)1 : NULL);
431 r_opts.selabel_opt_path = altpath;
432
433 if (nerr)
434 exit(-1);
435
436 restore_init(&r_opts);
437
438 if (use_input_file) {
439 FILE *f = stdin;
440 ssize_t len;
441 int delim;
442
443 if (strcmp(input_filename, "-") != 0)
444 f = fopen(input_filename, "r");
445
446 if (f == NULL) {
447 fprintf(stderr, "Unable to open %s: %s\n",
448 input_filename,
449 strerror(errno));
450 usage(argv[0]);
451 }
452 __fsetlocking(f, FSETLOCKING_BYCALLER);
453
454 delim = (null_terminated != 0) ? '\0' : '\n';
455 while ((len = getdelim(&buf, &buf_len, delim, f)) > 0) {
456 buf[len - 1] = 0;
457 if (!strcmp(buf, "/"))
458 r_opts.mass_relabel = SELINUX_RESTORECON_MASS_RELABEL;
459 errors |= process_glob(buf, &r_opts) < 0;
460 }
461 if (strcmp(input_filename, "-") != 0)
462 fclose(f);
463 } else {
464 for (i = optind; i < argc; i++)
465 errors |= process_glob(argv[i], &r_opts) < 0;
466 }
467
468 maybe_audit_mass_relabel(r_opts.mass_relabel, errors);
469
470 if (warn_no_match)
471 selabel_stats(r_opts.hnd);
472
473 selabel_close(r_opts.hnd);
474 restore_finish();
475
476 if (r_opts.progress)
477 fprintf(stdout, "\n");
478
479 exit(errors ? -1 : 0);
480 }
481