1 /* chcon -- change security context of files
2 Copyright (C) 2005-2015 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/>. */
19 #include <sys/types.h>
25 #include "ignore-value.h"
27 #include "root-dev-ino.h"
28 #include "selinux-at.h"
31 /* The official name of this program (e.g., no 'g' prefix). */
32 #define PROGRAM_NAME "chcon"
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. */
45 /* Level of verbosity. */
48 /* Pointer to the device and inode numbers of '/', when --recursive.
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. */
65 DEREFERENCE_OPTION
= CHAR_MAX
+ 1,
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
},
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. */
93 compute_context_from_mask (char const *context
, context_t
*ret
)
96 context_t new_context
= context_new (context
);
99 error (0, errno
, _("failed to create security context: %s"),
104 #define SET_COMPONENT(C, comp) \
107 if (specified_ ## comp \
108 && context_ ## comp ## _set ((C), specified_ ## comp)) \
111 _("failed to set %s security context component to %s"), \
112 #comp, quote (specified_ ## comp)); \
118 SET_COMPONENT (new_context
, user
);
119 SET_COMPONENT (new_context
, range
);
120 SET_COMPONENT (new_context
, role
);
121 SET_COMPONENT (new_context
, type
);
125 int saved_errno
= errno
;
126 context_free (new_context
);
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. */
140 change_file_context (int fd
, char const *file
)
142 char *file_context
= NULL
;
143 context_t context
IF_LINT (= 0);
144 char const * context_string
;
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"),
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"),
170 if (compute_context_from_mask (file_context
, &context
))
173 context_string
= context_str (context
);
177 context_string
= specified_context
;
180 if (file_context
== NULL
|| ! STREQ (context_string
, file_context
))
182 int fail
= (affect_symlink_referent
183 ? setfileconat (fd
, file
, se_const (context_string
))
184 : lsetfileconat (fd
, file
, se_const (context_string
)));
189 error (0, errno
, _("failed to change context of %s to %s"),
190 quote_n (0, file
), quote_n (1, context_string
));
194 if (specified_context
== NULL
)
196 context_free (context
);
197 freecon (file_context
);
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. */
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
;
215 switch (ent
->fts_info
)
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 ignore_value (fts_read (fts
));
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)
251 fts_set (fts
, ent
, FTS_AGAIN
);
254 error (0, ent
->fts_errno
, _("cannot access %s"), quote (file_full_name
));
259 error (0, ent
->fts_errno
, "%s", quote (file_full_name
));
264 error (0, ent
->fts_errno
, _("cannot read directory %s"),
265 quote (file_full_name
));
269 case FTS_DC
: /* directory that causes cycles */
270 if (cycle_warning_required (fts
, ent
))
272 emit_cycle_warning (file_full_name
);
281 if (ent
->fts_info
== FTS_DP
282 && ok
&& ROOT_DEV_INO_CHECK (root_dev_ino
, file_stats
))
284 ROOT_DEV_INO_WARN (file_full_name
);
291 printf (_("changing security context of %s\n"),
292 quote (file_full_name
));
294 if (change_file_context (fts
->fts_cwd_fd
, file
) != 0)
299 fts_set (fts
, ent
, FTS_SKIP
);
304 /* Recursively operate on the specified FILES (the last entry
305 of which is NULL). BIT_FLAGS controls how fts works.
306 Return true if successful. */
309 process_files (char **files
, int bit_flags
)
313 FTS
*fts
= xfts_open (files
, bit_flags
, NULL
);
319 ent
= fts_read (fts
);
324 /* FIXME: try to give a better message */
325 error (0, errno
, _("fts_read failed"));
331 ok
&= process_file (fts
, ent
);
334 if (fts_close (fts
) != 0)
336 error (0, errno
, _("fts_close failed"));
346 if (status
!= EXIT_SUCCESS
)
351 Usage: %s [OPTION]... CONTEXT FILE...\n\
352 or: %s [OPTION]... [-u USER] [-r ROLE] [-l RANGE] [-t TYPE] FILE...\n\
353 or: %s [OPTION]... --reference=RFILE FILE...\n\
355 program_name
, program_name
, program_name
);
357 Change the SELinux security context of each FILE to CONTEXT.\n\
358 With --reference, change the security context of each FILE to that of RFILE.\n\
361 emit_mandatory_arg_note ();
364 --dereference affect the referent of each symbolic link (this is\n\
365 the default), rather than the symbolic link itself\n\
366 -h, --no-dereference affect symbolic links instead of any referenced file\n\
369 -u, --user=USER set user USER in the target security context\n\
370 -r, --role=ROLE set role ROLE in the target security context\n\
371 -t, --type=TYPE set type TYPE in the target security context\n\
372 -l, --range=RANGE set range RANGE in the target security context\n\
375 --no-preserve-root do not treat '/' specially (the default)\n\
376 --preserve-root fail to operate recursively on '/'\n\
379 --reference=RFILE use RFILE's security context rather than specifying\n\
383 -R, --recursive operate on files and directories recursively\n\
386 -v, --verbose output a diagnostic for every file processed\n\
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\
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\
398 -P do not traverse any symbolic links (default)\n\
401 fputs (HELP_OPTION_DESCRIPTION
, stdout
);
402 fputs (VERSION_OPTION_DESCRIPTION
, stdout
);
403 emit_ancillary_info (PROGRAM_NAME
);
409 main (int argc
, char **argv
)
411 /* Bit flags that control how fts works. */
412 int bit_flags
= FTS_PHYSICAL
;
414 /* 1 if --dereference, 0 if --no-dereference, -1 if neither has been
416 int dereference
= -1;
419 bool preserve_root
= false;
420 bool component_specified
= false;
421 char *reference_file
= NULL
;
424 initialize_main (&argc
, &argv
);
425 set_program_name (argv
[0]);
426 setlocale (LC_ALL
, "");
427 bindtextdomain (PACKAGE
, LOCALEDIR
);
428 textdomain (PACKAGE
);
430 atexit (close_stdout
);
432 while ((optc
= getopt_long (argc
, argv
, "HLPRhvu:r:t:l:", long_options
, NULL
))
437 case 'H': /* Traverse command-line symlinks-to-directories. */
438 bit_flags
= FTS_COMFOLLOW
| FTS_PHYSICAL
;
441 case 'L': /* Traverse all symlinks-to-directories. */
442 bit_flags
= FTS_LOGICAL
;
445 case 'P': /* Traverse no symlinks-to-directories. */
446 bit_flags
= FTS_PHYSICAL
;
449 case 'h': /* --no-dereference: affect symlinks */
453 case DEREFERENCE_OPTION
: /* --dereference: affect the referent
458 case NO_PRESERVE_ROOT
:
459 preserve_root
= false;
463 preserve_root
= true;
466 case REFERENCE_FILE_OPTION
:
467 reference_file
= optarg
;
483 specified_user
= optarg
;
484 component_specified
= true;
488 specified_role
= optarg
;
489 component_specified
= true;
493 specified_type
= optarg
;
494 component_specified
= true;
498 specified_range
= optarg
;
499 component_specified
= true;
502 case_GETOPT_HELP_CHAR
;
503 case_GETOPT_VERSION_CHAR (PROGRAM_NAME
, AUTHORS
);
505 usage (EXIT_FAILURE
);
511 if (bit_flags
== FTS_PHYSICAL
)
513 if (dereference
== 1)
514 error (EXIT_FAILURE
, 0,
515 _("-R --dereference requires either -H or -L"));
516 affect_symlink_referent
= false;
520 if (dereference
== 0)
521 error (EXIT_FAILURE
, 0, _("-R -h requires -P"));
522 affect_symlink_referent
= true;
527 bit_flags
= FTS_PHYSICAL
;
528 affect_symlink_referent
= (dereference
!= 0);
531 if (argc
- optind
< (reference_file
|| component_specified
? 1 : 2))
534 error (0, 0, _("missing operand"));
536 error (0, 0, _("missing operand after %s"), quote (argv
[argc
- 1]));
537 usage (EXIT_FAILURE
);
542 char *ref_context
= NULL
;
544 if (getfilecon (reference_file
, &ref_context
) < 0)
545 error (EXIT_FAILURE
, errno
, _("failed to get security context of %s"),
546 quote (reference_file
));
548 specified_context
= ref_context
;
550 else if (component_specified
)
552 /* FIXME: it's already null, so this is a no-op. */
553 specified_context
= NULL
;
557 specified_context
= argv
[optind
++];
558 if (security_check_context (se_const (specified_context
)) < 0)
559 error (EXIT_FAILURE
, errno
, _("invalid context: %s"),
560 quote (specified_context
));
563 if (reference_file
&& component_specified
)
565 error (0, 0, _("conflicting security context specifiers given"));
566 usage (EXIT_FAILURE
);
569 if (recurse
&& preserve_root
)
571 static struct dev_ino dev_ino_buf
;
572 root_dev_ino
= get_root_dev_ino (&dev_ino_buf
);
573 if (root_dev_ino
== NULL
)
574 error (EXIT_FAILURE
, errno
, _("failed to get attributes of %s"),
582 ok
= process_files (argv
+ optind
, bit_flags
| FTS_NOSTAT
);
584 return ok
? EXIT_SUCCESS
: EXIT_FAILURE
;