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