blob: af780a359927f7962e46e9a9357bea09286b7eca [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
William Robertsf0e0a942012-08-27 15:41:15 -070039/**
40 * Whether or not the "key" from a key vaue pair is considered an
41 * input or an output.
42 */
43enum key_dir {
44 dir_in, dir_out
45};
46
47/**
48 * Used as options to rule_map_free()
49 *
50 * This is needed to get around the fact that GNU C's hash_map doesn't copy the key, so
51 * we cannot free a key when overrding rule_map's in the table.
52 */
53enum rule_map_switch {
54 rule_map_preserve_key, /** Used to preserve the key in the rule_map, ie don't free it*/
55 rule_map_destroy_key /** Used when you need a full free of the rule_map structure*/
56};
57
58/**
59 * The expected "type" of data the value in the key
60 * value pair should be.
61 */
62enum data_type {
63 dt_bool, dt_string
64};
65
66/**
67 * This list is used to store a double pointer to each
68 * hash table / line rule combination. This way a replacement
69 * in the hash table automatically updates the list. The list
70 * is also used to keep "first encountered" ordering amongst
71 * the encountered key value pairs in the rules file.
72 */
73struct line_order_list {
74 hash_entry *e;
75 line_order_list *next;
76};
77
78/**
79 * The workhorse of the logic. This struct maps key value pairs to
80 * an associated set of meta data maintained in rule_map_new()
81 */
82struct key_map {
83 char *name;
84 key_dir dir;
85 data_type type;
86 char *data;
87};
88
89/**
90 * Key value pair struct, this represents the raw kvp values coming
91 * from the rules files.
92 */
93struct kvp {
94 char *key;
95 char *value;
96};
97
98/**
99 * Rules are made up of meta data and an associated set of kvp stored in a
100 * key_map array.
101 */
102struct rule_map {
103 char *key; /** key value before hashing */
William Roberts610a4b12013-10-15 18:26:00 -0700104 size_t length; /** length of the key map */
William Robertsf0e0a942012-08-27 15:41:15 -0700105 int lineno; /** Line number rule was encounter on */
William Robertsf0e0a942012-08-27 15:41:15 -0700106 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
William Roberts63297212013-04-19 19:06:23 -0700129/** set to !0 to enable strict checking of duplicate entries */
130static int is_strict = 0;
131
William Robertsf0e0a942012-08-27 15:41:15 -0700132/** file handle to the output file */
133static FILE *output_file = NULL;
134
135/** file handle to the input file */
136static FILE *input_file = NULL;
137
138/** output file path name */
139static char *out_file_name = NULL;
140
141/** input file path name */
142static char *in_file_name = NULL;
143
144static policy_info pol = {
145 .policy_file_name = NULL,
146 .policy_file = NULL,
147 .db = NULL,
148 .pf = NULL,
149 .handle = NULL,
150 .con = NULL
151};
152
153/**
154 * The heart of the mapping process, this must be updated if a new key value pair is added
155 * to a rule.
156 */
157key_map rules[] = {
158 /*Inputs*/
159 { .name = "isSystemServer", .type = dt_bool, .dir = dir_in, .data = NULL },
Stephen Smalleyff4db912014-09-15 15:16:06 -0400160 { .name = "isOwner", .type = dt_bool, .dir = dir_in, .data = NULL },
William Robertsf0e0a942012-08-27 15:41:15 -0700161 { .name = "user", .type = dt_string, .dir = dir_in, .data = NULL },
162 { .name = "seinfo", .type = dt_string, .dir = dir_in, .data = NULL },
163 { .name = "name", .type = dt_string, .dir = dir_in, .data = NULL },
Stephen Smalley6139de52014-02-19 10:54:41 -0500164 { .name = "path", .type = dt_string, .dir = dir_in, .data = NULL },
William Robertsf0e0a942012-08-27 15:41:15 -0700165 { .name = "sebool", .type = dt_string, .dir = dir_in, .data = NULL },
166 /*Outputs*/
167 { .name = "domain", .type = dt_string, .dir = dir_out, .data = NULL },
168 { .name = "type", .type = dt_string, .dir = dir_out, .data = NULL },
169 { .name = "levelFromUid", .type = dt_bool, .dir = dir_out, .data = NULL },
Stephen Smalley38084142012-11-28 10:46:18 -0500170 { .name = "levelFrom", .type = dt_string, .dir = dir_out, .data = NULL },
William Robertsf0e0a942012-08-27 15:41:15 -0700171 { .name = "level", .type = dt_string, .dir = dir_out, .data = NULL },
William Robertsfff29802012-11-27 14:20:34 -0800172};
William Robertsf0e0a942012-08-27 15:41:15 -0700173
174/**
175 * Head pointer to a linked list of
176 * rule map table entries, used for
177 * preserving the order of entries
178 * based on "first encounter"
179 */
180static line_order_list *list_head = NULL;
181
182/**
183 * Pointer to the tail of the list for
184 * quick appends to the end of the list
185 */
186static line_order_list *list_tail = NULL;
187
188/**
189 * Send a logging message to a file
190 * @param out
191 * Output file to send message too
192 * @param prefix
193 * A special prefix to write to the file, such as "Error:"
194 * @param fmt
195 * The printf style formatter to use, such as "%d"
196 */
William Roberts1e8c0612013-04-19 19:06:02 -0700197static void __attribute__ ((format(printf, 3, 4)))
198log_msg(FILE *out, const char *prefix, const char *fmt, ...) {
199
William Robertsf0e0a942012-08-27 15:41:15 -0700200 fprintf(out, "%s", prefix);
201 va_list args;
202 va_start(args, fmt);
203 vfprintf(out, fmt, args);
204 va_end(args);
205}
206
207/**
208 * Checks for a type in the policy.
209 * @param db
210 * The policy db to search
211 * @param type
212 * The type to search for
213 * @return
214 * 1 if the type is found, 0 otherwise.
215 * @warning
216 * This function always returns 1 if libsepol is not linked
217 * statically to this executable and LINK_SEPOL_STATIC is not
218 * defined.
219 */
220int check_type(sepol_policydb_t *db, char *type) {
221
222 int rc = 1;
223#if defined(LINK_SEPOL_STATIC)
224 policydb_t *d = (policydb_t *)db;
225 hashtab_datum_t dat;
226 dat = hashtab_search(d->p_types.table, type);
227 rc = (dat == NULL) ? 0 : 1;
228#endif
229 return rc;
230}
231
232/**
233 * Validates a key_map against a set of enforcement rules, this
234 * function exits the application on a type that cannot be properly
235 * checked
236 *
237 * @param m
238 * The key map to check
239 * @param lineno
240 * The line number in the source file for the corresponding key map
William Robertsfff29802012-11-27 14:20:34 -0800241 * @return
242 * 1 if valid, 0 if invalid
William Robertsf0e0a942012-08-27 15:41:15 -0700243 */
244static int key_map_validate(key_map *m, int lineno) {
245
246 int rc = 1;
247 int ret = 1;
William Robertsf0e0a942012-08-27 15:41:15 -0700248 int resp;
249 char *key = m->name;
250 char *value = m->data;
251 data_type type = m->type;
252 sepol_bool_key_t *se_key;
253
William Roberts0ae3a8a2012-09-04 11:51:04 -0700254 log_info("Validating %s=%s\n", key, value);
255
William Robertsf0e0a942012-08-27 15:41:15 -0700256 /* Booleans can always be checked for sanity */
257 if (type == dt_bool && (!strcmp("true", value) || !strcmp("false", value))) {
258 goto out;
259 }
260 else if (type == dt_bool) {
William Robertsae23a1f2012-09-05 12:53:52 -0700261 log_error("Expected boolean value got: %s=%s on line: %d in file: %s\n",
262 key, value, lineno, out_file_name);
William Robertsf0e0a942012-08-27 15:41:15 -0700263 rc = 0;
264 goto out;
265 }
266
Stephen Smalley38084142012-11-28 10:46:18 -0500267 if (!strcasecmp(key, "levelFrom") &&
268 (strcasecmp(value, "none") && strcasecmp(value, "all") &&
269 strcasecmp(value, "app") && strcasecmp(value, "user"))) {
270 log_error("Unknown levelFrom=%s on line: %d in file: %s\n",
271 value, lineno, out_file_name);
272 rc = 0;
273 goto out;
274 }
275
William Robertsf0e0a942012-08-27 15:41:15 -0700276 /*
William Roberts63297212013-04-19 19:06:23 -0700277 * If there is no policy file present,
278 * then it is not going to enforce the types against the policy so just return.
William Robertsf0e0a942012-08-27 15:41:15 -0700279 * User and name cannot really be checked.
280 */
281 if (!pol.policy_file) {
282 goto out;
283 }
284 else if (!strcasecmp(key, "sebool")) {
285
286 ret = sepol_bool_key_create(pol.handle, value, &se_key);
287 if (ret < 0) {
288 log_error("Could not create selinux boolean key, error: %s\n",
289 strerror(errno));
290 rc = 0;
291 goto out;
292 }
293
294 ret = sepol_bool_exists(pol.handle, pol.db, se_key, &resp);
295 if (ret < 0) {
296 log_error("Could not check selinux boolean, error: %s\n",
297 strerror(errno));
298 rc = 0;
William Robertsa53ccf32012-09-17 12:53:44 -0700299 sepol_bool_key_free(se_key);
300 goto out;
William Robertsf0e0a942012-08-27 15:41:15 -0700301 }
302
303 if(!resp) {
304 log_error("Could not find selinux boolean \"%s\" on line: %d in file: %s\n",
305 value, lineno, out_file_name);
306 rc = 0;
William Robertsa53ccf32012-09-17 12:53:44 -0700307 sepol_bool_key_free(se_key);
308 goto out;
William Robertsf0e0a942012-08-27 15:41:15 -0700309 }
William Robertsa53ccf32012-09-17 12:53:44 -0700310 sepol_bool_key_free(se_key);
William Robertsf0e0a942012-08-27 15:41:15 -0700311 }
312 else if (!strcasecmp(key, "type") || !strcasecmp(key, "domain")) {
313
314 if(!check_type(pol.db, value)) {
315 log_error("Could not find selinux type \"%s\" on line: %d in file: %s\n", value,
316 lineno, out_file_name);
317 rc = 0;
318 }
319 goto out;
320 }
William Robertsf0e0a942012-08-27 15:41:15 -0700321 else if (!strcasecmp(key, "level")) {
322
William Roberts0ae3a8a2012-09-04 11:51:04 -0700323 ret = sepol_mls_check(pol.handle, pol.db, value);
324 if (ret < 0) {
William Robertsae23a1f2012-09-05 12:53:52 -0700325 log_error("Could not find selinux level \"%s\", on line: %d in file: %s\n", value,
326 lineno, out_file_name);
William Roberts0ae3a8a2012-09-04 11:51:04 -0700327 rc = 0;
328 goto out;
William Robertsf0e0a942012-08-27 15:41:15 -0700329 }
330 }
331
William Roberts0ae3a8a2012-09-04 11:51:04 -0700332out:
333 log_info("Key map validate returning: %d\n", rc);
334 return rc;
William Robertsf0e0a942012-08-27 15:41:15 -0700335}
336
337/**
338 * Prints a rule map back to a file
339 * @param fp
340 * The file handle to print too
341 * @param r
342 * The rule map to print
343 */
344static void rule_map_print(FILE *fp, rule_map *r) {
345
William Roberts610a4b12013-10-15 18:26:00 -0700346 size_t i;
William Robertsf0e0a942012-08-27 15:41:15 -0700347 key_map *m;
348
349 for (i = 0; i < r->length; i++) {
350 m = &(r->m[i]);
351 if (i < r->length - 1)
352 fprintf(fp, "%s=%s ", m->name, m->data);
353 else
354 fprintf(fp, "%s=%s", m->name, m->data);
355 }
356}
357
358/**
359 * Compare two rule maps for equality
360 * @param rmA
361 * a rule map to check
362 * @param rmB
363 * a rule map to check
364 * @return
William Robertsae23a1f2012-09-05 12:53:52 -0700365 * a map_match enum indicating the result
William Robertsf0e0a942012-08-27 15:41:15 -0700366 */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700367static map_match rule_map_cmp(rule_map *rmA, rule_map *rmB) {
William Robertsf0e0a942012-08-27 15:41:15 -0700368
William Roberts610a4b12013-10-15 18:26:00 -0700369 size_t i;
370 size_t j;
William Robertsf0e0a942012-08-27 15:41:15 -0700371 int inputs_found = 0;
372 int num_of_matched_inputs = 0;
373 int input_mode = 0;
William Roberts610a4b12013-10-15 18:26:00 -0700374 size_t matches = 0;
William Robertsf0e0a942012-08-27 15:41:15 -0700375 key_map *mA;
376 key_map *mB;
377
378 if (rmA->length != rmB->length)
William Roberts0ae3a8a2012-09-04 11:51:04 -0700379 return map_no_matches;
William Robertsf0e0a942012-08-27 15:41:15 -0700380
381 for (i = 0; i < rmA->length; i++) {
382 mA = &(rmA->m[i]);
383
384 for (j = 0; j < rmB->length; j++) {
385 mB = &(rmB->m[j]);
386 input_mode = 0;
387
388 if (mA->type != mB->type)
389 continue;
390
391 if (strcmp(mA->name, mB->name))
392 continue;
393
394 if (strcmp(mA->data, mB->data))
395 continue;
396
397 if (mB->dir != mA->dir)
398 continue;
399 else if (mB->dir == dir_in) {
400 input_mode = 1;
401 inputs_found++;
402 }
403
William Roberts0ae3a8a2012-09-04 11:51:04 -0700404 if (input_mode) {
William Roberts1e8c0612013-04-19 19:06:02 -0700405 log_info("Matched input lines: name=%s data=%s\n", mA->name, mA->data);
William Robertsf0e0a942012-08-27 15:41:15 -0700406 num_of_matched_inputs++;
William Roberts0ae3a8a2012-09-04 11:51:04 -0700407 }
William Robertsf0e0a942012-08-27 15:41:15 -0700408
409 /* Match found, move on */
William Roberts1e8c0612013-04-19 19:06:02 -0700410 log_info("Matched lines: name=%s data=%s", mA->name, mA->data);
William Robertsf0e0a942012-08-27 15:41:15 -0700411 matches++;
412 break;
413 }
414 }
415
416 /* If they all matched*/
William Roberts0ae3a8a2012-09-04 11:51:04 -0700417 if (matches == rmA->length) {
418 log_info("Rule map cmp MATCH\n");
419 return map_matched;
420 }
William Robertsf0e0a942012-08-27 15:41:15 -0700421
422 /* They didn't all match but the input's did */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700423 else if (num_of_matched_inputs == inputs_found) {
424 log_info("Rule map cmp INPUT MATCH\n");
425 return map_input_matched;
426 }
William Robertsf0e0a942012-08-27 15:41:15 -0700427
428 /* They didn't all match, and the inputs didn't match, ie it didn't
429 * match */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700430 else {
431 log_info("Rule map cmp NO MATCH\n");
432 return map_no_matches;
433 }
William Robertsf0e0a942012-08-27 15:41:15 -0700434}
435
436/**
437 * Frees a rule map
438 * @param rm
439 * rule map to be freed.
440 */
Robert Craigc9bb91d2013-11-01 10:24:36 -0400441static void rule_map_free(rule_map *rm,
442 rule_map_switch s __attribute__((unused)) /* only glibc builds, ignored otherwise */) {
William Robertsf0e0a942012-08-27 15:41:15 -0700443
William Roberts610a4b12013-10-15 18:26:00 -0700444 size_t i;
445 size_t len = rm->length;
William Robertsf0e0a942012-08-27 15:41:15 -0700446 for (i = 0; i < len; i++) {
447 key_map *m = &(rm->m[i]);
448 free(m->data);
449 }
450
rpcraig5dbfdc02012-10-23 11:03:47 -0400451/* hdestroy() frees comparsion keys for non glibc */
452#ifdef __GLIBC__
William Robertsf0e0a942012-08-27 15:41:15 -0700453 if(s == rule_map_destroy_key && rm->key)
454 free(rm->key);
rpcraig5dbfdc02012-10-23 11:03:47 -0400455#endif
William Robertsf0e0a942012-08-27 15:41:15 -0700456
457 free(rm);
458}
459
460static void free_kvp(kvp *k) {
461 free(k->key);
462 free(k->value);
463}
464
465/**
William Roberts61846292013-10-15 09:38:24 -0700466 * Checks a rule_map for any variation of KVP's that shouldn't be allowed.
467 * Note that this function logs all errors.
468 *
469 * Current Checks:
470 * 1. That a specified name entry should have a specified seinfo entry as well.
471 * @param rm
472 * The rule map to check for validity.
473 * @return
474 * true if the rule is valid, false otherwise.
475 */
476static bool rule_map_validate(const rule_map *rm) {
477
William Roberts610a4b12013-10-15 18:26:00 -0700478 size_t i;
William Roberts61846292013-10-15 09:38:24 -0700479 bool found_name = false;
480 bool found_seinfo = false;
481 char *name = NULL;
Stephen Smalley7b2bee92013-10-31 09:22:26 -0400482 const key_map *tmp;
William Roberts61846292013-10-15 09:38:24 -0700483
484 for(i=0; i < rm->length; i++) {
485 tmp = &(rm->m[i]);
486
487 if(!strcmp(tmp->name, "name") && tmp->data) {
488 name = tmp->data;
489 found_name = true;
490 }
Stephen Smalleyf4fa7562014-04-04 14:16:46 -0400491 if(!strcmp(tmp->name, "seinfo") && tmp->data && strcmp(tmp->data, "default")) {
William Roberts61846292013-10-15 09:38:24 -0700492 found_seinfo = true;
493 }
494 }
495
496 if(found_name && !found_seinfo) {
Stephen Smalleyf4fa7562014-04-04 14:16:46 -0400497 log_error("No specific seinfo value specified with name=\"%s\", on line: %d: insecure configuration!\n",
William Roberts61846292013-10-15 09:38:24 -0700498 name, rm->lineno);
499 return false;
500 }
501
502 return true;
503}
504
505/**
William Robertsf0e0a942012-08-27 15:41:15 -0700506 * Given a set of key value pairs, this will construct a new rule map.
507 * On error this function calls exit.
508 * @param keys
509 * Keys from a rule line to map
510 * @param num_of_keys
511 * The length of the keys array
512 * @param lineno
513 * The line number the keys were extracted from
514 * @return
515 * A rule map pointer.
516 */
William Roberts610a4b12013-10-15 18:26:00 -0700517static rule_map *rule_map_new(kvp keys[], size_t num_of_keys, int lineno) {
William Robertsf0e0a942012-08-27 15:41:15 -0700518
William Roberts610a4b12013-10-15 18:26:00 -0700519 size_t i = 0, j = 0;
William Roberts61846292013-10-15 09:38:24 -0700520 bool valid_rule;
William Robertsf0e0a942012-08-27 15:41:15 -0700521 rule_map *new_map = NULL;
522 kvp *k = NULL;
523 key_map *r = NULL, *x = NULL;
524
525 new_map = calloc(1, (num_of_keys * sizeof(key_map)) + sizeof(rule_map));
526 if (!new_map)
527 goto oom;
528
529 new_map->length = num_of_keys;
530 new_map->lineno = lineno;
531
532 /* For all the keys in a rule line*/
533 for (i = 0; i < num_of_keys; i++) {
534 k = &(keys[i]);
535 r = &(new_map->m[i]);
536
537 for (j = 0; j < KVP_NUM_OF_RULES; j++) {
538 x = &(rules[j]);
539
540 /* Only assign key name to map name */
541 if (strcasecmp(k->key, x->name)) {
542 if (i == KVP_NUM_OF_RULES) {
543 log_error("No match for key: %s\n", k->key);
544 goto err;
545 }
546 continue;
547 }
548
549 memcpy(r, x, sizeof(key_map));
550
551 /* Assign rule map value to one from file */
552 r->data = strdup(k->value);
553 if (!r->data)
554 goto oom;
555
556 /* Enforce type check*/
William Roberts0ae3a8a2012-09-04 11:51:04 -0700557 log_info("Validating keys!\n");
William Robertsf0e0a942012-08-27 15:41:15 -0700558 if (!key_map_validate(r, lineno)) {
559 log_error("Could not validate\n");
560 goto err;
561 }
562
563 /* Only build key off of inputs*/
564 if (r->dir == dir_in) {
565 char *tmp;
William Robertsb3ab56c2012-09-17 14:35:02 -0700566 int key_len = strlen(k->key);
567 int val_len = strlen(k->value);
568 int l = (new_map->key) ? strlen(new_map->key) : 0;
569 l = l + key_len + val_len;
William Robertsf0e0a942012-08-27 15:41:15 -0700570 l += 1;
571
572 tmp = realloc(new_map->key, l);
573 if (!tmp)
574 goto oom;
575
William Robertsb3ab56c2012-09-17 14:35:02 -0700576 if (!new_map->key)
577 memset(tmp, 0, l);
578
William Robertsf0e0a942012-08-27 15:41:15 -0700579 new_map->key = tmp;
580
William Robertsb3ab56c2012-09-17 14:35:02 -0700581 strncat(new_map->key, k->key, key_len);
582 strncat(new_map->key, k->value, val_len);
William Robertsf0e0a942012-08-27 15:41:15 -0700583 }
584 break;
585 }
586 free_kvp(k);
587 }
588
589 if (new_map->key == NULL) {
590 log_error("Strange, no keys found, input file corrupt perhaps?\n");
591 goto err;
592 }
593
William Roberts61846292013-10-15 09:38:24 -0700594 valid_rule = rule_map_validate(new_map);
595 if(!valid_rule) {
596 /* Error message logged from rule_map_validate() */
597 goto err;
598 }
599
William Robertsf0e0a942012-08-27 15:41:15 -0700600 return new_map;
601
602oom:
603 log_error("Out of memory!\n");
604err:
605 if(new_map) {
606 rule_map_free(new_map, rule_map_destroy_key);
607 for (; i < num_of_keys; i++) {
608 k = &(keys[i]);
609 free_kvp(k);
610 }
611 }
612 exit(EXIT_FAILURE);
613}
614
615/**
616 * Print the usage of the program
617 */
618static void usage() {
619 printf(
620 "checkseapp [options] <input file>\n"
621 "Processes an seapp_contexts file specified by argument <input file> (default stdin) "
William Robertsae23a1f2012-09-05 12:53:52 -0700622 "and allows later declarations to override previous ones on a match.\n"
William Robertsf0e0a942012-08-27 15:41:15 -0700623 "Options:\n"
624 "-h - print this help message\n"
William Roberts63297212013-04-19 19:06:23 -0700625 "-s - enable strict checking of duplicates. This causes the program to exit on a duplicate entry with a non-zero exit status\n"
William Robertsf0e0a942012-08-27 15:41:15 -0700626 "-v - enable verbose debugging informations\n"
William Roberts63297212013-04-19 19:06:23 -0700627 "-p policy file - specify policy file for strict checking of output selectors against the policy\n"
William Robertsf0e0a942012-08-27 15:41:15 -0700628 "-o output file - specify output file, default is stdout\n");
629}
630
631static void init() {
632
633 /* If not set on stdin already */
634 if(!input_file) {
635 log_info("Opening input file: %s\n", in_file_name);
636 input_file = fopen(in_file_name, "r");
637 if (!input_file) {
638 log_error("Could not open file: %s error: %s\n", in_file_name, strerror(errno));
639 exit(EXIT_FAILURE);
640 }
641 }
642
643 /* If not set on std out already */
644 if(!output_file) {
645 output_file = fopen(out_file_name, "w+");
646 if (!output_file) {
647 log_error("Could not open file: %s error: %s\n", out_file_name, strerror(errno));
648 exit(EXIT_FAILURE);
649 }
650 }
651
652 if (pol.policy_file_name) {
653
654 log_info("Opening policy file: %s\n", pol.policy_file_name);
655 pol.policy_file = fopen(pol.policy_file_name, "rb");
656 if (!pol.policy_file) {
657 log_error("Could not open file: %s error: %s\n",
658 pol.policy_file_name, strerror(errno));
659 exit(EXIT_FAILURE);
660 }
661
662 pol.handle = sepol_handle_create();
663 if (!pol.handle) {
664 log_error("Could not create sepolicy handle: %s\n",
665 strerror(errno));
666 exit(EXIT_FAILURE);
667 }
668
669 if (sepol_policy_file_create(&pol.pf) < 0) {
670 log_error("Could not create sepolicy file: %s!\n",
671 strerror(errno));
672 exit(EXIT_FAILURE);
673 }
674
675 sepol_policy_file_set_fp(pol.pf, pol.policy_file);
676 sepol_policy_file_set_handle(pol.pf, pol.handle);
677
678 if (sepol_policydb_create(&pol.db) < 0) {
679 log_error("Could not create sepolicy db: %s!\n",
680 strerror(errno));
681 exit(EXIT_FAILURE);
682 }
683
684 if (sepol_policydb_read(pol.db, pol.pf) < 0) {
685 log_error("Could not lod policy file to db: %s!\n",
686 strerror(errno));
687 exit(EXIT_FAILURE);
688 }
689 }
690
691 log_info("Policy file set to: %s\n", (pol.policy_file_name == NULL) ? "None" : pol.policy_file_name);
692 log_info("Input file set to: %s\n", (in_file_name == NULL) ? "stdin" : in_file_name);
693 log_info("Output file set to: %s\n", (out_file_name == NULL) ? "stdout" : out_file_name);
694
William Roberts0ae3a8a2012-09-04 11:51:04 -0700695#if !defined(LINK_SEPOL_STATIC)
William Robertsa53ccf32012-09-17 12:53:44 -0700696 log_warn("LINK_SEPOL_STATIC is not defined\n""Not checking types!");
William Roberts0ae3a8a2012-09-04 11:51:04 -0700697#endif
698
William Robertsf0e0a942012-08-27 15:41:15 -0700699}
700
701/**
702 * Handle parsing and setting the global flags for the command line
703 * options. This function calls exit on failure.
704 * @param argc
705 * argument count
706 * @param argv
707 * argument list
708 */
709static void handle_options(int argc, char *argv[]) {
710
711 int c;
712 int num_of_args;
713
William Roberts63297212013-04-19 19:06:23 -0700714 while ((c = getopt(argc, argv, "ho:p:sv")) != -1) {
William Robertsf0e0a942012-08-27 15:41:15 -0700715 switch (c) {
716 case 'h':
717 usage();
718 exit(EXIT_SUCCESS);
719 case 'o':
720 out_file_name = optarg;
721 break;
722 case 'p':
723 pol.policy_file_name = optarg;
724 break;
William Roberts63297212013-04-19 19:06:23 -0700725 case 's':
726 is_strict = 1;
727 break;
William Robertsf0e0a942012-08-27 15:41:15 -0700728 case 'v':
729 log_set_verbose();
730 break;
731 case '?':
732 if (optopt == 'o' || optopt == 'p')
733 log_error("Option -%c requires an argument.\n", optopt);
734 else if (isprint (optopt))
735 log_error("Unknown option `-%c'.\n", optopt);
736 else {
737 log_error(
738 "Unknown option character `\\x%x'.\n",
739 optopt);
William Robertsf0e0a942012-08-27 15:41:15 -0700740 }
William Robertsf0e0a942012-08-27 15:41:15 -0700741 default:
742 exit(EXIT_FAILURE);
743 }
744 }
745
746 num_of_args = argc - optind;
747
748 if (num_of_args > 1) {
749 log_error("Too many arguments, expected 0 or 1, argument, got %d\n", num_of_args);
750 usage();
751 exit(EXIT_FAILURE);
752 } else if (num_of_args == 1) {
753 in_file_name = argv[argc - 1];
754 } else {
755 input_file = stdin;
756 in_file_name = "stdin";
757 }
758
759 if (!out_file_name) {
760 output_file = stdout;
761 out_file_name = "stdout";
762 }
763}
764
765/**
766 * Adds a rule_map double pointer, ie the hash table pointer to the list.
767 * By using a double pointer, the hash table can have a line be overridden
768 * and the value is updated in the list. This function calls exit on failure.
769 * @param rm
770 * the rule_map to add.
771 */
772static void list_add(hash_entry *e) {
773
774 line_order_list *node = malloc(sizeof(line_order_list));
775 if (node == NULL)
776 goto oom;
777
778 node->next = NULL;
779 node->e = e;
780
781 if (list_head == NULL)
782 list_head = list_tail = node;
783 else {
784 list_tail->next = node;
785 list_tail = list_tail->next;
786 }
787 return;
788
789oom:
790 log_error("Out of memory!\n");
791 exit(EXIT_FAILURE);
792}
793
794/**
795 * Free's the rule map list, which ultimatley contains
796 * all the malloc'd rule_maps.
797 */
798static void list_free() {
799 line_order_list *cursor, *tmp;
800 hash_entry *e;
801
802 cursor = list_head;
803 while (cursor) {
804 e = cursor->e;
805 rule_map_free(e->r, rule_map_destroy_key);
806 tmp = cursor;
807 cursor = cursor->next;
808 free(e);
809 free(tmp);
810 }
811}
812
813/**
814 * Adds a rule to the hash table and to the ordered list if needed.
815 * @param rm
816 * The rule map to add.
817 */
818static void rule_add(rule_map *rm) {
819
William Roberts0ae3a8a2012-09-04 11:51:04 -0700820 map_match cmp;
William Robertsf0e0a942012-08-27 15:41:15 -0700821 ENTRY e;
822 ENTRY *f;
823 hash_entry *entry;
824 hash_entry *tmp;
825 char *preserved_key;
826
827 e.key = rm->key;
828
William Roberts0ae3a8a2012-09-04 11:51:04 -0700829 log_info("Searching for key: %s\n", e.key);
William Robertsf0e0a942012-08-27 15:41:15 -0700830 /* Check to see if it has already been added*/
831 f = hsearch(e, FIND);
832
833 /*
834 * Since your only hashing on a partial key, the inputs we need to handle
835 * when you want to override the outputs for a given input set, as well as
836 * checking for duplicate entries.
837 */
838 if(f) {
William Roberts0ae3a8a2012-09-04 11:51:04 -0700839 log_info("Existing entry found!\n");
William Robertsf0e0a942012-08-27 15:41:15 -0700840 tmp = (hash_entry *)f->data;
841 cmp = rule_map_cmp(rm, tmp->r);
William Roberts0ae3a8a2012-09-04 11:51:04 -0700842 log_info("Comparing on rule map ret: %d\n", cmp);
William Robertsf0e0a942012-08-27 15:41:15 -0700843 /* Override be freeing the old rule map and updating
844 the pointer */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700845 if(cmp != map_matched) {
William Robertsf0e0a942012-08-27 15:41:15 -0700846
847 /*
848 * DO NOT free key pointers given to the hash map, instead
849 * free the new key. The ordering here is critical!
850 */
851 preserved_key = tmp->r->key;
852 rule_map_free(tmp->r, rule_map_preserve_key);
rpcraig5dbfdc02012-10-23 11:03:47 -0400853/* hdestroy() frees comparsion keys for non glibc */
854#ifdef __GLIBC__
William Robertsf0e0a942012-08-27 15:41:15 -0700855 free(rm->key);
rpcraig5dbfdc02012-10-23 11:03:47 -0400856#endif
William Robertsf0e0a942012-08-27 15:41:15 -0700857 rm->key = preserved_key;
858 tmp->r = rm;
859 }
860 /* Duplicate */
861 else {
William Roberts63297212013-04-19 19:06:23 -0700862 /* if is_strict is set, then don't allow duplicates */
863 if(is_strict) {
864 log_error("Duplicate line detected in file: %s\n"
865 "Lines %d and %d match!\n",
866 out_file_name, tmp->r->lineno, rm->lineno);
867 rule_map_free(rm, rule_map_destroy_key);
868 goto err;
869 }
870
871 /* Allow duplicates, just drop the entry*/
872 log_info("Duplicate line detected in file: %s\n"
William Robertsf0e0a942012-08-27 15:41:15 -0700873 "Lines %d and %d match!\n",
874 out_file_name, tmp->r->lineno, rm->lineno);
875 rule_map_free(rm, rule_map_destroy_key);
William Robertsf0e0a942012-08-27 15:41:15 -0700876 }
877 }
878 /* It wasn't found, just add the rule map to the table */
879 else {
880
881 entry = malloc(sizeof(hash_entry));
882 if (!entry)
883 goto oom;
884
885 entry->r = rm;
886 e.data = entry;
887
888 f = hsearch(e, ENTER);
889 if(f == NULL) {
890 goto oom;
891 }
892
893 /* new entries must be added to the ordered list */
894 entry->r = rm;
895 list_add(entry);
896 }
897
898 return;
899oom:
900 if (e.key)
901 free(e.key);
902 if (entry)
903 free(entry);
904 if (rm)
905 free(rm);
906 log_error("Out of memory in function: %s\n", __FUNCTION__);
907err:
908 exit(EXIT_FAILURE);
909}
910
911/**
912 * Parses the seapp_contexts file and adds them to the
913 * hash table and ordered list entries when it encounters them.
914 * Calls exit on failure.
915 */
916static void parse() {
917
918 char line_buf[BUFSIZ];
919 char *token;
920 unsigned lineno = 0;
921 char *p, *name = NULL, *value = NULL, *saveptr;
922 size_t len;
923 kvp keys[KVP_NUM_OF_RULES];
William Roberts610a4b12013-10-15 18:26:00 -0700924 size_t token_cnt = 0;
William Robertsf0e0a942012-08-27 15:41:15 -0700925
926 while (fgets(line_buf, sizeof line_buf - 1, input_file)) {
927
928 lineno++;
929 log_info("Got line %d\n", lineno);
930 len = strlen(line_buf);
931 if (line_buf[len - 1] == '\n')
Alice Chuf6647eb2012-10-30 16:27:00 -0700932 line_buf[len - 1] = '\0';
William Robertsf0e0a942012-08-27 15:41:15 -0700933 p = line_buf;
934 while (isspace(*p))
935 p++;
Alice Chuf6647eb2012-10-30 16:27:00 -0700936 if (*p == '#' || *p == '\0')
William Robertsf0e0a942012-08-27 15:41:15 -0700937 continue;
938
939 token = strtok_r(p, " \t", &saveptr);
940 if (!token)
941 goto err;
942
943 token_cnt = 0;
944 memset(keys, 0, sizeof(kvp) * KVP_NUM_OF_RULES);
945 while (1) {
William Roberts0ae3a8a2012-09-04 11:51:04 -0700946
William Robertsf0e0a942012-08-27 15:41:15 -0700947 name = token;
948 value = strchr(name, '=');
949 if (!value)
950 goto err;
951 *value++ = 0;
952
953 keys[token_cnt].key = strdup(name);
954 if (!keys[token_cnt].key)
955 goto oom;
956
957 keys[token_cnt].value = strdup(value);
958 if (!keys[token_cnt].value)
959 goto oom;
960
961 token_cnt++;
962
963 token = strtok_r(NULL, " \t", &saveptr);
964 if (!token)
965 break;
966
967 } /*End token parsing */
968
969 rule_map *r = rule_map_new(keys, token_cnt, lineno);
970 rule_add(r);
971
972 } /* End file parsing */
973 return;
974
975err:
976 log_error("reading %s, line %u, name %s, value %s\n",
977 in_file_name, lineno, name, value);
978 exit(EXIT_FAILURE);
979oom:
980 log_error("In function %s: Out of memory\n", __FUNCTION__);
981 exit(EXIT_FAILURE);
982}
983
984/**
985 * Should be called after parsing to cause the printing of the rule_maps
986 * stored in the ordered list, head first, which preserves the "first encountered"
987 * ordering.
988 */
989static void output() {
990
991 rule_map *r;
992 line_order_list *cursor;
993 cursor = list_head;
994
995 while (cursor) {
996 r = cursor->e->r;
997 rule_map_print(output_file, r);
998 cursor = cursor->next;
William Robertsa8613182012-09-05 11:23:40 -0700999 fprintf(output_file, "\n");
William Robertsf0e0a942012-08-27 15:41:15 -07001000 }
1001}
1002
1003/**
1004 * This function is registered to the at exit handler and should clean up
1005 * the programs dynamic resources, such as memory and fd's.
1006 */
1007static void cleanup() {
1008
1009 /* Only close this when it was opened by me and not the crt */
1010 if (out_file_name && output_file) {
1011 log_info("Closing file: %s\n", out_file_name);
1012 fclose(output_file);
1013 }
1014
1015 /* Only close this when it was opened by me and not the crt */
1016 if (in_file_name && input_file) {
1017 log_info("Closing file: %s\n", in_file_name);
1018 fclose(input_file);
1019 }
1020
1021 if (pol.policy_file) {
1022
1023 log_info("Closing file: %s\n", pol.policy_file_name);
1024 fclose(pol.policy_file);
1025
1026 if (pol.db)
1027 sepol_policydb_free(pol.db);
1028
1029 if (pol.pf)
1030 sepol_policy_file_free(pol.pf);
1031
1032 if (pol.handle)
1033 sepol_handle_destroy(pol.handle);
1034 }
1035
1036 log_info("Freeing list\n");
1037 list_free();
1038 hdestroy();
1039}
1040
1041int main(int argc, char *argv[]) {
1042 if (!hcreate(TABLE_SIZE)) {
1043 log_error("Could not create hash table: %s\n", strerror(errno));
1044 exit(EXIT_FAILURE);
1045 }
1046 atexit(cleanup);
1047 handle_options(argc, argv);
1048 init();
1049 log_info("Starting to parse\n");
1050 parse();
1051 log_info("Parsing completed, generating output\n");
1052 output();
1053 log_info("Success, generated output\n");
1054 exit(EXIT_SUCCESS);
1055}