aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Bottomley <James.Bottomley@HansenPartnership.com>2016-03-04 17:02:37 -0800
committerJames Bottomley <James.Bottomley@HansenPartnership.com>2016-03-25 11:10:12 -0400
commit0cfe45200c3fc1d014c42c26d5fcfa5a2910a353 (patch)
treeac549ca7b19a4cce43c4195d6cae3227c0a8ae6a
parent40eb1ba73a400a2c42cb460c6afc460c0d26432e (diff)
downloadefitools-0cfe45200c3fc1d014c42c26d5fcfa5a2910a353.tar.gz
pkcs7verify: add allow and deny checkers
Export useful deny checker which checks explicitly the hashes in MokListX and dbx and an allow checker which checks the hashes first and then does a VerifySignature to see if the signature is allowed. Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
-rw-r--r--include/pkcs7verify.h8
-rw-r--r--lib/pkcs7verify.c193
2 files changed, 200 insertions, 1 deletions
diff --git a/include/pkcs7verify.h b/include/pkcs7verify.h
index 5ed85f2..9db7949 100644
--- a/include/pkcs7verify.h
+++ b/include/pkcs7verify.h
@@ -208,5 +208,13 @@ struct _EFI_PKCS7_VERIFY_PROTOCOL {
EFI_STATUS
pkcs7verify_get_protocol(EFI_HANDLE image, EFI_PKCS7_VERIFY_PROTOCOL **p7vp, CHAR16 **error);
+BOOLEAN
+pkcs7verify_deny(VOID *data, UINTN len);
+EFI_SIGNATURE_LIST **
+pkcs7verify_to_cert_list(VOID *data, UINTN len);
+BOOLEAN
+pkcs7verify_allow(VOID *data, UINTN len);
+
+
#endif
diff --git a/lib/pkcs7verify.c b/lib/pkcs7verify.c
index 9aaf540..06701fd 100644
--- a/lib/pkcs7verify.c
+++ b/lib/pkcs7verify.c
@@ -4,9 +4,14 @@
#include <efiauthenticated.h>
#include <guid.h>
#include <pkcs7verify.h>
+#include <variables.h>
#include <execute.h>
+#include <pecoff.h>
-CHAR16 *p7bin = L"\\Pkcs7VerifyDxe.efi";
+#define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
+
+static CHAR16 *p7bin = L"\\Pkcs7VerifyDxe.efi";
+static EFI_PKCS7_VERIFY_PROTOCOL *pkcs7verifyprotocol;
EFI_STATUS
pkcs7verify_get_protocol(EFI_HANDLE image, EFI_PKCS7_VERIFY_PROTOCOL **p7vp, CHAR16 **error)
@@ -54,6 +59,192 @@ pkcs7verify_get_protocol(EFI_HANDLE image, EFI_PKCS7_VERIFY_PROTOCOL **p7vp, CHA
if (status != EFI_SUCCESS)
*error = L"Loaded module but it didn't provide the pkcs7Verify protocol";
+ else
+ pkcs7verifyprotocol = *p7vp;
return status;
}
+
+/*
+ * Checks the variable for the binary hash. Returns 1 if found, 0 if
+ * not and -1 on error.
+ */
+static int
+pkcs7verify_is_hash_present(CHAR16 *var, EFI_GUID owner, VOID *data, UINTN len)
+{
+ EFI_GUID **hashes;
+ UINT8 *hash;
+ int count, i;
+ int present = -1;
+
+ hashes = AllocatePool(allowed_hashes_size * sizeof(EFI_GUID *));
+ if (!hashes)
+ goto out;
+
+ count = hashes_in_variable(var, owner, hashes);
+ if (count < 0)
+ goto out;
+
+ for (i = 0; i < count; i++) {
+ if (CompareGuid(hashes[i], &EFI_CERT_SHA256_GUID) == 0) {
+ hash = AllocatePool(SHA256_DIGEST_SIZE);
+ if (!hash)
+ goto out;
+ if (sha256_get_pecoff_digest_mem(data, len, hash) != EFI_SUCCESS) {
+ FreePool(hash);
+ goto out;
+ }
+ if (find_in_variable_esl(var, owner, hash, SHA256_DIGEST_SIZE) == EFI_SUCCESS) {
+ present = 1;
+ FreePool(hash);
+ goto out;
+ }
+ FreePool(hash);
+ } else {
+ Print(L"FIXME: found an unrecognised hash algorithm %g\n",
+ hashes[i]);
+ goto out;
+ }
+ }
+ present = 0;
+ out:
+ if (hashes)
+ FreePool(hashes);
+ return present;
+}
+
+
+BOOLEAN
+pkcs7verify_deny(VOID *data, UINTN len)
+{
+ int deny;
+
+ deny = pkcs7verify_is_hash_present(L"dbx", SIG_DB, data, len);
+ if (deny == 1 || deny < 0) {
+ deny = 1;
+ goto out;
+ }
+ deny = pkcs7verify_is_hash_present(L"MokListX", MOK_OWNER, data, len);
+ if (deny == 1 || deny < 0)
+ deny = 1;
+ out:
+ return deny ? TRUE : FALSE;
+}
+
+/*
+ * The Plcs7Verify protocol doesn't take raw signature lists and lengths,
+ * it takes a null terminated list of pointers to signature lists, so
+ * make the conversion
+ */
+EFI_SIGNATURE_LIST **
+pkcs7verify_to_cert_list(VOID *data, UINTN len)
+{
+ EFI_SIGNATURE_LIST *CertList, **retval;
+
+ int size, count=0;
+
+ if (!data)
+ return data;
+
+ certlist_for_each_certentry(CertList, data, size, len)
+ count++;
+
+ retval = AllocatePool((count + 1) * sizeof(void *));
+ if (!retval)
+ return NULL;
+ count = 0;
+ certlist_for_each_certentry(CertList, data, size, len)
+ retval[count++] = CertList;
+ retval[count] = NULL;
+
+ return retval;
+}
+
+BOOLEAN
+pkcs7verify_allow(VOID *data, UINTN len)
+{
+ PE_COFF_LOADER_IMAGE_CONTEXT context;
+ UINT8 hash[SHA256_DIGEST_SIZE];
+ BOOLEAN allow = FALSE;
+ CHAR16 *check[] = { L"MokList", L"db" };
+ CHAR16 *forbid[] = { L"MokListX", L"dbx" };
+ EFI_GUID owners[] = { MOK_OWNER, SIG_DB };
+ EFI_STATUS status;
+ int i;
+
+ status = pecoff_read_header(&context, data);
+ if (status != EFI_SUCCESS)
+ goto out;
+
+ /* FIXME: this is technically wrong, because the hash
+ * could be non-sha256, but it isn't a security breach
+ * because we'll refuse a binary we should have accepted */
+ status = sha256_get_pecoff_digest_mem(data, len, hash);
+ if (status != EFI_SUCCESS)
+ goto out;
+
+ /* first look up the hashes because the verify protocol can't
+ * do this anyway */
+
+ if (find_in_variable_esl(L"MokList", MOK_OWNER, hash, SHA256_DIGEST_SIZE) == EFI_SUCCESS ||
+ find_in_variable_esl(L"db", SIG_DB, hash, SHA256_DIGEST_SIZE) == EFI_SUCCESS) {
+ allow = TRUE;
+ goto out;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(check); i++) {
+ VOID *db = NULL, *dbx = NULL;
+ EFI_SIGNATURE_LIST **dblist = NULL, **dbxlist = NULL;
+ UINTN db_len = 0, dbx_len = 0;
+ int j;
+
+ status = get_variable(check[i], (UINT8 **)&db, &db_len, owners[i]);
+ if (status != EFI_SUCCESS)
+ goto next;
+ status = get_variable(forbid[i], (UINT8 **)&dbx, &dbx_len, owners[i]);
+ if (status != EFI_SUCCESS && status != EFI_NOT_FOUND)
+ goto next;
+ dblist = pkcs7verify_to_cert_list(db, db_len);
+ if (status != EFI_NOT_FOUND)
+ dbxlist = pkcs7verify_to_cert_list(dbx, dbx_len);
+ if ((db_len != 0 && dblist == NULL) ||
+ (dbx_len != 0 && dbxlist == NULL))
+ goto next;
+ for (j = 0; ; j++) {
+ WIN_CERTIFICATE *cert;
+
+ status = pecoff_get_signature(&context, data,
+ &cert, j);
+ if (status != EFI_SUCCESS)
+ break;
+
+ status = pkcs7verifyprotocol->
+ VerifySignature(pkcs7verifyprotocol,
+ (VOID *)(cert + 1),
+ cert->dwLength - sizeof(*cert),
+ hash, sizeof(hash),
+ dblist, dbxlist, NULL);
+
+ if (status == EFI_SUCCESS) {
+ allow = TRUE;
+ break;
+ }
+ }
+ next:
+ if (dblist)
+ FreePool(dblist);
+ if (dbxlist)
+ FreePool(dbxlist);
+ if (db)
+ FreePool(db);
+ if (dbx)
+ FreePool(dbx);
+ if (allow)
+ break;
+ }
+
+ out:
+ return allow;
+
+
+}