+/** @file\r
+ PKCS#7 SignedData Verification Wrapper Implementation over OpenSSL.\r
+\r
+ Caution: This module requires additional review when modified.\r
+ This library will have external input - signature (e.g. UEFI Authenticated\r
+ Variable). It may by input in SMM mode.\r
+ This external input must be validated carefully to avoid security issue like\r
+ buffer overflow, integer overflow.\r
+\r
+ WrapPkcs7Data(), Pkcs7GetSigners(), Pkcs7Verify() will get UEFI Authenticated\r
+ Variable and will do basic check for data structure.\r
+\r
+Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution. The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "InternalCryptLib.h"\r
+\r
+#include <openssl/objects.h>\r
+#include <openssl/x509.h>\r
+#include <openssl/pkcs7.h>\r
+\r
+UINT8 mOidValue[9] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02 };\r
+\r
+/**\r
+ Verification callback function to override any existing callbacks in OpenSSL\r
+ for intermediate certificate supports.\r
+\r
+ @param[in] Status Original status before calling this callback.\r
+ @param[in] Context X509 store context.\r
+\r
+ @retval 1 Current X509 certificate is verified successfully.\r
+ @retval 0 Verification failed.\r
+\r
+**/\r
+int\r
+X509VerifyCb (\r
+ IN int Status,\r
+ IN X509_STORE_CTX *Context\r
+ )\r
+{\r
+ X509_OBJECT *Obj;\r
+ INTN Error;\r
+ INTN Index;\r
+ INTN Count;\r
+\r
+ Obj = NULL;\r
+ Error = (INTN) X509_STORE_CTX_get_error (Context);\r
+\r
+ //\r
+ // X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT and X509_V_ERR_UNABLE_TO_GET_ISSUER_\r
+ // CERT_LOCALLY mean a X509 certificate is not self signed and its issuer\r
+ // can not be found in X509_verify_cert of X509_vfy.c.\r
+ // In order to support intermediate certificate node, we override the\r
+ // errors if the certification is obtained from X509 store, i.e. it is\r
+ // a trusted ceritifcate node that is enrolled by user.\r
+ // Besides,X509_V_ERR_CERT_UNTRUSTED and X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE\r
+ // are also ignored to enable such feature.\r
+ //\r
+ if ((Error == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT) ||\r
+ (Error == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY)) {\r
+ Obj = (X509_OBJECT *) malloc (sizeof (X509_OBJECT));\r
+ if (Obj == NULL) {\r
+ return 0;\r
+ }\r
+\r
+ Obj->type = X509_LU_X509;\r
+ Obj->data.x509 = Context->current_cert;\r
+\r
+ CRYPTO_w_lock (CRYPTO_LOCK_X509_STORE);\r
+\r
+ if (X509_OBJECT_retrieve_match (Context->ctx->objs, Obj)) {\r
+ Status = 1;\r
+ } else {\r
+ //\r
+ // If any certificate in the chain is enrolled as trusted certificate,\r
+ // pass the certificate verification.\r
+ //\r
+ if (Error == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY) {\r
+ Count = (INTN) sk_X509_num (Context->chain);\r
+ for (Index = 0; Index < Count; Index++) {\r
+ Obj->data.x509 = sk_X509_value (Context->chain, (int) Index);\r
+ if (X509_OBJECT_retrieve_match (Context->ctx->objs, Obj)) {\r
+ Status = 1;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ CRYPTO_w_unlock (CRYPTO_LOCK_X509_STORE);\r
+ }\r
+\r
+ if ((Error == X509_V_ERR_CERT_UNTRUSTED) ||\r
+ (Error == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE)) {\r
+ Status = 1;\r
+ }\r
+\r
+ if (Obj != NULL) {\r
+ OPENSSL_free (Obj);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Check input P7Data is a wrapped ContentInfo structure or not. If not construct\r
+ a new structure to wrap P7Data.\r
+\r
+ Caution: This function may receive untrusted input.\r
+ UEFI Authenticated Variable is external input, so this function will do basic\r
+ check for PKCS#7 data structure.\r
+\r
+ @param[in] P7Data Pointer to the PKCS#7 message to verify.\r
+ @param[in] P7Length Length of the PKCS#7 message in bytes.\r
+ @param[out] WrapFlag If TRUE P7Data is a ContentInfo structure, otherwise\r
+ return FALSE.\r
+ @param[out] WrapData If return status of this function is TRUE: \r
+ 1) when WrapFlag is TRUE, pointer to P7Data.\r
+ 2) when WrapFlag is FALSE, pointer to a new ContentInfo\r
+ structure. It's caller's responsibility to free this\r
+ buffer.\r
+ @param[out] WrapDataSize Length of ContentInfo structure in bytes.\r
+\r
+ @retval TRUE The operation is finished successfully.\r
+ @retval FALSE The operation is failed due to lack of resources.\r
+\r
+**/\r
+BOOLEAN\r
+WrapPkcs7Data (\r
+ IN CONST UINT8 *P7Data,\r
+ IN UINTN P7Length,\r
+ OUT BOOLEAN *WrapFlag,\r
+ OUT UINT8 **WrapData,\r
+ OUT UINTN *WrapDataSize\r
+ )\r
+{\r
+ BOOLEAN Wrapped;\r
+ UINT8 *SignedData;\r
+\r
+ //\r
+ // Check whether input P7Data is a wrapped ContentInfo structure or not.\r
+ //\r
+ Wrapped = FALSE;\r
+ if ((P7Data[4] == 0x06) && (P7Data[5] == 0x09)) {\r
+ if (CompareMem (P7Data + 6, mOidValue, sizeof (mOidValue)) == 0) {\r
+ if ((P7Data[15] == 0xA0) && (P7Data[16] == 0x82)) {\r
+ Wrapped = TRUE;\r
+ }\r
+ }\r
+ }\r
+\r
+ if (Wrapped) {\r
+ *WrapData = (UINT8 *) P7Data;\r
+ *WrapDataSize = P7Length;\r
+ } else {\r
+ //\r
+ // Wrap PKCS#7 signeddata to a ContentInfo structure - add a header in 19 bytes.\r
+ //\r
+ *WrapDataSize = P7Length + 19;\r
+ *WrapData = malloc (*WrapDataSize);\r
+ if (*WrapData == NULL) {\r
+ *WrapFlag = Wrapped;\r
+ return FALSE;\r
+ }\r
+\r
+ SignedData = *WrapData;\r
+\r
+ //\r
+ // Part1: 0x30, 0x82.\r
+ //\r
+ SignedData[0] = 0x30;\r
+ SignedData[1] = 0x82;\r
+\r
+ //\r
+ // Part2: Length1 = P7Length + 19 - 4, in big endian.\r
+ //\r
+ SignedData[2] = (UINT8) (((UINT16) (*WrapDataSize - 4)) >> 8);\r
+ SignedData[3] = (UINT8) (((UINT16) (*WrapDataSize - 4)) & 0xff);\r
+\r
+ //\r
+ // Part3: 0x06, 0x09.\r
+ //\r
+ SignedData[4] = 0x06;\r
+ SignedData[5] = 0x09;\r
+\r
+ //\r
+ // Part4: OID value -- 0x2A 0x86 0x48 0x86 0xF7 0x0D 0x01 0x07 0x02.\r
+ //\r
+ CopyMem (SignedData + 6, mOidValue, sizeof (mOidValue));\r
+\r
+ //\r
+ // Part5: 0xA0, 0x82.\r
+ //\r
+ SignedData[15] = 0xA0;\r
+ SignedData[16] = 0x82;\r
+\r
+ //\r
+ // Part6: Length2 = P7Length, in big endian.\r
+ //\r
+ SignedData[17] = (UINT8) (((UINT16) P7Length) >> 8);\r
+ SignedData[18] = (UINT8) (((UINT16) P7Length) & 0xff);\r
+\r
+ //\r
+ // Part7: P7Data.\r
+ //\r
+ CopyMem (SignedData + 19, P7Data, P7Length);\r
+ }\r
+\r
+ *WrapFlag = Wrapped;\r
+ return TRUE;\r
+}\r
+\r
+/**\r
+ Get the signer's certificates from PKCS#7 signed data as described in "PKCS #7:\r
+ Cryptographic Message Syntax Standard". The input signed data could be wrapped\r
+ in a ContentInfo structure.\r
+\r
+ If P7Data, CertStack, StackLength, TrustedCert or CertLength is NULL, then\r
+ return FALSE. If P7Length overflow, then return FAlSE.\r
+\r
+ Caution: This function may receive untrusted input.\r
+ UEFI Authenticated Variable is external input, so this function will do basic\r
+ check for PKCS#7 data structure.\r
+\r
+ @param[in] P7Data Pointer to the PKCS#7 message to verify.\r
+ @param[in] P7Length Length of the PKCS#7 message in bytes.\r
+ @param[out] CertStack Pointer to Signer's certificates retrieved from P7Data.\r
+ It's caller's responsiblity to free the buffer.\r
+ @param[out] StackLength Length of signer's certificates in bytes.\r
+ @param[out] TrustedCert Pointer to a trusted certificate from Signer's certificates.\r
+ It's caller's responsiblity to free the buffer.\r
+ @param[out] CertLength Length of the trusted certificate in bytes.\r
+\r
+ @retval TRUE The operation is finished successfully.\r
+ @retval FALSE Error occurs during the operation.\r
+\r
+**/\r
+BOOLEAN\r
+EFIAPI\r
+Pkcs7GetSigners (\r
+ IN CONST UINT8 *P7Data,\r
+ IN UINTN P7Length,\r
+ OUT UINT8 **CertStack,\r
+ OUT UINTN *StackLength,\r
+ OUT UINT8 **TrustedCert,\r
+ OUT UINTN *CertLength\r
+ )\r
+{\r
+ PKCS7 *Pkcs7;\r
+ BOOLEAN Status;\r
+ UINT8 *SignedData;\r
+ UINT8 *Temp;\r
+ UINTN SignedDataSize;\r
+ BOOLEAN Wrapped;\r
+ STACK_OF(X509) *Stack;\r
+ UINT8 Index;\r
+ UINT8 *CertBuf;\r
+ UINT8 *OldBuf;\r
+ UINTN BufferSize;\r
+ UINTN OldSize;\r
+ UINT8 *SingleCert;\r
+ UINTN SingleCertSize;\r
+\r
+ if ((P7Data == NULL) || (CertStack == NULL) || (StackLength == NULL) ||\r
+ (TrustedCert == NULL) || (CertLength == NULL) || (P7Length > INT_MAX)) {\r
+ return FALSE;\r
+ }\r
+ \r
+ Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &SignedData, &SignedDataSize);\r
+ if (!Status) {\r
+ return Status;\r
+ }\r
+\r
+ Status = FALSE;\r
+ Pkcs7 = NULL;\r
+ Stack = NULL;\r
+ CertBuf = NULL;\r
+ OldBuf = NULL;\r
+ SingleCert = NULL;\r
+\r
+ //\r
+ // Retrieve PKCS#7 Data (DER encoding)\r
+ //\r
+ if (SignedDataSize > INT_MAX) {\r
+ goto _Exit;\r
+ }\r
+\r
+ Temp = SignedData;\r
+ Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **) &Temp, (int) SignedDataSize);\r
+ if (Pkcs7 == NULL) {\r
+ goto _Exit;\r
+ }\r
+\r
+ //\r
+ // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)\r
+ //\r
+ if (!PKCS7_type_is_signed (Pkcs7)) {\r
+ goto _Exit;\r
+ }\r
+\r
+ Stack = PKCS7_get0_signers(Pkcs7, NULL, PKCS7_BINARY);\r
+ if (Stack == NULL) {\r
+ goto _Exit;\r
+ }\r
+\r
+ //\r
+ // Convert CertStack to buffer in following format:\r
+ // UINT8 CertNumber;\r
+ // UINT32 Cert1Length;\r
+ // UINT8 Cert1[];\r
+ // UINT32 Cert2Length;\r
+ // UINT8 Cert2[];\r
+ // ...\r
+ // UINT32 CertnLength;\r
+ // UINT8 Certn[];\r
+ //\r
+ BufferSize = sizeof (UINT8);\r
+ OldSize = BufferSize;\r
+ \r
+ for (Index = 0; ; Index++) {\r
+ Status = X509PopCertificate (Stack, &SingleCert, &SingleCertSize);\r
+ if (!Status) {\r
+ break;\r
+ }\r
+\r
+ OldSize = BufferSize;\r
+ OldBuf = CertBuf;\r
+ BufferSize = OldSize + SingleCertSize + sizeof (UINT32);\r
+ CertBuf = malloc (BufferSize);\r
+\r
+ if (CertBuf == NULL) {\r
+ goto _Exit;\r
+ }\r
+\r
+ if (OldBuf != NULL) {\r
+ CopyMem (CertBuf, OldBuf, OldSize);\r
+ free (OldBuf);\r
+ OldBuf = NULL;\r
+ }\r
+\r
+ WriteUnaligned32 ((UINT32 *) (CertBuf + OldSize), (UINT32) SingleCertSize);\r
+ CopyMem (CertBuf + OldSize + sizeof (UINT32), SingleCert, SingleCertSize);\r
+\r
+ free (SingleCert);\r
+ SingleCert = NULL;\r
+ }\r
+\r
+ if (CertBuf != NULL) {\r
+ //\r
+ // Update CertNumber.\r
+ //\r
+ CertBuf[0] = Index;\r
+\r
+ *CertLength = BufferSize - OldSize - sizeof (UINT32);\r
+ *TrustedCert = malloc (*CertLength);\r
+ if (*TrustedCert == NULL) {\r
+ goto _Exit;\r
+ }\r
+\r
+ CopyMem (*TrustedCert, CertBuf + OldSize + sizeof (UINT32), *CertLength);\r
+ *CertStack = CertBuf;\r
+ *StackLength = BufferSize;\r
+ Status = TRUE;\r
+ } \r
+\r
+_Exit:\r
+ //\r
+ // Release Resources\r
+ //\r
+ if (!Wrapped) {\r
+ free (SignedData);\r
+ }\r
+\r
+ if (Pkcs7 != NULL) {\r
+ PKCS7_free (Pkcs7);\r
+ }\r
+\r
+ if (Stack != NULL) {\r
+ sk_X509_pop_free(Stack, X509_free);\r
+ }\r
+\r
+ if (SingleCert != NULL) {\r
+ free (SingleCert);\r
+ }\r
+\r
+ if (!Status && (CertBuf != NULL)) {\r
+ free (CertBuf);\r
+ *CertStack = NULL;\r
+ }\r
+\r
+ if (OldBuf != NULL) {\r
+ free (OldBuf);\r
+ }\r
+ \r
+ return Status;\r
+}\r
+\r
+/**\r
+ Wrap function to use free() to free allocated memory for certificates.\r
+\r
+ @param[in] Certs Pointer to the certificates to be freed.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Pkcs7FreeSigners (\r
+ IN UINT8 *Certs\r
+ )\r
+{\r
+ if (Certs == NULL) {\r
+ return;\r
+ }\r
+\r
+ free (Certs);\r
+}\r
+\r
+/**\r
+ Verifies the validility of a PKCS#7 signed data as described in "PKCS #7:\r
+ Cryptographic Message Syntax Standard". The input signed data could be wrapped\r
+ in a ContentInfo structure.\r
+\r
+ If P7Data, TrustedCert or InData is NULL, then return FALSE.\r
+ If P7Length, CertLength or DataLength overflow, then return FAlSE.\r
+\r
+ Caution: This function may receive untrusted input.\r
+ UEFI Authenticated Variable is external input, so this function will do basic\r
+ check for PKCS#7 data structure.\r
+\r
+ @param[in] P7Data Pointer to the PKCS#7 message to verify.\r
+ @param[in] P7Length Length of the PKCS#7 message in bytes.\r
+ @param[in] TrustedCert Pointer to a trusted/root certificate encoded in DER, which\r
+ is used for certificate chain verification.\r
+ @param[in] CertLength Length of the trusted certificate in bytes.\r
+ @param[in] InData Pointer to the content to be verified.\r
+ @param[in] DataLength Length of InData in bytes.\r
+\r
+ @retval TRUE The specified PKCS#7 signed data is valid.\r
+ @retval FALSE Invalid PKCS#7 signed data.\r
+\r
+**/\r
+BOOLEAN\r
+EFIAPI\r
+Pkcs7Verify (\r
+ IN CONST UINT8 *P7Data,\r
+ IN UINTN P7Length,\r
+ IN CONST UINT8 *TrustedCert,\r
+ IN UINTN CertLength,\r
+ IN CONST UINT8 *InData,\r
+ IN UINTN DataLength\r
+ )\r
+{\r
+ PKCS7 *Pkcs7;\r
+ BIO *CertBio;\r
+ BIO *DataBio;\r
+ BOOLEAN Status;\r
+ X509 *Cert;\r
+ X509_STORE *CertStore;\r
+ UINT8 *SignedData;\r
+ UINT8 *Temp;\r
+ UINTN SignedDataSize;\r
+ BOOLEAN Wrapped;\r
+\r
+ //\r
+ // Check input parameters.\r
+ //\r
+ if (P7Data == NULL || TrustedCert == NULL || InData == NULL || \r
+ P7Length > INT_MAX || CertLength > INT_MAX || DataLength > INT_MAX) {\r
+ return FALSE;\r
+ }\r
+ \r
+ Pkcs7 = NULL;\r
+ CertBio = NULL;\r
+ DataBio = NULL;\r
+ Cert = NULL;\r
+ CertStore = NULL;\r
+\r
+ //\r
+ // Register & Initialize necessary digest algorithms for PKCS#7 Handling\r
+ //\r
+ EVP_add_digest (EVP_md5());\r
+ EVP_add_digest (EVP_sha1());\r
+ EVP_add_digest_alias (SN_sha1WithRSAEncryption, SN_sha1WithRSA);\r
+ EVP_add_digest (EVP_sha256());\r
+\r
+ Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &SignedData, &SignedDataSize);\r
+ if (!Status) {\r
+ return Status;\r
+ }\r
+\r
+ Status = FALSE;\r
+ \r
+ //\r
+ // Retrieve PKCS#7 Data (DER encoding)\r
+ //\r
+ if (SignedDataSize > INT_MAX) {\r
+ goto _Exit;\r
+ }\r
+\r
+ Temp = SignedData;\r
+ Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **) &Temp, (int) SignedDataSize);\r
+ if (Pkcs7 == NULL) {\r
+ goto _Exit;\r
+ }\r
+\r
+ //\r
+ // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)\r
+ //\r
+ if (!PKCS7_type_is_signed (Pkcs7)) {\r
+ goto _Exit;\r
+ }\r
+\r
+ //\r
+ // Read DER-encoded root certificate and Construct X509 Certificate\r
+ //\r
+ CertBio = BIO_new (BIO_s_mem ());\r
+ BIO_write (CertBio, TrustedCert, (int)CertLength);\r
+ if (CertBio == NULL) {\r
+ goto _Exit;\r
+ }\r
+ Cert = d2i_X509_bio (CertBio, NULL);\r
+ if (Cert == NULL) {\r
+ goto _Exit;\r
+ }\r
+\r
+ //\r
+ // Setup X509 Store for trusted certificate\r
+ //\r
+ CertStore = X509_STORE_new ();\r
+ if (CertStore == NULL) {\r
+ goto _Exit;\r
+ }\r
+ if (!(X509_STORE_add_cert (CertStore, Cert))) {\r
+ goto _Exit;\r
+ }\r
+\r
+ //\r
+ // Register customized X509 verification callback function to support\r
+ // trusted intermediate certificate anchor.\r
+ //\r
+ CertStore->verify_cb = X509VerifyCb;\r
+\r
+ //\r
+ // For generic PKCS#7 handling, InData may be NULL if the content is present\r
+ // in PKCS#7 structure. So ignore NULL checking here.\r
+ //\r
+ DataBio = BIO_new (BIO_s_mem ());\r
+ BIO_write (DataBio, InData, (int)DataLength);\r
+\r
+ //\r
+ // Verifies the PKCS#7 signedData structure\r
+ //\r
+ Status = (BOOLEAN) PKCS7_verify (Pkcs7, NULL, CertStore, DataBio, NULL, PKCS7_BINARY);\r
+\r
+_Exit:\r
+ //\r
+ // Release Resources\r
+ //\r
+ BIO_free (DataBio);\r
+ BIO_free (CertBio);\r
+ X509_free (Cert);\r
+ X509_STORE_free (CertStore);\r
+ PKCS7_free (Pkcs7);\r
+\r
+ if (!Wrapped) {\r
+ OPENSSL_free (SignedData);\r
+ }\r
+\r
+ return Status;\r
+}\r