Index: head/stand/i386/gptboot/gptboot.c =================================================================== --- head/stand/i386/gptboot/gptboot.c (revision 363089) +++ head/stand/i386/gptboot/gptboot.c (revision 363090) @@ -1,658 +1,658 @@ /*- * Copyright (c) 1998 Robert Nordier * All rights reserved. * * Redistribution and use in source and binary forms are freely * permitted provided that the above copyright notice and this * paragraph and the following disclaimer are duplicated in all * such forms. * * This software is provided "AS IS" and without any express or * implied warranties, including, without limitation, the implied * warranties of merchantability and fitness for a particular * purpose. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include "stand.h" #include "bootargs.h" #include "lib.h" #include "rbx.h" #include "drv.h" #include "cons.h" #include "gpt.h" #include "paths.h" #define ARGS 0x900 #define NOPT 14 #define NDEV 3 #define MEM_BASE 0x12 #define MEM_EXT 0x15 #define DRV_HARD 0x80 #define DRV_MASK 0x7f #define TYPE_AD 0 #define TYPE_DA 1 #define TYPE_MAXHARD TYPE_DA #define TYPE_FD 2 extern uint32_t _end; static const uuid_t freebsd_ufs_uuid = GPT_ENT_TYPE_FREEBSD_UFS; static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */ static const unsigned char flags[NOPT] = { RBX_DUAL, RBX_SERIAL, RBX_ASKNAME, RBX_CDROM, RBX_CONFIG, RBX_KDB, RBX_GDB, RBX_MUTE, RBX_NOINTR, RBX_PAUSE, RBX_QUIET, RBX_DFLTROOT, RBX_SINGLE, RBX_VERBOSE }; uint32_t opts; static const char *const dev_nm[NDEV] = {"ad", "da", "fd"}; static const unsigned char dev_maj[NDEV] = {30, 4, 2}; static char kname[1024]; static int comspeed = SIOSPD; static struct bootinfo bootinfo; #ifdef LOADER_GELI_SUPPORT static struct geli_boot_args geliargs; #endif 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; static void load(void); static int parse_cmds(char *, int *); static int dskread(void *, daddr_t, unsigned); #ifdef LOADER_GELI_SUPPORT static int vdev_read(void *vdev __unused, void *priv, off_t off, void *buf, size_t bytes); #endif #include "ufsread.c" #include "gpt.c" #ifdef LOADER_GELI_SUPPORT #include "geliboot.h" static char gelipw[GELI_PW_MAXLEN]; #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) { if ((size_t)fsread(inode, buf, nbyte) != nbyte) { printf("Invalid %s\n", "format"); return (-1); } return (0); } static void bios_getmem(void) { uint64_t size; /* 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(); 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 gptinit(void) { if (gptread(&gdsk.dsk, dmadat->secbuf) == -1) { printf("%s: unable to load GPT\n", BOOTPROG); return (-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 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); } } #endif dsk_meta = 0; return (0); } int main(void); int main(void) { char cmd[512], cmdtmp[512]; ssize_t sz; int autoboot, dskupdated; 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); } setheap(heap_next, heap_end); v86.ctl = V86_FLAGS; v86.efl = PSL_RESERVED_DEFAULT | PSL_I; 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 = gdsk.dsk.drive; /* Process configuration file */ if (gptinit() != 0) return (-1); autoboot = 1; *cmd = '\0'; for (;;) { *kname = '\0'; if ((ino = lookup(PATH_CONFIG)) || (ino = lookup(PATH_DOTCONFIG))) { sz = fsread(ino, cmd, sizeof(cmd) - 1); cmd[(sz < 0) ? 0 : sz] = '\0'; } if (*cmd != '\0') { memcpy(cmdtmp, cmd, sizeof(cmdtmp)); if (parse_cmds(cmdtmp, &dskupdated)) break; if (dskupdated && gptinit() != 0) break; if (!OPT_CHECK(RBX_QUIET)) printf("%s: %s", PATH_CONFIG, cmd); *cmd = '\0'; } if (autoboot && keyhit(3)) { if (*kname == '\0') memcpy(kname, PATH_LOADER, sizeof(PATH_LOADER)); break; } autoboot = 0; /* * Try to exec stage 3 boot loader. If interrupted by a * keypress, or in case of failure, try to load a kernel * directly instead. */ if (*kname != '\0') load(); memcpy(kname, PATH_LOADER, sizeof(PATH_LOADER)); load(); memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL)); load(); gptbootfailed(&gdsk.dsk); if (gptfind(&freebsd_ufs_uuid, &gdsk.dsk, -1) == -1) break; dsk_meta = 0; } /* Present the user with the boot2 prompt. */ for (;;) { if (!OPT_CHECK(RBX_QUIET)) { printf("\nFreeBSD/x86 boot\n" "Default: %u:%s(%up%u)%s\n" "boot: ", gdsk.dsk.drive & DRV_MASK, dev_nm[gdsk.dsk.type], gdsk.dsk.unit, gdsk.dsk.part, kname); } if (ioctrl & IO_SERIAL) sio_flush(); *cmd = '\0'; if (keyhit(0)) getstr(cmd, sizeof(cmd)); else if (!OPT_CHECK(RBX_QUIET)) putchar('\n'); if (parse_cmds(cmd, &dskupdated)) { putchar('\a'); continue; } if (dskupdated && gptinit() != 0) continue; load(); } /* NOTREACHED */ } /* XXX - Needed for btxld to link the boot2 binary; do not remove. */ void exit(int x) { while (1); __unreachable(); } static void load(void) { union { struct exec ex; Elf32_Ehdr eh; } hdr; static Elf32_Phdr ep[2]; static Elf32_Shdr es[2]; caddr_t p; ufs_ino_t ino; uint32_t addr, x; int fmt, i, j; if (!(ino = lookup(kname))) { if (!ls) { printf("%s: No %s on %u:%s(%up%u)\n", BOOTPROG, kname, gdsk.dsk.drive & DRV_MASK, dev_nm[gdsk.dsk.type], gdsk.dsk.unit, gdsk.dsk.part); } return; } if (xfsread(ino, &hdr, sizeof(hdr))) return; if (N_GETMAGIC(hdr.ex) == ZMAGIC) fmt = 0; else if (IS_ELF(hdr.eh)) fmt = 1; else { printf("Invalid %s\n", "format"); return; } if (fmt == 0) { addr = hdr.ex.a_entry & 0xffffff; p = PTOV(addr); fs_off = PAGE_SIZE; if (xfsread(ino, p, hdr.ex.a_text)) return; p += roundup2(hdr.ex.a_text, PAGE_SIZE); if (xfsread(ino, p, hdr.ex.a_data)) return; p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE); bootinfo.bi_symtab = VTOP(p); memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms)); p += sizeof(hdr.ex.a_syms); if (hdr.ex.a_syms) { if (xfsread(ino, p, hdr.ex.a_syms)) return; p += hdr.ex.a_syms; if (xfsread(ino, p, sizeof(int))) return; x = *(uint32_t *)p; p += sizeof(int); x -= sizeof(int); if (xfsread(ino, p, x)) return; p += x; } } else { fs_off = hdr.eh.e_phoff; for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) { if (xfsread(ino, ep + j, sizeof(ep[0]))) return; if (ep[j].p_type == PT_LOAD) j++; } for (i = 0; i < 2; i++) { p = PTOV(ep[i].p_paddr & 0xffffff); fs_off = ep[i].p_offset; if (xfsread(ino, p, ep[i].p_filesz)) return; } p += roundup2(ep[1].p_memsz, PAGE_SIZE); bootinfo.bi_symtab = VTOP(p); if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) { fs_off = hdr.eh.e_shoff + sizeof(es[0]) * (hdr.eh.e_shstrndx + 1); if (xfsread(ino, &es, sizeof(es))) return; for (i = 0; i < 2; i++) { memcpy(p, &es[i].sh_size, sizeof(es[i].sh_size)); p += sizeof(es[i].sh_size); fs_off = es[i].sh_offset; if (xfsread(ino, p, es[i].sh_size)) return; p += es[i].sh_size; } } addr = hdr.eh.e_entry & 0xffffff; } bootinfo.bi_esymtab = VTOP(p); bootinfo.bi_kernelname = VTOP(kname); bootinfo.bi_bios_dev = gdsk.dsk.drive; #ifdef LOADER_GELI_SUPPORT geliargs.size = sizeof(geliargs); explicit_bzero(gelipw, sizeof(gelipw)); export_geli_boot_data(&geliargs.gelidata); #endif /* * Note that the geliargs struct is passed by value, not by pointer. * Code in btxldr.S copies the values from the entry stack to a fixed * location within loader(8) at startup due to the presence of the * KARGS_FLAGS_EXTARG flag. */ __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK), MAKEBOOTDEV(dev_maj[gdsk.dsk.type], gdsk.dsk.part + 1, gdsk.dsk.unit, 0xff), #ifdef LOADER_GELI_SUPPORT KARGS_FLAGS_GELI | KARGS_FLAGS_EXTARG, 0, 0, VTOP(&bootinfo), geliargs #else 0, 0, 0, VTOP(&bootinfo) #endif ); } static int parse_cmds(char *cmdstr, int *dskupdated) { char *arg; char *ep, *p, *q; const char *cp; unsigned int drv; int c, i, j; arg = cmdstr; *dskupdated = 0; while ((c = *arg++)) { if (c == ' ' || c == '\t' || c == '\n') continue; for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++); ep = p; if (*p) *p++ = 0; if (c == '-') { while ((c = *arg++)) { if (c == 'P') { if (*(uint8_t *)PTOV(0x496) & 0x10) { cp = "yes"; } else { opts |= OPT_SET(RBX_DUAL) | OPT_SET(RBX_SERIAL); cp = "no"; } printf("Keyboard: %s\n", cp); continue; } else if (c == 'S') { j = 0; while ((unsigned int)(i = *arg++ - '0') <= 9) j = j * 10 + i; if (j > 0 && i == -'0') { comspeed = j; break; } /* * Fall through to error below * ('S' not in optstr[]). */ } for (i = 0; c != optstr[i]; i++) if (i == NOPT - 1) return (-1); opts ^= OPT_SET(flags[i]); } ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) : OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD; if (ioctrl & IO_SERIAL) { if (sio_init(115200 / comspeed) != 0) ioctrl &= ~IO_SERIAL; } } else { for (q = arg--; *q && *q != '('; q++); if (*q) { drv = -1; if (arg[1] == ':') { drv = *arg - '0'; if (drv > 9) return (-1); arg += 2; } if (q - arg != 2) return (-1); for (i = 0; arg[0] != dev_nm[i][0] || arg[1] != dev_nm[i][1]; i++) if (i == NDEV - 1) return (-1); gdsk.dsk.type = i; arg += 3; gdsk.dsk.unit = *arg - '0'; if (arg[1] != 'p' || gdsk.dsk.unit > 9) return (-1); arg += 2; j = 0; while (*arg >= '0' && *arg <= '9') j = j * 10 + *arg++ - '0'; gdsk.dsk.part = j; if (gdsk.dsk.part < 1 || gdsk.dsk.part > 128) return (-1); if (arg[0] != ')') return (-1); arg++; if (drv == -1) drv = gdsk.dsk.unit; gdsk.dsk.drive = (gdsk.dsk.type <= TYPE_MAXHARD ? DRV_HARD : 0) + drv; *dskupdated = 1; } if ((i = ep - arg)) { if ((size_t)i >= sizeof(kname)) return (-1); memcpy(kname, arg, i + 1); } } arg = p; } return (0); } static int dskread(void *buf, daddr_t lba, unsigned nblk) { int err; err = drvread(&gdsk.dsk, buf, lba + gdsk.dsk.start, nblk); #ifdef LOADER_GELI_SUPPORT if (err == 0 && gdsk.gdev != NULL) { /* Decrypt */ - if (geli_read(gdsk.gdev, lba * DEV_BSIZE, buf, + if (geli_io(gdsk.gdev, GELI_DECRYPT, lba * DEV_BSIZE, buf, nblk * DEV_BSIZE)) return (err); } #endif return (err); } #ifdef LOADER_GELI_SUPPORT /* * 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) { char *p; daddr_t lba; unsigned int nb; struct gptdsk *dskp; dskp = (struct gptdsk *)priv; if ((off & (DEV_BSIZE - 1)) || (bytes & (DEV_BSIZE - 1))) return (-1); p = buf; lba = off / DEV_BSIZE; lba += dskp->dsk.start; while (bytes > 0) { nb = bytes / DEV_BSIZE; if (nb > VBLKSIZE / DEV_BSIZE) nb = VBLKSIZE / DEV_BSIZE; if (drvread(&dskp->dsk, 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 */ Index: head/stand/libsa/geli/geliboot.c =================================================================== --- head/stand/libsa/geli/geliboot.c (revision 363089) +++ head/stand/libsa/geli/geliboot.c (revision 363090) @@ -1,398 +1,400 @@ /*- * Copyright (c) 2015 Allan Jude * Copyright (c) 2005-2011 Pawel Jakub Dawidek * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE 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 "geliboot.h" #include "geliboot_internal.h" struct known_dev { char name[GELIDEV_NAMELEN]; struct geli_dev *gdev; SLIST_ENTRY(known_dev) entries; }; 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; /* * Copy keys from local storage to the keybuf struct. * Destroy the local storage when finished. */ void geli_export_key_buffer(struct keybuf *fkeybuf) { unsigned int i; for (i = 0; i < nsaved_keys; i++) { fkeybuf->kb_ents[i].ke_type = KEYBUF_TYPE_GELI; memcpy(fkeybuf->kb_ents[i].ke_data, saved_keys[i], G_ELI_USERKEYLEN); } fkeybuf->kb_nents = nsaved_keys; explicit_bzero(saved_keys, sizeof(saved_keys)); } /* * Copy keys from a keybuf struct into local storage. * Zero out the keybuf. */ void geli_import_key_buffer(struct keybuf *skeybuf) { unsigned int i; for (i = 0; i < skeybuf->kb_nents && i < GELI_MAX_KEYS; i++) { memcpy(saved_keys[i], skeybuf->kb_ents[i].ke_data, G_ELI_USERKEYLEN); explicit_bzero(skeybuf->kb_ents[i].ke_data, G_ELI_USERKEYLEN); skeybuf->kb_ents[i].ke_type = KEYBUF_TYPE_NONE; } nsaved_keys = skeybuf->kb_nents; skeybuf->kb_nents = 0; } void geli_add_key(geli_ukey key) { /* * If we run out of key space, the worst that will happen is * it will ask the user for the password again. */ if (nsaved_keys < GELI_MAX_KEYS) { memcpy(saved_keys[nsaved_keys], key, G_ELI_USERKEYLEN); nsaved_keys++; } } static int geli_findkey(struct geli_dev *gdev, u_char *mkey) { u_int keynum; int i; 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(&gdev->md, saved_keys[i], mkey, &keynum) == 0) { gdev->keybuf_slot = i; return (0); } } return (1); } /* * Read the last sector of a drive or partition and see if it is GELI encrypted. */ struct geli_dev * geli_taste(geli_readfunc readfunc, void *readpriv, daddr_t lastsector, const char *namefmt, ...) { va_list args; struct g_eli_metadata md; 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; } error = readfunc(NULL, readpriv, alignsector, buf, DEV_GELIBOOT_BSIZE); if (error != 0) { 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) { /* Try the last 512 byte sector instead. */ error = eli_metadata_decode(buf + (DEV_GELIBOOT_BSIZE - DEV_BSIZE), &md); if (error != 0) { goto out; } } if (!(md.md_flags & G_ELI_FLAG_GELIBOOT)) { /* The GELIBOOT feature is not activated */ goto out; } if ((md.md_flags & G_ELI_FLAG_ONETIME)) { /* Swap device, skip it. */ goto out; } /* * 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); kdev->gdev = gdev; out: free(buf); if (kdev == NULL) return (NULL); return (kdev->gdev); } /* * 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_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; struct hmac_ctx ctx; int error; if (mkeyp != NULL) { memcpy(&mkey, mkeyp, G_ELI_DATAIVKEYLEN); explicit_bzero(mkeyp, G_ELI_DATAIVKEYLEN); goto found_key; } if (geli_findkey(gdev, mkey) == 0) { goto found_key; } g_eli_crypto_hmac_init(&ctx, NULL, 0); /* * Prepare Derived-Key from the user passphrase. */ if (gdev->md.md_iterations < 0) { /* XXX TODO: Support loading key files. */ return (1); } 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 (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), 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(&gdev->md, key, mkey, &keynum); if (error == -1) { explicit_bzero(mkey, sizeof(mkey)); explicit_bzero(key, sizeof(key)); printf("Bad GELI key: bad password?\n"); return (error); } else if (error != 0) { explicit_bzero(mkey, sizeof(mkey)); explicit_bzero(key, sizeof(key)); printf("Failed to decrypt GELI master key: %d\n", error); return (error); } else { /* Add key to keychain */ geli_add_key(key); explicit_bzero(&key, sizeof(key)); } found_key: /* Store the keys */ 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, gdev->sc.sc_ekey, 0); } explicit_bzero(mkey, sizeof(mkey)); /* Initialize the per-sector IV. */ switch (gdev->sc.sc_ealgo) { case CRYPTO_AES_XTS: break; default: SHA256_Init(&gdev->sc.sc_ivctx); SHA256_Update(&gdev->sc.sc_ivctx, gdev->sc.sc_ivkey, sizeof(gdev->sc.sc_ivkey)); break; } return (0); } int -geli_read(struct geli_dev *gdev, off_t offset, u_char *buf, size_t bytes) +geli_io(struct geli_dev *gdev, geli_op_t enc, off_t offset, u_char *buf, + size_t bytes) { u_char iv[G_ELI_IVKEYLEN]; u_char *pbuf; int error; off_t dstoff; uint64_t keyno; size_t n, nsec, secsize; struct g_eli_key gkey; pbuf = buf; 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) { 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(&gdev->sc, &gkey, keyno); - error = geliboot_crypt(gdev->sc.sc_ealgo, 0, pbuf, secsize, + error = geliboot_crypt(gdev->sc.sc_ealgo, enc, 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()!"); + printf("%s: Failed to %s!", __func__, + enc ? "encrypt" : "decrypt"); return (error); } pbuf += secsize; } explicit_bzero(&gkey, sizeof(gkey)); return (0); } int geli_havekey(struct geli_dev *gdev) { u_char mkey[G_ELI_DATAIVKEYLEN]; int err; err = ENOENT; if (geli_findkey(gdev, mkey) == 0) { if (geli_probe(gdev, NULL, mkey) == 0) err = 0; explicit_bzero(mkey, sizeof(mkey)); } return (err); } int geli_passphrase(struct geli_dev *gdev, char *pw) { int i; /* 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: head/stand/libsa/geli/geliboot.h =================================================================== --- head/stand/libsa/geli/geliboot.h (revision 363089) +++ head/stand/libsa/geli/geliboot.h (revision 363090) @@ -1,95 +1,101 @@ /*- * Copyright (c) 2015 Allan Jude * Copyright (c) 2005-2011 Pawel Jakub Dawidek * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE 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 #ifndef _GELIBOOT_H_ #define _GELIBOOT_H_ #include #ifndef DEV_BSIZE #define DEV_BSIZE 512 #endif #ifndef DEV_GELIBOOT_BSIZE #define DEV_GELIBOOT_BSIZE 4096 #endif #ifndef MIN #define MIN(a,b) (((a) < (b)) ? (a) : (b)) #endif #define GELI_MAX_KEYS 64 #define GELI_PW_MAXLEN 256 #define GELI_KEYBUF_SIZE (sizeof(struct keybuf) + \ (GELI_MAX_KEYS * sizeof(struct keybuf_ent))) +typedef enum geli_op { + GELI_DECRYPT, + GELI_ENCRYPT +} geli_op_t; + extern void pwgets(char *buf, int n, int hide); 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; /* * 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, +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_io(struct geli_dev *gdev, geli_op_t, 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_ */ Index: head/stand/libsa/geli/geliboot_crypto.c =================================================================== --- head/stand/libsa/geli/geliboot_crypto.c (revision 363089) +++ head/stand/libsa/geli/geliboot_crypto.c (revision 363090) @@ -1,139 +1,143 @@ /*- * Copyright (c) 2005-2010 Pawel Jakub Dawidek * Copyright (c) 2015 Allan Jude * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE 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 "geliboot_internal.h" #include "geliboot.h" int -geliboot_crypt(u_int algo, int enc, u_char *data, size_t datasize, +geliboot_crypt(u_int algo, geli_op_t enc, u_char *data, size_t datasize, const u_char *key, size_t keysize, u_char *iv) { keyInstance aeskey; cipherInstance cipher; struct aes_xts_ctx xtsctx, *ctxp; size_t xts_len; int err, blks, i; switch (algo) { case CRYPTO_AES_CBC: err = rijndael_makeKey(&aeskey, !enc, keysize, (const char *)key); if (err < 0) { - printf("Failed to setup decryption keys: %d\n", err); + printf("Failed to setup crypo keys: %d\n", err); return (err); } err = rijndael_cipherInit(&cipher, MODE_CBC, iv); if (err < 0) { printf("Failed to setup IV: %d\n", err); return (err); } - if (enc == 0) { - /* decrypt */ + switch (enc) { + case GELI_DECRYPT: blks = rijndael_blockDecrypt(&cipher, &aeskey, data, datasize * 8, data); - } else { - /* encrypt */ + break; + case GELI_ENCRYPT: blks = rijndael_blockEncrypt(&cipher, &aeskey, data, datasize * 8, data); + break; } if (datasize != (blks / 8)) { - printf("Failed to decrypt the entire input: " - "%u != %zu\n", blks, datasize); + printf("Failed to %s the entire input: %u != %zu\n", + enc ? "decrypt" : "encrypt", + blks, datasize); return (1); } break; case CRYPTO_AES_XTS: xts_len = keysize << 1; ctxp = &xtsctx; enc_xform_aes_xts.setkey(ctxp, key, xts_len / 8); enc_xform_aes_xts.reinit(ctxp, iv); switch (enc) { - case 0: /* decrypt */ + case GELI_DECRYPT: for (i = 0; i < datasize; i += AES_XTS_BLOCKSIZE) { enc_xform_aes_xts.decrypt(ctxp, data + i, data + i); } break; - case 1: /* encrypt */ + case GELI_ENCRYPT: for (i = 0; i < datasize; i += AES_XTS_BLOCKSIZE) { enc_xform_aes_xts.encrypt(ctxp, data + i, - data + 1); + data + i); } break; } break; default: printf("Unsupported crypto algorithm #%d\n", algo); return (1); } return (0); } static int -g_eli_crypto_cipher(u_int algo, int enc, u_char *data, size_t datasize, +g_eli_crypto_cipher(u_int algo, geli_op_t enc, u_char *data, size_t datasize, const u_char *key, size_t keysize) { u_char iv[keysize]; explicit_bzero(iv, sizeof(iv)); return (geliboot_crypt(algo, enc, data, datasize, key, keysize, iv)); } int g_eli_crypto_encrypt(u_int algo, u_char *data, size_t datasize, const u_char *key, size_t keysize) { /* We prefer AES-CBC for metadata protection. */ if (algo == CRYPTO_AES_XTS) algo = CRYPTO_AES_CBC; - return (g_eli_crypto_cipher(algo, 1, data, datasize, key, keysize)); + return (g_eli_crypto_cipher(algo, GELI_ENCRYPT, data, datasize, key, + keysize)); } int g_eli_crypto_decrypt(u_int algo, u_char *data, size_t datasize, const u_char *key, size_t keysize) { /* We prefer AES-CBC for metadata protection. */ if (algo == CRYPTO_AES_XTS) algo = CRYPTO_AES_CBC; - return (g_eli_crypto_cipher(algo, 0, data, datasize, key, keysize)); + return (g_eli_crypto_cipher(algo, GELI_DECRYPT, data, datasize, key, + keysize)); } Index: head/stand/libsa/geli/geliboot_internal.h =================================================================== --- head/stand/libsa/geli/geliboot_internal.h (revision 363089) +++ head/stand/libsa/geli/geliboot_internal.h (revision 363090) @@ -1,71 +1,73 @@ /*- * Copyright (c) 2015 Allan Jude * Copyright (c) 2005-2011 Pawel Jakub Dawidek * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE 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$ */ #ifndef _GELIBOOT_INTERNAL_H_ #define _GELIBOOT_INTERNAL_H_ #define _STRING_H_ #define _STRINGS_H_ #define _STDIO_H_ #include #include #include #include #include /* Pull in the md5, sha256, and sha512 implementations */ #include #include #include /* Pull in AES implementation */ #include /* AES-XTS implementation */ #define _STAND 1 #define STAND_H /* We don't want stand.h in {gpt,zfs,gptzfs}boot */ #include +#include "geliboot.h" + #define GELIDEV_NAMELEN 32 struct geli_dev { off_t part_end; struct g_eli_softc sc; struct g_eli_metadata md; int keybuf_slot; char *name; /* for prompting; it ends in ':' */ }; -int geliboot_crypt(u_int algo, int enc, u_char *data, size_t datasize, +int geliboot_crypt(u_int algo, geli_op_t enc, u_char *data, size_t datasize, const u_char *key, size_t keysize, u_char *iv); #endif /* _GELIBOOT_INTERNAL_H_ */ Index: head/stand/libsa/geli/gelidev.c =================================================================== --- head/stand/libsa/geli/gelidev.c (revision 363089) +++ head/stand/libsa/geli/gelidev.c (revision 363090) @@ -1,323 +1,348 @@ /*- * 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 alignment requires us to read/write more than the size of the + * provided buffer, allocate a temporary buffer. + * The writes will always get temporary buffer because of encryption. */ - if (alnsize <= size) + if (alnsize <= size && (rw & F_MASK) == F_READ) 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; + switch (rw & F_MASK) { + case F_READ: + /* + * 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_io(gdesc->gdev, GELI_DECRYPT, 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 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; + if (rsize != NULL) + *rsize = size; + break; + case F_WRITE: + if (iobuf != buf) { + /* Read, decrypt, then modify. */ + rc = gdesc->hdesc->dd.d_dev->dv_strategy(gdesc->hdesc, + F_READ, alnstart / DEV_BSIZE, alnsize, iobuf, NULL); + if (rc != 0) + goto out; + rc = geli_io(gdesc->gdev, GELI_DECRYPT, alnstart, iobuf, + alnsize); + if (rc != 0) + goto out; + /* Copy data to iobuf */ + memcpy(iobuf + (reqstart - alnstart), buf, size); + } + + /* Encrypt and write it. */ + rc = geli_io(gdesc->gdev, GELI_ENCRYPT, alnstart, iobuf, + alnsize); + if (rc != 0) + goto out; + rc = gdesc->hdesc->dd.d_dev->dv_strategy(gdesc->hdesc, + rw, alnstart / DEV_BSIZE, alnsize, iobuf, NULL); + } 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_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; }