build: perform check-AUTHORS via syntax-check, not via "make check"
[coreutils.git] / src / chcon.c
blobf5b5f22df728403e4e6260cfaf811a3641ac83b0
1 /* chcon -- change security context of files
2 Copyright (C) 2005-2009 Free Software Foundation, Inc.
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.
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.
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/>. */
17 #include <config.h>
18 #include <stdio.h>
19 #include <sys/types.h>
20 #include <getopt.h>
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"
31 /* The official name of this program (e.g., no `g' prefix). */
32 #define PROGRAM_NAME "chcon"
34 #define AUTHORS \
35 proper_name ("Russell Coker"), \
36 proper_name ("Jim Meyering")
38 /* If nonzero, and the systems has support for it, change the context
39 of symbolic links rather than any files they point to. */
40 static bool affect_symlink_referent;
42 /* If true, change the modes of directories recursively. */
43 static bool recurse;
45 /* Level of verbosity. */
46 static bool verbose;
48 /* Pointer to the device and inode numbers of `/', when --recursive.
49 Otherwise NULL. */
50 static struct dev_ino *root_dev_ino;
52 /* The name of the context file is being given. */
53 static char const *specified_context;
55 /* Specific components of the context */
56 static char const *specified_user;
57 static char const *specified_role;
58 static char const *specified_range;
59 static char const *specified_type;
61 /* For long options that have no equivalent short option, use a
62 non-character as a pseudo short option, starting with CHAR_MAX + 1. */
63 enum
65 DEREFERENCE_OPTION = CHAR_MAX + 1,
66 NO_PRESERVE_ROOT,
67 PRESERVE_ROOT,
68 REFERENCE_FILE_OPTION
71 static struct option const long_options[] =
73 {"recursive", no_argument, NULL, 'R'},
74 {"dereference", no_argument, NULL, DEREFERENCE_OPTION},
75 {"no-dereference", no_argument, NULL, 'h'},
76 {"no-preserve-root", no_argument, NULL, NO_PRESERVE_ROOT},
77 {"preserve-root", no_argument, NULL, PRESERVE_ROOT},
78 {"reference", required_argument, NULL, REFERENCE_FILE_OPTION},
79 {"user", required_argument, NULL, 'u'},
80 {"role", required_argument, NULL, 'r'},
81 {"type", required_argument, NULL, 't'},
82 {"range", required_argument, NULL, 'l'},
83 {"verbose", no_argument, NULL, 'v'},
84 {GETOPT_HELP_OPTION_DECL},
85 {GETOPT_VERSION_OPTION_DECL},
86 {NULL, 0, NULL, 0}
89 /* Given a security context, CONTEXT, derive a context_t (*RET),
90 setting any portions selected via the global variables, specified_user,
91 specified_role, etc. */
92 static int
93 compute_context_from_mask (security_context_t context, context_t *ret)
95 bool ok = true;
96 context_t new_context = context_new (context);
97 if (!new_context)
99 error (0, errno, _("failed to create security context: %s"),
100 quotearg_colon (context));
101 return 1;
104 #define SET_COMPONENT(C, comp) \
105 do \
107 if (specified_ ## comp \
108 && context_ ## comp ## _set ((C), specified_ ## comp)) \
110 error (0, errno, \
111 _("failed to set %s security context component to %s"), \
112 #comp, quote (specified_ ## comp)); \
113 ok = false; \
116 while (0)
118 SET_COMPONENT (new_context, user);
119 SET_COMPONENT (new_context, range);
120 SET_COMPONENT (new_context, role);
121 SET_COMPONENT (new_context, type);
123 if (!ok)
125 int saved_errno = errno;
126 context_free (new_context);
127 errno = saved_errno;
128 return 1;
131 *ret = new_context;
132 return 0;
135 /* Change the context of FILE, using specified components.
136 If it is a directory and -R is given, recurse.
137 Return 0 if successful, 1 if errors occurred. */
139 static int
140 change_file_context (int fd, char const *file)
142 security_context_t file_context = NULL;
143 context_t context;
144 security_context_t context_string;
145 int errors = 0;
147 if (specified_context == NULL)
149 int status = (affect_symlink_referent
150 ? getfileconat (fd, file, &file_context)
151 : lgetfileconat (fd, file, &file_context));
153 if (status < 0 && errno != ENODATA)
155 error (0, errno, _("failed to get security context of %s"),
156 quote (file));
157 return 1;
160 /* If the file doesn't have a context, and we're not setting all of
161 the context components, there isn't really an obvious default.
162 Thus, we just give up. */
163 if (file_context == NULL)
165 error (0, 0, _("can't apply partial context to unlabeled file %s"),
166 quote (file));
167 return 1;
170 if (compute_context_from_mask (file_context, &context))
171 return 1;
173 else
175 /* FIXME: this should be done exactly once, in main. */
176 context = context_new (specified_context);
177 if (!context)
178 abort ();
181 context_string = context_str (context);
183 if (file_context == NULL || ! STREQ (context_string, file_context))
185 int fail = (affect_symlink_referent
186 ? setfileconat (fd, file, context_string)
187 : lsetfileconat (fd, file, context_string));
189 if (fail)
191 errors = 1;
192 error (0, errno, _("failed to change context of %s to %s"),
193 quote_n (0, file), quote_n (1, context_string));
197 context_free (context);
198 freecon (file_context);
200 return errors;
203 /* Change the context of FILE.
204 Return true if successful. This function is called
205 once for every file system object that fts encounters. */
207 static bool
208 process_file (FTS *fts, FTSENT *ent)
210 char const *file_full_name = ent->fts_path;
211 char const *file = ent->fts_accpath;
212 const struct stat *file_stats = ent->fts_statp;
213 bool ok = true;
215 switch (ent->fts_info)
217 case FTS_D:
218 if (recurse)
220 if (ROOT_DEV_INO_CHECK (root_dev_ino, ent->fts_statp))
222 /* This happens e.g., with "chcon -R --preserve-root ... /"
223 and with "chcon -RH --preserve-root ... symlink-to-root". */
224 ROOT_DEV_INO_WARN (file_full_name);
225 /* Tell fts not to traverse into this hierarchy. */
226 fts_set (fts, ent, FTS_SKIP);
227 /* Ensure that we do not process "/" on the second visit. */
228 ent = fts_read (fts);
229 return false;
231 return true;
233 break;
235 case FTS_DP:
236 if (! recurse)
237 return true;
238 break;
240 case FTS_NS:
241 /* For a top-level file or directory, this FTS_NS (stat failed)
242 indicator is determined at the time of the initial fts_open call.
243 With programs like chmod, chown, and chgrp, that modify
244 permissions, it is possible that the file in question is
245 accessible when control reaches this point. So, if this is
246 the first time we've seen the FTS_NS for this file, tell
247 fts_read to stat it "again". */
248 if (ent->fts_level == 0 && ent->fts_number == 0)
250 ent->fts_number = 1;
251 fts_set (fts, ent, FTS_AGAIN);
252 return true;
254 error (0, ent->fts_errno, _("cannot access %s"), quote (file_full_name));
255 ok = false;
256 break;
258 case FTS_ERR:
259 error (0, ent->fts_errno, _("%s"), quote (file_full_name));
260 ok = false;
261 break;
263 case FTS_DNR:
264 error (0, ent->fts_errno, _("cannot read directory %s"),
265 quote (file_full_name));
266 ok = false;
267 break;
269 default:
270 break;
273 if (ent->fts_info == FTS_DP
274 && ok && ROOT_DEV_INO_CHECK (root_dev_ino, file_stats))
276 ROOT_DEV_INO_WARN (file_full_name);
277 ok = false;
280 if (ok)
282 if (verbose)
283 printf (_("changing security context of %s\n"),
284 quote (file_full_name));
286 if (change_file_context (fts->fts_cwd_fd, file) != 0)
287 ok = false;
290 if ( ! recurse)
291 fts_set (fts, ent, FTS_SKIP);
293 return ok;
296 /* Recursively operate on the specified FILES (the last entry
297 of which is NULL). BIT_FLAGS controls how fts works.
298 Return true if successful. */
300 static bool
301 process_files (char **files, int bit_flags)
303 bool ok = true;
305 FTS *fts = xfts_open (files, bit_flags, NULL);
307 while (1)
309 FTSENT *ent;
311 ent = fts_read (fts);
312 if (ent == NULL)
314 if (errno != 0)
316 /* FIXME: try to give a better message */
317 error (0, errno, _("fts_read failed"));
318 ok = false;
320 break;
323 ok &= process_file (fts, ent);
326 /* Ignore failure, since the only way it can do so is in failing to
327 return to the original directory, and since we're about to exit,
328 that doesn't matter. */
329 fts_close (fts);
331 return ok;
334 void
335 usage (int status)
337 if (status != EXIT_SUCCESS)
338 fprintf (stderr, _("Try `%s --help' for more information.\n"),
339 program_name);
340 else
342 printf (_("\
343 Usage: %s [OPTION]... CONTEXT FILE...\n\
344 or: %s [OPTION]... [-u USER] [-r ROLE] [-l RANGE] [-t TYPE] FILE...\n\
345 or: %s [OPTION]... --reference=RFILE FILE...\n\
347 program_name, program_name, program_name);
348 fputs (_("\
349 Change the security context of each FILE to CONTEXT.\n\
350 With --reference, change the security context of each FILE to that of RFILE.\n\
352 -h, --no-dereference affect symbolic links instead of any referenced file\n\
353 "), stdout);
354 fputs (_("\
355 --reference=RFILE use RFILE's security context rather than specifying\n\
356 a CONTEXT value\n\
357 -R, --recursive operate on files and directories recursively\n\
358 -v, --verbose output a diagnostic for every file processed\n\
359 "), stdout);
360 fputs (_("\
361 -u, --user=USER set user USER in the target security context\n\
362 -r, --role=ROLE set role ROLE in the target security context\n\
363 -t, --type=TYPE set type TYPE in the target security context\n\
364 -l, --range=RANGE set range RANGE in the target security context\n\
366 "), stdout);
367 fputs (_("\
368 The following options modify how a hierarchy is traversed when the -R\n\
369 option is also specified. If more than one is specified, only the final\n\
370 one takes effect.\n\
372 -H if a command line argument is a symbolic link\n\
373 to a directory, traverse it\n\
374 -L traverse every symbolic link to a directory\n\
375 encountered\n\
376 -P do not traverse any symbolic links (default)\n\
378 "), stdout);
379 fputs (HELP_OPTION_DESCRIPTION, stdout);
380 fputs (VERSION_OPTION_DESCRIPTION, stdout);
381 emit_bug_reporting_address ();
383 exit (status);
387 main (int argc, char **argv)
389 security_context_t ref_context = NULL;
391 /* Bit flags that control how fts works. */
392 int bit_flags = FTS_PHYSICAL;
394 /* 1 if --dereference, 0 if --no-dereference, -1 if neither has been
395 specified. */
396 int dereference = -1;
398 bool ok;
399 bool preserve_root = false;
400 bool component_specified = false;
401 char *reference_file = NULL;
402 int optc;
404 initialize_main (&argc, &argv);
405 set_program_name (argv[0]);
406 setlocale (LC_ALL, "");
407 bindtextdomain (PACKAGE, LOCALEDIR);
408 textdomain (PACKAGE);
410 atexit (close_stdout);
412 while ((optc = getopt_long (argc, argv, "HLPRhvu:r:t:l:", long_options, NULL))
413 != -1)
415 switch (optc)
417 case 'H': /* Traverse command-line symlinks-to-directories. */
418 bit_flags = FTS_COMFOLLOW | FTS_PHYSICAL;
419 break;
421 case 'L': /* Traverse all symlinks-to-directories. */
422 bit_flags = FTS_LOGICAL;
423 break;
425 case 'P': /* Traverse no symlinks-to-directories. */
426 bit_flags = FTS_PHYSICAL;
427 break;
429 case 'h': /* --no-dereference: affect symlinks */
430 dereference = 0;
431 break;
433 case DEREFERENCE_OPTION: /* --dereference: affect the referent
434 of each symlink */
435 dereference = 1;
436 break;
438 case NO_PRESERVE_ROOT:
439 preserve_root = false;
440 break;
442 case PRESERVE_ROOT:
443 preserve_root = true;
444 break;
446 case REFERENCE_FILE_OPTION:
447 reference_file = optarg;
448 break;
450 case 'R':
451 recurse = true;
452 break;
454 case 'f':
455 /* ignore */
456 break;
458 case 'v':
459 verbose = true;
460 break;
462 case 'u':
463 specified_user = optarg;
464 component_specified = true;
465 break;
467 case 'r':
468 specified_role = optarg;
469 component_specified = true;
470 break;
472 case 't':
473 specified_type = optarg;
474 component_specified = true;
475 break;
477 case 'l':
478 specified_range = optarg;
479 component_specified = true;
480 break;
482 case_GETOPT_HELP_CHAR;
483 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
484 default:
485 usage (EXIT_FAILURE);
489 if (recurse)
491 if (bit_flags == FTS_PHYSICAL)
493 if (dereference == 1)
494 error (EXIT_FAILURE, 0,
495 _("-R --dereference requires either -H or -L"));
496 affect_symlink_referent = false;
498 else
500 if (dereference == 0)
501 error (EXIT_FAILURE, 0, _("-R -h requires -P"));
502 affect_symlink_referent = true;
505 else
507 bit_flags = FTS_PHYSICAL;
508 affect_symlink_referent = (dereference != 0);
511 if (argc - optind < (reference_file || component_specified ? 1 : 2))
513 if (argc <= optind)
514 error (0, 0, _("missing operand"));
515 else
516 error (0, 0, _("missing operand after %s"), quote (argv[argc - 1]));
517 usage (EXIT_FAILURE);
520 if (reference_file)
522 if (getfilecon (reference_file, &ref_context) < 0)
523 error (EXIT_FAILURE, errno, _("failed to get security context of %s"),
524 quote (reference_file));
526 specified_context = ref_context;
528 else if (component_specified)
530 /* FIXME: it's already null, so this is a no-op. */
531 specified_context = NULL;
533 else
535 context_t context;
536 specified_context = argv[optind++];
537 context = context_new (specified_context);
538 if (!context)
539 error (EXIT_FAILURE, 0, _("invalid context: %s"),
540 quotearg_colon (specified_context));
541 context_free (context);
544 if (reference_file && component_specified)
546 error (0, 0, _("conflicting security context specifiers given"));
547 usage (1);
550 if (recurse & preserve_root)
552 static struct dev_ino dev_ino_buf;
553 root_dev_ino = get_root_dev_ino (&dev_ino_buf);
554 if (root_dev_ino == NULL)
555 error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
556 quote ("/"));
558 else
560 root_dev_ino = NULL;
563 ok = process_files (argv + optind, bit_flags | FTS_NOSTAT);
565 exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);