1 /** @file
2   PKCS#7 SignedData Verification Wrapper Implementation over OpenSSL.
3 
4   Caution: This module requires additional review when modified.
5   This library will have external input - signature (e.g. UEFI Authenticated
6   Variable). It may by input in SMM mode.
7   This external input must be validated carefully to avoid security issue like
8   buffer overflow, integer overflow.
9 
10   WrapPkcs7Data(), Pkcs7GetSigners(), Pkcs7Verify() will get UEFI Authenticated
11   Variable and will do basic check for data structure.
12 
13 Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
14 This program and the accompanying materials
15 are licensed and made available under the terms and conditions of the BSD License
16 which accompanies this distribution.  The full text of the license may be found at
17 http://opensource.org/licenses/bsd-license.php
18 
19 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
20 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
21 
22 **/
23 
24 #include "InternalCryptLib.h"
25 
26 #include <openssl/objects.h>
27 #include <openssl/x509.h>
28 #include <openssl/x509v3.h>
29 #include <openssl/pkcs7.h>
30 
31 UINT8 mOidValue[9] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02 };
32 
33 /**
34   Check input P7Data is a wrapped ContentInfo structure or not. If not construct
35   a new structure to wrap P7Data.
36 
37   Caution: This function may receive untrusted input.
38   UEFI Authenticated Variable is external input, so this function will do basic
39   check for PKCS#7 data structure.
40 
41   @param[in]  P7Data       Pointer to the PKCS#7 message to verify.
42   @param[in]  P7Length     Length of the PKCS#7 message in bytes.
43   @param[out] WrapFlag     If TRUE P7Data is a ContentInfo structure, otherwise
44                            return FALSE.
45   @param[out] WrapData     If return status of this function is TRUE:
46                            1) when WrapFlag is TRUE, pointer to P7Data.
47                            2) when WrapFlag is FALSE, pointer to a new ContentInfo
48                            structure. It's caller's responsibility to free this
49                            buffer.
50   @param[out] WrapDataSize Length of ContentInfo structure in bytes.
51 
52   @retval     TRUE         The operation is finished successfully.
53   @retval     FALSE        The operation is failed due to lack of resources.
54 
55 **/
56 BOOLEAN
WrapPkcs7Data(IN CONST UINT8 * P7Data,IN UINTN P7Length,OUT BOOLEAN * WrapFlag,OUT UINT8 ** WrapData,OUT UINTN * WrapDataSize)57 WrapPkcs7Data (
58   IN  CONST UINT8  *P7Data,
59   IN  UINTN        P7Length,
60   OUT BOOLEAN      *WrapFlag,
61   OUT UINT8        **WrapData,
62   OUT UINTN        *WrapDataSize
63   )
64 {
65   BOOLEAN          Wrapped;
66   UINT8            *SignedData;
67 
68   //
69   // Check whether input P7Data is a wrapped ContentInfo structure or not.
70   //
71   Wrapped = FALSE;
72   if ((P7Data[4] == 0x06) && (P7Data[5] == 0x09)) {
73     if (CompareMem (P7Data + 6, mOidValue, sizeof (mOidValue)) == 0) {
74       if ((P7Data[15] == 0xA0) && (P7Data[16] == 0x82)) {
75         Wrapped = TRUE;
76       }
77     }
78   }
79 
80   if (Wrapped) {
81     *WrapData     = (UINT8 *) P7Data;
82     *WrapDataSize = P7Length;
83   } else {
84     //
85     // Wrap PKCS#7 signeddata to a ContentInfo structure - add a header in 19 bytes.
86     //
87     *WrapDataSize = P7Length + 19;
88     *WrapData     = malloc (*WrapDataSize);
89     if (*WrapData == NULL) {
90       *WrapFlag = Wrapped;
91       return FALSE;
92     }
93 
94     SignedData = *WrapData;
95 
96     //
97     // Part1: 0x30, 0x82.
98     //
99     SignedData[0] = 0x30;
100     SignedData[1] = 0x82;
101 
102     //
103     // Part2: Length1 = P7Length + 19 - 4, in big endian.
104     //
105     SignedData[2] = (UINT8) (((UINT16) (*WrapDataSize - 4)) >> 8);
106     SignedData[3] = (UINT8) (((UINT16) (*WrapDataSize - 4)) & 0xff);
107 
108     //
109     // Part3: 0x06, 0x09.
110     //
111     SignedData[4] = 0x06;
112     SignedData[5] = 0x09;
113 
114     //
115     // Part4: OID value -- 0x2A 0x86 0x48 0x86 0xF7 0x0D 0x01 0x07 0x02.
116     //
117     CopyMem (SignedData + 6, mOidValue, sizeof (mOidValue));
118 
119     //
120     // Part5: 0xA0, 0x82.
121     //
122     SignedData[15] = 0xA0;
123     SignedData[16] = 0x82;
124 
125     //
126     // Part6: Length2 = P7Length, in big endian.
127     //
128     SignedData[17] = (UINT8) (((UINT16) P7Length) >> 8);
129     SignedData[18] = (UINT8) (((UINT16) P7Length) & 0xff);
130 
131     //
132     // Part7: P7Data.
133     //
134     CopyMem (SignedData + 19, P7Data, P7Length);
135   }
136 
137   *WrapFlag = Wrapped;
138   return TRUE;
139 }
140 
141 /**
142   Pop single certificate from STACK_OF(X509).
143 
144   If X509Stack, Cert, or CertSize is NULL, then return FALSE.
145 
146   @param[in]  X509Stack       Pointer to a X509 stack object.
147   @param[out] Cert            Pointer to a X509 certificate.
148   @param[out] CertSize        Length of output X509 certificate in bytes.
149 
150   @retval     TRUE            The X509 stack pop succeeded.
151   @retval     FALSE           The pop operation failed.
152 
153 **/
154 BOOLEAN
X509PopCertificate(IN VOID * X509Stack,OUT UINT8 ** Cert,OUT UINTN * CertSize)155 X509PopCertificate (
156   IN  VOID  *X509Stack,
157   OUT UINT8 **Cert,
158   OUT UINTN *CertSize
159   )
160 {
161   BIO             *CertBio;
162   X509            *X509Cert;
163   STACK_OF(X509)  *CertStack;
164   BOOLEAN         Status;
165   INT32           Result;
166   INT32           Length;
167   VOID            *Buffer;
168 
169   Status = FALSE;
170 
171   if ((X509Stack == NULL) || (Cert == NULL) || (CertSize == NULL)) {
172     return Status;
173   }
174 
175   CertStack = (STACK_OF(X509) *) X509Stack;
176 
177   X509Cert = sk_X509_pop (CertStack);
178 
179   if (X509Cert == NULL) {
180     return Status;
181   }
182 
183   Buffer = NULL;
184 
185   CertBio = BIO_new (BIO_s_mem ());
186   if (CertBio == NULL) {
187     return Status;
188   }
189 
190   Result = i2d_X509_bio (CertBio, X509Cert);
191   if (Result == 0) {
192     goto _Exit;
193   }
194 
195   Length = (INT32)(((BUF_MEM *) CertBio->ptr)->length);
196   if (Length <= 0) {
197     goto _Exit;
198   }
199 
200   Buffer = malloc (Length);
201   if (Buffer == NULL) {
202     goto _Exit;
203   }
204 
205   Result = BIO_read (CertBio, Buffer, Length);
206   if (Result != Length) {
207     goto _Exit;
208   }
209 
210   *Cert     = Buffer;
211   *CertSize = Length;
212 
213   Status = TRUE;
214 
215 _Exit:
216 
217   BIO_free (CertBio);
218 
219   if (!Status && (Buffer != NULL)) {
220     free (Buffer);
221   }
222 
223   return Status;
224 }
225 
226 /**
227   Get the signer's certificates from PKCS#7 signed data as described in "PKCS #7:
228   Cryptographic Message Syntax Standard". The input signed data could be wrapped
229   in a ContentInfo structure.
230 
231   If P7Data, CertStack, StackLength, TrustedCert or CertLength is NULL, then
232   return FALSE. If P7Length overflow, then return FALSE.
233 
234   Caution: This function may receive untrusted input.
235   UEFI Authenticated Variable is external input, so this function will do basic
236   check for PKCS#7 data structure.
237 
238   @param[in]  P7Data       Pointer to the PKCS#7 message to verify.
239   @param[in]  P7Length     Length of the PKCS#7 message in bytes.
240   @param[out] CertStack    Pointer to Signer's certificates retrieved from P7Data.
241                            It's caller's responsibility to free the buffer.
242   @param[out] StackLength  Length of signer's certificates in bytes.
243   @param[out] TrustedCert  Pointer to a trusted certificate from Signer's certificates.
244                            It's caller's responsibility to free the buffer.
245   @param[out] CertLength   Length of the trusted certificate in bytes.
246 
247   @retval  TRUE            The operation is finished successfully.
248   @retval  FALSE           Error occurs during the operation.
249 
250 **/
251 BOOLEAN
252 EFIAPI
Pkcs7GetSigners(IN CONST UINT8 * P7Data,IN UINTN P7Length,OUT UINT8 ** CertStack,OUT UINTN * StackLength,OUT UINT8 ** TrustedCert,OUT UINTN * CertLength)253 Pkcs7GetSigners (
254   IN  CONST UINT8  *P7Data,
255   IN  UINTN        P7Length,
256   OUT UINT8        **CertStack,
257   OUT UINTN        *StackLength,
258   OUT UINT8        **TrustedCert,
259   OUT UINTN        *CertLength
260   )
261 {
262   PKCS7            *Pkcs7;
263   BOOLEAN          Status;
264   UINT8            *SignedData;
265   CONST UINT8      *Temp;
266   UINTN            SignedDataSize;
267   BOOLEAN          Wrapped;
268   STACK_OF(X509)   *Stack;
269   UINT8            Index;
270   UINT8            *CertBuf;
271   UINT8            *OldBuf;
272   UINTN            BufferSize;
273   UINTN            OldSize;
274   UINT8            *SingleCert;
275   UINTN            SingleCertSize;
276 
277   if ((P7Data == NULL) || (CertStack == NULL) || (StackLength == NULL) ||
278       (TrustedCert == NULL) || (CertLength == NULL) || (P7Length > INT_MAX)) {
279     return FALSE;
280   }
281 
282   Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &SignedData, &SignedDataSize);
283   if (!Status) {
284     return Status;
285   }
286 
287   Status     = FALSE;
288   Pkcs7      = NULL;
289   Stack      = NULL;
290   CertBuf    = NULL;
291   OldBuf     = NULL;
292   SingleCert = NULL;
293 
294   //
295   // Retrieve PKCS#7 Data (DER encoding)
296   //
297   if (SignedDataSize > INT_MAX) {
298     goto _Exit;
299   }
300 
301   Temp = SignedData;
302   Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **) &Temp, (int) SignedDataSize);
303   if (Pkcs7 == NULL) {
304     goto _Exit;
305   }
306 
307   //
308   // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)
309   //
310   if (!PKCS7_type_is_signed (Pkcs7)) {
311     goto _Exit;
312   }
313 
314   Stack = PKCS7_get0_signers(Pkcs7, NULL, PKCS7_BINARY);
315   if (Stack == NULL) {
316     goto _Exit;
317   }
318 
319   //
320   // Convert CertStack to buffer in following format:
321   // UINT8  CertNumber;
322   // UINT32 Cert1Length;
323   // UINT8  Cert1[];
324   // UINT32 Cert2Length;
325   // UINT8  Cert2[];
326   // ...
327   // UINT32 CertnLength;
328   // UINT8  Certn[];
329   //
330   BufferSize = sizeof (UINT8);
331   OldSize    = BufferSize;
332 
333   for (Index = 0; ; Index++) {
334     Status = X509PopCertificate (Stack, &SingleCert, &SingleCertSize);
335     if (!Status) {
336       break;
337     }
338 
339     OldSize    = BufferSize;
340     OldBuf     = CertBuf;
341     BufferSize = OldSize + SingleCertSize + sizeof (UINT32);
342     CertBuf    = malloc (BufferSize);
343 
344     if (CertBuf == NULL) {
345       goto _Exit;
346     }
347 
348     if (OldBuf != NULL) {
349       CopyMem (CertBuf, OldBuf, OldSize);
350       free (OldBuf);
351       OldBuf = NULL;
352     }
353 
354     WriteUnaligned32 ((UINT32 *) (CertBuf + OldSize), (UINT32) SingleCertSize);
355     CopyMem (CertBuf + OldSize + sizeof (UINT32), SingleCert, SingleCertSize);
356 
357     free (SingleCert);
358     SingleCert = NULL;
359   }
360 
361   if (CertBuf != NULL) {
362     //
363     // Update CertNumber.
364     //
365     CertBuf[0] = Index;
366 
367     *CertLength = BufferSize - OldSize - sizeof (UINT32);
368     *TrustedCert = malloc (*CertLength);
369     if (*TrustedCert == NULL) {
370       goto _Exit;
371     }
372 
373     CopyMem (*TrustedCert, CertBuf + OldSize + sizeof (UINT32), *CertLength);
374     *CertStack   = CertBuf;
375     *StackLength = BufferSize;
376     Status = TRUE;
377   }
378 
379 _Exit:
380   //
381   // Release Resources
382   //
383   if (!Wrapped) {
384     free (SignedData);
385   }
386 
387   if (Pkcs7 != NULL) {
388     PKCS7_free (Pkcs7);
389   }
390 
391   if (Stack != NULL) {
392     sk_X509_pop_free(Stack, X509_free);
393   }
394 
395   if (SingleCert !=  NULL) {
396     free (SingleCert);
397   }
398 
399   if (!Status && (CertBuf != NULL)) {
400     free (CertBuf);
401     *CertStack = NULL;
402   }
403 
404   if (OldBuf != NULL) {
405     free (OldBuf);
406   }
407 
408   return Status;
409 }
410 
411 /**
412   Wrap function to use free() to free allocated memory for certificates.
413 
414   @param[in]  Certs        Pointer to the certificates to be freed.
415 
416 **/
417 VOID
418 EFIAPI
Pkcs7FreeSigners(IN UINT8 * Certs)419 Pkcs7FreeSigners (
420   IN  UINT8        *Certs
421   )
422 {
423   if (Certs == NULL) {
424     return;
425   }
426 
427   free (Certs);
428 }
429 
430 /**
431   Retrieves all embedded certificates from PKCS#7 signed data as described in "PKCS #7:
432   Cryptographic Message Syntax Standard", and outputs two certificate lists chained and
433   unchained to the signer's certificates.
434   The input signed data could be wrapped in a ContentInfo structure.
435 
436   @param[in]  P7Data            Pointer to the PKCS#7 message.
437   @param[in]  P7Length          Length of the PKCS#7 message in bytes.
438   @param[out] SignerChainCerts  Pointer to the certificates list chained to signer's
439                                 certificate. It's caller's responsibility to free the buffer.
440   @param[out] ChainLength       Length of the chained certificates list buffer in bytes.
441   @param[out] UnchainCerts      Pointer to the unchained certificates lists. It's caller's
442                                 responsibility to free the buffer.
443   @param[out] UnchainLength     Length of the unchained certificates list buffer in bytes.
444 
445   @retval  TRUE         The operation is finished successfully.
446   @retval  FALSE        Error occurs during the operation.
447 
448 **/
449 BOOLEAN
450 EFIAPI
Pkcs7GetCertificatesList(IN CONST UINT8 * P7Data,IN UINTN P7Length,OUT UINT8 ** SignerChainCerts,OUT UINTN * ChainLength,OUT UINT8 ** UnchainCerts,OUT UINTN * UnchainLength)451 Pkcs7GetCertificatesList (
452   IN  CONST UINT8  *P7Data,
453   IN  UINTN        P7Length,
454   OUT UINT8        **SignerChainCerts,
455   OUT UINTN        *ChainLength,
456   OUT UINT8        **UnchainCerts,
457   OUT UINTN        *UnchainLength
458   )
459 {
460   BOOLEAN          Status;
461   UINT8            *NewP7Data;
462   UINTN            NewP7Length;
463   BOOLEAN          Wrapped;
464   UINT8            Index;
465   PKCS7            *Pkcs7;
466   X509_STORE_CTX   CertCtx;
467   STACK_OF(X509)   *Signers;
468   X509             *Signer;
469   X509             *Cert;
470   X509             *TempCert;
471   X509             *Issuer;
472   UINT8            *CertBuf;
473   UINT8            *OldBuf;
474   UINTN            BufferSize;
475   UINTN            OldSize;
476   UINT8            *SingleCert;
477   UINTN            CertSize;
478 
479   //
480   // Initializations
481   //
482   Status         = FALSE;
483   NewP7Data      = NULL;
484   Pkcs7          = NULL;
485   Cert           = NULL;
486   TempCert       = NULL;
487   SingleCert     = NULL;
488   CertBuf        = NULL;
489   OldBuf         = NULL;
490   Signers        = NULL;
491 
492   ZeroMem (&CertCtx, sizeof (CertCtx));
493 
494   //
495   // Parameter Checking
496   //
497   if ((P7Data == NULL) || (SignerChainCerts == NULL) || (ChainLength == NULL) ||
498       (UnchainCerts == NULL) || (UnchainLength == NULL) || (P7Length > INT_MAX)) {
499     return Status;
500   }
501 
502   *SignerChainCerts = NULL;
503   *ChainLength      = 0;
504   *UnchainCerts     = NULL;
505   *UnchainLength    = 0;
506 
507   //
508   // Construct a new PKCS#7 data wrapping with ContentInfo structure if needed.
509   //
510   Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &NewP7Data, &NewP7Length);
511   if (!Status || (NewP7Length > INT_MAX)) {
512     goto _Error;
513   }
514 
515   //
516   // Decodes PKCS#7 SignedData
517   //
518   Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **) &NewP7Data, (int) NewP7Length);
519   if ((Pkcs7 == NULL) || (!PKCS7_type_is_signed (Pkcs7))) {
520     goto _Error;
521   }
522 
523   //
524   // Obtains Signer's Certificate from PKCS#7 data
525   // NOTE: Only one signer case will be handled in this function, which means SignerInfos
526   //       should include only one signer's certificate.
527   //
528   Signers = PKCS7_get0_signers (Pkcs7, NULL, PKCS7_BINARY);
529   if ((Signers == NULL) || (sk_X509_num (Signers) != 1)) {
530     goto _Error;
531   }
532   Signer = sk_X509_value (Signers, 0);
533 
534   if (!X509_STORE_CTX_init (&CertCtx, NULL, Signer, Pkcs7->d.sign->cert)) {
535     goto _Error;
536   }
537   //
538   // Initialize Chained & Untrusted stack
539   //
540   if (CertCtx.chain == NULL) {
541     if (((CertCtx.chain = sk_X509_new_null ()) == NULL) ||
542         (!sk_X509_push (CertCtx.chain, CertCtx.cert))) {
543       goto _Error;
544     }
545   }
546   (VOID)sk_X509_delete_ptr (CertCtx.untrusted, Signer);
547 
548   //
549   // Build certificates stack chained from Signer's certificate.
550   //
551   Cert = Signer;
552   for (; ;) {
553     //
554     // Self-Issue checking
555     //
556     if (CertCtx.check_issued (&CertCtx, Cert, Cert)) {
557       break;
558     }
559 
560     //
561     // Found the issuer of the current certificate
562     //
563     if (CertCtx.untrusted != NULL) {
564       Issuer = NULL;
565       for (Index = 0; Index < sk_X509_num (CertCtx.untrusted); Index++) {
566         TempCert = sk_X509_value (CertCtx.untrusted, Index);
567         if (CertCtx.check_issued (&CertCtx, Cert, TempCert)) {
568           Issuer = TempCert;
569           break;
570         }
571       }
572       if (Issuer != NULL) {
573         if (!sk_X509_push (CertCtx.chain, Issuer)) {
574           goto _Error;
575         }
576         (VOID)sk_X509_delete_ptr (CertCtx.untrusted, Issuer);
577 
578         Cert = Issuer;
579         continue;
580       }
581     }
582 
583     break;
584   }
585 
586   //
587   // Converts Chained and Untrusted Certificate to Certificate Buffer in following format:
588   //      UINT8  CertNumber;
589   //      UINT32 Cert1Length;
590   //      UINT8  Cert1[];
591   //      UINT32 Cert2Length;
592   //      UINT8  Cert2[];
593   //      ...
594   //      UINT32 CertnLength;
595   //      UINT8  Certn[];
596   //
597 
598   if (CertCtx.chain != NULL) {
599     BufferSize = sizeof (UINT8);
600     OldSize    = BufferSize;
601     CertBuf    = NULL;
602 
603     for (Index = 0; ; Index++) {
604       Status = X509PopCertificate (CertCtx.chain, &SingleCert, &CertSize);
605       if (!Status) {
606         break;
607       }
608 
609       OldSize    = BufferSize;
610       OldBuf     = CertBuf;
611       BufferSize = OldSize + CertSize + sizeof (UINT32);
612       CertBuf    = malloc (BufferSize);
613 
614       if (CertBuf == NULL) {
615         Status = FALSE;
616         goto _Error;
617       }
618       if (OldBuf != NULL) {
619         CopyMem (CertBuf, OldBuf, OldSize);
620         free (OldBuf);
621         OldBuf = NULL;
622       }
623 
624       WriteUnaligned32 ((UINT32 *) (CertBuf + OldSize), (UINT32) CertSize);
625       CopyMem (CertBuf + OldSize + sizeof (UINT32), SingleCert, CertSize);
626 
627       free (SingleCert);
628       SingleCert = NULL;
629     }
630 
631     if (CertBuf != NULL) {
632       //
633       // Update CertNumber.
634       //
635       CertBuf[0] = Index;
636 
637       *SignerChainCerts = CertBuf;
638       *ChainLength      = BufferSize;
639     }
640   }
641 
642   if (CertCtx.untrusted != NULL) {
643     BufferSize = sizeof (UINT8);
644     OldSize    = BufferSize;
645     CertBuf    = NULL;
646 
647     for (Index = 0; ; Index++) {
648       Status = X509PopCertificate (CertCtx.untrusted, &SingleCert, &CertSize);
649       if (!Status) {
650         break;
651       }
652 
653       OldSize    = BufferSize;
654       OldBuf     = CertBuf;
655       BufferSize = OldSize + CertSize + sizeof (UINT32);
656       CertBuf    = malloc (BufferSize);
657 
658       if (CertBuf == NULL) {
659         Status = FALSE;
660         goto _Error;
661       }
662       if (OldBuf != NULL) {
663         CopyMem (CertBuf, OldBuf, OldSize);
664         free (OldBuf);
665         OldBuf = NULL;
666       }
667 
668       WriteUnaligned32 ((UINT32 *) (CertBuf + OldSize), (UINT32) CertSize);
669       CopyMem (CertBuf + OldSize + sizeof (UINT32), SingleCert, CertSize);
670 
671       free (SingleCert);
672       SingleCert = NULL;
673     }
674 
675     if (CertBuf != NULL) {
676       //
677       // Update CertNumber.
678       //
679       CertBuf[0] = Index;
680 
681       *UnchainCerts  = CertBuf;
682       *UnchainLength = BufferSize;
683     }
684   }
685 
686   Status = TRUE;
687 
688 _Error:
689   //
690   // Release Resources.
691   //
692   if (!Wrapped && (NewP7Data != NULL)) {
693     free (NewP7Data);
694   }
695 
696   if (Pkcs7 != NULL) {
697     PKCS7_free (Pkcs7);
698   }
699   sk_X509_free (Signers);
700 
701   X509_STORE_CTX_cleanup (&CertCtx);
702 
703   if (SingleCert != NULL) {
704     free (SingleCert);
705   }
706 
707   if (OldBuf != NULL) {
708     free (OldBuf);
709   }
710 
711   if (!Status && (CertBuf != NULL)) {
712     free (CertBuf);
713     *SignerChainCerts = NULL;
714     *UnchainCerts     = NULL;
715   }
716 
717   return Status;
718 }
719 
720 /**
721   Verifies the validity of a PKCS#7 signed data as described in "PKCS #7:
722   Cryptographic Message Syntax Standard". The input signed data could be wrapped
723   in a ContentInfo structure.
724 
725   If P7Data, TrustedCert or InData is NULL, then return FALSE.
726   If P7Length, CertLength or DataLength overflow, then return FALSE.
727 
728   Caution: This function may receive untrusted input.
729   UEFI Authenticated Variable is external input, so this function will do basic
730   check for PKCS#7 data structure.
731 
732   @param[in]  P7Data       Pointer to the PKCS#7 message to verify.
733   @param[in]  P7Length     Length of the PKCS#7 message in bytes.
734   @param[in]  TrustedCert  Pointer to a trusted/root certificate encoded in DER, which
735                            is used for certificate chain verification.
736   @param[in]  CertLength   Length of the trusted certificate in bytes.
737   @param[in]  InData       Pointer to the content to be verified.
738   @param[in]  DataLength   Length of InData in bytes.
739 
740   @retval  TRUE  The specified PKCS#7 signed data is valid.
741   @retval  FALSE Invalid PKCS#7 signed data.
742 
743 **/
744 BOOLEAN
745 EFIAPI
Pkcs7Verify(IN CONST UINT8 * P7Data,IN UINTN P7Length,IN CONST UINT8 * TrustedCert,IN UINTN CertLength,IN CONST UINT8 * InData,IN UINTN DataLength)746 Pkcs7Verify (
747   IN  CONST UINT8  *P7Data,
748   IN  UINTN        P7Length,
749   IN  CONST UINT8  *TrustedCert,
750   IN  UINTN        CertLength,
751   IN  CONST UINT8  *InData,
752   IN  UINTN        DataLength
753   )
754 {
755   PKCS7       *Pkcs7;
756   BIO         *DataBio;
757   BOOLEAN     Status;
758   X509        *Cert;
759   X509_STORE  *CertStore;
760   UINT8       *SignedData;
761   CONST UINT8 *Temp;
762   UINTN       SignedDataSize;
763   BOOLEAN     Wrapped;
764 
765   //
766   // Check input parameters.
767   //
768   if (P7Data == NULL || TrustedCert == NULL || InData == NULL ||
769     P7Length > INT_MAX || CertLength > INT_MAX || DataLength > INT_MAX) {
770     return FALSE;
771   }
772 
773   Pkcs7     = NULL;
774   DataBio   = NULL;
775   Cert      = NULL;
776   CertStore = NULL;
777 
778   //
779   // Register & Initialize necessary digest algorithms for PKCS#7 Handling
780   //
781   if (EVP_add_digest (EVP_md5 ()) == 0) {
782     return FALSE;
783   }
784   if (EVP_add_digest (EVP_sha1 ()) == 0) {
785     return FALSE;
786   }
787   if (EVP_add_digest (EVP_sha256 ()) == 0) {
788     return FALSE;
789   }
790   if (EVP_add_digest (EVP_sha384 ()) == 0) {
791     return FALSE;
792   }
793   if (EVP_add_digest (EVP_sha512 ()) == 0) {
794     return FALSE;
795   }
796   if (EVP_add_digest_alias (SN_sha1WithRSAEncryption, SN_sha1WithRSA) == 0) {
797     return FALSE;
798   }
799 
800   Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &SignedData, &SignedDataSize);
801   if (!Status) {
802     return Status;
803   }
804 
805   Status = FALSE;
806 
807   //
808   // Retrieve PKCS#7 Data (DER encoding)
809   //
810   if (SignedDataSize > INT_MAX) {
811     goto _Exit;
812   }
813 
814   Temp = SignedData;
815   Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **) &Temp, (int) SignedDataSize);
816   if (Pkcs7 == NULL) {
817     goto _Exit;
818   }
819 
820   //
821   // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)
822   //
823   if (!PKCS7_type_is_signed (Pkcs7)) {
824     goto _Exit;
825   }
826 
827   //
828   // Read DER-encoded root certificate and Construct X509 Certificate
829   //
830   Temp = TrustedCert;
831   Cert = d2i_X509 (NULL, &Temp, (long) CertLength);
832   if (Cert == NULL) {
833     goto _Exit;
834   }
835 
836   //
837   // Setup X509 Store for trusted certificate
838   //
839   CertStore = X509_STORE_new ();
840   if (CertStore == NULL) {
841     goto _Exit;
842   }
843   if (!(X509_STORE_add_cert (CertStore, Cert))) {
844     goto _Exit;
845   }
846 
847   //
848   // For generic PKCS#7 handling, InData may be NULL if the content is present
849   // in PKCS#7 structure. So ignore NULL checking here.
850   //
851   DataBio = BIO_new (BIO_s_mem ());
852   if (DataBio == NULL) {
853     goto _Exit;
854   }
855 
856   if (BIO_write (DataBio, InData, (int) DataLength) <= 0) {
857     goto _Exit;
858   }
859 
860   //
861   // Allow partial certificate chains, terminated by a non-self-signed but
862   // still trusted intermediate certificate. Also disable time checks.
863   //
864   X509_STORE_set_flags (CertStore,
865                         X509_V_FLAG_PARTIAL_CHAIN | X509_V_FLAG_NO_CHECK_TIME);
866 
867   //
868   // OpenSSL PKCS7 Verification by default checks for SMIME (email signing) and
869   // doesn't support the extended key usage for Authenticode Code Signing.
870   // Bypass the certificate purpose checking by enabling any purposes setting.
871   //
872   X509_STORE_set_purpose (CertStore, X509_PURPOSE_ANY);
873 
874   //
875   // Verifies the PKCS#7 signedData structure
876   //
877   Status = (BOOLEAN) PKCS7_verify (Pkcs7, NULL, CertStore, DataBio, NULL, PKCS7_BINARY);
878 
879 _Exit:
880   //
881   // Release Resources
882   //
883   BIO_free (DataBio);
884   X509_free (Cert);
885   X509_STORE_free (CertStore);
886   PKCS7_free (Pkcs7);
887 
888   if (!Wrapped) {
889     OPENSSL_free (SignedData);
890   }
891 
892   return Status;
893 }
894 
895 /**
896   Extracts the attached content from a PKCS#7 signed data if existed. The input signed
897   data could be wrapped in a ContentInfo structure.
898 
899   If P7Data, Content, or ContentSize is NULL, then return FALSE. If P7Length overflow,
900   then return FALSE. If the P7Data is not correctly formatted, then return FALSE.
901 
902   Caution: This function may receive untrusted input. So this function will do
903            basic check for PKCS#7 data structure.
904 
905   @param[in]   P7Data       Pointer to the PKCS#7 signed data to process.
906   @param[in]   P7Length     Length of the PKCS#7 signed data in bytes.
907   @param[out]  Content      Pointer to the extracted content from the PKCS#7 signedData.
908                             It's caller's responsibility to free the buffer.
909   @param[out]  ContentSize  The size of the extracted content in bytes.
910 
911   @retval     TRUE          The P7Data was correctly formatted for processing.
912   @retval     FALSE         The P7Data was not correctly formatted for processing.
913 
914 */
915 BOOLEAN
916 EFIAPI
Pkcs7GetAttachedContent(IN CONST UINT8 * P7Data,IN UINTN P7Length,OUT VOID ** Content,OUT UINTN * ContentSize)917 Pkcs7GetAttachedContent (
918   IN  CONST UINT8  *P7Data,
919   IN  UINTN        P7Length,
920   OUT VOID         **Content,
921   OUT UINTN        *ContentSize
922   )
923 {
924   BOOLEAN            Status;
925   PKCS7              *Pkcs7;
926   UINT8              *SignedData;
927   UINTN              SignedDataSize;
928   BOOLEAN            Wrapped;
929   CONST UINT8        *Temp;
930   ASN1_OCTET_STRING  *OctStr;
931 
932   //
933   // Check input parameter.
934   //
935   if ((P7Data == NULL) || (P7Length > INT_MAX) || (Content == NULL) || (ContentSize == NULL)) {
936     return FALSE;
937   }
938 
939   *Content   = NULL;
940   Pkcs7      = NULL;
941   SignedData = NULL;
942   OctStr     = NULL;
943 
944   Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &SignedData, &SignedDataSize);
945   if (!Status || (SignedDataSize > INT_MAX)) {
946     goto _Exit;
947   }
948 
949   Status = FALSE;
950 
951   //
952   // Decoding PKCS#7 SignedData
953   //
954   Temp  = SignedData;
955   Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **)&Temp, (int)SignedDataSize);
956   if (Pkcs7 == NULL) {
957     goto _Exit;
958   }
959 
960   //
961   // The type of Pkcs7 must be signedData
962   //
963   if (!PKCS7_type_is_signed (Pkcs7)) {
964     goto _Exit;
965   }
966 
967   //
968   // Check for detached or attached content
969   //
970   if (PKCS7_get_detached (Pkcs7)) {
971     //
972     // No Content supplied for PKCS7 detached signedData
973     //
974     *Content     = NULL;
975     *ContentSize = 0;
976   } else {
977     //
978     // Retrieve the attached content in PKCS7 signedData
979     //
980     OctStr = Pkcs7->d.sign->contents->d.data;
981     if ((OctStr->length > 0) && (OctStr->data != NULL)) {
982       *ContentSize = OctStr->length;
983       *Content     = malloc (*ContentSize);
984       if (*Content == NULL) {
985         *ContentSize = 0;
986         goto _Exit;
987       }
988       CopyMem (*Content, OctStr->data, *ContentSize);
989     }
990   }
991   Status = TRUE;
992 
993 _Exit:
994   //
995   // Release Resources
996   //
997   PKCS7_free (Pkcs7);
998 
999   if (!Wrapped) {
1000     OPENSSL_free (SignedData);
1001   }
1002 
1003   return Status;
1004 }
1005