]> git.proxmox.com Git - mirror_edk2.git/blobdiff - SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
SecurityPkg/DxeImageVerificationLib: Differentiate error/search result (1) (CVE-2019...
[mirror_edk2.git] / SecurityPkg / Library / DxeImageVerificationLib / DxeImageVerificationLib.c
index c98b9e45923f48063198c8856c73d5eae423734f..85261ba7f2ae853c7cb67e846bda5297c98c2ef2 100644 (file)
@@ -704,7 +704,7 @@ GetImageExeInfoTableSize (
   @param[in]  Name            Input a null-terminated, user-friendly name.\r
   @param[in]  DevicePath      Input device path pointer.\r
   @param[in]  Signature       Input signature info in EFI_SIGNATURE_LIST data structure.\r
-  @param[in]  SignatureSize   Size of signature.\r
+  @param[in]  SignatureSize   Size of signature. Must be zero if Signature is NULL.\r
 \r
 **/\r
 VOID\r
@@ -761,6 +761,7 @@ AddImageExeInfo (
   //\r
   // Signature size can be odd. Pad after signature to ensure next EXECUTION_INFO entry align\r
   //\r
+  ASSERT (Signature != NULL || SignatureSize == 0);\r
   NewImageExeInfoEntrySize = sizeof (EFI_IMAGE_EXECUTION_INFO) + NameStringLen + DevicePathSize + SignatureSize;\r
 \r
   NewImageExeInfoTable      = (EFI_IMAGE_EXECUTION_INFO_TABLE *) AllocateRuntimePool (ImageExeInfoTableSize + NewImageExeInfoEntrySize);\r
@@ -821,22 +822,23 @@ AddImageExeInfo (
   @param[in]  SignatureList     Pointer to the Signature List in forbidden database.\r
   @param[in]  SignatureListSize Size of Signature List.\r
   @param[out] RevocationTime    Return the time that the certificate was revoked.\r
+  @param[out] IsFound           Search result. Only valid if EFI_SUCCESS returned.\r
 \r
-  @return TRUE   The certificate hash is found in the forbidden database.\r
-  @return FALSE  The certificate hash is not found in the forbidden database.\r
+  @retval EFI_SUCCESS           Finished the search without any error.\r
+  @retval Others                Error occurred in the search of database.\r
 \r
 **/\r
-BOOLEAN\r
+EFI_STATUS\r
 IsCertHashFoundInDatabase (\r
   IN  UINT8               *Certificate,\r
   IN  UINTN               CertSize,\r
   IN  EFI_SIGNATURE_LIST  *SignatureList,\r
   IN  UINTN               SignatureListSize,\r
-  OUT EFI_TIME            *RevocationTime\r
+  OUT EFI_TIME            *RevocationTime,\r
+  OUT BOOLEAN             *IsFound\r
   )\r
 {\r
-  BOOLEAN             IsFound;\r
-  BOOLEAN             Status;\r
+  EFI_STATUS          Status;\r
   EFI_SIGNATURE_LIST  *DbxList;\r
   UINTN               DbxSize;\r
   EFI_SIGNATURE_DATA  *CertHash;\r
@@ -850,21 +852,22 @@ IsCertHashFoundInDatabase (
   UINT8               *TBSCert;\r
   UINTN               TBSCertSize;\r
 \r
-  IsFound  = FALSE;\r
+  Status   = EFI_ABORTED;\r
+  *IsFound = FALSE;\r
   DbxList  = SignatureList;\r
   DbxSize  = SignatureListSize;\r
   HashCtx  = NULL;\r
   HashAlg  = HASHALG_MAX;\r
 \r
   if ((RevocationTime == NULL) || (DbxList == NULL)) {\r
-    return FALSE;\r
+    return EFI_INVALID_PARAMETER;\r
   }\r
 \r
   //\r
   // Retrieve the TBSCertificate from the X.509 Certificate.\r
   //\r
   if (!X509GetTBSCert (Certificate, CertSize, &TBSCert, &TBSCertSize)) {\r
-    return FALSE;\r
+    return Status;\r
   }\r
 \r
   while ((DbxSize > 0) && (SignatureListSize >= DbxList->SignatureListSize)) {\r
@@ -894,19 +897,19 @@ IsCertHashFoundInDatabase (
     if (HashCtx == NULL) {\r
       goto Done;\r
     }\r
-    Status = mHash[HashAlg].HashInit (HashCtx);\r
-    if (!Status) {\r
+    if (!mHash[HashAlg].HashInit (HashCtx)) {\r
       goto Done;\r
     }\r
-    Status = mHash[HashAlg].HashUpdate (HashCtx, TBSCert, TBSCertSize);\r
-    if (!Status) {\r
+    if (!mHash[HashAlg].HashUpdate (HashCtx, TBSCert, TBSCertSize)) {\r
       goto Done;\r
     }\r
-    Status = mHash[HashAlg].HashFinal (HashCtx, CertDigest);\r
-    if (!Status) {\r
+    if (!mHash[HashAlg].HashFinal (HashCtx, CertDigest)) {\r
       goto Done;\r
     }\r
 \r
+    FreePool (HashCtx);\r
+    HashCtx = NULL;\r
+\r
     SiglistHeaderSize = sizeof (EFI_SIGNATURE_LIST) + DbxList->SignatureHeaderSize;\r
     CertHash          = (EFI_SIGNATURE_DATA *) ((UINT8 *) DbxList + SiglistHeaderSize);\r
     CertHashCount     = (DbxList->SignatureListSize - SiglistHeaderSize) / DbxList->SignatureSize;\r
@@ -919,7 +922,8 @@ IsCertHashFoundInDatabase (
         //\r
         // Hash of Certificate is found in forbidden database.\r
         //\r
-        IsFound = TRUE;\r
+        Status   = EFI_SUCCESS;\r
+        *IsFound = TRUE;\r
 \r
         //\r
         // Return the revocation time.\r
@@ -934,12 +938,14 @@ IsCertHashFoundInDatabase (
     DbxList  = (EFI_SIGNATURE_LIST *) ((UINT8 *) DbxList + DbxList->SignatureListSize);\r
   }\r
 \r
+  Status = EFI_SUCCESS;\r
+\r
 Done:\r
   if (HashCtx != NULL) {\r
     FreePool (HashCtx);\r
   }\r
 \r
-  return IsFound;\r
+  return Status;\r
 }\r
 \r
 /**\r
@@ -1212,6 +1218,7 @@ IsForbiddenByDbx (
 {\r
   EFI_STATUS                Status;\r
   BOOLEAN                   IsForbidden;\r
+  BOOLEAN                   IsFound;\r
   UINT8                     *Data;\r
   UINTN                     DataSize;\r
   EFI_SIGNATURE_LIST        *CertList;\r
@@ -1322,7 +1329,7 @@ IsForbiddenByDbx (
   //       UINT8  Certn[];\r
   //\r
   Pkcs7GetSigners (AuthData, AuthDataSize, &CertBuffer, &BufferLength, &TrustedCert, &TrustedCertLength);\r
-  if ((BufferLength == 0) || (CertBuffer == NULL)) {\r
+  if ((BufferLength == 0) || (CertBuffer == NULL) || (*CertBuffer) == 0) {\r
     IsForbidden = TRUE;\r
     goto Done;\r
   }\r
@@ -1340,20 +1347,29 @@ IsForbiddenByDbx (
     //\r
     CertPtr = CertPtr + sizeof (UINT32) + CertSize;\r
 \r
-    if (IsCertHashFoundInDatabase (Cert, CertSize, (EFI_SIGNATURE_LIST *)Data, DataSize, &RevocationTime)) {\r
+    Status = IsCertHashFoundInDatabase (Cert, CertSize, (EFI_SIGNATURE_LIST *)Data, DataSize, &RevocationTime, &IsFound);\r
+    if (EFI_ERROR (Status)) {\r
       //\r
-      // Check the timestamp signature and signing time to determine if the image can be trusted.\r
+      // Error in searching dbx. Consider it as 'found'. RevocationTime might\r
+      // not be valid in such situation.\r
       //\r
       IsForbidden = TRUE;\r
+    } else if (IsFound) {\r
+      //\r
+      // Found Cert in dbx successfully. Check the timestamp signature and\r
+      // signing time to determine if the image can be trusted.\r
+      //\r
       if (PassTimestampCheck (AuthData, AuthDataSize, &RevocationTime)) {\r
         IsForbidden = FALSE;\r
         //\r
         // Pass DBT check. Continue to check other certs in image signer's cert list against DBX, DBT\r
         //\r
         continue;\r
+      } else {\r
+        IsForbidden = TRUE;\r
+        DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed but signature failed the timestamp check.\n"));\r
+        goto Done;\r
       }\r
-      DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed but signature failed the timestamp check.\n"));\r
-      goto Done;\r
     }\r
 \r
   }\r
@@ -1388,6 +1404,7 @@ IsAllowedByDb (
 {\r
   EFI_STATUS                Status;\r
   BOOLEAN                   VerifyStatus;\r
+  BOOLEAN                   IsFound;\r
   EFI_SIGNATURE_LIST        *CertList;\r
   EFI_SIGNATURE_DATA        *CertData;\r
   UINTN                     DataSize;\r
@@ -1408,65 +1425,100 @@ IsAllowedByDb (
   RootCertSize      = 0;\r
   VerifyStatus      = FALSE;\r
 \r
+  //\r
+  // Fetch 'db' content. If 'db' doesn't exist or encounters problem to get the\r
+  // data, return not-allowed-by-db (FALSE).\r
+  //\r
   DataSize = 0;\r
   Status   = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE, &gEfiImageSecurityDatabaseGuid, NULL, &DataSize, NULL);\r
-  if (Status == EFI_BUFFER_TOO_SMALL) {\r
-    Data = (UINT8 *) AllocateZeroPool (DataSize);\r
-    if (Data == NULL) {\r
-      return VerifyStatus;\r
+  ASSERT (EFI_ERROR (Status));\r
+  if (Status != EFI_BUFFER_TOO_SMALL) {\r
+    return VerifyStatus;\r
+  }\r
+\r
+  Data = (UINT8 *) AllocateZeroPool (DataSize);\r
+  if (Data == NULL) {\r
+    return VerifyStatus;\r
+  }\r
+\r
+  Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE, &gEfiImageSecurityDatabaseGuid, NULL, &DataSize, (VOID *) Data);\r
+  if (EFI_ERROR (Status)) {\r
+    goto Done;\r
+  }\r
+\r
+  //\r
+  // Fetch 'dbx' content. If 'dbx' doesn't exist, continue to check 'db'.\r
+  // If any other errors occured, no need to check 'db' but just return\r
+  // not-allowed-by-db (FALSE) to avoid bypass.\r
+  //\r
+  DbxDataSize = 0;\r
+  Status      = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE1, &gEfiImageSecurityDatabaseGuid, NULL, &DbxDataSize, NULL);\r
+  ASSERT (EFI_ERROR (Status));\r
+  if (Status != EFI_BUFFER_TOO_SMALL) {\r
+    if (Status != EFI_NOT_FOUND) {\r
+      goto Done;\r
+    }\r
+    //\r
+    // 'dbx' does not exist. Continue to check 'db'.\r
+    //\r
+  } else {\r
+    //\r
+    // 'dbx' exists. Get its content.\r
+    //\r
+    DbxData = (UINT8 *) AllocateZeroPool (DbxDataSize);\r
+    if (DbxData == NULL) {\r
+      goto Done;\r
     }\r
 \r
-    Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE, &gEfiImageSecurityDatabaseGuid, NULL, &DataSize, (VOID *) Data);\r
+    Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE1, &gEfiImageSecurityDatabaseGuid, NULL, &DbxDataSize, (VOID *) DbxData);\r
     if (EFI_ERROR (Status)) {\r
       goto Done;\r
     }\r
+  }\r
 \r
-    //\r
-    // Find X509 certificate in Signature List to verify the signature in pkcs7 signed data.\r
-    //\r
-    CertList = (EFI_SIGNATURE_LIST *) Data;\r
-    while ((DataSize > 0) && (DataSize >= CertList->SignatureListSize)) {\r
-      if (CompareGuid (&CertList->SignatureType, &gEfiCertX509Guid)) {\r
-        CertData  = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);\r
-        CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize;\r
+  //\r
+  // Find X509 certificate in Signature List to verify the signature in pkcs7 signed data.\r
+  //\r
+  CertList = (EFI_SIGNATURE_LIST *) Data;\r
+  while ((DataSize > 0) && (DataSize >= CertList->SignatureListSize)) {\r
+    if (CompareGuid (&CertList->SignatureType, &gEfiCertX509Guid)) {\r
+      CertData  = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);\r
+      CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize;\r
 \r
-        for (Index = 0; Index < CertCount; Index++) {\r
-          //\r
-          // Iterate each Signature Data Node within this CertList for verify.\r
-          //\r
-          RootCert     = CertData->SignatureData;\r
-          RootCertSize = CertList->SignatureSize - sizeof (EFI_GUID);\r
+      for (Index = 0; Index < CertCount; Index++) {\r
+        //\r
+        // Iterate each Signature Data Node within this CertList for verify.\r
+        //\r
+        RootCert     = CertData->SignatureData;\r
+        RootCertSize = CertList->SignatureSize - sizeof (EFI_GUID);\r
 \r
+        //\r
+        // Call AuthenticodeVerify library to Verify Authenticode struct.\r
+        //\r
+        VerifyStatus = AuthenticodeVerify (\r
+                         AuthData,\r
+                         AuthDataSize,\r
+                         RootCert,\r
+                         RootCertSize,\r
+                         mImageDigest,\r
+                         mImageDigestSize\r
+                         );\r
+        if (VerifyStatus) {\r
           //\r
-          // Call AuthenticodeVerify library to Verify Authenticode struct.\r
+          // The image is signed and its signature is found in 'db'.\r
           //\r
-          VerifyStatus = AuthenticodeVerify (\r
-                           AuthData,\r
-                           AuthDataSize,\r
-                           RootCert,\r
-                           RootCertSize,\r
-                           mImageDigest,\r
-                           mImageDigestSize\r
-                           );\r
-          if (VerifyStatus) {\r
+          if (DbxData != NULL) {\r
             //\r
             // Here We still need to check if this RootCert's Hash is revoked\r
             //\r
-            Status   = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE1, &gEfiImageSecurityDatabaseGuid, NULL, &DbxDataSize, NULL);\r
-            if (Status == EFI_BUFFER_TOO_SMALL) {\r
-              goto Done;\r
-            }\r
-            DbxData = (UINT8 *) AllocateZeroPool (DbxDataSize);\r
-            if (DbxData == NULL) {\r
-              goto Done;\r
-            }\r
-\r
-            Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE1, &gEfiImageSecurityDatabaseGuid, NULL, &DbxDataSize, (VOID *) DbxData);\r
+            Status = IsCertHashFoundInDatabase (RootCert, RootCertSize, (EFI_SIGNATURE_LIST *)DbxData, DbxDataSize, &RevocationTime, &IsFound);\r
             if (EFI_ERROR (Status)) {\r
-              goto Done;\r
-            }\r
-\r
-            if (IsCertHashFoundInDatabase (RootCert, RootCertSize, (EFI_SIGNATURE_LIST *)DbxData, DbxDataSize, &RevocationTime)) {\r
+              //\r
+              // Error in searching dbx. Consider it as 'found'. RevocationTime might\r
+              // not be valid in such situation.\r
+              //\r
+              VerifyStatus = FALSE;\r
+            } else if (IsFound) {\r
               //\r
               // Check the timestamp signature and signing time to determine if the RootCert can be trusted.\r
               //\r
@@ -1475,17 +1527,23 @@ IsAllowedByDb (
                 DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed and signature is accepted by DB, but its root cert failed the timestamp check.\n"));\r
               }\r
             }\r
-\r
-            goto Done;\r
           }\r
 \r
-          CertData = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertData + CertList->SignatureSize);\r
+          //\r
+          // There's no 'dbx' to check revocation time against (must-be pass),\r
+          // or, there's revocation time found in 'dbx' and checked againt 'dbt'\r
+          // (maybe pass or fail, depending on timestamp compare result). Either\r
+          // way the verification job has been completed at this point.\r
+          //\r
+          goto Done;\r
         }\r
-      }\r
 \r
-      DataSize -= CertList->SignatureListSize;\r
-      CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);\r
+        CertData = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertData + CertList->SignatureSize);\r
+      }\r
     }\r
+\r
+    DataSize -= CertList->SignatureListSize;\r
+    CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);\r
   }\r
 \r
 Done:\r
@@ -1547,7 +1605,8 @@ Done:
                                  execution table.\r
   @retval EFI_ACCESS_DENIED      The file specified by File and FileBuffer did not\r
                                  authenticate, and the platform policy dictates that the DXE\r
-                                 Foundation many not use File.\r
+                                 Foundation may not use File. The image has\r
+                                 been added to the file execution table.\r
 \r
 **/\r
 EFI_STATUS\r
@@ -1858,6 +1917,7 @@ DxeImageVerificationHandler (
     SignatureListSize = sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1 + mImageDigestSize;\r
     SignatureList     = (EFI_SIGNATURE_LIST *) AllocateZeroPool (SignatureListSize);\r
     if (SignatureList == NULL) {\r
+      SignatureListSize = 0;\r
       goto Failed;\r
     }\r
     SignatureList->SignatureHeaderSize  = 0;\r
@@ -1870,7 +1930,8 @@ DxeImageVerificationHandler (
 \r
 Failed:\r
   //\r
-  // Policy decides to defer or reject the image; add its information in image executable information table.\r
+  // Policy decides to defer or reject the image; add its information in image\r
+  // executable information table in either case.\r
   //\r
   NameStr = ConvertDevicePathToText (File, FALSE, TRUE);\r
   AddImageExeInfo (Action, NameStr, File, SignatureList, SignatureListSize);\r
@@ -1883,7 +1944,10 @@ Failed:
     FreePool (SignatureList);\r
   }\r
 \r
-  return EFI_SECURITY_VIOLATION;\r
+  if (Policy == DEFER_EXECUTE_ON_SECURITY_VIOLATION) {\r
+    return EFI_SECURITY_VIOLATION;\r
+  }\r
+  return EFI_ACCESS_DENIED;\r
 }\r
 \r
 /**\r