• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <ctype.h>
2 #include <errno.h>
3 #include <stdint.h>
4 #include <stdio.h>
5 #include <string.h>
6 #include <unistd.h>
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <getopt.h>
10 #include <limits.h>
11 #include <selinux/selinux.h>
12 #include <sepol/sepol.h>
13 
14 #include "../src/label_file.h"
15 #include "../src/regex.h"
16 
17 const char *policy_file;
18 static int ctx_err;
19 
validate_context(char ** ctxp)20 static int validate_context(char **ctxp)
21 {
22 	char *ctx = *ctxp;
23 
24 	if (policy_file && sepol_check_context(ctx) < 0) {
25 		ctx_err = -1;
26 		return ctx_err;
27 	}
28 
29 	return 0;
30 }
31 
process_file(struct selabel_handle * rec,const char * filename)32 static int process_file(struct selabel_handle *rec, const char *filename)
33 {
34 	unsigned int line_num;
35 	int rc;
36 	char *line_buf = NULL;
37 	size_t line_len = 0;
38 	FILE *context_file;
39 	const char *prefix = NULL;
40 
41 	context_file = fopen(filename, "r");
42 	if (!context_file) {
43 		fprintf(stderr, "Error opening %s: %s\n",
44 			    filename, strerror(errno));
45 		return -1;
46 	}
47 
48 	line_num = 0;
49 	rc = 0;
50 	while (getline(&line_buf, &line_len, context_file) > 0) {
51 		rc = process_line(rec, filename, prefix, line_buf, ++line_num);
52 		if (rc || ctx_err) {
53 			/* With -p option need to check and fail if ctx err as
54 			 * process_line() context validation on Linux does not
55 			 * return an error, but does print the error line to
56 			 * stderr. Android will set both to error and print
57 			 * the error line. */
58 			rc = -1;
59 			goto out;
60 		}
61 	}
62 out:
63 	free(line_buf);
64 	fclose(context_file);
65 	return rc;
66 }
67 
68 /*
69  * File Format
70  *
71  * u32 - magic number
72  * u32 - version
73  * u32 - length of pcre version EXCLUDING nul
74  * char - pcre version string EXCLUDING nul
75  * u32 - number of stems
76  * ** Stems
77  *	u32  - length of stem EXCLUDING nul
78  *	char - stem char array INCLUDING nul
79  * u32 - number of regexs
80  * ** Regexes
81  *	u32  - length of upcoming context INCLUDING nul
82  *	char - char array of the raw context
83  *	u32  - length of the upcoming regex_str
84  *	char - char array of the original regex string including the stem.
85  *	u32  - mode bits for >= SELINUX_COMPILED_FCONTEXT_MODE
86  *	       mode_t for <= SELINUX_COMPILED_FCONTEXT_PCRE_VERS
87  *	s32  - stemid associated with the regex
88  *	u32  - spec has meta characters
89  *	u32  - The specs prefix_len if >= SELINUX_COMPILED_FCONTEXT_PREFIX_LEN
90  *	u32  - data length of the pcre regex
91  *	char - a buffer holding the raw pcre regex info
92  *	u32  - data length of the pcre regex study daya
93  *	char - a buffer holding the raw pcre regex study data
94  */
write_binary_file(struct saved_data * data,int fd,int do_write_precompregex)95 static int write_binary_file(struct saved_data *data, int fd,
96 			     int do_write_precompregex)
97 {
98 	struct spec *specs = data->spec_arr;
99 	FILE *bin_file;
100 	size_t len;
101 	uint32_t magic = SELINUX_MAGIC_COMPILED_FCONTEXT;
102 	uint32_t section_len;
103 	uint32_t i;
104 	int rc;
105 	const char *reg_version;
106 	const char *reg_arch;
107 
108 	bin_file = fdopen(fd, "w");
109 	if (!bin_file) {
110 		perror("fopen output_file");
111 		exit(EXIT_FAILURE);
112 	}
113 
114 	/* write some magic number */
115 	len = fwrite(&magic, sizeof(uint32_t), 1, bin_file);
116 	if (len != 1)
117 		goto err;
118 
119 	/* write the version */
120 	section_len = SELINUX_COMPILED_FCONTEXT_MAX_VERS;
121 	len = fwrite(&section_len, sizeof(uint32_t), 1, bin_file);
122 	if (len != 1)
123 		goto err;
124 
125 	/* write version of the regex back-end */
126 	reg_version = regex_version();
127 	if (!reg_version)
128 		goto err;
129 	section_len = strlen(reg_version);
130 	len = fwrite(&section_len, sizeof(uint32_t), 1, bin_file);
131 	if (len != 1)
132 		goto err;
133 	len = fwrite(reg_version, sizeof(char), section_len, bin_file);
134 	if (len != section_len)
135 		goto err;
136 
137 	/* write regex arch string */
138 	reg_arch = regex_arch_string();
139 	if (!reg_arch)
140 		goto err;
141 	section_len = strlen(reg_arch);
142 	len = fwrite(&section_len, sizeof(uint32_t), 1, bin_file);
143 	if (len != 1)
144 		goto err;
145 	len = fwrite(reg_arch, sizeof(char), section_len, bin_file);
146 	if (len != section_len)
147 		goto err;
148 
149 	/* write the number of stems coming */
150 	section_len = data->num_stems;
151 	len = fwrite(&section_len, sizeof(uint32_t), 1, bin_file);
152 	if (len != 1)
153 		goto err;
154 
155 	for (i = 0; i < section_len; i++) {
156 		char *stem = data->stem_arr[i].buf;
157 		uint32_t stem_len = data->stem_arr[i].len;
158 
159 		/* write the strlen (aka no nul) */
160 		len = fwrite(&stem_len, sizeof(uint32_t), 1, bin_file);
161 		if (len != 1)
162 			goto err;
163 
164 		/* include the nul in the file */
165 		stem_len += 1;
166 		len = fwrite(stem, sizeof(char), stem_len, bin_file);
167 		if (len != stem_len)
168 			goto err;
169 	}
170 
171 	/* write the number of regexes coming */
172 	section_len = data->nspec;
173 	len = fwrite(&section_len, sizeof(uint32_t), 1, bin_file);
174 	if (len != 1)
175 		goto err;
176 
177 	for (i = 0; i < section_len; i++) {
178 		char *context = specs[i].lr.ctx_raw;
179 		char *regex_str = specs[i].regex_str;
180 		mode_t mode = specs[i].mode;
181 		size_t prefix_len = specs[i].prefix_len;
182 		int32_t stem_id = specs[i].stem_id;
183 		struct regex_data *re = specs[i].regex;
184 		uint32_t to_write;
185 
186 		/* length of the context string (including nul) */
187 		to_write = strlen(context) + 1;
188 		len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file);
189 		if (len != 1)
190 			goto err;
191 
192 		/* original context strin (including nul) */
193 		len = fwrite(context, sizeof(char), to_write, bin_file);
194 		if (len != to_write)
195 			goto err;
196 
197 		/* length of the original regex string (including nul) */
198 		to_write = strlen(regex_str) + 1;
199 		len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file);
200 		if (len != 1)
201 			goto err;
202 
203 		/* original regex string */
204 		len = fwrite(regex_str, sizeof(char), to_write, bin_file);
205 		if (len != to_write)
206 			goto err;
207 
208 		/* binary F_MODE bits */
209 		to_write = mode;
210 		len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file);
211 		if (len != 1)
212 			goto err;
213 
214 		/* stem for this regex (could be -1) */
215 		len = fwrite(&stem_id, sizeof(stem_id), 1, bin_file);
216 		if (len != 1)
217 			goto err;
218 
219 		/* does this spec have a metaChar? */
220 		to_write = specs[i].hasMetaChars;
221 		len = fwrite(&to_write, sizeof(to_write), 1, bin_file);
222 		if (len != 1)
223 			goto err;
224 
225 		/* For SELINUX_COMPILED_FCONTEXT_PREFIX_LEN */
226 		to_write = prefix_len;
227 		len = fwrite(&to_write, sizeof(to_write), 1, bin_file);
228 		if (len != 1)
229 			goto err;
230 
231 		/* Write regex related data */
232 		rc = regex_writef(re, bin_file, do_write_precompregex);
233 		if (rc < 0)
234 			goto err;
235 	}
236 
237 	rc = 0;
238 out:
239 	fclose(bin_file);
240 	return rc;
241 err:
242 	rc = -1;
243 	goto out;
244 }
245 
free_specs(struct saved_data * data)246 static void free_specs(struct saved_data *data)
247 {
248 	struct spec *specs = data->spec_arr;
249 	unsigned int num_entries = data->nspec;
250 	unsigned int i;
251 
252 	for (i = 0; i < num_entries; i++) {
253 		free(specs[i].lr.ctx_raw);
254 		free(specs[i].lr.ctx_trans);
255 		free(specs[i].regex_str);
256 		free(specs[i].type_str);
257 		regex_data_free(specs[i].regex);
258 	}
259 	free(specs);
260 
261 	num_entries = data->num_stems;
262 	for (i = 0; i < num_entries; i++)
263 		free(data->stem_arr[i].buf);
264 	free(data->stem_arr);
265 
266 	memset(data, 0, sizeof(*data));
267 }
268 
usage(const char * progname)269 static __attribute__ ((__noreturn__)) void usage(const char *progname)
270 {
271 	fprintf(stderr,
272 	    "usage: %s [-o out_file] [-p policy_file] fc_file\n"
273 	    "Where:\n\t"
274 	    "-o       Optional file name of the PCRE formatted binary\n\t"
275 	    "         file to be output. If not specified the default\n\t"
276 	    "         will be fc_file with the .bin suffix appended.\n\t"
277 	    "-p       Optional binary policy file that will be used to\n\t"
278 	    "         validate contexts defined in the fc_file.\n\t"
279 	    "-r       Omit precompiled regular expressions from the output.\n\t"
280 	    "         (PCRE2 only. Compiled PCRE2 regular expressions are\n\t"
281 	    "         not portable across architectures. Use this flag\n\t"
282 	    "         if you know that you build for an incompatible\n\t"
283 	    "         architecture to save space. When linked against\n\t"
284 	    "         PCRE1 this flag is ignored.)\n\t"
285 	    "-i       Print regular expression info end exit. That is, back\n\t"
286 	    "         end version and architecture identifier.\n\t"
287 	    "         Arch identifier format (PCRE2):\n\t"
288 	    "         <pointer width>-<size type width>-<endianness>, e.g.,\n\t"
289 	    "         \"8-8-el\" for x86_64.\n\t"
290 	    "fc_file  The text based file contexts file to be processed.\n",
291 	    progname);
292 		exit(EXIT_FAILURE);
293 }
294 
main(int argc,char * argv[])295 int main(int argc, char *argv[])
296 {
297 	const char *path = NULL;
298 	const char *out_file = NULL;
299 	int do_write_precompregex = 1;
300 	char stack_path[PATH_MAX + 1];
301 	char *tmp = NULL;
302 	int fd, rc, opt;
303 	FILE *policy_fp = NULL;
304 	struct stat buf;
305 	struct selabel_handle *rec = NULL;
306 	struct saved_data *data = NULL;
307 
308 	if (argc < 2)
309 		usage(argv[0]);
310 
311 	while ((opt = getopt(argc, argv, "io:p:r")) > 0) {
312 		switch (opt) {
313 		case 'o':
314 			out_file = optarg;
315 			break;
316 		case 'p':
317 			policy_file = optarg;
318 			break;
319 		case 'r':
320 			do_write_precompregex = 0;
321 			break;
322 		case 'i':
323 			printf("%s (%s)\n", regex_version(),
324 					regex_arch_string());
325 			return 0;
326 		default:
327 			usage(argv[0]);
328 		}
329 	}
330 
331 	if (optind >= argc)
332 		usage(argv[0]);
333 
334 	path = argv[optind];
335 	if (stat(path, &buf) < 0) {
336 		fprintf(stderr, "%s: could not stat: %s: %s\n", argv[0], path, strerror(errno));
337 		exit(EXIT_FAILURE);
338 	}
339 
340 	/* Open binary policy if supplied. */
341 	if (policy_file) {
342 		policy_fp = fopen(policy_file, "r");
343 
344 		if (!policy_fp) {
345 			fprintf(stderr, "%s: failed to open %s: %s\n",
346 				argv[0], policy_file, strerror(errno));
347 			exit(EXIT_FAILURE);
348 		}
349 
350 		if (sepol_set_policydb_from_file(policy_fp) < 0) {
351 			fprintf(stderr, "%s: failed to load policy from %s\n",
352 				argv[0], policy_file);
353 			fclose(policy_fp);
354 			exit(EXIT_FAILURE);
355 		}
356 	}
357 
358 	/* Generate dummy handle for process_line() function */
359 	rec = (struct selabel_handle *)calloc(1, sizeof(*rec));
360 	if (!rec) {
361 		fprintf(stderr, "%s: calloc failed: %s\n", argv[0], strerror(errno));
362 		if (policy_fp)
363 			fclose(policy_fp);
364 		exit(EXIT_FAILURE);
365 	}
366 	rec->backend = SELABEL_CTX_FILE;
367 
368 	/* Need to set validation on to get the bin file generated by the
369 	 * process_line function, however as the bin file being generated
370 	 * may not be related to the currently loaded policy (that it
371 	 * would be validated against), then set callback to ignore any
372 	 * validation - unless the -p option is used in which case if an
373 	 * error is detected, the process will be aborted. */
374 	rec->validating = 1;
375 	selinux_set_callback(SELINUX_CB_VALIDATE,
376 			    (union selinux_callback)&validate_context);
377 
378 	data = (struct saved_data *)calloc(1, sizeof(*data));
379 	if (!data) {
380 		fprintf(stderr, "%s: calloc failed: %s\n", argv[0], strerror(errno));
381 		free(rec);
382 		if (policy_fp)
383 			fclose(policy_fp);
384 		exit(EXIT_FAILURE);
385 	}
386 
387 	rec->data = data;
388 
389 	rc = process_file(rec, path);
390 	if (rc < 0) {
391 		fprintf(stderr, "%s: process_file failed\n", argv[0]);
392 		goto err;
393 	}
394 
395 	rc = sort_specs(data);
396 	if (rc) {
397 		fprintf(stderr, "%s: sort_specs failed\n", argv[0]);
398 		goto err;
399 	}
400 
401 	if (out_file)
402 		rc = snprintf(stack_path, sizeof(stack_path), "%s", out_file);
403 	else
404 		rc = snprintf(stack_path, sizeof(stack_path), "%s.bin", path);
405 
406 	if (rc < 0 || rc >= (int)sizeof(stack_path)) {
407 		fprintf(stderr, "%s: snprintf failed\n", argv[0]);
408 		goto err;
409 	}
410 
411 	tmp = malloc(strlen(stack_path) + 7);
412 	if (!tmp) {
413 		fprintf(stderr, "%s: malloc failed: %s\n", argv[0], strerror(errno));
414 		goto err;
415 	}
416 
417 	rc = sprintf(tmp, "%sXXXXXX", stack_path);
418 	if (rc < 0) {
419 		fprintf(stderr, "%s: sprintf failed\n", argv[0]);
420 		goto err;
421 	}
422 
423 	fd  = mkstemp(tmp);
424 	if (fd < 0) {
425 		fprintf(stderr, "%s: mkstemp %s failed: %s\n", argv[0], tmp, strerror(errno));
426 		goto err;
427 	}
428 
429 	rc = fchmod(fd, buf.st_mode);
430 	if (rc < 0) {
431 		fprintf(stderr, "%s: fchmod %s failed: %s\n", argv[0], tmp, strerror(errno));
432 		goto err_unlink;
433 	}
434 
435 	rc = write_binary_file(data, fd, do_write_precompregex);
436 	if (rc < 0) {
437 		fprintf(stderr, "%s: write_binary_file %s failed\n", argv[0], tmp);
438 		goto err_unlink;
439 	}
440 
441 	rc = rename(tmp, stack_path);
442 	if (rc < 0) {
443 		fprintf(stderr, "%s: rename %s -> %s failed: %s\n", argv[0], tmp, stack_path, strerror(errno));
444 		goto err_unlink;
445 	}
446 
447 	rc = 0;
448 out:
449 	if (policy_fp)
450 		fclose(policy_fp);
451 
452 	free_specs(data);
453 	free(rec);
454 	free(data);
455 	free(tmp);
456 	return rc;
457 
458 err_unlink:
459 	unlink(tmp);
460 err:
461 	rc = -1;
462 	goto out;
463 }
464