keyrings.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Jarkko Sakkinen <jarkko@kernel.org>
To: Andrew Zaborowski <andrew.zaborowski@intel.com>
Cc: keyrings@vger.kernel.org, David Howells <dhowells@redhat.com>
Subject: Re: [PATCH v2] keys: X.509 public key issuer lookup without AKID
Date: Fri, 15 Jan 2021 10:19:17 +0200	[thread overview]
Message-ID: <YAFQBT/pKw4PDenV@kernel.org> (raw)
In-Reply-To: <20210114204035.2046219-1-andrew.zaborowski@intel.com>

On Thu, Jan 14, 2021 at 09:40:35PM +0100, Andrew Zaborowski wrote:
> There are non-root X.509 v3 certificates in use there that contain no
> Authority Key Identifier extension (RFC5280 section 4.2.1.1).  The
> asymmetric key's issuer key IDs 1 and 2 for those certificates are
> generated from the AKID data so current code has no way to look up the
> issuer certificate for verification.  Add a third ID blob to the arrays
> in both asymmetric_key_ids (for certficate subject) and in the
> public_keys_signature's auth_ids (for issuer lookup), using just raw
> subject and issuer DNs from the certificate.  There's no other place
> this data is currently saved that could be used in find_asymmetric_key.
> Adapt asymmetric_key_ids() and the callers to use the third ID for
> lookups when none of the other two are available.  Attempt to keep the
> logic intact when they are, to minimise behaviour changes.  Adapt the
> restrict functions' NULL-checks to include that ID too.  The lookup
> logic in pkcs7_verify.c is not modified, the AKID extensions are still
> required there.
> 
> I believe this implements what (2) in the struct asymmetric_key_id
> comment (include/keys/asymmetric-type.h) talks about already, so that
> comment isn't modified.  It's also how "openssl verify" looks up issuer
> certificates without the AKID available.

Preferably no language in persons.

> Internally the search specifier string generated for the key lookup in
> find_asymmetric_key() uses a new prefix that tells
> asymmetric_key_match_preparse to only match the data against the raw
> Distinguished Name in the third ID and shouldn't conflict with search
> specifiers already in use.
> 
> Lookups are unambiguous only provided that the CAs respect the condition
> in RFC5280 4.2.1.1 that the AKID may only be omitted if the CA uses a
> single signing key.
> 
> Signed-off-by: Andrew Zaborowski <andrew.zaborowski@intel.com>
> ---
> v2:
>  - focus the commit message on the implementation
>  - shorten the prefix used in find_asymmetric_key
>  - clarify find_asymmetric_key comment
> 
>  crypto/asymmetric_keys/asymmetric_type.c  | 52 +++++++++++++++++------
>  crypto/asymmetric_keys/pkcs7_trust.c      |  6 +--
>  crypto/asymmetric_keys/restrict.c         | 48 ++++++++++++---------
>  crypto/asymmetric_keys/x509_cert_parser.c | 10 +++++
>  crypto/asymmetric_keys/x509_public_key.c  | 10 +++++
>  include/crypto/public_key.h               |  2 +-
>  include/keys/asymmetric-type.h            |  3 +-
>  7 files changed, 95 insertions(+), 36 deletions(-)
> 
> diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c
> index 33e77d846ca..e411f2a7a84 100644
> --- a/crypto/asymmetric_keys/asymmetric_type.c
> +++ b/crypto/asymmetric_keys/asymmetric_type.c
> @@ -36,8 +36,11 @@ static DECLARE_RWSEM(asymmetric_key_parsers_sem);
>   * find_asymmetric_key - Find a key by ID.
>   * @keyring: The keys to search.
>   * @id_0: The first ID to look for or NULL.
> - * @id_1: The second ID to look for or NULL.
> - * @partial: Use partial match if true, exact if false.
> + * @id_1: The second ID to look for or NULL, matched together with @id_0
> + * against @keyring keys' id[0] and id[1].
> + * @id_2: The fallback ID to match against @keyring keys' id[2] if both of the
> + * other IDs are NULL.
> + * @partial: Use partial match for @id_0 and @id_1 if true, exact if false.
>   *
>   * Find a key in the given keyring by identifier.  The preferred identifier is
>   * the id_0 and the fallback identifier is the id_1.  If both are given, the
> @@ -46,6 +49,7 @@ static DECLARE_RWSEM(asymmetric_key_parsers_sem);
>  struct key *find_asymmetric_key(struct key *keyring,
>  				const struct asymmetric_key_id *id_0,
>  				const struct asymmetric_key_id *id_1,
> +				const struct asymmetric_key_id *id_2,
>  				bool partial)
>  {
>  	struct key *key;
> @@ -54,14 +58,17 @@ struct key *find_asymmetric_key(struct key *keyring,
>  	char *req, *p;
>  	int len;
>  
> -	BUG_ON(!id_0 && !id_1);
> +	BUG_ON(!id_0 && !id_1 && !id_2);
>  
>  	if (id_0) {
>  		lookup = id_0->data;
>  		len = id_0->len;
> -	} else {
> +	} else if (id_1) {
>  		lookup = id_1->data;
>  		len = id_1->len;
> +	} else {
> +		lookup = id_2->data;
> +		len = id_2->len;
>  	}
>  
>  	/* Construct an identifier "id:<keyid>". */
> @@ -69,7 +76,10 @@ struct key *find_asymmetric_key(struct key *keyring,
>  	if (!req)
>  		return ERR_PTR(-ENOMEM);
>  
> -	if (partial) {
> +	if (!id_0 && !id_1) {
> +		*p++ = 'd';
> +		*p++ = 'n';
> +	} else if (partial) {
>  		*p++ = 'i';
>  		*p++ = 'd';
>  	} else {
> @@ -183,8 +193,8 @@ bool asymmetric_key_id_partial(const struct asymmetric_key_id *kid1,
>  EXPORT_SYMBOL_GPL(asymmetric_key_id_partial);
>  
>  /**
> - * asymmetric_match_key_ids - Search asymmetric key IDs
> - * @kids: The list of key IDs to check
> + * asymmetric_match_key_ids - Search asymmetric key IDs 1 & 2
> + * @kids: The pair of key IDs to check
>   * @match_id: The key ID we're looking for
>   * @match: The match function to use
>   */
> @@ -198,7 +208,7 @@ static bool asymmetric_match_key_ids(
>  
>  	if (!kids || !match_id)
>  		return false;
> -	for (i = 0; i < ARRAY_SIZE(kids->id); i++)
> +	for (i = 0; i < 2; i++)
>  		if (match(kids->id[i], match_id))
>  			return true;
>  	return false;

Why are key ID 2 and key ID 3 handled differently? They are both
optional.

> @@ -242,7 +252,7 @@ struct asymmetric_key_id *asymmetric_key_hex_to_key_id(const char *id)
>  }
>  
>  /*
> - * Match asymmetric keys by an exact match on an ID.
> + * Match asymmetric keys by an exact match on one of the first two IDs.
>   */
>  static bool asymmetric_key_cmp(const struct key *key,
>  			       const struct key_match_data *match_data)
> @@ -255,7 +265,7 @@ static bool asymmetric_key_cmp(const struct key *key,
>  }
>  
>  /*
> - * Match asymmetric keys by a partial match on an IDs.
> + * Match asymmetric keys by a partial match on one of the first two IDs.
>   */
>  static bool asymmetric_key_cmp_partial(const struct key *key,
>  				       const struct key_match_data *match_data)
> @@ -267,6 +277,18 @@ static bool asymmetric_key_cmp_partial(const struct key *key,
>  					asymmetric_key_id_partial);
>  }
>  
> +/*
> + * Match asymmetric keys by an exact match on the third IDs.
> + */
> +static bool asymmetric_key_cmp_name(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 kids && asymmetric_key_id_same(kids->id[2], match_id);
> +}
> +
>  /*
>   * Preparse the match criterion.  If we don't set lookup_type and cmp,
>   * the default will be an exact match on the key description.
> @@ -274,8 +296,9 @@ static bool asymmetric_key_cmp_partial(const struct key *key,
>   * There are some specifiers for matching key IDs rather than by the key
>   * description:
>   *
> - *	"id:<id>" - find a key by partial match on any available ID
> - *	"ex:<id>" - find a key by exact match on any available ID
> + *	"id:<id>" - find a key by partial match on one of the first two IDs
> + *	"ex:<id>" - find a key by exact match on one of the first two IDs
> + *	"dn:<id>" - find a key by exact match on the third ID
>   *
>   * These have to be searched by iteration rather than by direct lookup because
>   * the key is hashed according to its description.
> @@ -299,6 +322,11 @@ static int asymmetric_key_match_preparse(struct key_match_data *match_data)
>  		   spec[1] == 'x' &&
>  		   spec[2] == ':') {
>  		id = spec + 3;
> +	} else if (spec[0] == 'd' &&
> +		   spec[1] == 'n' &&
> +		   spec[2] == ':') {
> +		id = spec + 3;
> +		cmp = asymmetric_key_cmp_name;
>  	} else {
>  		goto default_match;
>  	}
> diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c
> index 61af3c4d82c..998ba0e2ffb 100644
> --- a/crypto/asymmetric_keys/pkcs7_trust.c
> +++ b/crypto/asymmetric_keys/pkcs7_trust.c
> @@ -48,7 +48,7 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
>  		 * keys.
>  		 */
>  		key = find_asymmetric_key(trust_keyring,
> -					  x509->id, x509->skid, false);
> +					  x509->id, x509->skid, NULL, 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.
> @@ -82,7 +82,7 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
>  		key = find_asymmetric_key(trust_keyring,
>  					  last->sig->auth_ids[0],
>  					  last->sig->auth_ids[1],
> -					  false);
> +					  NULL, false);
>  		if (!IS_ERR(key)) {
>  			x509 = last;
>  			pr_devel("sinfo %u: Root cert %u signer is key %x\n",
> @@ -97,7 +97,7 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
>  	 * the signed info directly.
>  	 */
>  	key = find_asymmetric_key(trust_keyring,
> -				  sinfo->sig->auth_ids[0], NULL, false);
> +				  sinfo->sig->auth_ids[0], NULL, NULL, 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/restrict.c b/crypto/asymmetric_keys/restrict.c
> index 77ebebada29..f77875ec942 100644
> --- a/crypto/asymmetric_keys/restrict.c
> +++ b/crypto/asymmetric_keys/restrict.c
> @@ -87,7 +87,7 @@ int restrict_link_by_signature(struct key *dest_keyring,
>  	sig = payload->data[asym_auth];
>  	if (!sig)
>  		return -ENOPKG;
> -	if (!sig->auth_ids[0] && !sig->auth_ids[1])
> +	if (!sig->auth_ids[0] && !sig->auth_ids[1] && !sig->auth_ids[2])
>  		return -ENOKEY;
>  
>  	if (ca_keyid && !asymmetric_key_id_partial(sig->auth_ids[1], ca_keyid))
> @@ -96,7 +96,7 @@ int restrict_link_by_signature(struct key *dest_keyring,
>  	/* See if we have a key that signed this one. */
>  	key = find_asymmetric_key(trust_keyring,
>  				  sig->auth_ids[0], sig->auth_ids[1],
> -				  false);
> +				  sig->auth_ids[2], false);
>  	if (IS_ERR(key))
>  		return -ENOKEY;
>  
> @@ -108,11 +108,11 @@ int restrict_link_by_signature(struct key *dest_keyring,
>  	return ret;
>  }
>  
> -static bool match_either_id(const struct asymmetric_key_ids *pair,
> +static bool match_either_id(const struct asymmetric_key_id **pair,
>  			    const struct asymmetric_key_id *single)
>  {
> -	return (asymmetric_key_id_same(pair->id[0], single) ||
> -		asymmetric_key_id_same(pair->id[1], single));
> +	return (asymmetric_key_id_same(pair[0], single) ||
> +		asymmetric_key_id_same(pair[1], single));
>  }
>  
>  static int key_or_keyring_common(struct key *dest_keyring,
> @@ -140,20 +140,22 @@ static int key_or_keyring_common(struct key *dest_keyring,
>  	sig = payload->data[asym_auth];
>  	if (!sig)
>  		return -ENOPKG;
> -	if (!sig->auth_ids[0] && !sig->auth_ids[1])
> +	if (!sig->auth_ids[0] && !sig->auth_ids[1] && !sig->auth_ids[2])
>  		return -ENOKEY;
>  
>  	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);
> +						  sig->auth_ids[1],
> +						  sig->auth_ids[2], false);
>  			if (IS_ERR(key))
>  				key = NULL;
>  		} else if (trusted->type == &key_type_asymmetric) {
> -			const struct asymmetric_key_ids *signer_ids;
> +			const struct asymmetric_key_id **signer_ids;
>  
> -			signer_ids = asymmetric_key_ids(trusted);
> +			signer_ids = (const struct asymmetric_key_id **)
> +				asymmetric_key_ids(trusted)->id;
>  
>  			/*
>  			 * The auth_ids come from the candidate key (the
> @@ -164,22 +166,29 @@ static int key_or_keyring_common(struct key *dest_keyring,
>  			 * 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.
> +			 * The first auth_id is the preferred id, 2nd and
> +			 * 3rd are the fallbacks. If excatly one of
> +			 * auth_ids[0] and auth_ids[1] is present, it may
> +			 * match either signer_ids[0] or signed_ids[1].
> +			 * If both are present the first one may match
> +			 * either signed_id but the second one must match
> +			 * the second signer_id. If neither of them is
> +			 * available, auth_ids[2] is matched against
> +			 * signer_ids[2] as a fallback.
>  			 */
> -			if (!sig->auth_ids[0] || !sig->auth_ids[1]) {
> +			if (!sig->auth_ids[0] && !sig->auth_ids[1]) {
> +				if (asymmetric_key_id_same(signer_ids[2],
> +							   sig->auth_ids[2]))
> +					key = __key_get(trusted);
> +
> +			} else if (!sig->auth_ids[0] || !sig->auth_ids[1]) {
>  				const struct asymmetric_key_id *auth_id;
>  
>  				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],
> +			} else if (asymmetric_key_id_same(signer_ids[1],
>  							  sig->auth_ids[1]) &&
>  				   match_either_id(signer_ids,
>  						   sig->auth_ids[0])) {
> @@ -193,7 +202,8 @@ static int key_or_keyring_common(struct key *dest_keyring,
>  	if (check_dest && !key) {
>  		/* 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);
> +					  sig->auth_ids[1], sig->auth_ids[2],
> +					  false);
>  		if (IS_ERR(key))
>  			key = NULL;
>  	}
> diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c
> index 52c9b455fc7..59dfffa77cf 100644
> --- a/crypto/asymmetric_keys/x509_cert_parser.c
> +++ b/crypto/asymmetric_keys/x509_cert_parser.c
> @@ -415,8 +415,18 @@ int x509_note_issuer(void *context, size_t hdrlen,
>  		     const void *value, size_t vlen)
>  {
>  	struct x509_parse_context *ctx = context;
> +	struct asymmetric_key_id *kid;
> +
>  	ctx->cert->raw_issuer = value;
>  	ctx->cert->raw_issuer_size = vlen;
> +
> +	if (!ctx->cert->sig->auth_ids[2]) {
> +		kid = asymmetric_key_generate_id(value, vlen, "", 0);
> +		if (IS_ERR(kid))
> +			return PTR_ERR(kid);
> +		ctx->cert->sig->auth_ids[2] = kid;
> +	}
> +
>  	return x509_fabricate_name(ctx, hdrlen, tag, &ctx->cert->issuer, vlen);
>  }
>  
> diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c
> index ae450eb8be1..a95cc351518 100644
> --- a/crypto/asymmetric_keys/x509_public_key.c
> +++ b/crypto/asymmetric_keys/x509_public_key.c
> @@ -221,6 +221,13 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
>  		goto error_free_desc;
>  	kids->id[0] = cert->id;
>  	kids->id[1] = cert->skid;
> +	kids->id[2] = asymmetric_key_generate_id(cert->raw_subject,
> +						 cert->raw_subject_size,
> +						 "", 0);
> +	if (IS_ERR(kids->id[2])) {
> +		ret = PTR_ERR(kids->id[2]);
> +		goto error_free_kids;
> +	}
>  
>  	/* We're pinning the module by being linked against it */
>  	__module_get(public_key_subtype.owner);
> @@ -237,8 +244,11 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
>  	cert->skid = NULL;
>  	cert->sig = NULL;
>  	desc = NULL;
> +	kids = NULL;
>  	ret = 0;
>  
> +error_free_kids:
> +	kfree(kids);
>  error_free_desc:
>  	kfree(desc);
>  error_free_cert:
> diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h
> index 948c5203ca9..4819e63a772 100644
> --- a/include/crypto/public_key.h
> +++ b/include/crypto/public_key.h
> @@ -37,7 +37,7 @@ extern void public_key_free(struct public_key *key);
>   * Public key cryptography signature data
>   */
>  struct public_key_signature {
> -	struct asymmetric_key_id *auth_ids[2];
> +	struct asymmetric_key_id *auth_ids[3];
>  	u8 *s;			/* Signature */
>  	u32 s_size;		/* Number of bytes in signature */
>  	u8 *digest;
> diff --git a/include/keys/asymmetric-type.h b/include/keys/asymmetric-type.h
> index a29d3ff2e7e..344ab9f178d 100644
> --- a/include/keys/asymmetric-type.h
> +++ b/include/keys/asymmetric-type.h
> @@ -53,7 +53,7 @@ struct asymmetric_key_id {
>  };
>  
>  struct asymmetric_key_ids {
> -	void		*id[2];
> +	void		*id[3];
>  };
>  
>  extern bool asymmetric_key_id_same(const struct asymmetric_key_id *kid1,
> @@ -75,6 +75,7 @@ const struct asymmetric_key_ids *asymmetric_key_ids(const struct key *key)
>  extern struct key *find_asymmetric_key(struct key *keyring,
>  				       const struct asymmetric_key_id *id_0,
>  				       const struct asymmetric_key_id *id_1,
> +				       const struct asymmetric_key_id *id_2,
>  				       bool partial);
>  
>  /*
> -- 
> 2.27.0
> 
> 

This gives checkpatch error but is not due to this patch. I sent a
bug fix (and also cc'd you to that). I'll apply that to my tree as
soon as I get it ack'd first.

Overally, I don't understand differianting the semantics. Emphasis
on that I haven't been involved with asymmetric keys implementation,
so it is entirely possible that there are good reasons to do this
but the commit message did not shed any light on this.

/Jarkko

  reply	other threads:[~2021-01-15  8:20 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-01-14 20:40 [PATCH v2] keys: X.509 public key issuer lookup without AKID Andrew Zaborowski
2021-01-15  8:19 ` Jarkko Sakkinen [this message]
2021-01-15  8:21   ` Jarkko Sakkinen
2021-01-15 10:40   ` Andrew Zaborowski
2021-01-15 10:44     ` Andrew Zaborowski
2021-01-20  1:54     ` Jarkko Sakkinen
2021-01-21 11:31       ` Andrew Zaborowski
2021-01-21 15:22         ` Jarkko Sakkinen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=YAFQBT/pKw4PDenV@kernel.org \
    --to=jarkko@kernel.org \
    --cc=andrew.zaborowski@intel.com \
    --cc=dhowells@redhat.com \
    --cc=keyrings@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).