blob: ae4f7e3e03d9b4c9641e02029c6b1235f33a91f9 [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 Robertsf0e0a942012-08-27 15:41:15 -0700116};
117
118/**
119 * Key value pair struct, this represents the raw kvp values coming
120 * from the rules files.
121 */
122struct kvp {
123 char *key;
124 char *value;
125};
126
127/**
128 * Rules are made up of meta data and an associated set of kvp stored in a
129 * key_map array.
130 */
131struct rule_map {
William Roberts81e1f902015-06-03 21:57:47 -0700132 bool is_never_allow;
133 list violations;
134 list_element listify;
William Robertsf0e0a942012-08-27 15:41:15 -0700135 char *key; /** key value before hashing */
William Roberts610a4b12013-10-15 18:26:00 -0700136 size_t length; /** length of the key map */
William Robertsf0e0a942012-08-27 15:41:15 -0700137 int lineno; /** Line number rule was encounter on */
William Roberts81e1f902015-06-03 21:57:47 -0700138 char *filename; /** File it was found in */
William Robertsf0e0a942012-08-27 15:41:15 -0700139 key_map m[]; /** key value mapping */
140};
141
142struct hash_entry {
William Roberts81e1f902015-06-03 21:57:47 -0700143 list_element listify;
William Robertsf0e0a942012-08-27 15:41:15 -0700144 rule_map *r; /** The rule map to store at that location */
145};
146
147/**
148 * Data associated for a policy file
149 */
150struct policy_info {
151
152 char *policy_file_name; /** policy file path name */
153 FILE *policy_file; /** file handle to the policy file */
154 sepol_policydb_t *db;
155 sepol_policy_file_t *pf;
156 sepol_handle_t *handle;
157 sepol_context_t *con;
158};
159
William Roberts81e1f902015-06-03 21:57:47 -0700160struct file_info {
161 FILE *file; /** file itself */
162 const char *name; /** name of file. do not free, these are not alloc'd */
163 list_element listify;
164};
165
166static void input_file_list_freefn(list_element *e);
167static void line_order_list_freefn(list_element *e);
168static void rule_map_free(rule_map *rm, bool is_in_htable);
169
William Robertsf0e0a942012-08-27 15:41:15 -0700170/** Set to !0 to enable verbose logging */
171static int logging_verbose = 0;
172
173/** file handle to the output file */
William Roberts81e1f902015-06-03 21:57:47 -0700174static file_info out_file;
William Robertsf0e0a942012-08-27 15:41:15 -0700175
William Roberts81e1f902015-06-03 21:57:47 -0700176static list input_file_list = list_init(input_file_list_freefn);
William Robertsf0e0a942012-08-27 15:41:15 -0700177
178static policy_info pol = {
179 .policy_file_name = NULL,
180 .policy_file = NULL,
181 .db = NULL,
182 .pf = NULL,
183 .handle = NULL,
184 .con = NULL
185};
186
187/**
William Roberts81e1f902015-06-03 21:57:47 -0700188 * Head pointer to a linked list of
189 * rule map table entries (hash_entry), used for
190 * preserving the order of entries
191 * based on "first encounter"
192 */
193static list line_order_list = list_init(line_order_list_freefn);
194
195/*
196 * List of hash_entrys for never allow rules.
197 */
198static list nallow_list = list_init(line_order_list_freefn);
199
200/**
William Robertsf0e0a942012-08-27 15:41:15 -0700201 * The heart of the mapping process, this must be updated if a new key value pair is added
202 * to a rule.
203 */
204key_map rules[] = {
205 /*Inputs*/
206 { .name = "isSystemServer", .type = dt_bool, .dir = dir_in, .data = NULL },
Stephen Smalleyff4db912014-09-15 15:16:06 -0400207 { .name = "isOwner", .type = dt_bool, .dir = dir_in, .data = NULL },
William Robertsf0e0a942012-08-27 15:41:15 -0700208 { .name = "user", .type = dt_string, .dir = dir_in, .data = NULL },
209 { .name = "seinfo", .type = dt_string, .dir = dir_in, .data = NULL },
210 { .name = "name", .type = dt_string, .dir = dir_in, .data = NULL },
Stephen Smalley6139de52014-02-19 10:54:41 -0500211 { .name = "path", .type = dt_string, .dir = dir_in, .data = NULL },
William Robertsf0e0a942012-08-27 15:41:15 -0700212 /*Outputs*/
213 { .name = "domain", .type = dt_string, .dir = dir_out, .data = NULL },
214 { .name = "type", .type = dt_string, .dir = dir_out, .data = NULL },
215 { .name = "levelFromUid", .type = dt_bool, .dir = dir_out, .data = NULL },
Stephen Smalley38084142012-11-28 10:46:18 -0500216 { .name = "levelFrom", .type = dt_string, .dir = dir_out, .data = NULL },
William Robertsf0e0a942012-08-27 15:41:15 -0700217 { .name = "level", .type = dt_string, .dir = dir_out, .data = NULL },
William Robertsfff29802012-11-27 14:20:34 -0800218};
William Robertsf0e0a942012-08-27 15:41:15 -0700219
220/**
William Roberts81e1f902015-06-03 21:57:47 -0700221 * Appends to the end of the list.
222 * @list The list to append to
223 * @e the element to append
William Robertsf0e0a942012-08-27 15:41:15 -0700224 */
William Roberts81e1f902015-06-03 21:57:47 -0700225void list_append(list *list, list_element *e) {
226
227 memset(e, 0, sizeof(*e));
228
229 if (list->head == NULL ) {
230 list->head = list->tail = e;
231 return;
232 }
233
234 list->tail->next = e;
235 list->tail = e;
236 return;
237}
William Robertsf0e0a942012-08-27 15:41:15 -0700238
239/**
William Roberts81e1f902015-06-03 21:57:47 -0700240 * Free's all the elements in the specified list.
241 * @list The list to free
William Robertsf0e0a942012-08-27 15:41:15 -0700242 */
William Roberts81e1f902015-06-03 21:57:47 -0700243static void list_free(list *list) {
244
245 list_element *tmp;
246 list_element *cursor = list->head;
247
248 while (cursor) {
249 tmp = cursor;
250 cursor = cursor->next;
251 if (list->freefn) {
252 list->freefn(tmp);
253 }
254 }
255}
256
257/*
258 * called when the lists are freed
259 */
260static void line_order_list_freefn(list_element *e) {
261 hash_entry *h = list_entry(e, typeof(*h), listify);
262 rule_map_free(h->r, true);
263 free(h);
264}
265
266static void input_file_list_freefn(list_element *e) {
267 file_info *f = list_entry(e, typeof(*f), listify);
268
269 if (f->file) {
270 fclose(f->file);
271 }
272 free(f);
273}
William Robertsf0e0a942012-08-27 15:41:15 -0700274
275/**
276 * Send a logging message to a file
277 * @param out
278 * Output file to send message too
279 * @param prefix
280 * A special prefix to write to the file, such as "Error:"
281 * @param fmt
282 * The printf style formatter to use, such as "%d"
283 */
William Roberts1e8c0612013-04-19 19:06:02 -0700284static void __attribute__ ((format(printf, 3, 4)))
285log_msg(FILE *out, const char *prefix, const char *fmt, ...) {
286
William Robertsf0e0a942012-08-27 15:41:15 -0700287 fprintf(out, "%s", prefix);
288 va_list args;
289 va_start(args, fmt);
290 vfprintf(out, fmt, args);
291 va_end(args);
292}
293
294/**
295 * Checks for a type in the policy.
296 * @param db
297 * The policy db to search
298 * @param type
299 * The type to search for
300 * @return
301 * 1 if the type is found, 0 otherwise.
302 * @warning
303 * This function always returns 1 if libsepol is not linked
304 * statically to this executable and LINK_SEPOL_STATIC is not
305 * defined.
306 */
307int check_type(sepol_policydb_t *db, char *type) {
308
309 int rc = 1;
310#if defined(LINK_SEPOL_STATIC)
311 policydb_t *d = (policydb_t *)db;
312 hashtab_datum_t dat;
313 dat = hashtab_search(d->p_types.table, type);
314 rc = (dat == NULL) ? 0 : 1;
315#endif
316 return rc;
317}
318
William Roberts81e1f902015-06-03 21:57:47 -0700319static bool match_regex(key_map *assert, const key_map *check) {
320
321 char *tomatch = check->data;
322
323 int ret = pcre_exec(assert->regex.compiled, assert->regex.extra, tomatch,
324 strlen(tomatch), 0, 0, NULL, 0);
325
326 /* 0 from pcre_exec means matched */
327 return !ret;
328}
329
330static bool compile_regex(key_map *km, const char **errbuf, int *erroff) {
331
332 size_t size;
333 char *anchored;
334
335 /*
336 * Explicitly anchor all regex's
337 * The size is the length of the string to anchor (km->data), the anchor
338 * characters ^ and $ and the null byte. Hence strlen(km->data) + 3
339 */
340 size = strlen(km->data) + 3;
341 anchored = alloca(size);
342 sprintf(anchored, "^%s$", km->data);
343
344 km->regex.compiled = pcre_compile(anchored, PCRE_DOTALL, errbuf, erroff,
345 NULL );
346 if (!km->regex.compiled) {
347 return false;
348 }
349
350 km->regex.extra = pcre_study(km->regex.compiled, 0, errbuf);
351 return true;
352}
353
William Robertsf0e0a942012-08-27 15:41:15 -0700354/**
355 * Validates a key_map against a set of enforcement rules, this
356 * function exits the application on a type that cannot be properly
357 * checked
358 *
359 * @param m
360 * The key map to check
361 * @param lineno
362 * The line number in the source file for the corresponding key map
William Robertsfff29802012-11-27 14:20:34 -0800363 * @return
William Roberts81e1f902015-06-03 21:57:47 -0700364 * true if valid, false if invalid
William Robertsf0e0a942012-08-27 15:41:15 -0700365 */
William Roberts81e1f902015-06-03 21:57:47 -0700366static bool key_map_validate(key_map *m, const char *filename, int lineno,
367 bool is_neverallow) {
William Robertsf0e0a942012-08-27 15:41:15 -0700368
William Roberts81e1f902015-06-03 21:57:47 -0700369 int erroff;
370 const char *errbuf;
371 bool rc = true;
William Robertsf0e0a942012-08-27 15:41:15 -0700372 int ret = 1;
William Robertsf0e0a942012-08-27 15:41:15 -0700373 char *key = m->name;
374 char *value = m->data;
375 data_type type = m->type;
William Robertsf0e0a942012-08-27 15:41:15 -0700376
William Roberts0ae3a8a2012-09-04 11:51:04 -0700377 log_info("Validating %s=%s\n", key, value);
378
William Roberts81e1f902015-06-03 21:57:47 -0700379 /*
380 * Neverallows are completely skipped from sanity checking so you can match
381 * un-unspecified inputs.
382 */
383 if (is_neverallow) {
384 if (!m->regex.compiled) {
385 rc = compile_regex(m, &errbuf, &erroff);
386 if (!rc) {
387 log_error("Invalid regex on line %d : %s PCRE error: %s at offset %d",
388 lineno, value, errbuf, erroff);
389 }
390 }
391 goto out;
392 }
393
394 /* Booleans can always be checked for sanity */
William Robertsf0e0a942012-08-27 15:41:15 -0700395 if (type == dt_bool && (!strcmp("true", value) || !strcmp("false", value))) {
396 goto out;
397 }
398 else if (type == dt_bool) {
William Robertsae23a1f2012-09-05 12:53:52 -0700399 log_error("Expected boolean value got: %s=%s on line: %d in file: %s\n",
William Roberts81e1f902015-06-03 21:57:47 -0700400 key, value, lineno, filename);
401 rc = false;
William Robertsf0e0a942012-08-27 15:41:15 -0700402 goto out;
403 }
404
Stephen Smalley38084142012-11-28 10:46:18 -0500405 if (!strcasecmp(key, "levelFrom") &&
406 (strcasecmp(value, "none") && strcasecmp(value, "all") &&
407 strcasecmp(value, "app") && strcasecmp(value, "user"))) {
408 log_error("Unknown levelFrom=%s on line: %d in file: %s\n",
William Roberts81e1f902015-06-03 21:57:47 -0700409 value, lineno, filename);
410 rc = false;
Stephen Smalley38084142012-11-28 10:46:18 -0500411 goto out;
412 }
413
William Robertsf0e0a942012-08-27 15:41:15 -0700414 /*
William Roberts63297212013-04-19 19:06:23 -0700415 * If there is no policy file present,
416 * then it is not going to enforce the types against the policy so just return.
William Robertsf0e0a942012-08-27 15:41:15 -0700417 * User and name cannot really be checked.
418 */
419 if (!pol.policy_file) {
420 goto out;
421 }
William Robertsf0e0a942012-08-27 15:41:15 -0700422 else if (!strcasecmp(key, "type") || !strcasecmp(key, "domain")) {
423
424 if(!check_type(pol.db, value)) {
425 log_error("Could not find selinux type \"%s\" on line: %d in file: %s\n", value,
William Roberts81e1f902015-06-03 21:57:47 -0700426 lineno, filename);
427 rc = false;
William Robertsf0e0a942012-08-27 15:41:15 -0700428 }
429 goto out;
430 }
William Robertsf0e0a942012-08-27 15:41:15 -0700431 else if (!strcasecmp(key, "level")) {
432
William Roberts0ae3a8a2012-09-04 11:51:04 -0700433 ret = sepol_mls_check(pol.handle, pol.db, value);
434 if (ret < 0) {
William Robertsae23a1f2012-09-05 12:53:52 -0700435 log_error("Could not find selinux level \"%s\", on line: %d in file: %s\n", value,
William Roberts81e1f902015-06-03 21:57:47 -0700436 lineno, filename);
437 rc = false;
William Roberts0ae3a8a2012-09-04 11:51:04 -0700438 goto out;
William Robertsf0e0a942012-08-27 15:41:15 -0700439 }
440 }
441
William Roberts0ae3a8a2012-09-04 11:51:04 -0700442out:
443 log_info("Key map validate returning: %d\n", rc);
444 return rc;
William Robertsf0e0a942012-08-27 15:41:15 -0700445}
446
447/**
448 * Prints a rule map back to a file
449 * @param fp
450 * The file handle to print too
451 * @param r
452 * The rule map to print
453 */
454static void rule_map_print(FILE *fp, rule_map *r) {
455
William Roberts610a4b12013-10-15 18:26:00 -0700456 size_t i;
William Robertsf0e0a942012-08-27 15:41:15 -0700457 key_map *m;
458
459 for (i = 0; i < r->length; i++) {
460 m = &(r->m[i]);
461 if (i < r->length - 1)
462 fprintf(fp, "%s=%s ", m->name, m->data);
463 else
464 fprintf(fp, "%s=%s", m->name, m->data);
465 }
466}
467
468/**
469 * Compare two rule maps for equality
470 * @param rmA
471 * a rule map to check
472 * @param rmB
473 * a rule map to check
474 * @return
William Robertsae23a1f2012-09-05 12:53:52 -0700475 * a map_match enum indicating the result
William Robertsf0e0a942012-08-27 15:41:15 -0700476 */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700477static map_match rule_map_cmp(rule_map *rmA, rule_map *rmB) {
William Robertsf0e0a942012-08-27 15:41:15 -0700478
William Roberts610a4b12013-10-15 18:26:00 -0700479 size_t i;
480 size_t j;
William Robertsf0e0a942012-08-27 15:41:15 -0700481 int inputs_found = 0;
482 int num_of_matched_inputs = 0;
483 int input_mode = 0;
William Roberts610a4b12013-10-15 18:26:00 -0700484 size_t matches = 0;
William Robertsf0e0a942012-08-27 15:41:15 -0700485 key_map *mA;
486 key_map *mB;
487
William Robertsf0e0a942012-08-27 15:41:15 -0700488 for (i = 0; i < rmA->length; i++) {
489 mA = &(rmA->m[i]);
490
491 for (j = 0; j < rmB->length; j++) {
492 mB = &(rmB->m[j]);
493 input_mode = 0;
494
495 if (mA->type != mB->type)
496 continue;
497
498 if (strcmp(mA->name, mB->name))
499 continue;
500
501 if (strcmp(mA->data, mB->data))
502 continue;
503
504 if (mB->dir != mA->dir)
505 continue;
506 else if (mB->dir == dir_in) {
507 input_mode = 1;
508 inputs_found++;
509 }
510
William Roberts0ae3a8a2012-09-04 11:51:04 -0700511 if (input_mode) {
William Roberts1e8c0612013-04-19 19:06:02 -0700512 log_info("Matched input lines: name=%s data=%s\n", mA->name, mA->data);
William Robertsf0e0a942012-08-27 15:41:15 -0700513 num_of_matched_inputs++;
William Roberts0ae3a8a2012-09-04 11:51:04 -0700514 }
William Robertsf0e0a942012-08-27 15:41:15 -0700515
516 /* Match found, move on */
William Roberts1e8c0612013-04-19 19:06:02 -0700517 log_info("Matched lines: name=%s data=%s", mA->name, mA->data);
William Robertsf0e0a942012-08-27 15:41:15 -0700518 matches++;
519 break;
520 }
521 }
522
523 /* If they all matched*/
William Roberts0ae3a8a2012-09-04 11:51:04 -0700524 if (matches == rmA->length) {
525 log_info("Rule map cmp MATCH\n");
526 return map_matched;
527 }
William Robertsf0e0a942012-08-27 15:41:15 -0700528
529 /* They didn't all match but the input's did */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700530 else if (num_of_matched_inputs == inputs_found) {
531 log_info("Rule map cmp INPUT MATCH\n");
532 return map_input_matched;
533 }
William Robertsf0e0a942012-08-27 15:41:15 -0700534
535 /* They didn't all match, and the inputs didn't match, ie it didn't
536 * match */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700537 else {
538 log_info("Rule map cmp NO MATCH\n");
539 return map_no_matches;
540 }
William Robertsf0e0a942012-08-27 15:41:15 -0700541}
542
543/**
544 * Frees a rule map
545 * @param rm
546 * rule map to be freed.
William Roberts7d65b542015-06-19 09:43:28 -0700547 * @is_in_htable
548 * True if the rule map has been added to the hash table, false
549 * otherwise.
550 */
551static void rule_map_free(rule_map *rm, bool is_in_htable) {
William Robertsf0e0a942012-08-27 15:41:15 -0700552
William Roberts610a4b12013-10-15 18:26:00 -0700553 size_t i;
554 size_t len = rm->length;
William Robertsf0e0a942012-08-27 15:41:15 -0700555 for (i = 0; i < len; i++) {
556 key_map *m = &(rm->m[i]);
557 free(m->data);
William Roberts81e1f902015-06-03 21:57:47 -0700558
559 if (m->regex.compiled) {
560 pcre_free(m->regex.compiled);
561 }
562
563 if (m->regex.extra) {
564 pcre_free_study(m->regex.extra);
565 }
William Robertsf0e0a942012-08-27 15:41:15 -0700566 }
567
William Roberts7d65b542015-06-19 09:43:28 -0700568 /*
569 * hdestroy() frees comparsion keys for non glibc
570 * on GLIBC we always free on NON-GLIBC we free if
571 * it is not in the htable.
572 */
573 if (rm->key) {
rpcraig5dbfdc02012-10-23 11:03:47 -0400574#ifdef __GLIBC__
William Roberts7d65b542015-06-19 09:43:28 -0700575 /* silence unused warning */
576 (void)is_in_htable;
William Robertsf0e0a942012-08-27 15:41:15 -0700577 free(rm->key);
William Roberts7d65b542015-06-19 09:43:28 -0700578#else
579 if (!is_in_htable) {
580 free(rm->key);
581 }
rpcraig5dbfdc02012-10-23 11:03:47 -0400582#endif
William Roberts7d65b542015-06-19 09:43:28 -0700583 }
William Robertsf0e0a942012-08-27 15:41:15 -0700584
William Roberts81e1f902015-06-03 21:57:47 -0700585 free(rm->filename);
William Robertsf0e0a942012-08-27 15:41:15 -0700586 free(rm);
587}
588
589static void free_kvp(kvp *k) {
590 free(k->key);
591 free(k->value);
592}
593
594/**
William Roberts61846292013-10-15 09:38:24 -0700595 * Checks a rule_map for any variation of KVP's that shouldn't be allowed.
William Roberts81e1f902015-06-03 21:57:47 -0700596 * It builds an assertion failure list for each rule map.
William Roberts61846292013-10-15 09:38:24 -0700597 * Note that this function logs all errors.
598 *
599 * Current Checks:
600 * 1. That a specified name entry should have a specified seinfo entry as well.
William Roberts81e1f902015-06-03 21:57:47 -0700601 * 2. That no rule violates a neverallow
William Roberts61846292013-10-15 09:38:24 -0700602 * @param rm
603 * The rule map to check for validity.
William Roberts61846292013-10-15 09:38:24 -0700604 */
William Roberts81e1f902015-06-03 21:57:47 -0700605static void rule_map_validate(rule_map *rm) {
William Roberts61846292013-10-15 09:38:24 -0700606
William Roberts81e1f902015-06-03 21:57:47 -0700607 size_t i, j;
608 const key_map *rule;
609 key_map *nrule;
610 hash_entry *e;
611 rule_map *assert;
612 list_element *cursor;
William Roberts61846292013-10-15 09:38:24 -0700613
William Roberts81e1f902015-06-03 21:57:47 -0700614 list_for_each(&nallow_list, cursor) {
615 e = list_entry(cursor, typeof(*e), listify);
616 assert = e->r;
William Roberts61846292013-10-15 09:38:24 -0700617
William Roberts81e1f902015-06-03 21:57:47 -0700618 size_t cnt = 0;
619
620 for (j = 0; j < assert->length; j++) {
621 nrule = &(assert->m[j]);
622
623 // mark that nrule->name is for a null check
624 bool is_null_check = !strcmp(nrule->data, "\"\"");
625
626 for (i = 0; i < rm->length; i++) {
627 rule = &(rm->m[i]);
628
629 if (!strcmp(rule->name, nrule->name)) {
630
631 /* the name was found, (data cannot be false) then it was specified */
632 is_null_check = false;
633
634 if (match_regex(nrule, rule)) {
635 cnt++;
636 }
637 }
638 }
639
640 /*
641 * the nrule was marked in a null check and we never found a match on nrule, thus
642 * it matched and we update the cnt
643 */
644 if (is_null_check) {
645 cnt++;
646 }
William Roberts61846292013-10-15 09:38:24 -0700647 }
William Roberts81e1f902015-06-03 21:57:47 -0700648 if (cnt == assert->length) {
649 list_append(&rm->violations, &assert->listify);
William Roberts61846292013-10-15 09:38:24 -0700650 }
651 }
William Roberts61846292013-10-15 09:38:24 -0700652}
653
654/**
William Robertsf0e0a942012-08-27 15:41:15 -0700655 * Given a set of key value pairs, this will construct a new rule map.
656 * On error this function calls exit.
657 * @param keys
658 * Keys from a rule line to map
659 * @param num_of_keys
660 * The length of the keys array
661 * @param lineno
662 * The line number the keys were extracted from
663 * @return
664 * A rule map pointer.
665 */
William Roberts81e1f902015-06-03 21:57:47 -0700666static rule_map *rule_map_new(kvp keys[], size_t num_of_keys, int lineno,
667 const char *filename, bool is_never_allow) {
William Robertsf0e0a942012-08-27 15:41:15 -0700668
William Roberts610a4b12013-10-15 18:26:00 -0700669 size_t i = 0, j = 0;
William Robertsf0e0a942012-08-27 15:41:15 -0700670 rule_map *new_map = NULL;
671 kvp *k = NULL;
672 key_map *r = NULL, *x = NULL;
Stephen Smalley534fb072015-02-13 14:06:08 -0500673 bool seen[KVP_NUM_OF_RULES];
674
675 for (i = 0; i < KVP_NUM_OF_RULES; i++)
676 seen[i] = false;
William Robertsf0e0a942012-08-27 15:41:15 -0700677
678 new_map = calloc(1, (num_of_keys * sizeof(key_map)) + sizeof(rule_map));
679 if (!new_map)
680 goto oom;
681
William Roberts81e1f902015-06-03 21:57:47 -0700682 new_map->is_never_allow = is_never_allow;
William Robertsf0e0a942012-08-27 15:41:15 -0700683 new_map->length = num_of_keys;
684 new_map->lineno = lineno;
William Roberts81e1f902015-06-03 21:57:47 -0700685 new_map->filename = strdup(filename);
686 if (!new_map->filename) {
687 goto oom;
688 }
William Robertsf0e0a942012-08-27 15:41:15 -0700689
690 /* For all the keys in a rule line*/
691 for (i = 0; i < num_of_keys; i++) {
692 k = &(keys[i]);
693 r = &(new_map->m[i]);
694
695 for (j = 0; j < KVP_NUM_OF_RULES; j++) {
696 x = &(rules[j]);
697
698 /* Only assign key name to map name */
699 if (strcasecmp(k->key, x->name)) {
700 if (i == KVP_NUM_OF_RULES) {
701 log_error("No match for key: %s\n", k->key);
702 goto err;
703 }
704 continue;
705 }
706
Stephen Smalley534fb072015-02-13 14:06:08 -0500707 if (seen[j]) {
708 log_error("Duplicated key: %s\n", k->key);
709 goto err;
710 }
711 seen[j] = true;
712
William Robertsf0e0a942012-08-27 15:41:15 -0700713 memcpy(r, x, sizeof(key_map));
714
715 /* Assign rule map value to one from file */
716 r->data = strdup(k->value);
717 if (!r->data)
718 goto oom;
719
720 /* Enforce type check*/
William Roberts0ae3a8a2012-09-04 11:51:04 -0700721 log_info("Validating keys!\n");
William Roberts81e1f902015-06-03 21:57:47 -0700722 if (!key_map_validate(r, filename, lineno, new_map->is_never_allow)) {
William Robertsf0e0a942012-08-27 15:41:15 -0700723 log_error("Could not validate\n");
724 goto err;
725 }
726
William Roberts81e1f902015-06-03 21:57:47 -0700727 /*
728 * Only build key off of inputs with the exception of neverallows.
729 * Neverallows are keyed off of all key value pairs,
730 */
731 if (r->dir == dir_in || new_map->is_never_allow) {
William Robertsf0e0a942012-08-27 15:41:15 -0700732 char *tmp;
William Robertsb3ab56c2012-09-17 14:35:02 -0700733 int key_len = strlen(k->key);
734 int val_len = strlen(k->value);
735 int l = (new_map->key) ? strlen(new_map->key) : 0;
736 l = l + key_len + val_len;
William Robertsf0e0a942012-08-27 15:41:15 -0700737 l += 1;
738
739 tmp = realloc(new_map->key, l);
740 if (!tmp)
741 goto oom;
742
William Robertsb3ab56c2012-09-17 14:35:02 -0700743 if (!new_map->key)
744 memset(tmp, 0, l);
745
William Robertsf0e0a942012-08-27 15:41:15 -0700746 new_map->key = tmp;
747
William Robertsb3ab56c2012-09-17 14:35:02 -0700748 strncat(new_map->key, k->key, key_len);
749 strncat(new_map->key, k->value, val_len);
William Robertsf0e0a942012-08-27 15:41:15 -0700750 }
751 break;
752 }
753 free_kvp(k);
754 }
755
756 if (new_map->key == NULL) {
757 log_error("Strange, no keys found, input file corrupt perhaps?\n");
758 goto err;
759 }
760
761 return new_map;
762
763oom:
764 log_error("Out of memory!\n");
765err:
766 if(new_map) {
William Roberts7d65b542015-06-19 09:43:28 -0700767 rule_map_free(new_map, false);
William Robertsf0e0a942012-08-27 15:41:15 -0700768 for (; i < num_of_keys; i++) {
769 k = &(keys[i]);
770 free_kvp(k);
771 }
772 }
Stephen Smalley534fb072015-02-13 14:06:08 -0500773 return NULL;
William Robertsf0e0a942012-08-27 15:41:15 -0700774}
775
776/**
777 * Print the usage of the program
778 */
779static void usage() {
780 printf(
781 "checkseapp [options] <input file>\n"
782 "Processes an seapp_contexts file specified by argument <input file> (default stdin) "
William Robertsae23a1f2012-09-05 12:53:52 -0700783 "and allows later declarations to override previous ones on a match.\n"
William Robertsf0e0a942012-08-27 15:41:15 -0700784 "Options:\n"
785 "-h - print this help message\n"
786 "-v - enable verbose debugging informations\n"
William Roberts63297212013-04-19 19:06:23 -0700787 "-p policy file - specify policy file for strict checking of output selectors against the policy\n"
William Roberts81e1f902015-06-03 21:57:47 -0700788 "-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 -0700789}
790
791static void init() {
792
William Roberts81e1f902015-06-03 21:57:47 -0700793 bool has_out_file;
794 list_element *cursor;
795 file_info *tmp;
796
797 /* input files if the list is empty, use stdin */
798 if (!input_file_list.head) {
799 log_info("Using stdin for input\n");
800 tmp = malloc(sizeof(*tmp));
801 if (!tmp) {
802 log_error("oom");
William Robertsf0e0a942012-08-27 15:41:15 -0700803 exit(EXIT_FAILURE);
804 }
William Roberts81e1f902015-06-03 21:57:47 -0700805 tmp->name = "stdin";
806 tmp->file = stdin;
807 list_append(&input_file_list, &(tmp->listify));
808 }
809 else {
810 list_for_each(&input_file_list, cursor) {
811 tmp = list_entry(cursor, typeof(*tmp), listify);
812
813 log_info("Opening input file: \"%s\"\n", tmp->name);
814 tmp->file = fopen(tmp->name, "r");
815 if (!tmp->file) {
816 log_error("Could not open file: %s error: %s\n", tmp->name,
817 strerror(errno));
818 exit(EXIT_FAILURE);
819 }
820 }
William Robertsf0e0a942012-08-27 15:41:15 -0700821 }
822
William Roberts81e1f902015-06-03 21:57:47 -0700823 has_out_file = out_file.name != NULL;
824
825 /* If output file is -, then use stdout, else open the path */
826 if (has_out_file && !strcmp(out_file.name, "-")) {
827 out_file.file = stdout;
828 out_file.name = "stdout";
829 }
830 else if (has_out_file) {
831 out_file.file = fopen(out_file.name, "w+");
832 }
833
834 if (has_out_file && !out_file.file) {
835 log_error("Could not open file: \"%s\" error: \"%s\"\n", out_file.name,
836 strerror(errno));
837 exit(EXIT_FAILURE);
William Robertsf0e0a942012-08-27 15:41:15 -0700838 }
839
840 if (pol.policy_file_name) {
William Robertsf0e0a942012-08-27 15:41:15 -0700841 log_info("Opening policy file: %s\n", pol.policy_file_name);
842 pol.policy_file = fopen(pol.policy_file_name, "rb");
843 if (!pol.policy_file) {
844 log_error("Could not open file: %s error: %s\n",
845 pol.policy_file_name, strerror(errno));
846 exit(EXIT_FAILURE);
847 }
848
849 pol.handle = sepol_handle_create();
850 if (!pol.handle) {
851 log_error("Could not create sepolicy handle: %s\n",
852 strerror(errno));
853 exit(EXIT_FAILURE);
854 }
855
856 if (sepol_policy_file_create(&pol.pf) < 0) {
857 log_error("Could not create sepolicy file: %s!\n",
858 strerror(errno));
859 exit(EXIT_FAILURE);
860 }
861
862 sepol_policy_file_set_fp(pol.pf, pol.policy_file);
863 sepol_policy_file_set_handle(pol.pf, pol.handle);
864
865 if (sepol_policydb_create(&pol.db) < 0) {
866 log_error("Could not create sepolicy db: %s!\n",
867 strerror(errno));
868 exit(EXIT_FAILURE);
869 }
870
871 if (sepol_policydb_read(pol.db, pol.pf) < 0) {
872 log_error("Could not lod policy file to db: %s!\n",
873 strerror(errno));
874 exit(EXIT_FAILURE);
875 }
876 }
877
William Roberts81e1f902015-06-03 21:57:47 -0700878 list_for_each(&input_file_list, cursor) {
879 tmp = list_entry(cursor, typeof(*tmp), listify);
880 log_info("Input file set to: \"%s\"\n", tmp->name);
881 }
882
883 log_info("Policy file set to: \"%s\"\n",
884 (pol.policy_file_name == NULL) ? "None" : pol.policy_file_name);
885 log_info("Output file set to: \"%s\"\n", out_file.name);
William Robertsf0e0a942012-08-27 15:41:15 -0700886
William Roberts0ae3a8a2012-09-04 11:51:04 -0700887#if !defined(LINK_SEPOL_STATIC)
William Robertsa53ccf32012-09-17 12:53:44 -0700888 log_warn("LINK_SEPOL_STATIC is not defined\n""Not checking types!");
William Roberts0ae3a8a2012-09-04 11:51:04 -0700889#endif
890
William Robertsf0e0a942012-08-27 15:41:15 -0700891}
892
893/**
894 * Handle parsing and setting the global flags for the command line
895 * options. This function calls exit on failure.
896 * @param argc
897 * argument count
898 * @param argv
899 * argument list
900 */
901static void handle_options(int argc, char *argv[]) {
902
903 int c;
William Roberts81e1f902015-06-03 21:57:47 -0700904 file_info *input_file;
William Robertsf0e0a942012-08-27 15:41:15 -0700905
William Robertsf26b6d42015-06-23 10:22:45 -0700906 while ((c = getopt(argc, argv, "ho:p:v")) != -1) {
William Robertsf0e0a942012-08-27 15:41:15 -0700907 switch (c) {
908 case 'h':
909 usage();
910 exit(EXIT_SUCCESS);
911 case 'o':
William Roberts81e1f902015-06-03 21:57:47 -0700912 out_file.name = optarg;
William Robertsf0e0a942012-08-27 15:41:15 -0700913 break;
914 case 'p':
915 pol.policy_file_name = optarg;
916 break;
917 case 'v':
918 log_set_verbose();
919 break;
920 case '?':
921 if (optopt == 'o' || optopt == 'p')
922 log_error("Option -%c requires an argument.\n", optopt);
923 else if (isprint (optopt))
924 log_error("Unknown option `-%c'.\n", optopt);
925 else {
926 log_error(
927 "Unknown option character `\\x%x'.\n",
928 optopt);
William Robertsf0e0a942012-08-27 15:41:15 -0700929 }
William Robertsf0e0a942012-08-27 15:41:15 -0700930 default:
931 exit(EXIT_FAILURE);
932 }
933 }
934
William Roberts81e1f902015-06-03 21:57:47 -0700935 for (c = optind; c < argc; c++) {
William Robertsf0e0a942012-08-27 15:41:15 -0700936
William Roberts81e1f902015-06-03 21:57:47 -0700937 input_file = calloc(1, sizeof(*input_file));
938 if (!input_file) {
939 log_error("oom");
940 exit(EXIT_FAILURE);
941 }
942 input_file->name = argv[c];
943 list_append(&input_file_list, &input_file->listify);
William Robertsf0e0a942012-08-27 15:41:15 -0700944 }
945}
946
947/**
948 * Adds a rule to the hash table and to the ordered list if needed.
949 * @param rm
950 * The rule map to add.
951 */
952static void rule_add(rule_map *rm) {
953
William Roberts0ae3a8a2012-09-04 11:51:04 -0700954 map_match cmp;
William Robertsf0e0a942012-08-27 15:41:15 -0700955 ENTRY e;
956 ENTRY *f;
957 hash_entry *entry;
958 hash_entry *tmp;
William Roberts81e1f902015-06-03 21:57:47 -0700959 list *list_to_addto;
William Robertsf0e0a942012-08-27 15:41:15 -0700960
961 e.key = rm->key;
962
William Roberts0ae3a8a2012-09-04 11:51:04 -0700963 log_info("Searching for key: %s\n", e.key);
William Robertsf0e0a942012-08-27 15:41:15 -0700964 /* Check to see if it has already been added*/
965 f = hsearch(e, FIND);
966
967 /*
968 * Since your only hashing on a partial key, the inputs we need to handle
969 * when you want to override the outputs for a given input set, as well as
970 * checking for duplicate entries.
971 */
972 if(f) {
William Roberts0ae3a8a2012-09-04 11:51:04 -0700973 log_info("Existing entry found!\n");
William Robertsf0e0a942012-08-27 15:41:15 -0700974 tmp = (hash_entry *)f->data;
975 cmp = rule_map_cmp(rm, tmp->r);
Stephen Smalley0b820042015-02-13 14:58:31 -0500976 log_error("Duplicate line detected in file: %s\n"
977 "Lines %d and %d %s!\n",
William Roberts81e1f902015-06-03 21:57:47 -0700978 rm->filename, tmp->r->lineno, rm->lineno,
Stephen Smalley0b820042015-02-13 14:58:31 -0500979 map_match_str[cmp]);
William Roberts7d65b542015-06-19 09:43:28 -0700980 rule_map_free(rm, false);
Stephen Smalley0b820042015-02-13 14:58:31 -0500981 goto err;
William Robertsf0e0a942012-08-27 15:41:15 -0700982 }
983 /* It wasn't found, just add the rule map to the table */
984 else {
985
986 entry = malloc(sizeof(hash_entry));
987 if (!entry)
988 goto oom;
989
990 entry->r = rm;
991 e.data = entry;
992
993 f = hsearch(e, ENTER);
994 if(f == NULL) {
995 goto oom;
996 }
997
998 /* new entries must be added to the ordered list */
999 entry->r = rm;
William Roberts81e1f902015-06-03 21:57:47 -07001000 list_to_addto = rm->is_never_allow ? &nallow_list : &line_order_list;
1001 list_append(list_to_addto, &entry->listify);
William Robertsf0e0a942012-08-27 15:41:15 -07001002 }
1003
1004 return;
1005oom:
1006 if (e.key)
1007 free(e.key);
1008 if (entry)
1009 free(entry);
1010 if (rm)
1011 free(rm);
1012 log_error("Out of memory in function: %s\n", __FUNCTION__);
1013err:
1014 exit(EXIT_FAILURE);
1015}
1016
William Roberts81e1f902015-06-03 21:57:47 -07001017static void parse_file(file_info *in_file) {
William Robertsf0e0a942012-08-27 15:41:15 -07001018
William Roberts81e1f902015-06-03 21:57:47 -07001019 char *p;
William Robertsf0e0a942012-08-27 15:41:15 -07001020 size_t len;
William Roberts81e1f902015-06-03 21:57:47 -07001021 char *token;
1022 char *saveptr;
1023 bool is_never_allow;
1024 bool found_whitespace;
1025
1026 size_t lineno = 0;
1027 char *name = NULL;
1028 char *value = NULL;
William Roberts610a4b12013-10-15 18:26:00 -07001029 size_t token_cnt = 0;
William Robertsf0e0a942012-08-27 15:41:15 -07001030
William Roberts81e1f902015-06-03 21:57:47 -07001031 char line_buf[BUFSIZ];
1032 kvp keys[KVP_NUM_OF_RULES];
William Robertsf0e0a942012-08-27 15:41:15 -07001033
William Roberts81e1f902015-06-03 21:57:47 -07001034 while (fgets(line_buf, sizeof(line_buf) - 1, in_file->file)) {
William Robertsf0e0a942012-08-27 15:41:15 -07001035 lineno++;
William Roberts81e1f902015-06-03 21:57:47 -07001036 is_never_allow = false;
1037 found_whitespace = false;
1038 log_info("Got line %zu\n", lineno);
William Robertsf0e0a942012-08-27 15:41:15 -07001039 len = strlen(line_buf);
1040 if (line_buf[len - 1] == '\n')
Alice Chuf6647eb2012-10-30 16:27:00 -07001041 line_buf[len - 1] = '\0';
William Robertsf0e0a942012-08-27 15:41:15 -07001042 p = line_buf;
William Roberts81e1f902015-06-03 21:57:47 -07001043
1044 /* neverallow lines must start with neverallow (ie ^neverallow) */
1045 if (!strncasecmp(p, "neverallow", strlen("neverallow"))) {
1046 p += strlen("neverallow");
1047 is_never_allow = true;
1048 }
1049
1050 /* strip trailing whitespace skip comments */
1051 while (isspace(*p)) {
William Robertsf0e0a942012-08-27 15:41:15 -07001052 p++;
William Roberts81e1f902015-06-03 21:57:47 -07001053 found_whitespace = true;
1054 }
Alice Chuf6647eb2012-10-30 16:27:00 -07001055 if (*p == '#' || *p == '\0')
William Robertsf0e0a942012-08-27 15:41:15 -07001056 continue;
1057
1058 token = strtok_r(p, " \t", &saveptr);
1059 if (!token)
1060 goto err;
1061
1062 token_cnt = 0;
1063 memset(keys, 0, sizeof(kvp) * KVP_NUM_OF_RULES);
1064 while (1) {
William Roberts0ae3a8a2012-09-04 11:51:04 -07001065
William Robertsf0e0a942012-08-27 15:41:15 -07001066 name = token;
1067 value = strchr(name, '=');
1068 if (!value)
1069 goto err;
1070 *value++ = 0;
1071
1072 keys[token_cnt].key = strdup(name);
1073 if (!keys[token_cnt].key)
1074 goto oom;
1075
1076 keys[token_cnt].value = strdup(value);
1077 if (!keys[token_cnt].value)
1078 goto oom;
1079
1080 token_cnt++;
1081
1082 token = strtok_r(NULL, " \t", &saveptr);
1083 if (!token)
1084 break;
1085
1086 } /*End token parsing */
1087
William Roberts81e1f902015-06-03 21:57:47 -07001088 rule_map *r = rule_map_new(keys, token_cnt, lineno, in_file->name, is_never_allow);
Stephen Smalley534fb072015-02-13 14:06:08 -05001089 if (!r)
1090 goto err;
William Robertsf0e0a942012-08-27 15:41:15 -07001091 rule_add(r);
1092
1093 } /* End file parsing */
1094 return;
1095
1096err:
William Roberts81e1f902015-06-03 21:57:47 -07001097 log_error("reading %s, line %zu, name %s, value %s\n",
1098 in_file->name, lineno, name, value);
1099 if(found_whitespace && name && !strcasecmp(name, "neverallow")) {
1100 log_error("perhaps whitespace before neverallow\n");
1101 }
William Robertsf0e0a942012-08-27 15:41:15 -07001102 exit(EXIT_FAILURE);
1103oom:
1104 log_error("In function %s: Out of memory\n", __FUNCTION__);
1105 exit(EXIT_FAILURE);
1106}
1107
1108/**
William Roberts81e1f902015-06-03 21:57:47 -07001109 * Parses the seapp_contexts file and neverallow file
1110 * and adds them to the hash table and ordered list entries
1111 * when it encounters them.
1112 * Calls exit on failure.
1113 */
1114static void parse() {
1115
1116 file_info *current;
1117 list_element *cursor;
1118 list_for_each(&input_file_list, cursor) {
1119 current = list_entry(cursor, typeof(*current), listify);
1120 parse_file(current);
1121 }
1122}
1123
1124static void validate() {
1125
1126 list_element *cursor, *v;
1127 bool found_issues = false;
1128 hash_entry *e;
1129 rule_map *r;
1130 list_for_each(&line_order_list, cursor) {
1131 e = list_entry(cursor, typeof(*e), listify);
1132 rule_map_validate(e->r);
1133 }
1134
1135 list_for_each(&line_order_list, cursor) {
1136 e = list_entry(cursor, typeof(*e), listify);
1137 r = e->r;
1138 list_for_each(&r->violations, v) {
1139 found_issues = true;
1140 log_error("Rule in File \"%s\" on line %d: \"", e->r->filename, e->r->lineno);
1141 rule_map_print(stderr, e->r);
1142 r = list_entry(v, rule_map, listify);
1143 fprintf(stderr, "\" violates neverallow in File \"%s\" on line %d: \"", r->filename, r->lineno);
1144 rule_map_print(stderr, r);
1145 fprintf(stderr, "\"\n");
1146 }
1147 }
1148
1149 if (found_issues) {
1150 exit(EXIT_FAILURE);
1151 }
1152}
1153
1154/**
William Robertsf0e0a942012-08-27 15:41:15 -07001155 * Should be called after parsing to cause the printing of the rule_maps
1156 * stored in the ordered list, head first, which preserves the "first encountered"
1157 * ordering.
1158 */
1159static void output() {
1160
William Roberts81e1f902015-06-03 21:57:47 -07001161 hash_entry *e;
1162 list_element *cursor;
William Robertsf0e0a942012-08-27 15:41:15 -07001163
William Roberts81e1f902015-06-03 21:57:47 -07001164 if (!out_file.file) {
1165 log_info("No output file, not outputting.\n");
1166 return;
1167 }
1168
1169 list_for_each(&line_order_list, cursor) {
1170 e = list_entry(cursor, hash_entry, listify);
1171 rule_map_print(out_file.file, e->r);
1172 fprintf(out_file.file, "\n");
William Robertsf0e0a942012-08-27 15:41:15 -07001173 }
1174}
1175
1176/**
1177 * This function is registered to the at exit handler and should clean up
1178 * the programs dynamic resources, such as memory and fd's.
1179 */
1180static void cleanup() {
1181
1182 /* Only close this when it was opened by me and not the crt */
William Roberts81e1f902015-06-03 21:57:47 -07001183 if (out_file.name && strcmp(out_file.name, "stdout") && out_file.file) {
1184 log_info("Closing file: %s\n", out_file.name);
1185 fclose(out_file.file);
William Robertsf0e0a942012-08-27 15:41:15 -07001186 }
1187
1188 if (pol.policy_file) {
1189
1190 log_info("Closing file: %s\n", pol.policy_file_name);
1191 fclose(pol.policy_file);
1192
1193 if (pol.db)
1194 sepol_policydb_free(pol.db);
1195
1196 if (pol.pf)
1197 sepol_policy_file_free(pol.pf);
1198
1199 if (pol.handle)
1200 sepol_handle_destroy(pol.handle);
1201 }
1202
William Roberts81e1f902015-06-03 21:57:47 -07001203 log_info("Freeing lists\n");
1204 list_free(&input_file_list);
1205 list_free(&line_order_list);
1206 list_free(&nallow_list);
William Robertsf0e0a942012-08-27 15:41:15 -07001207 hdestroy();
1208}
1209
1210int main(int argc, char *argv[]) {
1211 if (!hcreate(TABLE_SIZE)) {
1212 log_error("Could not create hash table: %s\n", strerror(errno));
1213 exit(EXIT_FAILURE);
1214 }
1215 atexit(cleanup);
1216 handle_options(argc, argv);
1217 init();
1218 log_info("Starting to parse\n");
1219 parse();
1220 log_info("Parsing completed, generating output\n");
William Roberts81e1f902015-06-03 21:57:47 -07001221 validate();
William Robertsf0e0a942012-08-27 15:41:15 -07001222 output();
1223 log_info("Success, generated output\n");
1224 exit(EXIT_SUCCESS);
1225}