blob: c487331928f1a188bed5c3f3609f2fb091103b7a [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 { .name = "sebool", .type = dt_string, .dir = dir_in, .data = NULL },
169 /*Outputs*/
170 { .name = "domain", .type = dt_string, .dir = dir_out, .data = NULL },
171 { .name = "type", .type = dt_string, .dir = dir_out, .data = NULL },
172 { .name = "levelFromUid", .type = dt_bool, .dir = dir_out, .data = NULL },
Stephen Smalley38084142012-11-28 10:46:18 -0500173 { .name = "levelFrom", .type = dt_string, .dir = dir_out, .data = NULL },
William Robertsf0e0a942012-08-27 15:41:15 -0700174 { .name = "level", .type = dt_string, .dir = dir_out, .data = NULL },
William Robertsfff29802012-11-27 14:20:34 -0800175};
William Robertsf0e0a942012-08-27 15:41:15 -0700176
177/**
178 * Head pointer to a linked list of
179 * rule map table entries, used for
180 * preserving the order of entries
181 * based on "first encounter"
182 */
183static line_order_list *list_head = NULL;
184
185/**
186 * Pointer to the tail of the list for
187 * quick appends to the end of the list
188 */
189static line_order_list *list_tail = NULL;
190
191/**
192 * Send a logging message to a file
193 * @param out
194 * Output file to send message too
195 * @param prefix
196 * A special prefix to write to the file, such as "Error:"
197 * @param fmt
198 * The printf style formatter to use, such as "%d"
199 */
William Roberts1e8c0612013-04-19 19:06:02 -0700200static void __attribute__ ((format(printf, 3, 4)))
201log_msg(FILE *out, const char *prefix, const char *fmt, ...) {
202
William Robertsf0e0a942012-08-27 15:41:15 -0700203 fprintf(out, "%s", prefix);
204 va_list args;
205 va_start(args, fmt);
206 vfprintf(out, fmt, args);
207 va_end(args);
208}
209
210/**
211 * Checks for a type in the policy.
212 * @param db
213 * The policy db to search
214 * @param type
215 * The type to search for
216 * @return
217 * 1 if the type is found, 0 otherwise.
218 * @warning
219 * This function always returns 1 if libsepol is not linked
220 * statically to this executable and LINK_SEPOL_STATIC is not
221 * defined.
222 */
223int check_type(sepol_policydb_t *db, char *type) {
224
225 int rc = 1;
226#if defined(LINK_SEPOL_STATIC)
227 policydb_t *d = (policydb_t *)db;
228 hashtab_datum_t dat;
229 dat = hashtab_search(d->p_types.table, type);
230 rc = (dat == NULL) ? 0 : 1;
231#endif
232 return rc;
233}
234
235/**
236 * Validates a key_map against a set of enforcement rules, this
237 * function exits the application on a type that cannot be properly
238 * checked
239 *
240 * @param m
241 * The key map to check
242 * @param lineno
243 * The line number in the source file for the corresponding key map
William Robertsfff29802012-11-27 14:20:34 -0800244 * @return
245 * 1 if valid, 0 if invalid
William Robertsf0e0a942012-08-27 15:41:15 -0700246 */
247static int key_map_validate(key_map *m, int lineno) {
248
249 int rc = 1;
250 int ret = 1;
William Robertsf0e0a942012-08-27 15:41:15 -0700251 int resp;
252 char *key = m->name;
253 char *value = m->data;
254 data_type type = m->type;
255 sepol_bool_key_t *se_key;
256
William Roberts0ae3a8a2012-09-04 11:51:04 -0700257 log_info("Validating %s=%s\n", key, value);
258
William Robertsf0e0a942012-08-27 15:41:15 -0700259 /* Booleans can always be checked for sanity */
260 if (type == dt_bool && (!strcmp("true", value) || !strcmp("false", value))) {
261 goto out;
262 }
263 else if (type == dt_bool) {
William Robertsae23a1f2012-09-05 12:53:52 -0700264 log_error("Expected boolean value got: %s=%s on line: %d in file: %s\n",
265 key, value, lineno, out_file_name);
William Robertsf0e0a942012-08-27 15:41:15 -0700266 rc = 0;
267 goto out;
268 }
269
Stephen Smalley38084142012-11-28 10:46:18 -0500270 if (!strcasecmp(key, "levelFrom") &&
271 (strcasecmp(value, "none") && strcasecmp(value, "all") &&
272 strcasecmp(value, "app") && strcasecmp(value, "user"))) {
273 log_error("Unknown levelFrom=%s on line: %d in file: %s\n",
274 value, lineno, out_file_name);
275 rc = 0;
276 goto out;
277 }
278
William Robertsf0e0a942012-08-27 15:41:15 -0700279 /*
William Roberts63297212013-04-19 19:06:23 -0700280 * If there is no policy file present,
281 * then it is not going to enforce the types against the policy so just return.
William Robertsf0e0a942012-08-27 15:41:15 -0700282 * User and name cannot really be checked.
283 */
284 if (!pol.policy_file) {
285 goto out;
286 }
287 else if (!strcasecmp(key, "sebool")) {
288
289 ret = sepol_bool_key_create(pol.handle, value, &se_key);
290 if (ret < 0) {
291 log_error("Could not create selinux boolean key, error: %s\n",
292 strerror(errno));
293 rc = 0;
294 goto out;
295 }
296
297 ret = sepol_bool_exists(pol.handle, pol.db, se_key, &resp);
298 if (ret < 0) {
299 log_error("Could not check selinux boolean, error: %s\n",
300 strerror(errno));
301 rc = 0;
William Robertsa53ccf32012-09-17 12:53:44 -0700302 sepol_bool_key_free(se_key);
303 goto out;
William Robertsf0e0a942012-08-27 15:41:15 -0700304 }
305
306 if(!resp) {
307 log_error("Could not find selinux boolean \"%s\" on line: %d in file: %s\n",
308 value, lineno, out_file_name);
309 rc = 0;
William Robertsa53ccf32012-09-17 12:53:44 -0700310 sepol_bool_key_free(se_key);
311 goto out;
William Robertsf0e0a942012-08-27 15:41:15 -0700312 }
William Robertsa53ccf32012-09-17 12:53:44 -0700313 sepol_bool_key_free(se_key);
William Robertsf0e0a942012-08-27 15:41:15 -0700314 }
315 else if (!strcasecmp(key, "type") || !strcasecmp(key, "domain")) {
316
317 if(!check_type(pol.db, value)) {
318 log_error("Could not find selinux type \"%s\" on line: %d in file: %s\n", value,
319 lineno, out_file_name);
320 rc = 0;
321 }
322 goto out;
323 }
William Robertsf0e0a942012-08-27 15:41:15 -0700324 else if (!strcasecmp(key, "level")) {
325
William Roberts0ae3a8a2012-09-04 11:51:04 -0700326 ret = sepol_mls_check(pol.handle, pol.db, value);
327 if (ret < 0) {
William Robertsae23a1f2012-09-05 12:53:52 -0700328 log_error("Could not find selinux level \"%s\", on line: %d in file: %s\n", value,
329 lineno, out_file_name);
William Roberts0ae3a8a2012-09-04 11:51:04 -0700330 rc = 0;
331 goto out;
William Robertsf0e0a942012-08-27 15:41:15 -0700332 }
333 }
334
William Roberts0ae3a8a2012-09-04 11:51:04 -0700335out:
336 log_info("Key map validate returning: %d\n", rc);
337 return rc;
William Robertsf0e0a942012-08-27 15:41:15 -0700338}
339
340/**
341 * Prints a rule map back to a file
342 * @param fp
343 * The file handle to print too
344 * @param r
345 * The rule map to print
346 */
347static void rule_map_print(FILE *fp, rule_map *r) {
348
William Roberts610a4b12013-10-15 18:26:00 -0700349 size_t i;
William Robertsf0e0a942012-08-27 15:41:15 -0700350 key_map *m;
351
352 for (i = 0; i < r->length; i++) {
353 m = &(r->m[i]);
354 if (i < r->length - 1)
355 fprintf(fp, "%s=%s ", m->name, m->data);
356 else
357 fprintf(fp, "%s=%s", m->name, m->data);
358 }
359}
360
361/**
362 * Compare two rule maps for equality
363 * @param rmA
364 * a rule map to check
365 * @param rmB
366 * a rule map to check
367 * @return
William Robertsae23a1f2012-09-05 12:53:52 -0700368 * a map_match enum indicating the result
William Robertsf0e0a942012-08-27 15:41:15 -0700369 */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700370static map_match rule_map_cmp(rule_map *rmA, rule_map *rmB) {
William Robertsf0e0a942012-08-27 15:41:15 -0700371
William Roberts610a4b12013-10-15 18:26:00 -0700372 size_t i;
373 size_t j;
William Robertsf0e0a942012-08-27 15:41:15 -0700374 int inputs_found = 0;
375 int num_of_matched_inputs = 0;
376 int input_mode = 0;
William Roberts610a4b12013-10-15 18:26:00 -0700377 size_t matches = 0;
William Robertsf0e0a942012-08-27 15:41:15 -0700378 key_map *mA;
379 key_map *mB;
380
381 if (rmA->length != rmB->length)
William Roberts0ae3a8a2012-09-04 11:51:04 -0700382 return map_no_matches;
William Robertsf0e0a942012-08-27 15:41:15 -0700383
384 for (i = 0; i < rmA->length; i++) {
385 mA = &(rmA->m[i]);
386
387 for (j = 0; j < rmB->length; j++) {
388 mB = &(rmB->m[j]);
389 input_mode = 0;
390
391 if (mA->type != mB->type)
392 continue;
393
394 if (strcmp(mA->name, mB->name))
395 continue;
396
397 if (strcmp(mA->data, mB->data))
398 continue;
399
400 if (mB->dir != mA->dir)
401 continue;
402 else if (mB->dir == dir_in) {
403 input_mode = 1;
404 inputs_found++;
405 }
406
William Roberts0ae3a8a2012-09-04 11:51:04 -0700407 if (input_mode) {
William Roberts1e8c0612013-04-19 19:06:02 -0700408 log_info("Matched input lines: name=%s data=%s\n", mA->name, mA->data);
William Robertsf0e0a942012-08-27 15:41:15 -0700409 num_of_matched_inputs++;
William Roberts0ae3a8a2012-09-04 11:51:04 -0700410 }
William Robertsf0e0a942012-08-27 15:41:15 -0700411
412 /* Match found, move on */
William Roberts1e8c0612013-04-19 19:06:02 -0700413 log_info("Matched lines: name=%s data=%s", mA->name, mA->data);
William Robertsf0e0a942012-08-27 15:41:15 -0700414 matches++;
415 break;
416 }
417 }
418
419 /* If they all matched*/
William Roberts0ae3a8a2012-09-04 11:51:04 -0700420 if (matches == rmA->length) {
421 log_info("Rule map cmp MATCH\n");
422 return map_matched;
423 }
William Robertsf0e0a942012-08-27 15:41:15 -0700424
425 /* They didn't all match but the input's did */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700426 else if (num_of_matched_inputs == inputs_found) {
427 log_info("Rule map cmp INPUT MATCH\n");
428 return map_input_matched;
429 }
William Robertsf0e0a942012-08-27 15:41:15 -0700430
431 /* They didn't all match, and the inputs didn't match, ie it didn't
432 * match */
William Roberts0ae3a8a2012-09-04 11:51:04 -0700433 else {
434 log_info("Rule map cmp NO MATCH\n");
435 return map_no_matches;
436 }
William Robertsf0e0a942012-08-27 15:41:15 -0700437}
438
439/**
440 * Frees a rule map
441 * @param rm
442 * rule map to be freed.
443 */
Robert Craigc9bb91d2013-11-01 10:24:36 -0400444static void rule_map_free(rule_map *rm,
445 rule_map_switch s __attribute__((unused)) /* only glibc builds, ignored otherwise */) {
William Robertsf0e0a942012-08-27 15:41:15 -0700446
William Roberts610a4b12013-10-15 18:26:00 -0700447 size_t i;
448 size_t len = rm->length;
William Robertsf0e0a942012-08-27 15:41:15 -0700449 for (i = 0; i < len; i++) {
450 key_map *m = &(rm->m[i]);
451 free(m->data);
452 }
453
rpcraig5dbfdc02012-10-23 11:03:47 -0400454/* hdestroy() frees comparsion keys for non glibc */
455#ifdef __GLIBC__
William Robertsf0e0a942012-08-27 15:41:15 -0700456 if(s == rule_map_destroy_key && rm->key)
457 free(rm->key);
rpcraig5dbfdc02012-10-23 11:03:47 -0400458#endif
William Robertsf0e0a942012-08-27 15:41:15 -0700459
460 free(rm);
461}
462
463static void free_kvp(kvp *k) {
464 free(k->key);
465 free(k->value);
466}
467
468/**
William Roberts61846292013-10-15 09:38:24 -0700469 * Checks a rule_map for any variation of KVP's that shouldn't be allowed.
470 * Note that this function logs all errors.
471 *
472 * Current Checks:
473 * 1. That a specified name entry should have a specified seinfo entry as well.
474 * @param rm
475 * The rule map to check for validity.
476 * @return
477 * true if the rule is valid, false otherwise.
478 */
479static bool rule_map_validate(const rule_map *rm) {
480
William Roberts610a4b12013-10-15 18:26:00 -0700481 size_t i;
William Roberts61846292013-10-15 09:38:24 -0700482 bool found_name = false;
483 bool found_seinfo = false;
484 char *name = NULL;
Stephen Smalley7b2bee92013-10-31 09:22:26 -0400485 const key_map *tmp;
William Roberts61846292013-10-15 09:38:24 -0700486
487 for(i=0; i < rm->length; i++) {
488 tmp = &(rm->m[i]);
489
490 if(!strcmp(tmp->name, "name") && tmp->data) {
491 name = tmp->data;
492 found_name = true;
493 }
Stephen Smalleyf4fa7562014-04-04 14:16:46 -0400494 if(!strcmp(tmp->name, "seinfo") && tmp->data && strcmp(tmp->data, "default")) {
William Roberts61846292013-10-15 09:38:24 -0700495 found_seinfo = true;
496 }
497 }
498
499 if(found_name && !found_seinfo) {
Stephen Smalleyf4fa7562014-04-04 14:16:46 -0400500 log_error("No specific seinfo value specified with name=\"%s\", on line: %d: insecure configuration!\n",
William Roberts61846292013-10-15 09:38:24 -0700501 name, rm->lineno);
502 return false;
503 }
504
505 return true;
506}
507
508/**
William Robertsf0e0a942012-08-27 15:41:15 -0700509 * Given a set of key value pairs, this will construct a new rule map.
510 * On error this function calls exit.
511 * @param keys
512 * Keys from a rule line to map
513 * @param num_of_keys
514 * The length of the keys array
515 * @param lineno
516 * The line number the keys were extracted from
517 * @return
518 * A rule map pointer.
519 */
William Roberts610a4b12013-10-15 18:26:00 -0700520static rule_map *rule_map_new(kvp keys[], size_t num_of_keys, int lineno) {
William Robertsf0e0a942012-08-27 15:41:15 -0700521
William Roberts610a4b12013-10-15 18:26:00 -0700522 size_t i = 0, j = 0;
William Roberts61846292013-10-15 09:38:24 -0700523 bool valid_rule;
William Robertsf0e0a942012-08-27 15:41:15 -0700524 rule_map *new_map = NULL;
525 kvp *k = NULL;
526 key_map *r = NULL, *x = NULL;
Stephen Smalley534fb072015-02-13 14:06:08 -0500527 bool seen[KVP_NUM_OF_RULES];
528
529 for (i = 0; i < KVP_NUM_OF_RULES; i++)
530 seen[i] = false;
William Robertsf0e0a942012-08-27 15:41:15 -0700531
532 new_map = calloc(1, (num_of_keys * sizeof(key_map)) + sizeof(rule_map));
533 if (!new_map)
534 goto oom;
535
536 new_map->length = num_of_keys;
537 new_map->lineno = lineno;
538
539 /* For all the keys in a rule line*/
540 for (i = 0; i < num_of_keys; i++) {
541 k = &(keys[i]);
542 r = &(new_map->m[i]);
543
544 for (j = 0; j < KVP_NUM_OF_RULES; j++) {
545 x = &(rules[j]);
546
547 /* Only assign key name to map name */
548 if (strcasecmp(k->key, x->name)) {
549 if (i == KVP_NUM_OF_RULES) {
550 log_error("No match for key: %s\n", k->key);
551 goto err;
552 }
553 continue;
554 }
555
Stephen Smalley534fb072015-02-13 14:06:08 -0500556 if (seen[j]) {
557 log_error("Duplicated key: %s\n", k->key);
558 goto err;
559 }
560 seen[j] = true;
561
William Robertsf0e0a942012-08-27 15:41:15 -0700562 memcpy(r, x, sizeof(key_map));
563
564 /* Assign rule map value to one from file */
565 r->data = strdup(k->value);
566 if (!r->data)
567 goto oom;
568
569 /* Enforce type check*/
William Roberts0ae3a8a2012-09-04 11:51:04 -0700570 log_info("Validating keys!\n");
William Robertsf0e0a942012-08-27 15:41:15 -0700571 if (!key_map_validate(r, lineno)) {
572 log_error("Could not validate\n");
573 goto err;
574 }
575
576 /* Only build key off of inputs*/
577 if (r->dir == dir_in) {
578 char *tmp;
William Robertsb3ab56c2012-09-17 14:35:02 -0700579 int key_len = strlen(k->key);
580 int val_len = strlen(k->value);
581 int l = (new_map->key) ? strlen(new_map->key) : 0;
582 l = l + key_len + val_len;
William Robertsf0e0a942012-08-27 15:41:15 -0700583 l += 1;
584
585 tmp = realloc(new_map->key, l);
586 if (!tmp)
587 goto oom;
588
William Robertsb3ab56c2012-09-17 14:35:02 -0700589 if (!new_map->key)
590 memset(tmp, 0, l);
591
William Robertsf0e0a942012-08-27 15:41:15 -0700592 new_map->key = tmp;
593
William Robertsb3ab56c2012-09-17 14:35:02 -0700594 strncat(new_map->key, k->key, key_len);
595 strncat(new_map->key, k->value, val_len);
William Robertsf0e0a942012-08-27 15:41:15 -0700596 }
597 break;
598 }
599 free_kvp(k);
600 }
601
602 if (new_map->key == NULL) {
603 log_error("Strange, no keys found, input file corrupt perhaps?\n");
604 goto err;
605 }
606
William Roberts61846292013-10-15 09:38:24 -0700607 valid_rule = rule_map_validate(new_map);
608 if(!valid_rule) {
609 /* Error message logged from rule_map_validate() */
610 goto err;
611 }
612
William Robertsf0e0a942012-08-27 15:41:15 -0700613 return new_map;
614
615oom:
616 log_error("Out of memory!\n");
617err:
618 if(new_map) {
619 rule_map_free(new_map, rule_map_destroy_key);
620 for (; i < num_of_keys; i++) {
621 k = &(keys[i]);
622 free_kvp(k);
623 }
624 }
Stephen Smalley534fb072015-02-13 14:06:08 -0500625 return NULL;
William Robertsf0e0a942012-08-27 15:41:15 -0700626}
627
628/**
629 * Print the usage of the program
630 */
631static void usage() {
632 printf(
633 "checkseapp [options] <input file>\n"
634 "Processes an seapp_contexts file specified by argument <input file> (default stdin) "
William Robertsae23a1f2012-09-05 12:53:52 -0700635 "and allows later declarations to override previous ones on a match.\n"
William Robertsf0e0a942012-08-27 15:41:15 -0700636 "Options:\n"
637 "-h - print this help message\n"
638 "-v - enable verbose debugging informations\n"
William Roberts63297212013-04-19 19:06:23 -0700639 "-p policy file - specify policy file for strict checking of output selectors against the policy\n"
William Robertsf0e0a942012-08-27 15:41:15 -0700640 "-o output file - specify output file, default is stdout\n");
641}
642
643static void init() {
644
645 /* If not set on stdin already */
646 if(!input_file) {
647 log_info("Opening input file: %s\n", in_file_name);
648 input_file = fopen(in_file_name, "r");
649 if (!input_file) {
650 log_error("Could not open file: %s error: %s\n", in_file_name, strerror(errno));
651 exit(EXIT_FAILURE);
652 }
653 }
654
655 /* If not set on std out already */
656 if(!output_file) {
657 output_file = fopen(out_file_name, "w+");
658 if (!output_file) {
659 log_error("Could not open file: %s error: %s\n", out_file_name, strerror(errno));
660 exit(EXIT_FAILURE);
661 }
662 }
663
664 if (pol.policy_file_name) {
665
666 log_info("Opening policy file: %s\n", pol.policy_file_name);
667 pol.policy_file = fopen(pol.policy_file_name, "rb");
668 if (!pol.policy_file) {
669 log_error("Could not open file: %s error: %s\n",
670 pol.policy_file_name, strerror(errno));
671 exit(EXIT_FAILURE);
672 }
673
674 pol.handle = sepol_handle_create();
675 if (!pol.handle) {
676 log_error("Could not create sepolicy handle: %s\n",
677 strerror(errno));
678 exit(EXIT_FAILURE);
679 }
680
681 if (sepol_policy_file_create(&pol.pf) < 0) {
682 log_error("Could not create sepolicy file: %s!\n",
683 strerror(errno));
684 exit(EXIT_FAILURE);
685 }
686
687 sepol_policy_file_set_fp(pol.pf, pol.policy_file);
688 sepol_policy_file_set_handle(pol.pf, pol.handle);
689
690 if (sepol_policydb_create(&pol.db) < 0) {
691 log_error("Could not create sepolicy db: %s!\n",
692 strerror(errno));
693 exit(EXIT_FAILURE);
694 }
695
696 if (sepol_policydb_read(pol.db, pol.pf) < 0) {
697 log_error("Could not lod policy file to db: %s!\n",
698 strerror(errno));
699 exit(EXIT_FAILURE);
700 }
701 }
702
703 log_info("Policy file set to: %s\n", (pol.policy_file_name == NULL) ? "None" : pol.policy_file_name);
704 log_info("Input file set to: %s\n", (in_file_name == NULL) ? "stdin" : in_file_name);
705 log_info("Output file set to: %s\n", (out_file_name == NULL) ? "stdout" : out_file_name);
706
William Roberts0ae3a8a2012-09-04 11:51:04 -0700707#if !defined(LINK_SEPOL_STATIC)
William Robertsa53ccf32012-09-17 12:53:44 -0700708 log_warn("LINK_SEPOL_STATIC is not defined\n""Not checking types!");
William Roberts0ae3a8a2012-09-04 11:51:04 -0700709#endif
710
William Robertsf0e0a942012-08-27 15:41:15 -0700711}
712
713/**
714 * Handle parsing and setting the global flags for the command line
715 * options. This function calls exit on failure.
716 * @param argc
717 * argument count
718 * @param argv
719 * argument list
720 */
721static void handle_options(int argc, char *argv[]) {
722
723 int c;
724 int num_of_args;
725
William Roberts63297212013-04-19 19:06:23 -0700726 while ((c = getopt(argc, argv, "ho:p:sv")) != -1) {
William Robertsf0e0a942012-08-27 15:41:15 -0700727 switch (c) {
728 case 'h':
729 usage();
730 exit(EXIT_SUCCESS);
731 case 'o':
732 out_file_name = optarg;
733 break;
734 case 'p':
735 pol.policy_file_name = optarg;
736 break;
737 case 'v':
738 log_set_verbose();
739 break;
740 case '?':
741 if (optopt == 'o' || optopt == 'p')
742 log_error("Option -%c requires an argument.\n", optopt);
743 else if (isprint (optopt))
744 log_error("Unknown option `-%c'.\n", optopt);
745 else {
746 log_error(
747 "Unknown option character `\\x%x'.\n",
748 optopt);
William Robertsf0e0a942012-08-27 15:41:15 -0700749 }
William Robertsf0e0a942012-08-27 15:41:15 -0700750 default:
751 exit(EXIT_FAILURE);
752 }
753 }
754
755 num_of_args = argc - optind;
756
757 if (num_of_args > 1) {
758 log_error("Too many arguments, expected 0 or 1, argument, got %d\n", num_of_args);
759 usage();
760 exit(EXIT_FAILURE);
761 } else if (num_of_args == 1) {
762 in_file_name = argv[argc - 1];
763 } else {
764 input_file = stdin;
765 in_file_name = "stdin";
766 }
767
768 if (!out_file_name) {
769 output_file = stdout;
770 out_file_name = "stdout";
771 }
772}
773
774/**
775 * Adds a rule_map double pointer, ie the hash table pointer to the list.
776 * By using a double pointer, the hash table can have a line be overridden
777 * and the value is updated in the list. This function calls exit on failure.
778 * @param rm
779 * the rule_map to add.
780 */
781static void list_add(hash_entry *e) {
782
783 line_order_list *node = malloc(sizeof(line_order_list));
784 if (node == NULL)
785 goto oom;
786
787 node->next = NULL;
788 node->e = e;
789
790 if (list_head == NULL)
791 list_head = list_tail = node;
792 else {
793 list_tail->next = node;
794 list_tail = list_tail->next;
795 }
796 return;
797
798oom:
799 log_error("Out of memory!\n");
800 exit(EXIT_FAILURE);
801}
802
803/**
804 * Free's the rule map list, which ultimatley contains
805 * all the malloc'd rule_maps.
806 */
807static void list_free() {
808 line_order_list *cursor, *tmp;
809 hash_entry *e;
810
811 cursor = list_head;
812 while (cursor) {
813 e = cursor->e;
814 rule_map_free(e->r, rule_map_destroy_key);
815 tmp = cursor;
816 cursor = cursor->next;
817 free(e);
818 free(tmp);
819 }
820}
821
822/**
823 * Adds a rule to the hash table and to the ordered list if needed.
824 * @param rm
825 * The rule map to add.
826 */
827static void rule_add(rule_map *rm) {
828
William Roberts0ae3a8a2012-09-04 11:51:04 -0700829 map_match cmp;
William Robertsf0e0a942012-08-27 15:41:15 -0700830 ENTRY e;
831 ENTRY *f;
832 hash_entry *entry;
833 hash_entry *tmp;
William Robertsf0e0a942012-08-27 15:41:15 -0700834
835 e.key = rm->key;
836
William Roberts0ae3a8a2012-09-04 11:51:04 -0700837 log_info("Searching for key: %s\n", e.key);
William Robertsf0e0a942012-08-27 15:41:15 -0700838 /* Check to see if it has already been added*/
839 f = hsearch(e, FIND);
840
841 /*
842 * Since your only hashing on a partial key, the inputs we need to handle
843 * when you want to override the outputs for a given input set, as well as
844 * checking for duplicate entries.
845 */
846 if(f) {
William Roberts0ae3a8a2012-09-04 11:51:04 -0700847 log_info("Existing entry found!\n");
William Robertsf0e0a942012-08-27 15:41:15 -0700848 tmp = (hash_entry *)f->data;
849 cmp = rule_map_cmp(rm, tmp->r);
Stephen Smalley0b820042015-02-13 14:58:31 -0500850 log_error("Duplicate line detected in file: %s\n"
851 "Lines %d and %d %s!\n",
852 out_file_name, tmp->r->lineno, rm->lineno,
853 map_match_str[cmp]);
854 rule_map_free(rm, rule_map_destroy_key);
855 goto err;
William Robertsf0e0a942012-08-27 15:41:15 -0700856 }
857 /* It wasn't found, just add the rule map to the table */
858 else {
859
860 entry = malloc(sizeof(hash_entry));
861 if (!entry)
862 goto oom;
863
864 entry->r = rm;
865 e.data = entry;
866
867 f = hsearch(e, ENTER);
868 if(f == NULL) {
869 goto oom;
870 }
871
872 /* new entries must be added to the ordered list */
873 entry->r = rm;
874 list_add(entry);
875 }
876
877 return;
878oom:
879 if (e.key)
880 free(e.key);
881 if (entry)
882 free(entry);
883 if (rm)
884 free(rm);
885 log_error("Out of memory in function: %s\n", __FUNCTION__);
886err:
887 exit(EXIT_FAILURE);
888}
889
890/**
891 * Parses the seapp_contexts file and adds them to the
892 * hash table and ordered list entries when it encounters them.
893 * Calls exit on failure.
894 */
895static void parse() {
896
897 char line_buf[BUFSIZ];
898 char *token;
899 unsigned lineno = 0;
900 char *p, *name = NULL, *value = NULL, *saveptr;
901 size_t len;
902 kvp keys[KVP_NUM_OF_RULES];
William Roberts610a4b12013-10-15 18:26:00 -0700903 size_t token_cnt = 0;
William Robertsf0e0a942012-08-27 15:41:15 -0700904
905 while (fgets(line_buf, sizeof line_buf - 1, input_file)) {
906
907 lineno++;
908 log_info("Got line %d\n", lineno);
909 len = strlen(line_buf);
910 if (line_buf[len - 1] == '\n')
Alice Chuf6647eb2012-10-30 16:27:00 -0700911 line_buf[len - 1] = '\0';
William Robertsf0e0a942012-08-27 15:41:15 -0700912 p = line_buf;
913 while (isspace(*p))
914 p++;
Alice Chuf6647eb2012-10-30 16:27:00 -0700915 if (*p == '#' || *p == '\0')
William Robertsf0e0a942012-08-27 15:41:15 -0700916 continue;
917
918 token = strtok_r(p, " \t", &saveptr);
919 if (!token)
920 goto err;
921
922 token_cnt = 0;
923 memset(keys, 0, sizeof(kvp) * KVP_NUM_OF_RULES);
924 while (1) {
William Roberts0ae3a8a2012-09-04 11:51:04 -0700925
William Robertsf0e0a942012-08-27 15:41:15 -0700926 name = token;
927 value = strchr(name, '=');
928 if (!value)
929 goto err;
930 *value++ = 0;
931
932 keys[token_cnt].key = strdup(name);
933 if (!keys[token_cnt].key)
934 goto oom;
935
936 keys[token_cnt].value = strdup(value);
937 if (!keys[token_cnt].value)
938 goto oom;
939
940 token_cnt++;
941
942 token = strtok_r(NULL, " \t", &saveptr);
943 if (!token)
944 break;
945
946 } /*End token parsing */
947
948 rule_map *r = rule_map_new(keys, token_cnt, lineno);
Stephen Smalley534fb072015-02-13 14:06:08 -0500949 if (!r)
950 goto err;
William Robertsf0e0a942012-08-27 15:41:15 -0700951 rule_add(r);
952
953 } /* End file parsing */
954 return;
955
956err:
957 log_error("reading %s, line %u, name %s, value %s\n",
958 in_file_name, lineno, name, value);
959 exit(EXIT_FAILURE);
960oom:
961 log_error("In function %s: Out of memory\n", __FUNCTION__);
962 exit(EXIT_FAILURE);
963}
964
965/**
966 * Should be called after parsing to cause the printing of the rule_maps
967 * stored in the ordered list, head first, which preserves the "first encountered"
968 * ordering.
969 */
970static void output() {
971
972 rule_map *r;
973 line_order_list *cursor;
974 cursor = list_head;
975
976 while (cursor) {
977 r = cursor->e->r;
978 rule_map_print(output_file, r);
979 cursor = cursor->next;
William Robertsa8613182012-09-05 11:23:40 -0700980 fprintf(output_file, "\n");
William Robertsf0e0a942012-08-27 15:41:15 -0700981 }
982}
983
984/**
985 * This function is registered to the at exit handler and should clean up
986 * the programs dynamic resources, such as memory and fd's.
987 */
988static void cleanup() {
989
990 /* Only close this when it was opened by me and not the crt */
991 if (out_file_name && output_file) {
992 log_info("Closing file: %s\n", out_file_name);
993 fclose(output_file);
994 }
995
996 /* Only close this when it was opened by me and not the crt */
997 if (in_file_name && input_file) {
998 log_info("Closing file: %s\n", in_file_name);
999 fclose(input_file);
1000 }
1001
1002 if (pol.policy_file) {
1003
1004 log_info("Closing file: %s\n", pol.policy_file_name);
1005 fclose(pol.policy_file);
1006
1007 if (pol.db)
1008 sepol_policydb_free(pol.db);
1009
1010 if (pol.pf)
1011 sepol_policy_file_free(pol.pf);
1012
1013 if (pol.handle)
1014 sepol_handle_destroy(pol.handle);
1015 }
1016
1017 log_info("Freeing list\n");
1018 list_free();
1019 hdestroy();
1020}
1021
1022int main(int argc, char *argv[]) {
1023 if (!hcreate(TABLE_SIZE)) {
1024 log_error("Could not create hash table: %s\n", strerror(errno));
1025 exit(EXIT_FAILURE);
1026 }
1027 atexit(cleanup);
1028 handle_options(argc, argv);
1029 init();
1030 log_info("Starting to parse\n");
1031 parse();
1032 log_info("Parsing completed, generating output\n");
1033 output();
1034 log_info("Success, generated output\n");
1035 exit(EXIT_SUCCESS);
1036}