blob: b80d0e6056b157a3e80a4397d4bb2e6ac3572082 [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"
Inseob Kim292f22a2023-08-18 15:24:59 +090025#define VENDOR_SEAPP_ASSIGNS_COREDOMAIN_VIOLATORS "vendor_seapp_assigns_coredomain_violators"
Alan Stokes3c4375a2020-11-11 14:45:32 +000026
William Roberts81e1f902015-06-03 21:57:47 -070027/**
28 * Initializes an empty, static list.
29 */
Chih-Hung Hsieh33500c92016-05-11 14:59:45 -070030#define list_init(free_fn) { .head = NULL, .tail = NULL, .freefn = (free_fn) }
William Roberts81e1f902015-06-03 21:57:47 -070031
32/**
33 * given an item in the list, finds the offset for the container
34 * it was stored in.
35 *
36 * @element The element from the list
37 * @type The container type ie what you allocated that has the list_element structure in it.
38 * @name The name of the field that is the list_element
39 *
40 */
41#define list_entry(element, type, name) \
Chih-Hung Hsieh33500c92016-05-11 14:59:45 -070042 (type *)(((uint8_t *)(element)) - (uint8_t *)&(((type *)NULL)->name))
William Roberts81e1f902015-06-03 21:57:47 -070043
44/**
45 * Iterates over the list, do not free elements from the list when using this.
46 * @list The list head to walk
47 * @var The variable name for the cursor
48 */
49#define list_for_each(list, var) \
Chih-Hung Hsieh33500c92016-05-11 14:59:45 -070050 for(var = (list)->head; var != NULL; var = var->next) /*NOLINT*/
William Roberts81e1f902015-06-03 21:57:47 -070051
52
William Robertsf0e0a942012-08-27 15:41:15 -070053typedef struct hash_entry hash_entry;
54typedef enum key_dir key_dir;
55typedef enum data_type data_type;
56typedef enum rule_map_switch rule_map_switch;
William Roberts0ae3a8a2012-09-04 11:51:04 -070057typedef enum map_match map_match;
William Robertsf0e0a942012-08-27 15:41:15 -070058typedef struct key_map key_map;
59typedef struct kvp kvp;
60typedef struct rule_map rule_map;
61typedef struct policy_info policy_info;
William Roberts81e1f902015-06-03 21:57:47 -070062typedef struct list_element list_element;
63typedef struct list list;
64typedef struct key_map_regex key_map_regex;
65typedef struct file_info file_info;
Inseob Kimd7d36092023-06-26 20:48:48 +090066typedef struct coredomain_violation_entry coredomain_violation_entry;
William Robertsf0e0a942012-08-27 15:41:15 -070067
William Roberts0ae3a8a2012-09-04 11:51:04 -070068enum map_match {
69 map_no_matches,
70 map_input_matched,
71 map_matched
72};
73
Stephen Smalley0b820042015-02-13 14:58:31 -050074const char *map_match_str[] = {
75 "do not match",
76 "match on all inputs",
77 "match on everything"
78};
79
William Robertsf0e0a942012-08-27 15:41:15 -070080/**
81 * Whether or not the "key" from a key vaue pair is considered an
82 * input or an output.
83 */
84enum key_dir {
85 dir_in, dir_out
86};
87
William Roberts81e1f902015-06-03 21:57:47 -070088struct list_element {
89 list_element *next;
90};
91
92struct list {
93 list_element *head;
94 list_element *tail;
95 void (*freefn)(list_element *e);
96};
97
98struct key_map_regex {
Janis Danisevskis750d7972016-04-01 13:31:57 +010099 pcre2_code *compiled;
100 pcre2_match_data *match_data;
William Robertsf0e0a942012-08-27 15:41:15 -0700101};
102
103/**
104 * The workhorse of the logic. This struct maps key value pairs to
105 * an associated set of meta data maintained in rule_map_new()
106 */
107struct key_map {
108 char *name;
109 key_dir dir;
William Robertsf0e0a942012-08-27 15:41:15 -0700110 char *data;
William Roberts81e1f902015-06-03 21:57:47 -0700111 key_map_regex regex;
Inseob Kimd7d36092023-06-26 20:48:48 +0900112 bool (*fn_validate)(char *value, const char *filename, int lineno, char **errmsg);
William Robertsf0e0a942012-08-27 15:41:15 -0700113};
114
115/**
116 * Key value pair struct, this represents the raw kvp values coming
117 * from the rules files.
118 */
119struct kvp {
120 char *key;
121 char *value;
122};
123
124/**
125 * Rules are made up of meta data and an associated set of kvp stored in a
126 * key_map array.
127 */
128struct rule_map {
William Roberts81e1f902015-06-03 21:57:47 -0700129 bool is_never_allow;
130 list violations;
131 list_element listify;
William Robertsf0e0a942012-08-27 15:41:15 -0700132 char *key; /** key value before hashing */
William Roberts610a4b12013-10-15 18:26:00 -0700133 size_t length; /** length of the key map */
William Robertsf0e0a942012-08-27 15:41:15 -0700134 int lineno; /** Line number rule was encounter on */
William Roberts81e1f902015-06-03 21:57:47 -0700135 char *filename; /** File it was found in */
William Robertsf0e0a942012-08-27 15:41:15 -0700136 key_map m[]; /** key value mapping */
137};
138
139struct hash_entry {
William Roberts81e1f902015-06-03 21:57:47 -0700140 list_element listify;
William Robertsf0e0a942012-08-27 15:41:15 -0700141 rule_map *r; /** The rule map to store at that location */
142};
143
144/**
145 * Data associated for a policy file
146 */
147struct policy_info {
148
149 char *policy_file_name; /** policy file path name */
150 FILE *policy_file; /** file handle to the policy file */
151 sepol_policydb_t *db;
152 sepol_policy_file_t *pf;
153 sepol_handle_t *handle;
154 sepol_context_t *con;
Inseob Kimd7d36092023-06-26 20:48:48 +0900155 bool vendor;
William Robertsf0e0a942012-08-27 15:41:15 -0700156};
157
William Roberts81e1f902015-06-03 21:57:47 -0700158struct file_info {
159 FILE *file; /** file itself */
160 const char *name; /** name of file. do not free, these are not alloc'd */
161 list_element listify;
162};
163
Inseob Kimd7d36092023-06-26 20:48:48 +0900164struct coredomain_violation_entry {
165 list_element listify;
166 char *domain;
167 char *filename;
168 int lineno;
169};
170
171static void coredomain_violation_list_freefn(list_element *e);
William Roberts81e1f902015-06-03 21:57:47 -0700172static void input_file_list_freefn(list_element *e);
173static void line_order_list_freefn(list_element *e);
174static void rule_map_free(rule_map *rm, bool is_in_htable);
175
William Robertsf0e0a942012-08-27 15:41:15 -0700176/** Set to !0 to enable verbose logging */
177static int logging_verbose = 0;
178
179/** file handle to the output file */
William Roberts81e1f902015-06-03 21:57:47 -0700180static file_info out_file;
William Robertsf0e0a942012-08-27 15:41:15 -0700181
William Roberts81e1f902015-06-03 21:57:47 -0700182static list input_file_list = list_init(input_file_list_freefn);
William Robertsf0e0a942012-08-27 15:41:15 -0700183
Inseob Kimd7d36092023-06-26 20:48:48 +0900184static list coredomain_violation_list = list_init(coredomain_violation_list_freefn);
185
William Robertsf0e0a942012-08-27 15:41:15 -0700186static policy_info pol = {
187 .policy_file_name = NULL,
188 .policy_file = NULL,
189 .db = NULL,
190 .pf = NULL,
191 .handle = NULL,
Inseob Kimd7d36092023-06-26 20:48:48 +0900192 .con = NULL,
193 .vendor = false
William Robertsf0e0a942012-08-27 15:41:15 -0700194};
195
196/**
William Roberts81e1f902015-06-03 21:57:47 -0700197 * Head pointer to a linked list of
198 * rule map table entries (hash_entry), used for
199 * preserving the order of entries
200 * based on "first encounter"
201 */
202static list line_order_list = list_init(line_order_list_freefn);
203
204/*
205 * List of hash_entrys for never allow rules.
206 */
207static list nallow_list = list_init(line_order_list_freefn);
208
William Roberts696a66b2016-01-29 10:41:50 -0800209/* validation call backs */
Inseob Kimd7d36092023-06-26 20:48:48 +0900210static bool validate_bool(char *value, const char *filename, int lineno, char **errmsg);
211static bool validate_levelFrom(char *value, const char *filename, int lineno, char **errmsg);
212static bool validate_domain(char *value, const char *filename, int lineno, char **errmsg);
213static bool validate_type(char *value, const char *filename, int lineno, char **errmsg);
214static bool validate_selinux_level(char *value, const char *filename, int lineno, char **errmsg);
215static bool validate_uint(char *value, const char *filename, int lineno, char **errmsg);
William Roberts696a66b2016-01-29 10:41:50 -0800216
William Roberts81e1f902015-06-03 21:57:47 -0700217/**
William Robertsf0e0a942012-08-27 15:41:15 -0700218 * The heart of the mapping process, this must be updated if a new key value pair is added
219 * to a rule.
220 */
221key_map rules[] = {
222 /*Inputs*/
William Roberts29adea52016-01-29 15:12:58 -0800223 { .name = "isSystemServer", .dir = dir_in, .fn_validate = validate_bool },
Chad Brubaker06cf31e2016-10-06 13:15:44 -0700224 { .name = "isEphemeralApp", .dir = dir_in, .fn_validate = validate_bool },
William Roberts29adea52016-01-29 15:12:58 -0800225 { .name = "user", .dir = dir_in, },
226 { .name = "seinfo", .dir = dir_in, },
227 { .name = "name", .dir = dir_in, },
William Roberts29adea52016-01-29 15:12:58 -0800228 { .name = "isPrivApp", .dir = dir_in, .fn_validate = validate_bool },
Michael Peckf54b3622017-02-14 09:48:57 -0800229 { .name = "minTargetSdkVersion", .dir = dir_in, .fn_validate = validate_uint },
Yabin Cuiffa2b612018-11-02 14:34:06 -0700230 { .name = "fromRunAs", .dir = dir_in, .fn_validate = validate_bool },
Charles Chen30704922023-01-15 05:41:42 +0000231 { .name = "isIsolatedComputeApp", .dir = dir_in, .fn_validate = validate_bool },
Mugdha Lakhani49075f92023-05-07 17:41:57 +0000232 { .name = "isSdkSandboxNext", .dir = dir_in, .fn_validate = validate_bool },
William Robertsf0e0a942012-08-27 15:41:15 -0700233 /*Outputs*/
Alan Stokes3c4375a2020-11-11 14:45:32 +0000234 { .name = "domain", .dir = dir_out, .fn_validate = validate_domain },
235 { .name = "type", .dir = dir_out, .fn_validate = validate_type },
William Roberts29adea52016-01-29 15:12:58 -0800236 { .name = "levelFromUid", .dir = dir_out, .fn_validate = validate_bool },
237 { .name = "levelFrom", .dir = dir_out, .fn_validate = validate_levelFrom },
238 { .name = "level", .dir = dir_out, .fn_validate = validate_selinux_level },
William Robertsfff29802012-11-27 14:20:34 -0800239};
William Robertsf0e0a942012-08-27 15:41:15 -0700240
241/**
William Roberts81e1f902015-06-03 21:57:47 -0700242 * Appends to the end of the list.
243 * @list The list to append to
244 * @e the element to append
William Robertsf0e0a942012-08-27 15:41:15 -0700245 */
William Roberts81e1f902015-06-03 21:57:47 -0700246void list_append(list *list, list_element *e) {
247
248 memset(e, 0, sizeof(*e));
249
250 if (list->head == NULL ) {
251 list->head = list->tail = e;
252 return;
253 }
254
255 list->tail->next = e;
256 list->tail = e;
257 return;
258}
William Robertsf0e0a942012-08-27 15:41:15 -0700259
260/**
William Roberts81e1f902015-06-03 21:57:47 -0700261 * Free's all the elements in the specified list.
262 * @list The list to free
William Robertsf0e0a942012-08-27 15:41:15 -0700263 */
William Roberts81e1f902015-06-03 21:57:47 -0700264static void list_free(list *list) {
265
266 list_element *tmp;
267 list_element *cursor = list->head;
268
269 while (cursor) {
270 tmp = cursor;
271 cursor = cursor->next;
272 if (list->freefn) {
273 list->freefn(tmp);
274 }
275 }
276}
277
278/*
279 * called when the lists are freed
280 */
281static void line_order_list_freefn(list_element *e) {
282 hash_entry *h = list_entry(e, typeof(*h), listify);
283 rule_map_free(h->r, true);
284 free(h);
285}
286
287static void input_file_list_freefn(list_element *e) {
288 file_info *f = list_entry(e, typeof(*f), listify);
289
290 if (f->file) {
291 fclose(f->file);
292 }
293 free(f);
294}
William Robertsf0e0a942012-08-27 15:41:15 -0700295
Inseob Kimd7d36092023-06-26 20:48:48 +0900296static void coredomain_violation_list_freefn(list_element *e) {
297 coredomain_violation_entry *c = list_entry(e, typeof(*c), listify);
298
299 free(c->domain);
300 free(c->filename);
301 free(c);
302}
303
William Robertsf0e0a942012-08-27 15:41:15 -0700304/**
305 * Send a logging message to a file
306 * @param out
307 * Output file to send message too
308 * @param prefix
309 * A special prefix to write to the file, such as "Error:"
310 * @param fmt
311 * The printf style formatter to use, such as "%d"
312 */
William Roberts1e8c0612013-04-19 19:06:02 -0700313static void __attribute__ ((format(printf, 3, 4)))
314log_msg(FILE *out, const char *prefix, const char *fmt, ...) {
315
William Robertsf0e0a942012-08-27 15:41:15 -0700316 fprintf(out, "%s", prefix);
317 va_list args;
318 va_start(args, fmt);
319 vfprintf(out, fmt, args);
320 va_end(args);
321}
322
323/**
Alan Stokes3c4375a2020-11-11 14:45:32 +0000324 * Look up a type in the policy.
William Robertsf0e0a942012-08-27 15:41:15 -0700325 * @param db
326 * The policy db to search
327 * @param type
328 * The type to search for
Alan Stokes3c4375a2020-11-11 14:45:32 +0000329 * @param flavor
330 * The expected flavor of type
William Robertsf0e0a942012-08-27 15:41:15 -0700331 * @return
Alan Stokes3c4375a2020-11-11 14:45:32 +0000332 * Pointer to the type's datum if it exists in the policy with the expected
333 * flavor, NULL otherwise.
William Robertsf0e0a942012-08-27 15:41:15 -0700334 * @warning
Alan Stokes3c4375a2020-11-11 14:45:32 +0000335 * This function should not be called if libsepol is not linked statically
336 * to this executable and LINK_SEPOL_STATIC is not defined.
William Robertsf0e0a942012-08-27 15:41:15 -0700337 */
Alan Stokes3c4375a2020-11-11 14:45:32 +0000338static type_datum_t *find_type(sepol_policydb_t *db, char *type, uint32_t flavor) {
William Robertsf0e0a942012-08-27 15:41:15 -0700339
Alan Stokes3c4375a2020-11-11 14:45:32 +0000340 policydb_t *d = &db->p;
341 hashtab_datum_t dat = hashtab_search(d->p_types.table, type);
342 if (!dat) {
343 return NULL;
344 }
345 type_datum_t *type_dat = (type_datum_t *) dat;
346 if (type_dat->flavor != flavor) {
347 return NULL;
348 }
349 return type_dat;
350}
351
352static bool type_has_attribute(sepol_policydb_t *db, type_datum_t *type_dat,
353 type_datum_t *attrib_dat) {
354 policydb_t *d = &db->p;
355 ebitmap_t *attr_bits = &d->type_attr_map[type_dat->s.value - 1];
356 return ebitmap_get_bit(attr_bits, attrib_dat->s.value - 1) != 0;
William Robertsf0e0a942012-08-27 15:41:15 -0700357}
358
William Roberts81e1f902015-06-03 21:57:47 -0700359static bool match_regex(key_map *assert, const key_map *check) {
360
361 char *tomatch = check->data;
362
Janis Danisevskis750d7972016-04-01 13:31:57 +0100363 int ret = pcre2_match(assert->regex.compiled, (PCRE2_SPTR) tomatch,
364 PCRE2_ZERO_TERMINATED, 0, 0,
365 assert->regex.match_data, NULL);
William Roberts81e1f902015-06-03 21:57:47 -0700366
Janis Danisevskis750d7972016-04-01 13:31:57 +0100367 /* ret > 0 from pcre2_match means matched */
368 return ret > 0;
William Roberts81e1f902015-06-03 21:57:47 -0700369}
370
Janis Danisevskis750d7972016-04-01 13:31:57 +0100371static bool compile_regex(key_map *km, int *errcode, PCRE2_SIZE *erroff) {
William Roberts81e1f902015-06-03 21:57:47 -0700372
373 size_t size;
374 char *anchored;
375
376 /*
377 * Explicitly anchor all regex's
378 * The size is the length of the string to anchor (km->data), the anchor
379 * characters ^ and $ and the null byte. Hence strlen(km->data) + 3
380 */
381 size = strlen(km->data) + 3;
382 anchored = alloca(size);
383 sprintf(anchored, "^%s$", km->data);
384
Janis Danisevskis750d7972016-04-01 13:31:57 +0100385 km->regex.compiled = pcre2_compile((PCRE2_SPTR) anchored,
386 PCRE2_ZERO_TERMINATED,
387 PCRE2_DOTALL,
388 errcode, erroff,
389 NULL);
William Roberts81e1f902015-06-03 21:57:47 -0700390 if (!km->regex.compiled) {
391 return false;
392 }
393
Janis Danisevskis750d7972016-04-01 13:31:57 +0100394 km->regex.match_data = pcre2_match_data_create_from_pattern(
395 km->regex.compiled, NULL);
396 if (!km->regex.match_data) {
397 pcre2_code_free(km->regex.compiled);
398 return false;
399 }
William Roberts81e1f902015-06-03 21:57:47 -0700400 return true;
401}
402
Inseob Kimd7d36092023-06-26 20:48:48 +0900403static bool validate_bool(
404 char *value,
405 __attribute__ ((unused)) const char *filename,
406 __attribute__ ((unused)) int lineno,
407 char **errmsg) {
William Roberts696a66b2016-01-29 10:41:50 -0800408 if (!strcmp("true", value) || !strcmp("false", value)) {
409 return true;
410 }
411
412 *errmsg = "Expecting \"true\" or \"false\"";
413 return false;
414}
415
Inseob Kimd7d36092023-06-26 20:48:48 +0900416static bool validate_levelFrom(
417 char *value,
418 __attribute__ ((unused)) const char *filename,
419 __attribute__ ((unused)) int lineno,
420 char **errmsg) {
Alan Stokes3c4375a2020-11-11 14:45:32 +0000421 if (strcasecmp(value, "none") && strcasecmp(value, "all") &&
William Roberts696a66b2016-01-29 10:41:50 -0800422 strcasecmp(value, "app") && strcasecmp(value, "user")) {
423 *errmsg = "Expecting one of: \"none\", \"all\", \"app\" or \"user\"";
424 return false;
425 }
426 return true;
427}
428
Inseob Kimd7d36092023-06-26 20:48:48 +0900429static bool validate_domain(char *value, const char *filename, int lineno, char **errmsg) {
William Roberts696a66b2016-01-29 10:41:50 -0800430
Alan Stokes3c4375a2020-11-11 14:45:32 +0000431#if defined(LINK_SEPOL_STATIC)
William Roberts696a66b2016-01-29 10:41:50 -0800432 /*
433 * No policy file present means we cannot check
434 * SE Linux types
435 */
436 if (!pol.policy_file) {
437 return true;
438 }
439
Inseob Kimd7d36092023-06-26 20:48:48 +0900440 type_datum_t *type_dat = find_type(pol.db, value, TYPE_TYPE);
441 if (!type_dat) {
William Roberts696a66b2016-01-29 10:41:50 -0800442 *errmsg = "Expecting a valid SELinux type";
443 return false;
444 }
Inseob Kimd7d36092023-06-26 20:48:48 +0900445
446 if (pol.vendor) {
447 type_datum_t *attrib_dat = find_type(pol.db, COREDOMAIN, TYPE_ATTRIB);
448 if (!attrib_dat) {
449 *errmsg = "The attribute " COREDOMAIN " is not defined in the policy";
450 return false;
451 }
452
Inseob Kim292f22a2023-08-18 15:24:59 +0900453 type_datum_t *attrib_violators = find_type(pol.db,
454 VENDOR_SEAPP_ASSIGNS_COREDOMAIN_VIOLATORS,
455 TYPE_ATTRIB);
456 bool allowlisted = attrib_violators != NULL &&
457 type_has_attribute(pol.db, type_dat, attrib_violators);
458
459 if (type_has_attribute(pol.db, type_dat, attrib_dat) && !allowlisted) {
Inseob Kimd7d36092023-06-26 20:48:48 +0900460 coredomain_violation_entry *entry = (coredomain_violation_entry *)malloc(sizeof(*entry));
461 entry->domain = strdup(value);
462 entry->filename = strdup(filename);
463 entry->lineno = lineno;
464 list_append(&coredomain_violation_list, &entry->listify);
465 }
466 }
Alan Stokes3c4375a2020-11-11 14:45:32 +0000467#endif
468
469 return true;
470}
471
Inseob Kimd7d36092023-06-26 20:48:48 +0900472static bool validate_type(
473 char *value,
474 __attribute__ ((unused)) const char *filename,
475 __attribute__ ((unused)) int lineno,
476 char **errmsg) {
Alan Stokes3c4375a2020-11-11 14:45:32 +0000477#if defined(LINK_SEPOL_STATIC)
478 /*
479 * No policy file present means we cannot check
480 * SE Linux types
481 */
482 if (!pol.policy_file) {
483 return true;
484 }
485
486 type_datum_t *type_dat = find_type(pol.db, value, TYPE_TYPE);
487 if (!type_dat) {
488 *errmsg = "Expecting a valid SELinux type";
489 return false;
490 }
491
492 type_datum_t *attrib_dat = find_type(pol.db, APP_DATA_REQUIRED_ATTRIB,
493 TYPE_ATTRIB);
494 if (!attrib_dat) {
495 /* If the policy doesn't contain the attribute, we can't check it */
496 return true;
497 }
498
499 if (!type_has_attribute(pol.db, type_dat, attrib_dat)) {
500 *errmsg = "Missing required attribute " APP_DATA_REQUIRED_ATTRIB;
501 return false;
502 }
503
504#endif
William Roberts696a66b2016-01-29 10:41:50 -0800505
506 return true;
507}
508
Inseob Kimd7d36092023-06-26 20:48:48 +0900509static bool validate_selinux_level(
510 char *value,
511 __attribute__ ((unused)) const char *filename,
512 __attribute__ ((unused)) int lineno,
513 char **errmsg) {
William Roberts696a66b2016-01-29 10:41:50 -0800514 /*
515 * No policy file present means we cannot check
516 * SE Linux MLS
517 */
518 if (!pol.policy_file) {
519 return true;
520 }
521
522 int ret = sepol_mls_check(pol.handle, pol.db, value);
523 if (ret < 0) {
524 *errmsg = "Expecting a valid SELinux MLS value";
525 return false;
526 }
527
528 return true;
529}
530
Inseob Kimd7d36092023-06-26 20:48:48 +0900531static bool validate_uint(
532 char *value,
533 __attribute__ ((unused)) const char *filename,
534 __attribute__ ((unused)) int lineno,
535 char **errmsg) {
Michael Peckf54b3622017-02-14 09:48:57 -0800536 char *endptr;
537 long longvalue;
538 longvalue = strtol(value, &endptr, 10);
539 if (('\0' != *endptr) || (longvalue < 0) || (longvalue > INT32_MAX)) {
540 *errmsg = "Expecting a valid unsigned integer";
541 return false;
542 }
543
544 return true;
545}
546
William Robertsf0e0a942012-08-27 15:41:15 -0700547/**
548 * Validates a key_map against a set of enforcement rules, this
549 * function exits the application on a type that cannot be properly
550 * checked
551 *
552 * @param m
553 * The key map to check
554 * @param lineno
555 * The line number in the source file for the corresponding key map
William Robertsfff29802012-11-27 14:20:34 -0800556 * @return
William Roberts81e1f902015-06-03 21:57:47 -0700557 * true if valid, false if invalid
William Robertsf0e0a942012-08-27 15:41:15 -0700558 */
William Roberts81e1f902015-06-03 21:57:47 -0700559static bool key_map_validate(key_map *m, const char *filename, int lineno,
560 bool is_neverallow) {
William Robertsf0e0a942012-08-27 15:41:15 -0700561
Janis Danisevskis750d7972016-04-01 13:31:57 +0100562 PCRE2_SIZE erroff;
563 int errcode;
William Roberts81e1f902015-06-03 21:57:47 -0700564 bool rc = true;
William Robertsf0e0a942012-08-27 15:41:15 -0700565 char *key = m->name;
566 char *value = m->data;
William Roberts696a66b2016-01-29 10:41:50 -0800567 char *errmsg = NULL;
Janis Danisevskis750d7972016-04-01 13:31:57 +0100568 char errstr[256];
William Robertsf0e0a942012-08-27 15:41:15 -0700569
William Roberts0ae3a8a2012-09-04 11:51:04 -0700570 log_info("Validating %s=%s\n", key, value);
571
William Roberts81e1f902015-06-03 21:57:47 -0700572 /*
Joel Galensonb0d74a12020-07-27 09:30:34 -0700573 * Neverallows are completely skipped from validity checking so you can match
William Roberts81e1f902015-06-03 21:57:47 -0700574 * un-unspecified inputs.
575 */
576 if (is_neverallow) {
577 if (!m->regex.compiled) {
Janis Danisevskis750d7972016-04-01 13:31:57 +0100578 rc = compile_regex(m, &errcode, &erroff);
William Roberts81e1f902015-06-03 21:57:47 -0700579 if (!rc) {
Janis Danisevskis750d7972016-04-01 13:31:57 +0100580 pcre2_get_error_message(errcode,
581 (PCRE2_UCHAR*) errstr,
582 sizeof(errstr));
583 log_error("Invalid regex on line %d : %s PCRE error: %s at offset %lu",
584 lineno, value, errstr, erroff);
William Roberts81e1f902015-06-03 21:57:47 -0700585 }
586 }
587 goto out;
588 }
589
William Roberts696a66b2016-01-29 10:41:50 -0800590 /* If the key has a validation routine, call it */
591 if (m->fn_validate) {
Inseob Kimd7d36092023-06-26 20:48:48 +0900592 rc = m->fn_validate(value, filename, lineno, &errmsg);
William Robertsf0e0a942012-08-27 15:41:15 -0700593
William Roberts696a66b2016-01-29 10:41:50 -0800594 if (!rc) {
595 log_error("Could not validate key \"%s\" for value \"%s\" on line: %d in file: \"%s\": %s\n", key, value,
596 lineno, filename, errmsg);
William Robertsf0e0a942012-08-27 15:41:15 -0700597 }
598 }
599
William Roberts0ae3a8a2012-09-04 11:51:04 -0700600out:
601 log_info("Key map validate returning: %d\n", rc);
602 return rc;
William Robertsf0e0a942012-08-27 15:41:15 -0700603}
604
605/**
606 * Prints a rule map back to a file
607 * @param fp
608 * The file handle to print too
609 * @param r
610 * The rule map to print
611 */
612static void rule_map_print(FILE *fp, rule_map *r) {
613
William Roberts610a4b12013-10-15 18:26:00 -0700614 size_t i;
William Robertsf0e0a942012-08-27 15:41:15 -0700615 key_map *m;
616
617 for (i = 0; i < r->length; i++) {
618 m = &(r->m[i]);
619 if (i < r->length - 1)
620 fprintf(fp, "%s=%s ", m->name, m->data);
621 else
622 fprintf(fp, "%s=%s", m->name, m->data);
623 }
624}
625
626/**
627 * Compare two rule maps for equality
628 * @param rmA
629 * a rule map to check
630 * @param rmB
631 * a rule map to check
632 * @return
William Robertsae23a1f2012-09-05 12:53:52 -0700633 * a map_match enum indicating the result
William Robertsf0e0a942012-08-27 15:41:15 -0700634 */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700635static map_match rule_map_cmp(rule_map *rmA, rule_map *rmB) {
William Robertsf0e0a942012-08-27 15:41:15 -0700636
William Roberts610a4b12013-10-15 18:26:00 -0700637 size_t i;
638 size_t j;
William Robertsf0e0a942012-08-27 15:41:15 -0700639 int inputs_found = 0;
640 int num_of_matched_inputs = 0;
641 int input_mode = 0;
William Roberts610a4b12013-10-15 18:26:00 -0700642 size_t matches = 0;
William Robertsf0e0a942012-08-27 15:41:15 -0700643 key_map *mA;
644 key_map *mB;
645
William Robertsf0e0a942012-08-27 15:41:15 -0700646 for (i = 0; i < rmA->length; i++) {
647 mA = &(rmA->m[i]);
648
649 for (j = 0; j < rmB->length; j++) {
650 mB = &(rmB->m[j]);
651 input_mode = 0;
652
William Robertsf0e0a942012-08-27 15:41:15 -0700653 if (strcmp(mA->name, mB->name))
654 continue;
655
656 if (strcmp(mA->data, mB->data))
657 continue;
658
659 if (mB->dir != mA->dir)
660 continue;
661 else if (mB->dir == dir_in) {
662 input_mode = 1;
663 inputs_found++;
664 }
665
William Roberts0ae3a8a2012-09-04 11:51:04 -0700666 if (input_mode) {
William Roberts1e8c0612013-04-19 19:06:02 -0700667 log_info("Matched input lines: name=%s data=%s\n", mA->name, mA->data);
William Robertsf0e0a942012-08-27 15:41:15 -0700668 num_of_matched_inputs++;
William Roberts0ae3a8a2012-09-04 11:51:04 -0700669 }
William Robertsf0e0a942012-08-27 15:41:15 -0700670
671 /* Match found, move on */
William Roberts1e8c0612013-04-19 19:06:02 -0700672 log_info("Matched lines: name=%s data=%s", mA->name, mA->data);
William Robertsf0e0a942012-08-27 15:41:15 -0700673 matches++;
674 break;
675 }
676 }
677
678 /* If they all matched*/
William Roberts0ae3a8a2012-09-04 11:51:04 -0700679 if (matches == rmA->length) {
680 log_info("Rule map cmp MATCH\n");
681 return map_matched;
682 }
William Robertsf0e0a942012-08-27 15:41:15 -0700683
684 /* They didn't all match but the input's did */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700685 else if (num_of_matched_inputs == inputs_found) {
686 log_info("Rule map cmp INPUT MATCH\n");
687 return map_input_matched;
688 }
William Robertsf0e0a942012-08-27 15:41:15 -0700689
690 /* They didn't all match, and the inputs didn't match, ie it didn't
691 * match */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700692 else {
693 log_info("Rule map cmp NO MATCH\n");
694 return map_no_matches;
695 }
William Robertsf0e0a942012-08-27 15:41:15 -0700696}
697
698/**
699 * Frees a rule map
700 * @param rm
701 * rule map to be freed.
William Roberts7d65b542015-06-19 09:43:28 -0700702 * @is_in_htable
703 * True if the rule map has been added to the hash table, false
704 * otherwise.
705 */
706static void rule_map_free(rule_map *rm, bool is_in_htable) {
William Robertsf0e0a942012-08-27 15:41:15 -0700707
William Roberts610a4b12013-10-15 18:26:00 -0700708 size_t i;
709 size_t len = rm->length;
William Robertsf0e0a942012-08-27 15:41:15 -0700710 for (i = 0; i < len; i++) {
711 key_map *m = &(rm->m[i]);
712 free(m->data);
William Roberts81e1f902015-06-03 21:57:47 -0700713
714 if (m->regex.compiled) {
Janis Danisevskis750d7972016-04-01 13:31:57 +0100715 pcre2_code_free(m->regex.compiled);
William Roberts81e1f902015-06-03 21:57:47 -0700716 }
717
Janis Danisevskis750d7972016-04-01 13:31:57 +0100718 if (m->regex.match_data) {
719 pcre2_match_data_free(m->regex.match_data);
William Roberts81e1f902015-06-03 21:57:47 -0700720 }
William Robertsf0e0a942012-08-27 15:41:15 -0700721 }
722
William Roberts7d65b542015-06-19 09:43:28 -0700723 /*
724 * hdestroy() frees comparsion keys for non glibc
725 * on GLIBC we always free on NON-GLIBC we free if
726 * it is not in the htable.
727 */
728 if (rm->key) {
rpcraig5dbfdc02012-10-23 11:03:47 -0400729#ifdef __GLIBC__
William Roberts7d65b542015-06-19 09:43:28 -0700730 /* silence unused warning */
731 (void)is_in_htable;
William Robertsf0e0a942012-08-27 15:41:15 -0700732 free(rm->key);
William Roberts7d65b542015-06-19 09:43:28 -0700733#else
734 if (!is_in_htable) {
735 free(rm->key);
736 }
rpcraig5dbfdc02012-10-23 11:03:47 -0400737#endif
William Roberts7d65b542015-06-19 09:43:28 -0700738 }
William Robertsf0e0a942012-08-27 15:41:15 -0700739
William Roberts81e1f902015-06-03 21:57:47 -0700740 free(rm->filename);
William Robertsf0e0a942012-08-27 15:41:15 -0700741 free(rm);
742}
743
744static void free_kvp(kvp *k) {
745 free(k->key);
746 free(k->value);
747}
748
749/**
William Roberts61846292013-10-15 09:38:24 -0700750 * Checks a rule_map for any variation of KVP's that shouldn't be allowed.
William Roberts81e1f902015-06-03 21:57:47 -0700751 * It builds an assertion failure list for each rule map.
William Roberts61846292013-10-15 09:38:24 -0700752 * Note that this function logs all errors.
753 *
754 * Current Checks:
755 * 1. That a specified name entry should have a specified seinfo entry as well.
William Roberts81e1f902015-06-03 21:57:47 -0700756 * 2. That no rule violates a neverallow
William Roberts61846292013-10-15 09:38:24 -0700757 * @param rm
758 * The rule map to check for validity.
William Roberts61846292013-10-15 09:38:24 -0700759 */
William Roberts81e1f902015-06-03 21:57:47 -0700760static void rule_map_validate(rule_map *rm) {
William Roberts61846292013-10-15 09:38:24 -0700761
William Roberts81e1f902015-06-03 21:57:47 -0700762 size_t i, j;
763 const key_map *rule;
764 key_map *nrule;
765 hash_entry *e;
766 rule_map *assert;
767 list_element *cursor;
William Roberts61846292013-10-15 09:38:24 -0700768
William Roberts81e1f902015-06-03 21:57:47 -0700769 list_for_each(&nallow_list, cursor) {
770 e = list_entry(cursor, typeof(*e), listify);
771 assert = e->r;
William Roberts61846292013-10-15 09:38:24 -0700772
William Roberts81e1f902015-06-03 21:57:47 -0700773 size_t cnt = 0;
774
775 for (j = 0; j < assert->length; j++) {
776 nrule = &(assert->m[j]);
777
778 // mark that nrule->name is for a null check
779 bool is_null_check = !strcmp(nrule->data, "\"\"");
780
781 for (i = 0; i < rm->length; i++) {
782 rule = &(rm->m[i]);
783
784 if (!strcmp(rule->name, nrule->name)) {
785
786 /* the name was found, (data cannot be false) then it was specified */
787 is_null_check = false;
788
789 if (match_regex(nrule, rule)) {
790 cnt++;
791 }
792 }
793 }
794
795 /*
796 * the nrule was marked in a null check and we never found a match on nrule, thus
797 * it matched and we update the cnt
798 */
799 if (is_null_check) {
800 cnt++;
801 }
William Roberts61846292013-10-15 09:38:24 -0700802 }
William Roberts81e1f902015-06-03 21:57:47 -0700803 if (cnt == assert->length) {
804 list_append(&rm->violations, &assert->listify);
William Roberts61846292013-10-15 09:38:24 -0700805 }
806 }
William Roberts61846292013-10-15 09:38:24 -0700807}
808
809/**
William Robertsf0e0a942012-08-27 15:41:15 -0700810 * Given a set of key value pairs, this will construct a new rule map.
811 * On error this function calls exit.
812 * @param keys
813 * Keys from a rule line to map
814 * @param num_of_keys
815 * The length of the keys array
816 * @param lineno
817 * The line number the keys were extracted from
818 * @return
819 * A rule map pointer.
820 */
William Roberts81e1f902015-06-03 21:57:47 -0700821static rule_map *rule_map_new(kvp keys[], size_t num_of_keys, int lineno,
822 const char *filename, bool is_never_allow) {
William Robertsf0e0a942012-08-27 15:41:15 -0700823
William Roberts610a4b12013-10-15 18:26:00 -0700824 size_t i = 0, j = 0;
William Robertsf0e0a942012-08-27 15:41:15 -0700825 rule_map *new_map = NULL;
826 kvp *k = NULL;
827 key_map *r = NULL, *x = NULL;
Stephen Smalley534fb072015-02-13 14:06:08 -0500828 bool seen[KVP_NUM_OF_RULES];
829
830 for (i = 0; i < KVP_NUM_OF_RULES; i++)
831 seen[i] = false;
William Robertsf0e0a942012-08-27 15:41:15 -0700832
833 new_map = calloc(1, (num_of_keys * sizeof(key_map)) + sizeof(rule_map));
834 if (!new_map)
835 goto oom;
836
William Roberts81e1f902015-06-03 21:57:47 -0700837 new_map->is_never_allow = is_never_allow;
William Robertsf0e0a942012-08-27 15:41:15 -0700838 new_map->length = num_of_keys;
839 new_map->lineno = lineno;
William Roberts81e1f902015-06-03 21:57:47 -0700840 new_map->filename = strdup(filename);
841 if (!new_map->filename) {
842 goto oom;
843 }
William Robertsf0e0a942012-08-27 15:41:15 -0700844
845 /* For all the keys in a rule line*/
846 for (i = 0; i < num_of_keys; i++) {
847 k = &(keys[i]);
848 r = &(new_map->m[i]);
849
850 for (j = 0; j < KVP_NUM_OF_RULES; j++) {
851 x = &(rules[j]);
852
853 /* Only assign key name to map name */
854 if (strcasecmp(k->key, x->name)) {
liwugang57d66ef2018-11-14 16:39:39 +0800855 if (j == KVP_NUM_OF_RULES - 1) {
William Robertsf0e0a942012-08-27 15:41:15 -0700856 log_error("No match for key: %s\n", k->key);
857 goto err;
858 }
859 continue;
860 }
861
Stephen Smalley534fb072015-02-13 14:06:08 -0500862 if (seen[j]) {
863 log_error("Duplicated key: %s\n", k->key);
864 goto err;
865 }
866 seen[j] = true;
867
William Robertsf0e0a942012-08-27 15:41:15 -0700868 memcpy(r, x, sizeof(key_map));
869
870 /* Assign rule map value to one from file */
871 r->data = strdup(k->value);
872 if (!r->data)
873 goto oom;
874
875 /* Enforce type check*/
William Roberts0ae3a8a2012-09-04 11:51:04 -0700876 log_info("Validating keys!\n");
William Roberts81e1f902015-06-03 21:57:47 -0700877 if (!key_map_validate(r, filename, lineno, new_map->is_never_allow)) {
William Robertsf0e0a942012-08-27 15:41:15 -0700878 log_error("Could not validate\n");
879 goto err;
880 }
881
William Roberts81e1f902015-06-03 21:57:47 -0700882 /*
883 * Only build key off of inputs with the exception of neverallows.
884 * Neverallows are keyed off of all key value pairs,
885 */
886 if (r->dir == dir_in || new_map->is_never_allow) {
William Robertsf0e0a942012-08-27 15:41:15 -0700887 char *tmp;
William Robertsb3ab56c2012-09-17 14:35:02 -0700888 int key_len = strlen(k->key);
889 int val_len = strlen(k->value);
890 int l = (new_map->key) ? strlen(new_map->key) : 0;
891 l = l + key_len + val_len;
William Robertsf0e0a942012-08-27 15:41:15 -0700892 l += 1;
893
894 tmp = realloc(new_map->key, l);
895 if (!tmp)
896 goto oom;
897
William Robertsb3ab56c2012-09-17 14:35:02 -0700898 if (!new_map->key)
899 memset(tmp, 0, l);
900
William Robertsf0e0a942012-08-27 15:41:15 -0700901 new_map->key = tmp;
902
William Robertsb3ab56c2012-09-17 14:35:02 -0700903 strncat(new_map->key, k->key, key_len);
904 strncat(new_map->key, k->value, val_len);
William Robertsf0e0a942012-08-27 15:41:15 -0700905 }
906 break;
907 }
908 free_kvp(k);
909 }
910
911 if (new_map->key == NULL) {
912 log_error("Strange, no keys found, input file corrupt perhaps?\n");
913 goto err;
914 }
915
916 return new_map;
917
918oom:
919 log_error("Out of memory!\n");
920err:
Alan Stokes3c4375a2020-11-11 14:45:32 +0000921 if (new_map) {
William Roberts7d65b542015-06-19 09:43:28 -0700922 rule_map_free(new_map, false);
William Robertsf0e0a942012-08-27 15:41:15 -0700923 for (; i < num_of_keys; i++) {
924 k = &(keys[i]);
925 free_kvp(k);
926 }
927 }
Stephen Smalley534fb072015-02-13 14:06:08 -0500928 return NULL;
William Robertsf0e0a942012-08-27 15:41:15 -0700929}
930
931/**
932 * Print the usage of the program
933 */
934static void usage() {
935 printf(
936 "checkseapp [options] <input file>\n"
937 "Processes an seapp_contexts file specified by argument <input file> (default stdin) "
William Robertsae23a1f2012-09-05 12:53:52 -0700938 "and allows later declarations to override previous ones on a match.\n"
William Robertsf0e0a942012-08-27 15:41:15 -0700939 "Options:\n"
940 "-h - print this help message\n"
941 "-v - enable verbose debugging informations\n"
William Roberts63297212013-04-19 19:06:23 -0700942 "-p policy file - specify policy file for strict checking of output selectors against the policy\n"
William Roberts81e1f902015-06-03 21:57:47 -0700943 "-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 -0700944}
945
946static void init() {
947
William Roberts81e1f902015-06-03 21:57:47 -0700948 bool has_out_file;
949 list_element *cursor;
950 file_info *tmp;
951
952 /* input files if the list is empty, use stdin */
953 if (!input_file_list.head) {
954 log_info("Using stdin for input\n");
955 tmp = malloc(sizeof(*tmp));
956 if (!tmp) {
957 log_error("oom");
William Robertsf0e0a942012-08-27 15:41:15 -0700958 exit(EXIT_FAILURE);
959 }
William Roberts81e1f902015-06-03 21:57:47 -0700960 tmp->name = "stdin";
961 tmp->file = stdin;
962 list_append(&input_file_list, &(tmp->listify));
963 }
964 else {
965 list_for_each(&input_file_list, cursor) {
966 tmp = list_entry(cursor, typeof(*tmp), listify);
967
968 log_info("Opening input file: \"%s\"\n", tmp->name);
969 tmp->file = fopen(tmp->name, "r");
970 if (!tmp->file) {
971 log_error("Could not open file: %s error: %s\n", tmp->name,
972 strerror(errno));
973 exit(EXIT_FAILURE);
974 }
975 }
William Robertsf0e0a942012-08-27 15:41:15 -0700976 }
977
William Roberts81e1f902015-06-03 21:57:47 -0700978 has_out_file = out_file.name != NULL;
979
980 /* If output file is -, then use stdout, else open the path */
981 if (has_out_file && !strcmp(out_file.name, "-")) {
982 out_file.file = stdout;
983 out_file.name = "stdout";
984 }
985 else if (has_out_file) {
986 out_file.file = fopen(out_file.name, "w+");
987 }
988
989 if (has_out_file && !out_file.file) {
990 log_error("Could not open file: \"%s\" error: \"%s\"\n", out_file.name,
991 strerror(errno));
992 exit(EXIT_FAILURE);
William Robertsf0e0a942012-08-27 15:41:15 -0700993 }
994
995 if (pol.policy_file_name) {
William Robertsf0e0a942012-08-27 15:41:15 -0700996 log_info("Opening policy file: %s\n", pol.policy_file_name);
997 pol.policy_file = fopen(pol.policy_file_name, "rb");
998 if (!pol.policy_file) {
999 log_error("Could not open file: %s error: %s\n",
1000 pol.policy_file_name, strerror(errno));
1001 exit(EXIT_FAILURE);
1002 }
1003
1004 pol.handle = sepol_handle_create();
1005 if (!pol.handle) {
1006 log_error("Could not create sepolicy handle: %s\n",
1007 strerror(errno));
1008 exit(EXIT_FAILURE);
1009 }
1010
1011 if (sepol_policy_file_create(&pol.pf) < 0) {
1012 log_error("Could not create sepolicy file: %s!\n",
1013 strerror(errno));
1014 exit(EXIT_FAILURE);
1015 }
1016
1017 sepol_policy_file_set_fp(pol.pf, pol.policy_file);
1018 sepol_policy_file_set_handle(pol.pf, pol.handle);
1019
1020 if (sepol_policydb_create(&pol.db) < 0) {
1021 log_error("Could not create sepolicy db: %s!\n",
1022 strerror(errno));
1023 exit(EXIT_FAILURE);
1024 }
1025
1026 if (sepol_policydb_read(pol.db, pol.pf) < 0) {
William Robertsf7d6bb32016-08-08 10:31:54 -07001027 log_error("Could not load policy file to db: invalid input file!\n");
William Robertsf0e0a942012-08-27 15:41:15 -07001028 exit(EXIT_FAILURE);
1029 }
1030 }
1031
William Roberts81e1f902015-06-03 21:57:47 -07001032 list_for_each(&input_file_list, cursor) {
1033 tmp = list_entry(cursor, typeof(*tmp), listify);
1034 log_info("Input file set to: \"%s\"\n", tmp->name);
1035 }
1036
1037 log_info("Policy file set to: \"%s\"\n",
1038 (pol.policy_file_name == NULL) ? "None" : pol.policy_file_name);
1039 log_info("Output file set to: \"%s\"\n", out_file.name);
William Robertsf0e0a942012-08-27 15:41:15 -07001040
William Roberts0ae3a8a2012-09-04 11:51:04 -07001041#if !defined(LINK_SEPOL_STATIC)
William Robertsa53ccf32012-09-17 12:53:44 -07001042 log_warn("LINK_SEPOL_STATIC is not defined\n""Not checking types!");
William Roberts0ae3a8a2012-09-04 11:51:04 -07001043#endif
1044
William Robertsf0e0a942012-08-27 15:41:15 -07001045}
1046
1047/**
1048 * Handle parsing and setting the global flags for the command line
1049 * options. This function calls exit on failure.
1050 * @param argc
1051 * argument count
1052 * @param argv
1053 * argument list
1054 */
1055static void handle_options(int argc, char *argv[]) {
1056
1057 int c;
William Roberts81e1f902015-06-03 21:57:47 -07001058 file_info *input_file;
William Robertsf0e0a942012-08-27 15:41:15 -07001059
Inseob Kimd7d36092023-06-26 20:48:48 +09001060 while ((c = getopt(argc, argv, "ho:p:vc")) != -1) {
William Robertsf0e0a942012-08-27 15:41:15 -07001061 switch (c) {
1062 case 'h':
1063 usage();
1064 exit(EXIT_SUCCESS);
1065 case 'o':
William Roberts81e1f902015-06-03 21:57:47 -07001066 out_file.name = optarg;
William Robertsf0e0a942012-08-27 15:41:15 -07001067 break;
1068 case 'p':
1069 pol.policy_file_name = optarg;
1070 break;
1071 case 'v':
1072 log_set_verbose();
1073 break;
Inseob Kimd7d36092023-06-26 20:48:48 +09001074 case 'c':
1075 pol.vendor = true;
1076 break;
William Robertsf0e0a942012-08-27 15:41:15 -07001077 case '?':
1078 if (optopt == 'o' || optopt == 'p')
1079 log_error("Option -%c requires an argument.\n", optopt);
1080 else if (isprint (optopt))
1081 log_error("Unknown option `-%c'.\n", optopt);
1082 else {
1083 log_error(
1084 "Unknown option character `\\x%x'.\n",
1085 optopt);
William Robertsf0e0a942012-08-27 15:41:15 -07001086 }
William Robertsf0e0a942012-08-27 15:41:15 -07001087 default:
1088 exit(EXIT_FAILURE);
1089 }
1090 }
1091
William Roberts81e1f902015-06-03 21:57:47 -07001092 for (c = optind; c < argc; c++) {
William Robertsf0e0a942012-08-27 15:41:15 -07001093
William Roberts81e1f902015-06-03 21:57:47 -07001094 input_file = calloc(1, sizeof(*input_file));
1095 if (!input_file) {
1096 log_error("oom");
1097 exit(EXIT_FAILURE);
1098 }
1099 input_file->name = argv[c];
1100 list_append(&input_file_list, &input_file->listify);
William Robertsf0e0a942012-08-27 15:41:15 -07001101 }
1102}
1103
1104/**
1105 * Adds a rule to the hash table and to the ordered list if needed.
1106 * @param rm
1107 * The rule map to add.
1108 */
1109static void rule_add(rule_map *rm) {
1110
William Roberts0ae3a8a2012-09-04 11:51:04 -07001111 map_match cmp;
William Robertsf0e0a942012-08-27 15:41:15 -07001112 ENTRY e;
1113 ENTRY *f;
1114 hash_entry *entry;
1115 hash_entry *tmp;
William Roberts81e1f902015-06-03 21:57:47 -07001116 list *list_to_addto;
William Robertsf0e0a942012-08-27 15:41:15 -07001117
1118 e.key = rm->key;
Rahul Chaudhrye1682c72016-10-12 12:22:39 -07001119 e.data = NULL;
William Robertsf0e0a942012-08-27 15:41:15 -07001120
William Roberts0ae3a8a2012-09-04 11:51:04 -07001121 log_info("Searching for key: %s\n", e.key);
William Robertsf0e0a942012-08-27 15:41:15 -07001122 /* Check to see if it has already been added*/
1123 f = hsearch(e, FIND);
1124
1125 /*
1126 * Since your only hashing on a partial key, the inputs we need to handle
1127 * when you want to override the outputs for a given input set, as well as
1128 * checking for duplicate entries.
1129 */
Alan Stokes3c4375a2020-11-11 14:45:32 +00001130 if (f) {
William Roberts0ae3a8a2012-09-04 11:51:04 -07001131 log_info("Existing entry found!\n");
William Robertsf0e0a942012-08-27 15:41:15 -07001132 tmp = (hash_entry *)f->data;
1133 cmp = rule_map_cmp(rm, tmp->r);
Stephen Smalley0b820042015-02-13 14:58:31 -05001134 log_error("Duplicate line detected in file: %s\n"
1135 "Lines %d and %d %s!\n",
William Roberts81e1f902015-06-03 21:57:47 -07001136 rm->filename, tmp->r->lineno, rm->lineno,
Stephen Smalley0b820042015-02-13 14:58:31 -05001137 map_match_str[cmp]);
William Roberts7d65b542015-06-19 09:43:28 -07001138 rule_map_free(rm, false);
Stephen Smalley0b820042015-02-13 14:58:31 -05001139 goto err;
William Robertsf0e0a942012-08-27 15:41:15 -07001140 }
1141 /* It wasn't found, just add the rule map to the table */
1142 else {
1143
1144 entry = malloc(sizeof(hash_entry));
1145 if (!entry)
1146 goto oom;
1147
1148 entry->r = rm;
1149 e.data = entry;
1150
1151 f = hsearch(e, ENTER);
Alan Stokes3c4375a2020-11-11 14:45:32 +00001152 if (f == NULL) {
William Robertsf0e0a942012-08-27 15:41:15 -07001153 goto oom;
1154 }
1155
1156 /* new entries must be added to the ordered list */
1157 entry->r = rm;
William Roberts81e1f902015-06-03 21:57:47 -07001158 list_to_addto = rm->is_never_allow ? &nallow_list : &line_order_list;
1159 list_append(list_to_addto, &entry->listify);
William Robertsf0e0a942012-08-27 15:41:15 -07001160 }
1161
1162 return;
1163oom:
1164 if (e.key)
1165 free(e.key);
1166 if (entry)
1167 free(entry);
1168 if (rm)
1169 free(rm);
1170 log_error("Out of memory in function: %s\n", __FUNCTION__);
1171err:
1172 exit(EXIT_FAILURE);
1173}
1174
William Roberts81e1f902015-06-03 21:57:47 -07001175static void parse_file(file_info *in_file) {
William Robertsf0e0a942012-08-27 15:41:15 -07001176
William Roberts81e1f902015-06-03 21:57:47 -07001177 char *p;
William Robertsf0e0a942012-08-27 15:41:15 -07001178 size_t len;
William Roberts81e1f902015-06-03 21:57:47 -07001179 char *token;
1180 char *saveptr;
1181 bool is_never_allow;
1182 bool found_whitespace;
1183
1184 size_t lineno = 0;
1185 char *name = NULL;
1186 char *value = NULL;
William Roberts610a4b12013-10-15 18:26:00 -07001187 size_t token_cnt = 0;
William Robertsf0e0a942012-08-27 15:41:15 -07001188
William Roberts81e1f902015-06-03 21:57:47 -07001189 char line_buf[BUFSIZ];
1190 kvp keys[KVP_NUM_OF_RULES];
William Robertsf0e0a942012-08-27 15:41:15 -07001191
William Roberts81e1f902015-06-03 21:57:47 -07001192 while (fgets(line_buf, sizeof(line_buf) - 1, in_file->file)) {
William Robertsf0e0a942012-08-27 15:41:15 -07001193 lineno++;
William Roberts81e1f902015-06-03 21:57:47 -07001194 is_never_allow = false;
1195 found_whitespace = false;
1196 log_info("Got line %zu\n", lineno);
William Robertsf0e0a942012-08-27 15:41:15 -07001197 len = strlen(line_buf);
1198 if (line_buf[len - 1] == '\n')
Alice Chuf6647eb2012-10-30 16:27:00 -07001199 line_buf[len - 1] = '\0';
William Robertsf0e0a942012-08-27 15:41:15 -07001200 p = line_buf;
William Roberts81e1f902015-06-03 21:57:47 -07001201
1202 /* neverallow lines must start with neverallow (ie ^neverallow) */
1203 if (!strncasecmp(p, "neverallow", strlen("neverallow"))) {
1204 p += strlen("neverallow");
1205 is_never_allow = true;
1206 }
1207
1208 /* strip trailing whitespace skip comments */
1209 while (isspace(*p)) {
William Robertsf0e0a942012-08-27 15:41:15 -07001210 p++;
William Roberts81e1f902015-06-03 21:57:47 -07001211 found_whitespace = true;
1212 }
Alice Chuf6647eb2012-10-30 16:27:00 -07001213 if (*p == '#' || *p == '\0')
William Robertsf0e0a942012-08-27 15:41:15 -07001214 continue;
1215
1216 token = strtok_r(p, " \t", &saveptr);
1217 if (!token)
1218 goto err;
1219
1220 token_cnt = 0;
1221 memset(keys, 0, sizeof(kvp) * KVP_NUM_OF_RULES);
1222 while (1) {
William Roberts0ae3a8a2012-09-04 11:51:04 -07001223
William Robertsf0e0a942012-08-27 15:41:15 -07001224 name = token;
1225 value = strchr(name, '=');
1226 if (!value)
1227 goto err;
1228 *value++ = 0;
1229
1230 keys[token_cnt].key = strdup(name);
1231 if (!keys[token_cnt].key)
1232 goto oom;
1233
1234 keys[token_cnt].value = strdup(value);
1235 if (!keys[token_cnt].value)
1236 goto oom;
1237
1238 token_cnt++;
1239
1240 token = strtok_r(NULL, " \t", &saveptr);
1241 if (!token)
1242 break;
1243
liwugangeb74dd92018-11-15 20:13:15 +08001244 if (token_cnt == KVP_NUM_OF_RULES)
1245 goto oob;
1246
William Robertsf0e0a942012-08-27 15:41:15 -07001247 } /*End token parsing */
1248
William Roberts81e1f902015-06-03 21:57:47 -07001249 rule_map *r = rule_map_new(keys, token_cnt, lineno, in_file->name, is_never_allow);
Stephen Smalley534fb072015-02-13 14:06:08 -05001250 if (!r)
1251 goto err;
William Robertsf0e0a942012-08-27 15:41:15 -07001252 rule_add(r);
1253
1254 } /* End file parsing */
1255 return;
1256
1257err:
William Robertsefebf972016-01-29 10:34:04 -08001258 log_error("Reading file: \"%s\" line: %zu name: \"%s\" value: \"%s\"\n",
William Roberts81e1f902015-06-03 21:57:47 -07001259 in_file->name, lineno, name, value);
Alan Stokes3c4375a2020-11-11 14:45:32 +00001260 if (found_whitespace && name && !strcasecmp(name, "neverallow")) {
William Roberts81e1f902015-06-03 21:57:47 -07001261 log_error("perhaps whitespace before neverallow\n");
1262 }
William Robertsf0e0a942012-08-27 15:41:15 -07001263 exit(EXIT_FAILURE);
1264oom:
1265 log_error("In function %s: Out of memory\n", __FUNCTION__);
1266 exit(EXIT_FAILURE);
liwugangeb74dd92018-11-15 20:13:15 +08001267oob:
1268 log_error("Reading file: \"%s\" line: %zu reason: the size of key pairs exceeds the MAX(%zu)\n",
1269 in_file->name, lineno, KVP_NUM_OF_RULES);
1270 exit(EXIT_FAILURE);
William Robertsf0e0a942012-08-27 15:41:15 -07001271}
1272
1273/**
William Roberts81e1f902015-06-03 21:57:47 -07001274 * Parses the seapp_contexts file and neverallow file
1275 * and adds them to the hash table and ordered list entries
1276 * when it encounters them.
1277 * Calls exit on failure.
1278 */
1279static void parse() {
1280
1281 file_info *current;
1282 list_element *cursor;
1283 list_for_each(&input_file_list, cursor) {
1284 current = list_entry(cursor, typeof(*current), listify);
1285 parse_file(current);
1286 }
1287}
1288
1289static void validate() {
1290
1291 list_element *cursor, *v;
1292 bool found_issues = false;
1293 hash_entry *e;
1294 rule_map *r;
Inseob Kimd7d36092023-06-26 20:48:48 +09001295 coredomain_violation_entry *c;
William Roberts81e1f902015-06-03 21:57:47 -07001296 list_for_each(&line_order_list, cursor) {
1297 e = list_entry(cursor, typeof(*e), listify);
1298 rule_map_validate(e->r);
1299 }
1300
1301 list_for_each(&line_order_list, cursor) {
1302 e = list_entry(cursor, typeof(*e), listify);
1303 r = e->r;
1304 list_for_each(&r->violations, v) {
1305 found_issues = true;
1306 log_error("Rule in File \"%s\" on line %d: \"", e->r->filename, e->r->lineno);
1307 rule_map_print(stderr, e->r);
1308 r = list_entry(v, rule_map, listify);
1309 fprintf(stderr, "\" violates neverallow in File \"%s\" on line %d: \"", r->filename, r->lineno);
1310 rule_map_print(stderr, r);
1311 fprintf(stderr, "\"\n");
1312 }
1313 }
1314
Inseob Kime65098d2023-08-30 00:24:06 +00001315 bool coredomain_violation = false;
Inseob Kimd7d36092023-06-26 20:48:48 +09001316 list_for_each(&coredomain_violation_list, cursor) {
1317 c = list_entry(cursor, typeof(*c), listify);
1318 fprintf(stderr, "Forbidden attribute " COREDOMAIN " assigned to domain \"%s\" in "
Inseob Kime65098d2023-08-30 00:24:06 +00001319 "File \"%s\" on line %d\n", c->domain, c->filename, c->lineno);
1320 coredomain_violation = true;
1321 }
1322
1323 if (coredomain_violation) {
1324 fprintf(stderr, "********************************************************************************\n");
1325 fprintf(stderr, "You tried to assign coredomain with vendor seapp_contexts, which is not allowed.\n"
1326 "Either move offending entries to system, system_ext, or product seapp_contexts,\n"
1327 "or remove 'coredomain' attribute from the domains.\n"
1328 "See an example of how to fix this:\n"
1329 "https://android-review.googlesource.com/2671075\n");
1330 fprintf(stderr, "********************************************************************************\n");
1331 found_issues = true;
Inseob Kimd7d36092023-06-26 20:48:48 +09001332 }
1333
William Roberts81e1f902015-06-03 21:57:47 -07001334 if (found_issues) {
1335 exit(EXIT_FAILURE);
1336 }
1337}
1338
1339/**
William Robertsf0e0a942012-08-27 15:41:15 -07001340 * Should be called after parsing to cause the printing of the rule_maps
1341 * stored in the ordered list, head first, which preserves the "first encountered"
1342 * ordering.
1343 */
1344static void output() {
1345
William Roberts81e1f902015-06-03 21:57:47 -07001346 hash_entry *e;
1347 list_element *cursor;
William Robertsf0e0a942012-08-27 15:41:15 -07001348
William Roberts81e1f902015-06-03 21:57:47 -07001349 if (!out_file.file) {
1350 log_info("No output file, not outputting.\n");
1351 return;
1352 }
1353
1354 list_for_each(&line_order_list, cursor) {
1355 e = list_entry(cursor, hash_entry, listify);
1356 rule_map_print(out_file.file, e->r);
1357 fprintf(out_file.file, "\n");
William Robertsf0e0a942012-08-27 15:41:15 -07001358 }
1359}
1360
1361/**
1362 * This function is registered to the at exit handler and should clean up
1363 * the programs dynamic resources, such as memory and fd's.
1364 */
1365static void cleanup() {
1366
1367 /* Only close this when it was opened by me and not the crt */
William Roberts81e1f902015-06-03 21:57:47 -07001368 if (out_file.name && strcmp(out_file.name, "stdout") && out_file.file) {
1369 log_info("Closing file: %s\n", out_file.name);
1370 fclose(out_file.file);
William Robertsf0e0a942012-08-27 15:41:15 -07001371 }
1372
1373 if (pol.policy_file) {
1374
1375 log_info("Closing file: %s\n", pol.policy_file_name);
1376 fclose(pol.policy_file);
1377
1378 if (pol.db)
1379 sepol_policydb_free(pol.db);
1380
1381 if (pol.pf)
1382 sepol_policy_file_free(pol.pf);
1383
1384 if (pol.handle)
1385 sepol_handle_destroy(pol.handle);
1386 }
1387
William Roberts81e1f902015-06-03 21:57:47 -07001388 log_info("Freeing lists\n");
1389 list_free(&input_file_list);
1390 list_free(&line_order_list);
1391 list_free(&nallow_list);
Inseob Kimd7d36092023-06-26 20:48:48 +09001392 list_free(&coredomain_violation_list);
William Robertsf0e0a942012-08-27 15:41:15 -07001393 hdestroy();
1394}
1395
1396int main(int argc, char *argv[]) {
1397 if (!hcreate(TABLE_SIZE)) {
1398 log_error("Could not create hash table: %s\n", strerror(errno));
1399 exit(EXIT_FAILURE);
1400 }
1401 atexit(cleanup);
1402 handle_options(argc, argv);
1403 init();
1404 log_info("Starting to parse\n");
1405 parse();
1406 log_info("Parsing completed, generating output\n");
William Roberts81e1f902015-06-03 21:57:47 -07001407 validate();
William Robertsf0e0a942012-08-27 15:41:15 -07001408 output();
1409 log_info("Success, generated output\n");
1410 exit(EXIT_SUCCESS);
1411}