Line data Source code
1 : /* chcon -- change security context of files
2 : Copyright (C) 2005-2008 Free Software Foundation, Inc.
3 :
4 : This program is free software: you can redistribute it and/or modify
5 : it under the terms of the GNU General Public License as published by
6 : the Free Software Foundation, either version 3 of the License, or
7 : (at your option) any later version.
8 :
9 : This program is distributed in the hope that it will be useful,
10 : but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : GNU General Public License for more details.
13 :
14 : You should have received a copy of the GNU General Public License
15 : along with this program. If not, see <http://www.gnu.org/licenses/>. */
16 :
17 : #include <config.h>
18 : #include <stdio.h>
19 : #include <sys/types.h>
20 : #include <getopt.h>
21 :
22 : #include "system.h"
23 : #include "dev-ino.h"
24 : #include "error.h"
25 : #include "quote.h"
26 : #include "quotearg.h"
27 : #include "root-dev-ino.h"
28 : #include "selinux-at.h"
29 : #include "xfts.h"
30 :
31 : /* The official name of this program (e.g., no `g' prefix). */
32 : #define PROGRAM_NAME "chcon"
33 :
34 : #define AUTHORS "Russell Coker", "Jim Meyering"
35 :
36 : enum Change_status
37 : {
38 : CH_NOT_APPLIED,
39 : CH_SUCCEEDED,
40 : CH_FAILED,
41 : CH_NO_CHANGE_REQUESTED
42 : };
43 :
44 : enum Verbosity
45 : {
46 : /* Print a message for each file that is processed. */
47 : V_high,
48 :
49 : /* Print a message for each file whose attributes we change. */
50 : V_changes_only,
51 :
52 : /* Do not be verbose. This is the default. */
53 : V_off
54 : };
55 :
56 : /* The name the program was run with. */
57 : char *program_name;
58 :
59 : /* If nonzero, and the systems has support for it, change the context
60 : of symbolic links rather than any files they point to. */
61 : static bool affect_symlink_referent;
62 :
63 : /* If true, change the modes of directories recursively. */
64 : static bool recurse;
65 :
66 : /* Level of verbosity. */
67 : static bool verbose;
68 :
69 : /* Pointer to the device and inode numbers of `/', when --recursive.
70 : Otherwise NULL. */
71 : static struct dev_ino *root_dev_ino;
72 :
73 : /* The name of the context file is being given. */
74 : static char const *specified_context;
75 :
76 : /* Specific components of the context */
77 : static char const *specified_user;
78 : static char const *specified_role;
79 : static char const *specified_range;
80 : static char const *specified_type;
81 :
82 : /* For long options that have no equivalent short option, use a
83 : non-character as a pseudo short option, starting with CHAR_MAX + 1. */
84 : enum
85 : {
86 : DEREFERENCE_OPTION = CHAR_MAX + 1,
87 : NO_PRESERVE_ROOT,
88 : PRESERVE_ROOT,
89 : REFERENCE_FILE_OPTION
90 : };
91 :
92 : static struct option const long_options[] =
93 : {
94 : {"recursive", no_argument, NULL, 'R'},
95 : {"dereference", no_argument, NULL, DEREFERENCE_OPTION},
96 : {"no-dereference", no_argument, NULL, 'h'},
97 : {"no-preserve-root", no_argument, NULL, NO_PRESERVE_ROOT},
98 : {"preserve-root", no_argument, NULL, PRESERVE_ROOT},
99 : {"reference", required_argument, NULL, REFERENCE_FILE_OPTION},
100 : {"user", required_argument, NULL, 'u'},
101 : {"role", required_argument, NULL, 'r'},
102 : {"type", required_argument, NULL, 't'},
103 : {"range", required_argument, NULL, 'l'},
104 : {"verbose", no_argument, NULL, 'v'},
105 : {GETOPT_HELP_OPTION_DECL},
106 : {GETOPT_VERSION_OPTION_DECL},
107 : {NULL, 0, NULL, 0}
108 : };
109 :
110 : /* Given a security context, CONTEXT, derive a context_t (*RET),
111 : setting any portions selected via the global variables, specified_user,
112 : specified_role, etc. */
113 : static int
114 0 : compute_context_from_mask (security_context_t context, context_t *ret)
115 : {
116 0 : bool ok = true;
117 0 : context_t new_context = context_new (context);
118 0 : if (!new_context)
119 : {
120 0 : error (0, errno, _("failed to create security context: %s"),
121 : quotearg_colon (context));
122 0 : return 1;
123 : }
124 :
125 : #define SET_COMPONENT(C, comp) \
126 : do \
127 : { \
128 : if (specified_ ## comp \
129 : && context_ ## comp ## _set ((C), specified_ ## comp)) \
130 : { \
131 : error (0, errno, \
132 : _("failed to set %s security context component to %s"), \
133 : #comp, quote (specified_ ## comp)); \
134 : ok = false; \
135 : } \
136 : } \
137 : while (0)
138 :
139 0 : SET_COMPONENT (new_context, user);
140 0 : SET_COMPONENT (new_context, range);
141 0 : SET_COMPONENT (new_context, role);
142 0 : SET_COMPONENT (new_context, type);
143 :
144 0 : if (!ok)
145 : {
146 0 : int saved_errno = errno;
147 0 : context_free (new_context);
148 0 : errno = saved_errno;
149 0 : return 1;
150 : }
151 :
152 0 : *ret = new_context;
153 0 : return 0;
154 : }
155 :
156 : /* Change the context of FILE, using specified components.
157 : If it is a directory and -R is given, recurse.
158 : Return 0 if successful, 1 if errors occurred. */
159 :
160 : static int
161 133394 : change_file_context (int fd, char const *file)
162 : {
163 133394 : security_context_t file_context = NULL;
164 : context_t context;
165 : security_context_t context_string;
166 133394 : int errors = 0;
167 :
168 133394 : if (specified_context == NULL)
169 : {
170 133394 : int status = (affect_symlink_referent
171 133394 : ? getfileconat (fd, file, &file_context)
172 133394 : : lgetfileconat (fd, file, &file_context));
173 :
174 133394 : if (status < 0 && errno != ENODATA)
175 : {
176 133394 : error (0, errno, _("failed to get security context of %s"),
177 : quote (file));
178 133394 : return 1;
179 : }
180 :
181 : /* If the file doesn't have a context, and we're not setting all of
182 : the context components, there isn't really an obvious default.
183 : Thus, we just give up. */
184 0 : if (file_context == NULL)
185 : {
186 0 : error (0, 0, _("can't apply partial context to unlabeled file %s"),
187 : quote (file));
188 0 : return 1;
189 : }
190 :
191 0 : if (compute_context_from_mask (file_context, &context))
192 0 : return 1;
193 : }
194 : else
195 : {
196 : /* FIXME: this should be done exactly once, in main. */
197 0 : context = context_new (specified_context);
198 0 : if (!context)
199 0 : abort ();
200 : }
201 :
202 0 : context_string = context_str (context);
203 :
204 0 : if (file_context == NULL || ! STREQ (context_string, file_context))
205 : {
206 0 : int fail = (affect_symlink_referent
207 0 : ? setfileconat (fd, file, context_string)
208 0 : : lsetfileconat (fd, file, context_string));
209 :
210 0 : if (fail)
211 : {
212 0 : errors = 1;
213 0 : error (0, errno, _("failed to change context of %s to %s"),
214 : quote_n (0, file), quote_n (1, context_string));
215 : }
216 : }
217 :
218 0 : context_free (context);
219 0 : freecon (file_context);
220 :
221 0 : return errors;
222 : }
223 :
224 : /* Change the context of FILE.
225 : Return true if successful. This function is called
226 : once for every file system object that fts encounters. */
227 :
228 : static bool
229 146689 : process_file (FTS *fts, FTSENT *ent)
230 : {
231 146689 : char const *file_full_name = ent->fts_path;
232 146689 : char const *file = ent->fts_accpath;
233 146689 : const struct stat *file_stats = ent->fts_statp;
234 146689 : bool ok = true;
235 :
236 146689 : switch (ent->fts_info)
237 : {
238 13266 : case FTS_D:
239 13266 : if (recurse)
240 : {
241 13256 : if (ROOT_DEV_INO_CHECK (root_dev_ino, ent->fts_statp))
242 : {
243 : /* This happens e.g., with "chcon -R --preserve-root ... /"
244 : and with "chcon -RH --preserve-root ... symlink-to-root". */
245 0 : ROOT_DEV_INO_WARN (file_full_name);
246 : /* Tell fts not to traverse into this hierarchy. */
247 0 : fts_set (fts, ent, FTS_SKIP);
248 : /* Ensure that we do not process "/" on the second visit. */
249 0 : ent = fts_read (fts);
250 0 : return false;
251 : }
252 13256 : return true;
253 : }
254 10 : break;
255 :
256 13259 : case FTS_DP:
257 13259 : if (! recurse)
258 10 : return true;
259 13249 : break;
260 :
261 22 : case FTS_NS:
262 : /* For a top-level file or directory, this FTS_NS (stat failed)
263 : indicator is determined at the time of the initial fts_open call.
264 : With programs like chmod, chown, and chgrp, that modify
265 : permissions, it is possible that the file in question is
266 : accessible when control reaches this point. So, if this is
267 : the first time we've seen the FTS_NS for this file, tell
268 : fts_read to stat it "again". */
269 22 : if (ent->fts_level == 0 && ent->fts_number == 0)
270 : {
271 11 : ent->fts_number = 1;
272 11 : fts_set (fts, ent, FTS_AGAIN);
273 11 : return true;
274 : }
275 11 : error (0, ent->fts_errno, _("cannot access %s"), quote (file_full_name));
276 11 : ok = false;
277 11 : break;
278 :
279 0 : case FTS_ERR:
280 0 : error (0, ent->fts_errno, _("%s"), quote (file_full_name));
281 0 : ok = false;
282 0 : break;
283 :
284 7 : case FTS_DNR:
285 7 : error (0, ent->fts_errno, _("cannot read directory %s"),
286 : quote (file_full_name));
287 7 : ok = false;
288 7 : break;
289 :
290 120135 : default:
291 120135 : break;
292 : }
293 :
294 133412 : if (ent->fts_info == FTS_DP
295 13249 : && ok && ROOT_DEV_INO_CHECK (root_dev_ino, file_stats))
296 : {
297 0 : ROOT_DEV_INO_WARN (file_full_name);
298 0 : ok = false;
299 : }
300 :
301 133412 : if (ok)
302 : {
303 133394 : if (verbose)
304 0 : printf (_("changing security context of %s"),
305 : quote (file_full_name));
306 :
307 133394 : if (change_file_context (fts->fts_cwd_fd, file) != 0)
308 133394 : ok = false;
309 : }
310 :
311 133412 : if ( ! recurse)
312 24 : fts_set (fts, ent, FTS_SKIP);
313 :
314 133412 : return ok;
315 : }
316 :
317 : /* Recursively operate on the specified FILES (the last entry
318 : of which is NULL). BIT_FLAGS controls how fts works.
319 : Return true if successful. */
320 :
321 : static bool
322 25 : process_files (char **files, int bit_flags)
323 : {
324 25 : bool ok = true;
325 :
326 25 : FTS *fts = xfts_open (files, bit_flags, NULL);
327 :
328 : while (1)
329 146689 : {
330 : FTSENT *ent;
331 :
332 146711 : ent = fts_read (fts);
333 146711 : if (ent == NULL)
334 : {
335 22 : if (errno != 0)
336 : {
337 : /* FIXME: try to give a better message */
338 0 : error (0, errno, _("fts_read failed"));
339 0 : ok = false;
340 : }
341 22 : break;
342 : }
343 :
344 146689 : ok &= process_file (fts, ent);
345 : }
346 :
347 : /* Ignore failure, since the only way it can do so is in failing to
348 : return to the original directory, and since we're about to exit,
349 : that doesn't matter. */
350 22 : fts_close (fts);
351 :
352 22 : return ok;
353 : }
354 :
355 : void
356 38 : usage (int status)
357 : {
358 38 : if (status != EXIT_SUCCESS)
359 37 : fprintf (stderr, _("Try `%s --help' for more information.\n"),
360 : program_name);
361 : else
362 : {
363 1 : printf (_("\
364 : Usage: %s [OPTION]... CONTEXT FILE...\n\
365 : or: %s [OPTION]... [-u USER] [-r ROLE] [-l RANGE] [-t TYPE] FILE...\n\
366 : or: %s [OPTION]... --reference=RFILE FILE...\n\
367 : "),
368 : program_name, program_name, program_name);
369 1 : fputs (_("\
370 : Change the security context of each FILE to CONTEXT.\n\
371 : With --reference, change the security context of each FILE to that of RFILE.\n\
372 : \n\
373 : -c, --changes like verbose but report only when a change is made\n\
374 : -h, --no-dereference affect symbolic links instead of any referenced file\n\
375 : "), stdout);
376 1 : fputs (_("\
377 : --reference=RFILE use RFILE's security context rather than specifying\n\
378 : a CONTEXT value\n\
379 : -R, --recursive operate on files and directories recursively\n\
380 : -v, --verbose output a diagnostic for every file processed\n\
381 : "), stdout);
382 1 : fputs (_("\
383 : -u, --user=USER set user USER in the target security context\n\
384 : -r, --role=ROLE set role ROLE in the target security context\n\
385 : -t, --type=TYPE set type TYPE in the target security context\n\
386 : -l, --range=RANGE set range RANGE in the target security context\n\
387 : \n\
388 : "), stdout);
389 1 : fputs (_("\
390 : The following options modify how a hierarchy is traversed when the -R\n\
391 : option is also specified. If more than one is specified, only the final\n\
392 : one takes effect.\n\
393 : \n\
394 : -H if a command line argument is a symbolic link\n\
395 : to a directory, traverse it\n\
396 : -L traverse every symbolic link to a directory\n\
397 : encountered\n\
398 : -P do not traverse any symbolic links (default)\n\
399 : \n\
400 : "), stdout);
401 1 : fputs (HELP_OPTION_DESCRIPTION, stdout);
402 1 : fputs (VERSION_OPTION_DESCRIPTION, stdout);
403 : }
404 38 : exit (status);
405 : }
406 :
407 : int
408 83 : main (int argc, char **argv)
409 : {
410 83 : security_context_t ref_context = NULL;
411 :
412 : /* Bit flags that control how fts works. */
413 83 : int bit_flags = FTS_PHYSICAL;
414 :
415 : /* 1 if --dereference, 0 if --no-dereference, -1 if neither has been
416 : specified. */
417 83 : int dereference = -1;
418 :
419 : bool ok;
420 83 : bool preserve_root = false;
421 83 : bool component_specified = false;
422 83 : char *reference_file = NULL;
423 : int optc;
424 :
425 : initialize_main (&argc, &argv);
426 83 : program_name = argv[0];
427 83 : setlocale (LC_ALL, "");
428 : bindtextdomain (PACKAGE, LOCALEDIR);
429 : textdomain (PACKAGE);
430 :
431 83 : atexit (close_stdout);
432 :
433 221 : while ((optc = getopt_long (argc, argv, "HLPRchvu:r:t:l:", long_options, NULL))
434 : != -1)
435 : {
436 67 : switch (optc)
437 : {
438 5 : case 'H': /* Traverse command-line symlinks-to-directories. */
439 5 : bit_flags = FTS_COMFOLLOW | FTS_PHYSICAL;
440 5 : break;
441 :
442 2 : case 'L': /* Traverse all symlinks-to-directories. */
443 2 : bit_flags = FTS_LOGICAL;
444 2 : break;
445 :
446 1 : case 'P': /* Traverse no symlinks-to-directories. */
447 1 : bit_flags = FTS_PHYSICAL;
448 1 : break;
449 :
450 3 : case 'h': /* --no-dereference: affect symlinks */
451 3 : dereference = 0;
452 3 : break;
453 :
454 2 : case DEREFERENCE_OPTION: /* --dereference: affect the referent
455 : of each symlink */
456 2 : dereference = 1;
457 2 : break;
458 :
459 1 : case NO_PRESERVE_ROOT:
460 1 : preserve_root = false;
461 1 : break;
462 :
463 1 : case PRESERVE_ROOT:
464 1 : preserve_root = true;
465 1 : break;
466 :
467 2 : case REFERENCE_FILE_OPTION:
468 2 : reference_file = optarg;
469 2 : break;
470 :
471 7 : case 'R':
472 7 : recurse = true;
473 7 : break;
474 :
475 0 : case 'f':
476 : /* ignore */
477 0 : break;
478 :
479 1 : case 'v':
480 1 : verbose = true;
481 1 : break;
482 :
483 14 : case 'u':
484 14 : specified_user = optarg;
485 14 : component_specified = true;
486 14 : break;
487 :
488 6 : case 'r':
489 6 : specified_role = optarg;
490 6 : component_specified = true;
491 6 : break;
492 :
493 6 : case 't':
494 6 : specified_type = optarg;
495 6 : component_specified = true;
496 6 : break;
497 :
498 4 : case 'l':
499 4 : specified_range = optarg;
500 4 : component_specified = true;
501 4 : break;
502 :
503 1 : case_GETOPT_HELP_CHAR;
504 1 : case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
505 10 : default:
506 10 : usage (EXIT_FAILURE);
507 : }
508 : }
509 :
510 71 : if (recurse)
511 : {
512 7 : if (bit_flags == FTS_PHYSICAL)
513 : {
514 5 : if (dereference == 1)
515 1 : error (EXIT_FAILURE, 0,
516 : _("-R --dereference requires either -H or -L"));
517 4 : affect_symlink_referent = false;
518 : }
519 : else
520 : {
521 2 : if (dereference == 0)
522 1 : error (EXIT_FAILURE, 0, _("-R -h requires -P"));
523 1 : affect_symlink_referent = true;
524 : }
525 : }
526 : else
527 : {
528 64 : bit_flags = FTS_PHYSICAL;
529 64 : affect_symlink_referent = (dereference != 0);
530 : }
531 :
532 69 : if (argc - optind < (reference_file || component_specified ? 1 : 2))
533 : {
534 27 : if (argc <= optind)
535 19 : error (0, 0, _("missing operand"));
536 : else
537 8 : error (0, 0, _("missing operand after %s"), quote (argv[argc - 1]));
538 27 : usage (EXIT_FAILURE);
539 : }
540 :
541 42 : if (reference_file)
542 : {
543 1 : if (getfilecon (reference_file, &ref_context) < 0)
544 1 : error (EXIT_FAILURE, errno, _("failed to get security context of %s"),
545 : quote (reference_file));
546 :
547 0 : specified_context = ref_context;
548 : }
549 41 : else if (component_specified)
550 : {
551 : /* FIXME: it's already null, so this is a no-op. */
552 25 : specified_context = NULL;
553 : }
554 : else
555 : {
556 : context_t context;
557 16 : specified_context = argv[optind++];
558 16 : context = context_new (specified_context);
559 16 : if (!context)
560 16 : error (EXIT_FAILURE, 0, _("invalid context: %s"),
561 : quotearg_colon (specified_context));
562 0 : context_free (context);
563 : }
564 :
565 25 : if (reference_file && component_specified)
566 : {
567 0 : error (0, 0, _("conflicting security context specifiers given"));
568 0 : usage (1);
569 : }
570 :
571 25 : if (recurse & preserve_root)
572 : {
573 : static struct dev_ino dev_ino_buf;
574 0 : root_dev_ino = get_root_dev_ino (&dev_ino_buf);
575 0 : if (root_dev_ino == NULL)
576 0 : error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
577 : quote ("/"));
578 : }
579 : else
580 : {
581 25 : root_dev_ino = NULL;
582 : }
583 :
584 25 : ok = process_files (argv + optind, bit_flags | FTS_NOSTAT);
585 :
586 22 : exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
587 : }
|