]> git.proxmox.com Git - mirror_edk2.git/blame - CryptoPkg/Library/BaseCryptLib/Pk/CryptPkcs7Verify.c
Fix several issues in BaseCryptLib:
[mirror_edk2.git] / CryptoPkg / Library / BaseCryptLib / Pk / CryptPkcs7Verify.c
CommitLineData
532616bb 1/** @file\r
2 PKCS#7 SignedData Verification Wrapper Implementation over OpenSSL.\r
3\r
4 Caution: This module requires additional review when modified.\r
5 This library will have external input - signature (e.g. UEFI Authenticated\r
6 Variable). It may by input in SMM mode.\r
7 This external input must be validated carefully to avoid security issue like\r
8 buffer overflow, integer overflow.\r
9\r
10 WrapPkcs7Data(), Pkcs7GetSigners(), Pkcs7Verify() will get UEFI Authenticated\r
11 Variable and will do basic check for data structure.\r
12\r
13Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.<BR>\r
14This program and the accompanying materials\r
15are licensed and made available under the terms and conditions of the BSD License\r
16which accompanies this distribution. The full text of the license may be found at\r
17http://opensource.org/licenses/bsd-license.php\r
18\r
19THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
20WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
21\r
22**/\r
23\r
24#include "InternalCryptLib.h"\r
25\r
26#include <openssl/objects.h>\r
27#include <openssl/x509.h>\r
28#include <openssl/pkcs7.h>\r
29\r
30UINT8 mOidValue[9] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02 };\r
31\r
32/**\r
33 Verification callback function to override any existing callbacks in OpenSSL\r
34 for intermediate certificate supports.\r
35\r
36 @param[in] Status Original status before calling this callback.\r
37 @param[in] Context X509 store context.\r
38\r
39 @retval 1 Current X509 certificate is verified successfully.\r
40 @retval 0 Verification failed.\r
41\r
42**/\r
43int\r
44X509VerifyCb (\r
45 IN int Status,\r
46 IN X509_STORE_CTX *Context\r
47 )\r
48{\r
49 X509_OBJECT *Obj;\r
50 INTN Error;\r
51 INTN Index;\r
52 INTN Count;\r
53\r
54 Obj = NULL;\r
55 Error = (INTN) X509_STORE_CTX_get_error (Context);\r
56\r
57 //\r
58 // X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT and X509_V_ERR_UNABLE_TO_GET_ISSUER_\r
59 // CERT_LOCALLY mean a X509 certificate is not self signed and its issuer\r
60 // can not be found in X509_verify_cert of X509_vfy.c.\r
61 // In order to support intermediate certificate node, we override the\r
62 // errors if the certification is obtained from X509 store, i.e. it is\r
63 // a trusted ceritifcate node that is enrolled by user.\r
64 // Besides,X509_V_ERR_CERT_UNTRUSTED and X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE\r
65 // are also ignored to enable such feature.\r
66 //\r
67 if ((Error == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT) ||\r
68 (Error == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY)) {\r
69 Obj = (X509_OBJECT *) malloc (sizeof (X509_OBJECT));\r
70 if (Obj == NULL) {\r
71 return 0;\r
72 }\r
73\r
74 Obj->type = X509_LU_X509;\r
75 Obj->data.x509 = Context->current_cert;\r
76\r
77 CRYPTO_w_lock (CRYPTO_LOCK_X509_STORE);\r
78\r
79 if (X509_OBJECT_retrieve_match (Context->ctx->objs, Obj)) {\r
80 Status = 1;\r
81 } else {\r
82 //\r
83 // If any certificate in the chain is enrolled as trusted certificate,\r
84 // pass the certificate verification.\r
85 //\r
86 if (Error == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY) {\r
87 Count = (INTN) sk_X509_num (Context->chain);\r
88 for (Index = 0; Index < Count; Index++) {\r
89 Obj->data.x509 = sk_X509_value (Context->chain, (int) Index);\r
90 if (X509_OBJECT_retrieve_match (Context->ctx->objs, Obj)) {\r
91 Status = 1;\r
92 break;\r
93 }\r
94 }\r
95 }\r
96 }\r
97\r
98 CRYPTO_w_unlock (CRYPTO_LOCK_X509_STORE);\r
99 }\r
100\r
101 if ((Error == X509_V_ERR_CERT_UNTRUSTED) ||\r
102 (Error == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE)) {\r
103 Status = 1;\r
104 }\r
105\r
106 if (Obj != NULL) {\r
107 OPENSSL_free (Obj);\r
108 }\r
109\r
110 return Status;\r
111}\r
112\r
113/**\r
114 Check input P7Data is a wrapped ContentInfo structure or not. If not construct\r
115 a new structure to wrap P7Data.\r
116\r
117 Caution: This function may receive untrusted input.\r
118 UEFI Authenticated Variable is external input, so this function will do basic\r
119 check for PKCS#7 data structure.\r
120\r
121 @param[in] P7Data Pointer to the PKCS#7 message to verify.\r
122 @param[in] P7Length Length of the PKCS#7 message in bytes.\r
123 @param[out] WrapFlag If TRUE P7Data is a ContentInfo structure, otherwise\r
124 return FALSE.\r
125 @param[out] WrapData If return status of this function is TRUE: \r
126 1) when WrapFlag is TRUE, pointer to P7Data.\r
127 2) when WrapFlag is FALSE, pointer to a new ContentInfo\r
128 structure. It's caller's responsibility to free this\r
129 buffer.\r
130 @param[out] WrapDataSize Length of ContentInfo structure in bytes.\r
131\r
132 @retval TRUE The operation is finished successfully.\r
133 @retval FALSE The operation is failed due to lack of resources.\r
134\r
135**/\r
136BOOLEAN\r
137WrapPkcs7Data (\r
138 IN CONST UINT8 *P7Data,\r
139 IN UINTN P7Length,\r
140 OUT BOOLEAN *WrapFlag,\r
141 OUT UINT8 **WrapData,\r
142 OUT UINTN *WrapDataSize\r
143 )\r
144{\r
145 BOOLEAN Wrapped;\r
146 UINT8 *SignedData;\r
147\r
148 //\r
149 // Check whether input P7Data is a wrapped ContentInfo structure or not.\r
150 //\r
151 Wrapped = FALSE;\r
152 if ((P7Data[4] == 0x06) && (P7Data[5] == 0x09)) {\r
153 if (CompareMem (P7Data + 6, mOidValue, sizeof (mOidValue)) == 0) {\r
154 if ((P7Data[15] == 0xA0) && (P7Data[16] == 0x82)) {\r
155 Wrapped = TRUE;\r
156 }\r
157 }\r
158 }\r
159\r
160 if (Wrapped) {\r
161 *WrapData = (UINT8 *) P7Data;\r
162 *WrapDataSize = P7Length;\r
163 } else {\r
164 //\r
165 // Wrap PKCS#7 signeddata to a ContentInfo structure - add a header in 19 bytes.\r
166 //\r
167 *WrapDataSize = P7Length + 19;\r
168 *WrapData = malloc (*WrapDataSize);\r
169 if (*WrapData == NULL) {\r
170 *WrapFlag = Wrapped;\r
171 return FALSE;\r
172 }\r
173\r
174 SignedData = *WrapData;\r
175\r
176 //\r
177 // Part1: 0x30, 0x82.\r
178 //\r
179 SignedData[0] = 0x30;\r
180 SignedData[1] = 0x82;\r
181\r
182 //\r
183 // Part2: Length1 = P7Length + 19 - 4, in big endian.\r
184 //\r
185 SignedData[2] = (UINT8) (((UINT16) (*WrapDataSize - 4)) >> 8);\r
186 SignedData[3] = (UINT8) (((UINT16) (*WrapDataSize - 4)) & 0xff);\r
187\r
188 //\r
189 // Part3: 0x06, 0x09.\r
190 //\r
191 SignedData[4] = 0x06;\r
192 SignedData[5] = 0x09;\r
193\r
194 //\r
195 // Part4: OID value -- 0x2A 0x86 0x48 0x86 0xF7 0x0D 0x01 0x07 0x02.\r
196 //\r
197 CopyMem (SignedData + 6, mOidValue, sizeof (mOidValue));\r
198\r
199 //\r
200 // Part5: 0xA0, 0x82.\r
201 //\r
202 SignedData[15] = 0xA0;\r
203 SignedData[16] = 0x82;\r
204\r
205 //\r
206 // Part6: Length2 = P7Length, in big endian.\r
207 //\r
208 SignedData[17] = (UINT8) (((UINT16) P7Length) >> 8);\r
209 SignedData[18] = (UINT8) (((UINT16) P7Length) & 0xff);\r
210\r
211 //\r
212 // Part7: P7Data.\r
213 //\r
214 CopyMem (SignedData + 19, P7Data, P7Length);\r
215 }\r
216\r
217 *WrapFlag = Wrapped;\r
218 return TRUE;\r
219}\r
220\r
221/**\r
222 Get the signer's certificates from PKCS#7 signed data as described in "PKCS #7:\r
223 Cryptographic Message Syntax Standard". The input signed data could be wrapped\r
224 in a ContentInfo structure.\r
225\r
226 If P7Data, CertStack, StackLength, TrustedCert or CertLength is NULL, then\r
227 return FALSE. If P7Length overflow, then return FAlSE.\r
228\r
229 Caution: This function may receive untrusted input.\r
230 UEFI Authenticated Variable is external input, so this function will do basic\r
231 check for PKCS#7 data structure.\r
232\r
233 @param[in] P7Data Pointer to the PKCS#7 message to verify.\r
234 @param[in] P7Length Length of the PKCS#7 message in bytes.\r
235 @param[out] CertStack Pointer to Signer's certificates retrieved from P7Data.\r
236 It's caller's responsiblity to free the buffer.\r
237 @param[out] StackLength Length of signer's certificates in bytes.\r
238 @param[out] TrustedCert Pointer to a trusted certificate from Signer's certificates.\r
239 It's caller's responsiblity to free the buffer.\r
240 @param[out] CertLength Length of the trusted certificate in bytes.\r
241\r
242 @retval TRUE The operation is finished successfully.\r
243 @retval FALSE Error occurs during the operation.\r
244\r
245**/\r
246BOOLEAN\r
247EFIAPI\r
248Pkcs7GetSigners (\r
249 IN CONST UINT8 *P7Data,\r
250 IN UINTN P7Length,\r
251 OUT UINT8 **CertStack,\r
252 OUT UINTN *StackLength,\r
253 OUT UINT8 **TrustedCert,\r
254 OUT UINTN *CertLength\r
255 )\r
256{\r
257 PKCS7 *Pkcs7;\r
258 BOOLEAN Status;\r
259 UINT8 *SignedData;\r
260 UINT8 *Temp;\r
261 UINTN SignedDataSize;\r
262 BOOLEAN Wrapped;\r
263 STACK_OF(X509) *Stack;\r
264 UINT8 Index;\r
265 UINT8 *CertBuf;\r
266 UINT8 *OldBuf;\r
267 UINTN BufferSize;\r
268 UINTN OldSize;\r
269 UINT8 *SingleCert;\r
270 UINTN SingleCertSize;\r
271\r
272 if ((P7Data == NULL) || (CertStack == NULL) || (StackLength == NULL) ||\r
273 (TrustedCert == NULL) || (CertLength == NULL) || (P7Length > INT_MAX)) {\r
274 return FALSE;\r
275 }\r
276 \r
277 Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &SignedData, &SignedDataSize);\r
278 if (!Status) {\r
279 return Status;\r
280 }\r
281\r
282 Status = FALSE;\r
283 Pkcs7 = NULL;\r
284 Stack = NULL;\r
285 CertBuf = NULL;\r
286 OldBuf = NULL;\r
287 SingleCert = NULL;\r
288\r
289 //\r
290 // Retrieve PKCS#7 Data (DER encoding)\r
291 //\r
292 if (SignedDataSize > INT_MAX) {\r
293 goto _Exit;\r
294 }\r
295\r
296 Temp = SignedData;\r
297 Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **) &Temp, (int) SignedDataSize);\r
298 if (Pkcs7 == NULL) {\r
299 goto _Exit;\r
300 }\r
301\r
302 //\r
303 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)\r
304 //\r
305 if (!PKCS7_type_is_signed (Pkcs7)) {\r
306 goto _Exit;\r
307 }\r
308\r
309 Stack = PKCS7_get0_signers(Pkcs7, NULL, PKCS7_BINARY);\r
310 if (Stack == NULL) {\r
311 goto _Exit;\r
312 }\r
313\r
314 //\r
315 // Convert CertStack to buffer in following format:\r
316 // UINT8 CertNumber;\r
317 // UINT32 Cert1Length;\r
318 // UINT8 Cert1[];\r
319 // UINT32 Cert2Length;\r
320 // UINT8 Cert2[];\r
321 // ...\r
322 // UINT32 CertnLength;\r
323 // UINT8 Certn[];\r
324 //\r
325 BufferSize = sizeof (UINT8);\r
326 OldSize = BufferSize;\r
327 \r
328 for (Index = 0; ; Index++) {\r
329 Status = X509PopCertificate (Stack, &SingleCert, &SingleCertSize);\r
330 if (!Status) {\r
331 break;\r
332 }\r
333\r
334 OldSize = BufferSize;\r
335 OldBuf = CertBuf;\r
336 BufferSize = OldSize + SingleCertSize + sizeof (UINT32);\r
337 CertBuf = malloc (BufferSize);\r
338\r
339 if (CertBuf == NULL) {\r
340 goto _Exit;\r
341 }\r
342\r
343 if (OldBuf != NULL) {\r
344 CopyMem (CertBuf, OldBuf, OldSize);\r
345 free (OldBuf);\r
346 OldBuf = NULL;\r
347 }\r
348\r
349 WriteUnaligned32 ((UINT32 *) (CertBuf + OldSize), (UINT32) SingleCertSize);\r
350 CopyMem (CertBuf + OldSize + sizeof (UINT32), SingleCert, SingleCertSize);\r
351\r
352 free (SingleCert);\r
353 SingleCert = NULL;\r
354 }\r
355\r
356 if (CertBuf != NULL) {\r
357 //\r
358 // Update CertNumber.\r
359 //\r
360 CertBuf[0] = Index;\r
361\r
362 *CertLength = BufferSize - OldSize - sizeof (UINT32);\r
363 *TrustedCert = malloc (*CertLength);\r
364 if (*TrustedCert == NULL) {\r
365 goto _Exit;\r
366 }\r
367\r
368 CopyMem (*TrustedCert, CertBuf + OldSize + sizeof (UINT32), *CertLength);\r
369 *CertStack = CertBuf;\r
370 *StackLength = BufferSize;\r
371 Status = TRUE;\r
372 } \r
373\r
374_Exit:\r
375 //\r
376 // Release Resources\r
377 //\r
378 if (!Wrapped) {\r
379 free (SignedData);\r
380 }\r
381\r
382 if (Pkcs7 != NULL) {\r
383 PKCS7_free (Pkcs7);\r
384 }\r
385\r
386 if (Stack != NULL) {\r
387 sk_X509_pop_free(Stack, X509_free);\r
388 }\r
389\r
390 if (SingleCert != NULL) {\r
391 free (SingleCert);\r
392 }\r
393\r
394 if (!Status && (CertBuf != NULL)) {\r
395 free (CertBuf);\r
396 *CertStack = NULL;\r
397 }\r
398\r
399 if (OldBuf != NULL) {\r
400 free (OldBuf);\r
401 }\r
402 \r
403 return Status;\r
404}\r
405\r
406/**\r
407 Wrap function to use free() to free allocated memory for certificates.\r
408\r
409 @param[in] Certs Pointer to the certificates to be freed.\r
410\r
411**/\r
412VOID\r
413EFIAPI\r
414Pkcs7FreeSigners (\r
415 IN UINT8 *Certs\r
416 )\r
417{\r
418 if (Certs == NULL) {\r
419 return;\r
420 }\r
421\r
422 free (Certs);\r
423}\r
424\r
425/**\r
426 Verifies the validility of a PKCS#7 signed data as described in "PKCS #7:\r
427 Cryptographic Message Syntax Standard". The input signed data could be wrapped\r
428 in a ContentInfo structure.\r
429\r
430 If P7Data, TrustedCert or InData is NULL, then return FALSE.\r
431 If P7Length, CertLength or DataLength overflow, then return FAlSE.\r
432\r
433 Caution: This function may receive untrusted input.\r
434 UEFI Authenticated Variable is external input, so this function will do basic\r
435 check for PKCS#7 data structure.\r
436\r
437 @param[in] P7Data Pointer to the PKCS#7 message to verify.\r
438 @param[in] P7Length Length of the PKCS#7 message in bytes.\r
439 @param[in] TrustedCert Pointer to a trusted/root certificate encoded in DER, which\r
440 is used for certificate chain verification.\r
441 @param[in] CertLength Length of the trusted certificate in bytes.\r
442 @param[in] InData Pointer to the content to be verified.\r
443 @param[in] DataLength Length of InData in bytes.\r
444\r
445 @retval TRUE The specified PKCS#7 signed data is valid.\r
446 @retval FALSE Invalid PKCS#7 signed data.\r
447\r
448**/\r
449BOOLEAN\r
450EFIAPI\r
451Pkcs7Verify (\r
452 IN CONST UINT8 *P7Data,\r
453 IN UINTN P7Length,\r
454 IN CONST UINT8 *TrustedCert,\r
455 IN UINTN CertLength,\r
456 IN CONST UINT8 *InData,\r
457 IN UINTN DataLength\r
458 )\r
459{\r
460 PKCS7 *Pkcs7;\r
461 BIO *CertBio;\r
462 BIO *DataBio;\r
463 BOOLEAN Status;\r
464 X509 *Cert;\r
465 X509_STORE *CertStore;\r
466 UINT8 *SignedData;\r
467 UINT8 *Temp;\r
468 UINTN SignedDataSize;\r
469 BOOLEAN Wrapped;\r
470\r
471 //\r
472 // Check input parameters.\r
473 //\r
474 if (P7Data == NULL || TrustedCert == NULL || InData == NULL || \r
475 P7Length > INT_MAX || CertLength > INT_MAX || DataLength > INT_MAX) {\r
476 return FALSE;\r
477 }\r
478 \r
479 Pkcs7 = NULL;\r
480 CertBio = NULL;\r
481 DataBio = NULL;\r
482 Cert = NULL;\r
483 CertStore = NULL;\r
484\r
485 //\r
486 // Register & Initialize necessary digest algorithms for PKCS#7 Handling\r
487 //\r
dda39f3a 488 if (EVP_add_digest (EVP_md5 ()) == 0) {\r
489 return FALSE;\r
490 }\r
491 if (EVP_add_digest (EVP_sha1 ()) == 0) {\r
492 return FALSE;\r
493 }\r
494 if (EVP_add_digest (EVP_sha256 ()) == 0) {\r
495 return FALSE;\r
496 }\r
497 if (EVP_add_digest_alias (SN_sha1WithRSAEncryption, SN_sha1WithRSA) == 0) {\r
498 return FALSE;\r
499 }\r
500\r
532616bb 501\r
502 Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &SignedData, &SignedDataSize);\r
503 if (!Status) {\r
504 return Status;\r
505 }\r
506\r
507 Status = FALSE;\r
508 \r
509 //\r
510 // Retrieve PKCS#7 Data (DER encoding)\r
511 //\r
512 if (SignedDataSize > INT_MAX) {\r
513 goto _Exit;\r
514 }\r
515\r
516 Temp = SignedData;\r
517 Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **) &Temp, (int) SignedDataSize);\r
518 if (Pkcs7 == NULL) {\r
519 goto _Exit;\r
520 }\r
521\r
522 //\r
523 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)\r
524 //\r
525 if (!PKCS7_type_is_signed (Pkcs7)) {\r
526 goto _Exit;\r
527 }\r
528\r
529 //\r
530 // Read DER-encoded root certificate and Construct X509 Certificate\r
531 //\r
532 CertBio = BIO_new (BIO_s_mem ());\r
533 BIO_write (CertBio, TrustedCert, (int)CertLength);\r
534 if (CertBio == NULL) {\r
535 goto _Exit;\r
536 }\r
537 Cert = d2i_X509_bio (CertBio, NULL);\r
538 if (Cert == NULL) {\r
539 goto _Exit;\r
540 }\r
541\r
542 //\r
543 // Setup X509 Store for trusted certificate\r
544 //\r
545 CertStore = X509_STORE_new ();\r
546 if (CertStore == NULL) {\r
547 goto _Exit;\r
548 }\r
549 if (!(X509_STORE_add_cert (CertStore, Cert))) {\r
550 goto _Exit;\r
551 }\r
552\r
553 //\r
554 // Register customized X509 verification callback function to support\r
555 // trusted intermediate certificate anchor.\r
556 //\r
557 CertStore->verify_cb = X509VerifyCb;\r
558\r
559 //\r
560 // For generic PKCS#7 handling, InData may be NULL if the content is present\r
561 // in PKCS#7 structure. So ignore NULL checking here.\r
562 //\r
563 DataBio = BIO_new (BIO_s_mem ());\r
564 BIO_write (DataBio, InData, (int)DataLength);\r
565\r
566 //\r
567 // Verifies the PKCS#7 signedData structure\r
568 //\r
569 Status = (BOOLEAN) PKCS7_verify (Pkcs7, NULL, CertStore, DataBio, NULL, PKCS7_BINARY);\r
570\r
571_Exit:\r
572 //\r
573 // Release Resources\r
574 //\r
575 BIO_free (DataBio);\r
576 BIO_free (CertBio);\r
577 X509_free (Cert);\r
578 X509_STORE_free (CertStore);\r
579 PKCS7_free (Pkcs7);\r
580\r
581 if (!Wrapped) {\r
582 OPENSSL_free (SignedData);\r
583 }\r
584\r
585 return Status;\r
586}\r