From 1f152c027551e887d150ea837afc06cec3fe5fcf Mon Sep 17 00:00:00 2001 From: Toomas Soome Date: Sat, 11 Jul 2020 06:51:42 +0000 Subject: [PATCH] loader: implement GELI writes Bug: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=247482 This patch is based on initial work from allanjude. PR: 247482 (cherry picked from commit de776da3239ee3edc5f77bd0e48d0bb15262024b) --- stand/i386/gptboot/gptboot.c | 2 +- stand/libsa/geli/geliboot.c | 8 +-- stand/libsa/geli/geliboot.h | 10 +++- stand/libsa/geli/geliboot_crypto.c | 33 ++++++------ stand/libsa/geli/geliboot_internal.h | 4 +- stand/libsa/geli/gelidev.c | 75 ++++++++++++++++++---------- 6 files changed, 86 insertions(+), 46 deletions(-) diff --git a/stand/i386/gptboot/gptboot.c b/stand/i386/gptboot/gptboot.c index 7b5423ede6d..78d876554c8 100644 --- a/stand/i386/gptboot/gptboot.c +++ b/stand/i386/gptboot/gptboot.c @@ -610,7 +610,7 @@ dskread(void *buf, daddr_t lba, unsigned 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); } diff --git a/stand/libsa/geli/geliboot.c b/stand/libsa/geli/geliboot.c index 00b9af93573..954a3ec3404 100644 --- a/stand/libsa/geli/geliboot.c +++ b/stand/libsa/geli/geliboot.c @@ -310,7 +310,8 @@ geli_probe(struct geli_dev *gdev, const char *passphrase, u_char *mkeyp) } 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; @@ -343,12 +344,13 @@ geli_read(struct geli_dev *gdev, off_t offset, u_char *buf, size_t bytes) 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; diff --git a/stand/libsa/geli/geliboot.h b/stand/libsa/geli/geliboot.h index aeebde4b0a2..0c2ef817e97 100644 --- a/stand/libsa/geli/geliboot.h +++ b/stand/libsa/geli/geliboot.h @@ -50,6 +50,11 @@ #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]; @@ -73,9 +78,10 @@ struct preloaded_file; 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); diff --git a/stand/libsa/geli/geliboot_crypto.c b/stand/libsa/geli/geliboot_crypto.c index 82a137c0621..50c62bccf1c 100644 --- a/stand/libsa/geli/geliboot_crypto.c +++ b/stand/libsa/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, geli_op_t enc, u_char *data, size_t datasize, const u_char *key, size_t keysize, u_char *iv) { keyInstance aeskey; @@ -49,7 +49,7 @@ geliboot_crypt(u_int algo, int enc, u_char *data, size_t datasize, 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); } @@ -59,18 +59,20 @@ geliboot_crypt(u_int algo, int enc, u_char *data, size_t datasize, 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; @@ -84,14 +86,15 @@ geliboot_crypt(u_int algo, int enc, u_char *data, size_t datasize, enc_xform_aes_xts.reinit((caddr_t)ctxp, iv); switch (enc) { - case 0: /* decrypt */ + case GELI_DECRYPT: for (i = 0; i < datasize; i += AES_XTS_BLOCKSIZE) { enc_xform_aes_xts.decrypt((caddr_t)ctxp, data + i); } break; - case 1: /* encrypt */ + case GELI_ENCRYPT: for (i = 0; i < datasize; i += AES_XTS_BLOCKSIZE) { - enc_xform_aes_xts.encrypt((caddr_t)ctxp, data + i); + enc_xform_aes_xts.encrypt((caddr_t)ctxp, + data + i); } break; } @@ -105,7 +108,7 @@ geliboot_crypt(u_int algo, int enc, u_char *data, size_t datasize, } 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]; @@ -123,7 +126,8 @@ g_eli_crypto_encrypt(u_int algo, u_char *data, size_t datasize, 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 @@ -135,5 +139,6 @@ g_eli_crypto_decrypt(u_int algo, u_char *data, size_t datasize, 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)); } diff --git a/stand/libsa/geli/geliboot_internal.h b/stand/libsa/geli/geliboot_internal.h index 2352a844f7d..2af74466179 100644 --- a/stand/libsa/geli/geliboot_internal.h +++ b/stand/libsa/geli/geliboot_internal.h @@ -55,6 +55,8 @@ #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 { @@ -65,7 +67,7 @@ struct geli_dev { 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_ */ diff --git a/stand/libsa/geli/gelidev.c b/stand/libsa/geli/gelidev.c index 75125563611..e1444c9b7fc 100644 --- a/stand/libsa/geli/gelidev.c +++ b/stand/libsa/geli/gelidev.c @@ -115,10 +115,6 @@ geli_dev_strategy(void *devdata, int rw, daddr_t blk, size_t size, char *buf, char *iobuf; int rc; - /* We only handle reading; no write support. */ - if ((rw & F_MASK) != F_READ) - return (EOPNOTSUPP); - gdesc = (struct geli_devdesc *)devdata; /* @@ -139,34 +135,63 @@ geli_dev_strategy(void *devdata, int rw, daddr_t blk, size_t size, char *buf, 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; + 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); + } - if (rsize != NULL) - *rsize = 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); -- 2.45.0