On Wed, Nov 10, 2021 at 6:12 AM Bram Bonne <brambonne@xxxxxxxxxx> wrote: > > Reuse the existing extended permissions infrastructure to support > sepolicy for different netlink message types. > > When individual netlink message types are omitted only the existing > permissions are checked. As is the case for ioctl xperms, this feature > is intended to provide finer granularity for nlmsg_read and nlmsg_write > permissions, as they may be too imprecise. For example, a single > NETLINK_ROUTE socket may provide access to both an interface's IP > address and to its ARP table, which might have different privacy > implications. In addition, the set of message types has grown over time, > so even if the current list is acceptable, future additions might not be. > It was suggested before on the mailing list [1] that extended permissions > would be a good fit for this purpose. > > Existing policy using the nlmsg_read and nlmsg_write permissions will > continue to work as-is. Similar to ioctl xperms, netlink xperms allow > for a more fine-grained policy where needed. > > Example policy on Android, preventing regular apps from accessing the > device's MAC address and ARP table, but allowing this access to > privileged apps, looks as follows: > > allow netdomain self:netlink_route_socket { > create read getattr write setattr lock append connect getopt > setopt shutdown nlmsg_read > }; > allowxperm netdomain self:netlink_route_socket nlmsg ~{ > RTM_GETLINK RTM_GETNEIGH RTM_GETNEIGHTBL Where are these defined? What are the valid values for userspace? Thanks, Jim > }; > allowxperm priv_app self:netlink_route_socket nlmsg { > RTM_GETLINK RTM_GETNEIGH RTM_GETNEIGHTBL > }; > > Android currently uses code similar to [1] as a temporary workaround to > limit access to certain netlink message types; our hope is that this patch > will allow us to move back to upstream code with an approach that works for > everyone. > > [1] https://lore.kernel.org/selinux/CAHC9VhRSUhozBycHMZcMaJsibJDxNMsTsKVT2zOnW=5H4R4mdg@xxxxxxxxxxxxxx/ > > Signed-off-by: Bram Bonne <brambonne@xxxxxxxxxx> > --- > checkpolicy/policy_define.c | 124 +++++++++++------ > checkpolicy/test/dismod.c | 2 + > libsepol/cil/src/cil.c | 2 + > libsepol/cil/src/cil_binary.c | 154 ++++++++++++++++++--- > libsepol/cil/src/cil_build_ast.c | 4 +- > libsepol/cil/src/cil_internal.h | 2 + > libsepol/cil/src/cil_policy.c | 2 + > libsepol/cil/src/cil_verify.c | 3 + > libsepol/cil/src/cil_write_ast.c | 16 ++- > libsepol/include/sepol/policydb/avtab.h | 1 + > libsepol/include/sepol/policydb/policydb.h | 1 + > libsepol/src/assertion.c | 15 +- > libsepol/src/expand.c | 3 + > libsepol/src/kernel_to_cil.c | 23 ++- > libsepol/src/module_to_cil.c | 29 +++- > libsepol/src/optimize.c | 6 + > libsepol/src/util.c | 11 +- > 17 files changed, 321 insertions(+), 77 deletions(-) > > diff --git a/checkpolicy/policy_define.c b/checkpolicy/policy_define.c > index d3eb6111..8ca22993 100644 > --- a/checkpolicy/policy_define.c > +++ b/checkpolicy/policy_define.c > @@ -1818,27 +1818,27 @@ avrule_t *define_cond_pol_list(avrule_t * avlist, avrule_t * sl) > return sl; > } > > -typedef struct av_ioctl_range { > +typedef struct av_xperm_range { > uint16_t low; > uint16_t high; > -} av_ioctl_range_t; > +} av_xperm_range_t; > > -struct av_ioctl_range_list { > +struct av_xperm_range_list { > uint8_t omit; > - av_ioctl_range_t range; > - struct av_ioctl_range_list *next; > + av_xperm_range_t range; > + struct av_xperm_range_list *next; > }; > > -static int avrule_sort_ioctls(struct av_ioctl_range_list **rangehead) > +static int avrule_sort_xperms(struct av_xperm_range_list **rangehead) > { > - struct av_ioctl_range_list *r, *r2, *sorted, *sortedhead = NULL; > + struct av_xperm_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_ioctl_range_list)); > + sorted = malloc(sizeof(struct av_xperm_range_list)); > if (sorted == NULL) > goto error; > - memcpy(sorted, r, sizeof(struct av_ioctl_range_list)); > + memcpy(sorted, r, sizeof(struct av_xperm_range_list)); > sorted->next = NULL; > if (sortedhead == NULL) { > sortedhead = sorted; > @@ -1877,9 +1877,9 @@ error: > return -1; > } > > -static int avrule_merge_ioctls(struct av_ioctl_range_list **rangehead) > +static int avrule_merge_xperms(struct av_xperm_range_list **rangehead) > { > - struct av_ioctl_range_list *r, *tmp; > + struct av_xperm_range_list *r, *tmp; > r = *rangehead; > while (r != NULL && r->next != NULL) { > /* merge */ > @@ -1897,15 +1897,15 @@ static int avrule_merge_ioctls(struct av_ioctl_range_list **rangehead) > return 0; > } > > -static int avrule_read_ioctls(struct av_ioctl_range_list **rangehead) > +static int avrule_read_xperm_ranges(struct av_xperm_range_list **rangehead) > { > char *id; > - struct av_ioctl_range_list *rnew, *r = NULL; > + struct av_xperm_range_list *rnew, *r = NULL; > uint8_t omit = 0; > > *rangehead = NULL; > > - /* read in all the ioctl commands */ > + /* read in all the netlink ranges / ioctl commands */ > while ((id = queue_remove(id_queue))) { > if (strcmp(id,"~") == 0) { > /* these are values to be omitted */ > @@ -1917,13 +1917,13 @@ static int avrule_read_ioctls(struct av_ioctl_range_list **rangehead) > 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."); > + yyerror("xperm ranges must be in ascending order."); > return -1; > } > free(id); > } else { > /* read in new low value */ > - rnew = malloc(sizeof(struct av_ioctl_range_list)); > + rnew = malloc(sizeof(struct av_xperm_range_list)); > if (rnew == NULL) > goto error; > rnew->next = NULL; > @@ -1950,11 +1950,11 @@ error: > } > > /* flip to included ranges */ > -static int avrule_omit_ioctls(struct av_ioctl_range_list **rangehead) > +static int avrule_omit_xperms(struct av_xperm_range_list **rangehead) > { > - struct av_ioctl_range_list *rnew, *r, *newhead, *r2; > + struct av_xperm_range_list *rnew, *r, *newhead, *r2; > > - rnew = calloc(1, sizeof(struct av_ioctl_range_list)); > + rnew = calloc(1, sizeof(struct av_xperm_range_list)); > if (!rnew) > goto error; > > @@ -1972,7 +1972,7 @@ static int avrule_omit_ioctls(struct av_ioctl_range_list **rangehead) > > while (r) { > r2->range.high = r->range.low - 1; > - rnew = calloc(1, sizeof(struct av_ioctl_range_list)); > + rnew = calloc(1, sizeof(struct av_xperm_range_list)); > if (!rnew) > goto error; > r2->next = rnew; > @@ -1998,27 +1998,27 @@ error: > return -1; > } > > -static int avrule_ioctl_ranges(struct av_ioctl_range_list **rangelist) > +static int avrule_xperm_ranges(struct av_xperm_range_list **rangelist) > { > - struct av_ioctl_range_list *rangehead; > + struct av_xperm_range_list *rangehead; > uint8_t omit; > > /* read in ranges to include and omit */ > - if (avrule_read_ioctls(&rangehead)) > + if (avrule_read_xperm_ranges(&rangehead)) > return -1; > if (rangehead == NULL) { > - yyerror("error processing ioctl commands"); > + yyerror("error processing ioctl/netlink commands"); > return -1; > } > omit = rangehead->omit; > - /* sort and merge the input ioctls */ > - if (avrule_sort_ioctls(&rangehead)) > + /* sort and merge the input ranges */ > + if (avrule_sort_xperms(&rangehead)) > return -1; > - if (avrule_merge_ioctls(&rangehead)) > + if (avrule_merge_xperms(&rangehead)) > return -1; > /* flip ranges if these are omitted */ > if (omit) { > - if (avrule_omit_ioctls(&rangehead)) > + if (avrule_omit_xperms(&rangehead)) > return -1; > } > > @@ -2189,11 +2189,11 @@ static int avrule_xperms_used(const av_extended_perms_t *xperms) > #define IOC_DRIV(x) ((x) >> 8) > #define IOC_FUNC(x) ((x) & 0xff) > #define IOC_CMD(driver, func) (((driver) << 8) + (func)) > -static int avrule_ioctl_partialdriver(struct av_ioctl_range_list *rangelist, > +static int avrule_ioctl_partialdriver(struct av_xperm_range_list *rangelist, > av_extended_perms_t *complete_driver, > av_extended_perms_t **extended_perms) > { > - struct av_ioctl_range_list *r; > + struct av_xperm_range_list *r; > av_extended_perms_t *xperms; > uint8_t low, high; > > @@ -2228,10 +2228,10 @@ static int avrule_ioctl_partialdriver(struct av_ioctl_range_list *rangelist, > > } > > -static int avrule_ioctl_completedriver(struct av_ioctl_range_list *rangelist, > +static int avrule_ioctl_completedriver(struct av_xperm_range_list *rangelist, > av_extended_perms_t **extended_perms) > { > - struct av_ioctl_range_list *r; > + struct av_xperm_range_list *r; > av_extended_perms_t *xperms; > uint16_t low, high; > xperms = calloc(1, sizeof(av_extended_perms_t)); > @@ -2270,10 +2270,10 @@ static int avrule_ioctl_completedriver(struct av_ioctl_range_list *rangelist, > return 0; > } > > -static int avrule_ioctl_func(struct av_ioctl_range_list *rangelist, > +static int avrule_xperms_single_driver(struct av_xperm_range_list *rangelist, > av_extended_perms_t **extended_perms, unsigned int driver) > { > - struct av_ioctl_range_list *r; > + struct av_xperm_range_list *r; > av_extended_perms_t *xperms; > uint16_t low, high; > > @@ -2307,7 +2307,6 @@ static int avrule_ioctl_func(struct av_ioctl_range_list *rangelist, > high = IOC_FUNC(high); > avrule_xperm_setrangebits(low, high, xperms); > xperms->driver = driver; > - xperms->specified = AVRULE_XPERMS_IOCTLFUNCTION; > r = r->next; > } > > @@ -2320,6 +2319,18 @@ static int avrule_ioctl_func(struct av_ioctl_range_list *rangelist, > return 0; > } > > +void avrule_ioctl_freeranges(struct av_xperm_range_list *rangelist) > +{ > + struct av_xperm_range_list *r, *tmp; > + > + r = rangelist; > + while (r) { > + tmp = r; > + r = r->next; > + free(tmp); > + } > +} > + > static unsigned int xperms_for_each_bit(unsigned int *bit, av_extended_perms_t *xperms) > { > unsigned int i; > @@ -2384,13 +2395,13 @@ static int avrule_cpy(avrule_t *dest, const avrule_t *src) > static int define_te_avtab_ioctl(const avrule_t *avrule_template) > { > avrule_t *avrule; > - struct av_ioctl_range_list *rangelist, *r; > + struct av_xperm_range_list *rangelist, *r; > av_extended_perms_t *complete_driver, *partial_driver, *xperms; > unsigned int i; > > > /* organize ioctl ranges */ > - if (avrule_ioctl_ranges(&rangelist)) > + if (avrule_xperm_ranges(&rangelist)) > return -1; > > /* create rule for ioctl driver types that are entirely enabled */ > @@ -2422,10 +2433,11 @@ static int define_te_avtab_ioctl(const avrule_t *avrule_template) > */ > i = 0; > while (xperms_for_each_bit(&i, partial_driver)) { > - if (avrule_ioctl_func(rangelist, &xperms, i)) > + if (avrule_xperms_single_driver(rangelist, &xperms, i)) > return -1; > > if (xperms) { > + xperms->specified = AVRULE_XPERMS_IOCTLFUNCTION; > avrule = (avrule_t *) calloc(1, sizeof(avrule_t)); > if (!avrule) { > yyerror("out of memory"); > @@ -2451,6 +2463,38 @@ done: > return 0; > } > > +int define_te_avtab_netlink(avrule_t *avrule_template) > +{ > + avrule_t *avrule; > + struct av_xperm_range_list *range_list; > + av_extended_perms_t *xperms = NULL; > + > + /* organize message ranges */ > + if (avrule_xperm_ranges(&range_list)) > + return -1; > + > + /* Netlink message types comfortably fit into a single driver > + * (see RTM_MAX in uapi/linux/rtnetlink.h) > + */ > + avrule_xperms_single_driver(range_list, &xperms, 0); > + > + if (xperms && avrule_xperms_used(xperms)) { > + xperms->specified = AVRULE_XPERMS_NLMSG; > + 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->xperms = xperms; > + append_avrule(avrule); > + } else { > + free(xperms); > + } > + return 0; > +} > + > int define_te_avtab_extended_perms(int which) > { > char *id; > @@ -2473,8 +2517,10 @@ int define_te_avtab_extended_perms(int which) > id = queue_remove(id_queue); > if (strcmp(id,"ioctl") == 0) { > rc = define_te_avtab_ioctl(avrule_template); > + } else if (strcmp(id, "nlmsg") == 0) { > + rc = define_te_avtab_netlink(avrule_template); > } else { > - yyerror("only ioctl extended permissions are supported"); > + yyerror("only ioctl / nlmsg extended permissions are supported"); > rc = -1; > } > > diff --git a/checkpolicy/test/dismod.c b/checkpolicy/test/dismod.c > index ec2a3e9a..f8652ec5 100644 > --- a/checkpolicy/test/dismod.c > +++ b/checkpolicy/test/dismod.c > @@ -296,6 +296,8 @@ static int display_avrule(avrule_t * avrule, policydb_t * policy, > xperms.specified = AVTAB_XPERMS_IOCTLFUNCTION; > else if (avrule->xperms->specified == AVRULE_XPERMS_IOCTLDRIVER) > xperms.specified = AVTAB_XPERMS_IOCTLDRIVER; > + else if (avrule->xperms->specified == AVRULE_XPERMS_NLMSG) > + xperms.specified = AVTAB_XPERMS_NLMSG; > else { > fprintf(fp, " ERROR: no valid xperms specified\n"); > return -1; > diff --git a/libsepol/cil/src/cil.c b/libsepol/cil/src/cil.c > index 4cc7f87f..fbdb005a 100644 > --- a/libsepol/cil/src/cil.c > +++ b/libsepol/cil/src/cil.c > @@ -219,6 +219,7 @@ char *CIL_KEY_DONTAUDITX; > char *CIL_KEY_NEVERALLOWX; > char *CIL_KEY_PERMISSIONX; > char *CIL_KEY_IOCTL; > +char *CIL_KEY_NLMSG; > char *CIL_KEY_UNORDERED; > char *CIL_KEY_SRC_INFO; > char *CIL_KEY_SRC_CIL; > @@ -388,6 +389,7 @@ static void cil_init_keys(void) > CIL_KEY_NEVERALLOWX = cil_strpool_add("neverallowx"); > CIL_KEY_PERMISSIONX = cil_strpool_add("permissionx"); > CIL_KEY_IOCTL = cil_strpool_add("ioctl"); > + CIL_KEY_NLMSG = cil_strpool_add("nlmsg"); > CIL_KEY_UNORDERED = cil_strpool_add("unordered"); > CIL_KEY_SRC_INFO = cil_strpool_add("<src_info>"); > CIL_KEY_SRC_CIL = cil_strpool_add("cil"); > diff --git a/libsepol/cil/src/cil_binary.c b/libsepol/cil/src/cil_binary.c > index d8aa495a..f9938d66 100644 > --- a/libsepol/cil/src/cil_binary.c > +++ b/libsepol/cil/src/cil_binary.c > @@ -66,6 +66,7 @@ struct cil_args_binary { > int pass; > hashtab_t role_trans_table; > hashtab_t avrulex_ioctl_table; > + hashtab_t avrulex_nlmsg_table; > void **type_value_to_cil; > }; > > @@ -1553,7 +1554,7 @@ void __avrule_xperm_setrangebits(uint16_t low, uint16_t high, struct avtab_exten > #define IOC_DRIV(x) (x >> 8) > #define IOC_FUNC(x) (x & 0xff) > > -int __cil_permx_bitmap_to_sepol_xperms_list(ebitmap_t *xperms, struct cil_list **xperms_list) > +int __cil_permx_ioctl_bitmap_to_sepol_xperms_list(ebitmap_t *xperms, struct cil_list **xperms_list) > { > ebitmap_node_t *node; > unsigned int i; > @@ -1618,13 +1619,53 @@ int __cil_permx_bitmap_to_sepol_xperms_list(ebitmap_t *xperms, struct cil_list * > return SEPOL_OK; > } > > -int __cil_avrulex_ioctl_to_policydb(hashtab_key_t k, hashtab_datum_t datum, void *args) > +int __cil_permx_nlmsg_bitmap_to_sepol_xperms_list(ebitmap_t *xperms, struct cil_list **xperms_list) > +{ > + ebitmap_node_t *node; > + uint16_t i; > + uint16_t low; > + struct avtab_extended_perms *avtab = NULL; > + int start_new_range = 1; > + > + cil_list_init(xperms_list, CIL_NONE); > + > + ebitmap_for_each_positive_bit(xperms, node, i) { > + if (start_new_range) { > + low = i; > + start_new_range = 0; > + } > + > + // Continue if the current bit isn't the end of the driver range > + // or the next bit is set > + if (ebitmap_get_bit(xperms, i + 1)) { > + continue; > + } > + > + start_new_range = 1; > + > + if (!avtab) { > + avtab = cil_calloc(1, sizeof(*avtab)); > + // Netlink message types all fit in driver 0 > + avtab->driver = 0; > + avtab->specified = AVTAB_XPERMS_NLMSG; > + } > + > + __avrule_xperm_setrangebits(low, i, avtab); > + } > + > + if (avtab) { > + cil_list_append(*xperms_list, CIL_NONE, avtab); > + } > + > + return SEPOL_OK; > +} > + > +int __cil_avrulex_to_policydb(hashtab_key_t k, struct cil_list* xperms_list, char* cil_key, void *args) > { > int rc = SEPOL_OK; > struct policydb *pdb; > avtab_key_t *avtab_key; > avtab_datum_t avtab_datum; > - struct cil_list *xperms_list = NULL; > struct cil_list_item *item; > class_datum_t *sepol_obj; > uint32_t data = 0; > @@ -1637,17 +1678,12 @@ int __cil_avrulex_ioctl_to_policydb(hashtab_key_t k, hashtab_datum_t datum, void > // setting the data for an extended avtab isn't really necessary because > // it is ignored by the kernel. However, neverallow checking requires that > // the data value be set, so set it for that to work. > - rc = __perm_str_to_datum(CIL_KEY_IOCTL, sepol_obj, &data); > + rc = __perm_str_to_datum(cil_key, sepol_obj, &data); > if (rc != SEPOL_OK) { > goto exit; > } > avtab_datum.data = data; > > - rc = __cil_permx_bitmap_to_sepol_xperms_list(datum, &xperms_list); > - if (rc != SEPOL_OK) { > - goto exit; > - } > - > cil_list_for_each(item, xperms_list) { > avtab_datum.xperms = item->data; > rc = avtab_insert(&pdb->te_avtab, avtab_key, &avtab_datum); > @@ -1659,16 +1695,57 @@ int __cil_avrulex_ioctl_to_policydb(hashtab_key_t k, hashtab_datum_t datum, void > rc = SEPOL_OK; > > exit: > + return rc; > +} > + > +void __cil_cleanup_xperms(struct cil_list *xperms_list) > +{ > + struct cil_list_item *item; > + > if (xperms_list != NULL) { > cil_list_for_each(item, xperms_list) { > free(item->data); > + item->data = NULL; > } > cil_list_destroy(&xperms_list, CIL_FALSE); > } > +} > + > +int __cil_avrulex_ioctl_to_policydb(hashtab_key_t k, hashtab_datum_t datum, void *args) > +{ > + struct cil_list *xperms_list = NULL; > + int rc; > + > + rc = __cil_permx_ioctl_bitmap_to_sepol_xperms_list(datum, &xperms_list); > + if (rc != SEPOL_OK) { > + goto exit; > + } > + rc = __cil_avrulex_to_policydb(k, xperms_list, CIL_KEY_IOCTL, args); > + > +exit: > + __cil_cleanup_xperms(xperms_list); > + > return rc; > } > > -int __cil_avrulex_ioctl_to_hashtable(hashtab_t h, uint16_t kind, uint32_t src, uint32_t tgt, uint32_t obj, ebitmap_t *xperms) > +int __cil_avrulex_nlmsg_to_policydb(hashtab_key_t k, hashtab_datum_t datum, void *args) > +{ > + struct cil_list *xperms_list = NULL; > + int rc; > + > + rc = __cil_permx_nlmsg_bitmap_to_sepol_xperms_list(datum, &xperms_list); > + if (rc != SEPOL_OK) { > + goto exit; > + } > + rc = __cil_avrulex_to_policydb(k, xperms_list, CIL_KEY_NLMSG, args); > + > +exit: > + __cil_cleanup_xperms(xperms_list); > + > + return rc; > +} > + > +int __cil_avrulex_to_hashtable(hashtab_t h, uint16_t kind, uint32_t src, uint32_t tgt, uint32_t obj, ebitmap_t *xperms) > { > uint16_t specified; > avtab_key_t *avtab_key; > @@ -1747,8 +1824,12 @@ int __cil_avrulex_to_hashtable_helper(policydb_t *pdb, uint16_t kind, struct cil > if (rc != SEPOL_OK) goto exit; > > switch (permx->kind) { > - case CIL_PERMX_KIND_IOCTL: > - rc = __cil_avrulex_ioctl_to_hashtable(args->avrulex_ioctl_table, kind, sepol_src->s.value, sepol_tgt->s.value, sepol_obj->s.value, permx->perms); > + case CIL_PERMX_KIND_IOCTL: > + rc = __cil_avrulex_to_hashtable(args->avrulex_ioctl_table, kind, sepol_src->s.value, sepol_tgt->s.value, sepol_obj->s.value, permx->perms); > + if (rc != SEPOL_OK) goto exit; > + break; > + case CIL_PERMX_KIND_NLMSG: > + rc = __cil_avrulex_to_hashtable(args->avrulex_nlmsg_table, kind, sepol_src->s.value, sepol_tgt->s.value, sepol_obj->s.value, permx->perms); > if (rc != SEPOL_OK) goto exit; > break; > default: > @@ -4417,6 +4498,9 @@ static int __cil_permx_to_sepol_class_perms(policydb_t *pdb, struct cil_permissi > case CIL_PERMX_KIND_IOCTL: > perm_str = CIL_KEY_IOCTL; > break; > + case CIL_PERMX_KIND_NLMSG: > + perm_str = CIL_KEY_NLMSG; > + break; > default: > rc = SEPOL_ERR; > goto exit; > @@ -4563,6 +4647,9 @@ static void __cil_print_permissionx(struct cil_permissionx *px) > case CIL_PERMX_KIND_IOCTL: > kind_str = CIL_KEY_IOCTL; > break; > + case CIL_PERMX_KIND_NLMSG: > + kind_str = CIL_KEY_NLMSG; > + break; > default: > kind_str = "unknown"; > break; > @@ -4696,8 +4783,21 @@ static int cil_check_neverallow(const struct cil_db *db, policydb_t *pdb, struct > goto exit; > } > > - rc = __cil_permx_bitmap_to_sepol_xperms_list(cil_rule->perms.x.permx->perms, &xperms); > - if (rc != SEPOL_OK) { > + switch (cil_rule->perms.x.permx->kind) { > + case CIL_PERMX_KIND_IOCTL: > + rc = __cil_permx_ioctl_bitmap_to_sepol_xperms_list(cil_rule->perms.x.permx->perms, &xperms); > + if (rc != SEPOL_OK) { > + goto exit; > + } > + break; > + case CIL_PERMX_KIND_NLMSG: > + rc = __cil_permx_nlmsg_bitmap_to_sepol_xperms_list(cil_rule->perms.x.permx->perms, &xperms); > + if (rc != SEPOL_OK) { > + goto exit; > + } > + break; > + default: > + rc = SEPOL_ERR; > goto exit; > } > > @@ -4715,13 +4815,7 @@ static int cil_check_neverallow(const struct cil_db *db, policydb_t *pdb, struct > } > > exit: > - if (xperms != NULL) { > - cil_list_for_each(item, xperms) { > - free(item->data); > - item->data = NULL; > - } > - cil_list_destroy(&xperms, CIL_FALSE); > - } > + __cil_cleanup_xperms(xperms); > > rule->xperms = NULL; > __cil_destroy_sepol_avrules(rule); > @@ -4904,6 +4998,7 @@ int cil_binary_create_allocated_pdb(const struct cil_db *db, sepol_policydb_t *p > struct cil_list *neverallows = NULL; > hashtab_t role_trans_table = NULL; > hashtab_t avrulex_ioctl_table = NULL; > + hashtab_t avrulex_nlmsg_table = NULL; > void **type_value_to_cil = NULL; > struct cil_class **class_value_to_cil = NULL; > struct cil_perm ***perm_value_to_cil = NULL; > @@ -4947,7 +5042,13 @@ int cil_binary_create_allocated_pdb(const struct cil_db *db, sepol_policydb_t *p > > avrulex_ioctl_table = hashtab_create(avrulex_hash, avrulex_compare, AVRULEX_TABLE_SIZE); > if (!avrulex_ioctl_table) { > - cil_log(CIL_INFO, "Failure to create hashtab for avrulex\n"); > + cil_log(CIL_INFO, "Failure to create hashtab for ioctl\n"); > + goto exit; > + } > + > + avrulex_nlmsg_table = hashtab_create(avrulex_hash, avrulex_compare, AVRULEX_TABLE_SIZE); > + if (!avrulex_nlmsg_table) { > + cil_log(CIL_INFO, "Failure to create hashtab for nlmsg\n"); > goto exit; > } > > @@ -4958,6 +5059,7 @@ int cil_binary_create_allocated_pdb(const struct cil_db *db, sepol_policydb_t *p > extra_args.neverallows = neverallows; > extra_args.role_trans_table = role_trans_table; > extra_args.avrulex_ioctl_table = avrulex_ioctl_table; > + extra_args.avrulex_nlmsg_table = avrulex_nlmsg_table; > extra_args.type_value_to_cil = type_value_to_cil; > > for (i = 1; i <= 3; i++) { > @@ -4980,7 +5082,12 @@ int cil_binary_create_allocated_pdb(const struct cil_db *db, sepol_policydb_t *p > if (i == 3) { > rc = hashtab_map(avrulex_ioctl_table, __cil_avrulex_ioctl_to_policydb, pdb); > if (rc != SEPOL_OK) { > - cil_log(CIL_INFO, "Failure creating avrulex rules\n"); > + cil_log(CIL_INFO, "Failure creating ioctl avrulex rules\n"); > + goto exit; > + } > + rc = hashtab_map(avrulex_nlmsg_table, __cil_avrulex_nlmsg_to_policydb, pdb); > + if (rc != SEPOL_OK) { > + cil_log(CIL_INFO, "Failure creating nlmsg avrulex rules\n"); > goto exit; > } > } > @@ -5056,6 +5163,7 @@ exit: > hashtab_destroy(role_trans_table); > hashtab_map(avrulex_ioctl_table, __cil_avrulex_ioctl_destroy, NULL); > hashtab_destroy(avrulex_ioctl_table); > + hashtab_destroy(avrulex_nlmsg_table); > free(type_value_to_cil); > free(class_value_to_cil); > if (perm_value_to_cil != NULL) { > diff --git a/libsepol/cil/src/cil_build_ast.c b/libsepol/cil/src/cil_build_ast.c > index 9c34be23..b1cfb4f0 100644 > --- a/libsepol/cil/src/cil_build_ast.c > +++ b/libsepol/cil/src/cil_build_ast.c > @@ -2159,8 +2159,10 @@ int cil_fill_permissionx(struct cil_tree_node *parse_current, struct cil_permiss > > if (parse_current->data == CIL_KEY_IOCTL) { > permx->kind = CIL_PERMX_KIND_IOCTL; > + } else if (parse_current->data == CIL_KEY_NLMSG) { > + permx->kind = CIL_PERMX_KIND_NLMSG; > } else { > - cil_log(CIL_ERR, "Unknown permissionx kind, %s. Must be \"ioctl\"\n", (char *)parse_current->data); > + cil_log(CIL_ERR, "Unknown permissionx kind, %s. Must be one of [%s, %s]\n", (char *)parse_current->data, CIL_KEY_IOCTL, CIL_KEY_NLMSG); > rc = SEPOL_ERR; > goto exit; > } > diff --git a/libsepol/cil/src/cil_internal.h b/libsepol/cil/src/cil_internal.h > index 6f1d3cb5..c968cf1e 100644 > --- a/libsepol/cil/src/cil_internal.h > +++ b/libsepol/cil/src/cil_internal.h > @@ -236,6 +236,7 @@ extern char *CIL_KEY_DONTAUDITX; > extern char *CIL_KEY_NEVERALLOWX; > extern char *CIL_KEY_PERMISSIONX; > extern char *CIL_KEY_IOCTL; > +extern char *CIL_KEY_NLMSG; > extern char *CIL_KEY_UNORDERED; > extern char *CIL_KEY_SRC_INFO; > extern char *CIL_KEY_SRC_CIL; > @@ -623,6 +624,7 @@ struct cil_avrule { > }; > > #define CIL_PERMX_KIND_IOCTL 1 > +#define CIL_PERMX_KIND_NLMSG 2 > struct cil_permissionx { > struct cil_symtab_datum datum; > uint32_t kind; > diff --git a/libsepol/cil/src/cil_policy.c b/libsepol/cil/src/cil_policy.c > index 7c543c47..2703d764 100644 > --- a/libsepol/cil/src/cil_policy.c > +++ b/libsepol/cil/src/cil_policy.c > @@ -1112,6 +1112,8 @@ static void cil_xperms_to_policy(FILE *out, struct cil_permissionx *permx) > > if (permx->kind == CIL_PERMX_KIND_IOCTL) { > kind = "ioctl"; > + } else if (permx->kind == CIL_PERMX_KIND_NLMSG) { > + kind = CIL_KEY_NLMSG; > } else { > kind = "???"; > } > diff --git a/libsepol/cil/src/cil_verify.c b/libsepol/cil/src/cil_verify.c > index d994d717..67bdb0e9 100644 > --- a/libsepol/cil/src/cil_verify.c > +++ b/libsepol/cil/src/cil_verify.c > @@ -1427,6 +1427,9 @@ int __cil_verify_permissionx(struct cil_permissionx *permx, struct cil_tree_node > case CIL_PERMX_KIND_IOCTL: > kind_str = CIL_KEY_IOCTL; > break; > + case CIL_PERMX_KIND_NLMSG: > + kind_str = CIL_KEY_NLMSG; > + break; > default: > cil_tree_log(node, CIL_ERR, "Invalid permissionx kind (%d)", permx->kind); > rc = SEPOL_ERR; > diff --git a/libsepol/cil/src/cil_write_ast.c b/libsepol/cil/src/cil_write_ast.c > index d7f00bcc..b93a4fa1 100644 > --- a/libsepol/cil/src/cil_write_ast.c > +++ b/libsepol/cil/src/cil_write_ast.c > @@ -303,7 +303,13 @@ static void write_permx(FILE *out, struct cil_permissionx *permx) > fprintf(out, "%s", datum_to_str(DATUM(permx))); > } else { > fprintf(out, "("); > - fprintf(out, "%s ", permx->kind == CIL_PERMX_KIND_IOCTL ? "ioctl" : "<?KIND>"); > + if (permx->kind == CIL_PERMX_KIND_IOCTL) { > + fprintf(out, "%s ", "ioctl"); > + } else if (permx->kind == CIL_PERMX_KIND_NLMSG) { > + fprintf(out, "%s ", "nlmsg"); > + } else { > + fprintf(out, "%s ", "<?KIND>"); > + } > fprintf(out, "%s ", datum_or_str(DATUM(permx->obj), permx->obj_str)); > write_expr(out, permx->expr_str); > fprintf(out, ")"); > @@ -812,7 +818,13 @@ void cil_write_ast_node(FILE *out, struct cil_tree_node *node) > case CIL_PERMISSIONX: { > struct cil_permissionx *permx = node->data; > fprintf(out, "(permissionx %s (", datum_to_str(DATUM(permx))); > - fprintf(out, "%s ", permx->kind == CIL_PERMX_KIND_IOCTL ? "ioctl" : "<?KIND>"); > + if (permx->kind == CIL_PERMX_KIND_IOCTL) { > + fprintf(out, "%s ", "ioctl"); > + } else if (permx->kind == CIL_PERMX_KIND_NLMSG) { > + fprintf(out, "%s ", "nlmsg"); > + } else { > + fprintf(out, "%s ", "<?KIND>"); > + } > fprintf(out, "%s ", datum_or_str(DATUM(permx->obj), permx->obj_str)); > write_expr(out, permx->expr_str); > fprintf(out, "))\n"); > diff --git a/libsepol/include/sepol/policydb/avtab.h b/libsepol/include/sepol/policydb/avtab.h > index 10ecde9a..aa7481d3 100644 > --- a/libsepol/include/sepol/policydb/avtab.h > +++ b/libsepol/include/sepol/policydb/avtab.h > @@ -74,6 +74,7 @@ typedef struct avtab_extended_perms { > > #define AVTAB_XPERMS_IOCTLFUNCTION 0x01 > #define AVTAB_XPERMS_IOCTLDRIVER 0x02 > +#define AVTAB_XPERMS_NLMSG 0x03 > /* extension of the avtab_key specified */ > uint8_t specified; > uint8_t driver; > diff --git a/libsepol/include/sepol/policydb/policydb.h b/libsepol/include/sepol/policydb/policydb.h > index 4bf9f05d..55918e1d 100644 > --- a/libsepol/include/sepol/policydb/policydb.h > +++ b/libsepol/include/sepol/policydb/policydb.h > @@ -259,6 +259,7 @@ typedef struct class_perm_node { > typedef struct av_extended_perms { > #define AVRULE_XPERMS_IOCTLFUNCTION 0x01 > #define AVRULE_XPERMS_IOCTLDRIVER 0x02 > +#define AVRULE_XPERMS_NLMSG 0x03 > uint8_t specified; > uint8_t driver; > /* 256 bits of permissions */ > diff --git a/libsepol/src/assertion.c b/libsepol/src/assertion.c > index dd2749a0..47e45713 100644 > --- a/libsepol/src/assertion.c > +++ b/libsepol/src/assertion.c > @@ -101,6 +101,9 @@ static int check_extended_permissions(av_extended_perms_t *neverallow, avtab_ext > } else if ((neverallow->specified == AVRULE_XPERMS_IOCTLDRIVER) > && (allow->specified == AVTAB_XPERMS_IOCTLDRIVER)) { > rc = extended_permissions_and(neverallow->perms, allow->perms); > + } else if ((neverallow->specified == AVRULE_XPERMS_NLMSG) > + && (allow->specified == AVTAB_XPERMS_NLMSG)) { > + rc = extended_permissions_and(neverallow->perms, allow->perms); > } > > return rc; > @@ -133,6 +136,12 @@ static void extended_permissions_violated(avtab_extended_perms_t *result, > result->specified = AVTAB_XPERMS_IOCTLDRIVER; > for (i = 0; i < EXTENDED_PERMS_LEN; i++) > result->perms[i] = neverallow->perms[i] & allow->perms[i]; > + } else if ((neverallow->specified == AVRULE_XPERMS_NLMSG) > + && (allow->specified == AVTAB_XPERMS_NLMSG)) { > + result->specified = AVTAB_XPERMS_NLMSG; > + result->driver = 0; > + for (i = 0; i < EXTENDED_PERMS_LEN; i++) > + result->perms[i] = neverallow->perms[i] & allow->perms[i]; > } > } > > @@ -166,7 +175,8 @@ static int report_assertion_extended_permissions(sepol_handle_t *handle, > node = avtab_search_node_next(node, tmp_key.specified)) { > xperms = node->datum.xperms; > if ((xperms->specified != AVTAB_XPERMS_IOCTLFUNCTION) > - && (xperms->specified != AVTAB_XPERMS_IOCTLDRIVER)) > + && (xperms->specified != AVTAB_XPERMS_IOCTLDRIVER) > + && (xperms->specified != AVTAB_XPERMS_NLMSG)) > continue; > > rc = check_extended_permissions(avrule->xperms, xperms); > @@ -346,7 +356,8 @@ static int check_assertion_extended_permissions_avtab(avrule_t *avrule, avtab_t > xperms = node->datum.xperms; > > if ((xperms->specified != AVTAB_XPERMS_IOCTLFUNCTION) > - && (xperms->specified != AVTAB_XPERMS_IOCTLDRIVER)) > + && (xperms->specified != AVTAB_XPERMS_IOCTLDRIVER) > + && (xperms->specified != AVTAB_XPERMS_NLMSG)) > continue; > rc = check_extended_permissions(neverallow_xperms, xperms); > if (rc) > diff --git a/libsepol/src/expand.c b/libsepol/src/expand.c > index a6a466f7..959e6eb2 100644 > --- a/libsepol/src/expand.c > +++ b/libsepol/src/expand.c > @@ -1796,6 +1796,9 @@ static int allocate_xperms(sepol_handle_t * handle, avtab_datum_t * avdatump, > case AVRULE_XPERMS_IOCTLDRIVER: > xperms->specified = AVTAB_XPERMS_IOCTLDRIVER; > break; > + case AVRULE_XPERMS_NLMSG: > + xperms->specified = AVTAB_XPERMS_NLMSG; > + break; > default: > return -1; > } > diff --git a/libsepol/src/kernel_to_cil.c b/libsepol/src/kernel_to_cil.c > index 305567a5..090d4e43 100644 > --- a/libsepol/src/kernel_to_cil.c > +++ b/libsepol/src/kernel_to_cil.c > @@ -1617,7 +1617,8 @@ static char *xperms_to_str(avtab_extended_perms_t *xperms) > remaining = sizeof(xpermsbuf); > > if ((xperms->specified != AVTAB_XPERMS_IOCTLFUNCTION) > - && (xperms->specified != AVTAB_XPERMS_IOCTLDRIVER)) { > + && (xperms->specified != AVTAB_XPERMS_IOCTLDRIVER) > + && (xperms->specified != AVTAB_XPERMS_NLMSG)) { > return NULL; > } > > @@ -1637,7 +1638,8 @@ static char *xperms_to_str(avtab_extended_perms_t *xperms) > continue; > } > > - if (xperms->specified & AVTAB_XPERMS_IOCTLFUNCTION) { > + if (xperms->specified & AVTAB_XPERMS_IOCTLFUNCTION > + || xperms->specified & AVTAB_XPERMS_NLMSG) { > value = xperms->driver<<8 | bit; > if (in_range) { > low_value = xperms->driver<<8 | low_bit; > @@ -1679,7 +1681,7 @@ static char *avtab_node_to_str(struct policydb *pdb, avtab_key_t *key, avtab_dat > { > uint32_t data = datum->data; > type_datum_t *type; > - const char *flavor, *tgt; > + const char *flavor, *tgt, *func; > char *src, *class, *perms, *new; > char *rule = NULL; > > @@ -1742,8 +1744,21 @@ static char *avtab_node_to_str(struct policydb *pdb, avtab_key_t *key, avtab_dat > goto exit; > } > > + switch (datum->xperms->specified) { > + case AVRULE_XPERMS_IOCTLDRIVER: > + case AVRULE_XPERMS_IOCTLFUNCTION: > + func = "ioctl"; > + break; > + case AVRULE_XPERMS_NLMSG: > + func = "nlmsg"; > + break; > + default: > + sepol_log_err("Unexpected xperm spec: %hhu", datum->xperms->specified); > + goto exit; > + } > + > rule = create_str("(%s %s %s (%s %s (%s)))", 6, > - flavor, src, tgt, "ioctl", class, perms); > + flavor, src, tgt, func, class, perms); > } else { > new = pdb->p_type_val_to_name[data - 1]; > > diff --git a/libsepol/src/module_to_cil.c b/libsepol/src/module_to_cil.c > index 16e4004e..47d9b171 100644 > --- a/libsepol/src/module_to_cil.c > +++ b/libsepol/src/module_to_cil.c > @@ -627,14 +627,15 @@ exit: > static int xperms_to_cil(const av_extended_perms_t *xperms) > { > uint16_t value; > - uint16_t low_bit; > + uint16_t low_bit = 0; > uint16_t low_value; > unsigned int bit; > unsigned int in_range = 0; > int first = 1; > > if ((xperms->specified != AVTAB_XPERMS_IOCTLFUNCTION) > - && (xperms->specified != AVTAB_XPERMS_IOCTLDRIVER)) > + && (xperms->specified != AVTAB_XPERMS_IOCTLDRIVER) > + && (xperms->specified != AVTAB_XPERMS_NLMSG)) > return -1; > > for (bit = 0; bit < sizeof(xperms->perms)*8; bit++) { > @@ -674,6 +675,13 @@ static int xperms_to_cil(const av_extended_perms_t *xperms) > } else { > cil_printf("(range 0x%hx 0x%hx)", value, (uint16_t) (value|0xff)); > } > + } else if (xperms->specified & AVTAB_XPERMS_NLMSG) { > + if (in_range) { > + cil_printf("(range 0x%hx 0x%hx)", low_bit, (uint16_t) bit); > + in_range = 0; > + } else { > + cil_printf("0x%hx", (uint16_t) bit); > + } > } > } > > @@ -683,7 +691,7 @@ static int xperms_to_cil(const av_extended_perms_t *xperms) > static int avrulex_to_cil(int indent, struct policydb *pdb, uint32_t type, const char *src, const char *tgt, const class_perm_node_t *classperms, const av_extended_perms_t *xperms) > { > int rc = -1; > - const char *rule; > + const char *rule, *func; > const struct class_perm_node *classperm; > > switch (type) { > @@ -705,10 +713,23 @@ static int avrulex_to_cil(int indent, struct policydb *pdb, uint32_t type, const > goto exit; > } > > + switch (xperms->specified) { > + case AVRULE_XPERMS_IOCTLDRIVER: > + case AVRULE_XPERMS_IOCTLFUNCTION: > + func = "ioctl"; > + break; > + case AVRULE_XPERMS_NLMSG: > + func = "nlmsg"; > + break; > + default: > + log_err("Unexpected xperm spec for %s, %s, %s: %hhu", rule, src, tgt, xperms->specified); > + goto exit; > + } > + > for (classperm = classperms; classperm != NULL; classperm = classperm->next) { > cil_indent(indent); > cil_printf("(%s %s %s (%s %s (", rule, src, tgt, > - "ioctl", pdb->p_class_val_to_name[classperm->tclass - 1]); > + func, pdb->p_class_val_to_name[classperm->tclass - 1]); > xperms_to_cil(xperms); > cil_printf(")))\n"); > } > diff --git a/libsepol/src/optimize.c b/libsepol/src/optimize.c > index 6826155c..f23cfd17 100644 > --- a/libsepol/src/optimize.c > +++ b/libsepol/src/optimize.c > @@ -180,6 +180,12 @@ static int process_avtab_datum(uint16_t specified, > > if (x2->specified == AVTAB_XPERMS_IOCTLDRIVER) > return process_xperms(x1->perms, x2->perms); > + } else if (x1->specified == AVTAB_XPERMS_NLMSG) { > + if (x2->specified == AVTAB_XPERMS_NLMSG) { > + if (x1->driver != x2->driver) > + return 0; > + return process_xperms(x1->perms, x2->perms); > + } > } > return 0; > } > diff --git a/libsepol/src/util.c b/libsepol/src/util.c > index 902c63c5..b5b41f87 100644 > --- a/libsepol/src/util.c > +++ b/libsepol/src/util.c > @@ -124,7 +124,7 @@ char *sepol_av_to_string(policydb_t * policydbp, uint32_t tclass, > char *sepol_extended_perms_to_string(avtab_extended_perms_t *xperms) > { > uint16_t value; > - uint16_t low_bit; > + uint16_t low_bit = 0; > uint16_t low_value; > unsigned int bit; > unsigned int in_range = 0; > @@ -135,7 +135,8 @@ char *sepol_extended_perms_to_string(avtab_extended_perms_t *xperms) > p = xpermsbuf; > > if ((xperms->specified != AVTAB_XPERMS_IOCTLFUNCTION) > - && (xperms->specified != AVTAB_XPERMS_IOCTLDRIVER)) > + && (xperms->specified != AVTAB_XPERMS_IOCTLDRIVER) > + && (xperms->specified != AVTAB_XPERMS_NLMSG)) > return NULL; > > len = snprintf(p, sizeof(xpermsbuf) - xpermslen, "ioctl { "); > @@ -173,6 +174,12 @@ char *sepol_extended_perms_to_string(avtab_extended_perms_t *xperms) > len = snprintf(p, sizeof(xpermsbuf) - xpermslen, "0x%hx-0x%hx ", value, (uint16_t) (value|0xff)); > } > > + } else if (xperms->specified & AVTAB_XPERMS_NLMSG) { > + if (in_range) { > + len = snprintf(p, sizeof(xpermsbuf) - xpermslen, "0x%hx-0x%hx ", low_bit, (uint16_t) bit); > + } else { > + len = snprintf(p, sizeof(xpermsbuf) - xpermslen, "0x%hx ", (uint16_t) bit); > + } > } > > if (len < 0 || (size_t) len >= (sizeof(xpermsbuf) - xpermslen)) > -- > 2.34.0.rc0.344.g81b53c2807-goog >