selinux: implement new format of filename transitions
authorOndrej Mosnacek <omosnace@redhat.com>
Thu, 16 Apr 2020 17:13:55 +0000 (19:13 +0200)
committerPaul Moore <paul@paul-moore.com>
Fri, 17 Apr 2020 20:42:01 +0000 (16:42 -0400)
Implement a new, more space-efficient way of storing filename
transitions in the binary policy. The internal structures have already
been converted to this new representation; this patch just implements
reading/writing an equivalent represntation from/to the binary policy.

This new format reduces the size of Fedora policy from 7.6 MB to only
3.3 MB (with policy optimization enabled in both cases). With the
unconfined module disabled, the size is reduced from 3.3 MB to 2.4 MB.

The time to load policy into kernel is also shorter with the new format.
On Fedora Rawhide x86_64 it dropped from 157 ms to 106 ms; without the
unconfined module from 115 ms to 105 ms.

Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
security/selinux/include/security.h
security/selinux/ss/policydb.c

index d6036c0..b0e02cf 100644 (file)
 #define POLICYDB_VERSION_XPERMS_IOCTL  30
 #define POLICYDB_VERSION_INFINIBAND            31
 #define POLICYDB_VERSION_GLBLUB                32
+#define POLICYDB_VERSION_COMP_FTRANS   33 /* compressed filename transitions */
 
 /* Range of policy versions we understand*/
 #define POLICYDB_VERSION_MIN   POLICYDB_VERSION_BASE
-#define POLICYDB_VERSION_MAX   POLICYDB_VERSION_GLBLUB
+#define POLICYDB_VERSION_MAX   POLICYDB_VERSION_COMP_FTRANS
 
 /* Mask for just the mount related flags */
 #define SE_MNTMASK     0x0f
index dc67298..ef8d5b4 100644 (file)
@@ -154,6 +154,11 @@ static struct policydb_compat_info policydb_compat[] = {
                .sym_num        = SYM_NUM,
                .ocon_num       = OCON_NUM,
        },
+       {
+               .version        = POLICYDB_VERSION_COMP_FTRANS,
+               .sym_num        = SYM_NUM,
+               .ocon_num       = OCON_NUM,
+       },
 };
 
 static struct policydb_compat_info *policydb_lookup_compat(int version)
@@ -492,23 +497,16 @@ static int role_trans_cmp(struct hashtab *h, const void *k1, const void *k2)
 /*
  * Initialize a policy database structure.
  */
-static int policydb_init(struct policydb *p)
+static void policydb_init(struct policydb *p)
 {
        memset(p, 0, sizeof(*p));
 
        avtab_init(&p->te_avtab);
        cond_policydb_init(p);
 
-       p->filename_trans = hashtab_create(filenametr_hash, filenametr_cmp,
-                                          (1 << 11));
-       if (!p->filename_trans)
-               return -ENOMEM;
-
        ebitmap_init(&p->filename_trans_ttypes);
        ebitmap_init(&p->policycaps);
        ebitmap_init(&p->permissive_map);
-
-       return 0;
 }
 
 /*
@@ -1862,7 +1860,7 @@ out:
        return rc;
 }
 
-static int filename_trans_read_one(struct policydb *p, void *fp)
+static int filename_trans_read_helper_compat(struct policydb *p, void *fp)
 {
        struct filename_trans_key key, *ft = NULL;
        struct filename_trans_datum *last, *datum = NULL;
@@ -1945,6 +1943,99 @@ out:
        return rc;
 }
 
+static int filename_trans_read_helper(struct policydb *p, void *fp)
+{
+       struct filename_trans_key *ft = NULL;
+       struct filename_trans_datum **dst, *datum, *first = NULL;
+       char *name = NULL;
+       u32 len, ttype, tclass, ndatum, i;
+       __le32 buf[3];
+       int rc;
+
+       /* length of the path component string */
+       rc = next_entry(buf, fp, sizeof(u32));
+       if (rc)
+               return rc;
+       len = le32_to_cpu(buf[0]);
+
+       /* path component string */
+       rc = str_read(&name, GFP_KERNEL, fp, len);
+       if (rc)
+               return rc;
+
+       rc = next_entry(buf, fp, sizeof(u32) * 3);
+       if (rc)
+               goto out;
+
+       ttype = le32_to_cpu(buf[0]);
+       tclass = le32_to_cpu(buf[1]);
+
+       ndatum = le32_to_cpu(buf[2]);
+       if (ndatum == 0) {
+               pr_err("SELinux:  Filename transition key with no datum\n");
+               rc = -ENOENT;
+               goto out;
+       }
+
+       dst = &first;
+       for (i = 0; i < ndatum; i++) {
+               rc = -ENOMEM;
+               datum = kmalloc(sizeof(*datum), GFP_KERNEL);
+               if (!datum)
+                       goto out;
+
+               *dst = datum;
+
+               /* ebitmap_read() will at least init the bitmap */
+               rc = ebitmap_read(&datum->stypes, fp);
+               if (rc)
+                       goto out;
+
+               rc = next_entry(buf, fp, sizeof(u32));
+               if (rc)
+                       goto out;
+
+               datum->otype = le32_to_cpu(buf[0]);
+               datum->next = NULL;
+
+               dst = &datum->next;
+       }
+
+       rc = -ENOMEM;
+       ft = kmalloc(sizeof(*ft), GFP_KERNEL);
+       if (!ft)
+               goto out;
+
+       ft->ttype = ttype;
+       ft->tclass = tclass;
+       ft->name = name;
+
+       rc = hashtab_insert(p->filename_trans, ft, first);
+       if (rc == -EEXIST)
+               pr_err("SELinux:  Duplicate filename transition key\n");
+       if (rc)
+               goto out;
+
+       rc = ebitmap_set_bit(&p->filename_trans_ttypes, ttype, 1);
+       if (rc)
+               return rc;
+
+       p->filename_trans_count += ndatum;
+       return 0;
+
+out:
+       kfree(ft);
+       kfree(name);
+       while (first) {
+               datum = first;
+               first = first->next;
+
+               ebitmap_destroy(&datum->stypes);
+               kfree(datum);
+       }
+       return rc;
+}
+
 static int filename_trans_read(struct policydb *p, void *fp)
 {
        u32 nel;
@@ -1959,12 +2050,29 @@ static int filename_trans_read(struct policydb *p, void *fp)
                return rc;
        nel = le32_to_cpu(buf[0]);
 
-       p->filename_trans_count = nel;
+       if (p->policyvers < POLICYDB_VERSION_COMP_FTRANS) {
+               p->filename_trans_count = nel;
+               p->filename_trans = hashtab_create(filenametr_hash,
+                                                  filenametr_cmp, (1 << 11));
+               if (!p->filename_trans)
+                       return -ENOMEM;
 
-       for (i = 0; i < nel; i++) {
-               rc = filename_trans_read_one(p, fp);
-               if (rc)
-                       return rc;
+               for (i = 0; i < nel; i++) {
+                       rc = filename_trans_read_helper_compat(p, fp);
+                       if (rc)
+                               return rc;
+               }
+       } else {
+               p->filename_trans = hashtab_create(filenametr_hash,
+                                                  filenametr_cmp, nel);
+               if (!p->filename_trans)
+                       return -ENOMEM;
+
+               for (i = 0; i < nel; i++) {
+                       rc = filename_trans_read_helper(p, fp);
+                       if (rc)
+                               return rc;
+               }
        }
        hash_eval(p->filename_trans, "filenametr");
        return 0;
@@ -2281,9 +2389,7 @@ int policydb_read(struct policydb *p, void *fp)
        char *policydb_str;
        struct policydb_compat_info *info;
 
-       rc = policydb_init(p);
-       if (rc)
-               return rc;
+       policydb_init(p);
 
        /* Read the magic number and string length. */
        rc = next_entry(buf, fp, sizeof(u32) * 2);
@@ -3367,7 +3473,7 @@ static int range_write(struct policydb *p, void *fp)
        return 0;
 }
 
-static int filename_write_helper(void *key, void *data, void *ptr)
+static int filename_write_helper_compat(void *key, void *data, void *ptr)
 {
        struct filename_trans_key *ft = key;
        struct filename_trans_datum *datum = data;
@@ -3404,26 +3510,82 @@ static int filename_write_helper(void *key, void *data, void *ptr)
        return 0;
 }
 
-static int filename_trans_write(struct policydb *p, void *fp)
+static int filename_write_helper(void *key, void *data, void *ptr)
 {
-       __le32 buf[1];
+       struct filename_trans_key *ft = key;
+       struct filename_trans_datum *datum;
+       void *fp = ptr;
+       __le32 buf[3];
        int rc;
+       u32 ndatum, len = strlen(ft->name);
 
-       if (p->policyvers < POLICYDB_VERSION_FILENAME_TRANS)
-               return 0;
-
-       buf[0] = cpu_to_le32(p->filename_trans_count);
+       buf[0] = cpu_to_le32(len);
        rc = put_entry(buf, sizeof(u32), 1, fp);
        if (rc)
                return rc;
 
-       rc = hashtab_map(p->filename_trans, filename_write_helper, fp);
+       rc = put_entry(ft->name, sizeof(char), len, fp);
        if (rc)
                return rc;
 
+       ndatum = 0;
+       datum = data;
+       do {
+               ndatum++;
+               datum = datum->next;
+       } while (unlikely(datum));
+
+       buf[0] = cpu_to_le32(ft->ttype);
+       buf[1] = cpu_to_le32(ft->tclass);
+       buf[2] = cpu_to_le32(ndatum);
+       rc = put_entry(buf, sizeof(u32), 3, fp);
+       if (rc)
+               return rc;
+
+       datum = data;
+       do {
+               rc = ebitmap_write(&datum->stypes, fp);
+               if (rc)
+                       return rc;
+
+               buf[0] = cpu_to_le32(datum->otype);
+               rc = put_entry(buf, sizeof(u32), 1, fp);
+               if (rc)
+                       return rc;
+
+               datum = datum->next;
+       } while (unlikely(datum));
+
        return 0;
 }
 
+static int filename_trans_write(struct policydb *p, void *fp)
+{
+       __le32 buf[1];
+       int rc;
+
+       if (p->policyvers < POLICYDB_VERSION_FILENAME_TRANS)
+               return 0;
+
+       if (p->policyvers < POLICYDB_VERSION_COMP_FTRANS) {
+               buf[0] = cpu_to_le32(p->filename_trans_count);
+               rc = put_entry(buf, sizeof(u32), 1, fp);
+               if (rc)
+                       return rc;
+
+               rc = hashtab_map(p->filename_trans,
+                                filename_write_helper_compat, fp);
+       } else {
+               buf[0] = cpu_to_le32(p->filename_trans->nel);
+               rc = put_entry(buf, sizeof(u32), 1, fp);
+               if (rc)
+                       return rc;
+
+               rc = hashtab_map(p->filename_trans, filename_write_helper, fp);
+       }
+       return rc;
+}
+
 /*
  * Write the configuration data in a policy database
  * structure to a policy database binary representation