LCOV - code coverage report
Current view: top level - src - chcon.c (source / functions) Hit Total Coverage
Test: coreutils.info Lines: 161 219 73.5 %
Date: 2018-01-30 Functions: 5 6 83.3 %

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

Generated by: LCOV version 1.10