Index: usr.sbin/Makefile =================================================================== --- usr.sbin/Makefile +++ usr.sbin/Makefile @@ -194,6 +194,7 @@ SUBDIR.${MK_QUOTAS}+= edquota SUBDIR.${MK_QUOTAS}+= quotaon SUBDIR.${MK_QUOTAS}+= repquota +SUBDIR.${MK_SECUREBOOT}+= binsign SUBDIR.${MK_SENDMAIL}+= editmap SUBDIR.${MK_SENDMAIL}+= mailstats SUBDIR.${MK_SENDMAIL}+= makemap Index: usr.sbin/binsign/Makefile =================================================================== --- /dev/null +++ usr.sbin/binsign/Makefile @@ -0,0 +1,11 @@ +# $FreeBSD$ + +PROG= binsign +SRCS= binsign.c +MAN= binsign.8 + +LIBADD= secureboot bearssl + +WARNS= 6 + +.include Index: usr.sbin/binsign/binsign.8 =================================================================== --- /dev/null +++ usr.sbin/binsign/binsign.8 @@ -0,0 +1,95 @@ +.\" +.\" Copyright (c) 2019 Stormshield. +.\" Copyright (c) 2019 Semihalf. +.\" +.\" 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. +.\" +.\"$FreeBSD$ +.Dd January 10, 2019 +.Dt BINSIGN 8 +.Os +.Sh NAME +.Nm binsign +.Nd Secure Boot signing utility +.Sh SYNOPSIS +.Nm +.Fl c Ar cert +.Fl k Ar key +.Op Fl o Ar output +.Ar file +.Sh DESCRIPTION +The +.Nm +utility signs any type of file by appending a signature in PKCS#1 v1.5 standard +together with a certificate used for the signing to its end. +.Pp +It is different from +.Xr uefisign 8 +in that the former is used to sign PE binaries that are supposed to be verified +and run directly by +.Xr UEFI 8 +.Pp +.Xr loader 8 can be configured by to verify the kernel and modules integrity +signed with this tool. To do that one needs to compile it with +.Nm -MK_SECUREBOOT="yes" +.Sh EXIT STATUS +The +.Nm +utility exits 0 on success, and 1 if an error occurs. +.Sh EXAMPLES +.Bl -tag -width 0n +.Pp +.Li binsign -c cert.der -k cert.key file +.Pp +The +.Pa cert.key +and +.Pa cert.der +have to contain a DER encoded +RSA private key and X509 certificate respectively, those can be generated with +.Xr openssl 1 +.Sh SEE ALSO +.Xr openssl 1 +.Xr loader 8 +.Xr uefisign 8 +.Xr UEFI 8 +.Sh STANDARDS +.Bl -item +.It +.Rs +.%A B. Kaliski +.%T "PKCS #1: RSA Encryption Version 1.5" +.%R RFC 2313 +.%D March 1998 +.Re +.El +.Sh HISTORY +The +.Nm +command appeared in +.Fx 13.0 . +.Sh AUTHORS +The +.Nm +utility was developed by +.An Kornel Duleba Aq Mt mindal@semihalf.com +under sponsorship from Stormshield. Index: usr.sbin/binsign/binsign.c =================================================================== --- /dev/null +++ usr.sbin/binsign/binsign.c @@ -0,0 +1,277 @@ +/*- + * Copyright (c) 2019 Stormshield. + * Copyright (c) 2019 Semihalf. + * + * 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include + +#include + +#include + +static void usage() { + printf("binsign -c cert -k key [-o output] file\n"); + exit(1); +} + +static void +gen_sha256(char* buffer, size_t length, unsigned char* output) +{ + br_hash_compat_context hash_ctx; + const br_hash_class *md; + + md = &br_sha256_vtable; + + md->init(&hash_ctx.vtable); + md->update(&hash_ctx.vtable, buffer, length); + md->out(&hash_ctx.vtable, output); +} + +static int +sign(char* cert, size_t cert_size, char* key, size_t key_size, + FILE *fp, unsigned char **output, size_t *output_size) +{ + struct stat sb; + struct signature_info sig_info; + unsigned char *buf = NULL; + int result; + unsigned char digest[br_sha256_SIZE]; + br_skey_decoder_context ctx; + const br_rsa_private_key *priv; + unsigned int signature_length; + + memset(&sb, 0, sizeof(sb)); + memset(&sig_info, 0, sizeof(sig_info)); + + result = fstat(fileno(fp), &sb); + if (result != 0) { + result = errno; + printf("Fstat failed\n"); + goto fail; + } + + br_skey_decoder_init(&ctx); + br_skey_decoder_push(&ctx, key, key_size); + priv = br_skey_decoder_get_rsa(&ctx); + if (priv == NULL) { + printf("Failed to process private key\n"); + result = EINVAL; + goto fail; + } + + signature_length = (priv->n_bitlen + 7) >> 3; + + buf = calloc(1, sb.st_size + signature_length + cert_size + sizeof(sig_info)); + if (buf == NULL) { + printf("Failed to allocate memory\n"); + result = ENOMEM; + goto fail; + } + + if (fread(buf, sb.st_size, 1, fp) != 1) { + printf("Failed to read input file\n"); + result = ferror(fp); + goto fail; + } + + gen_sha256(buf, sb.st_size, digest); + + result = br_rsa_pkcs1_sign_get_default()( + BR_HASH_OID_SHA256, digest, br_sha256_SIZE, + priv, buf + sb.st_size); + if (!result) { + printf("Failed to sign the digest\n"); + result = EINVAL; + goto fail; + } + + memcpy((buf + sb.st_size + signature_length), cert, cert_size); + + sig_info.magic = MAGIC; + sig_info.kernel_size = sb.st_size; + sig_info.signature_size = signature_length; + sig_info.cert_size = cert_size; + memcpy((buf + sb.st_size + signature_length + cert_size), &sig_info, sizeof(sig_info)); + + *output_size = sb.st_size + signature_length + cert_size + sizeof(sig_info); + *output = buf; + + return (0); + +fail: + if (buf != NULL) + free(buf); + + return result; +} + +int +main(int argc, char **argv) +{ + int ch; + const char *cert_path, *key_path, *input_path, *output_path; + FILE *fp; + int result; + struct stat sb; + char *cert; + size_t cert_size, priv_size; + char *priv; + unsigned char *output; + size_t length; + + cert_path = NULL; + key_path = NULL; + input_path = NULL; + output_path = NULL; + cert = NULL; + priv = NULL; + output = NULL; + fp = NULL; + + while ((ch = getopt(argc, argv, "c:k:o:")) != -1) { + switch (ch) { + case 'c': + cert_path = strdup(optarg); + break; + case 'k': + key_path = strdup(optarg); + break; + case 'o': + output_path = strdup(optarg); + break; + default: + usage(); + } + } + + if (cert_path == NULL) { + printf("-c option is mandatory\n"); + usage(); + } + + if (key_path == NULL) { + printf("-k option is mandatory\n"); + usage(); + } + + input_path = argv[optind]; + if (output_path == NULL) + output_path = input_path; + + fp = fopen(cert_path, "r"); + if (fp == NULL) { + result = errno; + printf("Failed to open certificate file\n"); + goto out; + } + result = fstat(fileno(fp), &sb); + if (result != 0) { + result = errno; + printf("Fstat failed\n"); + goto out; + } + cert_size = sb.st_size; + cert = malloc(cert_size); + if (cert == NULL) { + result = errno; + printf("Failed to allocate memory\n"); + goto out; + } + if (fread(cert, cert_size, 1, fp) != 1) { + result = ferror(fp); + printf("Failed to read the certificate\n"); + goto out; + } + fclose(fp); + + fp = fopen(key_path, "r"); + if (fp == NULL) { + result = errno; + printf("Failed to open private key file\n"); + goto out; + } + result = fstat(fileno(fp), &sb); + if (result != 0) { + result = errno; + printf("Fstat failed with\n"); + goto out; + + } + priv_size = sb.st_size; + priv = malloc(cert_size); + if (priv == NULL) { + result = ENOMEM; + printf("Failed to allocate memory\n"); + goto out; + } + if (fread(priv, priv_size, 1, fp) != 1) { + result = ferror(fp); + printf("Failed to read private key\n"); + goto out; + } + fclose(fp); + + fp = fopen(input_path, "rb"); + if (fp == NULL) { + result = errno; + printf("Failed to open input file\n"); + goto out; + } + + sign(cert, cert_size, priv, priv_size, fp, &output, &length); + if (result != 0) + goto out; + + fclose(fp); + + fp = fopen(output_path, "wb"); + if (fp == NULL) { + result = errno; + printf("Failed to open output file\n"); + goto out; + } + + if (fwrite(output, length, 1, fp) != 1) { + result = ferror(fp); + printf("Failed to write the output file\n"); + goto out; + } +out: + if (fp != NULL) + fclose(fp); + if (cert != NULL) + free(cert); + if (priv != NULL) + free(priv); + if (output != NULL) + free(output); + + exit(result); +}