Author: allanjude
Date: Wed Apr  6 23:21:44 2016
New Revision: 297629
URL: https://svnweb.freebsd.org/changeset/base/297629

Log:
  Fix GELIBoot support for GELI sector size is > 512
  
  Add support for 4k sector GELI encrypted partitions to the bootloader
  This is the default created by the installer
  
  Because the IV is different for each sector, and the XTS tweak carries 
forward you can not decrypt a partial sector if the starting offset is not 0
  
  Make boot2 and the loader read in 4k aligned chunks
  
  Reviewed by:  ed, oshogbo
  Sponsored by: ScaleEngine Inc.
  Differential Revision:        https://reviews.freebsd.org/D5820

Modified:
  head/sys/boot/geli/geliboot.c
  head/sys/boot/geli/geliboot.h
  head/sys/boot/i386/libi386/biosdisk.c
  head/sys/boot/i386/zfsboot/zfsboot.c

Modified: head/sys/boot/geli/geliboot.c
==============================================================================
--- head/sys/boot/geli/geliboot.c       Wed Apr  6 23:17:05 2016        
(r297628)
+++ head/sys/boot/geli/geliboot.c       Wed Apr  6 23:21:44 2016        
(r297629)
@@ -73,30 +73,34 @@ geli_taste(int read_func(void *vdev, voi
     size_t bytes), struct dsk *dskp, daddr_t lastsector)
 {
        struct g_eli_metadata md;
-       u_char buf[DEV_BSIZE];
+       u_char buf[DEV_GELIBOOT_BSIZE];
        int error;
+       off_t alignsector;
 
-       error = read_func(NULL, dskp, (off_t) lastsector * DEV_BSIZE, &buf,
-           (size_t) DEV_BSIZE);
+       alignsector = (lastsector * DEV_BSIZE) &
+           ~(off_t)(DEV_GELIBOOT_BSIZE - 1);
+       error = read_func(NULL, dskp, alignsector, &buf, DEV_GELIBOOT_BSIZE);
        if (error != 0) {
                return (error);
        }
-       error = eli_metadata_decode(buf, &md);
+       /* Extract the last DEV_BSIZE bytes from the block. */
+       error = eli_metadata_decode(buf + (DEV_GELIBOOT_BSIZE - DEV_BSIZE),
+           &md);
        if (error != 0) {
                return (error);
        }
 
        if ((md.md_flags & G_ELI_FLAG_ONETIME)) {
-               /* Swap device, skip it */
+               /* Swap device, skip it. */
                return (1);
        }
        if (!(md.md_flags & G_ELI_FLAG_BOOT)) {
-               /* Disk is not GELI boot device, skip it */
+               /* Disk is not GELI boot device, skip it. */
                return (1);
        }
        if (md.md_iterations < 0) {
-               /* XXX TODO: Support loading key files */
-               /* Disk does not have a passphrase, skip it */
+               /* XXX TODO: Support loading key files. */
+               /* Disk does not have a passphrase, skip it. */
                return (1);
        }
        geli_e = malloc(sizeof(struct geli_entry));
@@ -143,7 +147,7 @@ geli_attach(struct dsk *dskp, const char
                 * Prepare Derived-Key from the user passphrase.
                 */
                if (geli_e->md.md_iterations < 0) {
-                       /* XXX TODO: Support loading key files */
+                       /* 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,
@@ -151,8 +155,8 @@ geli_attach(struct dsk *dskp, const char
                        g_eli_crypto_hmac_update(&ctx, passphrase,
                            strlen(passphrase));
                } else if (geli_e->md.md_iterations > 0) {
-                       printf("Calculating GELI Decryption Key disk%dp%d @ %lu 
"
-                           "iterations...\n", dskp->unit,
+                       printf("Calculating GELI Decryption Key disk%dp%d @ %lu"
+                           " iterations...\n", dskp->unit,
                            (dskp->slice > 0 ? dskp->slice : dskp->part),
                            geli_e->md.md_iterations);
                        u_char dkey[G_ELI_USERKEYLEN];
@@ -193,7 +197,7 @@ geli_attach(struct dsk *dskp, const char
                }
                bzero(&mkey, sizeof(mkey));
 
-               /* Initialize the per-sector IV */
+               /* Initialize the per-sector IV. */
                switch (geli_e->sc.sc_ealgo) {
                case CRYPTO_AES_XTS:
                        break;
@@ -207,7 +211,7 @@ geli_attach(struct dsk *dskp, const char
                return (0);
        }
 
-       /* Disk not found */
+       /* Disk not found. */
        return (2);
 }
 
@@ -229,35 +233,49 @@ geli_read(struct dsk *dskp, off_t offset
        u_char iv[G_ELI_IVKEYLEN];
        u_char *pbuf;
        int error;
-       off_t os;
+       off_t dstoff;
        uint64_t keyno;
-       size_t n, nb;
+       size_t n, nsec, secsize;
        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;
                }
 
-               nb = bytes / DEV_BSIZE;
-               for (n = 0; n < nb; n++) {
-                       os = offset + (n * DEV_BSIZE);
-                       pbuf = buf + (n * DEV_BSIZE);
+               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;
+               }
+
+               for (n = 0, dstoff = offset; n < nsec; n++, dstoff += secsize) {
 
-                       g_eli_crypto_ivgen(&geli_e->sc, os, iv, G_ELI_IVKEYLEN);
+                       g_eli_crypto_ivgen(&geli_e->sc, dstoff, iv,
+                           G_ELI_IVKEYLEN);
 
-                       /* Get the key that corresponds to this offset */
-                       keyno = (os >> G_ELI_KEY_SHIFT) / DEV_BSIZE;
+                       /* 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,
-                           DEV_BSIZE, gkey.gek_key, geli_e->sc.sc_ekeylen, iv);
+                           secsize, gkey.gek_key,
+                           geli_e->sc.sc_ekeylen, iv);
 
                        if (error != 0) {
                                bzero(&gkey, sizeof(gkey));
                                printf("Failed to decrypt in geli_read()!");
                                return (error);
                        }
+                       pbuf += secsize;
                }
                bzero(&gkey, sizeof(gkey));
                return (0);

Modified: head/sys/boot/geli/geliboot.h
==============================================================================
--- head/sys/boot/geli/geliboot.h       Wed Apr  6 23:17:05 2016        
(r297628)
+++ head/sys/boot/geli/geliboot.h       Wed Apr  6 23:21:44 2016        
(r297629)
@@ -55,6 +55,9 @@
 #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))

Modified: head/sys/boot/i386/libi386/biosdisk.c
==============================================================================
--- head/sys/boot/i386/libi386/biosdisk.c       Wed Apr  6 23:17:05 2016        
(r297628)
+++ head/sys/boot/i386/libi386/biosdisk.c       Wed Apr  6 23:21:44 2016        
(r297629)
@@ -706,15 +706,38 @@ bd_read(struct disk_devdesc *dev, daddr_
 {
 #ifdef LOADER_GELI_SUPPORT
        struct dsk dskp;
-       off_t p_off;
-       int err, n;
+       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->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);
+               /*
+                * 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 = dblk &
+                   ~(daddr_t)((DEV_GELIBOOT_BSIZE / BIOSDISK_SECSIZE) - 1);
+               /*
+                * Round number of blocks to read up to nearest multiple of
+                * DEV_GELIBOOT_BSIZE
+                */
+               alignblks = blks + (dblk - alignlba) +
+                   ((DEV_GELIBOOT_BSIZE / BIOSDISK_SECSIZE) - 1) &
+                   ~(int)((DEV_GELIBOOT_BSIZE / BIOSDISK_SECSIZE) - 1);
+               diff = (dblk - alignlba) * BIOSDISK_SECSIZE;
+               /*
+                * Use a temporary buffer here because the buffer provided by
+                * the caller may be too small.
+                */
+               tmpbuf = alloca(alignblks * BIOSDISK_SECSIZE);
+
+               err = bd_io(dev, alignlba, alignblks, tmpbuf, 0);
                if (err)
                        return (err);
 
@@ -726,13 +749,14 @@ bd_read(struct disk_devdesc *dev, daddr_
                dskp.start = dev->d_offset;
 
                /* GELI needs the offset relative to the partition start */
-               p_off = dblk - dskp.start;
+               p_off = alignlba - dskp.start;
 
-               err = geli_read(&dskp, p_off * BIOSDISK_SECSIZE, dest,
-                   blks * BIOSDISK_SECSIZE);
+               err = geli_read(&dskp, p_off * BIOSDISK_SECSIZE, tmpbuf,
+                   alignblks * BIOSDISK_SECSIZE);
                if (err)
                        return (err);
 
+               bcopy(tmpbuf + diff, dest, blks * BIOSDISK_SECSIZE);
                return (0);
        }
 #endif /* LOADER_GELI_SUPPORT */

Modified: head/sys/boot/i386/zfsboot/zfsboot.c
==============================================================================
--- head/sys/boot/i386/zfsboot/zfsboot.c        Wed Apr  6 23:17:05 2016        
(r297628)
+++ head/sys/boot/i386/zfsboot/zfsboot.c        Wed Apr  6 23:21:44 2016        
(r297629)
@@ -46,18 +46,20 @@ __FBSDID("$FreeBSD$");
 
 #include "libzfs.h"
 
-#define ARGS           0x900
-#define NOPT           14
-#define NDEV           3
-
-#define BIOS_NUMDRIVES 0x475
-#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
+#define ARGS                   0x900
+#define NOPT                   14
+#define NDEV                   3
+
+#define BIOS_NUMDRIVES         0x475
+#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
+
+#define DEV_GELIBOOT_BSIZE     4096
 
 extern uint32_t _end;
 
@@ -104,13 +106,13 @@ static struct bios_smap smap;
 /*
  * The minimum amount of memory to reserve in bios_extmem for the heap.
  */
-#define        HEAP_MIN        (3 * 1024 * 1024)
+#define        HEAP_MIN                (3 * 1024 * 1024)
 
 static char *heap_next;
 static char *heap_end;
 
 /* Buffers that must not span a 64k boundary. */
-#define READ_BUF_SIZE  8192
+#define READ_BUF_SIZE          8192
 struct dmadat {
        char rdbuf[READ_BUF_SIZE];      /* for reading large things */
        char secbuf[READ_BUF_SIZE];     /* for MBR/disklabel */
@@ -198,8 +200,9 @@ static int
 vdev_read(vdev_t *vdev, void *priv, off_t off, void *buf, size_t bytes)
 {
        char *p;
-       daddr_t lba;
-       unsigned int nb;
+       daddr_t lba, alignlba;
+       off_t alignoff, diff;
+       unsigned int nb, alignnb;
        struct dsk *dsk = (struct dsk *) priv;
 
        if ((off & (DEV_BSIZE - 1)) || (bytes & (DEV_BSIZE - 1)))
@@ -208,24 +211,52 @@ vdev_read(vdev_t *vdev, void *priv, off_
        p = buf;
        lba = off / DEV_BSIZE;
        lba += dsk->start;
+       /* Align reads to 4k else 4k sector GELIs will not decrypt. */
+       alignoff = off & ~ (off_t)(DEV_GELIBOOT_BSIZE - 1);
+       /* Round LBA down to nearest multiple of DEV_GELIBOOT_BSIZE bytes. */
+       alignlba = alignoff / 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;
+
        while (bytes > 0) {
                nb = bytes / DEV_BSIZE;
                if (nb > READ_BUF_SIZE / DEV_BSIZE)
                        nb = READ_BUF_SIZE / DEV_BSIZE;
-               if (drvread(dsk, dmadat->rdbuf, lba, nb))
+               /*
+                * Ensure that the read size plus the leading offset does not
+                * exceed the size of the read buffer.
+                */
+               if (nb * DEV_BSIZE + diff > READ_BUF_SIZE)
+                   nb -= diff / DEV_BSIZE;
+               /*
+                * Round the number of blocks to read up to the nearest multiple
+                * of DEV_GELIBOOT_BSIZE.
+                */
+               alignnb = nb + (diff / DEV_BSIZE) +
+                   (DEV_GELIBOOT_BSIZE / DEV_BSIZE - 1) & ~
+                   (unsigned int)(DEV_GELIBOOT_BSIZE / DEV_BSIZE - 1);
+
+               if (drvread(dsk, dmadat->rdbuf, alignlba, alignnb))
                        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);
+                       if (geli_read(dsk, ((alignlba - dsk->start) *
+                           DEV_BSIZE), dmadat->rdbuf, alignnb * DEV_BSIZE))
+                               return (-1);
                }
 #endif
-               memcpy(p, dmadat->rdbuf, nb * DEV_BSIZE);
+               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;
        }
 
        return 0;
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to