busybox/selinux/chcon.c
<<
>>
Prefs
   1/*
   2 * chcon -- change security context, based on coreutils-5.97-13
   3 *
   4 * Port to busybox: KaiGai Kohei <kaigai@kaigai.gr.jp>
   5 *
   6 * Copyright (C) 2006 - 2007 KaiGai Kohei <kaigai@kaigai.gr.jp>
   7 *
   8 * Licensed under GPLv2, see file LICENSE in this source tree.
   9 */
  10
  11//usage:#define chcon_trivial_usage
  12//usage:       "[OPTIONS] CONTEXT FILE..."
  13//usage:       "\n      chcon [OPTIONS] [-u USER] [-r ROLE] [-l RANGE] [-t TYPE] FILE..."
  14//usage:        IF_FEATURE_CHCON_LONG_OPTIONS(
  15//usage:       "\n      chcon [OPTIONS] --reference=RFILE FILE..."
  16//usage:        )
  17//usage:#define chcon_full_usage "\n\n"
  18//usage:       "Change the security context of each FILE to CONTEXT\n"
  19//usage:        IF_FEATURE_CHCON_LONG_OPTIONS(
  20//usage:     "\n        -v,--verbose            Verbose"
  21//usage:     "\n        -c,--changes            Report changes made"
  22//usage:     "\n        -h,--no-dereference     Affect symlinks instead of their targets"
  23//usage:     "\n        -f,--silent,--quiet     Suppress most error messages"
  24//usage:     "\n        --reference=RFILE       Use RFILE's group instead of using a CONTEXT value"
  25//usage:     "\n        -u,--user=USER          Set user/role/type/range in the target"
  26//usage:     "\n        -r,--role=ROLE          security context"
  27//usage:     "\n        -t,--type=TYPE"
  28//usage:     "\n        -l,--range=RANGE"
  29//usage:     "\n        -R,--recursive          Recurse"
  30//usage:        )
  31//usage:        IF_NOT_FEATURE_CHCON_LONG_OPTIONS(
  32//usage:     "\n        -v      Verbose"
  33//usage:     "\n        -c      Report changes made"
  34//usage:     "\n        -h      Affect symlinks instead of their targets"
  35//usage:     "\n        -f      Suppress most error messages"
  36//usage:     "\n        -u USER Set user/role/type/range in the target security context"
  37//usage:     "\n        -r ROLE"
  38//usage:     "\n        -t TYPE"
  39//usage:     "\n        -l RNG"
  40//usage:     "\n        -R      Recurse"
  41//usage:        )
  42
  43#include <selinux/context.h>
  44
  45#include "libbb.h"
  46
  47#define OPT_RECURSIVE           (1<<0)  /* 'R' */
  48#define OPT_CHANHES             (1<<1)  /* 'c' */
  49#define OPT_NODEREFERENCE       (1<<2)  /* 'h' */
  50#define OPT_QUIET               (1<<3)  /* 'f' */
  51#define OPT_USER                (1<<4)  /* 'u' */
  52#define OPT_ROLE                (1<<5)  /* 'r' */
  53#define OPT_TYPE                (1<<6)  /* 't' */
  54#define OPT_RANGE               (1<<7)  /* 'l' */
  55#define OPT_VERBOSE             (1<<8)  /* 'v' */
  56#define OPT_REFERENCE           ((1<<9) * ENABLE_FEATURE_CHCON_LONG_OPTIONS)
  57#define OPT_COMPONENT_SPECIFIED (OPT_USER | OPT_ROLE | OPT_TYPE | OPT_RANGE)
  58
  59static char *user = NULL;
  60static char *role = NULL;
  61static char *type = NULL;
  62static char *range = NULL;
  63static char *specified_context = NULL;
  64
  65static int FAST_FUNC change_filedir_context(
  66                const char *fname,
  67                struct stat *stbuf UNUSED_PARAM,
  68                void *userData UNUSED_PARAM,
  69                int depth UNUSED_PARAM)
  70{
  71        context_t context = NULL;
  72        security_context_t file_context = NULL;
  73        security_context_t context_string;
  74        int rc = FALSE;
  75        int status = 0;
  76
  77        if (option_mask32 & OPT_NODEREFERENCE) {
  78                status = lgetfilecon(fname, &file_context);
  79        } else {
  80                status = getfilecon(fname, &file_context);
  81        }
  82        if (status < 0 && errno != ENODATA) {
  83                if ((option_mask32 & OPT_QUIET) == 0)
  84                        bb_error_msg("can't obtain security context: %s", fname);
  85                goto skip;
  86        }
  87
  88        if (file_context == NULL && specified_context == NULL) {
  89                bb_error_msg("can't apply partial context to unlabeled file %s", fname);
  90                goto skip;
  91        }
  92
  93        if (specified_context == NULL) {
  94                context = set_security_context_component(file_context,
  95                                                         user, role, type, range);
  96                if (!context) {
  97                        bb_error_msg("can't compute security context from %s", file_context);
  98                        goto skip;
  99                }
 100        } else {
 101                context = context_new(specified_context);
 102                if (!context) {
 103                        bb_error_msg("invalid context: %s", specified_context);
 104                        goto skip;
 105                }
 106        }
 107
 108        context_string = context_str(context);
 109        if (!context_string) {
 110                bb_error_msg("can't obtain security context in text expression");
 111                goto skip;
 112        }
 113
 114        if (file_context == NULL || strcmp(context_string, file_context) != 0) {
 115                int fail;
 116
 117                if (option_mask32 & OPT_NODEREFERENCE) {
 118                        fail = lsetfilecon(fname, context_string);
 119                } else {
 120                        fail = setfilecon(fname, context_string);
 121                }
 122                if ((option_mask32 & OPT_VERBOSE) || ((option_mask32 & OPT_CHANHES) && !fail)) {
 123                        printf(!fail
 124                               ? "context of %s changed to %s\n"
 125                               : "can't change context of %s to %s\n",
 126                               fname, context_string);
 127                }
 128                if (!fail) {
 129                        rc = TRUE;
 130                } else if ((option_mask32 & OPT_QUIET) == 0) {
 131                        bb_error_msg("can't change context of %s to %s",
 132                                     fname, context_string);
 133                }
 134        } else if (option_mask32 & OPT_VERBOSE) {
 135                printf("context of %s retained as %s\n", fname, context_string);
 136                rc = TRUE;
 137        }
 138skip:
 139        context_free(context);
 140        freecon(file_context);
 141
 142        return rc;
 143}
 144
 145#if ENABLE_FEATURE_CHCON_LONG_OPTIONS
 146static const char chcon_longopts[] ALIGN1 =
 147        "recursive\0"      No_argument       "R"
 148        "changes\0"        No_argument       "c"
 149        "no-dereference\0" No_argument       "h"
 150        "silent\0"         No_argument       "f"
 151        "quiet\0"          No_argument       "f"
 152        "user\0"           Required_argument "u"
 153        "role\0"           Required_argument "r"
 154        "type\0"           Required_argument "t"
 155        "range\0"          Required_argument "l"
 156        "verbose\0"        No_argument       "v"
 157        "reference\0"      Required_argument "\xff" /* no short option */
 158        ;
 159#endif
 160
 161int chcon_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 162int chcon_main(int argc UNUSED_PARAM, char **argv)
 163{
 164        char *reference_file;
 165        char *fname;
 166        int i, errors = 0;
 167
 168#if ENABLE_FEATURE_CHCON_LONG_OPTIONS
 169        applet_long_options = chcon_longopts;
 170#endif
 171        opt_complementary = "-1"  /* at least 1 param */
 172                ":?"  /* error if exclusivity constraints are violated */
 173#if ENABLE_FEATURE_CHCON_LONG_OPTIONS
 174                ":\xff--urtl:u--\xff:r--\xff:t--\xff:l--\xff"
 175#endif
 176                ":f--v:v--f";  /* 'verbose' and 'quiet' are exclusive */
 177        getopt32(argv, "Rchfu:r:t:l:v",
 178                &user, &role, &type, &range, &reference_file);
 179        argv += optind;
 180
 181#if ENABLE_FEATURE_CHCON_LONG_OPTIONS
 182        if (option_mask32 & OPT_REFERENCE) {
 183                /* FIXME: lgetfilecon() should be used when '-h' is specified.
 184                   But current implementation follows the original one. */
 185                if (getfilecon(reference_file, &specified_context) < 0)
 186                        bb_perror_msg_and_die("getfilecon('%s') failed", reference_file);
 187        } else
 188#endif
 189        if ((option_mask32 & OPT_COMPONENT_SPECIFIED) == 0) {
 190                specified_context = *argv++;
 191                /* specified_context is never NULL -
 192                 * "-1" in opt_complementary prevents this. */
 193                if (!argv[0])
 194                        bb_error_msg_and_die("too few arguments");
 195        }
 196
 197        for (i = 0; (fname = argv[i]) != NULL; i++) {
 198                int fname_len = strlen(fname);
 199                while (fname_len > 1 && fname[fname_len - 1] == '/')
 200                        fname_len--;
 201                fname[fname_len] = '\0';
 202
 203                if (recursive_action(fname,
 204                                     1<<option_mask32 & OPT_RECURSIVE,
 205                                     change_filedir_context,
 206                                     change_filedir_context,
 207                                     NULL, 0) != TRUE)
 208                        errors = 1;
 209        }
 210        return errors;
 211}
 212