]> git.proxmox.com Git - mirror_edk2.git/blame - CryptoPkg/Library/BaseCryptLib/Pk/CryptPkcs7Verify.c
CryptoPkg/BaseCryptLib: Fix mismatched memory allocation/free
[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
532616bb 245 @param[out] StackLength Length of signer's certificates in bytes.\r
246 @param[out] TrustedCert Pointer to a trusted certificate from Signer's certificates.\r
6fe575d0
LQ
247 It's caller's responsibility to free the buffer with\r
248 Pkcs7FreeSigners().\r
532616bb 249 @param[out] CertLength Length of the trusted certificate in bytes.\r
250\r
251 @retval TRUE The operation is finished successfully.\r
252 @retval FALSE Error occurs during the operation.\r
253\r
254**/\r
255BOOLEAN\r
256EFIAPI\r
257Pkcs7GetSigners (\r
258 IN CONST UINT8 *P7Data,\r
259 IN UINTN P7Length,\r
260 OUT UINT8 **CertStack,\r
261 OUT UINTN *StackLength,\r
262 OUT UINT8 **TrustedCert,\r
263 OUT UINTN *CertLength\r
264 )\r
265{\r
266 PKCS7 *Pkcs7;\r
267 BOOLEAN Status;\r
268 UINT8 *SignedData;\r
1463ce18 269 CONST UINT8 *Temp;\r
532616bb 270 UINTN SignedDataSize;\r
271 BOOLEAN Wrapped;\r
272 STACK_OF(X509) *Stack;\r
273 UINT8 Index;\r
274 UINT8 *CertBuf;\r
275 UINT8 *OldBuf;\r
276 UINTN BufferSize;\r
277 UINTN OldSize;\r
278 UINT8 *SingleCert;\r
279 UINTN SingleCertSize;\r
280\r
281 if ((P7Data == NULL) || (CertStack == NULL) || (StackLength == NULL) ||\r
282 (TrustedCert == NULL) || (CertLength == NULL) || (P7Length > INT_MAX)) {\r
283 return FALSE;\r
284 }\r
2ac68e8b 285\r
532616bb 286 Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &SignedData, &SignedDataSize);\r
287 if (!Status) {\r
288 return Status;\r
289 }\r
290\r
291 Status = FALSE;\r
292 Pkcs7 = NULL;\r
293 Stack = NULL;\r
294 CertBuf = NULL;\r
295 OldBuf = NULL;\r
296 SingleCert = NULL;\r
297\r
298 //\r
299 // Retrieve PKCS#7 Data (DER encoding)\r
300 //\r
301 if (SignedDataSize > INT_MAX) {\r
302 goto _Exit;\r
303 }\r
304\r
305 Temp = SignedData;\r
306 Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **) &Temp, (int) SignedDataSize);\r
307 if (Pkcs7 == NULL) {\r
308 goto _Exit;\r
309 }\r
310\r
311 //\r
312 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)\r
313 //\r
314 if (!PKCS7_type_is_signed (Pkcs7)) {\r
315 goto _Exit;\r
316 }\r
317\r
318 Stack = PKCS7_get0_signers(Pkcs7, NULL, PKCS7_BINARY);\r
319 if (Stack == NULL) {\r
320 goto _Exit;\r
321 }\r
322\r
323 //\r
324 // Convert CertStack to buffer in following format:\r
325 // UINT8 CertNumber;\r
326 // UINT32 Cert1Length;\r
327 // UINT8 Cert1[];\r
328 // UINT32 Cert2Length;\r
329 // UINT8 Cert2[];\r
330 // ...\r
331 // UINT32 CertnLength;\r
332 // UINT8 Certn[];\r
333 //\r
334 BufferSize = sizeof (UINT8);\r
335 OldSize = BufferSize;\r
2ac68e8b 336\r
532616bb 337 for (Index = 0; ; Index++) {\r
338 Status = X509PopCertificate (Stack, &SingleCert, &SingleCertSize);\r
339 if (!Status) {\r
340 break;\r
341 }\r
342\r
343 OldSize = BufferSize;\r
344 OldBuf = CertBuf;\r
345 BufferSize = OldSize + SingleCertSize + sizeof (UINT32);\r
346 CertBuf = malloc (BufferSize);\r
347\r
348 if (CertBuf == NULL) {\r
349 goto _Exit;\r
350 }\r
351\r
352 if (OldBuf != NULL) {\r
353 CopyMem (CertBuf, OldBuf, OldSize);\r
354 free (OldBuf);\r
355 OldBuf = NULL;\r
356 }\r
357\r
358 WriteUnaligned32 ((UINT32 *) (CertBuf + OldSize), (UINT32) SingleCertSize);\r
359 CopyMem (CertBuf + OldSize + sizeof (UINT32), SingleCert, SingleCertSize);\r
360\r
361 free (SingleCert);\r
362 SingleCert = NULL;\r
363 }\r
364\r
365 if (CertBuf != NULL) {\r
366 //\r
367 // Update CertNumber.\r
368 //\r
369 CertBuf[0] = Index;\r
370\r
371 *CertLength = BufferSize - OldSize - sizeof (UINT32);\r
372 *TrustedCert = malloc (*CertLength);\r
373 if (*TrustedCert == NULL) {\r
374 goto _Exit;\r
375 }\r
376\r
377 CopyMem (*TrustedCert, CertBuf + OldSize + sizeof (UINT32), *CertLength);\r
378 *CertStack = CertBuf;\r
379 *StackLength = BufferSize;\r
380 Status = TRUE;\r
2ac68e8b 381 }\r
532616bb 382\r
383_Exit:\r
384 //\r
385 // Release Resources\r
386 //\r
387 if (!Wrapped) {\r
388 free (SignedData);\r
389 }\r
390\r
391 if (Pkcs7 != NULL) {\r
392 PKCS7_free (Pkcs7);\r
393 }\r
394\r
395 if (Stack != NULL) {\r
396 sk_X509_pop_free(Stack, X509_free);\r
397 }\r
398\r
399 if (SingleCert != NULL) {\r
400 free (SingleCert);\r
401 }\r
402\r
403 if (!Status && (CertBuf != NULL)) {\r
404 free (CertBuf);\r
405 *CertStack = NULL;\r
406 }\r
407\r
408 if (OldBuf != NULL) {\r
409 free (OldBuf);\r
410 }\r
2ac68e8b 411\r
532616bb 412 return Status;\r
413}\r
414\r
415/**\r
416 Wrap function to use free() to free allocated memory for certificates.\r
417\r
418 @param[in] Certs Pointer to the certificates to be freed.\r
419\r
420**/\r
421VOID\r
422EFIAPI\r
423Pkcs7FreeSigners (\r
424 IN UINT8 *Certs\r
425 )\r
426{\r
427 if (Certs == NULL) {\r
428 return;\r
429 }\r
430\r
431 free (Certs);\r
432}\r
433\r
45419de6
QL
434/**\r
435 Retrieves all embedded certificates from PKCS#7 signed data as described in "PKCS #7:\r
436 Cryptographic Message Syntax Standard", and outputs two certificate lists chained and\r
437 unchained to the signer's certificates.\r
438 The input signed data could be wrapped in a ContentInfo structure.\r
439\r
440 @param[in] P7Data Pointer to the PKCS#7 message.\r
441 @param[in] P7Length Length of the PKCS#7 message in bytes.\r
0f5f6b3d 442 @param[out] SignerChainCerts Pointer to the certificates list chained to signer's\r
6fe575d0
LQ
443 certificate. It's caller's responsibility to free the buffer\r
444 with Pkcs7FreeSigners().\r
45419de6
QL
445 @param[out] ChainLength Length of the chained certificates list buffer in bytes.\r
446 @param[out] UnchainCerts Pointer to the unchained certificates lists. It's caller's\r
6fe575d0 447 responsibility to free the buffer with Pkcs7FreeSigners().\r
45419de6
QL
448 @param[out] UnchainLength Length of the unchained certificates list buffer in bytes.\r
449\r
450 @retval TRUE The operation is finished successfully.\r
451 @retval FALSE Error occurs during the operation.\r
452\r
453**/\r
454BOOLEAN\r
455EFIAPI\r
456Pkcs7GetCertificatesList (\r
457 IN CONST UINT8 *P7Data,\r
458 IN UINTN P7Length,\r
459 OUT UINT8 **SignerChainCerts,\r
460 OUT UINTN *ChainLength,\r
461 OUT UINT8 **UnchainCerts,\r
462 OUT UINTN *UnchainLength\r
463 )\r
464{\r
465 BOOLEAN Status;\r
466 UINT8 *NewP7Data;\r
467 UINTN NewP7Length;\r
468 BOOLEAN Wrapped;\r
469 UINT8 Index;\r
470 PKCS7 *Pkcs7;\r
f56b11d2
QL
471 X509_STORE_CTX *CertCtx;\r
472 STACK_OF(X509) *CtxChain;\r
473 STACK_OF(X509) *CtxUntrusted;\r
474 X509 *CtxCert;\r
45419de6
QL
475 STACK_OF(X509) *Signers;\r
476 X509 *Signer;\r
477 X509 *Cert;\r
45419de6 478 X509 *Issuer;\r
f56b11d2 479 X509_NAME *IssuerName;\r
45419de6
QL
480 UINT8 *CertBuf;\r
481 UINT8 *OldBuf;\r
482 UINTN BufferSize;\r
483 UINTN OldSize;\r
484 UINT8 *SingleCert;\r
485 UINTN CertSize;\r
486\r
487 //\r
488 // Initializations\r
489 //\r
490 Status = FALSE;\r
491 NewP7Data = NULL;\r
492 Pkcs7 = NULL;\r
f56b11d2
QL
493 CertCtx = NULL;\r
494 CtxChain = NULL;\r
495 CtxCert = NULL;\r
496 CtxUntrusted = NULL;\r
45419de6 497 Cert = NULL;\r
45419de6
QL
498 SingleCert = NULL;\r
499 CertBuf = NULL;\r
500 OldBuf = NULL;\r
501 Signers = NULL;\r
502\r
07cae065
HW
503 ZeroMem (&CertCtx, sizeof (CertCtx));\r
504\r
45419de6
QL
505 //\r
506 // Parameter Checking\r
507 //\r
508 if ((P7Data == NULL) || (SignerChainCerts == NULL) || (ChainLength == NULL) ||\r
509 (UnchainCerts == NULL) || (UnchainLength == NULL) || (P7Length > INT_MAX)) {\r
510 return Status;\r
511 }\r
512\r
513 *SignerChainCerts = NULL;\r
514 *ChainLength = 0;\r
515 *UnchainCerts = NULL;\r
516 *UnchainLength = 0;\r
517\r
518 //\r
519 // Construct a new PKCS#7 data wrapping with ContentInfo structure if needed.\r
520 //\r
521 Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &NewP7Data, &NewP7Length);\r
522 if (!Status || (NewP7Length > INT_MAX)) {\r
523 goto _Error;\r
524 }\r
525\r
526 //\r
527 // Decodes PKCS#7 SignedData\r
528 //\r
529 Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **) &NewP7Data, (int) NewP7Length);\r
530 if ((Pkcs7 == NULL) || (!PKCS7_type_is_signed (Pkcs7))) {\r
531 goto _Error;\r
532 }\r
533\r
534 //\r
535 // Obtains Signer's Certificate from PKCS#7 data\r
536 // NOTE: Only one signer case will be handled in this function, which means SignerInfos\r
0f5f6b3d 537 // should include only one signer's certificate.\r
45419de6
QL
538 //\r
539 Signers = PKCS7_get0_signers (Pkcs7, NULL, PKCS7_BINARY);\r
540 if ((Signers == NULL) || (sk_X509_num (Signers) != 1)) {\r
541 goto _Error;\r
542 }\r
543 Signer = sk_X509_value (Signers, 0);\r
544\r
f56b11d2
QL
545 CertCtx = X509_STORE_CTX_new ();\r
546 if (CertCtx == NULL) {\r
547 goto _Error;\r
548 }\r
549 if (!X509_STORE_CTX_init (CertCtx, NULL, Signer, Pkcs7->d.sign->cert)) {\r
45419de6
QL
550 goto _Error;\r
551 }\r
552 //\r
553 // Initialize Chained & Untrusted stack\r
554 //\r
f56b11d2
QL
555 CtxChain = X509_STORE_CTX_get0_chain (CertCtx);\r
556 CtxCert = X509_STORE_CTX_get0_cert (CertCtx);\r
557 if (CtxChain == NULL) {\r
558 if (((CtxChain = sk_X509_new_null ()) == NULL) ||\r
559 (!sk_X509_push (CtxChain, CtxCert))) {\r
45419de6
QL
560 goto _Error;\r
561 }\r
562 }\r
f56b11d2 563 CtxUntrusted = X509_STORE_CTX_get0_untrusted (CertCtx);\r
a9fb7b78
LQ
564 if (CtxUntrusted != NULL) {\r
565 (VOID)sk_X509_delete_ptr (CtxUntrusted, Signer);\r
566 }\r
45419de6
QL
567\r
568 //\r
569 // Build certificates stack chained from Signer's certificate.\r
570 //\r
571 Cert = Signer;\r
572 for (; ;) {\r
573 //\r
574 // Self-Issue checking\r
575 //\r
f56b11d2
QL
576 Issuer = NULL;\r
577 if (X509_STORE_CTX_get1_issuer (&Issuer, CertCtx, Cert) == 1) {\r
578 if (X509_cmp (Issuer, Cert) == 0) {\r
579 break;\r
580 }\r
45419de6
QL
581 }\r
582\r
583 //\r
584 // Found the issuer of the current certificate\r
585 //\r
f56b11d2 586 if (CtxUntrusted != NULL) {\r
45419de6 587 Issuer = NULL;\r
f56b11d2
QL
588 IssuerName = X509_get_issuer_name (Cert);\r
589 Issuer = X509_find_by_subject (CtxUntrusted, IssuerName);\r
45419de6 590 if (Issuer != NULL) {\r
f56b11d2 591 if (!sk_X509_push (CtxChain, Issuer)) {\r
45419de6
QL
592 goto _Error;\r
593 }\r
f56b11d2 594 (VOID)sk_X509_delete_ptr (CtxUntrusted, Issuer);\r
45419de6
QL
595\r
596 Cert = Issuer;\r
597 continue;\r
598 }\r
599 }\r
600\r
601 break;\r
602 }\r
603\r
604 //\r
605 // Converts Chained and Untrusted Certificate to Certificate Buffer in following format:\r
606 // UINT8 CertNumber;\r
607 // UINT32 Cert1Length;\r
608 // UINT8 Cert1[];\r
609 // UINT32 Cert2Length;\r
610 // UINT8 Cert2[];\r
611 // ...\r
612 // UINT32 CertnLength;\r
613 // UINT8 Certn[];\r
614 //\r
615\r
f56b11d2 616 if (CtxChain != NULL) {\r
45419de6 617 BufferSize = sizeof (UINT8);\r
45419de6
QL
618 CertBuf = NULL;\r
619\r
620 for (Index = 0; ; Index++) {\r
f56b11d2 621 Status = X509PopCertificate (CtxChain, &SingleCert, &CertSize);\r
45419de6
QL
622 if (!Status) {\r
623 break;\r
624 }\r
625\r
626 OldSize = BufferSize;\r
627 OldBuf = CertBuf;\r
628 BufferSize = OldSize + CertSize + sizeof (UINT32);\r
629 CertBuf = malloc (BufferSize);\r
630\r
631 if (CertBuf == NULL) {\r
632 Status = FALSE;\r
633 goto _Error;\r
634 }\r
635 if (OldBuf != NULL) {\r
636 CopyMem (CertBuf, OldBuf, OldSize);\r
637 free (OldBuf);\r
638 OldBuf = NULL;\r
639 }\r
640\r
641 WriteUnaligned32 ((UINT32 *) (CertBuf + OldSize), (UINT32) CertSize);\r
642 CopyMem (CertBuf + OldSize + sizeof (UINT32), SingleCert, CertSize);\r
643\r
644 free (SingleCert);\r
645 SingleCert = NULL;\r
646 }\r
647\r
648 if (CertBuf != NULL) {\r
649 //\r
650 // Update CertNumber.\r
651 //\r
652 CertBuf[0] = Index;\r
653\r
654 *SignerChainCerts = CertBuf;\r
655 *ChainLength = BufferSize;\r
656 }\r
657 }\r
658\r
f56b11d2 659 if (CtxUntrusted != NULL) {\r
45419de6 660 BufferSize = sizeof (UINT8);\r
45419de6
QL
661 CertBuf = NULL;\r
662\r
663 for (Index = 0; ; Index++) {\r
f56b11d2 664 Status = X509PopCertificate (CtxUntrusted, &SingleCert, &CertSize);\r
45419de6
QL
665 if (!Status) {\r
666 break;\r
667 }\r
668\r
669 OldSize = BufferSize;\r
670 OldBuf = CertBuf;\r
671 BufferSize = OldSize + CertSize + sizeof (UINT32);\r
672 CertBuf = malloc (BufferSize);\r
673\r
674 if (CertBuf == NULL) {\r
675 Status = FALSE;\r
676 goto _Error;\r
677 }\r
678 if (OldBuf != NULL) {\r
679 CopyMem (CertBuf, OldBuf, OldSize);\r
680 free (OldBuf);\r
681 OldBuf = NULL;\r
682 }\r
683\r
684 WriteUnaligned32 ((UINT32 *) (CertBuf + OldSize), (UINT32) CertSize);\r
685 CopyMem (CertBuf + OldSize + sizeof (UINT32), SingleCert, CertSize);\r
686\r
687 free (SingleCert);\r
688 SingleCert = NULL;\r
689 }\r
690\r
691 if (CertBuf != NULL) {\r
692 //\r
693 // Update CertNumber.\r
694 //\r
695 CertBuf[0] = Index;\r
696\r
697 *UnchainCerts = CertBuf;\r
698 *UnchainLength = BufferSize;\r
699 }\r
700 }\r
701\r
702 Status = TRUE;\r
703\r
704_Error:\r
705 //\r
706 // Release Resources.\r
707 //\r
708 if (!Wrapped && (NewP7Data != NULL)) {\r
709 free (NewP7Data);\r
710 }\r
711\r
712 if (Pkcs7 != NULL) {\r
713 PKCS7_free (Pkcs7);\r
714 }\r
715 sk_X509_free (Signers);\r
716\r
a9fb7b78
LQ
717 if (CertCtx != NULL) {\r
718 X509_STORE_CTX_cleanup (CertCtx);\r
719 X509_STORE_CTX_free (CertCtx);\r
720 }\r
45419de6
QL
721\r
722 if (SingleCert != NULL) {\r
723 free (SingleCert);\r
724 }\r
725\r
726 if (OldBuf != NULL) {\r
727 free (OldBuf);\r
728 }\r
729\r
730 if (!Status && (CertBuf != NULL)) {\r
731 free (CertBuf);\r
732 *SignerChainCerts = NULL;\r
733 *UnchainCerts = NULL;\r
734 }\r
735\r
736 return Status;\r
737}\r
738\r
532616bb 739/**\r
2998af86 740 Verifies the validity of a PKCS#7 signed data as described in "PKCS #7:\r
532616bb 741 Cryptographic Message Syntax Standard". The input signed data could be wrapped\r
742 in a ContentInfo structure.\r
743\r
744 If P7Data, TrustedCert or InData is NULL, then return FALSE.\r
2998af86 745 If P7Length, CertLength or DataLength overflow, then return FALSE.\r
532616bb 746\r
747 Caution: This function may receive untrusted input.\r
748 UEFI Authenticated Variable is external input, so this function will do basic\r
749 check for PKCS#7 data structure.\r
750\r
751 @param[in] P7Data Pointer to the PKCS#7 message to verify.\r
752 @param[in] P7Length Length of the PKCS#7 message in bytes.\r
753 @param[in] TrustedCert Pointer to a trusted/root certificate encoded in DER, which\r
754 is used for certificate chain verification.\r
755 @param[in] CertLength Length of the trusted certificate in bytes.\r
756 @param[in] InData Pointer to the content to be verified.\r
757 @param[in] DataLength Length of InData in bytes.\r
758\r
759 @retval TRUE The specified PKCS#7 signed data is valid.\r
760 @retval FALSE Invalid PKCS#7 signed data.\r
761\r
762**/\r
763BOOLEAN\r
764EFIAPI\r
765Pkcs7Verify (\r
766 IN CONST UINT8 *P7Data,\r
767 IN UINTN P7Length,\r
768 IN CONST UINT8 *TrustedCert,\r
769 IN UINTN CertLength,\r
770 IN CONST UINT8 *InData,\r
771 IN UINTN DataLength\r
772 )\r
773{\r
774 PKCS7 *Pkcs7;\r
532616bb 775 BIO *DataBio;\r
776 BOOLEAN Status;\r
777 X509 *Cert;\r
778 X509_STORE *CertStore;\r
779 UINT8 *SignedData;\r
1463ce18 780 CONST UINT8 *Temp;\r
532616bb 781 UINTN SignedDataSize;\r
782 BOOLEAN Wrapped;\r
783\r
784 //\r
785 // Check input parameters.\r
786 //\r
2ac68e8b 787 if (P7Data == NULL || TrustedCert == NULL || InData == NULL ||\r
532616bb 788 P7Length > INT_MAX || CertLength > INT_MAX || DataLength > INT_MAX) {\r
789 return FALSE;\r
790 }\r
2ac68e8b 791\r
532616bb 792 Pkcs7 = NULL;\r
532616bb 793 DataBio = NULL;\r
794 Cert = NULL;\r
795 CertStore = NULL;\r
796\r
797 //\r
798 // Register & Initialize necessary digest algorithms for PKCS#7 Handling\r
799 //\r
dda39f3a 800 if (EVP_add_digest (EVP_md5 ()) == 0) {\r
801 return FALSE;\r
802 }\r
803 if (EVP_add_digest (EVP_sha1 ()) == 0) {\r
804 return FALSE;\r
805 }\r
806 if (EVP_add_digest (EVP_sha256 ()) == 0) {\r
807 return FALSE;\r
808 }\r
2ac68e8b
QL
809 if (EVP_add_digest (EVP_sha384 ()) == 0) {\r
810 return FALSE;\r
811 }\r
812 if (EVP_add_digest (EVP_sha512 ()) == 0) {\r
813 return FALSE;\r
814 }\r
dda39f3a 815 if (EVP_add_digest_alias (SN_sha1WithRSAEncryption, SN_sha1WithRSA) == 0) {\r
816 return FALSE;\r
817 }\r
818\r
532616bb 819 Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &SignedData, &SignedDataSize);\r
820 if (!Status) {\r
821 return Status;\r
822 }\r
823\r
824 Status = FALSE;\r
2ac68e8b 825\r
532616bb 826 //\r
827 // Retrieve PKCS#7 Data (DER encoding)\r
828 //\r
829 if (SignedDataSize > INT_MAX) {\r
830 goto _Exit;\r
831 }\r
832\r
833 Temp = SignedData;\r
834 Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **) &Temp, (int) SignedDataSize);\r
835 if (Pkcs7 == NULL) {\r
836 goto _Exit;\r
837 }\r
838\r
839 //\r
840 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)\r
841 //\r
842 if (!PKCS7_type_is_signed (Pkcs7)) {\r
843 goto _Exit;\r
844 }\r
845\r
846 //\r
847 // Read DER-encoded root certificate and Construct X509 Certificate\r
848 //\r
1463ce18
QL
849 Temp = TrustedCert;\r
850 Cert = d2i_X509 (NULL, &Temp, (long) CertLength);\r
532616bb 851 if (Cert == NULL) {\r
852 goto _Exit;\r
853 }\r
854\r
855 //\r
856 // Setup X509 Store for trusted certificate\r
857 //\r
858 CertStore = X509_STORE_new ();\r
859 if (CertStore == NULL) {\r
860 goto _Exit;\r
861 }\r
862 if (!(X509_STORE_add_cert (CertStore, Cert))) {\r
863 goto _Exit;\r
864 }\r
865\r
532616bb 866 //\r
867 // For generic PKCS#7 handling, InData may be NULL if the content is present\r
868 // in PKCS#7 structure. So ignore NULL checking here.\r
869 //\r
870 DataBio = BIO_new (BIO_s_mem ());\r
5b2956ea
YT
871 if (DataBio == NULL) {\r
872 goto _Exit;\r
873 }\r
874\r
875 if (BIO_write (DataBio, InData, (int) DataLength) <= 0) {\r
876 goto _Exit;\r
877 }\r
532616bb 878\r
68547181
DW
879 //\r
880 // Allow partial certificate chains, terminated by a non-self-signed but\r
de0408be 881 // still trusted intermediate certificate. Also disable time checks.\r
68547181 882 //\r
de0408be
DW
883 X509_STORE_set_flags (CertStore,\r
884 X509_V_FLAG_PARTIAL_CHAIN | X509_V_FLAG_NO_CHECK_TIME);\r
68547181 885\r
02ee8d3b 886 //\r
887 // OpenSSL PKCS7 Verification by default checks for SMIME (email signing) and\r
888 // doesn't support the extended key usage for Authenticode Code Signing.\r
889 // Bypass the certificate purpose checking by enabling any purposes setting.\r
890 //\r
891 X509_STORE_set_purpose (CertStore, X509_PURPOSE_ANY);\r
892\r
532616bb 893 //\r
894 // Verifies the PKCS#7 signedData structure\r
895 //\r
896 Status = (BOOLEAN) PKCS7_verify (Pkcs7, NULL, CertStore, DataBio, NULL, PKCS7_BINARY);\r
897\r
898_Exit:\r
899 //\r
900 // Release Resources\r
901 //\r
902 BIO_free (DataBio);\r
532616bb 903 X509_free (Cert);\r
904 X509_STORE_free (CertStore);\r
905 PKCS7_free (Pkcs7);\r
906\r
907 if (!Wrapped) {\r
908 OPENSSL_free (SignedData);\r
909 }\r
910\r
911 return Status;\r
afeb55e4
QL
912}\r
913\r
914/**\r
915 Extracts the attached content from a PKCS#7 signed data if existed. The input signed\r
916 data could be wrapped in a ContentInfo structure.\r
917\r
918 If P7Data, Content, or ContentSize is NULL, then return FALSE. If P7Length overflow,\r
2998af86 919 then return FALSE. If the P7Data is not correctly formatted, then return FALSE.\r
afeb55e4
QL
920\r
921 Caution: This function may receive untrusted input. So this function will do\r
922 basic check for PKCS#7 data structure.\r
923\r
924 @param[in] P7Data Pointer to the PKCS#7 signed data to process.\r
925 @param[in] P7Length Length of the PKCS#7 signed data in bytes.\r
926 @param[out] Content Pointer to the extracted content from the PKCS#7 signedData.\r
6fe575d0 927 It's caller's responsibility to free the buffer with FreePool().\r
afeb55e4
QL
928 @param[out] ContentSize The size of the extracted content in bytes.\r
929\r
930 @retval TRUE The P7Data was correctly formatted for processing.\r
931 @retval FALSE The P7Data was not correctly formatted for processing.\r
932\r
0c9fc4b1 933**/\r
afeb55e4
QL
934BOOLEAN\r
935EFIAPI\r
936Pkcs7GetAttachedContent (\r
937 IN CONST UINT8 *P7Data,\r
938 IN UINTN P7Length,\r
939 OUT VOID **Content,\r
940 OUT UINTN *ContentSize\r
941 )\r
942{\r
943 BOOLEAN Status;\r
944 PKCS7 *Pkcs7;\r
945 UINT8 *SignedData;\r
946 UINTN SignedDataSize;\r
947 BOOLEAN Wrapped;\r
948 CONST UINT8 *Temp;\r
949 ASN1_OCTET_STRING *OctStr;\r
950\r
afeb55e4
QL
951 //\r
952 // Check input parameter.\r
953 //\r
954 if ((P7Data == NULL) || (P7Length > INT_MAX) || (Content == NULL) || (ContentSize == NULL)) {\r
955 return FALSE;\r
956 }\r
957\r
2aabd146
QL
958 *Content = NULL;\r
959 Pkcs7 = NULL;\r
960 SignedData = NULL;\r
961 OctStr = NULL;\r
962\r
afeb55e4
QL
963 Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &SignedData, &SignedDataSize);\r
964 if (!Status || (SignedDataSize > INT_MAX)) {\r
965 goto _Exit;\r
966 }\r
967\r
968 Status = FALSE;\r
969\r
970 //\r
971 // Decoding PKCS#7 SignedData\r
972 //\r
973 Temp = SignedData;\r
974 Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **)&Temp, (int)SignedDataSize);\r
975 if (Pkcs7 == NULL) {\r
976 goto _Exit;\r
977 }\r
978\r
979 //\r
980 // The type of Pkcs7 must be signedData\r
981 //\r
982 if (!PKCS7_type_is_signed (Pkcs7)) {\r
983 goto _Exit;\r
984 }\r
985\r
986 //\r
987 // Check for detached or attached content\r
988 //\r
989 if (PKCS7_get_detached (Pkcs7)) {\r
990 //\r
991 // No Content supplied for PKCS7 detached signedData\r
992 //\r
993 *Content = NULL;\r
994 *ContentSize = 0;\r
995 } else {\r
996 //\r
997 // Retrieve the attached content in PKCS7 signedData\r
998 //\r
999 OctStr = Pkcs7->d.sign->contents->d.data;\r
1000 if ((OctStr->length > 0) && (OctStr->data != NULL)) {\r
1001 *ContentSize = OctStr->length;\r
6fe575d0 1002 *Content = AllocatePool (*ContentSize);\r
2aabd146
QL
1003 if (*Content == NULL) {\r
1004 *ContentSize = 0;\r
1005 goto _Exit;\r
1006 }\r
afeb55e4
QL
1007 CopyMem (*Content, OctStr->data, *ContentSize);\r
1008 }\r
1009 }\r
1010 Status = TRUE;\r
1011\r
1012_Exit:\r
1013 //\r
1014 // Release Resources\r
1015 //\r
1016 PKCS7_free (Pkcs7);\r
1017\r
1018 if (!Wrapped) {\r
1019 OPENSSL_free (SignedData);\r
1020 }\r
1021\r
1022 return Status;\r
1023}\r