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 {
 135                if (option_mask32 & OPT_VERBOSE) {
 136                        printf("context of %s retained as %s\n", fname, context_string);
 137                }
 138                rc = TRUE;
 139        }
 140skip:
 141        context_free(context);
 142        freecon(file_context);
 143
 144        return rc;
 145}
 146
 147#if ENABLE_LONG_OPTS
 148static const char chcon_longopts[] ALIGN1 =
 149        "recursive\0"      No_argument       "R"
 150        "changes\0"        No_argument       "c"
 151        "no-dereference\0" No_argument       "h"
 152        "silent\0"         No_argument       "f"
 153        "quiet\0"          No_argument       "f"
 154        "user\0"           Required_argument "u"
 155        "role\0"           Required_argument "r"
 156        "type\0"           Required_argument "t"
 157        "range\0"          Required_argument "l"
 158        "verbose\0"        No_argument       "v"
 159        "reference\0"      Required_argument "\xff" /* no short option */
 160        ;
 161#endif
 162
 163int chcon_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 164int chcon_main(int argc UNUSED_PARAM, char **argv)
 165{
 166        char *reference_file;
 167        char *fname;
 168        int i, errors = 0;
 169
 170        getopt32long(argv, "^"
 171                "Rchfu:r:t:l:v"
 172                "\0"
 173                "-1" /* at least 1 arg */
 174                ":?" /* error if exclusivity constraints are violated */
 175#if ENABLE_LONG_OPTS
 176                ":\xff--urtl:u--\xff:r--\xff:t--\xff:l--\xff"
 177#endif
 178                ":f--v:v--f"  /* 'verbose' and 'quiet' are exclusive */
 179                , chcon_longopts,
 180                &user, &role, &type, &range, &reference_file
 181        );
 182        argv += optind;
 183
 184#if ENABLE_LONG_OPTS
 185        if (option_mask32 & OPT_REFERENCE) {
 186                /* FIXME: lgetfilecon() should be used when '-h' is specified.
 187                 * But current implementation follows the original one. */
 188                if (getfilecon(reference_file, &specified_context) < 0)
 189                        bb_perror_msg_and_die("getfilecon('%s') failed", reference_file);
 190        } else
 191#endif
 192        if ((option_mask32 & OPT_COMPONENT_SPECIFIED) == 0) {
 193                specified_context = *argv++;
 194                /* specified_context is never NULL -
 195                 * "-1" in opt_complementary prevents this. */
 196                if (!argv[0])
 197                        bb_error_msg_and_die("too few arguments");
 198        }
 199
 200        for (i = 0; (fname = argv[i]) != NULL; i++) {
 201                int fname_len = strlen(fname);
 202                while (fname_len > 1 && fname[fname_len - 1] == '/')
 203                        fname_len--;
 204                fname[fname_len] = '\0';
 205
 206                if (recursive_action(fname,
 207                                        ((option_mask32 & OPT_RECURSIVE) ? ACTION_RECURSE : 0),
 208                                        change_filedir_context,
 209                                        change_filedir_context,
 210                                        NULL, 0) != TRUE)
 211                        errors = 1;
 212        }
 213        return errors;
 214}
 215