diff options
author | Jeff Vander Stoep <jeffv@google.com> | 2015-04-14 22:08:25 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2015-04-14 22:08:26 +0000 |
commit | 8e39f8fa1467ffc146a6c27af43c3e47430e7b33 (patch) | |
tree | a585e2feb2f5f87b5813e62da34d7807978ec592 | |
parent | 3978a33242e150061365a33311e1b09c223e1011 (diff) | |
parent | f6d78c1272406f632a6f3f059bcc1f35f9560716 (diff) | |
download | checkpolicy-8e39f8fa1467ffc146a6c27af43c3e47430e7b33.tar.gz |
Merge "Add ioctl command whitelisting rules"
-rw-r--r-- | policy_define.c | 615 | ||||
-rw-r--r-- | policy_define.h | 2 | ||||
-rw-r--r-- | policy_parse.y | 34 |
3 files changed, 650 insertions, 1 deletions
diff --git a/policy_define.c b/policy_define.c index a6c5d65..63bdff1 100644 --- a/policy_define.c +++ b/policy_define.c @@ -39,6 +39,7 @@ #include <arpa/inet.h> #include <stdlib.h> #include <limits.h> +#include <ctype.h> #include <sepol/policydb/expand.h> #include <sepol/policydb/policydb.h> @@ -1726,6 +1727,619 @@ avrule_t *define_cond_pol_list(avrule_t * avlist, avrule_t * sl) return sl; } +#define operation_perm_test(x, p) (1 & (p[x >> 5] >> (x & 0x1f))) +#define operation_perm_set(x, p) (p[x >> 5] |= (1 << (x & 0x1f))) +#define operation_perm_clear(x, p) (p[x >> 5] &= ~(1 << (x & 0x1f))) + +typedef struct av_operations_range { + uint16_t low; + uint16_t high; +} av_operations_range_t; + +struct av_operations_range_list { + uint8_t omit; + av_operations_range_t range; + struct av_operations_range_list *next; +}; + +int avrule_sort_operations( + struct av_operations_range_list **rangehead) +{ + struct av_operations_range_list *r, *r2, *sorted, *sortedhead = NULL; + + /* order list by range.low */ + for (r = *rangehead; r != NULL; r = r->next) { + sorted = malloc(sizeof(struct av_operations_range_list)); + if (sorted == NULL) + goto error; + memcpy(sorted, r, sizeof(struct av_operations_range_list)); + sorted->next = NULL; + if (sortedhead == NULL) { + sortedhead = sorted; + continue; + } + for (r2 = sortedhead; r2 != NULL; r2 = r2->next) { + if (sorted->range.low < r2->range.low) { + /* range is the new head */ + sorted->next = r2; + sortedhead = sorted; + break; + } else if ((r2 ->next != NULL) && + (r->range.low < r2->next->range.low)) { + /* insert range between elements */ + sorted->next = r2->next; + r2->next = sorted; + break; + } else if (r2->next == NULL) { + /* range is the new tail*/ + r2->next = sorted; + break; + } + } + } + + r = *rangehead; + while (r != NULL) { + r2 = r; + r = r->next; + free(r2); + } + *rangehead = sortedhead; + return 0; +error: + yyerror("out of memory"); + return -1; +} + +int avrule_merge_operations(struct av_operations_range_list **rangehead) +{ + struct av_operations_range_list *r, *tmp; + r = *rangehead; + while (r != NULL && r->next != NULL) { + /* merge */ + if ((r->range.high + 1) >= r->next->range.low) { + /* keep the higher of the two */ + if (r->range.high < r->next->range.high) + r->range.high = r->next->range.high; + tmp = r->next; + r->next = r->next->next; + free(tmp); + continue; + } + r = r->next; + } + return 0; +} + +int avrule_read_operations(struct av_operations_range_list **rangehead) +{ + char *id; + struct av_operations_range_list *rnew, *r = NULL; + *rangehead = NULL; + uint8_t omit = 0; + + /* read in all the operations */ + while ((id = queue_remove(id_queue))) { + if (strcmp(id,"~") == 0) { + /* these are values to be omitted */ + free(id); + omit = 1; + } else if (strcmp(id,"-") == 0) { + /* high value of range */ + free(id); + id = queue_remove(id_queue); + r->range.high = (uint16_t) strtoul(id,NULL,0); + if (r->range.high < r->range.low) { + yyerror("Ioctl ranges must be in ascending order."); + return -1; + } + free(id); + } else { + /* read in new low value */ + rnew = malloc(sizeof(struct av_operations_range_list)); + if (rnew == NULL) + goto error; + rnew->next = NULL; + if (*rangehead == NULL) { + *rangehead = rnew; + r = *rangehead; + } else { + r->next = rnew; + r = r->next; + } + rnew->range.low = (uint16_t) strtoul(id,NULL,0); + rnew->range.high = rnew->range.low; + free(id); + } + } + r = *rangehead; + r->omit = omit; + return 0; +error: + yyerror("out of memory"); + return -1; +} + +/* flip to included ranges */ +int avrule_omit_operations(struct av_operations_range_list **rangehead) +{ + struct av_operations_range_list *rnew, *r, *newhead, *r2; + + rnew = calloc(1, sizeof(struct av_operations_range_list)); + if (!rnew) + goto error; + + newhead = rnew; + + r = *rangehead; + r2 = newhead; + + if (r->range.low == 0) { + r2->range.low = r->range.high + 1; + r = r->next; + } else { + r2->range.low = 0; + } + + while (r) { + r2->range.high = r->range.low - 1; + rnew = calloc(1, sizeof(struct av_operations_range_list)); + if (!rnew) + goto error; + r2->next = rnew; + r2 = r2->next; + + r2->range.low = r->range.high + 1; + if (!r->next) + r2->range.high = 0xffff; + r = r->next; + } + + r = *rangehead; + while (r != NULL) { + r2 = r; + r = r->next; + free(r2); + } + *rangehead = newhead; + return 0; + +error: + yyerror("out of memory"); + return -1; +} + +int avrule_operation_ranges(struct av_operations_range_list **rangelist) +{ + struct av_operations_range_list *rangehead; + uint8_t omit; + + /* read in ranges to include and omit */ + if (avrule_read_operations(&rangehead)) + return -1; + omit = rangehead->omit; + if (rangehead == NULL) { + yyerror("error processing ioctl operations"); + return -1; + } + /* sort and merge the input operations */ + if (avrule_sort_operations(&rangehead)) + return -1; + if (avrule_merge_operations(&rangehead)) + return -1; + /* flip ranges if these are ommited*/ + if (omit) { + if (avrule_omit_operations(&rangehead)) + return -1; + } + + *rangelist = rangehead; + return 0; +} + +int define_te_avtab_operation_helper(int which, avrule_t ** rule) +{ + char *id; + class_perm_node_t *perms, *tail = NULL, *cur_perms = NULL; + ebitmap_t tclasses; + ebitmap_node_t *node; + avrule_t *avrule; + unsigned int i; + int add = 1, ret = 0; + + avrule = (avrule_t *) malloc(sizeof(avrule_t)); + if (!avrule) { + yyerror("out of memory"); + ret = -1; + goto out; + } + avrule_init(avrule); + avrule->specified = which; + avrule->line = policydb_lineno; + avrule->source_line = source_lineno; + avrule->source_filename = strdup(source_file); + avrule->ops = NULL; + if (!avrule->source_filename) { + yyerror("out of memory"); + return -1; + } + + while ((id = queue_remove(id_queue))) { + if (set_types + (&avrule->stypes, id, &add, + which == AVRULE_NEVERALLOW ? 1 : 0)) { + ret = -1; + goto out; + } + } + add = 1; + while ((id = queue_remove(id_queue))) { + if (strcmp(id, "self") == 0) { + free(id); + avrule->flags |= RULE_SELF; + continue; + } + if (set_types + (&avrule->ttypes, id, &add, + which == AVRULE_NEVERALLOW ? 1 : 0)) { + ret = -1; + goto out; + } + } + + ebitmap_init(&tclasses); + ret = read_classes(&tclasses); + if (ret) + goto out; + + perms = NULL; + ebitmap_for_each_bit(&tclasses, node, i) { + if (!ebitmap_node_get_bit(node, i)) + continue; + cur_perms = + (class_perm_node_t *) malloc(sizeof(class_perm_node_t)); + if (!cur_perms) { + yyerror("out of memory"); + ret = -1; + goto out; + } + class_perm_node_init(cur_perms); + cur_perms->tclass = i + 1; + if (!perms) + perms = cur_perms; + if (tail) + tail->next = cur_perms; + tail = cur_perms; + } + + ebitmap_destroy(&tclasses); + + avrule->perms = perms; + *rule = avrule; + +out: + return ret; +} + +/* index of the u32 containing the permission */ +#define OP_IDX(x) (x >> 5) +/* set bits 0 through x-1 within the u32 */ +#define OP_SETBITS(x) ((1 << (x & 0x1f)) - 1) +/* low value for this u32 */ +#define OP_LOW(x) (x << 5) +/* high value for this u32 */ +#define OP_HIGH(x) (((x + 1) << 5) - 1) +void avrule_operation_setrangebits(uint16_t low, uint16_t high, av_operations_t *ops) +{ + unsigned int i; + uint16_t h = high + 1; + /* for each u32 that this low-high range touches, set type permissions */ + for (i = OP_IDX(low); i <= OP_IDX(high); i++) { + /* set all bits in u32 */ + if ((low <= OP_LOW(i)) && (high >= OP_HIGH(i))) + ops->perms[i] |= ~0U; + /* set low bits */ + else if ((low <= OP_LOW(i)) && (high < OP_HIGH(i))) + ops->perms[i] |= OP_SETBITS(h); + /* set high bits */ + else if ((low > OP_LOW(i)) && (high >= OP_HIGH(i))) + ops->perms[i] |= ~0U - OP_SETBITS(low); + /* set middle bits */ + else if ((low > OP_LOW(i)) && (high <= OP_HIGH(i))) + ops->perms[i] |= OP_SETBITS(h) - OP_SETBITS(low); + } +} + +int avrule_operation_used(av_operations_t *ops) +{ + unsigned int i; + + for (i = 0; i < sizeof(ops->perms)/sizeof(ops->perms[0]); i++) { + if (ops->perms[i]) + return 1; + } + return 0; +} + +#define OP_TYPE(x) (x >> 8) +#define OP_NUM(x) (x & 0xff) +#define OP_CMD(type, num) ((type << 8) + num) +int avrule_operation_partialtype(struct av_operations_range_list *rangelist, + av_operations_t *complete_type, + av_operations_t **operations) +{ + struct av_operations_range_list *r; + av_operations_t *ops; + uint8_t low, high; + + ops = calloc(1, sizeof(av_operations_t)); + if (!ops) { + yyerror("out of memory"); + return - 1; + } + + r = rangelist; + while(r) { + low = OP_TYPE(r->range.low); + high = OP_TYPE(r->range.high); + if (complete_type) { + if (!operation_perm_test(low, complete_type->perms)) + operation_perm_set(low, ops->perms); + if (!operation_perm_test(high, complete_type->perms)) + operation_perm_set(high, ops->perms); + } else { + operation_perm_set(low, ops->perms); + operation_perm_set(high, ops->perms); + } + r = r->next; + } + if (avrule_operation_used(ops)) { + *operations = ops; + } else { + free(ops); + *operations = NULL; + } + return 0; + +} + +int avrule_operation_completetype(struct av_operations_range_list *rangelist, + av_operations_t **operations) +{ + struct av_operations_range_list *r; + av_operations_t *ops; + uint16_t low, high; + ops = calloc(1, sizeof(av_operations_t)); + if (!ops) { + yyerror("out of memory"); + return - 1; + } + + r = rangelist; + while(r) { + /* + * Any type that has numbers 0x00 - 0xff is a complete type, + * + * if command number = 0xff, then round high up to next type, + * else 0x00 - 0xfe keep current type + * of this range. temporarily u32 for the + 1 + * to account for possible rollover before right shift + */ + high = OP_TYPE((uint32_t) (r->range.high + 1)); + /* if 0x00 keep current type else 0x01 - 0xff round up to next type */ + low = OP_TYPE(r->range.low); + if (OP_NUM(r->range.low)) + low++; + if (high > low) + avrule_operation_setrangebits(low, high - 1, ops); + r = r->next; + } + if (avrule_operation_used(ops)) { + *operations = ops; + } else { + free(ops); + *operations = NULL; + } + return 0; +} + +int avrule_operation_num(struct av_operations_range_list *rangelist, + av_operations_t **operations, unsigned int type) +{ + struct av_operations_range_list *r; + av_operations_t *ops; + uint16_t low, high; + + *operations = NULL; + ops = calloc(1, sizeof(av_operations_t)); + if (!ops) { + yyerror("out of memory"); + return - 1; + } + + r = rangelist; + /* for the passed in types, find the ranges that apply */ + while (r) { + low = r->range.low; + high = r->range.high; + if ((type != OP_TYPE(low)) && (type != OP_TYPE(high))) { + r = r->next; + continue; + } + + if (type == OP_TYPE(low)) { + if (high > OP_CMD(type, 0xff)) + high = OP_CMD(type, 0xff); + + } else { + if (low < OP_CMD(type, 0)) + low = OP_CMD(type, 0); + } + + low = OP_NUM(low); + high = OP_NUM(high); + avrule_operation_setrangebits(low, high, ops); + ops->type = type; + r = r->next; + } + + if (avrule_operation_used(ops)) { + *operations = ops; + } else { + free(ops); + *operations = NULL; + } + return 0; +} + +void avrule_operation_freeranges(struct av_operations_range_list *rangelist) +{ + struct av_operations_range_list *r, *tmp; + r = rangelist; + while (r) { + tmp = r; + r = r->next; + free(tmp); + } +} + +unsigned int operation_for_each_bit(unsigned int *bit, av_operations_t *ops) +{ + unsigned int i; + for (i = *bit; i < sizeof(ops->perms)*8; i++) { + if (operation_perm_test(i,ops->perms)) { + operation_perm_clear(i, ops->perms); + *bit = i; + return 1; + } + } + return 0; +} + +int avrule_cpy(avrule_t *dest, avrule_t *src) +{ + class_perm_node_t *src_perms; + class_perm_node_t *dest_perms, *dest_tail; + dest_tail = NULL; + + avrule_init(dest); + dest->specified = src->specified; + dest->flags = src->flags; + if (type_set_cpy(&dest->stypes, &src->stypes)) { + yyerror("out of memory"); + return - 1; + } + if (type_set_cpy(&dest->ttypes, &src->ttypes)) { + yyerror("out of memory"); + return - 1; + } + dest->line = src->line; + dest->source_filename = strdup(source_file); + dest->source_line = src->source_line; + + /* increment through the class perms and copy over */ + src_perms = src->perms; + while (src_perms) { + dest_perms = (class_perm_node_t *) calloc(1, sizeof(class_perm_node_t)); + class_perm_node_init(dest_perms); + if (!dest_perms) { + yyerror("out of memory"); + return -1; + } + if (!dest->perms) + dest->perms = dest_perms; + else + dest_tail->next = dest_perms; + + dest_perms->tclass = src_perms->tclass; + dest_perms->data = src_perms->data; + dest_perms->next = NULL; + dest_tail = dest_perms; + src_perms = src_perms->next; + } + return 0; +} + +int define_te_avtab_operation(int which) +{ + char *id; + avrule_t *avrule_template; + avrule_t *avrule; + struct av_operations_range_list *rangelist; + av_operations_t *complete_type, *partial_type, *ops; + unsigned int i; + + if (pass == 1) { + for (i = 0; i < 4; i++) { + while ((id = queue_remove(id_queue))) + free(id); + } + return 0; + } + + /* populate avrule template with source/target/tclass */ + if (define_te_avtab_operation_helper(which, &avrule_template)) + return -1; + + /* organize operation ranges */ + if (avrule_operation_ranges(&rangelist)) + return -1; + + /* create rule for ioctl operation types that are entirely enabled */ + if (avrule_operation_completetype(rangelist, &complete_type)) + return -1; + if (complete_type) { + avrule = (avrule_t *) calloc(1, sizeof(avrule_t)); + if (!avrule) { + yyerror("out of memory"); + return -1; + } + if (avrule_cpy(avrule, avrule_template)) + return -1; + avrule->ops = complete_type; + if (which == AVRULE_OPNUM_ALLOWED) + avrule->specified = AVRULE_OPTYPE_ALLOWED; + else if (which == AVRULE_OPNUM_AUDITALLOW) + avrule->specified = AVRULE_OPTYPE_AUDITALLOW; + else if (which == AVRULE_OPNUM_DONTAUDIT) + avrule->specified = AVRULE_OPTYPE_DONTAUDIT; + + append_avrule(avrule); + } + + /* flag ioctl types that are partially enabled */ + if (avrule_operation_partialtype(rangelist, complete_type, &partial_type)) + return -1; + + if (!partial_type || !avrule_operation_used(partial_type)) + goto done; + + /* create rule for each partially enabled type */ + i = 0; + while (operation_for_each_bit(&i, partial_type)) { + if (avrule_operation_num(rangelist, &ops, i)) + return -1; + + if (ops) { + avrule = (avrule_t *) calloc(1, sizeof(avrule_t)); + if (!avrule) { + yyerror("out of memory"); + return -1; + } + if (avrule_cpy(avrule, avrule_template)) + return -1; + avrule->ops = ops; + append_avrule(avrule); + } + } + +done: + if (partial_type) + free(partial_type); + + return 0; +} + int define_te_avtab_helper(int which, avrule_t ** rule) { char *id; @@ -1750,6 +2364,7 @@ int define_te_avtab_helper(int which, avrule_t ** rule) avrule->line = policydb_lineno; avrule->source_line = source_lineno; avrule->source_filename = strdup(source_file); + avrule->ops = NULL; if (!avrule->source_filename) { yyerror("out of memory"); return -1; diff --git a/policy_define.h b/policy_define.h index 4ef0f4f..4b25b1f 100644 --- a/policy_define.h +++ b/policy_define.h @@ -9,7 +9,6 @@ * for NULL (ie 0) because that is a potentially valid return. */ #define COND_ERR ((avrule_t *)-1) - #define TRUE 1 #define FALSE 0 @@ -58,6 +57,7 @@ int define_roleattribute(void); int define_filename_trans(void); int define_sens(void); int define_te_avtab(int which); +int define_te_avtab_operation(int which); int define_typealias(void); int define_typeattribute(void); int define_typebounds(void); diff --git a/policy_parse.y b/policy_parse.y index 15c8997..3e89067 100644 --- a/policy_parse.y +++ b/policy_parse.y @@ -454,6 +454,9 @@ te_avtab_def : allow_def | auditdeny_def | dontaudit_def | neverallow_def + | operation_allow_def + | operation_auditallow_def + | operation_dontaudit_def ; allow_def : ALLOW names names ':' names names ';' {if (define_te_avtab(AVRULE_ALLOWED)) return -1; } @@ -470,6 +473,15 @@ dontaudit_def : DONTAUDIT names names ':' names names ';' neverallow_def : NEVERALLOW names names ':' names names ';' {if (define_te_avtab(AVRULE_NEVERALLOW)) return -1; } ; +operation_allow_def : ALLOW names names ':' names operations ';' + {if (define_te_avtab_operation(AVRULE_OPNUM_ALLOWED)) return -1; } + ; +operation_auditallow_def: AUDITALLOW names names ':' names operations ';' + {if (define_te_avtab_operation(AVRULE_OPNUM_AUDITALLOW)) return -1; } + ; +operation_dontaudit_def : DONTAUDIT names names ':' names operations ';' + {if (define_te_avtab_operation(AVRULE_OPNUM_DONTAUDIT)) return -1; } + ; attribute_role_def : ATTRIBUTE_ROLE identifier ';' {if (define_attrib_role()) return -1; } ; @@ -730,6 +742,28 @@ genfs_context_def : GENFSCON filesystem path '-' identifier security_context_def ipv4_addr_def : IPV4_ADDR { if (insert_id(yytext,0)) return -1; } ; +operations : operation + { if (insert_separator(0)) return -1; } + | nested_operation_set + { if (insert_separator(0)) return -1; } + | tilde operation + { if (insert_id("~", 0)) return -1; } + | tilde nested_operation_set + { if (insert_id("~", 0)) return -1; + if (insert_separator(0)) return -1; } + ; +nested_operation_set : '{' nested_operation_list '}' + ; +nested_operation_list : nested_operation_element + | nested_operation_list nested_operation_element + ; +nested_operation_element: operation '-' { if (insert_id("-", 0)) return -1; } operation + | operation + | nested_operation_set + ; +operation : number + { if (insert_id(yytext,0)) return -1; } + ; security_context_def : identifier ':' identifier ':' identifier opt_mls_range_def ; opt_mls_range_def : ':' mls_range_def |