0eab6864ea867103581abf6a7aad5121a523a7bd
[platform/upstream/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 #include "busybox.h"
9 #include <getopt.h>
10 #include <selinux/context.h>
11
12 #define OPT_RECURSIVE           (1<<0)  /* 'R' */
13 #define OPT_CHANHES             (1<<1)  /* 'c' */
14 #define OPT_NODEREFERENCE       (1<<2)  /* 'h' */
15 #define OPT_QUIET               (1<<3)  /* 'f' */
16 #define OPT_USER                (1<<4)  /* 'u' */
17 #define OPT_ROLE                (1<<5)  /* 'r' */
18 #define OPT_TYPE                (1<<6)  /* 't' */
19 #define OPT_RANGE               (1<<7)  /* 'l' */
20 #define OPT_VERBOSE             (1<<8)  /* 'v' */
21 #define OPT_REFERENCE           ((1<<9) * ENABLE_FEATURE_CHCON_LONG_OPTIONS)
22 #define OPT_COMPONENT_SPECIFIED (OPT_USER | OPT_ROLE | OPT_TYPE | OPT_RANGE)
23
24 static char *user = NULL;
25 static char *role = NULL;
26 static char *type = NULL;
27 static char *range = NULL;
28 static char *specified_context = NULL;
29
30 static int change_filedir_context(const char *fname, struct stat *stbuf, void *userData, int depth)
31 {
32         context_t context = NULL;
33         security_context_t file_context = NULL;
34         security_context_t context_string;
35         int rc = FALSE;
36         int status = 0;
37
38         if (option_mask32 & OPT_NODEREFERENCE) {
39                 status = lgetfilecon(fname, &file_context);
40         } else {
41                 status = getfilecon(fname, &file_context);
42         }
43         if (status < 0 && errno != ENODATA) {
44                 if ((option_mask32 & OPT_QUIET) == 0)
45                         bb_error_msg("cannot obtain security context: %s", fname);
46                 goto skip;
47         }
48
49         if (file_context == NULL && specified_context == NULL) {
50                 bb_error_msg("cannot apply partial context to unlabeled file %s", fname);
51                 goto skip;
52         }
53
54         if (specified_context == NULL) {
55                 context = set_security_context_component(file_context,
56                                                          user, role, type, range);
57                 if (!context) {
58                         bb_error_msg("cannot compute security context from %s", file_context);
59                         goto skip;
60                 }
61         } else {
62                 context = context_new(specified_context);
63                 if (!context) {
64                         bb_error_msg("invalid context: %s", specified_context);
65                         goto skip;
66                 }
67         }
68
69         context_string = context_str(context);
70         if (!context_string) {
71                 bb_error_msg("cannot obtain security context in text expression");
72                 goto skip;
73         }
74
75         if (file_context == NULL || strcmp(context_string, file_context) != 0) {
76                 int fail;
77
78                 if (option_mask32 & OPT_NODEREFERENCE) {
79                         fail = lsetfilecon(fname, context_string);
80                 } else {
81                         fail = setfilecon(fname, context_string);
82                 }
83                 if ((option_mask32 & OPT_VERBOSE) || ((option_mask32 & OPT_CHANHES) && !fail)) {
84                         printf(!fail
85                                ? "context of %s changed to %s\n"
86                                : "failed to change context of %s to %s\n",
87                                fname, context_string);
88                 }
89                 if (!fail) {
90                         rc = TRUE;
91                 } else if ((option_mask32 & OPT_QUIET) == 0) {
92                         bb_error_msg("failed to change context of %s to %s",
93                                      fname, context_string);
94                 }
95         } else if (option_mask32 & OPT_VERBOSE) {
96                 printf("context of %s retained as %s\n", fname, context_string);
97                 rc = TRUE;
98         }
99 skip:
100         context_free(context);
101         freecon(file_context);
102
103         return rc;
104 }
105
106 #if ENABLE_FEATURE_CHCON_LONG_OPTIONS
107 static struct option chcon_options[] = {
108         { "recursive",      0, NULL, 'R' },
109         { "changes",        0, NULL, 'c' },
110         { "no-dereference", 0, NULL, 'h' },
111         { "silent",         0, NULL, 'f' },
112         { "quiet",          0, NULL, 'f' },
113         { "user",           1, NULL, 'u' },
114         { "role",           1, NULL, 'r' },
115         { "type",           1, NULL, 't' },
116         { "range",          1, NULL, 'l' },
117         { "verbose",        0, NULL, 'v' },
118         { "reference",      1, NULL, 0xff }, /* no short option */
119         { NULL,             0, NULL, 0 },
120 };
121 #endif
122
123 int chcon_main(int argc, char *argv[]);
124 int chcon_main(int argc, char *argv[])
125 {
126         char *reference_file;
127         char *fname;
128         int i, errors = 0;
129
130 #if ENABLE_FEATURE_CHCON_LONG_OPTIONS
131         applet_long_options = chcon_options;
132 #endif
133         opt_complementary = "-1"  /* at least 1 param */
134                 ":?"  /* error if exclusivity constraints are violated */
135 #if ENABLE_FEATURE_CHCON_LONG_OPTIONS
136                 ":\xff--urtl:u--\xff:r--\xff:t--\xff:l--\xff"
137 #endif
138                 ":f--v:v--f";  /* 'verbose' and 'quiet' are exclusive */
139         getopt32(argc, argv, "Rchf:u:r:t:l:v",
140                 &user, &role, &type, &range, &reference_file);
141         argv += optind;
142
143 #if ENABLE_FEATURE_CHCON_LONG_OPTIONS
144         if (option_mask32 & OPT_REFERENCE) {
145                 /* FIXME: lgetfilecon() should be used when '-h' is specified.
146                    But current implementation follows the original one. */
147                 if (getfilecon(reference_file, &specified_context) < 0)
148                         bb_perror_msg_and_die("getfilecon('%s') failed", reference_file);
149         } else
150 #endif
151         if ((option_mask32 & OPT_COMPONENT_SPECIFIED) == 0) {
152                 specified_context = *argv++;
153                 /* specified_context is never NULL -
154                  * "-1" in opt_complementary prevents this. */
155                 if (!argv[0])
156                         bb_error_msg_and_die("too few arguments");
157         }
158
159         for (i = 0; (fname = argv[i]) != NULL; i++) {
160                 int fname_len = strlen(fname);
161                 while (fname_len > 1 && fname[fname_len - 1] == '/')
162                         fname_len--;
163                 fname[fname_len] = '\0';
164
165                 if (recursive_action(fname,
166                                      1<<option_mask32 & OPT_RECURSIVE,
167                                      change_filedir_context,
168                                      change_filedir_context,
169                                      NULL, 0) != TRUE)
170                         errors = 1;
171         }
172         return errors;
173 }