keys: Simplify key description management
authorDavid Howells <dhowells@redhat.com>
Wed, 26 Jun 2019 20:02:31 +0000 (21:02 +0100)
committerDavid Howells <dhowells@redhat.com>
Wed, 26 Jun 2019 20:02:31 +0000 (21:02 +0100)
Simplify key description management by cramming the word containing the
length with the first few chars of the description also.  This simplifies
the code that generates the index-key used by assoc_array.  It should speed
up key searching a bit too.

Signed-off-by: David Howells <dhowells@redhat.com>
include/linux/key.h
security/keys/internal.h
security/keys/key.c
security/keys/keyring.c
security/keys/persistent.c

index 4cd5669..86ccc2d 100644 (file)
@@ -86,9 +86,20 @@ struct keyring_list;
 struct keyring_name;
 
 struct keyring_index_key {
+       union {
+               struct {
+#ifdef __LITTLE_ENDIAN /* Put desc_len at the LSB of x */
+                       u8      desc_len;
+                       char    desc[sizeof(long) - 1]; /* First few chars of description */
+#else
+                       char    desc[sizeof(long) - 1]; /* First few chars of description */
+                       u8      desc_len;
+#endif
+               };
+               unsigned long x;
+       };
        struct key_type         *type;
        const char              *description;
-       size_t                  desc_len;
 };
 
 union key_payload {
@@ -202,6 +213,7 @@ struct key {
        union {
                struct keyring_index_key index_key;
                struct {
+                       unsigned long   len_desc;
                        struct key_type *type;          /* type of key */
                        char            *description;
                };
index 3d5c08d..ee71c72 100644 (file)
@@ -90,6 +90,12 @@ extern struct mutex key_construction_mutex;
 extern wait_queue_head_t request_key_conswq;
 
 
+static inline void key_set_index_key(struct keyring_index_key *index_key)
+{
+       size_t n = min_t(size_t, index_key->desc_len, sizeof(index_key->desc));
+       memcpy(index_key->desc, index_key->description, n);
+}
+
 extern struct key_type *key_type_lookup(const char *type);
 extern void key_type_put(struct key_type *ktype);
 
index e792d65..0a3828f 100644 (file)
@@ -285,6 +285,7 @@ struct key *key_alloc(struct key_type *type, const char *desc,
        key->index_key.description = kmemdup(desc, desclen + 1, GFP_KERNEL);
        if (!key->index_key.description)
                goto no_memory_3;
+       key_set_index_key(&key->index_key);
 
        refcount_set(&key->usage, 1);
        init_rwsem(&key->sem);
@@ -868,6 +869,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
                        goto error_free_prep;
        }
        index_key.desc_len = strlen(index_key.description);
+       key_set_index_key(&index_key);
 
        ret = __key_link_lock(keyring, &index_key);
        if (ret < 0) {
index afa6d40..ebf5207 100644 (file)
@@ -179,9 +179,9 @@ static unsigned long hash_key_type_and_desc(const struct keyring_index_key *inde
        int n, desc_len = index_key->desc_len;
 
        type = (unsigned long)index_key->type;
-
        acc = mult_64x32_and_fold(type, desc_len + 13);
        acc = mult_64x32_and_fold(acc, 9207);
+
        for (;;) {
                n = desc_len;
                if (n <= 0)
@@ -215,23 +215,13 @@ static unsigned long hash_key_type_and_desc(const struct keyring_index_key *inde
 /*
  * Build the next index key chunk.
  *
- * On 32-bit systems the index key is laid out as:
- *
- *     0       4       5       9...
- *     hash    desclen typeptr desc[]
- *
- * On 64-bit systems:
- *
- *     0       8       9       17...
- *     hash    desclen typeptr desc[]
- *
  * We return it one word-sized chunk at a time.
  */
 static unsigned long keyring_get_key_chunk(const void *data, int level)
 {
        const struct keyring_index_key *index_key = data;
        unsigned long chunk = 0;
-       long offset = 0;
+       const u8 *d;
        int desc_len = index_key->desc_len, n = sizeof(chunk);
 
        level /= ASSOC_ARRAY_KEY_CHUNK_SIZE;
@@ -239,33 +229,23 @@ static unsigned long keyring_get_key_chunk(const void *data, int level)
        case 0:
                return hash_key_type_and_desc(index_key);
        case 1:
-               return ((unsigned long)index_key->type << 8) | desc_len;
+               return index_key->x;
        case 2:
-               if (desc_len == 0)
-                       return (u8)((unsigned long)index_key->type >>
-                                   (ASSOC_ARRAY_KEY_CHUNK_SIZE - 8));
-               n--;
-               offset = 1;
-               /* fall through */
+               return (unsigned long)index_key->type;
        default:
-               offset += sizeof(chunk) - 1;
-               offset += (level - 3) * sizeof(chunk);
-               if (offset >= desc_len)
+               level -= 3;
+               if (desc_len <= sizeof(index_key->desc))
                        return 0;
-               desc_len -= offset;
+
+               d = index_key->description + sizeof(index_key->desc);
+               d += level * sizeof(long);
+               desc_len -= sizeof(index_key->desc);
                if (desc_len > n)
                        desc_len = n;
-               offset += desc_len;
                do {
                        chunk <<= 8;
-                       chunk |= ((u8*)index_key->description)[--offset];
+                       chunk |= *d++;
                } while (--desc_len > 0);
-
-               if (level == 2) {
-                       chunk <<= 8;
-                       chunk |= (u8)((unsigned long)index_key->type >>
-                                     (ASSOC_ARRAY_KEY_CHUNK_SIZE - 8));
-               }
                return chunk;
        }
 }
@@ -304,39 +284,28 @@ static int keyring_diff_objects(const void *object, const void *data)
        seg_b = hash_key_type_and_desc(b);
        if ((seg_a ^ seg_b) != 0)
                goto differ;
+       level += ASSOC_ARRAY_KEY_CHUNK_SIZE / 8;
 
        /* The number of bits contributed by the hash is controlled by a
         * constant in the assoc_array headers.  Everything else thereafter we
         * can deal with as being machine word-size dependent.
         */
-       level += ASSOC_ARRAY_KEY_CHUNK_SIZE / 8;
-       seg_a = a->desc_len;
-       seg_b = b->desc_len;
+       seg_a = a->x;
+       seg_b = b->x;
        if ((seg_a ^ seg_b) != 0)
                goto differ;
+       level += sizeof(unsigned long);
 
        /* The next bit may not work on big endian */
-       level++;
        seg_a = (unsigned long)a->type;
        seg_b = (unsigned long)b->type;
        if ((seg_a ^ seg_b) != 0)
                goto differ;
-
        level += sizeof(unsigned long);
-       if (a->desc_len == 0)
-               goto same;
 
-       i = 0;
-       if (((unsigned long)a->description | (unsigned long)b->description) &
-           (sizeof(unsigned long) - 1)) {
-               do {
-                       seg_a = *(unsigned long *)(a->description + i);
-                       seg_b = *(unsigned long *)(b->description + i);
-                       if ((seg_a ^ seg_b) != 0)
-                               goto differ_plus_i;
-                       i += sizeof(unsigned long);
-               } while (i < (a->desc_len & (sizeof(unsigned long) - 1)));
-       }
+       i = sizeof(a->desc);
+       if (a->desc_len <= i)
+               goto same;
 
        for (; i < a->desc_len; i++) {
                seg_a = *(unsigned char *)(a->description + i);
@@ -662,6 +631,9 @@ static bool search_nested_keyrings(struct key *keyring,
        BUG_ON((ctx->flags & STATE_CHECKS) == 0 ||
               (ctx->flags & STATE_CHECKS) == STATE_CHECKS);
 
+       if (ctx->index_key.description)
+               key_set_index_key(&ctx->index_key);
+
        /* Check to see if this top-level keyring is what we are looking for
         * and whether it is valid or not.
         */
index d0cb5b3..fc29ec5 100644 (file)
@@ -87,6 +87,7 @@ static long key_get_persistent(struct user_namespace *ns, kuid_t uid,
        index_key.type = &key_type_keyring;
        index_key.description = buf;
        index_key.desc_len = sprintf(buf, "_persistent.%u", from_kuid(ns, uid));
+       key_set_index_key(&index_key);
 
        if (ns->persistent_keyring_register) {
                reg_ref = make_key_ref(ns->persistent_keyring_register, true);