]> git.proxmox.com Git - mirror_edk2.git/blame - CryptoPkg/Library/BaseCryptLib/Pk/CryptPkcs7Verify.c
CryptoPkg: Add one new API (Pkcs7GetCertificatesList) for certs retrieving.
[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
1463ce18 13Copyright (c) 2009 - 2015, 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
438 @param[out] SingerChainCerts Pointer to the certificates list chained to signer's\r
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
492 //\r
493 // Parameter Checking\r
494 //\r
495 if ((P7Data == NULL) || (SignerChainCerts == NULL) || (ChainLength == NULL) ||\r
496 (UnchainCerts == NULL) || (UnchainLength == NULL) || (P7Length > INT_MAX)) {\r
497 return Status;\r
498 }\r
499\r
500 *SignerChainCerts = NULL;\r
501 *ChainLength = 0;\r
502 *UnchainCerts = NULL;\r
503 *UnchainLength = 0;\r
504\r
505 //\r
506 // Construct a new PKCS#7 data wrapping with ContentInfo structure if needed.\r
507 //\r
508 Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &NewP7Data, &NewP7Length);\r
509 if (!Status || (NewP7Length > INT_MAX)) {\r
510 goto _Error;\r
511 }\r
512\r
513 //\r
514 // Decodes PKCS#7 SignedData\r
515 //\r
516 Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **) &NewP7Data, (int) NewP7Length);\r
517 if ((Pkcs7 == NULL) || (!PKCS7_type_is_signed (Pkcs7))) {\r
518 goto _Error;\r
519 }\r
520\r
521 //\r
522 // Obtains Signer's Certificate from PKCS#7 data\r
523 // NOTE: Only one signer case will be handled in this function, which means SignerInfos\r
524 // should include only one singer's certificate.\r
525 //\r
526 Signers = PKCS7_get0_signers (Pkcs7, NULL, PKCS7_BINARY);\r
527 if ((Signers == NULL) || (sk_X509_num (Signers) != 1)) {\r
528 goto _Error;\r
529 }\r
530 Signer = sk_X509_value (Signers, 0);\r
531\r
532 if (!X509_STORE_CTX_init (&CertCtx, NULL, Signer, Pkcs7->d.sign->cert)) {\r
533 goto _Error;\r
534 }\r
535 //\r
536 // Initialize Chained & Untrusted stack\r
537 //\r
538 if (CertCtx.chain == NULL) {\r
539 if (((CertCtx.chain = sk_X509_new_null ()) == NULL) ||\r
540 (!sk_X509_push (CertCtx.chain, CertCtx.cert))) {\r
541 goto _Error;\r
542 }\r
543 }\r
544 sk_X509_delete_ptr (CertCtx.untrusted, Signer);\r
545\r
546 //\r
547 // Build certificates stack chained from Signer's certificate.\r
548 //\r
549 Cert = Signer;\r
550 for (; ;) {\r
551 //\r
552 // Self-Issue checking\r
553 //\r
554 if (CertCtx.check_issued (&CertCtx, Cert, Cert)) {\r
555 break;\r
556 }\r
557\r
558 //\r
559 // Found the issuer of the current certificate\r
560 //\r
561 if (CertCtx.untrusted != NULL) {\r
562 Issuer = NULL;\r
563 for (Index = 0; Index < sk_X509_num (CertCtx.untrusted); Index++) {\r
564 TempCert = sk_X509_value (CertCtx.untrusted, Index);\r
565 if (CertCtx.check_issued (&CertCtx, Cert, TempCert)) {\r
566 Issuer = TempCert;\r
567 break;\r
568 }\r
569 }\r
570 if (Issuer != NULL) {\r
571 if (!sk_X509_push (CertCtx.chain, Issuer)) {\r
572 goto _Error;\r
573 }\r
574 sk_X509_delete_ptr (CertCtx.untrusted, Issuer);\r
575\r
576 Cert = Issuer;\r
577 continue;\r
578 }\r
579 }\r
580\r
581 break;\r
582 }\r
583\r
584 //\r
585 // Converts Chained and Untrusted Certificate to Certificate Buffer in following format:\r
586 // UINT8 CertNumber;\r
587 // UINT32 Cert1Length;\r
588 // UINT8 Cert1[];\r
589 // UINT32 Cert2Length;\r
590 // UINT8 Cert2[];\r
591 // ...\r
592 // UINT32 CertnLength;\r
593 // UINT8 Certn[];\r
594 //\r
595\r
596 if (CertCtx.chain != NULL) {\r
597 BufferSize = sizeof (UINT8);\r
598 OldSize = BufferSize;\r
599 CertBuf = NULL;\r
600\r
601 for (Index = 0; ; Index++) {\r
602 Status = X509PopCertificate (CertCtx.chain, &SingleCert, &CertSize);\r
603 if (!Status) {\r
604 break;\r
605 }\r
606\r
607 OldSize = BufferSize;\r
608 OldBuf = CertBuf;\r
609 BufferSize = OldSize + CertSize + sizeof (UINT32);\r
610 CertBuf = malloc (BufferSize);\r
611\r
612 if (CertBuf == NULL) {\r
613 Status = FALSE;\r
614 goto _Error;\r
615 }\r
616 if (OldBuf != NULL) {\r
617 CopyMem (CertBuf, OldBuf, OldSize);\r
618 free (OldBuf);\r
619 OldBuf = NULL;\r
620 }\r
621\r
622 WriteUnaligned32 ((UINT32 *) (CertBuf + OldSize), (UINT32) CertSize);\r
623 CopyMem (CertBuf + OldSize + sizeof (UINT32), SingleCert, CertSize);\r
624\r
625 free (SingleCert);\r
626 SingleCert = NULL;\r
627 }\r
628\r
629 if (CertBuf != NULL) {\r
630 //\r
631 // Update CertNumber.\r
632 //\r
633 CertBuf[0] = Index;\r
634\r
635 *SignerChainCerts = CertBuf;\r
636 *ChainLength = BufferSize;\r
637 }\r
638 }\r
639\r
640 if (CertCtx.untrusted != NULL) {\r
641 BufferSize = sizeof (UINT8);\r
642 OldSize = BufferSize;\r
643 CertBuf = NULL;\r
644\r
645 for (Index = 0; ; Index++) {\r
646 Status = X509PopCertificate (CertCtx.untrusted, &SingleCert, &CertSize);\r
647 if (!Status) {\r
648 break;\r
649 }\r
650\r
651 OldSize = BufferSize;\r
652 OldBuf = CertBuf;\r
653 BufferSize = OldSize + CertSize + sizeof (UINT32);\r
654 CertBuf = malloc (BufferSize);\r
655\r
656 if (CertBuf == NULL) {\r
657 Status = FALSE;\r
658 goto _Error;\r
659 }\r
660 if (OldBuf != NULL) {\r
661 CopyMem (CertBuf, OldBuf, OldSize);\r
662 free (OldBuf);\r
663 OldBuf = NULL;\r
664 }\r
665\r
666 WriteUnaligned32 ((UINT32 *) (CertBuf + OldSize), (UINT32) CertSize);\r
667 CopyMem (CertBuf + OldSize + sizeof (UINT32), SingleCert, CertSize);\r
668\r
669 free (SingleCert);\r
670 SingleCert = NULL;\r
671 }\r
672\r
673 if (CertBuf != NULL) {\r
674 //\r
675 // Update CertNumber.\r
676 //\r
677 CertBuf[0] = Index;\r
678\r
679 *UnchainCerts = CertBuf;\r
680 *UnchainLength = BufferSize;\r
681 }\r
682 }\r
683\r
684 Status = TRUE;\r
685\r
686_Error:\r
687 //\r
688 // Release Resources.\r
689 //\r
690 if (!Wrapped && (NewP7Data != NULL)) {\r
691 free (NewP7Data);\r
692 }\r
693\r
694 if (Pkcs7 != NULL) {\r
695 PKCS7_free (Pkcs7);\r
696 }\r
697 sk_X509_free (Signers);\r
698\r
699 X509_STORE_CTX_cleanup (&CertCtx);\r
700\r
701 if (SingleCert != NULL) {\r
702 free (SingleCert);\r
703 }\r
704\r
705 if (OldBuf != NULL) {\r
706 free (OldBuf);\r
707 }\r
708\r
709 if (!Status && (CertBuf != NULL)) {\r
710 free (CertBuf);\r
711 *SignerChainCerts = NULL;\r
712 *UnchainCerts = NULL;\r
713 }\r
714\r
715 return Status;\r
716}\r
717\r
532616bb 718/**\r
719 Verifies the validility of a PKCS#7 signed data as described in "PKCS #7:\r
720 Cryptographic Message Syntax Standard". The input signed data could be wrapped\r
721 in a ContentInfo structure.\r
722\r
723 If P7Data, TrustedCert or InData is NULL, then return FALSE.\r
724 If P7Length, CertLength or DataLength overflow, then return FAlSE.\r
725\r
726 Caution: This function may receive untrusted input.\r
727 UEFI Authenticated Variable is external input, so this function will do basic\r
728 check for PKCS#7 data structure.\r
729\r
730 @param[in] P7Data Pointer to the PKCS#7 message to verify.\r
731 @param[in] P7Length Length of the PKCS#7 message in bytes.\r
732 @param[in] TrustedCert Pointer to a trusted/root certificate encoded in DER, which\r
733 is used for certificate chain verification.\r
734 @param[in] CertLength Length of the trusted certificate in bytes.\r
735 @param[in] InData Pointer to the content to be verified.\r
736 @param[in] DataLength Length of InData in bytes.\r
737\r
738 @retval TRUE The specified PKCS#7 signed data is valid.\r
739 @retval FALSE Invalid PKCS#7 signed data.\r
740\r
741**/\r
742BOOLEAN\r
743EFIAPI\r
744Pkcs7Verify (\r
745 IN CONST UINT8 *P7Data,\r
746 IN UINTN P7Length,\r
747 IN CONST UINT8 *TrustedCert,\r
748 IN UINTN CertLength,\r
749 IN CONST UINT8 *InData,\r
750 IN UINTN DataLength\r
751 )\r
752{\r
753 PKCS7 *Pkcs7;\r
532616bb 754 BIO *DataBio;\r
755 BOOLEAN Status;\r
756 X509 *Cert;\r
757 X509_STORE *CertStore;\r
758 UINT8 *SignedData;\r
1463ce18 759 CONST UINT8 *Temp;\r
532616bb 760 UINTN SignedDataSize;\r
761 BOOLEAN Wrapped;\r
762\r
763 //\r
764 // Check input parameters.\r
765 //\r
2ac68e8b 766 if (P7Data == NULL || TrustedCert == NULL || InData == NULL ||\r
532616bb 767 P7Length > INT_MAX || CertLength > INT_MAX || DataLength > INT_MAX) {\r
768 return FALSE;\r
769 }\r
2ac68e8b 770\r
532616bb 771 Pkcs7 = NULL;\r
532616bb 772 DataBio = NULL;\r
773 Cert = NULL;\r
774 CertStore = NULL;\r
775\r
776 //\r
777 // Register & Initialize necessary digest algorithms for PKCS#7 Handling\r
778 //\r
dda39f3a 779 if (EVP_add_digest (EVP_md5 ()) == 0) {\r
780 return FALSE;\r
781 }\r
782 if (EVP_add_digest (EVP_sha1 ()) == 0) {\r
783 return FALSE;\r
784 }\r
785 if (EVP_add_digest (EVP_sha256 ()) == 0) {\r
786 return FALSE;\r
787 }\r
2ac68e8b
QL
788 if (EVP_add_digest (EVP_sha384 ()) == 0) {\r
789 return FALSE;\r
790 }\r
791 if (EVP_add_digest (EVP_sha512 ()) == 0) {\r
792 return FALSE;\r
793 }\r
dda39f3a 794 if (EVP_add_digest_alias (SN_sha1WithRSAEncryption, SN_sha1WithRSA) == 0) {\r
795 return FALSE;\r
796 }\r
797\r
532616bb 798 Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &SignedData, &SignedDataSize);\r
799 if (!Status) {\r
800 return Status;\r
801 }\r
802\r
803 Status = FALSE;\r
2ac68e8b 804\r
532616bb 805 //\r
806 // Retrieve PKCS#7 Data (DER encoding)\r
807 //\r
808 if (SignedDataSize > INT_MAX) {\r
809 goto _Exit;\r
810 }\r
811\r
812 Temp = SignedData;\r
813 Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **) &Temp, (int) SignedDataSize);\r
814 if (Pkcs7 == NULL) {\r
815 goto _Exit;\r
816 }\r
817\r
818 //\r
819 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)\r
820 //\r
821 if (!PKCS7_type_is_signed (Pkcs7)) {\r
822 goto _Exit;\r
823 }\r
824\r
825 //\r
826 // Read DER-encoded root certificate and Construct X509 Certificate\r
827 //\r
1463ce18
QL
828 Temp = TrustedCert;\r
829 Cert = d2i_X509 (NULL, &Temp, (long) CertLength);\r
532616bb 830 if (Cert == NULL) {\r
831 goto _Exit;\r
832 }\r
833\r
834 //\r
835 // Setup X509 Store for trusted certificate\r
836 //\r
837 CertStore = X509_STORE_new ();\r
838 if (CertStore == NULL) {\r
839 goto _Exit;\r
840 }\r
841 if (!(X509_STORE_add_cert (CertStore, Cert))) {\r
842 goto _Exit;\r
843 }\r
844\r
532616bb 845 //\r
846 // For generic PKCS#7 handling, InData may be NULL if the content is present\r
847 // in PKCS#7 structure. So ignore NULL checking here.\r
848 //\r
849 DataBio = BIO_new (BIO_s_mem ());\r
5b2956ea
YT
850 if (DataBio == NULL) {\r
851 goto _Exit;\r
852 }\r
853\r
854 if (BIO_write (DataBio, InData, (int) DataLength) <= 0) {\r
855 goto _Exit;\r
856 }\r
532616bb 857\r
68547181
DW
858 //\r
859 // Allow partial certificate chains, terminated by a non-self-signed but\r
de0408be 860 // still trusted intermediate certificate. Also disable time checks.\r
68547181 861 //\r
de0408be
DW
862 X509_STORE_set_flags (CertStore,\r
863 X509_V_FLAG_PARTIAL_CHAIN | X509_V_FLAG_NO_CHECK_TIME);\r
68547181 864\r
02ee8d3b 865 //\r
866 // OpenSSL PKCS7 Verification by default checks for SMIME (email signing) and\r
867 // doesn't support the extended key usage for Authenticode Code Signing.\r
868 // Bypass the certificate purpose checking by enabling any purposes setting.\r
869 //\r
870 X509_STORE_set_purpose (CertStore, X509_PURPOSE_ANY);\r
871\r
532616bb 872 //\r
873 // Verifies the PKCS#7 signedData structure\r
874 //\r
875 Status = (BOOLEAN) PKCS7_verify (Pkcs7, NULL, CertStore, DataBio, NULL, PKCS7_BINARY);\r
876\r
877_Exit:\r
878 //\r
879 // Release Resources\r
880 //\r
881 BIO_free (DataBio);\r
532616bb 882 X509_free (Cert);\r
883 X509_STORE_free (CertStore);\r
884 PKCS7_free (Pkcs7);\r
885\r
886 if (!Wrapped) {\r
887 OPENSSL_free (SignedData);\r
888 }\r
889\r
890 return Status;\r
afeb55e4
QL
891}\r
892\r
893/**\r
894 Extracts the attached content from a PKCS#7 signed data if existed. The input signed\r
895 data could be wrapped in a ContentInfo structure.\r
896\r
897 If P7Data, Content, or ContentSize is NULL, then return FALSE. If P7Length overflow,\r
898 then return FAlSE. If the P7Data is not correctly formatted, then return FALSE.\r
899\r
900 Caution: This function may receive untrusted input. So this function will do\r
901 basic check for PKCS#7 data structure.\r
902\r
903 @param[in] P7Data Pointer to the PKCS#7 signed data to process.\r
904 @param[in] P7Length Length of the PKCS#7 signed data in bytes.\r
905 @param[out] Content Pointer to the extracted content from the PKCS#7 signedData.\r
906 It's caller's responsiblity to free the buffer.\r
907 @param[out] ContentSize The size of the extracted content in bytes.\r
908\r
909 @retval TRUE The P7Data was correctly formatted for processing.\r
910 @retval FALSE The P7Data was not correctly formatted for processing.\r
911\r
912*/\r
913BOOLEAN\r
914EFIAPI\r
915Pkcs7GetAttachedContent (\r
916 IN CONST UINT8 *P7Data,\r
917 IN UINTN P7Length,\r
918 OUT VOID **Content,\r
919 OUT UINTN *ContentSize\r
920 )\r
921{\r
922 BOOLEAN Status;\r
923 PKCS7 *Pkcs7;\r
924 UINT8 *SignedData;\r
925 UINTN SignedDataSize;\r
926 BOOLEAN Wrapped;\r
927 CONST UINT8 *Temp;\r
928 ASN1_OCTET_STRING *OctStr;\r
929\r
afeb55e4
QL
930 //\r
931 // Check input parameter.\r
932 //\r
933 if ((P7Data == NULL) || (P7Length > INT_MAX) || (Content == NULL) || (ContentSize == NULL)) {\r
934 return FALSE;\r
935 }\r
936\r
2aabd146
QL
937 *Content = NULL;\r
938 Pkcs7 = NULL;\r
939 SignedData = NULL;\r
940 OctStr = NULL;\r
941\r
afeb55e4
QL
942 Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &SignedData, &SignedDataSize);\r
943 if (!Status || (SignedDataSize > INT_MAX)) {\r
944 goto _Exit;\r
945 }\r
946\r
947 Status = FALSE;\r
948\r
949 //\r
950 // Decoding PKCS#7 SignedData\r
951 //\r
952 Temp = SignedData;\r
953 Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **)&Temp, (int)SignedDataSize);\r
954 if (Pkcs7 == NULL) {\r
955 goto _Exit;\r
956 }\r
957\r
958 //\r
959 // The type of Pkcs7 must be signedData\r
960 //\r
961 if (!PKCS7_type_is_signed (Pkcs7)) {\r
962 goto _Exit;\r
963 }\r
964\r
965 //\r
966 // Check for detached or attached content\r
967 //\r
968 if (PKCS7_get_detached (Pkcs7)) {\r
969 //\r
970 // No Content supplied for PKCS7 detached signedData\r
971 //\r
972 *Content = NULL;\r
973 *ContentSize = 0;\r
974 } else {\r
975 //\r
976 // Retrieve the attached content in PKCS7 signedData\r
977 //\r
978 OctStr = Pkcs7->d.sign->contents->d.data;\r
979 if ((OctStr->length > 0) && (OctStr->data != NULL)) {\r
980 *ContentSize = OctStr->length;\r
981 *Content = malloc (*ContentSize);\r
2aabd146
QL
982 if (*Content == NULL) {\r
983 *ContentSize = 0;\r
984 goto _Exit;\r
985 }\r
afeb55e4
QL
986 CopyMem (*Content, OctStr->data, *ContentSize);\r
987 }\r
988 }\r
989 Status = TRUE;\r
990\r
991_Exit:\r
992 //\r
993 // Release Resources\r
994 //\r
995 PKCS7_free (Pkcs7);\r
996\r
997 if (!Wrapped) {\r
998 OPENSSL_free (SignedData);\r
999 }\r
1000\r
1001 return Status;\r
1002}\r