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