KEYS: Preparse match data
authorDavid Howells <dhowells@redhat.com>
Tue, 16 Sep 2014 16:36:02 +0000 (17:36 +0100)
committerDavid Howells <dhowells@redhat.com>
Tue, 16 Sep 2014 16:36:02 +0000 (17:36 +0100)
Preparse the match data.  This provides several advantages:

 (1) The preparser can reject invalid criteria up front.

 (2) The preparser can convert the criteria to binary data if necessary (the
     asymmetric key type really wants to do binary comparison of the key IDs).

 (3) The preparser can set the type of search to be performed.  This means
     that it's not then a one-off setting in the key type.

 (4) The preparser can set an appropriate comparator function.

Signed-off-by: David Howells <dhowells@redhat.com>
Acked-by: Vivek Goyal <vgoyal@redhat.com>
crypto/asymmetric_keys/asymmetric_type.c
include/keys/user-type.h
include/linux/key-type.h
net/dns_resolver/dns_key.c
security/keys/internal.h
security/keys/keyring.c
security/keys/proc.c
security/keys/process_keys.c
security/keys/request_key.c
security/keys/request_key_auth.c
security/keys/user_defined.c

index eb8cd46..f666b4e 100644 (file)
@@ -59,9 +59,11 @@ EXPORT_SYMBOL_GPL(asymmetric_keyid_match);
  *     "id:<id>"       - request a key matching the ID
  *     "<subtype>:<id>" - request a key of a subtype
  */
-static int asymmetric_key_match(const struct key *key, const void *description)
+static int asymmetric_key_match(const struct key *key,
+                               const struct key_match_data *match_data)
 {
        const struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key);
+       const char *description = match_data->raw_data;
        const char *spec = description;
        const char *id;
        ptrdiff_t speclen;
@@ -93,6 +95,31 @@ static int asymmetric_key_match(const struct key *key, const void *description)
        return 0;
 }
 
+/*
+ * Preparse the match criterion.  If we don't set lookup_type and cmp,
+ * the default will be an exact match on the key description.
+ *
+ * There are some specifiers for matching key IDs rather than by the key
+ * description:
+ *
+ *     "id:<id>" - request a key by any available ID
+ *
+ * These have to be searched by iteration rather than by direct lookup because
+ * the key is hashed according to its description.
+ */
+static int asymmetric_key_match_preparse(struct key_match_data *match_data)
+{
+       match_data->lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE;
+       return 0;
+}
+
+/*
+ * Free the preparsed the match criterion.
+ */
+static void asymmetric_key_match_free(struct key_match_data *match_data)
+{
+}
+
 /*
  * Describe the asymmetric key
  */
@@ -196,7 +223,9 @@ struct key_type key_type_asymmetric = {
        .preparse       = asymmetric_key_preparse,
        .free_preparse  = asymmetric_key_free_preparse,
        .instantiate    = generic_key_instantiate,
+       .match_preparse = asymmetric_key_match_preparse,
        .match          = asymmetric_key_match,
+       .match_free     = asymmetric_key_match_free,
        .destroy        = asymmetric_key_destroy,
        .describe       = asymmetric_key_describe,
        .def_lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE,
index 3ab1873..66d92af 100644 (file)
@@ -36,11 +36,13 @@ extern struct key_type key_type_user;
 extern struct key_type key_type_logon;
 
 struct key_preparsed_payload;
+struct key_match_data;
 
 extern int user_preparse(struct key_preparsed_payload *prep);
 extern void user_free_preparse(struct key_preparsed_payload *prep);
 extern int user_update(struct key *key, struct key_preparsed_payload *prep);
-extern int user_match(const struct key *key, const void *criterion);
+extern int user_match(const struct key *key,
+                     const struct key_match_data *match_data);
 extern void user_revoke(struct key *key);
 extern void user_destroy(struct key *key);
 extern void user_describe(const struct key *user, struct seq_file *m);
index 44792ee..8aba688 100644 (file)
@@ -52,6 +52,22 @@ struct key_preparsed_payload {
 typedef int (*request_key_actor_t)(struct key_construction *key,
                                   const char *op, void *aux);
 
+/*
+ * Preparsed matching criterion.
+ */
+struct key_match_data {
+       /* Comparison function, defaults to type->match, but can be replaced by
+        * type->match_preparse(). */
+       int (*cmp)(const struct key *key,
+                  const struct key_match_data *match_data);
+
+       const void      *raw_data;      /* Raw match data */
+       void            *preparsed;     /* For ->match_preparse() to stash stuff */
+       unsigned        lookup_type;    /* Type of lookup for this search. */
+#define KEYRING_SEARCH_LOOKUP_DIRECT   0x0000  /* Direct lookup by description. */
+#define KEYRING_SEARCH_LOOKUP_ITERATE  0x0001  /* Iterative search. */
+};
+
 /*
  * kernel managed key type definition
  */
@@ -67,8 +83,6 @@ struct key_type {
 
        /* Default key search algorithm. */
        unsigned def_lookup_type;
-#define KEYRING_SEARCH_LOOKUP_DIRECT   0x0000  /* Direct lookup by description. */
-#define KEYRING_SEARCH_LOOKUP_ITERATE  0x0001  /* Iterative search. */
 
        /* vet a description */
        int (*vet_description)(const char *description);
@@ -96,8 +110,19 @@ struct key_type {
         */
        int (*update)(struct key *key, struct key_preparsed_payload *prep);
 
+       /* Preparse the data supplied to ->match() (optional).  The
+        * data to be preparsed can be found in match_data->raw_data.
+        * The lookup type can also be set by this function.
+        */
+       int (*match_preparse)(struct key_match_data *match_data);
+
        /* match a key against a description */
-       int (*match)(const struct key *key, const void *desc);
+       int (*match)(const struct key *key,
+                    const struct key_match_data *match_data);
+
+       /* Free preparsed match data (optional).  This should be supplied it
+        * ->match_preparse() is supplied. */
+       void (*match_free)(struct key_match_data *match_data);
 
        /* clear some of the data from a key on revokation (optional)
         * - the key's semaphore will be write-locked by the caller
index f380b2c..92df6e5 100644 (file)
@@ -177,10 +177,11 @@ static void dns_resolver_free_preparse(struct key_preparsed_payload *prep)
  * should end with a period).  The domain name is case-independent.
  */
 static int
-dns_resolver_match(const struct key *key, const void *description)
+dns_resolver_match(const struct key *key,
+                  const struct key_match_data *match_data)
 {
        int slen, dlen, ret = 0;
-       const char *src = key->description, *dsp = description;
+       const char *src = key->description, *dsp = match_data->raw_data;
 
        kenter("%s,%s", src, dsp);
 
index 5f20da0..805e60b 100644 (file)
@@ -107,13 +107,10 @@ extern int iterate_over_keyring(const struct key *keyring,
                                int (*func)(const struct key *key, void *data),
                                void *data);
 
-typedef int (*key_match_func_t)(const struct key *, const void *);
-
 struct keyring_search_context {
        struct keyring_index_key index_key;
        const struct cred       *cred;
-       key_match_func_t        match;
-       const void              *match_data;
+       struct key_match_data   match_data;
        unsigned                flags;
 #define KEYRING_SEARCH_LOOKUP_TYPE     0x0001  /* [as type->def_lookup_type] */
 #define KEYRING_SEARCH_NO_STATE_CHECK  0x0002  /* Skip state checks */
@@ -152,7 +149,8 @@ extern struct key *request_key_and_link(struct key_type *type,
                                        struct key *dest_keyring,
                                        unsigned long flags);
 
-extern int lookup_user_key_possessed(const struct key *key, const void *target);
+extern int lookup_user_key_possessed(const struct key *key,
+                                    const struct key_match_data *match_data);
 extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags,
                                 key_perm_t perm);
 #define KEY_LOOKUP_CREATE      0x01
index 8314a7d..10f0a5f 100644 (file)
@@ -545,7 +545,7 @@ static int keyring_search_iterator(const void *object, void *iterator_data)
        }
 
        /* keys that don't match */
-       if (!ctx->match(key, ctx->match_data)) {
+       if (!ctx->match_data.cmp(key, &ctx->match_data)) {
                kleave(" = 0 [!match]");
                return 0;
        }
@@ -585,8 +585,7 @@ skipped:
  */
 static int search_keyring(struct key *keyring, struct keyring_search_context *ctx)
 {
-       if ((ctx->flags & KEYRING_SEARCH_LOOKUP_TYPE) ==
-           KEYRING_SEARCH_LOOKUP_DIRECT) {
+       if (ctx->match_data.lookup_type == KEYRING_SEARCH_LOOKUP_DIRECT) {
                const void *object;
 
                object = assoc_array_find(&keyring->keys,
@@ -627,7 +626,7 @@ static bool search_nested_keyrings(struct key *keyring,
        /* Check to see if this top-level keyring is what we are looking for
         * and whether it is valid or not.
         */
-       if (ctx->flags & KEYRING_SEARCH_LOOKUP_ITERATE ||
+       if (ctx->match_data.lookup_type == KEYRING_SEARCH_LOOKUP_ITERATE ||
            keyring_compare_object(keyring, &ctx->index_key)) {
                ctx->skipped_ret = 2;
                ctx->flags |= KEYRING_SEARCH_DO_STATE_CHECK;
@@ -885,16 +884,28 @@ key_ref_t keyring_search(key_ref_t keyring,
                .index_key.type         = type,
                .index_key.description  = description,
                .cred                   = current_cred(),
-               .match                  = type->match,
-               .match_data             = description,
-               .flags                  = (type->def_lookup_type |
-                                          KEYRING_SEARCH_DO_STATE_CHECK),
+               .match_data.cmp         = type->match,
+               .match_data.raw_data    = description,
+               .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
+               .flags                  = KEYRING_SEARCH_DO_STATE_CHECK,
        };
+       key_ref_t key;
+       int ret;
 
-       if (!ctx.match)
+       if (!ctx.match_data.cmp)
                return ERR_PTR(-ENOKEY);
 
-       return keyring_search_aux(keyring, &ctx);
+       if (type->match_preparse) {
+               ret = type->match_preparse(&ctx.match_data);
+               if (ret < 0)
+                       return ERR_PTR(ret);
+       }
+
+       key = keyring_search_aux(keyring, &ctx);
+
+       if (type->match_free)
+               type->match_free(&ctx.match_data);
+       return key;
 }
 EXPORT_SYMBOL(keyring_search);
 
@@ -1014,7 +1025,7 @@ static int keyring_detect_cycle_iterator(const void *object,
 
        /* We might get a keyring with matching index-key that is nonetheless a
         * different keyring. */
-       if (key != ctx->match_data)
+       if (key != ctx->match_data.raw_data)
                return 0;
 
        ctx->result = ERR_PTR(-EDEADLK);
@@ -1031,14 +1042,14 @@ static int keyring_detect_cycle_iterator(const void *object,
 static int keyring_detect_cycle(struct key *A, struct key *B)
 {
        struct keyring_search_context ctx = {
-               .index_key      = A->index_key,
-               .match_data     = A,
-               .iterator       = keyring_detect_cycle_iterator,
-               .flags          = (KEYRING_SEARCH_LOOKUP_DIRECT |
-                                  KEYRING_SEARCH_NO_STATE_CHECK |
-                                  KEYRING_SEARCH_NO_UPDATE_TIME |
-                                  KEYRING_SEARCH_NO_CHECK_PERM |
-                                  KEYRING_SEARCH_DETECT_TOO_DEEP),
+               .index_key              = A->index_key,
+               .match_data.raw_data    = A,
+               .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
+               .iterator               = keyring_detect_cycle_iterator,
+               .flags                  = (KEYRING_SEARCH_NO_STATE_CHECK |
+                                          KEYRING_SEARCH_NO_UPDATE_TIME |
+                                          KEYRING_SEARCH_NO_CHECK_PERM |
+                                          KEYRING_SEARCH_DETECT_TOO_DEEP),
        };
 
        rcu_read_lock();
index d3f6f2f..972eeb3 100644 (file)
@@ -194,10 +194,10 @@ static int proc_keys_show(struct seq_file *m, void *v)
                .index_key.type         = key->type,
                .index_key.description  = key->description,
                .cred                   = current_cred(),
-               .match                  = lookup_user_key_possessed,
-               .match_data             = key,
-               .flags                  = (KEYRING_SEARCH_NO_STATE_CHECK |
-                                          KEYRING_SEARCH_LOOKUP_DIRECT),
+               .match_data.cmp         = lookup_user_key_possessed,
+               .match_data.raw_data    = key,
+               .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
+               .flags                  = KEYRING_SEARCH_NO_STATE_CHECK,
        };
 
        key_ref = make_key_ref(key, 0);
index 0cf8a13..08bd533 100644 (file)
@@ -489,9 +489,10 @@ found:
 /*
  * See if the key we're looking at is the target key.
  */
-int lookup_user_key_possessed(const struct key *key, const void *target)
+int lookup_user_key_possessed(const struct key *key,
+                             const struct key_match_data *match_data)
 {
-       return key == target;
+       return key == match_data->raw_data;
 }
 
 /*
@@ -516,9 +517,9 @@ key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags,
                          key_perm_t perm)
 {
        struct keyring_search_context ctx = {
-               .match  = lookup_user_key_possessed,
-               .flags  = (KEYRING_SEARCH_NO_STATE_CHECK |
-                          KEYRING_SEARCH_LOOKUP_DIRECT),
+               .match_data.cmp         = lookup_user_key_possessed,
+               .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
+               .flags                  = KEYRING_SEARCH_NO_STATE_CHECK,
        };
        struct request_key_auth *rka;
        struct key *key;
@@ -673,7 +674,7 @@ try_again:
                ctx.index_key.type              = key->type;
                ctx.index_key.description       = key->description;
                ctx.index_key.desc_len          = strlen(key->description);
-               ctx.match_data                  = key;
+               ctx.match_data.raw_data         = key;
                kdebug("check possessed");
                skey_ref = search_process_keyrings(&ctx);
                kdebug("possessed=%p", skey_ref);
index 3814119..408523e 100644 (file)
@@ -531,9 +531,9 @@ struct key *request_key_and_link(struct key_type *type,
                .index_key.type         = type,
                .index_key.description  = description,
                .cred                   = current_cred(),
-               .match                  = type->match,
-               .match_data             = description,
-               .flags                  = KEYRING_SEARCH_LOOKUP_DIRECT,
+               .match_data.cmp         = type->match,
+               .match_data.raw_data    = description,
+               .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
        };
        struct key *key;
        key_ref_t key_ref;
@@ -543,6 +543,14 @@ struct key *request_key_and_link(struct key_type *type,
               ctx.index_key.type->name, ctx.index_key.description,
               callout_info, callout_len, aux, dest_keyring, flags);
 
+       if (type->match_preparse) {
+               ret = type->match_preparse(&ctx.match_data);
+               if (ret < 0) {
+                       key = ERR_PTR(ret);
+                       goto error;
+               }
+       }
+
        /* search all the process keyrings for a key */
        key_ref = search_process_keyrings(&ctx);
 
@@ -555,7 +563,7 @@ struct key *request_key_and_link(struct key_type *type,
                        if (ret < 0) {
                                key_put(key);
                                key = ERR_PTR(ret);
-                               goto error;
+                               goto error_free;
                        }
                }
        } else if (PTR_ERR(key_ref) != -EAGAIN) {
@@ -565,12 +573,15 @@ struct key *request_key_and_link(struct key_type *type,
                 * should consult userspace if we can */
                key = ERR_PTR(-ENOKEY);
                if (!callout_info)
-                       goto error;
+                       goto error_free;
 
                key = construct_key_and_link(&ctx, callout_info, callout_len,
                                             aux, dest_keyring, flags);
        }
 
+error_free:
+       if (type->match_free)
+               type->match_free(&ctx.match_data);
 error:
        kleave(" = %p", key);
        return key;
index 739e745..9ae0281 100644 (file)
@@ -246,9 +246,9 @@ struct key *key_get_instantiation_authkey(key_serial_t target_id)
                .index_key.type         = &key_type_request_key_auth,
                .index_key.description  = description,
                .cred                   = current_cred(),
-               .match                  = user_match,
-               .match_data             = description,
-               .flags                  = KEYRING_SEARCH_LOOKUP_DIRECT,
+               .match_data.cmp         = user_match,
+               .match_data.raw_data    = description,
+               .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
        };
        struct key *authkey;
        key_ref_t authkey_ref;
index eee3400..ec8a560 100644 (file)
@@ -141,9 +141,9 @@ EXPORT_SYMBOL_GPL(user_update);
 /*
  * match users on their name
  */
-int user_match(const struct key *key, const void *description)
+int user_match(const struct key *key, const struct key_match_data *match_data)
 {
-       return strcmp(key->description, description) == 0;
+       return strcmp(key->description, match_data->raw_data) == 0;
 }
 
 EXPORT_SYMBOL_GPL(user_match);