1#include <ctype.h>
2#include <errno.h>
3#include <pcre.h>
4#include <stdint.h>
5#include <stdio.h>
6#include <string.h>
7#include <unistd.h>
8#include <sys/types.h>
9#include <sys/stat.h>
10#include <getopt.h>
11#include <limits.h>
12#include <selinux/selinux.h>
13#include <sepol/sepol.h>
14
15#include "../src/label_file.h"
16
17const char *policy_file;
18static int ctx_err;
19
20static 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
32static 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	}
62out:
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 bufer 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 */
95static int write_binary_file(struct saved_data *data, int fd)
96{
97	struct spec *specs = data->spec_arr;
98	FILE *bin_file;
99	size_t len;
100	uint32_t magic = SELINUX_MAGIC_COMPILED_FCONTEXT;
101	uint32_t section_len;
102	uint32_t i;
103	int rc;
104
105	bin_file = fdopen(fd, "w");
106	if (!bin_file) {
107		perror("fopen output_file");
108		exit(EXIT_FAILURE);
109	}
110
111	/* write some magic number */
112	len = fwrite(&magic, sizeof(uint32_t), 1, bin_file);
113	if (len != 1)
114		goto err;
115
116	/* write the version */
117	section_len = SELINUX_COMPILED_FCONTEXT_MAX_VERS;
118	len = fwrite(&section_len, sizeof(uint32_t), 1, bin_file);
119	if (len != 1)
120		goto err;
121
122	/* write the pcre version */
123	section_len = strlen(pcre_version());
124	len = fwrite(&section_len, sizeof(uint32_t), 1, bin_file);
125	if (len != 1)
126		goto err;
127	len = fwrite(pcre_version(), sizeof(char), section_len, bin_file);
128	if (len != section_len)
129		goto err;
130
131	/* write the number of stems coming */
132	section_len = data->num_stems;
133	len = fwrite(&section_len, sizeof(uint32_t), 1, bin_file);
134	if (len != 1)
135		goto err;
136
137	for (i = 0; i < section_len; i++) {
138		char *stem = data->stem_arr[i].buf;
139		uint32_t stem_len = data->stem_arr[i].len;
140
141		/* write the strlen (aka no nul) */
142		len = fwrite(&stem_len, sizeof(uint32_t), 1, bin_file);
143		if (len != 1)
144			goto err;
145
146		/* include the nul in the file */
147		stem_len += 1;
148		len = fwrite(stem, sizeof(char), stem_len, bin_file);
149		if (len != stem_len)
150			goto err;
151	}
152
153	/* write the number of regexes coming */
154	section_len = data->nspec;
155	len = fwrite(&section_len, sizeof(uint32_t), 1, bin_file);
156	if (len != 1)
157		goto err;
158
159	for (i = 0; i < section_len; i++) {
160		char *context = specs[i].lr.ctx_raw;
161		char *regex_str = specs[i].regex_str;
162		mode_t mode = specs[i].mode;
163		size_t prefix_len = specs[i].prefix_len;
164		int32_t stem_id = specs[i].stem_id;
165		pcre *re = specs[i].regex;
166		pcre_extra *sd = get_pcre_extra(&specs[i]);
167		uint32_t to_write;
168		size_t size;
169
170		/* length of the context string (including nul) */
171		to_write = strlen(context) + 1;
172		len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file);
173		if (len != 1)
174			goto err;
175
176		/* original context strin (including nul) */
177		len = fwrite(context, sizeof(char), to_write, bin_file);
178		if (len != to_write)
179			goto err;
180
181		/* length of the original regex string (including nul) */
182		to_write = strlen(regex_str) + 1;
183		len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file);
184		if (len != 1)
185			goto err;
186
187		/* original regex string */
188		len = fwrite(regex_str, sizeof(char), to_write, bin_file);
189		if (len != to_write)
190			goto err;
191
192		/* binary F_MODE bits */
193		to_write = mode;
194		len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file);
195		if (len != 1)
196			goto err;
197
198		/* stem for this regex (could be -1) */
199		len = fwrite(&stem_id, sizeof(stem_id), 1, bin_file);
200		if (len != 1)
201			goto err;
202
203		/* does this spec have a metaChar? */
204		to_write = specs[i].hasMetaChars;
205		len = fwrite(&to_write, sizeof(to_write), 1, bin_file);
206		if (len != 1)
207			goto err;
208
209		/* For SELINUX_COMPILED_FCONTEXT_PREFIX_LEN */
210		to_write = prefix_len;
211		len = fwrite(&to_write, sizeof(to_write), 1, bin_file);
212		if (len != 1)
213			goto err;
214
215		/* determine the size of the pcre data in bytes */
216		rc = pcre_fullinfo(re, NULL, PCRE_INFO_SIZE, &size);
217		if (rc < 0)
218			goto err;
219
220		/* write the number of bytes in the pcre data */
221		to_write = size;
222		len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file);
223		if (len != 1)
224			goto err;
225
226		/* write the actual pcre data as a char array */
227		len = fwrite(re, 1, to_write, bin_file);
228		if (len != to_write)
229			goto err;
230
231		/* determine the size of the pcre study info */
232		rc = pcre_fullinfo(re, sd, PCRE_INFO_STUDYSIZE, &size);
233		if (rc < 0)
234			goto err;
235
236		/* write the number of bytes in the pcre study data */
237		to_write = size;
238		len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file);
239		if (len != 1)
240			goto err;
241
242		/* write the actual pcre study data as a char array */
243		len = fwrite(sd->study_data, 1, to_write, bin_file);
244		if (len != to_write)
245			goto err;
246	}
247
248	rc = 0;
249out:
250	fclose(bin_file);
251	return rc;
252err:
253	rc = -1;
254	goto out;
255}
256
257static void free_specs(struct saved_data *data)
258{
259	struct spec *specs = data->spec_arr;
260	unsigned int num_entries = data->nspec;
261	unsigned int i;
262
263	for (i = 0; i < num_entries; i++) {
264		free(specs[i].lr.ctx_raw);
265		free(specs[i].lr.ctx_trans);
266		free(specs[i].regex_str);
267		free(specs[i].type_str);
268		pcre_free(specs[i].regex);
269		pcre_free_study(specs[i].sd);
270	}
271	free(specs);
272
273	num_entries = data->num_stems;
274	for (i = 0; i < num_entries; i++)
275		free(data->stem_arr[i].buf);
276	free(data->stem_arr);
277
278	memset(data, 0, sizeof(*data));
279}
280
281static void usage(const char *progname)
282{
283	fprintf(stderr,
284	    "usage: %s [-o out_file] [-p policy_file] fc_file\n"
285	    "Where:\n\t"
286	    "-o       Optional file name of the PCRE formatted binary\n\t"
287	    "         file to be output. If not specified the default\n\t"
288	    "         will be fc_file with the .bin suffix appended.\n\t"
289	    "-p       Optional binary policy file that will be used to\n\t"
290	    "         validate contexts defined in the fc_file.\n\t"
291	    "fc_file  The text based file contexts file to be processed.\n",
292	    progname);
293		exit(EXIT_FAILURE);
294}
295
296int main(int argc, char *argv[])
297{
298	const char *path = NULL;
299	const char *out_file = NULL;
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, "o:p:")) > 0) {
312		switch (opt) {
313		case 'o':
314			out_file = optarg;
315			break;
316		case 'p':
317			policy_file = optarg;
318			break;
319		default:
320			usage(argv[0]);
321		}
322	}
323
324	if (optind >= argc)
325		usage(argv[0]);
326
327	path = argv[optind];
328	if (stat(path, &buf) < 0) {
329		fprintf(stderr, "Can not stat: %s: %m\n", path);
330		exit(EXIT_FAILURE);
331	}
332
333	/* Open binary policy if supplied. */
334	if (policy_file) {
335		policy_fp = fopen(policy_file, "r");
336
337		if (!policy_fp) {
338			fprintf(stderr, "Failed to open policy: %s\n",
339							    policy_file);
340			exit(EXIT_FAILURE);
341		}
342
343		if (sepol_set_policydb_from_file(policy_fp) < 0) {
344			fprintf(stderr, "Failed to load policy: %s\n",
345							    policy_file);
346			fclose(policy_fp);
347			exit(EXIT_FAILURE);
348		}
349	}
350
351	/* Generate dummy handle for process_line() function */
352	rec = (struct selabel_handle *)calloc(1, sizeof(*rec));
353	if (!rec) {
354		fprintf(stderr, "Failed to calloc handle\n");
355		if (policy_fp)
356			fclose(policy_fp);
357		exit(EXIT_FAILURE);
358	}
359	rec->backend = SELABEL_CTX_FILE;
360
361	/* Need to set validation on to get the bin file generated by the
362	 * process_line function, however as the bin file being generated
363	 * may not be related to the currently loaded policy (that it
364	 * would be validated against), then set callback to ignore any
365	 * validation - unless the -p option is used in which case if an
366	 * error is detected, the process will be aborted. */
367	rec->validating = 1;
368	selinux_set_callback(SELINUX_CB_VALIDATE,
369			    (union selinux_callback)&validate_context);
370
371	data = (struct saved_data *)calloc(1, sizeof(*data));
372	if (!data) {
373		fprintf(stderr, "Failed to calloc saved_data\n");
374		free(rec);
375		if (policy_fp)
376			fclose(policy_fp);
377		exit(EXIT_FAILURE);
378	}
379
380	rec->data = data;
381
382	rc = process_file(rec, path);
383	if (rc < 0)
384		goto err;
385
386	rc = sort_specs(data);
387	if (rc)
388		goto err;
389
390	if (out_file)
391		rc = snprintf(stack_path, sizeof(stack_path), "%s", out_file);
392	else
393		rc = snprintf(stack_path, sizeof(stack_path), "%s.bin", path);
394
395	if (rc < 0 || rc >= (int)sizeof(stack_path))
396		goto err;
397
398	tmp = malloc(strlen(stack_path) + 7);
399	if (!tmp)
400		goto err;
401
402	rc = sprintf(tmp, "%sXXXXXX", stack_path);
403	if (rc < 0)
404		goto err;
405
406	fd  = mkstemp(tmp);
407	if (fd < 0)
408		goto err;
409
410	rc = fchmod(fd, buf.st_mode);
411	if (rc < 0) {
412		perror("fchmod failed to set permission on compiled regexs");
413		goto err_unlink;
414	}
415
416	rc = write_binary_file(data, fd);
417	if (rc < 0)
418		goto err_unlink;
419
420	rc = rename(tmp, stack_path);
421	if (rc < 0)
422		goto err_unlink;
423
424	rc = 0;
425out:
426	if (policy_fp)
427		fclose(policy_fp);
428
429	free_specs(data);
430	free(rec);
431	free(data);
432	free(tmp);
433	return rc;
434
435err_unlink:
436	unlink(tmp);
437err:
438	rc = -1;
439	goto out;
440}
441