Blame SOURCES/0058-Handle-binaries-with-multiple-signatures.patch

d1e1c8
From 76c0447e204c7e4ce918c4887ce8aae0e0816271 Mon Sep 17 00:00:00 2001
d1e1c8
From: Peter Jones <pjones@redhat.com>
d1e1c8
Date: Thu, 23 Jul 2020 16:32:05 -0400
d1e1c8
Subject: [PATCH 58/62] Handle binaries with multiple signatures.
d1e1c8
d1e1c8
This adds support for multiple signatures.  It first tries validating
d1e1c8
the binary by hash, first against our dbx lists, then against our db
d1e1c8
lists.  If it isn't allowed or rejected at that step, it continues to
d1e1c8
the normal routine of checking all the signatures.
d1e1c8
d1e1c8
At this point it does *not* reject a binary just because a signature is
d1e1c8
by a cert on a dbx list, though that will override any db list that
d1e1c8
certificate is listed on.  If at any point any assertion about the
d1e1c8
binary or signature list being well-formed fails, the binary is
d1e1c8
immediately rejected, though we do allow skipping over signatures
d1e1c8
which have an unsupported sig->Hdr.wCertificateType.
d1e1c8
d1e1c8
Signed-off-by: Peter Jones <pjones@redhat.com>
d1e1c8
Upstream: pr#210
d1e1c8
---
d1e1c8
 shim.c | 287 +++++++++++++++++++++++++++++++++++++++------------------
d1e1c8
 1 file changed, 198 insertions(+), 89 deletions(-)
d1e1c8
d1e1c8
diff --git a/shim.c b/shim.c
d1e1c8
index ee62248ca4e..d10a1ba1cac 100644
d1e1c8
--- a/shim.c
d1e1c8
+++ b/shim.c
d1e1c8
@@ -690,7 +690,7 @@ static EFI_STATUS check_whitelist (WIN_CERTIFICATE_EFI_PKCS *cert,
d1e1c8
 	}
d1e1c8
 
d1e1c8
 	update_verification_method(VERIFIED_BY_NOTHING);
d1e1c8
-	return EFI_SECURITY_VIOLATION;
d1e1c8
+	return EFI_NOT_FOUND;
d1e1c8
 }
d1e1c8
 
d1e1c8
 /*
d1e1c8
@@ -1004,6 +1004,103 @@ done:
d1e1c8
 	return efi_status;
d1e1c8
 }
d1e1c8
 
d1e1c8
+static EFI_STATUS
d1e1c8
+verify_one_signature(WIN_CERTIFICATE_EFI_PKCS *sig,
d1e1c8
+		     UINT8 *sha256hash, UINT8 *sha1hash)
d1e1c8
+{
d1e1c8
+	EFI_STATUS efi_status;
d1e1c8
+
d1e1c8
+	/*
d1e1c8
+	 * Ensure that the binary isn't blacklisted
d1e1c8
+	 */
d1e1c8
+	drain_openssl_errors();
d1e1c8
+	efi_status = check_blacklist(sig, sha256hash, sha1hash);
d1e1c8
+	if (EFI_ERROR(efi_status)) {
d1e1c8
+		perror(L"Binary is blacklisted: %r\n", efi_status);
d1e1c8
+		PrintErrors();
d1e1c8
+		ClearErrors();
d1e1c8
+		crypterr(efi_status);
d1e1c8
+		return efi_status;
d1e1c8
+	}
d1e1c8
+
d1e1c8
+	/*
d1e1c8
+	 * Check whether the binary is whitelisted in any of the firmware
d1e1c8
+	 * databases
d1e1c8
+	 */
d1e1c8
+	drain_openssl_errors();
d1e1c8
+	efi_status = check_whitelist(sig, sha256hash, sha1hash);
d1e1c8
+	if (EFI_ERROR(efi_status)) {
d1e1c8
+		if (efi_status != EFI_NOT_FOUND) {
d1e1c8
+			dprint(L"check_whitelist(): %r\n", efi_status);
d1e1c8
+			PrintErrors();
d1e1c8
+			ClearErrors();
d1e1c8
+			crypterr(efi_status);
d1e1c8
+		}
d1e1c8
+	} else {
d1e1c8
+		drain_openssl_errors();
d1e1c8
+		return efi_status;
d1e1c8
+	}
d1e1c8
+
d1e1c8
+	efi_status = EFI_NOT_FOUND;
d1e1c8
+#if defined(ENABLE_SHIM_CERT)
d1e1c8
+	/*
d1e1c8
+	 * Check against the shim build key
d1e1c8
+	 */
d1e1c8
+	drain_openssl_errors();
d1e1c8
+	if (build_cert && build_cert_size) {
d1e1c8
+		dprint("verifying against shim cert\n");
d1e1c8
+	}
d1e1c8
+	if (build_cert && build_cert_size &&
d1e1c8
+	    AuthenticodeVerify(sig->CertData,
d1e1c8
+		       sig->Hdr.dwLength - sizeof(sig->Hdr),
d1e1c8
+		       build_cert, build_cert_size, sha256hash,
d1e1c8
+		       SHA256_DIGEST_SIZE)) {
d1e1c8
+		dprint(L"AuthenticodeVerify(shim_cert) succeeded\n");
d1e1c8
+		update_verification_method(VERIFIED_BY_CERT);
d1e1c8
+		tpm_measure_variable(L"Shim", SHIM_LOCK_GUID,
d1e1c8
+				     build_cert_size, build_cert);
d1e1c8
+		efi_status = EFI_SUCCESS;
d1e1c8
+		drain_openssl_errors();
d1e1c8
+		return efi_status;
d1e1c8
+	} else {
d1e1c8
+		dprint(L"AuthenticodeVerify(shim_cert) failed\n");
d1e1c8
+		PrintErrors();
d1e1c8
+		ClearErrors();
d1e1c8
+		crypterr(EFI_NOT_FOUND);
d1e1c8
+	}
d1e1c8
+#endif /* defined(ENABLE_SHIM_CERT) */
d1e1c8
+
d1e1c8
+#if defined(VENDOR_CERT_FILE)
d1e1c8
+	/*
d1e1c8
+	 * And finally, check against shim's built-in key
d1e1c8
+	 */
d1e1c8
+	drain_openssl_errors();
d1e1c8
+	if (vendor_cert_size) {
d1e1c8
+		dprint("verifying against vendor_cert\n");
d1e1c8
+	}
d1e1c8
+	if (vendor_cert_size &&
d1e1c8
+	    AuthenticodeVerify(sig->CertData,
d1e1c8
+			       sig->Hdr.dwLength - sizeof(sig->Hdr),
d1e1c8
+			       vendor_cert, vendor_cert_size,
d1e1c8
+			       sha256hash, SHA256_DIGEST_SIZE)) {
d1e1c8
+		dprint(L"AuthenticodeVerify(vendor_cert) succeeded\n");
d1e1c8
+		update_verification_method(VERIFIED_BY_CERT);
d1e1c8
+		tpm_measure_variable(L"Shim", SHIM_LOCK_GUID,
d1e1c8
+				     vendor_cert_size, vendor_cert);
d1e1c8
+		efi_status = EFI_SUCCESS;
d1e1c8
+		drain_openssl_errors();
d1e1c8
+		return efi_status;
d1e1c8
+	} else {
d1e1c8
+		dprint(L"AuthenticodeVerify(vendor_cert) failed\n");
d1e1c8
+		PrintErrors();
d1e1c8
+		ClearErrors();
d1e1c8
+		crypterr(EFI_NOT_FOUND);
d1e1c8
+	}
d1e1c8
+#endif /* defined(VENDOR_CERT_FILE) */
d1e1c8
+
d1e1c8
+	return efi_status;
d1e1c8
+}
d1e1c8
+
d1e1c8
 /*
d1e1c8
  * Check that the signature is valid and matches the binary
d1e1c8
  */
d1e1c8
@@ -1011,40 +1108,14 @@ static EFI_STATUS verify_buffer (char *data, int datasize,
d1e1c8
 				 PE_COFF_LOADER_IMAGE_CONTEXT *context,
d1e1c8
 				 UINT8 *sha256hash, UINT8 *sha1hash)
d1e1c8
 {
d1e1c8
-	EFI_STATUS efi_status = EFI_SECURITY_VIOLATION;
d1e1c8
-	WIN_CERTIFICATE_EFI_PKCS *cert = NULL;
d1e1c8
-	unsigned int size = datasize;
d1e1c8
+	EFI_STATUS ret_efi_status;
d1e1c8
+	size_t size = datasize;
d1e1c8
+	size_t offset = 0;
d1e1c8
+	unsigned int i = 0;
d1e1c8
 
d1e1c8
 	if (datasize < 0)
d1e1c8
 		return EFI_INVALID_PARAMETER;
d1e1c8
 
d1e1c8
-	if (context->SecDir->Size != 0) {
d1e1c8
-		if (context->SecDir->Size >= size) {
d1e1c8
-			perror(L"Certificate Database size is too large\n");
d1e1c8
-			return EFI_INVALID_PARAMETER;
d1e1c8
-		}
d1e1c8
-
d1e1c8
-		cert = ImageAddress (data, size,
d1e1c8
-				     context->SecDir->VirtualAddress);
d1e1c8
-
d1e1c8
-		if (!cert) {
d1e1c8
-			perror(L"Certificate located outside the image\n");
d1e1c8
-			return EFI_INVALID_PARAMETER;
d1e1c8
-		}
d1e1c8
-
d1e1c8
-		if (cert->Hdr.dwLength > context->SecDir->Size) {
d1e1c8
-			perror(L"Certificate list size is inconsistent with PE headers");
d1e1c8
-			return EFI_INVALID_PARAMETER;
d1e1c8
-		}
d1e1c8
-
d1e1c8
-		if (cert->Hdr.wCertificateType !=
d1e1c8
-		    WIN_CERT_TYPE_PKCS_SIGNED_DATA) {
d1e1c8
-			perror(L"Unsupported certificate type %x\n",
d1e1c8
-				cert->Hdr.wCertificateType);
d1e1c8
-			return EFI_UNSUPPORTED;
d1e1c8
-		}
d1e1c8
-	}
d1e1c8
-
d1e1c8
 	/*
d1e1c8
 	 * Clear OpenSSL's error log, because we get some DSO unimplemented
d1e1c8
 	 * errors during its intialization, and we don't want those to look
d1e1c8
@@ -1052,81 +1123,119 @@ static EFI_STATUS verify_buffer (char *data, int datasize,
d1e1c8
 	 */
d1e1c8
 	drain_openssl_errors();
d1e1c8
 
d1e1c8
-	efi_status = generate_hash(data, datasize, context, sha256hash, sha1hash);
d1e1c8
-	if (EFI_ERROR(efi_status)) {
d1e1c8
-		LogError(L"generate_hash: %r\n", efi_status);
d1e1c8
-		return efi_status;
d1e1c8
+	ret_efi_status = generate_hash(data, datasize, context, sha256hash, sha1hash);
d1e1c8
+	if (EFI_ERROR(ret_efi_status)) {
d1e1c8
+		dprint(L"generate_hash: %r\n", ret_efi_status);
d1e1c8
+		PrintErrors();
d1e1c8
+		ClearErrors();
d1e1c8
+		crypterr(ret_efi_status);
d1e1c8
+		return ret_efi_status;
d1e1c8
 	}
d1e1c8
 
d1e1c8
 	/*
d1e1c8
-	 * Ensure that the binary isn't blacklisted
d1e1c8
+	 * Ensure that the binary isn't blacklisted by hash
d1e1c8
 	 */
d1e1c8
-	efi_status = check_blacklist(cert, sha256hash, sha1hash);
d1e1c8
-	if (EFI_ERROR(efi_status)) {
d1e1c8
+	drain_openssl_errors();
d1e1c8
+	ret_efi_status = check_blacklist(NULL, sha256hash, sha1hash);
d1e1c8
+	if (EFI_ERROR(ret_efi_status)) {
d1e1c8
 		perror(L"Binary is blacklisted\n");
d1e1c8
-		LogError(L"Binary is blacklisted: %r\n", efi_status);
d1e1c8
-		return efi_status;
d1e1c8
+		dprint(L"Binary is blacklisted: %r\n", ret_efi_status);
d1e1c8
+		PrintErrors();
d1e1c8
+		ClearErrors();
d1e1c8
+		crypterr(ret_efi_status);
d1e1c8
+		return ret_efi_status;
d1e1c8
 	}
d1e1c8
 
d1e1c8
 	/*
d1e1c8
-	 * Check whether the binary is whitelisted in any of the firmware
d1e1c8
-	 * databases
d1e1c8
+	 * Check whether the binary is whitelisted by hash in any of the
d1e1c8
+	 * firmware databases
d1e1c8
 	 */
d1e1c8
-	efi_status = check_whitelist(cert, sha256hash, sha1hash);
d1e1c8
-	if (EFI_ERROR(efi_status)) {
d1e1c8
-		LogError(L"check_whitelist(): %r\n", efi_status);
d1e1c8
+	drain_openssl_errors();
d1e1c8
+	ret_efi_status = check_whitelist(NULL, sha256hash, sha1hash);
d1e1c8
+	if (EFI_ERROR(ret_efi_status)) {
d1e1c8
+		dprint(L"check_whitelist: %r\n", ret_efi_status);
d1e1c8
+		if (ret_efi_status != EFI_NOT_FOUND) {
d1e1c8
+			PrintErrors();
d1e1c8
+			ClearErrors();
d1e1c8
+			crypterr(ret_efi_status);
d1e1c8
+			return ret_efi_status;
d1e1c8
+		}
d1e1c8
 	} else {
d1e1c8
 		drain_openssl_errors();
d1e1c8
-		return efi_status;
d1e1c8
+		return ret_efi_status;
d1e1c8
 	}
d1e1c8
 
d1e1c8
-	if (cert) {
d1e1c8
-#if defined(ENABLE_SHIM_CERT)
d1e1c8
-		/*
d1e1c8
-		 * Check against the shim build key
d1e1c8
-		 */
d1e1c8
-		if (sizeof(shim_cert) &&
d1e1c8
-		    AuthenticodeVerify(cert->CertData,
d1e1c8
-			       cert->Hdr.dwLength - sizeof(cert->Hdr),
d1e1c8
-			       shim_cert, sizeof(shim_cert), sha256hash,
d1e1c8
-			       SHA256_DIGEST_SIZE)) {
d1e1c8
-			update_verification_method(VERIFIED_BY_CERT);
d1e1c8
-			tpm_measure_variable(L"Shim", SHIM_LOCK_GUID,
d1e1c8
-					     sizeof(shim_cert), shim_cert);
d1e1c8
-			efi_status = EFI_SUCCESS;
d1e1c8
-			drain_openssl_errors();
d1e1c8
-			return efi_status;
d1e1c8
-		} else {
d1e1c8
-			LogError(L"AuthenticodeVerify(shim_cert) failed\n");
d1e1c8
+	if (context->SecDir->Size == 0) {
d1e1c8
+		dprint(L"No signatures found\n");
d1e1c8
+		return EFI_SECURITY_VIOLATION;
d1e1c8
+	}
d1e1c8
+
d1e1c8
+	if (context->SecDir->Size >= size) {
d1e1c8
+		perror(L"Certificate Database size is too large\n");
d1e1c8
+		return EFI_INVALID_PARAMETER;
d1e1c8
+	}
d1e1c8
+
d1e1c8
+	ret_efi_status = EFI_NOT_FOUND;
d1e1c8
+	do {
d1e1c8
+		WIN_CERTIFICATE_EFI_PKCS *sig = NULL;
d1e1c8
+		size_t sz;
d1e1c8
+
d1e1c8
+		sig = ImageAddress(data, size,
d1e1c8
+				   context->SecDir->VirtualAddress + offset);
d1e1c8
+		if (!sig)
d1e1c8
+			break;
d1e1c8
+
d1e1c8
+		sz = offset + offsetof(WIN_CERTIFICATE_EFI_PKCS, Hdr.dwLength)
d1e1c8
+		     + sizeof(sig->Hdr.dwLength);
d1e1c8
+		if (sz > context->SecDir->Size) {
d1e1c8
+			perror(L"Certificate size is too large for secruity database");
d1e1c8
+			return EFI_INVALID_PARAMETER;
d1e1c8
+		}
d1e1c8
+
d1e1c8
+		sz = sig->Hdr.dwLength;
d1e1c8
+		if (sz > context->SecDir->Size - offset) {
d1e1c8
+			perror(L"Certificate size is too large for secruity database");
d1e1c8
+			return EFI_INVALID_PARAMETER;
d1e1c8
 		}
d1e1c8
-#endif /* defined(ENABLE_SHIM_CERT) */
d1e1c8
-
d1e1c8
-#if defined(VENDOR_CERT_FILE)
d1e1c8
-		/*
d1e1c8
-		 * And finally, check against shim's built-in key
d1e1c8
-		 */
d1e1c8
-		if (vendor_authorized_size &&
d1e1c8
-		    AuthenticodeVerify(cert->CertData,
d1e1c8
-				       cert->Hdr.dwLength - sizeof(cert->Hdr),
d1e1c8
-				       vendor_authorized, vendor_authorized_size,
d1e1c8
-				       sha256hash, SHA256_DIGEST_SIZE)) {
d1e1c8
-			update_verification_method(VERIFIED_BY_CERT);
d1e1c8
-			tpm_measure_variable(L"Shim", SHIM_LOCK_GUID,
d1e1c8
-					     vendor_authorized_size, vendor_authorized);
d1e1c8
-			efi_status = EFI_SUCCESS;
d1e1c8
-			drain_openssl_errors();
d1e1c8
-			return efi_status;
d1e1c8
+
d1e1c8
+		if (sz < sizeof(sig->Hdr)) {
d1e1c8
+			perror(L"Certificate size is too small for certificate data");
d1e1c8
+			return EFI_INVALID_PARAMETER;
d1e1c8
+		}
d1e1c8
+
d1e1c8
+		if (sig->Hdr.wCertificateType == WIN_CERT_TYPE_PKCS_SIGNED_DATA) {
d1e1c8
+			EFI_STATUS efi_status;
d1e1c8
+
d1e1c8
+			dprint(L"Attempting to verify signature %d:\n", i++);
d1e1c8
+
d1e1c8
+			efi_status = verify_one_signature(sig, sha256hash, sha1hash);
d1e1c8
+
d1e1c8
+			/*
d1e1c8
+			 * If we didn't get EFI_SECURITY_VIOLATION from
d1e1c8
+			 * checking the hashes above, then any dbx entries are
d1e1c8
+			 * for a certificate, not this individual binary.
d1e1c8
+			 *
d1e1c8
+			 * So don't clobber successes with security violation
d1e1c8
+			 * here; that just means it isn't a success.
d1e1c8
+			 */
d1e1c8
+			if (ret_efi_status != EFI_SUCCESS)
d1e1c8
+				ret_efi_status = efi_status;
d1e1c8
 		} else {
d1e1c8
-			LogError(L"AuthenticodeVerify(vendor_authorized) failed\n");
d1e1c8
+			perror(L"Unsupported certificate type %x\n",
d1e1c8
+				sig->Hdr.wCertificateType);
d1e1c8
 		}
d1e1c8
-#endif /* defined(VENDOR_CERT_FILE) */
d1e1c8
-	}
d1e1c8
+		offset = ALIGN_VALUE(offset + sz, 8);
d1e1c8
+	} while (offset < context->SecDir->Size);
d1e1c8
 
d1e1c8
-	LogError(L"Binary is not whitelisted\n");
d1e1c8
-	crypterr(EFI_SECURITY_VIOLATION);
d1e1c8
-	PrintErrors();
d1e1c8
-	efi_status = EFI_SECURITY_VIOLATION;
d1e1c8
-	return efi_status;
d1e1c8
+	if (ret_efi_status != EFI_SUCCESS) {
d1e1c8
+		dprint(L"Binary is not whitelisted\n");
d1e1c8
+		PrintErrors();
d1e1c8
+		ClearErrors();
d1e1c8
+		crypterr(EFI_SECURITY_VIOLATION);
d1e1c8
+		ret_efi_status = EFI_SECURITY_VIOLATION;
d1e1c8
+	}
d1e1c8
+	drain_openssl_errors();
d1e1c8
+	return ret_efi_status;
d1e1c8
 }
d1e1c8
 
d1e1c8
 /*
d1e1c8
-- 
d1e1c8
2.26.2
d1e1c8