aboutsummaryrefslogtreecommitdiffstats
path: root/crypto/asymmetric_keys
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2014-09-16 12:36:15 -0400
committerDavid Howells <dhowells@redhat.com>2014-09-16 12:36:15 -0400
commit41559420003cfe99522257dded7793192c77b4e9 (patch)
tree478af8309836992b40385a1aff6d8eae537d44c4 /crypto/asymmetric_keys
parent46963b774d441c833afc1535f6d84b3df2a94204 (diff)
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 <dhowells@redhat.com> Acked-by: Vivek Goyal <vgoyal@redhat.com>
Diffstat (limited to 'crypto/asymmetric_keys')
-rw-r--r--crypto/asymmetric_keys/pkcs7_parser.h1
-rw-r--r--crypto/asymmetric_keys/pkcs7_trust.c29
-rw-r--r--crypto/asymmetric_keys/pkcs7_verify.c46
-rw-r--r--crypto/asymmetric_keys/x509_parser.h1
-rw-r--r--crypto/asymmetric_keys/x509_public_key.c13
5 files changed, 74 insertions, 16 deletions
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 {
23 struct x509_certificate *signer; /* Signing certificate (in msg->certs) */ 23 struct x509_certificate *signer; /* Signing certificate (in msg->certs) */
24 unsigned index; 24 unsigned index;
25 bool trusted; 25 bool trusted;
26 bool unsupported_crypto; /* T if not usable due to missing crypto */
26 27
27 /* Message digest - the digest of the Content Data (or NULL) */ 28 /* Message digest - the digest of the Content Data (or NULL) */
28 const void *msgdigest; 29 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,
35 35
36 kenter(",%u,", sinfo->index); 36 kenter(",%u,", sinfo->index);
37 37
38 if (sinfo->unsupported_crypto) {
39 kleave(" = -ENOPKG [cached]");
40 return -ENOPKG;
41 }
42
38 for (x509 = sinfo->signer; x509; x509 = x509->signer) { 43 for (x509 = sinfo->signer; x509; x509 = x509->signer) {
39 if (x509->seen) { 44 if (x509->seen) {
40 if (x509->verified) { 45 if (x509->verified) {
@@ -139,24 +144,28 @@ int pkcs7_validate_trust(struct pkcs7_message *pkcs7,
139{ 144{
140 struct pkcs7_signed_info *sinfo; 145 struct pkcs7_signed_info *sinfo;
141 struct x509_certificate *p; 146 struct x509_certificate *p;
142 int cached_ret = 0, ret; 147 int cached_ret = -ENOKEY;
148 int ret;
143 149
144 for (p = pkcs7->certs; p; p = p->next) 150 for (p = pkcs7->certs; p; p = p->next)
145 p->seen = false; 151 p->seen = false;
146 152
147 for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) { 153 for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) {
148 ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring); 154 ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring);
149 if (ret < 0) { 155 switch (ret) {
150 if (ret == -ENOPKG) { 156 case -ENOKEY:
157 continue;
158 case -ENOPKG:
159 if (cached_ret == -ENOKEY)
151 cached_ret = -ENOPKG; 160 cached_ret = -ENOPKG;
152 } else if (ret == -ENOKEY) { 161 continue;
153 if (cached_ret == 0) 162 case 0:
154 cached_ret = -ENOKEY; 163 *_trusted |= sinfo->trusted;
155 } else { 164 cached_ret = 0;
156 return ret; 165 continue;
157 } 166 default:
167 return ret;
158 } 168 }
159 *_trusted |= sinfo->trusted;
160 } 169 }
161 170
162 return cached_ret; 171 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,
181 x509->seen = true; 181 x509->seen = true;
182 ret = x509_get_sig_params(x509); 182 ret = x509_get_sig_params(x509);
183 if (ret < 0) 183 if (ret < 0)
184 return ret; 184 goto maybe_missing_crypto_in_x509;
185 185
186 pr_debug("- issuer %s\n", x509->issuer); 186 pr_debug("- issuer %s\n", x509->issuer);
187 if (x509->authority) 187 if (x509->authority)
@@ -203,7 +203,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
203 203
204 ret = x509_check_signature(x509->pub, x509); 204 ret = x509_check_signature(x509->pub, x509);
205 if (ret < 0) 205 if (ret < 0)
206 return ret; 206 goto maybe_missing_crypto_in_x509;
207 x509->signer = x509; 207 x509->signer = x509;
208 pr_debug("- self-signed\n"); 208 pr_debug("- self-signed\n");
209 return 0; 209 return 0;
@@ -245,6 +245,17 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
245 x509 = p; 245 x509 = p;
246 might_sleep(); 246 might_sleep();
247 } 247 }
248
249maybe_missing_crypto_in_x509:
250 /* Just prune the certificate chain at this point if we lack some
251 * crypto module to go further. Note, however, we don't want to set
252 * sinfo->missing_crypto as the signed info block may still be
253 * validatable against an X.509 cert lower in the chain that we have a
254 * trusted copy of.
255 */
256 if (ret == -ENOPKG)
257 return 0;
258 return ret;
248} 259}
249 260
250/* 261/*
@@ -286,11 +297,33 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7,
286/** 297/**
287 * pkcs7_verify - Verify a PKCS#7 message 298 * pkcs7_verify - Verify a PKCS#7 message
288 * @pkcs7: The PKCS#7 message to be verified 299 * @pkcs7: The PKCS#7 message to be verified
300 *
301 * Verify a PKCS#7 message is internally consistent - that is, the data digest
302 * matches the digest in the AuthAttrs and any signature in the message or one
303 * of the X.509 certificates it carries that matches another X.509 cert in the
304 * message can be verified.
305 *
306 * This does not look to match the contents of the PKCS#7 message against any
307 * external public keys.
308 *
309 * Returns, in order of descending priority:
310 *
311 * (*) -EKEYREJECTED if a signature failed to match for which we found an
312 * appropriate X.509 certificate, or:
313 *
314 * (*) -EBADMSG if some part of the message was invalid, or:
315 *
316 * (*) -ENOPKG if none of the signature chains are verifiable because suitable
317 * crypto modules couldn't be found, or:
318 *
319 * (*) 0 if all the signature chains that don't incur -ENOPKG can be verified
320 * (note that a signature chain may be of zero length), or:
289 */ 321 */
290int pkcs7_verify(struct pkcs7_message *pkcs7) 322int pkcs7_verify(struct pkcs7_message *pkcs7)
291{ 323{
292 struct pkcs7_signed_info *sinfo; 324 struct pkcs7_signed_info *sinfo;
293 struct x509_certificate *x509; 325 struct x509_certificate *x509;
326 int enopkg = -ENOPKG;
294 int ret, n; 327 int ret, n;
295 328
296 kenter(""); 329 kenter("");
@@ -306,12 +339,17 @@ int pkcs7_verify(struct pkcs7_message *pkcs7)
306 for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) { 339 for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) {
307 ret = pkcs7_verify_one(pkcs7, sinfo); 340 ret = pkcs7_verify_one(pkcs7, sinfo);
308 if (ret < 0) { 341 if (ret < 0) {
342 if (ret == -ENOPKG) {
343 sinfo->unsupported_crypto = true;
344 continue;
345 }
309 kleave(" = %d", ret); 346 kleave(" = %d", ret);
310 return ret; 347 return ret;
311 } 348 }
349 enopkg = 0;
312 } 350 }
313 351
314 kleave(" = 0"); 352 kleave(" = %d", enopkg);
315 return 0; 353 return enopkg;
316} 354}
317EXPORT_SYMBOL_GPL(pkcs7_verify); 355EXPORT_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 {
38 bool seen; /* Infinite recursion prevention */ 38 bool seen; /* Infinite recursion prevention */
39 bool verified; 39 bool verified;
40 bool trusted; 40 bool trusted;
41 bool unsupported_crypto; /* T if can't be verified due to missing crypto */
41}; 42};
42 43
43/* 44/*
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)
115 115
116 pr_devel("==>%s()\n", __func__); 116 pr_devel("==>%s()\n", __func__);
117 117
118 if (cert->unsupported_crypto)
119 return -ENOPKG;
118 if (cert->sig.rsa.s) 120 if (cert->sig.rsa.s)
119 return 0; 121 return 0;
120 122
@@ -127,8 +129,13 @@ int x509_get_sig_params(struct x509_certificate *cert)
127 * big the hash operational data will be. 129 * big the hash operational data will be.
128 */ 130 */
129 tfm = crypto_alloc_shash(hash_algo_name[cert->sig.pkey_hash_algo], 0, 0); 131 tfm = crypto_alloc_shash(hash_algo_name[cert->sig.pkey_hash_algo], 0, 0);
130 if (IS_ERR(tfm)) 132 if (IS_ERR(tfm)) {
131 return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm); 133 if (PTR_ERR(tfm) == -ENOENT) {
134 cert->unsupported_crypto = true;
135 return -ENOPKG;
136 }
137 return PTR_ERR(tfm);
138 }
132 139
133 desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); 140 desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
134 digest_size = crypto_shash_digestsize(tfm); 141 digest_size = crypto_shash_digestsize(tfm);
@@ -175,6 +182,8 @@ int x509_check_signature(const struct public_key *pub,
175 return ret; 182 return ret;
176 183
177 ret = public_key_verify_signature(pub, &cert->sig); 184 ret = public_key_verify_signature(pub, &cert->sig);
185 if (ret == -ENOPKG)
186 cert->unsupported_crypto = true;
178 pr_debug("Cert Verification: %d\n", ret); 187 pr_debug("Cert Verification: %d\n", ret);
179 return ret; 188 return ret;
180} 189}