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