blob: a7de7a2d2d949e4ade6f09e3df717c59707e9060 [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>
Janis Danisevskis750d7972016-04-01 13:31:57 +010014#include <pcre2.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 */
Chih-Hung Hsieh33500c92016-05-11 14:59:45 -070026#define list_init(free_fn) { .head = NULL, .tail = NULL, .freefn = (free_fn) }
William Roberts81e1f902015-06-03 21:57:47 -070027
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) \
Chih-Hung Hsieh33500c92016-05-11 14:59:45 -070038 (type *)(((uint8_t *)(element)) - (uint8_t *)&(((type *)NULL)->name))
William Roberts81e1f902015-06-03 21:57:47 -070039
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) \
Chih-Hung Hsieh33500c92016-05-11 14:59:45 -070046 for(var = (list)->head; var != NULL; var = var->next) /*NOLINT*/
William Roberts81e1f902015-06-03 21:57:47 -070047
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
William Roberts81e1f902015-06-03 21:57:47 -070083struct list_element {
84 list_element *next;
85};
86
87struct list {
88 list_element *head;
89 list_element *tail;
90 void (*freefn)(list_element *e);
91};
92
93struct key_map_regex {
Janis Danisevskis750d7972016-04-01 13:31:57 +010094 pcre2_code *compiled;
95 pcre2_match_data *match_data;
William Robertsf0e0a942012-08-27 15:41:15 -070096};
97
98/**
99 * The workhorse of the logic. This struct maps key value pairs to
100 * an associated set of meta data maintained in rule_map_new()
101 */
102struct key_map {
103 char *name;
104 key_dir dir;
William Robertsf0e0a942012-08-27 15:41:15 -0700105 char *data;
William Roberts81e1f902015-06-03 21:57:47 -0700106 key_map_regex regex;
William Roberts696a66b2016-01-29 10:41:50 -0800107 bool (*fn_validate)(char *value, char **errmsg);
William Robertsf0e0a942012-08-27 15:41:15 -0700108};
109
110/**
111 * Key value pair struct, this represents the raw kvp values coming
112 * from the rules files.
113 */
114struct kvp {
115 char *key;
116 char *value;
117};
118
119/**
120 * Rules are made up of meta data and an associated set of kvp stored in a
121 * key_map array.
122 */
123struct rule_map {
William Roberts81e1f902015-06-03 21:57:47 -0700124 bool is_never_allow;
125 list violations;
126 list_element listify;
William Robertsf0e0a942012-08-27 15:41:15 -0700127 char *key; /** key value before hashing */
William Roberts610a4b12013-10-15 18:26:00 -0700128 size_t length; /** length of the key map */
William Robertsf0e0a942012-08-27 15:41:15 -0700129 int lineno; /** Line number rule was encounter on */
William Roberts81e1f902015-06-03 21:57:47 -0700130 char *filename; /** File it was found in */
William Robertsf0e0a942012-08-27 15:41:15 -0700131 key_map m[]; /** key value mapping */
132};
133
134struct hash_entry {
William Roberts81e1f902015-06-03 21:57:47 -0700135 list_element listify;
William Robertsf0e0a942012-08-27 15:41:15 -0700136 rule_map *r; /** The rule map to store at that location */
137};
138
139/**
140 * Data associated for a policy file
141 */
142struct policy_info {
143
144 char *policy_file_name; /** policy file path name */
145 FILE *policy_file; /** file handle to the policy file */
146 sepol_policydb_t *db;
147 sepol_policy_file_t *pf;
148 sepol_handle_t *handle;
149 sepol_context_t *con;
150};
151
William Roberts81e1f902015-06-03 21:57:47 -0700152struct file_info {
153 FILE *file; /** file itself */
154 const char *name; /** name of file. do not free, these are not alloc'd */
155 list_element listify;
156};
157
158static void input_file_list_freefn(list_element *e);
159static void line_order_list_freefn(list_element *e);
160static void rule_map_free(rule_map *rm, bool is_in_htable);
161
William Robertsf0e0a942012-08-27 15:41:15 -0700162/** Set to !0 to enable verbose logging */
163static int logging_verbose = 0;
164
165/** file handle to the output file */
William Roberts81e1f902015-06-03 21:57:47 -0700166static file_info out_file;
William Robertsf0e0a942012-08-27 15:41:15 -0700167
William Roberts81e1f902015-06-03 21:57:47 -0700168static list input_file_list = list_init(input_file_list_freefn);
William Robertsf0e0a942012-08-27 15:41:15 -0700169
170static policy_info pol = {
171 .policy_file_name = NULL,
172 .policy_file = NULL,
173 .db = NULL,
174 .pf = NULL,
175 .handle = NULL,
176 .con = NULL
177};
178
179/**
William Roberts81e1f902015-06-03 21:57:47 -0700180 * Head pointer to a linked list of
181 * rule map table entries (hash_entry), used for
182 * preserving the order of entries
183 * based on "first encounter"
184 */
185static list line_order_list = list_init(line_order_list_freefn);
186
187/*
188 * List of hash_entrys for never allow rules.
189 */
190static list nallow_list = list_init(line_order_list_freefn);
191
William Roberts696a66b2016-01-29 10:41:50 -0800192/* validation call backs */
193static bool validate_bool(char *value, char **errmsg);
194static bool validate_levelFrom(char *value, char **errmsg);
195static bool validate_selinux_type(char *value, char **errmsg);
196static bool validate_selinux_level(char *value, char **errmsg);
Michael Peckf54b3622017-02-14 09:48:57 -0800197static bool validate_uint(char *value, char **errmsg);
William Roberts696a66b2016-01-29 10:41:50 -0800198
William Roberts81e1f902015-06-03 21:57:47 -0700199/**
William Robertsf0e0a942012-08-27 15:41:15 -0700200 * The heart of the mapping process, this must be updated if a new key value pair is added
201 * to a rule.
202 */
203key_map rules[] = {
204 /*Inputs*/
William Roberts29adea52016-01-29 15:12:58 -0800205 { .name = "isSystemServer", .dir = dir_in, .fn_validate = validate_bool },
Chad Brubaker06cf31e2016-10-06 13:15:44 -0700206 { .name = "isEphemeralApp", .dir = dir_in, .fn_validate = validate_bool },
Chad Brubakera782a812017-02-06 10:31:45 -0800207 { .name = "isV2App", .dir = dir_in, .fn_validate = validate_bool },
William Roberts29adea52016-01-29 15:12:58 -0800208 { .name = "isOwner", .dir = dir_in, .fn_validate = validate_bool },
209 { .name = "user", .dir = dir_in, },
210 { .name = "seinfo", .dir = dir_in, },
211 { .name = "name", .dir = dir_in, },
212 { .name = "path", .dir = dir_in, },
213 { .name = "isPrivApp", .dir = dir_in, .fn_validate = validate_bool },
Michael Peckf54b3622017-02-14 09:48:57 -0800214 { .name = "minTargetSdkVersion", .dir = dir_in, .fn_validate = validate_uint },
Yabin Cuiffa2b612018-11-02 14:34:06 -0700215 { .name = "fromRunAs", .dir = dir_in, .fn_validate = validate_bool },
William Robertsf0e0a942012-08-27 15:41:15 -0700216 /*Outputs*/
William Roberts29adea52016-01-29 15:12:58 -0800217 { .name = "domain", .dir = dir_out, .fn_validate = validate_selinux_type },
218 { .name = "type", .dir = dir_out, .fn_validate = validate_selinux_type },
219 { .name = "levelFromUid", .dir = dir_out, .fn_validate = validate_bool },
220 { .name = "levelFrom", .dir = dir_out, .fn_validate = validate_levelFrom },
221 { .name = "level", .dir = dir_out, .fn_validate = validate_selinux_level },
William Robertsfff29802012-11-27 14:20:34 -0800222};
William Robertsf0e0a942012-08-27 15:41:15 -0700223
224/**
William Roberts81e1f902015-06-03 21:57:47 -0700225 * Appends to the end of the list.
226 * @list The list to append to
227 * @e the element to append
William Robertsf0e0a942012-08-27 15:41:15 -0700228 */
William Roberts81e1f902015-06-03 21:57:47 -0700229void list_append(list *list, list_element *e) {
230
231 memset(e, 0, sizeof(*e));
232
233 if (list->head == NULL ) {
234 list->head = list->tail = e;
235 return;
236 }
237
238 list->tail->next = e;
239 list->tail = e;
240 return;
241}
William Robertsf0e0a942012-08-27 15:41:15 -0700242
243/**
William Roberts81e1f902015-06-03 21:57:47 -0700244 * Free's all the elements in the specified list.
245 * @list The list to free
William Robertsf0e0a942012-08-27 15:41:15 -0700246 */
William Roberts81e1f902015-06-03 21:57:47 -0700247static void list_free(list *list) {
248
249 list_element *tmp;
250 list_element *cursor = list->head;
251
252 while (cursor) {
253 tmp = cursor;
254 cursor = cursor->next;
255 if (list->freefn) {
256 list->freefn(tmp);
257 }
258 }
259}
260
261/*
262 * called when the lists are freed
263 */
264static void line_order_list_freefn(list_element *e) {
265 hash_entry *h = list_entry(e, typeof(*h), listify);
266 rule_map_free(h->r, true);
267 free(h);
268}
269
270static void input_file_list_freefn(list_element *e) {
271 file_info *f = list_entry(e, typeof(*f), listify);
272
273 if (f->file) {
274 fclose(f->file);
275 }
276 free(f);
277}
William Robertsf0e0a942012-08-27 15:41:15 -0700278
279/**
280 * Send a logging message to a file
281 * @param out
282 * Output file to send message too
283 * @param prefix
284 * A special prefix to write to the file, such as "Error:"
285 * @param fmt
286 * The printf style formatter to use, such as "%d"
287 */
William Roberts1e8c0612013-04-19 19:06:02 -0700288static void __attribute__ ((format(printf, 3, 4)))
289log_msg(FILE *out, const char *prefix, const char *fmt, ...) {
290
William Robertsf0e0a942012-08-27 15:41:15 -0700291 fprintf(out, "%s", prefix);
292 va_list args;
293 va_start(args, fmt);
294 vfprintf(out, fmt, args);
295 va_end(args);
296}
297
298/**
299 * Checks for a type in the policy.
300 * @param db
301 * The policy db to search
302 * @param type
303 * The type to search for
304 * @return
305 * 1 if the type is found, 0 otherwise.
306 * @warning
307 * This function always returns 1 if libsepol is not linked
308 * statically to this executable and LINK_SEPOL_STATIC is not
309 * defined.
310 */
William Roberts25528cf2016-01-29 10:32:34 -0800311static int check_type(sepol_policydb_t *db, char *type) {
William Robertsf0e0a942012-08-27 15:41:15 -0700312
313 int rc = 1;
314#if defined(LINK_SEPOL_STATIC)
315 policydb_t *d = (policydb_t *)db;
316 hashtab_datum_t dat;
317 dat = hashtab_search(d->p_types.table, type);
318 rc = (dat == NULL) ? 0 : 1;
319#endif
320 return rc;
321}
322
William Roberts81e1f902015-06-03 21:57:47 -0700323static bool match_regex(key_map *assert, const key_map *check) {
324
325 char *tomatch = check->data;
326
Janis Danisevskis750d7972016-04-01 13:31:57 +0100327 int ret = pcre2_match(assert->regex.compiled, (PCRE2_SPTR) tomatch,
328 PCRE2_ZERO_TERMINATED, 0, 0,
329 assert->regex.match_data, NULL);
William Roberts81e1f902015-06-03 21:57:47 -0700330
Janis Danisevskis750d7972016-04-01 13:31:57 +0100331 /* ret > 0 from pcre2_match means matched */
332 return ret > 0;
William Roberts81e1f902015-06-03 21:57:47 -0700333}
334
Janis Danisevskis750d7972016-04-01 13:31:57 +0100335static bool compile_regex(key_map *km, int *errcode, PCRE2_SIZE *erroff) {
William Roberts81e1f902015-06-03 21:57:47 -0700336
337 size_t size;
338 char *anchored;
339
340 /*
341 * Explicitly anchor all regex's
342 * The size is the length of the string to anchor (km->data), the anchor
343 * characters ^ and $ and the null byte. Hence strlen(km->data) + 3
344 */
345 size = strlen(km->data) + 3;
346 anchored = alloca(size);
347 sprintf(anchored, "^%s$", km->data);
348
Janis Danisevskis750d7972016-04-01 13:31:57 +0100349 km->regex.compiled = pcre2_compile((PCRE2_SPTR) anchored,
350 PCRE2_ZERO_TERMINATED,
351 PCRE2_DOTALL,
352 errcode, erroff,
353 NULL);
William Roberts81e1f902015-06-03 21:57:47 -0700354 if (!km->regex.compiled) {
355 return false;
356 }
357
Janis Danisevskis750d7972016-04-01 13:31:57 +0100358 km->regex.match_data = pcre2_match_data_create_from_pattern(
359 km->regex.compiled, NULL);
360 if (!km->regex.match_data) {
361 pcre2_code_free(km->regex.compiled);
362 return false;
363 }
William Roberts81e1f902015-06-03 21:57:47 -0700364 return true;
365}
366
William Roberts696a66b2016-01-29 10:41:50 -0800367static bool validate_bool(char *value, char **errmsg) {
368
369 if (!strcmp("true", value) || !strcmp("false", value)) {
370 return true;
371 }
372
373 *errmsg = "Expecting \"true\" or \"false\"";
374 return false;
375}
376
377static bool validate_levelFrom(char *value, char **errmsg) {
378
379 if(strcasecmp(value, "none") && strcasecmp(value, "all") &&
380 strcasecmp(value, "app") && strcasecmp(value, "user")) {
381 *errmsg = "Expecting one of: \"none\", \"all\", \"app\" or \"user\"";
382 return false;
383 }
384 return true;
385}
386
387static bool validate_selinux_type(char *value, char **errmsg) {
388
389 /*
390 * No policy file present means we cannot check
391 * SE Linux types
392 */
393 if (!pol.policy_file) {
394 return true;
395 }
396
397 if(!check_type(pol.db, value)) {
398 *errmsg = "Expecting a valid SELinux type";
399 return false;
400 }
401
402 return true;
403}
404
405static bool validate_selinux_level(char *value, char **errmsg) {
406
407 /*
408 * No policy file present means we cannot check
409 * SE Linux MLS
410 */
411 if (!pol.policy_file) {
412 return true;
413 }
414
415 int ret = sepol_mls_check(pol.handle, pol.db, value);
416 if (ret < 0) {
417 *errmsg = "Expecting a valid SELinux MLS value";
418 return false;
419 }
420
421 return true;
422}
423
Michael Peckf54b3622017-02-14 09:48:57 -0800424static bool validate_uint(char *value, char **errmsg) {
425
426 char *endptr;
427 long longvalue;
428 longvalue = strtol(value, &endptr, 10);
429 if (('\0' != *endptr) || (longvalue < 0) || (longvalue > INT32_MAX)) {
430 *errmsg = "Expecting a valid unsigned integer";
431 return false;
432 }
433
434 return true;
435}
436
William Robertsf0e0a942012-08-27 15:41:15 -0700437/**
438 * Validates a key_map against a set of enforcement rules, this
439 * function exits the application on a type that cannot be properly
440 * checked
441 *
442 * @param m
443 * The key map to check
444 * @param lineno
445 * The line number in the source file for the corresponding key map
William Robertsfff29802012-11-27 14:20:34 -0800446 * @return
William Roberts81e1f902015-06-03 21:57:47 -0700447 * true if valid, false if invalid
William Robertsf0e0a942012-08-27 15:41:15 -0700448 */
William Roberts81e1f902015-06-03 21:57:47 -0700449static bool key_map_validate(key_map *m, const char *filename, int lineno,
450 bool is_neverallow) {
William Robertsf0e0a942012-08-27 15:41:15 -0700451
Janis Danisevskis750d7972016-04-01 13:31:57 +0100452 PCRE2_SIZE erroff;
453 int errcode;
William Roberts81e1f902015-06-03 21:57:47 -0700454 bool rc = true;
William Robertsf0e0a942012-08-27 15:41:15 -0700455 char *key = m->name;
456 char *value = m->data;
William Roberts696a66b2016-01-29 10:41:50 -0800457 char *errmsg = NULL;
Janis Danisevskis750d7972016-04-01 13:31:57 +0100458 char errstr[256];
William Robertsf0e0a942012-08-27 15:41:15 -0700459
William Roberts0ae3a8a2012-09-04 11:51:04 -0700460 log_info("Validating %s=%s\n", key, value);
461
William Roberts81e1f902015-06-03 21:57:47 -0700462 /*
463 * Neverallows are completely skipped from sanity checking so you can match
464 * un-unspecified inputs.
465 */
466 if (is_neverallow) {
467 if (!m->regex.compiled) {
Janis Danisevskis750d7972016-04-01 13:31:57 +0100468 rc = compile_regex(m, &errcode, &erroff);
William Roberts81e1f902015-06-03 21:57:47 -0700469 if (!rc) {
Janis Danisevskis750d7972016-04-01 13:31:57 +0100470 pcre2_get_error_message(errcode,
471 (PCRE2_UCHAR*) errstr,
472 sizeof(errstr));
473 log_error("Invalid regex on line %d : %s PCRE error: %s at offset %lu",
474 lineno, value, errstr, erroff);
William Roberts81e1f902015-06-03 21:57:47 -0700475 }
476 }
477 goto out;
478 }
479
William Roberts696a66b2016-01-29 10:41:50 -0800480 /* If the key has a validation routine, call it */
481 if (m->fn_validate) {
482 rc = m->fn_validate(value, &errmsg);
William Robertsf0e0a942012-08-27 15:41:15 -0700483
William Roberts696a66b2016-01-29 10:41:50 -0800484 if (!rc) {
485 log_error("Could not validate key \"%s\" for value \"%s\" on line: %d in file: \"%s\": %s\n", key, value,
486 lineno, filename, errmsg);
William Robertsf0e0a942012-08-27 15:41:15 -0700487 }
488 }
489
William Roberts0ae3a8a2012-09-04 11:51:04 -0700490out:
491 log_info("Key map validate returning: %d\n", rc);
492 return rc;
William Robertsf0e0a942012-08-27 15:41:15 -0700493}
494
495/**
496 * Prints a rule map back to a file
497 * @param fp
498 * The file handle to print too
499 * @param r
500 * The rule map to print
501 */
502static void rule_map_print(FILE *fp, rule_map *r) {
503
William Roberts610a4b12013-10-15 18:26:00 -0700504 size_t i;
William Robertsf0e0a942012-08-27 15:41:15 -0700505 key_map *m;
506
507 for (i = 0; i < r->length; i++) {
508 m = &(r->m[i]);
509 if (i < r->length - 1)
510 fprintf(fp, "%s=%s ", m->name, m->data);
511 else
512 fprintf(fp, "%s=%s", m->name, m->data);
513 }
514}
515
516/**
517 * Compare two rule maps for equality
518 * @param rmA
519 * a rule map to check
520 * @param rmB
521 * a rule map to check
522 * @return
William Robertsae23a1f2012-09-05 12:53:52 -0700523 * a map_match enum indicating the result
William Robertsf0e0a942012-08-27 15:41:15 -0700524 */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700525static map_match rule_map_cmp(rule_map *rmA, rule_map *rmB) {
William Robertsf0e0a942012-08-27 15:41:15 -0700526
William Roberts610a4b12013-10-15 18:26:00 -0700527 size_t i;
528 size_t j;
William Robertsf0e0a942012-08-27 15:41:15 -0700529 int inputs_found = 0;
530 int num_of_matched_inputs = 0;
531 int input_mode = 0;
William Roberts610a4b12013-10-15 18:26:00 -0700532 size_t matches = 0;
William Robertsf0e0a942012-08-27 15:41:15 -0700533 key_map *mA;
534 key_map *mB;
535
William Robertsf0e0a942012-08-27 15:41:15 -0700536 for (i = 0; i < rmA->length; i++) {
537 mA = &(rmA->m[i]);
538
539 for (j = 0; j < rmB->length; j++) {
540 mB = &(rmB->m[j]);
541 input_mode = 0;
542
William Robertsf0e0a942012-08-27 15:41:15 -0700543 if (strcmp(mA->name, mB->name))
544 continue;
545
546 if (strcmp(mA->data, mB->data))
547 continue;
548
549 if (mB->dir != mA->dir)
550 continue;
551 else if (mB->dir == dir_in) {
552 input_mode = 1;
553 inputs_found++;
554 }
555
William Roberts0ae3a8a2012-09-04 11:51:04 -0700556 if (input_mode) {
William Roberts1e8c0612013-04-19 19:06:02 -0700557 log_info("Matched input lines: name=%s data=%s\n", mA->name, mA->data);
William Robertsf0e0a942012-08-27 15:41:15 -0700558 num_of_matched_inputs++;
William Roberts0ae3a8a2012-09-04 11:51:04 -0700559 }
William Robertsf0e0a942012-08-27 15:41:15 -0700560
561 /* Match found, move on */
William Roberts1e8c0612013-04-19 19:06:02 -0700562 log_info("Matched lines: name=%s data=%s", mA->name, mA->data);
William Robertsf0e0a942012-08-27 15:41:15 -0700563 matches++;
564 break;
565 }
566 }
567
568 /* If they all matched*/
William Roberts0ae3a8a2012-09-04 11:51:04 -0700569 if (matches == rmA->length) {
570 log_info("Rule map cmp MATCH\n");
571 return map_matched;
572 }
William Robertsf0e0a942012-08-27 15:41:15 -0700573
574 /* They didn't all match but the input's did */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700575 else if (num_of_matched_inputs == inputs_found) {
576 log_info("Rule map cmp INPUT MATCH\n");
577 return map_input_matched;
578 }
William Robertsf0e0a942012-08-27 15:41:15 -0700579
580 /* They didn't all match, and the inputs didn't match, ie it didn't
581 * match */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700582 else {
583 log_info("Rule map cmp NO MATCH\n");
584 return map_no_matches;
585 }
William Robertsf0e0a942012-08-27 15:41:15 -0700586}
587
588/**
589 * Frees a rule map
590 * @param rm
591 * rule map to be freed.
William Roberts7d65b542015-06-19 09:43:28 -0700592 * @is_in_htable
593 * True if the rule map has been added to the hash table, false
594 * otherwise.
595 */
596static void rule_map_free(rule_map *rm, bool is_in_htable) {
William Robertsf0e0a942012-08-27 15:41:15 -0700597
William Roberts610a4b12013-10-15 18:26:00 -0700598 size_t i;
599 size_t len = rm->length;
William Robertsf0e0a942012-08-27 15:41:15 -0700600 for (i = 0; i < len; i++) {
601 key_map *m = &(rm->m[i]);
602 free(m->data);
William Roberts81e1f902015-06-03 21:57:47 -0700603
604 if (m->regex.compiled) {
Janis Danisevskis750d7972016-04-01 13:31:57 +0100605 pcre2_code_free(m->regex.compiled);
William Roberts81e1f902015-06-03 21:57:47 -0700606 }
607
Janis Danisevskis750d7972016-04-01 13:31:57 +0100608 if (m->regex.match_data) {
609 pcre2_match_data_free(m->regex.match_data);
William Roberts81e1f902015-06-03 21:57:47 -0700610 }
William Robertsf0e0a942012-08-27 15:41:15 -0700611 }
612
William Roberts7d65b542015-06-19 09:43:28 -0700613 /*
614 * hdestroy() frees comparsion keys for non glibc
615 * on GLIBC we always free on NON-GLIBC we free if
616 * it is not in the htable.
617 */
618 if (rm->key) {
rpcraig5dbfdc02012-10-23 11:03:47 -0400619#ifdef __GLIBC__
William Roberts7d65b542015-06-19 09:43:28 -0700620 /* silence unused warning */
621 (void)is_in_htable;
William Robertsf0e0a942012-08-27 15:41:15 -0700622 free(rm->key);
William Roberts7d65b542015-06-19 09:43:28 -0700623#else
624 if (!is_in_htable) {
625 free(rm->key);
626 }
rpcraig5dbfdc02012-10-23 11:03:47 -0400627#endif
William Roberts7d65b542015-06-19 09:43:28 -0700628 }
William Robertsf0e0a942012-08-27 15:41:15 -0700629
William Roberts81e1f902015-06-03 21:57:47 -0700630 free(rm->filename);
William Robertsf0e0a942012-08-27 15:41:15 -0700631 free(rm);
632}
633
634static void free_kvp(kvp *k) {
635 free(k->key);
636 free(k->value);
637}
638
639/**
William Roberts61846292013-10-15 09:38:24 -0700640 * Checks a rule_map for any variation of KVP's that shouldn't be allowed.
William Roberts81e1f902015-06-03 21:57:47 -0700641 * It builds an assertion failure list for each rule map.
William Roberts61846292013-10-15 09:38:24 -0700642 * Note that this function logs all errors.
643 *
644 * Current Checks:
645 * 1. That a specified name entry should have a specified seinfo entry as well.
William Roberts81e1f902015-06-03 21:57:47 -0700646 * 2. That no rule violates a neverallow
William Roberts61846292013-10-15 09:38:24 -0700647 * @param rm
648 * The rule map to check for validity.
William Roberts61846292013-10-15 09:38:24 -0700649 */
William Roberts81e1f902015-06-03 21:57:47 -0700650static void rule_map_validate(rule_map *rm) {
William Roberts61846292013-10-15 09:38:24 -0700651
William Roberts81e1f902015-06-03 21:57:47 -0700652 size_t i, j;
653 const key_map *rule;
654 key_map *nrule;
655 hash_entry *e;
656 rule_map *assert;
657 list_element *cursor;
William Roberts61846292013-10-15 09:38:24 -0700658
William Roberts81e1f902015-06-03 21:57:47 -0700659 list_for_each(&nallow_list, cursor) {
660 e = list_entry(cursor, typeof(*e), listify);
661 assert = e->r;
William Roberts61846292013-10-15 09:38:24 -0700662
William Roberts81e1f902015-06-03 21:57:47 -0700663 size_t cnt = 0;
664
665 for (j = 0; j < assert->length; j++) {
666 nrule = &(assert->m[j]);
667
668 // mark that nrule->name is for a null check
669 bool is_null_check = !strcmp(nrule->data, "\"\"");
670
671 for (i = 0; i < rm->length; i++) {
672 rule = &(rm->m[i]);
673
674 if (!strcmp(rule->name, nrule->name)) {
675
676 /* the name was found, (data cannot be false) then it was specified */
677 is_null_check = false;
678
679 if (match_regex(nrule, rule)) {
680 cnt++;
681 }
682 }
683 }
684
685 /*
686 * the nrule was marked in a null check and we never found a match on nrule, thus
687 * it matched and we update the cnt
688 */
689 if (is_null_check) {
690 cnt++;
691 }
William Roberts61846292013-10-15 09:38:24 -0700692 }
William Roberts81e1f902015-06-03 21:57:47 -0700693 if (cnt == assert->length) {
694 list_append(&rm->violations, &assert->listify);
William Roberts61846292013-10-15 09:38:24 -0700695 }
696 }
William Roberts61846292013-10-15 09:38:24 -0700697}
698
699/**
William Robertsf0e0a942012-08-27 15:41:15 -0700700 * Given a set of key value pairs, this will construct a new rule map.
701 * On error this function calls exit.
702 * @param keys
703 * Keys from a rule line to map
704 * @param num_of_keys
705 * The length of the keys array
706 * @param lineno
707 * The line number the keys were extracted from
708 * @return
709 * A rule map pointer.
710 */
William Roberts81e1f902015-06-03 21:57:47 -0700711static rule_map *rule_map_new(kvp keys[], size_t num_of_keys, int lineno,
712 const char *filename, bool is_never_allow) {
William Robertsf0e0a942012-08-27 15:41:15 -0700713
William Roberts610a4b12013-10-15 18:26:00 -0700714 size_t i = 0, j = 0;
William Robertsf0e0a942012-08-27 15:41:15 -0700715 rule_map *new_map = NULL;
716 kvp *k = NULL;
717 key_map *r = NULL, *x = NULL;
Stephen Smalley534fb072015-02-13 14:06:08 -0500718 bool seen[KVP_NUM_OF_RULES];
719
720 for (i = 0; i < KVP_NUM_OF_RULES; i++)
721 seen[i] = false;
William Robertsf0e0a942012-08-27 15:41:15 -0700722
723 new_map = calloc(1, (num_of_keys * sizeof(key_map)) + sizeof(rule_map));
724 if (!new_map)
725 goto oom;
726
William Roberts81e1f902015-06-03 21:57:47 -0700727 new_map->is_never_allow = is_never_allow;
William Robertsf0e0a942012-08-27 15:41:15 -0700728 new_map->length = num_of_keys;
729 new_map->lineno = lineno;
William Roberts81e1f902015-06-03 21:57:47 -0700730 new_map->filename = strdup(filename);
731 if (!new_map->filename) {
732 goto oom;
733 }
William Robertsf0e0a942012-08-27 15:41:15 -0700734
735 /* For all the keys in a rule line*/
736 for (i = 0; i < num_of_keys; i++) {
737 k = &(keys[i]);
738 r = &(new_map->m[i]);
739
740 for (j = 0; j < KVP_NUM_OF_RULES; j++) {
741 x = &(rules[j]);
742
743 /* Only assign key name to map name */
744 if (strcasecmp(k->key, x->name)) {
liwugang57d66ef2018-11-14 16:39:39 +0800745 if (j == KVP_NUM_OF_RULES - 1) {
William Robertsf0e0a942012-08-27 15:41:15 -0700746 log_error("No match for key: %s\n", k->key);
747 goto err;
748 }
749 continue;
750 }
751
Stephen Smalley534fb072015-02-13 14:06:08 -0500752 if (seen[j]) {
753 log_error("Duplicated key: %s\n", k->key);
754 goto err;
755 }
756 seen[j] = true;
757
William Robertsf0e0a942012-08-27 15:41:15 -0700758 memcpy(r, x, sizeof(key_map));
759
760 /* Assign rule map value to one from file */
761 r->data = strdup(k->value);
762 if (!r->data)
763 goto oom;
764
765 /* Enforce type check*/
William Roberts0ae3a8a2012-09-04 11:51:04 -0700766 log_info("Validating keys!\n");
William Roberts81e1f902015-06-03 21:57:47 -0700767 if (!key_map_validate(r, filename, lineno, new_map->is_never_allow)) {
William Robertsf0e0a942012-08-27 15:41:15 -0700768 log_error("Could not validate\n");
769 goto err;
770 }
771
William Roberts81e1f902015-06-03 21:57:47 -0700772 /*
773 * Only build key off of inputs with the exception of neverallows.
774 * Neverallows are keyed off of all key value pairs,
775 */
776 if (r->dir == dir_in || new_map->is_never_allow) {
William Robertsf0e0a942012-08-27 15:41:15 -0700777 char *tmp;
William Robertsb3ab56c2012-09-17 14:35:02 -0700778 int key_len = strlen(k->key);
779 int val_len = strlen(k->value);
780 int l = (new_map->key) ? strlen(new_map->key) : 0;
781 l = l + key_len + val_len;
William Robertsf0e0a942012-08-27 15:41:15 -0700782 l += 1;
783
784 tmp = realloc(new_map->key, l);
785 if (!tmp)
786 goto oom;
787
William Robertsb3ab56c2012-09-17 14:35:02 -0700788 if (!new_map->key)
789 memset(tmp, 0, l);
790
William Robertsf0e0a942012-08-27 15:41:15 -0700791 new_map->key = tmp;
792
William Robertsb3ab56c2012-09-17 14:35:02 -0700793 strncat(new_map->key, k->key, key_len);
794 strncat(new_map->key, k->value, val_len);
William Robertsf0e0a942012-08-27 15:41:15 -0700795 }
796 break;
797 }
798 free_kvp(k);
799 }
800
801 if (new_map->key == NULL) {
802 log_error("Strange, no keys found, input file corrupt perhaps?\n");
803 goto err;
804 }
805
806 return new_map;
807
808oom:
809 log_error("Out of memory!\n");
810err:
811 if(new_map) {
William Roberts7d65b542015-06-19 09:43:28 -0700812 rule_map_free(new_map, false);
William Robertsf0e0a942012-08-27 15:41:15 -0700813 for (; i < num_of_keys; i++) {
814 k = &(keys[i]);
815 free_kvp(k);
816 }
817 }
Stephen Smalley534fb072015-02-13 14:06:08 -0500818 return NULL;
William Robertsf0e0a942012-08-27 15:41:15 -0700819}
820
821/**
822 * Print the usage of the program
823 */
824static void usage() {
825 printf(
826 "checkseapp [options] <input file>\n"
827 "Processes an seapp_contexts file specified by argument <input file> (default stdin) "
William Robertsae23a1f2012-09-05 12:53:52 -0700828 "and allows later declarations to override previous ones on a match.\n"
William Robertsf0e0a942012-08-27 15:41:15 -0700829 "Options:\n"
830 "-h - print this help message\n"
831 "-v - enable verbose debugging informations\n"
William Roberts63297212013-04-19 19:06:23 -0700832 "-p policy file - specify policy file for strict checking of output selectors against the policy\n"
William Roberts81e1f902015-06-03 21:57:47 -0700833 "-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 -0700834}
835
836static void init() {
837
William Roberts81e1f902015-06-03 21:57:47 -0700838 bool has_out_file;
839 list_element *cursor;
840 file_info *tmp;
841
842 /* input files if the list is empty, use stdin */
843 if (!input_file_list.head) {
844 log_info("Using stdin for input\n");
845 tmp = malloc(sizeof(*tmp));
846 if (!tmp) {
847 log_error("oom");
William Robertsf0e0a942012-08-27 15:41:15 -0700848 exit(EXIT_FAILURE);
849 }
William Roberts81e1f902015-06-03 21:57:47 -0700850 tmp->name = "stdin";
851 tmp->file = stdin;
852 list_append(&input_file_list, &(tmp->listify));
853 }
854 else {
855 list_for_each(&input_file_list, cursor) {
856 tmp = list_entry(cursor, typeof(*tmp), listify);
857
858 log_info("Opening input file: \"%s\"\n", tmp->name);
859 tmp->file = fopen(tmp->name, "r");
860 if (!tmp->file) {
861 log_error("Could not open file: %s error: %s\n", tmp->name,
862 strerror(errno));
863 exit(EXIT_FAILURE);
864 }
865 }
William Robertsf0e0a942012-08-27 15:41:15 -0700866 }
867
William Roberts81e1f902015-06-03 21:57:47 -0700868 has_out_file = out_file.name != NULL;
869
870 /* If output file is -, then use stdout, else open the path */
871 if (has_out_file && !strcmp(out_file.name, "-")) {
872 out_file.file = stdout;
873 out_file.name = "stdout";
874 }
875 else if (has_out_file) {
876 out_file.file = fopen(out_file.name, "w+");
877 }
878
879 if (has_out_file && !out_file.file) {
880 log_error("Could not open file: \"%s\" error: \"%s\"\n", out_file.name,
881 strerror(errno));
882 exit(EXIT_FAILURE);
William Robertsf0e0a942012-08-27 15:41:15 -0700883 }
884
885 if (pol.policy_file_name) {
William Robertsf0e0a942012-08-27 15:41:15 -0700886 log_info("Opening policy file: %s\n", pol.policy_file_name);
887 pol.policy_file = fopen(pol.policy_file_name, "rb");
888 if (!pol.policy_file) {
889 log_error("Could not open file: %s error: %s\n",
890 pol.policy_file_name, strerror(errno));
891 exit(EXIT_FAILURE);
892 }
893
894 pol.handle = sepol_handle_create();
895 if (!pol.handle) {
896 log_error("Could not create sepolicy handle: %s\n",
897 strerror(errno));
898 exit(EXIT_FAILURE);
899 }
900
901 if (sepol_policy_file_create(&pol.pf) < 0) {
902 log_error("Could not create sepolicy file: %s!\n",
903 strerror(errno));
904 exit(EXIT_FAILURE);
905 }
906
907 sepol_policy_file_set_fp(pol.pf, pol.policy_file);
908 sepol_policy_file_set_handle(pol.pf, pol.handle);
909
910 if (sepol_policydb_create(&pol.db) < 0) {
911 log_error("Could not create sepolicy db: %s!\n",
912 strerror(errno));
913 exit(EXIT_FAILURE);
914 }
915
916 if (sepol_policydb_read(pol.db, pol.pf) < 0) {
William Robertsf7d6bb32016-08-08 10:31:54 -0700917 log_error("Could not load policy file to db: invalid input file!\n");
William Robertsf0e0a942012-08-27 15:41:15 -0700918 exit(EXIT_FAILURE);
919 }
920 }
921
William Roberts81e1f902015-06-03 21:57:47 -0700922 list_for_each(&input_file_list, cursor) {
923 tmp = list_entry(cursor, typeof(*tmp), listify);
924 log_info("Input file set to: \"%s\"\n", tmp->name);
925 }
926
927 log_info("Policy file set to: \"%s\"\n",
928 (pol.policy_file_name == NULL) ? "None" : pol.policy_file_name);
929 log_info("Output file set to: \"%s\"\n", out_file.name);
William Robertsf0e0a942012-08-27 15:41:15 -0700930
William Roberts0ae3a8a2012-09-04 11:51:04 -0700931#if !defined(LINK_SEPOL_STATIC)
William Robertsa53ccf32012-09-17 12:53:44 -0700932 log_warn("LINK_SEPOL_STATIC is not defined\n""Not checking types!");
William Roberts0ae3a8a2012-09-04 11:51:04 -0700933#endif
934
William Robertsf0e0a942012-08-27 15:41:15 -0700935}
936
937/**
938 * Handle parsing and setting the global flags for the command line
939 * options. This function calls exit on failure.
940 * @param argc
941 * argument count
942 * @param argv
943 * argument list
944 */
945static void handle_options(int argc, char *argv[]) {
946
947 int c;
William Roberts81e1f902015-06-03 21:57:47 -0700948 file_info *input_file;
William Robertsf0e0a942012-08-27 15:41:15 -0700949
William Robertsf26b6d42015-06-23 10:22:45 -0700950 while ((c = getopt(argc, argv, "ho:p:v")) != -1) {
William Robertsf0e0a942012-08-27 15:41:15 -0700951 switch (c) {
952 case 'h':
953 usage();
954 exit(EXIT_SUCCESS);
955 case 'o':
William Roberts81e1f902015-06-03 21:57:47 -0700956 out_file.name = optarg;
William Robertsf0e0a942012-08-27 15:41:15 -0700957 break;
958 case 'p':
959 pol.policy_file_name = optarg;
960 break;
961 case 'v':
962 log_set_verbose();
963 break;
964 case '?':
965 if (optopt == 'o' || optopt == 'p')
966 log_error("Option -%c requires an argument.\n", optopt);
967 else if (isprint (optopt))
968 log_error("Unknown option `-%c'.\n", optopt);
969 else {
970 log_error(
971 "Unknown option character `\\x%x'.\n",
972 optopt);
William Robertsf0e0a942012-08-27 15:41:15 -0700973 }
William Robertsf0e0a942012-08-27 15:41:15 -0700974 default:
975 exit(EXIT_FAILURE);
976 }
977 }
978
William Roberts81e1f902015-06-03 21:57:47 -0700979 for (c = optind; c < argc; c++) {
William Robertsf0e0a942012-08-27 15:41:15 -0700980
William Roberts81e1f902015-06-03 21:57:47 -0700981 input_file = calloc(1, sizeof(*input_file));
982 if (!input_file) {
983 log_error("oom");
984 exit(EXIT_FAILURE);
985 }
986 input_file->name = argv[c];
987 list_append(&input_file_list, &input_file->listify);
William Robertsf0e0a942012-08-27 15:41:15 -0700988 }
989}
990
991/**
992 * Adds a rule to the hash table and to the ordered list if needed.
993 * @param rm
994 * The rule map to add.
995 */
996static void rule_add(rule_map *rm) {
997
William Roberts0ae3a8a2012-09-04 11:51:04 -0700998 map_match cmp;
William Robertsf0e0a942012-08-27 15:41:15 -0700999 ENTRY e;
1000 ENTRY *f;
1001 hash_entry *entry;
1002 hash_entry *tmp;
William Roberts81e1f902015-06-03 21:57:47 -07001003 list *list_to_addto;
William Robertsf0e0a942012-08-27 15:41:15 -07001004
1005 e.key = rm->key;
Rahul Chaudhrye1682c72016-10-12 12:22:39 -07001006 e.data = NULL;
William Robertsf0e0a942012-08-27 15:41:15 -07001007
William Roberts0ae3a8a2012-09-04 11:51:04 -07001008 log_info("Searching for key: %s\n", e.key);
William Robertsf0e0a942012-08-27 15:41:15 -07001009 /* Check to see if it has already been added*/
1010 f = hsearch(e, FIND);
1011
1012 /*
1013 * Since your only hashing on a partial key, the inputs we need to handle
1014 * when you want to override the outputs for a given input set, as well as
1015 * checking for duplicate entries.
1016 */
1017 if(f) {
William Roberts0ae3a8a2012-09-04 11:51:04 -07001018 log_info("Existing entry found!\n");
William Robertsf0e0a942012-08-27 15:41:15 -07001019 tmp = (hash_entry *)f->data;
1020 cmp = rule_map_cmp(rm, tmp->r);
Stephen Smalley0b820042015-02-13 14:58:31 -05001021 log_error("Duplicate line detected in file: %s\n"
1022 "Lines %d and %d %s!\n",
William Roberts81e1f902015-06-03 21:57:47 -07001023 rm->filename, tmp->r->lineno, rm->lineno,
Stephen Smalley0b820042015-02-13 14:58:31 -05001024 map_match_str[cmp]);
William Roberts7d65b542015-06-19 09:43:28 -07001025 rule_map_free(rm, false);
Stephen Smalley0b820042015-02-13 14:58:31 -05001026 goto err;
William Robertsf0e0a942012-08-27 15:41:15 -07001027 }
1028 /* It wasn't found, just add the rule map to the table */
1029 else {
1030
1031 entry = malloc(sizeof(hash_entry));
1032 if (!entry)
1033 goto oom;
1034
1035 entry->r = rm;
1036 e.data = entry;
1037
1038 f = hsearch(e, ENTER);
1039 if(f == NULL) {
1040 goto oom;
1041 }
1042
1043 /* new entries must be added to the ordered list */
1044 entry->r = rm;
William Roberts81e1f902015-06-03 21:57:47 -07001045 list_to_addto = rm->is_never_allow ? &nallow_list : &line_order_list;
1046 list_append(list_to_addto, &entry->listify);
William Robertsf0e0a942012-08-27 15:41:15 -07001047 }
1048
1049 return;
1050oom:
1051 if (e.key)
1052 free(e.key);
1053 if (entry)
1054 free(entry);
1055 if (rm)
1056 free(rm);
1057 log_error("Out of memory in function: %s\n", __FUNCTION__);
1058err:
1059 exit(EXIT_FAILURE);
1060}
1061
William Roberts81e1f902015-06-03 21:57:47 -07001062static void parse_file(file_info *in_file) {
William Robertsf0e0a942012-08-27 15:41:15 -07001063
William Roberts81e1f902015-06-03 21:57:47 -07001064 char *p;
William Robertsf0e0a942012-08-27 15:41:15 -07001065 size_t len;
William Roberts81e1f902015-06-03 21:57:47 -07001066 char *token;
1067 char *saveptr;
1068 bool is_never_allow;
1069 bool found_whitespace;
1070
1071 size_t lineno = 0;
1072 char *name = NULL;
1073 char *value = NULL;
William Roberts610a4b12013-10-15 18:26:00 -07001074 size_t token_cnt = 0;
William Robertsf0e0a942012-08-27 15:41:15 -07001075
William Roberts81e1f902015-06-03 21:57:47 -07001076 char line_buf[BUFSIZ];
1077 kvp keys[KVP_NUM_OF_RULES];
William Robertsf0e0a942012-08-27 15:41:15 -07001078
William Roberts81e1f902015-06-03 21:57:47 -07001079 while (fgets(line_buf, sizeof(line_buf) - 1, in_file->file)) {
William Robertsf0e0a942012-08-27 15:41:15 -07001080 lineno++;
William Roberts81e1f902015-06-03 21:57:47 -07001081 is_never_allow = false;
1082 found_whitespace = false;
1083 log_info("Got line %zu\n", lineno);
William Robertsf0e0a942012-08-27 15:41:15 -07001084 len = strlen(line_buf);
1085 if (line_buf[len - 1] == '\n')
Alice Chuf6647eb2012-10-30 16:27:00 -07001086 line_buf[len - 1] = '\0';
William Robertsf0e0a942012-08-27 15:41:15 -07001087 p = line_buf;
William Roberts81e1f902015-06-03 21:57:47 -07001088
1089 /* neverallow lines must start with neverallow (ie ^neverallow) */
1090 if (!strncasecmp(p, "neverallow", strlen("neverallow"))) {
1091 p += strlen("neverallow");
1092 is_never_allow = true;
1093 }
1094
1095 /* strip trailing whitespace skip comments */
1096 while (isspace(*p)) {
William Robertsf0e0a942012-08-27 15:41:15 -07001097 p++;
William Roberts81e1f902015-06-03 21:57:47 -07001098 found_whitespace = true;
1099 }
Alice Chuf6647eb2012-10-30 16:27:00 -07001100 if (*p == '#' || *p == '\0')
William Robertsf0e0a942012-08-27 15:41:15 -07001101 continue;
1102
1103 token = strtok_r(p, " \t", &saveptr);
1104 if (!token)
1105 goto err;
1106
1107 token_cnt = 0;
1108 memset(keys, 0, sizeof(kvp) * KVP_NUM_OF_RULES);
1109 while (1) {
William Roberts0ae3a8a2012-09-04 11:51:04 -07001110
William Robertsf0e0a942012-08-27 15:41:15 -07001111 name = token;
1112 value = strchr(name, '=');
1113 if (!value)
1114 goto err;
1115 *value++ = 0;
1116
1117 keys[token_cnt].key = strdup(name);
1118 if (!keys[token_cnt].key)
1119 goto oom;
1120
1121 keys[token_cnt].value = strdup(value);
1122 if (!keys[token_cnt].value)
1123 goto oom;
1124
1125 token_cnt++;
1126
1127 token = strtok_r(NULL, " \t", &saveptr);
1128 if (!token)
1129 break;
1130
1131 } /*End token parsing */
1132
William Roberts81e1f902015-06-03 21:57:47 -07001133 rule_map *r = rule_map_new(keys, token_cnt, lineno, in_file->name, is_never_allow);
Stephen Smalley534fb072015-02-13 14:06:08 -05001134 if (!r)
1135 goto err;
William Robertsf0e0a942012-08-27 15:41:15 -07001136 rule_add(r);
1137
1138 } /* End file parsing */
1139 return;
1140
1141err:
William Robertsefebf972016-01-29 10:34:04 -08001142 log_error("Reading file: \"%s\" line: %zu name: \"%s\" value: \"%s\"\n",
William Roberts81e1f902015-06-03 21:57:47 -07001143 in_file->name, lineno, name, value);
1144 if(found_whitespace && name && !strcasecmp(name, "neverallow")) {
1145 log_error("perhaps whitespace before neverallow\n");
1146 }
William Robertsf0e0a942012-08-27 15:41:15 -07001147 exit(EXIT_FAILURE);
1148oom:
1149 log_error("In function %s: Out of memory\n", __FUNCTION__);
1150 exit(EXIT_FAILURE);
1151}
1152
1153/**
William Roberts81e1f902015-06-03 21:57:47 -07001154 * Parses the seapp_contexts file and neverallow file
1155 * and adds them to the hash table and ordered list entries
1156 * when it encounters them.
1157 * Calls exit on failure.
1158 */
1159static void parse() {
1160
1161 file_info *current;
1162 list_element *cursor;
1163 list_for_each(&input_file_list, cursor) {
1164 current = list_entry(cursor, typeof(*current), listify);
1165 parse_file(current);
1166 }
1167}
1168
1169static void validate() {
1170
1171 list_element *cursor, *v;
1172 bool found_issues = false;
1173 hash_entry *e;
1174 rule_map *r;
1175 list_for_each(&line_order_list, cursor) {
1176 e = list_entry(cursor, typeof(*e), listify);
1177 rule_map_validate(e->r);
1178 }
1179
1180 list_for_each(&line_order_list, cursor) {
1181 e = list_entry(cursor, typeof(*e), listify);
1182 r = e->r;
1183 list_for_each(&r->violations, v) {
1184 found_issues = true;
1185 log_error("Rule in File \"%s\" on line %d: \"", e->r->filename, e->r->lineno);
1186 rule_map_print(stderr, e->r);
1187 r = list_entry(v, rule_map, listify);
1188 fprintf(stderr, "\" violates neverallow in File \"%s\" on line %d: \"", r->filename, r->lineno);
1189 rule_map_print(stderr, r);
1190 fprintf(stderr, "\"\n");
1191 }
1192 }
1193
1194 if (found_issues) {
1195 exit(EXIT_FAILURE);
1196 }
1197}
1198
1199/**
William Robertsf0e0a942012-08-27 15:41:15 -07001200 * Should be called after parsing to cause the printing of the rule_maps
1201 * stored in the ordered list, head first, which preserves the "first encountered"
1202 * ordering.
1203 */
1204static void output() {
1205
William Roberts81e1f902015-06-03 21:57:47 -07001206 hash_entry *e;
1207 list_element *cursor;
William Robertsf0e0a942012-08-27 15:41:15 -07001208
William Roberts81e1f902015-06-03 21:57:47 -07001209 if (!out_file.file) {
1210 log_info("No output file, not outputting.\n");
1211 return;
1212 }
1213
1214 list_for_each(&line_order_list, cursor) {
1215 e = list_entry(cursor, hash_entry, listify);
1216 rule_map_print(out_file.file, e->r);
1217 fprintf(out_file.file, "\n");
William Robertsf0e0a942012-08-27 15:41:15 -07001218 }
1219}
1220
1221/**
1222 * This function is registered to the at exit handler and should clean up
1223 * the programs dynamic resources, such as memory and fd's.
1224 */
1225static void cleanup() {
1226
1227 /* Only close this when it was opened by me and not the crt */
William Roberts81e1f902015-06-03 21:57:47 -07001228 if (out_file.name && strcmp(out_file.name, "stdout") && out_file.file) {
1229 log_info("Closing file: %s\n", out_file.name);
1230 fclose(out_file.file);
William Robertsf0e0a942012-08-27 15:41:15 -07001231 }
1232
1233 if (pol.policy_file) {
1234
1235 log_info("Closing file: %s\n", pol.policy_file_name);
1236 fclose(pol.policy_file);
1237
1238 if (pol.db)
1239 sepol_policydb_free(pol.db);
1240
1241 if (pol.pf)
1242 sepol_policy_file_free(pol.pf);
1243
1244 if (pol.handle)
1245 sepol_handle_destroy(pol.handle);
1246 }
1247
William Roberts81e1f902015-06-03 21:57:47 -07001248 log_info("Freeing lists\n");
1249 list_free(&input_file_list);
1250 list_free(&line_order_list);
1251 list_free(&nallow_list);
William Robertsf0e0a942012-08-27 15:41:15 -07001252 hdestroy();
1253}
1254
1255int main(int argc, char *argv[]) {
1256 if (!hcreate(TABLE_SIZE)) {
1257 log_error("Could not create hash table: %s\n", strerror(errno));
1258 exit(EXIT_FAILURE);
1259 }
1260 atexit(cleanup);
1261 handle_options(argc, argv);
1262 init();
1263 log_info("Starting to parse\n");
1264 parse();
1265 log_info("Parsing completed, generating output\n");
William Roberts81e1f902015-06-03 21:57:47 -07001266 validate();
William Robertsf0e0a942012-08-27 15:41:15 -07001267 output();
1268 log_info("Success, generated output\n");
1269 exit(EXIT_SUCCESS);
1270}