blob: 10c97e08b486c240155b967f49988539351bdee4 [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>
14
15#define TABLE_SIZE 1024
16#define KVP_NUM_OF_RULES (sizeof(rules) / sizeof(key_map))
17#define log_set_verbose() do { logging_verbose = 1; log_info("Enabling verbose\n"); } while(0)
18#define log_error(fmt, ...) log_msg(stderr, "Error: ", fmt, ##__VA_ARGS__)
19#define log_warn(fmt, ...) log_msg(stderr, "Warning: ", fmt, ##__VA_ARGS__)
20#define log_info(fmt, ...) if (logging_verbose ) { log_msg(stdout, "Info: ", fmt, ##__VA_ARGS__); }
21
22typedef struct line_order_list line_order_list;
23typedef struct hash_entry hash_entry;
24typedef enum key_dir key_dir;
25typedef enum data_type data_type;
26typedef enum rule_map_switch rule_map_switch;
William Roberts0ae3a8a2012-09-04 11:51:04 -070027typedef enum map_match map_match;
William Robertsf0e0a942012-08-27 15:41:15 -070028typedef struct key_map key_map;
29typedef struct kvp kvp;
30typedef struct rule_map rule_map;
31typedef struct policy_info policy_info;
32
William Roberts0ae3a8a2012-09-04 11:51:04 -070033enum map_match {
34 map_no_matches,
35 map_input_matched,
36 map_matched
37};
38
Stephen Smalley0b820042015-02-13 14:58:31 -050039const char *map_match_str[] = {
40 "do not match",
41 "match on all inputs",
42 "match on everything"
43};
44
William Robertsf0e0a942012-08-27 15:41:15 -070045/**
46 * Whether or not the "key" from a key vaue pair is considered an
47 * input or an output.
48 */
49enum key_dir {
50 dir_in, dir_out
51};
52
53/**
William Robertsf0e0a942012-08-27 15:41:15 -070054 * The expected "type" of data the value in the key
55 * value pair should be.
56 */
57enum data_type {
58 dt_bool, dt_string
59};
60
61/**
62 * This list is used to store a double pointer to each
63 * hash table / line rule combination. This way a replacement
64 * in the hash table automatically updates the list. The list
65 * is also used to keep "first encountered" ordering amongst
66 * the encountered key value pairs in the rules file.
67 */
68struct line_order_list {
69 hash_entry *e;
70 line_order_list *next;
71};
72
73/**
74 * The workhorse of the logic. This struct maps key value pairs to
75 * an associated set of meta data maintained in rule_map_new()
76 */
77struct key_map {
78 char *name;
79 key_dir dir;
80 data_type type;
81 char *data;
82};
83
84/**
85 * Key value pair struct, this represents the raw kvp values coming
86 * from the rules files.
87 */
88struct kvp {
89 char *key;
90 char *value;
91};
92
93/**
94 * Rules are made up of meta data and an associated set of kvp stored in a
95 * key_map array.
96 */
97struct rule_map {
98 char *key; /** key value before hashing */
William Roberts610a4b12013-10-15 18:26:00 -070099 size_t length; /** length of the key map */
William Robertsf0e0a942012-08-27 15:41:15 -0700100 int lineno; /** Line number rule was encounter on */
William Robertsf0e0a942012-08-27 15:41:15 -0700101 key_map m[]; /** key value mapping */
102};
103
104struct hash_entry {
105 rule_map *r; /** The rule map to store at that location */
106};
107
108/**
109 * Data associated for a policy file
110 */
111struct policy_info {
112
113 char *policy_file_name; /** policy file path name */
114 FILE *policy_file; /** file handle to the policy file */
115 sepol_policydb_t *db;
116 sepol_policy_file_t *pf;
117 sepol_handle_t *handle;
118 sepol_context_t *con;
119};
120
121/** Set to !0 to enable verbose logging */
122static int logging_verbose = 0;
123
124/** file handle to the output file */
125static FILE *output_file = NULL;
126
127/** file handle to the input file */
128static FILE *input_file = NULL;
129
130/** output file path name */
131static char *out_file_name = NULL;
132
133/** input file path name */
134static char *in_file_name = NULL;
135
136static policy_info pol = {
137 .policy_file_name = NULL,
138 .policy_file = NULL,
139 .db = NULL,
140 .pf = NULL,
141 .handle = NULL,
142 .con = NULL
143};
144
145/**
146 * The heart of the mapping process, this must be updated if a new key value pair is added
147 * to a rule.
148 */
149key_map rules[] = {
150 /*Inputs*/
151 { .name = "isSystemServer", .type = dt_bool, .dir = dir_in, .data = NULL },
Stephen Smalleyff4db912014-09-15 15:16:06 -0400152 { .name = "isOwner", .type = dt_bool, .dir = dir_in, .data = NULL },
William Robertsf0e0a942012-08-27 15:41:15 -0700153 { .name = "user", .type = dt_string, .dir = dir_in, .data = NULL },
154 { .name = "seinfo", .type = dt_string, .dir = dir_in, .data = NULL },
155 { .name = "name", .type = dt_string, .dir = dir_in, .data = NULL },
Stephen Smalley6139de52014-02-19 10:54:41 -0500156 { .name = "path", .type = dt_string, .dir = dir_in, .data = NULL },
William Robertsf0e0a942012-08-27 15:41:15 -0700157 /*Outputs*/
158 { .name = "domain", .type = dt_string, .dir = dir_out, .data = NULL },
159 { .name = "type", .type = dt_string, .dir = dir_out, .data = NULL },
160 { .name = "levelFromUid", .type = dt_bool, .dir = dir_out, .data = NULL },
Stephen Smalley38084142012-11-28 10:46:18 -0500161 { .name = "levelFrom", .type = dt_string, .dir = dir_out, .data = NULL },
William Robertsf0e0a942012-08-27 15:41:15 -0700162 { .name = "level", .type = dt_string, .dir = dir_out, .data = NULL },
William Robertsfff29802012-11-27 14:20:34 -0800163};
William Robertsf0e0a942012-08-27 15:41:15 -0700164
165/**
166 * Head pointer to a linked list of
167 * rule map table entries, used for
168 * preserving the order of entries
169 * based on "first encounter"
170 */
171static line_order_list *list_head = NULL;
172
173/**
174 * Pointer to the tail of the list for
175 * quick appends to the end of the list
176 */
177static line_order_list *list_tail = NULL;
178
179/**
180 * Send a logging message to a file
181 * @param out
182 * Output file to send message too
183 * @param prefix
184 * A special prefix to write to the file, such as "Error:"
185 * @param fmt
186 * The printf style formatter to use, such as "%d"
187 */
William Roberts1e8c0612013-04-19 19:06:02 -0700188static void __attribute__ ((format(printf, 3, 4)))
189log_msg(FILE *out, const char *prefix, const char *fmt, ...) {
190
William Robertsf0e0a942012-08-27 15:41:15 -0700191 fprintf(out, "%s", prefix);
192 va_list args;
193 va_start(args, fmt);
194 vfprintf(out, fmt, args);
195 va_end(args);
196}
197
198/**
199 * Checks for a type in the policy.
200 * @param db
201 * The policy db to search
202 * @param type
203 * The type to search for
204 * @return
205 * 1 if the type is found, 0 otherwise.
206 * @warning
207 * This function always returns 1 if libsepol is not linked
208 * statically to this executable and LINK_SEPOL_STATIC is not
209 * defined.
210 */
211int check_type(sepol_policydb_t *db, char *type) {
212
213 int rc = 1;
214#if defined(LINK_SEPOL_STATIC)
215 policydb_t *d = (policydb_t *)db;
216 hashtab_datum_t dat;
217 dat = hashtab_search(d->p_types.table, type);
218 rc = (dat == NULL) ? 0 : 1;
219#endif
220 return rc;
221}
222
223/**
224 * Validates a key_map against a set of enforcement rules, this
225 * function exits the application on a type that cannot be properly
226 * checked
227 *
228 * @param m
229 * The key map to check
230 * @param lineno
231 * The line number in the source file for the corresponding key map
William Robertsfff29802012-11-27 14:20:34 -0800232 * @return
233 * 1 if valid, 0 if invalid
William Robertsf0e0a942012-08-27 15:41:15 -0700234 */
235static int key_map_validate(key_map *m, int lineno) {
236
237 int rc = 1;
238 int ret = 1;
William Robertsf0e0a942012-08-27 15:41:15 -0700239 char *key = m->name;
240 char *value = m->data;
241 data_type type = m->type;
William Robertsf0e0a942012-08-27 15:41:15 -0700242
William Roberts0ae3a8a2012-09-04 11:51:04 -0700243 log_info("Validating %s=%s\n", key, value);
244
William Robertsf0e0a942012-08-27 15:41:15 -0700245 /* Booleans can always be checked for sanity */
246 if (type == dt_bool && (!strcmp("true", value) || !strcmp("false", value))) {
247 goto out;
248 }
249 else if (type == dt_bool) {
William Robertsae23a1f2012-09-05 12:53:52 -0700250 log_error("Expected boolean value got: %s=%s on line: %d in file: %s\n",
William Roberts8d3a1b52015-06-19 09:12:01 -0700251 key, value, lineno, in_file_name);
William Robertsf0e0a942012-08-27 15:41:15 -0700252 rc = 0;
253 goto out;
254 }
255
Stephen Smalley38084142012-11-28 10:46:18 -0500256 if (!strcasecmp(key, "levelFrom") &&
257 (strcasecmp(value, "none") && strcasecmp(value, "all") &&
258 strcasecmp(value, "app") && strcasecmp(value, "user"))) {
259 log_error("Unknown levelFrom=%s on line: %d in file: %s\n",
William Roberts8d3a1b52015-06-19 09:12:01 -0700260 value, lineno, in_file_name);
Stephen Smalley38084142012-11-28 10:46:18 -0500261 rc = 0;
262 goto out;
263 }
264
William Robertsf0e0a942012-08-27 15:41:15 -0700265 /*
William Roberts63297212013-04-19 19:06:23 -0700266 * If there is no policy file present,
267 * then it is not going to enforce the types against the policy so just return.
William Robertsf0e0a942012-08-27 15:41:15 -0700268 * User and name cannot really be checked.
269 */
270 if (!pol.policy_file) {
271 goto out;
272 }
William Robertsf0e0a942012-08-27 15:41:15 -0700273 else if (!strcasecmp(key, "type") || !strcasecmp(key, "domain")) {
274
275 if(!check_type(pol.db, value)) {
276 log_error("Could not find selinux type \"%s\" on line: %d in file: %s\n", value,
William Roberts8d3a1b52015-06-19 09:12:01 -0700277 lineno, in_file_name);
William Robertsf0e0a942012-08-27 15:41:15 -0700278 rc = 0;
279 }
280 goto out;
281 }
William Robertsf0e0a942012-08-27 15:41:15 -0700282 else if (!strcasecmp(key, "level")) {
283
William Roberts0ae3a8a2012-09-04 11:51:04 -0700284 ret = sepol_mls_check(pol.handle, pol.db, value);
285 if (ret < 0) {
William Robertsae23a1f2012-09-05 12:53:52 -0700286 log_error("Could not find selinux level \"%s\", on line: %d in file: %s\n", value,
William Roberts8d3a1b52015-06-19 09:12:01 -0700287 lineno, in_file_name);
William Roberts0ae3a8a2012-09-04 11:51:04 -0700288 rc = 0;
289 goto out;
William Robertsf0e0a942012-08-27 15:41:15 -0700290 }
291 }
292
William Roberts0ae3a8a2012-09-04 11:51:04 -0700293out:
294 log_info("Key map validate returning: %d\n", rc);
295 return rc;
William Robertsf0e0a942012-08-27 15:41:15 -0700296}
297
298/**
299 * Prints a rule map back to a file
300 * @param fp
301 * The file handle to print too
302 * @param r
303 * The rule map to print
304 */
305static void rule_map_print(FILE *fp, rule_map *r) {
306
William Roberts610a4b12013-10-15 18:26:00 -0700307 size_t i;
William Robertsf0e0a942012-08-27 15:41:15 -0700308 key_map *m;
309
310 for (i = 0; i < r->length; i++) {
311 m = &(r->m[i]);
312 if (i < r->length - 1)
313 fprintf(fp, "%s=%s ", m->name, m->data);
314 else
315 fprintf(fp, "%s=%s", m->name, m->data);
316 }
317}
318
319/**
320 * Compare two rule maps for equality
321 * @param rmA
322 * a rule map to check
323 * @param rmB
324 * a rule map to check
325 * @return
William Robertsae23a1f2012-09-05 12:53:52 -0700326 * a map_match enum indicating the result
William Robertsf0e0a942012-08-27 15:41:15 -0700327 */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700328static map_match rule_map_cmp(rule_map *rmA, rule_map *rmB) {
William Robertsf0e0a942012-08-27 15:41:15 -0700329
William Roberts610a4b12013-10-15 18:26:00 -0700330 size_t i;
331 size_t j;
William Robertsf0e0a942012-08-27 15:41:15 -0700332 int inputs_found = 0;
333 int num_of_matched_inputs = 0;
334 int input_mode = 0;
William Roberts610a4b12013-10-15 18:26:00 -0700335 size_t matches = 0;
William Robertsf0e0a942012-08-27 15:41:15 -0700336 key_map *mA;
337 key_map *mB;
338
William Robertsf0e0a942012-08-27 15:41:15 -0700339 for (i = 0; i < rmA->length; i++) {
340 mA = &(rmA->m[i]);
341
342 for (j = 0; j < rmB->length; j++) {
343 mB = &(rmB->m[j]);
344 input_mode = 0;
345
346 if (mA->type != mB->type)
347 continue;
348
349 if (strcmp(mA->name, mB->name))
350 continue;
351
352 if (strcmp(mA->data, mB->data))
353 continue;
354
355 if (mB->dir != mA->dir)
356 continue;
357 else if (mB->dir == dir_in) {
358 input_mode = 1;
359 inputs_found++;
360 }
361
William Roberts0ae3a8a2012-09-04 11:51:04 -0700362 if (input_mode) {
William Roberts1e8c0612013-04-19 19:06:02 -0700363 log_info("Matched input lines: name=%s data=%s\n", mA->name, mA->data);
William Robertsf0e0a942012-08-27 15:41:15 -0700364 num_of_matched_inputs++;
William Roberts0ae3a8a2012-09-04 11:51:04 -0700365 }
William Robertsf0e0a942012-08-27 15:41:15 -0700366
367 /* Match found, move on */
William Roberts1e8c0612013-04-19 19:06:02 -0700368 log_info("Matched lines: name=%s data=%s", mA->name, mA->data);
William Robertsf0e0a942012-08-27 15:41:15 -0700369 matches++;
370 break;
371 }
372 }
373
374 /* If they all matched*/
William Roberts0ae3a8a2012-09-04 11:51:04 -0700375 if (matches == rmA->length) {
376 log_info("Rule map cmp MATCH\n");
377 return map_matched;
378 }
William Robertsf0e0a942012-08-27 15:41:15 -0700379
380 /* They didn't all match but the input's did */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700381 else if (num_of_matched_inputs == inputs_found) {
382 log_info("Rule map cmp INPUT MATCH\n");
383 return map_input_matched;
384 }
William Robertsf0e0a942012-08-27 15:41:15 -0700385
386 /* They didn't all match, and the inputs didn't match, ie it didn't
387 * match */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700388 else {
389 log_info("Rule map cmp NO MATCH\n");
390 return map_no_matches;
391 }
William Robertsf0e0a942012-08-27 15:41:15 -0700392}
393
394/**
395 * Frees a rule map
396 * @param rm
397 * rule map to be freed.
398 */
William Roberts7d65b542015-06-19 09:43:28 -0700399/**
400 * Frees a rule map
401 * @param rm
402 * rule map to be freed.
403 * @is_in_htable
404 * True if the rule map has been added to the hash table, false
405 * otherwise.
406 */
407static void rule_map_free(rule_map *rm, bool is_in_htable) {
William Robertsf0e0a942012-08-27 15:41:15 -0700408
William Roberts610a4b12013-10-15 18:26:00 -0700409 size_t i;
410 size_t len = rm->length;
William Robertsf0e0a942012-08-27 15:41:15 -0700411 for (i = 0; i < len; i++) {
412 key_map *m = &(rm->m[i]);
413 free(m->data);
414 }
415
William Roberts7d65b542015-06-19 09:43:28 -0700416 /*
417 * hdestroy() frees comparsion keys for non glibc
418 * on GLIBC we always free on NON-GLIBC we free if
419 * it is not in the htable.
420 */
421 if (rm->key) {
rpcraig5dbfdc02012-10-23 11:03:47 -0400422#ifdef __GLIBC__
William Roberts7d65b542015-06-19 09:43:28 -0700423 /* silence unused warning */
424 (void)is_in_htable;
William Robertsf0e0a942012-08-27 15:41:15 -0700425 free(rm->key);
William Roberts7d65b542015-06-19 09:43:28 -0700426#else
427 if (!is_in_htable) {
428 free(rm->key);
429 }
rpcraig5dbfdc02012-10-23 11:03:47 -0400430#endif
William Roberts7d65b542015-06-19 09:43:28 -0700431 }
William Robertsf0e0a942012-08-27 15:41:15 -0700432
433 free(rm);
434}
435
436static void free_kvp(kvp *k) {
437 free(k->key);
438 free(k->value);
439}
440
441/**
William Roberts61846292013-10-15 09:38:24 -0700442 * Checks a rule_map for any variation of KVP's that shouldn't be allowed.
443 * Note that this function logs all errors.
444 *
445 * Current Checks:
446 * 1. That a specified name entry should have a specified seinfo entry as well.
447 * @param rm
448 * The rule map to check for validity.
449 * @return
450 * true if the rule is valid, false otherwise.
451 */
452static bool rule_map_validate(const rule_map *rm) {
453
William Roberts610a4b12013-10-15 18:26:00 -0700454 size_t i;
William Roberts61846292013-10-15 09:38:24 -0700455 bool found_name = false;
456 bool found_seinfo = false;
457 char *name = NULL;
Stephen Smalley7b2bee92013-10-31 09:22:26 -0400458 const key_map *tmp;
William Roberts61846292013-10-15 09:38:24 -0700459
460 for(i=0; i < rm->length; i++) {
461 tmp = &(rm->m[i]);
462
463 if(!strcmp(tmp->name, "name") && tmp->data) {
464 name = tmp->data;
465 found_name = true;
466 }
Stephen Smalleyf4fa7562014-04-04 14:16:46 -0400467 if(!strcmp(tmp->name, "seinfo") && tmp->data && strcmp(tmp->data, "default")) {
William Roberts61846292013-10-15 09:38:24 -0700468 found_seinfo = true;
469 }
470 }
471
472 if(found_name && !found_seinfo) {
Stephen Smalleyf4fa7562014-04-04 14:16:46 -0400473 log_error("No specific seinfo value specified with name=\"%s\", on line: %d: insecure configuration!\n",
William Roberts61846292013-10-15 09:38:24 -0700474 name, rm->lineno);
475 return false;
476 }
477
478 return true;
479}
480
481/**
William Robertsf0e0a942012-08-27 15:41:15 -0700482 * Given a set of key value pairs, this will construct a new rule map.
483 * On error this function calls exit.
484 * @param keys
485 * Keys from a rule line to map
486 * @param num_of_keys
487 * The length of the keys array
488 * @param lineno
489 * The line number the keys were extracted from
490 * @return
491 * A rule map pointer.
492 */
William Roberts610a4b12013-10-15 18:26:00 -0700493static rule_map *rule_map_new(kvp keys[], size_t num_of_keys, int lineno) {
William Robertsf0e0a942012-08-27 15:41:15 -0700494
William Roberts610a4b12013-10-15 18:26:00 -0700495 size_t i = 0, j = 0;
William Roberts61846292013-10-15 09:38:24 -0700496 bool valid_rule;
William Robertsf0e0a942012-08-27 15:41:15 -0700497 rule_map *new_map = NULL;
498 kvp *k = NULL;
499 key_map *r = NULL, *x = NULL;
Stephen Smalley534fb072015-02-13 14:06:08 -0500500 bool seen[KVP_NUM_OF_RULES];
501
502 for (i = 0; i < KVP_NUM_OF_RULES; i++)
503 seen[i] = false;
William Robertsf0e0a942012-08-27 15:41:15 -0700504
505 new_map = calloc(1, (num_of_keys * sizeof(key_map)) + sizeof(rule_map));
506 if (!new_map)
507 goto oom;
508
509 new_map->length = num_of_keys;
510 new_map->lineno = lineno;
511
512 /* For all the keys in a rule line*/
513 for (i = 0; i < num_of_keys; i++) {
514 k = &(keys[i]);
515 r = &(new_map->m[i]);
516
517 for (j = 0; j < KVP_NUM_OF_RULES; j++) {
518 x = &(rules[j]);
519
520 /* Only assign key name to map name */
521 if (strcasecmp(k->key, x->name)) {
522 if (i == KVP_NUM_OF_RULES) {
523 log_error("No match for key: %s\n", k->key);
524 goto err;
525 }
526 continue;
527 }
528
Stephen Smalley534fb072015-02-13 14:06:08 -0500529 if (seen[j]) {
530 log_error("Duplicated key: %s\n", k->key);
531 goto err;
532 }
533 seen[j] = true;
534
William Robertsf0e0a942012-08-27 15:41:15 -0700535 memcpy(r, x, sizeof(key_map));
536
537 /* Assign rule map value to one from file */
538 r->data = strdup(k->value);
539 if (!r->data)
540 goto oom;
541
542 /* Enforce type check*/
William Roberts0ae3a8a2012-09-04 11:51:04 -0700543 log_info("Validating keys!\n");
William Robertsf0e0a942012-08-27 15:41:15 -0700544 if (!key_map_validate(r, lineno)) {
545 log_error("Could not validate\n");
546 goto err;
547 }
548
549 /* Only build key off of inputs*/
550 if (r->dir == dir_in) {
551 char *tmp;
William Robertsb3ab56c2012-09-17 14:35:02 -0700552 int key_len = strlen(k->key);
553 int val_len = strlen(k->value);
554 int l = (new_map->key) ? strlen(new_map->key) : 0;
555 l = l + key_len + val_len;
William Robertsf0e0a942012-08-27 15:41:15 -0700556 l += 1;
557
558 tmp = realloc(new_map->key, l);
559 if (!tmp)
560 goto oom;
561
William Robertsb3ab56c2012-09-17 14:35:02 -0700562 if (!new_map->key)
563 memset(tmp, 0, l);
564
William Robertsf0e0a942012-08-27 15:41:15 -0700565 new_map->key = tmp;
566
William Robertsb3ab56c2012-09-17 14:35:02 -0700567 strncat(new_map->key, k->key, key_len);
568 strncat(new_map->key, k->value, val_len);
William Robertsf0e0a942012-08-27 15:41:15 -0700569 }
570 break;
571 }
572 free_kvp(k);
573 }
574
575 if (new_map->key == NULL) {
576 log_error("Strange, no keys found, input file corrupt perhaps?\n");
577 goto err;
578 }
579
William Roberts61846292013-10-15 09:38:24 -0700580 valid_rule = rule_map_validate(new_map);
581 if(!valid_rule) {
582 /* Error message logged from rule_map_validate() */
583 goto err;
584 }
585
William Robertsf0e0a942012-08-27 15:41:15 -0700586 return new_map;
587
588oom:
589 log_error("Out of memory!\n");
590err:
591 if(new_map) {
William Roberts7d65b542015-06-19 09:43:28 -0700592 rule_map_free(new_map, false);
William Robertsf0e0a942012-08-27 15:41:15 -0700593 for (; i < num_of_keys; i++) {
594 k = &(keys[i]);
595 free_kvp(k);
596 }
597 }
Stephen Smalley534fb072015-02-13 14:06:08 -0500598 return NULL;
William Robertsf0e0a942012-08-27 15:41:15 -0700599}
600
601/**
602 * Print the usage of the program
603 */
604static void usage() {
605 printf(
606 "checkseapp [options] <input file>\n"
607 "Processes an seapp_contexts file specified by argument <input file> (default stdin) "
William Robertsae23a1f2012-09-05 12:53:52 -0700608 "and allows later declarations to override previous ones on a match.\n"
William Robertsf0e0a942012-08-27 15:41:15 -0700609 "Options:\n"
610 "-h - print this help message\n"
611 "-v - enable verbose debugging informations\n"
William Roberts63297212013-04-19 19:06:23 -0700612 "-p policy file - specify policy file for strict checking of output selectors against the policy\n"
William Robertsf0e0a942012-08-27 15:41:15 -0700613 "-o output file - specify output file, default is stdout\n");
614}
615
616static void init() {
617
618 /* If not set on stdin already */
619 if(!input_file) {
620 log_info("Opening input file: %s\n", in_file_name);
621 input_file = fopen(in_file_name, "r");
622 if (!input_file) {
623 log_error("Could not open file: %s error: %s\n", in_file_name, strerror(errno));
624 exit(EXIT_FAILURE);
625 }
626 }
627
628 /* If not set on std out already */
629 if(!output_file) {
630 output_file = fopen(out_file_name, "w+");
631 if (!output_file) {
632 log_error("Could not open file: %s error: %s\n", out_file_name, strerror(errno));
633 exit(EXIT_FAILURE);
634 }
635 }
636
637 if (pol.policy_file_name) {
638
639 log_info("Opening policy file: %s\n", pol.policy_file_name);
640 pol.policy_file = fopen(pol.policy_file_name, "rb");
641 if (!pol.policy_file) {
642 log_error("Could not open file: %s error: %s\n",
643 pol.policy_file_name, strerror(errno));
644 exit(EXIT_FAILURE);
645 }
646
647 pol.handle = sepol_handle_create();
648 if (!pol.handle) {
649 log_error("Could not create sepolicy handle: %s\n",
650 strerror(errno));
651 exit(EXIT_FAILURE);
652 }
653
654 if (sepol_policy_file_create(&pol.pf) < 0) {
655 log_error("Could not create sepolicy file: %s!\n",
656 strerror(errno));
657 exit(EXIT_FAILURE);
658 }
659
660 sepol_policy_file_set_fp(pol.pf, pol.policy_file);
661 sepol_policy_file_set_handle(pol.pf, pol.handle);
662
663 if (sepol_policydb_create(&pol.db) < 0) {
664 log_error("Could not create sepolicy db: %s!\n",
665 strerror(errno));
666 exit(EXIT_FAILURE);
667 }
668
669 if (sepol_policydb_read(pol.db, pol.pf) < 0) {
670 log_error("Could not lod policy file to db: %s!\n",
671 strerror(errno));
672 exit(EXIT_FAILURE);
673 }
674 }
675
676 log_info("Policy file set to: %s\n", (pol.policy_file_name == NULL) ? "None" : pol.policy_file_name);
677 log_info("Input file set to: %s\n", (in_file_name == NULL) ? "stdin" : in_file_name);
678 log_info("Output file set to: %s\n", (out_file_name == NULL) ? "stdout" : out_file_name);
679
William Roberts0ae3a8a2012-09-04 11:51:04 -0700680#if !defined(LINK_SEPOL_STATIC)
William Robertsa53ccf32012-09-17 12:53:44 -0700681 log_warn("LINK_SEPOL_STATIC is not defined\n""Not checking types!");
William Roberts0ae3a8a2012-09-04 11:51:04 -0700682#endif
683
William Robertsf0e0a942012-08-27 15:41:15 -0700684}
685
686/**
687 * Handle parsing and setting the global flags for the command line
688 * options. This function calls exit on failure.
689 * @param argc
690 * argument count
691 * @param argv
692 * argument list
693 */
694static void handle_options(int argc, char *argv[]) {
695
696 int c;
697 int num_of_args;
698
William Robertsf26b6d42015-06-23 10:22:45 -0700699 while ((c = getopt(argc, argv, "ho:p:v")) != -1) {
William Robertsf0e0a942012-08-27 15:41:15 -0700700 switch (c) {
701 case 'h':
702 usage();
703 exit(EXIT_SUCCESS);
704 case 'o':
705 out_file_name = optarg;
706 break;
707 case 'p':
708 pol.policy_file_name = optarg;
709 break;
710 case 'v':
711 log_set_verbose();
712 break;
713 case '?':
714 if (optopt == 'o' || optopt == 'p')
715 log_error("Option -%c requires an argument.\n", optopt);
716 else if (isprint (optopt))
717 log_error("Unknown option `-%c'.\n", optopt);
718 else {
719 log_error(
720 "Unknown option character `\\x%x'.\n",
721 optopt);
William Robertsf0e0a942012-08-27 15:41:15 -0700722 }
William Robertsf0e0a942012-08-27 15:41:15 -0700723 default:
724 exit(EXIT_FAILURE);
725 }
726 }
727
728 num_of_args = argc - optind;
729
730 if (num_of_args > 1) {
731 log_error("Too many arguments, expected 0 or 1, argument, got %d\n", num_of_args);
732 usage();
733 exit(EXIT_FAILURE);
734 } else if (num_of_args == 1) {
735 in_file_name = argv[argc - 1];
736 } else {
737 input_file = stdin;
738 in_file_name = "stdin";
739 }
740
741 if (!out_file_name) {
742 output_file = stdout;
743 out_file_name = "stdout";
744 }
745}
746
747/**
748 * Adds a rule_map double pointer, ie the hash table pointer to the list.
749 * By using a double pointer, the hash table can have a line be overridden
750 * and the value is updated in the list. This function calls exit on failure.
751 * @param rm
752 * the rule_map to add.
753 */
754static void list_add(hash_entry *e) {
755
756 line_order_list *node = malloc(sizeof(line_order_list));
757 if (node == NULL)
758 goto oom;
759
760 node->next = NULL;
761 node->e = e;
762
763 if (list_head == NULL)
764 list_head = list_tail = node;
765 else {
766 list_tail->next = node;
767 list_tail = list_tail->next;
768 }
769 return;
770
771oom:
772 log_error("Out of memory!\n");
773 exit(EXIT_FAILURE);
774}
775
776/**
777 * Free's the rule map list, which ultimatley contains
778 * all the malloc'd rule_maps.
779 */
780static void list_free() {
781 line_order_list *cursor, *tmp;
782 hash_entry *e;
783
784 cursor = list_head;
785 while (cursor) {
786 e = cursor->e;
William Roberts7d65b542015-06-19 09:43:28 -0700787 rule_map_free(e->r, true);
William Robertsf0e0a942012-08-27 15:41:15 -0700788 tmp = cursor;
789 cursor = cursor->next;
790 free(e);
791 free(tmp);
792 }
793}
794
795/**
796 * Adds a rule to the hash table and to the ordered list if needed.
797 * @param rm
798 * The rule map to add.
799 */
800static void rule_add(rule_map *rm) {
801
William Roberts0ae3a8a2012-09-04 11:51:04 -0700802 map_match cmp;
William Robertsf0e0a942012-08-27 15:41:15 -0700803 ENTRY e;
804 ENTRY *f;
805 hash_entry *entry;
806 hash_entry *tmp;
William Robertsf0e0a942012-08-27 15:41:15 -0700807
808 e.key = rm->key;
809
William Roberts0ae3a8a2012-09-04 11:51:04 -0700810 log_info("Searching for key: %s\n", e.key);
William Robertsf0e0a942012-08-27 15:41:15 -0700811 /* Check to see if it has already been added*/
812 f = hsearch(e, FIND);
813
814 /*
815 * Since your only hashing on a partial key, the inputs we need to handle
816 * when you want to override the outputs for a given input set, as well as
817 * checking for duplicate entries.
818 */
819 if(f) {
William Roberts0ae3a8a2012-09-04 11:51:04 -0700820 log_info("Existing entry found!\n");
William Robertsf0e0a942012-08-27 15:41:15 -0700821 tmp = (hash_entry *)f->data;
822 cmp = rule_map_cmp(rm, tmp->r);
Stephen Smalley0b820042015-02-13 14:58:31 -0500823 log_error("Duplicate line detected in file: %s\n"
824 "Lines %d and %d %s!\n",
William Roberts773d4122015-06-11 17:09:25 -0700825 in_file_name, tmp->r->lineno, rm->lineno,
Stephen Smalley0b820042015-02-13 14:58:31 -0500826 map_match_str[cmp]);
William Roberts7d65b542015-06-19 09:43:28 -0700827 rule_map_free(rm, false);
Stephen Smalley0b820042015-02-13 14:58:31 -0500828 goto err;
William Robertsf0e0a942012-08-27 15:41:15 -0700829 }
830 /* It wasn't found, just add the rule map to the table */
831 else {
832
833 entry = malloc(sizeof(hash_entry));
834 if (!entry)
835 goto oom;
836
837 entry->r = rm;
838 e.data = entry;
839
840 f = hsearch(e, ENTER);
841 if(f == NULL) {
842 goto oom;
843 }
844
845 /* new entries must be added to the ordered list */
846 entry->r = rm;
847 list_add(entry);
848 }
849
850 return;
851oom:
852 if (e.key)
853 free(e.key);
854 if (entry)
855 free(entry);
856 if (rm)
857 free(rm);
858 log_error("Out of memory in function: %s\n", __FUNCTION__);
859err:
860 exit(EXIT_FAILURE);
861}
862
863/**
864 * Parses the seapp_contexts file and adds them to the
865 * hash table and ordered list entries when it encounters them.
866 * Calls exit on failure.
867 */
868static void parse() {
869
870 char line_buf[BUFSIZ];
871 char *token;
872 unsigned lineno = 0;
873 char *p, *name = NULL, *value = NULL, *saveptr;
874 size_t len;
875 kvp keys[KVP_NUM_OF_RULES];
William Roberts610a4b12013-10-15 18:26:00 -0700876 size_t token_cnt = 0;
William Robertsf0e0a942012-08-27 15:41:15 -0700877
878 while (fgets(line_buf, sizeof line_buf - 1, input_file)) {
879
880 lineno++;
881 log_info("Got line %d\n", lineno);
882 len = strlen(line_buf);
883 if (line_buf[len - 1] == '\n')
Alice Chuf6647eb2012-10-30 16:27:00 -0700884 line_buf[len - 1] = '\0';
William Robertsf0e0a942012-08-27 15:41:15 -0700885 p = line_buf;
886 while (isspace(*p))
887 p++;
Alice Chuf6647eb2012-10-30 16:27:00 -0700888 if (*p == '#' || *p == '\0')
William Robertsf0e0a942012-08-27 15:41:15 -0700889 continue;
890
891 token = strtok_r(p, " \t", &saveptr);
892 if (!token)
893 goto err;
894
895 token_cnt = 0;
896 memset(keys, 0, sizeof(kvp) * KVP_NUM_OF_RULES);
897 while (1) {
William Roberts0ae3a8a2012-09-04 11:51:04 -0700898
William Robertsf0e0a942012-08-27 15:41:15 -0700899 name = token;
900 value = strchr(name, '=');
901 if (!value)
902 goto err;
903 *value++ = 0;
904
905 keys[token_cnt].key = strdup(name);
906 if (!keys[token_cnt].key)
907 goto oom;
908
909 keys[token_cnt].value = strdup(value);
910 if (!keys[token_cnt].value)
911 goto oom;
912
913 token_cnt++;
914
915 token = strtok_r(NULL, " \t", &saveptr);
916 if (!token)
917 break;
918
919 } /*End token parsing */
920
921 rule_map *r = rule_map_new(keys, token_cnt, lineno);
Stephen Smalley534fb072015-02-13 14:06:08 -0500922 if (!r)
923 goto err;
William Robertsf0e0a942012-08-27 15:41:15 -0700924 rule_add(r);
925
926 } /* End file parsing */
927 return;
928
929err:
930 log_error("reading %s, line %u, name %s, value %s\n",
931 in_file_name, lineno, name, value);
932 exit(EXIT_FAILURE);
933oom:
934 log_error("In function %s: Out of memory\n", __FUNCTION__);
935 exit(EXIT_FAILURE);
936}
937
938/**
939 * Should be called after parsing to cause the printing of the rule_maps
940 * stored in the ordered list, head first, which preserves the "first encountered"
941 * ordering.
942 */
943static void output() {
944
945 rule_map *r;
946 line_order_list *cursor;
947 cursor = list_head;
948
949 while (cursor) {
950 r = cursor->e->r;
951 rule_map_print(output_file, r);
952 cursor = cursor->next;
William Robertsa8613182012-09-05 11:23:40 -0700953 fprintf(output_file, "\n");
William Robertsf0e0a942012-08-27 15:41:15 -0700954 }
955}
956
957/**
958 * This function is registered to the at exit handler and should clean up
959 * the programs dynamic resources, such as memory and fd's.
960 */
961static void cleanup() {
962
963 /* Only close this when it was opened by me and not the crt */
964 if (out_file_name && output_file) {
965 log_info("Closing file: %s\n", out_file_name);
966 fclose(output_file);
967 }
968
969 /* Only close this when it was opened by me and not the crt */
970 if (in_file_name && input_file) {
971 log_info("Closing file: %s\n", in_file_name);
972 fclose(input_file);
973 }
974
975 if (pol.policy_file) {
976
977 log_info("Closing file: %s\n", pol.policy_file_name);
978 fclose(pol.policy_file);
979
980 if (pol.db)
981 sepol_policydb_free(pol.db);
982
983 if (pol.pf)
984 sepol_policy_file_free(pol.pf);
985
986 if (pol.handle)
987 sepol_handle_destroy(pol.handle);
988 }
989
990 log_info("Freeing list\n");
991 list_free();
992 hdestroy();
993}
994
995int main(int argc, char *argv[]) {
996 if (!hcreate(TABLE_SIZE)) {
997 log_error("Could not create hash table: %s\n", strerror(errno));
998 exit(EXIT_FAILURE);
999 }
1000 atexit(cleanup);
1001 handle_options(argc, argv);
1002 init();
1003 log_info("Starting to parse\n");
1004 parse();
1005 log_info("Parsing completed, generating output\n");
1006 output();
1007 log_info("Success, generated output\n");
1008 exit(EXIT_SUCCESS);
1009}