]> git.proxmox.com Git - mirror_edk2.git/blame - CryptoPkg/Library/BaseCryptLib/Pk/CryptPkcs7Verify.c
CrptoPkg/BaseCryptLib: Fix type mismatch when calling OpenSSL function
[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
f56b11d2 13Copyright (c) 2009 - 2017, Intel Corporation. All rights reserved.<BR>\r
532616bb 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
02ee8d3b 28#include <openssl/x509v3.h>\r
532616bb 29#include <openssl/pkcs7.h>\r
30\r
31UINT8 mOidValue[9] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02 };\r
32\r
532616bb 33/**\r
34 Check input P7Data is a wrapped ContentInfo structure or not. If not construct\r
35 a new structure to wrap P7Data.\r
36\r
37 Caution: This function may receive untrusted input.\r
38 UEFI Authenticated Variable is external input, so this function will do basic\r
39 check for PKCS#7 data structure.\r
40\r
41 @param[in] P7Data Pointer to the PKCS#7 message to verify.\r
42 @param[in] P7Length Length of the PKCS#7 message in bytes.\r
43 @param[out] WrapFlag If TRUE P7Data is a ContentInfo structure, otherwise\r
44 return FALSE.\r
2ac68e8b 45 @param[out] WrapData If return status of this function is TRUE:\r
532616bb 46 1) when WrapFlag is TRUE, pointer to P7Data.\r
47 2) when WrapFlag is FALSE, pointer to a new ContentInfo\r
48 structure. It's caller's responsibility to free this\r
49 buffer.\r
50 @param[out] WrapDataSize Length of ContentInfo structure in bytes.\r
51\r
52 @retval TRUE The operation is finished successfully.\r
53 @retval FALSE The operation is failed due to lack of resources.\r
54\r
55**/\r
56BOOLEAN\r
57WrapPkcs7Data (\r
58 IN CONST UINT8 *P7Data,\r
59 IN UINTN P7Length,\r
60 OUT BOOLEAN *WrapFlag,\r
61 OUT UINT8 **WrapData,\r
62 OUT UINTN *WrapDataSize\r
63 )\r
64{\r
65 BOOLEAN Wrapped;\r
66 UINT8 *SignedData;\r
67\r
68 //\r
69 // Check whether input P7Data is a wrapped ContentInfo structure or not.\r
70 //\r
71 Wrapped = FALSE;\r
72 if ((P7Data[4] == 0x06) && (P7Data[5] == 0x09)) {\r
73 if (CompareMem (P7Data + 6, mOidValue, sizeof (mOidValue)) == 0) {\r
74 if ((P7Data[15] == 0xA0) && (P7Data[16] == 0x82)) {\r
75 Wrapped = TRUE;\r
76 }\r
77 }\r
78 }\r
79\r
80 if (Wrapped) {\r
81 *WrapData = (UINT8 *) P7Data;\r
82 *WrapDataSize = P7Length;\r
83 } else {\r
84 //\r
85 // Wrap PKCS#7 signeddata to a ContentInfo structure - add a header in 19 bytes.\r
86 //\r
87 *WrapDataSize = P7Length + 19;\r
88 *WrapData = malloc (*WrapDataSize);\r
89 if (*WrapData == NULL) {\r
90 *WrapFlag = Wrapped;\r
91 return FALSE;\r
92 }\r
93\r
94 SignedData = *WrapData;\r
95\r
96 //\r
97 // Part1: 0x30, 0x82.\r
98 //\r
99 SignedData[0] = 0x30;\r
100 SignedData[1] = 0x82;\r
101\r
102 //\r
103 // Part2: Length1 = P7Length + 19 - 4, in big endian.\r
104 //\r
105 SignedData[2] = (UINT8) (((UINT16) (*WrapDataSize - 4)) >> 8);\r
106 SignedData[3] = (UINT8) (((UINT16) (*WrapDataSize - 4)) & 0xff);\r
107\r
108 //\r
109 // Part3: 0x06, 0x09.\r
110 //\r
111 SignedData[4] = 0x06;\r
112 SignedData[5] = 0x09;\r
113\r
114 //\r
115 // Part4: OID value -- 0x2A 0x86 0x48 0x86 0xF7 0x0D 0x01 0x07 0x02.\r
116 //\r
117 CopyMem (SignedData + 6, mOidValue, sizeof (mOidValue));\r
118\r
119 //\r
120 // Part5: 0xA0, 0x82.\r
121 //\r
122 SignedData[15] = 0xA0;\r
123 SignedData[16] = 0x82;\r
124\r
125 //\r
126 // Part6: Length2 = P7Length, in big endian.\r
127 //\r
128 SignedData[17] = (UINT8) (((UINT16) P7Length) >> 8);\r
129 SignedData[18] = (UINT8) (((UINT16) P7Length) & 0xff);\r
130\r
131 //\r
132 // Part7: P7Data.\r
133 //\r
134 CopyMem (SignedData + 19, P7Data, P7Length);\r
135 }\r
136\r
137 *WrapFlag = Wrapped;\r
138 return TRUE;\r
139}\r
140\r
efad60c5 141/**\r
142 Pop single certificate from STACK_OF(X509).\r
143\r
144 If X509Stack, Cert, or CertSize is NULL, then return FALSE.\r
145\r
146 @param[in] X509Stack Pointer to a X509 stack object.\r
147 @param[out] Cert Pointer to a X509 certificate.\r
148 @param[out] CertSize Length of output X509 certificate in bytes.\r
2ac68e8b 149\r
efad60c5 150 @retval TRUE The X509 stack pop succeeded.\r
151 @retval FALSE The pop operation failed.\r
152\r
153**/\r
154BOOLEAN\r
155X509PopCertificate (\r
156 IN VOID *X509Stack,\r
157 OUT UINT8 **Cert,\r
158 OUT UINTN *CertSize\r
159 )\r
160{\r
161 BIO *CertBio;\r
162 X509 *X509Cert;\r
163 STACK_OF(X509) *CertStack;\r
164 BOOLEAN Status;\r
eb58f559 165 INT32 Result;\r
f56b11d2 166 BUF_MEM *Ptr;\r
eb58f559 167 INT32 Length;\r
efad60c5 168 VOID *Buffer;\r
169\r
170 Status = FALSE;\r
171\r
172 if ((X509Stack == NULL) || (Cert == NULL) || (CertSize == NULL)) {\r
173 return Status;\r
174 }\r
175\r
176 CertStack = (STACK_OF(X509) *) X509Stack;\r
177\r
178 X509Cert = sk_X509_pop (CertStack);\r
179\r
180 if (X509Cert == NULL) {\r
181 return Status;\r
182 }\r
183\r
184 Buffer = NULL;\r
185\r
186 CertBio = BIO_new (BIO_s_mem ());\r
187 if (CertBio == NULL) {\r
188 return Status;\r
189 }\r
190\r
191 Result = i2d_X509_bio (CertBio, X509Cert);\r
192 if (Result == 0) {\r
193 goto _Exit;\r
194 }\r
195\r
f56b11d2
QL
196 BIO_get_mem_ptr (CertBio, &Ptr);\r
197 Length = (INT32)(Ptr->length);\r
efad60c5 198 if (Length <= 0) {\r
199 goto _Exit;\r
200 }\r
201\r
202 Buffer = malloc (Length);\r
203 if (Buffer == NULL) {\r
204 goto _Exit;\r
205 }\r
206\r
207 Result = BIO_read (CertBio, Buffer, Length);\r
208 if (Result != Length) {\r
209 goto _Exit;\r
210 }\r
211\r
212 *Cert = Buffer;\r
213 *CertSize = Length;\r
214\r
215 Status = TRUE;\r
216\r
217_Exit:\r
218\r
219 BIO_free (CertBio);\r
220\r
221 if (!Status && (Buffer != NULL)) {\r
222 free (Buffer);\r
223 }\r
224\r
225 return Status;\r
226}\r
227\r
532616bb 228/**\r
229 Get the signer's certificates from PKCS#7 signed data as described in "PKCS #7:\r
230 Cryptographic Message Syntax Standard". The input signed data could be wrapped\r
231 in a ContentInfo structure.\r
232\r
233 If P7Data, CertStack, StackLength, TrustedCert or CertLength is NULL, then\r
2998af86 234 return FALSE. If P7Length overflow, then return FALSE.\r
532616bb 235\r
236 Caution: This function may receive untrusted input.\r
237 UEFI Authenticated Variable is external input, so this function will do basic\r
238 check for PKCS#7 data structure.\r
239\r
240 @param[in] P7Data Pointer to the PKCS#7 message to verify.\r
241 @param[in] P7Length Length of the PKCS#7 message in bytes.\r
242 @param[out] CertStack Pointer to Signer's certificates retrieved from P7Data.\r
6fe575d0
LQ
243 It's caller's responsibility to free the buffer with\r
244 Pkcs7FreeSigners().\r
3702637a 245 This data structure is EFI_CERT_STACK type.\r
532616bb 246 @param[out] StackLength Length of signer's certificates in bytes.\r
247 @param[out] TrustedCert Pointer to a trusted certificate from Signer's certificates.\r
6fe575d0
LQ
248 It's caller's responsibility to free the buffer with\r
249 Pkcs7FreeSigners().\r
532616bb 250 @param[out] CertLength Length of the trusted certificate in bytes.\r
251\r
252 @retval TRUE The operation is finished successfully.\r
253 @retval FALSE Error occurs during the operation.\r
254\r
255**/\r
256BOOLEAN\r
257EFIAPI\r
258Pkcs7GetSigners (\r
259 IN CONST UINT8 *P7Data,\r
260 IN UINTN P7Length,\r
261 OUT UINT8 **CertStack,\r
262 OUT UINTN *StackLength,\r
263 OUT UINT8 **TrustedCert,\r
264 OUT UINTN *CertLength\r
265 )\r
266{\r
267 PKCS7 *Pkcs7;\r
268 BOOLEAN Status;\r
269 UINT8 *SignedData;\r
1463ce18 270 CONST UINT8 *Temp;\r
532616bb 271 UINTN SignedDataSize;\r
272 BOOLEAN Wrapped;\r
273 STACK_OF(X509) *Stack;\r
274 UINT8 Index;\r
275 UINT8 *CertBuf;\r
276 UINT8 *OldBuf;\r
277 UINTN BufferSize;\r
278 UINTN OldSize;\r
279 UINT8 *SingleCert;\r
280 UINTN SingleCertSize;\r
281\r
282 if ((P7Data == NULL) || (CertStack == NULL) || (StackLength == NULL) ||\r
283 (TrustedCert == NULL) || (CertLength == NULL) || (P7Length > INT_MAX)) {\r
284 return FALSE;\r
285 }\r
2ac68e8b 286\r
532616bb 287 Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &SignedData, &SignedDataSize);\r
288 if (!Status) {\r
289 return Status;\r
290 }\r
291\r
292 Status = FALSE;\r
293 Pkcs7 = NULL;\r
294 Stack = NULL;\r
295 CertBuf = NULL;\r
296 OldBuf = NULL;\r
297 SingleCert = NULL;\r
298\r
299 //\r
300 // Retrieve PKCS#7 Data (DER encoding)\r
301 //\r
302 if (SignedDataSize > INT_MAX) {\r
303 goto _Exit;\r
304 }\r
305\r
306 Temp = SignedData;\r
307 Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **) &Temp, (int) SignedDataSize);\r
308 if (Pkcs7 == NULL) {\r
309 goto _Exit;\r
310 }\r
311\r
312 //\r
313 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)\r
314 //\r
315 if (!PKCS7_type_is_signed (Pkcs7)) {\r
316 goto _Exit;\r
317 }\r
318\r
319 Stack = PKCS7_get0_signers(Pkcs7, NULL, PKCS7_BINARY);\r
320 if (Stack == NULL) {\r
321 goto _Exit;\r
322 }\r
323\r
324 //\r
325 // Convert CertStack to buffer in following format:\r
326 // UINT8 CertNumber;\r
327 // UINT32 Cert1Length;\r
328 // UINT8 Cert1[];\r
329 // UINT32 Cert2Length;\r
330 // UINT8 Cert2[];\r
331 // ...\r
332 // UINT32 CertnLength;\r
333 // UINT8 Certn[];\r
334 //\r
335 BufferSize = sizeof (UINT8);\r
336 OldSize = BufferSize;\r
2ac68e8b 337\r
532616bb 338 for (Index = 0; ; Index++) {\r
339 Status = X509PopCertificate (Stack, &SingleCert, &SingleCertSize);\r
340 if (!Status) {\r
341 break;\r
342 }\r
343\r
344 OldSize = BufferSize;\r
345 OldBuf = CertBuf;\r
346 BufferSize = OldSize + SingleCertSize + sizeof (UINT32);\r
347 CertBuf = malloc (BufferSize);\r
348\r
349 if (CertBuf == NULL) {\r
350 goto _Exit;\r
351 }\r
352\r
353 if (OldBuf != NULL) {\r
354 CopyMem (CertBuf, OldBuf, OldSize);\r
355 free (OldBuf);\r
356 OldBuf = NULL;\r
357 }\r
358\r
359 WriteUnaligned32 ((UINT32 *) (CertBuf + OldSize), (UINT32) SingleCertSize);\r
360 CopyMem (CertBuf + OldSize + sizeof (UINT32), SingleCert, SingleCertSize);\r
361\r
362 free (SingleCert);\r
363 SingleCert = NULL;\r
364 }\r
365\r
366 if (CertBuf != NULL) {\r
367 //\r
368 // Update CertNumber.\r
369 //\r
370 CertBuf[0] = Index;\r
371\r
372 *CertLength = BufferSize - OldSize - sizeof (UINT32);\r
373 *TrustedCert = malloc (*CertLength);\r
374 if (*TrustedCert == NULL) {\r
375 goto _Exit;\r
376 }\r
377\r
378 CopyMem (*TrustedCert, CertBuf + OldSize + sizeof (UINT32), *CertLength);\r
379 *CertStack = CertBuf;\r
380 *StackLength = BufferSize;\r
381 Status = TRUE;\r
2ac68e8b 382 }\r
532616bb 383\r
384_Exit:\r
385 //\r
386 // Release Resources\r
387 //\r
388 if (!Wrapped) {\r
389 free (SignedData);\r
390 }\r
391\r
392 if (Pkcs7 != NULL) {\r
393 PKCS7_free (Pkcs7);\r
394 }\r
395\r
396 if (Stack != NULL) {\r
397 sk_X509_pop_free(Stack, X509_free);\r
398 }\r
399\r
400 if (SingleCert != NULL) {\r
401 free (SingleCert);\r
402 }\r
403\r
404 if (!Status && (CertBuf != NULL)) {\r
405 free (CertBuf);\r
406 *CertStack = NULL;\r
407 }\r
408\r
409 if (OldBuf != NULL) {\r
410 free (OldBuf);\r
411 }\r
2ac68e8b 412\r
532616bb 413 return Status;\r
414}\r
415\r
416/**\r
417 Wrap function to use free() to free allocated memory for certificates.\r
418\r
419 @param[in] Certs Pointer to the certificates to be freed.\r
420\r
421**/\r
422VOID\r
423EFIAPI\r
424Pkcs7FreeSigners (\r
425 IN UINT8 *Certs\r
426 )\r
427{\r
428 if (Certs == NULL) {\r
429 return;\r
430 }\r
431\r
432 free (Certs);\r
433}\r
434\r
45419de6
QL
435/**\r
436 Retrieves all embedded certificates from PKCS#7 signed data as described in "PKCS #7:\r
437 Cryptographic Message Syntax Standard", and outputs two certificate lists chained and\r
438 unchained to the signer's certificates.\r
439 The input signed data could be wrapped in a ContentInfo structure.\r
440\r
441 @param[in] P7Data Pointer to the PKCS#7 message.\r
442 @param[in] P7Length Length of the PKCS#7 message in bytes.\r
0f5f6b3d 443 @param[out] SignerChainCerts Pointer to the certificates list chained to signer's\r
6fe575d0
LQ
444 certificate. It's caller's responsibility to free the buffer\r
445 with Pkcs7FreeSigners().\r
3702637a 446 This data structure is EFI_CERT_STACK type.\r
45419de6
QL
447 @param[out] ChainLength Length of the chained certificates list buffer in bytes.\r
448 @param[out] UnchainCerts Pointer to the unchained certificates lists. It's caller's\r
6fe575d0 449 responsibility to free the buffer with Pkcs7FreeSigners().\r
3702637a 450 This data structure is EFI_CERT_STACK type.\r
45419de6
QL
451 @param[out] UnchainLength Length of the unchained certificates list buffer in bytes.\r
452\r
453 @retval TRUE The operation is finished successfully.\r
454 @retval FALSE Error occurs during the operation.\r
455\r
456**/\r
457BOOLEAN\r
458EFIAPI\r
459Pkcs7GetCertificatesList (\r
460 IN CONST UINT8 *P7Data,\r
461 IN UINTN P7Length,\r
462 OUT UINT8 **SignerChainCerts,\r
463 OUT UINTN *ChainLength,\r
464 OUT UINT8 **UnchainCerts,\r
465 OUT UINTN *UnchainLength\r
466 )\r
467{\r
468 BOOLEAN Status;\r
469 UINT8 *NewP7Data;\r
470 UINTN NewP7Length;\r
471 BOOLEAN Wrapped;\r
472 UINT8 Index;\r
473 PKCS7 *Pkcs7;\r
f56b11d2
QL
474 X509_STORE_CTX *CertCtx;\r
475 STACK_OF(X509) *CtxChain;\r
476 STACK_OF(X509) *CtxUntrusted;\r
477 X509 *CtxCert;\r
45419de6
QL
478 STACK_OF(X509) *Signers;\r
479 X509 *Signer;\r
480 X509 *Cert;\r
45419de6 481 X509 *Issuer;\r
f56b11d2 482 X509_NAME *IssuerName;\r
45419de6
QL
483 UINT8 *CertBuf;\r
484 UINT8 *OldBuf;\r
485 UINTN BufferSize;\r
486 UINTN OldSize;\r
487 UINT8 *SingleCert;\r
488 UINTN CertSize;\r
489\r
490 //\r
491 // Initializations\r
492 //\r
493 Status = FALSE;\r
494 NewP7Data = NULL;\r
495 Pkcs7 = NULL;\r
f56b11d2
QL
496 CertCtx = NULL;\r
497 CtxChain = NULL;\r
498 CtxCert = NULL;\r
499 CtxUntrusted = NULL;\r
45419de6 500 Cert = NULL;\r
45419de6
QL
501 SingleCert = NULL;\r
502 CertBuf = NULL;\r
503 OldBuf = NULL;\r
504 Signers = NULL;\r
505\r
07cae065
HW
506 ZeroMem (&CertCtx, sizeof (CertCtx));\r
507\r
45419de6
QL
508 //\r
509 // Parameter Checking\r
510 //\r
511 if ((P7Data == NULL) || (SignerChainCerts == NULL) || (ChainLength == NULL) ||\r
512 (UnchainCerts == NULL) || (UnchainLength == NULL) || (P7Length > INT_MAX)) {\r
513 return Status;\r
514 }\r
515\r
516 *SignerChainCerts = NULL;\r
517 *ChainLength = 0;\r
518 *UnchainCerts = NULL;\r
519 *UnchainLength = 0;\r
520\r
521 //\r
522 // Construct a new PKCS#7 data wrapping with ContentInfo structure if needed.\r
523 //\r
524 Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &NewP7Data, &NewP7Length);\r
525 if (!Status || (NewP7Length > INT_MAX)) {\r
526 goto _Error;\r
527 }\r
528\r
529 //\r
530 // Decodes PKCS#7 SignedData\r
531 //\r
532 Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **) &NewP7Data, (int) NewP7Length);\r
533 if ((Pkcs7 == NULL) || (!PKCS7_type_is_signed (Pkcs7))) {\r
534 goto _Error;\r
535 }\r
536\r
537 //\r
538 // Obtains Signer's Certificate from PKCS#7 data\r
539 // NOTE: Only one signer case will be handled in this function, which means SignerInfos\r
0f5f6b3d 540 // should include only one signer's certificate.\r
45419de6
QL
541 //\r
542 Signers = PKCS7_get0_signers (Pkcs7, NULL, PKCS7_BINARY);\r
543 if ((Signers == NULL) || (sk_X509_num (Signers) != 1)) {\r
544 goto _Error;\r
545 }\r
546 Signer = sk_X509_value (Signers, 0);\r
547\r
f56b11d2
QL
548 CertCtx = X509_STORE_CTX_new ();\r
549 if (CertCtx == NULL) {\r
550 goto _Error;\r
551 }\r
552 if (!X509_STORE_CTX_init (CertCtx, NULL, Signer, Pkcs7->d.sign->cert)) {\r
45419de6
QL
553 goto _Error;\r
554 }\r
555 //\r
556 // Initialize Chained & Untrusted stack\r
557 //\r
f56b11d2
QL
558 CtxChain = X509_STORE_CTX_get0_chain (CertCtx);\r
559 CtxCert = X509_STORE_CTX_get0_cert (CertCtx);\r
560 if (CtxChain == NULL) {\r
561 if (((CtxChain = sk_X509_new_null ()) == NULL) ||\r
562 (!sk_X509_push (CtxChain, CtxCert))) {\r
45419de6
QL
563 goto _Error;\r
564 }\r
565 }\r
f56b11d2 566 CtxUntrusted = X509_STORE_CTX_get0_untrusted (CertCtx);\r
a9fb7b78
LQ
567 if (CtxUntrusted != NULL) {\r
568 (VOID)sk_X509_delete_ptr (CtxUntrusted, Signer);\r
569 }\r
45419de6
QL
570\r
571 //\r
572 // Build certificates stack chained from Signer's certificate.\r
573 //\r
574 Cert = Signer;\r
575 for (; ;) {\r
576 //\r
577 // Self-Issue checking\r
578 //\r
f56b11d2
QL
579 Issuer = NULL;\r
580 if (X509_STORE_CTX_get1_issuer (&Issuer, CertCtx, Cert) == 1) {\r
581 if (X509_cmp (Issuer, Cert) == 0) {\r
582 break;\r
583 }\r
45419de6
QL
584 }\r
585\r
586 //\r
587 // Found the issuer of the current certificate\r
588 //\r
f56b11d2 589 if (CtxUntrusted != NULL) {\r
45419de6 590 Issuer = NULL;\r
f56b11d2
QL
591 IssuerName = X509_get_issuer_name (Cert);\r
592 Issuer = X509_find_by_subject (CtxUntrusted, IssuerName);\r
45419de6 593 if (Issuer != NULL) {\r
f56b11d2 594 if (!sk_X509_push (CtxChain, Issuer)) {\r
45419de6
QL
595 goto _Error;\r
596 }\r
f56b11d2 597 (VOID)sk_X509_delete_ptr (CtxUntrusted, Issuer);\r
45419de6
QL
598\r
599 Cert = Issuer;\r
600 continue;\r
601 }\r
602 }\r
603\r
604 break;\r
605 }\r
606\r
607 //\r
608 // Converts Chained and Untrusted Certificate to Certificate Buffer in following format:\r
609 // UINT8 CertNumber;\r
610 // UINT32 Cert1Length;\r
611 // UINT8 Cert1[];\r
612 // UINT32 Cert2Length;\r
613 // UINT8 Cert2[];\r
614 // ...\r
615 // UINT32 CertnLength;\r
616 // UINT8 Certn[];\r
617 //\r
618\r
f56b11d2 619 if (CtxChain != NULL) {\r
45419de6 620 BufferSize = sizeof (UINT8);\r
45419de6
QL
621 CertBuf = NULL;\r
622\r
623 for (Index = 0; ; Index++) {\r
f56b11d2 624 Status = X509PopCertificate (CtxChain, &SingleCert, &CertSize);\r
45419de6
QL
625 if (!Status) {\r
626 break;\r
627 }\r
628\r
629 OldSize = BufferSize;\r
630 OldBuf = CertBuf;\r
631 BufferSize = OldSize + CertSize + sizeof (UINT32);\r
632 CertBuf = malloc (BufferSize);\r
633\r
634 if (CertBuf == NULL) {\r
635 Status = FALSE;\r
636 goto _Error;\r
637 }\r
638 if (OldBuf != NULL) {\r
639 CopyMem (CertBuf, OldBuf, OldSize);\r
640 free (OldBuf);\r
641 OldBuf = NULL;\r
642 }\r
643\r
644 WriteUnaligned32 ((UINT32 *) (CertBuf + OldSize), (UINT32) CertSize);\r
645 CopyMem (CertBuf + OldSize + sizeof (UINT32), SingleCert, CertSize);\r
646\r
647 free (SingleCert);\r
648 SingleCert = NULL;\r
649 }\r
650\r
651 if (CertBuf != NULL) {\r
652 //\r
653 // Update CertNumber.\r
654 //\r
655 CertBuf[0] = Index;\r
656\r
657 *SignerChainCerts = CertBuf;\r
658 *ChainLength = BufferSize;\r
659 }\r
660 }\r
661\r
f56b11d2 662 if (CtxUntrusted != NULL) {\r
45419de6 663 BufferSize = sizeof (UINT8);\r
45419de6
QL
664 CertBuf = NULL;\r
665\r
666 for (Index = 0; ; Index++) {\r
f56b11d2 667 Status = X509PopCertificate (CtxUntrusted, &SingleCert, &CertSize);\r
45419de6
QL
668 if (!Status) {\r
669 break;\r
670 }\r
671\r
672 OldSize = BufferSize;\r
673 OldBuf = CertBuf;\r
674 BufferSize = OldSize + CertSize + sizeof (UINT32);\r
675 CertBuf = malloc (BufferSize);\r
676\r
677 if (CertBuf == NULL) {\r
678 Status = FALSE;\r
679 goto _Error;\r
680 }\r
681 if (OldBuf != NULL) {\r
682 CopyMem (CertBuf, OldBuf, OldSize);\r
683 free (OldBuf);\r
684 OldBuf = NULL;\r
685 }\r
686\r
687 WriteUnaligned32 ((UINT32 *) (CertBuf + OldSize), (UINT32) CertSize);\r
688 CopyMem (CertBuf + OldSize + sizeof (UINT32), SingleCert, CertSize);\r
689\r
690 free (SingleCert);\r
691 SingleCert = NULL;\r
692 }\r
693\r
694 if (CertBuf != NULL) {\r
695 //\r
696 // Update CertNumber.\r
697 //\r
698 CertBuf[0] = Index;\r
699\r
700 *UnchainCerts = CertBuf;\r
701 *UnchainLength = BufferSize;\r
702 }\r
703 }\r
704\r
705 Status = TRUE;\r
706\r
707_Error:\r
708 //\r
709 // Release Resources.\r
710 //\r
711 if (!Wrapped && (NewP7Data != NULL)) {\r
712 free (NewP7Data);\r
713 }\r
714\r
715 if (Pkcs7 != NULL) {\r
716 PKCS7_free (Pkcs7);\r
717 }\r
718 sk_X509_free (Signers);\r
719\r
a9fb7b78
LQ
720 if (CertCtx != NULL) {\r
721 X509_STORE_CTX_cleanup (CertCtx);\r
722 X509_STORE_CTX_free (CertCtx);\r
723 }\r
45419de6
QL
724\r
725 if (SingleCert != NULL) {\r
726 free (SingleCert);\r
727 }\r
728\r
729 if (OldBuf != NULL) {\r
730 free (OldBuf);\r
731 }\r
732\r
733 if (!Status && (CertBuf != NULL)) {\r
734 free (CertBuf);\r
735 *SignerChainCerts = NULL;\r
736 *UnchainCerts = NULL;\r
737 }\r
738\r
739 return Status;\r
740}\r
741\r
532616bb 742/**\r
2998af86 743 Verifies the validity of a PKCS#7 signed data as described in "PKCS #7:\r
532616bb 744 Cryptographic Message Syntax Standard". The input signed data could be wrapped\r
745 in a ContentInfo structure.\r
746\r
747 If P7Data, TrustedCert or InData is NULL, then return FALSE.\r
2998af86 748 If P7Length, CertLength or DataLength overflow, then return FALSE.\r
532616bb 749\r
750 Caution: This function may receive untrusted input.\r
751 UEFI Authenticated Variable is external input, so this function will do basic\r
752 check for PKCS#7 data structure.\r
753\r
754 @param[in] P7Data Pointer to the PKCS#7 message to verify.\r
755 @param[in] P7Length Length of the PKCS#7 message in bytes.\r
756 @param[in] TrustedCert Pointer to a trusted/root certificate encoded in DER, which\r
757 is used for certificate chain verification.\r
758 @param[in] CertLength Length of the trusted certificate in bytes.\r
759 @param[in] InData Pointer to the content to be verified.\r
760 @param[in] DataLength Length of InData in bytes.\r
761\r
762 @retval TRUE The specified PKCS#7 signed data is valid.\r
763 @retval FALSE Invalid PKCS#7 signed data.\r
764\r
765**/\r
766BOOLEAN\r
767EFIAPI\r
768Pkcs7Verify (\r
769 IN CONST UINT8 *P7Data,\r
770 IN UINTN P7Length,\r
771 IN CONST UINT8 *TrustedCert,\r
772 IN UINTN CertLength,\r
773 IN CONST UINT8 *InData,\r
774 IN UINTN DataLength\r
775 )\r
776{\r
777 PKCS7 *Pkcs7;\r
532616bb 778 BIO *DataBio;\r
779 BOOLEAN Status;\r
780 X509 *Cert;\r
781 X509_STORE *CertStore;\r
782 UINT8 *SignedData;\r
1463ce18 783 CONST UINT8 *Temp;\r
532616bb 784 UINTN SignedDataSize;\r
785 BOOLEAN Wrapped;\r
786\r
787 //\r
788 // Check input parameters.\r
789 //\r
2ac68e8b 790 if (P7Data == NULL || TrustedCert == NULL || InData == NULL ||\r
532616bb 791 P7Length > INT_MAX || CertLength > INT_MAX || DataLength > INT_MAX) {\r
792 return FALSE;\r
793 }\r
2ac68e8b 794\r
532616bb 795 Pkcs7 = NULL;\r
532616bb 796 DataBio = NULL;\r
797 Cert = NULL;\r
798 CertStore = NULL;\r
799\r
800 //\r
801 // Register & Initialize necessary digest algorithms for PKCS#7 Handling\r
802 //\r
dda39f3a 803 if (EVP_add_digest (EVP_md5 ()) == 0) {\r
804 return FALSE;\r
805 }\r
806 if (EVP_add_digest (EVP_sha1 ()) == 0) {\r
807 return FALSE;\r
808 }\r
809 if (EVP_add_digest (EVP_sha256 ()) == 0) {\r
810 return FALSE;\r
811 }\r
2ac68e8b
QL
812 if (EVP_add_digest (EVP_sha384 ()) == 0) {\r
813 return FALSE;\r
814 }\r
815 if (EVP_add_digest (EVP_sha512 ()) == 0) {\r
816 return FALSE;\r
817 }\r
dda39f3a 818 if (EVP_add_digest_alias (SN_sha1WithRSAEncryption, SN_sha1WithRSA) == 0) {\r
819 return FALSE;\r
820 }\r
821\r
532616bb 822 Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &SignedData, &SignedDataSize);\r
823 if (!Status) {\r
824 return Status;\r
825 }\r
826\r
827 Status = FALSE;\r
2ac68e8b 828\r
532616bb 829 //\r
830 // Retrieve PKCS#7 Data (DER encoding)\r
831 //\r
832 if (SignedDataSize > INT_MAX) {\r
833 goto _Exit;\r
834 }\r
835\r
836 Temp = SignedData;\r
837 Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **) &Temp, (int) SignedDataSize);\r
838 if (Pkcs7 == NULL) {\r
839 goto _Exit;\r
840 }\r
841\r
842 //\r
843 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)\r
844 //\r
845 if (!PKCS7_type_is_signed (Pkcs7)) {\r
846 goto _Exit;\r
847 }\r
848\r
849 //\r
850 // Read DER-encoded root certificate and Construct X509 Certificate\r
851 //\r
1463ce18
QL
852 Temp = TrustedCert;\r
853 Cert = d2i_X509 (NULL, &Temp, (long) CertLength);\r
532616bb 854 if (Cert == NULL) {\r
855 goto _Exit;\r
856 }\r
857\r
858 //\r
859 // Setup X509 Store for trusted certificate\r
860 //\r
861 CertStore = X509_STORE_new ();\r
862 if (CertStore == NULL) {\r
863 goto _Exit;\r
864 }\r
865 if (!(X509_STORE_add_cert (CertStore, Cert))) {\r
866 goto _Exit;\r
867 }\r
868\r
532616bb 869 //\r
870 // For generic PKCS#7 handling, InData may be NULL if the content is present\r
871 // in PKCS#7 structure. So ignore NULL checking here.\r
872 //\r
873 DataBio = BIO_new (BIO_s_mem ());\r
5b2956ea
YT
874 if (DataBio == NULL) {\r
875 goto _Exit;\r
876 }\r
877\r
878 if (BIO_write (DataBio, InData, (int) DataLength) <= 0) {\r
879 goto _Exit;\r
880 }\r
532616bb 881\r
68547181
DW
882 //\r
883 // Allow partial certificate chains, terminated by a non-self-signed but\r
de0408be 884 // still trusted intermediate certificate. Also disable time checks.\r
68547181 885 //\r
de0408be
DW
886 X509_STORE_set_flags (CertStore,\r
887 X509_V_FLAG_PARTIAL_CHAIN | X509_V_FLAG_NO_CHECK_TIME);\r
68547181 888\r
02ee8d3b 889 //\r
890 // OpenSSL PKCS7 Verification by default checks for SMIME (email signing) and\r
891 // doesn't support the extended key usage for Authenticode Code Signing.\r
892 // Bypass the certificate purpose checking by enabling any purposes setting.\r
893 //\r
894 X509_STORE_set_purpose (CertStore, X509_PURPOSE_ANY);\r
895\r
532616bb 896 //\r
897 // Verifies the PKCS#7 signedData structure\r
898 //\r
899 Status = (BOOLEAN) PKCS7_verify (Pkcs7, NULL, CertStore, DataBio, NULL, PKCS7_BINARY);\r
900\r
901_Exit:\r
902 //\r
903 // Release Resources\r
904 //\r
905 BIO_free (DataBio);\r
532616bb 906 X509_free (Cert);\r
907 X509_STORE_free (CertStore);\r
908 PKCS7_free (Pkcs7);\r
909\r
910 if (!Wrapped) {\r
911 OPENSSL_free (SignedData);\r
912 }\r
913\r
914 return Status;\r
afeb55e4
QL
915}\r
916\r
917/**\r
918 Extracts the attached content from a PKCS#7 signed data if existed. The input signed\r
919 data could be wrapped in a ContentInfo structure.\r
920\r
921 If P7Data, Content, or ContentSize is NULL, then return FALSE. If P7Length overflow,\r
2998af86 922 then return FALSE. If the P7Data is not correctly formatted, then return FALSE.\r
afeb55e4
QL
923\r
924 Caution: This function may receive untrusted input. So this function will do\r
925 basic check for PKCS#7 data structure.\r
926\r
927 @param[in] P7Data Pointer to the PKCS#7 signed data to process.\r
928 @param[in] P7Length Length of the PKCS#7 signed data in bytes.\r
929 @param[out] Content Pointer to the extracted content from the PKCS#7 signedData.\r
6fe575d0 930 It's caller's responsibility to free the buffer with FreePool().\r
afeb55e4
QL
931 @param[out] ContentSize The size of the extracted content in bytes.\r
932\r
933 @retval TRUE The P7Data was correctly formatted for processing.\r
934 @retval FALSE The P7Data was not correctly formatted for processing.\r
935\r
0c9fc4b1 936**/\r
afeb55e4
QL
937BOOLEAN\r
938EFIAPI\r
939Pkcs7GetAttachedContent (\r
940 IN CONST UINT8 *P7Data,\r
941 IN UINTN P7Length,\r
942 OUT VOID **Content,\r
943 OUT UINTN *ContentSize\r
944 )\r
945{\r
946 BOOLEAN Status;\r
947 PKCS7 *Pkcs7;\r
948 UINT8 *SignedData;\r
949 UINTN SignedDataSize;\r
950 BOOLEAN Wrapped;\r
951 CONST UINT8 *Temp;\r
952 ASN1_OCTET_STRING *OctStr;\r
953\r
afeb55e4
QL
954 //\r
955 // Check input parameter.\r
956 //\r
957 if ((P7Data == NULL) || (P7Length > INT_MAX) || (Content == NULL) || (ContentSize == NULL)) {\r
958 return FALSE;\r
959 }\r
960\r
2aabd146
QL
961 *Content = NULL;\r
962 Pkcs7 = NULL;\r
963 SignedData = NULL;\r
964 OctStr = NULL;\r
965\r
afeb55e4
QL
966 Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &SignedData, &SignedDataSize);\r
967 if (!Status || (SignedDataSize > INT_MAX)) {\r
968 goto _Exit;\r
969 }\r
970\r
971 Status = FALSE;\r
972\r
973 //\r
974 // Decoding PKCS#7 SignedData\r
975 //\r
976 Temp = SignedData;\r
977 Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **)&Temp, (int)SignedDataSize);\r
978 if (Pkcs7 == NULL) {\r
979 goto _Exit;\r
980 }\r
981\r
982 //\r
983 // The type of Pkcs7 must be signedData\r
984 //\r
985 if (!PKCS7_type_is_signed (Pkcs7)) {\r
986 goto _Exit;\r
987 }\r
988\r
989 //\r
990 // Check for detached or attached content\r
991 //\r
992 if (PKCS7_get_detached (Pkcs7)) {\r
993 //\r
994 // No Content supplied for PKCS7 detached signedData\r
995 //\r
996 *Content = NULL;\r
997 *ContentSize = 0;\r
998 } else {\r
999 //\r
1000 // Retrieve the attached content in PKCS7 signedData\r
1001 //\r
1002 OctStr = Pkcs7->d.sign->contents->d.data;\r
1003 if ((OctStr->length > 0) && (OctStr->data != NULL)) {\r
1004 *ContentSize = OctStr->length;\r
6fe575d0 1005 *Content = AllocatePool (*ContentSize);\r
2aabd146
QL
1006 if (*Content == NULL) {\r
1007 *ContentSize = 0;\r
1008 goto _Exit;\r
1009 }\r
afeb55e4
QL
1010 CopyMem (*Content, OctStr->data, *ContentSize);\r
1011 }\r
1012 }\r
1013 Status = TRUE;\r
1014\r
1015_Exit:\r
1016 //\r
1017 // Release Resources\r
1018 //\r
1019 PKCS7_free (Pkcs7);\r
1020\r
1021 if (!Wrapped) {\r
1022 OPENSSL_free (SignedData);\r
1023 }\r
1024\r
1025 return Status;\r
1026}\r