Index: sys/boot/geli/Makefile =================================================================== --- sys/boot/geli/Makefile +++ sys/boot/geli/Makefile @@ -39,6 +39,7 @@ # AES implementation from sys/crypto .PATH: ${.CURDIR}/../../crypto/rijndael CFLAGS+= -I${.CURDIR}/../../ +CFLAGS+= -I${.CURDIR}/../common/ # Remove asserts CFLAGS+= -DNDEBUG SRCS+= rijndael-alg-fst.c rijndael-api-fst.c rijndael-api.c Index: sys/boot/geli/geliboot.h =================================================================== --- sys/boot/geli/geliboot.h +++ sys/boot/geli/geliboot.h @@ -30,6 +30,10 @@ #include #include +#include + +#include + #ifndef _GELIBOOT_H_ #define _GELIBOOT_H_ @@ -63,6 +67,7 @@ #define MIN(a,b) (((a) < (b)) ? (a) : (b)) #endif +#define GELI_MAX_KEYS 64 #define GELI_PW_MAXLEN 256 extern void pwgets(char *buf, int n); @@ -89,4 +94,6 @@ int geliboot_crypt(u_int algo, int enc, u_char *data, size_t datasize, const u_char *key, size_t keysize, u_char *iv); +void geli_fill_keybuf(struct keybuf *keybuf); + #endif /* _GELIBOOT_H_ */ Index: sys/boot/geli/geliboot.c =================================================================== --- sys/boot/geli/geliboot.c +++ sys/boot/geli/geliboot.c @@ -27,11 +27,47 @@ * $FreeBSD$ */ +#include + #include "geliboot.h" SLIST_HEAD(geli_list, geli_entry) geli_head = SLIST_HEAD_INITIALIZER(geli_head); struct geli_list *geli_headp; +typedef u_char geli_mkey[G_ELI_DATAIVKEYLEN]; + +static geli_mkey saved_keys[GELI_MAX_KEYS]; +static unsigned int nsaved_keys = 0; + +void +geli_fill_keybuf(struct keybuf *keybuf) +{ + unsigned int i; + + for (i = 0; i < nsaved_keys; i++) { + keybuf->kb_ents[i].ke_type = KEYBUF_TYPE_GELI; + memcpy(keybuf->kb_ents[i].ke_data, saved_keys[i], + G_ELI_DATAIVKEYLEN); + } + + keybuf->kb_nents = nsaved_keys; + bzero(saved_keys, sizeof(saved_keys)); +} + +static void +save_key(geli_mkey key) +{ + + /* + * If we run out of key space, the worst that will happen is + * it will ask the user for the password again. + */ + if (nsaved_keys < GELI_MAX_KEYS) { + memcpy(saved_keys[nsaved_keys], key, G_ELI_DATAIVKEYLEN); + nsaved_keys++; + } +} + static int geli_same_device(struct geli_entry *ge, struct dsk *dskp) { @@ -191,6 +227,7 @@ } /* Store the keys */ + save_key(mkey); bcopy(mkey, geli_e->sc.sc_mkey, sizeof(geli_e->sc.sc_mkey)); bcopy(mkey, geli_e->sc.sc_ivkey, sizeof(geli_e->sc.sc_ivkey)); mkp = mkey + sizeof(geli_e->sc.sc_ivkey); @@ -231,7 +268,7 @@ return (0); } } - + return (1); } Index: sys/boot/i386/gptboot/gptboot.c =================================================================== --- sys/boot/i386/gptboot/gptboot.c +++ sys/boot/i386/gptboot/gptboot.c @@ -101,7 +101,7 @@ void exit(int); static void load(void); -static int parse(char *, int *); +static int parse_cmds(char *, int *); static int dskread(void *, daddr_t, unsigned); void *malloc(size_t n); void free(void *ptr); @@ -316,7 +316,7 @@ } if (*cmd != '\0') { memcpy(cmdtmp, cmd, sizeof(cmdtmp)); - if (parse(cmdtmp, &dskupdated)) + if (parse_cmds(cmdtmp, &dskupdated)) break; if (dskupdated && gptinit() != 0) break; @@ -366,7 +366,7 @@ getstr(cmd, sizeof(cmd)); else if (!OPT_CHECK(RBX_QUIET)) putchar('\n'); - if (parse(cmd, &dskupdated)) { + if (parse_cmds(cmd, &dskupdated)) { putchar('\a'); continue; } @@ -489,7 +489,7 @@ } static int -parse(char *cmdstr, int *dskupdated) +parse_cmds(char *cmdstr, int *dskupdated) { char *arg = cmdstr; char *ep, *p, *q; Index: sys/boot/i386/libi386/bootinfo32.c =================================================================== --- sys/boot/i386/libi386/bootinfo32.c +++ sys/boot/i386/libi386/bootinfo32.c @@ -32,10 +32,18 @@ #include #include #include +#include #include "bootstrap.h" #include "libi386.h" #include "btxv86.h" +#ifdef LOADER_GELI_SUPPORT +#include "geliboot.h" + +static const size_t keybuf_size = sizeof(struct keybuf) + + (GELI_MAX_KEYS * sizeof(struct keybuf_ent)); +#endif + static struct bootinfo bi; /* @@ -146,11 +154,15 @@ int bootdevnr, i, howto; char *kernelname; const char *kernelpath; +#ifdef LOADER_GELI_SUPPORT + char buf[keybuf_size]; + struct keybuf *keybuf = (struct keybuf *)buf; +#endif howto = bi_getboothowto(args); - /* - * Allow the environment variable 'rootdev' to override the supplied device + /* + * Allow the environment variable 'rootdev' to override the supplied device * This should perhaps go to MI code and/or have $rootdev tested/set by * MI code before launching the kernel. */ @@ -185,7 +197,7 @@ case DEVT_NET: case DEVT_ZFS: break; - + default: printf("WARNING - don't know how to boot from device type %d\n", rootdev->d_type); } @@ -221,6 +233,11 @@ file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp); file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend); bios_addsmapdata(kfp); +#ifdef LOADER_GELI_SUPPORT + geli_fill_keybuf(keybuf); + file_addmetadata(kfp, MODINFOMD_KEYBUF, keybuf_size, buf); + bzero(buf, sizeof(buf)); +#endif /* Figure out the size and location of the metadata */ *modulep = addr; Index: sys/boot/i386/libi386/bootinfo64.c =================================================================== --- sys/boot/i386/libi386/bootinfo64.c +++ sys/boot/i386/libi386/bootinfo64.c @@ -40,6 +40,13 @@ #include "libi386.h" #include "btxv86.h" +#ifdef LOADER_GELI_SUPPORT +#include "geliboot.h" + +static const size_t keybuf_size = sizeof(struct keybuf) + + (GELI_MAX_KEYS * sizeof(struct keybuf_ent)); +#endif + /* * Copy module-related data into the load area, where it can be * used as a directory for loaded modules. @@ -189,6 +196,10 @@ vm_offset_t size; char *rootdevname; int howto; +#ifdef LOADER_GELI_SUPPORT + char buf[keybuf_size]; + struct keybuf *keybuf = (struct keybuf *)buf; +#endif if (!bi_checkcpu()) { printf("CPU doesn't support long mode\n"); @@ -197,8 +208,8 @@ howto = bi_getboothowto(args); - /* - * Allow the environment variable 'rootdev' to override the supplied device + /* + * Allow the environment variable 'rootdev' to override the supplied device * This should perhaps go to MI code and/or have $rootdev tested/set by * MI code before launching the kernel. */ @@ -238,6 +249,12 @@ if (add_smap != 0) bios_addsmapdata(kfp); +#ifdef LOADER_GELI_SUPPORT + geli_fill_keybuf(keybuf); + file_addmetadata(kfp, MODINFOMD_KEYBUF, keybuf_size, buf); + bzero(buf, sizeof(buf)); +#endif + size = bi_copymodules64(0); /* copy our environment */ Index: sys/boot/i386/zfsboot/zfsboot.c =================================================================== --- sys/boot/i386/zfsboot/zfsboot.c +++ sys/boot/i386/zfsboot/zfsboot.c @@ -121,7 +121,7 @@ void exit(int); void reboot(void); static void load(void); -static int parse(void); +static int parse_cmd(void); static void bios_getmem(void); void *malloc(size_t n); void free(void *ptr); @@ -398,7 +398,7 @@ v86.ctl = 0; v86.addr = 0x12; /* int 0x12 */ v86int(); - + bios_basemem = (v86.eax & 0xffff) * 1024; } @@ -442,7 +442,7 @@ v86.eax = 0x800; v86.edx = drive; v86int(); - + if (!V86_CY(v86.efl) && /* carry clear */ ((v86.edx & 0xff) != (drive & DRV_MASK))) { /* unit # OK */ if ((v86.ecx & 0x3f) == 0) { /* absurd sector size */ @@ -729,7 +729,7 @@ */ nextboot = 1; memcpy(cmddup, cmd, sizeof(cmd)); - if (parse()) { + if (parse_cmd()) { printf("failed to parse pad2 area of primary vdev\n"); reboot(); } @@ -756,11 +756,11 @@ if (*cmd) { /* - * Note that parse() is destructive to cmd[] and we also want + * Note that parse_cmd() is destructive to cmd[] and we also want * to honor RBX_QUIET option that could be present in cmd[]. */ memcpy(cmddup, cmd, sizeof(cmd)); - if (parse()) + if (parse_cmd()) autoboot = 0; if (!OPT_CHECK(RBX_QUIET)) printf("%s: %s\n", PATH_CONFIG, cmddup); @@ -810,7 +810,7 @@ else if (!autoboot || !OPT_CHECK(RBX_QUIET)) putchar('\n'); autoboot = 0; - if (parse()) + if (parse_cmd()) putchar('\a'); else load(); @@ -979,7 +979,7 @@ } static int -parse(void) +parse_cmd(void) { char *arg = cmd; char *ep, *p, *q; Index: sys/crypto/intake.h =================================================================== --- /dev/null +++ sys/crypto/intake.h @@ -0,0 +1,62 @@ +/*- + * 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 _INTAKE_H_ +#define _INTAKE_H_ + +#include + +/* + * This file provides an interface for providing keys to the kernel + * during boot time. + */ + +#define MAX_KEY_BITS 4096 +#define MAX_KEY_BYTES (MAX_KEY_BITS / NBBY) + +enum { + KEYBUF_TYPE_NONE, + KEYBUF_TYPE_GELI +}; + +struct keybuf_ent { + unsigned int ke_type; + char ke_data[MAX_KEY_BYTES]; +}; + +struct keybuf { + unsigned int kb_nents; + struct keybuf_ent kb_ents[]; +}; + +#ifdef _KERNEL +/* Get the key intake buffer */ +extern struct keybuf* get_keybuf(void); +#endif + +#endif Index: sys/geom/eli/g_eli.h =================================================================== --- sys/geom/eli/g_eli.h +++ sys/geom/eli/g_eli.h @@ -41,6 +41,7 @@ #include #include #include +#include #else #include #include @@ -139,6 +140,10 @@ #define G_ELI_CRYPTO_SW 2 #ifdef _KERNEL +#if (MAX_KEY_BYTES < G_ELI_DATAKEYLEN) +#error "MAX_KEY_BYTES is less than G_ELI_DATAKEYLEN" +#endif + extern int g_eli_debug; extern u_int g_eli_overwrites; extern u_int g_eli_batch; Index: sys/geom/eli/g_eli.c =================================================================== --- sys/geom/eli/g_eli.c +++ sys/geom/eli/g_eli.c @@ -53,6 +53,8 @@ #include #include +#include + FEATURE(geom_eli, "GEOM crypto module"); MALLOC_DEFINE(M_ELI, "eli data", "GEOM_ELI Data"); @@ -111,13 +113,39 @@ } SYSINIT(geli_fetch_loader_passphrase, SI_SUB_KMEM + 1, SI_ORDER_ANY, fetch_loader_passphrase, NULL); + static void -zero_boot_passcache(void * dummy) +zero_boot_passcache(void) { - memset(cached_passphrase, 0, sizeof(cached_passphrase)); + explicit_bzero(cached_passphrase, sizeof(cached_passphrase)); +} + +static void +zero_geli_intake_keys(void) +{ + struct keybuf *keybuf; + int i; + + if ((keybuf = get_keybuf()) != NULL) { + /* Scan the key buffer, clear all GELI keys. */ + for (i = 0; i < keybuf->kb_nents; i++) { + if (keybuf->kb_ents[i].ke_type == KEYBUF_TYPE_GELI) { + explicit_bzero(keybuf->kb_ents[i].ke_data, + sizeof(keybuf->kb_ents[i].ke_data)); + keybuf->kb_ents[i].ke_type = KEYBUF_TYPE_NONE; + } + } + } +} + +static void +zero_intake_passcache(void *dummy) +{ + zero_boot_passcache(); + zero_geli_intake_keys(); } -EVENTHANDLER_DEFINE(mountroot, zero_boot_passcache, NULL, 0); +EVENTHANDLER_DEFINE(mountroot, zero_intake_passcache, NULL, 0); static eventhandler_tag g_eli_pre_sync = NULL; @@ -997,6 +1025,7 @@ u_char key[G_ELI_USERKEYLEN], mkey[G_ELI_DATAIVKEYLEN]; u_int i, nkey, nkeyfiles, tries; int error; + struct keybuf *keybuf; g_trace(G_T_TOPOLOGY, "%s(%s, %s)", __func__, mp->name, pp->name); g_topology_assert(); @@ -1035,97 +1064,114 @@ tries = g_eli_tries; } - for (i = 0; i <= tries; i++) { - g_eli_crypto_hmac_init(&ctx, NULL, 0); - - /* - * Load all key files. - */ - nkeyfiles = g_eli_keyfiles_load(&ctx, pp->name); - - if (nkeyfiles == 0 && md.md_iterations == -1) { - /* - * No key files and no passphrase, something is - * definitely wrong here. - * geli(8) doesn't allow for such situation, so assume - * that there was really no passphrase and in that case - * key files are no properly defined in loader.conf. - */ - G_ELI_DEBUG(0, - "Found no key files in loader.conf for %s.", - pp->name); - return (NULL); - } - - /* Ask for the passphrase if defined. */ - if (md.md_iterations >= 0) { - /* Try first with cached passphrase. */ - if (i == 0) { - if (!g_eli_boot_passcache) - continue; - memcpy(passphrase, cached_passphrase, - sizeof(passphrase)); - } else { - printf("Enter passphrase for %s: ", pp->name); - cngets(passphrase, sizeof(passphrase), - g_eli_visible_passphrase); - memcpy(cached_passphrase, passphrase, - sizeof(passphrase)); - } - } - - /* - * 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)); - bzero(passphrase, sizeof(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); - bzero(passphrase, sizeof(passphrase)); - g_eli_crypto_hmac_update(&ctx, dkey, sizeof(dkey)); - bzero(dkey, sizeof(dkey)); - } - - g_eli_crypto_hmac_final(&ctx, key, 0); - - /* - * Decrypt Master-Key. - */ - error = g_eli_mkey_decrypt(&md, key, mkey, &nkey); - bzero(key, sizeof(key)); - if (error == -1) { - if (i == tries) { - G_ELI_DEBUG(0, - "Wrong key for %s. No tries left.", - pp->name); - g_eli_keyfiles_clear(pp->name); - return (NULL); - } - if (i > 0) { - G_ELI_DEBUG(0, - "Wrong key for %s. Tries left: %u.", - pp->name, tries - i); - } - /* Try again. */ - continue; - } else if (error > 0) { - G_ELI_DEBUG(0, - "Cannot decrypt Master Key for %s (error=%d).", - pp->name, error); - g_eli_keyfiles_clear(pp->name); - return (NULL); - } - g_eli_keyfiles_clear(pp->name); - G_ELI_DEBUG(1, "Using Master Key %u for %s.", nkey, pp->name); - break; - } + if ((keybuf = get_keybuf()) != NULL) { + /* Scan the key buffer, try all GELI keys. */ + for (i = 0; i < keybuf->kb_nents; i++) { + if (keybuf->kb_ents[i].ke_type == KEYBUF_TYPE_GELI) { + memcpy(key, keybuf->kb_ents[i].ke_data, + sizeof(key)); + + if (g_eli_mkey_decrypt(&md, key, + mkey, &nkey) == 0 ) { + explicit_bzero(key, sizeof(key)); + goto have_key; + } + } + } + } + + for (i = 0; i <= tries; i++) { + g_eli_crypto_hmac_init(&ctx, NULL, 0); + + /* + * Load all key files. + */ + nkeyfiles = g_eli_keyfiles_load(&ctx, pp->name); + + if (nkeyfiles == 0 && md.md_iterations == -1) { + /* + * No key files and no passphrase, something is + * definitely wrong here. + * geli(8) doesn't allow for such situation, so assume + * that there was really no passphrase and in that case + * key files are no properly defined in loader.conf. + */ + G_ELI_DEBUG(0, + "Found no key files in loader.conf for %s.", + pp->name); + return (NULL); + } + + /* Ask for the passphrase if defined. */ + if (md.md_iterations >= 0) { + /* Try first with cached passphrase. */ + if (i == 0) { + if (!g_eli_boot_passcache) + continue; + memcpy(passphrase, cached_passphrase, + sizeof(passphrase)); + } else { + printf("Enter passphrase for %s: ", pp->name); + cngets(passphrase, sizeof(passphrase), + g_eli_visible_passphrase); + memcpy(cached_passphrase, passphrase, + sizeof(passphrase)); + } + } + + /* + * 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)); + explicit_bzero(passphrase, sizeof(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); + bzero(passphrase, sizeof(passphrase)); + g_eli_crypto_hmac_update(&ctx, dkey, sizeof(dkey)); + explicit_bzero(dkey, sizeof(dkey)); + } + + g_eli_crypto_hmac_final(&ctx, key, 0); + + /* + * Decrypt Master-Key. + */ + error = g_eli_mkey_decrypt(&md, key, mkey, &nkey); + bzero(key, sizeof(key)); + if (error == -1) { + if (i == tries) { + G_ELI_DEBUG(0, + "Wrong key for %s. No tries left.", + pp->name); + g_eli_keyfiles_clear(pp->name); + return (NULL); + } + if (i > 0) { + G_ELI_DEBUG(0, + "Wrong key for %s. Tries left: %u.", + pp->name, tries - i); + } + /* Try again. */ + continue; + } else if (error > 0) { + G_ELI_DEBUG(0, + "Cannot decrypt Master Key for %s (error=%d).", + pp->name, error); + g_eli_keyfiles_clear(pp->name); + return (NULL); + } + g_eli_keyfiles_clear(pp->name); + G_ELI_DEBUG(1, "Using Master Key %u for %s.", nkey, pp->name); + break; + } +have_key: /* * We have correct key, let's attach provider. Index: sys/opencrypto/crypto.c =================================================================== --- sys/opencrypto/crypto.c +++ sys/opencrypto/crypto.c @@ -63,6 +63,7 @@ #include #include #include +#include #include #include #include @@ -74,6 +75,7 @@ #include #include +#include #include #include /* XXX for M_XDATA */ @@ -84,6 +86,7 @@ #if defined(__i386__) || defined(__amd64__) || defined(__aarch64__) #include #endif +#include SDT_PROVIDER_DEFINE(opencrypto); @@ -186,6 +189,37 @@ &crypto_timing, 0, "Enable/disable crypto timing support"); #endif +/* Try to avoid directly exposing the key buffer as a symbol */ +static struct keybuf *keybuf; + +static struct keybuf empty_keybuf = { + .kb_nents = 0 +}; + +/* Obtain the key buffer from boot metadata */ +static void +keybuf_init(void) +{ + caddr_t kmdp; + + kmdp = preload_search_by_type("elf kernel"); + + if (kmdp == NULL) + kmdp = preload_search_by_type("elf64 kernel"); + + keybuf = (struct keybuf *)preload_search_info(kmdp, + MODINFO_METADATA | MODINFOMD_KEYBUF); + + if (keybuf == NULL) + keybuf = &empty_keybuf; +} + +/* It'd be nice if we could store these in some kind of secure memory... */ +struct keybuf * get_keybuf(void) { + + return (keybuf); +} + static int crypto_init(void) { @@ -238,6 +272,9 @@ error); goto bad; } + + keybuf_init(); + return 0; bad: crypto_destroy(); @@ -282,7 +319,7 @@ /* XXX flush queues??? */ - /* + /* * Reclaim dynamically allocated resources. */ if (crypto_drivers != NULL) Index: sys/sys/linker.h =================================================================== --- sys/sys/linker.h +++ sys/sys/linker.h @@ -217,6 +217,7 @@ #define MODINFOMD_CTORS_ADDR 0x000a /* address of .ctors */ #define MODINFOMD_CTORS_SIZE 0x000b /* size of .ctors */ #define MODINFOMD_FW_HANDLE 0x000c /* Firmware dependent handle */ +#define MODINFOMD_KEYBUF 0x000d /* Crypto key intake buffer */ #define MODINFOMD_NOCOPY 0x8000 /* don't copy this metadata to the kernel */ #define MODINFOMD_DEPLIST (0x4001 | MODINFOMD_NOCOPY) /* depends on */