]> git.proxmox.com Git - mirror_edk2.git/blobdiff - SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c
SecurityPkg/DxeImageVerificationLib: refactor db/dbx fetching code (CVE-2019-14575)
[mirror_edk2.git] / SecurityPkg / Library / DxeImageVerificationLib / DxeImageVerificationLib.c
index 5cc82c1b3b224f41041932255dddde6beaf27102..8739d1fa294612ebb930143bac7d157532ec6550 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
@@ -907,6 +908,9 @@ IsCertHashFoundInDatabase (
       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
@@ -1322,7 +1326,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
@@ -1408,64 +1412,92 @@ 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
-            if (EFI_ERROR (Status)) {\r
-              goto Done;\r
-            }\r
-\r
             if (IsCertHashFoundInDatabase (RootCert, RootCertSize, (EFI_SIGNATURE_LIST *)DbxData, DbxDataSize, &RevocationTime)) {\r
               //\r
               // Check the timestamp signature and signing time to determine if the RootCert can be trusted.\r
@@ -1475,17 +1507,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
@@ -1541,14 +1579,14 @@ Done:
                                  and non-NULL FileBuffer did authenticate, and the platform\r
                                  policy dictates that the DXE Foundation may execute the image in\r
                                  FileBuffer.\r
-  @retval EFI_OUT_RESOURCE       Fail to allocate memory.\r
   @retval EFI_SECURITY_VIOLATION The file specified by File did not authenticate, and\r
                                  the platform policy dictates that File should be placed\r
                                  in the untrusted state. The image has been added to the file\r
                                  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
@@ -1561,7 +1599,6 @@ DxeImageVerificationHandler (
   IN  BOOLEAN                          BootPolicy\r
   )\r
 {\r
-  EFI_STATUS                           Status;\r
   EFI_IMAGE_DOS_HEADER                 *DosHdr;\r
   BOOLEAN                              IsVerified;\r
   EFI_SIGNATURE_LIST                   *SignatureList;\r
@@ -1589,7 +1626,6 @@ DxeImageVerificationHandler (
   SecDataDir        = NULL;\r
   PkcsCertData      = NULL;\r
   Action            = EFI_IMAGE_EXECUTION_AUTH_UNTESTED;\r
-  Status            = EFI_ACCESS_DENIED;\r
   IsVerified        = FALSE;\r
 \r
 \r
@@ -1658,7 +1694,7 @@ DxeImageVerificationHandler (
   // Read the Dos header.\r
   //\r
   if (FileBuffer == NULL) {\r
-    return EFI_INVALID_PARAMETER;\r
+    return EFI_ACCESS_DENIED;\r
   }\r
 \r
   mImageBase  = (UINT8 *) FileBuffer;\r
@@ -1677,7 +1713,7 @@ DxeImageVerificationHandler (
     // The information can't be got from the invalid PeImage\r
     //\r
     DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: PeImage invalid. Cannot retrieve image information.\n"));\r
-    goto Done;\r
+    goto Failed;\r
   }\r
 \r
   DosHdr = (EFI_IMAGE_DOS_HEADER *) mImageBase;\r
@@ -1699,7 +1735,7 @@ DxeImageVerificationHandler (
     // It is not a valid Pe/Coff file.\r
     //\r
     DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Not a valid PE/COFF image.\n"));\r
-    goto Done;\r
+    goto Failed;\r
   }\r
 \r
   if (mNtHeader.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\r
@@ -1730,7 +1766,7 @@ DxeImageVerificationHandler (
     //\r
     if (!HashPeImage (HASHALG_SHA256)) {\r
       DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Failed to hash this image using %s.\n", mHashTypeStr));\r
-      goto Done;\r
+      goto Failed;\r
     }\r
 \r
     if (IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE1, mImageDigest, &mCertType, mImageDigestSize)) {\r
@@ -1738,7 +1774,7 @@ DxeImageVerificationHandler (
       // Image Hash is in forbidden database (DBX).\r
       //\r
       DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is not signed and %s hash of image is forbidden by DBX.\n", mHashTypeStr));\r
-      goto Done;\r
+      goto Failed;\r
     }\r
 \r
     if (IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE, mImageDigest, &mCertType, mImageDigestSize)) {\r
@@ -1752,7 +1788,7 @@ DxeImageVerificationHandler (
     // Image Hash is not found in both forbidden and allowed database.\r
     //\r
     DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is not signed and %s hash of image is not found in DB/DBX.\n", mHashTypeStr));\r
-    goto Done;\r
+    goto Failed;\r
   }\r
 \r
   //\r
@@ -1854,7 +1890,6 @@ DxeImageVerificationHandler (
   if (IsVerified) {\r
     return EFI_SUCCESS;\r
   }\r
-  Status = EFI_ACCESS_DENIED;\r
   if (Action == EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED || Action == EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND) {\r
     //\r
     // Get image hash value as signature of executable.\r
@@ -1862,8 +1897,8 @@ DxeImageVerificationHandler (
     SignatureListSize = sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1 + mImageDigestSize;\r
     SignatureList     = (EFI_SIGNATURE_LIST *) AllocateZeroPool (SignatureListSize);\r
     if (SignatureList == NULL) {\r
-      Status = EFI_OUT_OF_RESOURCES;\r
-      goto Done;\r
+      SignatureListSize = 0;\r
+      goto Failed;\r
     }\r
     SignatureList->SignatureHeaderSize  = 0;\r
     SignatureList->SignatureListSize    = (UINT32) SignatureListSize;\r
@@ -1873,25 +1908,26 @@ DxeImageVerificationHandler (
     CopyMem (Signature->SignatureData, mImageDigest, mImageDigestSize);\r
   }\r
 \r
-Done:\r
-  if (Status != EFI_SUCCESS) {\r
-    //\r
-    // Policy decides to defer or reject the image; add its information in image executable information table.\r
-    //\r
-    NameStr = ConvertDevicePathToText (File, FALSE, TRUE);\r
-    AddImageExeInfo (Action, NameStr, File, SignatureList, SignatureListSize);\r
-    if (NameStr != NULL) {\r
-      DEBUG((EFI_D_INFO, "The image doesn't pass verification: %s\n", NameStr));\r
-      FreePool(NameStr);\r
-    }\r
-    Status = EFI_SECURITY_VIOLATION;\r
+Failed:\r
+  //\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
+  if (NameStr != NULL) {\r
+    DEBUG ((DEBUG_INFO, "The image doesn't pass verification: %s\n", NameStr));\r
+    FreePool(NameStr);\r
   }\r
 \r
   if (SignatureList != NULL) {\r
     FreePool (SignatureList);\r
   }\r
 \r
-  return Status;\r
+  if (Policy == DEFER_EXECUTE_ON_SECURITY_VIOLATION) {\r
+    return EFI_SECURITY_VIOLATION;\r
+  }\r
+  return EFI_ACCESS_DENIED;\r
 }\r
 \r
 /**\r