diff options
author | allanjude <allanjude@FreeBSD.org> | 2016-03-16 23:12:19 +0000 |
---|---|---|
committer | allanjude <allanjude@FreeBSD.org> | 2016-03-16 23:12:19 +0000 |
commit | 736b39250974facd8d5f4ab24f56636212c50a33 (patch) | |
tree | e3e47b4adfddec374c56af417380ab82142fa71a /sys/boot/i386 | |
parent | ecffce941a074abbd944385dc1cc659f4df1b04d (diff) | |
download | FreeBSD-src-736b39250974facd8d5f4ab24f56636212c50a33.zip FreeBSD-src-736b39250974facd8d5f4ab24f56636212c50a33.tar.gz |
Implement GELI (AES-XTS and AES-CBC only) in gptboot and gptzfsboot
Allows booting from a GELI encrypted root file system, via UFS or ZFS
Reviewed by: gnn, smh (previous version), delphij (previous version)
Relnotes: yes
Sponsored by: ScaleEngine Inc.
Differential Revision: https://reviews.freebsd.org/D4593
Diffstat (limited to 'sys/boot/i386')
-rw-r--r-- | sys/boot/i386/common/bootargs.h | 6 | ||||
-rw-r--r-- | sys/boot/i386/common/cons.c | 7 | ||||
-rw-r--r-- | sys/boot/i386/common/drv.c | 2 | ||||
-rw-r--r-- | sys/boot/i386/common/drv.h | 2 | ||||
-rw-r--r-- | sys/boot/i386/gptboot/Makefile | 16 | ||||
-rw-r--r-- | sys/boot/i386/gptboot/gptboot.c | 221 | ||||
-rw-r--r-- | sys/boot/i386/gptzfsboot/Makefile | 14 | ||||
-rw-r--r-- | sys/boot/i386/libi386/Makefile | 6 | ||||
-rw-r--r-- | sys/boot/i386/libi386/biosdisk.c | 178 | ||||
-rw-r--r-- | sys/boot/i386/loader/Makefile | 11 | ||||
-rw-r--r-- | sys/boot/i386/loader/main.c | 31 | ||||
-rw-r--r-- | sys/boot/i386/zfsboot/zfsboot.c | 119 |
12 files changed, 571 insertions, 42 deletions
diff --git a/sys/boot/i386/common/bootargs.h b/sys/boot/i386/common/bootargs.h index 4768d68..e6674e1 100644 --- a/sys/boot/i386/common/bootargs.h +++ b/sys/boot/i386/common/bootargs.h @@ -64,6 +64,12 @@ struct bootargs */ }; +struct geli_boot_args +{ + uint32_t size; + char gelipw[256]; +}; + #endif /*__ASSEMBLER__*/ #endif /* !_BOOT_I386_ARGS_H_ */ diff --git a/sys/boot/i386/common/cons.c b/sys/boot/i386/common/cons.c index 97019c6..b967d9b 100644 --- a/sys/boot/i386/common/cons.c +++ b/sys/boot/i386/common/cons.c @@ -97,6 +97,13 @@ xgetc(int fn) } int +getchar(void) +{ + + return (xgetc(0)); +} + +int keyhit(unsigned int secs) { uint32_t t0, t1; diff --git a/sys/boot/i386/common/drv.c b/sys/boot/i386/common/drv.c index 52933d5..f5133de 100644 --- a/sys/boot/i386/common/drv.c +++ b/sys/boot/i386/common/drv.c @@ -29,7 +29,6 @@ __FBSDID("$FreeBSD$"); #include "xreadorg.h" #endif -#ifdef GPT static struct edd_params params; uint64_t @@ -50,7 +49,6 @@ drvsize(struct dsk *dskp) } return (params.sectors); } -#endif /* GPT */ #ifndef USE_XREAD static struct edd_packet packet; diff --git a/sys/boot/i386/common/drv.h b/sys/boot/i386/common/drv.h index 1ecfbc3..8ad3c9c 100644 --- a/sys/boot/i386/common/drv.h +++ b/sys/boot/i386/common/drv.h @@ -42,7 +42,7 @@ struct dsk { int drvread(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk); #ifdef GPT int drvwrite(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk); -uint64_t drvsize(struct dsk *dskp); #endif /* GPT */ +uint64_t drvsize(struct dsk *dskp); #endif /* !_DRV_H_ */ diff --git a/sys/boot/i386/gptboot/Makefile b/sys/boot/i386/gptboot/Makefile index 1aafe0b..af9c9e0 100644 --- a/sys/boot/i386/gptboot/Makefile +++ b/sys/boot/i386/gptboot/Makefile @@ -39,6 +39,14 @@ CFLAGS= -DBOOTPROG=\"gptboot\" \ CFLAGS.gcc+= --param max-inline-insns-single=100 +.if !defined(LOADER_NO_GELI_SUPPORT) +CFLAGS+= -DLOADER_GELI_SUPPORT +CFLAGS+= -I${.CURDIR}/../../geli +LIBGELIBOOT= ${.OBJDIR}/../../geli/libgeliboot.a +.PATH: ${.CURDIR}/../../../opencrypto +OPENCRYPTO_XTS= xform_aes_xts.o +.endif + LD_FLAGS=-static -N --gc-sections LIBSTAND= ${.OBJDIR}/../../libstand32/libstand.a @@ -60,14 +68,14 @@ gptldr.bin: gptldr.out gptldr.out: gptldr.o ${LD} ${LD_FLAGS} -e start -Ttext ${ORG1} -o ${.TARGET} gptldr.o -CLEANFILES+= gptboot.bin gptboot.out gptboot.o sio.o gpt.o crc32.o drv.o \ - cons.o util.o +CLEANFILES+= gptboot.bin gptboot.out gptboot.o sio.o crc32.o drv.o \ + cons.o util.o ${OPENCRYPTO_XTS} gptboot.bin: gptboot.out ${OBJCOPY} -S -O binary gptboot.out ${.TARGET} -gptboot.out: ${BTXCRT} gptboot.o sio.o gpt.o crc32.o drv.o cons.o util.o - ${LD} ${LD_FLAGS} -Ttext ${ORG2} -o ${.TARGET} ${.ALLSRC} ${LIBSTAND} +gptboot.out: ${BTXCRT} gptboot.o sio.o crc32.o drv.o cons.o util.o ${OPENCRYPTO_XTS} + ${LD} ${LD_FLAGS} -Ttext ${ORG2} -o ${.TARGET} ${.ALLSRC} ${LIBSTAND} ${LIBGELIBOOT} gptboot.o: ${.CURDIR}/../../common/ufsread.c diff --git a/sys/boot/i386/gptboot/gptboot.c b/sys/boot/i386/gptboot/gptboot.c index a1f46eb..14438e6 100644 --- a/sys/boot/i386/gptboot/gptboot.c +++ b/sys/boot/i386/gptboot/gptboot.c @@ -23,6 +23,7 @@ __FBSDID("$FreeBSD$"); #include <machine/bootinfo.h> #include <machine/elf.h> +#include <machine/pc/bios.h> #include <machine/psl.h> #include <stdarg.h> @@ -31,6 +32,7 @@ __FBSDID("$FreeBSD$"); #include <btxv86.h> +#include "bootargs.h" #include "lib.h" #include "rbx.h" #include "drv.h" @@ -82,14 +84,60 @@ static struct dsk dsk; static char kname[1024]; static int comspeed = SIOSPD; static struct bootinfo bootinfo; +static struct geli_boot_args geliargs; + +static vm_offset_t high_heap_base; +static uint32_t bios_basemem, bios_extmem, high_heap_size; + +static struct bios_smap smap; + +/* + * The minimum amount of memory to reserve in bios_extmem for the heap. + */ +#define HEAP_MIN (3 * 1024 * 1024) + +static char *heap_next; +static char *heap_end; void exit(int); static void load(void); static int parse(char *, int *); static int dskread(void *, daddr_t, unsigned); -static uint32_t memsize(void); +void *malloc(size_t n); +void free(void *ptr); +#ifdef LOADER_GELI_SUPPORT +static int vdev_read(void *vdev __unused, void *priv, off_t off, void *buf, + size_t bytes); +#endif + +void * +malloc(size_t n) +{ + char *p = heap_next; + if (p + n > heap_end) { + printf("malloc failure\n"); + for (;;) + ; + /* NOTREACHED */ + return (0); + } + heap_next += n; + return (p); +} + +void +free(void *ptr) +{ + + return; +} #include "ufsread.c" +#include "gpt.c" +#ifdef LOADER_GELI_SUPPORT +#include "geliboot.c" +static char gelipw[GELI_PW_MAXLEN]; +#endif static inline int xfsread(ufs_ino_t inode, void *buf, size_t nbyte) @@ -102,14 +150,90 @@ xfsread(ufs_ino_t inode, void *buf, size_t nbyte) return (0); } -static inline uint32_t -memsize(void) +static void +bios_getmem(void) { + uint64_t size; - v86.addr = MEM_EXT; + /* Parse system memory map */ + v86.ebx = 0; + do { + v86.ctl = V86_FLAGS; + v86.addr = MEM_EXT; /* int 0x15 function 0xe820*/ + v86.eax = 0xe820; + v86.ecx = sizeof(struct bios_smap); + v86.edx = SMAP_SIG; + v86.es = VTOPSEG(&smap); + v86.edi = VTOPOFF(&smap); + v86int(); + if ((v86.efl & 1) || (v86.eax != SMAP_SIG)) + break; + /* look for a low-memory segment that's large enough */ + if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0) && + (smap.length >= (512 * 1024))) + bios_basemem = smap.length; + /* look for the first segment in 'extended' memory */ + if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0x100000)) { + bios_extmem = smap.length; + } + + /* + * Look for the largest segment in 'extended' memory beyond + * 1MB but below 4GB. + */ + if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base > 0x100000) && + (smap.base < 0x100000000ull)) { + size = smap.length; + + /* + * If this segment crosses the 4GB boundary, truncate it. + */ + if (smap.base + size > 0x100000000ull) + size = 0x100000000ull - smap.base; + + if (size > high_heap_size) { + high_heap_size = size; + high_heap_base = smap.base; + } + } + } while (v86.ebx != 0); + + /* Fall back to the old compatibility function for base memory */ + if (bios_basemem == 0) { + v86.ctl = 0; + v86.addr = 0x12; /* int 0x12 */ + v86int(); + + bios_basemem = (v86.eax & 0xffff) * 1024; + } + + /* Fall back through several compatibility functions for extended memory */ + if (bios_extmem == 0) { + v86.ctl = V86_FLAGS; + v86.addr = 0x15; /* int 0x15 function 0xe801*/ + v86.eax = 0xe801; + v86int(); + if (!(v86.efl & 1)) { + bios_extmem = ((v86.ecx & 0xffff) + ((v86.edx & 0xffff) * 64)) * 1024; + } + } + if (bios_extmem == 0) { + v86.ctl = 0; + v86.addr = 0x15; /* int 0x15 function 0x88*/ v86.eax = 0x8800; v86int(); - return (v86.eax); + bios_extmem = (v86.eax & 0xffff) * 1024; + } + + /* + * If we have extended memory and did not find a suitable heap + * region in the SMAP, use the last 3MB of 'extended' memory as a + * high heap candidate. + */ + if (bios_extmem >= HEAP_MIN && high_heap_size < HEAP_MIN) { + high_heap_size = HEAP_MIN; + high_heap_base = bios_extmem + 0x100000 - HEAP_MIN; + } } static int @@ -124,6 +248,16 @@ gptinit(void) 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_passphrase(&gelipw, dsk.unit, 'p', curent + 1, &dsk) != 0) { + printf("%s: unable to decrypt GELI key\n", BOOTPROG); + return (-1); + } + } +#endif + dsk_meta = 0; return (0); } @@ -137,6 +271,17 @@ main(void) ufs_ino_t ino; dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base); + + bios_getmem(); + + if (high_heap_size > 0) { + heap_end = PTOV(high_heap_base + high_heap_size); + heap_next = PTOV(high_heap_base); + } else { + heap_next = (char *)dmadat + sizeof(*dmadat); + heap_end = (char *)PTOV(bios_basemem); + } + v86.ctl = V86_FLAGS; v86.efl = PSL_RESERVED_DEFAULT | PSL_I; dsk.drive = *(uint8_t *)PTOV(ARGS); @@ -146,10 +291,14 @@ main(void) dsk.start = 0; bootinfo.bi_version = BOOTINFO_VERSION; bootinfo.bi_size = sizeof(bootinfo); - bootinfo.bi_basemem = 0; /* XXX will be filled by loader or kernel */ - bootinfo.bi_extmem = memsize(); + bootinfo.bi_basemem = bios_basemem / 1024; + bootinfo.bi_extmem = bios_extmem / 1024; bootinfo.bi_memsizes_valid++; + bootinfo.bi_bios_dev = dsk.drive; +#ifdef LOADER_GELI_SUPPORT + geli_init(); +#endif /* Process configuration file */ if (gptinit() != 0) @@ -327,9 +476,16 @@ load(void) bootinfo.bi_esymtab = VTOP(p); bootinfo.bi_kernelname = VTOP(kname); bootinfo.bi_bios_dev = dsk.drive; + geliargs.size = sizeof(geliargs); +#ifdef LOADER_GELI_SUPPORT + bcopy(gelipw, geliargs.gelipw, sizeof(geliargs.gelipw)); + bzero(gelipw, sizeof(gelipw)); +#else + geliargs.gelipw[0] = '\0'; +#endif __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK), MAKEBOOTDEV(dev_maj[dsk.type], dsk.part + 1, dsk.unit, 0xff), - 0, 0, 0, VTOP(&bootinfo)); + KARGS_FLAGS_EXTARG, 0, 0, VTOP(&bootinfo), geliargs); } static int @@ -430,6 +586,53 @@ parse(char *cmdstr, int *dskupdated) static int dskread(void *buf, daddr_t lba, unsigned nblk) { + int err; + + err = drvread(&dsk, buf, lba + dsk.start, nblk); - return drvread(&dsk, buf, lba + dsk.start, nblk); +#ifdef LOADER_GELI_SUPPORT + if (err == 0 && is_geli(&dsk) == 0) { + /* Decrypt */ + if (geli_read(&dsk, lba * DEV_BSIZE, buf, nblk * DEV_BSIZE)) + return (err); + } +#endif + + return (err); +} + +#ifdef LOADER_GELI_SUPPORT +/* + * Read function compartible 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) +{ + char *p; + daddr_t lba; + unsigned int nb; + struct dsk *dskp = (struct dsk *) priv; + + if ((off & (DEV_BSIZE - 1)) || (bytes & (DEV_BSIZE - 1))) + return (-1); + + p = buf; + lba = off / DEV_BSIZE; + lba += dskp->start; + + while (bytes > 0) { + nb = bytes / DEV_BSIZE; + if (nb > VBLKSIZE / DEV_BSIZE) + nb = VBLKSIZE / DEV_BSIZE; + if (drvread(dskp, dmadat->blkbuf, lba, nb)) + return (-1); + memcpy(p, dmadat->blkbuf, nb * DEV_BSIZE); + p += nb * DEV_BSIZE; + lba += nb; + bytes -= nb * DEV_BSIZE; + } + + return (0); } +#endif /* LOADER_GELI_SUPPORT */ diff --git a/sys/boot/i386/gptzfsboot/Makefile b/sys/boot/i386/gptzfsboot/Makefile index 1406333..8f6d3bc 100644 --- a/sys/boot/i386/gptzfsboot/Makefile +++ b/sys/boot/i386/gptzfsboot/Makefile @@ -35,6 +35,14 @@ CFLAGS= -DBOOTPROG=\"gptzfsboot\" \ -Wpointer-arith -Wshadow -Wstrict-prototypes -Wwrite-strings \ -Winline +.if !defined(LOADER_NO_GELI_SUPPORT) +CFLAGS+= -DLOADER_GELI_SUPPORT +CFLAGS+= -I${.CURDIR}/../../geli +LIBGELIBOOT= ${.OBJDIR}/../../geli/libgeliboot.a +.PATH: ${.CURDIR}/../../../opencrypto +OPENCRYPTO_XTS= xform_aes_xts.o +.endif + CFLAGS.gcc+= --param max-inline-insns-single=100 LD_FLAGS=-static -N --gc-sections @@ -59,13 +67,13 @@ gptldr.out: gptldr.o ${LD} ${LD_FLAGS} -e start -Ttext ${ORG1} -o ${.TARGET} gptldr.o CLEANFILES+= gptzfsboot.bin gptzfsboot.out zfsboot.o sio.o cons.o \ - drv.o gpt.o util.o + drv.o gpt.o util.o ${OPENCRYPTO_XTS} gptzfsboot.bin: gptzfsboot.out ${OBJCOPY} -S -O binary gptzfsboot.out ${.TARGET} -gptzfsboot.out: ${BTXCRT} zfsboot.o sio.o gpt.o drv.o cons.o util.o - ${LD} ${LD_FLAGS} -Ttext ${ORG2} -o ${.TARGET} ${.ALLSRC} ${LIBSTAND} +gptzfsboot.out: ${BTXCRT} zfsboot.o sio.o gpt.o drv.o cons.o util.o ${OPENCRYPTO_XTS} + ${LD} ${LD_FLAGS} -Ttext ${ORG2} -o ${.TARGET} ${.ALLSRC} ${LIBSTAND} ${LIBGELIBOOT} zfsboot.o: ${.CURDIR}/../../zfs/zfsimpl.c diff --git a/sys/boot/i386/libi386/Makefile b/sys/boot/i386/libi386/Makefile index f3c1d8d..d38f18c 100644 --- a/sys/boot/i386/libi386/Makefile +++ b/sys/boot/i386/libi386/Makefile @@ -30,6 +30,12 @@ CFLAGS+= -DCOMSPEED=${BOOT_COMCONSOLE_SPEED} CFLAGS+= -DDISK_DEBUG .endif +.if !defined(LOADER_NO_GELI_SUPPORT) +# Decrypt encrypted drives +CFLAGS+= -DLOADER_GELI_SUPPORT +CFLAGS+= -I${.CURDIR}/../../geli +.endif + .if !defined(BOOT_HIDE_SERIAL_NUMBERS) # Export serial numbers, UUID, and asset tag from loader. CFLAGS+= -DSMBIOS_SERIAL_NUMBERS diff --git a/sys/boot/i386/libi386/biosdisk.c b/sys/boot/i386/libi386/biosdisk.c index 2a71d64..1c54769 100644 --- a/sys/boot/i386/libi386/biosdisk.c +++ b/sys/boot/i386/libi386/biosdisk.c @@ -49,6 +49,34 @@ __FBSDID("$FreeBSD$"); #include "disk.h" #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" +#endif /* LOADER_GELI_SUPPORT */ + CTASSERT(sizeof(struct i386_devdesc) >= sizeof(struct disk_devdesc)); #define BIOS_NUMDRIVES 0x475 @@ -108,6 +136,18 @@ static int bd_ioctl(struct open_file *f, u_long cmd, void *data); static void bd_print(int verbose); static void bd_cleanup(void); +#ifdef LOADER_GELI_SUPPORT +static enum isgeli { + ISGELI_UNKNOWN, + ISGELI_NO, + ISGELI_YES +}; +static enum isgeli geli_status[MAXBDDEV][MAXTBLENTS]; + +int bios_read(void *vdev __unused, struct dsk *priv, off_t off, char *buf, + size_t bytes); +#endif /* LOADER_GELI_SUPPORT */ + struct devsw biosdisk = { "disk", DEVT_DISK, @@ -154,6 +194,9 @@ 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++) { @@ -299,7 +342,8 @@ bd_print(int verbose) static int bd_open(struct open_file *f, ...) { - struct disk_devdesc *dev; + struct disk_devdesc *dev, rdev; + int err, g_err; va_list ap; va_start(ap, f); @@ -309,9 +353,83 @@ bd_open(struct open_file *f, ...) if (dev->d_unit < 0 || dev->d_unit >= nbdinfo) return (EIO); - return (disk_open(dev, BD(dev).bd_sectors * BD(dev).bd_sectorsize, + err = disk_open(dev, BD(dev).bd_sectors * BD(dev).bd_sectorsize, BD(dev).bd_sectorsize, (BD(dev).bd_flags & BD_FLOPPY) ? - DISK_F_NOCACHE: 0)); + DISK_F_NOCACHE: 0); + +#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->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->d_unit); + dskp.type = dev->d_type; + dskp.unit = dev->d_unit; + dskp.slice = dev->d_slice; + dskp.part = dev->d_partition; + dskp.start = dev->d_offset; + + memcpy(&rdev, dev, sizeof(rdev)); + /* to read the GPT table, we need to read the first sector */ + rdev.d_offset = 0; + /* We need the LBA of the end of the partition */ + table = ptable_open(&rdev, 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->d_unit][dskp.slice] = ISGELI_YES; + return (0); + } + if (geli_taste(bios_read, &dskp, + entry->part.end - entry->part.start) == 0) { + 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->d_unit][dskp.slice] = ISGELI_YES; + geli_part++; + } + } else + geli_status[dev->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->d_unit][dev->d_slice] = ISGELI_NO; + } +#endif /* LOADER_GELI_SUPPORT */ + + return (err); } static int @@ -586,6 +704,38 @@ 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; + int err, n; + + /* if we already know there is no GELI, skip the rest */ + if (geli_status[dev->d_unit][dev->d_slice] != ISGELI_YES) + return (bd_io(dev, dblk, blks, dest, 0)); + + if (geli_status[dev->d_unit][dev->d_slice] == ISGELI_YES) { + err = bd_io(dev, dblk, blks, dest, 0); + if (err) + return (err); + + dskp.drive = bd_unit2bios(dev->d_unit); + dskp.type = dev->d_type; + dskp.unit = dev->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 = dblk - dskp.start; + + err = geli_read(&dskp, p_off * BIOSDISK_SECSIZE, dest, + blks * BIOSDISK_SECSIZE); + if (err) + return (err); + + return (0); + } +#endif /* LOADER_GELI_SUPPORT */ return (bd_io(dev, dblk, blks, dest, 0)); } @@ -682,3 +832,25 @@ 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, struct dsk *priv, off_t off, char *buf, size_t bytes) +{ + struct disk_devdesc dev; + + dev.d_dev = &biosdisk; + dev.d_type = priv->type; + dev.d_unit = priv->unit; + dev.d_slice = priv->slice; + dev.d_partition = priv->part; + dev.d_offset = priv->start; + + off = off / BIOSDISK_SECSIZE; + /* GELI gives us the offset relative to the partition start */ + off += dev.d_offset; + bytes = bytes / BIOSDISK_SECSIZE; + + return (bd_io(&dev, off, bytes, buf, 0)); +} +#endif /* LOADER_GELI_SUPPORT */ diff --git a/sys/boot/i386/loader/Makefile b/sys/boot/i386/loader/Makefile index 776ba92..233876a 100644 --- a/sys/boot/i386/loader/Makefile +++ b/sys/boot/i386/loader/Makefile @@ -58,6 +58,13 @@ CFLAGS+= -DLOADER_GZIP_SUPPORT .if defined(LOADER_NANDFS_SUPPORT) CFLAGS+= -DLOADER_NANDFS_SUPPORT .endif +.if !defined(LOADER_NO_GELI_SUPPORT) +CFLAGS+= -DLOADER_GELI_SUPPORT +LIBGELIBOOT= ${.OBJDIR}/../../geli/libgeliboot.a +.PATH: ${.CURDIR}/../../../opencrypto +SRCS+= xform_aes_xts.c +CFLAGS+= -I${.CURDIR}/../../.. -D_STAND +.endif # Always add MI sources .PATH: ${.CURDIR}/../../common @@ -116,8 +123,8 @@ FILES+= loader.rc menu.rc # XXX crt0.o needs to be first for pxeboot(8) to work OBJS= ${BTXCRT} -DPADD= ${LIBFICL} ${LIBFIREWIRE} ${LIBZFSBOOT} ${LIBI386} ${LIBSTAND} -LDADD= ${LIBFICL} ${LIBFIREWIRE} ${LIBZFSBOOT} ${LIBI386} ${LIBSTAND} +DPADD= ${LIBFICL} ${LIBFIREWIRE} ${LIBZFSBOOT} ${LIBI386} ${LIBSTAND} ${LIBGELIBOOT} +LDADD= ${LIBFICL} ${LIBFIREWIRE} ${LIBZFSBOOT} ${LIBI386} ${LIBSTAND} ${LIBGELIBOOT} .include <bsd.prog.mk> diff --git a/sys/boot/i386/loader/main.c b/sys/boot/i386/loader/main.c index c0782fc..b3e7b41 100644 --- a/sys/boot/i386/loader/main.c +++ b/sys/boot/i386/loader/main.c @@ -68,7 +68,11 @@ static void extract_currdev(void); static int isa_inb(int port); static void isa_outb(int port, int value); void exit(int code); +#ifdef LOADER_GELI_SUPPORT +struct geli_boot_args *gargs; +#endif #ifdef LOADER_ZFS_SUPPORT +struct zfs_boot_args *zargs; static void i386_zfs_probe(void); #endif @@ -164,7 +168,31 @@ main(void) archsw.arch_isaoutb = isa_outb; #ifdef LOADER_ZFS_SUPPORT archsw.arch_zfs_probe = i386_zfs_probe; -#endif + +#ifdef LOADER_GELI_SUPPORT + if ((kargs->bootflags & KARGS_FLAGS_EXTARG) != 0) { + zargs = (struct zfs_boot_args *)(kargs + 1); + if (zargs != NULL && zargs->size >= offsetof(struct zfs_boot_args, gelipw)) { + if (zargs->gelipw[0] != '\0') { + setenv("kern.geom.eli.passphrase", zargs->gelipw, 1); + bzero(zargs->gelipw, sizeof(zargs->gelipw)); + } + } + } +#endif /* LOADER_GELI_SUPPORT */ +#else /* !LOADER_ZFS_SUPPORT */ +#ifdef LOADER_GELI_SUPPORT + if ((kargs->bootflags & KARGS_FLAGS_EXTARG) != 0) { + gargs = (struct geli_boot_args *)(kargs + 1); + if (gargs != NULL && gargs->size >= offsetof(struct geli_boot_args, gelipw)) { + if (gargs->gelipw[0] != '\0') { + setenv("kern.geom.eli.passphrase", gargs->gelipw, 1); + bzero(gargs->gelipw, sizeof(gargs->gelipw)); + } + } + } +#endif /* LOADER_GELI_SUPPORT */ +#endif /* LOADER_ZFS_SUPPORT */ /* * March through the device switch probing for things. @@ -214,7 +242,6 @@ extract_currdev(void) struct i386_devdesc new_currdev; #ifdef LOADER_ZFS_SUPPORT char buf[20]; - struct zfs_boot_args *zargs; #endif int biosdev = -1; diff --git a/sys/boot/i386/zfsboot/zfsboot.c b/sys/boot/i386/zfsboot/zfsboot.c index 6fbbc6f..4b371dc 100644 --- a/sys/boot/i386/zfsboot/zfsboot.c +++ b/sys/boot/i386/zfsboot/zfsboot.c @@ -121,8 +121,10 @@ void exit(int); static void load(void); static int parse(void); static void bios_getmem(void); +void *malloc(size_t n); +void free(void *ptr); -static void * +void * malloc(size_t n) { char *p = heap_next; @@ -130,10 +132,18 @@ malloc(size_t n) printf("malloc failure\n"); for (;;) ; - return 0; + /* NOTREACHED */ + return (0); } heap_next += n; - return p; + return (p); +} + +void +free(void *ptr) +{ + + return; } static char * @@ -141,9 +151,14 @@ strdup(const char *s) { char *p = malloc(strlen(s) + 1); strcpy(p, s); - return p; + return (p); } +#ifdef LOADER_GELI_SUPPORT +#include "geliboot.c" +static char gelipw[GELI_PW_MAXLEN]; +#endif + #include "zfsimpl.c" /* @@ -199,6 +214,14 @@ vdev_read(vdev_t *vdev, void *priv, off_t off, void *buf, size_t bytes) nb = READ_BUF_SIZE / DEV_BSIZE; if (drvread(dsk, dmadat->rdbuf, lba, nb)) return -1; +#ifdef LOADER_GELI_SUPPORT + /* decrypt */ + if (is_geli(dsk) == 0) { + if (geli_read(dsk, ((lba - dsk->start) * DEV_BSIZE), + dmadat->rdbuf, nb * DEV_BSIZE)) + return (-1); + } +#endif memcpy(p, dmadat->rdbuf, nb * DEV_BSIZE); p += nb * DEV_BSIZE; lba += nb; @@ -302,7 +325,7 @@ bios_getmem(void) high_heap_size = HEAP_MIN; high_heap_base = bios_extmem + 0x100000 - HEAP_MIN; } -} +} /* * Try to detect a device supported by the legacy int13 BIOS @@ -346,21 +369,39 @@ probe_drive(struct dsk *dsk) #ifdef GPT struct gpt_hdr hdr; struct gpt_ent *ent; - daddr_t slba, elba; unsigned part, entries_per_sec; #endif + daddr_t slba, elba; struct dos_partition *dp; char *sec; unsigned i; /* - * If we find a vdev on the whole disk, stop here. Otherwise dig - * out the partition table and probe each slice/partition - * in turn for a vdev. + * If we find a vdev on the whole disk, stop here. */ if (vdev_probe(vdev_read, dsk, NULL) == 0) return; +#ifdef LOADER_GELI_SUPPORT + /* + * Taste the disk, if it is GELI encrypted, decrypt it and check to see if + * it is a usable vdev then. Otherwise dig + * out the partition table and probe each slice/partition + * in turn for a vdev or GELI encrypted vdev. + */ + elba = drvsize(dsk); + if (elba > 0) { + elba--; + } + if (geli_taste(vdev_read, dsk, elba) == 0) { + if (geli_passphrase(&gelipw, dsk->unit, ':', 0, dsk) == 0) { + if (vdev_probe(vdev_read, dsk, NULL) == 0) { + return; + } + } + } +#endif /* LOADER_GELI_SUPPORT */ + sec = dmadat->secbuf; dsk->start = 0; @@ -383,6 +424,8 @@ probe_drive(struct dsk *dsk) * return the spa_t for the first we find (if requested). This * will have the effect of booting from the first pool on the * disk. + * + * If no vdev is found, GELI decrypting the device and try again */ entries_per_sec = DEV_BSIZE / hdr.hdr_entsz; slba = hdr.hdr_lba_table; @@ -396,6 +439,8 @@ probe_drive(struct dsk *dsk) if (memcmp(&ent->ent_type, &freebsd_zfs_uuid, sizeof(uuid_t)) == 0) { dsk->start = ent->ent_lba_start; + dsk->slice = part + 1; + dsk->part = 255; if (vdev_probe(vdev_read, dsk, NULL) == 0) { /* * This slice had a vdev. We need a new dsk @@ -403,13 +448,31 @@ probe_drive(struct dsk *dsk) */ dsk = copy_dsk(dsk); } +#ifdef LOADER_GELI_SUPPORT + else if (geli_taste(vdev_read, dsk, ent->ent_lba_end - + ent->ent_lba_start) == 0) { + if (geli_passphrase(&gelipw, dsk->unit, 'p', dsk->slice, dsk) == 0) { + /* + * This slice has GELI, check it for ZFS. + */ + if (vdev_probe(vdev_read, dsk, 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); + } + break; + } + } +#endif /* LOADER_GELI_SUPPORT */ } } slba++; } return; trymbr: -#endif +#endif /* GPT */ if (drvread(dsk, sec, DOSBBSECTOR, 1)) return; @@ -419,13 +482,28 @@ trymbr: if (!dp[i].dp_typ) continue; dsk->start = dp[i].dp_start; + dsk->slice = i + 1; if (vdev_probe(vdev_read, dsk, 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); } +#ifdef LOADER_GELI_SUPPORT + else if (geli_taste(vdev_read, dsk, dp[i].dp_size - + dp[i].dp_start) == 0) { + if (geli_passphrase(&gelipw, dsk->unit, 's', i, dsk) == 0) { + /* + * This slice has GELI, check it for ZFS. + */ + if (vdev_probe(vdev_read, dsk, 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); + } + break; + } + } +#endif /* LOADER_GELI_SUPPORT */ } } @@ -445,8 +523,8 @@ main(void) heap_end = PTOV(high_heap_base + high_heap_size); heap_next = PTOV(high_heap_base); } else { - heap_next = (char *) dmadat + sizeof(*dmadat); - heap_end = (char *) PTOV(bios_basemem); + heap_next = (char *)dmadat + sizeof(*dmadat); + heap_end = (char *)PTOV(bios_basemem); } dsk = malloc(sizeof(struct dsk)); @@ -472,6 +550,9 @@ main(void) autoboot = 1; +#ifdef LOADER_GELI_SUPPORT + geli_init(); +#endif zfs_init(); /* @@ -690,6 +771,12 @@ load(void) zfsargs.pool = zfsmount.spa->spa_guid; zfsargs.root = zfsmount.rootobj; zfsargs.primary_pool = primary_spa->spa_guid; +#ifdef LOADER_GELI_SUPPORT + bcopy(gelipw, zfsargs.gelipw, sizeof(zfsargs.gelipw)); + bzero(gelipw, sizeof(gelipw)); +#else + zfsargs.gelipw[0] = '\0'; +#endif if (primary_vdev != NULL) zfsargs.primary_vdev = primary_vdev->v_guid; else |