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