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