From patchwork Tue Nov 29 00:44:32 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mat Martineau X-Patchwork-Id: 9450821 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 8FB1F600CB for ; Tue, 29 Nov 2016 00:44:49 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 836B2275A2 for ; Tue, 29 Nov 2016 00:44:49 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 767B027D64; Tue, 29 Nov 2016 00:44:49 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 84D65275A2 for ; Tue, 29 Nov 2016 00:44:48 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755044AbcK2Aoq (ORCPT ); Mon, 28 Nov 2016 19:44:46 -0500 Received: from mga09.intel.com ([134.134.136.24]:39300 "EHLO mga09.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755088AbcK2Aom (ORCPT ); Mon, 28 Nov 2016 19:44:42 -0500 Received: from orsmga004.jf.intel.com ([10.7.209.38]) by orsmga102.jf.intel.com with ESMTP; 28 Nov 2016 16:44:38 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.31,565,1473145200"; d="scan'208";a="35292307" Received: from mjmartin-nuc01.wa.intel.com ([10.232.97.135]) by orsmga004.jf.intel.com with ESMTP; 28 Nov 2016 16:44:37 -0800 From: Mat Martineau To: keyrings@vger.kernel.org, linux-security-module@vger.kernel.org, dhowells@redhat.com Cc: Mat Martineau , zohar@linux.vnet.ibm.com Subject: [PATCH v10 11/11] KEYS: Keyring restrict method with chaining Date: Mon, 28 Nov 2016 16:44:32 -0800 Message-Id: <20161129004432.17926-12-mathew.j.martineau@linux.intel.com> X-Mailer: git-send-email 2.10.2 In-Reply-To: <20161129004432.17926-1-mathew.j.martineau@linux.intel.com> References: <20161129004432.17926-1-mathew.j.martineau@linux.intel.com> Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: X-Virus-Scanned: ClamAV using ClamSMTP Add a restrict_link_by_key_or_keyring_chain link restriction that also searches for signing keys in the destination keyring, in addition to the signing key or keyring designated when the destination keyring was created. Userspace enables this behavior by including the "chain" option in the keyring payload. Signed-off-by: Mat Martineau --- Documentation/crypto/asymmetric-keys.txt | 9 +- crypto/asymmetric_keys/asymmetric_type.c | 28 +++++- crypto/asymmetric_keys/restrict.c | 165 ++++++++++++++++++++++++------- include/crypto/public_key.h | 5 + 4 files changed, 163 insertions(+), 44 deletions(-) diff --git a/Documentation/crypto/asymmetric-keys.txt b/Documentation/crypto/asymmetric-keys.txt index fc67969..c2bac5a 100644 --- a/Documentation/crypto/asymmetric-keys.txt +++ b/Documentation/crypto/asymmetric-keys.txt @@ -343,7 +343,7 @@ Several restriction methods are available: (3) Restrict using a separate key or keyring - Options used when creating the keyring: - - restrict=asymmetric:key_or_keyring: + - restrict=asymmetric:key_or_keyring:[:chain] Whenever a key link is requested, the link will only succeed if the key being linked is signed by one of the designated keys. This key may be @@ -351,6 +351,13 @@ Several restriction methods are available: a group of keys may be searched for the signing key by providing the serial number for a keyring. + When the "chain" option is provided at the end of the string, the keys + within the destination keyring will also be searched for signing keys. + This allows for verification of certificate chains by adding each + cert in order (starting closest to the root) to one keyring. In addition, + using key serial number 0 with the "chain" option will assume the first + key added to the keyring is the root (its signature will not be checked). + In all of these cases, if the signing key is found the signature of the key to be linked will be verified using the signing key. The requested key is added to the keyring only if the signature is successfully verified. -ENOKEY is diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index 4940eef..7281b7e 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -498,17 +498,35 @@ static struct key_restriction *asymmetric_lookup_restrict(char *restriction) restrict_method = strsep(&restriction, ":"); if ((strcmp(restrict_method, "key_or_keyring") == 0) && restriction) { + const char *key_text; key_serial_t serial; struct key *key; + key_restrict_link_func_t link_fn = + restrict_link_by_key_or_keyring; + bool allow_null_key = false; - if (kstrtos32(restriction, 0, &serial) < 0) - return ERR_PTR(-EINVAL); + key_text = strsep(&restriction, ":"); + + if (restriction) { + if (strcmp(restriction, "chain") != 0) + return ERR_PTR(-EINVAL); + + link_fn = restrict_link_by_key_or_keyring_chain; + allow_null_key = true; + } - key = key_lookup(serial); - if (IS_ERR(key)) + if (kstrtos32(key_text, 0, &serial) < 0) return ERR_PTR(-EINVAL); - return asymmetric_restriction_alloc(restrict_link_by_key_or_keyring, + if ((serial == 0) && allow_null_key) { + key = NULL; + } else { + key = key_lookup(serial); + if (IS_ERR(key)) + return ERR_PTR(-EINVAL); + } + + return asymmetric_restriction_alloc(link_fn, asymmetric_free_key_data, key); } diff --git a/crypto/asymmetric_keys/restrict.c b/crypto/asymmetric_keys/restrict.c index b61002b..f5581dc 100644 --- a/crypto/asymmetric_keys/restrict.c +++ b/crypto/asymmetric_keys/restrict.c @@ -110,31 +110,20 @@ int restrict_link_by_signature(struct key *dest_keyring, return ret; } -/** - * restrict_link_by_key_or_keyring - Restrict additions to a ring of public - * keys using the restrict_key information stored in the ring. - * @dest_keyring: Keyring being linked to. - * @type: The type of key being added. - * @payload: The payload of the new key. - * @data: A key or ring of keys that can be used to vouch for the new cert. - * - * Check the new certificate only against the key or keys passed in the data - * parameter. If one of those is the signing key and validates the new - * certificate, then mark the new certificate as being ok to link. - * - * Returns 0 if the new certificate was accepted, -ENOKEY if we - * couldn't find a matching parent certificate in the trusted list, - * -EKEYREJECTED if the signature check fails, and some other error if - * there is a matching certificate but the signature check cannot be - * performed. - */ -int restrict_link_by_key_or_keyring(struct key *dest_keyring, - const struct key_type *type, - const union key_payload *payload, - void *data) +static bool match_either_id(const struct asymmetric_key_ids *pair, + const struct asymmetric_key_id *single) +{ + return (asymmetric_key_id_same(pair->id[0], single) || + asymmetric_key_id_same(pair->id[1], single)); +} + +static int key_or_keyring_common(struct key *dest_keyring, + const struct key_type *type, + const union key_payload *payload, + void *data, bool check_dest) { const struct public_key_signature *sig; - struct key *key; + struct key *key = NULL; int ret; struct key *trusted = (struct key *) data; @@ -145,7 +134,7 @@ int restrict_link_by_key_or_keyring(struct key *dest_keyring, else if (dest_keyring->type != &key_type_keyring) return -EOPNOTSUPP; - if (!trusted) + if (!trusted && !check_dest) return -ENOKEY; if (type != &key_type_asymmetric) @@ -155,25 +144,73 @@ int restrict_link_by_key_or_keyring(struct key *dest_keyring, if (!sig->auth_ids[0] && !sig->auth_ids[1]) return -ENOKEY; - if (trusted->type == &key_type_keyring) { - /* See if we have a key that signed this one. */ - key = find_asymmetric_key(trusted, sig->auth_ids[0], - sig->auth_ids[1], false); - if (IS_ERR(key)) - return -ENOKEY; - } else if (trusted->type == &key_type_asymmetric) { - const struct asymmetric_key_ids *kids; + if (trusted) { + if (trusted->type == &key_type_keyring) { + /* See if we have a key that signed this one. */ + key = find_asymmetric_key(trusted, sig->auth_ids[0], + sig->auth_ids[1], false); + if (IS_ERR(key)) + key = NULL; + } else if (trusted->type == &key_type_asymmetric) { + const struct asymmetric_key_ids *signer_ids; - kids = asymmetric_key_ids(trusted); + signer_ids = asymmetric_key_ids(trusted); - if (!asymmetric_key_id_same(kids->id[1], sig->auth_ids[0])) - return -ENOKEY; + /* + * The auth_ids come from the candidate key (the + * one that is being considered for addition to + * dest_keyring) and identify the key that was + * used to sign. + * + * The signer_ids are identifiers for the + * signing key specified for dest_keyring. + * + * The first auth_id is the preferred id, and + * the second is the fallback. If only one + * auth_id is present, it may match against + * either signer_id. If two auth_ids are + * present, the first auth_id must match one + * signer_id and the second auth_id must match + * the second signer_id. + */ + if (!sig->auth_ids[0] || !sig->auth_ids[1]) { + const struct asymmetric_key_id *auth_id; - key = __key_get(trusted); - } else { - return -EOPNOTSUPP; + auth_id = sig->auth_ids[0] ?: sig->auth_ids[1]; + if (match_either_id(signer_ids, auth_id)) + key = __key_get(trusted); + + } else if (asymmetric_key_id_same(signer_ids->id[1], + sig->auth_ids[1]) && + match_either_id(signer_ids, + sig->auth_ids[0])) { + key = __key_get(trusted); + } + } else { + return -EOPNOTSUPP; + } } + if (check_dest && !key) { + /* + * When the keyring does not have a signing key or + * keyring defined, adding a key to an empty dest_keyring + * is allowed. + */ + if (!trusted && + !dest_keyring->type->read(dest_keyring, NULL, 0)) + return 0; + + /* See if the destination has a key that signed this one. */ + key = find_asymmetric_key(dest_keyring, sig->auth_ids[0], + sig->auth_ids[1], false); + if (IS_ERR(key)) + key = NULL; + } + + if (!key) + return -ENOKEY; + ret = key_validate(key); if (ret == 0) ret = verify_signature(key, sig); @@ -181,3 +218,55 @@ int restrict_link_by_key_or_keyring(struct key *dest_keyring, key_put(key); return ret; } + +/** + * restrict_link_by_key_or_keyring - Restrict additions to a ring of public + * keys using the restrict_key information stored in the ring. + * @dest_keyring: Keyring being linked to. + * @type: The type of key being added. + * @payload: The payload of the new key. + * @data: A key or ring of keys that can be used to vouch for the new cert. + * + * Check the new certificate only against the key or keys passed in the data + * parameter. If one of those is the signing key and validates the new + * certificate, then mark the new certificate as being ok to link. + * + * Returns 0 if the new certificate was accepted, -ENOKEY if we + * couldn't find a matching parent certificate in the trusted list, + * -EKEYREJECTED if the signature check fails, and some other error if + * there is a matching certificate but the signature check cannot be + * performed. + */ +int restrict_link_by_key_or_keyring(struct key *dest_keyring, + const struct key_type *type, + const union key_payload *payload, + void *data) +{ + return key_or_keyring_common(dest_keyring, type, payload, data, false); +} + +/** + * restrict_link_by_key_or_keyring_chain - Restrict additions to a ring of + * public keys using the restrict_key information stored in the ring. + * @dest_keyring: Keyring being linked to. + * @type: The type of key being added. + * @payload: The payload of the new key. + * @data: A key or ring of keys that can be used to vouch for the new cert. + * + * Check the new certificate only against the key or keys passed in the data + * parameter. If one of those is the signing key and validates the new + * certificate, then mark the new certificate as being ok to link. + * + * Returns 0 if the new certificate was accepted, -ENOKEY if we + * couldn't find a matching parent certificate in the trusted list, + * -EKEYREJECTED if the signature check fails, and some other error if + * there is a matching certificate but the signature check cannot be + * performed. + */ +int restrict_link_by_key_or_keyring_chain(struct key *dest_keyring, + const struct key_type *type, + const union key_payload *payload, + void *data) +{ + return key_or_keyring_common(dest_keyring, type, payload, data, true); +} diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index 7f7facc..24e5bef 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -60,6 +60,11 @@ extern int restrict_link_by_key_or_keyring(struct key *trust_keyring, const union key_payload *payload, void *data); +extern int restrict_link_by_key_or_keyring_chain(struct key *trust_keyring, + const struct key_type *type, + const union key_payload *payload, + void *data); + extern int verify_signature(const struct key *key, const struct public_key_signature *sig);