On 04/22/2015 04:53 PM, Jeff Vander Stoep wrote: > Adds support for new policy statements whitelisting individual ioctl > commands. Ioctls provide many of the operations necessary for driver control. > The typical driver supports a device specific set of operations accessible > by the ioctl system call and specified by the command argument. SELinux > provides per operation access control to many system operations e.g. chown, > kill, setuid, ipc_lock, etc. Ioclts on the other hand are granted on a per > file descriptor basis using the ioctl permission, meaning that the set of > operations provided by the driver are granted on an all-or-nothing basis. > In some cases this may be acceptable, but often the same driver provides a > large and diverse set of operations such as benign and necessary functionality > as well as dangerous capabilities or access to system information that should > be restricted. > > Example policy: > allow <source> <target>:<class> { 0x8900-0x8905 0x8910 } > auditallow <source> <target>:<class> 0x8901 > > The ioctl permission is still required in order to make an ioctl call. If no > individual ioctl commands are specified, only the ioctl permission is > checked by the kernel - i.e. status quo. This allows ioctl whitelisting to > done in a targeted manner, protecting desired drivers without requiring every > ioctl command to be known and specified before use and otherwise allowing > existing policy to be used as-is. > > This only implements ioctl whitelisting support for monolithic kernel policies > built via checkpolicy. Support for modules and CIL remains to be done. > > Bug: 19419509 > Change-Id: I198e8c9279b94d8ce4ae5625018daa99577ee970 > Signed-off-by: Jeff Vander Stoep <jeffv@xxxxxxxxxx> Acked-by: Stephen Smalley <sds@xxxxxxxxxxxxx> Thanks, applied. > --- > checkpolicy/policy_define.c | 615 +++++++++++++++++++++++++++++ > checkpolicy/policy_define.h | 2 +- > checkpolicy/policy_parse.y | 34 ++ > libsepol/include/sepol/policydb/avtab.h | 37 +- > libsepol/include/sepol/policydb/policydb.h | 39 +- > libsepol/src/avtab.c | 66 +++- > libsepol/src/expand.c | 109 ++++- > libsepol/src/policydb.c | 2 +- > libsepol/src/write.c | 35 +- > 9 files changed, 886 insertions(+), 53 deletions(-) > > diff --git a/checkpolicy/policy_define.c b/checkpolicy/policy_define.c > index de01f6f..093bded 100644 > --- a/checkpolicy/policy_define.c > +++ b/checkpolicy/policy_define.c > @@ -40,6 +40,7 @@ > #include <stdlib.h> > #include <limits.h> > #include <inttypes.h> > +#include <ctype.h> > > #include <sepol/policydb/expand.h> > #include <sepol/policydb/policydb.h> > @@ -1727,6 +1728,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; > @@ -1751,6 +2365,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/checkpolicy/policy_define.h b/checkpolicy/policy_define.h > index a87ced3..43c7c08 100644 > --- a/checkpolicy/policy_define.h > +++ b/checkpolicy/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 > > @@ -59,6 +58,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/checkpolicy/policy_parse.y b/checkpolicy/policy_parse.y > index 8b81f04..059b7b8 100644 > --- a/checkpolicy/policy_parse.y > +++ b/checkpolicy/policy_parse.y > @@ -457,6 +457,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; } > @@ -473,6 +476,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; } > ; > @@ -737,6 +749,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 > diff --git a/libsepol/include/sepol/policydb/avtab.h b/libsepol/include/sepol/policydb/avtab.h > index 3f56a0e..2ea821c 100644 > --- a/libsepol/include/sepol/policydb/avtab.h > +++ b/libsepol/include/sepol/policydb/avtab.h > @@ -50,22 +50,37 @@ typedef struct avtab_key { > uint16_t source_type; > uint16_t target_type; > uint16_t target_class; > -#define AVTAB_ALLOWED 1 > -#define AVTAB_AUDITALLOW 2 > -#define AVTAB_AUDITDENY 4 > -#define AVTAB_NEVERALLOW 128 > -#define AVTAB_AV (AVTAB_ALLOWED | AVTAB_AUDITALLOW | AVTAB_AUDITDENY) > -#define AVTAB_TRANSITION 16 > -#define AVTAB_MEMBER 32 > -#define AVTAB_CHANGE 64 > -#define AVTAB_TYPE (AVTAB_TRANSITION | AVTAB_MEMBER | AVTAB_CHANGE) > -#define AVTAB_ENABLED_OLD 0x80000000 > -#define AVTAB_ENABLED 0x8000 /* reserved for used in cond_avtab */ > +#define AVTAB_ALLOWED 0x0001 > +#define AVTAB_AUDITALLOW 0x0002 > +#define AVTAB_AUDITDENY 0x0004 > +#define AVTAB_NEVERALLOW 0x0080 > +#define AVTAB_AV (AVTAB_ALLOWED | AVTAB_AUDITALLOW | AVTAB_AUDITDENY) > +#define AVTAB_TRANSITION 0x0010 > +#define AVTAB_MEMBER 0x0020 > +#define AVTAB_CHANGE 0x0040 > +#define AVTAB_TYPE (AVTAB_TRANSITION | AVTAB_MEMBER | AVTAB_CHANGE) > +#define AVTAB_OPNUM_ALLOWED 0x0100 > +#define AVTAB_OPNUM_AUDITALLOW 0x0200 > +#define AVTAB_OPNUM_DONTAUDIT 0x0400 > +#define AVTAB_OPNUM (AVTAB_OPNUM_ALLOWED | AVTAB_OPNUM_AUDITALLOW | AVTAB_OPNUM_DONTAUDIT) > +#define AVTAB_OPTYPE_ALLOWED 0x1000 > +#define AVTAB_OPTYPE_AUDITALLOW 0x2000 > +#define AVTAB_OPTYPE_DONTAUDIT 0x4000 > +#define AVTAB_OPTYPE (AVTAB_OPTYPE_ALLOWED | AVTAB_OPTYPE_AUDITALLOW | AVTAB_OPTYPE_DONTAUDIT) > +#define AVTAB_OP (AVTAB_OPNUM | AVTAB_OPTYPE) > +#define AVTAB_ENABLED_OLD 0x80000000 > +#define AVTAB_ENABLED 0x8000 /* reserved for used in cond_avtab */ > uint16_t specified; /* what fields are specified */ > } avtab_key_t; > > +typedef struct avtab_operations { > + uint8_t type; > + uint32_t perms[8]; > +} avtab_operations_t; > + > typedef struct avtab_datum { > uint32_t data; /* access vector or type */ > + avtab_operations_t *ops; > } avtab_datum_t; > > typedef struct avtab_node *avtab_ptr_t; > diff --git a/libsepol/include/sepol/policydb/policydb.h b/libsepol/include/sepol/policydb/policydb.h > index 31efc3a..1d8310c 100644 > --- a/libsepol/include/sepol/policydb/policydb.h > +++ b/libsepol/include/sepol/policydb/policydb.h > @@ -241,25 +241,43 @@ typedef struct class_perm_node { > struct class_perm_node *next; > } class_perm_node_t; > > +typedef struct av_operations { > + uint8_t type; > + /* 256 bits of ioctl number permissions */ > + uint32_t perms[8]; > +} av_operations_t; > + > typedef struct avrule { > /* these typedefs are almost exactly the same as those in avtab.h - they are > * here because of the need to include neverallow and dontaudit messages */ > -#define AVRULE_ALLOWED 1 > -#define AVRULE_AUDITALLOW 2 > -#define AVRULE_AUDITDENY 4 > -#define AVRULE_DONTAUDIT 8 > -#define AVRULE_NEVERALLOW 128 > +#define AVRULE_ALLOWED 0x0001 > +#define AVRULE_AUDITALLOW 0x0002 > +#define AVRULE_AUDITDENY 0x0004 > +#define AVRULE_DONTAUDIT 0x0008 > +#define AVRULE_NEVERALLOW 0x0080 > #define AVRULE_AV (AVRULE_ALLOWED | AVRULE_AUDITALLOW | AVRULE_AUDITDENY | AVRULE_DONTAUDIT | AVRULE_NEVERALLOW) > -#define AVRULE_TRANSITION 16 > -#define AVRULE_MEMBER 32 > -#define AVRULE_CHANGE 64 > +#define AVRULE_TRANSITION 0x0010 > +#define AVRULE_MEMBER 0x0020 > +#define AVRULE_CHANGE 0x0040 > #define AVRULE_TYPE (AVRULE_TRANSITION | AVRULE_MEMBER | AVRULE_CHANGE) > +#define AVRULE_OPNUM_ALLOWED 0x0100 > +#define AVRULE_OPNUM_AUDITALLOW 0x0200 > +#define AVRULE_OPNUM_DONTAUDIT 0x0400 > +#define AVRULE_OPNUM (AVRULE_OPNUM_ALLOWED | AVRULE_OPNUM_AUDITALLOW | \ > + AVRULE_OPNUM_DONTAUDIT) > +#define AVRULE_OPTYPE_ALLOWED 0x1000 > +#define AVRULE_OPTYPE_AUDITALLOW 0x2000 > +#define AVRULE_OPTYPE_DONTAUDIT 0x4000 > +#define AVRULE_OPTYPE (AVRULE_OPTYPE_ALLOWED | AVRULE_OPTYPE_AUDITALLOW | \ > + AVRULE_OPTYPE_DONTAUDIT) > +#define AVRULE_OP (AVRULE_OPNUM | AVRULE_OPTYPE) > uint32_t specified; > #define RULE_SELF 1 > uint32_t flags; > type_set_t stypes; > type_set_t ttypes; > class_perm_node_t *perms; > + av_operations_t * ops; > unsigned long line; /* line number from policy.conf where > * this rule originated */ > /* source file name and line number (e.g. .te file) */ > @@ -690,11 +708,12 @@ extern int policydb_set_target_platform(policydb_t *p, int platform); > #define POLICYDB_VERSION_NEW_OBJECT_DEFAULTS 27 > #define POLICYDB_VERSION_DEFAULT_TYPE 28 > #define POLICYDB_VERSION_CONSTRAINT_NAMES 29 > -#define POLICYDB_VERSION_XEN_DEVICETREE 30 > +#define POLICYDB_VERSION_XEN_DEVICETREE 30 /* Xen-specific */ > +#define POLICYDB_VERSION_IOCTL_OPERATIONS 30 /* Linux-specific */ > > /* Range of policy versions we understand*/ > #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE > -#define POLICYDB_VERSION_MAX POLICYDB_VERSION_XEN_DEVICETREE > +#define POLICYDB_VERSION_MAX POLICYDB_VERSION_IOCTL_OPERATIONS > > /* Module versions and specific changes*/ > #define MOD_POLICYDB_VERSION_BASE 4 > diff --git a/libsepol/src/avtab.c b/libsepol/src/avtab.c > index d6041d4..d3745fe 100644 > --- a/libsepol/src/avtab.c > +++ b/libsepol/src/avtab.c > @@ -93,12 +93,28 @@ avtab_insert_node(avtab_t * h, int hvalue, avtab_ptr_t prev, avtab_key_t * key, > avtab_datum_t * datum) > { > avtab_ptr_t newnode; > + avtab_operations_t *ops; > + > newnode = (avtab_ptr_t) malloc(sizeof(struct avtab_node)); > if (newnode == NULL) > return NULL; > memset(newnode, 0, sizeof(struct avtab_node)); > newnode->key = *key; > - newnode->datum = *datum; > + > + if (key->specified & AVTAB_OP) { > + ops = calloc(1, sizeof(avtab_operations_t)); > + if (ops == NULL) { > + free(newnode); > + return NULL; > + } > + if (datum->ops) /* else caller populates ops*/ > + *ops = *(datum->ops); > + > + newnode->datum.ops = ops; > + } else { > + newnode->datum = *datum; > + } > + > if (prev) { > newnode->next = prev->next; > prev->next = newnode; > @@ -127,8 +143,11 @@ int avtab_insert(avtab_t * h, avtab_key_t * key, avtab_datum_t * datum) > if (key->source_type == cur->key.source_type && > key->target_type == cur->key.target_type && > key->target_class == cur->key.target_class && > - (specified & cur->key.specified)) > + (specified & cur->key.specified)) { > + if (specified & AVTAB_OPNUM) > + break; > return SEPOL_EEXIST; > + } > if (key->source_type < cur->key.source_type) > break; > if (key->source_type == cur->key.source_type && > @@ -396,23 +415,32 @@ static uint16_t spec_order[] = { > AVTAB_AUDITALLOW, > AVTAB_TRANSITION, > AVTAB_CHANGE, > - AVTAB_MEMBER > + AVTAB_MEMBER, > + AVTAB_OPNUM_ALLOWED, > + AVTAB_OPNUM_AUDITALLOW, > + AVTAB_OPNUM_DONTAUDIT, > + AVTAB_OPTYPE_ALLOWED, > + AVTAB_OPTYPE_AUDITALLOW, > + AVTAB_OPTYPE_DONTAUDIT > }; > > int avtab_read_item(struct policy_file *fp, uint32_t vers, avtab_t * a, > int (*insertf) (avtab_t * a, avtab_key_t * k, > avtab_datum_t * d, void *p), void *p) > { > + uint8_t buf8; > uint16_t buf16[4], enabled; > - uint32_t buf32[7], items, items2, val; > + uint32_t buf32[8], items, items2, val; > avtab_key_t key; > avtab_datum_t datum; > + avtab_operations_t ops; > unsigned set; > unsigned int i; > int rc; > > memset(&key, 0, sizeof(avtab_key_t)); > memset(&datum, 0, sizeof(avtab_datum_t)); > + memset(&ops, 0, sizeof(avtab_operations_t)); > > if (vers < POLICYDB_VERSION_AVTAB) { > rc = next_entry(buf32, fp, sizeof(uint32_t)); > @@ -505,12 +533,34 @@ int avtab_read_item(struct policy_file *fp, uint32_t vers, avtab_t * a, > return -1; > } > > - rc = next_entry(buf32, fp, sizeof(uint32_t)); > - if (rc < 0) { > - ERR(fp->handle, "truncated entry"); > + if ((vers < POLICYDB_VERSION_IOCTL_OPERATIONS) && > + (key.specified & AVTAB_OP)) { > + ERR(fp->handle, "policy version %u does not support ioctl " > + "operation rules and one was specified\n", vers); > return -1; > + } else if (key.specified & AVTAB_OP) { > + rc = next_entry(&buf8, fp, sizeof(uint8_t)); > + if (rc < 0) { > + ERR(fp->handle, "truncated entry"); > + return -1; > + } > + ops.type = buf8; > + rc = next_entry(buf32, fp, sizeof(uint32_t)*8); > + if (rc < 0) { > + ERR(fp->handle, "truncated entry"); > + return -1; > + } > + for (i = 0; i < ARRAY_SIZE(ops.perms); i++) > + ops.perms[i] = le32_to_cpu(buf32[i]); > + datum.ops = &ops; > + } else { > + rc = next_entry(buf32, fp, sizeof(uint32_t)); > + if (rc < 0) { > + ERR(fp->handle, "truncated entry"); > + return -1; > + } > + datum.data = le32_to_cpu(*buf32); > } > - datum.data = le32_to_cpu(*buf32); > return insertf(a, &key, &datum, p); > } > > diff --git a/libsepol/src/expand.c b/libsepol/src/expand.c > index a8b1115..b999890 100644 > --- a/libsepol/src/expand.c > +++ b/libsepol/src/expand.c > @@ -1603,13 +1603,29 @@ static int expand_range_trans(expand_state_t * state, > */ > static avtab_ptr_t find_avtab_node(sepol_handle_t * handle, > avtab_t * avtab, avtab_key_t * key, > - cond_av_list_t ** cond) > + cond_av_list_t ** cond, > + av_operations_t *operations) > { > avtab_ptr_t node; > avtab_datum_t avdatum; > cond_av_list_t *nl; > + int type_match = 0; > > - node = avtab_search_node(avtab, key); > + /* AVTAB_OPNUM entries are not necessarily unique */ > + if (key->specified & AVTAB_OPNUM) { > + node = avtab_search_node(avtab, key); > + while (node) { > + if (node->datum.ops->type == operations->type) { > + type_match = 1; > + break; > + } > + node = avtab_search_node_next(node, key->specified); > + } > + if (!type_match) > + node = NULL; > + } else { > + node = avtab_search_node(avtab, key); > + } > > /* If this is for conditional policies, keep searching in case > the node is part of my conditional avtab. */ > @@ -1733,7 +1749,7 @@ static int expand_terule_helper(sepol_handle_t * handle, > return EXPAND_RULE_CONFLICT; > } > > - node = find_avtab_node(handle, avtab, &avkey, cond); > + node = find_avtab_node(handle, avtab, &avkey, cond, NULL); > if (!node) > return -1; > if (enabled) { > @@ -1764,13 +1780,15 @@ static int expand_avrule_helper(sepol_handle_t * handle, > cond_av_list_t ** cond, > uint32_t stype, uint32_t ttype, > class_perm_node_t * perms, avtab_t * avtab, > - int enabled) > + int enabled, av_operations_t *operations) > { > avtab_key_t avkey; > avtab_datum_t *avdatump; > + avtab_operations_t *ops; > avtab_ptr_t node; > class_perm_node_t *cur; > uint32_t spec = 0; > + unsigned int i; > > if (specified & AVRULE_ALLOWED) { > spec = AVTAB_ALLOWED; > @@ -1784,6 +1802,22 @@ static int expand_avrule_helper(sepol_handle_t * handle, > spec = AVTAB_AUDITDENY; > } else if (specified & AVRULE_NEVERALLOW) { > spec = AVTAB_NEVERALLOW; > + } else if (specified & AVRULE_OPNUM_ALLOWED) { > + spec = AVTAB_OPNUM_ALLOWED; > + } else if (specified & AVRULE_OPNUM_AUDITALLOW) { > + spec = AVTAB_OPNUM_AUDITALLOW; > + } else if (specified & AVRULE_OPNUM_DONTAUDIT) { > + if (handle && handle->disable_dontaudit) > + return EXPAND_RULE_SUCCESS; > + spec = AVTAB_OPNUM_DONTAUDIT; > + } else if (specified & AVRULE_OPTYPE_ALLOWED) { > + spec = AVTAB_OPTYPE_ALLOWED; > + } else if (specified & AVRULE_OPTYPE_AUDITALLOW) { > + spec = AVTAB_OPTYPE_AUDITALLOW; > + } else if (specified & AVRULE_OPTYPE_DONTAUDIT) { > + if (handle && handle->disable_dontaudit) > + return EXPAND_RULE_SUCCESS; > + spec = AVTAB_OPTYPE_DONTAUDIT; > } else { > assert(0); /* unreachable */ > } > @@ -1795,7 +1829,7 @@ static int expand_avrule_helper(sepol_handle_t * handle, > avkey.target_class = cur->tclass; > avkey.specified = spec; > > - node = find_avtab_node(handle, avtab, &avkey, cond); > + node = find_avtab_node(handle, avtab, &avkey, cond, operations); > if (!node) > return EXPAND_RULE_ERROR; > if (enabled) { > @@ -1825,6 +1859,20 @@ static int expand_avrule_helper(sepol_handle_t * handle, > avdatump->data &= ~cur->data; > else > avdatump->data = ~cur->data; > + } else if (specified & AVRULE_OP) { > + if (!avdatump->ops) { > + ops = (avtab_operations_t *) > + calloc(1, sizeof(avtab_operations_t)); > + if (!ops) { > + ERR(handle, "Out of memory!"); > + return -1; > + } > + node->datum.ops = ops; > + } > + node->datum.ops->type = operations->type; > + for (i = 0; i < ARRAY_SIZE(operations->perms); i++) { > + node->datum.ops->perms[i] |= operations->perms[i]; > + } > } else { > assert(0); /* should never occur */ > } > @@ -1849,10 +1897,10 @@ static int expand_rule_helper(sepol_handle_t * handle, > if (!ebitmap_node_get_bit(snode, i)) > continue; > if (source_rule->flags & RULE_SELF) { > - if (source_rule->specified & AVRULE_AV) { > + if (source_rule->specified & (AVRULE_AV | AVRULE_OP)) { > retval = expand_avrule_helper(handle, source_rule->specified, > cond, i, i, source_rule->perms, > - dest_avtab, enabled); > + dest_avtab, enabled, source_rule->ops); > if (retval != EXPAND_RULE_SUCCESS) > return retval; > } else { > @@ -1867,10 +1915,10 @@ static int expand_rule_helper(sepol_handle_t * handle, > ebitmap_for_each_bit(ttypes, tnode, j) { > if (!ebitmap_node_get_bit(tnode, j)) > continue; > - if (source_rule->specified & AVRULE_AV) { > + if (source_rule->specified & (AVRULE_AV | AVRULE_OP)) { > retval = expand_avrule_helper(handle, source_rule->specified, > cond, i, j, source_rule->perms, > - dest_avtab, enabled); > + dest_avtab, enabled, source_rule->ops); > if (retval != EXPAND_RULE_SUCCESS) > return retval; > } else { > @@ -3107,18 +3155,31 @@ static int expand_avtab_insert(avtab_t * a, avtab_key_t * k, avtab_datum_t * d) > { > avtab_ptr_t node; > avtab_datum_t *avd; > - int rc; > - > - node = avtab_search_node(a, k); > - if (!node) { > - rc = avtab_insert(a, k, d); > - if (rc) > - ERR(NULL, "Out of memory!"); > - return rc; > + avtab_operations_t *ops; > + unsigned int i; > + unsigned int type_match = 0; > + > + if (k->specified & AVTAB_OPNUM) { > + /* > + * AVTAB_OPNUM entries are not necessarily unique. > + * find node with matching ops->type > + */ > + node = avtab_search_node(a, k); > + while (node) { > + if (node->datum.ops->type == d->ops->type) { > + type_match = 1; > + break; > + } > + node = avtab_search_node_next(node, k->specified); > + } > + if (!type_match) > + node = NULL; > + } else { > + node = avtab_search_node(a, k); > } > > - if ((k->specified & AVTAB_ENABLED) != > - (node->key.specified & AVTAB_ENABLED)) { > + if (!node || ((k->specified & AVTAB_ENABLED) != > + (node->key.specified & AVTAB_ENABLED))) { > node = avtab_insert_nonunique(a, k, d); > if (!node) { > ERR(NULL, "Out of memory!"); > @@ -3128,6 +3189,7 @@ static int expand_avtab_insert(avtab_t * a, avtab_key_t * k, avtab_datum_t * d) > } > > avd = &node->datum; > + ops = node->datum.ops; > switch (k->specified & ~AVTAB_ENABLED) { > case AVTAB_ALLOWED: > case AVTAB_AUDITALLOW: > @@ -3136,6 +3198,15 @@ static int expand_avtab_insert(avtab_t * a, avtab_key_t * k, avtab_datum_t * d) > case AVTAB_AUDITDENY: > avd->data &= d->data; > break; > + case AVTAB_OPNUM_ALLOWED: > + case AVTAB_OPNUM_AUDITALLOW: > + case AVTAB_OPNUM_DONTAUDIT: > + case AVTAB_OPTYPE_ALLOWED: > + case AVTAB_OPTYPE_AUDITALLOW: > + case AVTAB_OPTYPE_DONTAUDIT: > + for (i = 0; i < ARRAY_SIZE(ops->perms); i++) > + ops->perms[i] |= d->ops->perms[i]; > + break; > default: > ERR(NULL, "Type conflict!"); > return -1; > diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c > index d1c0018..8c3c7ac 100644 > --- a/libsepol/src/policydb.c > +++ b/libsepol/src/policydb.c > @@ -180,7 +180,7 @@ static struct policydb_compat_info policydb_compat[] = { > }, > { > .type = POLICY_KERN, > - .version = POLICYDB_VERSION_XEN_DEVICETREE, > + .version = POLICYDB_VERSION_IOCTL_OPERATIONS, > .sym_num = SYM_NUM, > .ocon_num = OCON_NODE6 + 1, > .target_platform = SEPOL_TARGET_SELINUX, > diff --git a/libsepol/src/write.c b/libsepol/src/write.c > index c97a4da..5e12d6b 100644 > --- a/libsepol/src/write.c > +++ b/libsepol/src/write.c > @@ -101,6 +101,7 @@ static int avtab_write_item(policydb_t * p, > unsigned merge, unsigned commit, uint32_t * nel) > { > avtab_ptr_t node; > + uint8_t buf8; > uint16_t buf16[4]; > uint32_t buf32[10], lookup, val; > size_t items, items2; > @@ -220,10 +221,38 @@ static int avtab_write_item(policydb_t * p, > items = put_entry(buf16, sizeof(uint16_t), 4, fp); > if (items != 4) > return POLICYDB_ERROR; > - buf32[0] = cpu_to_le32(cur->datum.data); > - items = put_entry(buf32, sizeof(uint32_t), 1, fp); > - if (items != 1) > + if ((p->policyvers < POLICYDB_VERSION_IOCTL_OPERATIONS) && > + (cur->key.specified & AVTAB_OP)) { > + ERR(fp->handle, "policy version %u does not support ioctl operation" > + " rules and one was specified", p->policyvers); > + return POLICYDB_ERROR; > + } > + > + if (p->target_platform != SEPOL_TARGET_SELINUX && > + (cur->key.specified & AVTAB_OP)) { > + ERR(fp->handle, "Target platform %s does not support ioctl " > + "operation rules and one was specified", > + policydb_target_strings[p->target_platform]); > return POLICYDB_ERROR; > + } > + > + if (cur->key.specified & AVTAB_OP) { > + buf8 = cur->datum.ops->type; > + items = put_entry(&buf8, sizeof(uint8_t),1,fp); > + if (items != 1) > + return POLICYDB_ERROR; > + for (i = 0; i < ARRAY_SIZE(cur->datum.ops->perms); i++) > + buf32[i] = cpu_to_le32(cur->datum.ops->perms[i]); > + items = put_entry(buf32, sizeof(uint32_t),8,fp); > + if (items != 8) > + return POLICYDB_ERROR; > + } else { > + buf32[0] = cpu_to_le32(cur->datum.data); > + items = put_entry(buf32, sizeof(uint32_t), 1, fp); > + if (items != 1) > + return POLICYDB_ERROR; > + } > + > return POLICYDB_SUCCESS; > } > > _______________________________________________ Selinux mailing list Selinux@xxxxxxxxxxxxx To unsubscribe, send email to Selinux-leave@xxxxxxxxxxxxx. To get help, send an email containing "help" to Selinux-request@xxxxxxxxxxxxx.