diff mbox series

[2/3] libsepol: Support nlmsg extended permissions

Message ID 20240822003757.1998016-2-tweek@google.com (mailing list archive)
State Accepted
Commit ba7945a250c0
Delegated to: Petr Lautrbach
Headers show
Series [1/3] libsepol: Rename ioctl xperms structures and functions | expand

Commit Message

Thiébaud Weksteen Aug. 22, 2024, 12:37 a.m. UTC
Add support for AVTAB_XPERMS_NLMSG as extended permissions for netlink
sockets. The behaviour is similar to the existing
AVTAB_XPERMS_IOCTLFUNCTION.

Signed-off-by: Thiébaud Weksteen <tweek@google.com>
---
 checkpolicy/policy_define.c                | 64 +++++++++++++++--
 checkpolicy/test/dismod.c                  |  2 +
 libsepol/cil/src/cil.c                     |  2 +
 libsepol/cil/src/cil_binary.c              | 84 ++++++++++++++++------
 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/expand.c                      |  3 +
 libsepol/src/kernel_to_cil.c               | 21 ++++--
 libsepol/src/module_to_cil.c               | 20 ++++--
 libsepol/src/policydb_validate.c           |  2 +
 libsepol/src/util.c                        | 14 ++--
 16 files changed, 200 insertions(+), 41 deletions(-)

Comments

Stephen Smalley Aug. 29, 2024, 12:49 p.m. UTC | #1
On Wed, Aug 21, 2024 at 8:38 PM Thiébaud Weksteen <tweek@google.com> wrote:
>
> Add support for AVTAB_XPERMS_NLMSG as extended permissions for netlink
> sockets. The behaviour is similar to the existing
> AVTAB_XPERMS_IOCTLFUNCTION.
>
> Signed-off-by: Thiébaud Weksteen <tweek@google.com>

Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>

(side bar: would be nice to reduce the duplicated checking/handling of
"ioctl" and "nlmsg" throughout, but you didn't introduce it for ioctl
so not fair to require you to fix it)

> ---
>  checkpolicy/policy_define.c                | 64 +++++++++++++++--
>  checkpolicy/test/dismod.c                  |  2 +
>  libsepol/cil/src/cil.c                     |  2 +
>  libsepol/cil/src/cil_binary.c              | 84 ++++++++++++++++------
>  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/expand.c                      |  3 +
>  libsepol/src/kernel_to_cil.c               | 21 ++++--
>  libsepol/src/module_to_cil.c               | 20 ++++--
>  libsepol/src/policydb_validate.c           |  2 +
>  libsepol/src/util.c                        | 14 ++--
>  16 files changed, 200 insertions(+), 41 deletions(-)
>
> diff --git a/checkpolicy/policy_define.c b/checkpolicy/policy_define.c
> index 4f6b2266..6b062657 100644
> --- a/checkpolicy/policy_define.c
> +++ b/checkpolicy/policy_define.c
> @@ -2342,8 +2342,8 @@ static int avrule_ioctl_completedriver(struct av_xperm_range_list *rangelist,
>         return 0;
>  }
>
> -static int avrule_ioctl_func(struct av_xperm_range_list *rangelist,
> -               av_extended_perms_t **extended_perms, unsigned int driver)
> +static int avrule_xperm_func(struct av_xperm_range_list *rangelist,
> +               av_extended_perms_t **extended_perms, unsigned int driver, uint8_t specified)
>  {
>         struct av_xperm_range_list *r;
>         av_extended_perms_t *xperms;
> @@ -2379,7 +2379,7 @@ static int avrule_ioctl_func(struct av_xperm_range_list *rangelist,
>                 high = IOC_FUNC(high);
>                 avrule_xperm_setrangebits(low, high, xperms);
>                 xperms->driver = driver;
> -               xperms->specified = AVRULE_XPERMS_IOCTLFUNCTION;
> +               xperms->specified = specified;
>                 r = r->next;
>         }
>
> @@ -2495,7 +2495,61 @@ 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_xperm_func(rangelist, &xperms, i, AVRULE_XPERMS_IOCTLFUNCTION))
> +                       return -1;
> +
> +               if (xperms) {
> +                       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);
> +               }
> +       }
> +
> +done:
> +       if (partial_driver)
> +               free(partial_driver);
> +
> +       while (rangelist != NULL) {
> +               r = rangelist;
> +               rangelist = rangelist->next;
> +               free(r);
> +       }
> +
> +       return 0;
> +}
> +
> +static int define_te_avtab_netlink(const avrule_t *avrule_template)
> +{
> +       avrule_t *avrule;
> +       struct av_xperm_range_list *rangelist, *r;
> +       av_extended_perms_t *partial_driver, *xperms;
> +       unsigned int i;
> +
> +       /* organize ranges */
> +       if (avrule_xperm_ranges(&rangelist))
> +               return -1;
> +
> +       /* flag driver codes that are partially enabled */
> +       if (avrule_xperm_partialdriver(rangelist, NULL, &partial_driver))
> +               return -1;
> +
> +       if (!partial_driver || !avrule_xperms_used(partial_driver))
> +               goto done;
> +
> +       /*
> +        * create rule for each partially used driver codes
> +        * "partially used" meaning that the code number e.g. socket 0x89
> +        * has some permission bits set and others not set.
> +        */
> +       i = 0;
> +       while (xperms_for_each_bit(&i, partial_driver)) {
> +               if (avrule_xperm_func(rangelist, &xperms, i, AVRULE_XPERMS_NLMSG))
>                         return -1;
>
>                 if (xperms) {
> @@ -2546,6 +2600,8 @@ 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 {
>                 yyerror2("only ioctl extended permissions are supported, found %s", id);
>                 rc = -1;
> diff --git a/checkpolicy/test/dismod.c b/checkpolicy/test/dismod.c
> index bd45c95e..4868190f 100644
> --- a/checkpolicy/test/dismod.c
> +++ b/checkpolicy/test/dismod.c
> @@ -353,6 +353,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 067e28a6..5521c7ea 100644
> --- a/libsepol/cil/src/cil.c
> +++ b/libsepol/cil/src/cil.c
> @@ -221,6 +221,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;
> @@ -393,6 +394,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 c8144a5a..3dec1883 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;
>  };
>
> @@ -1671,11 +1672,22 @@ static void __avrule_xperm_setrangebits(uint16_t low, uint16_t high, struct avta
>         }
>  }
>
> +static char* __cil_xperm_kind_to_str(uint32_t xperm_kind)
> +{
> +       switch (xperm_kind) {
> +               case CIL_PERMX_KIND_IOCTL:
> +                       return CIL_KEY_IOCTL;
> +               case CIL_PERMX_KIND_NLMSG:
> +                       return CIL_KEY_NLMSG;
> +               default:
> +                       return (char *) "unknown";
> +       }
> +}
>
>  #define IOC_DRIV(x) (x >> 8)
>  #define IOC_FUNC(x) (x & 0xff)
>
> -static int __cil_permx_bitmap_to_sepol_xperms_list(ebitmap_t *xperms, struct cil_list **xperms_list)
> +static int __cil_permx_bitmap_to_sepol_xperms_list(uint32_t kind, ebitmap_t *xperms, struct cil_list **xperms_list)
>  {
>         ebitmap_node_t *node;
>         unsigned int i;
> @@ -1705,7 +1717,7 @@ static int __cil_permx_bitmap_to_sepol_xperms_list(ebitmap_t *xperms, struct cil
>                 high = i;
>                 start_new_range = 1;
>
> -               if (IOC_FUNC(low) == 0x00 && IOC_FUNC(high) == 0xff) {
> +               if (kind == CIL_PERMX_KIND_IOCTL && IOC_FUNC(low) == 0x00 && IOC_FUNC(high) == 0xff) {
>                         if (!complete) {
>                                 complete = cil_calloc(1, sizeof(*complete));
>                                 complete->driver = 0x0;
> @@ -1722,7 +1734,14 @@ static int __cil_permx_bitmap_to_sepol_xperms_list(ebitmap_t *xperms, struct cil
>                         if (!partial) {
>                                 partial = cil_calloc(1, sizeof(*partial));
>                                 partial->driver = IOC_DRIV(low);
> -                               partial->specified = AVTAB_XPERMS_IOCTLFUNCTION;
> +                               switch (kind) {
> +                               case CIL_PERMX_KIND_IOCTL:
> +                                       partial->specified = AVTAB_XPERMS_IOCTLFUNCTION;
> +                                       break;
> +                               case CIL_PERMX_KIND_NLMSG:
> +                                       partial->specified = AVTAB_XPERMS_NLMSG;
> +                                       break;
> +                               }
>                         }
>
>                         __avrule_xperm_setrangebits(IOC_FUNC(low), IOC_FUNC(high), partial);
> @@ -1740,7 +1759,7 @@ static int __cil_permx_bitmap_to_sepol_xperms_list(ebitmap_t *xperms, struct cil
>         return SEPOL_OK;
>  }
>
> -static int __cil_avrulex_ioctl_to_policydb(hashtab_key_t k, hashtab_datum_t datum, void *args)
> +static int __cil_avrulex_xperm_to_policydb(hashtab_key_t k, hashtab_datum_t datum, uint32_t xperm_kind, void *args)
>  {
>         int rc = SEPOL_OK;
>         struct policydb *pdb;
> @@ -1750,6 +1769,7 @@ static int __cil_avrulex_ioctl_to_policydb(hashtab_key_t k, hashtab_datum_t datu
>         struct cil_list_item *item;
>         class_datum_t *sepol_obj;
>         uint32_t data = 0;
> +       char *kind = NULL;
>
>         avtab_key = (avtab_key_t *)k;
>         pdb = args;
> @@ -1759,13 +1779,14 @@ static int __cil_avrulex_ioctl_to_policydb(hashtab_key_t k, hashtab_datum_t datu
>         // 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);
> +       kind = __cil_xperm_kind_to_str(xperm_kind);
> +       rc = __perm_str_to_datum(kind, sepol_obj, &data);
>         if (rc != SEPOL_OK) {
>                 goto exit;
>         }
>         avtab_datum.data = data;
>
> -       rc = __cil_permx_bitmap_to_sepol_xperms_list(datum, &xperms_list);
> +       rc = __cil_permx_bitmap_to_sepol_xperms_list(xperm_kind, datum, &xperms_list);
>         if (rc != SEPOL_OK) {
>                 goto exit;
>         }
> @@ -1790,7 +1811,15 @@ exit:
>         return rc;
>  }
>
> -static int __cil_avrulex_ioctl_to_hashtable(hashtab_t h, uint16_t kind, uint32_t src, uint32_t tgt, uint32_t obj, ebitmap_t *xperms)
> +static int __cil_avrulex_ioctl_to_policydb(hashtab_key_t k, hashtab_datum_t datum, void *args) {
> +       return __cil_avrulex_xperm_to_policydb(k, datum, CIL_PERMX_KIND_IOCTL, args);
> +}
> +
> +static int __cil_avrulex_nlmsg_to_policydb(hashtab_key_t k, hashtab_datum_t datum, void *args) {
> +       return __cil_avrulex_xperm_to_policydb(k, datum, CIL_PERMX_KIND_NLMSG, args);
> +}
> +
> +static int __cil_avrulex_xperm_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;
> @@ -1870,7 +1899,11 @@ static int __cil_avrulex_to_hashtable_helper(policydb_t *pdb, uint16_t kind, str
>
>                 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);
> +                       rc = __cil_avrulex_xperm_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_xperm_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:
> @@ -2037,7 +2070,7 @@ exit:
>         return rc;
>  }
>
> -static int __cil_avrulex_ioctl_destroy(hashtab_key_t k, hashtab_datum_t datum, __attribute__((unused)) void *args)
> +static int __cil_avrulex_xperm_destroy(hashtab_key_t k, hashtab_datum_t datum, __attribute__((unused)) void *args)
>  {
>         free(k);
>         ebitmap_destroy(datum);
> @@ -4630,6 +4663,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;
> @@ -4769,17 +4805,10 @@ static void __cil_print_classperm(struct cil_list *cp_list)
>
>  static void __cil_print_permissionx(struct cil_permissionx *px)
>  {
> -       const char *kind_str = "";
> +       const char *kind_str = NULL;
>         char *expr_str;
>
> -       switch (px->kind) {
> -               case CIL_PERMX_KIND_IOCTL:
> -                       kind_str = CIL_KEY_IOCTL;
> -                       break;
> -               default:
> -                       kind_str = "unknown";
> -                       break;
> -       }
> +       kind_str = __cil_xperm_kind_to_str(px->kind);
>
>         __cil_expr_to_string(px->expr_str, CIL_PERMISSIONX, &expr_str);
>
> @@ -4928,7 +4957,7 @@ 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);
> +               rc = __cil_permx_bitmap_to_sepol_xperms_list(cil_rule->perms.x.permx->kind, cil_rule->perms.x.permx->perms, &xperms);
>                 if (rc != SEPOL_OK) {
>                         goto exit;
>                 }
> @@ -5137,6 +5166,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;
> @@ -5184,6 +5214,12 @@ int cil_binary_create_allocated_pdb(const struct cil_db *db, sepol_policydb_t *p
>                 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 avrulex\n");
> +               goto exit;
> +       }
> +
>         cil_list_init(&neverallows, CIL_LIST_ITEM);
>
>         extra_args.db = db;
> @@ -5191,6 +5227,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++) {
> @@ -5216,6 +5253,11 @@ int cil_binary_create_allocated_pdb(const struct cil_db *db, sepol_policydb_t *p
>                                 cil_log(CIL_INFO, "Failure creating 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 avrulex rules\n");
> +                               goto exit;
> +                       }
>                 }
>         }
>
> @@ -5287,8 +5329,10 @@ int cil_binary_create_allocated_pdb(const struct cil_db *db, sepol_policydb_t *p
>
>  exit:
>         hashtab_destroy(role_trans_table);
> -       hashtab_map(avrulex_ioctl_table, __cil_avrulex_ioctl_destroy, NULL);
> +       hashtab_map(avrulex_ioctl_table, __cil_avrulex_xperm_destroy, NULL);
>         hashtab_destroy(avrulex_ioctl_table);
> +       hashtab_map(avrulex_nlmsg_table, __cil_avrulex_xperm_destroy, NULL);
> +       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 56dac891..87178294 100644
> --- a/libsepol/cil/src/cil_build_ast.c
> +++ b/libsepol/cil/src/cil_build_ast.c
> @@ -2153,8 +2153,10 @@ static int cil_fill_permissionx(struct cil_tree_node *parse_current, struct cil_
>
>         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 \"ioctl\" or \"nlmsg\"\n", (char *)parse_current->data);
>                 rc = SEPOL_ERR;
>                 goto exit;
>         }
> diff --git a/libsepol/cil/src/cil_internal.h b/libsepol/cil/src/cil_internal.h
> index 47b67c89..959b31e3 100644
> --- a/libsepol/cil/src/cil_internal.h
> +++ b/libsepol/cil/src/cil_internal.h
> @@ -238,6 +238,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;
> @@ -636,6 +637,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 e9a8f75d..c497c8ab 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 = "nlmsg";
>         } else {
>                 kind = "???";
>         }
> diff --git a/libsepol/cil/src/cil_verify.c b/libsepol/cil/src/cil_verify.c
> index 4ef2cbab..9621a247 100644
> --- a/libsepol/cil/src/cil_verify.c
> +++ b/libsepol/cil/src/cil_verify.c
> @@ -1513,6 +1513,9 @@ static int __cil_verify_permissionx(struct cil_permissionx *permx, struct cil_tr
>                 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 46bd84db..cd1b6e6c 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, "ioctl ");
> +               } else if (permx->kind == CIL_PERMX_KIND_NLMSG) {
> +                       fprintf(out, "nlmsg ");
> +               } else {
> +                       fprintf(out, "<?KIND> ");
> +               }
>                 fprintf(out, "%s ", datum_or_str(DATUM(permx->obj), permx->obj_str));
>                 write_expr(out, permx->expr_str);
>                 fprintf(out, ")");
> @@ -825,7 +831,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, "ioctl ");
> +               } else if (permx->kind == CIL_PERMX_KIND_NLMSG) {
> +                       fprintf(out, "nlmsg ");
> +               } else {
> +                       fprintf(out, "<?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 2ab99c39..6e154cfe 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 856faeb7..104a7dc8 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/expand.c b/libsepol/src/expand.c
> index e63414b1..7032a83f 100644
> --- a/libsepol/src/expand.c
> +++ b/libsepol/src/expand.c
> @@ -1821,6 +1821,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 f94cb245..7243b3c0 100644
> --- a/libsepol/src/kernel_to_cil.c
> +++ b/libsepol/src/kernel_to_cil.c
> @@ -1651,7 +1651,8 @@ static char *xperms_to_str(const avtab_extended_perms_t *xperms)
>         size_t remaining, size = 128;
>
>         if ((xperms->specified != AVTAB_XPERMS_IOCTLFUNCTION)
> -               && (xperms->specified != AVTAB_XPERMS_IOCTLDRIVER)) {
> +               && (xperms->specified != AVTAB_XPERMS_IOCTLDRIVER)
> +               && (xperms->specified != AVTAB_XPERMS_NLMSG)) {
>                 return NULL;
>         }
>
> @@ -1681,7 +1682,8 @@ retry:
>                         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;
> @@ -1690,7 +1692,7 @@ retry:
>                         } else {
>                                 len = snprintf(p, remaining, " 0x%hx", value);
>                         }
> -               } else if (xperms->specified & AVTAB_XPERMS_IOCTLDRIVER) {
> +               } else if (xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) {
>                         value = bit << 8;
>                         if (in_range) {
>                                 low_value = low_bit << 8;
> @@ -1728,7 +1730,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;
> -       char *src, *class, *perms, *new;
> +       char *src, *class, *perms, *new, *xperm;
>         char *rule = NULL;
>
>         switch (0xFFF & key->specified) {
> @@ -1795,9 +1797,16 @@ static char *avtab_node_to_str(struct policydb *pdb, avtab_key_t *key, avtab_dat
>                         ERR(NULL, "Failed to generate extended permission string");
>                         goto exit;
>                 }
> -
> +               if (datum->xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION || datum->xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) {
> +                       xperm = (char *) "ioctl";
> +               } else if (datum->xperms->specified == AVTAB_XPERMS_NLMSG) {
> +                       xperm = (char *) "nlmsg";
> +               } else {
> +                       ERR(NULL, "Unknown extended permssion");
> +                       goto exit;
> +               }
>                 rule = create_str("(%s %s %s (%s %s (%s)))",
> -                                 flavor, src, tgt, "ioctl", class, perms);
> +                                 flavor, src, tgt, xperm, class, perms);
>                 free(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 2dbf137e..79636897 100644
> --- a/libsepol/src/module_to_cil.c
> +++ b/libsepol/src/module_to_cil.c
> @@ -630,7 +630,8 @@ static int xperms_to_cil(const av_extended_perms_t *xperms)
>         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++) {
> @@ -652,7 +653,8 @@ static int xperms_to_cil(const av_extended_perms_t *xperms)
>                 else
>                         first = 0;
>
> -               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;
> @@ -661,7 +663,7 @@ static int xperms_to_cil(const av_extended_perms_t *xperms)
>                         } else {
>                                 cil_printf("0x%hx", value);
>                         }
> -               } else if (xperms->specified & AVTAB_XPERMS_IOCTLDRIVER) {
> +               } else if (xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) {
>                         value = bit << 8;
>                         if (in_range) {
>                                 low_value = low_bit << 8;
> @@ -680,6 +682,7 @@ static int avrulex_to_cil(int indent, struct policydb *pdb, uint32_t type, const
>  {
>         int rc = -1;
>         const char *rule;
> +       const char *xperm;
>         const struct class_perm_node *classperm;
>
>         switch (type) {
> @@ -701,10 +704,19 @@ static int avrulex_to_cil(int indent, struct policydb *pdb, uint32_t type, const
>                 goto exit;
>         }
>
> +       if (xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION || xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) {
> +               xperm = "ioctl";
> +       } else if (xperms->specified == AVTAB_XPERMS_NLMSG) {
> +               xperm = "nlmsg";
> +       } else {
> +               ERR(NULL, "Unkown avrule xperms->specified: %i", xperms->specified);
> +               rc = -1;
> +               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]);
> +                          xperm, pdb->p_class_val_to_name[classperm->tclass - 1]);
>                 xperms_to_cil(xperms);
>                 cil_printf(")))\n");
>         }
> diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
> index 121fd46c..5035313b 100644
> --- a/libsepol/src/policydb_validate.c
> +++ b/libsepol/src/policydb_validate.c
> @@ -921,6 +921,7 @@ static int validate_xperms(const avtab_extended_perms_t *xperms)
>         switch (xperms->specified) {
>         case AVTAB_XPERMS_IOCTLDRIVER:
>         case AVTAB_XPERMS_IOCTLFUNCTION:
> +       case AVTAB_XPERMS_NLMSG:
>                 break;
>         default:
>                 goto bad;
> @@ -1067,6 +1068,7 @@ static int validate_avrules(sepol_handle_t *handle, const avrule_t *avrule, int
>                         switch (avrule->xperms->specified) {
>                         case AVRULE_XPERMS_IOCTLFUNCTION:
>                         case AVRULE_XPERMS_IOCTLDRIVER:
> +                       case AVRULE_XPERMS_NLMSG:
>                                 break;
>                         default:
>                                 goto bad;
> diff --git a/libsepol/src/util.c b/libsepol/src/util.c
> index b1eb9b38..a4befbd9 100644
> --- a/libsepol/src/util.c
> +++ b/libsepol/src/util.c
> @@ -146,7 +146,8 @@ char *sepol_extended_perms_to_string(const avtab_extended_perms_t *xperms)
>         size_t remaining, size = 128;
>
>         if ((xperms->specified != AVTAB_XPERMS_IOCTLFUNCTION)
> -               && (xperms->specified != AVTAB_XPERMS_IOCTLDRIVER))
> +               && (xperms->specified != AVTAB_XPERMS_IOCTLDRIVER)
> +               && (xperms->specified != AVTAB_XPERMS_NLMSG))
>                 return NULL;
>
>  retry:
> @@ -158,7 +159,12 @@ retry:
>         buffer = p;
>         remaining = size;
>
> -       len = snprintf(p, remaining, "ioctl { ");
> +       if ((xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION)
> +               || (xperms->specified == AVTAB_XPERMS_IOCTLDRIVER)) {
> +               len = snprintf(p, remaining, "ioctl { ");
> +       } else {
> +               len = snprintf(p, remaining, "nlmsg { ");
> +       }
>         if (len < 0 || (size_t)len >= remaining)
>                 goto err;
>         p += len;
> @@ -179,7 +185,7 @@ retry:
>                         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;
> @@ -187,7 +193,7 @@ retry:
>                         } else {
>                                 len = snprintf(p, remaining, "0x%hx ", value);
>                         }
> -               } else if (xperms->specified & AVTAB_XPERMS_IOCTLDRIVER) {
> +               } else if (xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) {
>                         value = bit << 8;
>                         if (in_range) {
>                                 low_value = low_bit << 8;
> --
> 2.46.0.184.g6999bdac58-goog
>
diff mbox series

Patch

diff --git a/checkpolicy/policy_define.c b/checkpolicy/policy_define.c
index 4f6b2266..6b062657 100644
--- a/checkpolicy/policy_define.c
+++ b/checkpolicy/policy_define.c
@@ -2342,8 +2342,8 @@  static int avrule_ioctl_completedriver(struct av_xperm_range_list *rangelist,
 	return 0;
 }
 
-static int avrule_ioctl_func(struct av_xperm_range_list *rangelist,
-		av_extended_perms_t **extended_perms, unsigned int driver)
+static int avrule_xperm_func(struct av_xperm_range_list *rangelist,
+		av_extended_perms_t **extended_perms, unsigned int driver, uint8_t specified)
 {
 	struct av_xperm_range_list *r;
 	av_extended_perms_t *xperms;
@@ -2379,7 +2379,7 @@  static int avrule_ioctl_func(struct av_xperm_range_list *rangelist,
 		high = IOC_FUNC(high);
 		avrule_xperm_setrangebits(low, high, xperms);
 		xperms->driver = driver;
-		xperms->specified = AVRULE_XPERMS_IOCTLFUNCTION;
+		xperms->specified = specified;
 		r = r->next;
 	}
 
@@ -2495,7 +2495,61 @@  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_xperm_func(rangelist, &xperms, i, AVRULE_XPERMS_IOCTLFUNCTION))
+			return -1;
+
+		if (xperms) {
+			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);
+		}
+	}
+
+done:
+	if (partial_driver)
+		free(partial_driver);
+
+	while (rangelist != NULL) {
+		r = rangelist;
+		rangelist = rangelist->next;
+		free(r);
+	}
+
+	return 0;
+}
+
+static int define_te_avtab_netlink(const avrule_t *avrule_template)
+{
+	avrule_t *avrule;
+	struct av_xperm_range_list *rangelist, *r;
+	av_extended_perms_t *partial_driver, *xperms;
+	unsigned int i;
+
+	/* organize ranges */
+	if (avrule_xperm_ranges(&rangelist))
+		return -1;
+
+	/* flag driver codes that are partially enabled */
+	if (avrule_xperm_partialdriver(rangelist, NULL, &partial_driver))
+		return -1;
+
+	if (!partial_driver || !avrule_xperms_used(partial_driver))
+		goto done;
+
+	/*
+	 * create rule for each partially used driver codes
+	 * "partially used" meaning that the code number e.g. socket 0x89
+	 * has some permission bits set and others not set.
+	 */
+	i = 0;
+	while (xperms_for_each_bit(&i, partial_driver)) {
+		if (avrule_xperm_func(rangelist, &xperms, i, AVRULE_XPERMS_NLMSG))
 			return -1;
 
 		if (xperms) {
@@ -2546,6 +2600,8 @@  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 {
 		yyerror2("only ioctl extended permissions are supported, found %s", id);
 		rc = -1;
diff --git a/checkpolicy/test/dismod.c b/checkpolicy/test/dismod.c
index bd45c95e..4868190f 100644
--- a/checkpolicy/test/dismod.c
+++ b/checkpolicy/test/dismod.c
@@ -353,6 +353,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 067e28a6..5521c7ea 100644
--- a/libsepol/cil/src/cil.c
+++ b/libsepol/cil/src/cil.c
@@ -221,6 +221,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;
@@ -393,6 +394,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 c8144a5a..3dec1883 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;
 };
 
@@ -1671,11 +1672,22 @@  static void __avrule_xperm_setrangebits(uint16_t low, uint16_t high, struct avta
 	}
 }
 
+static char* __cil_xperm_kind_to_str(uint32_t xperm_kind)
+{
+	switch (xperm_kind) {
+		case CIL_PERMX_KIND_IOCTL:
+			return CIL_KEY_IOCTL;
+		case CIL_PERMX_KIND_NLMSG:
+			return CIL_KEY_NLMSG;
+		default:
+			return (char *) "unknown";
+	}
+}
 
 #define IOC_DRIV(x) (x >> 8)
 #define IOC_FUNC(x) (x & 0xff)
 
-static int __cil_permx_bitmap_to_sepol_xperms_list(ebitmap_t *xperms, struct cil_list **xperms_list)
+static int __cil_permx_bitmap_to_sepol_xperms_list(uint32_t kind, ebitmap_t *xperms, struct cil_list **xperms_list)
 {
 	ebitmap_node_t *node;
 	unsigned int i;
@@ -1705,7 +1717,7 @@  static int __cil_permx_bitmap_to_sepol_xperms_list(ebitmap_t *xperms, struct cil
 		high = i;
 		start_new_range = 1;
 
-		if (IOC_FUNC(low) == 0x00 && IOC_FUNC(high) == 0xff) {
+		if (kind == CIL_PERMX_KIND_IOCTL && IOC_FUNC(low) == 0x00 && IOC_FUNC(high) == 0xff) {
 			if (!complete) {
 				complete = cil_calloc(1, sizeof(*complete));
 				complete->driver = 0x0;
@@ -1722,7 +1734,14 @@  static int __cil_permx_bitmap_to_sepol_xperms_list(ebitmap_t *xperms, struct cil
 			if (!partial) {
 				partial = cil_calloc(1, sizeof(*partial));
 				partial->driver = IOC_DRIV(low);
-				partial->specified = AVTAB_XPERMS_IOCTLFUNCTION;
+				switch (kind) {
+				case CIL_PERMX_KIND_IOCTL:
+					partial->specified = AVTAB_XPERMS_IOCTLFUNCTION;
+					break;
+				case CIL_PERMX_KIND_NLMSG:
+					partial->specified = AVTAB_XPERMS_NLMSG;
+					break;
+				}
 			}
 
 			__avrule_xperm_setrangebits(IOC_FUNC(low), IOC_FUNC(high), partial);
@@ -1740,7 +1759,7 @@  static int __cil_permx_bitmap_to_sepol_xperms_list(ebitmap_t *xperms, struct cil
 	return SEPOL_OK;
 }
 
-static int __cil_avrulex_ioctl_to_policydb(hashtab_key_t k, hashtab_datum_t datum, void *args)
+static int __cil_avrulex_xperm_to_policydb(hashtab_key_t k, hashtab_datum_t datum, uint32_t xperm_kind, void *args)
 {
 	int rc = SEPOL_OK;
 	struct policydb *pdb;
@@ -1750,6 +1769,7 @@  static int __cil_avrulex_ioctl_to_policydb(hashtab_key_t k, hashtab_datum_t datu
 	struct cil_list_item *item;
 	class_datum_t *sepol_obj;
 	uint32_t data = 0;
+	char *kind = NULL;
 
 	avtab_key = (avtab_key_t *)k;
 	pdb = args;
@@ -1759,13 +1779,14 @@  static int __cil_avrulex_ioctl_to_policydb(hashtab_key_t k, hashtab_datum_t datu
 	// 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);
+	kind = __cil_xperm_kind_to_str(xperm_kind);
+	rc = __perm_str_to_datum(kind, sepol_obj, &data);
 	if (rc != SEPOL_OK) {
 		goto exit;
 	}
 	avtab_datum.data = data;
 
-	rc = __cil_permx_bitmap_to_sepol_xperms_list(datum, &xperms_list);
+	rc = __cil_permx_bitmap_to_sepol_xperms_list(xperm_kind, datum, &xperms_list);
 	if (rc != SEPOL_OK) {
 		goto exit;
 	}
@@ -1790,7 +1811,15 @@  exit:
 	return rc;
 }
 
-static int __cil_avrulex_ioctl_to_hashtable(hashtab_t h, uint16_t kind, uint32_t src, uint32_t tgt, uint32_t obj, ebitmap_t *xperms)
+static int __cil_avrulex_ioctl_to_policydb(hashtab_key_t k, hashtab_datum_t datum, void *args) {
+	return __cil_avrulex_xperm_to_policydb(k, datum, CIL_PERMX_KIND_IOCTL, args);
+}
+
+static int __cil_avrulex_nlmsg_to_policydb(hashtab_key_t k, hashtab_datum_t datum, void *args) {
+	return __cil_avrulex_xperm_to_policydb(k, datum, CIL_PERMX_KIND_NLMSG, args);
+}
+
+static int __cil_avrulex_xperm_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;
@@ -1870,7 +1899,11 @@  static int __cil_avrulex_to_hashtable_helper(policydb_t *pdb, uint16_t kind, str
 
 		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);
+			rc = __cil_avrulex_xperm_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_xperm_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:
@@ -2037,7 +2070,7 @@  exit:
 	return rc;
 }
 
-static int __cil_avrulex_ioctl_destroy(hashtab_key_t k, hashtab_datum_t datum, __attribute__((unused)) void *args)
+static int __cil_avrulex_xperm_destroy(hashtab_key_t k, hashtab_datum_t datum, __attribute__((unused)) void *args)
 {
 	free(k);
 	ebitmap_destroy(datum);
@@ -4630,6 +4663,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;
@@ -4769,17 +4805,10 @@  static void __cil_print_classperm(struct cil_list *cp_list)
 
 static void __cil_print_permissionx(struct cil_permissionx *px)
 {
-	const char *kind_str = "";
+	const char *kind_str = NULL;
 	char *expr_str;
 
-	switch (px->kind) {
-		case CIL_PERMX_KIND_IOCTL:
-			kind_str = CIL_KEY_IOCTL;
-			break;
-		default:
-			kind_str = "unknown";
-			break;
-	}
+	kind_str = __cil_xperm_kind_to_str(px->kind);
 
 	__cil_expr_to_string(px->expr_str, CIL_PERMISSIONX, &expr_str);
 
@@ -4928,7 +4957,7 @@  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);
+		rc = __cil_permx_bitmap_to_sepol_xperms_list(cil_rule->perms.x.permx->kind, cil_rule->perms.x.permx->perms, &xperms);
 		if (rc != SEPOL_OK) {
 			goto exit;
 		}
@@ -5137,6 +5166,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;
@@ -5184,6 +5214,12 @@  int cil_binary_create_allocated_pdb(const struct cil_db *db, sepol_policydb_t *p
 		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 avrulex\n");
+		goto exit;
+	}
+
 	cil_list_init(&neverallows, CIL_LIST_ITEM);
 
 	extra_args.db = db;
@@ -5191,6 +5227,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++) {
@@ -5216,6 +5253,11 @@  int cil_binary_create_allocated_pdb(const struct cil_db *db, sepol_policydb_t *p
 				cil_log(CIL_INFO, "Failure creating 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 avrulex rules\n");
+				goto exit;
+			}
 		}
 	}
 
@@ -5287,8 +5329,10 @@  int cil_binary_create_allocated_pdb(const struct cil_db *db, sepol_policydb_t *p
 
 exit:
 	hashtab_destroy(role_trans_table);
-	hashtab_map(avrulex_ioctl_table, __cil_avrulex_ioctl_destroy, NULL);
+	hashtab_map(avrulex_ioctl_table, __cil_avrulex_xperm_destroy, NULL);
 	hashtab_destroy(avrulex_ioctl_table);
+	hashtab_map(avrulex_nlmsg_table, __cil_avrulex_xperm_destroy, NULL);
+	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 56dac891..87178294 100644
--- a/libsepol/cil/src/cil_build_ast.c
+++ b/libsepol/cil/src/cil_build_ast.c
@@ -2153,8 +2153,10 @@  static int cil_fill_permissionx(struct cil_tree_node *parse_current, struct cil_
 
 	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 \"ioctl\" or \"nlmsg\"\n", (char *)parse_current->data);
 		rc = SEPOL_ERR;
 		goto exit;
 	}
diff --git a/libsepol/cil/src/cil_internal.h b/libsepol/cil/src/cil_internal.h
index 47b67c89..959b31e3 100644
--- a/libsepol/cil/src/cil_internal.h
+++ b/libsepol/cil/src/cil_internal.h
@@ -238,6 +238,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;
@@ -636,6 +637,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 e9a8f75d..c497c8ab 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 = "nlmsg";
 	} else {
 		kind = "???";
 	}
diff --git a/libsepol/cil/src/cil_verify.c b/libsepol/cil/src/cil_verify.c
index 4ef2cbab..9621a247 100644
--- a/libsepol/cil/src/cil_verify.c
+++ b/libsepol/cil/src/cil_verify.c
@@ -1513,6 +1513,9 @@  static int __cil_verify_permissionx(struct cil_permissionx *permx, struct cil_tr
 		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 46bd84db..cd1b6e6c 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, "ioctl ");
+		} else if (permx->kind == CIL_PERMX_KIND_NLMSG) {
+			fprintf(out, "nlmsg ");
+		} else {
+			fprintf(out, "<?KIND> ");
+		}
 		fprintf(out, "%s ", datum_or_str(DATUM(permx->obj), permx->obj_str));
 		write_expr(out, permx->expr_str);
 		fprintf(out, ")");
@@ -825,7 +831,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, "ioctl ");
+		} else if (permx->kind == CIL_PERMX_KIND_NLMSG) {
+			fprintf(out, "nlmsg ");
+		} else {
+			fprintf(out, "<?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 2ab99c39..6e154cfe 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 856faeb7..104a7dc8 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/expand.c b/libsepol/src/expand.c
index e63414b1..7032a83f 100644
--- a/libsepol/src/expand.c
+++ b/libsepol/src/expand.c
@@ -1821,6 +1821,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 f94cb245..7243b3c0 100644
--- a/libsepol/src/kernel_to_cil.c
+++ b/libsepol/src/kernel_to_cil.c
@@ -1651,7 +1651,8 @@  static char *xperms_to_str(const avtab_extended_perms_t *xperms)
 	size_t remaining, size = 128;
 
 	if ((xperms->specified != AVTAB_XPERMS_IOCTLFUNCTION)
-		&& (xperms->specified != AVTAB_XPERMS_IOCTLDRIVER)) {
+		&& (xperms->specified != AVTAB_XPERMS_IOCTLDRIVER)
+		&& (xperms->specified != AVTAB_XPERMS_NLMSG)) {
 		return NULL;
 	}
 
@@ -1681,7 +1682,8 @@  retry:
 			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;
@@ -1690,7 +1692,7 @@  retry:
 			} else {
 				len = snprintf(p, remaining, " 0x%hx", value);
 			}
-		} else if (xperms->specified & AVTAB_XPERMS_IOCTLDRIVER) {
+		} else if (xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) {
 			value = bit << 8;
 			if (in_range) {
 				low_value = low_bit << 8;
@@ -1728,7 +1730,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;
-	char *src, *class, *perms, *new;
+	char *src, *class, *perms, *new, *xperm;
 	char *rule = NULL;
 
 	switch (0xFFF & key->specified) {
@@ -1795,9 +1797,16 @@  static char *avtab_node_to_str(struct policydb *pdb, avtab_key_t *key, avtab_dat
 			ERR(NULL, "Failed to generate extended permission string");
 			goto exit;
 		}
-
+		if (datum->xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION || datum->xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) {
+			xperm = (char *) "ioctl";
+		} else if (datum->xperms->specified == AVTAB_XPERMS_NLMSG) {
+			xperm = (char *) "nlmsg";
+		} else {
+			ERR(NULL, "Unknown extended permssion");
+			goto exit;
+		}
 		rule = create_str("(%s %s %s (%s %s (%s)))",
-				  flavor, src, tgt, "ioctl", class, perms);
+				  flavor, src, tgt, xperm, class, perms);
 		free(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 2dbf137e..79636897 100644
--- a/libsepol/src/module_to_cil.c
+++ b/libsepol/src/module_to_cil.c
@@ -630,7 +630,8 @@  static int xperms_to_cil(const av_extended_perms_t *xperms)
 	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++) {
@@ -652,7 +653,8 @@  static int xperms_to_cil(const av_extended_perms_t *xperms)
 		else
 			first = 0;
 
-		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;
@@ -661,7 +663,7 @@  static int xperms_to_cil(const av_extended_perms_t *xperms)
 			} else {
 				cil_printf("0x%hx", value);
 			}
-		} else if (xperms->specified & AVTAB_XPERMS_IOCTLDRIVER) {
+		} else if (xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) {
 			value = bit << 8;
 			if (in_range) {
 				low_value = low_bit << 8;
@@ -680,6 +682,7 @@  static int avrulex_to_cil(int indent, struct policydb *pdb, uint32_t type, const
 {
 	int rc = -1;
 	const char *rule;
+	const char *xperm;
 	const struct class_perm_node *classperm;
 
 	switch (type) {
@@ -701,10 +704,19 @@  static int avrulex_to_cil(int indent, struct policydb *pdb, uint32_t type, const
 		goto exit;
 	}
 
+	if (xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION || xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) {
+		xperm = "ioctl";
+	} else if (xperms->specified == AVTAB_XPERMS_NLMSG) {
+		xperm = "nlmsg";
+	} else {
+		ERR(NULL, "Unkown avrule xperms->specified: %i", xperms->specified);
+		rc = -1;
+		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]);
+			   xperm, pdb->p_class_val_to_name[classperm->tclass - 1]);
 		xperms_to_cil(xperms);
 		cil_printf(")))\n");
 	}
diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index 121fd46c..5035313b 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -921,6 +921,7 @@  static int validate_xperms(const avtab_extended_perms_t *xperms)
 	switch (xperms->specified) {
 	case AVTAB_XPERMS_IOCTLDRIVER:
 	case AVTAB_XPERMS_IOCTLFUNCTION:
+	case AVTAB_XPERMS_NLMSG:
 		break;
 	default:
 		goto bad;
@@ -1067,6 +1068,7 @@  static int validate_avrules(sepol_handle_t *handle, const avrule_t *avrule, int
 			switch (avrule->xperms->specified) {
 			case AVRULE_XPERMS_IOCTLFUNCTION:
 			case AVRULE_XPERMS_IOCTLDRIVER:
+			case AVRULE_XPERMS_NLMSG:
 				break;
 			default:
 				goto bad;
diff --git a/libsepol/src/util.c b/libsepol/src/util.c
index b1eb9b38..a4befbd9 100644
--- a/libsepol/src/util.c
+++ b/libsepol/src/util.c
@@ -146,7 +146,8 @@  char *sepol_extended_perms_to_string(const avtab_extended_perms_t *xperms)
 	size_t remaining, size = 128;
 
 	if ((xperms->specified != AVTAB_XPERMS_IOCTLFUNCTION)
-		&& (xperms->specified != AVTAB_XPERMS_IOCTLDRIVER))
+		&& (xperms->specified != AVTAB_XPERMS_IOCTLDRIVER)
+		&& (xperms->specified != AVTAB_XPERMS_NLMSG))
 		return NULL;
 
 retry:
@@ -158,7 +159,12 @@  retry:
 	buffer = p;
 	remaining = size;
 
-	len = snprintf(p, remaining, "ioctl { ");
+	if ((xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION)
+		|| (xperms->specified == AVTAB_XPERMS_IOCTLDRIVER)) {
+		len = snprintf(p, remaining, "ioctl { ");
+	} else {
+		len = snprintf(p, remaining, "nlmsg { ");
+	}
 	if (len < 0 || (size_t)len >= remaining)
 		goto err;
 	p += len;
@@ -179,7 +185,7 @@  retry:
 			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;
@@ -187,7 +193,7 @@  retry:
 			} else {
 				len = snprintf(p, remaining, "0x%hx ", value);
 			}
-		} else if (xperms->specified & AVTAB_XPERMS_IOCTLDRIVER) {
+		} else if (xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) {
 			value = bit << 8;
 			if (in_range) {
 				low_value = low_bit << 8;