1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <getopt.h>
5 #include <errno.h>
6 #include <stdbool.h>
7 #include <sepol/sepol.h>
8 #include <selinux/label.h>
9 #include <selinux/restorecon.h>
10
11 static char *policyfile;
12
13 static char **exclude_list;
14 static int exclude_count;
15
validate_context(char ** contextp)16 static int validate_context(char **contextp)
17 {
18 char *context = *contextp, *tmpcon;
19
20 if (policyfile) {
21 if (sepol_check_context(context) < 0) {
22 fprintf(stderr, "Invalid context %s\n", context);
23 exit(-1);
24 }
25 } else if (security_canonicalize_context_raw(context, &tmpcon) == 0) {
26 free(context);
27 *contextp = tmpcon;
28 } else if (errno != ENOENT) {
29 fprintf(stderr, "Validate context error: %s\n",
30 strerror(errno));
31 exit(-1);
32 }
33
34 return 0;
35 }
36
usage(const char * progname)37 static __attribute__ ((__noreturn__)) void usage(const char *progname)
38 {
39 fprintf(stderr,
40 "\nusage: %s [-FCnRrdmiIaAsl] [-e dir] [-v|-P]\n"
41 "[-x alt_rootpath] [-p policy] [-f specfile] pathname ...\n"
42 "\nWhere:\n\t"
43 "-F Set the label to that in specfile.\n\t"
44 " If not set then reset the \"type\" component of the "
45 "label to that\n\t in the specfile.\n\t"
46 "-C Check labels even if the stored SHA1 digest matches\n\t"
47 " the specfiles SHA1 digest.\n\t"
48 "-n Don't change any file labels (passive check).\n\t"
49 "-R Recursively change file and directory labels.\n\t"
50 "-v Show changes in file labels (-v and -P are mutually "
51 " exclusive).\n\t"
52 "-P Show progress by printing \"*\" to stdout every 1000 files"
53 ",\n\t unless relabeling entire OS, then show percentage complete.\n\t"
54 "-r Use realpath(3) to convert pathnames to canonical form.\n\t"
55 "-d Prevent descending into directories that have a "
56 "different\n\t device number than the pathname from which "
57 "the descent began.\n\t"
58 "-m Do not automatically read /proc/mounts to determine what\n\t"
59 " non-seclabel mounts to exclude from relabeling.\n\t"
60 "-e Exclude this directory (add multiple -e entries).\n\t"
61 "-i Do not set SELABEL_OPT_DIGEST option when calling "
62 " selabel_open(3).\n\t"
63 "-I Ignore files that do not exist.\n\t"
64 "-a Add an association between an inode and a context.\n\t"
65 " If there is a different context that matched the inode,\n\t"
66 " then use the first context that matched.\n\t"
67 "-A Abort on errors during the file tree walk.\n\t"
68 "-s Log any label changes to syslog(3).\n\t"
69 "-l Log what specfile context matched each file.\n\t"
70 "-x Set alternate rootpath.\n\t"
71 "-p Optional binary policy file (also sets validate context "
72 "option).\n\t"
73 "-f Optional file contexts file.\n\t"
74 "pathname One or more paths to relabel.\n\n",
75 progname);
76 exit(-1);
77 }
78
add_exclude(const char * directory)79 static void add_exclude(const char *directory)
80 {
81 char **tmp_list;
82
83 if (directory == NULL || directory[0] != '/') {
84 fprintf(stderr, "Full path required for exclude: %s.\n",
85 directory);
86 exit(-1);
87 }
88
89 /* Add another two entries, one for directory, and the other to
90 * terminate the list */
91 tmp_list = realloc(exclude_list, sizeof(char *) * (exclude_count + 2));
92 if (!tmp_list) {
93 fprintf(stderr, "ERROR: realloc failed.\n");
94 exit(-1);
95 }
96 exclude_list = tmp_list;
97
98 exclude_list[exclude_count] = strdup(directory);
99 if (!exclude_list[exclude_count]) {
100 fprintf(stderr, "ERROR: strdup failed.\n");
101 exit(-1);
102 }
103 exclude_count++;
104 exclude_list[exclude_count] = NULL;
105 }
106
main(int argc,char ** argv)107 int main(int argc, char **argv)
108 {
109 int opt, i;
110 unsigned int restorecon_flags = 0;
111 char *path = NULL, *digest = NULL, *validate = NULL;
112 char *alt_rootpath = NULL;
113 FILE *policystream;
114 bool ignore_digest = false, require_selinux = true;
115 bool verbose = false, progress = false;
116
117 struct selabel_handle *hnd = NULL;
118 struct selinux_opt selabel_option[] = {
119 { SELABEL_OPT_PATH, path },
120 { SELABEL_OPT_DIGEST, digest },
121 { SELABEL_OPT_VALIDATE, validate }
122 };
123
124 if (argc < 2)
125 usage(argv[0]);
126
127 exclude_list = NULL;
128 exclude_count = 0;
129
130 while ((opt = getopt(argc, argv, "iIFCnRvPrdaAslme:f:p:x:")) > 0) {
131 switch (opt) {
132 case 'F':
133 restorecon_flags |=
134 SELINUX_RESTORECON_SET_SPECFILE_CTX;
135 break;
136 case 'C':
137 restorecon_flags |=
138 SELINUX_RESTORECON_IGNORE_DIGEST;
139 break;
140 case 'n':
141 restorecon_flags |= SELINUX_RESTORECON_NOCHANGE;
142 break;
143 case 'R':
144 restorecon_flags |= SELINUX_RESTORECON_RECURSE;
145 break;
146 case 'v':
147 if (progress) {
148 fprintf(stderr,
149 "Progress and Verbose are mutually exclusive\n");
150 exit(-1);
151 }
152 verbose = true;
153 restorecon_flags |= SELINUX_RESTORECON_VERBOSE;
154 break;
155 case 'P':
156 if (verbose) {
157 fprintf(stderr,
158 "Progress and Verbose are mutually exclusive\n");
159 exit(-1);
160 }
161 progress = true;
162 restorecon_flags |= SELINUX_RESTORECON_PROGRESS;
163 break;
164 case 'r':
165 restorecon_flags |= SELINUX_RESTORECON_REALPATH;
166 break;
167 case 'd':
168 restorecon_flags |= SELINUX_RESTORECON_XDEV;
169 break;
170 case 'm':
171 restorecon_flags |= SELINUX_RESTORECON_IGNORE_MOUNTS;
172 break;
173 case 'e':
174 add_exclude(optarg);
175 break;
176 case 'p':
177 policyfile = optarg;
178
179 policystream = fopen(policyfile, "r");
180 if (!policystream) {
181 fprintf(stderr,
182 "ERROR: opening %s: %s\n",
183 policyfile, strerror(errno));
184 exit(-1);
185 }
186
187 if (sepol_set_policydb_from_file(policystream) < 0) {
188 fprintf(stderr,
189 "ERROR: reading policy %s: %s\n",
190 policyfile, strerror(errno));
191 exit(-1);
192 }
193 fclose(policystream);
194
195 selinux_set_callback(SELINUX_CB_VALIDATE,
196 (union selinux_callback)&validate_context);
197 require_selinux = false;
198 break;
199 case 'f':
200 path = optarg;
201 break;
202 case 'i':
203 ignore_digest = true;
204 break;
205 case 'I':
206 restorecon_flags |= SELINUX_RESTORECON_IGNORE_NOENTRY;
207 break;
208 case 'a':
209 restorecon_flags |= SELINUX_RESTORECON_ADD_ASSOC;
210 break;
211 case 'A':
212 restorecon_flags |= SELINUX_RESTORECON_ABORT_ON_ERROR;
213 break;
214 case 's':
215 restorecon_flags |= SELINUX_RESTORECON_SYSLOG_CHANGES;
216 break;
217 case 'l':
218 restorecon_flags |= SELINUX_RESTORECON_LOG_MATCHES;
219 break;
220 case 'x':
221 alt_rootpath = optarg;
222 break;
223 default:
224 usage(argv[0]);
225 }
226 }
227
228 if (require_selinux && (is_selinux_enabled() <= 0)) {
229 fprintf(stderr,
230 "SELinux must be enabled to perform this operation.\n");
231 exit(-1);
232 }
233
234 if (optind >= argc) {
235 fprintf(stderr, "No pathname specified\n");
236 exit(-1);
237 }
238
239 /* If any of these set then do our own selabel_open and pass
240 * handle to selinux_restorecon */
241 if (ignore_digest || path || policyfile) {
242 if (path)
243 selabel_option[0].value = path;
244 else
245 selabel_option[0].value = NULL;
246
247 if (ignore_digest)
248 selabel_option[1].value = NULL;
249 else
250 selabel_option[1].value = (char *)1;
251
252 if (policyfile) /* Validate */
253 selabel_option[2].value = (char *)1;
254 else
255 selabel_option[2].value = NULL;
256
257 hnd = selabel_open(SELABEL_CTX_FILE, selabel_option, 3);
258 if (!hnd) {
259 switch (errno) {
260 case EOVERFLOW:
261 fprintf(stderr, "ERROR: Number of specfiles or"
262 " specfile buffer caused an overflow.\n");
263 break;
264 default:
265 fprintf(stderr, "ERROR: selabel_open: %s\n",
266 strerror(errno));
267 }
268 exit(-1);
269 }
270 selinux_restorecon_set_sehandle(hnd);
271 }
272
273 if (exclude_list)
274 selinux_restorecon_set_exclude_list
275 ((const char **)exclude_list);
276
277 if (alt_rootpath)
278 selinux_restorecon_set_alt_rootpath(alt_rootpath);
279
280 /* Call restorecon for each path in list */
281 for (i = optind; i < argc; i++) {
282 if (selinux_restorecon(argv[i], restorecon_flags) < 0) {
283 fprintf(stderr, "ERROR: selinux_restorecon: %s\n",
284 strerror(errno));
285 exit(-1);
286 }
287 }
288
289 if (exclude_list) {
290 for (i = 0; exclude_list[i]; i++)
291 free(exclude_list[i]);
292 free(exclude_list);
293 }
294
295 if (hnd)
296 selabel_close(hnd);
297
298 return 0;
299 }
300