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(§ion_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(§ion_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(§ion_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(§ion_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