blob: 50f18374cc435076a72c0e80de96271d52be2b2f [file] [log] [blame]
William Robertsf0e0a942012-08-27 15:41:15 -07001#include <stdio.h>
2#include <stdarg.h>
3#include <ctype.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <unistd.h>
7#include <string.h>
8#include <errno.h>
9#include <stdint.h>
10#include <search.h>
William Roberts61846292013-10-15 09:38:24 -070011#include <stdbool.h>
William Robertsf0e0a942012-08-27 15:41:15 -070012#include <sepol/sepol.h>
13#include <sepol/policydb/policydb.h>
William Roberts81e1f902015-06-03 21:57:47 -070014#include <pcre.h>
William Robertsf0e0a942012-08-27 15:41:15 -070015
16#define TABLE_SIZE 1024
17#define KVP_NUM_OF_RULES (sizeof(rules) / sizeof(key_map))
18#define log_set_verbose() do { logging_verbose = 1; log_info("Enabling verbose\n"); } while(0)
19#define log_error(fmt, ...) log_msg(stderr, "Error: ", fmt, ##__VA_ARGS__)
20#define log_warn(fmt, ...) log_msg(stderr, "Warning: ", fmt, ##__VA_ARGS__)
21#define log_info(fmt, ...) if (logging_verbose ) { log_msg(stdout, "Info: ", fmt, ##__VA_ARGS__); }
22
William Roberts81e1f902015-06-03 21:57:47 -070023/**
24 * Initializes an empty, static list.
25 */
26#define list_init(free_fn) { .head = NULL, .tail = NULL, .freefn = free_fn }
27
28/**
29 * given an item in the list, finds the offset for the container
30 * it was stored in.
31 *
32 * @element The element from the list
33 * @type The container type ie what you allocated that has the list_element structure in it.
34 * @name The name of the field that is the list_element
35 *
36 */
37#define list_entry(element, type, name) \
38 (type *)(((uint8_t *)element) - (uint8_t *)&(((type *)NULL)->name))
39
40/**
41 * Iterates over the list, do not free elements from the list when using this.
42 * @list The list head to walk
43 * @var The variable name for the cursor
44 */
45#define list_for_each(list, var) \
46 for(var = (list)->head; var != NULL; var = var->next)
47
48
William Robertsf0e0a942012-08-27 15:41:15 -070049typedef struct hash_entry hash_entry;
50typedef enum key_dir key_dir;
51typedef enum data_type data_type;
52typedef enum rule_map_switch rule_map_switch;
William Roberts0ae3a8a2012-09-04 11:51:04 -070053typedef enum map_match map_match;
William Robertsf0e0a942012-08-27 15:41:15 -070054typedef struct key_map key_map;
55typedef struct kvp kvp;
56typedef struct rule_map rule_map;
57typedef struct policy_info policy_info;
William Roberts81e1f902015-06-03 21:57:47 -070058typedef struct list_element list_element;
59typedef struct list list;
60typedef struct key_map_regex key_map_regex;
61typedef struct file_info file_info;
William Robertsf0e0a942012-08-27 15:41:15 -070062
William Roberts0ae3a8a2012-09-04 11:51:04 -070063enum map_match {
64 map_no_matches,
65 map_input_matched,
66 map_matched
67};
68
Stephen Smalley0b820042015-02-13 14:58:31 -050069const char *map_match_str[] = {
70 "do not match",
71 "match on all inputs",
72 "match on everything"
73};
74
William Robertsf0e0a942012-08-27 15:41:15 -070075/**
76 * Whether or not the "key" from a key vaue pair is considered an
77 * input or an output.
78 */
79enum key_dir {
80 dir_in, dir_out
81};
82
83/**
William Robertsf0e0a942012-08-27 15:41:15 -070084 * The expected "type" of data the value in the key
85 * value pair should be.
86 */
87enum data_type {
88 dt_bool, dt_string
89};
90
William Roberts81e1f902015-06-03 21:57:47 -070091struct list_element {
92 list_element *next;
93};
94
95struct list {
96 list_element *head;
97 list_element *tail;
98 void (*freefn)(list_element *e);
99};
100
101struct key_map_regex {
102 pcre *compiled;
103 pcre_extra *extra;
William Robertsf0e0a942012-08-27 15:41:15 -0700104};
105
106/**
107 * The workhorse of the logic. This struct maps key value pairs to
108 * an associated set of meta data maintained in rule_map_new()
109 */
110struct key_map {
111 char *name;
112 key_dir dir;
113 data_type type;
114 char *data;
William Roberts81e1f902015-06-03 21:57:47 -0700115 key_map_regex regex;
William Roberts696a66b2016-01-29 10:41:50 -0800116 bool (*fn_validate)(char *value, char **errmsg);
William Robertsf0e0a942012-08-27 15:41:15 -0700117};
118
119/**
120 * Key value pair struct, this represents the raw kvp values coming
121 * from the rules files.
122 */
123struct kvp {
124 char *key;
125 char *value;
126};
127
128/**
129 * Rules are made up of meta data and an associated set of kvp stored in a
130 * key_map array.
131 */
132struct rule_map {
William Roberts81e1f902015-06-03 21:57:47 -0700133 bool is_never_allow;
134 list violations;
135 list_element listify;
William Robertsf0e0a942012-08-27 15:41:15 -0700136 char *key; /** key value before hashing */
William Roberts610a4b12013-10-15 18:26:00 -0700137 size_t length; /** length of the key map */
William Robertsf0e0a942012-08-27 15:41:15 -0700138 int lineno; /** Line number rule was encounter on */
William Roberts81e1f902015-06-03 21:57:47 -0700139 char *filename; /** File it was found in */
William Robertsf0e0a942012-08-27 15:41:15 -0700140 key_map m[]; /** key value mapping */
141};
142
143struct hash_entry {
William Roberts81e1f902015-06-03 21:57:47 -0700144 list_element listify;
William Robertsf0e0a942012-08-27 15:41:15 -0700145 rule_map *r; /** The rule map to store at that location */
146};
147
148/**
149 * Data associated for a policy file
150 */
151struct policy_info {
152
153 char *policy_file_name; /** policy file path name */
154 FILE *policy_file; /** file handle to the policy file */
155 sepol_policydb_t *db;
156 sepol_policy_file_t *pf;
157 sepol_handle_t *handle;
158 sepol_context_t *con;
159};
160
William Roberts81e1f902015-06-03 21:57:47 -0700161struct file_info {
162 FILE *file; /** file itself */
163 const char *name; /** name of file. do not free, these are not alloc'd */
164 list_element listify;
165};
166
167static void input_file_list_freefn(list_element *e);
168static void line_order_list_freefn(list_element *e);
169static void rule_map_free(rule_map *rm, bool is_in_htable);
170
William Robertsf0e0a942012-08-27 15:41:15 -0700171/** Set to !0 to enable verbose logging */
172static int logging_verbose = 0;
173
174/** file handle to the output file */
William Roberts81e1f902015-06-03 21:57:47 -0700175static file_info out_file;
William Robertsf0e0a942012-08-27 15:41:15 -0700176
William Roberts81e1f902015-06-03 21:57:47 -0700177static list input_file_list = list_init(input_file_list_freefn);
William Robertsf0e0a942012-08-27 15:41:15 -0700178
179static policy_info pol = {
180 .policy_file_name = NULL,
181 .policy_file = NULL,
182 .db = NULL,
183 .pf = NULL,
184 .handle = NULL,
185 .con = NULL
186};
187
188/**
William Roberts81e1f902015-06-03 21:57:47 -0700189 * Head pointer to a linked list of
190 * rule map table entries (hash_entry), used for
191 * preserving the order of entries
192 * based on "first encounter"
193 */
194static list line_order_list = list_init(line_order_list_freefn);
195
196/*
197 * List of hash_entrys for never allow rules.
198 */
199static list nallow_list = list_init(line_order_list_freefn);
200
William Roberts696a66b2016-01-29 10:41:50 -0800201/* validation call backs */
202static bool validate_bool(char *value, char **errmsg);
203static bool validate_levelFrom(char *value, char **errmsg);
204static bool validate_selinux_type(char *value, char **errmsg);
205static bool validate_selinux_level(char *value, char **errmsg);
206
William Roberts81e1f902015-06-03 21:57:47 -0700207/**
William Robertsf0e0a942012-08-27 15:41:15 -0700208 * The heart of the mapping process, this must be updated if a new key value pair is added
209 * to a rule.
210 */
211key_map rules[] = {
212 /*Inputs*/
William Roberts696a66b2016-01-29 10:41:50 -0800213 { .name = "isSystemServer", .type = dt_bool, .dir = dir_in, .data = NULL, .fn_validate = validate_bool },
214 { .name = "isOwner", .type = dt_bool, .dir = dir_in, .data = NULL, .fn_validate = validate_bool },
215 { .name = "user", .type = dt_string, .dir = dir_in, .data = NULL },
216 { .name = "seinfo", .type = dt_string, .dir = dir_in, .data = NULL },
217 { .name = "name", .type = dt_string, .dir = dir_in, .data = NULL },
218 { .name = "path", .type = dt_string, .dir = dir_in, .data = NULL },
219 { .name = "isPrivApp", .type = dt_bool, .dir = dir_in, .data = NULL, .fn_validate = validate_bool },
William Robertsf0e0a942012-08-27 15:41:15 -0700220 /*Outputs*/
William Roberts696a66b2016-01-29 10:41:50 -0800221 { .name = "domain", .type = dt_string, .dir = dir_out, .data = NULL, .fn_validate = validate_selinux_type },
222 { .name = "type", .type = dt_string, .dir = dir_out, .data = NULL, .fn_validate = validate_selinux_type },
223 { .name = "levelFromUid", .type = dt_bool, .dir = dir_out, .data = NULL, .fn_validate = validate_bool },
224 { .name = "levelFrom", .type = dt_string, .dir = dir_out, .data = NULL, .fn_validate = validate_levelFrom },
225 { .name = "level", .type = dt_string, .dir = dir_out, .data = NULL, .fn_validate = validate_selinux_level },
William Robertsfff29802012-11-27 14:20:34 -0800226};
William Robertsf0e0a942012-08-27 15:41:15 -0700227
228/**
William Roberts81e1f902015-06-03 21:57:47 -0700229 * Appends to the end of the list.
230 * @list The list to append to
231 * @e the element to append
William Robertsf0e0a942012-08-27 15:41:15 -0700232 */
William Roberts81e1f902015-06-03 21:57:47 -0700233void list_append(list *list, list_element *e) {
234
235 memset(e, 0, sizeof(*e));
236
237 if (list->head == NULL ) {
238 list->head = list->tail = e;
239 return;
240 }
241
242 list->tail->next = e;
243 list->tail = e;
244 return;
245}
William Robertsf0e0a942012-08-27 15:41:15 -0700246
247/**
William Roberts81e1f902015-06-03 21:57:47 -0700248 * Free's all the elements in the specified list.
249 * @list The list to free
William Robertsf0e0a942012-08-27 15:41:15 -0700250 */
William Roberts81e1f902015-06-03 21:57:47 -0700251static void list_free(list *list) {
252
253 list_element *tmp;
254 list_element *cursor = list->head;
255
256 while (cursor) {
257 tmp = cursor;
258 cursor = cursor->next;
259 if (list->freefn) {
260 list->freefn(tmp);
261 }
262 }
263}
264
265/*
266 * called when the lists are freed
267 */
268static void line_order_list_freefn(list_element *e) {
269 hash_entry *h = list_entry(e, typeof(*h), listify);
270 rule_map_free(h->r, true);
271 free(h);
272}
273
274static void input_file_list_freefn(list_element *e) {
275 file_info *f = list_entry(e, typeof(*f), listify);
276
277 if (f->file) {
278 fclose(f->file);
279 }
280 free(f);
281}
William Robertsf0e0a942012-08-27 15:41:15 -0700282
283/**
284 * Send a logging message to a file
285 * @param out
286 * Output file to send message too
287 * @param prefix
288 * A special prefix to write to the file, such as "Error:"
289 * @param fmt
290 * The printf style formatter to use, such as "%d"
291 */
William Roberts1e8c0612013-04-19 19:06:02 -0700292static void __attribute__ ((format(printf, 3, 4)))
293log_msg(FILE *out, const char *prefix, const char *fmt, ...) {
294
William Robertsf0e0a942012-08-27 15:41:15 -0700295 fprintf(out, "%s", prefix);
296 va_list args;
297 va_start(args, fmt);
298 vfprintf(out, fmt, args);
299 va_end(args);
300}
301
302/**
303 * Checks for a type in the policy.
304 * @param db
305 * The policy db to search
306 * @param type
307 * The type to search for
308 * @return
309 * 1 if the type is found, 0 otherwise.
310 * @warning
311 * This function always returns 1 if libsepol is not linked
312 * statically to this executable and LINK_SEPOL_STATIC is not
313 * defined.
314 */
William Roberts25528cf2016-01-29 10:32:34 -0800315static int check_type(sepol_policydb_t *db, char *type) {
William Robertsf0e0a942012-08-27 15:41:15 -0700316
317 int rc = 1;
318#if defined(LINK_SEPOL_STATIC)
319 policydb_t *d = (policydb_t *)db;
320 hashtab_datum_t dat;
321 dat = hashtab_search(d->p_types.table, type);
322 rc = (dat == NULL) ? 0 : 1;
323#endif
324 return rc;
325}
326
William Roberts81e1f902015-06-03 21:57:47 -0700327static bool match_regex(key_map *assert, const key_map *check) {
328
329 char *tomatch = check->data;
330
331 int ret = pcre_exec(assert->regex.compiled, assert->regex.extra, tomatch,
332 strlen(tomatch), 0, 0, NULL, 0);
333
334 /* 0 from pcre_exec means matched */
335 return !ret;
336}
337
338static bool compile_regex(key_map *km, const char **errbuf, int *erroff) {
339
340 size_t size;
341 char *anchored;
342
343 /*
344 * Explicitly anchor all regex's
345 * The size is the length of the string to anchor (km->data), the anchor
346 * characters ^ and $ and the null byte. Hence strlen(km->data) + 3
347 */
348 size = strlen(km->data) + 3;
349 anchored = alloca(size);
350 sprintf(anchored, "^%s$", km->data);
351
352 km->regex.compiled = pcre_compile(anchored, PCRE_DOTALL, errbuf, erroff,
353 NULL );
354 if (!km->regex.compiled) {
355 return false;
356 }
357
358 km->regex.extra = pcre_study(km->regex.compiled, 0, errbuf);
359 return true;
360}
361
William Roberts696a66b2016-01-29 10:41:50 -0800362static bool validate_bool(char *value, char **errmsg) {
363
364 if (!strcmp("true", value) || !strcmp("false", value)) {
365 return true;
366 }
367
368 *errmsg = "Expecting \"true\" or \"false\"";
369 return false;
370}
371
372static bool validate_levelFrom(char *value, char **errmsg) {
373
374 if(strcasecmp(value, "none") && strcasecmp(value, "all") &&
375 strcasecmp(value, "app") && strcasecmp(value, "user")) {
376 *errmsg = "Expecting one of: \"none\", \"all\", \"app\" or \"user\"";
377 return false;
378 }
379 return true;
380}
381
382static bool validate_selinux_type(char *value, char **errmsg) {
383
384 /*
385 * No policy file present means we cannot check
386 * SE Linux types
387 */
388 if (!pol.policy_file) {
389 return true;
390 }
391
392 if(!check_type(pol.db, value)) {
393 *errmsg = "Expecting a valid SELinux type";
394 return false;
395 }
396
397 return true;
398}
399
400static bool validate_selinux_level(char *value, char **errmsg) {
401
402 /*
403 * No policy file present means we cannot check
404 * SE Linux MLS
405 */
406 if (!pol.policy_file) {
407 return true;
408 }
409
410 int ret = sepol_mls_check(pol.handle, pol.db, value);
411 if (ret < 0) {
412 *errmsg = "Expecting a valid SELinux MLS value";
413 return false;
414 }
415
416 return true;
417}
418
William Robertsf0e0a942012-08-27 15:41:15 -0700419/**
420 * Validates a key_map against a set of enforcement rules, this
421 * function exits the application on a type that cannot be properly
422 * checked
423 *
424 * @param m
425 * The key map to check
426 * @param lineno
427 * The line number in the source file for the corresponding key map
William Robertsfff29802012-11-27 14:20:34 -0800428 * @return
William Roberts81e1f902015-06-03 21:57:47 -0700429 * true if valid, false if invalid
William Robertsf0e0a942012-08-27 15:41:15 -0700430 */
William Roberts81e1f902015-06-03 21:57:47 -0700431static bool key_map_validate(key_map *m, const char *filename, int lineno,
432 bool is_neverallow) {
William Robertsf0e0a942012-08-27 15:41:15 -0700433
William Roberts81e1f902015-06-03 21:57:47 -0700434 int erroff;
435 const char *errbuf;
436 bool rc = true;
William Robertsf0e0a942012-08-27 15:41:15 -0700437 char *key = m->name;
438 char *value = m->data;
William Roberts696a66b2016-01-29 10:41:50 -0800439 char *errmsg = NULL;
William Robertsf0e0a942012-08-27 15:41:15 -0700440
William Roberts0ae3a8a2012-09-04 11:51:04 -0700441 log_info("Validating %s=%s\n", key, value);
442
William Roberts81e1f902015-06-03 21:57:47 -0700443 /*
444 * Neverallows are completely skipped from sanity checking so you can match
445 * un-unspecified inputs.
446 */
447 if (is_neverallow) {
448 if (!m->regex.compiled) {
449 rc = compile_regex(m, &errbuf, &erroff);
450 if (!rc) {
451 log_error("Invalid regex on line %d : %s PCRE error: %s at offset %d",
452 lineno, value, errbuf, erroff);
453 }
454 }
455 goto out;
456 }
457
William Roberts696a66b2016-01-29 10:41:50 -0800458 /* If the key has a validation routine, call it */
459 if (m->fn_validate) {
460 rc = m->fn_validate(value, &errmsg);
William Robertsf0e0a942012-08-27 15:41:15 -0700461
William Roberts696a66b2016-01-29 10:41:50 -0800462 if (!rc) {
463 log_error("Could not validate key \"%s\" for value \"%s\" on line: %d in file: \"%s\": %s\n", key, value,
464 lineno, filename, errmsg);
William Robertsf0e0a942012-08-27 15:41:15 -0700465 }
466 }
467
William Roberts0ae3a8a2012-09-04 11:51:04 -0700468out:
469 log_info("Key map validate returning: %d\n", rc);
470 return rc;
William Robertsf0e0a942012-08-27 15:41:15 -0700471}
472
473/**
474 * Prints a rule map back to a file
475 * @param fp
476 * The file handle to print too
477 * @param r
478 * The rule map to print
479 */
480static void rule_map_print(FILE *fp, rule_map *r) {
481
William Roberts610a4b12013-10-15 18:26:00 -0700482 size_t i;
William Robertsf0e0a942012-08-27 15:41:15 -0700483 key_map *m;
484
485 for (i = 0; i < r->length; i++) {
486 m = &(r->m[i]);
487 if (i < r->length - 1)
488 fprintf(fp, "%s=%s ", m->name, m->data);
489 else
490 fprintf(fp, "%s=%s", m->name, m->data);
491 }
492}
493
494/**
495 * Compare two rule maps for equality
496 * @param rmA
497 * a rule map to check
498 * @param rmB
499 * a rule map to check
500 * @return
William Robertsae23a1f2012-09-05 12:53:52 -0700501 * a map_match enum indicating the result
William Robertsf0e0a942012-08-27 15:41:15 -0700502 */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700503static map_match rule_map_cmp(rule_map *rmA, rule_map *rmB) {
William Robertsf0e0a942012-08-27 15:41:15 -0700504
William Roberts610a4b12013-10-15 18:26:00 -0700505 size_t i;
506 size_t j;
William Robertsf0e0a942012-08-27 15:41:15 -0700507 int inputs_found = 0;
508 int num_of_matched_inputs = 0;
509 int input_mode = 0;
William Roberts610a4b12013-10-15 18:26:00 -0700510 size_t matches = 0;
William Robertsf0e0a942012-08-27 15:41:15 -0700511 key_map *mA;
512 key_map *mB;
513
William Robertsf0e0a942012-08-27 15:41:15 -0700514 for (i = 0; i < rmA->length; i++) {
515 mA = &(rmA->m[i]);
516
517 for (j = 0; j < rmB->length; j++) {
518 mB = &(rmB->m[j]);
519 input_mode = 0;
520
521 if (mA->type != mB->type)
522 continue;
523
524 if (strcmp(mA->name, mB->name))
525 continue;
526
527 if (strcmp(mA->data, mB->data))
528 continue;
529
530 if (mB->dir != mA->dir)
531 continue;
532 else if (mB->dir == dir_in) {
533 input_mode = 1;
534 inputs_found++;
535 }
536
William Roberts0ae3a8a2012-09-04 11:51:04 -0700537 if (input_mode) {
William Roberts1e8c0612013-04-19 19:06:02 -0700538 log_info("Matched input lines: name=%s data=%s\n", mA->name, mA->data);
William Robertsf0e0a942012-08-27 15:41:15 -0700539 num_of_matched_inputs++;
William Roberts0ae3a8a2012-09-04 11:51:04 -0700540 }
William Robertsf0e0a942012-08-27 15:41:15 -0700541
542 /* Match found, move on */
William Roberts1e8c0612013-04-19 19:06:02 -0700543 log_info("Matched lines: name=%s data=%s", mA->name, mA->data);
William Robertsf0e0a942012-08-27 15:41:15 -0700544 matches++;
545 break;
546 }
547 }
548
549 /* If they all matched*/
William Roberts0ae3a8a2012-09-04 11:51:04 -0700550 if (matches == rmA->length) {
551 log_info("Rule map cmp MATCH\n");
552 return map_matched;
553 }
William Robertsf0e0a942012-08-27 15:41:15 -0700554
555 /* They didn't all match but the input's did */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700556 else if (num_of_matched_inputs == inputs_found) {
557 log_info("Rule map cmp INPUT MATCH\n");
558 return map_input_matched;
559 }
William Robertsf0e0a942012-08-27 15:41:15 -0700560
561 /* They didn't all match, and the inputs didn't match, ie it didn't
562 * match */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700563 else {
564 log_info("Rule map cmp NO MATCH\n");
565 return map_no_matches;
566 }
William Robertsf0e0a942012-08-27 15:41:15 -0700567}
568
569/**
570 * Frees a rule map
571 * @param rm
572 * rule map to be freed.
William Roberts7d65b542015-06-19 09:43:28 -0700573 * @is_in_htable
574 * True if the rule map has been added to the hash table, false
575 * otherwise.
576 */
577static void rule_map_free(rule_map *rm, bool is_in_htable) {
William Robertsf0e0a942012-08-27 15:41:15 -0700578
William Roberts610a4b12013-10-15 18:26:00 -0700579 size_t i;
580 size_t len = rm->length;
William Robertsf0e0a942012-08-27 15:41:15 -0700581 for (i = 0; i < len; i++) {
582 key_map *m = &(rm->m[i]);
583 free(m->data);
William Roberts81e1f902015-06-03 21:57:47 -0700584
585 if (m->regex.compiled) {
586 pcre_free(m->regex.compiled);
587 }
588
589 if (m->regex.extra) {
590 pcre_free_study(m->regex.extra);
591 }
William Robertsf0e0a942012-08-27 15:41:15 -0700592 }
593
William Roberts7d65b542015-06-19 09:43:28 -0700594 /*
595 * hdestroy() frees comparsion keys for non glibc
596 * on GLIBC we always free on NON-GLIBC we free if
597 * it is not in the htable.
598 */
599 if (rm->key) {
rpcraig5dbfdc02012-10-23 11:03:47 -0400600#ifdef __GLIBC__
William Roberts7d65b542015-06-19 09:43:28 -0700601 /* silence unused warning */
602 (void)is_in_htable;
William Robertsf0e0a942012-08-27 15:41:15 -0700603 free(rm->key);
William Roberts7d65b542015-06-19 09:43:28 -0700604#else
605 if (!is_in_htable) {
606 free(rm->key);
607 }
rpcraig5dbfdc02012-10-23 11:03:47 -0400608#endif
William Roberts7d65b542015-06-19 09:43:28 -0700609 }
William Robertsf0e0a942012-08-27 15:41:15 -0700610
William Roberts81e1f902015-06-03 21:57:47 -0700611 free(rm->filename);
William Robertsf0e0a942012-08-27 15:41:15 -0700612 free(rm);
613}
614
615static void free_kvp(kvp *k) {
616 free(k->key);
617 free(k->value);
618}
619
620/**
William Roberts61846292013-10-15 09:38:24 -0700621 * Checks a rule_map for any variation of KVP's that shouldn't be allowed.
William Roberts81e1f902015-06-03 21:57:47 -0700622 * It builds an assertion failure list for each rule map.
William Roberts61846292013-10-15 09:38:24 -0700623 * Note that this function logs all errors.
624 *
625 * Current Checks:
626 * 1. That a specified name entry should have a specified seinfo entry as well.
William Roberts81e1f902015-06-03 21:57:47 -0700627 * 2. That no rule violates a neverallow
William Roberts61846292013-10-15 09:38:24 -0700628 * @param rm
629 * The rule map to check for validity.
William Roberts61846292013-10-15 09:38:24 -0700630 */
William Roberts81e1f902015-06-03 21:57:47 -0700631static void rule_map_validate(rule_map *rm) {
William Roberts61846292013-10-15 09:38:24 -0700632
William Roberts81e1f902015-06-03 21:57:47 -0700633 size_t i, j;
634 const key_map *rule;
635 key_map *nrule;
636 hash_entry *e;
637 rule_map *assert;
638 list_element *cursor;
William Roberts61846292013-10-15 09:38:24 -0700639
William Roberts81e1f902015-06-03 21:57:47 -0700640 list_for_each(&nallow_list, cursor) {
641 e = list_entry(cursor, typeof(*e), listify);
642 assert = e->r;
William Roberts61846292013-10-15 09:38:24 -0700643
William Roberts81e1f902015-06-03 21:57:47 -0700644 size_t cnt = 0;
645
646 for (j = 0; j < assert->length; j++) {
647 nrule = &(assert->m[j]);
648
649 // mark that nrule->name is for a null check
650 bool is_null_check = !strcmp(nrule->data, "\"\"");
651
652 for (i = 0; i < rm->length; i++) {
653 rule = &(rm->m[i]);
654
655 if (!strcmp(rule->name, nrule->name)) {
656
657 /* the name was found, (data cannot be false) then it was specified */
658 is_null_check = false;
659
660 if (match_regex(nrule, rule)) {
661 cnt++;
662 }
663 }
664 }
665
666 /*
667 * the nrule was marked in a null check and we never found a match on nrule, thus
668 * it matched and we update the cnt
669 */
670 if (is_null_check) {
671 cnt++;
672 }
William Roberts61846292013-10-15 09:38:24 -0700673 }
William Roberts81e1f902015-06-03 21:57:47 -0700674 if (cnt == assert->length) {
675 list_append(&rm->violations, &assert->listify);
William Roberts61846292013-10-15 09:38:24 -0700676 }
677 }
William Roberts61846292013-10-15 09:38:24 -0700678}
679
680/**
William Robertsf0e0a942012-08-27 15:41:15 -0700681 * Given a set of key value pairs, this will construct a new rule map.
682 * On error this function calls exit.
683 * @param keys
684 * Keys from a rule line to map
685 * @param num_of_keys
686 * The length of the keys array
687 * @param lineno
688 * The line number the keys were extracted from
689 * @return
690 * A rule map pointer.
691 */
William Roberts81e1f902015-06-03 21:57:47 -0700692static rule_map *rule_map_new(kvp keys[], size_t num_of_keys, int lineno,
693 const char *filename, bool is_never_allow) {
William Robertsf0e0a942012-08-27 15:41:15 -0700694
William Roberts610a4b12013-10-15 18:26:00 -0700695 size_t i = 0, j = 0;
William Robertsf0e0a942012-08-27 15:41:15 -0700696 rule_map *new_map = NULL;
697 kvp *k = NULL;
698 key_map *r = NULL, *x = NULL;
Stephen Smalley534fb072015-02-13 14:06:08 -0500699 bool seen[KVP_NUM_OF_RULES];
700
701 for (i = 0; i < KVP_NUM_OF_RULES; i++)
702 seen[i] = false;
William Robertsf0e0a942012-08-27 15:41:15 -0700703
704 new_map = calloc(1, (num_of_keys * sizeof(key_map)) + sizeof(rule_map));
705 if (!new_map)
706 goto oom;
707
William Roberts81e1f902015-06-03 21:57:47 -0700708 new_map->is_never_allow = is_never_allow;
William Robertsf0e0a942012-08-27 15:41:15 -0700709 new_map->length = num_of_keys;
710 new_map->lineno = lineno;
William Roberts81e1f902015-06-03 21:57:47 -0700711 new_map->filename = strdup(filename);
712 if (!new_map->filename) {
713 goto oom;
714 }
William Robertsf0e0a942012-08-27 15:41:15 -0700715
716 /* For all the keys in a rule line*/
717 for (i = 0; i < num_of_keys; i++) {
718 k = &(keys[i]);
719 r = &(new_map->m[i]);
720
721 for (j = 0; j < KVP_NUM_OF_RULES; j++) {
722 x = &(rules[j]);
723
724 /* Only assign key name to map name */
725 if (strcasecmp(k->key, x->name)) {
726 if (i == KVP_NUM_OF_RULES) {
727 log_error("No match for key: %s\n", k->key);
728 goto err;
729 }
730 continue;
731 }
732
Stephen Smalley534fb072015-02-13 14:06:08 -0500733 if (seen[j]) {
734 log_error("Duplicated key: %s\n", k->key);
735 goto err;
736 }
737 seen[j] = true;
738
William Robertsf0e0a942012-08-27 15:41:15 -0700739 memcpy(r, x, sizeof(key_map));
740
741 /* Assign rule map value to one from file */
742 r->data = strdup(k->value);
743 if (!r->data)
744 goto oom;
745
746 /* Enforce type check*/
William Roberts0ae3a8a2012-09-04 11:51:04 -0700747 log_info("Validating keys!\n");
William Roberts81e1f902015-06-03 21:57:47 -0700748 if (!key_map_validate(r, filename, lineno, new_map->is_never_allow)) {
William Robertsf0e0a942012-08-27 15:41:15 -0700749 log_error("Could not validate\n");
750 goto err;
751 }
752
William Roberts81e1f902015-06-03 21:57:47 -0700753 /*
754 * Only build key off of inputs with the exception of neverallows.
755 * Neverallows are keyed off of all key value pairs,
756 */
757 if (r->dir == dir_in || new_map->is_never_allow) {
William Robertsf0e0a942012-08-27 15:41:15 -0700758 char *tmp;
William Robertsb3ab56c2012-09-17 14:35:02 -0700759 int key_len = strlen(k->key);
760 int val_len = strlen(k->value);
761 int l = (new_map->key) ? strlen(new_map->key) : 0;
762 l = l + key_len + val_len;
William Robertsf0e0a942012-08-27 15:41:15 -0700763 l += 1;
764
765 tmp = realloc(new_map->key, l);
766 if (!tmp)
767 goto oom;
768
William Robertsb3ab56c2012-09-17 14:35:02 -0700769 if (!new_map->key)
770 memset(tmp, 0, l);
771
William Robertsf0e0a942012-08-27 15:41:15 -0700772 new_map->key = tmp;
773
William Robertsb3ab56c2012-09-17 14:35:02 -0700774 strncat(new_map->key, k->key, key_len);
775 strncat(new_map->key, k->value, val_len);
William Robertsf0e0a942012-08-27 15:41:15 -0700776 }
777 break;
778 }
779 free_kvp(k);
780 }
781
782 if (new_map->key == NULL) {
783 log_error("Strange, no keys found, input file corrupt perhaps?\n");
784 goto err;
785 }
786
787 return new_map;
788
789oom:
790 log_error("Out of memory!\n");
791err:
792 if(new_map) {
William Roberts7d65b542015-06-19 09:43:28 -0700793 rule_map_free(new_map, false);
William Robertsf0e0a942012-08-27 15:41:15 -0700794 for (; i < num_of_keys; i++) {
795 k = &(keys[i]);
796 free_kvp(k);
797 }
798 }
Stephen Smalley534fb072015-02-13 14:06:08 -0500799 return NULL;
William Robertsf0e0a942012-08-27 15:41:15 -0700800}
801
802/**
803 * Print the usage of the program
804 */
805static void usage() {
806 printf(
807 "checkseapp [options] <input file>\n"
808 "Processes an seapp_contexts file specified by argument <input file> (default stdin) "
William Robertsae23a1f2012-09-05 12:53:52 -0700809 "and allows later declarations to override previous ones on a match.\n"
William Robertsf0e0a942012-08-27 15:41:15 -0700810 "Options:\n"
811 "-h - print this help message\n"
812 "-v - enable verbose debugging informations\n"
William Roberts63297212013-04-19 19:06:23 -0700813 "-p policy file - specify policy file for strict checking of output selectors against the policy\n"
William Roberts81e1f902015-06-03 21:57:47 -0700814 "-o output file - specify output file or - for stdout. No argument runs in silent mode and outputs nothing\n");
William Robertsf0e0a942012-08-27 15:41:15 -0700815}
816
817static void init() {
818
William Roberts81e1f902015-06-03 21:57:47 -0700819 bool has_out_file;
820 list_element *cursor;
821 file_info *tmp;
822
823 /* input files if the list is empty, use stdin */
824 if (!input_file_list.head) {
825 log_info("Using stdin for input\n");
826 tmp = malloc(sizeof(*tmp));
827 if (!tmp) {
828 log_error("oom");
William Robertsf0e0a942012-08-27 15:41:15 -0700829 exit(EXIT_FAILURE);
830 }
William Roberts81e1f902015-06-03 21:57:47 -0700831 tmp->name = "stdin";
832 tmp->file = stdin;
833 list_append(&input_file_list, &(tmp->listify));
834 }
835 else {
836 list_for_each(&input_file_list, cursor) {
837 tmp = list_entry(cursor, typeof(*tmp), listify);
838
839 log_info("Opening input file: \"%s\"\n", tmp->name);
840 tmp->file = fopen(tmp->name, "r");
841 if (!tmp->file) {
842 log_error("Could not open file: %s error: %s\n", tmp->name,
843 strerror(errno));
844 exit(EXIT_FAILURE);
845 }
846 }
William Robertsf0e0a942012-08-27 15:41:15 -0700847 }
848
William Roberts81e1f902015-06-03 21:57:47 -0700849 has_out_file = out_file.name != NULL;
850
851 /* If output file is -, then use stdout, else open the path */
852 if (has_out_file && !strcmp(out_file.name, "-")) {
853 out_file.file = stdout;
854 out_file.name = "stdout";
855 }
856 else if (has_out_file) {
857 out_file.file = fopen(out_file.name, "w+");
858 }
859
860 if (has_out_file && !out_file.file) {
861 log_error("Could not open file: \"%s\" error: \"%s\"\n", out_file.name,
862 strerror(errno));
863 exit(EXIT_FAILURE);
William Robertsf0e0a942012-08-27 15:41:15 -0700864 }
865
866 if (pol.policy_file_name) {
William Robertsf0e0a942012-08-27 15:41:15 -0700867 log_info("Opening policy file: %s\n", pol.policy_file_name);
868 pol.policy_file = fopen(pol.policy_file_name, "rb");
869 if (!pol.policy_file) {
870 log_error("Could not open file: %s error: %s\n",
871 pol.policy_file_name, strerror(errno));
872 exit(EXIT_FAILURE);
873 }
874
875 pol.handle = sepol_handle_create();
876 if (!pol.handle) {
877 log_error("Could not create sepolicy handle: %s\n",
878 strerror(errno));
879 exit(EXIT_FAILURE);
880 }
881
882 if (sepol_policy_file_create(&pol.pf) < 0) {
883 log_error("Could not create sepolicy file: %s!\n",
884 strerror(errno));
885 exit(EXIT_FAILURE);
886 }
887
888 sepol_policy_file_set_fp(pol.pf, pol.policy_file);
889 sepol_policy_file_set_handle(pol.pf, pol.handle);
890
891 if (sepol_policydb_create(&pol.db) < 0) {
892 log_error("Could not create sepolicy db: %s!\n",
893 strerror(errno));
894 exit(EXIT_FAILURE);
895 }
896
897 if (sepol_policydb_read(pol.db, pol.pf) < 0) {
898 log_error("Could not lod policy file to db: %s!\n",
899 strerror(errno));
900 exit(EXIT_FAILURE);
901 }
902 }
903
William Roberts81e1f902015-06-03 21:57:47 -0700904 list_for_each(&input_file_list, cursor) {
905 tmp = list_entry(cursor, typeof(*tmp), listify);
906 log_info("Input file set to: \"%s\"\n", tmp->name);
907 }
908
909 log_info("Policy file set to: \"%s\"\n",
910 (pol.policy_file_name == NULL) ? "None" : pol.policy_file_name);
911 log_info("Output file set to: \"%s\"\n", out_file.name);
William Robertsf0e0a942012-08-27 15:41:15 -0700912
William Roberts0ae3a8a2012-09-04 11:51:04 -0700913#if !defined(LINK_SEPOL_STATIC)
William Robertsa53ccf32012-09-17 12:53:44 -0700914 log_warn("LINK_SEPOL_STATIC is not defined\n""Not checking types!");
William Roberts0ae3a8a2012-09-04 11:51:04 -0700915#endif
916
William Robertsf0e0a942012-08-27 15:41:15 -0700917}
918
919/**
920 * Handle parsing and setting the global flags for the command line
921 * options. This function calls exit on failure.
922 * @param argc
923 * argument count
924 * @param argv
925 * argument list
926 */
927static void handle_options(int argc, char *argv[]) {
928
929 int c;
William Roberts81e1f902015-06-03 21:57:47 -0700930 file_info *input_file;
William Robertsf0e0a942012-08-27 15:41:15 -0700931
William Robertsf26b6d42015-06-23 10:22:45 -0700932 while ((c = getopt(argc, argv, "ho:p:v")) != -1) {
William Robertsf0e0a942012-08-27 15:41:15 -0700933 switch (c) {
934 case 'h':
935 usage();
936 exit(EXIT_SUCCESS);
937 case 'o':
William Roberts81e1f902015-06-03 21:57:47 -0700938 out_file.name = optarg;
William Robertsf0e0a942012-08-27 15:41:15 -0700939 break;
940 case 'p':
941 pol.policy_file_name = optarg;
942 break;
943 case 'v':
944 log_set_verbose();
945 break;
946 case '?':
947 if (optopt == 'o' || optopt == 'p')
948 log_error("Option -%c requires an argument.\n", optopt);
949 else if (isprint (optopt))
950 log_error("Unknown option `-%c'.\n", optopt);
951 else {
952 log_error(
953 "Unknown option character `\\x%x'.\n",
954 optopt);
William Robertsf0e0a942012-08-27 15:41:15 -0700955 }
William Robertsf0e0a942012-08-27 15:41:15 -0700956 default:
957 exit(EXIT_FAILURE);
958 }
959 }
960
William Roberts81e1f902015-06-03 21:57:47 -0700961 for (c = optind; c < argc; c++) {
William Robertsf0e0a942012-08-27 15:41:15 -0700962
William Roberts81e1f902015-06-03 21:57:47 -0700963 input_file = calloc(1, sizeof(*input_file));
964 if (!input_file) {
965 log_error("oom");
966 exit(EXIT_FAILURE);
967 }
968 input_file->name = argv[c];
969 list_append(&input_file_list, &input_file->listify);
William Robertsf0e0a942012-08-27 15:41:15 -0700970 }
971}
972
973/**
974 * Adds a rule to the hash table and to the ordered list if needed.
975 * @param rm
976 * The rule map to add.
977 */
978static void rule_add(rule_map *rm) {
979
William Roberts0ae3a8a2012-09-04 11:51:04 -0700980 map_match cmp;
William Robertsf0e0a942012-08-27 15:41:15 -0700981 ENTRY e;
982 ENTRY *f;
983 hash_entry *entry;
984 hash_entry *tmp;
William Roberts81e1f902015-06-03 21:57:47 -0700985 list *list_to_addto;
William Robertsf0e0a942012-08-27 15:41:15 -0700986
987 e.key = rm->key;
988
William Roberts0ae3a8a2012-09-04 11:51:04 -0700989 log_info("Searching for key: %s\n", e.key);
William Robertsf0e0a942012-08-27 15:41:15 -0700990 /* Check to see if it has already been added*/
991 f = hsearch(e, FIND);
992
993 /*
994 * Since your only hashing on a partial key, the inputs we need to handle
995 * when you want to override the outputs for a given input set, as well as
996 * checking for duplicate entries.
997 */
998 if(f) {
William Roberts0ae3a8a2012-09-04 11:51:04 -0700999 log_info("Existing entry found!\n");
William Robertsf0e0a942012-08-27 15:41:15 -07001000 tmp = (hash_entry *)f->data;
1001 cmp = rule_map_cmp(rm, tmp->r);
Stephen Smalley0b820042015-02-13 14:58:31 -05001002 log_error("Duplicate line detected in file: %s\n"
1003 "Lines %d and %d %s!\n",
William Roberts81e1f902015-06-03 21:57:47 -07001004 rm->filename, tmp->r->lineno, rm->lineno,
Stephen Smalley0b820042015-02-13 14:58:31 -05001005 map_match_str[cmp]);
William Roberts7d65b542015-06-19 09:43:28 -07001006 rule_map_free(rm, false);
Stephen Smalley0b820042015-02-13 14:58:31 -05001007 goto err;
William Robertsf0e0a942012-08-27 15:41:15 -07001008 }
1009 /* It wasn't found, just add the rule map to the table */
1010 else {
1011
1012 entry = malloc(sizeof(hash_entry));
1013 if (!entry)
1014 goto oom;
1015
1016 entry->r = rm;
1017 e.data = entry;
1018
1019 f = hsearch(e, ENTER);
1020 if(f == NULL) {
1021 goto oom;
1022 }
1023
1024 /* new entries must be added to the ordered list */
1025 entry->r = rm;
William Roberts81e1f902015-06-03 21:57:47 -07001026 list_to_addto = rm->is_never_allow ? &nallow_list : &line_order_list;
1027 list_append(list_to_addto, &entry->listify);
William Robertsf0e0a942012-08-27 15:41:15 -07001028 }
1029
1030 return;
1031oom:
1032 if (e.key)
1033 free(e.key);
1034 if (entry)
1035 free(entry);
1036 if (rm)
1037 free(rm);
1038 log_error("Out of memory in function: %s\n", __FUNCTION__);
1039err:
1040 exit(EXIT_FAILURE);
1041}
1042
William Roberts81e1f902015-06-03 21:57:47 -07001043static void parse_file(file_info *in_file) {
William Robertsf0e0a942012-08-27 15:41:15 -07001044
William Roberts81e1f902015-06-03 21:57:47 -07001045 char *p;
William Robertsf0e0a942012-08-27 15:41:15 -07001046 size_t len;
William Roberts81e1f902015-06-03 21:57:47 -07001047 char *token;
1048 char *saveptr;
1049 bool is_never_allow;
1050 bool found_whitespace;
1051
1052 size_t lineno = 0;
1053 char *name = NULL;
1054 char *value = NULL;
William Roberts610a4b12013-10-15 18:26:00 -07001055 size_t token_cnt = 0;
William Robertsf0e0a942012-08-27 15:41:15 -07001056
William Roberts81e1f902015-06-03 21:57:47 -07001057 char line_buf[BUFSIZ];
1058 kvp keys[KVP_NUM_OF_RULES];
William Robertsf0e0a942012-08-27 15:41:15 -07001059
William Roberts81e1f902015-06-03 21:57:47 -07001060 while (fgets(line_buf, sizeof(line_buf) - 1, in_file->file)) {
William Robertsf0e0a942012-08-27 15:41:15 -07001061 lineno++;
William Roberts81e1f902015-06-03 21:57:47 -07001062 is_never_allow = false;
1063 found_whitespace = false;
1064 log_info("Got line %zu\n", lineno);
William Robertsf0e0a942012-08-27 15:41:15 -07001065 len = strlen(line_buf);
1066 if (line_buf[len - 1] == '\n')
Alice Chuf6647eb2012-10-30 16:27:00 -07001067 line_buf[len - 1] = '\0';
William Robertsf0e0a942012-08-27 15:41:15 -07001068 p = line_buf;
William Roberts81e1f902015-06-03 21:57:47 -07001069
1070 /* neverallow lines must start with neverallow (ie ^neverallow) */
1071 if (!strncasecmp(p, "neverallow", strlen("neverallow"))) {
1072 p += strlen("neverallow");
1073 is_never_allow = true;
1074 }
1075
1076 /* strip trailing whitespace skip comments */
1077 while (isspace(*p)) {
William Robertsf0e0a942012-08-27 15:41:15 -07001078 p++;
William Roberts81e1f902015-06-03 21:57:47 -07001079 found_whitespace = true;
1080 }
Alice Chuf6647eb2012-10-30 16:27:00 -07001081 if (*p == '#' || *p == '\0')
William Robertsf0e0a942012-08-27 15:41:15 -07001082 continue;
1083
1084 token = strtok_r(p, " \t", &saveptr);
1085 if (!token)
1086 goto err;
1087
1088 token_cnt = 0;
1089 memset(keys, 0, sizeof(kvp) * KVP_NUM_OF_RULES);
1090 while (1) {
William Roberts0ae3a8a2012-09-04 11:51:04 -07001091
William Robertsf0e0a942012-08-27 15:41:15 -07001092 name = token;
1093 value = strchr(name, '=');
1094 if (!value)
1095 goto err;
1096 *value++ = 0;
1097
1098 keys[token_cnt].key = strdup(name);
1099 if (!keys[token_cnt].key)
1100 goto oom;
1101
1102 keys[token_cnt].value = strdup(value);
1103 if (!keys[token_cnt].value)
1104 goto oom;
1105
1106 token_cnt++;
1107
1108 token = strtok_r(NULL, " \t", &saveptr);
1109 if (!token)
1110 break;
1111
1112 } /*End token parsing */
1113
William Roberts81e1f902015-06-03 21:57:47 -07001114 rule_map *r = rule_map_new(keys, token_cnt, lineno, in_file->name, is_never_allow);
Stephen Smalley534fb072015-02-13 14:06:08 -05001115 if (!r)
1116 goto err;
William Robertsf0e0a942012-08-27 15:41:15 -07001117 rule_add(r);
1118
1119 } /* End file parsing */
1120 return;
1121
1122err:
William Robertsefebf972016-01-29 10:34:04 -08001123 log_error("Reading file: \"%s\" line: %zu name: \"%s\" value: \"%s\"\n",
William Roberts81e1f902015-06-03 21:57:47 -07001124 in_file->name, lineno, name, value);
1125 if(found_whitespace && name && !strcasecmp(name, "neverallow")) {
1126 log_error("perhaps whitespace before neverallow\n");
1127 }
William Robertsf0e0a942012-08-27 15:41:15 -07001128 exit(EXIT_FAILURE);
1129oom:
1130 log_error("In function %s: Out of memory\n", __FUNCTION__);
1131 exit(EXIT_FAILURE);
1132}
1133
1134/**
William Roberts81e1f902015-06-03 21:57:47 -07001135 * Parses the seapp_contexts file and neverallow file
1136 * and adds them to the hash table and ordered list entries
1137 * when it encounters them.
1138 * Calls exit on failure.
1139 */
1140static void parse() {
1141
1142 file_info *current;
1143 list_element *cursor;
1144 list_for_each(&input_file_list, cursor) {
1145 current = list_entry(cursor, typeof(*current), listify);
1146 parse_file(current);
1147 }
1148}
1149
1150static void validate() {
1151
1152 list_element *cursor, *v;
1153 bool found_issues = false;
1154 hash_entry *e;
1155 rule_map *r;
1156 list_for_each(&line_order_list, cursor) {
1157 e = list_entry(cursor, typeof(*e), listify);
1158 rule_map_validate(e->r);
1159 }
1160
1161 list_for_each(&line_order_list, cursor) {
1162 e = list_entry(cursor, typeof(*e), listify);
1163 r = e->r;
1164 list_for_each(&r->violations, v) {
1165 found_issues = true;
1166 log_error("Rule in File \"%s\" on line %d: \"", e->r->filename, e->r->lineno);
1167 rule_map_print(stderr, e->r);
1168 r = list_entry(v, rule_map, listify);
1169 fprintf(stderr, "\" violates neverallow in File \"%s\" on line %d: \"", r->filename, r->lineno);
1170 rule_map_print(stderr, r);
1171 fprintf(stderr, "\"\n");
1172 }
1173 }
1174
1175 if (found_issues) {
1176 exit(EXIT_FAILURE);
1177 }
1178}
1179
1180/**
William Robertsf0e0a942012-08-27 15:41:15 -07001181 * Should be called after parsing to cause the printing of the rule_maps
1182 * stored in the ordered list, head first, which preserves the "first encountered"
1183 * ordering.
1184 */
1185static void output() {
1186
William Roberts81e1f902015-06-03 21:57:47 -07001187 hash_entry *e;
1188 list_element *cursor;
William Robertsf0e0a942012-08-27 15:41:15 -07001189
William Roberts81e1f902015-06-03 21:57:47 -07001190 if (!out_file.file) {
1191 log_info("No output file, not outputting.\n");
1192 return;
1193 }
1194
1195 list_for_each(&line_order_list, cursor) {
1196 e = list_entry(cursor, hash_entry, listify);
1197 rule_map_print(out_file.file, e->r);
1198 fprintf(out_file.file, "\n");
William Robertsf0e0a942012-08-27 15:41:15 -07001199 }
1200}
1201
1202/**
1203 * This function is registered to the at exit handler and should clean up
1204 * the programs dynamic resources, such as memory and fd's.
1205 */
1206static void cleanup() {
1207
1208 /* Only close this when it was opened by me and not the crt */
William Roberts81e1f902015-06-03 21:57:47 -07001209 if (out_file.name && strcmp(out_file.name, "stdout") && out_file.file) {
1210 log_info("Closing file: %s\n", out_file.name);
1211 fclose(out_file.file);
William Robertsf0e0a942012-08-27 15:41:15 -07001212 }
1213
1214 if (pol.policy_file) {
1215
1216 log_info("Closing file: %s\n", pol.policy_file_name);
1217 fclose(pol.policy_file);
1218
1219 if (pol.db)
1220 sepol_policydb_free(pol.db);
1221
1222 if (pol.pf)
1223 sepol_policy_file_free(pol.pf);
1224
1225 if (pol.handle)
1226 sepol_handle_destroy(pol.handle);
1227 }
1228
William Roberts81e1f902015-06-03 21:57:47 -07001229 log_info("Freeing lists\n");
1230 list_free(&input_file_list);
1231 list_free(&line_order_list);
1232 list_free(&nallow_list);
William Robertsf0e0a942012-08-27 15:41:15 -07001233 hdestroy();
1234}
1235
1236int main(int argc, char *argv[]) {
1237 if (!hcreate(TABLE_SIZE)) {
1238 log_error("Could not create hash table: %s\n", strerror(errno));
1239 exit(EXIT_FAILURE);
1240 }
1241 atexit(cleanup);
1242 handle_options(argc, argv);
1243 init();
1244 log_info("Starting to parse\n");
1245 parse();
1246 log_info("Parsing completed, generating output\n");
William Roberts81e1f902015-06-03 21:57:47 -07001247 validate();
William Robertsf0e0a942012-08-27 15:41:15 -07001248 output();
1249 log_info("Success, generated output\n");
1250 exit(EXIT_SUCCESS);
1251}