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//config:config CHCON
  11//config:       bool "chcon (8.9 kb)"
  12//config:       default n
  13//config:       depends on SELINUX
  14//config:       help
  15//config:       Enable support to change the security context of file.
  16
  17//applet:IF_CHCON(APPLET(chcon, BB_DIR_USR_BIN, BB_SUID_DROP))
  18
  19//kbuild:lib-$(CONFIG_CHCON) += chcon.o
  20
  21//usage:#define chcon_trivial_usage
  22//usage:       "[OPTIONS] CONTEXT FILE..."
  23//usage:       "\n      chcon [OPTIONS] [-u USER] [-r ROLE] [-l RANGE] [-t TYPE] FILE..."
  24//usage:        IF_LONG_OPTS(
  25//usage:       "\n      chcon [OPTIONS] --reference=RFILE FILE..."
  26//usage:        )
  27//usage:
  28//usage:#define chcon_full_usage "\n\n"
  29//usage:       "Change the security context of each FILE to CONTEXT\n"
  30//usage:     "\n        -v      Verbose"
  31//usage:     "\n        -c      Report changes made"
  32//usage:     "\n        -h      Affect symlinks instead of their targets"
  33//usage:     "\n        -f      Suppress most error messages"
  34//usage:        IF_LONG_OPTS(
  35//usage:     "\n        --reference RFILE Use RFILE's group instead of using a CONTEXT value"
  36//usage:        )
  37//usage:     "\n        -u USER Set user/role/type/range in the target security context"
  38//usage:     "\n        -r ROLE"
  39//usage:     "\n        -t TYPE"
  40//usage:     "\n        -l RNG"
  41//usage:     "\n        -R      Recurse"
  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_LONG_OPTS)
  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_LONG_OPTS
 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        getopt32long(argv, "^"
 169                "Rchfu:r:t:l:v"
 170                "\0"
 171                "-1" /* at least 1 arg */
 172                ":?" /* error if exclusivity constraints are violated */
 173#if ENABLE_LONG_OPTS
 174                ":\xff--urtl:u--\xff:r--\xff:t--\xff:l--\xff"
 175#endif
 176                ":f--v:v--f"  /* 'verbose' and 'quiet' are exclusive */
 177                , chcon_longopts,
 178                &user, &role, &type, &range, &reference_file
 179        );
 180        argv += optind;
 181
 182#if ENABLE_LONG_OPTS
 183        if (option_mask32 & OPT_REFERENCE) {
 184                /* FIXME: lgetfilecon() should be used when '-h' is specified.
 185                 * But current implementation follows the original one. */
 186                if (getfilecon(reference_file, &specified_context) < 0)
 187                        bb_perror_msg_and_die("getfilecon('%s') failed", reference_file);
 188        } else
 189#endif
 190        if ((option_mask32 & OPT_COMPONENT_SPECIFIED) == 0) {
 191                specified_context = *argv++;
 192                /* specified_context is never NULL -
 193                 * "-1" in opt_complementary prevents this. */
 194                if (!argv[0])
 195                        bb_error_msg_and_die("too few arguments");
 196        }
 197
 198        for (i = 0; (fname = argv[i]) != NULL; i++) {
 199                int fname_len = strlen(fname);
 200                while (fname_len > 1 && fname[fname_len - 1] == '/')
 201                        fname_len--;
 202                fname[fname_len] = '\0';
 203
 204                if (recursive_action(fname,
 205                                        1<<option_mask32 & OPT_RECURSIVE,
 206                                        change_filedir_context,
 207                                        change_filedir_context,
 208                                        NULL, 0) != TRUE)
 209                        errors = 1;
 210        }
 211        return errors;
 212}
 213