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