Index: share/mk/src.opts.mk =================================================================== --- share/mk/src.opts.mk +++ share/mk/src.opts.mk @@ -342,10 +342,6 @@ .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 Index: stand/common/devopen.c =================================================================== --- stand/common/devopen.c +++ stand/common/devopen.c @@ -32,6 +32,10 @@ #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 @@ 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 @@ 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); } Index: stand/common/metadata.c =================================================================== --- stand/common/metadata.c +++ stand/common/metadata.c @@ -44,6 +44,10 @@ #include "bootstrap.h" +#ifdef LOADER_GELI_SUPPORT +#include "geliboot.h" +#endif + #if defined(__sparc64__) #include @@ -405,7 +409,9 @@ #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); Index: stand/defs.mk =================================================================== --- stand/defs.mk +++ stand/defs.mk @@ -56,7 +56,6 @@ # 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 @@ 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 Index: stand/efi/loader/bootinfo.c =================================================================== --- stand/efi/loader/bootinfo.c +++ stand/efi/loader/bootinfo.c @@ -55,6 +55,10 @@ #include #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; @@ -471,7 +475,9 @@ #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. */ Index: stand/i386/gptboot/Makefile =================================================================== --- stand/i386/gptboot/Makefile +++ stand/i386/gptboot/Makefile @@ -1,7 +1,5 @@ # $FreeBSD$ -HAVE_GELI= yes - .include .PATH: ${BOOTSRC}/i386/boot2 ${BOOTSRC}/i386/common ${SASRC} Index: stand/i386/gptboot/gptboot.c =================================================================== --- stand/i386/gptboot/gptboot.c +++ stand/i386/gptboot/gptboot.c @@ -113,11 +113,20 @@ #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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ } 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 @@ { 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 @@ #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 @@ 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; Index: stand/i386/gptzfsboot/Makefile =================================================================== --- stand/i386/gptzfsboot/Makefile +++ stand/i386/gptzfsboot/Makefile @@ -1,7 +1,5 @@ # $FreeBSD$ -HAVE_GELI= yes - .include .PATH: ${BOOTSRC}/i386/boot2 ${BOOTSRC}/i386/gptboot \ Index: stand/i386/isoboot/Makefile =================================================================== --- stand/i386/isoboot/Makefile +++ stand/i386/isoboot/Makefile @@ -1,7 +1,5 @@ # $FreeBSD$ -HAVE_GELI= yes - .include .PATH: ${BOOTSRC}/i386/boot2 ${BOOTSRC}/i386/gptboot \ Index: stand/i386/libi386/Makefile =================================================================== --- stand/i386/libi386/Makefile +++ stand/i386/libi386/Makefile @@ -1,7 +1,5 @@ # $FreeBSD$ -HAVE_GELI= yes - .include LIB= i386 Index: stand/i386/libi386/biosdisk.c =================================================================== --- stand/i386/libi386/biosdisk.c +++ stand/i386/libi386/biosdisk.c @@ -51,31 +51,7 @@ #include "libi386.h" #ifdef LOADER_GELI_SUPPORT -#include "cons.h" -#include "drv.h" -#include "gpt.h" -#include "part.h" -#include -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_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 @@ { 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 @@ 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 @@ 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 @@ 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 @@ 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 */ Index: stand/i386/libi386/bootinfo32.c =================================================================== --- stand/i386/libi386/bootinfo32.c +++ stand/i386/libi386/bootinfo32.c @@ -39,9 +39,6 @@ #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 @@ 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 @@ 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 */ Index: stand/i386/libi386/bootinfo64.c =================================================================== --- stand/i386/libi386/bootinfo64.c +++ stand/i386/libi386/bootinfo64.c @@ -42,9 +42,6 @@ #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 @@ 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 @@ 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); Index: stand/i386/loader/Makefile =================================================================== --- stand/i386/loader/Makefile +++ stand/i386/loader/Makefile @@ -1,7 +1,5 @@ # $FreeBSD$ -HAVE_GELI= yes - LOADER_NET_SUPPORT?= yes LOADER_NFS_SUPPORT?= yes LOADER_TFTP_SUPPORT?= yes Index: stand/i386/loader/main.c =================================================================== --- stand/i386/loader/main.c +++ stand/i386/loader/main.c @@ -175,7 +175,7 @@ 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 @@ 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); Index: stand/i386/zfsboot/Makefile =================================================================== --- stand/i386/zfsboot/Makefile +++ stand/i386/zfsboot/Makefile @@ -1,7 +1,5 @@ # $FreeBSD$ -HAVE_GELI=yes - .include .PATH: ${BOOTSRC}/i386/boot2 ${BOOTSRC}/i386/common ${SASRC} Index: stand/i386/zfsboot/zfsboot.c =================================================================== --- stand/i386/zfsboot/zfsboot.c +++ stand/i386/zfsboot/zfsboot.c @@ -127,11 +127,18 @@ 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 @@ 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 @@ * 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 @@ 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 @@ 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 @@ * 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 @@ * 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 @@ 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 @@ /* * 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 @@ * 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 @@ #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 @@ 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 @@ 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 @@ { dnode_phys_t dn; off_t off; - struct dsk *dsk; + struct zfsdsk *zdsk; int autoboot, i; int nextboot; int rc; @@ -693,39 +707,37 @@ } 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 @@ 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 @@ #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; Index: stand/libsa/geli/Makefile.inc =================================================================== --- stand/libsa/geli/Makefile.inc +++ stand/libsa/geli/Makefile.inc @@ -27,7 +27,15 @@ # 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 Index: stand/libsa/geli/geli_metadata.c =================================================================== --- stand/libsa/geli/geli_metadata.c +++ stand/libsa/geli/geli_metadata.c @@ -0,0 +1,52 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2018 Ian Lepore + * + * 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 +#include +#include +#include +#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); +} Index: stand/libsa/geli/geliboot.h =================================================================== --- stand/libsa/geli/geliboot.h +++ stand/libsa/geli/geliboot.h @@ -32,6 +32,8 @@ #ifndef _GELIBOOT_H_ #define _GELIBOOT_H_ +#include + #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; + +/* Forward decls. */ +struct open_file; +struct preloaded_file; -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); +/* + * Low-level interface, used by early-stage bootloaders... + */ + +/* Read callback function type for geli_taste(). */ +typedef int (*geli_readfunc)(void *vdev, void *readpriv, off_t offbytes, + void *buf, size_t sizebytes); + +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); -int geliboot_crypt(u_int algo, int enc, u_char *data, size_t datasize, - const u_char *key, size_t keysize, u_char *iv); +/* + * Libsa device-and-file-level interface. + */ +void geli_probe_and_attach(struct open_file *f); -void geli_fill_keybuf(struct keybuf *keybuf); -void geli_save_keybuf(struct keybuf *keybuf); +/* + * 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_ */ Index: stand/libsa/geli/geliboot.c =================================================================== --- stand/libsa/geli/geliboot.c +++ stand/libsa/geli/geliboot.c @@ -27,13 +27,19 @@ * $FreeBSD$ */ -#include "geliboot_internal.h" +#include +#include #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 @@ * 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 @@ * 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 @@ skeybuf->kb_nents = 0; } -static void -save_key(geli_ukey key) +void +geli_add_key(geli_ukey key) { /* @@ -91,46 +97,22 @@ } 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 @@ 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 @@ 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); - } - 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; + goto out; } - 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 @@ 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 @@ /* * 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 @@ 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 @@ } 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 @@ 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); - - error = geliboot_crypt(geli_e->sc.sc_ealgo, 0, pbuf, - secsize, gkey.gek_key, - geli_e->sc.sc_ekeylen, iv); - - if (error != 0) { - explicit_bzero(&gkey, sizeof(gkey)); - printf("Failed to decrypt in geli_read()!"); - return (error); - } - pbuf += secsize; + /* 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(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); } - 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); Index: stand/libsa/geli/geliboot_internal.h =================================================================== --- stand/libsa/geli/geliboot_internal.h +++ 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 -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_ */ Index: stand/libsa/geli/gelidev.c =================================================================== --- stand/libsa/geli/gelidev.c +++ stand/libsa/geli/gelidev.c @@ -0,0 +1,323 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2018 Ian Lepore + * + * 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 +#include +#include +#include +#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; +} \ No newline at end of file Index: stand/userboot/userboot/bootinfo32.c =================================================================== --- stand/userboot/userboot/bootinfo32.c +++ stand/userboot/userboot/bootinfo32.c @@ -36,6 +36,10 @@ #include "bootstrap.h" #include "libuserboot.h" +#ifdef LOADER_GELI_SUPPORT +#include "geliboot.h" +#endif + static struct bootinfo bi; /* @@ -200,6 +204,9 @@ 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;