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:21:06 +0200	[thread overview]
Message-ID: <YAFQcrop6GVa/CuJ@kernel.org> (raw)
In-Reply-To: <YAFQBT/pKw4PDenV@kernel.org>

On Fri, Jan 15, 2021 at 10:19:21AM +0200, Jarkko Sakkinen wrote:
> 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.

Also, a small usage example of the feature would be a great addition
to the commit message, as this extends the API. It's nice for better
understanding, and also later on for backtracking.

/Jarkko

  reply	other threads:[~2021-01-15  8:21 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
2021-01-15  8:21   ` Jarkko Sakkinen [this message]
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=YAFQcrop6GVa/CuJ@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).