Fix several issues in BaseCryptLib:
[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 - 2012, 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/pkcs7.h>\r
29 \r
30 UINT8 mOidValue[9] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02 };\r
31 \r
32 /**\r
33   Verification callback function to override any existing callbacks in OpenSSL\r
34   for intermediate certificate supports.\r
35 \r
36   @param[in]  Status   Original status before calling this callback.\r
37   @param[in]  Context  X509 store context.\r
38 \r
39   @retval     1        Current X509 certificate is verified successfully.\r
40   @retval     0        Verification failed.\r
41 \r
42 **/\r
43 int\r
44 X509VerifyCb (\r
45   IN int            Status,\r
46   IN X509_STORE_CTX *Context\r
47   )\r
48 {\r
49   X509_OBJECT  *Obj;\r
50   INTN         Error;\r
51   INTN         Index;\r
52   INTN         Count;\r
53 \r
54   Obj   = NULL;\r
55   Error = (INTN) X509_STORE_CTX_get_error (Context);\r
56 \r
57   //\r
58   // X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT and X509_V_ERR_UNABLE_TO_GET_ISSUER_\r
59   // CERT_LOCALLY mean a X509 certificate is not self signed and its issuer\r
60   // can not be found in X509_verify_cert of X509_vfy.c.\r
61   // In order to support intermediate certificate node, we override the\r
62   // errors if the certification is obtained from X509 store, i.e. it is\r
63   // a trusted ceritifcate node that is enrolled by user.\r
64   // Besides,X509_V_ERR_CERT_UNTRUSTED and X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE\r
65   // are also ignored to enable such feature.\r
66   //\r
67   if ((Error == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT) ||\r
68       (Error == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY)) {\r
69     Obj = (X509_OBJECT *) malloc (sizeof (X509_OBJECT));\r
70     if (Obj == NULL) {\r
71       return 0;\r
72     }\r
73 \r
74     Obj->type      = X509_LU_X509;\r
75     Obj->data.x509 = Context->current_cert;\r
76 \r
77     CRYPTO_w_lock (CRYPTO_LOCK_X509_STORE);\r
78 \r
79     if (X509_OBJECT_retrieve_match (Context->ctx->objs, Obj)) {\r
80       Status = 1;\r
81     } else {\r
82       //\r
83       // If any certificate in the chain is enrolled as trusted certificate,\r
84       // pass the certificate verification.\r
85       //\r
86       if (Error == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY) {\r
87         Count = (INTN) sk_X509_num (Context->chain);\r
88         for (Index = 0; Index < Count; Index++) {\r
89           Obj->data.x509 = sk_X509_value (Context->chain, (int) Index);\r
90           if (X509_OBJECT_retrieve_match (Context->ctx->objs, Obj)) {\r
91             Status = 1;\r
92             break;\r
93           }\r
94         }\r
95       }\r
96     }\r
97 \r
98     CRYPTO_w_unlock (CRYPTO_LOCK_X509_STORE);\r
99   }\r
100 \r
101   if ((Error == X509_V_ERR_CERT_UNTRUSTED) ||\r
102       (Error == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE)) {\r
103     Status = 1;\r
104   }\r
105 \r
106   if (Obj != NULL) {\r
107     OPENSSL_free (Obj);\r
108   }\r
109 \r
110   return Status;\r
111 }\r
112 \r
113 /**\r
114   Check input P7Data is a wrapped ContentInfo structure or not. If not construct\r
115   a new structure to wrap P7Data.\r
116 \r
117   Caution: This function may receive untrusted input.\r
118   UEFI Authenticated Variable is external input, so this function will do basic\r
119   check for PKCS#7 data structure.\r
120 \r
121   @param[in]  P7Data       Pointer to the PKCS#7 message to verify.\r
122   @param[in]  P7Length     Length of the PKCS#7 message in bytes.\r
123   @param[out] WrapFlag     If TRUE P7Data is a ContentInfo structure, otherwise\r
124                            return FALSE.\r
125   @param[out] WrapData     If return status of this function is TRUE: \r
126                            1) when WrapFlag is TRUE, pointer to P7Data.\r
127                            2) when WrapFlag is FALSE, pointer to a new ContentInfo\r
128                            structure. It's caller's responsibility to free this\r
129                            buffer.\r
130   @param[out] WrapDataSize Length of ContentInfo structure in bytes.\r
131 \r
132   @retval     TRUE         The operation is finished successfully.\r
133   @retval     FALSE        The operation is failed due to lack of resources.\r
134 \r
135 **/\r
136 BOOLEAN\r
137 WrapPkcs7Data (\r
138   IN  CONST UINT8  *P7Data,\r
139   IN  UINTN        P7Length,\r
140   OUT BOOLEAN      *WrapFlag,\r
141   OUT UINT8        **WrapData,\r
142   OUT UINTN        *WrapDataSize\r
143   )\r
144 {\r
145   BOOLEAN          Wrapped;\r
146   UINT8            *SignedData;\r
147 \r
148   //\r
149   // Check whether input P7Data is a wrapped ContentInfo structure or not.\r
150   //\r
151   Wrapped = FALSE;\r
152   if ((P7Data[4] == 0x06) && (P7Data[5] == 0x09)) {\r
153     if (CompareMem (P7Data + 6, mOidValue, sizeof (mOidValue)) == 0) {\r
154       if ((P7Data[15] == 0xA0) && (P7Data[16] == 0x82)) {\r
155         Wrapped = TRUE;\r
156       }\r
157     }\r
158   }\r
159 \r
160   if (Wrapped) {\r
161     *WrapData     = (UINT8 *) P7Data;\r
162     *WrapDataSize = P7Length;\r
163   } else {\r
164     //\r
165     // Wrap PKCS#7 signeddata to a ContentInfo structure - add a header in 19 bytes.\r
166     //\r
167     *WrapDataSize = P7Length + 19;\r
168     *WrapData     = malloc (*WrapDataSize);\r
169     if (*WrapData == NULL) {\r
170       *WrapFlag = Wrapped;\r
171       return FALSE;\r
172     }\r
173 \r
174     SignedData = *WrapData;\r
175 \r
176     //\r
177     // Part1: 0x30, 0x82.\r
178     //\r
179     SignedData[0] = 0x30;\r
180     SignedData[1] = 0x82;\r
181 \r
182     //\r
183     // Part2: Length1 = P7Length + 19 - 4, in big endian.\r
184     //\r
185     SignedData[2] = (UINT8) (((UINT16) (*WrapDataSize - 4)) >> 8);\r
186     SignedData[3] = (UINT8) (((UINT16) (*WrapDataSize - 4)) & 0xff);\r
187 \r
188     //\r
189     // Part3: 0x06, 0x09.\r
190     //\r
191     SignedData[4] = 0x06;\r
192     SignedData[5] = 0x09;\r
193 \r
194     //\r
195     // Part4: OID value -- 0x2A 0x86 0x48 0x86 0xF7 0x0D 0x01 0x07 0x02.\r
196     //\r
197     CopyMem (SignedData + 6, mOidValue, sizeof (mOidValue));\r
198 \r
199     //\r
200     // Part5: 0xA0, 0x82.\r
201     //\r
202     SignedData[15] = 0xA0;\r
203     SignedData[16] = 0x82;\r
204 \r
205     //\r
206     // Part6: Length2 = P7Length, in big endian.\r
207     //\r
208     SignedData[17] = (UINT8) (((UINT16) P7Length) >> 8);\r
209     SignedData[18] = (UINT8) (((UINT16) P7Length) & 0xff);\r
210 \r
211     //\r
212     // Part7: P7Data.\r
213     //\r
214     CopyMem (SignedData + 19, P7Data, P7Length);\r
215   }\r
216 \r
217   *WrapFlag = Wrapped;\r
218   return TRUE;\r
219 }\r
220 \r
221 /**\r
222   Get the signer's certificates from PKCS#7 signed data as described in "PKCS #7:\r
223   Cryptographic Message Syntax Standard". The input signed data could be wrapped\r
224   in a ContentInfo structure.\r
225 \r
226   If P7Data, CertStack, StackLength, TrustedCert or CertLength is NULL, then\r
227   return FALSE. If P7Length overflow, then return FAlSE.\r
228 \r
229   Caution: This function may receive untrusted input.\r
230   UEFI Authenticated Variable is external input, so this function will do basic\r
231   check for PKCS#7 data structure.\r
232 \r
233   @param[in]  P7Data       Pointer to the PKCS#7 message to verify.\r
234   @param[in]  P7Length     Length of the PKCS#7 message in bytes.\r
235   @param[out] CertStack    Pointer to Signer's certificates retrieved from P7Data.\r
236                            It's caller's responsiblity to free the buffer.\r
237   @param[out] StackLength  Length of signer's certificates in bytes.\r
238   @param[out] TrustedCert  Pointer to a trusted certificate from Signer's certificates.\r
239                            It's caller's responsiblity to free the buffer.\r
240   @param[out] CertLength   Length of the trusted certificate in bytes.\r
241 \r
242   @retval  TRUE            The operation is finished successfully.\r
243   @retval  FALSE           Error occurs during the operation.\r
244 \r
245 **/\r
246 BOOLEAN\r
247 EFIAPI\r
248 Pkcs7GetSigners (\r
249   IN  CONST UINT8  *P7Data,\r
250   IN  UINTN        P7Length,\r
251   OUT UINT8        **CertStack,\r
252   OUT UINTN        *StackLength,\r
253   OUT UINT8        **TrustedCert,\r
254   OUT UINTN        *CertLength\r
255   )\r
256 {\r
257   PKCS7            *Pkcs7;\r
258   BOOLEAN          Status;\r
259   UINT8            *SignedData;\r
260   UINT8            *Temp;\r
261   UINTN            SignedDataSize;\r
262   BOOLEAN          Wrapped;\r
263   STACK_OF(X509)   *Stack;\r
264   UINT8            Index;\r
265   UINT8            *CertBuf;\r
266   UINT8            *OldBuf;\r
267   UINTN            BufferSize;\r
268   UINTN            OldSize;\r
269   UINT8            *SingleCert;\r
270   UINTN            SingleCertSize;\r
271 \r
272   if ((P7Data == NULL) || (CertStack == NULL) || (StackLength == NULL) ||\r
273       (TrustedCert == NULL) || (CertLength == NULL) || (P7Length > INT_MAX)) {\r
274     return FALSE;\r
275   }\r
276   \r
277   Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &SignedData, &SignedDataSize);\r
278   if (!Status) {\r
279     return Status;\r
280   }\r
281 \r
282   Status     = FALSE;\r
283   Pkcs7      = NULL;\r
284   Stack      = NULL;\r
285   CertBuf    = NULL;\r
286   OldBuf     = NULL;\r
287   SingleCert = NULL;\r
288 \r
289   //\r
290   // Retrieve PKCS#7 Data (DER encoding)\r
291   //\r
292   if (SignedDataSize > INT_MAX) {\r
293     goto _Exit;\r
294   }\r
295 \r
296   Temp = SignedData;\r
297   Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **) &Temp, (int) SignedDataSize);\r
298   if (Pkcs7 == NULL) {\r
299     goto _Exit;\r
300   }\r
301 \r
302   //\r
303   // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)\r
304   //\r
305   if (!PKCS7_type_is_signed (Pkcs7)) {\r
306     goto _Exit;\r
307   }\r
308 \r
309   Stack = PKCS7_get0_signers(Pkcs7, NULL, PKCS7_BINARY);\r
310   if (Stack == NULL) {\r
311     goto _Exit;\r
312   }\r
313 \r
314   //\r
315   // Convert CertStack to buffer in following format:\r
316   // UINT8  CertNumber;\r
317   // UINT32 Cert1Length;\r
318   // UINT8  Cert1[];\r
319   // UINT32 Cert2Length;\r
320   // UINT8  Cert2[];\r
321   // ...\r
322   // UINT32 CertnLength;\r
323   // UINT8  Certn[];\r
324   //\r
325   BufferSize = sizeof (UINT8);\r
326   OldSize    = BufferSize;\r
327   \r
328   for (Index = 0; ; Index++) {\r
329     Status = X509PopCertificate (Stack, &SingleCert, &SingleCertSize);\r
330     if (!Status) {\r
331       break;\r
332     }\r
333 \r
334     OldSize    = BufferSize;\r
335     OldBuf     = CertBuf;\r
336     BufferSize = OldSize + SingleCertSize + sizeof (UINT32);\r
337     CertBuf    = malloc (BufferSize);\r
338 \r
339     if (CertBuf == NULL) {\r
340       goto _Exit;\r
341     }\r
342 \r
343     if (OldBuf != NULL) {\r
344       CopyMem (CertBuf, OldBuf, OldSize);\r
345       free (OldBuf);\r
346       OldBuf = NULL;\r
347     }\r
348 \r
349     WriteUnaligned32 ((UINT32 *) (CertBuf + OldSize), (UINT32) SingleCertSize);\r
350     CopyMem (CertBuf + OldSize + sizeof (UINT32), SingleCert, SingleCertSize);\r
351 \r
352     free (SingleCert);\r
353     SingleCert = NULL;\r
354   }\r
355 \r
356   if (CertBuf != NULL) {\r
357     //\r
358     // Update CertNumber.\r
359     //\r
360     CertBuf[0] = Index;\r
361 \r
362     *CertLength = BufferSize - OldSize - sizeof (UINT32);\r
363     *TrustedCert = malloc (*CertLength);\r
364     if (*TrustedCert == NULL) {\r
365       goto _Exit;\r
366     }\r
367 \r
368     CopyMem (*TrustedCert, CertBuf + OldSize + sizeof (UINT32), *CertLength);\r
369     *CertStack   = CertBuf;\r
370     *StackLength = BufferSize;\r
371     Status = TRUE;\r
372   } \r
373 \r
374 _Exit:\r
375   //\r
376   // Release Resources\r
377   //\r
378   if (!Wrapped) {\r
379     free (SignedData);\r
380   }\r
381 \r
382   if (Pkcs7 != NULL) {\r
383     PKCS7_free (Pkcs7);\r
384   }\r
385 \r
386   if (Stack != NULL) {\r
387     sk_X509_pop_free(Stack, X509_free);\r
388   }\r
389 \r
390   if (SingleCert !=  NULL) {\r
391     free (SingleCert);\r
392   }\r
393 \r
394   if (!Status && (CertBuf != NULL)) {\r
395     free (CertBuf);\r
396     *CertStack = NULL;\r
397   }\r
398 \r
399   if (OldBuf != NULL) {\r
400     free (OldBuf);\r
401   }\r
402   \r
403   return Status;\r
404 }\r
405 \r
406 /**\r
407   Wrap function to use free() to free allocated memory for certificates.\r
408 \r
409   @param[in]  Certs        Pointer to the certificates to be freed.\r
410 \r
411 **/\r
412 VOID\r
413 EFIAPI\r
414 Pkcs7FreeSigners (\r
415   IN  UINT8        *Certs\r
416   )\r
417 {\r
418   if (Certs == NULL) {\r
419     return;\r
420   }\r
421 \r
422   free (Certs);\r
423 }\r
424 \r
425 /**\r
426   Verifies the validility of a PKCS#7 signed data as described in "PKCS #7:\r
427   Cryptographic Message Syntax Standard". The input signed data could be wrapped\r
428   in a ContentInfo structure.\r
429 \r
430   If P7Data, TrustedCert or InData is NULL, then return FALSE.\r
431   If P7Length, CertLength or DataLength overflow, then return FAlSE.\r
432 \r
433   Caution: This function may receive untrusted input.\r
434   UEFI Authenticated Variable is external input, so this function will do basic\r
435   check for PKCS#7 data structure.\r
436 \r
437   @param[in]  P7Data       Pointer to the PKCS#7 message to verify.\r
438   @param[in]  P7Length     Length of the PKCS#7 message in bytes.\r
439   @param[in]  TrustedCert  Pointer to a trusted/root certificate encoded in DER, which\r
440                            is used for certificate chain verification.\r
441   @param[in]  CertLength   Length of the trusted certificate in bytes.\r
442   @param[in]  InData       Pointer to the content to be verified.\r
443   @param[in]  DataLength   Length of InData in bytes.\r
444 \r
445   @retval  TRUE  The specified PKCS#7 signed data is valid.\r
446   @retval  FALSE Invalid PKCS#7 signed data.\r
447 \r
448 **/\r
449 BOOLEAN\r
450 EFIAPI\r
451 Pkcs7Verify (\r
452   IN  CONST UINT8  *P7Data,\r
453   IN  UINTN        P7Length,\r
454   IN  CONST UINT8  *TrustedCert,\r
455   IN  UINTN        CertLength,\r
456   IN  CONST UINT8  *InData,\r
457   IN  UINTN        DataLength\r
458   )\r
459 {\r
460   PKCS7       *Pkcs7;\r
461   BIO         *CertBio;\r
462   BIO         *DataBio;\r
463   BOOLEAN     Status;\r
464   X509        *Cert;\r
465   X509_STORE  *CertStore;\r
466   UINT8       *SignedData;\r
467   UINT8       *Temp;\r
468   UINTN       SignedDataSize;\r
469   BOOLEAN     Wrapped;\r
470 \r
471   //\r
472   // Check input parameters.\r
473   //\r
474   if (P7Data == NULL || TrustedCert == NULL || InData == NULL || \r
475     P7Length > INT_MAX || CertLength > INT_MAX || DataLength > INT_MAX) {\r
476     return FALSE;\r
477   }\r
478   \r
479   Pkcs7     = NULL;\r
480   CertBio   = NULL;\r
481   DataBio   = NULL;\r
482   Cert      = NULL;\r
483   CertStore = NULL;\r
484 \r
485   //\r
486   // Register & Initialize necessary digest algorithms for PKCS#7 Handling\r
487   //\r
488   if (EVP_add_digest (EVP_md5 ()) == 0) {\r
489     return FALSE;\r
490   }\r
491   if (EVP_add_digest (EVP_sha1 ()) == 0) {\r
492     return FALSE;\r
493   }\r
494   if (EVP_add_digest (EVP_sha256 ()) == 0) {\r
495     return FALSE;\r
496   }\r
497   if (EVP_add_digest_alias (SN_sha1WithRSAEncryption, SN_sha1WithRSA) == 0) {\r
498     return FALSE;\r
499   }\r
500 \r
501 \r
502   Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &SignedData, &SignedDataSize);\r
503   if (!Status) {\r
504     return Status;\r
505   }\r
506 \r
507   Status = FALSE;\r
508   \r
509   //\r
510   // Retrieve PKCS#7 Data (DER encoding)\r
511   //\r
512   if (SignedDataSize > INT_MAX) {\r
513     goto _Exit;\r
514   }\r
515 \r
516   Temp = SignedData;\r
517   Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **) &Temp, (int) SignedDataSize);\r
518   if (Pkcs7 == NULL) {\r
519     goto _Exit;\r
520   }\r
521 \r
522   //\r
523   // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)\r
524   //\r
525   if (!PKCS7_type_is_signed (Pkcs7)) {\r
526     goto _Exit;\r
527   }\r
528 \r
529   //\r
530   // Read DER-encoded root certificate and Construct X509 Certificate\r
531   //\r
532   CertBio = BIO_new (BIO_s_mem ());\r
533   BIO_write (CertBio, TrustedCert, (int)CertLength);\r
534   if (CertBio == NULL) {\r
535     goto _Exit;\r
536   }\r
537   Cert = d2i_X509_bio (CertBio, NULL);\r
538   if (Cert == NULL) {\r
539     goto _Exit;\r
540   }\r
541 \r
542   //\r
543   // Setup X509 Store for trusted certificate\r
544   //\r
545   CertStore = X509_STORE_new ();\r
546   if (CertStore == NULL) {\r
547     goto _Exit;\r
548   }\r
549   if (!(X509_STORE_add_cert (CertStore, Cert))) {\r
550     goto _Exit;\r
551   }\r
552 \r
553   //\r
554   // Register customized X509 verification callback function to support\r
555   // trusted intermediate certificate anchor.\r
556   //\r
557   CertStore->verify_cb = X509VerifyCb;\r
558 \r
559   //\r
560   // For generic PKCS#7 handling, InData may be NULL if the content is present\r
561   // in PKCS#7 structure. So ignore NULL checking here.\r
562   //\r
563   DataBio = BIO_new (BIO_s_mem ());\r
564   BIO_write (DataBio, InData, (int)DataLength);\r
565 \r
566   //\r
567   // Verifies the PKCS#7 signedData structure\r
568   //\r
569   Status = (BOOLEAN) PKCS7_verify (Pkcs7, NULL, CertStore, DataBio, NULL, PKCS7_BINARY);\r
570 \r
571 _Exit:\r
572   //\r
573   // Release Resources\r
574   //\r
575   BIO_free (DataBio);\r
576   BIO_free (CertBio);\r
577   X509_free (Cert);\r
578   X509_STORE_free (CertStore);\r
579   PKCS7_free (Pkcs7);\r
580 \r
581   if (!Wrapped) {\r
582     OPENSSL_free (SignedData);\r
583   }\r
584 \r
585   return Status;\r
586 }\r