Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 15 Feb 2017 09:15:34 -0500
From:      Eric McCorkle <eric@metricspace.net>
To:        "freebsd-hackers@freebsd.org" <freebsd-hackers@FreeBSD.org>, freebsd-amd64@freebsd.org
Subject:   CFT: Crypto key intake metadata and GELI BIOS boot
Message-ID:  <38263f5b-b33d-1a57-9891-b08cb114a3e6@metricspace.net>

next in thread | raw e-mail | index | archive | help
This is an OpenPGP/MIME signed message (RFC 4880 and 3156)
--X7FPO3HRCLtP8EF11JkUNmr81Rg9AtiPG
Content-Type: multipart/mixed; boundary="jmJEd4Js9xDh8P6M6aV64OhDxXmdRl4Bh";
 protected-headers="v1"
From: Eric McCorkle <eric@metricspace.net>
To: "freebsd-hackers@freebsd.org" <freebsd-hackers@FreeBSD.org>,
 freebsd-amd64@freebsd.org
Message-ID: <38263f5b-b33d-1a57-9891-b08cb114a3e6@metricspace.net>
Subject: CFT: Crypto key intake metadata and GELI BIOS boot

--jmJEd4Js9xDh8P6M6aV64OhDxXmdRl4Bh
Content-Type: multipart/mixed;
 boundary="------------E31E4CC531EE896A8C281EC0"

This is a multi-part message in MIME format.
--------------E31E4CC531EE896A8C281EC0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable

Hello everyone,

Attached is a patch that provides a flexible mechanism for providing
encryption keys to a kernel from the boot loader.  This patch also
modifies the current BIOS GELI support to use this mechanism.  The git
repo can be found here: https://github.com/emc2/freebsd/tree/keybuf (the
branch is the "keybuf" branch).

Please test this patch with a GELI BIOS boot setup and report the results=
=2E

Note that I have been encountering a strange problem with the BIOS GELI
support that causes gptboot to crash with an illegal instruction.  This
seems to manifest on an unmodified copy of master.

Also note that this patch is already under review on phabricator.

--------------E31E4CC531EE896A8C281EC0
Content-Type: text/x-patch;
 name="keybuf.diff"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
 filename="keybuf.diff"

diff --git a/sys/boot/geli/Makefile b/sys/boot/geli/Makefile
index f5ab2432903..2ab31d589e0 100644
--- a/sys/boot/geli/Makefile
+++ b/sys/boot/geli/Makefile
@@ -39,6 +39,7 @@ SRCS+=3D		md5c.c
 # AES implementation from sys/crypto
 .PATH: ${.CURDIR}/../../crypto/rijndael
 CFLAGS+=3D	-I${.CURDIR}/../../
+CFLAGS+=3D	-I${.CURDIR}/../common/
 # Remove asserts
 CFLAGS+=3D	-DNDEBUG
 SRCS+=3D		rijndael-alg-fst.c rijndael-api-fst.c rijndael-api.c
diff --git a/sys/boot/geli/geliboot.c b/sys/boot/geli/geliboot.c
index f9a128cb667..c8905a85b0a 100644
--- a/sys/boot/geli/geliboot.c
+++ b/sys/boot/geli/geliboot.c
@@ -29,9 +29,45 @@
=20
 #include "geliboot.h"
=20
+#include <crypto/intake.h>
+
 SLIST_HEAD(geli_list, geli_entry) geli_head =3D SLIST_HEAD_INITIALIZER(g=
eli_head);
 struct geli_list *geli_headp;
=20
+typedef u_char geli_mkey[G_ELI_DATAIVKEYLEN];
+
+static geli_mkey saved_keys[GELI_MAX_KEYS];
+static unsigned int nsaved_keys =3D 0;
+
+void
+geli_fill_keybuf(keybuf_t *keybuf)
+{
+        int i;
+
+        for(i =3D 0; i < nsaved_keys; i++) {
+                keybuf->kb_ents[i].ke_type =3D KEYBUF_TYPE_GELI;
+                memcpy(keybuf->kb_ents[i].ke_data, saved_keys[i],
+                    G_ELI_DATAIVKEYLEN);
+        }
+
+        keybuf->kb_nents =3D 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 @@ geli_attach(struct dsk *dskp, const char *passphrase)=

 		}
=20
 		/* 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 =3D mkey + sizeof(geli_e->sc.sc_ivkey);
@@ -231,7 +268,7 @@ is_geli(struct dsk *dskp)
 			return (0);
 		}
 	}
-=09
+
 	return (1);
 }
=20
diff --git a/sys/boot/geli/geliboot.h b/sys/boot/geli/geliboot.h
index 83df1529571..fbac5316a93 100644
--- a/sys/boot/geli/geliboot.h
+++ b/sys/boot/geli/geliboot.h
@@ -27,8 +27,10 @@
  * $FreeBSD$
  */
=20
+#include <crypto/intake.h>
 #include <sys/endian.h>
 #include <sys/queue.h>
+#include <bootstrap.h>
=20
 #ifndef _GELIBOOT_H_
 #define _GELIBOOT_H_
@@ -63,6 +65,7 @@
 #define    MIN(a,b) (((a) < (b)) ? (a) : (b))
 #endif
=20
+#define	GELI_MAX_KEYS			64
 #define GELI_PW_MAXLEN			256
 extern void pwgets(char *buf, int n);
=20
@@ -89,4 +92,6 @@ int geli_passphrase(char *pw, int disk, int parttype, i=
nt part, struct dsk *dskp
 int geliboot_crypt(u_int algo, int enc, u_char *data, size_t datasize,
     const u_char *key, size_t keysize, u_char *iv);
=20
+void geli_fill_keybuf(keybuf_t *keybuf);
+
 #endif /* _GELIBOOT_H_ */
diff --git a/sys/boot/i386/gptboot/gptboot.c b/sys/boot/i386/gptboot/gptb=
oot.c
index 14438e6659c..1cc5d963996 100644
--- a/sys/boot/i386/gptboot/gptboot.c
+++ b/sys/boot/i386/gptboot/gptboot.c
@@ -101,7 +101,7 @@ static char *heap_end;
=20
 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 @@ main(void)
 		}
 		if (*cmd !=3D '\0') {
 			memcpy(cmdtmp, cmd, sizeof(cmdtmp));
-			if (parse(cmdtmp, &dskupdated))
+			if (parse_cmds(cmdtmp, &dskupdated))
 				break;
 			if (dskupdated && gptinit() !=3D 0)
 				break;
@@ -366,7 +366,7 @@ main(void)
 			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 @@ load(void)
 }
=20
 static int
-parse(char *cmdstr, int *dskupdated)
+parse_cmds(char *cmdstr, int *dskupdated)
 {
     char *arg =3D cmdstr;
     char *ep, *p, *q;
diff --git a/sys/boot/i386/libi386/bootinfo32.c b/sys/boot/i386/libi386/b=
ootinfo32.c
index d4344278351..8e050c9d7b2 100644
--- a/sys/boot/i386/libi386/bootinfo32.c
+++ b/sys/boot/i386/libi386/bootinfo32.c
@@ -32,10 +32,18 @@ __FBSDID("$FreeBSD$");
 #include <sys/reboot.h>
 #include <sys/linker.h>
 #include <machine/bootinfo.h>
+#include <machine/metadata.h>
 #include "bootstrap.h"
 #include "libi386.h"
 #include "btxv86.h"
=20
+#ifdef LOADER_GELI_SUPPORT
+#include "geliboot.h"
+
+static const size_t keybuf_size =3D sizeof(keybuf_t) +
+    (GELI_MAX_KEYS * sizeof(keybuf_ent_t));
+#endif
+
 static struct bootinfo  bi;
=20
 /*
@@ -146,11 +154,15 @@ bi_load32(char *args, int *howtop, int *bootdevp, v=
m_offset_t *bip, vm_offset_t
     int				bootdevnr, i, howto;
     char			*kernelname;
     const char			*kernelpath;
+#ifdef LOADER_GELI_SUPPORT
+    char                        buf[keybuf_size];
+    keybuf_t                    *keybuf =3D (keybuf_t *)buf;
+#endif
=20
     howto =3D bi_getboothowto(args);
=20
-    /*=20
-     * Allow the environment variable 'rootdev' to override the supplied=
 device=20
+    /*
+     * 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 @@ bi_load32(char *args, int *howtop, int *bootdevp, vm_=
offset_t *bip, vm_offset_t
     case DEVT_NET:
     case DEVT_ZFS:
 	    break;
-	   =20
+
     default:
 	printf("WARNING - don't know how to boot from device type %d\n", rootde=
v->d_type);
     }
@@ -221,6 +233,11 @@ bi_load32(char *args, int *howtop, int *bootdevp, vm=
_offset_t *bip, vm_offset_t
     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
=20
     /* Figure out the size and location of the metadata */
     *modulep =3D addr;
diff --git a/sys/boot/i386/libi386/bootinfo64.c b/sys/boot/i386/libi386/b=
ootinfo64.c
index 751e806e1b8..c53ec91e4a9 100644
--- a/sys/boot/i386/libi386/bootinfo64.c
+++ b/sys/boot/i386/libi386/bootinfo64.c
@@ -40,6 +40,13 @@ __FBSDID("$FreeBSD$");
 #include "libi386.h"
 #include "btxv86.h"
=20
+#ifdef LOADER_GELI_SUPPORT
+#include "geliboot.h"
+
+static const size_t keybuf_size =3D sizeof(keybuf_t) +
+    (GELI_MAX_KEYS * sizeof(keybuf_ent_t));
+#endif
+
 /*
  * Copy module-related data into the load area, where it can be
  * used as a directory for loaded modules.
@@ -189,6 +196,10 @@ bi_load64(char *args, vm_offset_t addr, vm_offset_t =
*modulep,
     vm_offset_t			size;
     char			*rootdevname;
     int				howto;
+#ifdef LOADER_GELI_SUPPORT
+    char                        buf[keybuf_size];
+    keybuf_t                    *keybuf =3D (keybuf_t *)buf;
+#endif
=20
     if (!bi_checkcpu()) {
 	printf("CPU doesn't support long mode\n");
@@ -197,8 +208,8 @@ bi_load64(char *args, vm_offset_t addr, vm_offset_t *=
modulep,
=20
     howto =3D bi_getboothowto(args);
=20
-    /*=20
-     * Allow the environment variable 'rootdev' to override the supplied=
 device=20
+    /*
+     * 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 @@ bi_load64(char *args, vm_offset_t addr, vm_offset_t =
*modulep,
     if (add_smap !=3D 0)
         bios_addsmapdata(kfp);
=20
+#ifdef LOADER_GELI_SUPPORT
+    geli_fill_keybuf(keybuf);
+    file_addmetadata(kfp, MODINFOMD_KEYBUF, keybuf_size, buf);
+    bzero(buf, sizeof(buf));
+#endif
+
     size =3D bi_copymodules64(0);
=20
     /* copy our environment */
diff --git a/sys/boot/i386/zfsboot/zfsboot.c b/sys/boot/i386/zfsboot/zfsb=
oot.c
index bb64384fadc..b78339fd610 100644
--- a/sys/boot/i386/zfsboot/zfsboot.c
+++ b/sys/boot/i386/zfsboot/zfsboot.c
@@ -121,7 +121,7 @@ static struct dmadat *dmadat;
 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 @@ bios_getmem(void)
 	v86.ctl =3D 0;
 	v86.addr =3D 0x12;		/* int 0x12 */
 	v86int();
-=09
+
 	bios_basemem =3D (v86.eax & 0xffff) * 1024;
     }
=20
@@ -442,7 +442,7 @@ int13probe(int drive)
     v86.eax =3D 0x800;
     v86.edx =3D drive;
     v86int();
-   =20
+
     if (!V86_CY(v86.efl) &&				/* carry clear */
 	((v86.edx & 0xff) !=3D (drive & DRV_MASK))) {	/* unit # OK */
 	if ((v86.ecx & 0x3f) =3D=3D 0) {			/* absurd sector size */
@@ -729,7 +729,7 @@ main(void)
 		 */
 		nextboot =3D 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 @@ main(void)
=20
     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 =3D 0;
 	if (!OPT_CHECK(RBX_QUIET))
 	    printf("%s: %s\n", PATH_CONFIG, cmddup);
@@ -810,7 +810,7 @@ main(void)
 	else if (!autoboot || !OPT_CHECK(RBX_QUIET))
 	    putchar('\n');
 	autoboot =3D 0;
-	if (parse())
+	if (parse_cmd())
 	    putchar('\a');
 	else
 	    load();
@@ -979,7 +979,7 @@ zfs_mount_ds(char *dsname)
 }
=20
 static int
-parse(void)
+parse_cmd(void)
 {
     char *arg =3D cmd;
     char *ep, *p, *q;
diff --git a/sys/crypto/intake.h b/sys/crypto/intake.h
new file mode 100644
index 00000000000..4f1b1e8522e
--- /dev/null
+++ b/sys/crypto/intake.h
@@ -0,0 +1,60 @@
+/*-
+ * 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 th=
e
+ *    documentation and/or other materials provided with the distributio=
n.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AN=
D
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE=

+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PU=
RPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIAB=
LE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUE=
NTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOO=
DS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)=

+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, S=
TRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY=
 WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY O=
F
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _INTAKE_H_
+#define _INTAKE_H_
+
+/*
+ * 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 / 8)
+
+enum {
+  KEYBUF_TYPE_NONE,
+  KEYBUF_TYPE_GELI
+};
+
+typedef struct keybuf_ent_t {
+        unsigned int ke_type;
+        char ke_data[MAX_KEY_BYTES];
+} keybuf_ent_t;
+
+typedef struct keybuf_t {
+        unsigned int kb_nents;
+        keybuf_ent_t kb_ents[];
+} keybuf_t;
+
+#ifdef _KERNEL
+/* Get the key intake buffer */
+extern keybuf_t* get_keybuf(void);
+#endif
+
+#endif
diff --git a/sys/geom/eli/g_eli.c b/sys/geom/eli/g_eli.c
index 6d734aece18..2945149d5f9 100644
--- a/sys/geom/eli/g_eli.c
+++ b/sys/geom/eli/g_eli.c
@@ -53,6 +53,8 @@ __FBSDID("$FreeBSD$");
 #include <geom/eli/g_eli.h>
 #include <geom/eli/pkcs5v2.h>
=20
+#include <crypto/intake.h>
+
 FEATURE(geom_eli, "GEOM crypto module");
=20
 MALLOC_DEFINE(M_ELI, "eli data", "GEOM_ELI Data");
@@ -114,11 +116,29 @@ SYSINIT(geli_fetch_loader_passphrase, SI_SUB_KMEM +=
 1, SI_ORDER_ANY,
 static void
 zero_boot_passcache(void * dummy)
 {
-
-	memset(cached_passphrase, 0, sizeof(cached_passphrase));
+	explicit_bzero(cached_passphrase, sizeof(cached_passphrase));
 }
 EVENTHANDLER_DEFINE(mountroot, zero_boot_passcache, NULL, 0);
=20
+static void
+zero_geli_intake_keys(void *dummy)
+{
+        keybuf_t *keybuf;
+        int i;
+
+        if ((keybuf =3D get_keybuf()) !=3D NULL) {
+                /* Scan the key buffer, clear all GELI keys. */
+                for (i =3D 0; i < keybuf->kb_nents; i++) {
+                         if (keybuf->kb_ents[i].ke_type =3D=3D KEYBUF_TY=
PE_GELI) {
+                                 explicit_bzero(keybuf->kb_ents[i].ke_da=
ta,
+                                     sizeof(key));
+                                 keybuf->kb_ents[i].ke_type =3D KEYBUF_T=
YPE_NONE;
+                         }
+                }
+        }
+}
+EVENTHANDLER_DEFINE(mountroot, zero_geli_intake_keys, NULL, 0);
+
 static eventhandler_tag g_eli_pre_sync =3D NULL;
=20
 static int g_eli_destroy_geom(struct gctl_req *req, struct g_class *mp,
@@ -997,6 +1017,8 @@ g_eli_taste(struct g_class *mp, struct g_provider *p=
p, int flags __unused)
 	u_char key[G_ELI_USERKEYLEN], mkey[G_ELI_DATAIVKEYLEN];
 	u_int i, nkey, nkeyfiles, tries;
 	int error;
+        bool found_intake =3D false;
+        keybuf_t *keybuf;
=20
 	g_trace(G_T_TOPOLOGY, "%s(%s, %s)", __func__, mp->name, pp->name);
 	g_topology_assert();
@@ -1035,97 +1057,115 @@ g_eli_taste(struct g_class *mp, struct g_provide=
r *pp, int flags __unused)
 		tries =3D g_eli_tries;
 	}
=20
-	for (i =3D 0; i <=3D tries; i++) {
-		g_eli_crypto_hmac_init(&ctx, NULL, 0);
-
-		/*
-		 * Load all key files.
-		 */
-		nkeyfiles =3D g_eli_keyfiles_load(&ctx, pp->name);
-
-		if (nkeyfiles =3D=3D 0 && md.md_iterations =3D=3D -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 >=3D 0) {
-			/* Try first with cached passphrase. */
-			if (i =3D=3D 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 =3D=3D 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 =3D g_eli_mkey_decrypt(&md, key, mkey, &nkey);
-		bzero(key, sizeof(key));
-		if (error =3D=3D -1) {
-			if (i =3D=3D 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=3D%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 =3D get_keybuf()) !=3D NULL) {
+                /* Scan the key buffer, try all GELI keys. */
+                for (i =3D 0; i < keybuf->kb_nents; i++) {
+                         if (keybuf->kb_ents[i].ke_type =3D=3D KEYBUF_TY=
PE_GELI) {
+                                 memcpy(key, keybuf->kb_ents[i].ke_data,=

+                                     sizeof(key));
+
+                                 if (g_eli_mkey_decrypt(&md, key,
+                                     mkey, &nkey) =3D=3D 0 ) {
+                                         found_intake =3D true;
+                                         break;
+                                 }
+                         }
+                }
+        }
+        explicit_bzero(key, sizeof(key));
+        if (!found_intake) {
+                for (i =3D 0; i <=3D tries; i++) {
+                        g_eli_crypto_hmac_init(&ctx, NULL, 0);
+
+                        /*
+                         * Load all key files.
+                         */
+                        nkeyfiles =3D g_eli_keyfiles_load(&ctx, pp->name=
);
+
+                        if (nkeyfiles =3D=3D 0 && md.md_iterations =3D=3D=
 -1) {
+                                /*
+                                 * No key files and no passphrase, somet=
hing is
+                                 * definitely wrong here.
+                                 * geli(8) doesn't allow for such situat=
ion, so assume
+                                 * that there was really no passphrase a=
nd in that case
+                                 * key files are no properly defined in =
loader.conf.
+                                 */
+                                G_ELI_DEBUG(0,
+                                    "Found no key files in loader.conf f=
or %s.",
+                                    pp->name);
+                                return (NULL);
+                        }
+
+                        /* Ask for the passphrase if defined. */
+                        if (md.md_iterations >=3D 0) {
+                                /* Try first with cached passphrase. */
+                                if (i =3D=3D 0) {
+                                        if (!g_eli_boot_passcache)
+                                                continue;
+                                        memcpy(passphrase, cached_passph=
rase,
+                                            sizeof(passphrase));
+                                } else {
+                                        printf("Enter passphrase for %s:=
 ", pp->name);
+                                        cngets(passphrase, sizeof(passph=
rase),
+                                            g_eli_visible_passphrase);
+                                        memcpy(cached_passphrase, passph=
rase,
+                                            sizeof(passphrase));
+                                }
+                        }
+
+                        /*
+                         * Prepare Derived-Key from the user passphrase.=

+                         */
+                        if (md.md_iterations =3D=3D 0) {
+                                g_eli_crypto_hmac_update(&ctx, md.md_sal=
t,
+                                    sizeof(md.md_salt));
+                                g_eli_crypto_hmac_update(&ctx, passphras=
e,
+                                    strlen(passphrase));
+                                explicit_bzero(passphrase, sizeof(passph=
rase));
+                        } 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.m=
d_iterations);
+                                bzero(passphrase, sizeof(passphrase));
+                                g_eli_crypto_hmac_update(&ctx, dkey, siz=
eof(dkey));
+                                explicit_bzero(dkey, sizeof(dkey));
+                        }
+
+                        g_eli_crypto_hmac_final(&ctx, key, 0);
+
+                        /*
+                         * Decrypt Master-Key.
+                         */
+                        error =3D g_eli_mkey_decrypt(&md, key, mkey, &nk=
ey);
+                        bzero(key, sizeof(key));
+                        if (error =3D=3D -1) {
+                                if (i =3D=3D 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 lef=
t: %u.",
+                                            pp->name, tries - i);
+                                }
+                                /* Try again. */
+                                continue;
+                        } else if (error > 0) {
+                                G_ELI_DEBUG(0,
+                                    "Cannot decrypt Master Key for %s (e=
rror=3D%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.", nk=
ey, pp->name);
+                        break;
+                }
+        }
=20
 	/*
 	 * We have correct key, let's attach provider.
diff --git a/sys/opencrypto/crypto.c b/sys/opencrypto/crypto.c
index 9e769c34fea..e6fa01c54b3 100644
--- a/sys/opencrypto/crypto.c
+++ b/sys/opencrypto/crypto.c
@@ -63,6 +63,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/eventhandler.h>
 #include <sys/kernel.h>
 #include <sys/kthread.h>
+#include <sys/linker.h>
 #include <sys/lock.h>
 #include <sys/module.h>
 #include <sys/mutex.h>
@@ -74,6 +75,7 @@ __FBSDID("$FreeBSD$");
 #include <ddb/ddb.h>
=20
 #include <vm/uma.h>
+#include <crypto/intake.h>
 #include <opencrypto/cryptodev.h>
 #include <opencrypto/xform.h>			/* XXX for M_XDATA */
=20
@@ -84,6 +86,7 @@ __FBSDID("$FreeBSD$");
 #if defined(__i386__) || defined(__amd64__) || defined(__aarch64__)
 #include <machine/pcb.h>
 #endif
+#include <machine/metadata.h>
=20
 SDT_PROVIDER_DEFINE(opencrypto);
=20
@@ -186,6 +189,36 @@ SYSCTL_INT(_debug, OID_AUTO, crypto_timing, CTLFLAG_=
RW,
 	   &crypto_timing, 0, "Enable/disable crypto timing support");
 #endif
=20
+/* Try to avoid directly exposing the key buffer as a symbol */
+static keybuf_t *keybuf =3D NULL;
+
+static keybuf_t empty_keybuf =3D {
+        .kb_nents =3D 0
+};
+
+/* Obtain the key buffer from boot metadata */
+static void
+keybuf_init(void)
+{
+	caddr_t	kmdp;
+
+	kmdp =3D preload_search_by_type("elf kernel");
+
+	if (kmdp =3D=3D NULL)
+		kmdp =3D preload_search_by_type("elf64 kernel");
+
+	keybuf =3D (keybuf_t *)preload_search_info(kmdp,
+	    MODINFO_METADATA | MODINFOMD_KEYBUF);
+
+        if (keybuf =3D=3D NULL)
+                keybuf =3D &empty_keybuf;
+}
+
+/* It'd be nice if we could store these in some kind of secure memory...=
 */
+keybuf_t* get_keybuf(void) {
+        return (keybuf);
+}
+
 static int
 crypto_init(void)
 {
@@ -238,6 +271,9 @@ crypto_init(void)
 			error);
 		goto bad;
 	}
+
+        keybuf_init();
+
 	return 0;
 bad:
 	crypto_destroy();
diff --git a/sys/sys/linker.h b/sys/sys/linker.h
index e650967cecb..49ac80a4027 100644
--- a/sys/sys/linker.h
+++ b/sys/sys/linker.h
@@ -143,7 +143,7 @@ int linker_file_foreach(linker_predicate_t *_predicat=
e, void *_context);
  * Lookup a symbol in a file.  If deps is TRUE, look in dependencies
  * if not found in file.
  */
-caddr_t linker_file_lookup_symbol(linker_file_t _file, const char* _name=
,=20
+caddr_t linker_file_lookup_symbol(linker_file_t _file, const char* _name=
,
 				  int _deps);
=20
 /*
@@ -157,7 +157,7 @@ int linker_file_lookup_set(linker_file_t _file, const=
 char *_name,
 /*
  * List all functions in a file.
  */
-int linker_file_function_listall(linker_file_t,=20
+int linker_file_function_listall(linker_file_t,
 				 linker_function_nameval_callback_t, void *);
=20
 /*
@@ -217,6 +217,7 @@ void *linker_hwpmc_list_objects(void);
 #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 kern=
el */
=20
 #define MODINFOMD_DEPLIST	(0x4001 | MODINFOMD_NOCOPY)	/* depends on */

--------------E31E4CC531EE896A8C281EC0--

--jmJEd4Js9xDh8P6M6aV64OhDxXmdRl4Bh--

--X7FPO3HRCLtP8EF11JkUNmr81Rg9AtiPG
Content-Type: application/pgp-signature; name="signature.asc"
Content-Description: OpenPGP digital signature
Content-Disposition: attachment; filename="signature.asc"

-----BEGIN PGP SIGNATURE-----

iHUEARYIAB0WIQRELMWN3SgpoYkrmidWwohAqoAEjQUCWKRiigAKCRBWwohAqoAE
jXLkAQCP26VGHDnGoFbNWOGWsEdeaoMwPN17KP7yjIJuxJCmwAD/Rz2NbZ0mJVDQ
1elCb7zT2ziAha+MmFAaR+niNzBGjww=
=uFcz
-----END PGP SIGNATURE-----

--X7FPO3HRCLtP8EF11JkUNmr81Rg9AtiPG--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?38263f5b-b33d-1a57-9891-b08cb114a3e6>