#include <stand.h>
#include <stdarg.h>
#include "geliboot.h"
#include "geliboot_internal.h"
struct known_dev {
char name[GELIDEV_NAMELEN];
struct geli_dev *gdev;
SLIST_ENTRY(known_dev) entries;
};
SLIST_HEAD(known_dev_list, known_dev) known_devs_head =
SLIST_HEAD_INITIALIZER(known_devs_head);
static geli_ukey saved_keys[GELI_MAX_KEYS];
static unsigned int nsaved_keys = 0;
void
geli_export_key_buffer(struct keybuf *fkeybuf)
{
unsigned int i;
for (i = 0; i < nsaved_keys; i++) {
fkeybuf->kb_ents[i].ke_type = KEYBUF_TYPE_GELI;
memcpy(fkeybuf->kb_ents[i].ke_data, saved_keys[i],
G_ELI_USERKEYLEN);
}
fkeybuf->kb_nents = nsaved_keys;
explicit_bzero(saved_keys, sizeof(saved_keys));
}
void
geli_import_key_buffer(struct keybuf *skeybuf)
{
unsigned int i;
for (i = 0; i < skeybuf->kb_nents && i < GELI_MAX_KEYS; i++) {
memcpy(saved_keys[i], skeybuf->kb_ents[i].ke_data,
G_ELI_USERKEYLEN);
explicit_bzero(skeybuf->kb_ents[i].ke_data,
G_ELI_USERKEYLEN);
skeybuf->kb_ents[i].ke_type = KEYBUF_TYPE_NONE;
}
nsaved_keys = skeybuf->kb_nents;
skeybuf->kb_nents = 0;
}
void
geli_add_key(geli_ukey key)
{
if (nsaved_keys < GELI_MAX_KEYS) {
memcpy(saved_keys[nsaved_keys], key, G_ELI_USERKEYLEN);
nsaved_keys++;
}
}
static int
geli_findkey(struct geli_dev *gdev, u_char *mkey)
{
u_int keynum;
int i;
if (gdev->keybuf_slot >= 0) {
if (g_eli_mkey_decrypt_any(&gdev->md, saved_keys[gdev->keybuf_slot],
mkey, &keynum) == 0) {
return (0);
}
}
for (i = 0; i < nsaved_keys; i++) {
if (g_eli_mkey_decrypt_any(&gdev->md, saved_keys[i], mkey,
&keynum) == 0) {
gdev->keybuf_slot = i;
return (0);
}
}
return (1);
}
struct geli_dev *
geli_taste(geli_readfunc readfunc, void *readpriv, daddr_t lastsector,
const char *namefmt, ...)
{
va_list args;
struct g_eli_metadata md;
struct known_dev *kdev;
struct geli_dev *gdev;
u_char *buf;
char devname[GELIDEV_NAMELEN];
int error;
off_t alignsector;
va_start(args, namefmt);
vsnprintf(devname, sizeof(devname), namefmt, args);
va_end(args);
SLIST_FOREACH(kdev, &known_devs_head, entries) {
if (strcmp(kdev->name, devname) == 0)
return (kdev->gdev);
}
if ((buf = malloc(DEV_GELIBOOT_BSIZE)) == NULL)
goto out;
alignsector = rounddown2(lastsector * DEV_BSIZE, DEV_GELIBOOT_BSIZE);
if (alignsector + DEV_GELIBOOT_BSIZE > ((lastsector + 1) * DEV_BSIZE)) {
alignsector = (lastsector * DEV_BSIZE) + DEV_BSIZE -
DEV_GELIBOOT_BSIZE;
}
error = readfunc(NULL, readpriv, alignsector, buf, DEV_GELIBOOT_BSIZE);
if (error != 0) {
goto out;
}
if ((kdev = malloc(sizeof(*kdev))) == NULL)
goto out;
strlcpy(kdev->name, devname, sizeof(kdev->name));
kdev->gdev = NULL;
SLIST_INSERT_HEAD(&known_devs_head, kdev, entries);
error = eli_metadata_decode(buf, &md);
if (error != 0) {
error = eli_metadata_decode(buf +
(DEV_GELIBOOT_BSIZE - DEV_BSIZE), &md);
if (error != 0) {
goto out;
}
}
if (!(md.md_flags & G_ELI_FLAG_GELIBOOT)) {
goto out;
}
if ((md.md_flags & G_ELI_FLAG_ONETIME)) {
goto out;
}
gdev = malloc(sizeof(struct geli_dev));
if (gdev == NULL)
goto out;
gdev->part_end = lastsector;
gdev->keybuf_slot = -1;
gdev->md = md;
gdev->name = kdev->name;
eli_metadata_softc(&gdev->sc, &md, DEV_BSIZE,
(lastsector + DEV_BSIZE) * DEV_BSIZE);
kdev->gdev = gdev;
out:
free(buf);
if (kdev == NULL)
return (NULL);
return (kdev->gdev);
}
static int
geli_probe(struct geli_dev *gdev, const char *passphrase, u_char *mkeyp)
{
u_char key[G_ELI_USERKEYLEN], mkey[G_ELI_DATAIVKEYLEN], *mkp;
u_int keynum;
struct hmac_ctx ctx;
int error;
if (mkeyp != NULL) {
memcpy(&mkey, mkeyp, G_ELI_DATAIVKEYLEN);
explicit_bzero(mkeyp, G_ELI_DATAIVKEYLEN);
goto found_key;
}
if (geli_findkey(gdev, mkey) == 0) {
goto found_key;
}
g_eli_crypto_hmac_init(&ctx, NULL, 0);
if (gdev->md.md_iterations < 0) {
return (1);
} else if (gdev->md.md_iterations == 0) {
g_eli_crypto_hmac_update(&ctx, gdev->md.md_salt,
sizeof(gdev->md.md_salt));
g_eli_crypto_hmac_update(&ctx, (const uint8_t *)passphrase,
strlen(passphrase));
} else if (gdev->md.md_iterations > 0) {
printf("Calculating GELI Decryption Key for %s %d"
" iterations...\n", gdev->name, gdev->md.md_iterations);
u_char dkey[G_ELI_USERKEYLEN];
pkcs5v2_genkey(dkey, sizeof(dkey), gdev->md.md_salt,
sizeof(gdev->md.md_salt), passphrase,
gdev->md.md_iterations);
g_eli_crypto_hmac_update(&ctx, dkey, sizeof(dkey));
explicit_bzero(dkey, sizeof(dkey));
}
g_eli_crypto_hmac_final(&ctx, key, 0);
error = g_eli_mkey_decrypt_any(&gdev->md, key, mkey, &keynum);
if (error == -1) {
explicit_bzero(mkey, sizeof(mkey));
explicit_bzero(key, sizeof(key));
printf("Bad GELI key: bad password?\n");
return (error);
} else if (error != 0) {
explicit_bzero(mkey, sizeof(mkey));
explicit_bzero(key, sizeof(key));
printf("Failed to decrypt GELI master key: %d\n", error);
return (error);
} else {
geli_add_key(key);
explicit_bzero(&key, sizeof(key));
}
found_key:
bcopy(mkey, gdev->sc.sc_mkey, sizeof(gdev->sc.sc_mkey));
bcopy(mkey, gdev->sc.sc_ivkey, sizeof(gdev->sc.sc_ivkey));
mkp = mkey + sizeof(gdev->sc.sc_ivkey);
if ((gdev->sc.sc_flags & G_ELI_FLAG_AUTH) == 0) {
bcopy(mkp, gdev->sc.sc_ekey, G_ELI_DATAKEYLEN);
} else {
g_eli_crypto_hmac(mkp, G_ELI_MAXKEYLEN, (const uint8_t *)"\x10", 1,
gdev->sc.sc_ekey, 0);
}
explicit_bzero(mkey, sizeof(mkey));
switch (gdev->sc.sc_ealgo) {
case CRYPTO_AES_XTS:
break;
default:
SHA256_Init(&gdev->sc.sc_ivctx);
SHA256_Update(&gdev->sc.sc_ivctx, gdev->sc.sc_ivkey,
sizeof(gdev->sc.sc_ivkey));
break;
}
return (0);
}
int
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;
int error;
off_t dstoff;
uint64_t keyno;
size_t n, nsec, secsize;
struct g_eli_key gkey;
pbuf = buf;
secsize = gdev->sc.sc_sectorsize;
nsec = bytes / secsize;
if (nsec == 0) {
secsize = bytes;
nsec = 1;
}
for (n = 0, dstoff = offset; n < nsec; n++, dstoff += secsize) {
g_eli_crypto_ivgen(&gdev->sc, dstoff, iv, G_ELI_IVKEYLEN);
keyno = (dstoff >> G_ELI_KEY_SHIFT) / secsize;
g_eli_key_fill(&gdev->sc, &gkey, keyno);
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("%s: Failed to %s!", __func__,
enc ? "encrypt" : "decrypt");
return (error);
}
pbuf += secsize;
}
explicit_bzero(&gkey, sizeof(gkey));
return (0);
}
int
geli_havekey(struct geli_dev *gdev)
{
u_char mkey[G_ELI_DATAIVKEYLEN];
int err;
err = ENOENT;
if (geli_findkey(gdev, mkey) == 0) {
if (geli_probe(gdev, NULL, mkey) == 0)
err = 0;
explicit_bzero(mkey, sizeof(mkey));
}
return (err);
}
int
geli_passphrase(struct geli_dev *gdev, char *pw)
{
int i;
for (i = 0; i < 3; i++) {
if (i == 0 && pw[0] != '\0') {
if (geli_probe(gdev, pw, NULL) == 0) {
return (0);
}
}
printf("GELI Passphrase for %s ", gdev->name);
pwgets(pw, GELI_PW_MAXLEN,
(gdev->md.md_flags & G_ELI_FLAG_GELIDISPLAYPASS) == 0);
printf("\n");
if (geli_probe(gdev, pw, NULL) == 0) {
return (0);
}
}
return (1);
}