Re: [RFC PATCH] libsepol,checkpolicy: Add netlink xperm support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, Nov 16, 2021 at 10:44 AM James Carter <jwcart2@xxxxxxxxx> wrote:
>
> 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
> > };
> > 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)

I thought netlink message types were 16-bit, but they are limited to 8
bits in checkpolicy because the driver must be 0.
In avrule_xperms_single_driver() the following check is made
  if ((driver != IOC_DRIV(low)) && (driver != IOC_DRIV(high)))
with 0 passed in for the driver. This causes any nlmsg rule for a
message type  > 255 to be skipped.

I am not sure what was happening with the CIL policy, but limiting the
netlink message types to be < 256 fixes the problem.

There needs to be error messages if the netlink message type is > 255.

Jim


> >  {
> > -       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 { ");
>
> Obviously, it won't always be "ioctl" now.
>
>
> > @@ -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
> >
>
> Something is not working correctly.
> I should be able to compile both a CIL and policy.conf and then
> convert them back to CIL and policy.conf. The CIL policy has nothing
> for the xperms, while the policy.conf doesn't even have the rules.
>
> So in the original CIL policy we have
> (allowx TYPE_X2 TYPE_OBJX2 (ioctl CLASS2 (0x1110)))
> (allowx TYPE_X3 TYPE_OBJX3 (nlmsg CLASS3 (0x1110)))
> The resulting policy, when it is converted back to CIL, is
> (allowx TYPE_X2 TYPE_OBJX2 (ioctl CLASS2 ((0x1110))))
> (allowx TYPE_X3 TYPE_OBJX3 (nlmsg CLASS3 ())))
>
> Using secil2conf generates the correct policy.conf from CIL, so I
> suspect something is wrong in reading or writing the binary policy.
>
> For the policy.conf, we have
> allowxperm TYPE_X2 TYPE_OBJX2:CLASS2 ioctl { 0x1110 };
> allowxperm TYPE_X3 TYPE_OBJX3:CLASS3 nlmsg { 0x1110 };
> The resulting policy, when converted back to a policy.conf is
> allowxperm TYPE_X2 TYPE_OBJX2:CLASS2 ioctl { 0x1110 };
>
> Using "checkpolicy -M -C -o test.conf.cil test.conf" results in the
> nlmsg rules being left out as well.
>
> I am investigating and I will let you know what I find.
>
> Jim



[Index of Archives]     [Selinux Refpolicy]     [Linux SGX]     [Fedora Users]     [Fedora Desktop]     [Yosemite Photos]     [Yosemite Camping]     [Yosemite Campsites]     [KDE Users]     [Gnome Users]

  Powered by Linux