blob: 862ecce6220210f53f74740ec151454464526333 [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"
Inseob Kimd7d36092023-06-26 20:48:48 +090024#define COREDOMAIN "coredomain"
Alan Stokes3c4375a2020-11-11 14:45:32 +000025
William Roberts81e1f902015-06-03 21:57:47 -070026/**
27 * Initializes an empty, static list.
28 */
Chih-Hung Hsieh33500c92016-05-11 14:59:45 -070029#define list_init(free_fn) { .head = NULL, .tail = NULL, .freefn = (free_fn) }
William Roberts81e1f902015-06-03 21:57:47 -070030
31/**
32 * given an item in the list, finds the offset for the container
33 * it was stored in.
34 *
35 * @element The element from the list
36 * @type The container type ie what you allocated that has the list_element structure in it.
37 * @name The name of the field that is the list_element
38 *
39 */
40#define list_entry(element, type, name) \
Chih-Hung Hsieh33500c92016-05-11 14:59:45 -070041 (type *)(((uint8_t *)(element)) - (uint8_t *)&(((type *)NULL)->name))
William Roberts81e1f902015-06-03 21:57:47 -070042
43/**
44 * Iterates over the list, do not free elements from the list when using this.
45 * @list The list head to walk
46 * @var The variable name for the cursor
47 */
48#define list_for_each(list, var) \
Chih-Hung Hsieh33500c92016-05-11 14:59:45 -070049 for(var = (list)->head; var != NULL; var = var->next) /*NOLINT*/
William Roberts81e1f902015-06-03 21:57:47 -070050
51
William Robertsf0e0a942012-08-27 15:41:15 -070052typedef struct hash_entry hash_entry;
53typedef enum key_dir key_dir;
54typedef enum data_type data_type;
55typedef enum rule_map_switch rule_map_switch;
William Roberts0ae3a8a2012-09-04 11:51:04 -070056typedef enum map_match map_match;
William Robertsf0e0a942012-08-27 15:41:15 -070057typedef struct key_map key_map;
58typedef struct kvp kvp;
59typedef struct rule_map rule_map;
60typedef struct policy_info policy_info;
William Roberts81e1f902015-06-03 21:57:47 -070061typedef struct list_element list_element;
62typedef struct list list;
63typedef struct key_map_regex key_map_regex;
64typedef struct file_info file_info;
Inseob Kimd7d36092023-06-26 20:48:48 +090065typedef struct coredomain_violation_entry coredomain_violation_entry;
William Robertsf0e0a942012-08-27 15:41:15 -070066
William Roberts0ae3a8a2012-09-04 11:51:04 -070067enum map_match {
68 map_no_matches,
69 map_input_matched,
70 map_matched
71};
72
Stephen Smalley0b820042015-02-13 14:58:31 -050073const char *map_match_str[] = {
74 "do not match",
75 "match on all inputs",
76 "match on everything"
77};
78
William Robertsf0e0a942012-08-27 15:41:15 -070079/**
80 * Whether or not the "key" from a key vaue pair is considered an
81 * input or an output.
82 */
83enum key_dir {
84 dir_in, dir_out
85};
86
William Roberts81e1f902015-06-03 21:57:47 -070087struct list_element {
88 list_element *next;
89};
90
91struct list {
92 list_element *head;
93 list_element *tail;
94 void (*freefn)(list_element *e);
95};
96
97struct key_map_regex {
Janis Danisevskis750d7972016-04-01 13:31:57 +010098 pcre2_code *compiled;
99 pcre2_match_data *match_data;
William Robertsf0e0a942012-08-27 15:41:15 -0700100};
101
102/**
103 * The workhorse of the logic. This struct maps key value pairs to
104 * an associated set of meta data maintained in rule_map_new()
105 */
106struct key_map {
107 char *name;
108 key_dir dir;
William Robertsf0e0a942012-08-27 15:41:15 -0700109 char *data;
William Roberts81e1f902015-06-03 21:57:47 -0700110 key_map_regex regex;
Inseob Kimd7d36092023-06-26 20:48:48 +0900111 bool (*fn_validate)(char *value, const char *filename, int lineno, char **errmsg);
William Robertsf0e0a942012-08-27 15:41:15 -0700112};
113
114/**
115 * Key value pair struct, this represents the raw kvp values coming
116 * from the rules files.
117 */
118struct kvp {
119 char *key;
120 char *value;
121};
122
123/**
124 * Rules are made up of meta data and an associated set of kvp stored in a
125 * key_map array.
126 */
127struct rule_map {
William Roberts81e1f902015-06-03 21:57:47 -0700128 bool is_never_allow;
129 list violations;
130 list_element listify;
William Robertsf0e0a942012-08-27 15:41:15 -0700131 char *key; /** key value before hashing */
William Roberts610a4b12013-10-15 18:26:00 -0700132 size_t length; /** length of the key map */
William Robertsf0e0a942012-08-27 15:41:15 -0700133 int lineno; /** Line number rule was encounter on */
William Roberts81e1f902015-06-03 21:57:47 -0700134 char *filename; /** File it was found in */
William Robertsf0e0a942012-08-27 15:41:15 -0700135 key_map m[]; /** key value mapping */
136};
137
138struct hash_entry {
William Roberts81e1f902015-06-03 21:57:47 -0700139 list_element listify;
William Robertsf0e0a942012-08-27 15:41:15 -0700140 rule_map *r; /** The rule map to store at that location */
141};
142
143/**
144 * Data associated for a policy file
145 */
146struct policy_info {
147
148 char *policy_file_name; /** policy file path name */
149 FILE *policy_file; /** file handle to the policy file */
150 sepol_policydb_t *db;
151 sepol_policy_file_t *pf;
152 sepol_handle_t *handle;
153 sepol_context_t *con;
Inseob Kimd7d36092023-06-26 20:48:48 +0900154 bool vendor;
William Robertsf0e0a942012-08-27 15:41:15 -0700155};
156
William Roberts81e1f902015-06-03 21:57:47 -0700157struct file_info {
158 FILE *file; /** file itself */
159 const char *name; /** name of file. do not free, these are not alloc'd */
160 list_element listify;
161};
162
Inseob Kimd7d36092023-06-26 20:48:48 +0900163struct coredomain_violation_entry {
164 list_element listify;
165 char *domain;
166 char *filename;
167 int lineno;
168};
169
170static void coredomain_violation_list_freefn(list_element *e);
William Roberts81e1f902015-06-03 21:57:47 -0700171static void input_file_list_freefn(list_element *e);
172static void line_order_list_freefn(list_element *e);
173static void rule_map_free(rule_map *rm, bool is_in_htable);
174
William Robertsf0e0a942012-08-27 15:41:15 -0700175/** Set to !0 to enable verbose logging */
176static int logging_verbose = 0;
177
178/** file handle to the output file */
William Roberts81e1f902015-06-03 21:57:47 -0700179static file_info out_file;
William Robertsf0e0a942012-08-27 15:41:15 -0700180
William Roberts81e1f902015-06-03 21:57:47 -0700181static list input_file_list = list_init(input_file_list_freefn);
William Robertsf0e0a942012-08-27 15:41:15 -0700182
Inseob Kimd7d36092023-06-26 20:48:48 +0900183static list coredomain_violation_list = list_init(coredomain_violation_list_freefn);
184
William Robertsf0e0a942012-08-27 15:41:15 -0700185static policy_info pol = {
186 .policy_file_name = NULL,
187 .policy_file = NULL,
188 .db = NULL,
189 .pf = NULL,
190 .handle = NULL,
Inseob Kimd7d36092023-06-26 20:48:48 +0900191 .con = NULL,
192 .vendor = false
William Robertsf0e0a942012-08-27 15:41:15 -0700193};
194
195/**
William Roberts81e1f902015-06-03 21:57:47 -0700196 * Head pointer to a linked list of
197 * rule map table entries (hash_entry), used for
198 * preserving the order of entries
199 * based on "first encounter"
200 */
201static list line_order_list = list_init(line_order_list_freefn);
202
203/*
204 * List of hash_entrys for never allow rules.
205 */
206static list nallow_list = list_init(line_order_list_freefn);
207
William Roberts696a66b2016-01-29 10:41:50 -0800208/* validation call backs */
Inseob Kimd7d36092023-06-26 20:48:48 +0900209static bool validate_bool(char *value, const char *filename, int lineno, char **errmsg);
210static bool validate_levelFrom(char *value, const char *filename, int lineno, char **errmsg);
211static bool validate_domain(char *value, const char *filename, int lineno, char **errmsg);
212static bool validate_type(char *value, const char *filename, int lineno, char **errmsg);
213static bool validate_selinux_level(char *value, const char *filename, int lineno, char **errmsg);
214static bool validate_uint(char *value, const char *filename, int lineno, char **errmsg);
William Roberts696a66b2016-01-29 10:41:50 -0800215
William Roberts81e1f902015-06-03 21:57:47 -0700216/**
William Robertsf0e0a942012-08-27 15:41:15 -0700217 * The heart of the mapping process, this must be updated if a new key value pair is added
218 * to a rule.
219 */
220key_map rules[] = {
221 /*Inputs*/
William Roberts29adea52016-01-29 15:12:58 -0800222 { .name = "isSystemServer", .dir = dir_in, .fn_validate = validate_bool },
Chad Brubaker06cf31e2016-10-06 13:15:44 -0700223 { .name = "isEphemeralApp", .dir = dir_in, .fn_validate = validate_bool },
William Roberts29adea52016-01-29 15:12:58 -0800224 { .name = "user", .dir = dir_in, },
225 { .name = "seinfo", .dir = dir_in, },
226 { .name = "name", .dir = dir_in, },
William Roberts29adea52016-01-29 15:12:58 -0800227 { .name = "isPrivApp", .dir = dir_in, .fn_validate = validate_bool },
Michael Peckf54b3622017-02-14 09:48:57 -0800228 { .name = "minTargetSdkVersion", .dir = dir_in, .fn_validate = validate_uint },
Yabin Cuiffa2b612018-11-02 14:34:06 -0700229 { .name = "fromRunAs", .dir = dir_in, .fn_validate = validate_bool },
Charles Chen30704922023-01-15 05:41:42 +0000230 { .name = "isIsolatedComputeApp", .dir = dir_in, .fn_validate = validate_bool },
Mugdha Lakhani49075f92023-05-07 17:41:57 +0000231 { .name = "isSdkSandboxNext", .dir = dir_in, .fn_validate = validate_bool },
William Robertsf0e0a942012-08-27 15:41:15 -0700232 /*Outputs*/
Alan Stokes3c4375a2020-11-11 14:45:32 +0000233 { .name = "domain", .dir = dir_out, .fn_validate = validate_domain },
234 { .name = "type", .dir = dir_out, .fn_validate = validate_type },
William Roberts29adea52016-01-29 15:12:58 -0800235 { .name = "levelFromUid", .dir = dir_out, .fn_validate = validate_bool },
236 { .name = "levelFrom", .dir = dir_out, .fn_validate = validate_levelFrom },
237 { .name = "level", .dir = dir_out, .fn_validate = validate_selinux_level },
William Robertsfff29802012-11-27 14:20:34 -0800238};
William Robertsf0e0a942012-08-27 15:41:15 -0700239
240/**
William Roberts81e1f902015-06-03 21:57:47 -0700241 * Appends to the end of the list.
242 * @list The list to append to
243 * @e the element to append
William Robertsf0e0a942012-08-27 15:41:15 -0700244 */
William Roberts81e1f902015-06-03 21:57:47 -0700245void list_append(list *list, list_element *e) {
246
247 memset(e, 0, sizeof(*e));
248
249 if (list->head == NULL ) {
250 list->head = list->tail = e;
251 return;
252 }
253
254 list->tail->next = e;
255 list->tail = e;
256 return;
257}
William Robertsf0e0a942012-08-27 15:41:15 -0700258
259/**
William Roberts81e1f902015-06-03 21:57:47 -0700260 * Free's all the elements in the specified list.
261 * @list The list to free
William Robertsf0e0a942012-08-27 15:41:15 -0700262 */
William Roberts81e1f902015-06-03 21:57:47 -0700263static void list_free(list *list) {
264
265 list_element *tmp;
266 list_element *cursor = list->head;
267
268 while (cursor) {
269 tmp = cursor;
270 cursor = cursor->next;
271 if (list->freefn) {
272 list->freefn(tmp);
273 }
274 }
275}
276
277/*
278 * called when the lists are freed
279 */
280static void line_order_list_freefn(list_element *e) {
281 hash_entry *h = list_entry(e, typeof(*h), listify);
282 rule_map_free(h->r, true);
283 free(h);
284}
285
286static void input_file_list_freefn(list_element *e) {
287 file_info *f = list_entry(e, typeof(*f), listify);
288
289 if (f->file) {
290 fclose(f->file);
291 }
292 free(f);
293}
William Robertsf0e0a942012-08-27 15:41:15 -0700294
Inseob Kimd7d36092023-06-26 20:48:48 +0900295static void coredomain_violation_list_freefn(list_element *e) {
296 coredomain_violation_entry *c = list_entry(e, typeof(*c), listify);
297
298 free(c->domain);
299 free(c->filename);
300 free(c);
301}
302
William Robertsf0e0a942012-08-27 15:41:15 -0700303/**
304 * Send a logging message to a file
305 * @param out
306 * Output file to send message too
307 * @param prefix
308 * A special prefix to write to the file, such as "Error:"
309 * @param fmt
310 * The printf style formatter to use, such as "%d"
311 */
William Roberts1e8c0612013-04-19 19:06:02 -0700312static void __attribute__ ((format(printf, 3, 4)))
313log_msg(FILE *out, const char *prefix, const char *fmt, ...) {
314
William Robertsf0e0a942012-08-27 15:41:15 -0700315 fprintf(out, "%s", prefix);
316 va_list args;
317 va_start(args, fmt);
318 vfprintf(out, fmt, args);
319 va_end(args);
320}
321
322/**
Alan Stokes3c4375a2020-11-11 14:45:32 +0000323 * Look up a type in the policy.
William Robertsf0e0a942012-08-27 15:41:15 -0700324 * @param db
325 * The policy db to search
326 * @param type
327 * The type to search for
Alan Stokes3c4375a2020-11-11 14:45:32 +0000328 * @param flavor
329 * The expected flavor of type
William Robertsf0e0a942012-08-27 15:41:15 -0700330 * @return
Alan Stokes3c4375a2020-11-11 14:45:32 +0000331 * Pointer to the type's datum if it exists in the policy with the expected
332 * flavor, NULL otherwise.
William Robertsf0e0a942012-08-27 15:41:15 -0700333 * @warning
Alan Stokes3c4375a2020-11-11 14:45:32 +0000334 * This function should not be called if libsepol is not linked statically
335 * to this executable and LINK_SEPOL_STATIC is not defined.
William Robertsf0e0a942012-08-27 15:41:15 -0700336 */
Alan Stokes3c4375a2020-11-11 14:45:32 +0000337static type_datum_t *find_type(sepol_policydb_t *db, char *type, uint32_t flavor) {
William Robertsf0e0a942012-08-27 15:41:15 -0700338
Alan Stokes3c4375a2020-11-11 14:45:32 +0000339 policydb_t *d = &db->p;
340 hashtab_datum_t dat = hashtab_search(d->p_types.table, type);
341 if (!dat) {
342 return NULL;
343 }
344 type_datum_t *type_dat = (type_datum_t *) dat;
345 if (type_dat->flavor != flavor) {
346 return NULL;
347 }
348 return type_dat;
349}
350
351static bool type_has_attribute(sepol_policydb_t *db, type_datum_t *type_dat,
352 type_datum_t *attrib_dat) {
353 policydb_t *d = &db->p;
354 ebitmap_t *attr_bits = &d->type_attr_map[type_dat->s.value - 1];
355 return ebitmap_get_bit(attr_bits, attrib_dat->s.value - 1) != 0;
William Robertsf0e0a942012-08-27 15:41:15 -0700356}
357
William Roberts81e1f902015-06-03 21:57:47 -0700358static bool match_regex(key_map *assert, const key_map *check) {
359
360 char *tomatch = check->data;
361
Janis Danisevskis750d7972016-04-01 13:31:57 +0100362 int ret = pcre2_match(assert->regex.compiled, (PCRE2_SPTR) tomatch,
363 PCRE2_ZERO_TERMINATED, 0, 0,
364 assert->regex.match_data, NULL);
William Roberts81e1f902015-06-03 21:57:47 -0700365
Janis Danisevskis750d7972016-04-01 13:31:57 +0100366 /* ret > 0 from pcre2_match means matched */
367 return ret > 0;
William Roberts81e1f902015-06-03 21:57:47 -0700368}
369
Janis Danisevskis750d7972016-04-01 13:31:57 +0100370static bool compile_regex(key_map *km, int *errcode, PCRE2_SIZE *erroff) {
William Roberts81e1f902015-06-03 21:57:47 -0700371
372 size_t size;
373 char *anchored;
374
375 /*
376 * Explicitly anchor all regex's
377 * The size is the length of the string to anchor (km->data), the anchor
378 * characters ^ and $ and the null byte. Hence strlen(km->data) + 3
379 */
380 size = strlen(km->data) + 3;
381 anchored = alloca(size);
382 sprintf(anchored, "^%s$", km->data);
383
Janis Danisevskis750d7972016-04-01 13:31:57 +0100384 km->regex.compiled = pcre2_compile((PCRE2_SPTR) anchored,
385 PCRE2_ZERO_TERMINATED,
386 PCRE2_DOTALL,
387 errcode, erroff,
388 NULL);
William Roberts81e1f902015-06-03 21:57:47 -0700389 if (!km->regex.compiled) {
390 return false;
391 }
392
Janis Danisevskis750d7972016-04-01 13:31:57 +0100393 km->regex.match_data = pcre2_match_data_create_from_pattern(
394 km->regex.compiled, NULL);
395 if (!km->regex.match_data) {
396 pcre2_code_free(km->regex.compiled);
397 return false;
398 }
William Roberts81e1f902015-06-03 21:57:47 -0700399 return true;
400}
401
Inseob Kimd7d36092023-06-26 20:48:48 +0900402static bool validate_bool(
403 char *value,
404 __attribute__ ((unused)) const char *filename,
405 __attribute__ ((unused)) int lineno,
406 char **errmsg) {
William Roberts696a66b2016-01-29 10:41:50 -0800407 if (!strcmp("true", value) || !strcmp("false", value)) {
408 return true;
409 }
410
411 *errmsg = "Expecting \"true\" or \"false\"";
412 return false;
413}
414
Inseob Kimd7d36092023-06-26 20:48:48 +0900415static bool validate_levelFrom(
416 char *value,
417 __attribute__ ((unused)) const char *filename,
418 __attribute__ ((unused)) int lineno,
419 char **errmsg) {
Alan Stokes3c4375a2020-11-11 14:45:32 +0000420 if (strcasecmp(value, "none") && strcasecmp(value, "all") &&
William Roberts696a66b2016-01-29 10:41:50 -0800421 strcasecmp(value, "app") && strcasecmp(value, "user")) {
422 *errmsg = "Expecting one of: \"none\", \"all\", \"app\" or \"user\"";
423 return false;
424 }
425 return true;
426}
427
Inseob Kimd7d36092023-06-26 20:48:48 +0900428static bool validate_domain(char *value, const char *filename, int lineno, char **errmsg) {
William Roberts696a66b2016-01-29 10:41:50 -0800429
Alan Stokes3c4375a2020-11-11 14:45:32 +0000430#if defined(LINK_SEPOL_STATIC)
William Roberts696a66b2016-01-29 10:41:50 -0800431 /*
432 * No policy file present means we cannot check
433 * SE Linux types
434 */
435 if (!pol.policy_file) {
436 return true;
437 }
438
Inseob Kimd7d36092023-06-26 20:48:48 +0900439 type_datum_t *type_dat = find_type(pol.db, value, TYPE_TYPE);
440 if (!type_dat) {
William Roberts696a66b2016-01-29 10:41:50 -0800441 *errmsg = "Expecting a valid SELinux type";
442 return false;
443 }
Inseob Kimd7d36092023-06-26 20:48:48 +0900444
445 if (pol.vendor) {
446 type_datum_t *attrib_dat = find_type(pol.db, COREDOMAIN, TYPE_ATTRIB);
447 if (!attrib_dat) {
448 *errmsg = "The attribute " COREDOMAIN " is not defined in the policy";
449 return false;
450 }
451
Inseob Kim8bc8b752023-09-18 04:08:19 +0000452 if (type_has_attribute(pol.db, type_dat, attrib_dat)) {
Inseob Kimd7d36092023-06-26 20:48:48 +0900453 coredomain_violation_entry *entry = (coredomain_violation_entry *)malloc(sizeof(*entry));
454 entry->domain = strdup(value);
455 entry->filename = strdup(filename);
456 entry->lineno = lineno;
457 list_append(&coredomain_violation_list, &entry->listify);
458 }
459 }
Alan Stokes3c4375a2020-11-11 14:45:32 +0000460#endif
461
462 return true;
463}
464
Inseob Kimd7d36092023-06-26 20:48:48 +0900465static bool validate_type(
466 char *value,
467 __attribute__ ((unused)) const char *filename,
468 __attribute__ ((unused)) int lineno,
469 char **errmsg) {
Alan Stokes3c4375a2020-11-11 14:45:32 +0000470#if defined(LINK_SEPOL_STATIC)
471 /*
472 * No policy file present means we cannot check
473 * SE Linux types
474 */
475 if (!pol.policy_file) {
476 return true;
477 }
478
479 type_datum_t *type_dat = find_type(pol.db, value, TYPE_TYPE);
480 if (!type_dat) {
481 *errmsg = "Expecting a valid SELinux type";
482 return false;
483 }
484
485 type_datum_t *attrib_dat = find_type(pol.db, APP_DATA_REQUIRED_ATTRIB,
486 TYPE_ATTRIB);
487 if (!attrib_dat) {
488 /* If the policy doesn't contain the attribute, we can't check it */
489 return true;
490 }
491
492 if (!type_has_attribute(pol.db, type_dat, attrib_dat)) {
493 *errmsg = "Missing required attribute " APP_DATA_REQUIRED_ATTRIB;
494 return false;
495 }
496
497#endif
William Roberts696a66b2016-01-29 10:41:50 -0800498
499 return true;
500}
501
Inseob Kimd7d36092023-06-26 20:48:48 +0900502static bool validate_selinux_level(
503 char *value,
504 __attribute__ ((unused)) const char *filename,
505 __attribute__ ((unused)) int lineno,
506 char **errmsg) {
William Roberts696a66b2016-01-29 10:41:50 -0800507 /*
508 * No policy file present means we cannot check
509 * SE Linux MLS
510 */
511 if (!pol.policy_file) {
512 return true;
513 }
514
515 int ret = sepol_mls_check(pol.handle, pol.db, value);
516 if (ret < 0) {
517 *errmsg = "Expecting a valid SELinux MLS value";
518 return false;
519 }
520
521 return true;
522}
523
Inseob Kimd7d36092023-06-26 20:48:48 +0900524static bool validate_uint(
525 char *value,
526 __attribute__ ((unused)) const char *filename,
527 __attribute__ ((unused)) int lineno,
528 char **errmsg) {
Michael Peckf54b3622017-02-14 09:48:57 -0800529 char *endptr;
530 long longvalue;
531 longvalue = strtol(value, &endptr, 10);
532 if (('\0' != *endptr) || (longvalue < 0) || (longvalue > INT32_MAX)) {
533 *errmsg = "Expecting a valid unsigned integer";
534 return false;
535 }
536
537 return true;
538}
539
William Robertsf0e0a942012-08-27 15:41:15 -0700540/**
541 * Validates a key_map against a set of enforcement rules, this
542 * function exits the application on a type that cannot be properly
543 * checked
544 *
545 * @param m
546 * The key map to check
547 * @param lineno
548 * The line number in the source file for the corresponding key map
William Robertsfff29802012-11-27 14:20:34 -0800549 * @return
William Roberts81e1f902015-06-03 21:57:47 -0700550 * true if valid, false if invalid
William Robertsf0e0a942012-08-27 15:41:15 -0700551 */
William Roberts81e1f902015-06-03 21:57:47 -0700552static bool key_map_validate(key_map *m, const char *filename, int lineno,
553 bool is_neverallow) {
William Robertsf0e0a942012-08-27 15:41:15 -0700554
Janis Danisevskis750d7972016-04-01 13:31:57 +0100555 PCRE2_SIZE erroff;
556 int errcode;
William Roberts81e1f902015-06-03 21:57:47 -0700557 bool rc = true;
William Robertsf0e0a942012-08-27 15:41:15 -0700558 char *key = m->name;
559 char *value = m->data;
William Roberts696a66b2016-01-29 10:41:50 -0800560 char *errmsg = NULL;
Janis Danisevskis750d7972016-04-01 13:31:57 +0100561 char errstr[256];
William Robertsf0e0a942012-08-27 15:41:15 -0700562
William Roberts0ae3a8a2012-09-04 11:51:04 -0700563 log_info("Validating %s=%s\n", key, value);
564
William Roberts81e1f902015-06-03 21:57:47 -0700565 /*
Joel Galensonb0d74a12020-07-27 09:30:34 -0700566 * Neverallows are completely skipped from validity checking so you can match
William Roberts81e1f902015-06-03 21:57:47 -0700567 * un-unspecified inputs.
568 */
569 if (is_neverallow) {
570 if (!m->regex.compiled) {
Janis Danisevskis750d7972016-04-01 13:31:57 +0100571 rc = compile_regex(m, &errcode, &erroff);
William Roberts81e1f902015-06-03 21:57:47 -0700572 if (!rc) {
Janis Danisevskis750d7972016-04-01 13:31:57 +0100573 pcre2_get_error_message(errcode,
574 (PCRE2_UCHAR*) errstr,
575 sizeof(errstr));
576 log_error("Invalid regex on line %d : %s PCRE error: %s at offset %lu",
577 lineno, value, errstr, erroff);
William Roberts81e1f902015-06-03 21:57:47 -0700578 }
579 }
580 goto out;
581 }
582
William Roberts696a66b2016-01-29 10:41:50 -0800583 /* If the key has a validation routine, call it */
584 if (m->fn_validate) {
Inseob Kimd7d36092023-06-26 20:48:48 +0900585 rc = m->fn_validate(value, filename, lineno, &errmsg);
William Robertsf0e0a942012-08-27 15:41:15 -0700586
William Roberts696a66b2016-01-29 10:41:50 -0800587 if (!rc) {
588 log_error("Could not validate key \"%s\" for value \"%s\" on line: %d in file: \"%s\": %s\n", key, value,
589 lineno, filename, errmsg);
William Robertsf0e0a942012-08-27 15:41:15 -0700590 }
591 }
592
William Roberts0ae3a8a2012-09-04 11:51:04 -0700593out:
594 log_info("Key map validate returning: %d\n", rc);
595 return rc;
William Robertsf0e0a942012-08-27 15:41:15 -0700596}
597
598/**
599 * Prints a rule map back to a file
600 * @param fp
601 * The file handle to print too
602 * @param r
603 * The rule map to print
604 */
605static void rule_map_print(FILE *fp, rule_map *r) {
606
William Roberts610a4b12013-10-15 18:26:00 -0700607 size_t i;
William Robertsf0e0a942012-08-27 15:41:15 -0700608 key_map *m;
609
610 for (i = 0; i < r->length; i++) {
611 m = &(r->m[i]);
612 if (i < r->length - 1)
613 fprintf(fp, "%s=%s ", m->name, m->data);
614 else
615 fprintf(fp, "%s=%s", m->name, m->data);
616 }
617}
618
619/**
620 * Compare two rule maps for equality
621 * @param rmA
622 * a rule map to check
623 * @param rmB
624 * a rule map to check
625 * @return
William Robertsae23a1f2012-09-05 12:53:52 -0700626 * a map_match enum indicating the result
William Robertsf0e0a942012-08-27 15:41:15 -0700627 */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700628static map_match rule_map_cmp(rule_map *rmA, rule_map *rmB) {
William Robertsf0e0a942012-08-27 15:41:15 -0700629
William Roberts610a4b12013-10-15 18:26:00 -0700630 size_t i;
631 size_t j;
William Robertsf0e0a942012-08-27 15:41:15 -0700632 int inputs_found = 0;
633 int num_of_matched_inputs = 0;
634 int input_mode = 0;
William Roberts610a4b12013-10-15 18:26:00 -0700635 size_t matches = 0;
William Robertsf0e0a942012-08-27 15:41:15 -0700636 key_map *mA;
637 key_map *mB;
638
William Robertsf0e0a942012-08-27 15:41:15 -0700639 for (i = 0; i < rmA->length; i++) {
640 mA = &(rmA->m[i]);
641
642 for (j = 0; j < rmB->length; j++) {
643 mB = &(rmB->m[j]);
644 input_mode = 0;
645
William Robertsf0e0a942012-08-27 15:41:15 -0700646 if (strcmp(mA->name, mB->name))
647 continue;
648
649 if (strcmp(mA->data, mB->data))
650 continue;
651
652 if (mB->dir != mA->dir)
653 continue;
654 else if (mB->dir == dir_in) {
655 input_mode = 1;
656 inputs_found++;
657 }
658
William Roberts0ae3a8a2012-09-04 11:51:04 -0700659 if (input_mode) {
William Roberts1e8c0612013-04-19 19:06:02 -0700660 log_info("Matched input lines: name=%s data=%s\n", mA->name, mA->data);
William Robertsf0e0a942012-08-27 15:41:15 -0700661 num_of_matched_inputs++;
William Roberts0ae3a8a2012-09-04 11:51:04 -0700662 }
William Robertsf0e0a942012-08-27 15:41:15 -0700663
664 /* Match found, move on */
William Roberts1e8c0612013-04-19 19:06:02 -0700665 log_info("Matched lines: name=%s data=%s", mA->name, mA->data);
William Robertsf0e0a942012-08-27 15:41:15 -0700666 matches++;
667 break;
668 }
669 }
670
671 /* If they all matched*/
William Roberts0ae3a8a2012-09-04 11:51:04 -0700672 if (matches == rmA->length) {
673 log_info("Rule map cmp MATCH\n");
674 return map_matched;
675 }
William Robertsf0e0a942012-08-27 15:41:15 -0700676
677 /* They didn't all match but the input's did */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700678 else if (num_of_matched_inputs == inputs_found) {
679 log_info("Rule map cmp INPUT MATCH\n");
680 return map_input_matched;
681 }
William Robertsf0e0a942012-08-27 15:41:15 -0700682
683 /* They didn't all match, and the inputs didn't match, ie it didn't
684 * match */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700685 else {
686 log_info("Rule map cmp NO MATCH\n");
687 return map_no_matches;
688 }
William Robertsf0e0a942012-08-27 15:41:15 -0700689}
690
691/**
692 * Frees a rule map
693 * @param rm
694 * rule map to be freed.
William Roberts7d65b542015-06-19 09:43:28 -0700695 * @is_in_htable
696 * True if the rule map has been added to the hash table, false
697 * otherwise.
698 */
699static void rule_map_free(rule_map *rm, bool is_in_htable) {
William Robertsf0e0a942012-08-27 15:41:15 -0700700
William Roberts610a4b12013-10-15 18:26:00 -0700701 size_t i;
702 size_t len = rm->length;
William Robertsf0e0a942012-08-27 15:41:15 -0700703 for (i = 0; i < len; i++) {
704 key_map *m = &(rm->m[i]);
705 free(m->data);
William Roberts81e1f902015-06-03 21:57:47 -0700706
707 if (m->regex.compiled) {
Janis Danisevskis750d7972016-04-01 13:31:57 +0100708 pcre2_code_free(m->regex.compiled);
William Roberts81e1f902015-06-03 21:57:47 -0700709 }
710
Janis Danisevskis750d7972016-04-01 13:31:57 +0100711 if (m->regex.match_data) {
712 pcre2_match_data_free(m->regex.match_data);
William Roberts81e1f902015-06-03 21:57:47 -0700713 }
William Robertsf0e0a942012-08-27 15:41:15 -0700714 }
715
William Roberts7d65b542015-06-19 09:43:28 -0700716 /*
717 * hdestroy() frees comparsion keys for non glibc
718 * on GLIBC we always free on NON-GLIBC we free if
719 * it is not in the htable.
720 */
721 if (rm->key) {
rpcraig5dbfdc02012-10-23 11:03:47 -0400722#ifdef __GLIBC__
William Roberts7d65b542015-06-19 09:43:28 -0700723 /* silence unused warning */
724 (void)is_in_htable;
William Robertsf0e0a942012-08-27 15:41:15 -0700725 free(rm->key);
William Roberts7d65b542015-06-19 09:43:28 -0700726#else
727 if (!is_in_htable) {
728 free(rm->key);
729 }
rpcraig5dbfdc02012-10-23 11:03:47 -0400730#endif
William Roberts7d65b542015-06-19 09:43:28 -0700731 }
William Robertsf0e0a942012-08-27 15:41:15 -0700732
William Roberts81e1f902015-06-03 21:57:47 -0700733 free(rm->filename);
William Robertsf0e0a942012-08-27 15:41:15 -0700734 free(rm);
735}
736
737static void free_kvp(kvp *k) {
738 free(k->key);
739 free(k->value);
740}
741
742/**
William Roberts61846292013-10-15 09:38:24 -0700743 * Checks a rule_map for any variation of KVP's that shouldn't be allowed.
William Roberts81e1f902015-06-03 21:57:47 -0700744 * It builds an assertion failure list for each rule map.
William Roberts61846292013-10-15 09:38:24 -0700745 * Note that this function logs all errors.
746 *
747 * Current Checks:
748 * 1. That a specified name entry should have a specified seinfo entry as well.
William Roberts81e1f902015-06-03 21:57:47 -0700749 * 2. That no rule violates a neverallow
William Roberts61846292013-10-15 09:38:24 -0700750 * @param rm
751 * The rule map to check for validity.
William Roberts61846292013-10-15 09:38:24 -0700752 */
William Roberts81e1f902015-06-03 21:57:47 -0700753static void rule_map_validate(rule_map *rm) {
William Roberts61846292013-10-15 09:38:24 -0700754
William Roberts81e1f902015-06-03 21:57:47 -0700755 size_t i, j;
756 const key_map *rule;
757 key_map *nrule;
758 hash_entry *e;
759 rule_map *assert;
760 list_element *cursor;
William Roberts61846292013-10-15 09:38:24 -0700761
William Roberts81e1f902015-06-03 21:57:47 -0700762 list_for_each(&nallow_list, cursor) {
763 e = list_entry(cursor, typeof(*e), listify);
764 assert = e->r;
William Roberts61846292013-10-15 09:38:24 -0700765
William Roberts81e1f902015-06-03 21:57:47 -0700766 size_t cnt = 0;
767
768 for (j = 0; j < assert->length; j++) {
769 nrule = &(assert->m[j]);
770
771 // mark that nrule->name is for a null check
772 bool is_null_check = !strcmp(nrule->data, "\"\"");
773
774 for (i = 0; i < rm->length; i++) {
775 rule = &(rm->m[i]);
776
777 if (!strcmp(rule->name, nrule->name)) {
778
779 /* the name was found, (data cannot be false) then it was specified */
780 is_null_check = false;
781
782 if (match_regex(nrule, rule)) {
783 cnt++;
784 }
785 }
786 }
787
788 /*
789 * the nrule was marked in a null check and we never found a match on nrule, thus
790 * it matched and we update the cnt
791 */
792 if (is_null_check) {
793 cnt++;
794 }
William Roberts61846292013-10-15 09:38:24 -0700795 }
William Roberts81e1f902015-06-03 21:57:47 -0700796 if (cnt == assert->length) {
797 list_append(&rm->violations, &assert->listify);
William Roberts61846292013-10-15 09:38:24 -0700798 }
799 }
William Roberts61846292013-10-15 09:38:24 -0700800}
801
802/**
William Robertsf0e0a942012-08-27 15:41:15 -0700803 * Given a set of key value pairs, this will construct a new rule map.
804 * On error this function calls exit.
805 * @param keys
806 * Keys from a rule line to map
807 * @param num_of_keys
808 * The length of the keys array
809 * @param lineno
810 * The line number the keys were extracted from
811 * @return
812 * A rule map pointer.
813 */
William Roberts81e1f902015-06-03 21:57:47 -0700814static rule_map *rule_map_new(kvp keys[], size_t num_of_keys, int lineno,
815 const char *filename, bool is_never_allow) {
William Robertsf0e0a942012-08-27 15:41:15 -0700816
William Roberts610a4b12013-10-15 18:26:00 -0700817 size_t i = 0, j = 0;
William Robertsf0e0a942012-08-27 15:41:15 -0700818 rule_map *new_map = NULL;
819 kvp *k = NULL;
820 key_map *r = NULL, *x = NULL;
Stephen Smalley534fb072015-02-13 14:06:08 -0500821 bool seen[KVP_NUM_OF_RULES];
822
823 for (i = 0; i < KVP_NUM_OF_RULES; i++)
824 seen[i] = false;
William Robertsf0e0a942012-08-27 15:41:15 -0700825
826 new_map = calloc(1, (num_of_keys * sizeof(key_map)) + sizeof(rule_map));
827 if (!new_map)
828 goto oom;
829
William Roberts81e1f902015-06-03 21:57:47 -0700830 new_map->is_never_allow = is_never_allow;
William Robertsf0e0a942012-08-27 15:41:15 -0700831 new_map->length = num_of_keys;
832 new_map->lineno = lineno;
William Roberts81e1f902015-06-03 21:57:47 -0700833 new_map->filename = strdup(filename);
834 if (!new_map->filename) {
835 goto oom;
836 }
William Robertsf0e0a942012-08-27 15:41:15 -0700837
838 /* For all the keys in a rule line*/
839 for (i = 0; i < num_of_keys; i++) {
840 k = &(keys[i]);
841 r = &(new_map->m[i]);
842
843 for (j = 0; j < KVP_NUM_OF_RULES; j++) {
844 x = &(rules[j]);
845
846 /* Only assign key name to map name */
847 if (strcasecmp(k->key, x->name)) {
liwugang57d66ef2018-11-14 16:39:39 +0800848 if (j == KVP_NUM_OF_RULES - 1) {
William Robertsf0e0a942012-08-27 15:41:15 -0700849 log_error("No match for key: %s\n", k->key);
850 goto err;
851 }
852 continue;
853 }
854
Stephen Smalley534fb072015-02-13 14:06:08 -0500855 if (seen[j]) {
856 log_error("Duplicated key: %s\n", k->key);
857 goto err;
858 }
859 seen[j] = true;
860
William Robertsf0e0a942012-08-27 15:41:15 -0700861 memcpy(r, x, sizeof(key_map));
862
863 /* Assign rule map value to one from file */
864 r->data = strdup(k->value);
865 if (!r->data)
866 goto oom;
867
868 /* Enforce type check*/
William Roberts0ae3a8a2012-09-04 11:51:04 -0700869 log_info("Validating keys!\n");
William Roberts81e1f902015-06-03 21:57:47 -0700870 if (!key_map_validate(r, filename, lineno, new_map->is_never_allow)) {
William Robertsf0e0a942012-08-27 15:41:15 -0700871 log_error("Could not validate\n");
872 goto err;
873 }
874
William Roberts81e1f902015-06-03 21:57:47 -0700875 /*
876 * Only build key off of inputs with the exception of neverallows.
877 * Neverallows are keyed off of all key value pairs,
878 */
879 if (r->dir == dir_in || new_map->is_never_allow) {
William Robertsf0e0a942012-08-27 15:41:15 -0700880 char *tmp;
William Robertsb3ab56c2012-09-17 14:35:02 -0700881 int key_len = strlen(k->key);
882 int val_len = strlen(k->value);
883 int l = (new_map->key) ? strlen(new_map->key) : 0;
884 l = l + key_len + val_len;
William Robertsf0e0a942012-08-27 15:41:15 -0700885 l += 1;
886
887 tmp = realloc(new_map->key, l);
888 if (!tmp)
889 goto oom;
890
William Robertsb3ab56c2012-09-17 14:35:02 -0700891 if (!new_map->key)
892 memset(tmp, 0, l);
893
William Robertsf0e0a942012-08-27 15:41:15 -0700894 new_map->key = tmp;
895
William Robertsb3ab56c2012-09-17 14:35:02 -0700896 strncat(new_map->key, k->key, key_len);
897 strncat(new_map->key, k->value, val_len);
William Robertsf0e0a942012-08-27 15:41:15 -0700898 }
899 break;
900 }
901 free_kvp(k);
902 }
903
904 if (new_map->key == NULL) {
905 log_error("Strange, no keys found, input file corrupt perhaps?\n");
906 goto err;
907 }
908
909 return new_map;
910
911oom:
912 log_error("Out of memory!\n");
913err:
Alan Stokes3c4375a2020-11-11 14:45:32 +0000914 if (new_map) {
William Roberts7d65b542015-06-19 09:43:28 -0700915 rule_map_free(new_map, false);
William Robertsf0e0a942012-08-27 15:41:15 -0700916 for (; i < num_of_keys; i++) {
917 k = &(keys[i]);
918 free_kvp(k);
919 }
920 }
Stephen Smalley534fb072015-02-13 14:06:08 -0500921 return NULL;
William Robertsf0e0a942012-08-27 15:41:15 -0700922}
923
924/**
925 * Print the usage of the program
926 */
927static void usage() {
928 printf(
929 "checkseapp [options] <input file>\n"
930 "Processes an seapp_contexts file specified by argument <input file> (default stdin) "
William Robertsae23a1f2012-09-05 12:53:52 -0700931 "and allows later declarations to override previous ones on a match.\n"
William Robertsf0e0a942012-08-27 15:41:15 -0700932 "Options:\n"
933 "-h - print this help message\n"
934 "-v - enable verbose debugging informations\n"
William Roberts63297212013-04-19 19:06:23 -0700935 "-p policy file - specify policy file for strict checking of output selectors against the policy\n"
William Roberts81e1f902015-06-03 21:57:47 -0700936 "-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 -0700937}
938
939static void init() {
940
William Roberts81e1f902015-06-03 21:57:47 -0700941 bool has_out_file;
942 list_element *cursor;
943 file_info *tmp;
944
945 /* input files if the list is empty, use stdin */
946 if (!input_file_list.head) {
947 log_info("Using stdin for input\n");
948 tmp = malloc(sizeof(*tmp));
949 if (!tmp) {
950 log_error("oom");
William Robertsf0e0a942012-08-27 15:41:15 -0700951 exit(EXIT_FAILURE);
952 }
William Roberts81e1f902015-06-03 21:57:47 -0700953 tmp->name = "stdin";
954 tmp->file = stdin;
955 list_append(&input_file_list, &(tmp->listify));
956 }
957 else {
958 list_for_each(&input_file_list, cursor) {
959 tmp = list_entry(cursor, typeof(*tmp), listify);
960
961 log_info("Opening input file: \"%s\"\n", tmp->name);
962 tmp->file = fopen(tmp->name, "r");
963 if (!tmp->file) {
964 log_error("Could not open file: %s error: %s\n", tmp->name,
965 strerror(errno));
966 exit(EXIT_FAILURE);
967 }
968 }
William Robertsf0e0a942012-08-27 15:41:15 -0700969 }
970
William Roberts81e1f902015-06-03 21:57:47 -0700971 has_out_file = out_file.name != NULL;
972
973 /* If output file is -, then use stdout, else open the path */
974 if (has_out_file && !strcmp(out_file.name, "-")) {
975 out_file.file = stdout;
976 out_file.name = "stdout";
977 }
978 else if (has_out_file) {
979 out_file.file = fopen(out_file.name, "w+");
980 }
981
982 if (has_out_file && !out_file.file) {
983 log_error("Could not open file: \"%s\" error: \"%s\"\n", out_file.name,
984 strerror(errno));
985 exit(EXIT_FAILURE);
William Robertsf0e0a942012-08-27 15:41:15 -0700986 }
987
988 if (pol.policy_file_name) {
William Robertsf0e0a942012-08-27 15:41:15 -0700989 log_info("Opening policy file: %s\n", pol.policy_file_name);
990 pol.policy_file = fopen(pol.policy_file_name, "rb");
991 if (!pol.policy_file) {
992 log_error("Could not open file: %s error: %s\n",
993 pol.policy_file_name, strerror(errno));
994 exit(EXIT_FAILURE);
995 }
996
997 pol.handle = sepol_handle_create();
998 if (!pol.handle) {
999 log_error("Could not create sepolicy handle: %s\n",
1000 strerror(errno));
1001 exit(EXIT_FAILURE);
1002 }
1003
1004 if (sepol_policy_file_create(&pol.pf) < 0) {
1005 log_error("Could not create sepolicy file: %s!\n",
1006 strerror(errno));
1007 exit(EXIT_FAILURE);
1008 }
1009
1010 sepol_policy_file_set_fp(pol.pf, pol.policy_file);
1011 sepol_policy_file_set_handle(pol.pf, pol.handle);
1012
1013 if (sepol_policydb_create(&pol.db) < 0) {
1014 log_error("Could not create sepolicy db: %s!\n",
1015 strerror(errno));
1016 exit(EXIT_FAILURE);
1017 }
1018
1019 if (sepol_policydb_read(pol.db, pol.pf) < 0) {
William Robertsf7d6bb32016-08-08 10:31:54 -07001020 log_error("Could not load policy file to db: invalid input file!\n");
William Robertsf0e0a942012-08-27 15:41:15 -07001021 exit(EXIT_FAILURE);
1022 }
1023 }
1024
William Roberts81e1f902015-06-03 21:57:47 -07001025 list_for_each(&input_file_list, cursor) {
1026 tmp = list_entry(cursor, typeof(*tmp), listify);
1027 log_info("Input file set to: \"%s\"\n", tmp->name);
1028 }
1029
1030 log_info("Policy file set to: \"%s\"\n",
1031 (pol.policy_file_name == NULL) ? "None" : pol.policy_file_name);
1032 log_info("Output file set to: \"%s\"\n", out_file.name);
William Robertsf0e0a942012-08-27 15:41:15 -07001033
William Roberts0ae3a8a2012-09-04 11:51:04 -07001034#if !defined(LINK_SEPOL_STATIC)
William Robertsa53ccf32012-09-17 12:53:44 -07001035 log_warn("LINK_SEPOL_STATIC is not defined\n""Not checking types!");
William Roberts0ae3a8a2012-09-04 11:51:04 -07001036#endif
1037
William Robertsf0e0a942012-08-27 15:41:15 -07001038}
1039
1040/**
1041 * Handle parsing and setting the global flags for the command line
1042 * options. This function calls exit on failure.
1043 * @param argc
1044 * argument count
1045 * @param argv
1046 * argument list
1047 */
1048static void handle_options(int argc, char *argv[]) {
1049
1050 int c;
William Roberts81e1f902015-06-03 21:57:47 -07001051 file_info *input_file;
William Robertsf0e0a942012-08-27 15:41:15 -07001052
Inseob Kimd7d36092023-06-26 20:48:48 +09001053 while ((c = getopt(argc, argv, "ho:p:vc")) != -1) {
William Robertsf0e0a942012-08-27 15:41:15 -07001054 switch (c) {
1055 case 'h':
1056 usage();
1057 exit(EXIT_SUCCESS);
1058 case 'o':
William Roberts81e1f902015-06-03 21:57:47 -07001059 out_file.name = optarg;
William Robertsf0e0a942012-08-27 15:41:15 -07001060 break;
1061 case 'p':
1062 pol.policy_file_name = optarg;
1063 break;
1064 case 'v':
1065 log_set_verbose();
1066 break;
Inseob Kimd7d36092023-06-26 20:48:48 +09001067 case 'c':
1068 pol.vendor = true;
1069 break;
William Robertsf0e0a942012-08-27 15:41:15 -07001070 case '?':
1071 if (optopt == 'o' || optopt == 'p')
1072 log_error("Option -%c requires an argument.\n", optopt);
1073 else if (isprint (optopt))
1074 log_error("Unknown option `-%c'.\n", optopt);
1075 else {
1076 log_error(
1077 "Unknown option character `\\x%x'.\n",
1078 optopt);
William Robertsf0e0a942012-08-27 15:41:15 -07001079 }
William Robertsf0e0a942012-08-27 15:41:15 -07001080 default:
1081 exit(EXIT_FAILURE);
1082 }
1083 }
1084
William Roberts81e1f902015-06-03 21:57:47 -07001085 for (c = optind; c < argc; c++) {
William Robertsf0e0a942012-08-27 15:41:15 -07001086
William Roberts81e1f902015-06-03 21:57:47 -07001087 input_file = calloc(1, sizeof(*input_file));
1088 if (!input_file) {
1089 log_error("oom");
1090 exit(EXIT_FAILURE);
1091 }
1092 input_file->name = argv[c];
1093 list_append(&input_file_list, &input_file->listify);
William Robertsf0e0a942012-08-27 15:41:15 -07001094 }
1095}
1096
1097/**
1098 * Adds a rule to the hash table and to the ordered list if needed.
1099 * @param rm
1100 * The rule map to add.
1101 */
1102static void rule_add(rule_map *rm) {
1103
William Roberts0ae3a8a2012-09-04 11:51:04 -07001104 map_match cmp;
William Robertsf0e0a942012-08-27 15:41:15 -07001105 ENTRY e;
1106 ENTRY *f;
1107 hash_entry *entry;
1108 hash_entry *tmp;
William Roberts81e1f902015-06-03 21:57:47 -07001109 list *list_to_addto;
William Robertsf0e0a942012-08-27 15:41:15 -07001110
1111 e.key = rm->key;
Rahul Chaudhrye1682c72016-10-12 12:22:39 -07001112 e.data = NULL;
William Robertsf0e0a942012-08-27 15:41:15 -07001113
William Roberts0ae3a8a2012-09-04 11:51:04 -07001114 log_info("Searching for key: %s\n", e.key);
William Robertsf0e0a942012-08-27 15:41:15 -07001115 /* Check to see if it has already been added*/
1116 f = hsearch(e, FIND);
1117
1118 /*
1119 * Since your only hashing on a partial key, the inputs we need to handle
1120 * when you want to override the outputs for a given input set, as well as
1121 * checking for duplicate entries.
1122 */
Alan Stokes3c4375a2020-11-11 14:45:32 +00001123 if (f) {
William Roberts0ae3a8a2012-09-04 11:51:04 -07001124 log_info("Existing entry found!\n");
William Robertsf0e0a942012-08-27 15:41:15 -07001125 tmp = (hash_entry *)f->data;
1126 cmp = rule_map_cmp(rm, tmp->r);
Stephen Smalley0b820042015-02-13 14:58:31 -05001127 log_error("Duplicate line detected in file: %s\n"
1128 "Lines %d and %d %s!\n",
William Roberts81e1f902015-06-03 21:57:47 -07001129 rm->filename, tmp->r->lineno, rm->lineno,
Stephen Smalley0b820042015-02-13 14:58:31 -05001130 map_match_str[cmp]);
William Roberts7d65b542015-06-19 09:43:28 -07001131 rule_map_free(rm, false);
Stephen Smalley0b820042015-02-13 14:58:31 -05001132 goto err;
William Robertsf0e0a942012-08-27 15:41:15 -07001133 }
1134 /* It wasn't found, just add the rule map to the table */
1135 else {
1136
1137 entry = malloc(sizeof(hash_entry));
1138 if (!entry)
1139 goto oom;
1140
1141 entry->r = rm;
1142 e.data = entry;
1143
1144 f = hsearch(e, ENTER);
Alan Stokes3c4375a2020-11-11 14:45:32 +00001145 if (f == NULL) {
William Robertsf0e0a942012-08-27 15:41:15 -07001146 goto oom;
1147 }
1148
1149 /* new entries must be added to the ordered list */
1150 entry->r = rm;
William Roberts81e1f902015-06-03 21:57:47 -07001151 list_to_addto = rm->is_never_allow ? &nallow_list : &line_order_list;
1152 list_append(list_to_addto, &entry->listify);
William Robertsf0e0a942012-08-27 15:41:15 -07001153 }
1154
1155 return;
1156oom:
1157 if (e.key)
1158 free(e.key);
1159 if (entry)
1160 free(entry);
1161 if (rm)
1162 free(rm);
1163 log_error("Out of memory in function: %s\n", __FUNCTION__);
1164err:
1165 exit(EXIT_FAILURE);
1166}
1167
William Roberts81e1f902015-06-03 21:57:47 -07001168static void parse_file(file_info *in_file) {
William Robertsf0e0a942012-08-27 15:41:15 -07001169
William Roberts81e1f902015-06-03 21:57:47 -07001170 char *p;
William Robertsf0e0a942012-08-27 15:41:15 -07001171 size_t len;
William Roberts81e1f902015-06-03 21:57:47 -07001172 char *token;
1173 char *saveptr;
1174 bool is_never_allow;
1175 bool found_whitespace;
1176
1177 size_t lineno = 0;
1178 char *name = NULL;
1179 char *value = NULL;
William Roberts610a4b12013-10-15 18:26:00 -07001180 size_t token_cnt = 0;
William Robertsf0e0a942012-08-27 15:41:15 -07001181
William Roberts81e1f902015-06-03 21:57:47 -07001182 char line_buf[BUFSIZ];
1183 kvp keys[KVP_NUM_OF_RULES];
William Robertsf0e0a942012-08-27 15:41:15 -07001184
William Roberts81e1f902015-06-03 21:57:47 -07001185 while (fgets(line_buf, sizeof(line_buf) - 1, in_file->file)) {
William Robertsf0e0a942012-08-27 15:41:15 -07001186 lineno++;
William Roberts81e1f902015-06-03 21:57:47 -07001187 is_never_allow = false;
1188 found_whitespace = false;
1189 log_info("Got line %zu\n", lineno);
William Robertsf0e0a942012-08-27 15:41:15 -07001190 len = strlen(line_buf);
1191 if (line_buf[len - 1] == '\n')
Alice Chuf6647eb2012-10-30 16:27:00 -07001192 line_buf[len - 1] = '\0';
William Robertsf0e0a942012-08-27 15:41:15 -07001193 p = line_buf;
William Roberts81e1f902015-06-03 21:57:47 -07001194
1195 /* neverallow lines must start with neverallow (ie ^neverallow) */
1196 if (!strncasecmp(p, "neverallow", strlen("neverallow"))) {
1197 p += strlen("neverallow");
1198 is_never_allow = true;
1199 }
1200
1201 /* strip trailing whitespace skip comments */
1202 while (isspace(*p)) {
William Robertsf0e0a942012-08-27 15:41:15 -07001203 p++;
William Roberts81e1f902015-06-03 21:57:47 -07001204 found_whitespace = true;
1205 }
Alice Chuf6647eb2012-10-30 16:27:00 -07001206 if (*p == '#' || *p == '\0')
William Robertsf0e0a942012-08-27 15:41:15 -07001207 continue;
1208
1209 token = strtok_r(p, " \t", &saveptr);
1210 if (!token)
1211 goto err;
1212
1213 token_cnt = 0;
1214 memset(keys, 0, sizeof(kvp) * KVP_NUM_OF_RULES);
1215 while (1) {
William Roberts0ae3a8a2012-09-04 11:51:04 -07001216
William Robertsf0e0a942012-08-27 15:41:15 -07001217 name = token;
1218 value = strchr(name, '=');
1219 if (!value)
1220 goto err;
1221 *value++ = 0;
1222
1223 keys[token_cnt].key = strdup(name);
1224 if (!keys[token_cnt].key)
1225 goto oom;
1226
1227 keys[token_cnt].value = strdup(value);
1228 if (!keys[token_cnt].value)
1229 goto oom;
1230
1231 token_cnt++;
1232
1233 token = strtok_r(NULL, " \t", &saveptr);
1234 if (!token)
1235 break;
1236
liwugangeb74dd92018-11-15 20:13:15 +08001237 if (token_cnt == KVP_NUM_OF_RULES)
1238 goto oob;
1239
William Robertsf0e0a942012-08-27 15:41:15 -07001240 } /*End token parsing */
1241
William Roberts81e1f902015-06-03 21:57:47 -07001242 rule_map *r = rule_map_new(keys, token_cnt, lineno, in_file->name, is_never_allow);
Stephen Smalley534fb072015-02-13 14:06:08 -05001243 if (!r)
1244 goto err;
William Robertsf0e0a942012-08-27 15:41:15 -07001245 rule_add(r);
1246
1247 } /* End file parsing */
1248 return;
1249
1250err:
William Robertsefebf972016-01-29 10:34:04 -08001251 log_error("Reading file: \"%s\" line: %zu name: \"%s\" value: \"%s\"\n",
William Roberts81e1f902015-06-03 21:57:47 -07001252 in_file->name, lineno, name, value);
Alan Stokes3c4375a2020-11-11 14:45:32 +00001253 if (found_whitespace && name && !strcasecmp(name, "neverallow")) {
William Roberts81e1f902015-06-03 21:57:47 -07001254 log_error("perhaps whitespace before neverallow\n");
1255 }
William Robertsf0e0a942012-08-27 15:41:15 -07001256 exit(EXIT_FAILURE);
1257oom:
1258 log_error("In function %s: Out of memory\n", __FUNCTION__);
1259 exit(EXIT_FAILURE);
liwugangeb74dd92018-11-15 20:13:15 +08001260oob:
1261 log_error("Reading file: \"%s\" line: %zu reason: the size of key pairs exceeds the MAX(%zu)\n",
1262 in_file->name, lineno, KVP_NUM_OF_RULES);
1263 exit(EXIT_FAILURE);
William Robertsf0e0a942012-08-27 15:41:15 -07001264}
1265
1266/**
William Roberts81e1f902015-06-03 21:57:47 -07001267 * Parses the seapp_contexts file and neverallow file
1268 * and adds them to the hash table and ordered list entries
1269 * when it encounters them.
1270 * Calls exit on failure.
1271 */
1272static void parse() {
1273
1274 file_info *current;
1275 list_element *cursor;
1276 list_for_each(&input_file_list, cursor) {
1277 current = list_entry(cursor, typeof(*current), listify);
1278 parse_file(current);
1279 }
1280}
1281
1282static void validate() {
1283
1284 list_element *cursor, *v;
1285 bool found_issues = false;
1286 hash_entry *e;
1287 rule_map *r;
Inseob Kimd7d36092023-06-26 20:48:48 +09001288 coredomain_violation_entry *c;
William Roberts81e1f902015-06-03 21:57:47 -07001289 list_for_each(&line_order_list, cursor) {
1290 e = list_entry(cursor, typeof(*e), listify);
1291 rule_map_validate(e->r);
1292 }
1293
1294 list_for_each(&line_order_list, cursor) {
1295 e = list_entry(cursor, typeof(*e), listify);
1296 r = e->r;
1297 list_for_each(&r->violations, v) {
1298 found_issues = true;
1299 log_error("Rule in File \"%s\" on line %d: \"", e->r->filename, e->r->lineno);
1300 rule_map_print(stderr, e->r);
1301 r = list_entry(v, rule_map, listify);
1302 fprintf(stderr, "\" violates neverallow in File \"%s\" on line %d: \"", r->filename, r->lineno);
1303 rule_map_print(stderr, r);
1304 fprintf(stderr, "\"\n");
1305 }
1306 }
1307
Inseob Kimd7d36092023-06-26 20:48:48 +09001308 list_for_each(&coredomain_violation_list, cursor) {
1309 c = list_entry(cursor, typeof(*c), listify);
1310 fprintf(stderr, "Forbidden attribute " COREDOMAIN " assigned to domain \"%s\" in "
1311 "File \"%s\" on line %d\n", c->domain, c->filename, c->lineno);
1312 }
1313
William Roberts81e1f902015-06-03 21:57:47 -07001314 if (found_issues) {
1315 exit(EXIT_FAILURE);
1316 }
1317}
1318
1319/**
William Robertsf0e0a942012-08-27 15:41:15 -07001320 * Should be called after parsing to cause the printing of the rule_maps
1321 * stored in the ordered list, head first, which preserves the "first encountered"
1322 * ordering.
1323 */
1324static void output() {
1325
William Roberts81e1f902015-06-03 21:57:47 -07001326 hash_entry *e;
1327 list_element *cursor;
William Robertsf0e0a942012-08-27 15:41:15 -07001328
William Roberts81e1f902015-06-03 21:57:47 -07001329 if (!out_file.file) {
1330 log_info("No output file, not outputting.\n");
1331 return;
1332 }
1333
1334 list_for_each(&line_order_list, cursor) {
1335 e = list_entry(cursor, hash_entry, listify);
1336 rule_map_print(out_file.file, e->r);
1337 fprintf(out_file.file, "\n");
William Robertsf0e0a942012-08-27 15:41:15 -07001338 }
1339}
1340
1341/**
1342 * This function is registered to the at exit handler and should clean up
1343 * the programs dynamic resources, such as memory and fd's.
1344 */
1345static void cleanup() {
1346
1347 /* Only close this when it was opened by me and not the crt */
William Roberts81e1f902015-06-03 21:57:47 -07001348 if (out_file.name && strcmp(out_file.name, "stdout") && out_file.file) {
1349 log_info("Closing file: %s\n", out_file.name);
1350 fclose(out_file.file);
William Robertsf0e0a942012-08-27 15:41:15 -07001351 }
1352
1353 if (pol.policy_file) {
1354
1355 log_info("Closing file: %s\n", pol.policy_file_name);
1356 fclose(pol.policy_file);
1357
1358 if (pol.db)
1359 sepol_policydb_free(pol.db);
1360
1361 if (pol.pf)
1362 sepol_policy_file_free(pol.pf);
1363
1364 if (pol.handle)
1365 sepol_handle_destroy(pol.handle);
1366 }
1367
William Roberts81e1f902015-06-03 21:57:47 -07001368 log_info("Freeing lists\n");
1369 list_free(&input_file_list);
1370 list_free(&line_order_list);
1371 list_free(&nallow_list);
Inseob Kimd7d36092023-06-26 20:48:48 +09001372 list_free(&coredomain_violation_list);
William Robertsf0e0a942012-08-27 15:41:15 -07001373 hdestroy();
1374}
1375
1376int main(int argc, char *argv[]) {
1377 if (!hcreate(TABLE_SIZE)) {
1378 log_error("Could not create hash table: %s\n", strerror(errno));
1379 exit(EXIT_FAILURE);
1380 }
1381 atexit(cleanup);
1382 handle_options(argc, argv);
1383 init();
1384 log_info("Starting to parse\n");
1385 parse();
1386 log_info("Parsing completed, generating output\n");
William Roberts81e1f902015-06-03 21:57:47 -07001387 validate();
William Robertsf0e0a942012-08-27 15:41:15 -07001388 output();
1389 log_info("Success, generated output\n");
1390 exit(EXIT_SUCCESS);
1391}