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
11static char *policyfile;
12
13static char **exclude_list;
14static int exclude_count;
15
16static 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
37static __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
79static 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
107int 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