diff options
author | James Bottomley <James.Bottomley@HansenPartnership.com> | 2016-03-04 17:02:37 -0800 |
---|---|---|
committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2016-03-25 11:10:12 -0400 |
commit | 0cfe45200c3fc1d014c42c26d5fcfa5a2910a353 (patch) | |
tree | ac549ca7b19a4cce43c4195d6cae3227c0a8ae6a | |
parent | 40eb1ba73a400a2c42cb460c6afc460c0d26432e (diff) | |
download | efitools-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.h | 8 | ||||
-rw-r--r-- | lib/pkcs7verify.c | 193 |
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; + + +} |