Index: sys/boot/Makefile =================================================================== --- sys/boot/Makefile +++ sys/boot/Makefile @@ -9,6 +9,7 @@ .endif SUBDIR+= common +SUBDIR+= boot_crypto .include Index: sys/boot/boot_crypto/Makefile =================================================================== --- /dev/null +++ sys/boot/boot_crypto/Makefile @@ -0,0 +1,34 @@ +# $FreeBSD$ +# libboot_crypto + +MAN= + +.include +MK_SSP= no + +LIB= boot_crypto +INTERNALLIB= +MK_PROFILE= no +NO_PIC= + +WARNS?= 0 + +# sha256 and sha512 from sys/crypto +.PATH: ${.CURDIR}/../../crypto/sha2 +CFLAGS+= -DWEAK_REFS +SRCS+= sha256c.c sha512c.c + +# md5 from libmd +.PATH: ${.CURDIR}/../../../lib/libmd +SRCS+= md5c.c + +# AES implementation from sys/crypto +.PATH: ${.CURDIR}/../../crypto/rijndael +CFLAGS+= -I${.CURDIR}/../../ +# Remove asserts +CFLAGS+= -DNDEBUG +SRCS+= rijndael-alg-fst.c rijndael-api-fst.c rijndael-api.c +SRCS+= boot_crypto.c boot_crypto_aes.c + +.include +.include Index: sys/boot/boot_crypto/boot_crypto.h =================================================================== --- /dev/null +++ sys/boot/boot_crypto/boot_crypto.h @@ -0,0 +1,187 @@ +/*- + * Copyright (c) 2016 Eric McCorkle + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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$ + */ + +#ifndef _BOOT_CRYPTO_H_ +#define _BOOT_CRYPTO_H_ + +#include "boot_crypto_types.h" +#include "boot_crypto_aes.h" + +/* We want all the codes from cryptodev, but not the defs. Maybe + * these should be moved out to a separate file to allow them to be + * included separately? + */ + +/* Hash values */ +#define NULL_HASH_LEN 16 +#define MD5_HASH_LEN 16 +#define SHA1_HASH_LEN 20 +#define RIPEMD160_HASH_LEN 20 +#define SHA2_256_HASH_LEN 32 +#define SHA2_384_HASH_LEN 48 +#define SHA2_512_HASH_LEN 64 +#define MD5_KPDK_HASH_LEN 16 +#define SHA1_KPDK_HASH_LEN 20 +#define AES_GMAC_HASH_LEN 16 +/* Maximum hash algorithm result length */ +#define HASH_MAX_LEN SHA2_512_HASH_LEN /* Keep this updated */ + +/* HMAC values */ +#define NULL_HMAC_BLOCK_LEN 64 +#define MD5_HMAC_BLOCK_LEN 64 +#define SHA1_HMAC_BLOCK_LEN 64 +#define RIPEMD160_HMAC_BLOCK_LEN 64 +#define SHA2_256_HMAC_BLOCK_LEN 64 +#define SHA2_384_HMAC_BLOCK_LEN 128 +#define SHA2_512_HMAC_BLOCK_LEN 128 +/* Maximum HMAC block length */ +#define HMAC_MAX_BLOCK_LEN SHA2_512_HMAC_BLOCK_LEN /* Keep this updated */ +#define HMAC_IPAD_VAL 0x36 +#define HMAC_OPAD_VAL 0x5C +/* HMAC Key Length */ +#define NULL_HMAC_KEY_LEN 0 +#define MD5_HMAC_KEY_LEN 16 +#define SHA1_HMAC_KEY_LEN 20 +#define RIPEMD160_HMAC_KEY_LEN 20 +#define SHA2_256_HMAC_KEY_LEN 32 +#define SHA2_384_HMAC_KEY_LEN 48 +#define SHA2_512_HMAC_KEY_LEN 64 +#define AES_128_GMAC_KEY_LEN 16 +#define AES_192_GMAC_KEY_LEN 24 +#define AES_256_GMAC_KEY_LEN 32 + +/* Encryption algorithm block sizes */ +#define NULL_BLOCK_LEN 4 /* IPsec to maintain alignment */ +#define DES_BLOCK_LEN 8 +#define DES3_BLOCK_LEN 8 +#define BLOWFISH_BLOCK_LEN 8 +#define SKIPJACK_BLOCK_LEN 8 +#define CAST128_BLOCK_LEN 8 +#define RIJNDAEL128_BLOCK_LEN 16 +#define AES_BLOCK_LEN 16 +#define AES_ICM_BLOCK_LEN 1 +#define ARC4_BLOCK_LEN 1 +#define CAMELLIA_BLOCK_LEN 16 +#define EALG_MAX_BLOCK_LEN AES_BLOCK_LEN /* Keep this updated */ + +/* IV Lengths */ + +#define ARC4_IV_LEN 1 +#define AES_GCM_IV_LEN 12 +#define AES_XTS_IV_LEN 8 +#define AES_XTS_ALPHA 0x87 /* GF(2^128) generator polynomial */ + +/* Min and Max Encryption Key Sizes */ +#define NULL_MIN_KEY 0 +#define NULL_MAX_KEY 256 /* 2048 bits, max key */ +#define DES_MIN_KEY 8 +#define DES_MAX_KEY DES_MIN_KEY +#define TRIPLE_DES_MIN_KEY 24 +#define TRIPLE_DES_MAX_KEY TRIPLE_DES_MIN_KEY +#define BLOWFISH_MIN_KEY 5 +#define BLOWFISH_MAX_KEY 56 /* 448 bits, max key */ +#define CAST_MIN_KEY 5 +#define CAST_MAX_KEY 16 +#define SKIPJACK_MIN_KEY 10 +#define SKIPJACK_MAX_KEY SKIPJACK_MIN_KEY +#define RIJNDAEL_MIN_KEY 16 +#define RIJNDAEL_MAX_KEY 32 +#define AES_MIN_KEY RIJNDAEL_MIN_KEY +#define AES_MAX_KEY RIJNDAEL_MAX_KEY +#define AES_XTS_MIN_KEY (2 * AES_MIN_KEY) +#define AES_XTS_MAX_KEY (2 * AES_MAX_KEY) +#define ARC4_MIN_KEY 1 +#define ARC4_MAX_KEY 32 +#define CAMELLIA_MIN_KEY 8 +#define CAMELLIA_MAX_KEY 32 + +/* Maximum hash algorithm result length */ +#define AALG_MAX_RESULT_LEN 64 /* Keep this updated */ + +#define CRYPTO_ALGORITHM_MIN 1 +#define CRYPTO_DES_CBC 1 +#define CRYPTO_3DES_CBC 2 +#define CRYPTO_BLF_CBC 3 +#define CRYPTO_CAST_CBC 4 +#define CRYPTO_SKIPJACK_CBC 5 +#define CRYPTO_MD5_HMAC 6 +#define CRYPTO_SHA1_HMAC 7 +#define CRYPTO_RIPEMD160_HMAC 8 +#define CRYPTO_MD5_KPDK 9 +#define CRYPTO_SHA1_KPDK 10 +#define CRYPTO_RIJNDAEL128_CBC 11 /* 128 bit blocksize */ +#define CRYPTO_AES_CBC 11 /* 128 bit blocksize -- the same as above */ +#define CRYPTO_ARC4 12 +#define CRYPTO_MD5 13 +#define CRYPTO_SHA1 14 +#define CRYPTO_NULL_HMAC 15 +#define CRYPTO_NULL_CBC 16 +#define CRYPTO_DEFLATE_COMP 17 /* Deflate compression algorithm */ +#define CRYPTO_SHA2_256_HMAC 18 +#define CRYPTO_SHA2_384_HMAC 19 +#define CRYPTO_SHA2_512_HMAC 20 +#define CRYPTO_CAMELLIA_CBC 21 +#define CRYPTO_AES_XTS 22 +#define CRYPTO_AES_ICM 23 /* commonly known as CTR mode */ +#define CRYPTO_AES_NIST_GMAC 24 /* cipher side */ +#define CRYPTO_AES_NIST_GCM_16 25 /* 16 byte ICV */ +#define CRYPTO_AES_128_NIST_GMAC 26 /* auth side */ +#define CRYPTO_AES_192_NIST_GMAC 27 /* auth side */ +#define CRYPTO_AES_256_NIST_GMAC 28 /* auth side */ +#define CRYPTO_ALGORITHM_MAX 28 /* Keep updated - see below */ + +#define CRYPTO_ALGO_VALID(x) ((x) >= CRYPTO_ALGORITHM_MIN && \ + (x) <= CRYPTO_ALGORITHM_MAX) + +/* Algorithm flags */ +#define CRYPTO_ALG_FLAG_SUPPORTED 0x01 /* Algorithm is supported */ +#define CRYPTO_ALG_FLAG_RNG_ENABLE 0x02 /* Has HW RNG for DH/DSA */ +#define CRYPTO_ALG_FLAG_DSA_SHA 0x04 /* Can do SHA on msg */ + +struct symmetric_alg_t { + int (*ctxinit)(symmetric_alg_ctx_t *ctx, int enc, const u_char *key, + size_t keylen, u_char *iv); + int (*encrypt)(symmetric_alg_ctx_t *ctx, u_char *data, size_t len); + int (*decrypt)(symmetric_alg_ctx_t *ctx, u_char *data, size_t len); +}; + +union symmetric_alg_ctx_t { + struct aes_xts_ctx aes_xts; + struct aes_cbc_ctx aes_cbc; +}; + +/* Initalize a key and decrypt data */ +extern int decrypt_symmetric(const symmetric_alg_t *alg, u_char *data, + size_t datalen, const u_char *key, size_t keylen, + u_char *iv); +extern int encrypt_symmetric(const symmetric_alg_t *alg, u_char *data, + size_t datalen, const u_char *key, size_t keylen, + u_char *iv); +extern const symmetric_alg_t* get_symmetric_alg(int alg); + +#endif Index: sys/boot/boot_crypto/boot_crypto.c =================================================================== --- /dev/null +++ sys/boot/boot_crypto/boot_crypto.c @@ -0,0 +1,71 @@ +/*- + * Copyright (c) 2016 Eric McCorkle + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 "boot_crypto.h" +#include "boot_crypto_aes.h" + +int decrypt_symmetric(const symmetric_alg_t *alg, u_char *data, size_t datalen, + const u_char *key, size_t keylen, u_char *iv) +{ + symmetric_alg_ctx_t ctx; + int res; + + res = alg->ctxinit(&ctx, 0, key, keylen, iv); + + if(0 != res) { + return (res); + } else { + return alg->decrypt(&ctx, data, datalen); + } +} + +int encrypt_symmetric(const symmetric_alg_t *alg, u_char *data, size_t datalen, + const u_char *key, size_t keylen, u_char *iv) +{ + symmetric_alg_ctx_t ctx; + int res; + + res = alg->ctxinit(&ctx, 1, key, keylen, iv); + + if(0 != res) { + return (res); + } else { + return alg->encrypt(&ctx, data, datalen); + } +} + +const symmetric_alg_t* get_symmetric_alg(int alg) { + switch(alg) { + case CRYPTO_AES_XTS: return &alg_aes_xts; + case CRYPTO_AES_CBC: return &alg_aes_cbc; + default: return NULL; + } +} Index: sys/boot/boot_crypto/boot_crypto_aes.h =================================================================== --- /dev/null +++ sys/boot/boot_crypto/boot_crypto_aes.h @@ -0,0 +1,54 @@ +/*- + * Copyright (c) 2016 Eric McCorkle + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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$ + */ + +#ifndef _BOOT_CRYPTO_AES_H_ +#define _BOOT_CRYPTO_AES_H_ + +#include "boot_crypto_types.h" + +#include + +#define AES_XTS_BLOCKSIZE 16 +#define AES_XTS_IVSIZE 8 +#define AES_XTS_ALPHA 0x87 /* GF(2^128) generator polynomial */ + +struct aes_xts_ctx { + rijndael_ctx key1; + rijndael_ctx key2; + u_int8_t tweak[AES_XTS_BLOCKSIZE]; +}; + +struct aes_cbc_ctx { + keyInstance aeskey; + cipherInstance cipher; +}; + +extern const symmetric_alg_t alg_aes_cbc; +extern const symmetric_alg_t alg_aes_xts; + +#endif Index: sys/boot/boot_crypto/boot_crypto_aes.c =================================================================== --- /dev/null +++ sys/boot/boot_crypto/boot_crypto_aes.c @@ -0,0 +1,207 @@ +/*- + * Copyright (c) 2016 Eric McCorkle + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 "boot_crypto.h" +#include "boot_crypto_aes.h" + +static int +aes_xts_ctxinit(symmetric_alg_ctx_t *ptr, int enc __unused, + const u_char *key, size_t keylen, u_char *iv) +{ + struct aes_xts_ctx *ctx = &ptr->aes_xts; + u_int64_t blocknum; + size_t xts_len = keylen << 1; + u_int i; + + rijndael_set_key(&(ctx->key1), key, xts_len / 2); + rijndael_set_key(&(ctx->key2), key + (xts_len / 16), xts_len / 2); + /* + * Prepare tweak as E_k2(IV). IV is specified as LE representation + * of a 64-bit block number which we allow to be passed in directly. + */ + bcopy(iv, &blocknum, AES_XTS_IVSIZE); + for (i = 0; i < AES_XTS_IVSIZE; i++) { + ctx->tweak[i] = blocknum & 0xff; + blocknum >>= 8; + } + /* Last 64 bits of IV are always zero */ + bzero(ctx->tweak + AES_XTS_IVSIZE, AES_XTS_IVSIZE); + rijndael_encrypt(&ctx->key2, ctx->tweak, ctx->tweak); + + return (0); +} + +static int +aes_xts_decrypt_block(symmetric_alg_ctx_t *ptr, u_char *data) +{ + struct aes_xts_ctx *ctx = &ptr->aes_xts; + u_int8_t block[AES_XTS_BLOCKSIZE]; + u_int i, carry_in, carry_out; + + for (i = 0; i < AES_XTS_BLOCKSIZE; i++) + block[i] = data[i] ^ ctx->tweak[i]; + + rijndael_decrypt(&ctx->key1, block, data); + + for (i = 0; i < AES_XTS_BLOCKSIZE; i++) + data[i] ^= ctx->tweak[i]; + + /* Exponentiate tweak */ + carry_in = 0; + for (i = 0; i < AES_XTS_BLOCKSIZE; i++) { + carry_out = ctx->tweak[i] & 0x80; + ctx->tweak[i] = (ctx->tweak[i] << 1) | (carry_in ? 1 : 0); + carry_in = carry_out; + } + if (carry_in) + ctx->tweak[0] ^= AES_XTS_ALPHA; + bzero(block, sizeof(block)); + + return (0); +} + +static int +aes_xts_decrypt(symmetric_alg_ctx_t *ctx, u_char *data, size_t len) +{ + u_int i; + + for (i = 0; i < len; i += AES_XTS_BLOCKSIZE) { + aes_xts_decrypt_block(ctx, data + i); + } + + return (0); +} + +static int +aes_xts_encrypt_block(symmetric_alg_ctx_t *ptr, u_char *data) +{ + struct aes_xts_ctx *ctx = &ptr->aes_xts; + u_int8_t block[AES_XTS_BLOCKSIZE]; + u_int i, carry_in, carry_out; + + for (i = 0; i < AES_XTS_BLOCKSIZE; i++) + block[i] = data[i] ^ ctx->tweak[i]; + + rijndael_encrypt(&ctx->key1, block, data); + + for (i = 0; i < AES_XTS_BLOCKSIZE; i++) + data[i] ^= ctx->tweak[i]; + + /* Exponentiate tweak */ + carry_in = 0; + for (i = 0; i < AES_XTS_BLOCKSIZE; i++) { + carry_out = ctx->tweak[i] & 0x80; + ctx->tweak[i] = (ctx->tweak[i] << 1) | (carry_in ? 1 : 0); + carry_in = carry_out; + } + if (carry_in) + ctx->tweak[0] ^= AES_XTS_ALPHA; + bzero(block, sizeof(block)); + + return (0); +} + +static int +aes_xts_encrypt(symmetric_alg_ctx_t *ctx, u_char *data, size_t len) +{ + u_int i; + + for (i = 0; i < len; i += AES_XTS_BLOCKSIZE) { + aes_xts_encrypt_block(ctx, data + i); + } + + return (0); +} + +static int +aes_cbc_ctxinit(symmetric_alg_ctx_t *ptr, int enc, const u_char *key, + size_t keylen, u_char *iv) +{ + struct aes_cbc_ctx *ctx = &ptr->aes_cbc; + int err; + + err = rijndael_makeKey(&ctx->aeskey, !enc, keylen, + (const char *)key); + if (err < 0) { + return (err); + } + + err = rijndael_cipherInit(&ctx->cipher, MODE_CBC, iv); + if (err < 0) { + return (err); + } + + return (0); +} + +static int +aes_cbc_decrypt(symmetric_alg_ctx_t *ptr, u_char *data, size_t len) +{ + struct aes_cbc_ctx *ctx = &ptr->aes_cbc; + int blks; + + blks = rijndael_blockDecrypt(&ctx->cipher, &ctx->aeskey, data, + len * 8, data); + + if (len != (blks / 8)) { + return (1); + } else { + return (0); + } +} + +static int +aes_cbc_encrypt(symmetric_alg_ctx_t *ptr, u_char *data, size_t len) +{ + struct aes_cbc_ctx *ctx = &ptr->aes_cbc; + int blks; + + blks = rijndael_blockEncrypt(&ctx->cipher, &ctx->aeskey, data, + len * 8, data); + + if (len != (blks / 8)) { + return (1); + } else { + return (0); + } +} + +const symmetric_alg_t alg_aes_xts = { + .ctxinit = aes_xts_ctxinit, + .decrypt = aes_xts_decrypt, + .encrypt = aes_xts_encrypt +}; + +const symmetric_alg_t alg_aes_cbc = { + .ctxinit = aes_cbc_ctxinit, + .decrypt = aes_cbc_decrypt, + .encrypt = aes_cbc_encrypt +}; Index: sys/boot/boot_crypto/boot_crypto_types.h =================================================================== --- /dev/null +++ sys/boot/boot_crypto/boot_crypto_types.h @@ -0,0 +1,37 @@ +/*- + * Copyright (c) 2016 Eric McCorkle + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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$ + */ + +#ifndef _BOOT_CRYPTO_TYPES_H_ +#define _BOOT_CRYPTO_TYPES_H_ + +#include + +typedef struct symmetric_alg_t symmetric_alg_t; +typedef union symmetric_alg_ctx_t symmetric_alg_ctx_t; + +#endif Index: sys/boot/efi/boot1/Makefile =================================================================== --- sys/boot/efi/boot1/Makefile +++ sys/boot/efi/boot1/Makefile @@ -61,6 +61,7 @@ CFLAGS+= -I${.CURDIR}/../include/${MACHINE} CFLAGS+= -I${.CURDIR}/../../../contrib/dev/acpica/include CFLAGS+= -I${.CURDIR}/../../.. +CFLAGS+= -I${.CURDIR}/../../boot_crypto CFLAGS+= -DEFI_UFS_BOOT .ifdef(EFI_DEBUG) CFLAGS+= -DEFI_DEBUG @@ -104,13 +105,15 @@ LDFLAGS+= -Wl,-znocombreloc .endif +LIBBOOT_CRYPTO= ${.OBJDIR}/../../boot_crypto/libboot_crypto.a + # # Add libstand for the runtime functions used by the compiler - for example # __aeabi_* (arm) or __divdi3 (i386). # as well as required string and memory functions for all platforms. # -DPADD+= ${LIBEFI} ${LIBSTAND} -LDADD+= ${LIBEFI} ${LIBSTAND} +DPADD+= ${LIBEFI} ${LIBSTAND} ${LIBBOOT_CRYPTO} +LDADD+= ${LIBEFI} ${LIBSTAND} ${LIBBOOT_CRYPTO} DPADD+= ${LDSCRIPT} Index: sys/boot/efi/boot1/boot1.c =================================================================== --- sys/boot/efi/boot1/boot1.c +++ sys/boot/efi/boot1/boot1.c @@ -53,7 +53,9 @@ struct arch_switch archsw; /* MI/MD interface boundary */ -static const efi_driver_t *efi_drivers[] = { +const efi_driver_t *efi_drivers[] = { + &key_inject_driver, + &geli_driver, NULL }; @@ -484,7 +486,6 @@ currdev.pool_guid = pool_guid; currdev.root_guid = 0; devname = efi_fmtdev(&currdev); - env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev, env_nounset); @@ -508,7 +509,6 @@ currdev.d_slice = -1; currdev.d_partition = -1; devname = efi_fmtdev(&currdev); - env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev, env_nounset); @@ -525,7 +525,6 @@ currdev.d_slice = pp->pd_unit; currdev.d_partition = 255; devname = efi_fmtdev(&currdev); - env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev, env_nounset); @@ -623,14 +622,12 @@ currdev.pool_guid = zi->zi_pool_guid; currdev.root_guid = 0; devname = efi_fmtdev(&currdev); - env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev, env_nounset); if (probe_fs(filepath) == 0 && do_load(filepath, bufp, bufsize) == EFI_SUCCESS) { *handlep = zi->zi_handle; - printf("Succeeded\n"); return (0); } @@ -649,7 +646,6 @@ currdev.d_slice = -1; currdev.d_partition = -1; devname = efi_fmtdev(&currdev); - env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev, env_nounset); @@ -663,7 +659,7 @@ /* Assuming GPT partitioning. */ STAILQ_FOREACH(pp, &dp->pd_part, pd_link) { currdev.d_slice = pp->pd_unit; - currdev.d_partition = 255; + currdev.d_partition = -1; devname = efi_fmtdev(&currdev); env_setenv("currdev", EV_VOLATILE, devname, @@ -899,14 +895,14 @@ printf(" Initializing modules:"); for (i = 0; efi_drivers[i] != NULL; i++) { - printf(" %s", efi_drivers[i]->name); if (efi_drivers[i]->init != NULL) efi_drivers[i]->init(); } + printf("Probing devices:"); for (i = 0; devsw[i] != NULL; i++) { + printf(" %s", devsw[i]->dv_name); if (devsw[i]->dv_init != NULL) { - printf(" %s", devsw[i]->dv_name); (devsw[i]->dv_init)(); } } Index: sys/boot/efi/include/efi_drivers.h =================================================================== --- sys/boot/efi/include/efi_drivers.h +++ sys/boot/efi/include/efi_drivers.h @@ -36,10 +36,12 @@ void (*init)(void); } efi_driver_t; -extern struct devsw efipart_dev; +extern const efi_driver_t *efi_drivers[]; + extern int efipart_getdesc(struct devdesc *dev, char **out); /* EFI drivers. */ -extern const efi_driver_t fs_driver; +extern const efi_driver_t key_inject_driver; +extern const efi_driver_t geli_driver; #endif Index: sys/boot/efi/include/efilib.h =================================================================== --- sys/boot/efi/include/efilib.h +++ sys/boot/efi/include/efilib.h @@ -30,6 +30,9 @@ #ifndef _LOADER_EFILIB_H #define _LOADER_EFILIB_H +#define FREEBSD_GELI_GUID { 0x516e7cbc, 0x6ecf, 0x11d6, \ + {0x8f, 0xf8, 0x00, 0x02, 0x2d, 0x09, 0x71, 0x2b } } + #include #include Index: sys/boot/efi/include/efisec.h =================================================================== --- /dev/null +++ sys/boot/efi/include/efisec.h @@ -0,0 +1,294 @@ +/*- + * Copyright (c) 2016 Eric McCorkle + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 + +#ifndef _EFISEC_H_ +#define _EFISEC_H_ + +#define EFI_KMS_PROTOCOL \ + { 0xec3a978d, 0x7c4e, 0x48fa, { 0x9a, 0xbe, 0x6a, 0xd9, 0x1c, 0xc8, 0xf8, 0x11 } } + +#define EFI_KMS_DATA_TYPE_NONE 0 +#define EFI_KMS_DATA_TYPE_BINARY 1 +#define EFI_KMS_DATA_TYPE_ASCII 2 +#define EFI_KMS_DATA_TYPE_UNICODE 4 +#define EFI_KMS_DATA_TYPE_UTF8 8 + +typedef struct { + UINT16 ClientIdSize; + VOID *ClientId; + UINT8 ClientNameType; + UINT8 ClientNameCount; + VOID *ClientName; +} EFI_KMS_CLIENT_INFO; + +/* Note: GUIDs for insecure crypto have been omitted */ +#define EFI_KMS_FORMAT_GENERIC_128_GUID \ + { 0xec8a3d69, 0x6ddf, 0x4108, { 0x94, 0x76, 0x73, 0x37, 0xfc, 0x52, 0x21, 0x36 } } + +#define EFI_KMS_FORMAT_GENERIC_160_GUID \ + { 0xa3b3e6f8, 0xefca, 0x4bc1, { 0x88, 0xfb, 0xcb, 0x87, 0x33, 0x9b, 0x25, 0x79 } } + +#define EFI_KMS_FORMAT_GENERIC_256_GUID \ + { 0x70f64793, 0xc323, 0x4261, { 0xac, 0x2c, 0xd8, 0x76, 0xf2, 0x7c, 0x53, 0x45 } } + +#define EFI_KMS_FORMAT_GENERIC_512_GUID \ + { 0x978fe043, 0xd7af, 0x422e, { 0x8a, 0x92, 0x2b, 0x48, 0xe4, 0x63, 0xbd, 0xe6 } } + +#define EFI_KMS_FORMAT_GENERIC_1024_GUID \ + { 0x43be0b44, 0x874b, 0x4ead, { 0xb0, 0x9c, 0x24, 0x1a, 0x4f, 0xbd, 0x7e, 0xb3 } } + +#define EFI_KMS_FORMAT_GENERIC_2048_GUID \ + { 0x40093f23, 0x630c, 0x4626, { 0x9c, 0x48, 0x40, 0x37, 0x3b, 0x19, 0xcb, 0xbe } } + +#define EFI_KMS_FORMAT_GENERIC_3072_GUID \ + { 0xb9237513, 0x6c44, 0x4411, { 0xa9, 0x90, 0x21, 0xe5, 0x56, 0xe0, 0x5a, 0xde } } + +#define EFI_KMS_FORMAT_SHA256_GUID \ + { 0x6bb4f5cd, 0x8022, 0x448d, { 0xbc, 0x6d, 0x77, 0x1b, 0xae, 0x93, 0x5f, 0xc6 } } + +#define EFI_KMS_FORMAT_SHA512_GUID \ + { 0x2f240e12, 0xe1d4, 0x475c, { 0x83, 0xb0, 0xef, 0xff, 0x22, 0xd7, 0x7b, 0xe7 } } + +#define EFI_KMS_FORMAT_AESXTS_128_GUID \ + { 0x4776e33f, 0xdb47, 0x479a, { 0xa2, 0x5f, 0xa1, 0xcd, 0x0a, 0xfa, 0xb2, 0x8b } } + +#define EFI_KMS_FORMAT_AESXTS_256_GUID \ + { 0xdc7e8613, 0xc4bb, 0x4db0, { 0x84, 0x62, 0x13, 0x51, 0x13, 0x57, 0xab, 0xe2 } } + +#define EFI_KMS_FORMAT_AESCBC_128_GUID \ + { 0xa0e8ee89, 0x0e92, 0x44d4, { 0x86, 0x1b, 0x0e, 0xaa, 0x4a, 0xca, 0x44, 0xa2 } } + +#define EFI_KMS_FORMAT_AESCBC_256_GUID \ + { 0xd7e69789, 0x1f68, 0x45e8, { 0x96, 0xef, 0x3b, 0xe8, 0xbb, 0x17, 0xf8, 0xf9 } } + +#define EFI_KMS_FORMAT_RSASHA256_2048_GUID \ + { 0xa477af13, 0x877d, 0x4060, { 0xba, 0xa1, 0x25, 0xb1, 0xbe, 0xa0, 0x8a, 0xd3 } } + +#define EFI_KMS_FORMAT_RSASHA256_3072_GUID \ + { 0x4e1356c2, 0x0eed, 0x463f, { 0x81, 0x47, 0x99, 0x33, 0xab, 0xdb, 0xc7, 0xd5 } } + +#define EFI_KMS_KEY_IDENTIFIER_MAX_SIZE 255 +#define EFI_KMS_KEY_ATTRIBUTE_ID_MAX_SIZE 255 + +typedef struct { + UINT8 KeyIdentifierSize; + VOID *KeyIdentifier; + EFI_GUID KeyFormat; + VOID *KeyValue; + EFI_STATUS KeyStatus; +} EFI_KMS_KEY_DESCRIPTOR; + +#define EFI_KMS_ATTRIBUTE_TYPE_NONE 0x00 +#define EFI_KMS_ATTRIBUTE_TYPE_INTEGER 0x01 +#define EFI_KMS_ATTRIBUTE_TYPE_LONG_INTEGER 0x02 +#define EFI_KMS_ATTRIBUTE_TYPE_BIG_INTEGER 0x03 +#define EFI_KMS_ATTRIBUTE_TYPE_ENUMERATION 0x04 +#define EFI_KMS_ATTRIBUTE_TYPE_BOOLEAN 0x05 +#define EFI_KMS_ATTRIBUTE_TYPE_BYTE_STRING 0x06 +#define EFI_KMS_ATTRIBUTE_TYPE_TEXT_STRING 0x07 +#define EFI_KMS_ATTRIBUTE_TYPE_DATE_TIME 0x08 +#define EFI_KMS_ATTRIBUTE_TYPE_INTERVAL 0x09 +#define EFI_KMS_ATTRIBUTE_TYPE_STRUCTURE 0x0a +#define EFI_KMS_ATTRIBUTE_TYPE_DYNAMIC 0x0b + +typedef struct { + UINT16 Tag; + UINT16 Type; + UINT32 Length; + UINT8 KeyAttributeData[]; +} EFI_KMS_DYNAMIC_FIELD; + +typedef struct { + UINT32 FieldCount; + EFI_KMS_DYNAMIC_FIELD Field[]; +} EFI_KMS_DYNAMIC_ATTRIBUTE; + +typedef struct { + UINT8 KeyAttributeIdentifierType; + UINT8 KeyAttributeIdentifierCount; + VOID *KeyAttributeIdentifier; + UINT16 KeyAttributeInstance; + UINT16 KeyAttributeType; + UINT16 KeyAttributeValueSize; + VOID *KeyAttributeValue; + EFI_STATUS KeyAttributeStatus; +} EFI_KMS_KEY_ATTRIBUTE; + +INTERFACE_DECL(_EFI_KMS_SERVICE); + +typedef +EFI_STATUS +(EFIAPI *EFI_KMS_GET_SERVICE_STATUS) ( + IN struct _EFI_KMS_SERVICE *This + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_KMS_REGISTER_CLIENT) ( + IN struct _EFI_KMS_SERVICE *This, + IN EFI_KMS_CLIENT_INFO *Client, + IN OUT UINTN *ClientDataState OPTIONAL, + IN OUT VOID **ClientData OPTIONAL + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_KMS_CREATE_KEY) ( + IN struct _EFI_KMS_SERVICE *This, + IN EFI_KMS_CLIENT_INFO *Client, + IN OUT UINT16 *KeyDescriptorCount, + IN OUT EFI_KMS_KEY_DESCRIPTOR *KeyDescriptor, + IN OUT UINTN *ClientDataSize OPTIONAL, + IN OUT VOID **ClientData OPTIONAL + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_KMS_GET_KEY) ( + IN struct _EFI_KMS_SERVICE *This, + IN EFI_KMS_CLIENT_INFO *Client, + IN OUT UINT16 *KeyDescriptorCount, + IN OUT EFI_KMS_KEY_DESCRIPTOR *KeyDescriptor, + IN OUT UINTN *ClientDataSize OPTIONAL, + IN OUT VOID **ClientData OPTIONAL + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_KMS_ADD_KEY) ( + IN struct _EFI_KMS_SERVICE *This, + IN EFI_KMS_CLIENT_INFO *Client, + IN OUT UINT16 *KeyDescriptorCount, + IN OUT EFI_KMS_KEY_DESCRIPTOR *KeyDescriptor, + IN OUT UINTN *ClientDataSize OPTIONAL, + IN OUT VOID **ClientData OPTIONAL + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_KMS_DELETE_KEY) ( + IN struct _EFI_KMS_SERVICE *This, + IN EFI_KMS_CLIENT_INFO *Client, + IN OUT UINT16 *KeyDescriptorCount, + IN OUT EFI_KMS_KEY_DESCRIPTOR *KeyDescriptor, + IN OUT UINTN *ClientDataSize OPTIONAL, + IN OUT VOID **ClientData OPTIONAL + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_KMS_GET_KEY_ATTRIBUTES) ( + IN struct _EFI_KMS_SERVICE *This, + IN EFI_KMS_CLIENT_INFO *Client, + IN UINT8 *KeyIdentifierSize, + IN const VOID *KeyIdentifier, + IN OUT UINT16 *KeyAttributesCount, + IN OUT EFI_KMS_KEY_ATTRIBUTE *KeyAttributes, + IN OUT UINTN *ClientDataSize OPTIONAL, + IN OUT VOID **ClientData OPTIONAL + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_KMS_ADD_KEY_ATTRIBUTES) ( + IN struct _EFI_KMS_SERVICE *This, + IN EFI_KMS_CLIENT_INFO *Client, + IN UINT8 *KeyIdentifierSize, + IN const VOID *KeyIdentifier, + IN OUT UINT16 *KeyAttributesCount, + IN OUT EFI_KMS_KEY_ATTRIBUTE *KeyAttributes, + IN OUT UINTN *ClientDataSize OPTIONAL, + IN OUT VOID **ClientData OPTIONAL + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_KMS_DELETE_KEY_ATTRIBUTES) ( + IN struct _EFI_KMS_SERVICE *This, + IN EFI_KMS_CLIENT_INFO *Client, + IN UINT8 *KeyIdentifierSize, + IN const VOID *KeyIdentifier, + IN OUT UINT16 *KeyAttributesCount, + IN OUT EFI_KMS_KEY_ATTRIBUTE *KeyAttributes, + IN OUT UINTN *ClientDataSize OPTIONAL, + IN OUT VOID **ClientData OPTIONAL + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_KMS_GET_KEY_BY_ATTRIBUTES) ( + IN struct _EFI_KMS_SERVICE *This, + IN EFI_KMS_CLIENT_INFO *Client, + IN UINTN *KeyAttributeCount, + IN OUT EFI_KMS_KEY_ATTRIBUTE *KeyAttributes, + IN OUT UINTN *KeyDescriptorCount, + IN OUT EFI_KMS_KEY_DESCRIPTOR *KeyDescriptor, + IN OUT UINTN *ClientDataSize OPTIONAL, + IN OUT VOID **ClientData OPTIONAL + ); + +#define EFI_KMS_PROTOCOL_VERSION 0x00020040 + +typedef struct _EFI_KMS_SERVICE { + EFI_KMS_GET_SERVICE_STATUS GetServiceStatus; + EFI_KMS_REGISTER_CLIENT RegisterClient; + EFI_KMS_CREATE_KEY CreateKey; + EFI_KMS_GET_KEY GetKey; + EFI_KMS_ADD_KEY AddKey; + EFI_KMS_DELETE_KEY DeleteKey; + EFI_KMS_GET_KEY_ATTRIBUTES GetKeyAttributes; + EFI_KMS_ADD_KEY_ATTRIBUTES AddKeyAttributes; + EFI_KMS_DELETE_KEY_ATTRIBUTES DeleteKeyAttributes; + EFI_KMS_GET_KEY_BY_ATTRIBUTES GetKeyByAttributes; + UINT32 ProtocolVersion; + EFI_GUID ServiceId; + CHAR16 *ServiceName; + UINT32 ServiceVersion; + BOOLEAN ServiceAvailable; + BOOLEAN ClientIdSupported; + BOOLEAN ClientIdRequired; + UINT16 ClientIdMaxSize; + UINT8 ClientNameStringTypes; + BOOLEAN ClientNameRequired; + UINT16 ClientNameMaxCount; + BOOLEAN ClientDataSupported; + UINTN ClientDataMaxSize; + BOOLEAN KeyIdVariableLenSupported; + UINTN KeyIdMaxSize; + UINTN KeyFormatsCount; + EFI_GUID *KeyFormats; + BOOLEAN KeyAttributesSupported; + UINT8 KeyAttributeIdStringTypes; + UINT16 KeyAttributeIdMaxCount; + UINTN KeyAttributesCount; + EFI_KMS_KEY_ATTRIBUTE *KeyAttributes; +} EFI_KMS_SERVICE; + +#endif Index: sys/boot/efi/include/key_inject.h =================================================================== --- /dev/null +++ sys/boot/efi/include/key_inject.h @@ -0,0 +1,50 @@ +/*- + * Copyright (c) 2016 Eric McCorkle + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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$ + */ + +#ifndef _KEY_INJECT_H_ +#define _KEY_INJECT_H_ + +/* Registering a client with the name KERNEL and an ID structure as + * shown below sets the injection point for keys into the kernel. + */ +#define KERNEL_CLIENT_NAME "KERNEL" +#define KERNEL_CLIENT_NAME_LEN 6 + +#define KERNEL_KEY_INJECTOR_GUID \ + { 0x53badd16, 0x1e9c, 0x493b, { 0x9d, 0x22, 0xe0, 0xab, 0x24, 0xb1, 0xc0, 0x11 } } + +extern EFI_KMS_KEY_ATTRIBUTE * const key_attr_service_id_geli; +extern EFI_KMS_KEY_ATTRIBUTE * const key_attr_service_id_passphrase; + +/* Structure used as client ID for the "KERNEL" client */ +typedef struct { + void *keybuf; + size_t nents; +} kernel_client_id_t; + +#endif Index: sys/boot/efi/libefi/Makefile =================================================================== --- sys/boot/efi/libefi/Makefile +++ sys/boot/efi/libefi/Makefile @@ -12,7 +12,8 @@ WARNS?= 2 SRCS= delay.c devpath.c efi_console.c efinet.c efipart.c env.c errno.c \ - handles.c wchar.c libefi.c efizfs.c devicename.c + handles.c wchar.c libefi.c efizfs.c devicename.c key_inject.c \ + geli_driver.c .if ${MACHINE_CPUARCH} == "amd64" || ${MACHINE_CPUARCH} == "i386" SRCS+= time.c @@ -20,6 +21,10 @@ SRCS+= time_event.c .endif +.PATH: ${.CURDIR}/../../../geom/eli +CFLAGS+= -D_STAND +SRCS+= g_eli_hmac.c g_eli_key.c g_eli_key_cache.c pkcs5v2.c + # We implement a slightly non-standard %S in that it always takes a # CHAR16 that's common in UEFI-land instead of a wchar_t. This only # seems to matter on arm64 where wchar_t defaults to an int instead @@ -45,6 +50,9 @@ CFLAGS+= -DEFI_ZFS_BOOT .endif +CFLAGS+= -I${.CURDIR}/../../boot_crypto +CFLAGS+= -I${.CURDIR}/../../.. + # Pick up the bootstrap header for some interface items CFLAGS+= -I${.CURDIR}/../../common @@ -56,4 +64,12 @@ CFLAGS+= -DTERM_EMU .endif +beforedepend ${OBJS}: machine + +CLEANFILES+= machine + +machine: .NOMETA + ln -sf ${.CURDIR}/../../../${MACHINE}/include machine + + .include Index: sys/boot/efi/libefi/efipart.c =================================================================== --- sys/boot/efi/libefi/efipart.c +++ sys/boot/efi/libefi/efipart.c @@ -42,6 +42,7 @@ #include static EFI_GUID blkio_guid = BLOCK_IO_PROTOCOL; +static EFI_GUID FreeBSDGELIGUID = FREEBSD_GELI_GUID; static int efipart_initfd(void); static int efipart_initcd(void); @@ -351,23 +352,80 @@ return (0); } +static size_t +wcslen(const CHAR16 *s) +{ + size_t len; + + for(len = 0; s[len] != '\0'; len++); + + return len; +} + +static void +efifs_dev_print(EFI_DEVICE_PATH *devpath) +{ + CHAR16 *name16; + + name16 = efi_devpath_name(devpath); + char buf[wcslen(name16) + 1]; + memset(buf, 0, sizeof buf); + cpy16to8(name16, buf, wcslen(name16)); + printf("%s\n", buf); +} + static int efipart_hdinfo_add(EFI_HANDLE disk_handle, EFI_HANDLE part_handle) { - EFI_DEVICE_PATH *disk_devpath, *part_devpath; + EFI_DEVICE_PATH *disk_devpath, *part_devpath, *trimpath, *lastnode; + VENDOR_DEVICE_PATH *vendornode; HARDDRIVE_DEVICE_PATH *node; int unit; - pdinfo_t *hd, *pd, *last; + pdinfo_t *hd, *pd, *pp, *last; disk_devpath = efi_lookup_devpath(disk_handle); part_devpath = efi_lookup_devpath(part_handle); + if (disk_devpath == NULL || part_devpath == NULL) { return (ENOENT); } - node = (HARDDRIVE_DEVICE_PATH *)efi_devpath_last_node(part_devpath); - if (node == NULL) + + /* Get the disk partition node */ + lastnode = efi_devpath_last_node(part_devpath); + if (lastnode == NULL) return (ENOENT); /* This should not happen. */ + if (DevicePathType(lastnode) == MEDIA_DEVICE_PATH) { + if (DevicePathSubType(lastnode) == MEDIA_VENDOR_DP) { + vendornode = (VENDOR_DEVICE_PATH *)lastnode; + + /* We only want GELI partitions */ + if (memcmp(&(vendornode->Guid), &FreeBSDGELIGUID, + sizeof(EFI_GUID))) { + return (EINVAL); + } + + /* Trim off the vendor node */ + trimpath = efi_devpath_trim(part_devpath); + + if (trimpath == NULL) + return (ENOENT); + + lastnode = efi_devpath_last_node(trimpath); + + if (lastnode == NULL) + return (ENOENT); + + node = (HARDDRIVE_DEVICE_PATH *)lastnode; + } else if (DevicePathSubType(lastnode) == MEDIA_HARDDRIVE_DP) { + node = (HARDDRIVE_DEVICE_PATH *)lastnode; + } + else + return (EINVAL); + } else { + return (EINVAL); + } + pd = malloc(sizeof(pdinfo_t)); if (pd == NULL) { printf("Failed to add disk, out of memory\n"); @@ -378,7 +436,52 @@ STAILQ_FOREACH(hd, &hdinfo, pd_link) { if (efi_devpath_match(hd->pd_devpath, disk_devpath) != 0) { - /* Add the partition. */ + /* Check if there's a related device entry + * already here. + */ + STAILQ_FOREACH(pp, &hd->pd_part, pd_link) { + /* If the new device is a subpath of + * an existing device, then the + * existing entry subsumes the new + * one. + */ + trimpath = efi_devpath_trim(pp->pd_devpath); + + if (trimpath == NULL) + return (ENOMEM); + + if (efi_devpath_match(trimpath, + part_devpath) != 0) { + free(trimpath); + free(pd); + return (EBUSY); + } + + /* If the existing device path is a + * subpath of the new one, then the + * new entry subsumes the existing + * one. + */ + free(trimpath); + + trimpath = efi_devpath_trim(part_devpath); + + if (trimpath == NULL) + return (ENOMEM); + + if (efi_devpath_match(trimpath, + pp->pd_devpath) != 0) { + pp->pd_handle = part_handle; + pp->pd_devpath = part_devpath; + free(trimpath); + free(pd); + return (0); + } + free(trimpath); + + } + + /* Add the partition. */ pd->pd_handle = part_handle; pd->pd_unit = node->PartitionNumber; pd->pd_devpath = part_devpath; @@ -514,7 +617,6 @@ devpath = efi_lookup_devpath(efipart_handles[i]); if (devpath == NULL) continue; - if ((node = efi_devpath_last_node(devpath)) == NULL) continue; if (efipart_floppy(node) != NULL) @@ -525,21 +627,47 @@ if (EFI_ERROR(status)) continue; + /* Handle GELI volumes */ + if (DevicePathType(node) == MEDIA_DEVICE_PATH && + DevicePathSubType(node) == MEDIA_VENDOR_DP) { + VENDOR_DEVICE_PATH *vendornode; + + vendornode = (VENDOR_DEVICE_PATH *)node; + + /* We only want GELI partitions */ + if (memcmp(&(vendornode->Guid), &FreeBSDGELIGUID, + sizeof(EFI_GUID))) { + continue; + } + + /* Trim off the vendor node */ + devpathcpy = efi_devpath_trim(devpath); + if (devpathcpy == NULL) + continue; + if ((node = efi_devpath_last_node(devpathcpy)) == NULL) + continue; + + devpath = devpathcpy; + } + if (DevicePathType(node) == MEDIA_DEVICE_PATH && DevicePathSubType(node) == MEDIA_HARDDRIVE_DP) { devpathcpy = efi_devpath_trim(devpath); if (devpathcpy == NULL) continue; + tmpdevpath = devpathcpy; status = BS->LocateDevicePath(&blkio_guid, &tmpdevpath, &handle); free(devpathcpy); if (EFI_ERROR(status)) continue; + /* * We do not support nested partitions. */ devpathcpy = efi_lookup_devpath(handle); + if (devpathcpy == NULL) continue; if ((node = efi_devpath_last_node(devpathcpy)) == NULL) @@ -547,6 +675,7 @@ if (DevicePathType(node) == MEDIA_DEVICE_PATH && DevicePathSubType(node) == MEDIA_HARDDRIVE_DP) continue; + efipart_hdinfo_add(handle, efipart_handles[i]); continue; } @@ -633,18 +762,6 @@ pd_dev.d_slice = -1; pd_dev.d_partition = -1; pd_dev.d_opendata = blkio; - ret = disk_open(&pd_dev, blkio->Media->BlockSize * - (blkio->Media->LastBlock + 1), - blkio->Media->BlockSize); - if (ret == 0) { - ret = disk_print(&pd_dev, line, verbose); - disk_close(&pd_dev); - if (ret != 0) - return (ret); - } else { - /* Do not fail from disk_open() */ - ret = 0; - } } else { if ((ret = pager_output("\n")) != 0) break; @@ -674,8 +791,8 @@ pdinfo_list_t * efiblk_get_pdinfo_list(struct devsw *dev) { - if (dev->dv_type == DEVT_DISK) - return (&hdinfo); + if (dev->dv_type == DEVT_DISK) + return (&hdinfo); if (dev->dv_type == DEVT_CD) return (&cdinfo); if (dev->dv_type == DEVT_FD) @@ -684,49 +801,73 @@ } static int +efipart_lookupdev(struct disk_devdesc *dev, pdinfo_t **pp) +{ + pdinfo_list_t *pdi; + pdinfo_t *pd; + + if (dev == NULL) { + return (EINVAL); + } + + pdi = efiblk_get_pdinfo_list(dev->d_dev); + if (pdi == NULL) { + return (EINVAL); + } + + pd = efiblk_get_pdinfo(pdi, dev->d_unit); + if (pd == NULL) { + return (EIO); + } + + /* If we're looking up a specific partition, get the + * IO interface from that devide handle. + */ + if (dev->d_slice != -1) { + pd = efiblk_get_pdinfo(&pd->pd_part, dev->d_slice); + } + + *pp = pd; + + return (0); +} + +static int efipart_open(struct open_file *f, ...) { va_list args; struct disk_devdesc *dev; - pdinfo_list_t *pdi; pdinfo_t *pd; EFI_BLOCK_IO *blkio; EFI_STATUS status; + int err; va_start(args, f); dev = va_arg(args, struct disk_devdesc*); va_end(args); - if (dev == NULL) - return (EINVAL); - pdi = efiblk_get_pdinfo_list(dev->d_dev); - if (pdi == NULL) - return (EINVAL); - - pd = efiblk_get_pdinfo(pdi, dev->d_unit); - if (pd == NULL) - return (EIO); + if ((err = efipart_lookupdev(dev, &pd)) != 0) { + return (err); + } if (pd->pd_blkio == NULL) { - status = BS->HandleProtocol(pd->pd_handle, &blkio_guid, - (void **)&pd->pd_blkio); - if (EFI_ERROR(status)) - return (efi_status_to_errno(status)); + status = BS->HandleProtocol(pd->pd_handle, &blkio_guid, + (void **)&pd->pd_blkio); + if (EFI_ERROR(status)) + return (efi_status_to_errno(status)); } blkio = pd->pd_blkio; - if (!blkio->Media->MediaPresent) + if (!blkio->Media->MediaPresent) { return (EAGAIN); + } pd->pd_open++; if (pd->pd_bcache == NULL) pd->pd_bcache = bcache_allocate(); - if (dev->d_dev->dv_type == DEVT_DISK) { - return (disk_open(dev, - blkio->Media->BlockSize * (blkio->Media->LastBlock + 1), - blkio->Media->BlockSize)); - } + dev->d_offset = 0; + return (0); } @@ -734,28 +875,20 @@ efipart_close(struct open_file *f) { struct disk_devdesc *dev; - pdinfo_list_t *pdi; pdinfo_t *pd; + int err; dev = (struct disk_devdesc *)(f->f_devdata); - if (dev == NULL) - return (EINVAL); - pdi = efiblk_get_pdinfo_list(dev->d_dev); - if (pdi == NULL) - return (EINVAL); - - pd = efiblk_get_pdinfo(pdi, dev->d_unit); - if (pd == NULL) - return (EINVAL); - + if ((err = efipart_lookupdev(dev, &pd)) != 0) { + return (err); + } pd->pd_open--; if (pd->pd_open == 0) { pd->pd_blkio = NULL; bcache_free(pd->pd_bcache); pd->pd_bcache = NULL; } - if (dev->d_dev->dv_type == DEVT_DISK) - return (disk_close(dev)); + return (0); } @@ -763,26 +896,13 @@ efipart_ioctl(struct open_file *f, u_long cmd, void *data) { struct disk_devdesc *dev; - pdinfo_list_t *pdi; pdinfo_t *pd; - int rc; + int err; dev = (struct disk_devdesc *)(f->f_devdata); - if (dev == NULL) - return (EINVAL); - pdi = efiblk_get_pdinfo_list(dev->d_dev); - if (pdi == NULL) - return (EINVAL); - - pd = efiblk_get_pdinfo(pdi, dev->d_unit); - if (pd == NULL) - return (EINVAL); - - if (dev->d_dev->dv_type == DEVT_DISK) { - rc = disk_ioctl(dev, cmd, data); - if (rc != ENOTTY) - return (rc); - } + if ((err = efipart_lookupdev(dev, &pd)) != 0) { + return (err); + } switch (cmd) { case DIOCGSECTORSIZE: @@ -847,20 +967,13 @@ { struct bcache_devdata bcd; struct disk_devdesc *dev; - pdinfo_list_t *pdi; pdinfo_t *pd; + int err; dev = (struct disk_devdesc *)devdata; - if (dev == NULL) - return (EINVAL); - pdi = efiblk_get_pdinfo_list(dev->d_dev); - if (pdi == NULL) - return (EINVAL); - - pd = efiblk_get_pdinfo(pdi, dev->d_unit); - if (pd == NULL) - return (EINVAL); - + if ((err = efipart_lookupdev(dev, &pd)) != 0) { + return (err); + } if (pd->pd_blkio->Media->RemovableMedia && !pd->pd_blkio->Media->MediaPresent) return (EIO); @@ -881,7 +994,6 @@ char *buf, size_t *rsize) { struct disk_devdesc *dev = (struct disk_devdesc *)devdata; - pdinfo_list_t *pdi; pdinfo_t *pd; EFI_BLOCK_IO *blkio; uint64_t off, disk_blocks, d_offset = 0; @@ -890,16 +1002,12 @@ int error; size_t diskend, readstart; - if (dev == NULL || blk < 0) - return (EINVAL); - - pdi = efiblk_get_pdinfo_list(dev->d_dev); - if (pdi == NULL) + if (blk < 0) return (EINVAL); - pd = efiblk_get_pdinfo(pdi, dev->d_unit); - if (pd == NULL) - return (EINVAL); + if ((error = efipart_lookupdev(dev, &pd)) != 0) { + return (error); + } blkio = pd->pd_blkio; if (blkio == NULL) @@ -909,20 +1017,7 @@ return (EIO); off = blk * 512; - /* - * Get disk blocks, this value is either for whole disk or for - * partition. - */ - disk_blocks = 0; - if (dev->d_dev->dv_type == DEVT_DISK) { - if (disk_ioctl(dev, DIOCGMEDIASIZE, &disk_blocks) == 0) { - /* DIOCGMEDIASIZE does return bytes. */ - disk_blocks /= blkio->Media->BlockSize; - } - d_offset = dev->d_offset; - } - if (disk_blocks == 0) - disk_blocks = blkio->Media->LastBlock + 1 - d_offset; + disk_blocks = blkio->Media->LastBlock + 1 - d_offset; /* make sure we don't read past disk end */ if ((off + size) / blkio->Media->BlockSize > d_offset + disk_blocks) { Index: sys/boot/efi/libefi/geli_driver.c =================================================================== --- /dev/null +++ sys/boot/efi/libefi/geli_driver.c @@ -0,0 +1,996 @@ +/*- + * Copyright (c) 2017 Eric McCorkle + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 +#include +#include + +#include "boot_crypto.h" +#include "efi_drivers.h" +#include "key_inject.h" + +#define _STRING_H_ +#define _STRINGS_H_ +#define _STDIO_H_ +#include +#include + +#define MAXPWLEN 256 + +typedef struct { + struct g_eli_metadata md; + struct g_eli_softc sc; + EFI_DISK_IO *diskio; + EFI_BLOCK_IO *blkio; + EFI_HANDLE dev; +} geli_info_t; + +static EFI_GUID DiskIOProtocolGUID = DISK_IO_PROTOCOL; +static EFI_GUID BlockIOProtocolGUID = BLOCK_IO_PROTOCOL; +static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL; +static EFI_GUID DriverBindingProtocolGUID = DRIVER_BINDING_PROTOCOL; +static EFI_GUID EfiKmsProtocolGuid = EFI_KMS_PROTOCOL; +static EFI_GUID KernelKeyInjectorGuid = KERNEL_KEY_INJECTOR_GUID; +static EFI_GUID Generic512Guid = EFI_KMS_FORMAT_GENERIC_512_GUID; +static EFI_GUID Generic2048Guid = EFI_KMS_FORMAT_GENERIC_2048_GUID; +static EFI_GUID FreeBSDGELIGUID = FREEBSD_GELI_GUID; +static EFI_DRIVER_BINDING geli_efi_driver; +static EFI_KMS_SERVICE *kms; + + +int +geliboot_crypt(u_int algid, int enc, u_char *data, size_t datasize, + const u_char *key, size_t keysize, u_char *iv) +{ + const symmetric_alg_t* alg; + + alg = get_symmetric_alg(algid); + + if (alg == NULL) { + printf("Unsupported crypto algorithm #%d\n", algid); + return (1); + } + + if(enc) { + return encrypt_symmetric(alg, data, datasize, key, keysize, iv); + } else { + return decrypt_symmetric(alg, data, datasize, key, keysize, iv); + } +} + +static int +g_eli_crypto_cipher(u_int algo, int enc, u_char *data, size_t datasize, + const u_char *key, size_t keysize) +{ + u_char iv[keysize]; + + bzero(iv, sizeof(iv)); + return (geliboot_crypt(algo, enc, data, datasize, key, keysize, iv)); +} + +int +g_eli_crypto_encrypt(u_int algo, u_char *data, size_t datasize, + const u_char *key, size_t keysize) +{ + + /* We prefer AES-CBC for metadata protection. */ + if (algo == CRYPTO_AES_XTS) + algo = CRYPTO_AES_CBC; + + return (g_eli_crypto_cipher(algo, 1, data, datasize, key, keysize)); +} + +int +g_eli_crypto_decrypt(u_int algo, u_char *data, size_t datasize, + const u_char *key, size_t keysize) +{ + + /* We prefer AES-CBC for metadata protection. */ + if (algo == CRYPTO_AES_XTS) + algo = CRYPTO_AES_CBC; + + return (g_eli_crypto_cipher(algo, 0, data, datasize, key, keysize)); +} + +static void +pwgets(char *buf, int n) +{ + int c; + char *lp; + + for (lp = buf;;) + switch (c = getchar() & 0177) { + case '\n': + case '\r': + *lp = '\0'; + putchar('\n'); + return; + case '\b': + case '\177': + if (lp > buf) { + lp--; + putchar('\b'); + putchar(' '); + putchar('\b'); + } + break; + case 'u' & 037: + case 'w' & 037: + lp = buf; + putchar('\n'); + break; + default: + if ((n < 1) || ((lp - buf) < n - 1)) { + *lp++ = c; + putchar('*'); + } + } +} + +static EFI_STATUS +decrypt(unsigned int algid, u_char *data, size_t datasize, u_char *key, + size_t keysize, u_char *iv) +{ + const symmetric_alg_t *alg; + + alg = get_symmetric_alg(algid); + + if (alg == NULL) { + return (EFI_INVALID_PARAMETER); + } + + return decrypt_symmetric(alg, data, datasize, key, keysize, iv); +} + +#define KEY_ID_SIZE 5 + +static unsigned int keyid = 0; + +static EFI_STATUS +register_key(const struct g_eli_softc *sc, const char *passphrase, + const u_char key[G_ELI_USERKEYLEN]) +{ + EFI_STATUS status; + char id_buf[EFI_KMS_KEY_IDENTIFIER_MAX_SIZE + 1]; + UINT16 count = 1; + EFI_KMS_KEY_DESCRIPTOR desc; + UINT8 key_id_size; + + snprintf(id_buf, EFI_KMS_KEY_IDENTIFIER_MAX_SIZE, "GELI%u", keyid); + key_id_size = strlen(id_buf); + desc.KeyIdentifierSize = key_id_size; + desc.KeyIdentifier = id_buf; + memcpy(&(desc.KeyFormat), &Generic512Guid, sizeof(EFI_GUID)); + desc.KeyValue = (void*)key; + + status = kms->AddKey(kms, NULL, &count, &desc, NULL, NULL); + + if (EFI_ERROR(status)) { + printf("Failed to add key %lu\n", EFI_ERROR_CODE(status)); + return (status); + } + + status = kms->AddKeyAttributes(kms, NULL, &key_id_size, id_buf, &count, + key_attr_service_id_geli, NULL, NULL); + + if (EFI_ERROR(status)) { + printf("Failed to add key attributes %lu\n", + EFI_ERROR_CODE(status)); + return (status); + } + + if (passphrase != NULL) { + snprintf(id_buf, EFI_KMS_KEY_IDENTIFIER_MAX_SIZE, "GELIPASS%u", + keyid); + key_id_size = strlen(id_buf); + desc.KeyIdentifierSize = key_id_size; + desc.KeyIdentifier = id_buf; + memcpy(&(desc.KeyFormat), &Generic2048Guid, sizeof(EFI_GUID)); + desc.KeyValue = (void*)passphrase; + + status = kms->AddKey(kms, NULL, &count, &desc, NULL, NULL); + + if (EFI_ERROR(status)) { + printf("Failed to add key %lu\n", + EFI_ERROR_CODE(status)); + return (status); + } + + status = kms->AddKeyAttributes(kms, NULL, &key_id_size, id_buf, + &count, key_attr_service_id_passphrase, NULL, NULL); + + if (EFI_ERROR(status)) { + printf("Failed to add key attributes %lu\n", + EFI_ERROR_CODE(status)); + return (status); + } + } + + keyid++; + + return (EFI_SUCCESS); +} + +static EFI_STATUS +try_password(struct g_eli_metadata *md, struct g_eli_softc *sc, + char *passphrase, bool register_pw) +{ + u_char key[G_ELI_USERKEYLEN], mkey[G_ELI_DATAIVKEYLEN], *mkp; + u_int keynum; + struct hmac_ctx ctx; + int error; + + g_eli_crypto_hmac_init(&ctx, NULL, 0); + /* + * Prepare Derived-Key from the user passphrase. + */ + if (md->md_iterations == 0) { + g_eli_crypto_hmac_update(&ctx, md->md_salt, + sizeof(md->md_salt)); + g_eli_crypto_hmac_update(&ctx, passphrase, + strlen(passphrase)); + } else if (md->md_iterations > 0) { + u_char dkey[G_ELI_USERKEYLEN]; + + pkcs5v2_genkey(dkey, sizeof(dkey), md->md_salt, + sizeof(md->md_salt), passphrase, + md->md_iterations); + g_eli_crypto_hmac_update(&ctx, dkey, sizeof(dkey)); + explicit_bzero(&dkey, sizeof(dkey)); + } + + g_eli_crypto_hmac_final(&ctx, key, 0); + + error = g_eli_mkey_decrypt(md, key, mkey, &keynum); + if (error == -1) { + bzero(&key, sizeof(key)); + bzero(&mkey, sizeof(mkey)); + return (EFI_ACCESS_DENIED); + } else if (error != 0) { + bzero(&key, sizeof(key)); + bzero(&mkey, sizeof(mkey)); + printf("Failed to decrypt GELI master key: %d\n", error); + return (EFI_LOAD_ERROR); + } + + /* Register the new key */ + if (register_pw) + register_key(sc, passphrase, key); + else + register_key(sc, NULL, key); + + bzero(&key, sizeof(key)); + + /* Store the keys */ + bcopy(mkey, sc->sc_mkey, sizeof(sc->sc_mkey)); + bcopy(mkey, sc->sc_ivkey, sizeof(sc->sc_ivkey)); + mkp = mkey + sizeof(sc->sc_ivkey); + if ((sc->sc_flags & G_ELI_FLAG_AUTH) == 0) { + bcopy(mkp, sc->sc_ekey, G_ELI_DATAKEYLEN); + } else { + /* + * The encryption key is: ekey = HMAC_SHA512(Data-Key, 0x10) + */ + g_eli_crypto_hmac(mkp, G_ELI_MAXKEYLEN, "\x10", 1, + sc->sc_ekey, 0); + } + bzero(&mkey, sizeof(mkey)); + + /* Initialize the per-sector IV */ + switch (sc->sc_ealgo) { + case CRYPTO_AES_XTS: + break; + default: + SHA256_Init(&sc->sc_ivctx); + SHA256_Update(&sc->sc_ivctx, sc->sc_ivkey, + sizeof(sc->sc_ivkey)); + break; + } + return (EFI_SUCCESS); +} + +static EFI_STATUS +try_cached_passphrases(struct g_eli_metadata *md, struct g_eli_softc *sc) +{ + EFI_STATUS status; + UINTN i; + UINTN count = 1; + UINTN nkeydescs = 0; + EFI_KMS_KEY_DESCRIPTOR *keydescs; + + status = kms->GetKeyByAttributes(kms, NULL, &count, + key_attr_service_id_passphrase, &nkeydescs, NULL, NULL, NULL); + + /* We might get EFI_SUCCESS if there are no keys */ + if (status == EFI_SUCCESS || status == EFI_NOT_FOUND || + nkeydescs == 0) { + return (EFI_ACCESS_DENIED); + } else if (status != EFI_BUFFER_TOO_SMALL) { + printf("Error getting number of passphrases: %lu\n", + EFI_ERROR_CODE(status)); + return (status); + } + + keydescs = malloc(nkeydescs * sizeof(EFI_KMS_KEY_DESCRIPTOR)); + + for(i = 0; i < nkeydescs; i++) { + keydescs[i].KeyValue = malloc(MAX_KEY_BYTES); + } + + if(keydescs == NULL) { + return (EFI_OUT_OF_RESOURCES); + } + + status = kms->GetKeyByAttributes(kms, NULL, &count, + key_attr_service_id_passphrase, &nkeydescs, keydescs, NULL, NULL); + + if (EFI_ERROR(status)) { + printf("Error getting passphrases: %lu\n", + EFI_ERROR_CODE(status)); + free(keydescs); + + return (status); + } + + for(i = 0; i < nkeydescs; i++) { + status = try_password(md, sc, keydescs[i].KeyValue, false); + + if (status != EFI_ACCESS_DENIED) { + for(; i < nkeydescs; i++) { + memset(keydescs[i].KeyValue, 0, MAX_KEY_BYTES); + free(keydescs[i].KeyValue); + + } + + free(keydescs); + return (status); + } + + memset(keydescs[i].KeyValue, 0, MAX_KEY_BYTES); + free(keydescs[i].KeyValue); + } + + free(keydescs); + + return (EFI_ACCESS_DENIED); +} + +static EFI_STATUS +try_keys(struct g_eli_metadata *md, struct g_eli_softc *sc) +{ + EFI_STATUS status; + UINTN i; + UINTN count = 1; + UINTN nkeydescs = 0; + EFI_KMS_KEY_DESCRIPTOR *keydescs; + u_char mkey[G_ELI_DATAIVKEYLEN], *mkp; + u_int keynum; + + status = kms->GetKeyByAttributes(kms, NULL, &count, + key_attr_service_id_geli, &nkeydescs, NULL, NULL, NULL); + + /* We might get EFI_SUCCESS if there are no keys */ + if (status == EFI_SUCCESS || status == EFI_NOT_FOUND || + nkeydescs == 0) { + return (try_cached_passphrases(md, sc)); + } else if (status != EFI_BUFFER_TOO_SMALL) { + printf("Error getting number of keys: %lu\n", + EFI_ERROR_CODE(status)); + return (status); + } + + keydescs = malloc(nkeydescs * sizeof(EFI_KMS_KEY_DESCRIPTOR)); + + for(i = 0; i < nkeydescs; i++) { + keydescs[i].KeyValue = malloc(MAX_KEY_BYTES); + } + + if(keydescs == NULL) { + return (EFI_OUT_OF_RESOURCES); + } + + status = kms->GetKeyByAttributes(kms, NULL, &count, + key_attr_service_id_geli, &nkeydescs, keydescs, NULL, NULL); + + if (EFI_ERROR(status)) { + printf("Error getting keys: %lu\n", EFI_ERROR_CODE(status)); + free(keydescs); + + return (status); + } + + /* Try all keys with the right encryption type */ + for(i = 0; i < nkeydescs; i++) { + if(g_eli_mkey_decrypt(md, keydescs[i].KeyValue, + mkey, &keynum) == 0) { + for(; i < nkeydescs; i++) { + memset(keydescs[i].KeyValue, 0, MAX_KEY_BYTES); + free(keydescs[i].KeyValue); + + } + + free(keydescs); + + /* Store the keys */ + bcopy(mkey, sc->sc_mkey, sizeof(sc->sc_mkey)); + bcopy(mkey, sc->sc_ivkey, sizeof(sc->sc_ivkey)); + mkp = mkey + sizeof(sc->sc_ivkey); + if ((sc->sc_flags & G_ELI_FLAG_AUTH) == 0) { + bcopy(mkp, sc->sc_ekey, G_ELI_DATAKEYLEN); + } else { + /* + * The encryption key is: ekey = HMAC_SHA512(Data-Key, 0x10) + */ + g_eli_crypto_hmac(mkp, G_ELI_MAXKEYLEN, "\x10", 1, + sc->sc_ekey, 0); + } + bzero(&mkey, sizeof(mkey)); + + /* Initialize the per-sector IV */ + switch (sc->sc_ealgo) { + case CRYPTO_AES_XTS: + break; + default: + SHA256_Init(&sc->sc_ivctx); + SHA256_Update(&sc->sc_ivctx, sc->sc_ivkey, + sizeof(sc->sc_ivkey)); + break; + } + + memset(keydescs, 0, + nkeydescs * sizeof(EFI_KMS_KEY_DESCRIPTOR)); + free(keydescs); + + return (EFI_SUCCESS); + } else { + memset(keydescs[i].KeyValue, 0, MAX_KEY_BYTES); + free(keydescs[i].KeyValue); + } + + memset(keydescs[i].KeyValue, 0, MAX_KEY_BYTES); + } + + memset(keydescs, 0, nkeydescs * sizeof(EFI_KMS_KEY_DESCRIPTOR)); + free(keydescs); + + return (try_cached_passphrases(md, sc)); +} + +static EFI_STATUS +ask_password(struct g_eli_metadata *md, struct g_eli_softc *sc) { + char pwbuf[MAXPWLEN + 1]; + EFI_STATUS status; + UINTN i; + + for(i = 0, status = EFI_ACCESS_DENIED; + i < 5 && status == EFI_ACCESS_DENIED; i++) { + printf("Enter passphrase for encrypted volume: "); + pwgets(pwbuf, MAXPWLEN); + status = try_password(md, sc, pwbuf, true); + memset(pwbuf, 0, MAXPWLEN); + + if (status == EFI_SUCCESS) { + printf("OK\n"); + } else if (status == EFI_ACCESS_DENIED) { + printf("Incorrect\n"); + } else { + printf("Error!\n"); + } + } + + if (status == EFI_ACCESS_DENIED) { + printf("Access denied: too many tries\n"); + } + + return (status); +} + +static EFI_STATUS +discover(struct g_eli_metadata *md, struct g_eli_softc *sc, EFI_BLOCK_IO *inner, + EFI_HANDLE dev) +{ + u_char buf[inner->Media->BlockSize]; + int error; + EFI_STATUS status; + + memset(md, 0, sizeof(struct g_eli_metadata)); + memset(sc, 0, sizeof(struct g_eli_softc)); + status = inner->ReadBlocks(inner, inner->Media->MediaId, + inner->Media->LastBlock, inner->Media->BlockSize, buf); + + if (status != EFI_SUCCESS) { + if (status != EFI_NO_MEDIA && status != EFI_MEDIA_CHANGED) { + printf("Failed to read last block (%lu)\n", + EFI_ERROR_CODE(status)); + } + return (status); + } + + error = eli_metadata_decode(buf, md); + + /* EINVAL means not found */ + if (error == EINVAL) { + return (EFI_NOT_FOUND); + } else if (error == EOPNOTSUPP) { + return (EFI_UNSUPPORTED); + } else if (error != 0) { + return (EFI_LOAD_ERROR); + } + + if ((md->md_flags & G_ELI_FLAG_ONETIME)) { + /* Swap device, skip it */ + return (EFI_NOT_FOUND); + } + + if (!(md->md_flags & G_ELI_FLAG_BOOT)) { + /* Disk is not GELI boot device, skip it */ + return (EFI_NOT_FOUND); + } + + /* First, try all the existing keys */ + status = try_keys(md, sc); + + if (status != EFI_SUCCESS) { + /* If none of them work, give the user five tries to input a + * new password + */ + if (status == EFI_ACCESS_DENIED) { + status = ask_password(md, sc); + + if (status != EFI_SUCCESS) { + return (status); + } + } else { + return (status); + } + } + + eli_metadata_softc(sc, md, inner->Media->BlockSize, + (inner->Media->LastBlock * inner->Media->BlockSize) + + inner->Media->BlockSize); + + return (EFI_SUCCESS); +} + +static EFI_STATUS EFIAPI +reset_impl(EFI_BLOCK_IO *This, BOOLEAN ev) +{ + geli_info_t *info = (geli_info_t*)(This + 1); + + return info->blkio->Reset(info->blkio, ev); +} + +static EFI_STATUS EFIAPI +read_impl(EFI_BLOCK_IO *This, UINT32 MediaID, EFI_LBA LBA, + UINTN BufferSize, VOID *Buffer) +{ + geli_info_t *info = (geli_info_t*)(This + 1); + char iv[G_ELI_IVKEYLEN]; + char *pbuf = Buffer; + off_t offset; + uint64_t keyno; + size_t n, nb; + struct g_eli_key gkey; + EFI_STATUS status; + + // Read the raw data + status = info->blkio->ReadBlocks(info->blkio, + info->blkio->Media->MediaId, LBA, BufferSize, Buffer); + + if (EFI_ERROR(status)) { + printf("Error reading encrypted blocks (%lu)\n", + EFI_ERROR_CODE(status)); + return (status); + } + + nb = BufferSize / info->sc.sc_sectorsize; + + for (n = 0; n < nb; n++) { + offset = (LBA * info->blkio->Media->BlockSize) + + (n * info->sc.sc_sectorsize); + pbuf = (char*)Buffer + (n * info->sc.sc_sectorsize); + + g_eli_crypto_ivgen(&(info->sc), offset, iv, G_ELI_IVKEYLEN); + + /* Get the key that corresponds to this offset */ + keyno = (offset >> G_ELI_KEY_SHIFT) / + info->sc.sc_sectorsize; + + g_eli_key_fill(&(info->sc), &gkey, keyno); + + status = decrypt(info->sc.sc_ealgo, pbuf, + info->sc.sc_sectorsize, gkey.gek_key, + info->sc.sc_ekeylen, iv); + + if (status != EFI_SUCCESS) { + printf("Error decrypting blocks %lu\n", + EFI_ERROR_CODE(status)); + explicit_bzero(&gkey, sizeof(gkey)); + return (status); + } + } + + explicit_bzero(&gkey, sizeof(gkey)); + + return (EFI_SUCCESS); +} + +static EFI_STATUS EFIAPI +write_impl(EFI_BLOCK_IO *This __unused, UINT32 MediaID __unused, + EFI_LBA LBA __unused, UINTN BufferSize __unused, VOID *Buffer __unused) +{ + return (EFI_UNSUPPORTED); +} + +static EFI_STATUS EFIAPI +flush_impl(EFI_BLOCK_IO *This) +{ + geli_info_t *info = (geli_info_t*)(This + 1); + + return info->blkio->FlushBlocks(info->blkio); +} + +static EFI_BLOCK_IO* +make_block_io_iface(struct g_eli_metadata *md, struct g_eli_softc *sc, + EFI_BLOCK_IO *inner, EFI_HANDLE dev) +{ + EFI_BLOCK_IO *blkio; + geli_info_t *info; + + if ((blkio = malloc(sizeof(EFI_BLOCK_IO) + sizeof(geli_info_t))) == + NULL) { + return NULL; + } + + if ((blkio->Media = malloc(sizeof(EFI_BLOCK_IO_MEDIA))) == NULL) { + free(blkio); + + return NULL; + } + + info = (geli_info_t*)(blkio + 1); + blkio->Revision = EFI_BLOCK_IO_INTERFACE_REVISION; + blkio->Media->MediaId = inner->Media->MediaId; + blkio->Media->RemovableMedia = false; + blkio->Media->MediaPresent = true; + blkio->Media->LogicalPartition = true; + blkio->Media->ReadOnly = true; + blkio->Media->WriteCaching = false; + blkio->Media->BlockSize = inner->Media->BlockSize; + blkio->Media->IoAlign = inner->Media->IoAlign; + blkio->Media->LastBlock = inner->Media->LastBlock - 1; + blkio->Reset = reset_impl; + blkio->ReadBlocks = read_impl; + blkio->WriteBlocks = write_impl; + blkio->FlushBlocks = flush_impl; + memcpy(&(info->md), md, sizeof(struct g_eli_metadata)); + memcpy(&(info->sc), sc, sizeof(struct g_eli_softc)); + info->dev = dev; + + return blkio; +} + +static EFI_STATUS EFIAPI +supported_impl(EFI_DRIVER_BINDING *This, EFI_HANDLE handle, + EFI_DEVICE_PATH *RemainingDevicePath __unused) +{ + return (BS->OpenProtocol(handle, &BlockIOProtocolGUID, NULL, + This->DriverBindingHandle, handle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL)); +} + +static EFI_STATUS EFIAPI +start_impl(EFI_DRIVER_BINDING *This, EFI_HANDLE handle, + EFI_DEVICE_PATH *RemainingDevicePath __unused) +{ + EFI_BLOCK_IO *blkio; + EFI_DISK_IO *driver_diskio; + EFI_STATUS status; + EFI_BLOCK_IO *newio; + EFI_HANDLE newhandle = NULL; + EFI_DEVICE_PATH *devpath, *newpath, *currpath, *newcurr; + VENDOR_DEVICE_PATH *vendornode; + struct g_eli_metadata md; + struct g_eli_softc sc; + UINTN pathlen; + geli_info_t *info; + + /* Grab Disk IO to make sure that we don't end up registering + * this handle twice. + */ + status = BS->OpenProtocol(handle, &DiskIOProtocolGUID, + (void**)&driver_diskio, This->DriverBindingHandle, handle, + EFI_OPEN_PROTOCOL_BY_DRIVER); + + if (EFI_ERROR(status)) { + if (status != EFI_ACCESS_DENIED && + status != EFI_ALREADY_STARTED && + status != EFI_UNSUPPORTED) { + printf("Could not open device %lu\n", + EFI_ERROR_CODE(status)); + } + + return (status); + } + + /* Build device path */ + status = BS->OpenProtocol(handle, &DevicePathGUID, (void**)&devpath, + This->DriverBindingHandle, handle, EFI_OPEN_PROTOCOL_GET_PROTOCOL); + + if (EFI_ERROR(status)) { + printf("Failed to obtain device path %lu\n", + EFI_ERROR_CODE(status)); + free(newio); + + return (status); + } + + currpath = devpath; + pathlen = 0; + + while (!IsDevicePathEnd(currpath)) { + pathlen += DevicePathNodeLength(currpath); + currpath = NextDevicePathNode(currpath); + } + + pathlen += sizeof(EFI_DEVICE_PATH); + pathlen += sizeof(VENDOR_DEVICE_PATH); + newpath = malloc(pathlen); + + if (newpath == NULL) { + printf("Failed to create new device path\n"); + + return (status); + } + + currpath = devpath; + newcurr = newpath; + + while (!IsDevicePathEnd(currpath)) { + memcpy(newcurr, currpath, DevicePathNodeLength(currpath)); + currpath = NextDevicePathNode(currpath); + newcurr = NextDevicePathNode(newcurr); + } + + vendornode = (VENDOR_DEVICE_PATH *)newcurr; + vendornode->Header.Type = MEDIA_DEVICE_PATH; + vendornode->Header.SubType = MEDIA_VENDOR_DP; + vendornode->Header.Length[0] = sizeof(VENDOR_DEVICE_PATH); + vendornode->Header.Length[1] = 0; + memcpy(&(vendornode->Guid), &FreeBSDGELIGUID, sizeof(EFI_GUID)); + newcurr = NextDevicePathNode(newcurr); + SetDevicePathEndNode(newcurr); + devpath = newpath; + + BS->CloseProtocol(handle, &DevicePathGUID, This->DriverBindingHandle, + handle); + + /* Get block IO */ + status = BS->OpenProtocol(handle, &BlockIOProtocolGUID, (void**)&blkio, + This->DriverBindingHandle, handle, EFI_OPEN_PROTOCOL_GET_PROTOCOL); + + if (EFI_ERROR(status)) { + printf("Could not open device %lu\n", EFI_ERROR_CODE(status)); + free(newpath); + + return (status); + } + + /* Test for GELI presence */ + status = discover(&md, &sc, blkio, handle); + + if (EFI_ERROR(status)) { + BS->CloseProtocol(handle, &BlockIOProtocolGUID, + This->DriverBindingHandle, handle); + free(newpath); + + return (status); + } + + /* Make Block IO interface */ + newio = make_block_io_iface(&md, &sc, blkio, handle); + info = (geli_info_t*)(newio + 1); + info->diskio = driver_diskio; + BS->CloseProtocol(handle, &BlockIOProtocolGUID, + This->DriverBindingHandle, handle); + + if (newio == NULL) { + free(newpath); + printf("Failed to create new IO interface!\n"); + return (EFI_OUT_OF_RESOURCES); + } + + /* Create device handle and attach interfaces */ + status = BS->InstallMultipleProtocolInterfaces(&newhandle, + &BlockIOProtocolGUID, newio, &DevicePathGUID, newpath, NULL); + + if (EFI_ERROR(status)) { + printf("Could not create child device %lu\n", + EFI_ERROR_CODE(status)); + free(newio); + free(newpath); + + return (status); + } + + status = BS->OpenProtocol(handle, &BlockIOProtocolGUID, + (void**)&(info->blkio), This->DriverBindingHandle, newhandle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER); + + if (EFI_ERROR(status)) { + printf("Could not associate child device %lu\n", + EFI_ERROR_CODE(status)); + return (status); + } + + return (EFI_SUCCESS); +} + +static EFI_STATUS EFIAPI +stop_impl(EFI_DRIVER_BINDING *This __unused, EFI_HANDLE handle __unused, + UINTN NumberOfChildren __unused, EFI_HANDLE *ChildHandleBuffer __unused) +{ + return (EFI_UNSUPPORTED); +} + +static EFI_STATUS +locate_kms(void) +{ + EFI_HANDLE *handles; + EFI_STATUS status; + UINTN sz; + u_int n, nin; + + /* Try and find a usable KMS */ + sz = 0; + handles = NULL; + status = BS->LocateHandle(ByProtocol, &EfiKmsProtocolGuid, NULL, &sz, + NULL); + + if (status == EFI_BUFFER_TOO_SMALL) { + handles = (EFI_HANDLE *)malloc(sz); + status = BS->LocateHandle(ByProtocol, &EfiKmsProtocolGuid, 0, + &sz, handles); + if (EFI_ERROR(status)) { + printf("Error getting KMS handles (%lu)\n", + EFI_ERROR_CODE(status)); + free(handles); + + return (status); + } + } else { + printf("Error getting size of KMS buffer (%lu)\n", + EFI_ERROR_CODE(status)); + + return (status); + } + + nin = sz / sizeof(EFI_HANDLE); + + for (n = 0; n < nin; n++) { + status = BS->OpenProtocol(handles[n], &EfiKmsProtocolGuid, + (void**)&kms, IH, handles[n], + EFI_OPEN_PROTOCOL_GET_PROTOCOL); + + if (EFI_ERROR(status)) { + printf("Failed to obtain KMS protocol interface (%lu)\n", + EFI_ERROR_CODE(status)); + return (status); + } + + if (!memcmp(&KernelKeyInjectorGuid, &(kms->ServiceId), + sizeof(EFI_GUID))) { + free(handles); + + return (EFI_SUCCESS); + } + + BS->CloseProtocol(handles[n], &KernelKeyInjectorGuid, + IH, handles[n]); + } + + return (EFI_NOT_FOUND); +} + +static void +init(void) +{ + EFI_STATUS status; + EFI_HANDLE *handles; + UINTN nhandles, i, hsize; + + status = locate_kms(); + + if (EFI_ERROR(status)) { + printf("Error locating usable KMS (%lu)\n", + EFI_ERROR_CODE(status)); + + return; + } + + geli_efi_driver.ImageHandle = IH; + geli_efi_driver.DriverBindingHandle = NULL; + status = BS->InstallMultipleProtocolInterfaces( + &(geli_efi_driver.DriverBindingHandle), &DriverBindingProtocolGUID, + &geli_efi_driver, NULL); + + if (EFI_ERROR(status)) { + printf("Failed to install GELI driver (%ld)!\n", + EFI_ERROR_CODE(status)); + + return; + } + + nhandles = 0; + hsize = 0; + status = BS->LocateHandle(ByProtocol, &BlockIOProtocolGUID, NULL, + &hsize, NULL); + + if (status != EFI_BUFFER_TOO_SMALL) { + printf("Could not get number of handles! (%ld)\n", + EFI_ERROR_CODE(status)); + return; + } + + handles = malloc(hsize); + nhandles = hsize / sizeof(EFI_HANDLE); + + status = BS->LocateHandle(ByProtocol, &BlockIOProtocolGUID, NULL, + &hsize, handles); + + if (EFI_ERROR(status)) { + printf("Could not get handles! (%ld)\n", + EFI_ERROR_CODE(status)); + return; + } + + for (i = 0; i < nhandles; i++) { + BS->ConnectController(handles[i], NULL, NULL, false); + } + + free(handles); +} + +static EFI_DRIVER_BINDING geli_efi_driver = { + .Version = 0x10, + .Supported = supported_impl, + .Start = start_impl, + .Stop = stop_impl +}; + +const efi_driver_t geli_driver = +{ + .name = "GELI", + .init = init, +}; Index: sys/boot/efi/libefi/key_inject.c =================================================================== --- /dev/null +++ sys/boot/efi/libefi/key_inject.c @@ -0,0 +1,744 @@ +/*- + * Copyright (c) 2016 Eric McCorkle + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 + +#include + +#include "efi_drivers.h" +#include "key_inject.h" + +#define MAX_KEYS 64 + +#define KEY_ATTR_SERVICE_ID_NAME "SERVICE_ID" +#define KEY_ATTR_SERVICE_ID_NAME_LEN 10 + +enum key_attr_id_t { + KEY_ATTR_SERVICE_ID_GELI, + KEY_ATTR_SERVICE_ID_PASSPHRASE +}; + +/* Tell who provided the key */ +enum service_id_t { + SERVICE_ID_NONE, + SERVICE_ID_GELI, + SERVICE_ID_PASSPHRASE +}; + +typedef struct key_entry_t { + UINT8 k_id_size; + UINT8 k_id[EFI_KMS_KEY_IDENTIFIER_MAX_SIZE]; + enum service_id_t k_service; + EFI_GUID k_format; + char k_data[MAX_KEY_BYTES]; +} key_entry_t; + +static key_entry_t keys[MAX_KEYS]; + +static int service_id_geli = SERVICE_ID_GELI; +static int service_id_passphrase = SERVICE_ID_PASSPHRASE; +static EFI_GUID Generic128Guid = EFI_KMS_FORMAT_GENERIC_128_GUID; +static EFI_GUID Generic256Guid = EFI_KMS_FORMAT_GENERIC_256_GUID; +static EFI_GUID Generic512Guid = EFI_KMS_FORMAT_GENERIC_512_GUID; +static EFI_GUID Generic1024Guid = EFI_KMS_FORMAT_GENERIC_1024_GUID; +static EFI_GUID Generic2048Guid = EFI_KMS_FORMAT_GENERIC_2048_GUID; +static EFI_GUID Generic3072Guid = EFI_KMS_FORMAT_GENERIC_3072_GUID; +static EFI_GUID AesXts128Guid = EFI_KMS_FORMAT_AESXTS_128_GUID; +static EFI_GUID AesXts256Guid = EFI_KMS_FORMAT_AESXTS_256_GUID; +static EFI_GUID AesCbc128Guid = EFI_KMS_FORMAT_AESCBC_128_GUID; +static EFI_GUID AesCbc256Guid = EFI_KMS_FORMAT_AESCBC_256_GUID; +static EFI_GUID RsaSha2048Guid = EFI_KMS_FORMAT_RSASHA256_2048_GUID; +static EFI_GUID RsaSha3072Guid = EFI_KMS_FORMAT_RSASHA256_3072_GUID; +static EFI_GUID EfiKmsProtocolGuid = EFI_KMS_PROTOCOL; +static EFI_GUID KernelKeyInjectorGuid = KERNEL_KEY_INJECTOR_GUID; + +static EFI_KMS_SERVICE key_inject_kms; + +static void +fill_keybuf(struct keybuf *keybuf) +{ + int i, idx; + + for (i = 0, idx = 0; i < MAX_KEYS; i++) { + switch (keys[i].k_service) { + default: + printf("Unknown service type %u\n", keys[i].k_service); + + case SERVICE_ID_PASSPHRASE: + case SERVICE_ID_NONE: + break; + + case SERVICE_ID_GELI: + keybuf->kb_ents[idx].ke_type = KEYBUF_TYPE_GELI; + memcpy(keybuf->kb_ents[idx].ke_data, keys[i].k_data, + MAX_KEY_BYTES); + idx++; + break; + } + } + + keybuf->kb_nents = idx; +} + +static EFI_STATUS EFIAPI +register_client_impl(EFI_KMS_SERVICE *This, EFI_KMS_CLIENT_INFO *Client, + UINTN *ClientDataState __unused, VOID **ClientData __unused) +{ + size_t keybuf_size = sizeof(struct keybuf) + + (MAX_KEYS * sizeof(struct keybuf_ent)); + char buf[keybuf_size]; + struct preloaded_file *kfp; + + /* Spec compliance */ + if (This == NULL || Client == NULL) { + return (EFI_INVALID_PARAMETER); + } + + if (Client->ClientIdSize != sizeof(struct preloaded_file *)) { + return (EFI_INVALID_PARAMETER); + } + + kfp = (struct preloaded_file *)Client->ClientId; + fill_keybuf((struct keybuf *)buf); + file_addmetadata(kfp, MODINFOMD_KEYBUF, keybuf_size, buf); + + return (EFI_SUCCESS); +} + +/* We don't support secure creation of keys at runtime! */ +static EFI_STATUS EFIAPI +create_key_impl(EFI_KMS_SERVICE *This __unused, + EFI_KMS_CLIENT_INFO *Client __unused, UINT16 *KeyDescriptorCount __unused, + EFI_KMS_KEY_DESCRIPTOR *KeyDescriptor __unused, + UINTN *ClientDataSize __unused, VOID **ClientData __unused) +{ + return (EFI_UNSUPPORTED); +} + +static EFI_STATUS +key_size(const EFI_GUID *format, size_t *size) +{ + if (!memcmp(format, &Generic128Guid, sizeof(EFI_GUID))) { + *size = 128 / 8; + return (EFI_SUCCESS); + } else if (!memcmp(format, &Generic256Guid, sizeof(EFI_GUID))) { + *size = 256 / 8; + return (EFI_SUCCESS); + } else if (!memcmp(format, &Generic512Guid, sizeof(EFI_GUID))) { + *size = 512 / 8; + return (EFI_SUCCESS); + } else if (!memcmp(format, &Generic1024Guid, sizeof(EFI_GUID))) { + *size = 1024 / 8; + return (EFI_SUCCESS); + } else if (!memcmp(format, &Generic2048Guid, sizeof(EFI_GUID))) { + *size = 2048 / 8; + return (EFI_SUCCESS); + } else if (!memcmp(format, &Generic3072Guid, sizeof(EFI_GUID))) { + *size = 3072 / 8; + return (EFI_SUCCESS); + } else if (!memcmp(format, &AesXts128Guid, sizeof(EFI_GUID))) { + *size = 128 / 8; + return (EFI_SUCCESS); + } else if (!memcmp(format, &AesXts256Guid, sizeof(EFI_GUID))) { + *size = 256 / 8; + return (EFI_SUCCESS); + } else if (!memcmp(format, &AesCbc128Guid, sizeof(EFI_GUID))) { + *size = 128 / 8; + return (EFI_SUCCESS); + } else if (!memcmp(format, &AesCbc256Guid, sizeof(EFI_GUID))) { + *size = 256 / 8; + return (EFI_SUCCESS); + } else if (!memcmp(format, &RsaSha2048Guid, sizeof(EFI_GUID))) { + *size = 2048 / 8; + return (EFI_SUCCESS); + } else if (!memcmp(format, &RsaSha3072Guid, sizeof(EFI_GUID))) { + *size = 3072 / 8; + return (EFI_SUCCESS); + } else { + return (EFI_INVALID_PARAMETER); + } +} + +static EFI_STATUS +copy_key(void *dst, const void *src, const EFI_GUID *format) +{ + EFI_STATUS status; + size_t size; + + status = key_size(format, &size); + + if (EFI_ERROR(status)) { + return (status); + } + + memcpy(dst, src, size); + + return (EFI_SUCCESS); +} + +static void +get_one_key(EFI_KMS_KEY_DESCRIPTOR *KeyDescriptor) +{ + EFI_STATUS status; + int i; + + for (i = 0; i < MAX_KEYS; i++) { + if (keys[i].k_id_size != 0 && + keys[i].k_id_size == KeyDescriptor->KeyIdentifierSize && + !memcmp(keys[i].k_id, KeyDescriptor->KeyIdentifier, + keys[i].k_id_size)) { + memcpy(&(KeyDescriptor->KeyFormat), &keys[i].k_format, + sizeof(EFI_GUID)); + status = copy_key(KeyDescriptor->KeyValue, + keys[i].k_data, &keys[i].k_format); + KeyDescriptor->KeyStatus = status; + + return; + } + } + + KeyDescriptor->KeyStatus = EFI_NOT_FOUND; +} + +static EFI_STATUS EFIAPI +get_key_impl(EFI_KMS_SERVICE *This, EFI_KMS_CLIENT_INFO *Client, + UINT16 *KeyDescriptorCount, EFI_KMS_KEY_DESCRIPTOR *KeyDescriptor, + UINTN *ClientDataSize __unused, VOID **ClientData __unused) +{ + int i; + + /* Spec compliance */ + if (This == NULL || KeyDescriptorCount == NULL || + KeyDescriptor == NULL) { + return (EFI_INVALID_PARAMETER); + } + + for(i = 0; i < *KeyDescriptorCount; i++) { + get_one_key(KeyDescriptor + i); + } + + return (EFI_SUCCESS); +} + +static void +add_one_key(EFI_KMS_KEY_DESCRIPTOR *KeyDescriptor) +{ + EFI_STATUS status; + int i; + + for (i = 0; i < MAX_KEYS; i++) { + if (keys[i].k_id_size == 0) { + keys[i].k_id_size = KeyDescriptor->KeyIdentifierSize; + memcpy(keys[i].k_id, KeyDescriptor->KeyIdentifier, + keys[i].k_id_size); + memcpy(&(keys[i].k_format), &(KeyDescriptor->KeyFormat), + sizeof(EFI_GUID)); + status = copy_key(keys[i].k_data, + KeyDescriptor->KeyValue, &(keys[i].k_format)); + KeyDescriptor->KeyStatus = status; + + return; + } + } + + KeyDescriptor->KeyStatus = EFI_OUT_OF_RESOURCES; +} + +static EFI_STATUS EFIAPI +add_key_impl(EFI_KMS_SERVICE *This, + EFI_KMS_CLIENT_INFO *Client __unused, UINT16 *KeyDescriptorCount, + EFI_KMS_KEY_DESCRIPTOR *KeyDescriptor, UINTN *ClientDataSize __unused, + VOID **ClientData __unused) +{ + int i; + + /* Spec compliance */ + if (This == NULL || KeyDescriptorCount == NULL || + KeyDescriptor == NULL) { + return (EFI_INVALID_PARAMETER); + } + + for (i = 0; i < *KeyDescriptorCount; i++) { + add_one_key(KeyDescriptor + i); + } + + return (EFI_SUCCESS); +} + +static void +delete_one_key(EFI_KMS_KEY_DESCRIPTOR *KeyDescriptor) +{ + int i; + + for (i = 0; i < MAX_KEYS; i++) { + if (keys[i].k_id_size != 0 && + keys[i].k_id_size == KeyDescriptor->KeyIdentifierSize && + !memcmp(keys[i].k_id, KeyDescriptor->KeyIdentifier, + keys[i].k_id_size)) { + memset(keys + i, 0, sizeof(key_entry_t)); + + return; + } + } + + KeyDescriptor->KeyStatus = EFI_NOT_FOUND; +} + +static EFI_STATUS EFIAPI +delete_key_impl(EFI_KMS_SERVICE *This, + EFI_KMS_CLIENT_INFO *Client __unused, UINT16 *KeyDescriptorCount, + EFI_KMS_KEY_DESCRIPTOR *KeyDescriptor, UINTN *ClientDataSize __unused, + VOID **ClientData __unused) +{ + int i; + + /* Spec compliance */ + if (This == NULL || KeyDescriptorCount == NULL || + KeyDescriptor == NULL) { + return (EFI_INVALID_PARAMETER); + } + + for (i = 0; i < *KeyDescriptorCount; i++) { + delete_one_key(KeyDescriptor + i); + } + + + return (EFI_SUCCESS); +} + +static EFI_STATUS EFIAPI +get_service_status_impl(EFI_KMS_SERVICE *This __unused) +{ + return (EFI_SUCCESS); +} + +static void +do_get_key_attributes(key_entry_t *entry, EFI_KMS_KEY_ATTRIBUTE *KeyAttributes) +{ + KeyAttributes[0].KeyAttributeIdentifierType = EFI_KMS_DATA_TYPE_UTF8; + KeyAttributes[0].KeyAttributeIdentifierCount = + sizeof KEY_ATTR_SERVICE_ID_NAME; + KeyAttributes[0].KeyAttributeIdentifier = KEY_ATTR_SERVICE_ID_NAME; + KeyAttributes[0].KeyAttributeInstance = 1; + KeyAttributes[0].KeyAttributeType = EFI_KMS_ATTRIBUTE_TYPE_INTEGER; + KeyAttributes[0].KeyAttributeValueSize = sizeof(int); + *((int*)(KeyAttributes[0].KeyAttributeValue)) = entry->k_service; + KeyAttributes[0].KeyAttributeStatus = EFI_SUCCESS; +} + +static EFI_STATUS EFIAPI +get_key_attributes_impl(EFI_KMS_SERVICE *This, + EFI_KMS_CLIENT_INFO *Client __unused, UINT8 *KeyIdentifierSize, + const VOID *KeyIdentifier, UINT16 *KeyAttributesCount, + EFI_KMS_KEY_ATTRIBUTE *KeyAttributes, UINTN *ClientDataSize __unused, + VOID **ClientData __unused) +{ + UINTN i; + + if (This == NULL || KeyIdentifierSize == NULL || + KeyIdentifier == NULL) { + return (EFI_INVALID_PARAMETER); + } + + if (*KeyAttributesCount < 1) { + return (EFI_BUFFER_TOO_SMALL); + } + + for (i = 0; i < MAX_KEYS; i++) { + if (keys[i].k_id_size != 0 && + keys[i].k_id_size == *KeyIdentifierSize && + !memcmp(keys[i].k_id, KeyIdentifier, *KeyIdentifierSize)) { + do_get_key_attributes(keys + i, KeyAttributes); + + return (EFI_SUCCESS); + } + } + + return (EFI_NOT_FOUND); +} + +static void +do_add_key_attributes(key_entry_t *entry, EFI_KMS_KEY_ATTRIBUTE *KeyAttribute) +{ + if (KeyAttribute->KeyAttributeIdentifierCount == + KEY_ATTR_SERVICE_ID_NAME_LEN && + !memcmp(KEY_ATTR_SERVICE_ID_NAME, + KeyAttribute->KeyAttributeIdentifier, + KEY_ATTR_SERVICE_ID_NAME_LEN)) { + entry->k_service = *((int*)(KeyAttribute->KeyAttributeValue)); + KeyAttribute->KeyAttributeStatus = EFI_SUCCESS; + } else { + KeyAttribute->KeyAttributeStatus = EFI_INVALID_PARAMETER; + } +} + +static EFI_STATUS EFIAPI +add_key_attributes_impl(EFI_KMS_SERVICE *This, + EFI_KMS_CLIENT_INFO *Client __unused, UINT8 *KeyIdentifierSize, + const VOID *KeyIdentifier, UINT16 *KeyAttributesCount, + EFI_KMS_KEY_ATTRIBUTE *KeyAttributes, UINTN *ClientDataSize __unused, + VOID **ClientData __unused) +{ + UINTN i, j; + + if (This == NULL || KeyIdentifierSize == NULL || + KeyIdentifier == NULL) { + return (EFI_INVALID_PARAMETER); + } + + for (i = 0; i < MAX_KEYS; i++) { + if(keys[i].k_id_size != 0 && + keys[i].k_id_size == *KeyIdentifierSize && + !memcmp(keys[i].k_id, KeyIdentifier, *KeyIdentifierSize)) { + for (j = 0; j < *KeyAttributesCount; j++) { + do_add_key_attributes(keys + i, + KeyAttributes + j); + + } + + return (EFI_SUCCESS); + } + } + + return (EFI_NOT_FOUND); +} + +static void +do_delete_key_attributes(key_entry_t *entry, + EFI_KMS_KEY_ATTRIBUTE *KeyAttribute) +{ + if (KeyAttribute->KeyAttributeIdentifierCount == + KEY_ATTR_SERVICE_ID_NAME_LEN && + !memcmp(KEY_ATTR_SERVICE_ID_NAME, + KeyAttribute->KeyAttributeIdentifier, + KEY_ATTR_SERVICE_ID_NAME_LEN)) { + entry->k_service = SERVICE_ID_NONE; + KeyAttribute->KeyAttributeStatus = EFI_SUCCESS; + } else { + KeyAttribute->KeyAttributeStatus = EFI_INVALID_PARAMETER; + } +} + +static EFI_STATUS EFIAPI +delete_key_attributes_impl(EFI_KMS_SERVICE *This, + EFI_KMS_CLIENT_INFO *Client __unused, UINT8 *KeyIdentifierSize, + const VOID *KeyIdentifier, UINT16 *KeyAttributesCount, + EFI_KMS_KEY_ATTRIBUTE *KeyAttributes, UINTN *ClientDataSize __unused, + VOID **ClientData __unused) +{ + UINTN i, j; + + if (This == NULL || KeyIdentifierSize == NULL || + KeyIdentifier == NULL) { + return (EFI_INVALID_PARAMETER); + } + + for (i = 0; i < MAX_KEYS; i++) { + if(keys[i].k_id_size != 0 && + keys[i].k_id_size == *KeyIdentifierSize && + !memcmp(keys[i].k_id, KeyIdentifier, *KeyIdentifierSize)) { + for (j = 0; j < *KeyAttributesCount; j++) { + do_delete_key_attributes(keys + i, + KeyAttributes + j); + } + + return (EFI_SUCCESS); + } + } + + return (EFI_NOT_FOUND); +} + +static EFI_STATUS +check_match(key_entry_t *entry, EFI_KMS_KEY_ATTRIBUTE *KeyAttribute, + bool *match) +{ + if (KeyAttribute->KeyAttributeIdentifierCount == + KEY_ATTR_SERVICE_ID_NAME_LEN && + !memcmp(KEY_ATTR_SERVICE_ID_NAME, + KeyAttribute->KeyAttributeIdentifier, + KEY_ATTR_SERVICE_ID_NAME_LEN)) { + *match = (entry->k_service == + *((int*)(KeyAttribute->KeyAttributeValue))); + + return (EFI_SUCCESS); + } else { + return (EFI_INVALID_PARAMETER); + } +} + +static EFI_STATUS EFIAPI +get_key_by_attributes_impl(EFI_KMS_SERVICE *This, + EFI_KMS_CLIENT_INFO *Client __unused, UINTN *KeyAttributesCount, + EFI_KMS_KEY_ATTRIBUTE *KeyAttributes, UINTN *KeyDescriptorCount, + EFI_KMS_KEY_DESCRIPTOR *KeyDescriptors, UINTN *ClientDataSize __unused, + VOID **ClientData __unused) +{ + EFI_STATUS status; + UINT8 idxs[MAX_KEYS]; + UINT8 nmatches = 0; + UINTN i, j; + bool match; + + if (This == NULL || KeyAttributesCount == NULL || + KeyAttributes == NULL || KeyDescriptorCount == NULL) { + return (EFI_INVALID_PARAMETER); + } + + for (i = 0; i < MAX_KEYS; i++) { + match = true; + + for (j = 0; j < *KeyAttributesCount && match; j++) { + status = check_match(keys + i, KeyAttributes + j, + &match); + + if (EFI_ERROR(status)) { + return (status); + } + } + + if (match) { + idxs[nmatches] = i; + nmatches++; + } + } + + if (nmatches == 0) { + return (EFI_NOT_FOUND); + } + + if (*KeyDescriptorCount < nmatches) { + *KeyDescriptorCount = nmatches; + + return (EFI_BUFFER_TOO_SMALL); + } + + if (KeyDescriptors == NULL) { + return (EFI_INVALID_PARAMETER); + } + + *KeyDescriptorCount = nmatches; + + for (i = 0; i < nmatches; i++) { + KeyDescriptors[i].KeyIdentifierSize = keys[idxs[i]].k_id_size; + KeyDescriptors[i].KeyIdentifier = keys[idxs[i]].k_id; + memcpy(&(KeyDescriptors[i].KeyFormat), + &(keys[idxs[i]].k_format), sizeof(EFI_GUID)); + status = copy_key(KeyDescriptors[i].KeyValue, + keys[idxs[i]].k_data, &keys[idxs[i]].k_format); + KeyDescriptors[i].KeyStatus = status; + } + + return (EFI_SUCCESS); +} + +static void +register_kms(void) +{ + EFI_STATUS status; + EFI_HANDLE handle = NULL; + + status = BS->InstallMultipleProtocolInterfaces(&handle, + &EfiKmsProtocolGuid, &key_inject_kms, NULL); + + if (EFI_ERROR(status)) { + printf("Could not register kernel KMS (%lu)\n", + EFI_ERROR_CODE(status)); + } +} + +static void +init(void) +{ + EFI_HANDLE *handles; + EFI_KMS_SERVICE *kms; + EFI_STATUS status; + UINTN sz; + u_int n, nin; + bool found; + + /* Try and find an instance of our KMS */ + sz = 0; + handles = NULL; + status = BS->LocateHandle(ByProtocol, &EfiKmsProtocolGuid, 0, &sz, 0); + + if (status == EFI_BUFFER_TOO_SMALL) { + handles = (EFI_HANDLE *)malloc(sz); + status = BS->LocateHandle(ByProtocol, &EfiKmsProtocolGuid, + 0, &sz, handles); + + if (status == EFI_NOT_FOUND) { + /* No handles found, just register our KMS */ + register_kms(); + + return; + } else if (EFI_ERROR(status)) { + printf("Could not get KMS device handles (%lu)\n", + EFI_ERROR_CODE(status)); + free(handles); + + return; + } + } else if (status == EFI_NOT_FOUND) { + register_kms(); + + return; + } else { + printf("Could not get KMS device handles (%lu)\n", + EFI_ERROR_CODE(status)); + + return; + } + + nin = sz / sizeof(EFI_HANDLE); + + for (n = 0; n < nin && !found; n++) { + status = BS->OpenProtocol(handles[n], &KernelKeyInjectorGuid, + (void**)&kms, IH, handles[n], + EFI_OPEN_PROTOCOL_GET_PROTOCOL); + + if (EFI_ERROR(status)) { + printf("Could not open KMS service (%lu)\n", + EFI_ERROR_CODE(status)); + return; + } + + if (!memcmp(&KernelKeyInjectorGuid, &(kms->ServiceId), + sizeof(EFI_GUID))) { + found = true; + } + + BS->CloseProtocol(handles[n], &KernelKeyInjectorGuid, + IH, handles[n]); + } + + free(handles); + + if (!found) { + register_kms(); + } +} + +static CHAR16 kernel_inject_str[] = { + 'F', 'r', 'e', 'e', 'B', 'S', 'D', ' ', + 'K', 'e', 'r', 'n', 'e', 'l', ' ', + 'I', 'n', 'j', 'e', 'c', 't', 'i', 'o', 'n', ' ', + 'K', 'M', 'S', '\0' +}; + +static EFI_GUID key_formats[] = { + EFI_KMS_FORMAT_GENERIC_128_GUID, + EFI_KMS_FORMAT_GENERIC_160_GUID, + EFI_KMS_FORMAT_GENERIC_256_GUID, + EFI_KMS_FORMAT_GENERIC_512_GUID, + EFI_KMS_FORMAT_GENERIC_1024_GUID, + EFI_KMS_FORMAT_GENERIC_2048_GUID, + EFI_KMS_FORMAT_GENERIC_3072_GUID, + EFI_KMS_FORMAT_SHA256_GUID, + EFI_KMS_FORMAT_SHA512_GUID, + EFI_KMS_FORMAT_AESXTS_128_GUID, + EFI_KMS_FORMAT_AESXTS_256_GUID, + EFI_KMS_FORMAT_AESCBC_128_GUID, + EFI_KMS_FORMAT_AESCBC_256_GUID, + EFI_KMS_FORMAT_RSASHA256_2048_GUID, + EFI_KMS_FORMAT_RSASHA256_3072_GUID +}; + + +static EFI_KMS_KEY_ATTRIBUTE key_attributes[] = { + [KEY_ATTR_SERVICE_ID_GELI] = { + .KeyAttributeIdentifierType = EFI_KMS_DATA_TYPE_UTF8, + .KeyAttributeIdentifierCount = KEY_ATTR_SERVICE_ID_NAME_LEN, + .KeyAttributeIdentifier = KEY_ATTR_SERVICE_ID_NAME, + .KeyAttributeInstance = 1, + .KeyAttributeType = EFI_KMS_ATTRIBUTE_TYPE_INTEGER, + .KeyAttributeValueSize = sizeof(int), + .KeyAttributeValue = &service_id_geli, + }, + [KEY_ATTR_SERVICE_ID_PASSPHRASE] = { + .KeyAttributeIdentifierType = EFI_KMS_DATA_TYPE_UTF8, + .KeyAttributeIdentifierCount = KEY_ATTR_SERVICE_ID_NAME_LEN, + .KeyAttributeIdentifier = KEY_ATTR_SERVICE_ID_NAME, + .KeyAttributeInstance = 2, + .KeyAttributeType = EFI_KMS_ATTRIBUTE_TYPE_INTEGER, + .KeyAttributeValueSize = sizeof(int), + .KeyAttributeValue = &service_id_passphrase, + } +}; + +EFI_KMS_KEY_ATTRIBUTE * const key_attr_service_id_geli = + &(key_attributes[KEY_ATTR_SERVICE_ID_GELI]); + +EFI_KMS_KEY_ATTRIBUTE * const key_attr_service_id_passphrase = + &(key_attributes[KEY_ATTR_SERVICE_ID_PASSPHRASE]); + +static EFI_KMS_SERVICE key_inject_kms = { + .GetServiceStatus = get_service_status_impl, + .RegisterClient = register_client_impl, + .CreateKey = create_key_impl, + .GetKey = get_key_impl, + .AddKey = add_key_impl, + .DeleteKey = delete_key_impl, + .GetKeyAttributes = get_key_attributes_impl, + .AddKeyAttributes = add_key_attributes_impl, + .DeleteKeyAttributes = delete_key_attributes_impl, + .GetKeyByAttributes = get_key_by_attributes_impl, + .ProtocolVersion = EFI_KMS_PROTOCOL_VERSION, + .ServiceId = KERNEL_KEY_INJECTOR_GUID, + .ServiceName = kernel_inject_str, + .ServiceVersion = 1, + .ServiceAvailable = true, + .ClientIdSupported = true, + .ClientIdRequired = false, + .ClientNameStringTypes = EFI_KMS_DATA_TYPE_UTF8, + .ClientNameRequired = true, + .ClientNameMaxCount = 255, + .ClientDataSupported = true, + .ClientDataMaxSize = 0xffffffffffffffff, + .KeyIdVariableLenSupported = true, + .KeyIdMaxSize = EFI_KMS_KEY_IDENTIFIER_MAX_SIZE, + .KeyFormatsCount = sizeof key_formats / sizeof key_formats[0], + .KeyFormats = key_formats, + .KeyAttributesSupported = true, + .KeyAttributeIdStringTypes = EFI_KMS_DATA_TYPE_UTF8, + .KeyAttributeIdMaxCount = EFI_KMS_KEY_ATTRIBUTE_ID_MAX_SIZE, + .KeyAttributesCount = sizeof key_attributes / sizeof key_attributes[0], + .KeyAttributes = key_attributes +}; + +const efi_driver_t key_inject_driver = +{ + .name = "Key Inject KMS", + .init = init, +}; Index: sys/boot/efi/loader/Makefile =================================================================== --- sys/boot/efi/loader/Makefile +++ sys/boot/efi/loader/Makefile @@ -99,6 +99,8 @@ LIBFDT= ${.OBJDIR}/../../fdt/libfdt.a .endif +LIBBOOT_CRYPTO= ${.OBJDIR}/../../boot_crypto/libboot_crypto.a + # Include bcache code. HAVE_BCACHE= yes @@ -150,8 +152,9 @@ LIBEFI= ${.OBJDIR}/../libefi/libefi.a DPADD= ${LIBFICL} ${LIBEFI} ${LIBFDT} ${LIBEFI_FDT} ${LIBSTAND} \ - ${LDSCRIPT} -LDADD= ${LIBFICL} ${LIBEFI} ${LIBFDT} ${LIBEFI_FDT} ${LIBSTAND} + ${LDSCRIPT} ${LIBBOOT_CRYPTO} +LDADD= ${LIBFICL} ${LIBEFI} ${LIBFDT} ${LIBEFI_FDT} ${LIBSTAND} \ + ${LIBBOOT_CRYPTO} .include Index: sys/boot/efi/loader/bootinfo.c =================================================================== --- sys/boot/efi/loader/bootinfo.c +++ sys/boot/efi/loader/bootinfo.c @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -42,9 +43,11 @@ #include #include +#include #include "bootstrap.h" #include "loader_efi.h" +#include "key_inject.h" #if defined(__amd64__) #include @@ -58,6 +61,8 @@ int bi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp); extern EFI_SYSTEM_TABLE *ST; +static EFI_GUID EfiKmsProtocolGuid = EFI_KMS_PROTOCOL; +static EFI_GUID KernelKeyInjectorGuid = KERNEL_KEY_INJECTOR_GUID; static const char howto_switches[] = "aCdrgDmphsv"; static int howto_masks[] = { @@ -235,6 +240,76 @@ return(addr); } +static void +key_inject_set_client(struct preloaded_file *kfp) +{ + EFI_KMS_CLIENT_INFO client; + EFI_KMS_SERVICE *kms; + EFI_HANDLE *handles; + EFI_STATUS status; + UINTN sz; + u_int n, nin; + + /* Try and find a usable KMS instance */ + sz = 0; + handles = NULL; + status = BS->LocateHandle(ByProtocol, &EfiKmsProtocolGuid, 0, &sz, 0); + + if (status == EFI_BUFFER_TOO_SMALL) { + handles = (EFI_HANDLE *)malloc(sz); + status = BS->LocateHandle(ByProtocol, &EfiKmsProtocolGuid, + 0, &sz, handles); + if (EFI_ERROR(status)) { + printf("Error getting handles for kernel KMS %lu\n", + EFI_ERROR_CODE(status)); + free(handles); + } + } else { + printf("Error getting handles for kernel KMS %lu\n", + EFI_ERROR_CODE(status)); + return; + } + + nin = sz / sizeof(EFI_HANDLE); + + for (n = 0; n < nin; n++) { + status = BS->OpenProtocol(handles[n], &EfiKmsProtocolGuid, + (void**)&kms, IH, handles[n], + EFI_OPEN_PROTOCOL_GET_PROTOCOL); + + if (EFI_ERROR(status)) { + printf("Error getting protocol for kernel KMS %lu\n", + EFI_ERROR_CODE(status)); + return; + } + + + if (!memcmp(&KernelKeyInjectorGuid, &(kms->ServiceId), + sizeof(EFI_GUID))) { + client.ClientIdSize = sizeof(struct preloaded_file *); + client.ClientId = kfp; + client.ClientNameType = EFI_KMS_DATA_TYPE_UTF8; + client.ClientNameCount = strlen(kfp->f_name); + client.ClientName = kfp->f_name; + status = kms->RegisterClient(kms, &client, NULL, NULL); + + if (EFI_ERROR(status)) { + printf("Error registering client for kernel KMS %lu\n", + EFI_ERROR_CODE(status)); + } + + BS->CloseProtocol(handles[n], &EfiKmsProtocolGuid, + IH, handles[n]); + free(handles); + + return; + } + + BS->CloseProtocol(handles[n], &EfiKmsProtocolGuid, + IH, handles[n]); + } +} + static int bi_load_efi_data(struct preloaded_file *kfp) { @@ -247,6 +322,7 @@ UINT32 mmver; struct efi_map_header *efihdr; + key_inject_set_client(kfp); #if defined(__amd64__) struct efi_fb efifb; @@ -413,8 +489,10 @@ kfp = file_findfile(NULL, "elf kernel"); if (kfp == NULL) kfp = file_findfile(NULL, "elf64 kernel"); - if (kfp == NULL) - panic("can't find kernel file"); + + if (kfp == NULL) + panic("can't find kernel file"); + kernend = 0; /* fill it in later */ file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof howto, &howto); file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp); Index: sys/boot/efi/loader/conf.c =================================================================== --- sys/boot/efi/loader/conf.c +++ sys/boot/efi/loader/conf.c @@ -35,6 +35,14 @@ #include #endif +#include "efi_drivers.h" + +const efi_driver_t *efi_drivers[] = { + &key_inject_driver, + &geli_driver, + NULL +}; + struct devsw *devsw[] = { &efipart_fddev, &efipart_cddev, Index: sys/boot/efi/loader/main.c =================================================================== --- sys/boot/efi/loader/main.c +++ sys/boot/efi/loader/main.c @@ -52,6 +52,7 @@ #include "efizfs.h" #endif +#include "efi_drivers.h" #include "loader_efi.h" extern char bootprog_info[]; @@ -337,6 +338,11 @@ */ bcache_init(32768, 512); + for (i = 0; efi_drivers[i] != NULL; i++) { + if (efi_drivers[i]->init != NULL) + efi_drivers[i]->init(); + } + /* * Parse the args to set the console settings, etc * boot1.efi passes these in, if it can read /boot.config or /boot/config Index: sys/geom/part/g_part.h =================================================================== --- sys/geom/part/g_part.h +++ sys/geom/part/g_part.h @@ -67,6 +67,7 @@ G_PART_ALIAS_FREEBSD_UFS, /* A UFS/UFS2 file system entry. */ G_PART_ALIAS_FREEBSD_VINUM, /* A Vinum partition entry. */ G_PART_ALIAS_FREEBSD_ZFS, /* A ZFS file system entry. */ + G_PART_ALIAS_FREEBSD_GELI, /* A GELI encrypted partition */ G_PART_ALIAS_LINUX_DATA, /* A Linux data partition entry. */ G_PART_ALIAS_LINUX_LVM, /* A Linux LVM partition entry. */ G_PART_ALIAS_LINUX_RAID, /* A Linux RAID partition entry. */ Index: sys/geom/part/g_part.c =================================================================== --- sys/geom/part/g_part.c +++ sys/geom/part/g_part.c @@ -102,6 +102,7 @@ { "freebsd-ufs", G_PART_ALIAS_FREEBSD_UFS }, { "freebsd-vinum", G_PART_ALIAS_FREEBSD_VINUM }, { "freebsd-zfs", G_PART_ALIAS_FREEBSD_ZFS }, + { "freebsd-geli", G_PART_ALIAS_FREEBSD_GELI }, { "linux-data", G_PART_ALIAS_LINUX_DATA }, { "linux-lvm", G_PART_ALIAS_LINUX_LVM }, { "linux-raid", G_PART_ALIAS_LINUX_RAID }, Index: sys/geom/part/g_part_gpt.c =================================================================== --- sys/geom/part/g_part_gpt.c +++ sys/geom/part/g_part_gpt.c @@ -176,6 +176,7 @@ static struct uuid gpt_uuid_freebsd_ufs = GPT_ENT_TYPE_FREEBSD_UFS; static struct uuid gpt_uuid_freebsd_vinum = GPT_ENT_TYPE_FREEBSD_VINUM; static struct uuid gpt_uuid_freebsd_zfs = GPT_ENT_TYPE_FREEBSD_ZFS; +static struct uuid gpt_uuid_freebsd_geli = GPT_ENT_TYPE_FREEBSD_GELI; static struct uuid gpt_uuid_linux_data = GPT_ENT_TYPE_LINUX_DATA; static struct uuid gpt_uuid_linux_lvm = GPT_ENT_TYPE_LINUX_LVM; static struct uuid gpt_uuid_linux_raid = GPT_ENT_TYPE_LINUX_RAID; @@ -236,6 +237,7 @@ { &gpt_uuid_freebsd_ufs, G_PART_ALIAS_FREEBSD_UFS, 0 }, { &gpt_uuid_freebsd_vinum, G_PART_ALIAS_FREEBSD_VINUM, 0 }, { &gpt_uuid_freebsd_zfs, G_PART_ALIAS_FREEBSD_ZFS, 0 }, + { &gpt_uuid_freebsd_geli, G_PART_ALIAS_FREEBSD_GELI, 0 }, { &gpt_uuid_linux_data, G_PART_ALIAS_LINUX_DATA, 0x0b }, { &gpt_uuid_linux_lvm, G_PART_ALIAS_LINUX_LVM, 0 }, { &gpt_uuid_linux_raid, G_PART_ALIAS_LINUX_RAID, 0 }, Index: sys/sys/disk/gpt.h =================================================================== --- sys/sys/disk/gpt.h +++ sys/sys/disk/gpt.h @@ -118,6 +118,8 @@ {0x516e7cb8,0x6ecf,0x11d6,0x8f,0xf8,{0x00,0x02,0x2d,0x09,0x71,0x2b}} #define GPT_ENT_TYPE_FREEBSD_ZFS \ {0x516e7cba,0x6ecf,0x11d6,0x8f,0xf8,{0x00,0x02,0x2d,0x09,0x71,0x2b}} +#define GPT_ENT_TYPE_FREEBSD_GELI \ + {0x516e7cbc,0x6ecf,0x11d6,0x8f,0xf8,{0x00,0x02,0x2d,0x09,0x71,0x2b}} #define GPT_ENT_TYPE_PREP_BOOT \ {0x9e1a2d38,0xc612,0x4316,0xaa,0x26,{0x8b,0x49,0x52,0x1e,0x5a,0x8b}}