Index: stand/Makefile =================================================================== --- stand/Makefile +++ stand/Makefile @@ -9,6 +9,7 @@ SUBDIR+= forth .endif +SUBDIR+= boot_crypto SUBDIR+= man .include Index: stand/boot_crypto/Makefile =================================================================== --- /dev/null +++ stand/boot_crypto/Makefile @@ -0,0 +1,35 @@ +# $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: ${SYSDIR}/crypto/sha2 +CFLAGS+= -DWEAK_REFS +SRCS+= sha256c.c sha512c.c + +# md5 from libmd +.PATH: ${SRCTOP}/lib/libmd +SRCS+= md5c.c + +# AES implementation from sys/crypto +.PATH: ${SYSDIR}/crypto/rijndael +CFLAGS+= -I${LDRSRC} +# 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: stand/boot_crypto/boot_crypto.h =================================================================== --- /dev/null +++ stand/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: stand/boot_crypto/boot_crypto.c =================================================================== --- /dev/null +++ stand/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: stand/boot_crypto/boot_crypto_aes.h =================================================================== --- /dev/null +++ stand/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: stand/boot_crypto/boot_crypto_aes.c =================================================================== --- /dev/null +++ stand/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: stand/boot_crypto/boot_crypto_types.h =================================================================== --- /dev/null +++ stand/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: stand/efi/include/efi_drivers.h =================================================================== --- stand/efi/include/efi_drivers.h +++ stand/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: stand/efi/include/efilib.h =================================================================== --- stand/efi/include/efilib.h +++ stand/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 #include @@ -52,6 +55,7 @@ { STAILQ_ENTRY(pdinfo) pd_link; /* link in device list */ pdinfo_list_t pd_part; /* list of partitions */ + EFI_HANDLE pd_basehandle; EFI_HANDLE pd_handle; EFI_HANDLE pd_alias; EFI_DEVICE_PATH *pd_devpath; Index: stand/efi/include/efisec.h =================================================================== --- /dev/null +++ stand/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: stand/efi/include/key_inject.h =================================================================== --- /dev/null +++ stand/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: stand/efi/libefi/Makefile =================================================================== --- stand/efi/libefi/Makefile +++ stand/efi/libefi/Makefile @@ -11,7 +11,8 @@ WARNS?= 2 SRCS= delay.c devpath.c efi_console.c efichar.c efinet.c efipart.c env.c errno.c \ - handles.c wchar.c libefi.c efi_driver_utils.c efizfs.c devicename.c + handles.c wchar.c libefi.c efi_driver_utils.c efizfs.c devicename.c \ + key_inject.c geli_driver.c .if ${MACHINE_CPUARCH} == "amd64" || ${MACHINE_CPUARCH} == "i386" SRCS+= time.c @@ -19,6 +20,11 @@ SRCS+= time_event.c .endif +.PATH: ${SYSDIR}/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 @@ -41,6 +47,9 @@ CFLAGS+= -DEFI_ZFS_BOOT .endif +CFLAGS+= -I${SRCTOP}/stand/boot_crypto +CFLAGS+= -I${SYSDIR} + # Pick up the bootstrap header for some interface items CFLAGS+= -I${LDRSRC} @@ -52,6 +61,14 @@ CFLAGS+= -DTERM_EMU .endif +#eforedepend ${OBJS}: machine + +#CLEANFILES+= machine + +#machine: .NOMETA +# ln -sf ${SYSDIR}/${MACHINE}/include machine + + CFLAGS+= -DLIBEFI .include Index: stand/efi/libefi/efipart.c =================================================================== --- stand/efi/libefi/efipart.c +++ stand/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); @@ -259,7 +260,7 @@ } /* - * We assume the block size 512 or greater power of 2. + * We assume the block size 512 or greater power of 2. * iPXE is known to insert stub BLOCK IO device with * BlockSize 1. */ @@ -454,13 +455,16 @@ 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); - if (disk_devpath == NULL) + part_devpath = efi_lookup_devpath(part_handle); + + if (disk_devpath == NULL || part_devpath == NULL) return (ENOENT); if (part_handle != NULL) { @@ -476,7 +480,45 @@ 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 = calloc(1, sizeof(pdinfo_t)); + if (pd == NULL) { printf("Failed to add disk, out of memory\n"); return (ENOMEM); @@ -484,12 +526,56 @@ STAILQ_INIT(&pd->pd_part); STAILQ_FOREACH(hd, &hdinfo, pd_link) { - if (efi_devpath_match(hd->pd_devpath, disk_devpath) == true) { - if (part_devpath == NULL) - return (0); - - /* Add the partition. */ + if (efi_devpath_match(hd->pd_devpath, disk_devpath) != 0) { + /* 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_basehandle = disk_handle; + pp->pd_devpath = part_devpath; + free(trimpath); + free(pd); + return (0); + } + free(trimpath); + + } + + /* Add the partition. */ pd->pd_handle = part_handle; + pd->pd_basehandle = disk_handle; pd->pd_unit = node->PartitionNumber; pd->pd_devpath = part_devpath; STAILQ_INSERT_TAIL(&hd->pd_part, pd, pd_link); @@ -506,6 +592,7 @@ /* Add the disk. */ hd = pd; hd->pd_handle = disk_handle; + hd->pd_basehandle = disk_handle; hd->pd_unit = unit; hd->pd_devpath = disk_devpath; STAILQ_INSERT_TAIL(&hdinfo, hd, pd_link); @@ -522,6 +609,7 @@ /* Add the partition. */ pd->pd_handle = part_handle; + pd->pd_basehandle = disk_handle; pd->pd_unit = node->PartitionNumber; pd->pd_devpath = part_devpath; STAILQ_INSERT_TAIL(&hd->pd_part, pd, pd_link); @@ -581,6 +669,7 @@ */ if (p == NULL) { /* no colon, add the disk */ pd->pd_handle = disk_handle; + pd->pd_basehandle = disk_handle; pd->pd_unit = unit; pd->pd_devpath = devpath; STAILQ_INSERT_TAIL(&hdinfo, pd, pd_link); @@ -610,7 +699,8 @@ return (EINVAL); } /* Add the partition. */ - pd->pd_handle = disk_handle; + pd->pd_handle = disk_handle; + pd->pd_basehandle = disk_handle; pd->pd_unit = unit; pd->pd_devpath = devpath; STAILQ_INSERT_TAIL(&last->pd_part, pd, pd_link); @@ -632,7 +722,6 @@ devpath = efi_lookup_devpath(efipart_handles[i]); if (devpath == NULL) continue; - if ((node = efi_devpath_last_node(devpath)) == NULL) continue; @@ -644,6 +733,29 @@ 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_FILEPATH_DP) { efipart_hdinfo_add_filepath(efipart_handles[i]); @@ -655,16 +767,19 @@ 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) @@ -704,7 +819,8 @@ int ret = 0; EFI_BLOCK_IO *blkio; EFI_STATUS status; - EFI_HANDLE h; + EFI_HANDLE handle; + EFI_HANDLE base_handle; pdinfo_t *pd; CHAR16 *text; struct disk_devdesc pd_dev; @@ -718,9 +834,10 @@ return (ret); STAILQ_FOREACH(pd, pdlist, pd_link) { - h = pd->pd_handle; + handle = pd->pd_handle; + base_handle = pd->pd_basehandle; if (verbose) { /* Output the device path. */ - text = efi_devpath_name(efi_lookup_devpath(h)); + text = efi_devpath_name(efi_lookup_devpath(base_handle)); if (text != NULL) { printf(" %S", text); efi_free_devpath_name(text); @@ -731,7 +848,8 @@ snprintf(line, sizeof(line), " %s%d", dev->dv_name, pd->pd_unit); printf("%s:", line); - status = BS->HandleProtocol(h, &blkio_guid, (void **)&blkio); + status = BS->HandleProtocol(base_handle, &blkio_guid, + (void **)&blkio); if (!EFI_ERROR(status)) { printf(" %llu", blkio->Media->LastBlock == 0? 0: @@ -757,19 +875,21 @@ 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 { + + 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() */ + printf("Open device failed %d\n", ret); + ret = 0; + } + } else { if ((ret = pager_output("\n")) != 0) break; } @@ -795,6 +915,53 @@ return (efipart_print_common(&efipart_hddev, &hdinfo, verbose)); } +static int +efipart_lookupdev(struct disk_devdesc *dev, pdinfo_t **pp) +{ + pdinfo_list_t *pdi; + pdinfo_t *pd = NULL, *curr; + + if (dev == NULL) { + return (EINVAL); + } + + pdi = efiblk_get_pdinfo_list(dev->d_dev); + if (pdi == NULL) { + return (EINVAL); + } + + STAILQ_FOREACH(curr, pdi, pd_link) { + if (curr->pd_unit == dev->d_unit) { + pd = curr; + break; + } + } + + if (pd == NULL) + return (ENOENT); + + *pp = NULL; + + /* If we're looking up a specific partition, get the + * IO interface from that devide handle. + */ + if (dev->d_slice != -1) { + STAILQ_FOREACH(curr, &pd->pd_part, pd_link) { + if (curr->pd_unit == dev->d_slice) { + *pp = curr; + break; + } + } + } else { + *pp = pd; + } + + if (*pp == NULL) + return (ENOENT); + + return (0); +} + static int efipart_open(struct open_file *f, ...) { @@ -803,48 +970,34 @@ 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); - pd = efiblk_get_pdinfo((struct devdesc *)dev); - 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) { - int rc; - - rc = disk_open(dev, - blkio->Media->BlockSize * (blkio->Media->LastBlock + 1), - blkio->Media->BlockSize); - if (rc != 0) { - pd->pd_open--; - if (pd->pd_open == 0) { - pd->pd_blkio = NULL; - bcache_free(pd->pd_bcache); - pd->pd_bcache = NULL; - } - } - return (rc); - } + dev->d_offset = 0; + return (0); } @@ -853,14 +1006,13 @@ { struct disk_devdesc *dev; pdinfo_t *pd; + int err; dev = (struct disk_devdesc *)(f->f_devdata); - if (dev == NULL) - return (EINVAL); - pd = efiblk_get_pdinfo((struct devdesc *)dev); - if (pd == NULL) - return (EINVAL); + if ((err = efipart_lookupdev(dev, &pd)) != 0) { + return (err); + } pd->pd_open--; if (pd->pd_open == 0) { @@ -868,8 +1020,7 @@ bcache_free(pd->pd_bcache); pd->pd_bcache = NULL; } - if (dev->d_dev->dv_type == DEVT_DISK) - return (disk_close(dev)); + return (0); } @@ -878,21 +1029,13 @@ { struct disk_devdesc *dev; pdinfo_t *pd; - int rc; + int err; dev = (struct disk_devdesc *)(f->f_devdata); - if (dev == NULL) - return (EINVAL); - pd = efiblk_get_pdinfo((struct devdesc *)dev); - 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: @@ -958,14 +1101,13 @@ struct bcache_devdata bcd; struct disk_devdesc *dev; pdinfo_t *pd; + int err; dev = (struct disk_devdesc *)devdata; - if (dev == NULL) - return (EINVAL); - pd = efiblk_get_pdinfo((struct devdesc *)dev); - 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) @@ -999,12 +1141,12 @@ int error; size_t diskend, readstart; - if (dev == NULL || blk < 0) + if (blk < 0) return (EINVAL); - pd = efiblk_get_pdinfo((struct devdesc *)dev); - if (pd == NULL) - return (EINVAL); + if ((error = efipart_lookupdev(dev, &pd)) != 0) { + return (error); + } blkio = pd->pd_blkio; if (blkio == NULL) @@ -1014,20 +1156,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: stand/efi/libefi/geli_driver.c =================================================================== --- /dev/null +++ stand/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: stand/efi/libefi/key_inject.c =================================================================== --- /dev/null +++ stand/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: stand/efi/loader/Makefile =================================================================== --- stand/efi/loader/Makefile +++ stand/efi/loader/Makefile @@ -75,6 +75,8 @@ LIBEFI_FDT= ${BOOTOBJ}/efi/fdt/libefi_fdt.a .endif +LIBBOOT_CRYPTO= ${OBJTOP}/stand/boot_crypto/libboot_crypto.a + # Include bcache code. HAVE_BCACHE= yes @@ -125,7 +127,8 @@ LIBEFI= ${BOOTOBJ}/efi/libefi/libefi.a DPADD= ${LIBFICL} ${LIBEFI} ${LIBFDT} ${LIBEFI_FDT} ${LIBZFSBOOT} ${LIBSA} \ - ${LDSCRIPT} -LDADD= ${LIBFICL} ${LIBEFI} ${LIBFDT} ${LIBEFI_FDT} ${LIBZFSBOOT} ${LIBSA} + ${LDSCRIPT} ${LIBBOOT_CRYPTO} +LDADD= ${LIBFICL} ${LIBEFI} ${LIBFDT} ${LIBEFI_FDT} ${LIBZFSBOOT} ${LIBSA} \ + ${LIBBOOT_CRYPTO} .include Index: stand/efi/loader/bootinfo.c =================================================================== --- stand/efi/loader/bootinfo.c +++ stand/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 @@ -59,6 +62,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[] = { @@ -236,6 +241,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) { @@ -248,6 +323,7 @@ UINT32 mmver; struct efi_map_header *efihdr; + key_inject_set_client(kfp); #if defined(__amd64__) || defined(__aarch64__) struct efi_fb efifb; @@ -421,8 +497,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: stand/efi/loader/conf.c =================================================================== --- stand/efi/loader/conf.c +++ stand/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: stand/efi/loader/main.c =================================================================== --- stand/efi/loader/main.c +++ stand/efi/loader/main.c @@ -52,6 +52,7 @@ #include "efizfs.h" #endif +#include "efi_drivers.h" #include "loader_efi.h" extern char bootprog_info[]; @@ -73,14 +74,8 @@ EFI_GUID inputid = SIMPLE_TEXT_INPUT_PROTOCOL; static EFI_LOADED_IMAGE *img; - -#ifdef EFI_ZFS_BOOT -bool -efi_zfs_is_preferred(EFI_HANDLE *h) -{ - return (h == img->DeviceHandle); -} -#endif +static EFI_DEVICE_PATH *imgpath; +static EFI_DEVICE_PATH *imgprefix; static int has_keyboard(void) @@ -164,153 +159,196 @@ return retval; } +/* Check if this is a preferred device */ +#ifndef LOADER_CHECK_ALL_DEVS +static bool +check_preferred(EFI_HANDLE *h) +{ + EFI_DEVICE_PATH *path = efi_lookup_devpath(h); + bool out; + + if ((path = efi_lookup_devpath(h)) == NULL) + return (false); + + out = efi_devpath_is_prefix(imgpath, path) || + efi_devpath_is_prefix(imgprefix, path); + + return (out); +} + +static bool +zfs_check_preferred(uint64_t check_guid) +{ + return (pool_guid != 0 && check_guid == pool_guid); +} +#else +static bool +check_preferred(EFI_HANDLE *h) +{ + return (true); +} + +static bool +zfs_check_preferred(uint64_t check_guid) +{ + return (true); +} +#endif + +static void +set_vars(struct devdesc *currdev) +{ + char *devname; + + devname = efi_fmtdev(currdev); + env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev, + env_nounset); + env_setenv("loaddev", EV_VOLATILE, devname, env_noset, env_nounset); +} + static void set_devdesc_currdev(struct devsw *dev, int unit) { struct devdesc currdev; - char *devname; currdev.d_dev = dev; currdev.d_type = currdev.d_dev->dv_type; currdev.d_unit = unit; currdev.d_opendata = NULL; - devname = efi_fmtdev(&currdev); - - env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev, - env_nounset); - env_setenv("loaddev", EV_VOLATILE, devname, env_noset, env_nounset); + set_vars(&currdev); } +/* Directly check a devdesc for an installed system */ static int -find_currdev(EFI_LOADED_IMAGE *img) -{ - pdinfo_list_t *pdi_list; - pdinfo_t *dp, *pp; - EFI_DEVICE_PATH *devpath, *copy; - EFI_HANDLE h; - char *devname; - struct devsw *dev; - int unit; - uint64_t extra; - -#ifdef EFI_ZFS_BOOT - /* Did efi_zfs_probe() detect the boot pool? */ - if (pool_guid != 0) { - struct zfs_devdesc currdev; - - currdev.d_dev = &zfs_dev; - currdev.d_unit = 0; - currdev.d_type = currdev.d_dev->dv_type; - currdev.d_opendata = NULL; - currdev.pool_guid = pool_guid; - currdev.root_guid = 0; - devname = efi_fmtdev(&currdev); - - env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev, - env_nounset); - env_setenv("loaddev", EV_VOLATILE, devname, env_noset, - env_nounset); - init_zfs_bootenv(devname); - return (0); - } -#endif /* EFI_ZFS_BOOT */ - - /* We have device lists for hd, cd, fd, walk them all. */ - pdi_list = efiblk_get_pdinfo_list(&efipart_hddev); - STAILQ_FOREACH(dp, pdi_list, pd_link) { - struct disk_devdesc currdev; - - currdev.d_dev = &efipart_hddev; - currdev.d_type = currdev.d_dev->dv_type; - currdev.d_unit = dp->pd_unit; - currdev.d_opendata = NULL; - currdev.d_slice = -1; - currdev.d_partition = -1; - - if (dp->pd_handle == img->DeviceHandle) { - devname = efi_fmtdev(&currdev); - - env_setenv("currdev", EV_VOLATILE, devname, - efi_setcurrdev, env_nounset); - env_setenv("loaddev", EV_VOLATILE, devname, - env_noset, env_nounset); - return (0); - } - /* Assuming GPT partitioning. */ - STAILQ_FOREACH(pp, &dp->pd_part, pd_link) { - if (pp->pd_handle == img->DeviceHandle) { - currdev.d_slice = pp->pd_unit; - currdev.d_partition = 255; - devname = efi_fmtdev(&currdev); - - env_setenv("currdev", EV_VOLATILE, devname, - efi_setcurrdev, env_nounset); - env_setenv("loaddev", EV_VOLATILE, devname, - env_noset, env_nounset); - return (0); - } - } - } - - pdi_list = efiblk_get_pdinfo_list(&efipart_cddev); - STAILQ_FOREACH(dp, pdi_list, pd_link) { - if (dp->pd_handle == img->DeviceHandle || - dp->pd_alias == img->DeviceHandle) { - set_devdesc_currdev(&efipart_cddev, dp->pd_unit); - return (0); - } - } - - pdi_list = efiblk_get_pdinfo_list(&efipart_fddev); - STAILQ_FOREACH(dp, pdi_list, pd_link) { - if (dp->pd_handle == img->DeviceHandle) { - set_devdesc_currdev(&efipart_fddev, dp->pd_unit); - return (0); - } - } +check_devdesc(struct devdesc *currdev) { + /* Files that indicate the presence of an installed system */ + static const char* paths[] = { + "/boot/loader.conf", + "/boot/kernel", + NULL + }; + + struct stat sb; + int i, err; + + /* Check for the presence of any of the files */ + for (i = 0; paths[i] != NULL; i++) { + err = stat(paths[i], &sb); + if (errno != ENOENT) { + return (err); + } + } + + return (err); +} - /* - * Try the device handle from our loaded image first. If that - * fails, use the device path from the loaded image and see if - * any of the nodes in that path match one of the enumerated - * handles. - */ - if (efi_handle_lookup(img->DeviceHandle, &dev, &unit, &extra) == 0) { - set_devdesc_currdev(dev, unit); - return (0); - } +/* Set up a dev and then check it for an installed system */ +static int +check_dev(struct devsw *dev, int unit) { + struct devdesc currdev; - copy = NULL; - devpath = efi_lookup_image_devpath(IH); - while (devpath != NULL) { - h = efi_devpath_handle(devpath); - if (h == NULL) - break; + currdev.d_dev = dev; + currdev.d_type = currdev.d_dev->dv_type; + currdev.d_unit = unit; + currdev.d_opendata = NULL; + set_vars(&currdev); - free(copy); - copy = NULL; + return (check_devdesc(&currdev)); +} - if (efi_handle_lookup(h, &dev, &unit, &extra) == 0) { - set_devdesc_currdev(dev, unit); - return (0); - } +static int +find_currdev(void) +{ + pdinfo_list_t *pdi_list; + pdinfo_t *dp, *pp; + zfsinfo_list_t *zfsi_list; + zfsinfo_t *zi; + char *devname; - devpath = efi_lookup_devpath(h); - if (devpath != NULL) { - copy = efi_devpath_trim(devpath); - devpath = copy; - } - } - free(copy); +#ifdef EFI_ZFS_BOOT + zfsi_list = efizfs_get_zfsinfo_list(); + STAILQ_FOREACH(zi, zfsi_list, zi_link) { + if (zfs_check_preferred(zi->zi_pool_guid)) { + struct zfs_devdesc currdev; + + currdev.d_dev = &zfs_dev; + currdev.d_unit = 0; + currdev.d_type = currdev.d_dev->dv_type; + currdev.d_opendata = NULL; + currdev.pool_guid = zi->zi_pool_guid; + currdev.root_guid = 0; + devname = efi_fmtdev(&currdev); + set_vars((struct devdesc*)(&currdev)); + init_zfs_bootenv(devname); + + if (check_devdesc((struct devdesc*)(&currdev)) == 0) + return (0); + } + } +#endif /* EFI_ZFS_BOOT */ + /* We have device lists for hd, cd, fd, walk them all. */ + pdi_list = efiblk_get_pdinfo_list(&efipart_hddev); + STAILQ_FOREACH(dp, pdi_list, pd_link) { + struct disk_devdesc currdev; + + currdev.d_dev = &efipart_hddev; + currdev.d_type = currdev.d_dev->dv_type; + currdev.d_unit = dp->pd_unit; + currdev.d_opendata = NULL; + currdev.d_slice = -1; + currdev.d_partition = -1; + set_vars((struct devdesc*)(&currdev)); + + if (check_preferred(dp->pd_handle) && + check_devdesc((struct devdesc*)(&currdev)) == 0) + return (0); + + /* Assuming GPT partitioning. */ + STAILQ_FOREACH(pp, &dp->pd_part, pd_link) { + if (check_preferred(pp->pd_handle)) { + currdev.d_slice = pp->pd_unit; + currdev.d_partition = 255; + set_vars((struct devdesc*)(&currdev)); + + if (check_devdesc((struct devdesc*) + (&currdev)) == 0) + return (0); + } + } + } + +#ifdef LOADER_CHECK_ALL_DEVS + pdi_list = efiblk_get_pdinfo_list(&efipart_cddev); + STAILQ_FOREACH(dp, pdi_list, pd_link) { + if (check_preferred(dp->pd_handle) && + check_dev(&efipart_cddev, dp->pd_unit) == 0) + return (0); + } + + pdi_list = efiblk_get_pdinfo_list(&efipart_fddev); + STAILQ_FOREACH(dp, pdi_list, pd_link) { + if (check_preferred(dp->pd_handle) && + check_dev(&efipart_fddev, dp->pd_unit) == 0) + return (0); + } +#endif + return (ENOENT); +} - return (ENOENT); +#ifdef EFI_ZFS_BOOT +bool +efi_zfs_is_preferred(EFI_HANDLE *h) +{ + return (h == img->DeviceHandle || check_preferred(h)); } +#endif EFI_STATUS main(int argc, CHAR16 *argv[]) { char var[128]; EFI_GUID *guid; + EFI_STATUS status; int i, j, vargood, howto; UINTN k; int has_kbd; @@ -349,10 +387,29 @@ */ bcache_init(32768, 512); + for (i = 0; efi_drivers[i] != NULL; i++) { + if (efi_drivers[i]->init != NULL) + efi_drivers[i]->init(); + } + + if ((status = BS->HandleProtocol(img->DeviceHandle, &devid, + (VOID**)&imgpath)) != + EFI_SUCCESS) { + panic("Failed to query LoadedImage (%lu)\n", + EFI_ERROR_CODE(status)); + } + + /* The loaded image device path ends with a partition, then a + * file path. Trim them both to get the actual disk. + */ + if ((imgprefix = efi_devpath_trim(imgpath)) == NULL) { + panic("Couldn't trim device path"); + } + /* - * Parse the args to set the console settings, etc - * boot1.efi passes these in, if it can read /boot.config or /boot/config - * or iPXE may be setup to pass these in. + * Parse the args to set the console settings, etc boot1.efi + * passes these in, if it can read /boot.config or + * /boot/config or iPXE may be setup to pass these in. * * Loop through the args, and for each one that contains an '=' that is * not the first character, add it to the environment. This allows @@ -482,7 +539,7 @@ */ BS->SetWatchdogTimer(0, 0, 0, NULL); - if (find_currdev(img) != 0) + if (find_currdev() != 0) return (EFI_NOT_FOUND); efi_init_environment();