blob: 45e6be061656d08b04168bf3f036db4a12e2af01 [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/**
54 * Used as options to rule_map_free()
55 *
56 * This is needed to get around the fact that GNU C's hash_map doesn't copy the key, so
57 * we cannot free a key when overrding rule_map's in the table.
58 */
59enum rule_map_switch {
60 rule_map_preserve_key, /** Used to preserve the key in the rule_map, ie don't free it*/
61 rule_map_destroy_key /** Used when you need a full free of the rule_map structure*/
62};
63
64/**
65 * The expected "type" of data the value in the key
66 * value pair should be.
67 */
68enum data_type {
69 dt_bool, dt_string
70};
71
72/**
73 * This list is used to store a double pointer to each
74 * hash table / line rule combination. This way a replacement
75 * in the hash table automatically updates the list. The list
76 * is also used to keep "first encountered" ordering amongst
77 * the encountered key value pairs in the rules file.
78 */
79struct line_order_list {
80 hash_entry *e;
81 line_order_list *next;
82};
83
84/**
85 * The workhorse of the logic. This struct maps key value pairs to
86 * an associated set of meta data maintained in rule_map_new()
87 */
88struct key_map {
89 char *name;
90 key_dir dir;
91 data_type type;
92 char *data;
93};
94
95/**
96 * Key value pair struct, this represents the raw kvp values coming
97 * from the rules files.
98 */
99struct kvp {
100 char *key;
101 char *value;
102};
103
104/**
105 * Rules are made up of meta data and an associated set of kvp stored in a
106 * key_map array.
107 */
108struct rule_map {
109 char *key; /** key value before hashing */
William Roberts610a4b12013-10-15 18:26:00 -0700110 size_t length; /** length of the key map */
William Robertsf0e0a942012-08-27 15:41:15 -0700111 int lineno; /** Line number rule was encounter on */
William Robertsf0e0a942012-08-27 15:41:15 -0700112 key_map m[]; /** key value mapping */
113};
114
115struct hash_entry {
116 rule_map *r; /** The rule map to store at that location */
117};
118
119/**
120 * Data associated for a policy file
121 */
122struct policy_info {
123
124 char *policy_file_name; /** policy file path name */
125 FILE *policy_file; /** file handle to the policy file */
126 sepol_policydb_t *db;
127 sepol_policy_file_t *pf;
128 sepol_handle_t *handle;
129 sepol_context_t *con;
130};
131
132/** Set to !0 to enable verbose logging */
133static int logging_verbose = 0;
134
135/** file handle to the output file */
136static FILE *output_file = NULL;
137
138/** file handle to the input file */
139static FILE *input_file = NULL;
140
141/** output file path name */
142static char *out_file_name = NULL;
143
144/** input file path name */
145static char *in_file_name = NULL;
146
147static policy_info pol = {
148 .policy_file_name = NULL,
149 .policy_file = NULL,
150 .db = NULL,
151 .pf = NULL,
152 .handle = NULL,
153 .con = NULL
154};
155
156/**
157 * The heart of the mapping process, this must be updated if a new key value pair is added
158 * to a rule.
159 */
160key_map rules[] = {
161 /*Inputs*/
162 { .name = "isSystemServer", .type = dt_bool, .dir = dir_in, .data = NULL },
Stephen Smalleyff4db912014-09-15 15:16:06 -0400163 { .name = "isOwner", .type = dt_bool, .dir = dir_in, .data = NULL },
William Robertsf0e0a942012-08-27 15:41:15 -0700164 { .name = "user", .type = dt_string, .dir = dir_in, .data = NULL },
165 { .name = "seinfo", .type = dt_string, .dir = dir_in, .data = NULL },
166 { .name = "name", .type = dt_string, .dir = dir_in, .data = NULL },
Stephen Smalley6139de52014-02-19 10:54:41 -0500167 { .name = "path", .type = dt_string, .dir = dir_in, .data = NULL },
William Robertsf0e0a942012-08-27 15:41:15 -0700168 /*Outputs*/
169 { .name = "domain", .type = dt_string, .dir = dir_out, .data = NULL },
170 { .name = "type", .type = dt_string, .dir = dir_out, .data = NULL },
171 { .name = "levelFromUid", .type = dt_bool, .dir = dir_out, .data = NULL },
Stephen Smalley38084142012-11-28 10:46:18 -0500172 { .name = "levelFrom", .type = dt_string, .dir = dir_out, .data = NULL },
William Robertsf0e0a942012-08-27 15:41:15 -0700173 { .name = "level", .type = dt_string, .dir = dir_out, .data = NULL },
William Robertsfff29802012-11-27 14:20:34 -0800174};
William Robertsf0e0a942012-08-27 15:41:15 -0700175
176/**
177 * Head pointer to a linked list of
178 * rule map table entries, used for
179 * preserving the order of entries
180 * based on "first encounter"
181 */
182static line_order_list *list_head = NULL;
183
184/**
185 * Pointer to the tail of the list for
186 * quick appends to the end of the list
187 */
188static line_order_list *list_tail = NULL;
189
190/**
191 * Send a logging message to a file
192 * @param out
193 * Output file to send message too
194 * @param prefix
195 * A special prefix to write to the file, such as "Error:"
196 * @param fmt
197 * The printf style formatter to use, such as "%d"
198 */
William Roberts1e8c0612013-04-19 19:06:02 -0700199static void __attribute__ ((format(printf, 3, 4)))
200log_msg(FILE *out, const char *prefix, const char *fmt, ...) {
201
William Robertsf0e0a942012-08-27 15:41:15 -0700202 fprintf(out, "%s", prefix);
203 va_list args;
204 va_start(args, fmt);
205 vfprintf(out, fmt, args);
206 va_end(args);
207}
208
209/**
210 * Checks for a type in the policy.
211 * @param db
212 * The policy db to search
213 * @param type
214 * The type to search for
215 * @return
216 * 1 if the type is found, 0 otherwise.
217 * @warning
218 * This function always returns 1 if libsepol is not linked
219 * statically to this executable and LINK_SEPOL_STATIC is not
220 * defined.
221 */
222int check_type(sepol_policydb_t *db, char *type) {
223
224 int rc = 1;
225#if defined(LINK_SEPOL_STATIC)
226 policydb_t *d = (policydb_t *)db;
227 hashtab_datum_t dat;
228 dat = hashtab_search(d->p_types.table, type);
229 rc = (dat == NULL) ? 0 : 1;
230#endif
231 return rc;
232}
233
234/**
235 * Validates a key_map against a set of enforcement rules, this
236 * function exits the application on a type that cannot be properly
237 * checked
238 *
239 * @param m
240 * The key map to check
241 * @param lineno
242 * The line number in the source file for the corresponding key map
William Robertsfff29802012-11-27 14:20:34 -0800243 * @return
244 * 1 if valid, 0 if invalid
William Robertsf0e0a942012-08-27 15:41:15 -0700245 */
246static int key_map_validate(key_map *m, int lineno) {
247
248 int rc = 1;
249 int ret = 1;
William Robertsf0e0a942012-08-27 15:41:15 -0700250 char *key = m->name;
251 char *value = m->data;
252 data_type type = m->type;
William Robertsf0e0a942012-08-27 15:41:15 -0700253
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",
William Roberts8d3a1b52015-06-19 09:12:01 -0700262 key, value, lineno, in_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",
William Roberts8d3a1b52015-06-19 09:12:01 -0700271 value, lineno, in_file_name);
Stephen Smalley38084142012-11-28 10:46:18 -0500272 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 }
William Robertsf0e0a942012-08-27 15:41:15 -0700284 else if (!strcasecmp(key, "type") || !strcasecmp(key, "domain")) {
285
286 if(!check_type(pol.db, value)) {
287 log_error("Could not find selinux type \"%s\" on line: %d in file: %s\n", value,
William Roberts8d3a1b52015-06-19 09:12:01 -0700288 lineno, in_file_name);
William Robertsf0e0a942012-08-27 15:41:15 -0700289 rc = 0;
290 }
291 goto out;
292 }
William Robertsf0e0a942012-08-27 15:41:15 -0700293 else if (!strcasecmp(key, "level")) {
294
William Roberts0ae3a8a2012-09-04 11:51:04 -0700295 ret = sepol_mls_check(pol.handle, pol.db, value);
296 if (ret < 0) {
William Robertsae23a1f2012-09-05 12:53:52 -0700297 log_error("Could not find selinux level \"%s\", on line: %d in file: %s\n", value,
William Roberts8d3a1b52015-06-19 09:12:01 -0700298 lineno, in_file_name);
William Roberts0ae3a8a2012-09-04 11:51:04 -0700299 rc = 0;
300 goto out;
William Robertsf0e0a942012-08-27 15:41:15 -0700301 }
302 }
303
William Roberts0ae3a8a2012-09-04 11:51:04 -0700304out:
305 log_info("Key map validate returning: %d\n", rc);
306 return rc;
William Robertsf0e0a942012-08-27 15:41:15 -0700307}
308
309/**
310 * Prints a rule map back to a file
311 * @param fp
312 * The file handle to print too
313 * @param r
314 * The rule map to print
315 */
316static void rule_map_print(FILE *fp, rule_map *r) {
317
William Roberts610a4b12013-10-15 18:26:00 -0700318 size_t i;
William Robertsf0e0a942012-08-27 15:41:15 -0700319 key_map *m;
320
321 for (i = 0; i < r->length; i++) {
322 m = &(r->m[i]);
323 if (i < r->length - 1)
324 fprintf(fp, "%s=%s ", m->name, m->data);
325 else
326 fprintf(fp, "%s=%s", m->name, m->data);
327 }
328}
329
330/**
331 * Compare two rule maps for equality
332 * @param rmA
333 * a rule map to check
334 * @param rmB
335 * a rule map to check
336 * @return
William Robertsae23a1f2012-09-05 12:53:52 -0700337 * a map_match enum indicating the result
William Robertsf0e0a942012-08-27 15:41:15 -0700338 */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700339static map_match rule_map_cmp(rule_map *rmA, rule_map *rmB) {
William Robertsf0e0a942012-08-27 15:41:15 -0700340
William Roberts610a4b12013-10-15 18:26:00 -0700341 size_t i;
342 size_t j;
William Robertsf0e0a942012-08-27 15:41:15 -0700343 int inputs_found = 0;
344 int num_of_matched_inputs = 0;
345 int input_mode = 0;
William Roberts610a4b12013-10-15 18:26:00 -0700346 size_t matches = 0;
William Robertsf0e0a942012-08-27 15:41:15 -0700347 key_map *mA;
348 key_map *mB;
349
William Robertsf0e0a942012-08-27 15:41:15 -0700350 for (i = 0; i < rmA->length; i++) {
351 mA = &(rmA->m[i]);
352
353 for (j = 0; j < rmB->length; j++) {
354 mB = &(rmB->m[j]);
355 input_mode = 0;
356
357 if (mA->type != mB->type)
358 continue;
359
360 if (strcmp(mA->name, mB->name))
361 continue;
362
363 if (strcmp(mA->data, mB->data))
364 continue;
365
366 if (mB->dir != mA->dir)
367 continue;
368 else if (mB->dir == dir_in) {
369 input_mode = 1;
370 inputs_found++;
371 }
372
William Roberts0ae3a8a2012-09-04 11:51:04 -0700373 if (input_mode) {
William Roberts1e8c0612013-04-19 19:06:02 -0700374 log_info("Matched input lines: name=%s data=%s\n", mA->name, mA->data);
William Robertsf0e0a942012-08-27 15:41:15 -0700375 num_of_matched_inputs++;
William Roberts0ae3a8a2012-09-04 11:51:04 -0700376 }
William Robertsf0e0a942012-08-27 15:41:15 -0700377
378 /* Match found, move on */
William Roberts1e8c0612013-04-19 19:06:02 -0700379 log_info("Matched lines: name=%s data=%s", mA->name, mA->data);
William Robertsf0e0a942012-08-27 15:41:15 -0700380 matches++;
381 break;
382 }
383 }
384
385 /* If they all matched*/
William Roberts0ae3a8a2012-09-04 11:51:04 -0700386 if (matches == rmA->length) {
387 log_info("Rule map cmp MATCH\n");
388 return map_matched;
389 }
William Robertsf0e0a942012-08-27 15:41:15 -0700390
391 /* They didn't all match but the input's did */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700392 else if (num_of_matched_inputs == inputs_found) {
393 log_info("Rule map cmp INPUT MATCH\n");
394 return map_input_matched;
395 }
William Robertsf0e0a942012-08-27 15:41:15 -0700396
397 /* They didn't all match, and the inputs didn't match, ie it didn't
398 * match */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700399 else {
400 log_info("Rule map cmp NO MATCH\n");
401 return map_no_matches;
402 }
William Robertsf0e0a942012-08-27 15:41:15 -0700403}
404
405/**
406 * Frees a rule map
407 * @param rm
408 * rule map to be freed.
409 */
Robert Craigc9bb91d2013-11-01 10:24:36 -0400410static void rule_map_free(rule_map *rm,
411 rule_map_switch s __attribute__((unused)) /* only glibc builds, ignored otherwise */) {
William Robertsf0e0a942012-08-27 15:41:15 -0700412
William Roberts610a4b12013-10-15 18:26:00 -0700413 size_t i;
414 size_t len = rm->length;
William Robertsf0e0a942012-08-27 15:41:15 -0700415 for (i = 0; i < len; i++) {
416 key_map *m = &(rm->m[i]);
417 free(m->data);
418 }
419
rpcraig5dbfdc02012-10-23 11:03:47 -0400420/* hdestroy() frees comparsion keys for non glibc */
421#ifdef __GLIBC__
William Robertsf0e0a942012-08-27 15:41:15 -0700422 if(s == rule_map_destroy_key && rm->key)
423 free(rm->key);
rpcraig5dbfdc02012-10-23 11:03:47 -0400424#endif
William Robertsf0e0a942012-08-27 15:41:15 -0700425
426 free(rm);
427}
428
429static void free_kvp(kvp *k) {
430 free(k->key);
431 free(k->value);
432}
433
434/**
William Roberts61846292013-10-15 09:38:24 -0700435 * Checks a rule_map for any variation of KVP's that shouldn't be allowed.
436 * Note that this function logs all errors.
437 *
438 * Current Checks:
439 * 1. That a specified name entry should have a specified seinfo entry as well.
440 * @param rm
441 * The rule map to check for validity.
442 * @return
443 * true if the rule is valid, false otherwise.
444 */
445static bool rule_map_validate(const rule_map *rm) {
446
William Roberts610a4b12013-10-15 18:26:00 -0700447 size_t i;
William Roberts61846292013-10-15 09:38:24 -0700448 bool found_name = false;
449 bool found_seinfo = false;
450 char *name = NULL;
Stephen Smalley7b2bee92013-10-31 09:22:26 -0400451 const key_map *tmp;
William Roberts61846292013-10-15 09:38:24 -0700452
453 for(i=0; i < rm->length; i++) {
454 tmp = &(rm->m[i]);
455
456 if(!strcmp(tmp->name, "name") && tmp->data) {
457 name = tmp->data;
458 found_name = true;
459 }
Stephen Smalleyf4fa7562014-04-04 14:16:46 -0400460 if(!strcmp(tmp->name, "seinfo") && tmp->data && strcmp(tmp->data, "default")) {
William Roberts61846292013-10-15 09:38:24 -0700461 found_seinfo = true;
462 }
463 }
464
465 if(found_name && !found_seinfo) {
Stephen Smalleyf4fa7562014-04-04 14:16:46 -0400466 log_error("No specific seinfo value specified with name=\"%s\", on line: %d: insecure configuration!\n",
William Roberts61846292013-10-15 09:38:24 -0700467 name, rm->lineno);
468 return false;
469 }
470
471 return true;
472}
473
474/**
William Robertsf0e0a942012-08-27 15:41:15 -0700475 * Given a set of key value pairs, this will construct a new rule map.
476 * On error this function calls exit.
477 * @param keys
478 * Keys from a rule line to map
479 * @param num_of_keys
480 * The length of the keys array
481 * @param lineno
482 * The line number the keys were extracted from
483 * @return
484 * A rule map pointer.
485 */
William Roberts610a4b12013-10-15 18:26:00 -0700486static rule_map *rule_map_new(kvp keys[], size_t num_of_keys, int lineno) {
William Robertsf0e0a942012-08-27 15:41:15 -0700487
William Roberts610a4b12013-10-15 18:26:00 -0700488 size_t i = 0, j = 0;
William Roberts61846292013-10-15 09:38:24 -0700489 bool valid_rule;
William Robertsf0e0a942012-08-27 15:41:15 -0700490 rule_map *new_map = NULL;
491 kvp *k = NULL;
492 key_map *r = NULL, *x = NULL;
Stephen Smalley534fb072015-02-13 14:06:08 -0500493 bool seen[KVP_NUM_OF_RULES];
494
495 for (i = 0; i < KVP_NUM_OF_RULES; i++)
496 seen[i] = false;
William Robertsf0e0a942012-08-27 15:41:15 -0700497
498 new_map = calloc(1, (num_of_keys * sizeof(key_map)) + sizeof(rule_map));
499 if (!new_map)
500 goto oom;
501
502 new_map->length = num_of_keys;
503 new_map->lineno = lineno;
504
505 /* For all the keys in a rule line*/
506 for (i = 0; i < num_of_keys; i++) {
507 k = &(keys[i]);
508 r = &(new_map->m[i]);
509
510 for (j = 0; j < KVP_NUM_OF_RULES; j++) {
511 x = &(rules[j]);
512
513 /* Only assign key name to map name */
514 if (strcasecmp(k->key, x->name)) {
515 if (i == KVP_NUM_OF_RULES) {
516 log_error("No match for key: %s\n", k->key);
517 goto err;
518 }
519 continue;
520 }
521
Stephen Smalley534fb072015-02-13 14:06:08 -0500522 if (seen[j]) {
523 log_error("Duplicated key: %s\n", k->key);
524 goto err;
525 }
526 seen[j] = true;
527
William Robertsf0e0a942012-08-27 15:41:15 -0700528 memcpy(r, x, sizeof(key_map));
529
530 /* Assign rule map value to one from file */
531 r->data = strdup(k->value);
532 if (!r->data)
533 goto oom;
534
535 /* Enforce type check*/
William Roberts0ae3a8a2012-09-04 11:51:04 -0700536 log_info("Validating keys!\n");
William Robertsf0e0a942012-08-27 15:41:15 -0700537 if (!key_map_validate(r, lineno)) {
538 log_error("Could not validate\n");
539 goto err;
540 }
541
542 /* Only build key off of inputs*/
543 if (r->dir == dir_in) {
544 char *tmp;
William Robertsb3ab56c2012-09-17 14:35:02 -0700545 int key_len = strlen(k->key);
546 int val_len = strlen(k->value);
547 int l = (new_map->key) ? strlen(new_map->key) : 0;
548 l = l + key_len + val_len;
William Robertsf0e0a942012-08-27 15:41:15 -0700549 l += 1;
550
551 tmp = realloc(new_map->key, l);
552 if (!tmp)
553 goto oom;
554
William Robertsb3ab56c2012-09-17 14:35:02 -0700555 if (!new_map->key)
556 memset(tmp, 0, l);
557
William Robertsf0e0a942012-08-27 15:41:15 -0700558 new_map->key = tmp;
559
William Robertsb3ab56c2012-09-17 14:35:02 -0700560 strncat(new_map->key, k->key, key_len);
561 strncat(new_map->key, k->value, val_len);
William Robertsf0e0a942012-08-27 15:41:15 -0700562 }
563 break;
564 }
565 free_kvp(k);
566 }
567
568 if (new_map->key == NULL) {
569 log_error("Strange, no keys found, input file corrupt perhaps?\n");
570 goto err;
571 }
572
William Roberts61846292013-10-15 09:38:24 -0700573 valid_rule = rule_map_validate(new_map);
574 if(!valid_rule) {
575 /* Error message logged from rule_map_validate() */
576 goto err;
577 }
578
William Robertsf0e0a942012-08-27 15:41:15 -0700579 return new_map;
580
581oom:
582 log_error("Out of memory!\n");
583err:
584 if(new_map) {
585 rule_map_free(new_map, rule_map_destroy_key);
586 for (; i < num_of_keys; i++) {
587 k = &(keys[i]);
588 free_kvp(k);
589 }
590 }
Stephen Smalley534fb072015-02-13 14:06:08 -0500591 return NULL;
William Robertsf0e0a942012-08-27 15:41:15 -0700592}
593
594/**
595 * Print the usage of the program
596 */
597static void usage() {
598 printf(
599 "checkseapp [options] <input file>\n"
600 "Processes an seapp_contexts file specified by argument <input file> (default stdin) "
William Robertsae23a1f2012-09-05 12:53:52 -0700601 "and allows later declarations to override previous ones on a match.\n"
William Robertsf0e0a942012-08-27 15:41:15 -0700602 "Options:\n"
603 "-h - print this help message\n"
604 "-v - enable verbose debugging informations\n"
William Roberts63297212013-04-19 19:06:23 -0700605 "-p policy file - specify policy file for strict checking of output selectors against the policy\n"
William Robertsf0e0a942012-08-27 15:41:15 -0700606 "-o output file - specify output file, default is stdout\n");
607}
608
609static void init() {
610
611 /* If not set on stdin already */
612 if(!input_file) {
613 log_info("Opening input file: %s\n", in_file_name);
614 input_file = fopen(in_file_name, "r");
615 if (!input_file) {
616 log_error("Could not open file: %s error: %s\n", in_file_name, strerror(errno));
617 exit(EXIT_FAILURE);
618 }
619 }
620
621 /* If not set on std out already */
622 if(!output_file) {
623 output_file = fopen(out_file_name, "w+");
624 if (!output_file) {
625 log_error("Could not open file: %s error: %s\n", out_file_name, strerror(errno));
626 exit(EXIT_FAILURE);
627 }
628 }
629
630 if (pol.policy_file_name) {
631
632 log_info("Opening policy file: %s\n", pol.policy_file_name);
633 pol.policy_file = fopen(pol.policy_file_name, "rb");
634 if (!pol.policy_file) {
635 log_error("Could not open file: %s error: %s\n",
636 pol.policy_file_name, strerror(errno));
637 exit(EXIT_FAILURE);
638 }
639
640 pol.handle = sepol_handle_create();
641 if (!pol.handle) {
642 log_error("Could not create sepolicy handle: %s\n",
643 strerror(errno));
644 exit(EXIT_FAILURE);
645 }
646
647 if (sepol_policy_file_create(&pol.pf) < 0) {
648 log_error("Could not create sepolicy file: %s!\n",
649 strerror(errno));
650 exit(EXIT_FAILURE);
651 }
652
653 sepol_policy_file_set_fp(pol.pf, pol.policy_file);
654 sepol_policy_file_set_handle(pol.pf, pol.handle);
655
656 if (sepol_policydb_create(&pol.db) < 0) {
657 log_error("Could not create sepolicy db: %s!\n",
658 strerror(errno));
659 exit(EXIT_FAILURE);
660 }
661
662 if (sepol_policydb_read(pol.db, pol.pf) < 0) {
663 log_error("Could not lod policy file to db: %s!\n",
664 strerror(errno));
665 exit(EXIT_FAILURE);
666 }
667 }
668
669 log_info("Policy file set to: %s\n", (pol.policy_file_name == NULL) ? "None" : pol.policy_file_name);
670 log_info("Input file set to: %s\n", (in_file_name == NULL) ? "stdin" : in_file_name);
671 log_info("Output file set to: %s\n", (out_file_name == NULL) ? "stdout" : out_file_name);
672
William Roberts0ae3a8a2012-09-04 11:51:04 -0700673#if !defined(LINK_SEPOL_STATIC)
William Robertsa53ccf32012-09-17 12:53:44 -0700674 log_warn("LINK_SEPOL_STATIC is not defined\n""Not checking types!");
William Roberts0ae3a8a2012-09-04 11:51:04 -0700675#endif
676
William Robertsf0e0a942012-08-27 15:41:15 -0700677}
678
679/**
680 * Handle parsing and setting the global flags for the command line
681 * options. This function calls exit on failure.
682 * @param argc
683 * argument count
684 * @param argv
685 * argument list
686 */
687static void handle_options(int argc, char *argv[]) {
688
689 int c;
690 int num_of_args;
691
William Robertsf26b6d42015-06-23 10:22:45 -0700692 while ((c = getopt(argc, argv, "ho:p:v")) != -1) {
William Robertsf0e0a942012-08-27 15:41:15 -0700693 switch (c) {
694 case 'h':
695 usage();
696 exit(EXIT_SUCCESS);
697 case 'o':
698 out_file_name = optarg;
699 break;
700 case 'p':
701 pol.policy_file_name = optarg;
702 break;
703 case 'v':
704 log_set_verbose();
705 break;
706 case '?':
707 if (optopt == 'o' || optopt == 'p')
708 log_error("Option -%c requires an argument.\n", optopt);
709 else if (isprint (optopt))
710 log_error("Unknown option `-%c'.\n", optopt);
711 else {
712 log_error(
713 "Unknown option character `\\x%x'.\n",
714 optopt);
William Robertsf0e0a942012-08-27 15:41:15 -0700715 }
William Robertsf0e0a942012-08-27 15:41:15 -0700716 default:
717 exit(EXIT_FAILURE);
718 }
719 }
720
721 num_of_args = argc - optind;
722
723 if (num_of_args > 1) {
724 log_error("Too many arguments, expected 0 or 1, argument, got %d\n", num_of_args);
725 usage();
726 exit(EXIT_FAILURE);
727 } else if (num_of_args == 1) {
728 in_file_name = argv[argc - 1];
729 } else {
730 input_file = stdin;
731 in_file_name = "stdin";
732 }
733
734 if (!out_file_name) {
735 output_file = stdout;
736 out_file_name = "stdout";
737 }
738}
739
740/**
741 * Adds a rule_map double pointer, ie the hash table pointer to the list.
742 * By using a double pointer, the hash table can have a line be overridden
743 * and the value is updated in the list. This function calls exit on failure.
744 * @param rm
745 * the rule_map to add.
746 */
747static void list_add(hash_entry *e) {
748
749 line_order_list *node = malloc(sizeof(line_order_list));
750 if (node == NULL)
751 goto oom;
752
753 node->next = NULL;
754 node->e = e;
755
756 if (list_head == NULL)
757 list_head = list_tail = node;
758 else {
759 list_tail->next = node;
760 list_tail = list_tail->next;
761 }
762 return;
763
764oom:
765 log_error("Out of memory!\n");
766 exit(EXIT_FAILURE);
767}
768
769/**
770 * Free's the rule map list, which ultimatley contains
771 * all the malloc'd rule_maps.
772 */
773static void list_free() {
774 line_order_list *cursor, *tmp;
775 hash_entry *e;
776
777 cursor = list_head;
778 while (cursor) {
779 e = cursor->e;
780 rule_map_free(e->r, rule_map_destroy_key);
781 tmp = cursor;
782 cursor = cursor->next;
783 free(e);
784 free(tmp);
785 }
786}
787
788/**
789 * Adds a rule to the hash table and to the ordered list if needed.
790 * @param rm
791 * The rule map to add.
792 */
793static void rule_add(rule_map *rm) {
794
William Roberts0ae3a8a2012-09-04 11:51:04 -0700795 map_match cmp;
William Robertsf0e0a942012-08-27 15:41:15 -0700796 ENTRY e;
797 ENTRY *f;
798 hash_entry *entry;
799 hash_entry *tmp;
William Robertsf0e0a942012-08-27 15:41:15 -0700800
801 e.key = rm->key;
802
William Roberts0ae3a8a2012-09-04 11:51:04 -0700803 log_info("Searching for key: %s\n", e.key);
William Robertsf0e0a942012-08-27 15:41:15 -0700804 /* Check to see if it has already been added*/
805 f = hsearch(e, FIND);
806
807 /*
808 * Since your only hashing on a partial key, the inputs we need to handle
809 * when you want to override the outputs for a given input set, as well as
810 * checking for duplicate entries.
811 */
812 if(f) {
William Roberts0ae3a8a2012-09-04 11:51:04 -0700813 log_info("Existing entry found!\n");
William Robertsf0e0a942012-08-27 15:41:15 -0700814 tmp = (hash_entry *)f->data;
815 cmp = rule_map_cmp(rm, tmp->r);
Stephen Smalley0b820042015-02-13 14:58:31 -0500816 log_error("Duplicate line detected in file: %s\n"
817 "Lines %d and %d %s!\n",
William Roberts773d4122015-06-11 17:09:25 -0700818 in_file_name, tmp->r->lineno, rm->lineno,
Stephen Smalley0b820042015-02-13 14:58:31 -0500819 map_match_str[cmp]);
820 rule_map_free(rm, rule_map_destroy_key);
821 goto err;
William Robertsf0e0a942012-08-27 15:41:15 -0700822 }
823 /* It wasn't found, just add the rule map to the table */
824 else {
825
826 entry = malloc(sizeof(hash_entry));
827 if (!entry)
828 goto oom;
829
830 entry->r = rm;
831 e.data = entry;
832
833 f = hsearch(e, ENTER);
834 if(f == NULL) {
835 goto oom;
836 }
837
838 /* new entries must be added to the ordered list */
839 entry->r = rm;
840 list_add(entry);
841 }
842
843 return;
844oom:
845 if (e.key)
846 free(e.key);
847 if (entry)
848 free(entry);
849 if (rm)
850 free(rm);
851 log_error("Out of memory in function: %s\n", __FUNCTION__);
852err:
853 exit(EXIT_FAILURE);
854}
855
856/**
857 * Parses the seapp_contexts file and adds them to the
858 * hash table and ordered list entries when it encounters them.
859 * Calls exit on failure.
860 */
861static void parse() {
862
863 char line_buf[BUFSIZ];
864 char *token;
865 unsigned lineno = 0;
866 char *p, *name = NULL, *value = NULL, *saveptr;
867 size_t len;
868 kvp keys[KVP_NUM_OF_RULES];
William Roberts610a4b12013-10-15 18:26:00 -0700869 size_t token_cnt = 0;
William Robertsf0e0a942012-08-27 15:41:15 -0700870
871 while (fgets(line_buf, sizeof line_buf - 1, input_file)) {
872
873 lineno++;
874 log_info("Got line %d\n", lineno);
875 len = strlen(line_buf);
876 if (line_buf[len - 1] == '\n')
Alice Chuf6647eb2012-10-30 16:27:00 -0700877 line_buf[len - 1] = '\0';
William Robertsf0e0a942012-08-27 15:41:15 -0700878 p = line_buf;
879 while (isspace(*p))
880 p++;
Alice Chuf6647eb2012-10-30 16:27:00 -0700881 if (*p == '#' || *p == '\0')
William Robertsf0e0a942012-08-27 15:41:15 -0700882 continue;
883
884 token = strtok_r(p, " \t", &saveptr);
885 if (!token)
886 goto err;
887
888 token_cnt = 0;
889 memset(keys, 0, sizeof(kvp) * KVP_NUM_OF_RULES);
890 while (1) {
William Roberts0ae3a8a2012-09-04 11:51:04 -0700891
William Robertsf0e0a942012-08-27 15:41:15 -0700892 name = token;
893 value = strchr(name, '=');
894 if (!value)
895 goto err;
896 *value++ = 0;
897
898 keys[token_cnt].key = strdup(name);
899 if (!keys[token_cnt].key)
900 goto oom;
901
902 keys[token_cnt].value = strdup(value);
903 if (!keys[token_cnt].value)
904 goto oom;
905
906 token_cnt++;
907
908 token = strtok_r(NULL, " \t", &saveptr);
909 if (!token)
910 break;
911
912 } /*End token parsing */
913
914 rule_map *r = rule_map_new(keys, token_cnt, lineno);
Stephen Smalley534fb072015-02-13 14:06:08 -0500915 if (!r)
916 goto err;
William Robertsf0e0a942012-08-27 15:41:15 -0700917 rule_add(r);
918
919 } /* End file parsing */
920 return;
921
922err:
923 log_error("reading %s, line %u, name %s, value %s\n",
924 in_file_name, lineno, name, value);
925 exit(EXIT_FAILURE);
926oom:
927 log_error("In function %s: Out of memory\n", __FUNCTION__);
928 exit(EXIT_FAILURE);
929}
930
931/**
932 * Should be called after parsing to cause the printing of the rule_maps
933 * stored in the ordered list, head first, which preserves the "first encountered"
934 * ordering.
935 */
936static void output() {
937
938 rule_map *r;
939 line_order_list *cursor;
940 cursor = list_head;
941
942 while (cursor) {
943 r = cursor->e->r;
944 rule_map_print(output_file, r);
945 cursor = cursor->next;
William Robertsa8613182012-09-05 11:23:40 -0700946 fprintf(output_file, "\n");
William Robertsf0e0a942012-08-27 15:41:15 -0700947 }
948}
949
950/**
951 * This function is registered to the at exit handler and should clean up
952 * the programs dynamic resources, such as memory and fd's.
953 */
954static void cleanup() {
955
956 /* Only close this when it was opened by me and not the crt */
957 if (out_file_name && output_file) {
958 log_info("Closing file: %s\n", out_file_name);
959 fclose(output_file);
960 }
961
962 /* Only close this when it was opened by me and not the crt */
963 if (in_file_name && input_file) {
964 log_info("Closing file: %s\n", in_file_name);
965 fclose(input_file);
966 }
967
968 if (pol.policy_file) {
969
970 log_info("Closing file: %s\n", pol.policy_file_name);
971 fclose(pol.policy_file);
972
973 if (pol.db)
974 sepol_policydb_free(pol.db);
975
976 if (pol.pf)
977 sepol_policy_file_free(pol.pf);
978
979 if (pol.handle)
980 sepol_handle_destroy(pol.handle);
981 }
982
983 log_info("Freeing list\n");
984 list_free();
985 hdestroy();
986}
987
988int main(int argc, char *argv[]) {
989 if (!hcreate(TABLE_SIZE)) {
990 log_error("Could not create hash table: %s\n", strerror(errno));
991 exit(EXIT_FAILURE);
992 }
993 atexit(cleanup);
994 handle_options(argc, argv);
995 init();
996 log_info("Starting to parse\n");
997 parse();
998 log_info("Parsing completed, generating output\n");
999 output();
1000 log_info("Success, generated output\n");
1001 exit(EXIT_SUCCESS);
1002}