aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--share/mk/src.opts.mk4
-rw-r--r--stand/common/devopen.c18
-rw-r--r--stand/common/metadata.c8
-rw-r--r--stand/defs.mk2
-rw-r--r--stand/efi/loader/bootinfo.c8
-rw-r--r--stand/i386/gptboot/Makefile2
-rw-r--r--stand/i386/gptboot/gptboot.c80
-rw-r--r--stand/i386/gptzfsboot/Makefile2
-rw-r--r--stand/i386/isoboot/Makefile2
-rw-r--r--stand/i386/libi386/Makefile2
-rw-r--r--stand/i386/libi386/biosdisk.c212
-rw-r--r--stand/i386/libi386/bootinfo32.c11
-rw-r--r--stand/i386/libi386/bootinfo64.c12
-rw-r--r--stand/i386/loader/Makefile2
-rw-r--r--stand/i386/loader/main.c4
-rw-r--r--stand/i386/zfsboot/Makefile2
-rw-r--r--stand/i386/zfsboot/zfsboot.c182
-rw-r--r--stand/libsa/geli/Makefile.inc10
-rw-r--r--stand/libsa/geli/geli_metadata.c52
-rw-r--r--stand/libsa/geli/geliboot.c349
-rw-r--r--stand/libsa/geli/geliboot.h56
-rw-r--r--stand/libsa/geli/geliboot_internal.h12
-rw-r--r--stand/libsa/geli/gelidev.c323
-rw-r--r--stand/userboot/userboot/bootinfo32.c7
24 files changed, 771 insertions, 591 deletions
diff --git a/share/mk/src.opts.mk b/share/mk/src.opts.mk
index 5302b082558e..898104bae803 100644
--- a/share/mk/src.opts.mk
+++ b/share/mk/src.opts.mk
@@ -343,10 +343,6 @@ BROKEN_OPTIONS+=SSP
.if ${__T:Mmips*} || ${__T:Mpowerpc*} || ${__T:Msparc64} || ${__T:Mriscv*}
BROKEN_OPTIONS+=EFI
.endif
-# GELI isn't supported on !x86
-.if ${__T} != "i386" && ${__T} != "amd64"
-BROKEN_OPTIONS+=LOADER_GELI
-.endif
# OFW is only for powerpc and sparc64, exclude others
.if ${__T:Mpowerpc*} == "" && ${__T:Msparc64} == ""
BROKEN_OPTIONS+=LOADER_OFW
diff --git a/stand/common/devopen.c b/stand/common/devopen.c
index de6165c03e8e..23ee2e211001 100644
--- a/stand/common/devopen.c
+++ b/stand/common/devopen.c
@@ -32,6 +32,10 @@ __FBSDID("$FreeBSD$");
#include "bootstrap.h"
+#ifdef LOADER_GELI_SUPPORT
+#include "geliboot.h"
+#endif
+
int
devopen(struct open_file *f, const char *fname, const char **file)
{
@@ -43,6 +47,7 @@ devopen(struct open_file *f, const char *fname, const char **file)
return (result);
/* point to device-specific data so that device open can use it */
+ f->f_dev = dev->d_dev;
f->f_devdata = dev;
result = dev->d_dev->dv_open(f, dev);
if (result != 0) {
@@ -51,8 +56,17 @@ devopen(struct open_file *f, const char *fname, const char **file)
return (result);
}
- /* reference the devsw entry from the open_file structure */
- f->f_dev = dev->d_dev;
+#ifdef LOADER_GELI_SUPPORT
+ /*
+ * If f->f_dev is geli-encrypted and we can decrypt it (will prompt for
+ * pw if needed), this will attach the geli code to the open_file by
+ * replacing f->f_dev and f_devdata with pointers to a geli_devdesc.
+ */
+ if (f->f_dev->dv_type == DEVT_DISK) {
+ geli_probe_and_attach(f);
+ }
+#endif
+
return (0);
}
diff --git a/stand/common/metadata.c b/stand/common/metadata.c
index 96fad5b662ac..427a867bda6c 100644
--- a/stand/common/metadata.c
+++ b/stand/common/metadata.c
@@ -45,6 +45,10 @@ __FBSDID("$FreeBSD$");
#include "bootstrap.h"
+#ifdef LOADER_GELI_SUPPORT
+#include "geliboot.h"
+#endif
+
#if defined(__sparc64__)
#include <openfirm.h>
@@ -355,7 +359,9 @@ md_load_dual(char *args, vm_offset_t *modulep, vm_offset_t *dtb, int kern64)
#endif
file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend);
}
-
+#ifdef LOADER_GELI_SUPPORT
+ geli_export_key_metadata(kfp);
+#endif
#if defined(__sparc64__)
file_addmetadata(kfp, MODINFOMD_DTLB_SLOTS,
sizeof dtlb_slot, &dtlb_slot);
diff --git a/stand/defs.mk b/stand/defs.mk
index 1365f181a977..fc810f294793 100644
--- a/stand/defs.mk
+++ b/stand/defs.mk
@@ -56,7 +56,6 @@ CFLAGS+= -Ddouble=jagged-little-pill -Dfloat=floaty-mcfloatface
# GELI Support, with backward compat hooks (mostly)
-.if defined(HAVE_GELI)
.if defined(LOADER_NO_GELI_SUPPORT)
MK_LOADER_GELI=no
.warning "Please move from LOADER_NO_GELI_SUPPORT to WITHOUT_LOADER_GELI"
@@ -69,7 +68,6 @@ MK_LOADER_GELI=yes
CFLAGS+= -DLOADER_GELI_SUPPORT
CFLAGS+= -I${SASRC}/geli
.endif # MK_LOADER_GELI
-.endif # HAVE_GELI
# These should be confined to loader.mk, but can't because uboot/lib
# also uses it. It's part of loader, but isn't a loader so we can't
diff --git a/stand/efi/loader/bootinfo.c b/stand/efi/loader/bootinfo.c
index f432a3734dc9..4993c698e915 100644
--- a/stand/efi/loader/bootinfo.c
+++ b/stand/efi/loader/bootinfo.c
@@ -56,6 +56,10 @@ __FBSDID("$FreeBSD$");
#include <fdt_platform.h>
#endif
+#ifdef LOADER_GELI_SUPPORT
+#include "geliboot.h"
+#endif
+
int bi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp);
extern EFI_SYSTEM_TABLE *ST;
@@ -452,7 +456,9 @@ bi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp)
#endif
file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend);
file_addmetadata(kfp, MODINFOMD_FW_HANDLE, sizeof ST, &ST);
-
+#ifdef LOADER_GELI_SUPPORT
+ geli_export_key_metadata(kfp);
+#endif
bi_load_efi_data(kfp);
/* Figure out the size and location of the metadata. */
diff --git a/stand/i386/gptboot/Makefile b/stand/i386/gptboot/Makefile
index fde938f87405..c608bc3737d7 100644
--- a/stand/i386/gptboot/Makefile
+++ b/stand/i386/gptboot/Makefile
@@ -1,7 +1,5 @@
# $FreeBSD$
-HAVE_GELI= yes
-
.include <bsd.init.mk>
.PATH: ${BOOTSRC}/i386/boot2 ${BOOTSRC}/i386/common ${SASRC}
diff --git a/stand/i386/gptboot/gptboot.c b/stand/i386/gptboot/gptboot.c
index 9c2d9d160535..b057c9a29bfa 100644
--- a/stand/i386/gptboot/gptboot.c
+++ b/stand/i386/gptboot/gptboot.c
@@ -113,11 +113,20 @@ static int vdev_read(void *vdev __unused, void *priv, off_t off, void *buf,
#include "ufsread.c"
#include "gpt.c"
#ifdef LOADER_GELI_SUPPORT
-#include "geliboot.c"
+#include "geliboot.h"
static char gelipw[GELI_PW_MAXLEN];
static struct keybuf *gelibuf;
#endif
+struct gptdsk {
+ struct dsk dsk;
+#ifdef LOADER_GELI_SUPPORT
+ struct geli_dev *gdev;
+#endif
+};
+
+static struct gptdsk gdsk;
+
static inline int
xfsread(ufs_ino_t inode, void *buf, size_t nbyte)
{
@@ -225,19 +234,21 @@ static int
gptinit(void)
{
- if (gptread(&freebsd_ufs_uuid, &dsk, dmadat->secbuf) == -1) {
+ if (gptread(&freebsd_ufs_uuid, &gdsk.dsk, dmadat->secbuf) == -1) {
printf("%s: unable to load GPT\n", BOOTPROG);
return (-1);
}
- if (gptfind(&freebsd_ufs_uuid, &dsk, dsk.part) == -1) {
+ if (gptfind(&freebsd_ufs_uuid, &gdsk.dsk, gdsk.dsk.part) == -1) {
printf("%s: no UFS partition was found\n", BOOTPROG);
return (-1);
}
#ifdef LOADER_GELI_SUPPORT
- if (geli_taste(vdev_read, &dsk, (gpttable[curent].ent_lba_end -
- gpttable[curent].ent_lba_start)) == 0) {
- if (geli_havekey(&dsk) != 0 && geli_passphrase(gelipw,
- dsk.unit, 'p', curent + 1, &dsk) != 0) {
+ gdsk.gdev = geli_taste(vdev_read, &gdsk.dsk,
+ (gpttable[curent].ent_lba_end - gpttable[curent].ent_lba_start),
+ "disk%up%u:", gdsk.dsk.unit, curent + 1);
+ if (gdsk.gdev != NULL) {
+ if (geli_havekey(gdsk.gdev) != 0 &&
+ geli_passphrase(gdsk.gdev, gelipw) != 0) {
printf("%s: unable to decrypt GELI key\n", BOOTPROG);
return (-1);
}
@@ -273,21 +284,18 @@ main(void)
v86.ctl = V86_FLAGS;
v86.efl = PSL_RESERVED_DEFAULT | PSL_I;
- dsk.drive = *(uint8_t *)PTOV(ARGS);
- dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD;
- dsk.unit = dsk.drive & DRV_MASK;
- dsk.part = -1;
- dsk.start = 0;
+ gdsk.dsk.drive = *(uint8_t *)PTOV(ARGS);
+ gdsk.dsk.type = gdsk.dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD;
+ gdsk.dsk.unit = gdsk.dsk.drive & DRV_MASK;
+ gdsk.dsk.part = -1;
+ gdsk.dsk.start = 0;
bootinfo.bi_version = BOOTINFO_VERSION;
bootinfo.bi_size = sizeof(bootinfo);
bootinfo.bi_basemem = bios_basemem / 1024;
bootinfo.bi_extmem = bios_extmem / 1024;
bootinfo.bi_memsizes_valid++;
- bootinfo.bi_bios_dev = dsk.drive;
+ bootinfo.bi_bios_dev = gdsk.dsk.drive;
-#ifdef LOADER_GELI_SUPPORT
- geli_init();
-#endif
/* Process configuration file */
if (gptinit() != 0)
@@ -332,8 +340,8 @@ main(void)
load();
memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL));
load();
- gptbootfailed(&dsk);
- if (gptfind(&freebsd_ufs_uuid, &dsk, -1) == -1)
+ gptbootfailed(&gdsk.dsk);
+ if (gptfind(&freebsd_ufs_uuid, &gdsk.dsk, -1) == -1)
break;
dsk_meta = 0;
}
@@ -345,8 +353,8 @@ main(void)
printf("\nFreeBSD/x86 boot\n"
"Default: %u:%s(%up%u)%s\n"
"boot: ",
- dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit,
- dsk.part, kname);
+ gdsk.dsk.drive & DRV_MASK, dev_nm[gdsk.dsk.type],
+ gdsk.dsk.unit, gdsk.dsk.part, kname);
}
if (ioctrl & IO_SERIAL)
sio_flush();
@@ -392,10 +400,9 @@ load(void)
if (!(ino = lookup(kname))) {
if (!ls) {
printf("%s: No %s on %u:%s(%up%u)\n", BOOTPROG,
- kname, dsk.drive & DRV_MASK, dev_nm[dsk.type],
- dsk.unit,
- dsk.part);
- }
+ kname, gdsk.dsk.drive & DRV_MASK, dev_nm[gdsk.dsk.type],
+ gdsk.dsk.unit, gdsk.dsk.part);
+ }
return;
}
if (xfsread(ino, &hdr, sizeof(hdr)))
@@ -469,19 +476,19 @@ load(void)
}
bootinfo.bi_esymtab = VTOP(p);
bootinfo.bi_kernelname = VTOP(kname);
- bootinfo.bi_bios_dev = dsk.drive;
+ bootinfo.bi_bios_dev = gdsk.dsk.drive;
#ifdef LOADER_GELI_SUPPORT
geliargs.size = sizeof(geliargs);
explicit_bzero(gelipw, sizeof(gelipw));
gelibuf = malloc(sizeof(struct keybuf) +
(GELI_MAX_KEYS * sizeof(struct keybuf_ent)));
- geli_fill_keybuf(gelibuf);
+ geli_export_key_buffer(gelibuf);
geliargs.notapw = '\0';
geliargs.keybuf_sentinel = KEYBUF_SENTINEL;
geliargs.keybuf = gelibuf;
#endif
__exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
- MAKEBOOTDEV(dev_maj[dsk.type], dsk.part + 1, dsk.unit, 0xff),
+ MAKEBOOTDEV(dev_maj[gdsk.dsk.type], gdsk.dsk.part + 1, gdsk.dsk.unit, 0xff),
KARGS_FLAGS_EXTARG, 0, 0, VTOP(&bootinfo)
#ifdef LOADER_GELI_SUPPORT
, geliargs
@@ -595,12 +602,13 @@ dskread(void *buf, daddr_t lba, unsigned nblk)
{
int err;
- err = drvread(&dsk, buf, lba + dsk.start, nblk);
+ err = drvread(&gdsk.dsk, buf, lba + gdsk.dsk.start, nblk);
#ifdef LOADER_GELI_SUPPORT
- if (err == 0 && is_geli(&dsk) == 0) {
+ if (err == 0 && gdsk.gdev != NULL) {
/* Decrypt */
- if (geli_read(&dsk, lba * DEV_BSIZE, buf, nblk * DEV_BSIZE))
+ if (geli_read(gdsk.gdev, lba * DEV_BSIZE, buf,
+ nblk * DEV_BSIZE))
return (err);
}
#endif
@@ -610,8 +618,8 @@ dskread(void *buf, daddr_t lba, unsigned nblk)
#ifdef LOADER_GELI_SUPPORT
/*
- * Read function compartible with the ZFS callback, required to keep the GELI
- * Implementation the same for both UFS and ZFS
+ * Read function compatible with the ZFS callback, required to keep the GELI
+ * implementation the same for both UFS and ZFS.
*/
static int
vdev_read(void *vdev __unused, void *priv, off_t off, void *buf, size_t bytes)
@@ -619,22 +627,22 @@ vdev_read(void *vdev __unused, void *priv, off_t off, void *buf, size_t bytes)
char *p;
daddr_t lba;
unsigned int nb;
- struct dsk *dskp;
+ struct gptdsk *dskp;
- dskp = (struct dsk *)priv;
+ dskp = (struct gptdsk *)priv;
if ((off & (DEV_BSIZE - 1)) || (bytes & (DEV_BSIZE - 1)))
return (-1);
p = buf;
lba = off / DEV_BSIZE;
- lba += dskp->start;
+ lba += dskp->dsk.start;
while (bytes > 0) {
nb = bytes / DEV_BSIZE;
if (nb > VBLKSIZE / DEV_BSIZE)
nb = VBLKSIZE / DEV_BSIZE;
- if (drvread(dskp, dmadat->blkbuf, lba, nb))
+ if (drvread(&dskp->dsk, dmadat->blkbuf, lba, nb))
return (-1);
memcpy(p, dmadat->blkbuf, nb * DEV_BSIZE);
p += nb * DEV_BSIZE;
diff --git a/stand/i386/gptzfsboot/Makefile b/stand/i386/gptzfsboot/Makefile
index 4eb37722f4f4..9cc2bbae9753 100644
--- a/stand/i386/gptzfsboot/Makefile
+++ b/stand/i386/gptzfsboot/Makefile
@@ -1,7 +1,5 @@
# $FreeBSD$
-HAVE_GELI= yes
-
.include <bsd.init.mk>
.PATH: ${BOOTSRC}/i386/boot2 ${BOOTSRC}/i386/gptboot \
diff --git a/stand/i386/isoboot/Makefile b/stand/i386/isoboot/Makefile
index cc7e34fc6dee..76cddd3be8f7 100644
--- a/stand/i386/isoboot/Makefile
+++ b/stand/i386/isoboot/Makefile
@@ -1,7 +1,5 @@
# $FreeBSD$
-HAVE_GELI= yes
-
.include <bsd.init.mk>
.PATH: ${BOOTSRC}/i386/boot2 ${BOOTSRC}/i386/gptboot \
diff --git a/stand/i386/libi386/Makefile b/stand/i386/libi386/Makefile
index 381e458fbec4..d9529b93375f 100644
--- a/stand/i386/libi386/Makefile
+++ b/stand/i386/libi386/Makefile
@@ -1,7 +1,5 @@
# $FreeBSD$
-HAVE_GELI= yes
-
.include <bsd.init.mk>
LIB= i386
diff --git a/stand/i386/libi386/biosdisk.c b/stand/i386/libi386/biosdisk.c
index 4cf11e25ec2b..6a3670c18be8 100644
--- a/stand/i386/libi386/biosdisk.c
+++ b/stand/i386/libi386/biosdisk.c
@@ -51,31 +51,7 @@ __FBSDID("$FreeBSD$");
#include "libi386.h"
#ifdef LOADER_GELI_SUPPORT
-#include "cons.h"
-#include "drv.h"
-#include "gpt.h"
-#include "part.h"
-#include <uuid.h>
-struct pentry {
- struct ptable_entry part;
- uint64_t flags;
- union {
- uint8_t bsd;
- uint8_t mbr;
- uuid_t gpt;
- uint16_t vtoc8;
- } type;
- STAILQ_ENTRY(pentry) entry;
-};
-struct ptable {
- enum ptable_type type;
- uint16_t sectorsize;
- uint64_t sectors;
-
- STAILQ_HEAD(, pentry) entries;
-};
-
-#include "geliboot.c"
+#include "geliboot.h"
#endif /* LOADER_GELI_SUPPORT */
#define BIOS_NUMDRIVES 0x475
@@ -138,17 +114,6 @@ static int bd_close(struct open_file *f);
static int bd_ioctl(struct open_file *f, u_long cmd, void *data);
static int bd_print(int verbose);
-#ifdef LOADER_GELI_SUPPORT
-enum isgeli {
- ISGELI_UNKNOWN,
- ISGELI_NO,
- ISGELI_YES
-};
-static enum isgeli geli_status[MAXBDDEV][MAXTBLENTS];
-
-int bios_read(void *, void *, off_t off, void *buf, size_t bytes);
-#endif /* LOADER_GELI_SUPPORT */
-
struct devsw biosdisk = {
"disk",
DEVT_DISK,
@@ -195,9 +160,6 @@ bd_init(void)
{
int base, unit, nfd = 0;
-#ifdef LOADER_GELI_SUPPORT
- geli_init();
-#endif
/* sequence 0, 0x80 */
for (base = 0; base <= 0x80; base += 0x80) {
for (unit = base; (nbdinfo < MAXBDDEV); unit++) {
@@ -379,7 +341,7 @@ bd_print(int verbose)
static int
bd_open(struct open_file *f, ...)
{
- struct disk_devdesc *dev;
+ struct disk_devdesc *dev, rdev;
struct disk_devdesc disk;
int err, g_err;
va_list ap;
@@ -421,81 +383,6 @@ bd_open(struct open_file *f, ...)
err = disk_open(dev, BD(dev).bd_sectors * BD(dev).bd_sectorsize,
BD(dev).bd_sectorsize);
-#ifdef LOADER_GELI_SUPPORT
- static char gelipw[GELI_PW_MAXLEN];
- char *passphrase;
-
- if (err)
- return (err);
-
- /* if we already know there is no GELI, skip the rest */
- if (geli_status[dev->dd.d_unit][dev->d_slice] != ISGELI_UNKNOWN)
- return (err);
-
- struct dsk dskp;
- struct ptable *table = NULL;
- struct ptable_entry part;
- struct pentry *entry;
- int geli_part = 0;
-
- dskp.drive = bd_unit2bios(dev->dd.d_unit);
- dskp.type = dev->dd.d_dev->dv_type;
- dskp.unit = dev->dd.d_unit;
- dskp.slice = dev->d_slice;
- dskp.part = dev->d_partition;
- dskp.start = dev->d_offset;
-
- /* We need the LBA of the end of the partition */
- table = ptable_open(&disk, BD(dev).bd_sectors,
- BD(dev).bd_sectorsize, ptblread);
- if (table == NULL) {
- DEBUG("Can't read partition table");
- /* soft failure, return the exit status of disk_open */
- return (err);
- }
-
- if (table->type == PTABLE_GPT)
- dskp.part = 255;
-
- STAILQ_FOREACH(entry, &table->entries, entry) {
- dskp.slice = entry->part.index;
- dskp.start = entry->part.start;
- if (is_geli(&dskp) == 0) {
- geli_status[dev->dd.d_unit][dskp.slice] = ISGELI_YES;
- return (0);
- }
- if (geli_taste(bios_read, &dskp,
- entry->part.end - entry->part.start) == 0) {
- if (geli_havekey(&dskp) == 0) {
- geli_status[dev->dd.d_unit][dskp.slice] = ISGELI_YES;
- geli_part++;
- continue;
- }
- if ((passphrase = getenv("kern.geom.eli.passphrase"))
- != NULL) {
- /* Use the cached passphrase */
- bcopy(passphrase, &gelipw, GELI_PW_MAXLEN);
- }
- if (geli_passphrase(gelipw, dskp.unit, 'p',
- (dskp.slice > 0 ? dskp.slice : dskp.part),
- &dskp) == 0) {
- setenv("kern.geom.eli.passphrase", gelipw, 1);
- bzero(gelipw, sizeof(gelipw));
- geli_status[dev->dd.d_unit][dskp.slice] = ISGELI_YES;
- geli_part++;
- continue;
- }
- } else
- geli_status[dev->dd.d_unit][dskp.slice] = ISGELI_NO;
- }
-
- /* none of the partitions on this disk have GELI */
- if (geli_part == 0) {
- /* found no GELI */
- geli_status[dev->dd.d_unit][dev->d_slice] = ISGELI_NO;
- }
-#endif /* LOADER_GELI_SUPPORT */
-
return (err);
}
@@ -841,79 +728,6 @@ bd_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest, int write)
static int
bd_read(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest)
{
-#ifdef LOADER_GELI_SUPPORT
- struct dsk dskp;
- off_t p_off, diff;
- daddr_t alignlba;
- int err, n, alignblks;
- char *tmpbuf;
-
- /* if we already know there is no GELI, skip the rest */
- if (geli_status[dev->dd.d_unit][dev->d_slice] != ISGELI_YES)
- return (bd_io(dev, dblk, blks, dest, 0));
-
- if (geli_status[dev->dd.d_unit][dev->d_slice] == ISGELI_YES) {
- /*
- * Align reads to DEV_GELIBOOT_BSIZE bytes because partial
- * sectors cannot be decrypted. Round the requested LBA down to
- * nearest multiple of DEV_GELIBOOT_BSIZE bytes.
- */
- alignlba = rounddown2(dblk * BD(dev).bd_sectorsize,
- DEV_GELIBOOT_BSIZE) / BD(dev).bd_sectorsize;
- /*
- * Round number of blocks to read up to nearest multiple of
- * DEV_GELIBOOT_BSIZE
- */
- diff = (dblk - alignlba) * BD(dev).bd_sectorsize;
- alignblks = roundup2(blks * BD(dev).bd_sectorsize + diff,
- DEV_GELIBOOT_BSIZE) / BD(dev).bd_sectorsize;
-
- /*
- * If the read is rounded up to a larger size, use a temporary
- * buffer here because the buffer provided by the caller may be
- * too small.
- */
- if (diff == 0) {
- tmpbuf = dest;
- } else {
- tmpbuf = malloc(alignblks * BD(dev).bd_sectorsize);
- if (tmpbuf == NULL) {
- return (-1);
- }
- }
-
- if (alignlba + alignblks > BD(dev).bd_sectors) {
- DEBUG("Shorted read at %llu from %d to %llu blocks",
- alignlba, alignblks, BD(dev).bd_sectors - alignlba);
- alignblks = BD(dev).bd_sectors - alignlba;
- }
-
- err = bd_io(dev, alignlba, alignblks, tmpbuf, 0);
- if (err)
- return (err);
-
- dskp.drive = bd_unit2bios(dev->dd.d_unit);
- dskp.type = dev->dd.d_dev->dv_type;
- dskp.unit = dev->dd.d_unit;
- dskp.slice = dev->d_slice;
- dskp.part = dev->d_partition;
- dskp.start = dev->d_offset;
-
- /* GELI needs the offset relative to the partition start */
- p_off = alignlba - dskp.start;
-
- err = geli_read(&dskp, p_off * BD(dev).bd_sectorsize, (u_char *)tmpbuf,
- alignblks * BD(dev).bd_sectorsize);
- if (err)
- return (err);
-
- if (tmpbuf != dest) {
- bcopy(tmpbuf + diff, dest, blks * BD(dev).bd_sectorsize);
- free(tmpbuf);
- }
- return (0);
- }
-#endif /* LOADER_GELI_SUPPORT */
return (bd_io(dev, dblk, blks, dest, 0));
}
@@ -1009,25 +823,3 @@ bd_getdev(struct i386_devdesc *d)
DEBUG("dev is 0x%x\n", rootdev);
return(rootdev);
}
-
-#ifdef LOADER_GELI_SUPPORT
-int
-bios_read(void *vdev __unused, void *xpriv, off_t off, void *buf, size_t bytes)
-{
- struct disk_devdesc dev;
- struct dsk *priv = xpriv;
-
- dev.dd.d_dev = &biosdisk;
- dev.dd.d_unit = priv->unit;
- dev.d_slice = priv->slice;
- dev.d_partition = priv->part;
- dev.d_offset = priv->start;
-
- off = off / BD(&dev).bd_sectorsize;
- /* GELI gives us the offset relative to the partition start */
- off += dev.d_offset;
- bytes = bytes / BD(&dev).bd_sectorsize;
-
- return (bd_io(&dev, off, bytes, buf, 0));
-}
-#endif /* LOADER_GELI_SUPPORT */
diff --git a/stand/i386/libi386/bootinfo32.c b/stand/i386/libi386/bootinfo32.c
index c1851441ca8c..58881b705f9e 100644
--- a/stand/i386/libi386/bootinfo32.c
+++ b/stand/i386/libi386/bootinfo32.c
@@ -39,9 +39,6 @@ __FBSDID("$FreeBSD$");
#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;
@@ -154,10 +151,6 @@ bi_load32(char *args, int *howtop, int *bootdevp, vm_offset_t *bip, vm_offset_t
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);
@@ -235,9 +228,7 @@ bi_load32(char *args, int *howtop, int *bootdevp, vm_offset_t *bip, vm_offset_t
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));
+ geli_export_key_metadata(kfp);
#endif
/* Figure out the size and location of the metadata */
diff --git a/stand/i386/libi386/bootinfo64.c b/stand/i386/libi386/bootinfo64.c
index 80bb8357a113..c9e61cac7414 100644
--- a/stand/i386/libi386/bootinfo64.c
+++ b/stand/i386/libi386/bootinfo64.c
@@ -42,9 +42,6 @@ __FBSDID("$FreeBSD$");
#ifdef LOADER_GELI_SUPPORT
#include "geliboot.h"
-
-static const size_t keybuf_size = sizeof(struct keybuf) +
- (GELI_MAX_KEYS * sizeof(struct keybuf_ent));
#endif
/*
@@ -196,10 +193,6 @@ 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];
- struct keybuf *keybuf = (struct keybuf *)buf;
-#endif
if (!bi_checkcpu()) {
printf("CPU doesn't support long mode\n");
@@ -248,11 +241,8 @@ bi_load64(char *args, vm_offset_t addr, vm_offset_t *modulep,
file_addmetadata(kfp, MODINFOMD_MODULEP, sizeof module, &module);
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));
+ geli_export_key_metadata(kfp);
#endif
size = bi_copymodules64(0);
diff --git a/stand/i386/loader/Makefile b/stand/i386/loader/Makefile
index 3878622f9d6f..6b86bdd08221 100644
--- a/stand/i386/loader/Makefile
+++ b/stand/i386/loader/Makefile
@@ -1,7 +1,5 @@
# $FreeBSD$
-HAVE_GELI= yes
-
LOADER_NET_SUPPORT?= yes
LOADER_NFS_SUPPORT?= yes
LOADER_TFTP_SUPPORT?= yes
diff --git a/stand/i386/loader/main.c b/stand/i386/loader/main.c
index 6cb8e2585783..bb5b012079e0 100644
--- a/stand/i386/loader/main.c
+++ b/stand/i386/loader/main.c
@@ -175,7 +175,7 @@ main(void)
if (zargs != NULL && zargs->size >= offsetof(struct zfs_boot_args, gelipw)) {
if (zargs->size >= offsetof(struct zfs_boot_args, keybuf_sentinel) &&
zargs->keybuf_sentinel == KEYBUF_SENTINEL) {
- geli_save_keybuf(zargs->keybuf);
+ geli_import_key_buffer(zargs->keybuf);
}
if (zargs->gelipw[0] != '\0') {
setenv("kern.geom.eli.passphrase", zargs->gelipw, 1);
@@ -190,7 +190,7 @@ main(void)
gargs = (struct geli_boot_args *)(kargs + 1);
if (gargs != NULL && gargs->size >= offsetof(struct geli_boot_args, gelipw)) {
if (gargs->keybuf_sentinel == KEYBUF_SENTINEL) {
- geli_save_keybuf(gargs->keybuf);
+ geli_import_key_buffer(gargs->keybuf);
}
if (gargs->gelipw[0] != '\0') {
setenv("kern.geom.eli.passphrase", gargs->gelipw, 1);
diff --git a/stand/i386/zfsboot/Makefile b/stand/i386/zfsboot/Makefile
index 20e9737bac43..47ad398d886c 100644
--- a/stand/i386/zfsboot/Makefile
+++ b/stand/i386/zfsboot/Makefile
@@ -1,7 +1,5 @@
# $FreeBSD$
-HAVE_GELI=yes
-
.include <bsd.init.mk>
.PATH: ${BOOTSRC}/i386/boot2 ${BOOTSRC}/i386/common ${SASRC}
diff --git a/stand/i386/zfsboot/zfsboot.c b/stand/i386/zfsboot/zfsboot.c
index 8f67121e6ce2..90e4066ab5ed 100644
--- a/stand/i386/zfsboot/zfsboot.c
+++ b/stand/i386/zfsboot/zfsboot.c
@@ -127,11 +127,18 @@ static void bios_getmem(void);
int main(void);
#ifdef LOADER_GELI_SUPPORT
-#include "geliboot.c"
+#include "geliboot.h"
static char gelipw[GELI_PW_MAXLEN];
static struct keybuf *gelibuf;
#endif
+struct zfsdsk {
+ struct dsk dsk;
+#ifdef LOADER_GELI_SUPPORT
+ struct geli_dev *gdev;
+#endif
+};
+
#include "zfsimpl.c"
/*
@@ -174,14 +181,14 @@ vdev_read(void *xvdev, void *priv, off_t off, void *buf, size_t bytes)
daddr_t lba, alignlba;
off_t diff;
unsigned int nb, alignnb;
- struct dsk *dsk = (struct dsk *) priv;
+ struct zfsdsk *zdsk = (struct zfsdsk *) priv;
if ((off & (DEV_BSIZE - 1)) || (bytes & (DEV_BSIZE - 1)))
return -1;
p = buf;
lba = off / DEV_BSIZE;
- lba += dsk->start;
+ lba += zdsk->dsk.start;
/*
* Align reads to 4k else 4k sector GELIs will not decrypt.
* Round LBA down to nearest multiple of DEV_GELIBOOT_BSIZE bytes.
@@ -191,7 +198,7 @@ vdev_read(void *xvdev, void *priv, off_t off, void *buf, size_t bytes)
* The read must be aligned to DEV_GELIBOOT_BSIZE bytes relative to the
* start of the GELI partition, not the start of the actual disk.
*/
- alignlba += dsk->start;
+ alignlba += zdsk->dsk.start;
diff = (lba - alignlba) * DEV_BSIZE;
while (bytes > 0) {
@@ -209,18 +216,20 @@ vdev_read(void *xvdev, void *priv, off_t off, void *buf, size_t bytes)
alignnb = roundup2(nb * DEV_BSIZE + diff, DEV_GELIBOOT_BSIZE)
/ DEV_BSIZE;
- if (dsk->size > 0 && alignlba + alignnb > dsk->size + dsk->start) {
- printf("Shortening read at %lld from %d to %lld\n", alignlba,
- alignnb, (dsk->size + dsk->start) - alignlba);
- alignnb = (dsk->size + dsk->start) - alignlba;
+ if (zdsk->dsk.size > 0 && alignlba + alignnb >
+ zdsk->dsk.size + zdsk->dsk.start) {
+ printf("Shortening read at %lld from %d to %lld\n",
+ alignlba, alignnb,
+ (zdsk->dsk.size + zdsk->dsk.start) - alignlba);
+ alignnb = (zdsk->dsk.size + zdsk->dsk.start) - alignlba;
}
- if (drvread(dsk, dmadat->rdbuf, alignlba, alignnb))
+ if (drvread(&zdsk->dsk, dmadat->rdbuf, alignlba, alignnb))
return -1;
#ifdef LOADER_GELI_SUPPORT
/* decrypt */
- if (is_geli(dsk) == 0) {
- if (geli_read(dsk, ((alignlba - dsk->start) *
+ if (zdsk->gdev != NULL) {
+ if (geli_read(zdsk->gdev, ((alignlba - zdsk->dsk.start) *
DEV_BSIZE), dmadat->rdbuf, alignnb * DEV_BSIZE))
return (-1);
}
@@ -250,20 +259,20 @@ vdev_write(vdev_t *vdev, void *priv, off_t off, void *buf, size_t bytes)
char *p;
daddr_t lba;
unsigned int nb;
- struct dsk *dsk = (struct dsk *) priv;
+ struct zfsdsk *zdsk = (struct zfsdsk *) priv;
if ((off & (DEV_BSIZE - 1)) || (bytes & (DEV_BSIZE - 1)))
return -1;
p = buf;
lba = off / DEV_BSIZE;
- lba += dsk->start;
+ lba += zdsk->dsk.start;
while (bytes > 0) {
nb = bytes / DEV_BSIZE;
if (nb > READ_BUF_SIZE / DEV_BSIZE)
nb = READ_BUF_SIZE / DEV_BSIZE;
memcpy(dmadat->rdbuf, p, nb * DEV_BSIZE);
- if (drvwrite(dsk, dmadat->rdbuf, lba, nb))
+ if (drvwrite(&zdsk->dsk, dmadat->rdbuf, lba, nb))
return -1;
p += nb * DEV_BSIZE;
lba += nb;
@@ -441,13 +450,13 @@ int13probe(int drive)
* We call this when we find a ZFS vdev - ZFS consumes the dsk
* structure so we must make a new one.
*/
-static struct dsk *
-copy_dsk(struct dsk *dsk)
+static struct zfsdsk *
+copy_dsk(struct zfsdsk *zdsk)
{
- struct dsk *newdsk;
+ struct zfsdsk *newdsk;
- newdsk = malloc(sizeof(struct dsk));
- *newdsk = *dsk;
+ newdsk = malloc(sizeof(struct zfsdsk));
+ *newdsk = *zdsk;
return (newdsk);
}
@@ -459,11 +468,14 @@ copy_dsk(struct dsk *dsk)
* with boot2 and we can not afford to grow that code.
*/
static uint64_t
-drvsize_ext(struct dsk *dskp)
+drvsize_ext(struct zfsdsk *zdsk)
{
+ struct dsk *dskp;
uint64_t size, tmp;
int cyl, hds, sec;
+ dskp = &zdsk->dsk;
+
v86.ctl = V86_FLAGS;
v86.addr = 0x13;
v86.eax = 0x800;
@@ -508,17 +520,17 @@ drvsize_ext(struct dsk *dskp)
uint64_t
ldi_get_size(void *priv)
{
- struct dsk *dskp = priv;
- uint64_t size = dskp->size;
+ struct zfsdsk *zdsk = priv;
+ uint64_t size = zdsk->dsk.size;
- if (dskp->start == 0)
- size = drvsize_ext(dskp);
+ if (zdsk->dsk.start == 0)
+ size = drvsize_ext(zdsk);
return (size * DEV_BSIZE);
}
static void
-probe_drive(struct dsk *dsk)
+probe_drive(struct zfsdsk *zdsk)
{
#ifdef GPT
struct gpt_hdr hdr;
@@ -537,7 +549,7 @@ probe_drive(struct dsk *dsk)
/*
* If we find a vdev on the whole disk, stop here.
*/
- if (vdev_probe(vdev_read2, dsk, NULL) == 0)
+ if (vdev_probe(vdev_read2, zdsk, NULL) == 0)
return;
#ifdef LOADER_GELI_SUPPORT
@@ -547,14 +559,15 @@ probe_drive(struct dsk *dsk)
* out the partition table and probe each slice/partition
* in turn for a vdev or GELI encrypted vdev.
*/
- elba = drvsize_ext(dsk);
+ elba = drvsize_ext(zdsk);
if (elba > 0) {
elba--;
}
- if (geli_taste(vdev_read, dsk, elba) == 0) {
- if (geli_havekey(dsk) == 0 || geli_passphrase(gelipw, dsk->unit,
- ':', 0, dsk) == 0) {
- if (vdev_probe(vdev_read2, dsk, NULL) == 0) {
+ zdsk->gdev = geli_taste(vdev_read, zdsk, elba, "disk%u:0:");
+ if (zdsk->gdev != NULL) {
+ if (geli_havekey(zdsk->gdev) == 0 ||
+ geli_passphrase(zdsk->gdev, gelipw) == 0) {
+ if (vdev_probe(vdev_read2, zdsk, NULL) == 0) {
return;
}
}
@@ -562,13 +575,13 @@ probe_drive(struct dsk *dsk)
#endif /* LOADER_GELI_SUPPORT */
sec = dmadat->secbuf;
- dsk->start = 0;
+ zdsk->dsk.start = 0;
#ifdef GPT
/*
* First check for GPT.
*/
- if (drvread(dsk, sec, 1, 1)) {
+ if (drvread(&zdsk->dsk, sec, 1, 1)) {
return;
}
memcpy(&hdr, sec, sizeof(hdr));
@@ -590,38 +603,39 @@ probe_drive(struct dsk *dsk)
slba = hdr.hdr_lba_table;
elba = slba + hdr.hdr_entries / entries_per_sec;
while (slba < elba) {
- dsk->start = 0;
- if (drvread(dsk, sec, slba, 1))
+ zdsk->dsk.start = 0;
+ if (drvread(&zdsk->dsk, sec, slba, 1))
return;
for (part = 0; part < entries_per_sec; part++) {
ent = (struct gpt_ent *)(sec + part * hdr.hdr_entsz);
if (memcmp(&ent->ent_type, &freebsd_zfs_uuid,
sizeof(uuid_t)) == 0) {
- dsk->start = ent->ent_lba_start;
- dsk->size = ent->ent_lba_end - ent->ent_lba_start + 1;
- dsk->slice = part + 1;
- dsk->part = 255;
- if (vdev_probe(vdev_read2, dsk, NULL) == 0) {
+ zdsk->dsk.start = ent->ent_lba_start;
+ zdsk->dsk.size = ent->ent_lba_end - ent->ent_lba_start + 1;
+ zdsk->dsk.slice = part + 1;
+ zdsk->dsk.part = 255;
+ if (vdev_probe(vdev_read2, zdsk, NULL) == 0) {
/*
* This slice had a vdev. We need a new dsk
* structure now since the vdev now owns this one.
*/
- dsk = copy_dsk(dsk);
+ zdsk = copy_dsk(zdsk);
}
#ifdef LOADER_GELI_SUPPORT
- else if (geli_taste(vdev_read, dsk, ent->ent_lba_end -
- ent->ent_lba_start) == 0) {
- if (geli_havekey(dsk) == 0 || geli_passphrase(gelipw,
- dsk->unit, 'p', dsk->slice, dsk) == 0) {
+ else if ((zdsk->gdev = geli_taste(vdev_read, zdsk,
+ ent->ent_lba_end - ent->ent_lba_start, "disk%up%u:",
+ zdsk->dsk.unit, zdsk->dsk.slice)) != NULL) {
+ if (geli_havekey(zdsk->gdev) == 0 ||
+ geli_passphrase(zdsk->gdev, gelipw) == 0) {
/*
* This slice has GELI, check it for ZFS.
*/
- if (vdev_probe(vdev_read2, dsk, NULL) == 0) {
+ if (vdev_probe(vdev_read2, zdsk, NULL) == 0) {
/*
* This slice had a vdev. We need a new dsk
* structure now since the vdev now owns this one.
*/
- dsk = copy_dsk(dsk);
+ zdsk = copy_dsk(zdsk);
}
break;
}
@@ -635,33 +649,33 @@ probe_drive(struct dsk *dsk)
trymbr:
#endif /* GPT */
- if (drvread(dsk, sec, DOSBBSECTOR, 1))
+ if (drvread(&zdsk->dsk, sec, DOSBBSECTOR, 1))
return;
dp = (void *)(sec + DOSPARTOFF);
for (i = 0; i < NDOSPART; i++) {
if (!dp[i].dp_typ)
continue;
- dsk->start = dp[i].dp_start;
- dsk->size = dp[i].dp_size;
- dsk->slice = i + 1;
- if (vdev_probe(vdev_read2, dsk, NULL) == 0) {
- dsk = copy_dsk(dsk);
+ zdsk->dsk.start = dp[i].dp_start;
+ zdsk->dsk.size = dp[i].dp_size;
+ zdsk->dsk.slice = i + 1;
+ if (vdev_probe(vdev_read2, zdsk, NULL) == 0) {
+ zdsk = copy_dsk(zdsk);
}
#ifdef LOADER_GELI_SUPPORT
- else if (geli_taste(vdev_read, dsk, dp[i].dp_size -
- dp[i].dp_start) == 0) {
- if (geli_havekey(dsk) == 0 || geli_passphrase(gelipw, dsk->unit,
- 's', i, dsk) == 0) {
+ else if ((zdsk->gdev = geli_taste(vdev_read, zdsk, dp[i].dp_size -
+ dp[i].dp_start, "disk%us%u:")) != NULL) {
+ if (geli_havekey(zdsk->gdev) == 0 ||
+ geli_passphrase(zdsk->gdev, gelipw) == 0) {
/*
* This slice has GELI, check it for ZFS.
*/
- if (vdev_probe(vdev_read2, dsk, NULL) == 0) {
+ if (vdev_probe(vdev_read2, zdsk, NULL) == 0) {
/*
* This slice had a vdev. We need a new dsk
* structure now since the vdev now owns this one.
*/
- dsk = copy_dsk(dsk);
+ zdsk = copy_dsk(zdsk);
}
break;
}
@@ -675,7 +689,7 @@ main(void)
{
dnode_phys_t dn;
off_t off;
- struct dsk *dsk;
+ struct zfsdsk *zdsk;
int autoboot, i;
int nextboot;
int rc;
@@ -693,39 +707,37 @@ main(void)
}
setheap(heap_next, heap_end);
- dsk = malloc(sizeof(struct dsk));
- dsk->drive = *(uint8_t *)PTOV(ARGS);
- dsk->type = dsk->drive & DRV_HARD ? TYPE_AD : TYPE_FD;
- dsk->unit = dsk->drive & DRV_MASK;
- dsk->slice = *(uint8_t *)PTOV(ARGS + 1) + 1;
- dsk->part = 0;
- dsk->start = 0;
- dsk->size = drvsize_ext(dsk);
+ zdsk = malloc(sizeof(struct zfsdsk));
+ zdsk->gdev = NULL;
+ zdsk->dsk.drive = *(uint8_t *)PTOV(ARGS);
+ zdsk->dsk.type = zdsk->dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD;
+ zdsk->dsk.unit = zdsk->dsk.drive & DRV_MASK;
+ zdsk->dsk.slice = *(uint8_t *)PTOV(ARGS + 1) + 1;
+ zdsk->dsk.part = 0;
+ zdsk->dsk.start = 0;
+ zdsk->dsk.size = drvsize_ext(zdsk);
bootinfo.bi_version = BOOTINFO_VERSION;
bootinfo.bi_size = sizeof(bootinfo);
bootinfo.bi_basemem = bios_basemem / 1024;
bootinfo.bi_extmem = bios_extmem / 1024;
bootinfo.bi_memsizes_valid++;
- bootinfo.bi_bios_dev = dsk->drive;
+ bootinfo.bi_bios_dev = zdsk->dsk.drive;
- bootdev = MAKEBOOTDEV(dev_maj[dsk->type],
- dsk->slice, dsk->unit, dsk->part);
+ bootdev = MAKEBOOTDEV(dev_maj[zdsk->dsk.type],
+ zdsk->dsk.slice, zdsk->dsk.unit, zdsk->dsk.part);
/* Process configuration file */
autoboot = 1;
-#ifdef LOADER_GELI_SUPPORT
- geli_init();
-#endif
zfs_init();
/*
* Probe the boot drive first - we will try to boot from whatever
* pool we find on that drive.
*/
- probe_drive(dsk);
+ probe_drive(zdsk);
/*
* Probe the rest of the drives that the bios knows about. This
@@ -744,15 +756,15 @@ main(void)
if (!int13probe(i | DRV_HARD))
break;
- dsk = malloc(sizeof(struct dsk));
- dsk->drive = i | DRV_HARD;
- dsk->type = dsk->drive & TYPE_AD;
- dsk->unit = i;
- dsk->slice = 0;
- dsk->part = 0;
- dsk->start = 0;
- dsk->size = drvsize_ext(dsk);
- probe_drive(dsk);
+ zdsk = malloc(sizeof(struct zfsdsk));
+ zdsk->dsk.drive = i | DRV_HARD;
+ zdsk->dsk.type = zdsk->dsk.drive & TYPE_AD;
+ zdsk->dsk.unit = i;
+ zdsk->dsk.slice = 0;
+ zdsk->dsk.part = 0;
+ zdsk->dsk.start = 0;
+ zdsk->dsk.size = drvsize_ext(zdsk);
+ probe_drive(zdsk);
}
/*
@@ -983,7 +995,7 @@ load(void)
#ifdef LOADER_GELI_SUPPORT
explicit_bzero(gelipw, sizeof(gelipw));
gelibuf = malloc(sizeof(struct keybuf) + (GELI_MAX_KEYS * sizeof(struct keybuf_ent)));
- geli_fill_keybuf(gelibuf);
+ geli_export_key_buffer(gelibuf);
zfsargs.notapw = '\0';
zfsargs.keybuf_sentinel = KEYBUF_SENTINEL;
zfsargs.keybuf = gelibuf;
diff --git a/stand/libsa/geli/Makefile.inc b/stand/libsa/geli/Makefile.inc
index 610590a0031f..734bcf6be5e6 100644
--- a/stand/libsa/geli/Makefile.inc
+++ b/stand/libsa/geli/Makefile.inc
@@ -27,7 +27,15 @@ SRCS+= ${i}
# local GELI Implementation
.PATH: ${SYSDIR}/geom/eli
-SRCS+= geliboot_crypto.c g_eli_hmac.c g_eli_key.c g_eli_key_cache.c pkcs5v2.c
+SRCS+= \
+ geliboot.c \
+ geliboot_crypto.c \
+ gelidev.c \
+ geli_metadata.c \
+ g_eli_hmac.c \
+ g_eli_key.c \
+ g_eli_key_cache.c \
+ pkcs5v2.c \
# aes
.PATH: ${SYSDIR}/opencrypto
diff --git a/stand/libsa/geli/geli_metadata.c b/stand/libsa/geli/geli_metadata.c
new file mode 100644
index 000000000000..ba46058f688f
--- /dev/null
+++ b/stand/libsa/geli/geli_metadata.c
@@ -0,0 +1,52 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2018 Ian Lepore <ian@FreeBSD.org>
+ *
+ * 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 AUTHORS 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 AUTHORS 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 <stand.h>
+#include <bootstrap.h>
+#include <sys/param.h>
+#include <sys/linker.h>
+#include "geliboot.h"
+
+/*
+ * Export a keybuf as metadata attached to a kernel module. This is separate
+ * from the lower-level key management functions to avoid creating a linker
+ * dependency on the libsa metadata routines when the geli code is linked into
+ * early-stage bootloaders such as gptboot. Only loader(8) variants call this.
+ */
+void
+geli_export_key_metadata(struct preloaded_file *kfp)
+{
+ struct keybuf *keybuf;
+
+ keybuf = malloc(GELI_KEYBUF_SIZE);
+ geli_export_key_buffer(keybuf);
+ file_addmetadata(kfp, MODINFOMD_KEYBUF, GELI_KEYBUF_SIZE, keybuf);
+ explicit_bzero(keybuf, GELI_KEYBUF_SIZE);
+ free(keybuf);
+}
diff --git a/stand/libsa/geli/geliboot.c b/stand/libsa/geli/geliboot.c
index 9ae8fcaab775..00b9af93573a 100644
--- a/stand/libsa/geli/geliboot.c
+++ b/stand/libsa/geli/geliboot.c
@@ -27,13 +27,19 @@
* $FreeBSD$
*/
-#include "geliboot_internal.h"
+#include <stand.h>
+#include <stdarg.h>
#include "geliboot.h"
+#include "geliboot_internal.h"
-SLIST_HEAD(geli_list, geli_entry) geli_head = SLIST_HEAD_INITIALIZER(geli_head);
-struct geli_list *geli_headp;
+struct known_dev {
+ char name[GELIDEV_NAMELEN];
+ struct geli_dev *gdev;
+ SLIST_ENTRY(known_dev) entries;
+};
-typedef u_char geli_ukey[G_ELI_USERKEYLEN];
+SLIST_HEAD(known_dev_list, known_dev) known_devs_head =
+ SLIST_HEAD_INITIALIZER(known_devs_head);
static geli_ukey saved_keys[GELI_MAX_KEYS];
static unsigned int nsaved_keys = 0;
@@ -43,7 +49,7 @@ static unsigned int nsaved_keys = 0;
* Destroy the local storage when finished.
*/
void
-geli_fill_keybuf(struct keybuf *fkeybuf)
+geli_export_key_buffer(struct keybuf *fkeybuf)
{
unsigned int i;
@@ -61,7 +67,7 @@ geli_fill_keybuf(struct keybuf *fkeybuf)
* Zero out the keybuf.
*/
void
-geli_save_keybuf(struct keybuf *skeybuf)
+geli_import_key_buffer(struct keybuf *skeybuf)
{
unsigned int i;
@@ -76,8 +82,8 @@ geli_save_keybuf(struct keybuf *skeybuf)
skeybuf->kb_nents = 0;
}
-static void
-save_key(geli_ukey key)
+void
+geli_add_key(geli_ukey key)
{
/*
@@ -91,46 +97,22 @@ save_key(geli_ukey key)
}
static int
-geli_same_device(struct geli_entry *ge, struct dsk *dskp)
-{
-
- if (ge->dsk->drive == dskp->drive &&
- dskp->part == 255 && ge->dsk->part == dskp->slice) {
- /*
- * Sometimes slice = slice, and sometimes part = slice
- * If the incoming struct dsk has part=255, it means look at
- * the slice instead of the part number
- */
- return (0);
- }
-
- /* Is this the same device? */
- if (ge->dsk->drive != dskp->drive ||
- ge->dsk->slice != dskp->slice ||
- ge->dsk->part != dskp->part) {
- return (1);
- }
-
- return (0);
-}
-
-static int
-geli_findkey(struct geli_entry *ge, struct dsk *dskp, u_char *mkey)
+geli_findkey(struct geli_dev *gdev, u_char *mkey)
{
u_int keynum;
int i;
- if (ge->keybuf_slot >= 0) {
- if (g_eli_mkey_decrypt_any(&ge->md, saved_keys[ge->keybuf_slot],
+ if (gdev->keybuf_slot >= 0) {
+ if (g_eli_mkey_decrypt_any(&gdev->md, saved_keys[gdev->keybuf_slot],
mkey, &keynum) == 0) {
return (0);
}
}
for (i = 0; i < nsaved_keys; i++) {
- if (g_eli_mkey_decrypt_any(&ge->md, saved_keys[i], mkey,
+ if (g_eli_mkey_decrypt_any(&gdev->md, saved_keys[i], mkey,
&keynum) == 0) {
- ge->keybuf_slot = i;
+ gdev->keybuf_slot = i;
return (0);
}
}
@@ -138,37 +120,59 @@ geli_findkey(struct geli_entry *ge, struct dsk *dskp, u_char *mkey)
return (1);
}
-void
-geli_init(void)
-{
-
- geli_count = 0;
- SLIST_INIT(&geli_head);
-}
-
/*
- * Read the last sector of the drive or partition pointed to by dsk and see
- * if it is GELI encrypted
+ * Read the last sector of a drive or partition and see if it is GELI encrypted.
*/
-int
-geli_taste(int read_func(void *vdev, void *priv, off_t off, void *buf,
- size_t bytes), struct dsk *dskp, daddr_t lastsector)
+struct geli_dev *
+geli_taste(geli_readfunc readfunc, void *readpriv, daddr_t lastsector,
+ const char *namefmt, ...)
{
+ va_list args;
struct g_eli_metadata md;
- u_char buf[DEV_GELIBOOT_BSIZE];
+ struct known_dev *kdev;
+ struct geli_dev *gdev;
+ u_char *buf;
+ char devname[GELIDEV_NAMELEN];
int error;
off_t alignsector;
+ /*
+ * Format the name into a temp buffer and use that to search for an
+ * existing known_dev instance. If not found, this has the side effect
+ * of initializing kdev to NULL.
+ */
+ va_start(args, namefmt);
+ vsnprintf(devname, sizeof(devname), namefmt, args);
+ va_end(args);
+ SLIST_FOREACH(kdev, &known_devs_head, entries) {
+ if (strcmp(kdev->name, devname) == 0)
+ return (kdev->gdev);
+ }
+
+ /* Determine whether the new device is geli-encrypted... */
+ if ((buf = malloc(DEV_GELIBOOT_BSIZE)) == NULL)
+ goto out;
alignsector = rounddown2(lastsector * DEV_BSIZE, DEV_GELIBOOT_BSIZE);
if (alignsector + DEV_GELIBOOT_BSIZE > ((lastsector + 1) * DEV_BSIZE)) {
/* Don't read past the end of the disk */
- alignsector = (lastsector * DEV_BSIZE) + DEV_BSIZE
- - DEV_GELIBOOT_BSIZE;
+ alignsector = (lastsector * DEV_BSIZE) + DEV_BSIZE -
+ DEV_GELIBOOT_BSIZE;
}
- error = read_func(NULL, dskp, alignsector, &buf, DEV_GELIBOOT_BSIZE);
+ error = readfunc(NULL, readpriv, alignsector, buf, DEV_GELIBOOT_BSIZE);
if (error != 0) {
- return (error);
+ goto out;
}
+
+ /*
+ * We have a new known_device. Whether it's geli-encrypted or not,
+ * record its existance so we can avoid doing IO to probe it next time.
+ */
+ if ((kdev = malloc(sizeof(*kdev))) == NULL)
+ goto out;
+ strlcpy(kdev->name, devname, sizeof(kdev->name));
+ kdev->gdev = NULL;
+ SLIST_INSERT_HEAD(&known_devs_head, kdev, entries);
+
/* Extract the last 4k sector of the disk. */
error = eli_metadata_decode(buf, &md);
if (error != 0) {
@@ -176,53 +180,46 @@ geli_taste(int read_func(void *vdev, void *priv, off_t off, void *buf,
error = eli_metadata_decode(buf +
(DEV_GELIBOOT_BSIZE - DEV_BSIZE), &md);
if (error != 0) {
- return (error);
+ goto out;
}
}
if (!(md.md_flags & G_ELI_FLAG_GELIBOOT)) {
/* The GELIBOOT feature is not activated */
- return (1);
+ goto out;
}
if ((md.md_flags & G_ELI_FLAG_ONETIME)) {
/* Swap device, skip it. */
- return (1);
+ goto out;
}
- if (md.md_iterations < 0) {
- /* XXX TODO: Support loading key files. */
- /* Disk does not have a passphrase, skip it. */
- return (1);
- }
- geli_e = malloc(sizeof(struct geli_entry));
- if (geli_e == NULL)
- return (2);
-
- geli_e->dsk = malloc(sizeof(struct dsk));
- if (geli_e->dsk == NULL)
- return (2);
- memcpy(geli_e->dsk, dskp, sizeof(struct dsk));
- geli_e->part_end = lastsector;
- if (dskp->part == 255) {
- geli_e->dsk->part = dskp->slice;
- }
- geli_e->keybuf_slot = -1;
- geli_e->md = md;
- eli_metadata_softc(&geli_e->sc, &md, DEV_BSIZE,
+ /*
+ * It's geli-encrypted, create a geli_dev for it and link it into the
+ * known_dev instance.
+ */
+ gdev = malloc(sizeof(struct geli_dev));
+ if (gdev == NULL)
+ goto out;
+ gdev->part_end = lastsector;
+ gdev->keybuf_slot = -1;
+ gdev->md = md;
+ gdev->name = kdev->name;
+ eli_metadata_softc(&gdev->sc, &md, DEV_BSIZE,
(lastsector + DEV_BSIZE) * DEV_BSIZE);
-
- SLIST_INSERT_HEAD(&geli_head, geli_e, entries);
- geli_count++;
-
- return (0);
+ kdev->gdev = gdev;
+out:
+ free(buf);
+ if (kdev == NULL)
+ return (NULL);
+ return (kdev->gdev);
}
/*
- * Attempt to decrypt the device
+ * Attempt to decrypt the device. This will try existing keys first, then will
+ * prompt for a passphrase if there are no existing keys that work.
*/
static int
-geli_attach(struct geli_entry *ge, struct dsk *dskp, const char *passphrase,
- u_char *mkeyp)
+geli_probe(struct geli_dev *gdev, const char *passphrase, u_char *mkeyp)
{
u_char key[G_ELI_USERKEYLEN], mkey[G_ELI_DATAIVKEYLEN], *mkp;
u_int keynum;
@@ -232,9 +229,10 @@ geli_attach(struct geli_entry *ge, struct dsk *dskp, const char *passphrase,
if (mkeyp != NULL) {
memcpy(&mkey, mkeyp, G_ELI_DATAIVKEYLEN);
explicit_bzero(mkeyp, G_ELI_DATAIVKEYLEN);
+ goto found_key;
}
- if (mkeyp != NULL || geli_findkey(ge, dskp, mkey) == 0) {
+ if (geli_findkey(gdev, mkey) == 0) {
goto found_key;
}
@@ -242,31 +240,29 @@ geli_attach(struct geli_entry *ge, struct dsk *dskp, const char *passphrase,
/*
* Prepare Derived-Key from the user passphrase.
*/
- if (geli_e->md.md_iterations < 0) {
+ if (gdev->md.md_iterations < 0) {
/* XXX TODO: Support loading key files. */
return (1);
- } else if (geli_e->md.md_iterations == 0) {
- g_eli_crypto_hmac_update(&ctx, geli_e->md.md_salt,
- sizeof(geli_e->md.md_salt));
+ } else if (gdev->md.md_iterations == 0) {
+ g_eli_crypto_hmac_update(&ctx, gdev->md.md_salt,
+ sizeof(gdev->md.md_salt));
g_eli_crypto_hmac_update(&ctx, (const uint8_t *)passphrase,
strlen(passphrase));
- } else if (geli_e->md.md_iterations > 0) {
- printf("Calculating GELI Decryption Key disk%dp%d @ %d"
- " iterations...\n", dskp->unit,
- (dskp->slice > 0 ? dskp->slice : dskp->part),
- geli_e->md.md_iterations);
+ } else if (gdev->md.md_iterations > 0) {
+ printf("Calculating GELI Decryption Key for %s %d"
+ " iterations...\n", gdev->name, gdev->md.md_iterations);
u_char dkey[G_ELI_USERKEYLEN];
- pkcs5v2_genkey(dkey, sizeof(dkey), geli_e->md.md_salt,
- sizeof(geli_e->md.md_salt), passphrase,
- geli_e->md.md_iterations);
+ pkcs5v2_genkey(dkey, sizeof(dkey), gdev->md.md_salt,
+ sizeof(gdev->md.md_salt), passphrase,
+ gdev->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_any(&geli_e->md, key, mkey, &keynum);
+ error = g_eli_mkey_decrypt_any(&gdev->md, key, mkey, &keynum);
if (error == -1) {
explicit_bzero(mkey, sizeof(mkey));
explicit_bzero(key, sizeof(key));
@@ -279,34 +275,34 @@ geli_attach(struct geli_entry *ge, struct dsk *dskp, const char *passphrase,
return (error);
} else {
/* Add key to keychain */
- save_key(key);
+ geli_add_key(key);
explicit_bzero(&key, sizeof(key));
}
found_key:
/* Store the keys */
- 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);
- if ((geli_e->sc.sc_flags & G_ELI_FLAG_AUTH) == 0) {
- bcopy(mkp, geli_e->sc.sc_ekey, G_ELI_DATAKEYLEN);
+ bcopy(mkey, gdev->sc.sc_mkey, sizeof(gdev->sc.sc_mkey));
+ bcopy(mkey, gdev->sc.sc_ivkey, sizeof(gdev->sc.sc_ivkey));
+ mkp = mkey + sizeof(gdev->sc.sc_ivkey);
+ if ((gdev->sc.sc_flags & G_ELI_FLAG_AUTH) == 0) {
+ bcopy(mkp, gdev->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, (const uint8_t *)"\x10", 1,
- geli_e->sc.sc_ekey, 0);
+ gdev->sc.sc_ekey, 0);
}
explicit_bzero(mkey, sizeof(mkey));
/* Initialize the per-sector IV. */
- switch (geli_e->sc.sc_ealgo) {
+ switch (gdev->sc.sc_ealgo) {
case CRYPTO_AES_XTS:
break;
default:
- SHA256_Init(&geli_e->sc.sc_ivctx);
- SHA256_Update(&geli_e->sc.sc_ivctx, geli_e->sc.sc_ivkey,
- sizeof(geli_e->sc.sc_ivkey));
+ SHA256_Init(&gdev->sc.sc_ivctx);
+ SHA256_Update(&gdev->sc.sc_ivctx, gdev->sc.sc_ivkey,
+ sizeof(gdev->sc.sc_ivkey));
break;
}
@@ -314,19 +310,7 @@ found_key:
}
int
-is_geli(struct dsk *dskp)
-{
- SLIST_FOREACH_SAFE(geli_e, &geli_head, entries, geli_e_tmp) {
- if (geli_same_device(geli_e, dskp) == 0) {
- return (0);
- }
- }
-
- return (1);
-}
-
-int
-geli_read(struct dsk *dskp, off_t offset, u_char *buf, size_t bytes)
+geli_read(struct geli_dev *gdev, off_t offset, u_char *buf, size_t bytes)
{
u_char iv[G_ELI_IVKEYLEN];
u_char *pbuf;
@@ -337,100 +321,77 @@ geli_read(struct dsk *dskp, off_t offset, u_char *buf, size_t bytes)
struct g_eli_key gkey;
pbuf = buf;
- SLIST_FOREACH_SAFE(geli_e, &geli_head, entries, geli_e_tmp) {
- if (geli_same_device(geli_e, dskp) != 0) {
- continue;
- }
- secsize = geli_e->sc.sc_sectorsize;
- nsec = bytes / secsize;
- if (nsec == 0) {
- /*
- * A read of less than the GELI sector size has been
- * requested. The caller provided destination buffer may
- * not be big enough to boost the read to a full sector,
- * so just attempt to decrypt the truncated sector.
- */
- secsize = bytes;
- nsec = 1;
- }
+ secsize = gdev->sc.sc_sectorsize;
+ nsec = bytes / secsize;
+ if (nsec == 0) {
+ /*
+ * A read of less than the GELI sector size has been
+ * requested. The caller provided destination buffer may
+ * not be big enough to boost the read to a full sector,
+ * so just attempt to decrypt the truncated sector.
+ */
+ secsize = bytes;
+ nsec = 1;
+ }
- for (n = 0, dstoff = offset; n < nsec; n++, dstoff += secsize) {
+ for (n = 0, dstoff = offset; n < nsec; n++, dstoff += secsize) {
- g_eli_crypto_ivgen(&geli_e->sc, dstoff, iv,
- G_ELI_IVKEYLEN);
+ g_eli_crypto_ivgen(&gdev->sc, dstoff, iv, G_ELI_IVKEYLEN);
- /* Get the key that corresponds to this offset. */
- keyno = (dstoff >> G_ELI_KEY_SHIFT) / secsize;
- g_eli_key_fill(&geli_e->sc, &gkey, keyno);
+ /* Get the key that corresponds to this offset. */
+ keyno = (dstoff >> G_ELI_KEY_SHIFT) / secsize;
+ g_eli_key_fill(&gdev->sc, &gkey, keyno);
- error = geliboot_crypt(geli_e->sc.sc_ealgo, 0, pbuf,
- secsize, gkey.gek_key,
- geli_e->sc.sc_ekeylen, iv);
+ error = geliboot_crypt(gdev->sc.sc_ealgo, 0, pbuf, secsize,
+ gkey.gek_key, gdev->sc.sc_ekeylen, iv);
- if (error != 0) {
- explicit_bzero(&gkey, sizeof(gkey));
- printf("Failed to decrypt in geli_read()!");
- return (error);
- }
- pbuf += secsize;
+ if (error != 0) {
+ explicit_bzero(&gkey, sizeof(gkey));
+ printf("Failed to decrypt in geli_read()!");
+ return (error);
}
- explicit_bzero(&gkey, sizeof(gkey));
- return (0);
+ pbuf += secsize;
}
-
- printf("GELI provider not found\n");
- return (1);
+ explicit_bzero(&gkey, sizeof(gkey));
+ return (0);
}
int
-geli_havekey(struct dsk *dskp)
+geli_havekey(struct geli_dev *gdev)
{
u_char mkey[G_ELI_DATAIVKEYLEN];
+ int err;
- SLIST_FOREACH_SAFE(geli_e, &geli_head, entries, geli_e_tmp) {
- if (geli_same_device(geli_e, dskp) != 0) {
- continue;
- }
-
- if (geli_findkey(geli_e, dskp, mkey) == 0) {
- if (geli_attach(geli_e, dskp, NULL, mkey) == 0) {
- return (0);
- }
- }
+ err = ENOENT;
+ if (geli_findkey(gdev, mkey) == 0) {
+ if (geli_probe(gdev, NULL, mkey) == 0)
+ err = 0;
+ explicit_bzero(mkey, sizeof(mkey));
}
- explicit_bzero(mkey, sizeof(mkey));
-
- return (1);
+ return (err);
}
int
-geli_passphrase(char *pw, int disk, int parttype, int part, struct dsk *dskp)
+geli_passphrase(struct geli_dev *gdev, char *pw)
{
int i;
- SLIST_FOREACH_SAFE(geli_e, &geli_head, entries, geli_e_tmp) {
- if (geli_same_device(geli_e, dskp) != 0) {
- continue;
- }
-
- /* TODO: Implement GELI keyfile(s) support */
- for (i = 0; i < 3; i++) {
- /* Try cached passphrase */
- if (i == 0 && pw[0] != '\0') {
- if (geli_attach(geli_e, dskp, pw, NULL) == 0) {
- return (0);
- }
- }
- printf("GELI Passphrase for disk%d%c%d: ", disk,
- parttype, part);
- pwgets(pw, GELI_PW_MAXLEN,
- (geli_e->md.md_flags & G_ELI_FLAG_GELIDISPLAYPASS) == 0);
- printf("\n");
- if (geli_attach(geli_e, dskp, pw, NULL) == 0) {
+ /* TODO: Implement GELI keyfile(s) support */
+ for (i = 0; i < 3; i++) {
+ /* Try cached passphrase */
+ if (i == 0 && pw[0] != '\0') {
+ if (geli_probe(gdev, pw, NULL) == 0) {
return (0);
}
}
+ printf("GELI Passphrase for %s ", gdev->name);
+ pwgets(pw, GELI_PW_MAXLEN,
+ (gdev->md.md_flags & G_ELI_FLAG_GELIDISPLAYPASS) == 0);
+ printf("\n");
+ if (geli_probe(gdev, pw, NULL) == 0) {
+ return (0);
+ }
}
return (1);
diff --git a/stand/libsa/geli/geliboot.h b/stand/libsa/geli/geliboot.h
index 788b82c574ac..aeebde4b0a2e 100644
--- a/stand/libsa/geli/geliboot.h
+++ b/stand/libsa/geli/geliboot.h
@@ -32,6 +32,8 @@
#ifndef _GELIBOOT_H_
#define _GELIBOOT_H_
+#include <geom/eli/g_eli.h>
+
#ifndef DEV_BSIZE
#define DEV_BSIZE 512
#endif
@@ -44,26 +46,50 @@
#endif
#define GELI_MAX_KEYS 64
-#define GELI_PW_MAXLEN 256
+#define GELI_PW_MAXLEN 256
+#define GELI_KEYBUF_SIZE (sizeof(struct keybuf) + \
+ (GELI_MAX_KEYS * sizeof(struct keybuf_ent)))
extern void pwgets(char *buf, int n, int hide);
-struct dsk;
+typedef u_char geli_ukey[G_ELI_USERKEYLEN];
+
+/*
+ * An opaque struct used internally by geliboot functions. Returned by
+ * geli_taste(), a pointer to one of these is essentially a device handle. There
+ * is no need to release or free or "give back" the pointer.
+ */
+struct geli_dev;
-void geli_init(void);
-int geli_taste(int read_func(void *vdev, void *priv, off_t off,
- void *buf, size_t bytes), struct dsk *dsk, daddr_t lastsector);
-int is_geli(struct dsk *dsk);
-int geli_read(struct dsk *dsk, off_t offset, u_char *buf, size_t bytes);
-int geli_decrypt(u_int algo, u_char *data, size_t datasize,
- const u_char *key, size_t keysize, const uint8_t* iv);
-int geli_havekey(struct dsk *dskp);
-int geli_passphrase(char *pw, int disk, int parttype, int part, struct dsk *dskp);
+/* Forward decls. */
+struct open_file;
+struct preloaded_file;
+
+/*
+ * Low-level interface, used by early-stage bootloaders...
+ */
-int geliboot_crypt(u_int algo, int enc, u_char *data, size_t datasize,
- const u_char *key, size_t keysize, u_char *iv);
+/* Read callback function type for geli_taste(). */
+typedef int (*geli_readfunc)(void *vdev, void *readpriv, off_t offbytes,
+ void *buf, size_t sizebytes);
-void geli_fill_keybuf(struct keybuf *keybuf);
-void geli_save_keybuf(struct keybuf *keybuf);
+struct geli_dev * geli_taste(geli_readfunc readfunc, void *readpriv,
+ daddr_t lastsector, const char *namefmt, ...);
+int geli_read(struct geli_dev *gdev, off_t offset, u_char *buf, size_t bytes);
+int geli_havekey(struct geli_dev *gdev);
+int geli_passphrase(struct geli_dev *gdev, char *pw);
+
+/*
+ * Libsa device-and-file-level interface.
+ */
+void geli_probe_and_attach(struct open_file *f);
+
+/*
+ * Manage key data.
+ */
+void geli_add_key(geli_ukey key);
+void geli_import_key_buffer(struct keybuf *keybuf);
+void geli_export_key_buffer(struct keybuf *keybuf);
+void geli_export_key_metadata(struct preloaded_file *kfp);
#endif /* _GELIBOOT_H_ */
diff --git a/stand/libsa/geli/geliboot_internal.h b/stand/libsa/geli/geliboot_internal.h
index ac28db1fd658..2352a844f7dd 100644
--- a/stand/libsa/geli/geliboot_internal.h
+++ b/stand/libsa/geli/geliboot_internal.h
@@ -55,15 +55,17 @@
#define STAND_H /* We don't want stand.h in {gpt,zfs,gptzfs}boot */
#include <opencrypto/xform_enc.h>
-struct geli_entry {
- struct dsk *dsk;
+#define GELIDEV_NAMELEN 32
+
+struct geli_dev {
off_t part_end;
struct g_eli_softc sc;
struct g_eli_metadata md;
int keybuf_slot;
- SLIST_ENTRY(geli_entry) entries;
-} *geli_e, *geli_e_tmp;
+ char *name; /* for prompting; it ends in ':' */
+};
-static int geli_count;
+int geliboot_crypt(u_int algo, int enc, u_char *data, size_t datasize,
+ const u_char *key, size_t keysize, u_char *iv);
#endif /* _GELIBOOT_INTERNAL_H_ */
diff --git a/stand/libsa/geli/gelidev.c b/stand/libsa/geli/gelidev.c
new file mode 100644
index 000000000000..8c1359c0b49a
--- /dev/null
+++ b/stand/libsa/geli/gelidev.c
@@ -0,0 +1,323 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2018 Ian Lepore <ian@FreeBSD.org>
+ *
+ * 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 AUTHORS 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 AUTHORS 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 <stand.h>
+#include <stdarg.h>
+#include <uuid.h>
+#include <sys/disk.h>
+#include "disk.h"
+#include "geliboot.h"
+#include "geliboot_internal.h"
+
+static int geli_dev_init(void);
+static int geli_dev_strategy(void *, int, daddr_t, size_t, char *, size_t *);
+static int geli_dev_open(struct open_file *f, ...);
+static int geli_dev_close(struct open_file *f);
+static int geli_dev_ioctl(struct open_file *, u_long, void *);
+static int geli_dev_print(int);
+static void geli_dev_cleanup(void);
+
+/*
+ * geli_devsw is static because it never appears in any arch's global devsw
+ * array. Instead, when devopen() opens a DEVT_DISK device, it then calls
+ * geli_probe_and_attach(), and if we find that the disk_devdesc describes a
+ * geli-encrypted partition, we create a geli_devdesc which references this
+ * devsw and has a pointer to the original disk_devdesc of the underlying host
+ * disk. Then we manipulate the open_file struct to reference the new
+ * geli_devdesc, effectively routing all IO operations through our code.
+ */
+static struct devsw geli_devsw = {
+ .dv_name = "gelidisk",
+ .dv_type = DEVT_DISK,
+ .dv_init = geli_dev_init,
+ .dv_strategy = geli_dev_strategy,
+ .dv_open = geli_dev_open,
+ .dv_close = geli_dev_close,
+ .dv_ioctl = geli_dev_ioctl,
+ .dv_print = geli_dev_print,
+ .dv_cleanup = geli_dev_cleanup,
+};
+
+/*
+ * geli_devdesc instances replace the disk_devdesc in an open_file struct when
+ * the partition is encrypted. We keep a reference to the original host
+ * disk_devdesc so that we can read the raw encrypted data using it.
+ */
+struct geli_devdesc {
+ struct disk_devdesc ddd; /* Must be first. */
+ struct disk_devdesc *hdesc; /* disk/slice/part hosting geli vol */
+ struct geli_dev *gdev; /* geli_dev entry */
+};
+
+
+/*
+ * A geli_readfunc that reads via a disk_devdesc passed in readpriv. This is
+ * used to read the underlying host disk data when probing/tasting to see if the
+ * host provider is geli-encrypted.
+ */
+static int
+diskdev_read(void *vdev, void *readpriv, off_t offbytes,
+ void *buf, size_t sizebytes)
+{
+ struct disk_devdesc *ddev;
+
+ ddev = (struct disk_devdesc *)readpriv;
+
+ return (ddev->dd.d_dev->dv_strategy(ddev, F_READ, offbytes / DEV_BSIZE,
+ sizebytes, buf, NULL));
+}
+
+static int
+geli_dev_init(void)
+{
+
+ /*
+ * Since geli_devsw never gets referenced in any arch's global devsw
+ * table, this function should never get called.
+ */
+ panic("%s: should never be called", __func__);
+ return (ENXIO);
+}
+
+static int
+geli_dev_strategy(void *devdata, int rw, daddr_t blk, size_t size, char *buf,
+ size_t *rsize)
+{
+ struct geli_devdesc *gdesc;
+ off_t alnend, alnstart, reqend, reqstart;
+ size_t alnsize;
+ char *iobuf;
+ int rc;
+
+ /* We only handle reading; no write support. */
+ if ((rw & F_MASK) != F_READ)
+ return (EOPNOTSUPP);
+
+ gdesc = (struct geli_devdesc *)devdata;
+
+ /*
+ * We can only decrypt full geli blocks. The blk arg is expressed in
+ * units of DEV_BSIZE blocks, while size is in bytes. Convert
+ * everything to bytes, and calculate the geli-blocksize-aligned start
+ * and end points.
+ *
+ * Note: md_sectorsize must be cast to a signed type for the round2
+ * macros to work correctly (otherwise they get zero-extended to 64 bits
+ * and mask off the high order 32 bits of the requested start/end).
+ */
+
+ reqstart = blk * DEV_BSIZE;
+ reqend = reqstart + size;
+ alnstart = rounddown2(reqstart, (int)gdesc->gdev->md.md_sectorsize);
+ alnend = roundup2(reqend, (int)gdesc->gdev->md.md_sectorsize);
+ alnsize = alnend - alnstart;
+
+ /*
+ * If alignment requires us to read more than the size of the provided
+ * buffer, allocate a temporary buffer.
+ */
+ if (alnsize <= size)
+ iobuf = buf;
+ else if ((iobuf = malloc(alnsize)) == NULL)
+ return (ENOMEM);
+
+ /*
+ * Read the encrypted data using the host provider, then decrypt it.
+ */
+ rc = gdesc->hdesc->dd.d_dev->dv_strategy(gdesc->hdesc, rw,
+ alnstart / DEV_BSIZE, alnsize, iobuf, NULL);
+ if (rc != 0)
+ goto out;
+ rc = geli_read(gdesc->gdev, alnstart, iobuf, alnsize);
+ if (rc != 0)
+ goto out;
+
+ /*
+ * If we had to use a temporary buffer, copy the requested part of the
+ * data to the caller's buffer.
+ */
+ if (iobuf != buf)
+ memcpy(buf, iobuf + (reqstart - alnstart), size);
+
+ if (rsize != NULL)
+ *rsize = size;
+out:
+ if (iobuf != buf)
+ free(iobuf);
+
+ return (rc);
+}
+
+static int
+geli_dev_open(struct open_file *f, ...)
+{
+
+ /*
+ * Since geli_devsw never gets referenced in any arch's global devsw
+ * table, this function should never get called.
+ */
+ panic("%s: should never be called", __func__);
+ return (ENXIO);
+}
+
+static int
+geli_dev_close(struct open_file *f)
+{
+ struct geli_devdesc *gdesc;
+
+ /*
+ * Detach the geli_devdesc from the open_file and reattach the
+ * underlying host provider's disk_devdesc; this undoes the work done at
+ * the end of geli_probe_and_attach(). Call the host provider's
+ * dv_close() (because that's what our caller thought it was doing).
+ */
+ gdesc = (struct geli_devdesc *)f->f_devdata;
+ f->f_devdata = gdesc->hdesc;
+ f->f_dev = gdesc->hdesc->dd.d_dev;
+ free(gdesc);
+ f->f_dev->dv_close(f);
+ return (0);
+}
+
+static int
+geli_dev_ioctl(struct open_file *f, u_long cmd, void *data)
+{
+ struct geli_devdesc *gdesc;
+ struct g_eli_metadata *md;
+
+ gdesc = (struct geli_devdesc *)f->f_devdata;
+ md = &gdesc->gdev->md;
+
+ switch (cmd) {
+ case DIOCGSECTORSIZE:
+ *(u_int *)data = md->md_sectorsize;
+ break;
+ case DIOCGMEDIASIZE:
+ *(uint64_t *)data = md->md_sectorsize * md->md_provsize;
+ break;
+ default:
+ return (ENOTTY);
+ }
+
+ return (0);
+}
+
+static int
+geli_dev_print(int verbose)
+{
+
+ /*
+ * Since geli_devsw never gets referenced in any arch's global devsw
+ * table, this function should never get called.
+ */
+ panic("%s: should never be called", __func__);
+ return (ENXIO);
+}
+
+static void
+geli_dev_cleanup(void)
+{
+
+ /*
+ * Since geli_devsw never gets referenced in any arch's global devsw
+ * table, this function should never get called.
+ */
+ panic("%s: should never be called", __func__);
+}
+
+
+/*
+ * geli_probe_and_attach() is called from devopen() after it successfully calls
+ * the dv_open() method of a DEVT_DISK device. We taste the partition described
+ * by the disk_devdesc, and if it's geli-encrypted and we can decrypt it, we
+ * create a geli_devdesc and store it into the open_file struct in place of the
+ * underlying provider's disk_devdesc, effectively attaching our code to all IO
+ * processing for the partition. Not quite the elegant stacking provided by
+ * geom in the kernel, but it gets the job done.
+ */
+void
+geli_probe_and_attach(struct open_file *f)
+{
+ static char gelipw[GELI_PW_MAXLEN];
+ const char *envpw;
+ struct geli_dev *gdev;
+ struct geli_devdesc *gdesc;
+ struct disk_devdesc *hdesc;
+ uint64_t hmediasize;
+ daddr_t hlastblk;
+ int rc;
+
+ hdesc = (struct disk_devdesc *)(f->f_devdata);
+
+ /* Get the last block number for the host provider. */
+ hdesc->dd.d_dev->dv_ioctl(f, DIOCGMEDIASIZE, &hmediasize);
+ hlastblk = (hmediasize / DEV_BSIZE) - 1;
+
+ /* Taste the host provider. If it's not geli-encrypted just return. */
+ gdev = geli_taste(diskdev_read, hdesc, hlastblk, disk_fmtdev(hdesc));
+ if (gdev == NULL)
+ return;
+
+ /*
+ * It's geli, try to decrypt it with existing keys, or prompt for a
+ * passphrase if we don't yet have a cached key for it.
+ */
+ if ((rc = geli_havekey(gdev)) != 0) {
+ envpw = getenv("kern.geom.eli.passphrase");
+ if (envpw != NULL) {
+ /* Use the cached passphrase */
+ bcopy(envpw, &gelipw, GELI_PW_MAXLEN);
+ }
+ if ((rc = geli_passphrase(gdev, gelipw)) == 0) {
+ /* Passphrase is good, cache it. */
+ setenv("kern.geom.eli.passphrase", gelipw, 1);
+ }
+ explicit_bzero(gelipw, sizeof(gelipw));
+ if (rc != 0)
+ return;
+ }
+
+ /*
+ * It's geli-encrypted and we can decrypt it. Create a geli_devdesc,
+ * store a reference to the underlying provider's disk_devdesc in it,
+ * then attach it to the openfile struct in place of the host provider.
+ */
+ if ((gdesc = malloc(sizeof(*gdesc))) == NULL)
+ return;
+ gdesc->ddd.dd.d_dev = &geli_devsw;
+ gdesc->ddd.dd.d_opendata = NULL;
+ gdesc->ddd.dd.d_unit = hdesc->dd.d_unit;
+ gdesc->ddd.d_offset = hdesc->d_offset;
+ gdesc->ddd.d_partition = hdesc->d_partition;
+ gdesc->ddd.d_slice = hdesc->d_slice;
+ gdesc->hdesc = hdesc;
+ gdesc->gdev = gdev;
+ f->f_dev = gdesc->ddd.dd.d_dev;
+ f->f_devdata = gdesc;
+}
diff --git a/stand/userboot/userboot/bootinfo32.c b/stand/userboot/userboot/bootinfo32.c
index 4f1cef61361b..ecd4ccf2edd8 100644
--- a/stand/userboot/userboot/bootinfo32.c
+++ b/stand/userboot/userboot/bootinfo32.c
@@ -36,6 +36,10 @@ __FBSDID("$FreeBSD$");
#include "bootstrap.h"
#include "libuserboot.h"
+#ifdef LOADER_GELI_SUPPORT
+#include "geliboot.h"
+#endif
+
static struct bootinfo bi;
/*
@@ -200,6 +204,9 @@ 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_export_key_metadata(kfp);
+#endif
/* Figure out the size and location of the metadata */
*modulep = addr;