CryptoPkg/BaseCryptLib: Add NULL pointer checks in DH and P7Verify
[mirror_edk2.git] / CryptoPkg / Library / BaseCryptLib / Pk / CryptPkcs7Verify.c
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
13 Copyright (c) 2009 - 2017, Intel Corporation. All rights reserved.<BR>\r
14 This program and the accompanying materials\r
15 are licensed and made available under the terms and conditions of the BSD License\r
16 which accompanies this distribution.  The full text of the license may be found at\r
17 http://opensource.org/licenses/bsd-license.php\r
18 \r
19 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
20 WITHOUT 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
28 #include <openssl/x509v3.h>\r
29 #include <openssl/pkcs7.h>\r
30 \r
31 UINT8 mOidValue[9] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02 };\r
32 \r
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
45   @param[out] WrapData     If return status of this function is TRUE:\r
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
56 BOOLEAN\r
57 WrapPkcs7Data (\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
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
149 \r
150   @retval     TRUE            The X509 stack pop succeeded.\r
151   @retval     FALSE           The pop operation failed.\r
152 \r
153 **/\r
154 BOOLEAN\r
155 X509PopCertificate (\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
165   INT32           Result;\r
166   BUF_MEM         *Ptr;\r
167   INT32           Length;\r
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
196   BIO_get_mem_ptr (CertBio, &Ptr);\r
197   Length = (INT32)(Ptr->length);\r
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
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
234   return FALSE. If P7Length overflow, then return FALSE.\r
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
243                            It's caller's responsibility to free the buffer.\r
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
246                            It's caller's responsibility to free the buffer.\r
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
253 BOOLEAN\r
254 EFIAPI\r
255 Pkcs7GetSigners (\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
267   CONST UINT8      *Temp;\r
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
283 \r
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
334 \r
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
379   }\r
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
409 \r
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
419 VOID\r
420 EFIAPI\r
421 Pkcs7FreeSigners (\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
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
440   @param[out] SignerChainCerts  Pointer to the certificates list chained to signer's\r
441                                 certificate. It's caller's responsibility to free the buffer.\r
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
444                                 responsibility to free the buffer.\r
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
451 BOOLEAN\r
452 EFIAPI\r
453 Pkcs7GetCertificatesList (\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
468   X509_STORE_CTX   *CertCtx;\r
469   STACK_OF(X509)   *CtxChain;\r
470   STACK_OF(X509)   *CtxUntrusted;\r
471   X509             *CtxCert;\r
472   STACK_OF(X509)   *Signers;\r
473   X509             *Signer;\r
474   X509             *Cert;\r
475   X509             *Issuer;\r
476   X509_NAME        *IssuerName;\r
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
490   CertCtx        = NULL;\r
491   CtxChain       = NULL;\r
492   CtxCert        = NULL;\r
493   CtxUntrusted   = NULL;\r
494   Cert           = NULL;\r
495   SingleCert     = NULL;\r
496   CertBuf        = NULL;\r
497   OldBuf         = NULL;\r
498   Signers        = NULL;\r
499 \r
500   ZeroMem (&CertCtx, sizeof (CertCtx));\r
501 \r
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
534   //       should include only one signer's certificate.\r
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
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
547     goto _Error;\r
548   }\r
549   //\r
550   // Initialize Chained & Untrusted stack\r
551   //\r
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
557       goto _Error;\r
558     }\r
559   }\r
560   CtxUntrusted = X509_STORE_CTX_get0_untrusted (CertCtx);\r
561   if (CtxUntrusted != NULL) {\r
562     (VOID)sk_X509_delete_ptr (CtxUntrusted, Signer);\r
563   }\r
564 \r
565   //\r
566   // Build certificates stack chained from Signer's certificate.\r
567   //\r
568   Cert = Signer;\r
569   for (; ;) {\r
570     //\r
571     // Self-Issue checking\r
572     //\r
573     Issuer = NULL;\r
574     if (X509_STORE_CTX_get1_issuer (&Issuer, CertCtx, Cert) == 1) {\r
575       if (X509_cmp (Issuer, Cert) == 0) {\r
576         break;\r
577       }\r
578     }\r
579 \r
580     //\r
581     // Found the issuer of the current certificate\r
582     //\r
583     if (CtxUntrusted != NULL) {\r
584       Issuer = NULL;\r
585       IssuerName = X509_get_issuer_name (Cert);\r
586       Issuer     = X509_find_by_subject (CtxUntrusted, IssuerName);\r
587       if (Issuer != NULL) {\r
588         if (!sk_X509_push (CtxChain, Issuer)) {\r
589           goto _Error;\r
590         }\r
591         (VOID)sk_X509_delete_ptr (CtxUntrusted, Issuer);\r
592 \r
593         Cert = Issuer;\r
594         continue;\r
595       }\r
596     }\r
597 \r
598     break;\r
599   }\r
600 \r
601   //\r
602   // Converts Chained and Untrusted Certificate to Certificate Buffer in following format:\r
603   //      UINT8  CertNumber;\r
604   //      UINT32 Cert1Length;\r
605   //      UINT8  Cert1[];\r
606   //      UINT32 Cert2Length;\r
607   //      UINT8  Cert2[];\r
608   //      ...\r
609   //      UINT32 CertnLength;\r
610   //      UINT8  Certn[];\r
611   //\r
612 \r
613   if (CtxChain != NULL) {\r
614     BufferSize = sizeof (UINT8);\r
615     OldSize    = BufferSize;\r
616     CertBuf    = NULL;\r
617 \r
618     for (Index = 0; ; Index++) {\r
619       Status = X509PopCertificate (CtxChain, &SingleCert, &CertSize);\r
620       if (!Status) {\r
621         break;\r
622       }\r
623 \r
624       OldSize    = BufferSize;\r
625       OldBuf     = CertBuf;\r
626       BufferSize = OldSize + CertSize + sizeof (UINT32);\r
627       CertBuf    = malloc (BufferSize);\r
628 \r
629       if (CertBuf == NULL) {\r
630         Status = FALSE;\r
631         goto _Error;\r
632       }\r
633       if (OldBuf != NULL) {\r
634         CopyMem (CertBuf, OldBuf, OldSize);\r
635         free (OldBuf);\r
636         OldBuf = NULL;\r
637       }\r
638 \r
639       WriteUnaligned32 ((UINT32 *) (CertBuf + OldSize), (UINT32) CertSize);\r
640       CopyMem (CertBuf + OldSize + sizeof (UINT32), SingleCert, CertSize);\r
641 \r
642       free (SingleCert);\r
643       SingleCert = NULL;\r
644     }\r
645 \r
646     if (CertBuf != NULL) {\r
647       //\r
648       // Update CertNumber.\r
649       //\r
650       CertBuf[0] = Index;\r
651 \r
652       *SignerChainCerts = CertBuf;\r
653       *ChainLength      = BufferSize;\r
654     }\r
655   }\r
656 \r
657   if (CtxUntrusted != NULL) {\r
658     BufferSize = sizeof (UINT8);\r
659     OldSize    = BufferSize;\r
660     CertBuf    = NULL;\r
661 \r
662     for (Index = 0; ; Index++) {\r
663       Status = X509PopCertificate (CtxUntrusted, &SingleCert, &CertSize);\r
664       if (!Status) {\r
665         break;\r
666       }\r
667 \r
668       OldSize    = BufferSize;\r
669       OldBuf     = CertBuf;\r
670       BufferSize = OldSize + CertSize + sizeof (UINT32);\r
671       CertBuf    = malloc (BufferSize);\r
672 \r
673       if (CertBuf == NULL) {\r
674         Status = FALSE;\r
675         goto _Error;\r
676       }\r
677       if (OldBuf != NULL) {\r
678         CopyMem (CertBuf, OldBuf, OldSize);\r
679         free (OldBuf);\r
680         OldBuf = NULL;\r
681       }\r
682 \r
683       WriteUnaligned32 ((UINT32 *) (CertBuf + OldSize), (UINT32) CertSize);\r
684       CopyMem (CertBuf + OldSize + sizeof (UINT32), SingleCert, CertSize);\r
685 \r
686       free (SingleCert);\r
687       SingleCert = NULL;\r
688     }\r
689 \r
690     if (CertBuf != NULL) {\r
691       //\r
692       // Update CertNumber.\r
693       //\r
694       CertBuf[0] = Index;\r
695 \r
696       *UnchainCerts  = CertBuf;\r
697       *UnchainLength = BufferSize;\r
698     }\r
699   }\r
700 \r
701   Status = TRUE;\r
702 \r
703 _Error:\r
704   //\r
705   // Release Resources.\r
706   //\r
707   if (!Wrapped && (NewP7Data != NULL)) {\r
708     free (NewP7Data);\r
709   }\r
710 \r
711   if (Pkcs7 != NULL) {\r
712     PKCS7_free (Pkcs7);\r
713   }\r
714   sk_X509_free (Signers);\r
715 \r
716   if (CertCtx != NULL) {\r
717     X509_STORE_CTX_cleanup (CertCtx);\r
718     X509_STORE_CTX_free (CertCtx);\r
719   }\r
720 \r
721   if (SingleCert != NULL) {\r
722     free (SingleCert);\r
723   }\r
724 \r
725   if (OldBuf != NULL) {\r
726     free (OldBuf);\r
727   }\r
728 \r
729   if (!Status && (CertBuf != NULL)) {\r
730     free (CertBuf);\r
731     *SignerChainCerts = NULL;\r
732     *UnchainCerts     = NULL;\r
733   }\r
734 \r
735   return Status;\r
736 }\r
737 \r
738 /**\r
739   Verifies the validity of a PKCS#7 signed data as described in "PKCS #7:\r
740   Cryptographic Message Syntax Standard". The input signed data could be wrapped\r
741   in a ContentInfo structure.\r
742 \r
743   If P7Data, TrustedCert or InData is NULL, then return FALSE.\r
744   If P7Length, CertLength or DataLength overflow, then return FALSE.\r
745 \r
746   Caution: This function may receive untrusted input.\r
747   UEFI Authenticated Variable is external input, so this function will do basic\r
748   check for PKCS#7 data structure.\r
749 \r
750   @param[in]  P7Data       Pointer to the PKCS#7 message to verify.\r
751   @param[in]  P7Length     Length of the PKCS#7 message in bytes.\r
752   @param[in]  TrustedCert  Pointer to a trusted/root certificate encoded in DER, which\r
753                            is used for certificate chain verification.\r
754   @param[in]  CertLength   Length of the trusted certificate in bytes.\r
755   @param[in]  InData       Pointer to the content to be verified.\r
756   @param[in]  DataLength   Length of InData in bytes.\r
757 \r
758   @retval  TRUE  The specified PKCS#7 signed data is valid.\r
759   @retval  FALSE Invalid PKCS#7 signed data.\r
760 \r
761 **/\r
762 BOOLEAN\r
763 EFIAPI\r
764 Pkcs7Verify (\r
765   IN  CONST UINT8  *P7Data,\r
766   IN  UINTN        P7Length,\r
767   IN  CONST UINT8  *TrustedCert,\r
768   IN  UINTN        CertLength,\r
769   IN  CONST UINT8  *InData,\r
770   IN  UINTN        DataLength\r
771   )\r
772 {\r
773   PKCS7       *Pkcs7;\r
774   BIO         *DataBio;\r
775   BOOLEAN     Status;\r
776   X509        *Cert;\r
777   X509_STORE  *CertStore;\r
778   UINT8       *SignedData;\r
779   CONST UINT8 *Temp;\r
780   UINTN       SignedDataSize;\r
781   BOOLEAN     Wrapped;\r
782 \r
783   //\r
784   // Check input parameters.\r
785   //\r
786   if (P7Data == NULL || TrustedCert == NULL || InData == NULL ||\r
787     P7Length > INT_MAX || CertLength > INT_MAX || DataLength > INT_MAX) {\r
788     return FALSE;\r
789   }\r
790 \r
791   Pkcs7     = NULL;\r
792   DataBio   = NULL;\r
793   Cert      = NULL;\r
794   CertStore = NULL;\r
795 \r
796   //\r
797   // Register & Initialize necessary digest algorithms for PKCS#7 Handling\r
798   //\r
799   if (EVP_add_digest (EVP_md5 ()) == 0) {\r
800     return FALSE;\r
801   }\r
802   if (EVP_add_digest (EVP_sha1 ()) == 0) {\r
803     return FALSE;\r
804   }\r
805   if (EVP_add_digest (EVP_sha256 ()) == 0) {\r
806     return FALSE;\r
807   }\r
808   if (EVP_add_digest (EVP_sha384 ()) == 0) {\r
809     return FALSE;\r
810   }\r
811   if (EVP_add_digest (EVP_sha512 ()) == 0) {\r
812     return FALSE;\r
813   }\r
814   if (EVP_add_digest_alias (SN_sha1WithRSAEncryption, SN_sha1WithRSA) == 0) {\r
815     return FALSE;\r
816   }\r
817 \r
818   Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &SignedData, &SignedDataSize);\r
819   if (!Status) {\r
820     return Status;\r
821   }\r
822 \r
823   Status = FALSE;\r
824 \r
825   //\r
826   // Retrieve PKCS#7 Data (DER encoding)\r
827   //\r
828   if (SignedDataSize > INT_MAX) {\r
829     goto _Exit;\r
830   }\r
831 \r
832   Temp = SignedData;\r
833   Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **) &Temp, (int) SignedDataSize);\r
834   if (Pkcs7 == NULL) {\r
835     goto _Exit;\r
836   }\r
837 \r
838   //\r
839   // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)\r
840   //\r
841   if (!PKCS7_type_is_signed (Pkcs7)) {\r
842     goto _Exit;\r
843   }\r
844 \r
845   //\r
846   // Read DER-encoded root certificate and Construct X509 Certificate\r
847   //\r
848   Temp = TrustedCert;\r
849   Cert = d2i_X509 (NULL, &Temp, (long) CertLength);\r
850   if (Cert == NULL) {\r
851     goto _Exit;\r
852   }\r
853 \r
854   //\r
855   // Setup X509 Store for trusted certificate\r
856   //\r
857   CertStore = X509_STORE_new ();\r
858   if (CertStore == NULL) {\r
859     goto _Exit;\r
860   }\r
861   if (!(X509_STORE_add_cert (CertStore, Cert))) {\r
862     goto _Exit;\r
863   }\r
864 \r
865   //\r
866   // For generic PKCS#7 handling, InData may be NULL if the content is present\r
867   // in PKCS#7 structure. So ignore NULL checking here.\r
868   //\r
869   DataBio = BIO_new (BIO_s_mem ());\r
870   if (DataBio == NULL) {\r
871     goto _Exit;\r
872   }\r
873 \r
874   if (BIO_write (DataBio, InData, (int) DataLength) <= 0) {\r
875     goto _Exit;\r
876   }\r
877 \r
878   //\r
879   // Allow partial certificate chains, terminated by a non-self-signed but\r
880   // still trusted intermediate certificate. Also disable time checks.\r
881   //\r
882   X509_STORE_set_flags (CertStore,\r
883                         X509_V_FLAG_PARTIAL_CHAIN | X509_V_FLAG_NO_CHECK_TIME);\r
884 \r
885   //\r
886   // OpenSSL PKCS7 Verification by default checks for SMIME (email signing) and\r
887   // doesn't support the extended key usage for Authenticode Code Signing.\r
888   // Bypass the certificate purpose checking by enabling any purposes setting.\r
889   //\r
890   X509_STORE_set_purpose (CertStore, X509_PURPOSE_ANY);\r
891 \r
892   //\r
893   // Verifies the PKCS#7 signedData structure\r
894   //\r
895   Status = (BOOLEAN) PKCS7_verify (Pkcs7, NULL, CertStore, DataBio, NULL, PKCS7_BINARY);\r
896 \r
897 _Exit:\r
898   //\r
899   // Release Resources\r
900   //\r
901   BIO_free (DataBio);\r
902   X509_free (Cert);\r
903   X509_STORE_free (CertStore);\r
904   PKCS7_free (Pkcs7);\r
905 \r
906   if (!Wrapped) {\r
907     OPENSSL_free (SignedData);\r
908   }\r
909 \r
910   return Status;\r
911 }\r
912 \r
913 /**\r
914   Extracts the attached content from a PKCS#7 signed data if existed. The input signed\r
915   data could be wrapped in a ContentInfo structure.\r
916 \r
917   If P7Data, Content, or ContentSize is NULL, then return FALSE. If P7Length overflow,\r
918   then return FALSE. If the P7Data is not correctly formatted, then return FALSE.\r
919 \r
920   Caution: This function may receive untrusted input. So this function will do\r
921            basic check for PKCS#7 data structure.\r
922 \r
923   @param[in]   P7Data       Pointer to the PKCS#7 signed data to process.\r
924   @param[in]   P7Length     Length of the PKCS#7 signed data in bytes.\r
925   @param[out]  Content      Pointer to the extracted content from the PKCS#7 signedData.\r
926                             It's caller's responsibility to free the buffer.\r
927   @param[out]  ContentSize  The size of the extracted content in bytes.\r
928 \r
929   @retval     TRUE          The P7Data was correctly formatted for processing.\r
930   @retval     FALSE         The P7Data was not correctly formatted for processing.\r
931 \r
932 **/\r
933 BOOLEAN\r
934 EFIAPI\r
935 Pkcs7GetAttachedContent (\r
936   IN  CONST UINT8  *P7Data,\r
937   IN  UINTN        P7Length,\r
938   OUT VOID         **Content,\r
939   OUT UINTN        *ContentSize\r
940   )\r
941 {\r
942   BOOLEAN            Status;\r
943   PKCS7              *Pkcs7;\r
944   UINT8              *SignedData;\r
945   UINTN              SignedDataSize;\r
946   BOOLEAN            Wrapped;\r
947   CONST UINT8        *Temp;\r
948   ASN1_OCTET_STRING  *OctStr;\r
949 \r
950   //\r
951   // Check input parameter.\r
952   //\r
953   if ((P7Data == NULL) || (P7Length > INT_MAX) || (Content == NULL) || (ContentSize == NULL)) {\r
954     return FALSE;\r
955   }\r
956 \r
957   *Content   = NULL;\r
958   Pkcs7      = NULL;\r
959   SignedData = NULL;\r
960   OctStr     = NULL;\r
961 \r
962   Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &SignedData, &SignedDataSize);\r
963   if (!Status || (SignedDataSize > INT_MAX)) {\r
964     goto _Exit;\r
965   }\r
966 \r
967   Status = FALSE;\r
968 \r
969   //\r
970   // Decoding PKCS#7 SignedData\r
971   //\r
972   Temp  = SignedData;\r
973   Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **)&Temp, (int)SignedDataSize);\r
974   if (Pkcs7 == NULL) {\r
975     goto _Exit;\r
976   }\r
977 \r
978   //\r
979   // The type of Pkcs7 must be signedData\r
980   //\r
981   if (!PKCS7_type_is_signed (Pkcs7)) {\r
982     goto _Exit;\r
983   }\r
984 \r
985   //\r
986   // Check for detached or attached content\r
987   //\r
988   if (PKCS7_get_detached (Pkcs7)) {\r
989     //\r
990     // No Content supplied for PKCS7 detached signedData\r
991     //\r
992     *Content     = NULL;\r
993     *ContentSize = 0;\r
994   } else {\r
995     //\r
996     // Retrieve the attached content in PKCS7 signedData\r
997     //\r
998     OctStr = Pkcs7->d.sign->contents->d.data;\r
999     if ((OctStr->length > 0) && (OctStr->data != NULL)) {\r
1000       *ContentSize = OctStr->length;\r
1001       *Content     = malloc (*ContentSize);\r
1002       if (*Content == NULL) {\r
1003         *ContentSize = 0;\r
1004         goto _Exit;\r
1005       }\r
1006       CopyMem (*Content, OctStr->data, *ContentSize);\r
1007     }\r
1008   }\r
1009   Status = TRUE;\r
1010 \r
1011 _Exit:\r
1012   //\r
1013   // Release Resources\r
1014   //\r
1015   PKCS7_free (Pkcs7);\r
1016 \r
1017   if (!Wrapped) {\r
1018     OPENSSL_free (SignedData);\r
1019   }\r
1020 \r
1021   return Status;\r
1022 }\r