/* chcon -- change security context of files This is the chcon utility
Copyright (C) 2005-2018 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */ The GNUv3 license
#include <config.h> Provides system specific information
#include <stdio.h> Provides standard I/O capability
#include <sys/types.h> Provides system data types
#include <getopt.h> ...!includes auto-comment...
#include "system.h" ...!includes auto-comment...
#include "dev-ino.h" ...!includes auto-comment...
#include "die.h" ...!includes auto-comment...
#include "error.h" ...!includes auto-comment...
#include "ignore-value.h" ...!includes auto-comment...
#include "quote.h" ...!includes auto-comment...
#include "root-dev-ino.h" ...!includes auto-comment......!includes auto-comment...
#include "selinux-at.h" ...!includes auto-comment...
#include "xfts.h" ...!includes auto-comment...
/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "chcon" Line 33
#define AUTHORS \ Line 35
proper_name ("Russell Coker"), \ Line 36
proper_name ("Jim Meyering") Line 37
/* If nonzero, and the systems has support for it, change the context
of symbolic links rather than any files they point to. */
static bool affect_symlink_referent; Line 41
/* If true, change the modes of directories recursively. */
static bool recurse; Line 44
/* Level of verbosity. */
static bool verbose; Line 47
/* Pointer to the device and inode numbers of '/', when --recursive.
Otherwise NULL. */
static struct dev_ino *root_dev_ino; Line 51
/* The name of the context file is being given. */
static char const *specified_context; Line 54
/* Specific components of the context */
static char const *specified_user; Line 57
static char const *specified_role; Line 58
static char const *specified_range; Line 59
static char const *specified_type; Line 60
/* For long options that have no equivalent short option, use a
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
enum Line 64
{
DEREFERENCE_OPTION = CHAR_MAX + 1, Line 66
NO_PRESERVE_ROOT, Line 67
PRESERVE_ROOT, Line 68
REFERENCE_FILE_OPTION Line 69
}; Block 1
static struct option const long_options[] = Line 72
{
{"recursive", no_argument, NULL, 'R'}, Line 74
{"dereference", no_argument, NULL, DEREFERENCE_OPTION}, Line 75
{"no-dereference", no_argument, NULL, 'h'}, Line 76
{"no-preserve-root", no_argument, NULL, NO_PRESERVE_ROOT}, Line 77
{"preserve-root", no_argument, NULL, PRESERVE_ROOT}, Line 78
{"reference", required_argument, NULL, REFERENCE_FILE_OPTION}, Line 79
{"user", required_argument, NULL, 'u'}, Line 80
{"role", required_argument, NULL, 'r'}, Line 81
{"type", required_argument, NULL, 't'}, Line 82
{"range", required_argument, NULL, 'l'}, Line 83
{"verbose", no_argument, NULL, 'v'}, Line 84
{GETOPT_HELP_OPTION_DECL}, Line 85
{GETOPT_VERSION_OPTION_DECL}, Line 86
{NULL, 0, NULL, 0} Line 87
}; Block 2
/* Given a security context, CONTEXT, derive a context_t (*RET),
setting any portions selected via the global variables, specified_user,
specified_role, etc. */
static int Line 93
compute_context_from_mask (char const *context, context_t *ret) Line 94
{
bool ok = true; Line 96
context_t new_context = context_new (context); Line 97
if (!new_context) Line 98
{
error (0, errno, _("failed to create security context: %s"), Line 100
quote (context)); Line 101
return 1; Line 102
}
#define SET_COMPONENT(C, comp) \ Line 105
do \ Line 106
{ \ Line 107
if (specified_ ## comp \ Line 108
&& context_ ## comp ## _set ((C), specified_ ## comp)) \ Line 109
{ \ Line 110
error (0, errno, \ Line 111
_("failed to set %s security context component to %s"), \ Line 112
#comp, quote (specified_ ## comp)); \ Line 113
ok = false; \ Line 114
} \ Line 115
} \ Line 116
while (0) Line 117
SET_COMPONENT (new_context, user); Line 119
SET_COMPONENT (new_context, range); Line 120
SET_COMPONENT (new_context, role); Line 121
SET_COMPONENT (new_context, type); Line 122
if (!ok) Line 124
{
int saved_errno = errno; Line 126
context_free (new_context); Line 127
errno = saved_errno; Line 128
return 1; Line 129
}
*ret = new_context; Line 132
return 0; Line 133
} Block 3
/* Change the context of FILE, using specified components.
If it is a directory and -R is given, recurse.
Return 0 if successful, 1 if errors occurred. */
static int Line 140
change_file_context (int fd, char const *file) Line 141
{
char *file_context = NULL; Line 143
context_t context IF_LINT (= 0); Line 144
char const * context_string; Line 145
int errors = 0; Line 146
if (specified_context == NULL) Line 148
{
int status = (affect_symlink_referent Line 150
? getfileconat (fd, file, &file_context) Line 151
: lgetfileconat (fd, file, &file_context)); Line 152
if (status < 0 && errno != ENODATA) Line 154
{
error (0, errno, _("failed to get security context of %s"), Line 156
quoteaf (file)); Line 157
return 1; Line 158
}
/* If the file doesn't have a context, and we're not setting all of
the context components, there isn't really an obvious default.
Thus, we just give up. */
if (file_context == NULL) Line 164
{
error (0, 0, _("can't apply partial context to unlabeled file %s"), Line 166
quoteaf (file)); Line 167
return 1; Line 168
}
if (compute_context_from_mask (file_context, &context)) Line 171
return 1; Line 172
context_string = context_str (context); Line 174
}
else Line 176
{
context_string = specified_context; Line 178
}
if (file_context == NULL || ! STREQ (context_string, file_context)) Line 181
{
int fail = (affect_symlink_referent Line 183
? setfileconat (fd, file, se_const (context_string)) Line 184
: lsetfileconat (fd, file, se_const (context_string))); Line 185
if (fail) Line 187
{
errors = 1; Line 189
error (0, errno, _("failed to change context of %s to %s"), Line 190
quoteaf_n (0, file), quote_n (1, context_string)); Line 191
}
}
if (specified_context == NULL) Line 195
{
context_free (context); Line 197
freecon (file_context); Line 198
}
return errors; Line 201
} Block 4
/* Change the context of FILE.
Return true if successful. This function is called
once for every file system object that fts encounters. */
static bool Line 208
process_file (FTS *fts, FTSENT *ent) Line 209
{
char const *file_full_name = ent->fts_path; Line 211
char const *file = ent->fts_accpath; Line 212
const struct stat *file_stats = ent->fts_statp; Line 213
bool ok = true; Line 214
switch (ent->fts_info) Line 216
{
case FTS_D: Line 218
if (recurse) Line 219
{
if (ROOT_DEV_INO_CHECK (root_dev_ino, ent->fts_statp)) Line 221
{
/* This happens e.g., with "chcon -R --preserve-root ... /"
and with "chcon -RH --preserve-root ... symlink-to-root". */
ROOT_DEV_INO_WARN (file_full_name); Line 225
/* Tell fts not to traverse into this hierarchy. */
fts_set (fts, ent, FTS_SKIP); Line 227
/* Ensure that we do not process "/" on the second visit. */
ignore_value (fts_read (fts)); Line 229...!syscalls auto-comment...
return false; Line 230
}
return true; Line 232
}
break; Line 234
case FTS_DP: Line 236
if (! recurse) Line 237
return true; Line 238
break; Line 239
case FTS_NS: Line 241
/* For a top-level file or directory, this FTS_NS (stat failed)
indicator is determined at the time of the initial fts_open call.
With programs like chmod, chown, and chgrp, that modify
permissions, it is possible that the file in question is
accessible when control reaches this point. So, if this is
the first time we've seen the FTS_NS for this file, tell
fts_read to stat it "again". */
if (ent->fts_level == 0 && ent->fts_number == 0) Line 249
{
ent->fts_number = 1; Line 251
fts_set (fts, ent, FTS_AGAIN); Line 252
return true; Line 253
}
error (0, ent->fts_errno, _("cannot access %s"), Line 255
quoteaf (file_full_name)); Line 256
ok = false; Line 257
break; Line 258
case FTS_ERR: Line 260
error (0, ent->fts_errno, "%s", quotef (file_full_name)); Line 261
ok = false; Line 262
break; Line 263
case FTS_DNR: Line 265
error (0, ent->fts_errno, _("cannot read directory %s"), Line 266
quoteaf (file_full_name)); Line 267
ok = false; Line 268
break; Line 269
case FTS_DC: /* directory that causes cycles */ Line 271
if (cycle_warning_required (fts, ent)) Line 272
{
emit_cycle_warning (file_full_name); Line 274
return false; Line 275
}
break; Line 277
default: Line 279
break; Line 280
}
if (ent->fts_info == FTS_DP Line 283
&& ok && ROOT_DEV_INO_CHECK (root_dev_ino, file_stats)) Line 284
{
ROOT_DEV_INO_WARN (file_full_name); Line 286
ok = false; Line 287
}
if (ok) Line 290
{
if (verbose) Line 292
printf (_("changing security context of %s\n"), Line 293
quoteaf (file_full_name)); Line 294
if (change_file_context (fts->fts_cwd_fd, file) != 0) Line 296
ok = false; Line 297
}
if ( ! recurse) Line 300
fts_set (fts, ent, FTS_SKIP); Line 301
return ok; Line 303
} Block 5
/* Recursively operate on the specified FILES (the last entry
of which is NULL). BIT_FLAGS controls how fts works.
Return true if successful. */
static bool Line 310
process_files (char **files, int bit_flags) Line 311
{
bool ok = true; Line 313
FTS *fts = xfts_open (files, bit_flags, NULL); Line 315...!syscalls auto-comment...
while (1) Line 317
{
FTSENT *ent; Line 319
ent = fts_read (fts); Line 321...!syscalls auto-comment...
if (ent == NULL) Line 322
{
if (errno != 0) Line 324
{
/* FIXME: try to give a better message */
error (0, errno, _("fts_read failed")); Line 327
ok = false; Line 328
}
break; Line 330
}
ok &= process_file (fts, ent); Line 333
}
if (fts_close (fts) != 0) Line 336...!syscalls auto-comment...
{
error (0, errno, _("fts_close failed")); Line 338
ok = false; Line 339
}
return ok; Line 342
} Block 6
void Line 345
usage (int status) Line 346
{
if (status != EXIT_SUCCESS) Line 348
emit_try_help (); ...!common auto-comment...
else Line 350
{
printf (_("\ Line 352
Usage: %s [OPTION]... CONTEXT FILE...\n\ Line 353
or: %s [OPTION]... [-u USER] [-r ROLE] [-l RANGE] [-t TYPE] FILE...\n\ Line 354
or: %s [OPTION]... --reference=RFILE FILE...\n\ Line 355
"), Line 356
program_name, program_name, program_name); Line 357
fputs (_("\ Line 358
Change the SELinux security context of each FILE to CONTEXT.\n\ Line 359
With --reference, change the security context of each FILE to that of RFILE.\n\ Line 360
"), stdout); Line 361
emit_mandatory_arg_note (); ...!common auto-comment...
fputs (_("\ Line 365
--dereference affect the referent of each symbolic link (this is\n\ Line 366...!syscalls auto-comment...
the default), rather than the symbolic link itself\n\ Line 367
-h, --no-dereference affect symbolic links instead of any referenced file\n\Line 368
"), stdout); Line 369
fputs (_("\ Line 370
-u, --user=USER set user USER in the target security context\n\ Line 371
-r, --role=ROLE set role ROLE in the target security context\n\ Line 372
-t, --type=TYPE set type TYPE in the target security context\n\ Line 373
-l, --range=RANGE set range RANGE in the target security context\n\ Line 374
"), stdout); Line 375
fputs (_("\ Line 376
--no-preserve-root do not treat '/' specially (the default)\n\ Line 377
--preserve-root fail to operate recursively on '/'\n\ Line 378
"), stdout); Line 379
fputs (_("\ Line 380
--reference=RFILE use RFILE's security context rather than specifying\n\ Line 381
a CONTEXT value\n\ Line 382
"), stdout); Line 383
fputs (_("\ Line 384
-R, --recursive operate on files and directories recursively\n\ Line 385
"), stdout); Line 386
fputs (_("\ Line 387
-v, --verbose output a diagnostic for every file processed\n\ Line 388
"), stdout); Line 389
fputs (_("\ Line 390
\n\
The following options modify how a hierarchy is traversed when the -R\n\ Line 392
option is also specified. If more than one is specified, only the final\n\ Line 393
one takes effect.\n\ Line 394
\n\
-H if a command line argument is a symbolic link\n\ Line 396
to a directory, traverse it\n\ Line 397
-L traverse every symbolic link to a directory\n\ Line 398
encountered\n\ Line 399
-P do not traverse any symbolic links (default)\n\ Line 400
\n\
"), stdout); Line 402
fputs (HELP_OPTION_DESCRIPTION, stdout); Line 403
fputs (VERSION_OPTION_DESCRIPTION, stdout); Line 404
emit_ancillary_info (PROGRAM_NAME); Line 405
}
exit (status); Line 407
} Block 7
int
main (int argc, char **argv) Line 411
{
/* Bit flags that control how fts works. */
int bit_flags = FTS_PHYSICAL; Line 414
/* 1 if --dereference, 0 if --no-dereference, -1 if neither has been
specified. */
int dereference = -1; Line 418
bool ok; Line 420
bool preserve_root = false; Line 421
bool component_specified = false; Line 422
char *reference_file = NULL; Line 423
int optc; Line 424
initialize_main (&argc, &argv); VMS-specific entry point handling wildcard expansion
set_program_name (argv[0]); Retains program name and discards path
setlocale (LC_ALL, ""); Sets up internationalization (i18n)
bindtextdomain (PACKAGE, LOCALEDIR); Assigns i18n directorySets text domain for _() [gettext()] function
textdomain (PACKAGE); Sets text domain for _() [gettext()] function
atexit (close_stdout); Close stdout on exit (see gnulib)
while ((optc = getopt_long (argc, argv, "HLPRhvu:r:t:l:", long_options, NULL))Line 434
!= -1) Line 435
{
switch (optc) Line 437
{
case 'H': /* Traverse command-line symlinks-to-directories. */ Line 439
bit_flags = FTS_COMFOLLOW | FTS_PHYSICAL; Line 440
break; Line 441
case 'L': /* Traverse all symlinks-to-directories. */ Line 443
bit_flags = FTS_LOGICAL; Line 444
break; Line 445
case 'P': /* Traverse no symlinks-to-directories. */ Line 447
bit_flags = FTS_PHYSICAL; Line 448
break; Line 449
case 'h': /* --no-dereference: affect symlinks */ Line 451
dereference = 0; Line 452
break; Line 453
case DEREFERENCE_OPTION: /* --dereference: affect the referent Line 455
of each symlink */
dereference = 1; Line 457
break; Line 458
case NO_PRESERVE_ROOT: Line 460
preserve_root = false; Line 461
break; Line 462
case PRESERVE_ROOT: Line 464
preserve_root = true; Line 465
break; Line 466
case REFERENCE_FILE_OPTION: Line 468
reference_file = optarg; Line 469
break; Line 470
case 'R': Line 472
recurse = true; Line 473
break; Line 474
case 'f': Line 476
/* ignore */
break; Line 478
case 'v': Line 480
verbose = true; Line 481
break; Line 482
case 'u': Line 484
specified_user = optarg; Line 485
component_specified = true; Line 486
break; Line 487
case 'r': Line 489
specified_role = optarg; Line 490
component_specified = true; Line 491
break; Line 492
case 't': Line 494
specified_type = optarg; Line 495
component_specified = true; Line 496
break; Line 497
case 'l': Line 499
specified_range = optarg; Line 500
component_specified = true; Line 501
break; Line 502
case_GETOPT_HELP_CHAR; Line 504
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); Line 505
default: Line 506
usage (EXIT_FAILURE); Line 507
}
}
if (recurse) Line 511
{
if (bit_flags == FTS_PHYSICAL) Line 513
{
if (dereference == 1) Line 515
die (EXIT_FAILURE, 0, Line 516
_("-R --dereference requires either -H or -L")); Line 517
affect_symlink_referent = false; Line 518
}
else Line 520
{
if (dereference == 0) Line 522
die (EXIT_FAILURE, 0, _("-R -h requires -P")); Line 523
affect_symlink_referent = true; Line 524
}
}
else Line 527
{
bit_flags = FTS_PHYSICAL; Line 529
affect_symlink_referent = (dereference != 0); Line 530
}
if (argc - optind < (reference_file || component_specified ? 1 : 2)) Line 533
{
if (argc <= optind) Line 535
error (0, 0, _("missing operand")); Line 536
else Line 537
error (0, 0, _("missing operand after %s"), quote (argv[argc - 1])); Line 538
usage (EXIT_FAILURE); Line 539
}
if (reference_file) Line 542
{
char *ref_context = NULL; Line 544
if (getfilecon (reference_file, &ref_context) < 0) Line 546
die (EXIT_FAILURE, errno, _("failed to get security context of %s"), Line 547
quoteaf (reference_file)); Line 548
specified_context = ref_context; Line 550
}
else if (component_specified) Line 552
{
/* FIXME: it's already null, so this is a no-op. */
specified_context = NULL; Line 555
}
else Line 557
{
specified_context = argv[optind++]; Line 559
if (security_check_context (se_const (specified_context)) < 0) Line 560
die (EXIT_FAILURE, errno, _("invalid context: %s"), Line 561
quote (specified_context)); Line 562
}
if (reference_file && component_specified) Line 565
{
error (0, 0, _("conflicting security context specifiers given")); Line 567
usage (EXIT_FAILURE); Line 568
}
if (recurse && preserve_root) Line 571
{
static struct dev_ino dev_ino_buf; Line 573
root_dev_ino = get_root_dev_ino (&dev_ino_buf); Line 574
if (root_dev_ino == NULL) Line 575
die (EXIT_FAILURE, errno, _("failed to get attributes of %s"), Line 576
quoteaf ("/")); Line 577
}
else Line 579
{
root_dev_ino = NULL; Line 581
}
ok = process_files (argv + optind, bit_flags | FTS_NOSTAT); Line 584
return ok ? EXIT_SUCCESS : EXIT_FAILURE; Line 586
}