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