blob: 8001ceaf3ef51c60395da6a1d72dcbcb8b7f6e99 [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>
11#include <sepol/sepol.h>
12#include <sepol/policydb/policydb.h>
13
14#define TABLE_SIZE 1024
15#define KVP_NUM_OF_RULES (sizeof(rules) / sizeof(key_map))
16#define log_set_verbose() do { logging_verbose = 1; log_info("Enabling verbose\n"); } while(0)
17#define log_error(fmt, ...) log_msg(stderr, "Error: ", fmt, ##__VA_ARGS__)
18#define log_warn(fmt, ...) log_msg(stderr, "Warning: ", fmt, ##__VA_ARGS__)
19#define log_info(fmt, ...) if (logging_verbose ) { log_msg(stdout, "Info: ", fmt, ##__VA_ARGS__); }
20
21typedef struct line_order_list line_order_list;
22typedef struct hash_entry hash_entry;
23typedef enum key_dir key_dir;
24typedef enum data_type data_type;
25typedef enum rule_map_switch rule_map_switch;
William Roberts0ae3a8a2012-09-04 11:51:04 -070026typedef enum map_match map_match;
William Robertsf0e0a942012-08-27 15:41:15 -070027typedef struct key_map key_map;
28typedef struct kvp kvp;
29typedef struct rule_map rule_map;
30typedef struct policy_info policy_info;
31
William Roberts0ae3a8a2012-09-04 11:51:04 -070032enum map_match {
33 map_no_matches,
34 map_input_matched,
35 map_matched
36};
37
William Robertsf0e0a942012-08-27 15:41:15 -070038/**
39 * Whether or not the "key" from a key vaue pair is considered an
40 * input or an output.
41 */
42enum key_dir {
43 dir_in, dir_out
44};
45
46/**
47 * Used as options to rule_map_free()
48 *
49 * This is needed to get around the fact that GNU C's hash_map doesn't copy the key, so
50 * we cannot free a key when overrding rule_map's in the table.
51 */
52enum rule_map_switch {
53 rule_map_preserve_key, /** Used to preserve the key in the rule_map, ie don't free it*/
54 rule_map_destroy_key /** Used when you need a full free of the rule_map structure*/
55};
56
57/**
58 * The expected "type" of data the value in the key
59 * value pair should be.
60 */
61enum data_type {
62 dt_bool, dt_string
63};
64
65/**
66 * This list is used to store a double pointer to each
67 * hash table / line rule combination. This way a replacement
68 * in the hash table automatically updates the list. The list
69 * is also used to keep "first encountered" ordering amongst
70 * the encountered key value pairs in the rules file.
71 */
72struct line_order_list {
73 hash_entry *e;
74 line_order_list *next;
75};
76
77/**
78 * The workhorse of the logic. This struct maps key value pairs to
79 * an associated set of meta data maintained in rule_map_new()
80 */
81struct key_map {
82 char *name;
83 key_dir dir;
84 data_type type;
85 char *data;
86};
87
88/**
89 * Key value pair struct, this represents the raw kvp values coming
90 * from the rules files.
91 */
92struct kvp {
93 char *key;
94 char *value;
95};
96
97/**
98 * Rules are made up of meta data and an associated set of kvp stored in a
99 * key_map array.
100 */
101struct rule_map {
102 char *key; /** key value before hashing */
103 int length; /** length of the key map */
104 int lineno; /** Line number rule was encounter on */
105 rule_map *next; /** next pointer used in hash table for chaining on collision */
106 key_map m[]; /** key value mapping */
107};
108
109struct hash_entry {
110 rule_map *r; /** The rule map to store at that location */
111};
112
113/**
114 * Data associated for a policy file
115 */
116struct policy_info {
117
118 char *policy_file_name; /** policy file path name */
119 FILE *policy_file; /** file handle to the policy file */
120 sepol_policydb_t *db;
121 sepol_policy_file_t *pf;
122 sepol_handle_t *handle;
123 sepol_context_t *con;
124};
125
126/** Set to !0 to enable verbose logging */
127static int logging_verbose = 0;
128
129/** file handle to the output file */
130static FILE *output_file = NULL;
131
132/** file handle to the input file */
133static FILE *input_file = NULL;
134
135/** output file path name */
136static char *out_file_name = NULL;
137
138/** input file path name */
139static char *in_file_name = NULL;
140
141static policy_info pol = {
142 .policy_file_name = NULL,
143 .policy_file = NULL,
144 .db = NULL,
145 .pf = NULL,
146 .handle = NULL,
147 .con = NULL
148};
149
150/**
151 * The heart of the mapping process, this must be updated if a new key value pair is added
152 * to a rule.
153 */
154key_map rules[] = {
155 /*Inputs*/
156 { .name = "isSystemServer", .type = dt_bool, .dir = dir_in, .data = NULL },
157 { .name = "user", .type = dt_string, .dir = dir_in, .data = NULL },
158 { .name = "seinfo", .type = dt_string, .dir = dir_in, .data = NULL },
159 { .name = "name", .type = dt_string, .dir = dir_in, .data = NULL },
160 { .name = "sebool", .type = dt_string, .dir = dir_in, .data = NULL },
161 /*Outputs*/
162 { .name = "domain", .type = dt_string, .dir = dir_out, .data = NULL },
163 { .name = "type", .type = dt_string, .dir = dir_out, .data = NULL },
164 { .name = "levelFromUid", .type = dt_bool, .dir = dir_out, .data = NULL },
165 { .name = "level", .type = dt_string, .dir = dir_out, .data = NULL },
William Robertsfff29802012-11-27 14:20:34 -0800166};
William Robertsf0e0a942012-08-27 15:41:15 -0700167
168/**
169 * Head pointer to a linked list of
170 * rule map table entries, used for
171 * preserving the order of entries
172 * based on "first encounter"
173 */
174static line_order_list *list_head = NULL;
175
176/**
177 * Pointer to the tail of the list for
178 * quick appends to the end of the list
179 */
180static line_order_list *list_tail = NULL;
181
182/**
183 * Send a logging message to a file
184 * @param out
185 * Output file to send message too
186 * @param prefix
187 * A special prefix to write to the file, such as "Error:"
188 * @param fmt
189 * The printf style formatter to use, such as "%d"
190 */
191static void log_msg(FILE *out, const char *prefix, const char *fmt, ...) {
192 fprintf(out, "%s", prefix);
193 va_list args;
194 va_start(args, fmt);
195 vfprintf(out, fmt, args);
196 va_end(args);
197}
198
199/**
200 * Checks for a type in the policy.
201 * @param db
202 * The policy db to search
203 * @param type
204 * The type to search for
205 * @return
206 * 1 if the type is found, 0 otherwise.
207 * @warning
208 * This function always returns 1 if libsepol is not linked
209 * statically to this executable and LINK_SEPOL_STATIC is not
210 * defined.
211 */
212int check_type(sepol_policydb_t *db, char *type) {
213
214 int rc = 1;
215#if defined(LINK_SEPOL_STATIC)
216 policydb_t *d = (policydb_t *)db;
217 hashtab_datum_t dat;
218 dat = hashtab_search(d->p_types.table, type);
219 rc = (dat == NULL) ? 0 : 1;
220#endif
221 return rc;
222}
223
224/**
225 * Validates a key_map against a set of enforcement rules, this
226 * function exits the application on a type that cannot be properly
227 * checked
228 *
229 * @param m
230 * The key map to check
231 * @param lineno
232 * The line number in the source file for the corresponding key map
William Robertsfff29802012-11-27 14:20:34 -0800233 * @return
234 * 1 if valid, 0 if invalid
William Robertsf0e0a942012-08-27 15:41:15 -0700235 */
236static int key_map_validate(key_map *m, int lineno) {
237
238 int rc = 1;
239 int ret = 1;
William Robertsf0e0a942012-08-27 15:41:15 -0700240 int resp;
241 char *key = m->name;
242 char *value = m->data;
243 data_type type = m->type;
244 sepol_bool_key_t *se_key;
245
William Roberts0ae3a8a2012-09-04 11:51:04 -0700246 log_info("Validating %s=%s\n", key, value);
247
William Robertsf0e0a942012-08-27 15:41:15 -0700248 /* Booleans can always be checked for sanity */
249 if (type == dt_bool && (!strcmp("true", value) || !strcmp("false", value))) {
250 goto out;
251 }
252 else if (type == dt_bool) {
William Robertsae23a1f2012-09-05 12:53:52 -0700253 log_error("Expected boolean value got: %s=%s on line: %d in file: %s\n",
254 key, value, lineno, out_file_name);
William Robertsf0e0a942012-08-27 15:41:15 -0700255 rc = 0;
256 goto out;
257 }
258
259 /*
260 * If their is no policy file present,
261 * then it is not in strict mode so just return.
262 * User and name cannot really be checked.
263 */
264 if (!pol.policy_file) {
265 goto out;
266 }
267 else if (!strcasecmp(key, "sebool")) {
268
269 ret = sepol_bool_key_create(pol.handle, value, &se_key);
270 if (ret < 0) {
271 log_error("Could not create selinux boolean key, error: %s\n",
272 strerror(errno));
273 rc = 0;
274 goto out;
275 }
276
277 ret = sepol_bool_exists(pol.handle, pol.db, se_key, &resp);
278 if (ret < 0) {
279 log_error("Could not check selinux boolean, error: %s\n",
280 strerror(errno));
281 rc = 0;
William Robertsa53ccf32012-09-17 12:53:44 -0700282 sepol_bool_key_free(se_key);
283 goto out;
William Robertsf0e0a942012-08-27 15:41:15 -0700284 }
285
286 if(!resp) {
287 log_error("Could not find selinux boolean \"%s\" on line: %d in file: %s\n",
288 value, lineno, out_file_name);
289 rc = 0;
William Robertsa53ccf32012-09-17 12:53:44 -0700290 sepol_bool_key_free(se_key);
291 goto out;
William Robertsf0e0a942012-08-27 15:41:15 -0700292 }
William Robertsa53ccf32012-09-17 12:53:44 -0700293 sepol_bool_key_free(se_key);
William Robertsf0e0a942012-08-27 15:41:15 -0700294 }
295 else if (!strcasecmp(key, "type") || !strcasecmp(key, "domain")) {
296
297 if(!check_type(pol.db, value)) {
298 log_error("Could not find selinux type \"%s\" on line: %d in file: %s\n", value,
299 lineno, out_file_name);
300 rc = 0;
301 }
302 goto out;
303 }
William Robertsf0e0a942012-08-27 15:41:15 -0700304 else if (!strcasecmp(key, "level")) {
305
William Roberts0ae3a8a2012-09-04 11:51:04 -0700306 ret = sepol_mls_check(pol.handle, pol.db, value);
307 if (ret < 0) {
William Robertsae23a1f2012-09-05 12:53:52 -0700308 log_error("Could not find selinux level \"%s\", on line: %d in file: %s\n", value,
309 lineno, out_file_name);
William Roberts0ae3a8a2012-09-04 11:51:04 -0700310 rc = 0;
311 goto out;
William Robertsf0e0a942012-08-27 15:41:15 -0700312 }
313 }
314
William Roberts0ae3a8a2012-09-04 11:51:04 -0700315out:
316 log_info("Key map validate returning: %d\n", rc);
317 return rc;
William Robertsf0e0a942012-08-27 15:41:15 -0700318}
319
320/**
321 * Prints a rule map back to a file
322 * @param fp
323 * The file handle to print too
324 * @param r
325 * The rule map to print
326 */
327static void rule_map_print(FILE *fp, rule_map *r) {
328
329 int i;
330 key_map *m;
331
332 for (i = 0; i < r->length; i++) {
333 m = &(r->m[i]);
334 if (i < r->length - 1)
335 fprintf(fp, "%s=%s ", m->name, m->data);
336 else
337 fprintf(fp, "%s=%s", m->name, m->data);
338 }
339}
340
341/**
342 * Compare two rule maps for equality
343 * @param rmA
344 * a rule map to check
345 * @param rmB
346 * a rule map to check
347 * @return
William Robertsae23a1f2012-09-05 12:53:52 -0700348 * a map_match enum indicating the result
William Robertsf0e0a942012-08-27 15:41:15 -0700349 */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700350static map_match rule_map_cmp(rule_map *rmA, rule_map *rmB) {
William Robertsf0e0a942012-08-27 15:41:15 -0700351
352 int i;
353 int j;
354 int inputs_found = 0;
355 int num_of_matched_inputs = 0;
356 int input_mode = 0;
357 int matches = 0;
358 key_map *mA;
359 key_map *mB;
360
361 if (rmA->length != rmB->length)
William Roberts0ae3a8a2012-09-04 11:51:04 -0700362 return map_no_matches;
William Robertsf0e0a942012-08-27 15:41:15 -0700363
364 for (i = 0; i < rmA->length; i++) {
365 mA = &(rmA->m[i]);
366
367 for (j = 0; j < rmB->length; j++) {
368 mB = &(rmB->m[j]);
369 input_mode = 0;
370
371 if (mA->type != mB->type)
372 continue;
373
374 if (strcmp(mA->name, mB->name))
375 continue;
376
377 if (strcmp(mA->data, mB->data))
378 continue;
379
380 if (mB->dir != mA->dir)
381 continue;
382 else if (mB->dir == dir_in) {
383 input_mode = 1;
384 inputs_found++;
385 }
386
William Roberts0ae3a8a2012-09-04 11:51:04 -0700387 if (input_mode) {
388 log_info("Matched input lines: type=%s name=%s data=%s dir=%d\n", mA->type, mA->name, mA->data, mA->dir);
William Robertsf0e0a942012-08-27 15:41:15 -0700389 num_of_matched_inputs++;
William Roberts0ae3a8a2012-09-04 11:51:04 -0700390 }
William Robertsf0e0a942012-08-27 15:41:15 -0700391
392 /* Match found, move on */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700393 log_info("Matched lines: type=%s name=%s data=%s dir=%d\n", mA->type, mA->name, mA->data, mA->dir);
William Robertsf0e0a942012-08-27 15:41:15 -0700394 matches++;
395 break;
396 }
397 }
398
399 /* If they all matched*/
William Roberts0ae3a8a2012-09-04 11:51:04 -0700400 if (matches == rmA->length) {
401 log_info("Rule map cmp MATCH\n");
402 return map_matched;
403 }
William Robertsf0e0a942012-08-27 15:41:15 -0700404
405 /* They didn't all match but the input's did */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700406 else if (num_of_matched_inputs == inputs_found) {
407 log_info("Rule map cmp INPUT MATCH\n");
408 return map_input_matched;
409 }
William Robertsf0e0a942012-08-27 15:41:15 -0700410
411 /* They didn't all match, and the inputs didn't match, ie it didn't
412 * match */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700413 else {
414 log_info("Rule map cmp NO MATCH\n");
415 return map_no_matches;
416 }
William Robertsf0e0a942012-08-27 15:41:15 -0700417}
418
419/**
420 * Frees a rule map
421 * @param rm
422 * rule map to be freed.
423 */
424static void rule_map_free(rule_map *rm, rule_map_switch s) {
425
426 int i;
427 int len = rm->length;
428 for (i = 0; i < len; i++) {
429 key_map *m = &(rm->m[i]);
430 free(m->data);
431 }
432
rpcraig5dbfdc02012-10-23 11:03:47 -0400433/* hdestroy() frees comparsion keys for non glibc */
434#ifdef __GLIBC__
William Robertsf0e0a942012-08-27 15:41:15 -0700435 if(s == rule_map_destroy_key && rm->key)
436 free(rm->key);
rpcraig5dbfdc02012-10-23 11:03:47 -0400437#endif
William Robertsf0e0a942012-08-27 15:41:15 -0700438
439 free(rm);
440}
441
442static void free_kvp(kvp *k) {
443 free(k->key);
444 free(k->value);
445}
446
447/**
448 * Given a set of key value pairs, this will construct a new rule map.
449 * On error this function calls exit.
450 * @param keys
451 * Keys from a rule line to map
452 * @param num_of_keys
453 * The length of the keys array
454 * @param lineno
455 * The line number the keys were extracted from
456 * @return
457 * A rule map pointer.
458 */
459static rule_map *rule_map_new(kvp keys[], unsigned int num_of_keys, int lineno) {
460
461 unsigned int i = 0, j = 0;
462 rule_map *new_map = NULL;
463 kvp *k = NULL;
464 key_map *r = NULL, *x = NULL;
465
466 new_map = calloc(1, (num_of_keys * sizeof(key_map)) + sizeof(rule_map));
467 if (!new_map)
468 goto oom;
469
470 new_map->length = num_of_keys;
471 new_map->lineno = lineno;
472
473 /* For all the keys in a rule line*/
474 for (i = 0; i < num_of_keys; i++) {
475 k = &(keys[i]);
476 r = &(new_map->m[i]);
477
478 for (j = 0; j < KVP_NUM_OF_RULES; j++) {
479 x = &(rules[j]);
480
481 /* Only assign key name to map name */
482 if (strcasecmp(k->key, x->name)) {
483 if (i == KVP_NUM_OF_RULES) {
484 log_error("No match for key: %s\n", k->key);
485 goto err;
486 }
487 continue;
488 }
489
490 memcpy(r, x, sizeof(key_map));
491
492 /* Assign rule map value to one from file */
493 r->data = strdup(k->value);
494 if (!r->data)
495 goto oom;
496
497 /* Enforce type check*/
William Roberts0ae3a8a2012-09-04 11:51:04 -0700498 log_info("Validating keys!\n");
William Robertsf0e0a942012-08-27 15:41:15 -0700499 if (!key_map_validate(r, lineno)) {
500 log_error("Could not validate\n");
501 goto err;
502 }
503
504 /* Only build key off of inputs*/
505 if (r->dir == dir_in) {
506 char *tmp;
William Robertsb3ab56c2012-09-17 14:35:02 -0700507 int key_len = strlen(k->key);
508 int val_len = strlen(k->value);
509 int l = (new_map->key) ? strlen(new_map->key) : 0;
510 l = l + key_len + val_len;
William Robertsf0e0a942012-08-27 15:41:15 -0700511 l += 1;
512
513 tmp = realloc(new_map->key, l);
514 if (!tmp)
515 goto oom;
516
William Robertsb3ab56c2012-09-17 14:35:02 -0700517 if (!new_map->key)
518 memset(tmp, 0, l);
519
William Robertsf0e0a942012-08-27 15:41:15 -0700520 new_map->key = tmp;
521
William Robertsb3ab56c2012-09-17 14:35:02 -0700522 strncat(new_map->key, k->key, key_len);
523 strncat(new_map->key, k->value, val_len);
William Robertsf0e0a942012-08-27 15:41:15 -0700524 }
525 break;
526 }
527 free_kvp(k);
528 }
529
530 if (new_map->key == NULL) {
531 log_error("Strange, no keys found, input file corrupt perhaps?\n");
532 goto err;
533 }
534
535 return new_map;
536
537oom:
538 log_error("Out of memory!\n");
539err:
540 if(new_map) {
541 rule_map_free(new_map, rule_map_destroy_key);
542 for (; i < num_of_keys; i++) {
543 k = &(keys[i]);
544 free_kvp(k);
545 }
546 }
547 exit(EXIT_FAILURE);
548}
549
550/**
551 * Print the usage of the program
552 */
553static void usage() {
554 printf(
555 "checkseapp [options] <input file>\n"
556 "Processes an seapp_contexts file specified by argument <input file> (default stdin) "
William Robertsae23a1f2012-09-05 12:53:52 -0700557 "and allows later declarations to override previous ones on a match.\n"
William Robertsf0e0a942012-08-27 15:41:15 -0700558 "Options:\n"
559 "-h - print this help message\n"
560 "-v - enable verbose debugging informations\n"
561 "-p policy file - specify policy file for strict checking of output selectors\n"
562 "-o output file - specify output file, default is stdout\n");
563}
564
565static void init() {
566
567 /* If not set on stdin already */
568 if(!input_file) {
569 log_info("Opening input file: %s\n", in_file_name);
570 input_file = fopen(in_file_name, "r");
571 if (!input_file) {
572 log_error("Could not open file: %s error: %s\n", in_file_name, strerror(errno));
573 exit(EXIT_FAILURE);
574 }
575 }
576
577 /* If not set on std out already */
578 if(!output_file) {
579 output_file = fopen(out_file_name, "w+");
580 if (!output_file) {
581 log_error("Could not open file: %s error: %s\n", out_file_name, strerror(errno));
582 exit(EXIT_FAILURE);
583 }
584 }
585
586 if (pol.policy_file_name) {
587
588 log_info("Opening policy file: %s\n", pol.policy_file_name);
589 pol.policy_file = fopen(pol.policy_file_name, "rb");
590 if (!pol.policy_file) {
591 log_error("Could not open file: %s error: %s\n",
592 pol.policy_file_name, strerror(errno));
593 exit(EXIT_FAILURE);
594 }
595
596 pol.handle = sepol_handle_create();
597 if (!pol.handle) {
598 log_error("Could not create sepolicy handle: %s\n",
599 strerror(errno));
600 exit(EXIT_FAILURE);
601 }
602
603 if (sepol_policy_file_create(&pol.pf) < 0) {
604 log_error("Could not create sepolicy file: %s!\n",
605 strerror(errno));
606 exit(EXIT_FAILURE);
607 }
608
609 sepol_policy_file_set_fp(pol.pf, pol.policy_file);
610 sepol_policy_file_set_handle(pol.pf, pol.handle);
611
612 if (sepol_policydb_create(&pol.db) < 0) {
613 log_error("Could not create sepolicy db: %s!\n",
614 strerror(errno));
615 exit(EXIT_FAILURE);
616 }
617
618 if (sepol_policydb_read(pol.db, pol.pf) < 0) {
619 log_error("Could not lod policy file to db: %s!\n",
620 strerror(errno));
621 exit(EXIT_FAILURE);
622 }
623 }
624
625 log_info("Policy file set to: %s\n", (pol.policy_file_name == NULL) ? "None" : pol.policy_file_name);
626 log_info("Input file set to: %s\n", (in_file_name == NULL) ? "stdin" : in_file_name);
627 log_info("Output file set to: %s\n", (out_file_name == NULL) ? "stdout" : out_file_name);
628
William Roberts0ae3a8a2012-09-04 11:51:04 -0700629#if !defined(LINK_SEPOL_STATIC)
William Robertsa53ccf32012-09-17 12:53:44 -0700630 log_warn("LINK_SEPOL_STATIC is not defined\n""Not checking types!");
William Roberts0ae3a8a2012-09-04 11:51:04 -0700631#endif
632
William Robertsf0e0a942012-08-27 15:41:15 -0700633}
634
635/**
636 * Handle parsing and setting the global flags for the command line
637 * options. This function calls exit on failure.
638 * @param argc
639 * argument count
640 * @param argv
641 * argument list
642 */
643static void handle_options(int argc, char *argv[]) {
644
645 int c;
646 int num_of_args;
647
648 while ((c = getopt(argc, argv, "ho:p:v")) != -1) {
649 switch (c) {
650 case 'h':
651 usage();
652 exit(EXIT_SUCCESS);
653 case 'o':
654 out_file_name = optarg;
655 break;
656 case 'p':
657 pol.policy_file_name = optarg;
658 break;
659 case 'v':
660 log_set_verbose();
661 break;
662 case '?':
663 if (optopt == 'o' || optopt == 'p')
664 log_error("Option -%c requires an argument.\n", optopt);
665 else if (isprint (optopt))
666 log_error("Unknown option `-%c'.\n", optopt);
667 else {
668 log_error(
669 "Unknown option character `\\x%x'.\n",
670 optopt);
671 exit(EXIT_FAILURE);
672 }
673 break;
674 default:
675 exit(EXIT_FAILURE);
676 }
677 }
678
679 num_of_args = argc - optind;
680
681 if (num_of_args > 1) {
682 log_error("Too many arguments, expected 0 or 1, argument, got %d\n", num_of_args);
683 usage();
684 exit(EXIT_FAILURE);
685 } else if (num_of_args == 1) {
686 in_file_name = argv[argc - 1];
687 } else {
688 input_file = stdin;
689 in_file_name = "stdin";
690 }
691
692 if (!out_file_name) {
693 output_file = stdout;
694 out_file_name = "stdout";
695 }
696}
697
698/**
699 * Adds a rule_map double pointer, ie the hash table pointer to the list.
700 * By using a double pointer, the hash table can have a line be overridden
701 * and the value is updated in the list. This function calls exit on failure.
702 * @param rm
703 * the rule_map to add.
704 */
705static void list_add(hash_entry *e) {
706
707 line_order_list *node = malloc(sizeof(line_order_list));
708 if (node == NULL)
709 goto oom;
710
711 node->next = NULL;
712 node->e = e;
713
714 if (list_head == NULL)
715 list_head = list_tail = node;
716 else {
717 list_tail->next = node;
718 list_tail = list_tail->next;
719 }
720 return;
721
722oom:
723 log_error("Out of memory!\n");
724 exit(EXIT_FAILURE);
725}
726
727/**
728 * Free's the rule map list, which ultimatley contains
729 * all the malloc'd rule_maps.
730 */
731static void list_free() {
732 line_order_list *cursor, *tmp;
733 hash_entry *e;
734
735 cursor = list_head;
736 while (cursor) {
737 e = cursor->e;
738 rule_map_free(e->r, rule_map_destroy_key);
739 tmp = cursor;
740 cursor = cursor->next;
741 free(e);
742 free(tmp);
743 }
744}
745
746/**
747 * Adds a rule to the hash table and to the ordered list if needed.
748 * @param rm
749 * The rule map to add.
750 */
751static void rule_add(rule_map *rm) {
752
William Roberts0ae3a8a2012-09-04 11:51:04 -0700753 map_match cmp;
William Robertsf0e0a942012-08-27 15:41:15 -0700754 ENTRY e;
755 ENTRY *f;
756 hash_entry *entry;
757 hash_entry *tmp;
758 char *preserved_key;
759
760 e.key = rm->key;
761
William Roberts0ae3a8a2012-09-04 11:51:04 -0700762 log_info("Searching for key: %s\n", e.key);
William Robertsf0e0a942012-08-27 15:41:15 -0700763 /* Check to see if it has already been added*/
764 f = hsearch(e, FIND);
765
766 /*
767 * Since your only hashing on a partial key, the inputs we need to handle
768 * when you want to override the outputs for a given input set, as well as
769 * checking for duplicate entries.
770 */
771 if(f) {
William Roberts0ae3a8a2012-09-04 11:51:04 -0700772 log_info("Existing entry found!\n");
William Robertsf0e0a942012-08-27 15:41:15 -0700773 tmp = (hash_entry *)f->data;
774 cmp = rule_map_cmp(rm, tmp->r);
William Roberts0ae3a8a2012-09-04 11:51:04 -0700775 log_info("Comparing on rule map ret: %d\n", cmp);
William Robertsf0e0a942012-08-27 15:41:15 -0700776 /* Override be freeing the old rule map and updating
777 the pointer */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700778 if(cmp != map_matched) {
William Robertsf0e0a942012-08-27 15:41:15 -0700779
780 /*
781 * DO NOT free key pointers given to the hash map, instead
782 * free the new key. The ordering here is critical!
783 */
784 preserved_key = tmp->r->key;
785 rule_map_free(tmp->r, rule_map_preserve_key);
rpcraig5dbfdc02012-10-23 11:03:47 -0400786/* hdestroy() frees comparsion keys for non glibc */
787#ifdef __GLIBC__
William Robertsf0e0a942012-08-27 15:41:15 -0700788 free(rm->key);
rpcraig5dbfdc02012-10-23 11:03:47 -0400789#endif
William Robertsf0e0a942012-08-27 15:41:15 -0700790 rm->key = preserved_key;
791 tmp->r = rm;
792 }
793 /* Duplicate */
794 else {
795 log_error("Duplicate line detected in file: %s\n"
796 "Lines %d and %d match!\n",
797 out_file_name, tmp->r->lineno, rm->lineno);
798 rule_map_free(rm, rule_map_destroy_key);
799 goto err;
800 }
801 }
802 /* It wasn't found, just add the rule map to the table */
803 else {
804
805 entry = malloc(sizeof(hash_entry));
806 if (!entry)
807 goto oom;
808
809 entry->r = rm;
810 e.data = entry;
811
812 f = hsearch(e, ENTER);
813 if(f == NULL) {
814 goto oom;
815 }
816
817 /* new entries must be added to the ordered list */
818 entry->r = rm;
819 list_add(entry);
820 }
821
822 return;
823oom:
824 if (e.key)
825 free(e.key);
826 if (entry)
827 free(entry);
828 if (rm)
829 free(rm);
830 log_error("Out of memory in function: %s\n", __FUNCTION__);
831err:
832 exit(EXIT_FAILURE);
833}
834
835/**
836 * Parses the seapp_contexts file and adds them to the
837 * hash table and ordered list entries when it encounters them.
838 * Calls exit on failure.
839 */
840static void parse() {
841
842 char line_buf[BUFSIZ];
843 char *token;
844 unsigned lineno = 0;
845 char *p, *name = NULL, *value = NULL, *saveptr;
846 size_t len;
847 kvp keys[KVP_NUM_OF_RULES];
848 int token_cnt = 0;
849
850 while (fgets(line_buf, sizeof line_buf - 1, input_file)) {
851
852 lineno++;
853 log_info("Got line %d\n", lineno);
854 len = strlen(line_buf);
855 if (line_buf[len - 1] == '\n')
Alice Chuf6647eb2012-10-30 16:27:00 -0700856 line_buf[len - 1] = '\0';
William Robertsf0e0a942012-08-27 15:41:15 -0700857 p = line_buf;
858 while (isspace(*p))
859 p++;
Alice Chuf6647eb2012-10-30 16:27:00 -0700860 if (*p == '#' || *p == '\0')
William Robertsf0e0a942012-08-27 15:41:15 -0700861 continue;
862
863 token = strtok_r(p, " \t", &saveptr);
864 if (!token)
865 goto err;
866
867 token_cnt = 0;
868 memset(keys, 0, sizeof(kvp) * KVP_NUM_OF_RULES);
869 while (1) {
William Roberts0ae3a8a2012-09-04 11:51:04 -0700870
William Robertsf0e0a942012-08-27 15:41:15 -0700871 name = token;
872 value = strchr(name, '=');
873 if (!value)
874 goto err;
875 *value++ = 0;
876
877 keys[token_cnt].key = strdup(name);
878 if (!keys[token_cnt].key)
879 goto oom;
880
881 keys[token_cnt].value = strdup(value);
882 if (!keys[token_cnt].value)
883 goto oom;
884
885 token_cnt++;
886
887 token = strtok_r(NULL, " \t", &saveptr);
888 if (!token)
889 break;
890
891 } /*End token parsing */
892
893 rule_map *r = rule_map_new(keys, token_cnt, lineno);
894 rule_add(r);
895
896 } /* End file parsing */
897 return;
898
899err:
900 log_error("reading %s, line %u, name %s, value %s\n",
901 in_file_name, lineno, name, value);
902 exit(EXIT_FAILURE);
903oom:
904 log_error("In function %s: Out of memory\n", __FUNCTION__);
905 exit(EXIT_FAILURE);
906}
907
908/**
909 * Should be called after parsing to cause the printing of the rule_maps
910 * stored in the ordered list, head first, which preserves the "first encountered"
911 * ordering.
912 */
913static void output() {
914
915 rule_map *r;
916 line_order_list *cursor;
917 cursor = list_head;
918
919 while (cursor) {
920 r = cursor->e->r;
921 rule_map_print(output_file, r);
922 cursor = cursor->next;
William Robertsa8613182012-09-05 11:23:40 -0700923 fprintf(output_file, "\n");
William Robertsf0e0a942012-08-27 15:41:15 -0700924 }
925}
926
927/**
928 * This function is registered to the at exit handler and should clean up
929 * the programs dynamic resources, such as memory and fd's.
930 */
931static void cleanup() {
932
933 /* Only close this when it was opened by me and not the crt */
934 if (out_file_name && output_file) {
935 log_info("Closing file: %s\n", out_file_name);
936 fclose(output_file);
937 }
938
939 /* Only close this when it was opened by me and not the crt */
940 if (in_file_name && input_file) {
941 log_info("Closing file: %s\n", in_file_name);
942 fclose(input_file);
943 }
944
945 if (pol.policy_file) {
946
947 log_info("Closing file: %s\n", pol.policy_file_name);
948 fclose(pol.policy_file);
949
950 if (pol.db)
951 sepol_policydb_free(pol.db);
952
953 if (pol.pf)
954 sepol_policy_file_free(pol.pf);
955
956 if (pol.handle)
957 sepol_handle_destroy(pol.handle);
958 }
959
960 log_info("Freeing list\n");
961 list_free();
962 hdestroy();
963}
964
965int main(int argc, char *argv[]) {
966 if (!hcreate(TABLE_SIZE)) {
967 log_error("Could not create hash table: %s\n", strerror(errno));
968 exit(EXIT_FAILURE);
969 }
970 atexit(cleanup);
971 handle_options(argc, argv);
972 init();
973 log_info("Starting to parse\n");
974 parse();
975 log_info("Parsing completed, generating output\n");
976 output();
977 log_info("Success, generated output\n");
978 exit(EXIT_SUCCESS);
979}