blob: d5853aedee7da1dd4f83f1822f9e125273b96d86 [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 },
Jeff Vander Stoep400d3ac2015-10-30 12:43:19 -0700207 { .name = "isAutoPlayApp", .type = dt_bool, .dir = dir_in, .data = NULL },
Stephen Smalleyff4db912014-09-15 15:16:06 -0400208 { .name = "isOwner", .type = dt_bool, .dir = dir_in, .data = NULL },
William Robertsf0e0a942012-08-27 15:41:15 -0700209 { .name = "user", .type = dt_string, .dir = dir_in, .data = NULL },
210 { .name = "seinfo", .type = dt_string, .dir = dir_in, .data = NULL },
211 { .name = "name", .type = dt_string, .dir = dir_in, .data = NULL },
Stephen Smalley6139de52014-02-19 10:54:41 -0500212 { .name = "path", .type = dt_string, .dir = dir_in, .data = NULL },
Jeff Vander Stoepee9c0b52015-10-05 09:15:04 -0700213 { .name = "isPrivApp", .type = dt_bool, .dir = dir_in, .data = NULL },
William Robertsf0e0a942012-08-27 15:41:15 -0700214 /*Outputs*/
215 { .name = "domain", .type = dt_string, .dir = dir_out, .data = NULL },
216 { .name = "type", .type = dt_string, .dir = dir_out, .data = NULL },
217 { .name = "levelFromUid", .type = dt_bool, .dir = dir_out, .data = NULL },
Stephen Smalley38084142012-11-28 10:46:18 -0500218 { .name = "levelFrom", .type = dt_string, .dir = dir_out, .data = NULL },
William Robertsf0e0a942012-08-27 15:41:15 -0700219 { .name = "level", .type = dt_string, .dir = dir_out, .data = NULL },
William Robertsfff29802012-11-27 14:20:34 -0800220};
William Robertsf0e0a942012-08-27 15:41:15 -0700221
222/**
William Roberts81e1f902015-06-03 21:57:47 -0700223 * Appends to the end of the list.
224 * @list The list to append to
225 * @e the element to append
William Robertsf0e0a942012-08-27 15:41:15 -0700226 */
William Roberts81e1f902015-06-03 21:57:47 -0700227void list_append(list *list, list_element *e) {
228
229 memset(e, 0, sizeof(*e));
230
231 if (list->head == NULL ) {
232 list->head = list->tail = e;
233 return;
234 }
235
236 list->tail->next = e;
237 list->tail = e;
238 return;
239}
William Robertsf0e0a942012-08-27 15:41:15 -0700240
241/**
William Roberts81e1f902015-06-03 21:57:47 -0700242 * Free's all the elements in the specified list.
243 * @list The list to free
William Robertsf0e0a942012-08-27 15:41:15 -0700244 */
William Roberts81e1f902015-06-03 21:57:47 -0700245static void list_free(list *list) {
246
247 list_element *tmp;
248 list_element *cursor = list->head;
249
250 while (cursor) {
251 tmp = cursor;
252 cursor = cursor->next;
253 if (list->freefn) {
254 list->freefn(tmp);
255 }
256 }
257}
258
259/*
260 * called when the lists are freed
261 */
262static void line_order_list_freefn(list_element *e) {
263 hash_entry *h = list_entry(e, typeof(*h), listify);
264 rule_map_free(h->r, true);
265 free(h);
266}
267
268static void input_file_list_freefn(list_element *e) {
269 file_info *f = list_entry(e, typeof(*f), listify);
270
271 if (f->file) {
272 fclose(f->file);
273 }
274 free(f);
275}
William Robertsf0e0a942012-08-27 15:41:15 -0700276
277/**
278 * Send a logging message to a file
279 * @param out
280 * Output file to send message too
281 * @param prefix
282 * A special prefix to write to the file, such as "Error:"
283 * @param fmt
284 * The printf style formatter to use, such as "%d"
285 */
William Roberts1e8c0612013-04-19 19:06:02 -0700286static void __attribute__ ((format(printf, 3, 4)))
287log_msg(FILE *out, const char *prefix, const char *fmt, ...) {
288
William Robertsf0e0a942012-08-27 15:41:15 -0700289 fprintf(out, "%s", prefix);
290 va_list args;
291 va_start(args, fmt);
292 vfprintf(out, fmt, args);
293 va_end(args);
294}
295
296/**
297 * Checks for a type in the policy.
298 * @param db
299 * The policy db to search
300 * @param type
301 * The type to search for
302 * @return
303 * 1 if the type is found, 0 otherwise.
304 * @warning
305 * This function always returns 1 if libsepol is not linked
306 * statically to this executable and LINK_SEPOL_STATIC is not
307 * defined.
308 */
309int check_type(sepol_policydb_t *db, char *type) {
310
311 int rc = 1;
312#if defined(LINK_SEPOL_STATIC)
313 policydb_t *d = (policydb_t *)db;
314 hashtab_datum_t dat;
315 dat = hashtab_search(d->p_types.table, type);
316 rc = (dat == NULL) ? 0 : 1;
317#endif
318 return rc;
319}
320
William Roberts81e1f902015-06-03 21:57:47 -0700321static bool match_regex(key_map *assert, const key_map *check) {
322
323 char *tomatch = check->data;
324
325 int ret = pcre_exec(assert->regex.compiled, assert->regex.extra, tomatch,
326 strlen(tomatch), 0, 0, NULL, 0);
327
328 /* 0 from pcre_exec means matched */
329 return !ret;
330}
331
332static bool compile_regex(key_map *km, const char **errbuf, int *erroff) {
333
334 size_t size;
335 char *anchored;
336
337 /*
338 * Explicitly anchor all regex's
339 * The size is the length of the string to anchor (km->data), the anchor
340 * characters ^ and $ and the null byte. Hence strlen(km->data) + 3
341 */
342 size = strlen(km->data) + 3;
343 anchored = alloca(size);
344 sprintf(anchored, "^%s$", km->data);
345
346 km->regex.compiled = pcre_compile(anchored, PCRE_DOTALL, errbuf, erroff,
347 NULL );
348 if (!km->regex.compiled) {
349 return false;
350 }
351
352 km->regex.extra = pcre_study(km->regex.compiled, 0, errbuf);
353 return true;
354}
355
William Robertsf0e0a942012-08-27 15:41:15 -0700356/**
357 * Validates a key_map against a set of enforcement rules, this
358 * function exits the application on a type that cannot be properly
359 * checked
360 *
361 * @param m
362 * The key map to check
363 * @param lineno
364 * The line number in the source file for the corresponding key map
William Robertsfff29802012-11-27 14:20:34 -0800365 * @return
William Roberts81e1f902015-06-03 21:57:47 -0700366 * true if valid, false if invalid
William Robertsf0e0a942012-08-27 15:41:15 -0700367 */
William Roberts81e1f902015-06-03 21:57:47 -0700368static bool key_map_validate(key_map *m, const char *filename, int lineno,
369 bool is_neverallow) {
William Robertsf0e0a942012-08-27 15:41:15 -0700370
William Roberts81e1f902015-06-03 21:57:47 -0700371 int erroff;
372 const char *errbuf;
373 bool rc = true;
William Robertsf0e0a942012-08-27 15:41:15 -0700374 int ret = 1;
William Robertsf0e0a942012-08-27 15:41:15 -0700375 char *key = m->name;
376 char *value = m->data;
377 data_type type = m->type;
William Robertsf0e0a942012-08-27 15:41:15 -0700378
William Roberts0ae3a8a2012-09-04 11:51:04 -0700379 log_info("Validating %s=%s\n", key, value);
380
William Roberts81e1f902015-06-03 21:57:47 -0700381 /*
382 * Neverallows are completely skipped from sanity checking so you can match
383 * un-unspecified inputs.
384 */
385 if (is_neverallow) {
386 if (!m->regex.compiled) {
387 rc = compile_regex(m, &errbuf, &erroff);
388 if (!rc) {
389 log_error("Invalid regex on line %d : %s PCRE error: %s at offset %d",
390 lineno, value, errbuf, erroff);
391 }
392 }
393 goto out;
394 }
395
396 /* Booleans can always be checked for sanity */
William Robertsf0e0a942012-08-27 15:41:15 -0700397 if (type == dt_bool && (!strcmp("true", value) || !strcmp("false", value))) {
398 goto out;
399 }
400 else if (type == dt_bool) {
William Robertsae23a1f2012-09-05 12:53:52 -0700401 log_error("Expected boolean value got: %s=%s on line: %d in file: %s\n",
William Roberts81e1f902015-06-03 21:57:47 -0700402 key, value, lineno, filename);
403 rc = false;
William Robertsf0e0a942012-08-27 15:41:15 -0700404 goto out;
405 }
406
Stephen Smalley38084142012-11-28 10:46:18 -0500407 if (!strcasecmp(key, "levelFrom") &&
408 (strcasecmp(value, "none") && strcasecmp(value, "all") &&
409 strcasecmp(value, "app") && strcasecmp(value, "user"))) {
410 log_error("Unknown levelFrom=%s on line: %d in file: %s\n",
William Roberts81e1f902015-06-03 21:57:47 -0700411 value, lineno, filename);
412 rc = false;
Stephen Smalley38084142012-11-28 10:46:18 -0500413 goto out;
414 }
415
William Robertsf0e0a942012-08-27 15:41:15 -0700416 /*
William Roberts63297212013-04-19 19:06:23 -0700417 * If there is no policy file present,
418 * then it is not going to enforce the types against the policy so just return.
William Robertsf0e0a942012-08-27 15:41:15 -0700419 * User and name cannot really be checked.
420 */
421 if (!pol.policy_file) {
422 goto out;
423 }
William Robertsf0e0a942012-08-27 15:41:15 -0700424 else if (!strcasecmp(key, "type") || !strcasecmp(key, "domain")) {
425
426 if(!check_type(pol.db, value)) {
427 log_error("Could not find selinux type \"%s\" on line: %d in file: %s\n", value,
William Roberts81e1f902015-06-03 21:57:47 -0700428 lineno, filename);
429 rc = false;
William Robertsf0e0a942012-08-27 15:41:15 -0700430 }
431 goto out;
432 }
William Robertsf0e0a942012-08-27 15:41:15 -0700433 else if (!strcasecmp(key, "level")) {
434
William Roberts0ae3a8a2012-09-04 11:51:04 -0700435 ret = sepol_mls_check(pol.handle, pol.db, value);
436 if (ret < 0) {
William Robertsae23a1f2012-09-05 12:53:52 -0700437 log_error("Could not find selinux level \"%s\", on line: %d in file: %s\n", value,
William Roberts81e1f902015-06-03 21:57:47 -0700438 lineno, filename);
439 rc = false;
William Roberts0ae3a8a2012-09-04 11:51:04 -0700440 goto out;
William Robertsf0e0a942012-08-27 15:41:15 -0700441 }
442 }
443
William Roberts0ae3a8a2012-09-04 11:51:04 -0700444out:
445 log_info("Key map validate returning: %d\n", rc);
446 return rc;
William Robertsf0e0a942012-08-27 15:41:15 -0700447}
448
449/**
450 * Prints a rule map back to a file
451 * @param fp
452 * The file handle to print too
453 * @param r
454 * The rule map to print
455 */
456static void rule_map_print(FILE *fp, rule_map *r) {
457
William Roberts610a4b12013-10-15 18:26:00 -0700458 size_t i;
William Robertsf0e0a942012-08-27 15:41:15 -0700459 key_map *m;
460
461 for (i = 0; i < r->length; i++) {
462 m = &(r->m[i]);
463 if (i < r->length - 1)
464 fprintf(fp, "%s=%s ", m->name, m->data);
465 else
466 fprintf(fp, "%s=%s", m->name, m->data);
467 }
468}
469
470/**
471 * Compare two rule maps for equality
472 * @param rmA
473 * a rule map to check
474 * @param rmB
475 * a rule map to check
476 * @return
William Robertsae23a1f2012-09-05 12:53:52 -0700477 * a map_match enum indicating the result
William Robertsf0e0a942012-08-27 15:41:15 -0700478 */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700479static map_match rule_map_cmp(rule_map *rmA, rule_map *rmB) {
William Robertsf0e0a942012-08-27 15:41:15 -0700480
William Roberts610a4b12013-10-15 18:26:00 -0700481 size_t i;
482 size_t j;
William Robertsf0e0a942012-08-27 15:41:15 -0700483 int inputs_found = 0;
484 int num_of_matched_inputs = 0;
485 int input_mode = 0;
William Roberts610a4b12013-10-15 18:26:00 -0700486 size_t matches = 0;
William Robertsf0e0a942012-08-27 15:41:15 -0700487 key_map *mA;
488 key_map *mB;
489
William Robertsf0e0a942012-08-27 15:41:15 -0700490 for (i = 0; i < rmA->length; i++) {
491 mA = &(rmA->m[i]);
492
493 for (j = 0; j < rmB->length; j++) {
494 mB = &(rmB->m[j]);
495 input_mode = 0;
496
497 if (mA->type != mB->type)
498 continue;
499
500 if (strcmp(mA->name, mB->name))
501 continue;
502
503 if (strcmp(mA->data, mB->data))
504 continue;
505
506 if (mB->dir != mA->dir)
507 continue;
508 else if (mB->dir == dir_in) {
509 input_mode = 1;
510 inputs_found++;
511 }
512
William Roberts0ae3a8a2012-09-04 11:51:04 -0700513 if (input_mode) {
William Roberts1e8c0612013-04-19 19:06:02 -0700514 log_info("Matched input lines: name=%s data=%s\n", mA->name, mA->data);
William Robertsf0e0a942012-08-27 15:41:15 -0700515 num_of_matched_inputs++;
William Roberts0ae3a8a2012-09-04 11:51:04 -0700516 }
William Robertsf0e0a942012-08-27 15:41:15 -0700517
518 /* Match found, move on */
William Roberts1e8c0612013-04-19 19:06:02 -0700519 log_info("Matched lines: name=%s data=%s", mA->name, mA->data);
William Robertsf0e0a942012-08-27 15:41:15 -0700520 matches++;
521 break;
522 }
523 }
524
525 /* If they all matched*/
William Roberts0ae3a8a2012-09-04 11:51:04 -0700526 if (matches == rmA->length) {
527 log_info("Rule map cmp MATCH\n");
528 return map_matched;
529 }
William Robertsf0e0a942012-08-27 15:41:15 -0700530
531 /* They didn't all match but the input's did */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700532 else if (num_of_matched_inputs == inputs_found) {
533 log_info("Rule map cmp INPUT MATCH\n");
534 return map_input_matched;
535 }
William Robertsf0e0a942012-08-27 15:41:15 -0700536
537 /* They didn't all match, and the inputs didn't match, ie it didn't
538 * match */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700539 else {
540 log_info("Rule map cmp NO MATCH\n");
541 return map_no_matches;
542 }
William Robertsf0e0a942012-08-27 15:41:15 -0700543}
544
545/**
546 * Frees a rule map
547 * @param rm
548 * rule map to be freed.
William Roberts7d65b542015-06-19 09:43:28 -0700549 * @is_in_htable
550 * True if the rule map has been added to the hash table, false
551 * otherwise.
552 */
553static void rule_map_free(rule_map *rm, bool is_in_htable) {
William Robertsf0e0a942012-08-27 15:41:15 -0700554
William Roberts610a4b12013-10-15 18:26:00 -0700555 size_t i;
556 size_t len = rm->length;
William Robertsf0e0a942012-08-27 15:41:15 -0700557 for (i = 0; i < len; i++) {
558 key_map *m = &(rm->m[i]);
559 free(m->data);
William Roberts81e1f902015-06-03 21:57:47 -0700560
561 if (m->regex.compiled) {
562 pcre_free(m->regex.compiled);
563 }
564
565 if (m->regex.extra) {
566 pcre_free_study(m->regex.extra);
567 }
William Robertsf0e0a942012-08-27 15:41:15 -0700568 }
569
William Roberts7d65b542015-06-19 09:43:28 -0700570 /*
571 * hdestroy() frees comparsion keys for non glibc
572 * on GLIBC we always free on NON-GLIBC we free if
573 * it is not in the htable.
574 */
575 if (rm->key) {
rpcraig5dbfdc02012-10-23 11:03:47 -0400576#ifdef __GLIBC__
William Roberts7d65b542015-06-19 09:43:28 -0700577 /* silence unused warning */
578 (void)is_in_htable;
William Robertsf0e0a942012-08-27 15:41:15 -0700579 free(rm->key);
William Roberts7d65b542015-06-19 09:43:28 -0700580#else
581 if (!is_in_htable) {
582 free(rm->key);
583 }
rpcraig5dbfdc02012-10-23 11:03:47 -0400584#endif
William Roberts7d65b542015-06-19 09:43:28 -0700585 }
William Robertsf0e0a942012-08-27 15:41:15 -0700586
William Roberts81e1f902015-06-03 21:57:47 -0700587 free(rm->filename);
William Robertsf0e0a942012-08-27 15:41:15 -0700588 free(rm);
589}
590
591static void free_kvp(kvp *k) {
592 free(k->key);
593 free(k->value);
594}
595
596/**
William Roberts61846292013-10-15 09:38:24 -0700597 * Checks a rule_map for any variation of KVP's that shouldn't be allowed.
William Roberts81e1f902015-06-03 21:57:47 -0700598 * It builds an assertion failure list for each rule map.
William Roberts61846292013-10-15 09:38:24 -0700599 * Note that this function logs all errors.
600 *
601 * Current Checks:
602 * 1. That a specified name entry should have a specified seinfo entry as well.
William Roberts81e1f902015-06-03 21:57:47 -0700603 * 2. That no rule violates a neverallow
William Roberts61846292013-10-15 09:38:24 -0700604 * @param rm
605 * The rule map to check for validity.
William Roberts61846292013-10-15 09:38:24 -0700606 */
William Roberts81e1f902015-06-03 21:57:47 -0700607static void rule_map_validate(rule_map *rm) {
William Roberts61846292013-10-15 09:38:24 -0700608
William Roberts81e1f902015-06-03 21:57:47 -0700609 size_t i, j;
610 const key_map *rule;
611 key_map *nrule;
612 hash_entry *e;
613 rule_map *assert;
614 list_element *cursor;
William Roberts61846292013-10-15 09:38:24 -0700615
William Roberts81e1f902015-06-03 21:57:47 -0700616 list_for_each(&nallow_list, cursor) {
617 e = list_entry(cursor, typeof(*e), listify);
618 assert = e->r;
William Roberts61846292013-10-15 09:38:24 -0700619
William Roberts81e1f902015-06-03 21:57:47 -0700620 size_t cnt = 0;
621
622 for (j = 0; j < assert->length; j++) {
623 nrule = &(assert->m[j]);
624
625 // mark that nrule->name is for a null check
626 bool is_null_check = !strcmp(nrule->data, "\"\"");
627
628 for (i = 0; i < rm->length; i++) {
629 rule = &(rm->m[i]);
630
631 if (!strcmp(rule->name, nrule->name)) {
632
633 /* the name was found, (data cannot be false) then it was specified */
634 is_null_check = false;
635
636 if (match_regex(nrule, rule)) {
637 cnt++;
638 }
639 }
640 }
641
642 /*
643 * the nrule was marked in a null check and we never found a match on nrule, thus
644 * it matched and we update the cnt
645 */
646 if (is_null_check) {
647 cnt++;
648 }
William Roberts61846292013-10-15 09:38:24 -0700649 }
William Roberts81e1f902015-06-03 21:57:47 -0700650 if (cnt == assert->length) {
651 list_append(&rm->violations, &assert->listify);
William Roberts61846292013-10-15 09:38:24 -0700652 }
653 }
William Roberts61846292013-10-15 09:38:24 -0700654}
655
656/**
William Robertsf0e0a942012-08-27 15:41:15 -0700657 * Given a set of key value pairs, this will construct a new rule map.
658 * On error this function calls exit.
659 * @param keys
660 * Keys from a rule line to map
661 * @param num_of_keys
662 * The length of the keys array
663 * @param lineno
664 * The line number the keys were extracted from
665 * @return
666 * A rule map pointer.
667 */
William Roberts81e1f902015-06-03 21:57:47 -0700668static rule_map *rule_map_new(kvp keys[], size_t num_of_keys, int lineno,
669 const char *filename, bool is_never_allow) {
William Robertsf0e0a942012-08-27 15:41:15 -0700670
William Roberts610a4b12013-10-15 18:26:00 -0700671 size_t i = 0, j = 0;
William Robertsf0e0a942012-08-27 15:41:15 -0700672 rule_map *new_map = NULL;
673 kvp *k = NULL;
674 key_map *r = NULL, *x = NULL;
Stephen Smalley534fb072015-02-13 14:06:08 -0500675 bool seen[KVP_NUM_OF_RULES];
676
677 for (i = 0; i < KVP_NUM_OF_RULES; i++)
678 seen[i] = false;
William Robertsf0e0a942012-08-27 15:41:15 -0700679
680 new_map = calloc(1, (num_of_keys * sizeof(key_map)) + sizeof(rule_map));
681 if (!new_map)
682 goto oom;
683
William Roberts81e1f902015-06-03 21:57:47 -0700684 new_map->is_never_allow = is_never_allow;
William Robertsf0e0a942012-08-27 15:41:15 -0700685 new_map->length = num_of_keys;
686 new_map->lineno = lineno;
William Roberts81e1f902015-06-03 21:57:47 -0700687 new_map->filename = strdup(filename);
688 if (!new_map->filename) {
689 goto oom;
690 }
William Robertsf0e0a942012-08-27 15:41:15 -0700691
692 /* For all the keys in a rule line*/
693 for (i = 0; i < num_of_keys; i++) {
694 k = &(keys[i]);
695 r = &(new_map->m[i]);
696
697 for (j = 0; j < KVP_NUM_OF_RULES; j++) {
698 x = &(rules[j]);
699
700 /* Only assign key name to map name */
701 if (strcasecmp(k->key, x->name)) {
702 if (i == KVP_NUM_OF_RULES) {
703 log_error("No match for key: %s\n", k->key);
704 goto err;
705 }
706 continue;
707 }
708
Stephen Smalley534fb072015-02-13 14:06:08 -0500709 if (seen[j]) {
710 log_error("Duplicated key: %s\n", k->key);
711 goto err;
712 }
713 seen[j] = true;
714
William Robertsf0e0a942012-08-27 15:41:15 -0700715 memcpy(r, x, sizeof(key_map));
716
717 /* Assign rule map value to one from file */
718 r->data = strdup(k->value);
719 if (!r->data)
720 goto oom;
721
722 /* Enforce type check*/
William Roberts0ae3a8a2012-09-04 11:51:04 -0700723 log_info("Validating keys!\n");
William Roberts81e1f902015-06-03 21:57:47 -0700724 if (!key_map_validate(r, filename, lineno, new_map->is_never_allow)) {
William Robertsf0e0a942012-08-27 15:41:15 -0700725 log_error("Could not validate\n");
726 goto err;
727 }
728
William Roberts81e1f902015-06-03 21:57:47 -0700729 /*
730 * Only build key off of inputs with the exception of neverallows.
731 * Neverallows are keyed off of all key value pairs,
732 */
733 if (r->dir == dir_in || new_map->is_never_allow) {
William Robertsf0e0a942012-08-27 15:41:15 -0700734 char *tmp;
William Robertsb3ab56c2012-09-17 14:35:02 -0700735 int key_len = strlen(k->key);
736 int val_len = strlen(k->value);
737 int l = (new_map->key) ? strlen(new_map->key) : 0;
738 l = l + key_len + val_len;
William Robertsf0e0a942012-08-27 15:41:15 -0700739 l += 1;
740
741 tmp = realloc(new_map->key, l);
742 if (!tmp)
743 goto oom;
744
William Robertsb3ab56c2012-09-17 14:35:02 -0700745 if (!new_map->key)
746 memset(tmp, 0, l);
747
William Robertsf0e0a942012-08-27 15:41:15 -0700748 new_map->key = tmp;
749
William Robertsb3ab56c2012-09-17 14:35:02 -0700750 strncat(new_map->key, k->key, key_len);
751 strncat(new_map->key, k->value, val_len);
William Robertsf0e0a942012-08-27 15:41:15 -0700752 }
753 break;
754 }
755 free_kvp(k);
756 }
757
758 if (new_map->key == NULL) {
759 log_error("Strange, no keys found, input file corrupt perhaps?\n");
760 goto err;
761 }
762
763 return new_map;
764
765oom:
766 log_error("Out of memory!\n");
767err:
768 if(new_map) {
William Roberts7d65b542015-06-19 09:43:28 -0700769 rule_map_free(new_map, false);
William Robertsf0e0a942012-08-27 15:41:15 -0700770 for (; i < num_of_keys; i++) {
771 k = &(keys[i]);
772 free_kvp(k);
773 }
774 }
Stephen Smalley534fb072015-02-13 14:06:08 -0500775 return NULL;
William Robertsf0e0a942012-08-27 15:41:15 -0700776}
777
778/**
779 * Print the usage of the program
780 */
781static void usage() {
782 printf(
783 "checkseapp [options] <input file>\n"
784 "Processes an seapp_contexts file specified by argument <input file> (default stdin) "
William Robertsae23a1f2012-09-05 12:53:52 -0700785 "and allows later declarations to override previous ones on a match.\n"
William Robertsf0e0a942012-08-27 15:41:15 -0700786 "Options:\n"
787 "-h - print this help message\n"
788 "-v - enable verbose debugging informations\n"
William Roberts63297212013-04-19 19:06:23 -0700789 "-p policy file - specify policy file for strict checking of output selectors against the policy\n"
William Roberts81e1f902015-06-03 21:57:47 -0700790 "-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 -0700791}
792
793static void init() {
794
William Roberts81e1f902015-06-03 21:57:47 -0700795 bool has_out_file;
796 list_element *cursor;
797 file_info *tmp;
798
799 /* input files if the list is empty, use stdin */
800 if (!input_file_list.head) {
801 log_info("Using stdin for input\n");
802 tmp = malloc(sizeof(*tmp));
803 if (!tmp) {
804 log_error("oom");
William Robertsf0e0a942012-08-27 15:41:15 -0700805 exit(EXIT_FAILURE);
806 }
William Roberts81e1f902015-06-03 21:57:47 -0700807 tmp->name = "stdin";
808 tmp->file = stdin;
809 list_append(&input_file_list, &(tmp->listify));
810 }
811 else {
812 list_for_each(&input_file_list, cursor) {
813 tmp = list_entry(cursor, typeof(*tmp), listify);
814
815 log_info("Opening input file: \"%s\"\n", tmp->name);
816 tmp->file = fopen(tmp->name, "r");
817 if (!tmp->file) {
818 log_error("Could not open file: %s error: %s\n", tmp->name,
819 strerror(errno));
820 exit(EXIT_FAILURE);
821 }
822 }
William Robertsf0e0a942012-08-27 15:41:15 -0700823 }
824
William Roberts81e1f902015-06-03 21:57:47 -0700825 has_out_file = out_file.name != NULL;
826
827 /* If output file is -, then use stdout, else open the path */
828 if (has_out_file && !strcmp(out_file.name, "-")) {
829 out_file.file = stdout;
830 out_file.name = "stdout";
831 }
832 else if (has_out_file) {
833 out_file.file = fopen(out_file.name, "w+");
834 }
835
836 if (has_out_file && !out_file.file) {
837 log_error("Could not open file: \"%s\" error: \"%s\"\n", out_file.name,
838 strerror(errno));
839 exit(EXIT_FAILURE);
William Robertsf0e0a942012-08-27 15:41:15 -0700840 }
841
842 if (pol.policy_file_name) {
William Robertsf0e0a942012-08-27 15:41:15 -0700843 log_info("Opening policy file: %s\n", pol.policy_file_name);
844 pol.policy_file = fopen(pol.policy_file_name, "rb");
845 if (!pol.policy_file) {
846 log_error("Could not open file: %s error: %s\n",
847 pol.policy_file_name, strerror(errno));
848 exit(EXIT_FAILURE);
849 }
850
851 pol.handle = sepol_handle_create();
852 if (!pol.handle) {
853 log_error("Could not create sepolicy handle: %s\n",
854 strerror(errno));
855 exit(EXIT_FAILURE);
856 }
857
858 if (sepol_policy_file_create(&pol.pf) < 0) {
859 log_error("Could not create sepolicy file: %s!\n",
860 strerror(errno));
861 exit(EXIT_FAILURE);
862 }
863
864 sepol_policy_file_set_fp(pol.pf, pol.policy_file);
865 sepol_policy_file_set_handle(pol.pf, pol.handle);
866
867 if (sepol_policydb_create(&pol.db) < 0) {
868 log_error("Could not create sepolicy db: %s!\n",
869 strerror(errno));
870 exit(EXIT_FAILURE);
871 }
872
873 if (sepol_policydb_read(pol.db, pol.pf) < 0) {
874 log_error("Could not lod policy file to db: %s!\n",
875 strerror(errno));
876 exit(EXIT_FAILURE);
877 }
878 }
879
William Roberts81e1f902015-06-03 21:57:47 -0700880 list_for_each(&input_file_list, cursor) {
881 tmp = list_entry(cursor, typeof(*tmp), listify);
882 log_info("Input file set to: \"%s\"\n", tmp->name);
883 }
884
885 log_info("Policy file set to: \"%s\"\n",
886 (pol.policy_file_name == NULL) ? "None" : pol.policy_file_name);
887 log_info("Output file set to: \"%s\"\n", out_file.name);
William Robertsf0e0a942012-08-27 15:41:15 -0700888
William Roberts0ae3a8a2012-09-04 11:51:04 -0700889#if !defined(LINK_SEPOL_STATIC)
William Robertsa53ccf32012-09-17 12:53:44 -0700890 log_warn("LINK_SEPOL_STATIC is not defined\n""Not checking types!");
William Roberts0ae3a8a2012-09-04 11:51:04 -0700891#endif
892
William Robertsf0e0a942012-08-27 15:41:15 -0700893}
894
895/**
896 * Handle parsing and setting the global flags for the command line
897 * options. This function calls exit on failure.
898 * @param argc
899 * argument count
900 * @param argv
901 * argument list
902 */
903static void handle_options(int argc, char *argv[]) {
904
905 int c;
William Roberts81e1f902015-06-03 21:57:47 -0700906 file_info *input_file;
William Robertsf0e0a942012-08-27 15:41:15 -0700907
William Robertsf26b6d42015-06-23 10:22:45 -0700908 while ((c = getopt(argc, argv, "ho:p:v")) != -1) {
William Robertsf0e0a942012-08-27 15:41:15 -0700909 switch (c) {
910 case 'h':
911 usage();
912 exit(EXIT_SUCCESS);
913 case 'o':
William Roberts81e1f902015-06-03 21:57:47 -0700914 out_file.name = optarg;
William Robertsf0e0a942012-08-27 15:41:15 -0700915 break;
916 case 'p':
917 pol.policy_file_name = optarg;
918 break;
919 case 'v':
920 log_set_verbose();
921 break;
922 case '?':
923 if (optopt == 'o' || optopt == 'p')
924 log_error("Option -%c requires an argument.\n", optopt);
925 else if (isprint (optopt))
926 log_error("Unknown option `-%c'.\n", optopt);
927 else {
928 log_error(
929 "Unknown option character `\\x%x'.\n",
930 optopt);
William Robertsf0e0a942012-08-27 15:41:15 -0700931 }
William Robertsf0e0a942012-08-27 15:41:15 -0700932 default:
933 exit(EXIT_FAILURE);
934 }
935 }
936
William Roberts81e1f902015-06-03 21:57:47 -0700937 for (c = optind; c < argc; c++) {
William Robertsf0e0a942012-08-27 15:41:15 -0700938
William Roberts81e1f902015-06-03 21:57:47 -0700939 input_file = calloc(1, sizeof(*input_file));
940 if (!input_file) {
941 log_error("oom");
942 exit(EXIT_FAILURE);
943 }
944 input_file->name = argv[c];
945 list_append(&input_file_list, &input_file->listify);
William Robertsf0e0a942012-08-27 15:41:15 -0700946 }
947}
948
949/**
950 * Adds a rule to the hash table and to the ordered list if needed.
951 * @param rm
952 * The rule map to add.
953 */
954static void rule_add(rule_map *rm) {
955
William Roberts0ae3a8a2012-09-04 11:51:04 -0700956 map_match cmp;
William Robertsf0e0a942012-08-27 15:41:15 -0700957 ENTRY e;
958 ENTRY *f;
959 hash_entry *entry;
960 hash_entry *tmp;
William Roberts81e1f902015-06-03 21:57:47 -0700961 list *list_to_addto;
William Robertsf0e0a942012-08-27 15:41:15 -0700962
963 e.key = rm->key;
964
William Roberts0ae3a8a2012-09-04 11:51:04 -0700965 log_info("Searching for key: %s\n", e.key);
William Robertsf0e0a942012-08-27 15:41:15 -0700966 /* Check to see if it has already been added*/
967 f = hsearch(e, FIND);
968
969 /*
970 * Since your only hashing on a partial key, the inputs we need to handle
971 * when you want to override the outputs for a given input set, as well as
972 * checking for duplicate entries.
973 */
974 if(f) {
William Roberts0ae3a8a2012-09-04 11:51:04 -0700975 log_info("Existing entry found!\n");
William Robertsf0e0a942012-08-27 15:41:15 -0700976 tmp = (hash_entry *)f->data;
977 cmp = rule_map_cmp(rm, tmp->r);
Stephen Smalley0b820042015-02-13 14:58:31 -0500978 log_error("Duplicate line detected in file: %s\n"
979 "Lines %d and %d %s!\n",
William Roberts81e1f902015-06-03 21:57:47 -0700980 rm->filename, tmp->r->lineno, rm->lineno,
Stephen Smalley0b820042015-02-13 14:58:31 -0500981 map_match_str[cmp]);
William Roberts7d65b542015-06-19 09:43:28 -0700982 rule_map_free(rm, false);
Stephen Smalley0b820042015-02-13 14:58:31 -0500983 goto err;
William Robertsf0e0a942012-08-27 15:41:15 -0700984 }
985 /* It wasn't found, just add the rule map to the table */
986 else {
987
988 entry = malloc(sizeof(hash_entry));
989 if (!entry)
990 goto oom;
991
992 entry->r = rm;
993 e.data = entry;
994
995 f = hsearch(e, ENTER);
996 if(f == NULL) {
997 goto oom;
998 }
999
1000 /* new entries must be added to the ordered list */
1001 entry->r = rm;
William Roberts81e1f902015-06-03 21:57:47 -07001002 list_to_addto = rm->is_never_allow ? &nallow_list : &line_order_list;
1003 list_append(list_to_addto, &entry->listify);
William Robertsf0e0a942012-08-27 15:41:15 -07001004 }
1005
1006 return;
1007oom:
1008 if (e.key)
1009 free(e.key);
1010 if (entry)
1011 free(entry);
1012 if (rm)
1013 free(rm);
1014 log_error("Out of memory in function: %s\n", __FUNCTION__);
1015err:
1016 exit(EXIT_FAILURE);
1017}
1018
William Roberts81e1f902015-06-03 21:57:47 -07001019static void parse_file(file_info *in_file) {
William Robertsf0e0a942012-08-27 15:41:15 -07001020
William Roberts81e1f902015-06-03 21:57:47 -07001021 char *p;
William Robertsf0e0a942012-08-27 15:41:15 -07001022 size_t len;
William Roberts81e1f902015-06-03 21:57:47 -07001023 char *token;
1024 char *saveptr;
1025 bool is_never_allow;
1026 bool found_whitespace;
1027
1028 size_t lineno = 0;
1029 char *name = NULL;
1030 char *value = NULL;
William Roberts610a4b12013-10-15 18:26:00 -07001031 size_t token_cnt = 0;
William Robertsf0e0a942012-08-27 15:41:15 -07001032
William Roberts81e1f902015-06-03 21:57:47 -07001033 char line_buf[BUFSIZ];
1034 kvp keys[KVP_NUM_OF_RULES];
William Robertsf0e0a942012-08-27 15:41:15 -07001035
William Roberts81e1f902015-06-03 21:57:47 -07001036 while (fgets(line_buf, sizeof(line_buf) - 1, in_file->file)) {
William Robertsf0e0a942012-08-27 15:41:15 -07001037 lineno++;
William Roberts81e1f902015-06-03 21:57:47 -07001038 is_never_allow = false;
1039 found_whitespace = false;
1040 log_info("Got line %zu\n", lineno);
William Robertsf0e0a942012-08-27 15:41:15 -07001041 len = strlen(line_buf);
1042 if (line_buf[len - 1] == '\n')
Alice Chuf6647eb2012-10-30 16:27:00 -07001043 line_buf[len - 1] = '\0';
William Robertsf0e0a942012-08-27 15:41:15 -07001044 p = line_buf;
William Roberts81e1f902015-06-03 21:57:47 -07001045
1046 /* neverallow lines must start with neverallow (ie ^neverallow) */
1047 if (!strncasecmp(p, "neverallow", strlen("neverallow"))) {
1048 p += strlen("neverallow");
1049 is_never_allow = true;
1050 }
1051
1052 /* strip trailing whitespace skip comments */
1053 while (isspace(*p)) {
William Robertsf0e0a942012-08-27 15:41:15 -07001054 p++;
William Roberts81e1f902015-06-03 21:57:47 -07001055 found_whitespace = true;
1056 }
Alice Chuf6647eb2012-10-30 16:27:00 -07001057 if (*p == '#' || *p == '\0')
William Robertsf0e0a942012-08-27 15:41:15 -07001058 continue;
1059
1060 token = strtok_r(p, " \t", &saveptr);
1061 if (!token)
1062 goto err;
1063
1064 token_cnt = 0;
1065 memset(keys, 0, sizeof(kvp) * KVP_NUM_OF_RULES);
1066 while (1) {
William Roberts0ae3a8a2012-09-04 11:51:04 -07001067
William Robertsf0e0a942012-08-27 15:41:15 -07001068 name = token;
1069 value = strchr(name, '=');
1070 if (!value)
1071 goto err;
1072 *value++ = 0;
1073
1074 keys[token_cnt].key = strdup(name);
1075 if (!keys[token_cnt].key)
1076 goto oom;
1077
1078 keys[token_cnt].value = strdup(value);
1079 if (!keys[token_cnt].value)
1080 goto oom;
1081
1082 token_cnt++;
1083
1084 token = strtok_r(NULL, " \t", &saveptr);
1085 if (!token)
1086 break;
1087
1088 } /*End token parsing */
1089
William Roberts81e1f902015-06-03 21:57:47 -07001090 rule_map *r = rule_map_new(keys, token_cnt, lineno, in_file->name, is_never_allow);
Stephen Smalley534fb072015-02-13 14:06:08 -05001091 if (!r)
1092 goto err;
William Robertsf0e0a942012-08-27 15:41:15 -07001093 rule_add(r);
1094
1095 } /* End file parsing */
1096 return;
1097
1098err:
William Roberts81e1f902015-06-03 21:57:47 -07001099 log_error("reading %s, line %zu, name %s, value %s\n",
1100 in_file->name, lineno, name, value);
1101 if(found_whitespace && name && !strcasecmp(name, "neverallow")) {
1102 log_error("perhaps whitespace before neverallow\n");
1103 }
William Robertsf0e0a942012-08-27 15:41:15 -07001104 exit(EXIT_FAILURE);
1105oom:
1106 log_error("In function %s: Out of memory\n", __FUNCTION__);
1107 exit(EXIT_FAILURE);
1108}
1109
1110/**
William Roberts81e1f902015-06-03 21:57:47 -07001111 * Parses the seapp_contexts file and neverallow file
1112 * and adds them to the hash table and ordered list entries
1113 * when it encounters them.
1114 * Calls exit on failure.
1115 */
1116static void parse() {
1117
1118 file_info *current;
1119 list_element *cursor;
1120 list_for_each(&input_file_list, cursor) {
1121 current = list_entry(cursor, typeof(*current), listify);
1122 parse_file(current);
1123 }
1124}
1125
1126static void validate() {
1127
1128 list_element *cursor, *v;
1129 bool found_issues = false;
1130 hash_entry *e;
1131 rule_map *r;
1132 list_for_each(&line_order_list, cursor) {
1133 e = list_entry(cursor, typeof(*e), listify);
1134 rule_map_validate(e->r);
1135 }
1136
1137 list_for_each(&line_order_list, cursor) {
1138 e = list_entry(cursor, typeof(*e), listify);
1139 r = e->r;
1140 list_for_each(&r->violations, v) {
1141 found_issues = true;
1142 log_error("Rule in File \"%s\" on line %d: \"", e->r->filename, e->r->lineno);
1143 rule_map_print(stderr, e->r);
1144 r = list_entry(v, rule_map, listify);
1145 fprintf(stderr, "\" violates neverallow in File \"%s\" on line %d: \"", r->filename, r->lineno);
1146 rule_map_print(stderr, r);
1147 fprintf(stderr, "\"\n");
1148 }
1149 }
1150
1151 if (found_issues) {
1152 exit(EXIT_FAILURE);
1153 }
1154}
1155
1156/**
William Robertsf0e0a942012-08-27 15:41:15 -07001157 * Should be called after parsing to cause the printing of the rule_maps
1158 * stored in the ordered list, head first, which preserves the "first encountered"
1159 * ordering.
1160 */
1161static void output() {
1162
William Roberts81e1f902015-06-03 21:57:47 -07001163 hash_entry *e;
1164 list_element *cursor;
William Robertsf0e0a942012-08-27 15:41:15 -07001165
William Roberts81e1f902015-06-03 21:57:47 -07001166 if (!out_file.file) {
1167 log_info("No output file, not outputting.\n");
1168 return;
1169 }
1170
1171 list_for_each(&line_order_list, cursor) {
1172 e = list_entry(cursor, hash_entry, listify);
1173 rule_map_print(out_file.file, e->r);
1174 fprintf(out_file.file, "\n");
William Robertsf0e0a942012-08-27 15:41:15 -07001175 }
1176}
1177
1178/**
1179 * This function is registered to the at exit handler and should clean up
1180 * the programs dynamic resources, such as memory and fd's.
1181 */
1182static void cleanup() {
1183
1184 /* Only close this when it was opened by me and not the crt */
William Roberts81e1f902015-06-03 21:57:47 -07001185 if (out_file.name && strcmp(out_file.name, "stdout") && out_file.file) {
1186 log_info("Closing file: %s\n", out_file.name);
1187 fclose(out_file.file);
William Robertsf0e0a942012-08-27 15:41:15 -07001188 }
1189
1190 if (pol.policy_file) {
1191
1192 log_info("Closing file: %s\n", pol.policy_file_name);
1193 fclose(pol.policy_file);
1194
1195 if (pol.db)
1196 sepol_policydb_free(pol.db);
1197
1198 if (pol.pf)
1199 sepol_policy_file_free(pol.pf);
1200
1201 if (pol.handle)
1202 sepol_handle_destroy(pol.handle);
1203 }
1204
William Roberts81e1f902015-06-03 21:57:47 -07001205 log_info("Freeing lists\n");
1206 list_free(&input_file_list);
1207 list_free(&line_order_list);
1208 list_free(&nallow_list);
William Robertsf0e0a942012-08-27 15:41:15 -07001209 hdestroy();
1210}
1211
1212int main(int argc, char *argv[]) {
1213 if (!hcreate(TABLE_SIZE)) {
1214 log_error("Could not create hash table: %s\n", strerror(errno));
1215 exit(EXIT_FAILURE);
1216 }
1217 atexit(cleanup);
1218 handle_options(argc, argv);
1219 init();
1220 log_info("Starting to parse\n");
1221 parse();
1222 log_info("Parsing completed, generating output\n");
William Roberts81e1f902015-06-03 21:57:47 -07001223 validate();
William Robertsf0e0a942012-08-27 15:41:15 -07001224 output();
1225 log_info("Success, generated output\n");
1226 exit(EXIT_SUCCESS);
1227}