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
Obtained from:	https://reviews.freebsd.org/D10236
Differential Revision:	https://reviews.freebsd.org/D25605
This commit is contained in:
Toomas Soome 2020-07-11 06:51:42 +00:00
parent a0b083fbda
commit de776da323
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=363090
6 changed files with 85 additions and 46 deletions

View file

@ -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);
}

View file

@ -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;

View file

@ -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);

View file

@ -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;
@ -82,16 +84,16 @@ geliboot_crypt(u_int algo, int enc, u_char *data, size_t datasize,
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;
}
@ -105,7 +107,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 +125,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 +138,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));
}

View file

@ -55,6 +55,8 @@
#define STAND_H /* We don't want stand.h in {gpt,zfs,gptzfs}boot */
#include <opencrypto/xform_enc.h>
#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_ */

View file

@ -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;
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);