Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

keys: X.509 public key issuer lookup without AKID

There are non-root X.509 v3 certificates in use out there that contain
no Authority Key Identifier extension (RFC5280 section 4.2.1.1). For
trust verification purposes the kernel asymmetric key type keeps two
struct asymmetric_key_id instances that the key can be looked up by,
and another two to look up the key's issuer. The x509 public key type
and the PKCS7 type generate them from the SKID and AKID extensions in
the certificate. In effect current code has no way to look up the
issuer certificate for verification without the AKID.

To remedy this, add a third asymmetric_key_id blob to the arrays in
both asymmetric_key_id's (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. Adapt
asymmetric_key_ids() and its 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. Do not modify
the lookup logic in pkcs7_verify.c, the AKID extensions are still
required there.

Internally use a new "dn:" prefix to the search specifier string
generated for the key lookup in find_asymmetric_key(). This tells
asymmetric_key_match_preparse to only match the data against the raw
DN in the third ID and shouldn't conflict with search specifiers
already in use.

In effect implement what (2) in the struct asymmetric_key_id comment
(include/keys/asymmetric-type.h) is probably talking about already, so
do not modify that comment. It is also how "openssl verify" looks up
issuer certificates without the AKID available. Lookups by the raw
DN 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.

The following is an example of two things that this change enables.
A self-signed ceritficate is generated following the example from
https://letsencrypt.org/docs/certificates-for-localhost/, and can be
looked up by an identifier and verified against itself by linking to a
restricted keyring -- both things not possible before due to the missing
AKID extension:

$ openssl req -x509 -out localhost.crt -outform DER -keyout localhost.key \
-newkey rsa:2048 -nodes -sha256 \
-subj '/CN=localhost' -extensions EXT -config <( \
echo -e "[dn]\nCN=localhost\n[req]\ndistinguished_name = dn\n[EXT]\n" \
"subjectAltName=DNS:localhost\nkeyUsage=digitalSignature\n" \
"extendedKeyUsage=serverAuth")
$ keyring=`keyctl newring test @u`
$ trusted=`keyctl padd asymmetric trusted $keyring < localhost.crt`; \
echo $trusted
39726322
$ keyctl search $keyring asymmetric dn:3112301006035504030c096c6f63616c686f7374
39726322
$ keyctl restrict_keyring $keyring asymmetric key_or_keyring:$trusted
$ keyctl padd asymmetric verified $keyring < localhost.crt

Signed-off-by: Andrew Zaborowski <andrew.zaborowski@intel.com>
Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
Acked-by: Jarkko Sakkinen <jarkko@kernel.org>
Acked-by: David Howells <dhowells@redhat.com>
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>

authored by

Andrew Zaborowski and committed by
Jarkko Sakkinen
7d30198e e96d5282

+99 -37
+44 -13
crypto/asymmetric_keys/asymmetric_type.c
··· 36 36 * find_asymmetric_key - Find a key by ID. 37 37 * @keyring: The keys to search. 38 38 * @id_0: The first ID to look for or NULL. 39 - * @id_1: The second ID to look for or NULL. 40 - * @partial: Use partial match if true, exact if false. 39 + * @id_1: The second ID to look for or NULL, matched together with @id_0 40 + * against @keyring keys' id[0] and id[1]. 41 + * @id_2: The fallback ID to match against @keyring keys' id[2] if both of the 42 + * other IDs are NULL. 43 + * @partial: Use partial match for @id_0 and @id_1 if true, exact if false. 41 44 * 42 45 * Find a key in the given keyring by identifier. The preferred identifier is 43 46 * the id_0 and the fallback identifier is the id_1. If both are given, the 44 - * lookup is by the former, but the latter must also match. 47 + * former is matched (exactly or partially) against either of the sought key's 48 + * identifiers and the latter must match the found key's second identifier 49 + * exactly. If both are missing, id_2 must match the sought key's third 50 + * identifier exactly. 45 51 */ 46 52 struct key *find_asymmetric_key(struct key *keyring, 47 53 const struct asymmetric_key_id *id_0, 48 54 const struct asymmetric_key_id *id_1, 55 + const struct asymmetric_key_id *id_2, 49 56 bool partial) 50 57 { 51 58 struct key *key; ··· 61 54 char *req, *p; 62 55 int len; 63 56 64 - BUG_ON(!id_0 && !id_1); 57 + WARN_ON(!id_0 && !id_1 && !id_2); 65 58 66 59 if (id_0) { 67 60 lookup = id_0->data; 68 61 len = id_0->len; 69 - } else { 62 + } else if (id_1) { 70 63 lookup = id_1->data; 71 64 len = id_1->len; 65 + } else { 66 + lookup = id_2->data; 67 + len = id_2->len; 72 68 } 73 69 74 70 /* Construct an identifier "id:<keyid>". */ ··· 79 69 if (!req) 80 70 return ERR_PTR(-ENOMEM); 81 71 82 - if (partial) { 72 + if (!id_0 && !id_1) { 73 + *p++ = 'd'; 74 + *p++ = 'n'; 75 + } else if (partial) { 83 76 *p++ = 'i'; 84 77 *p++ = 'd'; 85 78 } else { ··· 198 185 EXPORT_SYMBOL_GPL(asymmetric_key_id_partial); 199 186 200 187 /** 201 - * asymmetric_match_key_ids - Search asymmetric key IDs 202 - * @kids: The list of key IDs to check 188 + * asymmetric_match_key_ids - Search asymmetric key IDs 1 & 2 189 + * @kids: The pair of key IDs to check 203 190 * @match_id: The key ID we're looking for 204 191 * @match: The match function to use 205 192 */ ··· 213 200 214 201 if (!kids || !match_id) 215 202 return false; 216 - for (i = 0; i < ARRAY_SIZE(kids->id); i++) 203 + for (i = 0; i < 2; i++) 217 204 if (match(kids->id[i], match_id)) 218 205 return true; 219 206 return false; ··· 257 244 } 258 245 259 246 /* 260 - * Match asymmetric keys by an exact match on an ID. 247 + * Match asymmetric keys by an exact match on one of the first two IDs. 261 248 */ 262 249 static bool asymmetric_key_cmp(const struct key *key, 263 250 const struct key_match_data *match_data) ··· 270 257 } 271 258 272 259 /* 273 - * Match asymmetric keys by a partial match on an IDs. 260 + * Match asymmetric keys by a partial match on one of the first two IDs. 274 261 */ 275 262 static bool asymmetric_key_cmp_partial(const struct key *key, 276 263 const struct key_match_data *match_data) ··· 283 270 } 284 271 285 272 /* 273 + * Match asymmetric keys by an exact match on the third IDs. 274 + */ 275 + static bool asymmetric_key_cmp_name(const struct key *key, 276 + const struct key_match_data *match_data) 277 + { 278 + const struct asymmetric_key_ids *kids = asymmetric_key_ids(key); 279 + const struct asymmetric_key_id *match_id = match_data->preparsed; 280 + 281 + return kids && asymmetric_key_id_same(kids->id[2], match_id); 282 + } 283 + 284 + /* 286 285 * Preparse the match criterion. If we don't set lookup_type and cmp, 287 286 * the default will be an exact match on the key description. 288 287 * 289 288 * There are some specifiers for matching key IDs rather than by the key 290 289 * description: 291 290 * 292 - * "id:<id>" - find a key by partial match on any available ID 293 - * "ex:<id>" - find a key by exact match on any available ID 291 + * "id:<id>" - find a key by partial match on one of the first two IDs 292 + * "ex:<id>" - find a key by exact match on one of the first two IDs 293 + * "dn:<id>" - find a key by exact match on the third ID 294 294 * 295 295 * These have to be searched by iteration rather than by direct lookup because 296 296 * the key is hashed according to its description. ··· 327 301 spec[1] == 'x' && 328 302 spec[2] == ':') { 329 303 id = spec + 3; 304 + } else if (spec[0] == 'd' && 305 + spec[1] == 'n' && 306 + spec[2] == ':') { 307 + id = spec + 3; 308 + cmp = asymmetric_key_cmp_name; 330 309 } else { 331 310 goto default_match; 332 311 }
+3 -3
crypto/asymmetric_keys/pkcs7_trust.c
··· 48 48 * keys. 49 49 */ 50 50 key = find_asymmetric_key(trust_keyring, 51 - x509->id, x509->skid, false); 51 + x509->id, x509->skid, NULL, false); 52 52 if (!IS_ERR(key)) { 53 53 /* One of the X.509 certificates in the PKCS#7 message 54 54 * is apparently the same as one we already trust. ··· 82 82 key = find_asymmetric_key(trust_keyring, 83 83 last->sig->auth_ids[0], 84 84 last->sig->auth_ids[1], 85 - false); 85 + NULL, false); 86 86 if (!IS_ERR(key)) { 87 87 x509 = last; 88 88 pr_devel("sinfo %u: Root cert %u signer is key %x\n", ··· 97 97 * the signed info directly. 98 98 */ 99 99 key = find_asymmetric_key(trust_keyring, 100 - sinfo->sig->auth_ids[0], NULL, false); 100 + sinfo->sig->auth_ids[0], NULL, NULL, false); 101 101 if (!IS_ERR(key)) { 102 102 pr_devel("sinfo %u: Direct signer is key %x\n", 103 103 sinfo->index, key_serial(key));
+29 -19
crypto/asymmetric_keys/restrict.c
··· 87 87 sig = payload->data[asym_auth]; 88 88 if (!sig) 89 89 return -ENOPKG; 90 - if (!sig->auth_ids[0] && !sig->auth_ids[1]) 90 + if (!sig->auth_ids[0] && !sig->auth_ids[1] && !sig->auth_ids[2]) 91 91 return -ENOKEY; 92 92 93 93 if (ca_keyid && !asymmetric_key_id_partial(sig->auth_ids[1], ca_keyid)) ··· 96 96 /* See if we have a key that signed this one. */ 97 97 key = find_asymmetric_key(trust_keyring, 98 98 sig->auth_ids[0], sig->auth_ids[1], 99 - false); 99 + sig->auth_ids[2], false); 100 100 if (IS_ERR(key)) 101 101 return -ENOKEY; 102 102 ··· 108 108 return ret; 109 109 } 110 110 111 - static bool match_either_id(const struct asymmetric_key_ids *pair, 111 + static bool match_either_id(const struct asymmetric_key_id **pair, 112 112 const struct asymmetric_key_id *single) 113 113 { 114 - return (asymmetric_key_id_same(pair->id[0], single) || 115 - asymmetric_key_id_same(pair->id[1], single)); 114 + return (asymmetric_key_id_same(pair[0], single) || 115 + asymmetric_key_id_same(pair[1], single)); 116 116 } 117 117 118 118 static int key_or_keyring_common(struct key *dest_keyring, ··· 140 140 sig = payload->data[asym_auth]; 141 141 if (!sig) 142 142 return -ENOPKG; 143 - if (!sig->auth_ids[0] && !sig->auth_ids[1]) 143 + if (!sig->auth_ids[0] && !sig->auth_ids[1] && !sig->auth_ids[2]) 144 144 return -ENOKEY; 145 145 146 146 if (trusted) { 147 147 if (trusted->type == &key_type_keyring) { 148 148 /* See if we have a key that signed this one. */ 149 149 key = find_asymmetric_key(trusted, sig->auth_ids[0], 150 - sig->auth_ids[1], false); 150 + sig->auth_ids[1], 151 + sig->auth_ids[2], false); 151 152 if (IS_ERR(key)) 152 153 key = NULL; 153 154 } else if (trusted->type == &key_type_asymmetric) { 154 - const struct asymmetric_key_ids *signer_ids; 155 + const struct asymmetric_key_id **signer_ids; 155 156 156 - signer_ids = asymmetric_key_ids(trusted); 157 + signer_ids = (const struct asymmetric_key_id **) 158 + asymmetric_key_ids(trusted)->id; 157 159 158 160 /* 159 161 * The auth_ids come from the candidate key (the ··· 166 164 * The signer_ids are identifiers for the 167 165 * signing key specified for dest_keyring. 168 166 * 169 - * The first auth_id is the preferred id, and 170 - * the second is the fallback. If only one 171 - * auth_id is present, it may match against 172 - * either signer_id. If two auth_ids are 173 - * present, the first auth_id must match one 174 - * signer_id and the second auth_id must match 175 - * the second signer_id. 167 + * The first auth_id is the preferred id, 2nd and 168 + * 3rd are the fallbacks. If exactly one of 169 + * auth_ids[0] and auth_ids[1] is present, it may 170 + * match either signer_ids[0] or signed_ids[1]. 171 + * If both are present the first one may match 172 + * either signed_id but the second one must match 173 + * the second signer_id. If neither of them is 174 + * available, auth_ids[2] is matched against 175 + * signer_ids[2] as a fallback. 176 176 */ 177 - if (!sig->auth_ids[0] || !sig->auth_ids[1]) { 177 + if (!sig->auth_ids[0] && !sig->auth_ids[1]) { 178 + if (asymmetric_key_id_same(signer_ids[2], 179 + sig->auth_ids[2])) 180 + key = __key_get(trusted); 181 + 182 + } else if (!sig->auth_ids[0] || !sig->auth_ids[1]) { 178 183 const struct asymmetric_key_id *auth_id; 179 184 180 185 auth_id = sig->auth_ids[0] ?: sig->auth_ids[1]; 181 186 if (match_either_id(signer_ids, auth_id)) 182 187 key = __key_get(trusted); 183 188 184 - } else if (asymmetric_key_id_same(signer_ids->id[1], 189 + } else if (asymmetric_key_id_same(signer_ids[1], 185 190 sig->auth_ids[1]) && 186 191 match_either_id(signer_ids, 187 192 sig->auth_ids[0])) { ··· 202 193 if (check_dest && !key) { 203 194 /* See if the destination has a key that signed this one. */ 204 195 key = find_asymmetric_key(dest_keyring, sig->auth_ids[0], 205 - sig->auth_ids[1], false); 196 + sig->auth_ids[1], sig->auth_ids[2], 197 + false); 206 198 if (IS_ERR(key)) 207 199 key = NULL; 208 200 }
+10
crypto/asymmetric_keys/x509_cert_parser.c
··· 441 441 const void *value, size_t vlen) 442 442 { 443 443 struct x509_parse_context *ctx = context; 444 + struct asymmetric_key_id *kid; 445 + 444 446 ctx->cert->raw_issuer = value; 445 447 ctx->cert->raw_issuer_size = vlen; 448 + 449 + if (!ctx->cert->sig->auth_ids[2]) { 450 + kid = asymmetric_key_generate_id(value, vlen, "", 0); 451 + if (IS_ERR(kid)) 452 + return PTR_ERR(kid); 453 + ctx->cert->sig->auth_ids[2] = kid; 454 + } 455 + 446 456 return x509_fabricate_name(ctx, hdrlen, tag, &ctx->cert->issuer, vlen); 447 457 } 448 458
+10
crypto/asymmetric_keys/x509_public_key.c
··· 223 223 goto error_free_desc; 224 224 kids->id[0] = cert->id; 225 225 kids->id[1] = cert->skid; 226 + kids->id[2] = asymmetric_key_generate_id(cert->raw_subject, 227 + cert->raw_subject_size, 228 + "", 0); 229 + if (IS_ERR(kids->id[2])) { 230 + ret = PTR_ERR(kids->id[2]); 231 + goto error_free_kids; 232 + } 226 233 227 234 /* We're pinning the module by being linked against it */ 228 235 __module_get(public_key_subtype.owner); ··· 246 239 cert->skid = NULL; 247 240 cert->sig = NULL; 248 241 desc = NULL; 242 + kids = NULL; 249 243 ret = 0; 250 244 245 + error_free_kids: 246 + kfree(kids); 251 247 error_free_desc: 252 248 kfree(desc); 253 249 error_free_cert:
+1 -1
include/crypto/public_key.h
··· 36 36 * Public key cryptography signature data 37 37 */ 38 38 struct public_key_signature { 39 - struct asymmetric_key_id *auth_ids[2]; 39 + struct asymmetric_key_id *auth_ids[3]; 40 40 u8 *s; /* Signature */ 41 41 u8 *digest; 42 42 u32 s_size; /* Number of bytes in signature */
+2 -1
include/keys/asymmetric-type.h
··· 53 53 }; 54 54 55 55 struct asymmetric_key_ids { 56 - void *id[2]; 56 + void *id[3]; 57 57 }; 58 58 59 59 extern bool asymmetric_key_id_same(const struct asymmetric_key_id *kid1, ··· 81 81 extern struct key *find_asymmetric_key(struct key *keyring, 82 82 const struct asymmetric_key_id *id_0, 83 83 const struct asymmetric_key_id *id_1, 84 + const struct asymmetric_key_id *id_2, 84 85 bool partial); 85 86 86 87 /*