diff options
author | David Howells <dhowells@redhat.com> | 2014-09-16 20:38:07 +0400 |
---|---|---|
committer | David Howells <dhowells@redhat.com> | 2014-09-16 20:38:07 +0400 |
commit | d3e4f41973753a7768a5728be53c7d9a3fdf86cb (patch) | |
tree | ad9bf9a2fa35fa4dbb1a4402f1c3d6b092fc7f28 | |
parent | 1c9c115ccc76d313f1a9232ffb903de325b64943 (diff) | |
parent | 757932e6da6de05b91cc4cb85493b17de48c2667 (diff) | |
download | linux-d3e4f41973753a7768a5728be53c7d9a3fdf86cb.tar.xz |
Merge tag 'keys-pkcs7-20140916' into keys-next
Changes for next to improve the matching of asymmetric keys and to improve the
handling of PKCS#7 certificates:
(1) Provide a method to preparse the data supplied for matching a key. This
permits they key type to extract out the bits it needs for matching once
only.
Further, the type of search (direct lookup or iterative) can be set and
the function used to actually check the match can be set by preparse
rather than being hard coded for the type.
(2) Improves asymmetric keys identification.
Keys derived from X.509 certs now get labelled with IDs derived from their
issuer and certificate number (required to match PKCS#7) and from their
SKID and subject (required to match X.509).
IDs are now binary and match criterion preparsing is provided so that
criteria can be turned into binary blobs to make matching faster.
(3) Improves PKCS#7 message handling to permit PKCS#7 messages without X.509
cert lists to be matched to trusted keys, thereby allowing minimally sized
PKCS#7 certs to be used.
(4) Improves PKCS#7 message handling to better handle certificate chains that
are broken due to unsupported crypto that can otherwise by used to
intersect a trust keyring.
These must go on top of the PKCS#7 parser cleanup fixes.
Signed-off-by: David Howells <dhowells@redhat.com>
34 files changed, 649 insertions, 305 deletions
diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt index 8727c194ca16..821c936e1a63 100644 --- a/Documentation/security/keys.txt +++ b/Documentation/security/keys.txt @@ -888,11 +888,11 @@ payload contents" for more information. const char *callout_info); This is used to request a key or keyring with a description that matches - the description specified according to the key type's match function. This - permits approximate matching to occur. If callout_string is not NULL, then - /sbin/request-key will be invoked in an attempt to obtain the key from - userspace. In that case, callout_string will be passed as an argument to - the program. + the description specified according to the key type's match_preparse() + method. This permits approximate matching to occur. If callout_string is + not NULL, then /sbin/request-key will be invoked in an attempt to obtain + the key from userspace. In that case, callout_string will be passed as an + argument to the program. Should the function fail error ENOKEY, EKEYEXPIRED or EKEYREVOKED will be returned. @@ -1170,7 +1170,7 @@ The structure has a number of fields, some of which are mandatory: The method should return 0 if successful or a negative error code otherwise. - + (*) void (*free_preparse)(struct key_preparsed_payload *prep); This method is only required if the preparse() method is provided, @@ -1225,16 +1225,55 @@ The structure has a number of fields, some of which are mandatory: It is safe to sleep in this method. - (*) int (*match)(const struct key *key, const void *desc); + (*) int (*match_preparse)(struct key_match_data *match_data); + + This method is optional. It is called when a key search is about to be + performed. It is given the following structure: - This method is called to match a key against a description. It should - return non-zero if the two match, zero if they don't. + struct key_match_data { + bool (*cmp)(const struct key *key, + const struct key_match_data *match_data); + const void *raw_data; + void *preparsed; + unsigned lookup_type; + }; - This method should not need to lock the key in any way. The type and - description can be considered invariant, and the payload should not be - accessed (the key may not yet be instantiated). + On entry, raw_data will be pointing to the criteria to be used in matching + a key by the caller and should not be modified. (*cmp)() will be pointing + to the default matcher function (which does an exact description match + against raw_data) and lookup_type will be set to indicate a direct lookup. - It is not safe to sleep in this method; the caller may hold spinlocks. + The following lookup_type values are available: + + [*] KEYRING_SEARCH_LOOKUP_DIRECT - A direct lookup hashes the type and + description to narrow down the search to a small number of keys. + + [*] KEYRING_SEARCH_LOOKUP_ITERATE - An iterative lookup walks all the + keys in the keyring until one is matched. This must be used for any + search that's not doing a simple direct match on the key description. + + The method may set cmp to point to a function of its choice that does some + other form of match, may set lookup_type to KEYRING_SEARCH_LOOKUP_ITERATE + and may attach something to the preparsed pointer for use by (*cmp)(). + (*cmp)() should return true if a key matches and false otherwise. + + If preparsed is set, it may be necessary to use the match_free() method to + clean it up. + + The method should return 0 if successful or a negative error code + otherwise. + + It is permitted to sleep in this method, but (*cmp)() may not sleep as + locks will be held over it. + + If match_preparse() is not provided, keys of this type will be matched + exactly by their description. + + + (*) void (*match_free)(struct key_match_data *match_data); + + This method is optional. If given, it called to clean up + match_data->preparsed after a successful call to match_preparse(). (*) void (*revoke)(struct key *key); diff --git a/crypto/asymmetric_keys/asymmetric_keys.h b/crypto/asymmetric_keys/asymmetric_keys.h index a63c551c6557..fd21ac28e0a0 100644 --- a/crypto/asymmetric_keys/asymmetric_keys.h +++ b/crypto/asymmetric_keys/asymmetric_keys.h @@ -9,9 +9,13 @@ * 2 of the Licence, or (at your option) any later version. */ -int asymmetric_keyid_match(const char *kid, const char *id); +extern bool asymmetric_match_key_ids(const struct asymmetric_key_ids *kids, + const struct asymmetric_key_id *match_id); -static inline const char *asymmetric_key_id(const struct key *key) +extern struct asymmetric_key_id *asymmetric_key_hex_to_key_id(const char *id); + +static inline +const struct asymmetric_key_ids *asymmetric_key_ids(const struct key *key) { return key->type_data.p[1]; } diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index eb8cd46961a5..718e779a010e 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -15,6 +15,7 @@ #include <linux/seq_file.h> #include <linux/module.h> #include <linux/slab.h> +#include <linux/ctype.h> #include "asymmetric_keys.h" MODULE_LICENSE("GPL"); @@ -22,85 +23,165 @@ MODULE_LICENSE("GPL"); static LIST_HEAD(asymmetric_key_parsers); static DECLARE_RWSEM(asymmetric_key_parsers_sem); -/* - * Match asymmetric key id with partial match - * @id: key id to match in a form "id:<id>" +/** + * asymmetric_key_generate_id: Construct an asymmetric key ID + * @val_1: First binary blob + * @len_1: Length of first binary blob + * @val_2: Second binary blob + * @len_2: Length of second binary blob + * + * Construct an asymmetric key ID from a pair of binary blobs. */ -int asymmetric_keyid_match(const char *kid, const char *id) +struct asymmetric_key_id *asymmetric_key_generate_id(const void *val_1, + size_t len_1, + const void *val_2, + size_t len_2) { - size_t idlen, kidlen; + struct asymmetric_key_id *kid; + + kid = kmalloc(sizeof(struct asymmetric_key_id) + len_1 + len_2, + GFP_KERNEL); + if (!kid) + return ERR_PTR(-ENOMEM); + kid->len = len_1 + len_2; + memcpy(kid->data, val_1, len_1); + memcpy(kid->data + len_1, val_2, len_2); + return kid; +} +EXPORT_SYMBOL_GPL(asymmetric_key_generate_id); - if (!kid || !id) - return 0; +/** + * asymmetric_key_id_same - Return true if two asymmetric keys IDs are the same. + * @kid_1, @kid_2: The key IDs to compare + */ +bool asymmetric_key_id_same(const struct asymmetric_key_id *kid1, + const struct asymmetric_key_id *kid2) +{ + if (!kid1 || !kid2) + return false; + if (kid1->len != kid2->len) + return false; + return memcmp(kid1->data, kid2->data, kid1->len) == 0; +} +EXPORT_SYMBOL_GPL(asymmetric_key_id_same); - /* make it possible to use id as in the request: "id:<id>" */ - if (strncmp(id, "id:", 3) == 0) - id += 3; +/** + * asymmetric_match_key_ids - Search asymmetric key IDs + * @kids: The list of key IDs to check + * @match_id: The key ID we're looking for + */ +bool asymmetric_match_key_ids(const struct asymmetric_key_ids *kids, + const struct asymmetric_key_id *match_id) +{ + if (!kids || !match_id) + return false; + if (asymmetric_key_id_same(kids->id[0], match_id)) + return true; + if (asymmetric_key_id_same(kids->id[1], match_id)) + return true; + return false; +} +EXPORT_SYMBOL_GPL(asymmetric_match_key_ids); - /* Anything after here requires a partial match on the ID string */ - idlen = strlen(id); - kidlen = strlen(kid); - if (idlen > kidlen) - return 0; +/** + * asymmetric_key_hex_to_key_id - Convert a hex string into a key ID. + * @id: The ID as a hex string. + */ +struct asymmetric_key_id *asymmetric_key_hex_to_key_id(const char *id) +{ + struct asymmetric_key_id *match_id; + const char *p; + ptrdiff_t hexlen; + + if (!*id) + return ERR_PTR(-EINVAL); + for (p = id; *p; p++) + if (!isxdigit(*p)) + return ERR_PTR(-EINVAL); + hexlen = p - id; + if (hexlen & 1) + return ERR_PTR(-EINVAL); + + match_id = kmalloc(sizeof(struct asymmetric_key_id) + hexlen / 2, + GFP_KERNEL); + if (!match_id) + return ERR_PTR(-ENOMEM); + match_id->len = hexlen / 2; + (void)hex2bin(match_id->data, id, hexlen / 2); + return match_id; +} - kid += kidlen - idlen; - if (strcasecmp(id, kid) != 0) - return 0; +/* + * Match asymmetric keys by ID. + */ +static bool asymmetric_key_cmp(const struct key *key, + const struct key_match_data *match_data) +{ + const struct asymmetric_key_ids *kids = asymmetric_key_ids(key); + const struct asymmetric_key_id *match_id = match_data->preparsed; - return 1; + return asymmetric_match_key_ids(kids, match_id); } -EXPORT_SYMBOL_GPL(asymmetric_keyid_match); /* - * Match asymmetric keys on (part of) their name - * We have some shorthand methods for matching keys. We allow: + * Preparse the match criterion. If we don't set lookup_type and cmp, + * the default will be an exact match on the key description. * - * "<desc>" - request a key by description - * "id:<id>" - request a key matching the ID - * "<subtype>:<id>" - request a key of a subtype + * 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(const struct key *key, const void *description) +static int asymmetric_key_match_preparse(struct key_match_data *match_data) { - const struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key); - const char *spec = description; + struct asymmetric_key_id *match_id; + const char *spec = match_data->raw_data; const char *id; - ptrdiff_t speclen; - - if (!subtype || !spec || !*spec) - return 0; - - /* See if the full key description matches as is */ - if (key->description && strcmp(key->description, description) == 0) - return 1; - /* All tests from here on break the criterion description into a - * specifier, a colon and then an identifier. - */ - id = strchr(spec, ':'); - if (!id) - return 0; - - speclen = id - spec; - id++; + if (!spec || !*spec) + return -EINVAL; + if (spec[0] == 'i' && + spec[1] == 'd' && + spec[2] == ':') { + id = spec + 3; + } else { + goto default_match; + } - if (speclen == 2 && memcmp(spec, "id", 2) == 0) - return asymmetric_keyid_match(asymmetric_key_id(key), id); + match_id = asymmetric_key_hex_to_key_id(id); + if (!match_id) + return -ENOMEM; - if (speclen == subtype->name_len && - memcmp(spec, subtype->name, speclen) == 0) - return 1; + match_data->preparsed = match_id; + match_data->cmp = asymmetric_key_cmp; + match_data->lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE; + return 0; +default_match: return 0; } /* + * Free the preparsed the match criterion. + */ +static void asymmetric_key_match_free(struct key_match_data *match_data) +{ + kfree(match_data->preparsed); +} + +/* * Describe the asymmetric key */ static void asymmetric_key_describe(const struct key *key, struct seq_file *m) { const struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key); - const char *kid = asymmetric_key_id(key); - size_t n; + const struct asymmetric_key_ids *kids = asymmetric_key_ids(key); + const struct asymmetric_key_id *kid; + const unsigned char *p; + int n; seq_puts(m, key->description); @@ -108,13 +189,16 @@ static void asymmetric_key_describe(const struct key *key, struct seq_file *m) seq_puts(m, ": "); subtype->describe(key, m); - if (kid) { + if (kids && kids->id[0]) { + kid = kids->id[0]; seq_putc(m, ' '); - n = strlen(kid); - if (n <= 8) - seq_puts(m, kid); - else - seq_puts(m, kid + n - 8); + n = kid->len; + p = kid->data; + if (n > 8) { + p += n - 8; + n = 8; + } + seq_printf(m, "%*phN", n, p); } seq_puts(m, " ["); @@ -165,6 +249,7 @@ static int asymmetric_key_preparse(struct key_preparsed_payload *prep) static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep) { struct asymmetric_key_subtype *subtype = prep->type_data[0]; + struct asymmetric_key_ids *kids = prep->type_data[1]; pr_devel("==>%s()\n", __func__); @@ -172,7 +257,11 @@ static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep) subtype->destroy(prep->payload[0]); module_put(subtype->owner); } - kfree(prep->type_data[1]); + if (kids) { + kfree(kids->id[0]); + kfree(kids->id[1]); + kfree(kids); + } kfree(prep->description); } @@ -182,13 +271,20 @@ static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep) static void asymmetric_key_destroy(struct key *key) { struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key); + struct asymmetric_key_ids *kids = key->type_data.p[1]; + if (subtype) { subtype->destroy(key->payload.data); module_put(subtype->owner); key->type_data.p[0] = NULL; } - kfree(key->type_data.p[1]); - key->type_data.p[1] = NULL; + + if (kids) { + kfree(kids->id[0]); + kfree(kids->id[1]); + kfree(kids); + key->type_data.p[1] = NULL; + } } struct key_type key_type_asymmetric = { @@ -196,10 +292,10 @@ struct key_type key_type_asymmetric = { .preparse = asymmetric_key_preparse, .free_preparse = asymmetric_key_free_preparse, .instantiate = generic_key_instantiate, - .match = asymmetric_key_match, + .match_preparse = asymmetric_key_match_preparse, + .match_free = asymmetric_key_match_free, .destroy = asymmetric_key_destroy, .describe = asymmetric_key_describe, - .def_lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE, }; EXPORT_SYMBOL_GPL(key_type_asymmetric); diff --git a/crypto/asymmetric_keys/pkcs7_key_type.c b/crypto/asymmetric_keys/pkcs7_key_type.c index 3de5fb011de0..751f8fd7335d 100644 --- a/crypto/asymmetric_keys/pkcs7_key_type.c +++ b/crypto/asymmetric_keys/pkcs7_key_type.c @@ -72,11 +72,9 @@ error: */ static struct key_type key_type_pkcs7 = { .name = "pkcs7_test", - .def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, .preparse = pkcs7_preparse, .free_preparse = user_free_preparse, .instantiate = generic_key_instantiate, - .match = user_match, .revoke = user_revoke, .destroy = user_destroy, .describe = user_describe, diff --git a/crypto/asymmetric_keys/pkcs7_parser.c b/crypto/asymmetric_keys/pkcs7_parser.c index 1e9861da7ee4..3bd5a1e4c493 100644 --- a/crypto/asymmetric_keys/pkcs7_parser.c +++ b/crypto/asymmetric_keys/pkcs7_parser.c @@ -29,6 +29,10 @@ struct pkcs7_parse_context { enum OID last_oid; /* Last OID encountered */ unsigned x509_index; unsigned sinfo_index; + const void *raw_serial; + unsigned raw_serial_size; + unsigned raw_issuer_size; + const void *raw_issuer; }; /* @@ -39,6 +43,7 @@ static void pkcs7_free_signed_info(struct pkcs7_signed_info *sinfo) if (sinfo) { mpi_free(sinfo->sig.mpi[0]); kfree(sinfo->sig.digest); + kfree(sinfo->signing_cert_id); kfree(sinfo); } } @@ -251,10 +256,10 @@ int pkcs7_extract_cert(void *context, size_t hdrlen, if (IS_ERR(x509)) return PTR_ERR(x509); - pr_debug("Got cert for %s\n", x509->subject); - pr_debug("- fingerprint %s\n", x509->fingerprint); - x509->index = ++ctx->x509_index; + pr_debug("Got cert %u for %s\n", x509->index, x509->subject); + pr_debug("- fingerprint %*phN\n", x509->id->len, x509->id->data); + *ctx->ppcerts = x509; ctx->ppcerts = &x509->next; return 0; @@ -343,8 +348,8 @@ int pkcs7_sig_note_serial(void *context, size_t hdrlen, const void *value, size_t vlen) { struct pkcs7_parse_context *ctx = context; - ctx->sinfo->raw_serial = value; - ctx->sinfo->raw_serial_size = vlen; + ctx->raw_serial = value; + ctx->raw_serial_size = vlen; return 0; } @@ -356,8 +361,8 @@ int pkcs7_sig_note_issuer(void *context, size_t hdrlen, const void *value, size_t vlen) { struct pkcs7_parse_context *ctx = context; - ctx->sinfo->raw_issuer = value; - ctx->sinfo->raw_issuer_size = vlen; + ctx->raw_issuer = value; + ctx->raw_issuer_size = vlen; return 0; } @@ -390,10 +395,21 @@ int pkcs7_note_signed_info(void *context, size_t hdrlen, const void *value, size_t vlen) { struct pkcs7_parse_context *ctx = context; - - ctx->sinfo->index = ++ctx->sinfo_index; - *ctx->ppsinfo = ctx->sinfo; - ctx->ppsinfo = &ctx->sinfo->next; + struct pkcs7_signed_info *sinfo = ctx->sinfo; + struct asymmetric_key_id *kid; + + /* Generate cert issuer + serial number key ID */ + kid = asymmetric_key_generate_id(ctx->raw_serial, + ctx->raw_serial_size, + ctx->raw_issuer, + ctx->raw_issuer_size); + if (IS_ERR(kid)) + return PTR_ERR(kid); + + sinfo->signing_cert_id = kid; + sinfo->index = ++ctx->sinfo_index; + *ctx->ppsinfo = sinfo; + ctx->ppsinfo = &sinfo->next; ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL); if (!ctx->sinfo) return -ENOMEM; diff --git a/crypto/asymmetric_keys/pkcs7_parser.h b/crypto/asymmetric_keys/pkcs7_parser.h index d25f4d15370f..efc7dc9b8f9c 100644 --- a/crypto/asymmetric_keys/pkcs7_parser.h +++ b/crypto/asymmetric_keys/pkcs7_parser.h @@ -23,6 +23,7 @@ struct pkcs7_signed_info { struct x509_certificate *signer; /* Signing certificate (in msg->certs) */ unsigned index; bool trusted; + bool unsupported_crypto; /* T if not usable due to missing crypto */ /* Message digest - the digest of the Content Data (or NULL) */ const void *msgdigest; @@ -33,10 +34,7 @@ struct pkcs7_signed_info { const void *authattrs; /* Issuing cert serial number and issuer's name */ - const void *raw_serial; - unsigned raw_serial_size; - unsigned raw_issuer_size; - const void *raw_issuer; + struct asymmetric_key_id *signing_cert_id; /* Message signature. * diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c index fad888ea4fad..ae47be6128c4 100644 --- a/crypto/asymmetric_keys/pkcs7_trust.c +++ b/crypto/asymmetric_keys/pkcs7_trust.c @@ -35,6 +35,11 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, kenter(",%u,", sinfo->index); + if (sinfo->unsupported_crypto) { + kleave(" = -ENOPKG [cached]"); + return -ENOPKG; + } + for (x509 = sinfo->signer; x509; x509 = x509->signer) { if (x509->seen) { if (x509->verified) { @@ -49,15 +54,17 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, /* Look to see if this certificate is present in the trusted * keys. */ - key = x509_request_asymmetric_key(trust_keyring, x509->subject, - x509->fingerprint); - if (!IS_ERR(key)) + key = x509_request_asymmetric_key(trust_keyring, x509->id); + if (!IS_ERR(key)) { /* One of the X.509 certificates in the PKCS#7 message * is apparently the same as one we already trust. * Verify that the trusted variant can also validate * the signature on the descendant. */ + pr_devel("sinfo %u: Cert %u as key %x\n", + sinfo->index, x509->index, key_serial(key)); goto matched; + } if (key == ERR_PTR(-ENOMEM)) return -ENOMEM; @@ -77,16 +84,34 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, /* No match - see if the root certificate has a signer amongst the * trusted keys. */ - if (!last || !last->issuer || !last->authority) { - kleave(" = -ENOKEY [no backref]"); - return -ENOKEY; + if (last && last->authority) { + key = x509_request_asymmetric_key(trust_keyring, last->authority); + if (!IS_ERR(key)) { + x509 = last; + pr_devel("sinfo %u: Root cert %u signer is key %x\n", + sinfo->index, x509->index, key_serial(key)); + goto matched; + } + if (PTR_ERR(key) != -ENOKEY) + return PTR_ERR(key); + } + + /* As a last resort, see if we have a trusted public key that matches + * the signed info directly. + */ + key = x509_request_asymmetric_key(trust_keyring, + sinfo->signing_cert_id); + if (!IS_ERR(key)) { + pr_devel("sinfo %u: Direct signer is key %x\n", + sinfo->index, key_serial(key)); + x509 = NULL; + goto matched; } + if (PTR_ERR(key) != -ENOKEY) + return PTR_ERR(key); - key = x509_request_asymmetric_key(trust_keyring, last->issuer, - last->authority); - if (IS_ERR(key)) - return PTR_ERR(key) == -ENOMEM ? -ENOMEM : -ENOKEY; - x509 = last; + kleave(" = -ENOKEY [no backref]"); + return -ENOKEY; matched: ret = verify_signature(key, sig); @@ -100,10 +125,12 @@ matched: } verified: - x509->verified = true; - for (p = sinfo->signer; p != x509; p = p->signer) { - p->verified = true; - p->trusted = trusted; + if (x509) { + x509->verified = true; + for (p = sinfo->signer; p != x509; p = p->signer) { + p->verified = true; + p->trusted = trusted; + } } sinfo->trusted = trusted; kleave(" = 0"); @@ -141,24 +168,28 @@ int pkcs7_validate_trust(struct pkcs7_message *pkcs7, { struct pkcs7_signed_info *sinfo; struct x509_certificate *p; - int cached_ret = 0, ret; + int cached_ret = -ENOKEY; + int ret; for (p = pkcs7->certs; p; p = p->next) p->seen = false; for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) { ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring); - if (ret < 0) { - if (ret == -ENOPKG) { + switch (ret) { + case -ENOKEY: + continue; + case -ENOPKG: + if (cached_ret == -ENOKEY) cached_ret = -ENOPKG; - } else if (ret == -ENOKEY) { - if (cached_ret == 0) - cached_ret = -ENOKEY; - } else { - return ret; - } + continue; + case 0: + *_trusted |= sinfo->trusted; + cached_ret = 0; + continue; + default: + return ret; } - *_trusted |= sinfo->trusted; } return cached_ret; diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys/pkcs7_verify.c index c62cf8006e1f..cd455450b069 100644 --- a/crypto/asymmetric_keys/pkcs7_verify.c +++ b/crypto/asymmetric_keys/pkcs7_verify.c @@ -131,8 +131,7 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7, struct x509_certificate *x509; unsigned certix = 1; - kenter("%u,%u,%u", - sinfo->index, sinfo->raw_serial_size, sinfo->raw_issuer_size); + kenter("%u", sinfo->index); for (x509 = pkcs7->certs; x509; x509 = x509->next, certix++) { /* I'm _assuming_ that the generator of the PKCS#7 message will @@ -140,21 +139,11 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7, * PKCS#7 message - but I can't be 100% sure of that. It's * possible this will need element-by-element comparison. */ - if (x509->raw_serial_size != sinfo->raw_serial_size || - memcmp(x509->raw_serial, sinfo->raw_serial, - sinfo->raw_serial_size) != 0) + if (!asymmetric_key_id_same(x509->id, sinfo->signing_cert_id)) continue; pr_devel("Sig %u: Found cert serial match X.509[%u]\n", sinfo->index, certix); - if (x509->raw_issuer_size != sinfo->raw_issuer_size || - memcmp(x509->raw_issuer, sinfo->raw_issuer, - sinfo->raw_issuer_size) != 0) { - pr_warn("Sig %u: X.509 subject and PKCS#7 issuer don't match\n", - sinfo->index); - continue; - } - if (x509->pub->pkey_algo != sinfo->sig.pkey_algo) { pr_warn("Sig %u: X.509 algo and PKCS#7 sig algo don't match\n", sinfo->index); @@ -164,9 +153,14 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7, sinfo->signer = x509; return 0; } - pr_warn("Sig %u: Issuing X.509 cert not found (#%*ph)\n", - sinfo->index, sinfo->raw_serial_size, sinfo->raw_serial); - return -ENOKEY; + + /* The relevant X.509 cert isn't found here, but it might be found in + * the trust keyring. + */ + pr_debug("Sig %u: Issuing X.509 cert not found (#%*phN)\n", + sinfo->index, + sinfo->signing_cert_id->len, sinfo->signing_cert_id->data); + return 0; } /* @@ -184,15 +178,18 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, p->seen = false; for (;;) { - pr_debug("verify %s: %s\n", x509->subject, x509->fingerprint); + pr_debug("verify %s: %*phN\n", + x509->subject, + x509->raw_serial_size, x509->raw_serial); x509->seen = true; ret = x509_get_sig_params(x509); if (ret < 0) - return ret; + goto maybe_missing_crypto_in_x509; pr_debug("- issuer %s\n", x509->issuer); if (x509->authority) - pr_debug("- authkeyid %s\n", x509->authority); + pr_debug("- authkeyid %*phN\n", + x509->authority->len, x509->authority->data); if (!x509->authority || strcmp(x509->subject, x509->issuer) == 0) { @@ -209,7 +206,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, ret = x509_check_signature(x509->pub, x509); if (ret < 0) - return ret; + goto maybe_missing_crypto_in_x509; x509->signer = x509; pr_debug("- self-signed\n"); return 0; @@ -218,13 +215,14 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, /* Look through the X.509 certificates in the PKCS#7 message's * list to see if the next one is there. */ - pr_debug("- want %s\n", x509->authority); + pr_debug("- want %*phN\n", + x509->authority->len, x509->authority->data); for (p = pkcs7->certs; p; p = p->next) { - pr_debug("- cmp [%u] %s\n", p->index, p->fingerprint); - if (p->raw_subject_size == x509->raw_issuer_size && - strcmp(p->fingerprint, x509->authority) == 0 && - memcmp(p->raw_subject, x509->raw_issuer, - x509->raw_issuer_size) == 0) + if (!p->skid) + continue; + pr_debug("- cmp [%u] %*phN\n", + p->index, p->skid->len, p->skid->data); + if (asymmetric_key_id_same(p->skid, x509->authority)) goto found_issuer; } @@ -233,7 +231,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, return 0; found_issuer: - pr_debug("- issuer %s\n", p->subject); + pr_debug("- subject %s\n", p->subject); if (p->seen) { pr_warn("Sig %u: X.509 chain contains loop\n", sinfo->index); @@ -250,6 +248,17 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, x509 = p; might_sleep(); } + +maybe_missing_crypto_in_x509: + /* Just prune the certificate chain at this point if we lack some + * crypto module to go further. Note, however, we don't want to set + * sinfo->missing_crypto as the signed info block may still be + * validatable against an X.509 cert lower in the chain that we have a + * trusted copy of. + */ + if (ret == -ENOPKG) + return 0; + return ret; } /* @@ -269,11 +278,14 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7, if (ret < 0) return ret; - /* Find the key for the signature */ + /* Find the key for the signature if there is one */ ret = pkcs7_find_key(pkcs7, sinfo); if (ret < 0) return ret; + if (!sinfo->signer) + return 0; + pr_devel("Using X.509[%u] for sig %u\n", sinfo->signer->index, sinfo->index); @@ -291,11 +303,33 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7, /** * pkcs7_verify - Verify a PKCS#7 message * @pkcs7: The PKCS#7 message to be verified + * + * Verify a PKCS#7 message is internally consistent - that is, the data digest + * matches the digest in the AuthAttrs and any signature in the message or one + * of the X.509 certificates it carries that matches another X.509 cert in the + * message can be verified. + * + * This does not look to match the contents of the PKCS#7 message against any + * external public keys. + * + * Returns, in order of descending priority: + * + * (*) -EKEYREJECTED if a signature failed to match for which we found an + * appropriate X.509 certificate, or: + * + * (*) -EBADMSG if some part of the message was invalid, or: + * + * (*) -ENOPKG if none of the signature chains are verifiable because suitable + * crypto modules couldn't be found, or: + * + * (*) 0 if all the signature chains that don't incur -ENOPKG can be verified + * (note that a signature chain may be of zero length), or: */ int pkcs7_verify(struct pkcs7_message *pkcs7) { struct pkcs7_signed_info *sinfo; struct x509_certificate *x509; + int enopkg = -ENOPKG; int ret, n; kenter(""); @@ -304,18 +338,24 @@ int pkcs7_verify(struct pkcs7_message *pkcs7) ret = x509_get_sig_params(x509); if (ret < 0) return ret; - pr_debug("X.509[%u] %s\n", n, x509->authority); + pr_debug("X.509[%u] %*phN\n", + n, x509->authority->len, x509->authority->data); } for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) { ret = pkcs7_verify_one(pkcs7, sinfo); if (ret < 0) { + if (ret == -ENOPKG) { + sinfo->unsupported_crypto = true; + continue; + } kleave(" = %d", ret); return ret; } + enopkg = 0; } - kleave(" = 0"); - return 0; + kleave(" = %d", enopkg); + return enopkg; } EXPORT_SYMBOL_GPL(pkcs7_verify); diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c index ac72348c186a..96151b2b91a2 100644 --- a/crypto/asymmetric_keys/x509_cert_parser.c +++ b/crypto/asymmetric_keys/x509_cert_parser.c @@ -46,7 +46,8 @@ void x509_free_certificate(struct x509_certificate *cert) public_key_destroy(cert->pub); kfree(cert->issuer); kfree(cert->subject); - kfree(cert->fingerprint); + kfree(cert->id); + kfree(cert->skid); kfree(cert->authority); kfree(cert->sig.digest); mpi_free(cert->sig.rsa.s); @@ -62,6 +63,7 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen) { struct x509_certificate *cert; struct x509_parse_context *ctx; + struct asymmetric_key_id *kid; long ret; ret = -ENOMEM; @@ -89,6 +91,17 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen) if (ret < 0) goto error_decode; + /* Generate cert issuer + serial number key ID */ + kid = asymmetric_key_generate_id(cert->raw_serial, + cert->raw_serial_size, + cert->raw_issuer, + cert->raw_issuer_size); + if (IS_ERR(kid)) { + ret = PTR_ERR(kid); + goto error_decode; + } + cert->id = kid; + kfree(ctx); return cert; @@ -407,36 +420,34 @@ int x509_process_extension(void *context, size_t hdrlen, const void *value, size_t vlen) { struct x509_parse_context *ctx = context; + struct asymmetric_key_id *kid; const unsigned char *v = value; - char *f; int i; pr_debug("Extension: %u\n", ctx->last_oid); if (ctx->last_oid == OID_subjectKeyIdentifier) { /* Get hold of the key fingerprint */ - if (vlen < 3) + if (ctx->cert->skid || vlen < 3) return -EBADMSG; if (v[0] != ASN1_OTS || v[1] != vlen - 2) return -EBADMSG; v += 2; vlen -= 2; - f = kmalloc(vlen * 2 + 1, GFP_KERNEL); - if (!f) - return -ENOMEM; - for (i = 0; i < vlen; i++) - sprintf(f + i * 2, "%02x", v[i]); - pr_debug("fingerprint %s\n", f); - ctx->cert->fingerprint = f; + kid = asymmetric_key_generate_id(v, vlen, + ctx->cert->raw_subject, + ctx->cert->raw_subject_size); + if (IS_ERR(kid)) + return PTR_ERR(kid); + ctx->cert->skid = kid; + pr_debug("subjkeyid %*phN\n", kid->len, kid->data); return 0; } if (ctx->last_oid == OID_authorityKeyIdentifier) { - size_t key_len; - /* Get hold of the CA key fingerprint */ - if (vlen < 5) + if (ctx->cert->authority || vlen < 5) return -EBADMSG; /* Authority Key Identifier must be a Constructed SEQUENCE */ @@ -454,7 +465,7 @@ int x509_process_extension(void *context, size_t hdrlen, v[3] > vlen - 4) return -EBADMSG; - key_len = v[3]; + vlen = v[3]; v += 4; } else { /* Long Form length */ @@ -476,17 +487,17 @@ int x509_process_extension(void *context, size_t hdrlen, v[sub + 1] > vlen - 4 - sub) return -EBADMSG; - key_len = v[sub + 1]; + vlen = v[sub + 1]; v += (sub + 2); } - f = kmalloc(key_len * 2 + 1, GFP_KERNEL); - if (!f) - return -ENOMEM; - for (i = 0; i < key_len; i++) - sprintf(f + i * 2, "%02x", v[i]); - pr_debug("authority %s\n", f); - ctx->cert->authority = f; + kid = asymmetric_key_generate_id(v, vlen, + ctx->cert->raw_issuer, + ctx->cert->raw_issuer_size); + if (IS_ERR(kid)) + return PTR_ERR(kid); + pr_debug("authkeyid %*phN\n", kid->len, kid->data); + ctx->cert->authority = kid; return 0; } diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h index 1b76f207c1f3..4e1a384901ed 100644 --- a/crypto/asymmetric_keys/x509_parser.h +++ b/crypto/asymmetric_keys/x509_parser.h @@ -19,8 +19,9 @@ struct x509_certificate { struct public_key_signature sig; /* Signature parameters */ char *issuer; /* Name of certificate issuer */ char *subject; /* Name of certificate subject */ - char *fingerprint; /* Key fingerprint as hex */ - char *authority; /* Authority key fingerprint as hex */ + struct asymmetric_key_id *id; /* Issuer + serial number */ + struct asymmetric_key_id *skid; /* Subject key identifier */ + struct asymmetric_key_id *authority; /* Authority key identifier */ struct tm valid_from; struct tm valid_to; const void *tbs; /* Signed data */ @@ -37,6 +38,7 @@ struct x509_certificate { bool seen; /* Infinite recursion prevention */ bool verified; bool trusted; + bool unsupported_crypto; /* T if can't be verified due to missing crypto */ }; /* diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index f3d62307e6ee..1d9a4c555376 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -25,7 +25,7 @@ #include "x509_parser.h" static bool use_builtin_keys; -static char *ca_keyid; +static struct asymmetric_key_id *ca_keyid; #ifndef MODULE static int __init ca_keys_setup(char *str) @@ -33,10 +33,16 @@ static int __init ca_keys_setup(char *str) if (!str) /* default system keyring */ return 1; - if (strncmp(str, "id:", 3) == 0) - ca_keyid = str; /* owner key 'id:xxxxxx' */ - else if (strcmp(str, "builtin") == 0) + if (strncmp(str, "id:", 3) == 0) { + struct asymmetric_key_id *p; + p = asymmetric_key_hex_to_key_id(str); + if (p == ERR_PTR(-EINVAL)) + pr_err("Unparsable hex string in ca_keys\n"); + else if (!IS_ERR(p)) + ca_keyid = p; /* owner key 'id:xxxxxx' */ + } else if (strcmp(str, "builtin") == 0) { use_builtin_keys = true; + } return 1; } @@ -46,31 +52,28 @@ __setup("ca_keys=", ca_keys_setup); /** * x509_request_asymmetric_key - Request a key by X.509 certificate params. * @keyring: The keys to search. - * @subject: The name of the subject to whom the key belongs. - * @key_id: The subject key ID as a hex string. + * @kid: The key ID. * * Find a key in the given keyring by subject name and key ID. These might, * for instance, be the issuer name and the authority key ID of an X.509 * certificate that needs to be verified. */ struct key *x509_request_asymmetric_key(struct key *keyring, - const char *subject, - const char *key_id) + const struct asymmetric_key_id *kid) { key_ref_t key; - size_t subject_len = strlen(subject), key_id_len = strlen(key_id); - char *id; + char *id, *p; - /* Construct an identifier "<subjname>:<keyid>". */ - id = kmalloc(subject_len + 2 + key_id_len + 1, GFP_KERNEL); + /* Construct an identifier "id:<keyid>". */ + p = id = kmalloc(2 + 1 + kid->len * 2 + 1, GFP_KERNEL); if (!id) return ERR_PTR(-ENOMEM); - memcpy(id, subject, subject_len); - id[subject_len + 0] = ':'; - id[subject_len + 1] = ' '; - memcpy(id + subject_len + 2, key_id, key_id_len); - id[subject_len + 2 + key_id_len] = 0; + *p++ = 'i'; + *p++ = 'd'; + *p++ = ':'; + p = bin2hex(p, kid->data, kid->len); + *p = 0; pr_debug("Look up: \"%s\"\n", id); @@ -112,6 +115,8 @@ int x509_get_sig_params(struct x509_certificate *cert) pr_devel("==>%s()\n", __func__); + if (cert->unsupported_crypto) + return -ENOPKG; if (cert->sig.rsa.s) return 0; @@ -124,8 +129,13 @@ int x509_get_sig_params(struct x509_certificate *cert) * big the hash operational data will be. */ tfm = crypto_alloc_shash(hash_algo_name[cert->sig.pkey_hash_algo], 0, 0); - if (IS_ERR(tfm)) - return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm); + if (IS_ERR(tfm)) { + if (PTR_ERR(tfm) == -ENOENT) { + cert->unsupported_crypto = true; + return -ENOPKG; + } + return PTR_ERR(tfm); + } desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); digest_size = crypto_shash_digestsize(tfm); @@ -172,6 +182,8 @@ int x509_check_signature(const struct public_key *pub, return ret; ret = public_key_verify_signature(pub, &cert->sig); + if (ret == -ENOPKG) + cert->unsupported_crypto = true; pr_debug("Cert Verification: %d\n", ret); return ret; } @@ -195,11 +207,10 @@ static int x509_validate_trust(struct x509_certificate *cert, if (!trust_keyring) return -EOPNOTSUPP; - if (ca_keyid && !asymmetric_keyid_match(cert->authority, ca_keyid)) + if (ca_keyid && !asymmetric_key_id_same(cert->authority, ca_keyid)) return -EPERM; - key = x509_request_asymmetric_key(trust_keyring, - cert->issuer, cert->authority); + key = x509_request_asymmetric_key(trust_keyring, cert->authority); if (!IS_ERR(key)) { if (!use_builtin_keys || test_bit(KEY_FLAG_BUILTIN, &key->flags)) @@ -214,9 +225,11 @@ static int x509_validate_trust(struct x509_certificate *cert, */ static int x509_key_preparse(struct key_preparsed_payload *prep) { + struct asymmetric_key_ids *kids; struct x509_certificate *cert; + const char *q; size_t srlen, sulen; - char *desc = NULL; + char *desc = NULL, *p; int ret; cert = x509_cert_parse(prep->data, prep->datalen); @@ -249,19 +262,12 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) pkey_algo_name[cert->sig.pkey_algo], hash_algo_name[cert->sig.pkey_hash_algo]); - if (!cert->fingerprint) { - pr_warn("Cert for '%s' must have a SubjKeyId extension\n", - cert->subject); - ret = -EKEYREJECTED; - goto error_free_cert; - } - cert->pub->algo = pkey_algo[cert->pub->pkey_algo]; cert->pub->id_type = PKEY_ID_X509; /* Check the signature on the key if it appears to be self-signed */ if (!cert->authority || - strcmp(cert->fingerprint, cert->authority) == 0) { + asymmetric_key_id_same(cert->skid, cert->authority)) { ret = x509_check_signature(cert->pub, cert); /* self-signed */ if (ret < 0) goto error_free_cert; @@ -273,31 +279,47 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) /* Propose a description */ sulen = strlen(cert->subject); - srlen = strlen(cert->fingerprint); + srlen = cert->raw_serial_size; + q = cert->raw_serial; + if (srlen > 1 && *q == 0) { + srlen--; + q++; + } + ret = -ENOMEM; - desc = kmalloc(sulen + 2 + srlen + 1, GFP_KERNEL); + desc = kmalloc(sulen + 2 + srlen * 2 + 1, GFP_KERNEL); if (!desc) goto error_free_cert; - memcpy(desc, cert->subject, sulen); - desc[sulen] = ':'; - desc[sulen + 1] = ' '; - memcpy(desc + sulen + 2, cert->fingerprint, srlen); - desc[sulen + 2 + srlen] = 0; + p = memcpy(desc, cert->subject, sulen); + p += sulen; + *p++ = ':'; + *p++ = ' '; + p = bin2hex(p, q, srlen); + *p = 0; + + kids = kmalloc(sizeof(struct asymmetric_key_ids), GFP_KERNEL); + if (!kids) + goto error_free_desc; + kids->id[0] = cert->id; + kids->id[1] = cert->skid; /* We're pinning the module by being linked against it */ __module_get(public_key_subtype.owner); prep->type_data[0] = &public_key_subtype; - prep->type_data[1] = cert->fingerprint; + prep->type_data[1] = kids; prep->payload[0] = cert->pub; prep->description = desc; prep->quotalen = 100; /* We've finished with the certificate */ cert->pub = NULL; - cert->fingerprint = NULL; + cert->id = NULL; + cert->skid = NULL; desc = NULL; ret = 0; +error_free_desc: + kfree(desc); error_free_cert: x509_free_certificate(cert); return ret; diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c index a3e932547617..f4cf200b3c76 100644 --- a/fs/cifs/cifs_spnego.c +++ b/fs/cifs/cifs_spnego.c @@ -62,7 +62,6 @@ cifs_spnego_key_destroy(struct key *key) struct key_type cifs_spnego_key_type = { .name = "cifs.spnego", .instantiate = cifs_spnego_key_instantiate, - .match = user_match, .destroy = cifs_spnego_key_destroy, .describe = user_describe, }; diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index 7ff866dbb89e..6d00c419cbae 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -84,7 +84,6 @@ static struct key_type cifs_idmap_key_type = { .instantiate = cifs_idmap_key_instantiate, .destroy = cifs_idmap_key_destroy, .describe = user_describe, - .match = user_match, }; static char * diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c index 7dd55b745c4d..2f5db844c172 100644 --- a/fs/nfs/idmap.c +++ b/fs/nfs/idmap.c @@ -177,7 +177,6 @@ static struct key_type key_type_id_resolver = { .preparse = user_preparse, .free_preparse = user_free_preparse, .instantiate = generic_key_instantiate, - .match = user_match, .revoke = user_revoke, .destroy = user_destroy, .describe = user_describe, @@ -401,7 +400,6 @@ static struct key_type key_type_id_resolver_legacy = { .preparse = user_preparse, .free_preparse = user_free_preparse, .instantiate = generic_key_instantiate, - .match = user_match, .revoke = user_revoke, .destroy = user_destroy, .describe = user_describe, diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index 0d164c6af539..fa73a6fd536c 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -15,6 +15,7 @@ #define _LINUX_PUBLIC_KEY_H #include <linux/mpi.h> +#include <keys/asymmetric-type.h> #include <crypto/hash_info.h> enum pkey_algo { @@ -98,8 +99,8 @@ struct key; extern int verify_signature(const struct key *key, const struct public_key_signature *sig); +struct asymmetric_key_id; extern struct key *x509_request_asymmetric_key(struct key *keyring, - const char *issuer, - const char *key_id); + const struct asymmetric_key_id *kid); #endif /* _LINUX_PUBLIC_KEY_H */ diff --git a/include/keys/asymmetric-type.h b/include/keys/asymmetric-type.h index 7dd473496180..044ab0d3aa45 100644 --- a/include/keys/asymmetric-type.h +++ b/include/keys/asymmetric-type.h @@ -19,6 +19,44 @@ extern struct key_type key_type_asymmetric; /* + * Identifiers for an asymmetric key ID. We have three ways of looking up a + * key derived from an X.509 certificate: + * + * (1) Serial Number & Issuer. Non-optional. This is the only valid way to + * map a PKCS#7 signature to an X.509 certificate. + * + * (2) Issuer & Subject Unique IDs. Optional. These were the original way to + * match X.509 certificates, but have fallen into disuse in favour of (3). + * + * (3) Auth & Subject Key Identifiers. Optional. SKIDs are only provided on + * CA keys that are intended to sign other keys, so don't appear in end + * user certificates unless forced. + * + * We could also support an PGP key identifier, which is just a SHA1 sum of the + * public key and certain parameters, but since we don't support PGP keys at + * the moment, we shall ignore those. + * + * What we actually do is provide a place where binary identifiers can be + * stashed and then compare against them when checking for an id match. + */ +struct asymmetric_key_id { + unsigned short len; + unsigned char data[]; +}; + +struct asymmetric_key_ids { + void *id[2]; +}; + +extern bool asymmetric_key_id_same(const struct asymmetric_key_id *kid1, + const struct asymmetric_key_id *kid2); + +extern struct asymmetric_key_id *asymmetric_key_generate_id(const void *val_1, + size_t len_1, + const void *val_2, + size_t len_2); + +/* * The payload is at the discretion of the subtype. */ diff --git a/include/keys/user-type.h b/include/keys/user-type.h index 3ab1873a4bfa..cebefb069c44 100644 --- a/include/keys/user-type.h +++ b/include/keys/user-type.h @@ -40,7 +40,6 @@ struct key_preparsed_payload; 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 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); diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 4c52907a6d8b..89a0b8e5a952 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -500,6 +500,7 @@ static inline char * __deprecated pack_hex_byte(char *buf, u8 byte) extern int hex_to_bin(char ch); extern int __must_check hex2bin(u8 *dst, const char *src, size_t count); +extern char *bin2hex(char *dst, const void *src, size_t count); int mac_pton(const char *s, u8 *mac); diff --git a/include/linux/key-type.h b/include/linux/key-type.h index 44792ee649de..ff9f1d394235 100644 --- a/include/linux/key-type.h +++ b/include/linux/key-type.h @@ -53,6 +53,24 @@ 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 exact description match, but can be + * overridden by type->match_preparse(). Should return true if a match + * is found and false if not. + */ + bool (*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 */ struct key_type { @@ -65,11 +83,6 @@ struct key_type { */ size_t def_datalen; - /* 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 +109,15 @@ struct key_type { */ int (*update)(struct key *key, struct key_preparsed_payload *prep); - /* match a key against a description */ - int (*match)(const struct key *key, const void *desc); + /* 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); + + /* 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 diff --git a/lib/hexdump.c b/lib/hexdump.c index 8499c810909a..270773b91923 100644 --- a/lib/hexdump.c +++ b/lib/hexdump.c @@ -59,6 +59,22 @@ int hex2bin(u8 *dst, const char *src, size_t count) EXPORT_SYMBOL(hex2bin); /** + * bin2hex - convert binary data to an ascii hexadecimal string + * @dst: ascii hexadecimal result + * @src: binary data + * @count: binary data length + */ +char *bin2hex(char *dst, const void *src, size_t count) +{ + const unsigned char *_src = src; + + while (count--) + dst = hex_byte_pack(dst, *_src++); + return dst; +} +EXPORT_SYMBOL(bin2hex); + +/** * hex_dump_to_buffer - convert a blob of data to "hex ASCII" in memory * @buf: data blob to dump * @len: number of bytes in the @buf diff --git a/net/ceph/crypto.c b/net/ceph/crypto.c index ffeba8f9dda9..62fc5e7a9acf 100644 --- a/net/ceph/crypto.c +++ b/net/ceph/crypto.c @@ -476,7 +476,6 @@ struct key_type key_type_ceph = { .preparse = ceph_key_preparse, .free_preparse = ceph_key_free_preparse, .instantiate = generic_key_instantiate, - .match = user_match, .destroy = ceph_key_destroy, }; diff --git a/net/dns_resolver/dns_key.c b/net/dns_resolver/dns_key.c index f380b2c58178..31cd4fd75486 100644 --- a/net/dns_resolver/dns_key.c +++ b/net/dns_resolver/dns_key.c @@ -176,11 +176,11 @@ static void dns_resolver_free_preparse(struct key_preparsed_payload *prep) * The domain name may be a simple name or an absolute domain name (which * should end with a period). The domain name is case-independent. */ -static int -dns_resolver_match(const struct key *key, const void *description) +static bool dns_resolver_cmp(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); @@ -209,6 +209,16 @@ no_match: } /* + * Preparse the match criterion. + */ +static int dns_resolver_match_preparse(struct key_match_data *match_data) +{ + match_data->lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE; + match_data->cmp = dns_resolver_cmp; + return 0; +} + +/* * Describe a DNS key */ static void dns_resolver_describe(const struct key *key, struct seq_file *m) @@ -242,7 +252,7 @@ struct key_type key_type_dns_resolver = { .preparse = dns_resolver_preparse, .free_preparse = dns_resolver_free_preparse, .instantiate = generic_key_instantiate, - .match = dns_resolver_match, + .match_preparse = dns_resolver_match_preparse, .revoke = user_revoke, .destroy = user_destroy, .describe = dns_resolver_describe, diff --git a/net/rxrpc/ar-key.c b/net/rxrpc/ar-key.c index 3907add75932..10c6cb694b43 100644 --- a/net/rxrpc/ar-key.c +++ b/net/rxrpc/ar-key.c @@ -44,7 +44,6 @@ struct key_type key_type_rxrpc = { .preparse = rxrpc_preparse, .free_preparse = rxrpc_free_preparse, .instantiate = generic_key_instantiate, - .match = user_match, .destroy = rxrpc_destroy, .describe = rxrpc_describe, .read = rxrpc_read, @@ -61,7 +60,6 @@ struct key_type key_type_rxrpc_s = { .preparse = rxrpc_preparse_s, .free_preparse = rxrpc_free_preparse_s, .instantiate = generic_key_instantiate, - .match = user_match, .destroy = rxrpc_destroy_s, .describe = rxrpc_describe, }; diff --git a/security/keys/big_key.c b/security/keys/big_key.c index c2f91a0cf889..b6adb94f6d52 100644 --- a/security/keys/big_key.c +++ b/security/keys/big_key.c @@ -33,11 +33,9 @@ MODULE_LICENSE("GPL"); */ struct key_type key_type_big_key = { .name = "big_key", - .def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, .preparse = big_key_preparse, .free_preparse = big_key_free_preparse, .instantiate = generic_key_instantiate, - .match = user_match, .revoke = big_key_revoke, .destroy = big_key_destroy, .describe = big_key_describe, diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c index 5fe443d120af..db9675db1026 100644 --- a/security/keys/encrypted-keys/encrypted.c +++ b/security/keys/encrypted-keys/encrypted.c @@ -970,7 +970,6 @@ struct key_type key_type_encrypted = { .name = "encrypted", .instantiate = encrypted_instantiate, .update = encrypted_update, - .match = user_match, .destroy = encrypted_destroy, .describe = user_describe, .read = encrypted_read, diff --git a/security/keys/internal.h b/security/keys/internal.h index 5f20da01fd8d..b8960c4959a5 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -107,20 +107,16 @@ 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 */ -#define KEYRING_SEARCH_DO_STATE_CHECK 0x0004 /* Override NO_STATE_CHECK */ -#define KEYRING_SEARCH_NO_UPDATE_TIME 0x0008 /* Don't update times */ -#define KEYRING_SEARCH_NO_CHECK_PERM 0x0010 /* Don't check permissions */ -#define KEYRING_SEARCH_DETECT_TOO_DEEP 0x0020 /* Give an error on excessive depth */ +#define KEYRING_SEARCH_NO_STATE_CHECK 0x0001 /* Skip state checks */ +#define KEYRING_SEARCH_DO_STATE_CHECK 0x0002 /* Override NO_STATE_CHECK */ +#define KEYRING_SEARCH_NO_UPDATE_TIME 0x0004 /* Don't update times */ +#define KEYRING_SEARCH_NO_CHECK_PERM 0x0008 /* Don't check permissions */ +#define KEYRING_SEARCH_DETECT_TOO_DEEP 0x0010 /* Give an error on excessive depth */ int (*iterator)(const void *object, void *iterator_data); @@ -131,6 +127,8 @@ struct keyring_search_context { struct timespec now; }; +extern bool key_default_cmp(const struct key *key, + const struct key_match_data *match_data); extern key_ref_t keyring_search_aux(key_ref_t keyring_ref, struct keyring_search_context *ctx); @@ -152,7 +150,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 bool 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 diff --git a/security/keys/key.c b/security/keys/key.c index b90a68c4e2c4..8c0092ca0443 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -799,7 +799,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, } key_ref = ERR_PTR(-EINVAL); - if (!index_key.type->match || !index_key.type->instantiate || + if (!index_key.type->instantiate || (!index_key.description && !index_key.type->preparse)) goto error_put_type; diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 8314a7d2104d..8177010174f7 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -89,7 +89,6 @@ struct key_type key_type_keyring = { .preparse = keyring_preparse, .free_preparse = keyring_free_preparse, .instantiate = keyring_instantiate, - .match = user_match, .revoke = keyring_revoke, .destroy = keyring_destroy, .describe = keyring_describe, @@ -512,6 +511,15 @@ struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid, EXPORT_SYMBOL(keyring_alloc); /* + * By default, we keys found by getting an exact match on their descriptions. + */ +bool key_default_cmp(const struct key *key, + const struct key_match_data *match_data) +{ + return strcmp(key->description, match_data->raw_data) == 0; +} + +/* * Iteration function to consider each key found. */ static int keyring_search_iterator(const void *object, void *iterator_data) @@ -545,7 +553,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 +593,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 +634,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 +892,25 @@ 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 = key_default_cmp, + .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) - return ERR_PTR(-ENOKEY); + if (type->match_preparse) { + ret = type->match_preparse(&ctx.match_data); + if (ret < 0) + return ERR_PTR(ret); + } - return keyring_search_aux(keyring, &ctx); + key = keyring_search_aux(keyring, &ctx); + + if (type->match_free) + type->match_free(&ctx.match_data); + return key; } EXPORT_SYMBOL(keyring_search); @@ -1014,7 +1030,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 +1047,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(); diff --git a/security/keys/proc.c b/security/keys/proc.c index d3f6f2fd21db..972eeb336b81 100644 --- a/security/keys/proc.c +++ b/security/keys/proc.c @@ -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); diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 0cf8a130a267..bd536cb221e2 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -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) +bool 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); diff --git a/security/keys/request_key.c b/security/keys/request_key.c index 381411941cc1..dc6ed32b7844 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -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 = key_default_cmp, + .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; diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c index 739e7455d388..6639e2cb8853 100644 --- a/security/keys/request_key_auth.c +++ b/security/keys/request_key_auth.c @@ -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 = key_default_cmp, + .match_data.raw_data = description, + .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, }; struct key *authkey; key_ref_t authkey_ref; diff --git a/security/keys/trusted.c b/security/keys/trusted.c index 6b804aa4529a..c0594cb07ada 100644 --- a/security/keys/trusted.c +++ b/security/keys/trusted.c @@ -1096,7 +1096,6 @@ struct key_type key_type_trusted = { .name = "trusted", .instantiate = trusted_instantiate, .update = trusted_update, - .match = user_match, .destroy = trusted_destroy, .describe = user_describe, .read = trusted_read, diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c index eee340011f2b..36b47bbd3d8c 100644 --- a/security/keys/user_defined.c +++ b/security/keys/user_defined.c @@ -26,12 +26,10 @@ static int logon_vet_description(const char *desc); */ struct key_type key_type_user = { .name = "user", - .def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, .preparse = user_preparse, .free_preparse = user_free_preparse, .instantiate = generic_key_instantiate, .update = user_update, - .match = user_match, .revoke = user_revoke, .destroy = user_destroy, .describe = user_describe, @@ -48,12 +46,10 @@ EXPORT_SYMBOL_GPL(key_type_user); */ struct key_type key_type_logon = { .name = "logon", - .def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, .preparse = user_preparse, .free_preparse = user_free_preparse, .instantiate = generic_key_instantiate, .update = user_update, - .match = user_match, .revoke = user_revoke, .destroy = user_destroy, .describe = user_describe, @@ -139,16 +135,6 @@ error: EXPORT_SYMBOL_GPL(user_update); /* - * match users on their name - */ -int user_match(const struct key *key, const void *description) -{ - return strcmp(key->description, description) == 0; -} - -EXPORT_SYMBOL_GPL(user_match); - -/* * dispose of the links from a revoked keyring * - called with the key sem write-locked */ |