Index: sys/boot/geli/geliboot.h =================================================================== --- sys/boot/geli/geliboot.h +++ sys/boot/geli/geliboot.h @@ -46,6 +46,11 @@ #define GELI_MAX_KEYS 64 #define GELI_PW_MAXLEN 256 +enum geli_switch { + GELI_DECRYPT, + GELI_ENCRYPT +}; + extern void pwgets(char *buf, int n); void geli_init(void); @@ -53,14 +58,16 @@ void *buf, size_t bytes), struct dsk *dsk, daddr_t lastsector); int geli_attach(struct dsk *dskp, const char *passphrase, const u_char *mkeyp); int is_geli(struct dsk *dsk); -int geli_read(struct dsk *dsk, off_t offset, u_char *buf, size_t bytes); +int geli_io(struct dsk *dsk, enum geli_switch enc, 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); +int geli_passphrase(char *pw, int disk, int parttype, int part, + struct dsk *dskp); -int geliboot_crypt(u_int algo, int enc, u_char *data, size_t datasize, - const u_char *key, size_t keysize, u_char *iv); +int geliboot_crypt(u_int algo, enum geli_switch enc, u_char *data, + size_t datasize, const u_char *key, size_t keysize, u_char *iv); void geli_fill_keybuf(struct keybuf *keybuf); void geli_save_keybuf(struct keybuf *keybuf); Index: sys/boot/geli/geliboot.c =================================================================== --- sys/boot/geli/geliboot.c +++ sys/boot/geli/geliboot.c @@ -334,7 +334,8 @@ } int -geli_read(struct dsk *dskp, off_t offset, u_char *buf, size_t bytes) +geli_io(struct dsk *dskp, enum geli_switch enc, off_t offset, u_char *buf, + size_t bytes) { u_char iv[G_ELI_IVKEYLEN]; u_char *pbuf; @@ -372,13 +373,14 @@ 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, + error = geliboot_crypt(geli_e->sc.sc_ealgo, enc, 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()!"); + printf("Failed to %s in geli_io()!", + enc ? "encrypt" : "decrypt"); return (error); } pbuf += secsize; Index: sys/boot/geli/geliboot_crypto.c =================================================================== --- sys/boot/geli/geliboot_crypto.c +++ sys/boot/geli/geliboot_crypto.c @@ -35,7 +35,7 @@ #include "geliboot.h" int -geliboot_crypt(u_int algo, int enc, u_char *data, size_t datasize, +geliboot_crypt(u_int algo, enum geli_switch enc, u_char *data, size_t datasize, const u_char *key, size_t keysize, u_char *iv) { keyInstance aeskey; @@ -60,11 +60,11 @@ } switch (enc) { - case 0: /* decrypt */ + case GELI_DECRYPT: /* decrypt */ blks = rijndael_blockDecrypt(&cipher, &aeskey, data, datasize * 8, data); break; - case 1: /* encrypt */ + case GELI_ENCRYPT: /* encrypt */ blks = rijndael_blockEncrypt(&cipher, &aeskey, data, datasize * 8, data); break; @@ -85,12 +85,12 @@ enc_xform_aes_xts.reinit(ctxp, iv); switch (enc) { - case 0: /* decrypt */ + case GELI_DECRYPT: /* decrypt */ for (i = 0; i < datasize; i += AES_XTS_BLOCKSIZE) { enc_xform_aes_xts.decrypt(ctxp, data + i); } break; - case 1: /* encrypt */ + case GELI_ENCRYPT: /* encrypt */ for (i = 0; i < datasize; i += AES_XTS_BLOCKSIZE) { enc_xform_aes_xts.encrypt(ctxp, data + i); } @@ -106,8 +106,8 @@ } static int -g_eli_crypto_cipher(u_int algo, int enc, u_char *data, size_t datasize, - const u_char *key, size_t keysize) +g_eli_crypto_cipher(u_int algo, enum geli_switch enc, u_char *data, + size_t datasize, const u_char *key, size_t keysize) { u_char iv[keysize]; Index: sys/boot/i386/gptboot/gptboot.c =================================================================== --- sys/boot/i386/gptboot/gptboot.c +++ sys/boot/i386/gptboot/gptboot.c @@ -603,7 +603,8 @@ #ifdef LOADER_GELI_SUPPORT if (err == 0 && is_geli(&dsk) == 0) { /* Decrypt */ - if (geli_read(&dsk, lba * DEV_BSIZE, buf, nblk * DEV_BSIZE)) + if (geli_io(&dsk, GELI_DECRYPT, lba * DEV_BSIZE, buf, + nblk * DEV_BSIZE)) return (err); } #endif Index: sys/boot/i386/libi386/biosdisk.c =================================================================== --- sys/boot/i386/libi386/biosdisk.c +++ sys/boot/i386/libi386/biosdisk.c @@ -834,10 +834,6 @@ int err, n, alignblks; char *tmpbuf; - /* 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) { /* * Align reads to DEV_GELIBOOT_BSIZE bytes because partial @@ -882,7 +878,8 @@ /* GELI needs the offset relative to the partition start */ p_off = alignlba - dskp.start; - err = geli_read(&dskp, p_off * BD(dev).bd_sectorsize, tmpbuf, + err = geli_io(&dskp, GELI_DECRYPT, p_off * + BD(dev).bd_sectorsize, tmpbuf, alignblks * BD(dev).bd_sectorsize); if (err) return (err); @@ -902,6 +899,79 @@ bd_write(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 (geli_status[dev->d_unit][dev->d_slice] == ISGELI_YES) { + 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; + /* + * Align writes to DEV_GELIBOOT_BSIZE bytes because partial + * sectors cannot be encrypted. 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; + /* GELI needs the offset relative to the partition start */ + p_off = alignlba - dskp.start; + /* + * 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); + } + /* + * Unaligned writes require read/modify/write. + */ + err = bd_io(dev, alignlba, DEV_GELIBOOT_BSIZE, tmpbuf, + 0); + if (err) + return (err); + /* Decrypt */ + err = geli_io(&dskp, GELI_DECRYPT, p_off * + BD(dev).bd_sectorsize, tmpbuf, DEV_GELIBOOT_BSIZE); + if (err) + return (err); + bcopy(dest, tmpbuf + diff, blks * BD(dev).bd_sectorsize); + } + + err = geli_io(&dskp, GELI_ENCRYPT, + p_off * BD(dev).bd_sectorsize, tmpbuf, + alignblks * BD(dev).bd_sectorsize); + if (err) + return (err); + + err = bd_io(dev, alignlba, alignblks, tmpbuf, 1); + if (err) + return (err); + + if (tmpbuf != dest) { + free(tmpbuf); + } + return (0); + } +#endif /* LOADER_GELI_SUPPORT */ return (bd_io(dev, dblk, blks, dest, 1)); } Index: sys/boot/i386/zfsboot/zfsboot.c =================================================================== --- sys/boot/i386/zfsboot/zfsboot.c +++ sys/boot/i386/zfsboot/zfsboot.c @@ -201,10 +201,14 @@ vdev_read(vdev_t *vdev, void *priv, off_t off, void *buf, size_t bytes) { char *p; - daddr_t lba, alignlba; - off_t diff; - unsigned int nb, alignnb; + daddr_t lba; + unsigned int nb; struct dsk *dsk = (struct dsk *) priv; +#ifdef LOADER_GELI_SUPPORT + daddr_t alignlba; + off_t diff; + unsigned int alignnb; +#endif if ((off & (DEV_BSIZE - 1)) || (bytes & (DEV_BSIZE - 1))) return -1; @@ -212,6 +216,7 @@ p = buf; lba = off / DEV_BSIZE; lba += dsk->start; +#ifdef LOADER_GELI_SUPPORT /* * Align reads to 4k else 4k sector GELIs will not decrypt. * Round LBA down to nearest multiple of DEV_GELIBOOT_BSIZE bytes. @@ -223,9 +228,11 @@ */ alignlba += dsk->start; diff = (lba - alignlba) * DEV_BSIZE; +#endif while (bytes > 0) { nb = bytes / DEV_BSIZE; +#ifdef LOADER_GELI_SUPPORT /* * Ensure that the read size plus the leading offset does not * exceed the size of the read buffer. @@ -241,21 +248,26 @@ if (drvread(dsk, dmadat->rdbuf, alignlba, alignnb)) return -1; -#ifdef LOADER_GELI_SUPPORT /* decrypt */ if (is_geli(dsk) == 0) { - if (geli_read(dsk, ((alignlba - dsk->start) * - DEV_BSIZE), dmadat->rdbuf, alignnb * DEV_BSIZE)) + if (geli_io(dsk, GELI_DECRYPT, ((alignlba - dsk->start) + * DEV_BSIZE), dmadat->rdbuf, alignnb * DEV_BSIZE)) return (-1); } -#endif memcpy(p, dmadat->rdbuf + diff, nb * DEV_BSIZE); - p += nb * DEV_BSIZE; - lba += nb; alignlba += alignnb; - bytes -= nb * DEV_BSIZE; /* Don't need the leading offset after the first block. */ diff = 0; +#else + if (nb > READ_BUF_SIZE / DEV_BSIZE) + nb = READ_BUF_SIZE / DEV_BSIZE; + if (drvread(dsk, dmadat->rdbuf, lba, nb)) + return -1; + memcpy(p, dmadat->rdbuf, nb * DEV_BSIZE); +#endif + p += nb * DEV_BSIZE; + lba += nb; + bytes -= nb * DEV_BSIZE; } return 0; @@ -268,6 +280,11 @@ daddr_t lba; unsigned int nb; struct dsk *dsk = (struct dsk *) priv; +#ifdef LOADER_GELI_SUPPORT + daddr_t alignlba; + off_t diff; + unsigned int alignnb; +#endif if ((off & (DEV_BSIZE - 1)) || (bytes & (DEV_BSIZE - 1))) return -1; @@ -275,13 +292,69 @@ p = buf; lba = off / DEV_BSIZE; lba += dsk->start; +#ifdef LOADER_GELI_SUPPORT + /* + * Align writes to 4k else 4k sector GELIs will not decrypt. + * Round LBA down to nearest multiple of DEV_GELIBOOT_BSIZE bytes. + */ + alignlba = rounddown2(off, DEV_GELIBOOT_BSIZE) / DEV_BSIZE; + /* + * 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; + diff = (lba - alignlba) * DEV_BSIZE; +#endif + while (bytes > 0) { nb = bytes / DEV_BSIZE; +#ifdef LOADER_GELI_SUPPORT + /* + * Ensure that the write size plus the leading offset does not + * exceed the size of the buffer. + */ + if (nb > (READ_BUF_SIZE - diff) / DEV_BSIZE) + nb = (READ_BUF_SIZE - diff) / DEV_BSIZE; + /* + * Round the number of blocks to read up to the nearest multiple + * of DEV_GELIBOOT_BSIZE. + */ + alignnb = roundup2(nb * DEV_BSIZE + diff, DEV_GELIBOOT_BSIZE) + / DEV_BSIZE; + + /* + * Unaligned writes require read/modify/write. + */ + if (diff > 0) { + vdev_read(vdev, priv, alignlba * DEV_BSIZE, + dmadat->rdbuf, DEV_GELIBOOT_BSIZE); + if (is_geli(dsk) == 0) { + if (geli_io(dsk, GELI_DECRYPT, + ((alignlba - dsk->start) * + DEV_BSIZE), dmadat->rdbuf, + DEV_GELIBOOT_BSIZE)) + return (-1); + } + } + memcpy(dmadat->rdbuf + diff, p, nb * DEV_BSIZE); + /* encrypt */ + if (is_geli(dsk) == 0) { + if (geli_io(dsk, GELI_ENCRYPT, ((alignlba - dsk->start) + * DEV_BSIZE), dmadat->rdbuf, alignnb * DEV_BSIZE)) + return (-1); + } + if (drvwrite(dsk, dmadat->rdbuf, alignlba, alignnb)) + return -1; + alignlba += alignnb; + /* Don't need the leading offset after the first block. */ + diff = 0; +#else 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)) return -1; +#endif p += nb * DEV_BSIZE; lba += nb; bytes -= nb * DEV_BSIZE; @@ -736,7 +809,7 @@ primary_vdev = spa_get_primary_vdev(spa); nextboot = 0; - rc = vdev_read_pad2(primary_vdev, cmd, sizeof(cmd)); + rc = vdev_read_pad2(primary_vdev, cmd, sizeof(cmd)); if (vdev_clear_pad2(primary_vdev)) printf("failed to clear pad2 area of primary vdev\n"); if (rc == 0) {