From c3ce6dfa48e3879206382cdfdc015bffc50dce30 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 2 Sep 2014 13:52:15 +0100 Subject: KEYS: Set pr_fmt() in asymmetric key signature handling Printing in base signature handling should have a prefix, so set pr_fmt(). Signed-off-by: David Howells Signed-off-by: James Morris --- crypto/asymmetric_keys/signature.c | 1 + 1 file changed, 1 insertion(+) (limited to 'crypto') diff --git a/crypto/asymmetric_keys/signature.c b/crypto/asymmetric_keys/signature.c index 50b3f880b4ff..7525fd183574 100644 --- a/crypto/asymmetric_keys/signature.c +++ b/crypto/asymmetric_keys/signature.c @@ -11,6 +11,7 @@ * 2 of the Licence, or (at your option) any later version. */ +#define pr_fmt(fmt) "SIG: "fmt #include #include #include -- cgit v1.2.3 From 15155b9a4530f8da42d6594927928e25b2b29875 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 16 Sep 2014 17:07:07 +0100 Subject: PKCS#7: Add a missing static Add a missing static (found by checker). Signed-off-by: David Howells Acked-by: Vivek Goyal --- crypto/asymmetric_keys/pkcs7_trust.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'crypto') diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c index e666eb011a85..fad888ea4fad 100644 --- a/crypto/asymmetric_keys/pkcs7_trust.c +++ b/crypto/asymmetric_keys/pkcs7_trust.c @@ -23,9 +23,9 @@ /** * Check the trust on one PKCS#7 SignedInfo block. */ -int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, - struct pkcs7_signed_info *sinfo, - struct key *trust_keyring) +static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, + struct pkcs7_signed_info *sinfo, + struct key *trust_keyring) { struct public_key_signature *sig = &sinfo->sig; struct x509_certificate *x509, *last = NULL, *p; -- cgit v1.2.3 From 3cd0920cde6a726b9965b60184f9d5a54358304d Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 16 Sep 2014 17:29:03 +0100 Subject: PKCS#7: Provide a single place to do signed info block freeing The code to free a signed info block is repeated several times, so move the code to do it into a function of its own. This gives us a place to add clean ups for stuff that gets added to pkcs7_signed_info. Signed-off-by: David Howells Acked-by: Vivek Goyal --- crypto/asymmetric_keys/pkcs7_parser.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) (limited to 'crypto') diff --git a/crypto/asymmetric_keys/pkcs7_parser.c b/crypto/asymmetric_keys/pkcs7_parser.c index 42e56aa7d277..4c4ea35c338b 100644 --- a/crypto/asymmetric_keys/pkcs7_parser.c +++ b/crypto/asymmetric_keys/pkcs7_parser.c @@ -31,6 +31,18 @@ struct pkcs7_parse_context { unsigned sinfo_index; }; +/* + * Free a signed information block. + */ +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); + } +} + /** * pkcs7_free_message - Free a PKCS#7 message * @pkcs7: The PKCS#7 message to free @@ -54,9 +66,7 @@ void pkcs7_free_message(struct pkcs7_message *pkcs7) while (pkcs7->signed_infos) { sinfo = pkcs7->signed_infos; pkcs7->signed_infos = sinfo->next; - mpi_free(sinfo->sig.mpi[0]); - kfree(sinfo->sig.digest); - kfree(sinfo); + pkcs7_free_signed_info(sinfo); } kfree(pkcs7); } @@ -100,16 +110,12 @@ struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen) ctx->certs = cert->next; x509_free_certificate(cert); } - mpi_free(ctx->sinfo->sig.mpi[0]); - kfree(ctx->sinfo->sig.digest); - kfree(ctx->sinfo); + pkcs7_free_signed_info(ctx->sinfo); kfree(ctx); return msg; error_decode: - mpi_free(ctx->sinfo->sig.mpi[0]); - kfree(ctx->sinfo->sig.digest); - kfree(ctx->sinfo); + pkcs7_free_signed_info(ctx->sinfo); error_no_sinfo: kfree(ctx); error_no_ctx: -- cgit v1.2.3 From cecf5d2e1208da512a4c951c24acd66c54a4d06c Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 16 Sep 2014 17:29:03 +0100 Subject: PKCS#7: Fix the parser cleanup to drain parsed out X.509 certs Fix the parser cleanup code to drain parsed out X.509 certs in the case that the decode fails and we jump to error_decode. The function is rearranged so that the same cleanup code is used in the success case as the error case - just that the message descriptor under construction is only released if it is still pointed to by the context struct at that point. Signed-off-by: David Howells Acked-by: Vivek Goyal --- crypto/asymmetric_keys/pkcs7_parser.c | 39 +++++++++++++++++------------------ 1 file changed, 19 insertions(+), 20 deletions(-) (limited to 'crypto') diff --git a/crypto/asymmetric_keys/pkcs7_parser.c b/crypto/asymmetric_keys/pkcs7_parser.c index 4c4ea35c338b..1e9861da7ee4 100644 --- a/crypto/asymmetric_keys/pkcs7_parser.c +++ b/crypto/asymmetric_keys/pkcs7_parser.c @@ -81,47 +81,46 @@ EXPORT_SYMBOL_GPL(pkcs7_free_message); struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen) { struct pkcs7_parse_context *ctx; - struct pkcs7_message *msg; - long ret; + struct pkcs7_message *msg = ERR_PTR(-ENOMEM); + int ret; - ret = -ENOMEM; - msg = kzalloc(sizeof(struct pkcs7_message), GFP_KERNEL); - if (!msg) - goto error_no_sig; ctx = kzalloc(sizeof(struct pkcs7_parse_context), GFP_KERNEL); if (!ctx) - goto error_no_ctx; + goto out_no_ctx; + ctx->msg = kzalloc(sizeof(struct pkcs7_message), GFP_KERNEL); + if (!ctx->msg) + goto out_no_msg; ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL); if (!ctx->sinfo) - goto error_no_sinfo; + goto out_no_sinfo; - ctx->msg = msg; ctx->data = (unsigned long)data; ctx->ppcerts = &ctx->certs; ctx->ppsinfo = &ctx->msg->signed_infos; /* Attempt to decode the signature */ ret = asn1_ber_decoder(&pkcs7_decoder, ctx, data, datalen); - if (ret < 0) - goto error_decode; + if (ret < 0) { + msg = ERR_PTR(ret); + goto out; + } + + msg = ctx->msg; + ctx->msg = NULL; +out: while (ctx->certs) { struct x509_certificate *cert = ctx->certs; ctx->certs = cert->next; x509_free_certificate(cert); } pkcs7_free_signed_info(ctx->sinfo); +out_no_sinfo: + pkcs7_free_message(ctx->msg); +out_no_msg: kfree(ctx); +out_no_ctx: return msg; - -error_decode: - pkcs7_free_signed_info(ctx->sinfo); -error_no_sinfo: - kfree(ctx); -error_no_ctx: - pkcs7_free_message(msg); -error_no_sig: - return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(pkcs7_parse_message); -- cgit v1.2.3 From 462919591a1791e76042dc5c1e0148715df59beb Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 16 Sep 2014 17:36:02 +0100 Subject: KEYS: Preparse match data 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 Acked-by: Vivek Goyal --- crypto/asymmetric_keys/asymmetric_type.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) (limited to 'crypto') diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index eb8cd46961a5..f666b4e8d256 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -59,9 +59,11 @@ EXPORT_SYMBOL_GPL(asymmetric_keyid_match); * "id:" - request a key matching the 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:" - 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, -- cgit v1.2.3 From 614d8c39014c185aa0f7254f0a470cc33fc1b284 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 16 Sep 2014 17:36:04 +0100 Subject: KEYS: Remove key_type::def_lookup_type Remove key_type::def_lookup_type as it's no longer used. The information now defaults to KEYRING_SEARCH_LOOKUP_DIRECT but may be overridden by type->match_preparse(). Signed-off-by: David Howells Acked-by: Vivek Goyal --- crypto/asymmetric_keys/asymmetric_type.c | 1 - crypto/asymmetric_keys/pkcs7_key_type.c | 1 - 2 files changed, 2 deletions(-) (limited to 'crypto') diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index f666b4e8d256..9d78ad7754d9 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -228,7 +228,6 @@ struct key_type key_type_asymmetric = { .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..d1faa1df1dec 100644 --- a/crypto/asymmetric_keys/pkcs7_key_type.c +++ b/crypto/asymmetric_keys/pkcs7_key_type.c @@ -72,7 +72,6 @@ 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, -- cgit v1.2.3 From c06cfb08b88dfbe13be44a69ae2fdc3a7c902d81 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 16 Sep 2014 17:36:06 +0100 Subject: KEYS: Remove key_type::match in favour of overriding default by match_preparse A previous patch added a ->match_preparse() method to the key type. This is allowed to override the function called by the iteration algorithm. Therefore, we can just set a default that simply checks for an exact match of the key description with the original criterion data and allow match_preparse to override it as needed. The key_type::match op is then redundant and can be removed, as can the user_match() function. Signed-off-by: David Howells Acked-by: Vivek Goyal --- crypto/asymmetric_keys/asymmetric_type.c | 6 +++--- crypto/asymmetric_keys/pkcs7_key_type.c | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) (limited to 'crypto') diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index 9d78ad7754d9..7c0498968975 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -59,8 +59,8 @@ EXPORT_SYMBOL_GPL(asymmetric_keyid_match); * "id:" - request a key matching the ID * ":" - request a key of a subtype */ -static int asymmetric_key_match(const struct key *key, - const struct key_match_data *match_data) +static int asymmetric_key_cmp(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; @@ -110,6 +110,7 @@ static int asymmetric_key_match(const struct key *key, static int asymmetric_key_match_preparse(struct key_match_data *match_data) { match_data->lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE; + match_data->cmp = asymmetric_key_cmp; return 0; } @@ -224,7 +225,6 @@ struct key_type key_type_asymmetric = { .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, diff --git a/crypto/asymmetric_keys/pkcs7_key_type.c b/crypto/asymmetric_keys/pkcs7_key_type.c index d1faa1df1dec..751f8fd7335d 100644 --- a/crypto/asymmetric_keys/pkcs7_key_type.c +++ b/crypto/asymmetric_keys/pkcs7_key_type.c @@ -75,7 +75,6 @@ static struct key_type key_type_pkcs7 = { .preparse = pkcs7_preparse, .free_preparse = user_free_preparse, .instantiate = generic_key_instantiate, - .match = user_match, .revoke = user_revoke, .destroy = user_destroy, .describe = user_describe, -- cgit v1.2.3 From 0c903ab64feb0fe83eac9f67a06e2f5b9508de16 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 16 Sep 2014 17:36:08 +0100 Subject: KEYS: Make the key matching functions return bool Make the key matching functions pointed to by key_match_data::cmp return bool rather than int. Signed-off-by: David Howells Acked-by: Vivek Goyal --- crypto/asymmetric_keys/asymmetric_type.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'crypto') diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index 7c0498968975..7755f918e8d9 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -59,8 +59,8 @@ EXPORT_SYMBOL_GPL(asymmetric_keyid_match); * "id:" - request a key matching the ID * ":" - request a key of a subtype */ -static int asymmetric_key_cmp(const struct key *key, - const struct key_match_data *match_data) +static bool asymmetric_key_cmp(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; -- cgit v1.2.3 From 7901c1a8effbe5f89673bfc09d6e37b8f334f1a7 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 16 Sep 2014 17:36:11 +0100 Subject: KEYS: Implement binary asymmetric key ID handling Implement the first step in using binary key IDs for asymmetric keys rather than hex string keys. The previously added match data preparsing will be able to convert hex criterion strings into binary which can then be compared more rapidly. Further, we actually want more then one ID string per public key. The problem is that X.509 certs refer to other X.509 certs by matching Issuer + AuthKeyId to Subject + SubjKeyId, but PKCS#7 messages match against X.509 Issuer + SerialNumber. This patch just provides facilities for a later patch to make use of. Signed-off-by: David Howells Acked-by: Vivek Goyal --- crypto/asymmetric_keys/asymmetric_keys.h | 4 ++ crypto/asymmetric_keys/asymmetric_type.c | 89 ++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) (limited to 'crypto') diff --git a/crypto/asymmetric_keys/asymmetric_keys.h b/crypto/asymmetric_keys/asymmetric_keys.h index a63c551c6557..917be6b985e7 100644 --- a/crypto/asymmetric_keys/asymmetric_keys.h +++ b/crypto/asymmetric_keys/asymmetric_keys.h @@ -10,6 +10,10 @@ */ 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); + +extern struct asymmetric_key_id *asymmetric_key_hex_to_key_id(const char *id); static inline const char *asymmetric_key_id(const struct key *key) { diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index 7755f918e8d9..92bfc438dd1d 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "asymmetric_keys.h" MODULE_LICENSE("GPL"); @@ -22,6 +23,94 @@ MODULE_LICENSE("GPL"); static LIST_HEAD(asymmetric_key_parsers); static DECLARE_RWSEM(asymmetric_key_parsers_sem); +/** + * 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. + */ +struct asymmetric_key_id *asymmetric_key_generate_id(const void *val_1, + size_t len_1, + const void *val_2, + size_t len_2) +{ + 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); + +/** + * 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); + +/** + * 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); + +/** + * 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; +} + /* * Match asymmetric key id with partial match * @id: key id to match in a form "id:" -- cgit v1.2.3 From 46963b774d441c833afc1535f6d84b3df2a94204 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 16 Sep 2014 17:36:13 +0100 Subject: KEYS: Overhaul key identification when searching for asymmetric keys Make use of the new match string preparsing to overhaul key identification when searching for asymmetric keys. The following changes are made: (1) Use the previously created asymmetric_key_id struct to hold the following key IDs derived from the X.509 certificate or PKCS#7 message: id: serial number + issuer skid: subjKeyId + subject authority: authKeyId + issuer (2) Replace the hex fingerprint attached to key->type_data[1] with an asymmetric_key_ids struct containing the id and the skid (if present). (3) Make the asymmetric_type match data preparse select one of two searches: (a) An iterative search for the key ID given if prefixed with "id:". The prefix is expected to be followed by a hex string giving the ID to search for. The criterion key ID is checked against all key IDs recorded on the key. (b) A direct search if the key ID is not prefixed with "id:". This will look for an exact match on the key description. (4) Make x509_request_asymmetric_key() take a key ID. This is then converted into "id:" and passed into keyring_search() where match preparsing will turn it back into a binary ID. (5) X.509 certificate verification then takes the authority key ID and looks up a key that matches it to find the public key for the certificate signature. (6) PKCS#7 certificate verification then takes the id key ID and looks up a key that matches it to find the public key for the signed information block signature. Additional changes: (1) Multiple subjKeyId and authKeyId values on an X.509 certificate cause the cert to be rejected with -EBADMSG. (2) The 'fingerprint' ID is gone. This was primarily intended to convey PGP public key fingerprints. If PGP is supported in future, this should generate a key ID that carries the fingerprint. (3) Th ca_keyid= kernel command line option is now converted to a key ID and used to match the authority key ID. Possibly this should only match the actual authKeyId part and not the issuer as well. Signed-off-by: David Howells Acked-by: Vivek Goyal --- crypto/asymmetric_keys/asymmetric_keys.h | 4 +- crypto/asymmetric_keys/asymmetric_type.c | 133 +++++++++++++----------------- crypto/asymmetric_keys/pkcs7_parser.c | 38 ++++++--- crypto/asymmetric_keys/pkcs7_parser.h | 5 +- crypto/asymmetric_keys/pkcs7_trust.c | 6 +- crypto/asymmetric_keys/pkcs7_verify.c | 44 +++++----- crypto/asymmetric_keys/x509_cert_parser.c | 55 +++++++----- crypto/asymmetric_keys/x509_parser.h | 5 +- crypto/asymmetric_keys/x509_public_key.c | 89 +++++++++++--------- 9 files changed, 195 insertions(+), 184 deletions(-) (limited to 'crypto') diff --git a/crypto/asymmetric_keys/asymmetric_keys.h b/crypto/asymmetric_keys/asymmetric_keys.h index 917be6b985e7..fd21ac28e0a0 100644 --- a/crypto/asymmetric_keys/asymmetric_keys.h +++ b/crypto/asymmetric_keys/asymmetric_keys.h @@ -9,13 +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); extern struct asymmetric_key_id *asymmetric_key_hex_to_key_id(const char *id); -static inline const char *asymmetric_key_id(const struct key *key) +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 92bfc438dd1d..718e779a010e 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -112,76 +112,15 @@ struct asymmetric_key_id *asymmetric_key_hex_to_key_id(const char *id) } /* - * Match asymmetric key id with partial match - * @id: key id to match in a form "id:" - */ -int asymmetric_keyid_match(const char *kid, const char *id) -{ - size_t idlen, kidlen; - - if (!kid || !id) - return 0; - - /* make it possible to use id as in the request: "id:" */ - if (strncmp(id, "id:", 3) == 0) - id += 3; - - /* Anything after here requires a partial match on the ID string */ - idlen = strlen(id); - kidlen = strlen(kid); - if (idlen > kidlen) - return 0; - - kid += kidlen - idlen; - if (strcasecmp(id, kid) != 0) - return 0; - - return 1; -} -EXPORT_SYMBOL_GPL(asymmetric_keyid_match); - -/* - * Match asymmetric keys on (part of) their name - * We have some shorthand methods for matching keys. We allow: - * - * "" - request a key by description - * "id:" - request a key matching the ID - * ":" - request a key of a subtype + * 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_subtype *subtype = asymmetric_key_subtype(key); - const char *description = match_data->raw_data; - const char *spec = description; - 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 (speclen == 2 && memcmp(spec, "id", 2) == 0) - return asymmetric_keyid_match(asymmetric_key_id(key), id); + const struct asymmetric_key_ids *kids = asymmetric_key_ids(key); + const struct asymmetric_key_id *match_id = match_data->preparsed; - if (speclen == subtype->name_len && - memcmp(spec, subtype->name, speclen) == 0) - return 1; - - return 0; + return asymmetric_match_key_ids(kids, match_id); } /* @@ -198,8 +137,30 @@ static bool asymmetric_key_cmp(const struct key *key, */ static int asymmetric_key_match_preparse(struct key_match_data *match_data) { - match_data->lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE; + struct asymmetric_key_id *match_id; + const char *spec = match_data->raw_data; + const char *id; + + if (!spec || !*spec) + return -EINVAL; + if (spec[0] == 'i' && + spec[1] == 'd' && + spec[2] == ':') { + id = spec + 3; + } else { + goto default_match; + } + + match_id = asymmetric_key_hex_to_key_id(id); + if (!match_id) + return -ENOMEM; + + 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; } @@ -208,6 +169,7 @@ static int asymmetric_key_match_preparse(struct key_match_data *match_data) */ static void asymmetric_key_match_free(struct key_match_data *match_data) { + kfree(match_data->preparsed); } /* @@ -216,8 +178,10 @@ static void asymmetric_key_match_free(struct key_match_data *match_data) 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); @@ -225,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, " ["); @@ -282,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__); @@ -289,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); } @@ -299,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 = { 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..91949f92bc72 100644 --- a/crypto/asymmetric_keys/pkcs7_parser.h +++ b/crypto/asymmetric_keys/pkcs7_parser.h @@ -33,10 +33,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..09197e50fa82 100644 --- a/crypto/asymmetric_keys/pkcs7_trust.c +++ b/crypto/asymmetric_keys/pkcs7_trust.c @@ -49,8 +49,7 @@ 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); + 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. @@ -82,8 +81,7 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, return -ENOKEY; } - key = x509_request_asymmetric_key(trust_keyring, last->issuer, - last->authority); + key = x509_request_asymmetric_key(trust_keyring, last->authority); if (IS_ERR(key)) return PTR_ERR(key) == -ENOMEM ? -ENOMEM : -ENOKEY; x509 = last; diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys/pkcs7_verify.c index c62cf8006e1f..57e90fa17f2b 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,8 +153,10 @@ 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); + sinfo->index, + sinfo->signing_cert_id->len, sinfo->signing_cert_id->data); return -ENOKEY; } @@ -184,7 +175,9 @@ 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) @@ -192,7 +185,8 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, 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) { @@ -218,13 +212,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 +228,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); @@ -304,7 +299,8 @@ 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) { 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..0e8d59b010fb 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 */ diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index f3d62307e6ee..c60905c3f4d2 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 ":". */ - id = kmalloc(subject_len + 2 + key_id_len + 1, GFP_KERNEL); + /* Construct an identifier "id:". */ + 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); @@ -195,11 +198,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 +216,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 +253,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 +270,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; -- cgit v1.2.3 From 41559420003cfe99522257dded7793192c77b4e9 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 16 Sep 2014 17:36:15 +0100 Subject: PKCS#7: Better handling of unsupported crypto Provide better handling of unsupported crypto when verifying a PKCS#7 message. If we can't bridge the gap between a pair of X.509 certs or between a signed info block and an X.509 cert because it involves some crypto we don't support, that's not necessarily the end of the world as there may be other ways points at which we can intersect with a ring of trusted keys. Instead, only produce ENOPKG immediately if all the signed info blocks in a PKCS#7 message require unsupported crypto to bridge to the first X.509 cert. Otherwise, we defer the generation of ENOPKG until we get ENOKEY during trust validation. Signed-off-by: David Howells Acked-by: Vivek Goyal --- crypto/asymmetric_keys/pkcs7_parser.h | 1 + crypto/asymmetric_keys/pkcs7_trust.c | 29 +++++++++++++------- crypto/asymmetric_keys/pkcs7_verify.c | 46 +++++++++++++++++++++++++++++--- crypto/asymmetric_keys/x509_parser.h | 1 + crypto/asymmetric_keys/x509_public_key.c | 13 +++++++-- 5 files changed, 74 insertions(+), 16 deletions(-) (limited to 'crypto') diff --git a/crypto/asymmetric_keys/pkcs7_parser.h b/crypto/asymmetric_keys/pkcs7_parser.h index 91949f92bc72..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; diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c index 09197e50fa82..8bd474e5e706 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) { @@ -139,24 +144,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 57e90fa17f2b..bd264052f751 100644 --- a/crypto/asymmetric_keys/pkcs7_verify.c +++ b/crypto/asymmetric_keys/pkcs7_verify.c @@ -181,7 +181,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, 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) @@ -203,7 +203,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; @@ -245,6 +245,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; } /* @@ -286,11 +297,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(""); @@ -306,12 +339,17 @@ int pkcs7_verify(struct pkcs7_message *pkcs7) 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_parser.h b/crypto/asymmetric_keys/x509_parser.h index 0e8d59b010fb..4e1a384901ed 100644 --- a/crypto/asymmetric_keys/x509_parser.h +++ b/crypto/asymmetric_keys/x509_parser.h @@ -38,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 c60905c3f4d2..1d9a4c555376 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -115,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; @@ -127,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); @@ -175,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; } -- cgit v1.2.3 From 757932e6da6de05b91cc4cb85493b17de48c2667 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 16 Sep 2014 17:36:17 +0100 Subject: PKCS#7: Handle PKCS#7 messages that contain no X.509 certs The X.509 certificate list in a PKCS#7 message is optional. To save space, we can omit the inclusion of any X.509 certificates if we are sure that we can look the relevant public key up by the serial number and issuer given in a signed info block. This also supports use of a signed info block for which we can't find a matching X.509 cert in the certificate list, though it be populated. Signed-off-by: David Howells Acked-by: Vivek Goyal --- crypto/asymmetric_keys/pkcs7_trust.c | 48 ++++++++++++++++++++++++++--------- crypto/asymmetric_keys/pkcs7_verify.c | 16 ++++++++---- 2 files changed, 47 insertions(+), 17 deletions(-) (limited to 'crypto') diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c index 8bd474e5e706..ae47be6128c4 100644 --- a/crypto/asymmetric_keys/pkcs7_trust.c +++ b/crypto/asymmetric_keys/pkcs7_trust.c @@ -55,13 +55,16 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, * keys. */ key = x509_request_asymmetric_key(trust_keyring, x509->id); - if (!IS_ERR(key)) + 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; @@ -81,15 +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); } - key = x509_request_asymmetric_key(trust_keyring, last->authority); - if (IS_ERR(key)) - return PTR_ERR(key) == -ENOMEM ? -ENOMEM : -ENOKEY; - x509 = last; + /* 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); + + kleave(" = -ENOKEY [no backref]"); + return -ENOKEY; matched: ret = verify_signature(key, sig); @@ -103,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"); diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys/pkcs7_verify.c index bd264052f751..cd455450b069 100644 --- a/crypto/asymmetric_keys/pkcs7_verify.c +++ b/crypto/asymmetric_keys/pkcs7_verify.c @@ -154,10 +154,13 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7, return 0; } - pr_warn("Sig %u: Issuing X.509 cert not found (#%*ph)\n", - sinfo->index, - sinfo->signing_cert_id->len, sinfo->signing_cert_id->data); - 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; } /* @@ -275,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); -- cgit v1.2.3 From d1ac5540455c3a2a11e943e19e2dc044cebe147d Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 22 Sep 2014 00:02:01 +0100 Subject: KEYS: Check hex2bin()'s return when generating an asymmetric key ID As it stands, the code to generate an asymmetric key ID prechecks the hex string it is given whilst determining the length, before it allocates the buffer for hex2bin() to translate into - which mean that checking the result of hex2bin() is redundant. Unfortunately, hex2bin() is marked as __must_check, which means that the following warning may be generated if the return value isn't checked: crypto/asymmetric_keys/asymmetric_type.c: In function asymmetric_key_hex_to_key_id: crypto/asymmetric_keys/asymmetric_type.c:110: warning: ignoring return value of hex2bin, declared with attribute warn_unused_result The warning can't be avoided by casting the result to void. Instead, use strlen() to check the length of the string and ignore the fact that the string might not be entirely valid hex until after the allocation has been done - in which case we can use the result of hex2bin() for this. Signed-off-by: David Howells --- crypto/asymmetric_keys/asymmetric_type.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'crypto') diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index 718e779a010e..f0f2111d2c66 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -90,15 +90,12 @@ EXPORT_SYMBOL_GPL(asymmetric_match_key_ids); 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; + size_t hexlen; + int ret; if (!*id) return ERR_PTR(-EINVAL); - for (p = id; *p; p++) - if (!isxdigit(*p)) - return ERR_PTR(-EINVAL); - hexlen = p - id; + hexlen = strlen(id); if (hexlen & 1) return ERR_PTR(-EINVAL); @@ -107,7 +104,11 @@ struct asymmetric_key_id *asymmetric_key_hex_to_key_id(const char *id) if (!match_id) return ERR_PTR(-ENOMEM); match_id->len = hexlen / 2; - (void)hex2bin(match_id->data, id, hexlen / 2); + ret = hex2bin(match_id->data, id, hexlen / 2); + if (ret < 0) { + kfree(match_id); + return ERR_PTR(-EINVAL); + } return match_id; } -- cgit v1.2.3 From 40b50e80c5ca78b3164d79d39b4889c4e58f462e Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Fri, 3 Oct 2014 11:53:28 +0300 Subject: KEYS: handle error code encoded in pointer If hexlen is odd then function returns an error. Use IS_ERR to check for error, otherwise invalid pointer is used and kernel gives oops: [ 132.816522] BUG: unable to handle kernel paging request at ffffffffffffffea [ 132.819902] IP: [] asymmetric_key_id_same+0x14/0x36 [ 132.820302] PGD 1a12067 PUD 1a14067 PMD 0 [ 132.820302] Oops: 0000 [#1] SMP [ 132.820302] Modules linked in: bridge(E) stp(E) llc(E) evdev(E) serio_raw(E) i2c_piix4(E) button(E) fuse(E) [ 132.820302] CPU: 0 PID: 2993 Comm: cat Tainted: G E 3.16.0-kds+ #2847 [ 132.820302] Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011 [ 132.820302] task: ffff88004249a430 ti: ffff880056640000 task.ti: ffff880056640000 [ 132.820302] RIP: 0010:[] [] asymmetric_key_id_same+0x14/0x36 [ 132.820302] RSP: 0018:ffff880056643930 EFLAGS: 00010246 [ 132.820302] RAX: 0000000000000000 RBX: ffffffffffffffea RCX: ffff880056643ae0 [ 132.820302] RDX: 000000000000005e RSI: ffffffffffffffea RDI: ffff88005bac9300 [ 132.820302] RBP: ffff880056643948 R08: 0000000000000003 R09: 00000007504aa01a [ 132.820302] R10: 0000000000000000 R11: 0000000000000000 R12: ffff88005d68ca40 [ 132.820302] R13: 0000000000000101 R14: 0000000000000000 R15: ffff88005bac5280 [ 132.820302] FS: 00007f67a153c740(0000) GS:ffff88005da00000(0000) knlGS:0000000000000000 [ 132.820302] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b [ 132.820302] CR2: ffffffffffffffea CR3: 000000002e663000 CR4: 00000000000006f0 [ 132.820302] Stack: [ 132.820302] ffffffff812bfc66 ffff880056643ae0 ffff88005bac5280 ffff880056643958 [ 132.820302] ffffffff812bfc9d ffff880056643980 ffffffff812971d9 ffff88005ce930c1 [ 132.820302] ffff88005ce930c0 0000000000000000 ffff8800566439c8 ffffffff812fb753 [ 132.820302] Call Trace: [ 132.820302] [] ? asymmetric_match_key_ids+0x24/0x42 [ 132.820302] [] asymmetric_key_cmp+0x19/0x1b [ 132.820302] [] keyring_search_iterator+0x74/0xd7 [ 132.820302] [] assoc_array_subtree_iterate+0x67/0xd2 [ 132.820302] [] ? key_default_cmp+0x20/0x20 [ 132.820302] [] assoc_array_iterate+0x19/0x1e [ 132.820302] [] search_nested_keyrings+0xf6/0x2b6 [ 132.820302] [] ? sched_clock_cpu+0x91/0xa2 [ 132.820302] [] ? mark_held_locks+0x58/0x6e [ 132.820302] [] ? current_kernel_time+0x77/0xb8 [ 132.820302] [] keyring_search_aux+0xe1/0x14c [ 132.820302] [] ? keyring_search_aux+0x6c/0x14c [ 132.820302] [] keyring_search+0x8f/0xb6 [ 132.820302] [] ? asymmetric_match_key_ids+0x42/0x42 [ 132.820302] [] ? key_default_cmp+0x20/0x20 [ 132.820302] [] asymmetric_verify+0xa4/0x214 [ 132.820302] [] integrity_digsig_verify+0xb1/0xe2 [ 132.820302] [] ? evm_verifyxattr+0x6a/0x7a [ 132.820302] [] ima_appraise_measurement+0x160/0x370 [ 132.820302] [] ? d_absolute_path+0x5b/0x7a [ 132.820302] [] process_measurement+0x322/0x404 Reported-by: Dmitry Kasatkin Signed-off-by: Dmitry Kasatkin Signed-off-by: David Howells --- crypto/asymmetric_keys/asymmetric_type.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'crypto') diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index f0f2111d2c66..29983cbb658d 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -153,8 +153,8 @@ static int asymmetric_key_match_preparse(struct key_match_data *match_data) } match_id = asymmetric_key_hex_to_key_id(id); - if (!match_id) - return -ENOMEM; + if (IS_ERR(match_id)) + return PTR_ERR(match_id); match_data->preparsed = match_id; match_data->cmp = asymmetric_key_cmp; -- cgit v1.2.3 From dd2f6c4481debfa389c1f2b2b1d5bd6449c42611 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 3 Oct 2014 16:17:02 +0100 Subject: X.509: If available, use the raw subjKeyId to form the key description Module signing matches keys by comparing against the key description exactly. However, the way the key description gets constructed got changed to be composed of the subject name plus the certificate serial number instead of the subject name and the subjectKeyId. I changed this to avoid problems with certificates that don't *have* a subjectKeyId. Instead, if available, use the raw subjectKeyId to form the key description and only use the serial number if the subjectKeyId doesn't exist. Reported-by: Dmitry Kasatkin Signed-off-by: David Howells --- crypto/asymmetric_keys/x509_cert_parser.c | 2 ++ crypto/asymmetric_keys/x509_parser.h | 2 ++ crypto/asymmetric_keys/x509_public_key.c | 9 +++++++-- 3 files changed, 11 insertions(+), 2 deletions(-) (limited to 'crypto') diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c index 96151b2b91a2..393706f33fa5 100644 --- a/crypto/asymmetric_keys/x509_cert_parser.c +++ b/crypto/asymmetric_keys/x509_cert_parser.c @@ -435,6 +435,8 @@ int x509_process_extension(void *context, size_t hdrlen, v += 2; vlen -= 2; + ctx->cert->raw_skid_size = vlen; + ctx->cert->raw_skid = v; kid = asymmetric_key_generate_id(v, vlen, ctx->cert->raw_subject, ctx->cert->raw_subject_size); diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h index 4e1a384901ed..3f0f0f081621 100644 --- a/crypto/asymmetric_keys/x509_parser.h +++ b/crypto/asymmetric_keys/x509_parser.h @@ -34,6 +34,8 @@ struct x509_certificate { const void *raw_issuer; /* Raw issuer name in ASN.1 */ const void *raw_subject; /* Raw subject name in ASN.1 */ unsigned raw_subject_size; + unsigned raw_skid_size; + const void *raw_skid; /* Raw subjectKeyId in ASN.1 */ unsigned index; bool seen; /* Infinite recursion prevention */ bool verified; diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index 1d9a4c555376..8bffb06b2683 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -279,8 +279,13 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) /* Propose a description */ sulen = strlen(cert->subject); - srlen = cert->raw_serial_size; - q = cert->raw_serial; + if (cert->raw_skid) { + srlen = cert->raw_skid_size; + q = cert->raw_skid; + } else { + srlen = cert->raw_serial_size; + q = cert->raw_serial; + } if (srlen > 1 && *q == 0) { srlen--; q++; -- cgit v1.2.3 From f1b731dbc2530cab93fcfc5fcb18c9f3a100feeb Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Mon, 6 Oct 2014 15:21:05 +0100 Subject: KEYS: Restore partial ID matching functionality for asymmetric keys Bring back the functionality whereby an asymmetric key can be matched with a partial match on one of its IDs. Whilst we're at it, allow for the possibility of having an increased number of IDs. Reported-by: Dmitry Kasatkin Signed-off-by: Dmitry Kasatkin Signed-off-by: David Howells --- crypto/asymmetric_keys/asymmetric_keys.h | 3 -- crypto/asymmetric_keys/asymmetric_type.c | 70 ++++++++++++++++++++++++++------ crypto/asymmetric_keys/pkcs7_trust.c | 9 ++-- crypto/asymmetric_keys/x509_public_key.c | 18 +++++--- 4 files changed, 76 insertions(+), 24 deletions(-) (limited to 'crypto') diff --git a/crypto/asymmetric_keys/asymmetric_keys.h b/crypto/asymmetric_keys/asymmetric_keys.h index fd21ac28e0a0..f97330886d58 100644 --- a/crypto/asymmetric_keys/asymmetric_keys.h +++ b/crypto/asymmetric_keys/asymmetric_keys.h @@ -9,9 +9,6 @@ * 2 of the Licence, or (at your option) any later version. */ -extern bool asymmetric_match_key_ids(const struct asymmetric_key_ids *kids, - const struct asymmetric_key_id *match_id); - extern struct asymmetric_key_id *asymmetric_key_hex_to_key_id(const char *id); static inline diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index 29983cbb658d..052e944bb109 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -65,23 +65,44 @@ bool asymmetric_key_id_same(const struct asymmetric_key_id *kid1, } EXPORT_SYMBOL_GPL(asymmetric_key_id_same); +/** + * asymmetric_key_id_partial - Return true if two asymmetric keys IDs + * partially match + * @kid_1, @kid_2: The key IDs to compare + */ +bool asymmetric_key_id_partial(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 + (kid1->len - kid2->len), + kid2->data, kid2->len) == 0; +} +EXPORT_SYMBOL_GPL(asymmetric_key_id_partial); + /** * 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 + * @match: The match function to use */ -bool asymmetric_match_key_ids(const struct asymmetric_key_ids *kids, - const struct asymmetric_key_id *match_id) +static bool asymmetric_match_key_ids( + const struct asymmetric_key_ids *kids, + const struct asymmetric_key_id *match_id, + bool (*match)(const struct asymmetric_key_id *kid1, + const struct asymmetric_key_id *kid2)) { + int i; + 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; + for (i = 0; i < ARRAY_SIZE(kids->id); i++) + if (match(kids->id[i], match_id)) + return true; return false; } -EXPORT_SYMBOL_GPL(asymmetric_match_key_ids); /** * asymmetric_key_hex_to_key_id - Convert a hex string into a key ID. @@ -113,7 +134,7 @@ struct asymmetric_key_id *asymmetric_key_hex_to_key_id(const char *id) } /* - * Match asymmetric keys by ID. + * Match asymmetric keys by an exact match on an ID. */ static bool asymmetric_key_cmp(const struct key *key, const struct key_match_data *match_data) @@ -121,7 +142,21 @@ static bool asymmetric_key_cmp(const struct key *key, const struct asymmetric_key_ids *kids = asymmetric_key_ids(key); const struct asymmetric_key_id *match_id = match_data->preparsed; - return asymmetric_match_key_ids(kids, match_id); + return asymmetric_match_key_ids(kids, match_id, + asymmetric_key_id_same); +} + +/* + * Match asymmetric keys by a partial match on an IDs. + */ +static bool asymmetric_key_cmp_partial(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 asymmetric_match_key_ids(kids, match_id, + asymmetric_key_id_partial); } /* @@ -131,7 +166,8 @@ static bool asymmetric_key_cmp(const struct key *key, * There are some specifiers for matching key IDs rather than by the key * description: * - * "id:" - request a key by any available ID + * "id:" - find a key by partial match on any available ID + * "ex:" - find a key by exact match on any available ID * * These have to be searched by iteration rather than by direct lookup because * the key is hashed according to its description. @@ -141,6 +177,8 @@ static int asymmetric_key_match_preparse(struct key_match_data *match_data) struct asymmetric_key_id *match_id; const char *spec = match_data->raw_data; const char *id; + bool (*cmp)(const struct key *, const struct key_match_data *) = + asymmetric_key_cmp; if (!spec || !*spec) return -EINVAL; @@ -148,6 +186,11 @@ static int asymmetric_key_match_preparse(struct key_match_data *match_data) spec[1] == 'd' && spec[2] == ':') { id = spec + 3; + cmp = asymmetric_key_cmp_partial; + } else if (spec[0] == 'e' && + spec[1] == 'x' && + spec[2] == ':') { + id = spec + 3; } else { goto default_match; } @@ -157,7 +200,7 @@ static int asymmetric_key_match_preparse(struct key_match_data *match_data) return PTR_ERR(match_id); match_data->preparsed = match_id; - match_data->cmp = asymmetric_key_cmp; + match_data->cmp = cmp; match_data->lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE; return 0; @@ -251,6 +294,7 @@ 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]; + int i; pr_devel("==>%s()\n", __func__); @@ -259,8 +303,8 @@ static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep) module_put(subtype->owner); } if (kids) { - kfree(kids->id[0]); - kfree(kids->id[1]); + for (i = 0; i < ARRAY_SIZE(kids->id); i++) + kfree(kids->id[i]); kfree(kids); } kfree(prep->description); diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c index ae47be6128c4..1d29376072da 100644 --- a/crypto/asymmetric_keys/pkcs7_trust.c +++ b/crypto/asymmetric_keys/pkcs7_trust.c @@ -54,7 +54,8 @@ 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->id); + key = x509_request_asymmetric_key(trust_keyring, x509->id, + false); if (!IS_ERR(key)) { /* One of the X.509 certificates in the PKCS#7 message * is apparently the same as one we already trust. @@ -85,7 +86,8 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, * trusted keys. */ if (last && last->authority) { - key = x509_request_asymmetric_key(trust_keyring, last->authority); + key = x509_request_asymmetric_key(trust_keyring, last->authority, + false); if (!IS_ERR(key)) { x509 = last; pr_devel("sinfo %u: Root cert %u signer is key %x\n", @@ -100,7 +102,8 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, * the signed info directly. */ key = x509_request_asymmetric_key(trust_keyring, - sinfo->signing_cert_id); + sinfo->signing_cert_id, + false); if (!IS_ERR(key)) { pr_devel("sinfo %u: Direct signer is key %x\n", sinfo->index, key_serial(key)); diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index 8bffb06b2683..6ef54495be87 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -53,13 +53,15 @@ __setup("ca_keys=", ca_keys_setup); * x509_request_asymmetric_key - Request a key by X.509 certificate params. * @keyring: The keys to search. * @kid: The key ID. + * @partial: Use partial match if true, exact if false. * * 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 struct asymmetric_key_id *kid) + const struct asymmetric_key_id *kid, + bool partial) { key_ref_t key; char *id, *p; @@ -69,8 +71,13 @@ struct key *x509_request_asymmetric_key(struct key *keyring, if (!id) return ERR_PTR(-ENOMEM); - *p++ = 'i'; - *p++ = 'd'; + if (partial) { + *p++ = 'i'; + *p++ = 'd'; + } else { + *p++ = 'e'; + *p++ = 'x'; + } *p++ = ':'; p = bin2hex(p, kid->data, kid->len); *p = 0; @@ -207,10 +214,11 @@ static int x509_validate_trust(struct x509_certificate *cert, if (!trust_keyring) return -EOPNOTSUPP; - if (ca_keyid && !asymmetric_key_id_same(cert->authority, ca_keyid)) + if (ca_keyid && !asymmetric_key_id_partial(cert->authority, ca_keyid)) return -EPERM; - key = x509_request_asymmetric_key(trust_keyring, cert->authority); + key = x509_request_asymmetric_key(trust_keyring, cert->authority, + false); if (!IS_ERR(key)) { if (!use_builtin_keys || test_bit(KEY_FLAG_BUILTIN, &key->flags)) -- cgit v1.2.3 From 8dd609805b87923a700a2fad646390a58013cdb9 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Mon, 6 Oct 2014 16:52:12 +0100 Subject: KEYS: use swapped SKID for performing partial matching Earlier KEYS code used pure subject key identifiers (fingerprint) for searching keys. Latest merged code removed that and broke compatibility with integrity subsytem signatures and original format of module signatures. This patch returns back partial matching on SKID. Reported-by: Dmitry Kasatkin Signed-off-by: Dmitry Kasatkin Signed-off-by: David Howells --- crypto/asymmetric_keys/x509_cert_parser.c | 12 ++++++------ crypto/asymmetric_keys/x509_parser.h | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'crypto') diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c index 393706f33fa5..a668d90302d3 100644 --- a/crypto/asymmetric_keys/x509_cert_parser.c +++ b/crypto/asymmetric_keys/x509_cert_parser.c @@ -437,9 +437,9 @@ int x509_process_extension(void *context, size_t hdrlen, ctx->cert->raw_skid_size = vlen; ctx->cert->raw_skid = v; - kid = asymmetric_key_generate_id(v, vlen, - ctx->cert->raw_subject, - ctx->cert->raw_subject_size); + kid = asymmetric_key_generate_id(ctx->cert->raw_subject, + ctx->cert->raw_subject_size, + v, vlen); if (IS_ERR(kid)) return PTR_ERR(kid); ctx->cert->skid = kid; @@ -493,9 +493,9 @@ int x509_process_extension(void *context, size_t hdrlen, v += (sub + 2); } - kid = asymmetric_key_generate_id(v, vlen, - ctx->cert->raw_issuer, - ctx->cert->raw_issuer_size); + kid = asymmetric_key_generate_id(ctx->cert->raw_issuer, + ctx->cert->raw_issuer_size, + v, vlen); if (IS_ERR(kid)) return PTR_ERR(kid); pr_debug("authkeyid %*phN\n", kid->len, kid->data); diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h index 3f0f0f081621..3dfe6b5d6f0b 100644 --- a/crypto/asymmetric_keys/x509_parser.h +++ b/crypto/asymmetric_keys/x509_parser.h @@ -19,9 +19,9 @@ struct x509_certificate { struct public_key_signature sig; /* Signature parameters */ char *issuer; /* Name of certificate issuer */ char *subject; /* Name of certificate subject */ - 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 asymmetric_key_id *id; /* Serial number + issuer */ + struct asymmetric_key_id *skid; /* Subject + subjectKeyId (optional) */ + struct asymmetric_key_id *authority; /* Authority key identifier (optional) */ struct tm valid_from; struct tm valid_to; const void *tbs; /* Signed data */ -- cgit v1.2.3 From 7a224e783ad2b325977253139aede1f5f38b1778 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Mon, 6 Oct 2014 17:02:32 +0100 Subject: KEYS: strip 'id:' from ca_keyid The 'id:' prefix must be stripped for asymmetric_key_hex_to_key_id() to be able to process ca_keyid. Signed-off-by: Dmitry Kasatkin Signed-off-by: David Howells --- crypto/asymmetric_keys/x509_public_key.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'crypto') diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index 6ef54495be87..a6c42031628e 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -35,7 +35,7 @@ static int __init ca_keys_setup(char *str) if (strncmp(str, "id:", 3) == 0) { struct asymmetric_key_id *p; - p = asymmetric_key_hex_to_key_id(str); + p = asymmetric_key_hex_to_key_id(str + 3); if (p == ERR_PTR(-EINVAL)) pr_err("Unparsable hex string in ca_keys\n"); else if (!IS_ERR(p)) -- cgit v1.2.3 From d4016589855156b6afe5e9bc8ef85ce43f27e704 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Mon, 6 Oct 2014 17:25:45 +0100 Subject: KEYS: output last portion of fingerprint in /proc/keys Previous version of KEYS used to output last 4 bytes of fingerprint. Now it outputs 8 last bytes of raw subject, which does not make any visual meaning at all. This patch restores old behavior. Signed-off-by: Dmitry Kasatkin Signed-off-by: David Howells --- crypto/asymmetric_keys/asymmetric_type.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'crypto') diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index 052e944bb109..bcbbbd794e1d 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -233,14 +233,14 @@ static void asymmetric_key_describe(const struct key *key, struct seq_file *m) seq_puts(m, ": "); subtype->describe(key, m); - if (kids && kids->id[0]) { - kid = kids->id[0]; + if (kids && kids->id[1]) { + kid = kids->id[1]; seq_putc(m, ' '); n = kid->len; p = kid->data; - if (n > 8) { - p += n - 8; - n = 8; + if (n > 4) { + p += n - 4; + n = 4; } seq_printf(m, "%*phN", n, p); } -- cgit v1.2.3