Changeset View
Changeset View
Standalone View
Standalone View
stand/common/verify.c
- This file was added.
/*- | |||||
* Copyright (c) 2019 Stormshield. | |||||
* Copyright (c) 2019 Semihalf. | |||||
* All rights reserved. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions | |||||
* are met: | |||||
* 1. Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions and the following disclaimer. | |||||
* 2. Redistributions in binary form must reproduce the above copyright | |||||
* notice, this list of conditions and the following disclaimer in the | |||||
* documentation and/or other materials provided with the distribution. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||||
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, | |||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN | |||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |||||
* POSSIBILITY OF SUCH DAMAGE. | |||||
*/ | |||||
#include <sys/cdefs.h> | |||||
__FBSDID("$FreeBSD$"); | |||||
#include <stand.h> | |||||
#include <secureboot.h> | |||||
#include "bootstrap.h" | |||||
#include "verify.h" | |||||
int | |||||
verify(const char *path) | |||||
{ | |||||
struct signature_info header; | |||||
unsigned char digest[br_sha256_SIZE]; | |||||
unsigned char decrypted_digest[br_sha256_SIZE]; | |||||
unsigned char *signature; | |||||
size_t tbs_size; | |||||
int result = 0; | |||||
ssize_t read_result; | |||||
int fd; | |||||
size_t trusted_count; | |||||
br_x509_certificate cert; | |||||
unsigned char *tbs_cert; | |||||
br_x509_certificate *trusted_certs; | |||||
br_x509_pkey *pkey; | |||||
br_rsa_pkcs1_vrfy vrfy_fp; | |||||
signature = NULL; | |||||
cert.data = NULL; | |||||
if (archsw.secure_boot_enabled == NULL) { | |||||
printf("archsw.secure_boot_enabled == NULL\n"); | |||||
return (EINVAL); | |||||
} | |||||
result = archsw.secure_boot_enabled(); | |||||
/* | |||||
* 0 - SecureBoot disabled, | |||||
* 1 - enabled, | |||||
* any other value - error | |||||
*/ | |||||
if (result == 0) | |||||
return (0); | |||||
if (result != 1) | |||||
return (EINVAL); | |||||
if (archsw.secure_boot_get_CAs == NULL) { | |||||
printf("archsw.secure_boot_getCAs == NULL\n"); | |||||
return (EINVAL); | |||||
} | |||||
if (archsw.secure_boot_get_forbidden_CAs == NULL) { | |||||
printf("archsw.secure_boot_get_forbidden_CAs == NULL\n"); | |||||
return (EINVAL); | |||||
} | |||||
if (archsw.secure_boot_cert_forbidden == NULL) { | |||||
printf("archsw.secure_boot_cert_forbidden == NULL\n"); | |||||
return (EINVAL); | |||||
} | |||||
/* | |||||
* XXX: Opening and closing a file causes a memory leak of 4kB | |||||
*/ | |||||
fd = open(path, O_RDONLY); | |||||
/* | |||||
* The structure with information about the signature | |||||
* is simply appended to the binary file. | |||||
*/ | |||||
lseek(fd, sizeof(header), SEEK_END); | |||||
read_result = checked_read(fd, (char *)&header, sizeof(header)); | |||||
if (read_result != sizeof(header)) { | |||||
printf("Failed to read header\n"); | |||||
result = errno; | |||||
goto out; | |||||
} | |||||
if (header.magic != MAGIC) { | |||||
printf("Bad signature magic\n"); | |||||
result = EINVAL; | |||||
goto out; | |||||
} | |||||
/* | |||||
* Check if the sizes are sane. | |||||
*/ | |||||
if (header.signature_size > 4096 || header.cert_size > 4096) { | |||||
result = EINVAL; | |||||
goto out; | |||||
} | |||||
lseek(fd, header.kernel_size, SEEK_SET); | |||||
signature = malloc(header.signature_size); | |||||
cert.data = malloc(header.cert_size); | |||||
if (signature == NULL || cert.data == NULL) { | |||||
printf("Failed to allocate memory\n"); | |||||
result = ENOMEM; | |||||
goto out; | |||||
} | |||||
cert.data_len = header.cert_size; | |||||
read_result = checked_read(fd, signature, header.signature_size); | |||||
if (read_result != header.signature_size) { | |||||
printf("Failed to read signature\n"); | |||||
result = errno; | |||||
goto out; | |||||
} | |||||
read_result = checked_read(fd, cert.data, header.cert_size); | |||||
if (read_result != header.cert_size) { | |||||
printf("Failed to read X509 certificate\n"); | |||||
result = errno; | |||||
goto out; | |||||
} | |||||
/* | |||||
* Check if the certificate is blacklisted. | |||||
*/ | |||||
tbs_cert = X509_to_tbs(cert.data, &tbs_size); | |||||
result = archsw.secure_boot_cert_forbidden(tbs_cert, tbs_size); | |||||
if (result != 0) { | |||||
printf("Certificate is forbidden\n"); | |||||
goto out; | |||||
} | |||||
/* | |||||
* Check if CA that was used to sign the certificate | |||||
* is on the forbidden list. | |||||
*/ | |||||
trusted_certs = archsw.secure_boot_get_forbidden_CAs(&trusted_count); | |||||
if (trusted_certs != NULL) { | |||||
pkey = verify_cert(&cert, trusted_certs, trusted_count); | |||||
free_certs(trusted_certs, trusted_count); | |||||
if (pkey != NULL) { | |||||
printf("Certificate is forbidden\n"); | |||||
result = EACCES; | |||||
goto out; | |||||
} | |||||
} | |||||
/* | |||||
* Verify if we trust the embedded certificate. | |||||
*/ | |||||
trusted_certs = archsw.secure_boot_get_CAs(&trusted_count); | |||||
if (trusted_certs == NULL) { | |||||
printf("Invalid CA count\n"); | |||||
result = EINVAL; | |||||
goto out; | |||||
} | |||||
pkey = verify_cert(&cert, trusted_certs, trusted_count); | |||||
free_certs(trusted_certs, trusted_count); | |||||
if (pkey == NULL) { | |||||
printf("Failed to verify certificate\n"); | |||||
result = EACCES; | |||||
goto out; | |||||
} | |||||
/* | |||||
* Decode encrypted digest using public key | |||||
* of the embedded certificate. | |||||
*/ | |||||
vrfy_fp = br_rsa_pkcs1_vrfy_get_default(); | |||||
result = vrfy_fp(signature, header.signature_size, | |||||
BR_HASH_OID_SHA256, br_sha256_SIZE, | |||||
&pkey->key.rsa, decrypted_digest); | |||||
free_pkey(pkey); | |||||
free(pkey); | |||||
if (result != 1) { | |||||
printf("Failed to decode signature\n"); | |||||
result = EINVAL; | |||||
goto out; | |||||
} | |||||
/* | |||||
* Calculate digest, excluding the signature | |||||
* and compare it against what we have just decoded. | |||||
*/ | |||||
lseek(fd, 0, SEEK_SET); | |||||
result = gen_digest(fd, header.kernel_size, digest); | |||||
if (result != 0) { | |||||
printf("Failed to hash the binary\n"); | |||||
goto out; | |||||
} | |||||
if (memcmp(digest, decrypted_digest, br_sha256_SIZE) != 0) { | |||||
printf("Decoded hash doesn't match\n"); | |||||
result = EACCES; | |||||
goto out; | |||||
} else | |||||
result = 0; | |||||
out: | |||||
if (signature != NULL) | |||||
free(signature); | |||||
if (cert.data != NULL) | |||||
free(cert.data); | |||||
close(fd); | |||||
return (result); | |||||
} |