blob: 55c80dfa176ba7d01805def9f42343ad877c7395 [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 },
Stephen Smalley38084142012-11-28 10:46:18 -0500165 { .name = "levelFrom", .type = dt_string, .dir = dir_out, .data = NULL },
William Robertsf0e0a942012-08-27 15:41:15 -0700166 { .name = "level", .type = dt_string, .dir = dir_out, .data = NULL },
William Robertsfff29802012-11-27 14:20:34 -0800167};
William Robertsf0e0a942012-08-27 15:41:15 -0700168
169/**
170 * Head pointer to a linked list of
171 * rule map table entries, used for
172 * preserving the order of entries
173 * based on "first encounter"
174 */
175static line_order_list *list_head = NULL;
176
177/**
178 * Pointer to the tail of the list for
179 * quick appends to the end of the list
180 */
181static line_order_list *list_tail = NULL;
182
183/**
184 * Send a logging message to a file
185 * @param out
186 * Output file to send message too
187 * @param prefix
188 * A special prefix to write to the file, such as "Error:"
189 * @param fmt
190 * The printf style formatter to use, such as "%d"
191 */
William Roberts1e8c0612013-04-19 19:06:02 -0700192static void __attribute__ ((format(printf, 3, 4)))
193log_msg(FILE *out, const char *prefix, const char *fmt, ...) {
194
William Robertsf0e0a942012-08-27 15:41:15 -0700195 fprintf(out, "%s", prefix);
196 va_list args;
197 va_start(args, fmt);
198 vfprintf(out, fmt, args);
199 va_end(args);
200}
201
202/**
203 * Checks for a type in the policy.
204 * @param db
205 * The policy db to search
206 * @param type
207 * The type to search for
208 * @return
209 * 1 if the type is found, 0 otherwise.
210 * @warning
211 * This function always returns 1 if libsepol is not linked
212 * statically to this executable and LINK_SEPOL_STATIC is not
213 * defined.
214 */
215int check_type(sepol_policydb_t *db, char *type) {
216
217 int rc = 1;
218#if defined(LINK_SEPOL_STATIC)
219 policydb_t *d = (policydb_t *)db;
220 hashtab_datum_t dat;
221 dat = hashtab_search(d->p_types.table, type);
222 rc = (dat == NULL) ? 0 : 1;
223#endif
224 return rc;
225}
226
227/**
228 * Validates a key_map against a set of enforcement rules, this
229 * function exits the application on a type that cannot be properly
230 * checked
231 *
232 * @param m
233 * The key map to check
234 * @param lineno
235 * The line number in the source file for the corresponding key map
William Robertsfff29802012-11-27 14:20:34 -0800236 * @return
237 * 1 if valid, 0 if invalid
William Robertsf0e0a942012-08-27 15:41:15 -0700238 */
239static int key_map_validate(key_map *m, int lineno) {
240
241 int rc = 1;
242 int ret = 1;
William Robertsf0e0a942012-08-27 15:41:15 -0700243 int resp;
244 char *key = m->name;
245 char *value = m->data;
246 data_type type = m->type;
247 sepol_bool_key_t *se_key;
248
William Roberts0ae3a8a2012-09-04 11:51:04 -0700249 log_info("Validating %s=%s\n", key, value);
250
William Robertsf0e0a942012-08-27 15:41:15 -0700251 /* Booleans can always be checked for sanity */
252 if (type == dt_bool && (!strcmp("true", value) || !strcmp("false", value))) {
253 goto out;
254 }
255 else if (type == dt_bool) {
William Robertsae23a1f2012-09-05 12:53:52 -0700256 log_error("Expected boolean value got: %s=%s on line: %d in file: %s\n",
257 key, value, lineno, out_file_name);
William Robertsf0e0a942012-08-27 15:41:15 -0700258 rc = 0;
259 goto out;
260 }
261
Stephen Smalley38084142012-11-28 10:46:18 -0500262 if (!strcasecmp(key, "levelFrom") &&
263 (strcasecmp(value, "none") && strcasecmp(value, "all") &&
264 strcasecmp(value, "app") && strcasecmp(value, "user"))) {
265 log_error("Unknown levelFrom=%s on line: %d in file: %s\n",
266 value, lineno, out_file_name);
267 rc = 0;
268 goto out;
269 }
270
William Robertsf0e0a942012-08-27 15:41:15 -0700271 /*
272 * If their is no policy file present,
273 * then it is not in strict mode so just return.
274 * User and name cannot really be checked.
275 */
276 if (!pol.policy_file) {
277 goto out;
278 }
279 else if (!strcasecmp(key, "sebool")) {
280
281 ret = sepol_bool_key_create(pol.handle, value, &se_key);
282 if (ret < 0) {
283 log_error("Could not create selinux boolean key, error: %s\n",
284 strerror(errno));
285 rc = 0;
286 goto out;
287 }
288
289 ret = sepol_bool_exists(pol.handle, pol.db, se_key, &resp);
290 if (ret < 0) {
291 log_error("Could not check selinux boolean, error: %s\n",
292 strerror(errno));
293 rc = 0;
William Robertsa53ccf32012-09-17 12:53:44 -0700294 sepol_bool_key_free(se_key);
295 goto out;
William Robertsf0e0a942012-08-27 15:41:15 -0700296 }
297
298 if(!resp) {
299 log_error("Could not find selinux boolean \"%s\" on line: %d in file: %s\n",
300 value, lineno, out_file_name);
301 rc = 0;
William Robertsa53ccf32012-09-17 12:53:44 -0700302 sepol_bool_key_free(se_key);
303 goto out;
William Robertsf0e0a942012-08-27 15:41:15 -0700304 }
William Robertsa53ccf32012-09-17 12:53:44 -0700305 sepol_bool_key_free(se_key);
William Robertsf0e0a942012-08-27 15:41:15 -0700306 }
307 else if (!strcasecmp(key, "type") || !strcasecmp(key, "domain")) {
308
309 if(!check_type(pol.db, value)) {
310 log_error("Could not find selinux type \"%s\" on line: %d in file: %s\n", value,
311 lineno, out_file_name);
312 rc = 0;
313 }
314 goto out;
315 }
William Robertsf0e0a942012-08-27 15:41:15 -0700316 else if (!strcasecmp(key, "level")) {
317
William Roberts0ae3a8a2012-09-04 11:51:04 -0700318 ret = sepol_mls_check(pol.handle, pol.db, value);
319 if (ret < 0) {
William Robertsae23a1f2012-09-05 12:53:52 -0700320 log_error("Could not find selinux level \"%s\", on line: %d in file: %s\n", value,
321 lineno, out_file_name);
William Roberts0ae3a8a2012-09-04 11:51:04 -0700322 rc = 0;
323 goto out;
William Robertsf0e0a942012-08-27 15:41:15 -0700324 }
325 }
326
William Roberts0ae3a8a2012-09-04 11:51:04 -0700327out:
328 log_info("Key map validate returning: %d\n", rc);
329 return rc;
William Robertsf0e0a942012-08-27 15:41:15 -0700330}
331
332/**
333 * Prints a rule map back to a file
334 * @param fp
335 * The file handle to print too
336 * @param r
337 * The rule map to print
338 */
339static void rule_map_print(FILE *fp, rule_map *r) {
340
341 int i;
342 key_map *m;
343
344 for (i = 0; i < r->length; i++) {
345 m = &(r->m[i]);
346 if (i < r->length - 1)
347 fprintf(fp, "%s=%s ", m->name, m->data);
348 else
349 fprintf(fp, "%s=%s", m->name, m->data);
350 }
351}
352
353/**
354 * Compare two rule maps for equality
355 * @param rmA
356 * a rule map to check
357 * @param rmB
358 * a rule map to check
359 * @return
William Robertsae23a1f2012-09-05 12:53:52 -0700360 * a map_match enum indicating the result
William Robertsf0e0a942012-08-27 15:41:15 -0700361 */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700362static map_match rule_map_cmp(rule_map *rmA, rule_map *rmB) {
William Robertsf0e0a942012-08-27 15:41:15 -0700363
364 int i;
365 int j;
366 int inputs_found = 0;
367 int num_of_matched_inputs = 0;
368 int input_mode = 0;
369 int matches = 0;
370 key_map *mA;
371 key_map *mB;
372
373 if (rmA->length != rmB->length)
William Roberts0ae3a8a2012-09-04 11:51:04 -0700374 return map_no_matches;
William Robertsf0e0a942012-08-27 15:41:15 -0700375
376 for (i = 0; i < rmA->length; i++) {
377 mA = &(rmA->m[i]);
378
379 for (j = 0; j < rmB->length; j++) {
380 mB = &(rmB->m[j]);
381 input_mode = 0;
382
383 if (mA->type != mB->type)
384 continue;
385
386 if (strcmp(mA->name, mB->name))
387 continue;
388
389 if (strcmp(mA->data, mB->data))
390 continue;
391
392 if (mB->dir != mA->dir)
393 continue;
394 else if (mB->dir == dir_in) {
395 input_mode = 1;
396 inputs_found++;
397 }
398
William Roberts0ae3a8a2012-09-04 11:51:04 -0700399 if (input_mode) {
William Roberts1e8c0612013-04-19 19:06:02 -0700400 log_info("Matched input lines: name=%s data=%s\n", mA->name, mA->data);
William Robertsf0e0a942012-08-27 15:41:15 -0700401 num_of_matched_inputs++;
William Roberts0ae3a8a2012-09-04 11:51:04 -0700402 }
William Robertsf0e0a942012-08-27 15:41:15 -0700403
404 /* Match found, move on */
William Roberts1e8c0612013-04-19 19:06:02 -0700405 log_info("Matched lines: name=%s data=%s", mA->name, mA->data);
William Robertsf0e0a942012-08-27 15:41:15 -0700406 matches++;
407 break;
408 }
409 }
410
411 /* If they all matched*/
William Roberts0ae3a8a2012-09-04 11:51:04 -0700412 if (matches == rmA->length) {
413 log_info("Rule map cmp MATCH\n");
414 return map_matched;
415 }
William Robertsf0e0a942012-08-27 15:41:15 -0700416
417 /* They didn't all match but the input's did */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700418 else if (num_of_matched_inputs == inputs_found) {
419 log_info("Rule map cmp INPUT MATCH\n");
420 return map_input_matched;
421 }
William Robertsf0e0a942012-08-27 15:41:15 -0700422
423 /* They didn't all match, and the inputs didn't match, ie it didn't
424 * match */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700425 else {
426 log_info("Rule map cmp NO MATCH\n");
427 return map_no_matches;
428 }
William Robertsf0e0a942012-08-27 15:41:15 -0700429}
430
431/**
432 * Frees a rule map
433 * @param rm
434 * rule map to be freed.
435 */
436static void rule_map_free(rule_map *rm, rule_map_switch s) {
437
438 int i;
439 int len = rm->length;
440 for (i = 0; i < len; i++) {
441 key_map *m = &(rm->m[i]);
442 free(m->data);
443 }
444
rpcraig5dbfdc02012-10-23 11:03:47 -0400445/* hdestroy() frees comparsion keys for non glibc */
446#ifdef __GLIBC__
William Robertsf0e0a942012-08-27 15:41:15 -0700447 if(s == rule_map_destroy_key && rm->key)
448 free(rm->key);
rpcraig5dbfdc02012-10-23 11:03:47 -0400449#endif
William Robertsf0e0a942012-08-27 15:41:15 -0700450
451 free(rm);
452}
453
454static void free_kvp(kvp *k) {
455 free(k->key);
456 free(k->value);
457}
458
459/**
460 * Given a set of key value pairs, this will construct a new rule map.
461 * On error this function calls exit.
462 * @param keys
463 * Keys from a rule line to map
464 * @param num_of_keys
465 * The length of the keys array
466 * @param lineno
467 * The line number the keys were extracted from
468 * @return
469 * A rule map pointer.
470 */
471static rule_map *rule_map_new(kvp keys[], unsigned int num_of_keys, int lineno) {
472
473 unsigned int i = 0, j = 0;
474 rule_map *new_map = NULL;
475 kvp *k = NULL;
476 key_map *r = NULL, *x = NULL;
477
478 new_map = calloc(1, (num_of_keys * sizeof(key_map)) + sizeof(rule_map));
479 if (!new_map)
480 goto oom;
481
482 new_map->length = num_of_keys;
483 new_map->lineno = lineno;
484
485 /* For all the keys in a rule line*/
486 for (i = 0; i < num_of_keys; i++) {
487 k = &(keys[i]);
488 r = &(new_map->m[i]);
489
490 for (j = 0; j < KVP_NUM_OF_RULES; j++) {
491 x = &(rules[j]);
492
493 /* Only assign key name to map name */
494 if (strcasecmp(k->key, x->name)) {
495 if (i == KVP_NUM_OF_RULES) {
496 log_error("No match for key: %s\n", k->key);
497 goto err;
498 }
499 continue;
500 }
501
502 memcpy(r, x, sizeof(key_map));
503
504 /* Assign rule map value to one from file */
505 r->data = strdup(k->value);
506 if (!r->data)
507 goto oom;
508
509 /* Enforce type check*/
William Roberts0ae3a8a2012-09-04 11:51:04 -0700510 log_info("Validating keys!\n");
William Robertsf0e0a942012-08-27 15:41:15 -0700511 if (!key_map_validate(r, lineno)) {
512 log_error("Could not validate\n");
513 goto err;
514 }
515
516 /* Only build key off of inputs*/
517 if (r->dir == dir_in) {
518 char *tmp;
William Robertsb3ab56c2012-09-17 14:35:02 -0700519 int key_len = strlen(k->key);
520 int val_len = strlen(k->value);
521 int l = (new_map->key) ? strlen(new_map->key) : 0;
522 l = l + key_len + val_len;
William Robertsf0e0a942012-08-27 15:41:15 -0700523 l += 1;
524
525 tmp = realloc(new_map->key, l);
526 if (!tmp)
527 goto oom;
528
William Robertsb3ab56c2012-09-17 14:35:02 -0700529 if (!new_map->key)
530 memset(tmp, 0, l);
531
William Robertsf0e0a942012-08-27 15:41:15 -0700532 new_map->key = tmp;
533
William Robertsb3ab56c2012-09-17 14:35:02 -0700534 strncat(new_map->key, k->key, key_len);
535 strncat(new_map->key, k->value, val_len);
William Robertsf0e0a942012-08-27 15:41:15 -0700536 }
537 break;
538 }
539 free_kvp(k);
540 }
541
542 if (new_map->key == NULL) {
543 log_error("Strange, no keys found, input file corrupt perhaps?\n");
544 goto err;
545 }
546
547 return new_map;
548
549oom:
550 log_error("Out of memory!\n");
551err:
552 if(new_map) {
553 rule_map_free(new_map, rule_map_destroy_key);
554 for (; i < num_of_keys; i++) {
555 k = &(keys[i]);
556 free_kvp(k);
557 }
558 }
559 exit(EXIT_FAILURE);
560}
561
562/**
563 * Print the usage of the program
564 */
565static void usage() {
566 printf(
567 "checkseapp [options] <input file>\n"
568 "Processes an seapp_contexts file specified by argument <input file> (default stdin) "
William Robertsae23a1f2012-09-05 12:53:52 -0700569 "and allows later declarations to override previous ones on a match.\n"
William Robertsf0e0a942012-08-27 15:41:15 -0700570 "Options:\n"
571 "-h - print this help message\n"
572 "-v - enable verbose debugging informations\n"
573 "-p policy file - specify policy file for strict checking of output selectors\n"
574 "-o output file - specify output file, default is stdout\n");
575}
576
577static void init() {
578
579 /* If not set on stdin already */
580 if(!input_file) {
581 log_info("Opening input file: %s\n", in_file_name);
582 input_file = fopen(in_file_name, "r");
583 if (!input_file) {
584 log_error("Could not open file: %s error: %s\n", in_file_name, strerror(errno));
585 exit(EXIT_FAILURE);
586 }
587 }
588
589 /* If not set on std out already */
590 if(!output_file) {
591 output_file = fopen(out_file_name, "w+");
592 if (!output_file) {
593 log_error("Could not open file: %s error: %s\n", out_file_name, strerror(errno));
594 exit(EXIT_FAILURE);
595 }
596 }
597
598 if (pol.policy_file_name) {
599
600 log_info("Opening policy file: %s\n", pol.policy_file_name);
601 pol.policy_file = fopen(pol.policy_file_name, "rb");
602 if (!pol.policy_file) {
603 log_error("Could not open file: %s error: %s\n",
604 pol.policy_file_name, strerror(errno));
605 exit(EXIT_FAILURE);
606 }
607
608 pol.handle = sepol_handle_create();
609 if (!pol.handle) {
610 log_error("Could not create sepolicy handle: %s\n",
611 strerror(errno));
612 exit(EXIT_FAILURE);
613 }
614
615 if (sepol_policy_file_create(&pol.pf) < 0) {
616 log_error("Could not create sepolicy file: %s!\n",
617 strerror(errno));
618 exit(EXIT_FAILURE);
619 }
620
621 sepol_policy_file_set_fp(pol.pf, pol.policy_file);
622 sepol_policy_file_set_handle(pol.pf, pol.handle);
623
624 if (sepol_policydb_create(&pol.db) < 0) {
625 log_error("Could not create sepolicy db: %s!\n",
626 strerror(errno));
627 exit(EXIT_FAILURE);
628 }
629
630 if (sepol_policydb_read(pol.db, pol.pf) < 0) {
631 log_error("Could not lod policy file to db: %s!\n",
632 strerror(errno));
633 exit(EXIT_FAILURE);
634 }
635 }
636
637 log_info("Policy file set to: %s\n", (pol.policy_file_name == NULL) ? "None" : pol.policy_file_name);
638 log_info("Input file set to: %s\n", (in_file_name == NULL) ? "stdin" : in_file_name);
639 log_info("Output file set to: %s\n", (out_file_name == NULL) ? "stdout" : out_file_name);
640
William Roberts0ae3a8a2012-09-04 11:51:04 -0700641#if !defined(LINK_SEPOL_STATIC)
William Robertsa53ccf32012-09-17 12:53:44 -0700642 log_warn("LINK_SEPOL_STATIC is not defined\n""Not checking types!");
William Roberts0ae3a8a2012-09-04 11:51:04 -0700643#endif
644
William Robertsf0e0a942012-08-27 15:41:15 -0700645}
646
647/**
648 * Handle parsing and setting the global flags for the command line
649 * options. This function calls exit on failure.
650 * @param argc
651 * argument count
652 * @param argv
653 * argument list
654 */
655static void handle_options(int argc, char *argv[]) {
656
657 int c;
658 int num_of_args;
659
660 while ((c = getopt(argc, argv, "ho:p:v")) != -1) {
661 switch (c) {
662 case 'h':
663 usage();
664 exit(EXIT_SUCCESS);
665 case 'o':
666 out_file_name = optarg;
667 break;
668 case 'p':
669 pol.policy_file_name = optarg;
670 break;
671 case 'v':
672 log_set_verbose();
673 break;
674 case '?':
675 if (optopt == 'o' || optopt == 'p')
676 log_error("Option -%c requires an argument.\n", optopt);
677 else if (isprint (optopt))
678 log_error("Unknown option `-%c'.\n", optopt);
679 else {
680 log_error(
681 "Unknown option character `\\x%x'.\n",
682 optopt);
683 exit(EXIT_FAILURE);
684 }
685 break;
686 default:
687 exit(EXIT_FAILURE);
688 }
689 }
690
691 num_of_args = argc - optind;
692
693 if (num_of_args > 1) {
694 log_error("Too many arguments, expected 0 or 1, argument, got %d\n", num_of_args);
695 usage();
696 exit(EXIT_FAILURE);
697 } else if (num_of_args == 1) {
698 in_file_name = argv[argc - 1];
699 } else {
700 input_file = stdin;
701 in_file_name = "stdin";
702 }
703
704 if (!out_file_name) {
705 output_file = stdout;
706 out_file_name = "stdout";
707 }
708}
709
710/**
711 * Adds a rule_map double pointer, ie the hash table pointer to the list.
712 * By using a double pointer, the hash table can have a line be overridden
713 * and the value is updated in the list. This function calls exit on failure.
714 * @param rm
715 * the rule_map to add.
716 */
717static void list_add(hash_entry *e) {
718
719 line_order_list *node = malloc(sizeof(line_order_list));
720 if (node == NULL)
721 goto oom;
722
723 node->next = NULL;
724 node->e = e;
725
726 if (list_head == NULL)
727 list_head = list_tail = node;
728 else {
729 list_tail->next = node;
730 list_tail = list_tail->next;
731 }
732 return;
733
734oom:
735 log_error("Out of memory!\n");
736 exit(EXIT_FAILURE);
737}
738
739/**
740 * Free's the rule map list, which ultimatley contains
741 * all the malloc'd rule_maps.
742 */
743static void list_free() {
744 line_order_list *cursor, *tmp;
745 hash_entry *e;
746
747 cursor = list_head;
748 while (cursor) {
749 e = cursor->e;
750 rule_map_free(e->r, rule_map_destroy_key);
751 tmp = cursor;
752 cursor = cursor->next;
753 free(e);
754 free(tmp);
755 }
756}
757
758/**
759 * Adds a rule to the hash table and to the ordered list if needed.
760 * @param rm
761 * The rule map to add.
762 */
763static void rule_add(rule_map *rm) {
764
William Roberts0ae3a8a2012-09-04 11:51:04 -0700765 map_match cmp;
William Robertsf0e0a942012-08-27 15:41:15 -0700766 ENTRY e;
767 ENTRY *f;
768 hash_entry *entry;
769 hash_entry *tmp;
770 char *preserved_key;
771
772 e.key = rm->key;
773
William Roberts0ae3a8a2012-09-04 11:51:04 -0700774 log_info("Searching for key: %s\n", e.key);
William Robertsf0e0a942012-08-27 15:41:15 -0700775 /* Check to see if it has already been added*/
776 f = hsearch(e, FIND);
777
778 /*
779 * Since your only hashing on a partial key, the inputs we need to handle
780 * when you want to override the outputs for a given input set, as well as
781 * checking for duplicate entries.
782 */
783 if(f) {
William Roberts0ae3a8a2012-09-04 11:51:04 -0700784 log_info("Existing entry found!\n");
William Robertsf0e0a942012-08-27 15:41:15 -0700785 tmp = (hash_entry *)f->data;
786 cmp = rule_map_cmp(rm, tmp->r);
William Roberts0ae3a8a2012-09-04 11:51:04 -0700787 log_info("Comparing on rule map ret: %d\n", cmp);
William Robertsf0e0a942012-08-27 15:41:15 -0700788 /* Override be freeing the old rule map and updating
789 the pointer */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700790 if(cmp != map_matched) {
William Robertsf0e0a942012-08-27 15:41:15 -0700791
792 /*
793 * DO NOT free key pointers given to the hash map, instead
794 * free the new key. The ordering here is critical!
795 */
796 preserved_key = tmp->r->key;
797 rule_map_free(tmp->r, rule_map_preserve_key);
rpcraig5dbfdc02012-10-23 11:03:47 -0400798/* hdestroy() frees comparsion keys for non glibc */
799#ifdef __GLIBC__
William Robertsf0e0a942012-08-27 15:41:15 -0700800 free(rm->key);
rpcraig5dbfdc02012-10-23 11:03:47 -0400801#endif
William Robertsf0e0a942012-08-27 15:41:15 -0700802 rm->key = preserved_key;
803 tmp->r = rm;
804 }
805 /* Duplicate */
806 else {
807 log_error("Duplicate line detected in file: %s\n"
808 "Lines %d and %d match!\n",
809 out_file_name, tmp->r->lineno, rm->lineno);
810 rule_map_free(rm, rule_map_destroy_key);
811 goto err;
812 }
813 }
814 /* It wasn't found, just add the rule map to the table */
815 else {
816
817 entry = malloc(sizeof(hash_entry));
818 if (!entry)
819 goto oom;
820
821 entry->r = rm;
822 e.data = entry;
823
824 f = hsearch(e, ENTER);
825 if(f == NULL) {
826 goto oom;
827 }
828
829 /* new entries must be added to the ordered list */
830 entry->r = rm;
831 list_add(entry);
832 }
833
834 return;
835oom:
836 if (e.key)
837 free(e.key);
838 if (entry)
839 free(entry);
840 if (rm)
841 free(rm);
842 log_error("Out of memory in function: %s\n", __FUNCTION__);
843err:
844 exit(EXIT_FAILURE);
845}
846
847/**
848 * Parses the seapp_contexts file and adds them to the
849 * hash table and ordered list entries when it encounters them.
850 * Calls exit on failure.
851 */
852static void parse() {
853
854 char line_buf[BUFSIZ];
855 char *token;
856 unsigned lineno = 0;
857 char *p, *name = NULL, *value = NULL, *saveptr;
858 size_t len;
859 kvp keys[KVP_NUM_OF_RULES];
860 int token_cnt = 0;
861
862 while (fgets(line_buf, sizeof line_buf - 1, input_file)) {
863
864 lineno++;
865 log_info("Got line %d\n", lineno);
866 len = strlen(line_buf);
867 if (line_buf[len - 1] == '\n')
Alice Chuf6647eb2012-10-30 16:27:00 -0700868 line_buf[len - 1] = '\0';
William Robertsf0e0a942012-08-27 15:41:15 -0700869 p = line_buf;
870 while (isspace(*p))
871 p++;
Alice Chuf6647eb2012-10-30 16:27:00 -0700872 if (*p == '#' || *p == '\0')
William Robertsf0e0a942012-08-27 15:41:15 -0700873 continue;
874
875 token = strtok_r(p, " \t", &saveptr);
876 if (!token)
877 goto err;
878
879 token_cnt = 0;
880 memset(keys, 0, sizeof(kvp) * KVP_NUM_OF_RULES);
881 while (1) {
William Roberts0ae3a8a2012-09-04 11:51:04 -0700882
William Robertsf0e0a942012-08-27 15:41:15 -0700883 name = token;
884 value = strchr(name, '=');
885 if (!value)
886 goto err;
887 *value++ = 0;
888
889 keys[token_cnt].key = strdup(name);
890 if (!keys[token_cnt].key)
891 goto oom;
892
893 keys[token_cnt].value = strdup(value);
894 if (!keys[token_cnt].value)
895 goto oom;
896
897 token_cnt++;
898
899 token = strtok_r(NULL, " \t", &saveptr);
900 if (!token)
901 break;
902
903 } /*End token parsing */
904
905 rule_map *r = rule_map_new(keys, token_cnt, lineno);
906 rule_add(r);
907
908 } /* End file parsing */
909 return;
910
911err:
912 log_error("reading %s, line %u, name %s, value %s\n",
913 in_file_name, lineno, name, value);
914 exit(EXIT_FAILURE);
915oom:
916 log_error("In function %s: Out of memory\n", __FUNCTION__);
917 exit(EXIT_FAILURE);
918}
919
920/**
921 * Should be called after parsing to cause the printing of the rule_maps
922 * stored in the ordered list, head first, which preserves the "first encountered"
923 * ordering.
924 */
925static void output() {
926
927 rule_map *r;
928 line_order_list *cursor;
929 cursor = list_head;
930
931 while (cursor) {
932 r = cursor->e->r;
933 rule_map_print(output_file, r);
934 cursor = cursor->next;
William Robertsa8613182012-09-05 11:23:40 -0700935 fprintf(output_file, "\n");
William Robertsf0e0a942012-08-27 15:41:15 -0700936 }
937}
938
939/**
940 * This function is registered to the at exit handler and should clean up
941 * the programs dynamic resources, such as memory and fd's.
942 */
943static void cleanup() {
944
945 /* Only close this when it was opened by me and not the crt */
946 if (out_file_name && output_file) {
947 log_info("Closing file: %s\n", out_file_name);
948 fclose(output_file);
949 }
950
951 /* Only close this when it was opened by me and not the crt */
952 if (in_file_name && input_file) {
953 log_info("Closing file: %s\n", in_file_name);
954 fclose(input_file);
955 }
956
957 if (pol.policy_file) {
958
959 log_info("Closing file: %s\n", pol.policy_file_name);
960 fclose(pol.policy_file);
961
962 if (pol.db)
963 sepol_policydb_free(pol.db);
964
965 if (pol.pf)
966 sepol_policy_file_free(pol.pf);
967
968 if (pol.handle)
969 sepol_handle_destroy(pol.handle);
970 }
971
972 log_info("Freeing list\n");
973 list_free();
974 hdestroy();
975}
976
977int main(int argc, char *argv[]) {
978 if (!hcreate(TABLE_SIZE)) {
979 log_error("Could not create hash table: %s\n", strerror(errno));
980 exit(EXIT_FAILURE);
981 }
982 atexit(cleanup);
983 handle_options(argc, argv);
984 init();
985 log_info("Starting to parse\n");
986 parse();
987 log_info("Parsing completed, generating output\n");
988 output();
989 log_info("Success, generated output\n");
990 exit(EXIT_SUCCESS);
991}