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