• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
14 #include "../src/label_file.h"
15 
validate_context(char ** ctx)16 static int validate_context(char __attribute__ ((unused)) **ctx)
17 {
18 	return 0;
19 }
20 
process_file(struct selabel_handle * rec,const char * filename)21 static int process_file(struct selabel_handle *rec, const char *filename)
22 {
23 	unsigned int line_num;
24 	int rc;
25 	char *line_buf = NULL;
26 	size_t line_len = 0;
27 	FILE *context_file;
28 	const char *prefix = NULL;
29 
30 	context_file = fopen(filename, "r");
31 	if (!context_file) {
32 		fprintf(stderr, "Error opening %s: %s\n",
33 			    filename, strerror(errno));
34 		return -1;
35 	}
36 
37 	line_num = 0;
38 	rc = 0;
39 	while (getline(&line_buf, &line_len, context_file) > 0) {
40 		rc = process_line(rec, filename, prefix, line_buf, ++line_num);
41 		if (rc)
42 			goto out;
43 	}
44 out:
45 	free(line_buf);
46 	fclose(context_file);
47 	return rc;
48 }
49 
50 /*
51  * File Format
52  *
53  * u32 - magic number
54  * u32 - version
55  * u32 - length of pcre version EXCLUDING nul
56  * char - pcre version string EXCLUDING nul
57  * u32 - number of stems
58  * ** Stems
59  *	u32  - length of stem EXCLUDING nul
60  *	char - stem char array INCLUDING nul
61  * u32 - number of regexs
62  * ** Regexes
63  *	u32  - length of upcoming context INCLUDING nul
64  *	char - char array of the raw context
65  *	u32  - length of the upcoming regex_str
66  *	char - char array of the original regex string including the stem.
67  *	u32  - mode bits for >= SELINUX_COMPILED_FCONTEXT_MODE
68  *	       mode_t for <= SELINUX_COMPILED_FCONTEXT_PCRE_VERS
69  *	s32  - stemid associated with the regex
70  *	u32  - spec has meta characters
71  *	u32  - The specs prefix_len if >= SELINUX_COMPILED_FCONTEXT_PREFIX_LEN
72  *	u32  - data length of the pcre regex
73  *	char - a bufer holding the raw pcre regex info
74  *	u32  - data length of the pcre regex study daya
75  *	char - a buffer holding the raw pcre regex study data
76  */
write_binary_file(struct saved_data * data,int fd)77 static int write_binary_file(struct saved_data *data, int fd)
78 {
79 	struct spec *specs = data->spec_arr;
80 	FILE *bin_file;
81 	size_t len;
82 	uint32_t magic = SELINUX_MAGIC_COMPILED_FCONTEXT;
83 	uint32_t section_len;
84 	uint32_t i;
85 	int rc;
86 
87 	bin_file = fdopen(fd, "w");
88 	if (!bin_file) {
89 		perror("fopen output_file");
90 		exit(EXIT_FAILURE);
91 	}
92 
93 	/* write some magic number */
94 	len = fwrite(&magic, sizeof(uint32_t), 1, bin_file);
95 	if (len != 1)
96 		goto err;
97 
98 	/* write the version */
99 	section_len = SELINUX_COMPILED_FCONTEXT_MAX_VERS;
100 	len = fwrite(&section_len, sizeof(uint32_t), 1, bin_file);
101 	if (len != 1)
102 		goto err;
103 
104 	/* write the pcre version */
105 	section_len = strlen(pcre_version());
106 	len = fwrite(&section_len, sizeof(uint32_t), 1, bin_file);
107 	if (len != 1)
108 		goto err;
109 	len = fwrite(pcre_version(), sizeof(char), section_len, bin_file);
110 	if (len != section_len)
111 		goto err;
112 
113 	/* write the number of stems coming */
114 	section_len = data->num_stems;
115 	len = fwrite(&section_len, sizeof(uint32_t), 1, bin_file);
116 	if (len != 1)
117 		goto err;
118 
119 	for (i = 0; i < section_len; i++) {
120 		char *stem = data->stem_arr[i].buf;
121 		uint32_t stem_len = data->stem_arr[i].len;
122 
123 		/* write the strlen (aka no nul) */
124 		len = fwrite(&stem_len, sizeof(uint32_t), 1, bin_file);
125 		if (len != 1)
126 			goto err;
127 
128 		/* include the nul in the file */
129 		stem_len += 1;
130 		len = fwrite(stem, sizeof(char), stem_len, bin_file);
131 		if (len != stem_len)
132 			goto err;
133 	}
134 
135 	/* write the number of regexes coming */
136 	section_len = data->nspec;
137 	len = fwrite(&section_len, sizeof(uint32_t), 1, bin_file);
138 	if (len != 1)
139 		goto err;
140 
141 	for (i = 0; i < section_len; i++) {
142 		char *context = specs[i].lr.ctx_raw;
143 		char *regex_str = specs[i].regex_str;
144 		mode_t mode = specs[i].mode;
145 		size_t prefix_len = specs[i].prefix_len;
146 		int32_t stem_id = specs[i].stem_id;
147 		pcre *re = specs[i].regex;
148 		pcre_extra *sd = get_pcre_extra(&specs[i]);
149 		uint32_t to_write;
150 		size_t size;
151 
152 		/* length of the context string (including nul) */
153 		to_write = strlen(context) + 1;
154 		len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file);
155 		if (len != 1)
156 			goto err;
157 
158 		/* original context strin (including nul) */
159 		len = fwrite(context, sizeof(char), to_write, bin_file);
160 		if (len != to_write)
161 			goto err;
162 
163 		/* length of the original regex string (including nul) */
164 		to_write = strlen(regex_str) + 1;
165 		len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file);
166 		if (len != 1)
167 			goto err;
168 
169 		/* original regex string */
170 		len = fwrite(regex_str, sizeof(char), to_write, bin_file);
171 		if (len != to_write)
172 			goto err;
173 
174 		/* binary F_MODE bits */
175 		to_write = mode;
176 		len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file);
177 		if (len != 1)
178 			goto err;
179 
180 		/* stem for this regex (could be -1) */
181 		len = fwrite(&stem_id, sizeof(stem_id), 1, bin_file);
182 		if (len != 1)
183 			goto err;
184 
185 		/* does this spec have a metaChar? */
186 		to_write = specs[i].hasMetaChars;
187 		len = fwrite(&to_write, sizeof(to_write), 1, bin_file);
188 		if (len != 1)
189 			goto err;
190 
191 		/* For SELINUX_COMPILED_FCONTEXT_PREFIX_LEN */
192 		to_write = prefix_len;
193 		len = fwrite(&to_write, sizeof(to_write), 1, bin_file);
194 		if (len != 1)
195 			goto err;
196 
197 		/* determine the size of the pcre data in bytes */
198 		rc = pcre_fullinfo(re, NULL, PCRE_INFO_SIZE, &size);
199 		if (rc < 0)
200 			goto err;
201 
202 		/* write the number of bytes in the pcre data */
203 		to_write = size;
204 		len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file);
205 		if (len != 1)
206 			goto err;
207 
208 		/* write the actual pcre data as a char array */
209 		len = fwrite(re, 1, to_write, bin_file);
210 		if (len != to_write)
211 			goto err;
212 
213 		/* determine the size of the pcre study info */
214 		rc = pcre_fullinfo(re, sd, PCRE_INFO_STUDYSIZE, &size);
215 		if (rc < 0)
216 			goto err;
217 
218 		/* write the number of bytes in the pcre study data */
219 		to_write = size;
220 		len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file);
221 		if (len != 1)
222 			goto err;
223 
224 		/* write the actual pcre study data as a char array */
225 		len = fwrite(sd->study_data, 1, to_write, bin_file);
226 		if (len != to_write)
227 			goto err;
228 	}
229 
230 	rc = 0;
231 out:
232 	fclose(bin_file);
233 	return rc;
234 err:
235 	rc = -1;
236 	goto out;
237 }
238 
free_specs(struct saved_data * data)239 static void free_specs(struct saved_data *data)
240 {
241 	struct spec *specs = data->spec_arr;
242 	unsigned int num_entries = data->nspec;
243 	unsigned int i;
244 
245 	for (i = 0; i < num_entries; i++) {
246 		free(specs[i].lr.ctx_raw);
247 		free(specs[i].lr.ctx_trans);
248 		free(specs[i].regex_str);
249 		free(specs[i].type_str);
250 		pcre_free(specs[i].regex);
251 		pcre_free_study(specs[i].sd);
252 	}
253 	free(specs);
254 
255 	num_entries = data->num_stems;
256 	for (i = 0; i < num_entries; i++)
257 		free(data->stem_arr[i].buf);
258 	free(data->stem_arr);
259 
260 	memset(data, 0, sizeof(*data));
261 }
262 
usage(const char * progname)263 static void usage(const char *progname)
264 {
265 	fprintf(stderr,
266 		"usage: %s [-o out_file] fc_file\n"
267 		"Where:\n\t"
268 		"-o      Optional file name of the PCRE formatted binary\n\t"
269 		"        file to be output. If not specified the default\n\t"
270 		"        will be fc_file with the .bin suffix appended.\n\t"
271 		"fc_file The text based file contexts file to be processed.\n",
272 		progname);
273 		exit(EXIT_FAILURE);
274 }
275 
main(int argc,char * argv[])276 int main(int argc, char *argv[])
277 {
278 	const char *path = NULL;
279 	const char *out_file = NULL;
280 	char stack_path[PATH_MAX + 1];
281 	char *tmp = NULL;
282 	int fd, rc, opt;
283 	struct stat buf;
284 	struct selabel_handle *rec = NULL;
285 	struct saved_data *data = NULL;
286 
287 	if (argc < 2)
288 		usage(argv[0]);
289 
290 	while ((opt = getopt(argc, argv, "o:")) > 0) {
291 		switch (opt) {
292 		case 'o':
293 			out_file = optarg;
294 			break;
295 		default:
296 			usage(argv[0]);
297 		}
298 	}
299 
300 	if (optind >= argc)
301 		usage(argv[0]);
302 
303 	path = argv[optind];
304 	if (stat(path, &buf) < 0) {
305 		fprintf(stderr, "Can not stat: %s: %m\n", path);
306 		exit(EXIT_FAILURE);
307 	}
308 
309 	/* Generate dummy handle for process_line() function */
310 	rec = (struct selabel_handle *)calloc(1, sizeof(*rec));
311 	if (!rec) {
312 		fprintf(stderr, "Failed to calloc handle\n");
313 		exit(EXIT_FAILURE);
314 	}
315 	rec->backend = SELABEL_CTX_FILE;
316 
317 	/* Need to set validation on to get the bin file generated by the
318 	 * process_line function, however as the bin file being generated
319 	 * may not be related to the currently loaded policy (that it
320 	 * would be validated against), then set callback to ignore any
321 	 * validation. */
322 	rec->validating = 1;
323 	selinux_set_callback(SELINUX_CB_VALIDATE,
324 			    (union selinux_callback)&validate_context);
325 
326 	data = (struct saved_data *)calloc(1, sizeof(*data));
327 	if (!data) {
328 		fprintf(stderr, "Failed to calloc saved_data\n");
329 		free(rec);
330 		exit(EXIT_FAILURE);
331 	}
332 
333 	rec->data = data;
334 
335 	rc = process_file(rec, path);
336 	if (rc < 0)
337 		goto err;
338 
339 	rc = sort_specs(data);
340 	if (rc)
341 		goto err;
342 
343 	if (out_file)
344 		rc = snprintf(stack_path, sizeof(stack_path), "%s", out_file);
345 	else
346 		rc = snprintf(stack_path, sizeof(stack_path), "%s.bin", path);
347 
348 	if (rc < 0 || rc >= (int)sizeof(stack_path))
349 		goto err;
350 
351 	tmp = malloc(strlen(stack_path) + 7);
352 	if (!tmp)
353 		goto err;
354 
355 	rc = sprintf(tmp, "%sXXXXXX", stack_path);
356 	if (rc < 0)
357 		goto err;
358 
359 	fd  = mkstemp(tmp);
360 	if (fd < 0)
361 		goto err;
362 
363 	rc = fchmod(fd, buf.st_mode);
364 	if (rc < 0) {
365 		perror("fchmod failed to set permission on compiled regexs");
366 		goto err_unlink;
367 	}
368 
369 	rc = write_binary_file(data, fd);
370 	if (rc < 0)
371 		goto err_unlink;
372 
373 	rc = rename(tmp, stack_path);
374 	if (rc < 0)
375 		goto err_unlink;
376 
377 	rc = 0;
378 out:
379 	free_specs(data);
380 	free(rec);
381 	free(data);
382 	free(tmp);
383 	return rc;
384 
385 err_unlink:
386 	unlink(tmp);
387 err:
388 	rc = -1;
389 	goto out;
390 }
391