blob: e57a6b3fe40d92c2dd50f0e27969cada905530ad [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
Alan Stokes3c4375a2020-11-11 14:45:32 +000023#define APP_DATA_REQUIRED_ATTRIB "app_data_file_type"
24
William Roberts81e1f902015-06-03 21:57:47 -070025/**
26 * Initializes an empty, static list.
27 */
Chih-Hung Hsieh33500c92016-05-11 14:59:45 -070028#define list_init(free_fn) { .head = NULL, .tail = NULL, .freefn = (free_fn) }
William Roberts81e1f902015-06-03 21:57:47 -070029
30/**
31 * given an item in the list, finds the offset for the container
32 * it was stored in.
33 *
34 * @element The element from the list
35 * @type The container type ie what you allocated that has the list_element structure in it.
36 * @name The name of the field that is the list_element
37 *
38 */
39#define list_entry(element, type, name) \
Chih-Hung Hsieh33500c92016-05-11 14:59:45 -070040 (type *)(((uint8_t *)(element)) - (uint8_t *)&(((type *)NULL)->name))
William Roberts81e1f902015-06-03 21:57:47 -070041
42/**
43 * Iterates over the list, do not free elements from the list when using this.
44 * @list The list head to walk
45 * @var The variable name for the cursor
46 */
47#define list_for_each(list, var) \
Chih-Hung Hsieh33500c92016-05-11 14:59:45 -070048 for(var = (list)->head; var != NULL; var = var->next) /*NOLINT*/
William Roberts81e1f902015-06-03 21:57:47 -070049
50
William Robertsf0e0a942012-08-27 15:41:15 -070051typedef struct hash_entry hash_entry;
52typedef enum key_dir key_dir;
53typedef enum data_type data_type;
54typedef enum rule_map_switch rule_map_switch;
William Roberts0ae3a8a2012-09-04 11:51:04 -070055typedef enum map_match map_match;
William Robertsf0e0a942012-08-27 15:41:15 -070056typedef struct key_map key_map;
57typedef struct kvp kvp;
58typedef struct rule_map rule_map;
59typedef struct policy_info policy_info;
William Roberts81e1f902015-06-03 21:57:47 -070060typedef struct list_element list_element;
61typedef struct list list;
62typedef struct key_map_regex key_map_regex;
63typedef struct file_info file_info;
William Robertsf0e0a942012-08-27 15:41:15 -070064
William Roberts0ae3a8a2012-09-04 11:51:04 -070065enum map_match {
66 map_no_matches,
67 map_input_matched,
68 map_matched
69};
70
Stephen Smalley0b820042015-02-13 14:58:31 -050071const char *map_match_str[] = {
72 "do not match",
73 "match on all inputs",
74 "match on everything"
75};
76
William Robertsf0e0a942012-08-27 15:41:15 -070077/**
78 * Whether or not the "key" from a key vaue pair is considered an
79 * input or an output.
80 */
81enum key_dir {
82 dir_in, dir_out
83};
84
William Roberts81e1f902015-06-03 21:57:47 -070085struct list_element {
86 list_element *next;
87};
88
89struct list {
90 list_element *head;
91 list_element *tail;
92 void (*freefn)(list_element *e);
93};
94
95struct key_map_regex {
Janis Danisevskis750d7972016-04-01 13:31:57 +010096 pcre2_code *compiled;
97 pcre2_match_data *match_data;
William Robertsf0e0a942012-08-27 15:41:15 -070098};
99
100/**
101 * The workhorse of the logic. This struct maps key value pairs to
102 * an associated set of meta data maintained in rule_map_new()
103 */
104struct key_map {
105 char *name;
106 key_dir dir;
William Robertsf0e0a942012-08-27 15:41:15 -0700107 char *data;
William Roberts81e1f902015-06-03 21:57:47 -0700108 key_map_regex regex;
William Roberts696a66b2016-01-29 10:41:50 -0800109 bool (*fn_validate)(char *value, char **errmsg);
William Robertsf0e0a942012-08-27 15:41:15 -0700110};
111
112/**
113 * Key value pair struct, this represents the raw kvp values coming
114 * from the rules files.
115 */
116struct kvp {
117 char *key;
118 char *value;
119};
120
121/**
122 * Rules are made up of meta data and an associated set of kvp stored in a
123 * key_map array.
124 */
125struct rule_map {
William Roberts81e1f902015-06-03 21:57:47 -0700126 bool is_never_allow;
127 list violations;
128 list_element listify;
William Robertsf0e0a942012-08-27 15:41:15 -0700129 char *key; /** key value before hashing */
William Roberts610a4b12013-10-15 18:26:00 -0700130 size_t length; /** length of the key map */
William Robertsf0e0a942012-08-27 15:41:15 -0700131 int lineno; /** Line number rule was encounter on */
William Roberts81e1f902015-06-03 21:57:47 -0700132 char *filename; /** File it was found in */
William Robertsf0e0a942012-08-27 15:41:15 -0700133 key_map m[]; /** key value mapping */
134};
135
136struct hash_entry {
William Roberts81e1f902015-06-03 21:57:47 -0700137 list_element listify;
William Robertsf0e0a942012-08-27 15:41:15 -0700138 rule_map *r; /** The rule map to store at that location */
139};
140
141/**
142 * Data associated for a policy file
143 */
144struct policy_info {
145
146 char *policy_file_name; /** policy file path name */
147 FILE *policy_file; /** file handle to the policy file */
148 sepol_policydb_t *db;
149 sepol_policy_file_t *pf;
150 sepol_handle_t *handle;
151 sepol_context_t *con;
152};
153
William Roberts81e1f902015-06-03 21:57:47 -0700154struct file_info {
155 FILE *file; /** file itself */
156 const char *name; /** name of file. do not free, these are not alloc'd */
157 list_element listify;
158};
159
160static void input_file_list_freefn(list_element *e);
161static void line_order_list_freefn(list_element *e);
162static void rule_map_free(rule_map *rm, bool is_in_htable);
163
William Robertsf0e0a942012-08-27 15:41:15 -0700164/** Set to !0 to enable verbose logging */
165static int logging_verbose = 0;
166
167/** file handle to the output file */
William Roberts81e1f902015-06-03 21:57:47 -0700168static file_info out_file;
William Robertsf0e0a942012-08-27 15:41:15 -0700169
William Roberts81e1f902015-06-03 21:57:47 -0700170static list input_file_list = list_init(input_file_list_freefn);
William Robertsf0e0a942012-08-27 15:41:15 -0700171
172static policy_info pol = {
173 .policy_file_name = NULL,
174 .policy_file = NULL,
175 .db = NULL,
176 .pf = NULL,
177 .handle = NULL,
178 .con = NULL
179};
180
181/**
William Roberts81e1f902015-06-03 21:57:47 -0700182 * Head pointer to a linked list of
183 * rule map table entries (hash_entry), used for
184 * preserving the order of entries
185 * based on "first encounter"
186 */
187static list line_order_list = list_init(line_order_list_freefn);
188
189/*
190 * List of hash_entrys for never allow rules.
191 */
192static list nallow_list = list_init(line_order_list_freefn);
193
William Roberts696a66b2016-01-29 10:41:50 -0800194/* validation call backs */
195static bool validate_bool(char *value, char **errmsg);
196static bool validate_levelFrom(char *value, char **errmsg);
Alan Stokes3c4375a2020-11-11 14:45:32 +0000197static bool validate_domain(char *value, char **errmsg);
198static bool validate_type(char *value, char **errmsg);
William Roberts696a66b2016-01-29 10:41:50 -0800199static bool validate_selinux_level(char *value, char **errmsg);
Michael Peckf54b3622017-02-14 09:48:57 -0800200static bool validate_uint(char *value, char **errmsg);
William Roberts696a66b2016-01-29 10:41:50 -0800201
William Roberts81e1f902015-06-03 21:57:47 -0700202/**
William Robertsf0e0a942012-08-27 15:41:15 -0700203 * The heart of the mapping process, this must be updated if a new key value pair is added
204 * to a rule.
205 */
206key_map rules[] = {
207 /*Inputs*/
William Roberts29adea52016-01-29 15:12:58 -0800208 { .name = "isSystemServer", .dir = dir_in, .fn_validate = validate_bool },
Chad Brubaker06cf31e2016-10-06 13:15:44 -0700209 { .name = "isEphemeralApp", .dir = dir_in, .fn_validate = validate_bool },
William Roberts29adea52016-01-29 15:12:58 -0800210 { .name = "user", .dir = dir_in, },
211 { .name = "seinfo", .dir = dir_in, },
212 { .name = "name", .dir = dir_in, },
William Roberts29adea52016-01-29 15:12:58 -0800213 { .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 },
Charles Chen30704922023-01-15 05:41:42 +0000216 { .name = "isIsolatedComputeApp", .dir = dir_in, .fn_validate = validate_bool },
William Robertsf0e0a942012-08-27 15:41:15 -0700217 /*Outputs*/
Alan Stokes3c4375a2020-11-11 14:45:32 +0000218 { .name = "domain", .dir = dir_out, .fn_validate = validate_domain },
219 { .name = "type", .dir = dir_out, .fn_validate = validate_type },
William Roberts29adea52016-01-29 15:12:58 -0800220 { .name = "levelFromUid", .dir = dir_out, .fn_validate = validate_bool },
221 { .name = "levelFrom", .dir = dir_out, .fn_validate = validate_levelFrom },
222 { .name = "level", .dir = dir_out, .fn_validate = validate_selinux_level },
William Robertsfff29802012-11-27 14:20:34 -0800223};
William Robertsf0e0a942012-08-27 15:41:15 -0700224
225/**
William Roberts81e1f902015-06-03 21:57:47 -0700226 * Appends to the end of the list.
227 * @list The list to append to
228 * @e the element to append
William Robertsf0e0a942012-08-27 15:41:15 -0700229 */
William Roberts81e1f902015-06-03 21:57:47 -0700230void list_append(list *list, list_element *e) {
231
232 memset(e, 0, sizeof(*e));
233
234 if (list->head == NULL ) {
235 list->head = list->tail = e;
236 return;
237 }
238
239 list->tail->next = e;
240 list->tail = e;
241 return;
242}
William Robertsf0e0a942012-08-27 15:41:15 -0700243
244/**
William Roberts81e1f902015-06-03 21:57:47 -0700245 * Free's all the elements in the specified list.
246 * @list The list to free
William Robertsf0e0a942012-08-27 15:41:15 -0700247 */
William Roberts81e1f902015-06-03 21:57:47 -0700248static void list_free(list *list) {
249
250 list_element *tmp;
251 list_element *cursor = list->head;
252
253 while (cursor) {
254 tmp = cursor;
255 cursor = cursor->next;
256 if (list->freefn) {
257 list->freefn(tmp);
258 }
259 }
260}
261
262/*
263 * called when the lists are freed
264 */
265static void line_order_list_freefn(list_element *e) {
266 hash_entry *h = list_entry(e, typeof(*h), listify);
267 rule_map_free(h->r, true);
268 free(h);
269}
270
271static void input_file_list_freefn(list_element *e) {
272 file_info *f = list_entry(e, typeof(*f), listify);
273
274 if (f->file) {
275 fclose(f->file);
276 }
277 free(f);
278}
William Robertsf0e0a942012-08-27 15:41:15 -0700279
280/**
281 * Send a logging message to a file
282 * @param out
283 * Output file to send message too
284 * @param prefix
285 * A special prefix to write to the file, such as "Error:"
286 * @param fmt
287 * The printf style formatter to use, such as "%d"
288 */
William Roberts1e8c0612013-04-19 19:06:02 -0700289static void __attribute__ ((format(printf, 3, 4)))
290log_msg(FILE *out, const char *prefix, const char *fmt, ...) {
291
William Robertsf0e0a942012-08-27 15:41:15 -0700292 fprintf(out, "%s", prefix);
293 va_list args;
294 va_start(args, fmt);
295 vfprintf(out, fmt, args);
296 va_end(args);
297}
298
299/**
Alan Stokes3c4375a2020-11-11 14:45:32 +0000300 * Look up a type in the policy.
William Robertsf0e0a942012-08-27 15:41:15 -0700301 * @param db
302 * The policy db to search
303 * @param type
304 * The type to search for
Alan Stokes3c4375a2020-11-11 14:45:32 +0000305 * @param flavor
306 * The expected flavor of type
William Robertsf0e0a942012-08-27 15:41:15 -0700307 * @return
Alan Stokes3c4375a2020-11-11 14:45:32 +0000308 * Pointer to the type's datum if it exists in the policy with the expected
309 * flavor, NULL otherwise.
William Robertsf0e0a942012-08-27 15:41:15 -0700310 * @warning
Alan Stokes3c4375a2020-11-11 14:45:32 +0000311 * This function should not be called if libsepol is not linked statically
312 * to this executable and LINK_SEPOL_STATIC is not defined.
William Robertsf0e0a942012-08-27 15:41:15 -0700313 */
Alan Stokes3c4375a2020-11-11 14:45:32 +0000314static type_datum_t *find_type(sepol_policydb_t *db, char *type, uint32_t flavor) {
William Robertsf0e0a942012-08-27 15:41:15 -0700315
Alan Stokes3c4375a2020-11-11 14:45:32 +0000316 policydb_t *d = &db->p;
317 hashtab_datum_t dat = hashtab_search(d->p_types.table, type);
318 if (!dat) {
319 return NULL;
320 }
321 type_datum_t *type_dat = (type_datum_t *) dat;
322 if (type_dat->flavor != flavor) {
323 return NULL;
324 }
325 return type_dat;
326}
327
328static bool type_has_attribute(sepol_policydb_t *db, type_datum_t *type_dat,
329 type_datum_t *attrib_dat) {
330 policydb_t *d = &db->p;
331 ebitmap_t *attr_bits = &d->type_attr_map[type_dat->s.value - 1];
332 return ebitmap_get_bit(attr_bits, attrib_dat->s.value - 1) != 0;
William Robertsf0e0a942012-08-27 15:41:15 -0700333}
334
William Roberts81e1f902015-06-03 21:57:47 -0700335static bool match_regex(key_map *assert, const key_map *check) {
336
337 char *tomatch = check->data;
338
Janis Danisevskis750d7972016-04-01 13:31:57 +0100339 int ret = pcre2_match(assert->regex.compiled, (PCRE2_SPTR) tomatch,
340 PCRE2_ZERO_TERMINATED, 0, 0,
341 assert->regex.match_data, NULL);
William Roberts81e1f902015-06-03 21:57:47 -0700342
Janis Danisevskis750d7972016-04-01 13:31:57 +0100343 /* ret > 0 from pcre2_match means matched */
344 return ret > 0;
William Roberts81e1f902015-06-03 21:57:47 -0700345}
346
Janis Danisevskis750d7972016-04-01 13:31:57 +0100347static bool compile_regex(key_map *km, int *errcode, PCRE2_SIZE *erroff) {
William Roberts81e1f902015-06-03 21:57:47 -0700348
349 size_t size;
350 char *anchored;
351
352 /*
353 * Explicitly anchor all regex's
354 * The size is the length of the string to anchor (km->data), the anchor
355 * characters ^ and $ and the null byte. Hence strlen(km->data) + 3
356 */
357 size = strlen(km->data) + 3;
358 anchored = alloca(size);
359 sprintf(anchored, "^%s$", km->data);
360
Janis Danisevskis750d7972016-04-01 13:31:57 +0100361 km->regex.compiled = pcre2_compile((PCRE2_SPTR) anchored,
362 PCRE2_ZERO_TERMINATED,
363 PCRE2_DOTALL,
364 errcode, erroff,
365 NULL);
William Roberts81e1f902015-06-03 21:57:47 -0700366 if (!km->regex.compiled) {
367 return false;
368 }
369
Janis Danisevskis750d7972016-04-01 13:31:57 +0100370 km->regex.match_data = pcre2_match_data_create_from_pattern(
371 km->regex.compiled, NULL);
372 if (!km->regex.match_data) {
373 pcre2_code_free(km->regex.compiled);
374 return false;
375 }
William Roberts81e1f902015-06-03 21:57:47 -0700376 return true;
377}
378
William Roberts696a66b2016-01-29 10:41:50 -0800379static bool validate_bool(char *value, char **errmsg) {
380
381 if (!strcmp("true", value) || !strcmp("false", value)) {
382 return true;
383 }
384
385 *errmsg = "Expecting \"true\" or \"false\"";
386 return false;
387}
388
389static bool validate_levelFrom(char *value, char **errmsg) {
390
Alan Stokes3c4375a2020-11-11 14:45:32 +0000391 if (strcasecmp(value, "none") && strcasecmp(value, "all") &&
William Roberts696a66b2016-01-29 10:41:50 -0800392 strcasecmp(value, "app") && strcasecmp(value, "user")) {
393 *errmsg = "Expecting one of: \"none\", \"all\", \"app\" or \"user\"";
394 return false;
395 }
396 return true;
397}
398
Alan Stokes3c4375a2020-11-11 14:45:32 +0000399static bool validate_domain(char *value, char **errmsg) {
William Roberts696a66b2016-01-29 10:41:50 -0800400
Alan Stokes3c4375a2020-11-11 14:45:32 +0000401#if defined(LINK_SEPOL_STATIC)
William Roberts696a66b2016-01-29 10:41:50 -0800402 /*
403 * No policy file present means we cannot check
404 * SE Linux types
405 */
406 if (!pol.policy_file) {
407 return true;
408 }
409
Alan Stokes3c4375a2020-11-11 14:45:32 +0000410 if (!find_type(pol.db, value, TYPE_TYPE)) {
William Roberts696a66b2016-01-29 10:41:50 -0800411 *errmsg = "Expecting a valid SELinux type";
412 return false;
413 }
Alan Stokes3c4375a2020-11-11 14:45:32 +0000414#endif
415
416 return true;
417}
418
419static bool validate_type(char *value, char **errmsg) {
420
421#if defined(LINK_SEPOL_STATIC)
422 /*
423 * No policy file present means we cannot check
424 * SE Linux types
425 */
426 if (!pol.policy_file) {
427 return true;
428 }
429
430 type_datum_t *type_dat = find_type(pol.db, value, TYPE_TYPE);
431 if (!type_dat) {
432 *errmsg = "Expecting a valid SELinux type";
433 return false;
434 }
435
436 type_datum_t *attrib_dat = find_type(pol.db, APP_DATA_REQUIRED_ATTRIB,
437 TYPE_ATTRIB);
438 if (!attrib_dat) {
439 /* If the policy doesn't contain the attribute, we can't check it */
440 return true;
441 }
442
443 if (!type_has_attribute(pol.db, type_dat, attrib_dat)) {
444 *errmsg = "Missing required attribute " APP_DATA_REQUIRED_ATTRIB;
445 return false;
446 }
447
448#endif
William Roberts696a66b2016-01-29 10:41:50 -0800449
450 return true;
451}
452
453static bool validate_selinux_level(char *value, char **errmsg) {
454
455 /*
456 * No policy file present means we cannot check
457 * SE Linux MLS
458 */
459 if (!pol.policy_file) {
460 return true;
461 }
462
463 int ret = sepol_mls_check(pol.handle, pol.db, value);
464 if (ret < 0) {
465 *errmsg = "Expecting a valid SELinux MLS value";
466 return false;
467 }
468
469 return true;
470}
471
Michael Peckf54b3622017-02-14 09:48:57 -0800472static bool validate_uint(char *value, char **errmsg) {
473
474 char *endptr;
475 long longvalue;
476 longvalue = strtol(value, &endptr, 10);
477 if (('\0' != *endptr) || (longvalue < 0) || (longvalue > INT32_MAX)) {
478 *errmsg = "Expecting a valid unsigned integer";
479 return false;
480 }
481
482 return true;
483}
484
William Robertsf0e0a942012-08-27 15:41:15 -0700485/**
486 * Validates a key_map against a set of enforcement rules, this
487 * function exits the application on a type that cannot be properly
488 * checked
489 *
490 * @param m
491 * The key map to check
492 * @param lineno
493 * The line number in the source file for the corresponding key map
William Robertsfff29802012-11-27 14:20:34 -0800494 * @return
William Roberts81e1f902015-06-03 21:57:47 -0700495 * true if valid, false if invalid
William Robertsf0e0a942012-08-27 15:41:15 -0700496 */
William Roberts81e1f902015-06-03 21:57:47 -0700497static bool key_map_validate(key_map *m, const char *filename, int lineno,
498 bool is_neverallow) {
William Robertsf0e0a942012-08-27 15:41:15 -0700499
Janis Danisevskis750d7972016-04-01 13:31:57 +0100500 PCRE2_SIZE erroff;
501 int errcode;
William Roberts81e1f902015-06-03 21:57:47 -0700502 bool rc = true;
William Robertsf0e0a942012-08-27 15:41:15 -0700503 char *key = m->name;
504 char *value = m->data;
William Roberts696a66b2016-01-29 10:41:50 -0800505 char *errmsg = NULL;
Janis Danisevskis750d7972016-04-01 13:31:57 +0100506 char errstr[256];
William Robertsf0e0a942012-08-27 15:41:15 -0700507
William Roberts0ae3a8a2012-09-04 11:51:04 -0700508 log_info("Validating %s=%s\n", key, value);
509
William Roberts81e1f902015-06-03 21:57:47 -0700510 /*
Joel Galensonb0d74a12020-07-27 09:30:34 -0700511 * Neverallows are completely skipped from validity checking so you can match
William Roberts81e1f902015-06-03 21:57:47 -0700512 * un-unspecified inputs.
513 */
514 if (is_neverallow) {
515 if (!m->regex.compiled) {
Janis Danisevskis750d7972016-04-01 13:31:57 +0100516 rc = compile_regex(m, &errcode, &erroff);
William Roberts81e1f902015-06-03 21:57:47 -0700517 if (!rc) {
Janis Danisevskis750d7972016-04-01 13:31:57 +0100518 pcre2_get_error_message(errcode,
519 (PCRE2_UCHAR*) errstr,
520 sizeof(errstr));
521 log_error("Invalid regex on line %d : %s PCRE error: %s at offset %lu",
522 lineno, value, errstr, erroff);
William Roberts81e1f902015-06-03 21:57:47 -0700523 }
524 }
525 goto out;
526 }
527
William Roberts696a66b2016-01-29 10:41:50 -0800528 /* If the key has a validation routine, call it */
529 if (m->fn_validate) {
530 rc = m->fn_validate(value, &errmsg);
William Robertsf0e0a942012-08-27 15:41:15 -0700531
William Roberts696a66b2016-01-29 10:41:50 -0800532 if (!rc) {
533 log_error("Could not validate key \"%s\" for value \"%s\" on line: %d in file: \"%s\": %s\n", key, value,
534 lineno, filename, errmsg);
William Robertsf0e0a942012-08-27 15:41:15 -0700535 }
536 }
537
William Roberts0ae3a8a2012-09-04 11:51:04 -0700538out:
539 log_info("Key map validate returning: %d\n", rc);
540 return rc;
William Robertsf0e0a942012-08-27 15:41:15 -0700541}
542
543/**
544 * Prints a rule map back to a file
545 * @param fp
546 * The file handle to print too
547 * @param r
548 * The rule map to print
549 */
550static void rule_map_print(FILE *fp, rule_map *r) {
551
William Roberts610a4b12013-10-15 18:26:00 -0700552 size_t i;
William Robertsf0e0a942012-08-27 15:41:15 -0700553 key_map *m;
554
555 for (i = 0; i < r->length; i++) {
556 m = &(r->m[i]);
557 if (i < r->length - 1)
558 fprintf(fp, "%s=%s ", m->name, m->data);
559 else
560 fprintf(fp, "%s=%s", m->name, m->data);
561 }
562}
563
564/**
565 * Compare two rule maps for equality
566 * @param rmA
567 * a rule map to check
568 * @param rmB
569 * a rule map to check
570 * @return
William Robertsae23a1f2012-09-05 12:53:52 -0700571 * a map_match enum indicating the result
William Robertsf0e0a942012-08-27 15:41:15 -0700572 */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700573static map_match rule_map_cmp(rule_map *rmA, rule_map *rmB) {
William Robertsf0e0a942012-08-27 15:41:15 -0700574
William Roberts610a4b12013-10-15 18:26:00 -0700575 size_t i;
576 size_t j;
William Robertsf0e0a942012-08-27 15:41:15 -0700577 int inputs_found = 0;
578 int num_of_matched_inputs = 0;
579 int input_mode = 0;
William Roberts610a4b12013-10-15 18:26:00 -0700580 size_t matches = 0;
William Robertsf0e0a942012-08-27 15:41:15 -0700581 key_map *mA;
582 key_map *mB;
583
William Robertsf0e0a942012-08-27 15:41:15 -0700584 for (i = 0; i < rmA->length; i++) {
585 mA = &(rmA->m[i]);
586
587 for (j = 0; j < rmB->length; j++) {
588 mB = &(rmB->m[j]);
589 input_mode = 0;
590
William Robertsf0e0a942012-08-27 15:41:15 -0700591 if (strcmp(mA->name, mB->name))
592 continue;
593
594 if (strcmp(mA->data, mB->data))
595 continue;
596
597 if (mB->dir != mA->dir)
598 continue;
599 else if (mB->dir == dir_in) {
600 input_mode = 1;
601 inputs_found++;
602 }
603
William Roberts0ae3a8a2012-09-04 11:51:04 -0700604 if (input_mode) {
William Roberts1e8c0612013-04-19 19:06:02 -0700605 log_info("Matched input lines: name=%s data=%s\n", mA->name, mA->data);
William Robertsf0e0a942012-08-27 15:41:15 -0700606 num_of_matched_inputs++;
William Roberts0ae3a8a2012-09-04 11:51:04 -0700607 }
William Robertsf0e0a942012-08-27 15:41:15 -0700608
609 /* Match found, move on */
William Roberts1e8c0612013-04-19 19:06:02 -0700610 log_info("Matched lines: name=%s data=%s", mA->name, mA->data);
William Robertsf0e0a942012-08-27 15:41:15 -0700611 matches++;
612 break;
613 }
614 }
615
616 /* If they all matched*/
William Roberts0ae3a8a2012-09-04 11:51:04 -0700617 if (matches == rmA->length) {
618 log_info("Rule map cmp MATCH\n");
619 return map_matched;
620 }
William Robertsf0e0a942012-08-27 15:41:15 -0700621
622 /* They didn't all match but the input's did */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700623 else if (num_of_matched_inputs == inputs_found) {
624 log_info("Rule map cmp INPUT MATCH\n");
625 return map_input_matched;
626 }
William Robertsf0e0a942012-08-27 15:41:15 -0700627
628 /* They didn't all match, and the inputs didn't match, ie it didn't
629 * match */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700630 else {
631 log_info("Rule map cmp NO MATCH\n");
632 return map_no_matches;
633 }
William Robertsf0e0a942012-08-27 15:41:15 -0700634}
635
636/**
637 * Frees a rule map
638 * @param rm
639 * rule map to be freed.
William Roberts7d65b542015-06-19 09:43:28 -0700640 * @is_in_htable
641 * True if the rule map has been added to the hash table, false
642 * otherwise.
643 */
644static void rule_map_free(rule_map *rm, bool is_in_htable) {
William Robertsf0e0a942012-08-27 15:41:15 -0700645
William Roberts610a4b12013-10-15 18:26:00 -0700646 size_t i;
647 size_t len = rm->length;
William Robertsf0e0a942012-08-27 15:41:15 -0700648 for (i = 0; i < len; i++) {
649 key_map *m = &(rm->m[i]);
650 free(m->data);
William Roberts81e1f902015-06-03 21:57:47 -0700651
652 if (m->regex.compiled) {
Janis Danisevskis750d7972016-04-01 13:31:57 +0100653 pcre2_code_free(m->regex.compiled);
William Roberts81e1f902015-06-03 21:57:47 -0700654 }
655
Janis Danisevskis750d7972016-04-01 13:31:57 +0100656 if (m->regex.match_data) {
657 pcre2_match_data_free(m->regex.match_data);
William Roberts81e1f902015-06-03 21:57:47 -0700658 }
William Robertsf0e0a942012-08-27 15:41:15 -0700659 }
660
William Roberts7d65b542015-06-19 09:43:28 -0700661 /*
662 * hdestroy() frees comparsion keys for non glibc
663 * on GLIBC we always free on NON-GLIBC we free if
664 * it is not in the htable.
665 */
666 if (rm->key) {
rpcraig5dbfdc02012-10-23 11:03:47 -0400667#ifdef __GLIBC__
William Roberts7d65b542015-06-19 09:43:28 -0700668 /* silence unused warning */
669 (void)is_in_htable;
William Robertsf0e0a942012-08-27 15:41:15 -0700670 free(rm->key);
William Roberts7d65b542015-06-19 09:43:28 -0700671#else
672 if (!is_in_htable) {
673 free(rm->key);
674 }
rpcraig5dbfdc02012-10-23 11:03:47 -0400675#endif
William Roberts7d65b542015-06-19 09:43:28 -0700676 }
William Robertsf0e0a942012-08-27 15:41:15 -0700677
William Roberts81e1f902015-06-03 21:57:47 -0700678 free(rm->filename);
William Robertsf0e0a942012-08-27 15:41:15 -0700679 free(rm);
680}
681
682static void free_kvp(kvp *k) {
683 free(k->key);
684 free(k->value);
685}
686
687/**
William Roberts61846292013-10-15 09:38:24 -0700688 * Checks a rule_map for any variation of KVP's that shouldn't be allowed.
William Roberts81e1f902015-06-03 21:57:47 -0700689 * It builds an assertion failure list for each rule map.
William Roberts61846292013-10-15 09:38:24 -0700690 * Note that this function logs all errors.
691 *
692 * Current Checks:
693 * 1. That a specified name entry should have a specified seinfo entry as well.
William Roberts81e1f902015-06-03 21:57:47 -0700694 * 2. That no rule violates a neverallow
William Roberts61846292013-10-15 09:38:24 -0700695 * @param rm
696 * The rule map to check for validity.
William Roberts61846292013-10-15 09:38:24 -0700697 */
William Roberts81e1f902015-06-03 21:57:47 -0700698static void rule_map_validate(rule_map *rm) {
William Roberts61846292013-10-15 09:38:24 -0700699
William Roberts81e1f902015-06-03 21:57:47 -0700700 size_t i, j;
701 const key_map *rule;
702 key_map *nrule;
703 hash_entry *e;
704 rule_map *assert;
705 list_element *cursor;
William Roberts61846292013-10-15 09:38:24 -0700706
William Roberts81e1f902015-06-03 21:57:47 -0700707 list_for_each(&nallow_list, cursor) {
708 e = list_entry(cursor, typeof(*e), listify);
709 assert = e->r;
William Roberts61846292013-10-15 09:38:24 -0700710
William Roberts81e1f902015-06-03 21:57:47 -0700711 size_t cnt = 0;
712
713 for (j = 0; j < assert->length; j++) {
714 nrule = &(assert->m[j]);
715
716 // mark that nrule->name is for a null check
717 bool is_null_check = !strcmp(nrule->data, "\"\"");
718
719 for (i = 0; i < rm->length; i++) {
720 rule = &(rm->m[i]);
721
722 if (!strcmp(rule->name, nrule->name)) {
723
724 /* the name was found, (data cannot be false) then it was specified */
725 is_null_check = false;
726
727 if (match_regex(nrule, rule)) {
728 cnt++;
729 }
730 }
731 }
732
733 /*
734 * the nrule was marked in a null check and we never found a match on nrule, thus
735 * it matched and we update the cnt
736 */
737 if (is_null_check) {
738 cnt++;
739 }
William Roberts61846292013-10-15 09:38:24 -0700740 }
William Roberts81e1f902015-06-03 21:57:47 -0700741 if (cnt == assert->length) {
742 list_append(&rm->violations, &assert->listify);
William Roberts61846292013-10-15 09:38:24 -0700743 }
744 }
William Roberts61846292013-10-15 09:38:24 -0700745}
746
747/**
William Robertsf0e0a942012-08-27 15:41:15 -0700748 * Given a set of key value pairs, this will construct a new rule map.
749 * On error this function calls exit.
750 * @param keys
751 * Keys from a rule line to map
752 * @param num_of_keys
753 * The length of the keys array
754 * @param lineno
755 * The line number the keys were extracted from
756 * @return
757 * A rule map pointer.
758 */
William Roberts81e1f902015-06-03 21:57:47 -0700759static rule_map *rule_map_new(kvp keys[], size_t num_of_keys, int lineno,
760 const char *filename, bool is_never_allow) {
William Robertsf0e0a942012-08-27 15:41:15 -0700761
William Roberts610a4b12013-10-15 18:26:00 -0700762 size_t i = 0, j = 0;
William Robertsf0e0a942012-08-27 15:41:15 -0700763 rule_map *new_map = NULL;
764 kvp *k = NULL;
765 key_map *r = NULL, *x = NULL;
Stephen Smalley534fb072015-02-13 14:06:08 -0500766 bool seen[KVP_NUM_OF_RULES];
767
768 for (i = 0; i < KVP_NUM_OF_RULES; i++)
769 seen[i] = false;
William Robertsf0e0a942012-08-27 15:41:15 -0700770
771 new_map = calloc(1, (num_of_keys * sizeof(key_map)) + sizeof(rule_map));
772 if (!new_map)
773 goto oom;
774
William Roberts81e1f902015-06-03 21:57:47 -0700775 new_map->is_never_allow = is_never_allow;
William Robertsf0e0a942012-08-27 15:41:15 -0700776 new_map->length = num_of_keys;
777 new_map->lineno = lineno;
William Roberts81e1f902015-06-03 21:57:47 -0700778 new_map->filename = strdup(filename);
779 if (!new_map->filename) {
780 goto oom;
781 }
William Robertsf0e0a942012-08-27 15:41:15 -0700782
783 /* For all the keys in a rule line*/
784 for (i = 0; i < num_of_keys; i++) {
785 k = &(keys[i]);
786 r = &(new_map->m[i]);
787
788 for (j = 0; j < KVP_NUM_OF_RULES; j++) {
789 x = &(rules[j]);
790
791 /* Only assign key name to map name */
792 if (strcasecmp(k->key, x->name)) {
liwugang57d66ef2018-11-14 16:39:39 +0800793 if (j == KVP_NUM_OF_RULES - 1) {
William Robertsf0e0a942012-08-27 15:41:15 -0700794 log_error("No match for key: %s\n", k->key);
795 goto err;
796 }
797 continue;
798 }
799
Stephen Smalley534fb072015-02-13 14:06:08 -0500800 if (seen[j]) {
801 log_error("Duplicated key: %s\n", k->key);
802 goto err;
803 }
804 seen[j] = true;
805
William Robertsf0e0a942012-08-27 15:41:15 -0700806 memcpy(r, x, sizeof(key_map));
807
808 /* Assign rule map value to one from file */
809 r->data = strdup(k->value);
810 if (!r->data)
811 goto oom;
812
813 /* Enforce type check*/
William Roberts0ae3a8a2012-09-04 11:51:04 -0700814 log_info("Validating keys!\n");
William Roberts81e1f902015-06-03 21:57:47 -0700815 if (!key_map_validate(r, filename, lineno, new_map->is_never_allow)) {
William Robertsf0e0a942012-08-27 15:41:15 -0700816 log_error("Could not validate\n");
817 goto err;
818 }
819
William Roberts81e1f902015-06-03 21:57:47 -0700820 /*
821 * Only build key off of inputs with the exception of neverallows.
822 * Neverallows are keyed off of all key value pairs,
823 */
824 if (r->dir == dir_in || new_map->is_never_allow) {
William Robertsf0e0a942012-08-27 15:41:15 -0700825 char *tmp;
William Robertsb3ab56c2012-09-17 14:35:02 -0700826 int key_len = strlen(k->key);
827 int val_len = strlen(k->value);
828 int l = (new_map->key) ? strlen(new_map->key) : 0;
829 l = l + key_len + val_len;
William Robertsf0e0a942012-08-27 15:41:15 -0700830 l += 1;
831
832 tmp = realloc(new_map->key, l);
833 if (!tmp)
834 goto oom;
835
William Robertsb3ab56c2012-09-17 14:35:02 -0700836 if (!new_map->key)
837 memset(tmp, 0, l);
838
William Robertsf0e0a942012-08-27 15:41:15 -0700839 new_map->key = tmp;
840
William Robertsb3ab56c2012-09-17 14:35:02 -0700841 strncat(new_map->key, k->key, key_len);
842 strncat(new_map->key, k->value, val_len);
William Robertsf0e0a942012-08-27 15:41:15 -0700843 }
844 break;
845 }
846 free_kvp(k);
847 }
848
849 if (new_map->key == NULL) {
850 log_error("Strange, no keys found, input file corrupt perhaps?\n");
851 goto err;
852 }
853
854 return new_map;
855
856oom:
857 log_error("Out of memory!\n");
858err:
Alan Stokes3c4375a2020-11-11 14:45:32 +0000859 if (new_map) {
William Roberts7d65b542015-06-19 09:43:28 -0700860 rule_map_free(new_map, false);
William Robertsf0e0a942012-08-27 15:41:15 -0700861 for (; i < num_of_keys; i++) {
862 k = &(keys[i]);
863 free_kvp(k);
864 }
865 }
Stephen Smalley534fb072015-02-13 14:06:08 -0500866 return NULL;
William Robertsf0e0a942012-08-27 15:41:15 -0700867}
868
869/**
870 * Print the usage of the program
871 */
872static void usage() {
873 printf(
874 "checkseapp [options] <input file>\n"
875 "Processes an seapp_contexts file specified by argument <input file> (default stdin) "
William Robertsae23a1f2012-09-05 12:53:52 -0700876 "and allows later declarations to override previous ones on a match.\n"
William Robertsf0e0a942012-08-27 15:41:15 -0700877 "Options:\n"
878 "-h - print this help message\n"
879 "-v - enable verbose debugging informations\n"
William Roberts63297212013-04-19 19:06:23 -0700880 "-p policy file - specify policy file for strict checking of output selectors against the policy\n"
William Roberts81e1f902015-06-03 21:57:47 -0700881 "-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 -0700882}
883
884static void init() {
885
William Roberts81e1f902015-06-03 21:57:47 -0700886 bool has_out_file;
887 list_element *cursor;
888 file_info *tmp;
889
890 /* input files if the list is empty, use stdin */
891 if (!input_file_list.head) {
892 log_info("Using stdin for input\n");
893 tmp = malloc(sizeof(*tmp));
894 if (!tmp) {
895 log_error("oom");
William Robertsf0e0a942012-08-27 15:41:15 -0700896 exit(EXIT_FAILURE);
897 }
William Roberts81e1f902015-06-03 21:57:47 -0700898 tmp->name = "stdin";
899 tmp->file = stdin;
900 list_append(&input_file_list, &(tmp->listify));
901 }
902 else {
903 list_for_each(&input_file_list, cursor) {
904 tmp = list_entry(cursor, typeof(*tmp), listify);
905
906 log_info("Opening input file: \"%s\"\n", tmp->name);
907 tmp->file = fopen(tmp->name, "r");
908 if (!tmp->file) {
909 log_error("Could not open file: %s error: %s\n", tmp->name,
910 strerror(errno));
911 exit(EXIT_FAILURE);
912 }
913 }
William Robertsf0e0a942012-08-27 15:41:15 -0700914 }
915
William Roberts81e1f902015-06-03 21:57:47 -0700916 has_out_file = out_file.name != NULL;
917
918 /* If output file is -, then use stdout, else open the path */
919 if (has_out_file && !strcmp(out_file.name, "-")) {
920 out_file.file = stdout;
921 out_file.name = "stdout";
922 }
923 else if (has_out_file) {
924 out_file.file = fopen(out_file.name, "w+");
925 }
926
927 if (has_out_file && !out_file.file) {
928 log_error("Could not open file: \"%s\" error: \"%s\"\n", out_file.name,
929 strerror(errno));
930 exit(EXIT_FAILURE);
William Robertsf0e0a942012-08-27 15:41:15 -0700931 }
932
933 if (pol.policy_file_name) {
William Robertsf0e0a942012-08-27 15:41:15 -0700934 log_info("Opening policy file: %s\n", pol.policy_file_name);
935 pol.policy_file = fopen(pol.policy_file_name, "rb");
936 if (!pol.policy_file) {
937 log_error("Could not open file: %s error: %s\n",
938 pol.policy_file_name, strerror(errno));
939 exit(EXIT_FAILURE);
940 }
941
942 pol.handle = sepol_handle_create();
943 if (!pol.handle) {
944 log_error("Could not create sepolicy handle: %s\n",
945 strerror(errno));
946 exit(EXIT_FAILURE);
947 }
948
949 if (sepol_policy_file_create(&pol.pf) < 0) {
950 log_error("Could not create sepolicy file: %s!\n",
951 strerror(errno));
952 exit(EXIT_FAILURE);
953 }
954
955 sepol_policy_file_set_fp(pol.pf, pol.policy_file);
956 sepol_policy_file_set_handle(pol.pf, pol.handle);
957
958 if (sepol_policydb_create(&pol.db) < 0) {
959 log_error("Could not create sepolicy db: %s!\n",
960 strerror(errno));
961 exit(EXIT_FAILURE);
962 }
963
964 if (sepol_policydb_read(pol.db, pol.pf) < 0) {
William Robertsf7d6bb32016-08-08 10:31:54 -0700965 log_error("Could not load policy file to db: invalid input file!\n");
William Robertsf0e0a942012-08-27 15:41:15 -0700966 exit(EXIT_FAILURE);
967 }
968 }
969
William Roberts81e1f902015-06-03 21:57:47 -0700970 list_for_each(&input_file_list, cursor) {
971 tmp = list_entry(cursor, typeof(*tmp), listify);
972 log_info("Input file set to: \"%s\"\n", tmp->name);
973 }
974
975 log_info("Policy file set to: \"%s\"\n",
976 (pol.policy_file_name == NULL) ? "None" : pol.policy_file_name);
977 log_info("Output file set to: \"%s\"\n", out_file.name);
William Robertsf0e0a942012-08-27 15:41:15 -0700978
William Roberts0ae3a8a2012-09-04 11:51:04 -0700979#if !defined(LINK_SEPOL_STATIC)
William Robertsa53ccf32012-09-17 12:53:44 -0700980 log_warn("LINK_SEPOL_STATIC is not defined\n""Not checking types!");
William Roberts0ae3a8a2012-09-04 11:51:04 -0700981#endif
982
William Robertsf0e0a942012-08-27 15:41:15 -0700983}
984
985/**
986 * Handle parsing and setting the global flags for the command line
987 * options. This function calls exit on failure.
988 * @param argc
989 * argument count
990 * @param argv
991 * argument list
992 */
993static void handle_options(int argc, char *argv[]) {
994
995 int c;
William Roberts81e1f902015-06-03 21:57:47 -0700996 file_info *input_file;
William Robertsf0e0a942012-08-27 15:41:15 -0700997
William Robertsf26b6d42015-06-23 10:22:45 -0700998 while ((c = getopt(argc, argv, "ho:p:v")) != -1) {
William Robertsf0e0a942012-08-27 15:41:15 -0700999 switch (c) {
1000 case 'h':
1001 usage();
1002 exit(EXIT_SUCCESS);
1003 case 'o':
William Roberts81e1f902015-06-03 21:57:47 -07001004 out_file.name = optarg;
William Robertsf0e0a942012-08-27 15:41:15 -07001005 break;
1006 case 'p':
1007 pol.policy_file_name = optarg;
1008 break;
1009 case 'v':
1010 log_set_verbose();
1011 break;
1012 case '?':
1013 if (optopt == 'o' || optopt == 'p')
1014 log_error("Option -%c requires an argument.\n", optopt);
1015 else if (isprint (optopt))
1016 log_error("Unknown option `-%c'.\n", optopt);
1017 else {
1018 log_error(
1019 "Unknown option character `\\x%x'.\n",
1020 optopt);
William Robertsf0e0a942012-08-27 15:41:15 -07001021 }
William Robertsf0e0a942012-08-27 15:41:15 -07001022 default:
1023 exit(EXIT_FAILURE);
1024 }
1025 }
1026
William Roberts81e1f902015-06-03 21:57:47 -07001027 for (c = optind; c < argc; c++) {
William Robertsf0e0a942012-08-27 15:41:15 -07001028
William Roberts81e1f902015-06-03 21:57:47 -07001029 input_file = calloc(1, sizeof(*input_file));
1030 if (!input_file) {
1031 log_error("oom");
1032 exit(EXIT_FAILURE);
1033 }
1034 input_file->name = argv[c];
1035 list_append(&input_file_list, &input_file->listify);
William Robertsf0e0a942012-08-27 15:41:15 -07001036 }
1037}
1038
1039/**
1040 * Adds a rule to the hash table and to the ordered list if needed.
1041 * @param rm
1042 * The rule map to add.
1043 */
1044static void rule_add(rule_map *rm) {
1045
William Roberts0ae3a8a2012-09-04 11:51:04 -07001046 map_match cmp;
William Robertsf0e0a942012-08-27 15:41:15 -07001047 ENTRY e;
1048 ENTRY *f;
1049 hash_entry *entry;
1050 hash_entry *tmp;
William Roberts81e1f902015-06-03 21:57:47 -07001051 list *list_to_addto;
William Robertsf0e0a942012-08-27 15:41:15 -07001052
1053 e.key = rm->key;
Rahul Chaudhrye1682c72016-10-12 12:22:39 -07001054 e.data = NULL;
William Robertsf0e0a942012-08-27 15:41:15 -07001055
William Roberts0ae3a8a2012-09-04 11:51:04 -07001056 log_info("Searching for key: %s\n", e.key);
William Robertsf0e0a942012-08-27 15:41:15 -07001057 /* Check to see if it has already been added*/
1058 f = hsearch(e, FIND);
1059
1060 /*
1061 * Since your only hashing on a partial key, the inputs we need to handle
1062 * when you want to override the outputs for a given input set, as well as
1063 * checking for duplicate entries.
1064 */
Alan Stokes3c4375a2020-11-11 14:45:32 +00001065 if (f) {
William Roberts0ae3a8a2012-09-04 11:51:04 -07001066 log_info("Existing entry found!\n");
William Robertsf0e0a942012-08-27 15:41:15 -07001067 tmp = (hash_entry *)f->data;
1068 cmp = rule_map_cmp(rm, tmp->r);
Stephen Smalley0b820042015-02-13 14:58:31 -05001069 log_error("Duplicate line detected in file: %s\n"
1070 "Lines %d and %d %s!\n",
William Roberts81e1f902015-06-03 21:57:47 -07001071 rm->filename, tmp->r->lineno, rm->lineno,
Stephen Smalley0b820042015-02-13 14:58:31 -05001072 map_match_str[cmp]);
William Roberts7d65b542015-06-19 09:43:28 -07001073 rule_map_free(rm, false);
Stephen Smalley0b820042015-02-13 14:58:31 -05001074 goto err;
William Robertsf0e0a942012-08-27 15:41:15 -07001075 }
1076 /* It wasn't found, just add the rule map to the table */
1077 else {
1078
1079 entry = malloc(sizeof(hash_entry));
1080 if (!entry)
1081 goto oom;
1082
1083 entry->r = rm;
1084 e.data = entry;
1085
1086 f = hsearch(e, ENTER);
Alan Stokes3c4375a2020-11-11 14:45:32 +00001087 if (f == NULL) {
William Robertsf0e0a942012-08-27 15:41:15 -07001088 goto oom;
1089 }
1090
1091 /* new entries must be added to the ordered list */
1092 entry->r = rm;
William Roberts81e1f902015-06-03 21:57:47 -07001093 list_to_addto = rm->is_never_allow ? &nallow_list : &line_order_list;
1094 list_append(list_to_addto, &entry->listify);
William Robertsf0e0a942012-08-27 15:41:15 -07001095 }
1096
1097 return;
1098oom:
1099 if (e.key)
1100 free(e.key);
1101 if (entry)
1102 free(entry);
1103 if (rm)
1104 free(rm);
1105 log_error("Out of memory in function: %s\n", __FUNCTION__);
1106err:
1107 exit(EXIT_FAILURE);
1108}
1109
William Roberts81e1f902015-06-03 21:57:47 -07001110static void parse_file(file_info *in_file) {
William Robertsf0e0a942012-08-27 15:41:15 -07001111
William Roberts81e1f902015-06-03 21:57:47 -07001112 char *p;
William Robertsf0e0a942012-08-27 15:41:15 -07001113 size_t len;
William Roberts81e1f902015-06-03 21:57:47 -07001114 char *token;
1115 char *saveptr;
1116 bool is_never_allow;
1117 bool found_whitespace;
1118
1119 size_t lineno = 0;
1120 char *name = NULL;
1121 char *value = NULL;
William Roberts610a4b12013-10-15 18:26:00 -07001122 size_t token_cnt = 0;
William Robertsf0e0a942012-08-27 15:41:15 -07001123
William Roberts81e1f902015-06-03 21:57:47 -07001124 char line_buf[BUFSIZ];
1125 kvp keys[KVP_NUM_OF_RULES];
William Robertsf0e0a942012-08-27 15:41:15 -07001126
William Roberts81e1f902015-06-03 21:57:47 -07001127 while (fgets(line_buf, sizeof(line_buf) - 1, in_file->file)) {
William Robertsf0e0a942012-08-27 15:41:15 -07001128 lineno++;
William Roberts81e1f902015-06-03 21:57:47 -07001129 is_never_allow = false;
1130 found_whitespace = false;
1131 log_info("Got line %zu\n", lineno);
William Robertsf0e0a942012-08-27 15:41:15 -07001132 len = strlen(line_buf);
1133 if (line_buf[len - 1] == '\n')
Alice Chuf6647eb2012-10-30 16:27:00 -07001134 line_buf[len - 1] = '\0';
William Robertsf0e0a942012-08-27 15:41:15 -07001135 p = line_buf;
William Roberts81e1f902015-06-03 21:57:47 -07001136
1137 /* neverallow lines must start with neverallow (ie ^neverallow) */
1138 if (!strncasecmp(p, "neverallow", strlen("neverallow"))) {
1139 p += strlen("neverallow");
1140 is_never_allow = true;
1141 }
1142
1143 /* strip trailing whitespace skip comments */
1144 while (isspace(*p)) {
William Robertsf0e0a942012-08-27 15:41:15 -07001145 p++;
William Roberts81e1f902015-06-03 21:57:47 -07001146 found_whitespace = true;
1147 }
Alice Chuf6647eb2012-10-30 16:27:00 -07001148 if (*p == '#' || *p == '\0')
William Robertsf0e0a942012-08-27 15:41:15 -07001149 continue;
1150
1151 token = strtok_r(p, " \t", &saveptr);
1152 if (!token)
1153 goto err;
1154
1155 token_cnt = 0;
1156 memset(keys, 0, sizeof(kvp) * KVP_NUM_OF_RULES);
1157 while (1) {
William Roberts0ae3a8a2012-09-04 11:51:04 -07001158
William Robertsf0e0a942012-08-27 15:41:15 -07001159 name = token;
1160 value = strchr(name, '=');
1161 if (!value)
1162 goto err;
1163 *value++ = 0;
1164
1165 keys[token_cnt].key = strdup(name);
1166 if (!keys[token_cnt].key)
1167 goto oom;
1168
1169 keys[token_cnt].value = strdup(value);
1170 if (!keys[token_cnt].value)
1171 goto oom;
1172
1173 token_cnt++;
1174
1175 token = strtok_r(NULL, " \t", &saveptr);
1176 if (!token)
1177 break;
1178
liwugangeb74dd92018-11-15 20:13:15 +08001179 if (token_cnt == KVP_NUM_OF_RULES)
1180 goto oob;
1181
William Robertsf0e0a942012-08-27 15:41:15 -07001182 } /*End token parsing */
1183
William Roberts81e1f902015-06-03 21:57:47 -07001184 rule_map *r = rule_map_new(keys, token_cnt, lineno, in_file->name, is_never_allow);
Stephen Smalley534fb072015-02-13 14:06:08 -05001185 if (!r)
1186 goto err;
William Robertsf0e0a942012-08-27 15:41:15 -07001187 rule_add(r);
1188
1189 } /* End file parsing */
1190 return;
1191
1192err:
William Robertsefebf972016-01-29 10:34:04 -08001193 log_error("Reading file: \"%s\" line: %zu name: \"%s\" value: \"%s\"\n",
William Roberts81e1f902015-06-03 21:57:47 -07001194 in_file->name, lineno, name, value);
Alan Stokes3c4375a2020-11-11 14:45:32 +00001195 if (found_whitespace && name && !strcasecmp(name, "neverallow")) {
William Roberts81e1f902015-06-03 21:57:47 -07001196 log_error("perhaps whitespace before neverallow\n");
1197 }
William Robertsf0e0a942012-08-27 15:41:15 -07001198 exit(EXIT_FAILURE);
1199oom:
1200 log_error("In function %s: Out of memory\n", __FUNCTION__);
1201 exit(EXIT_FAILURE);
liwugangeb74dd92018-11-15 20:13:15 +08001202oob:
1203 log_error("Reading file: \"%s\" line: %zu reason: the size of key pairs exceeds the MAX(%zu)\n",
1204 in_file->name, lineno, KVP_NUM_OF_RULES);
1205 exit(EXIT_FAILURE);
William Robertsf0e0a942012-08-27 15:41:15 -07001206}
1207
1208/**
William Roberts81e1f902015-06-03 21:57:47 -07001209 * Parses the seapp_contexts file and neverallow file
1210 * and adds them to the hash table and ordered list entries
1211 * when it encounters them.
1212 * Calls exit on failure.
1213 */
1214static void parse() {
1215
1216 file_info *current;
1217 list_element *cursor;
1218 list_for_each(&input_file_list, cursor) {
1219 current = list_entry(cursor, typeof(*current), listify);
1220 parse_file(current);
1221 }
1222}
1223
1224static void validate() {
1225
1226 list_element *cursor, *v;
1227 bool found_issues = false;
1228 hash_entry *e;
1229 rule_map *r;
1230 list_for_each(&line_order_list, cursor) {
1231 e = list_entry(cursor, typeof(*e), listify);
1232 rule_map_validate(e->r);
1233 }
1234
1235 list_for_each(&line_order_list, cursor) {
1236 e = list_entry(cursor, typeof(*e), listify);
1237 r = e->r;
1238 list_for_each(&r->violations, v) {
1239 found_issues = true;
1240 log_error("Rule in File \"%s\" on line %d: \"", e->r->filename, e->r->lineno);
1241 rule_map_print(stderr, e->r);
1242 r = list_entry(v, rule_map, listify);
1243 fprintf(stderr, "\" violates neverallow in File \"%s\" on line %d: \"", r->filename, r->lineno);
1244 rule_map_print(stderr, r);
1245 fprintf(stderr, "\"\n");
1246 }
1247 }
1248
1249 if (found_issues) {
1250 exit(EXIT_FAILURE);
1251 }
1252}
1253
1254/**
William Robertsf0e0a942012-08-27 15:41:15 -07001255 * Should be called after parsing to cause the printing of the rule_maps
1256 * stored in the ordered list, head first, which preserves the "first encountered"
1257 * ordering.
1258 */
1259static void output() {
1260
William Roberts81e1f902015-06-03 21:57:47 -07001261 hash_entry *e;
1262 list_element *cursor;
William Robertsf0e0a942012-08-27 15:41:15 -07001263
William Roberts81e1f902015-06-03 21:57:47 -07001264 if (!out_file.file) {
1265 log_info("No output file, not outputting.\n");
1266 return;
1267 }
1268
1269 list_for_each(&line_order_list, cursor) {
1270 e = list_entry(cursor, hash_entry, listify);
1271 rule_map_print(out_file.file, e->r);
1272 fprintf(out_file.file, "\n");
William Robertsf0e0a942012-08-27 15:41:15 -07001273 }
1274}
1275
1276/**
1277 * This function is registered to the at exit handler and should clean up
1278 * the programs dynamic resources, such as memory and fd's.
1279 */
1280static void cleanup() {
1281
1282 /* Only close this when it was opened by me and not the crt */
William Roberts81e1f902015-06-03 21:57:47 -07001283 if (out_file.name && strcmp(out_file.name, "stdout") && out_file.file) {
1284 log_info("Closing file: %s\n", out_file.name);
1285 fclose(out_file.file);
William Robertsf0e0a942012-08-27 15:41:15 -07001286 }
1287
1288 if (pol.policy_file) {
1289
1290 log_info("Closing file: %s\n", pol.policy_file_name);
1291 fclose(pol.policy_file);
1292
1293 if (pol.db)
1294 sepol_policydb_free(pol.db);
1295
1296 if (pol.pf)
1297 sepol_policy_file_free(pol.pf);
1298
1299 if (pol.handle)
1300 sepol_handle_destroy(pol.handle);
1301 }
1302
William Roberts81e1f902015-06-03 21:57:47 -07001303 log_info("Freeing lists\n");
1304 list_free(&input_file_list);
1305 list_free(&line_order_list);
1306 list_free(&nallow_list);
William Robertsf0e0a942012-08-27 15:41:15 -07001307 hdestroy();
1308}
1309
1310int main(int argc, char *argv[]) {
1311 if (!hcreate(TABLE_SIZE)) {
1312 log_error("Could not create hash table: %s\n", strerror(errno));
1313 exit(EXIT_FAILURE);
1314 }
1315 atexit(cleanup);
1316 handle_options(argc, argv);
1317 init();
1318 log_info("Starting to parse\n");
1319 parse();
1320 log_info("Parsing completed, generating output\n");
William Roberts81e1f902015-06-03 21:57:47 -07001321 validate();
William Robertsf0e0a942012-08-27 15:41:15 -07001322 output();
1323 log_info("Success, generated output\n");
1324 exit(EXIT_SUCCESS);
1325}