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:
parent
891c49fe3e
commit
e6939ede8b
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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_ */
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user