Index: sys/boot/common/bootstrap.h =================================================================== --- sys/boot/common/bootstrap.h +++ sys/boot/common/bootstrap.h @@ -121,6 +121,7 @@ }; extern struct console *consoles[]; void cons_probe(void); +void getpwstr(char *pwstr, size_t pwstrsize); /* * Plug-and-play enumerator/configurator interface. Index: sys/boot/common/console.c =================================================================== --- sys/boot/common/console.c +++ sys/boot/common/console.c @@ -261,3 +261,36 @@ return(CMD_OK); } + +void +getpwstr(char *pwstr, size_t pwstrsize) +{ + char *s; + int c; + + s = pwstr; + for (;;) { + switch (c = getchar()) { + case 0: + break; + case '\177': + case '\b': + if (s > pwstr) { + s--; + putchar('\b'); + putchar(' '); + putchar('\b'); + } + break; + case '\n': + case '\r': + *s = 0; + return; + default: + if (s - pwstr < pwstrsize - 1) + *s++ = c; + putchar('*'); + break; + } + } +} Index: sys/boot/common/disk.h =================================================================== --- sys/boot/common/disk.h +++ sys/boot/common/disk.h @@ -107,6 +107,7 @@ u_int blocks); extern int disk_write(struct disk_devdesc *dev, void *buf, off_t offset, u_int blocks); +extern int ptblread(void *d, void *buf, size_t blocks, off_t offset); /* * Print information about slices on a disk. Index: sys/boot/common/disk.c =================================================================== --- sys/boot/common/disk.c +++ sys/boot/common/disk.c @@ -170,7 +170,7 @@ return (buf); } -static int +int ptblread(void *d, void *buf, size_t blocks, off_t offset) { struct disk_devdesc *dev; Index: sys/boot/common/gpt.h =================================================================== --- sys/boot/common/gpt.h +++ sys/boot/common/gpt.h @@ -32,6 +32,8 @@ #include #include +#define MAXTBLENTS 128 + int gptread(const uuid_t *uuid, struct dsk *dskp, char *buf); int gptfind(const uuid_t *uuid, struct dsk *dskp, int part); void gptbootfailed(struct dsk *dskp); Index: sys/boot/common/gpt.c =================================================================== --- sys/boot/common/gpt.c +++ sys/boot/common/gpt.c @@ -39,8 +39,6 @@ #include "util.h" #include "gpt.h" -#define MAXTBLENTS 128 - static struct gpt_hdr hdr_primary, hdr_backup, *gpthdr; static uint64_t hdr_primary_lba, hdr_backup_lba; static struct gpt_ent table_primary[MAXTBLENTS], table_backup[MAXTBLENTS]; Index: sys/boot/geli/geli.h =================================================================== --- /dev/null +++ sys/boot/geli/geli.h @@ -0,0 +1,212 @@ +/*- + * Copyright (c) 2015 Allan Jude + * Copyright (c) 2005-2011 Pawel Jakub Dawidek + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include +#include + +/* + * Stop rijndael and others from pulling in string.h + */ +#define _STRING_H_ + +/* Pull in the sha256 and sha512 implementation */ +#include +#include + +/* + * rijndael uses assert() which in libstand depends on panic() + * which we do not have in the boot loader + */ +#define NDEBUG +/* Pull in AES inplementation */ +#include + +#ifndef DEV_BSIZE +#define DEV_BSIZE 512 +#endif + +#ifndef MIN +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +#define CRYPTO_AES_CBC 11 +#define CRYPTO_SHA2_512_HMAC 20 +#define CRYPTO_AES_XTS 22 + +#define SHA256_DIGEST_LENGTH 32 + +#define G_ELI_VERSION_00 0 +#define G_ELI_VERSION_01 1 +#define G_ELI_VERSION_02 2 +#define G_ELI_VERSION_03 3 +#define G_ELI_VERSION_04 4 +#define G_ELI_VERSION_05 5 +#define G_ELI_VERSION_06 6 +#define G_ELI_VERSION_07 7 +#define G_ELI_VERSION G_ELI_VERSION_07 + +/* ON DISK FLAGS. */ +/* Use random, onetime keys. */ +#define G_ELI_FLAG_ONETIME 0x00000001 +/* Ask for the passphrase from the kernel, before mounting root. */ +#define G_ELI_FLAG_BOOT 0x00000002 +/* Detach on last close, if we were open for writing. */ +#define G_ELI_FLAG_WO_DETACH 0x00000004 +/* Detach on last close. */ +#define G_ELI_FLAG_RW_DETACH 0x00000008 +/* Provide data authentication. */ +#define G_ELI_FLAG_AUTH 0x00000010 +/* Provider is read-only, we should deny all write attempts. */ +#define G_ELI_FLAG_RO 0x00000020 +/* RUNTIME FLAGS. */ +/* Provider was open for writing. */ +#define G_ELI_FLAG_WOPEN 0x00010000 +/* Destroy device. */ +#define G_ELI_FLAG_DESTROY 0x00020000 +/* Provider uses native byte-order for IV generation. */ +#define G_ELI_FLAG_NATIVE_BYTE_ORDER 0x00040000 +/* Provider uses single encryption key. */ +#define G_ELI_FLAG_SINGLE_KEY 0x00080000 +/* Device suspended. */ +#define G_ELI_FLAG_SUSPEND 0x00100000 +/* Provider uses first encryption key. */ +#define G_ELI_FLAG_FIRST_KEY 0x00200000 +/* Provider uses IV-Key for encryption key generation. */ +#define G_ELI_FLAG_ENC_IVKEY 0x00400000 + +#define SHA512_MDLEN 64 +#define G_ELI_AUTH_SECKEYLEN SHA256_DIGEST_LENGTH + +#define G_ELI_MAXMKEYS 2 +#define G_ELI_MAXKEYLEN 64 +#define G_ELI_USERKEYLEN G_ELI_MAXKEYLEN +#define G_ELI_DATAKEYLEN G_ELI_MAXKEYLEN +#define G_ELI_AUTHKEYLEN G_ELI_MAXKEYLEN +#define G_ELI_IVKEYLEN G_ELI_MAXKEYLEN +#define G_ELI_SALTLEN 64 +#define G_ELI_DATAIVKEYLEN (G_ELI_DATAKEYLEN + G_ELI_IVKEYLEN) +/* Data-Key, IV-Key, HMAC_SHA512(Derived-Key, Data-Key+IV-Key) */ +#define G_ELI_MKEYLEN (G_ELI_DATAIVKEYLEN + SHA512_MDLEN) +/* Switch data encryption key every 2^20 blocks. */ +#define G_ELI_KEY_SHIFT 20 + +struct g_eli_metadata { + char md_magic[16]; /* Magic value. */ + uint32_t md_version; /* Version number. */ + uint32_t md_flags; /* Additional flags. */ + uint16_t md_ealgo; /* Encryption algorithm. */ + uint16_t md_keylen; /* Key length. */ + uint16_t md_aalgo; /* Authentication algorithm. */ + uint64_t md_provsize; /* Provider's size. */ + uint32_t md_sectorsize; /* Sector size. */ + uint8_t md_keys; /* Available keys. */ + int32_t md_iterations; /* Number of iterations for PKCS#5v2. */ + uint8_t md_salt[G_ELI_SALTLEN]; /* Salt. */ + /* Encrypted master key (IV-key, Data-key, HMAC). */ + uint8_t md_mkeys[G_ELI_MAXMKEYS * G_ELI_MKEYLEN]; + u_char md_hash[16]; /* MD5 hash. */ +} __packed; + +struct hmac_ctx { + SHA512_CTX shactx; + u_char k_opad[128]; +}; + +static SLIST_HEAD(geli_list, geli_entry) geli_head = SLIST_HEAD_INITIALIZER(geli_head); +static struct geli_list *geli_headp; +static struct geli_entry { + struct dsk *dsk; + off_t part_end; + struct g_eli_metadata md; + uint8_t mkey[G_ELI_DATAIVKEYLEN]; + uint8_t ekey[G_ELI_DATAKEYLEN]; + uint8_t ivkey[G_ELI_IVKEYLEN]; + SHA256_CTX ivctx; + SLIST_ENTRY(geli_entry) entries; +} *geli_e, *geli_e_tmp; + +static int geli_count; + +static __inline int +eli_metadata_decode(const u_char *data, struct g_eli_metadata *md) +{ + const u_char *p; + int error = 0; + + bcopy(data, md->md_magic, sizeof(md->md_magic)); + if (strcmp(md->md_magic, "GEOM::ELI") != 0) + return (1); + md->md_version = le32dec(data + sizeof(md->md_magic)); + if (md->md_version < 1 && md->md_version > 7) + return (1); + p = data + sizeof(md->md_magic) + sizeof(md->md_version); + md->md_flags = le32dec(p); p += sizeof(md->md_flags); + if (md->md_version < G_ELI_VERSION_04) + md->md_flags |= G_ELI_FLAG_NATIVE_BYTE_ORDER; + if (md->md_version < G_ELI_VERSION_05) + md->md_flags |= G_ELI_FLAG_SINGLE_KEY; + if (md->md_version < G_ELI_VERSION_06 && + (md->md_flags & G_ELI_FLAG_AUTH) != 0) { + md->md_flags |= G_ELI_FLAG_FIRST_KEY; + } + if (md->md_version < G_ELI_VERSION_07) + md->md_flags |= G_ELI_FLAG_ENC_IVKEY; + md->md_ealgo = le16dec(p); p += sizeof(md->md_ealgo); + md->md_keylen = le16dec(p); p += sizeof(md->md_keylen); + md->md_aalgo = le16dec(p); p += sizeof(md->md_aalgo); + md->md_provsize = le64dec(p); p += sizeof(md->md_provsize); + md->md_sectorsize = le32dec(p); p += sizeof(md->md_sectorsize); + md->md_keys = *p; p += sizeof(md->md_keys); + md->md_iterations = le32dec(p); p += sizeof(md->md_iterations); + bcopy(p, md->md_salt, sizeof(md->md_salt)); p += sizeof(md->md_salt); + bcopy(p, md->md_mkeys, sizeof(md->md_mkeys)); p += sizeof(md->md_mkeys); + /* Don't bother with the MD5 hash in the boot loader */ + bzero(md->md_hash, sizeof(md->md_hash)); + + return (error); +} + +static void geli_init(void); +static int geli_taste(int read_func(void *vdev, void *priv, off_t off, + void *buf, size_t bytes), struct dsk *dsk, daddr_t lastsector); +static int geli_attach(int read_func(void *vdev, void *priv, off_t off, + void *buf, size_t bytes), struct dsk *dskp, const char *passphrase); +static int is_geli(struct dsk *dsk); +static int geli_read(struct dsk *dsk, off_t offset, u_char *buf, size_t bytes); +static int geli_decrypt(u_int algo, u_char *data, size_t datasize, + const u_char *key, size_t keysize, const uint8_t* iv); +static void geli_ivgen(struct geli_entry *geli_e, off_t offset, u_char *iv, + size_t size); +static void geli_key(struct geli_entry *gep, off_t offset, uint8_t *key); + +/* GELI components */ +#include "geli_hmac.c" + +/* Additional cipher (AES-XTS) */ +#include "geli_opencrypto.c" Index: sys/boot/geli/geli_hmac.c =================================================================== --- /dev/null +++ sys/boot/geli/geli_hmac.c @@ -0,0 +1,233 @@ +/*- + * Copyright (c) 2015 Allan Jude + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +static void geli_hmac_init(struct hmac_ctx *ctx, const uint8_t *hkey, + size_t hkeylen); +static void geli_hmac_update(struct hmac_ctx *ctx, const uint8_t *data, + size_t datasize); +static void geli_hmac_final(struct hmac_ctx *ctx, uint8_t *md, size_t mdsize); +static void geli_hmac(const uint8_t *hkey, size_t hkeysize, const uint8_t *data, + size_t datasize, uint8_t *md, size_t mdsize); +static int geli_mkey_verify(const unsigned char *mkey, const unsigned char *key); +static int geli_mkey_decrypt(struct geli_entry *gep, const unsigned char *key, + unsigned char *mkey, unsigned *nkeyp); +static void pkcs5v2_genkey(uint8_t *key, unsigned keylen, const uint8_t *salt, + size_t saltsize, const char *passphrase, u_int iterations); + +static void +geli_hmac_init(struct hmac_ctx *ctx, const uint8_t *hkey, + size_t hkeylen) +{ + u_char k_ipad[128], key[128]; + SHA512_CTX lctx; + u_int i; + + bzero(key, sizeof(key)); + if (hkeylen <= 128) + bcopy(hkey, key, hkeylen); + else { + /* If key is longer than 128 bytes reset it to key = SHA512(key). */ + SHA512_Init(&lctx); + SHA512_Update(&lctx, hkey, hkeylen); + SHA512_Final(key, &lctx); + } + + /* XOR key with ipad and opad values. */ + for (i = 0; i < sizeof(key); i++) { + k_ipad[i] = key[i] ^ 0x36; + ctx->k_opad[i] = key[i] ^ 0x5c; + } + bzero(key, sizeof(key)); + /* Perform inner SHA512. */ + SHA512_Init(&ctx->shactx); + SHA512_Update(&ctx->shactx, k_ipad, sizeof(k_ipad)); + bzero(k_ipad, sizeof(k_ipad)); +} + +static void +geli_hmac_update(struct hmac_ctx *ctx, const uint8_t *data, + size_t datasize) +{ + + SHA512_Update(&ctx->shactx, data, datasize); +} + +static void +geli_hmac_final(struct hmac_ctx *ctx, uint8_t *md, size_t mdsize) +{ + u_char digest[SHA512_MDLEN]; + SHA512_CTX lctx; + + SHA512_Final(digest, &ctx->shactx); + /* Perform outer SHA512. */ + SHA512_Init(&lctx); + SHA512_Update(&lctx, ctx->k_opad, sizeof(ctx->k_opad)); + bzero(ctx, sizeof(*ctx)); + SHA512_Update(&lctx, digest, sizeof(digest)); + SHA512_Final(digest, &lctx); + bzero(&lctx, sizeof(lctx)); + /* mdsize == 0 means "Give me the whole hash!" */ + if (mdsize == 0) + mdsize = SHA512_MDLEN; + bcopy(digest, md, mdsize); + bzero(digest, sizeof(digest)); +} + +static void +geli_hmac(const uint8_t *hkey, size_t hkeysize, const uint8_t *data, + size_t datasize, uint8_t *md, size_t mdsize) +{ + struct hmac_ctx ctx; + + geli_hmac_init(&ctx, hkey, hkeysize); + geli_hmac_update(&ctx, data, datasize); + geli_hmac_final(&ctx, md, mdsize); +} + +/* + * Verify if the given 'key' is correct. + * Return 1 if it is correct and 0 otherwise. + */ +static int +geli_mkey_verify(const unsigned char *mkey, const unsigned char *key) +{ + const unsigned char *odhmac; /* On-disk HMAC. */ + unsigned char chmac[SHA512_MDLEN]; /* Calculated HMAC. */ + unsigned char hmkey[SHA512_MDLEN]; /* Key for HMAC. */ + + /* + * The key for HMAC calculations is: hmkey = HMAC_SHA512(Derived-Key, 0) + */ + geli_hmac(key, G_ELI_USERKEYLEN, "\x00", 1, hmkey, 0); + + odhmac = mkey + G_ELI_DATAIVKEYLEN; + + /* Calculate HMAC from Data-Key and IV-Key. */ + geli_hmac(hmkey, sizeof(hmkey), mkey, G_ELI_DATAIVKEYLEN, chmac, 0); + + bzero(hmkey, sizeof(hmkey)); + + /* + * Compare calculated HMAC with HMAC from metadata. + * If two HMACs are equal, 'key' is correct. + */ + return (!bcmp(odhmac, chmac, SHA512_MDLEN)); +} + +/* + * Find and decrypt Master Key encrypted with 'key'. + * Return decrypted Master Key number in 'nkeyp' if not NULL. + * Return 0 on success, > 0 on failure, -1 on bad key. + */ +static int +geli_mkey_decrypt(struct geli_entry *gep, const unsigned char *key, + unsigned char *mkey, unsigned *nkeyp) +{ + unsigned char tmpmkey[G_ELI_MKEYLEN]; + unsigned char enckey[SHA512_MDLEN]; /* Key for encryption. */ + unsigned char ivkey[G_ELI_IVKEYLEN]; + const unsigned char *mmkey; + int bit, error, nkey; + + if (nkeyp != NULL) + *nkeyp = -1; + + bzero(ivkey, sizeof(ivkey)); + /* + * The key for encryption is: enckey = HMAC_SHA512(Derived-Key, 1) + */ + geli_hmac(key, G_ELI_USERKEYLEN, "\x01", 1, enckey, 0); + + mmkey = gep->md.md_mkeys; + for (nkey = 0; nkey < G_ELI_MAXMKEYS; nkey++, mmkey += G_ELI_MKEYLEN) { + bit = (1 << nkey); + if (!(gep->md.md_keys & bit)) + continue; + bcopy(mmkey, tmpmkey, G_ELI_MKEYLEN); + /* gep->md.md_ealgo */ + error = geli_decrypt(CRYPTO_AES_CBC, tmpmkey, + G_ELI_MKEYLEN, enckey, gep->md.md_keylen, ivkey); + if (error != 0) { + bzero(tmpmkey, sizeof(tmpmkey)); + bzero(enckey, sizeof(enckey)); + return (error); + } + if (geli_mkey_verify(tmpmkey, key)) { + bcopy(tmpmkey, mkey, G_ELI_DATAIVKEYLEN); + bzero(tmpmkey, sizeof(tmpmkey)); + bzero(enckey, sizeof(enckey)); + if (nkeyp != NULL) + *nkeyp = nkey; + return (0); + } + } + bzero(enckey, sizeof(enckey)); + bzero(tmpmkey, sizeof(tmpmkey)); + return (-1); +} + +static __inline void +xor(uint8_t *dst, const uint8_t *src, size_t size) +{ + + for (; size > 0; size--) + *dst++ ^= *src++; +} + +static void +pkcs5v2_genkey(uint8_t *key, unsigned keylen, const uint8_t *salt, + size_t saltsize, const char *passphrase, u_int iterations) +{ + uint8_t md[SHA512_MDLEN], saltcount[saltsize + sizeof(uint32_t)]; + uint8_t *counter, *keyp; + u_int i, bsize, passlen; + uint32_t count; + + passlen = strlen(passphrase); + bzero(key, keylen); + bcopy(salt, saltcount, saltsize); + counter = saltcount + saltsize; + + keyp = key; + for (count = 1; keylen > 0; count++, keylen -= bsize, keyp += bsize) { + bsize = MIN(keylen, sizeof(md)); + + counter[0] = (count >> 24) & 0xff; + counter[1] = (count >> 16) & 0xff; + counter[2] = (count >> 8) & 0xff; + counter[3] = count & 0xff; + geli_hmac(passphrase, passlen, saltcount, sizeof(saltcount), md, 0); + xor(keyp, md, bsize); + + for (i = 1; i < iterations; i++) { + geli_hmac(passphrase, passlen, md, sizeof(md), + md, 0); + xor(keyp, md, bsize); + } + } +} Index: sys/boot/geli/geli_opencrypto.c =================================================================== --- /dev/null +++ sys/boot/geli/geli_opencrypto.c @@ -0,0 +1,171 @@ +/* This code is borrowed from sys/opencrypto/xform.c */ +/* $OpenBSD: xform.c,v 1.16 2001/08/28 12:20:43 ben Exp $ */ +/*- + * The authors of this code are John Ioannidis (ji@tla.org), + * Angelos D. Keromytis (kermit@csd.uch.gr), + * Niels Provos (provos@physnet.uni-hamburg.de) and + * Damien Miller (djm@mindrot.org). + * + * This code was written by John Ioannidis for BSD/OS in Athens, Greece, + * in November 1995. + * + * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996, + * by Angelos D. Keromytis. + * + * Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis + * and Niels Provos. + * + * Additional features in 1999 by Angelos D. Keromytis. + * + * AES XTS implementation in 2008 by Damien Miller + * + * Copyright (C) 1995, 1996, 1997, 1998, 1999 by John Ioannidis, + * Angelos D. Keromytis and Niels Provos. + * + * Copyright (C) 2001, Angelos D. Keromytis. + * + * Copyright (C) 2008, Damien Miller + * Copyright (c) 2014 The FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were developed by John-Mark Gurney + * under sponsorship of the FreeBSD Foundation and + * Rubicon Communications, LLC (Netgate). + * + * Permission to use, copy, and modify this software with or without fee + * is hereby granted, provided that this entire notice is included in + * all copies of any software which is or includes a copy or + * modification of this software. + * You may use this code under the GNU public license if you so wish. Please + * contribute changes back to the authors under this freer than GPL license + * so that we may further the use of strong encryption without limitations to + * all. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE + * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR + * PURPOSE. + * + * $FreeBSD$ + */ + +#define AES_XTS_BLOCKSIZE 16 +#define AES_XTS_IVSIZE 8 +#define AES_XTS_ALPHA 0x87 /* GF(2^128) generator polynomial */ + +struct aes_xts_ctx { + rijndael_ctx key1; + rijndael_ctx key2; + u_int8_t tweak[AES_XTS_BLOCKSIZE]; +}; + +static void +aes_xts_reinit(struct aes_xts_ctx *ctx, u_int8_t *iv) +{ + u_int64_t blocknum; + u_int i; + + /* + * Prepare tweak as E_k2(IV). IV is specified as LE representation + * of a 64-bit block number which we allow to be passed in directly. + */ + bcopy(iv, &blocknum, AES_XTS_IVSIZE); + for (i = 0; i < AES_XTS_IVSIZE; i++) { + ctx->tweak[i] = blocknum & 0xff; + blocknum >>= 8; + } + /* Last 64 bits of IV are always zero */ + bzero(ctx->tweak + AES_XTS_IVSIZE, AES_XTS_IVSIZE); + + rijndael_encrypt(&ctx->key2, ctx->tweak, ctx->tweak); +} + +static void +aes_xts_crypt(struct aes_xts_ctx *ctx, u_int8_t *data, u_int do_encrypt) +{ + u_int8_t block[AES_XTS_BLOCKSIZE]; + u_int i, carry_in, carry_out; + + for (i = 0; i < AES_XTS_BLOCKSIZE; i++) + block[i] = data[i] ^ ctx->tweak[i]; + + if (do_encrypt) { + printf("Cannot encrypt\n"); + return; + } + + rijndael_decrypt(&ctx->key1, block, data); + + for (i = 0; i < AES_XTS_BLOCKSIZE; i++) + data[i] ^= ctx->tweak[i]; + + /* Exponentiate tweak */ + carry_in = 0; + for (i = 0; i < AES_XTS_BLOCKSIZE; i++) { + carry_out = ctx->tweak[i] & 0x80; + ctx->tweak[i] = (ctx->tweak[i] << 1) | (carry_in ? 1 : 0); + carry_in = carry_out; + } + if (carry_in) + ctx->tweak[0] ^= AES_XTS_ALPHA; + bzero(block, sizeof(block)); +} + +static void +aes_xts_encrypt(struct aes_xts_ctx *key, u_int8_t *data) +{ + + aes_xts_crypt(key, data, 1); +} + +static void +aes_xts_decrypt(struct aes_xts_ctx *key, u_int8_t *data) +{ + + aes_xts_crypt(key, data, 0); +} + +static void +aes_xts_decrypt_block(struct aes_xts_ctx *key, u_int8_t *data, int datasize) +{ + int i; + + for (i = 0; i < datasize; i += AES_XTS_BLOCKSIZE) { + aes_xts_crypt(key, data + i, 0); + } +} + +static int +aes_xts_setkey(u_int8_t *sched, const u_int8_t *key, int len) +{ + struct aes_xts_ctx *ctx; + + if (len != 32 && len != 64) + return (1); + + if (sched == NULL) { + sched = malloc(sizeof(struct aes_xts_ctx)); + if (sched == NULL) + return (1); + } + ctx = (struct aes_xts_ctx *)sched; + + rijndael_set_key(&ctx->key1, key, len * 4); + rijndael_set_key(&ctx->key2, key + (len / 2), len * 4); + + return (0); +} + +static void +aes_xts_zerokey(u_int8_t **sched) +{ + + bzero(*sched, sizeof(struct aes_xts_ctx)); + /* We do not have free() in the pre-boot environment */ +#ifdef free + free(*sched); + *sched = NULL; +#endif +} + Index: sys/boot/geli/geliimpl.c =================================================================== --- /dev/null +++ sys/boot/geli/geliimpl.c @@ -0,0 +1,376 @@ +/*- + * Copyright (c) 2015 Allan Jude + * Copyright (c) 2005-2011 Pawel Jakub Dawidek + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include "geli.h" + +static void +geli_init(void) +{ + + geli_count = 0; + SLIST_INIT(&geli_head); +} + +/* + * Read the last sector of the drive or partition pointed to by dsk and see + * if it is GELI encrypted + */ +static int +geli_taste(int read_func(void *vdev, void *priv, off_t off, void *buf, + size_t bytes), struct dsk *dskp, daddr_t lastsector) +{ + struct g_eli_metadata md; + u_char buf[DEV_BSIZE]; + int error; + + error = read_func(NULL, dskp, (off_t) lastsector * DEV_BSIZE, &buf, + (size_t) DEV_BSIZE); + if (error != 0) { + return (error); + } + error = eli_metadata_decode(buf, &md); + if (error != 0) { + return (error); + } + + if ((md.md_flags & G_ELI_FLAG_ONETIME)) { + /* Swap device, skip it */ + return (1); + } + if (!(md.md_flags & G_ELI_FLAG_BOOT)) { + /* Disk is not GELI boot device, skip it */ + return (1); + } + geli_e = malloc(sizeof(struct geli_entry)); + if (geli_e == NULL) + return (2); + geli_e->dsk = malloc(sizeof(struct dsk)); + if (geli_e->dsk == NULL) + return (2); + memcpy(geli_e->dsk, dskp, sizeof(struct dsk)); + geli_e->part_end = lastsector; + if (dskp->part == 255) { + geli_e->dsk->part = dskp->slice; + } + geli_e->md = md; + + SLIST_INSERT_HEAD(&geli_head, geli_e, entries); + geli_count++; + + return (0); +} + +/* + * Attempt to decrypt the device + */ +static int +geli_attach(int read_func(void *vdev, void *priv, off_t off, void *buf, + size_t bytes), struct dsk *dskp, const char *passphrase) +{ + u_char key[G_ELI_USERKEYLEN], mkey[G_ELI_DATAIVKEYLEN], *mkp; + u_int keynum; + struct hmac_ctx ctx; + int error; + + SLIST_FOREACH_SAFE(geli_e, &geli_head, entries, geli_e_tmp) { + if (geli_e->dsk->drive != dskp->drive) { + continue; + } + if (dskp->part == 255) { + if (geli_e->dsk->part != dskp->slice) { + /* Right disk, wrong partition */ + continue; + } + } else { + if (geli_e->dsk->slice != dskp->slice) { + /* Right disk, wrong slice */ + continue; + } else if (geli_e->dsk->part != dskp->part) { + /* Right disk, wrong partition */ + continue; + } + } + + geli_hmac_init(&ctx, NULL, 0); + /* + * Prepare Derived-Key from the user passphrase. + */ + if (geli_e->md.md_iterations == 0) { + geli_hmac_update(&ctx, geli_e->md.md_salt, + sizeof(geli_e->md.md_salt)); + geli_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, + (dskp->slice > 0 ? dskp->slice : dskp->part), + geli_e->md.md_iterations); + u_char dkey[G_ELI_USERKEYLEN]; + + pkcs5v2_genkey(dkey, sizeof(dkey), geli_e->md.md_salt, + sizeof(geli_e->md.md_salt), passphrase, + geli_e->md.md_iterations); + geli_hmac_update(&ctx, dkey, sizeof(dkey)); + bzero(dkey, sizeof(dkey)); + } + + geli_hmac_final(&ctx, key, 0); + + error = geli_mkey_decrypt(geli_e, key, mkey, &keynum); + if (error != 0) { + printf("Failed to decrypt GELI master key: %d\n", error); + return (error); + } + + /* Store the keys */ + bcopy(mkey, geli_e->mkey, sizeof(geli_e->mkey)); + bcopy(mkey, geli_e->ivkey, sizeof(geli_e->ivkey)); + mkp = mkey + sizeof(geli_e->ivkey); + if ((geli_e->md.md_flags & G_ELI_FLAG_AUTH) == 0) { + bcopy(mkp, geli_e->ekey, G_ELI_DATAKEYLEN); + } else { + /* + * The encryption key is: ekey = HMAC_SHA512(Data-Key, 0x10) + */ + geli_hmac(mkp, G_ELI_MAXKEYLEN, "\x10", 1, + geli_e->ekey, 0); + } + + /* Initialize the per-sector IV */ + switch (geli_e->md.md_ealgo) { + case CRYPTO_AES_XTS: + break; + default: + SHA256_Init(&geli_e->ivctx); + SHA256_Update(&geli_e->ivctx, geli_e->ivkey, + sizeof(geli_e->ivkey)); + break; + } + + return (0); + } + + /* Disk not found */ + return (2); +} + +static int +is_geli(struct dsk *dskp) +{ + SLIST_FOREACH_SAFE(geli_e, &geli_head, entries, geli_e_tmp) { + if (geli_e->dsk->drive != dskp->drive) { + continue; + } + if (dskp->part == 255 && geli_e->dsk->part == dskp->slice) { + /* Sometimes we track slice, sometimes part :( */ + return (0); + } + if (geli_e->dsk->slice != dskp->slice) { + /* Right disk, wrong slice */ + continue; + } + if (geli_e->dsk->part != dskp->part) { + /* Right disk, wrong partition */ + continue; + } + return (0); + } + + return (1); +} + +static int +geli_read(struct dsk *dskp, off_t offset, u_char *buf, size_t bytes) +{ + u_char iv[G_ELI_IVKEYLEN], key[G_ELI_DATAKEYLEN]; + u_char *pbuf; + int error; + off_t os; + size_t n, nb; + + SLIST_FOREACH_SAFE(geli_e, &geli_head, entries, geli_e_tmp) { + if (geli_e->dsk->drive != dskp->drive) { + continue; + } + if (dskp->part == 255) { + if (geli_e->dsk->part != dskp->slice) { + /* Right disk, wrong partition */ + continue; + } + } else { + if (geli_e->dsk->slice != dskp->slice) { + /* Right disk, wrong partition */ + continue; + } else if (geli_e->dsk->part != dskp->part) { + /* Right disk, wrong partition */ + continue; + } + } + + nb = bytes / DEV_BSIZE; + for (n = 0; n < nb; n++) { + os = offset + (n * DEV_BSIZE); + pbuf = buf + (n * DEV_BSIZE); + + geli_ivgen(geli_e, os, iv, G_ELI_IVKEYLEN); + + /* Get the key that corresponds to this offset */ + geli_key(geli_e, os, key); + + error = geli_decrypt(geli_e->md.md_ealgo, pbuf, + DEV_BSIZE, key, geli_e->md.md_keylen, iv); + + if (error != 0) { + bzero(key, sizeof(key)); + printf("Failed to decrypt in geli_read()!"); + return (error); + } + } + bzero(key, sizeof(key)); + return (0); + } + + printf("GELI provider not found\n"); + return (1); +} + +static int +geli_decrypt(u_int algo, u_char *data, size_t datasize, + const u_char *key, size_t keysize, const uint8_t* iv) +{ + keyInstance aeskey; + cipherInstance cipher; + struct aes_xts_ctx xtsctx; + size_t xts_keysize; + int err, blks; + + switch (algo) { + case CRYPTO_AES_CBC: + err = rijndael_makeKey(&aeskey, DIR_DECRYPT, keysize, + (const char *)key); + if (err < 0) { + printf("Failed to setup decryption keys: %d\n", err); + return (err); + } + + err = rijndael_cipherInit(&cipher, MODE_CBC, (u_int8_t *) iv); + if (err < 0) { + printf("Failed to setup IV: %d\n", err); + return (err); + } + + blks = rijndael_blockDecrypt(&cipher, &aeskey, data, + datasize * 8, data); + if (datasize != (blks / 8)) { + printf("Failed to decrypt the entire input: " + "%u != %u\n", blks, datasize); + return (1); + } + break; + case CRYPTO_AES_XTS: + xts_keysize = keysize << 1; + err = aes_xts_setkey(&xtsctx, key, xts_keysize / 8); + if (err != 0) { + printf("Failed to setup decryption keys: %d\n", err); + return (err); + } + + aes_xts_reinit(&xtsctx, iv); + + aes_xts_decrypt_block(&xtsctx, data, datasize); + break; + default: + printf("Unsupported crypto algorithm #%d\n", algo); + return (1); + } + + return (0); +} + +/* + * Here we generate IV. It is unique for every sector. + */ +static void +geli_ivgen(struct geli_entry *gep, off_t offset, u_char *iv, + size_t size) +{ + uint8_t off[8]; + + if ((gep->md.md_flags & G_ELI_FLAG_NATIVE_BYTE_ORDER) != 0) + bcopy(&offset, off, sizeof(off)); + else + le64enc(off, (uint64_t)offset); + + switch (gep->md.md_ealgo) { + case CRYPTO_AES_XTS: + bcopy(off, iv, sizeof(off)); + bzero(iv + sizeof(off), size - sizeof(off)); + break; + default: + { + u_char hash[SHA256_DIGEST_LENGTH]; + SHA256_CTX ctx; + + /* Copy precalculated SHA256 context for IV-Key. */ + bcopy(&gep->ivctx, &ctx, sizeof(ctx)); + SHA256_Update(&ctx, off, sizeof(off)); + SHA256_Final(hash, &ctx); + bcopy(hash, iv, MIN(sizeof(hash), size)); + break; + } + } +} + +static void +geli_key(struct geli_entry *gep, off_t offset, uint8_t *key) +{ + const uint8_t *ekey; + uint64_t keyno; + struct { + char magic[4]; + uint8_t keyno[8]; + } __packed hmacdata; + + if ((gep->md.md_flags & G_ELI_FLAG_SINGLE_KEY) != 0) { + bcopy(gep->ekey, key, G_ELI_DATAKEYLEN); + return; + } + if ((gep->md.md_flags & G_ELI_FLAG_ENC_IVKEY) != 0) { + ekey = gep->mkey; + } else { + ekey = gep->ekey; + } + + keyno = (offset >> G_ELI_KEY_SHIFT) / DEV_BSIZE; + bcopy("ekey", hmacdata.magic, 4); + le64enc(hmacdata.keyno, keyno); + + geli_hmac(ekey, G_ELI_MAXKEYLEN, (uint8_t *)&hmacdata, + sizeof(hmacdata), key, 0); +} + Index: sys/boot/i386/common/bootargs.h =================================================================== --- sys/boot/i386/common/bootargs.h +++ sys/boot/i386/common/bootargs.h @@ -64,6 +64,12 @@ */ }; +struct geli_boot_args +{ + uint32_t size; + char gelipw[256]; +}; + #endif /*__ASSEMBLER__*/ #endif /* !_BOOT_I386_ARGS_H_ */ Index: sys/boot/i386/common/cons.h =================================================================== --- sys/boot/i386/common/cons.h +++ sys/boot/i386/common/cons.h @@ -30,5 +30,6 @@ int xgetc(int fn); int keyhit(unsigned int secs); void getstr(char *cmdstr, size_t cmdstrsize); +void getpwstr(char *cmdstr, size_t cmdstrsize); #endif /* !_CONS_H_ */ Index: sys/boot/i386/common/cons.c =================================================================== --- sys/boot/i386/common/cons.c +++ sys/boot/i386/common/cons.c @@ -149,3 +149,34 @@ } } } + +void +getpwstr(char *pwstr, size_t pwstrsize) +{ + char *s; + int c; + + s = pwstr; + for (;;) { + switch (c = xgetc(0)) { + case 0: + break; + case '\177': + case '\b': + if (s > pwstr) { + s--; + printf("\b \b"); + } + break; + case '\n': + case '\r': + *s = 0; + return; + default: + if (s - pwstr < pwstrsize - 1) + *s++ = c; + putchar('*'); + break; + } + } +} Index: sys/boot/i386/common/drv.h =================================================================== --- sys/boot/i386/common/drv.h +++ sys/boot/i386/common/drv.h @@ -42,7 +42,7 @@ int drvread(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk); #ifdef GPT int drvwrite(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk); -uint64_t drvsize(struct dsk *dskp); #endif /* GPT */ +uint64_t drvsize(struct dsk *dskp); #endif /* !_DRV_H_ */ Index: sys/boot/i386/common/drv.c =================================================================== --- sys/boot/i386/common/drv.c +++ sys/boot/i386/common/drv.c @@ -29,7 +29,6 @@ #include "xreadorg.h" #endif -#ifdef GPT static struct edd_params params; uint64_t @@ -50,7 +49,6 @@ } return (params.sectors); } -#endif /* GPT */ #ifndef USE_XREAD static struct edd_packet packet; Index: sys/boot/i386/gptboot/Makefile =================================================================== --- sys/boot/i386/gptboot/Makefile +++ sys/boot/i386/gptboot/Makefile @@ -39,6 +39,12 @@ CFLAGS.gcc+= --param max-inline-insns-single=100 +.if !defined(LOADER_NO_GELI_SUPPORT) +CFLAGS+= -DGELI +CFLAGS+= -I${.CURDIR}/../../geli +LIBGELIBOOT= ${.OBJDIR}/../../geli/libgeliboot.a +.endif + LD_FLAGS=-static -N --gc-sections LIBSTAND= ${.OBJDIR}/../../libstand32/libstand.a @@ -60,14 +66,14 @@ gptldr.out: gptldr.o ${LD} ${LD_FLAGS} -e start -Ttext ${ORG1} -o ${.TARGET} gptldr.o -CLEANFILES+= gptboot.bin gptboot.out gptboot.o sio.o gpt.o crc32.o drv.o \ +CLEANFILES+= gptboot.bin gptboot.out gptboot.o sio.o crc32.o drv.o \ cons.o util.o gptboot.bin: gptboot.out ${OBJCOPY} -S -O binary gptboot.out ${.TARGET} -gptboot.out: ${BTXCRT} gptboot.o sio.o gpt.o crc32.o drv.o cons.o util.o - ${LD} ${LD_FLAGS} -Ttext ${ORG2} -o ${.TARGET} ${.ALLSRC} ${LIBSTAND} +gptboot.out: ${BTXCRT} gptboot.o sio.o crc32.o drv.o cons.o util.o + ${LD} ${LD_FLAGS} -Ttext ${ORG2} -o ${.TARGET} ${.ALLSRC} ${LIBSTAND} ${LIBGELIBOOT} gptboot.o: ${.CURDIR}/../../common/ufsread.c Index: sys/boot/i386/gptboot/gptboot.c =================================================================== --- sys/boot/i386/gptboot/gptboot.c +++ sys/boot/i386/gptboot/gptboot.c @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -31,6 +32,8 @@ #include +#include "../common/bootargs.h" + #include "lib.h" #include "rbx.h" #include "drv.h" @@ -84,16 +87,52 @@ static struct dsk dsk; static char kname[1024]; +static char gelipw[256]; static int comspeed = SIOSPD; static struct bootinfo bootinfo; +static struct geli_boot_args geliargs; + +static vm_offset_t high_heap_base; +static uint32_t bios_basemem, bios_extmem, high_heap_size; + +static struct bios_smap smap; + +/* + * The minimum amount of memory to reserve in bios_extmem for the heap. + */ +#define HEAP_MIN (3 * 1024 * 1024) + +static char *heap_next; +static char *heap_end; void exit(int); static void load(void); static int parse(char *, int *); static int dskread(void *, daddr_t, unsigned); -static uint32_t memsize(void); +#ifdef GELI +static int vdev_read(void *vdev __unused, void *priv, off_t off, void *buf, + size_t bytes); +#endif + +static void * +malloc(size_t n) +{ + char *p = heap_next; + if (p + n > heap_end) { + printf("malloc failure\n"); + for (;;) + ; + return (0); + } + heap_next += n; + return (p); +} #include "ufsread.c" +#include "gpt.c" +#ifdef GELI +#include "geliimpl.c" +#endif static inline int xfsread(ufs_ino_t inode, void *buf, size_t nbyte) @@ -106,19 +145,96 @@ return (0); } -static inline uint32_t -memsize(void) +static void +bios_getmem(void) { + uint64_t size; + + /* Parse system memory map */ + v86.ebx = 0; + do { + v86.ctl = V86_FLAGS; + v86.addr = MEM_EXT; /* int 0x15 function 0xe820*/ + v86.eax = 0xe820; + v86.ecx = sizeof(struct bios_smap); + v86.edx = SMAP_SIG; + v86.es = VTOPSEG(&smap); + v86.edi = VTOPOFF(&smap); + v86int(); + if ((v86.efl & 1) || (v86.eax != SMAP_SIG)) + break; + /* look for a low-memory segment that's large enough */ + if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0) && + (smap.length >= (512 * 1024))) + bios_basemem = smap.length; + /* look for the first segment in 'extended' memory */ + if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0x100000)) { + bios_extmem = smap.length; + } + + /* + * Look for the largest segment in 'extended' memory beyond + * 1MB but below 4GB. + */ + if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base > 0x100000) && + (smap.base < 0x100000000ull)) { + size = smap.length; + + /* + * If this segment crosses the 4GB boundary, truncate it. + */ + if (smap.base + size > 0x100000000ull) + size = 0x100000000ull - smap.base; + + if (size > high_heap_size) { + high_heap_size = size; + high_heap_base = smap.base; + } + } + } while (v86.ebx != 0); + + /* Fall back to the old compatibility function for base memory */ + if (bios_basemem == 0) { + v86.ctl = 0; + v86.addr = 0x12; /* int 0x12 */ + v86int(); + + bios_basemem = (v86.eax & 0xffff) * 1024; + } - v86.addr = MEM_EXT; + /* Fall back through several compatibility functions for extended memory */ + if (bios_extmem == 0) { + v86.ctl = V86_FLAGS; + v86.addr = 0x15; /* int 0x15 function 0xe801*/ + v86.eax = 0xe801; + v86int(); + if (!(v86.efl & 1)) { + bios_extmem = ((v86.ecx & 0xffff) + ((v86.edx & 0xffff) * 64)) * 1024; + } + } + if (bios_extmem == 0) { + v86.ctl = 0; + v86.addr = 0x15; /* int 0x15 function 0x88*/ v86.eax = 0x8800; v86int(); - return (v86.eax); + bios_extmem = (v86.eax & 0xffff) * 1024; + } + + /* + * If we have extended memory and did not find a suitable heap + * region in the SMAP, use the last 3MB of 'extended' memory as a + * high heap candidate. + */ + if (bios_extmem >= HEAP_MIN && high_heap_size < HEAP_MIN) { + high_heap_size = HEAP_MIN; + high_heap_base = bios_extmem + 0x100000 - HEAP_MIN; + } } static int gptinit(void) { + int i; if (gptread(&freebsd_ufs_uuid, &dsk, dmadat->secbuf) == -1) { printf("%s: unable to load GPT\n", BOOTPROG); @@ -128,6 +244,27 @@ printf("%s: no UFS partition was found\n", BOOTPROG); return (-1); } +#ifdef GELI + if (geli_taste(vdev_read, &dsk, (gpttable[curent].ent_lba_end - + gpttable[curent].ent_lba_start)) == 0) { + for (i = 0; i < 3; i++) { + /* Try cached passphrase */ + if (i == 0 && gelipw[0] != '\0') { + if (geli_attach(vdev_read, &dsk, &gelipw) == 0) { + break; + } + } + printf("GELI Password for disk%dp%d: ", dsk.unit, + curent + 1); + getpwstr(gelipw, sizeof(gelipw)); + printf("\n"); + if (geli_attach(vdev_read, &dsk, &gelipw) == 0) { + break; + } + } + } +#endif + dsk_meta = 0; return (0); } @@ -141,6 +278,17 @@ ufs_ino_t ino; dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base); + + bios_getmem(); + + if (high_heap_size > 0) { + heap_end = PTOV(high_heap_base + high_heap_size); + heap_next = PTOV(high_heap_base); + } else { + heap_next = (char *)dmadat + sizeof(*dmadat); + heap_end = (char *)PTOV(bios_basemem); + } + v86.ctl = V86_FLAGS; v86.efl = PSL_RESERVED_DEFAULT | PSL_I; dsk.drive = *(uint8_t *)PTOV(ARGS); @@ -150,10 +298,14 @@ dsk.start = 0; bootinfo.bi_version = BOOTINFO_VERSION; bootinfo.bi_size = sizeof(bootinfo); - bootinfo.bi_basemem = 0; /* XXX will be filled by loader or kernel */ - bootinfo.bi_extmem = memsize(); + bootinfo.bi_basemem = bios_basemem / 1024; + bootinfo.bi_extmem = bios_extmem / 1024; bootinfo.bi_memsizes_valid++; + bootinfo.bi_bios_dev = dsk.drive; +#ifdef GELI + geli_init(); +#endif /* Process configuration file */ if (gptinit() != 0) @@ -331,9 +483,12 @@ bootinfo.bi_esymtab = VTOP(p); bootinfo.bi_kernelname = VTOP(kname); bootinfo.bi_bios_dev = dsk.drive; + geliargs.size = sizeof(geliargs); + bcopy(gelipw, geliargs.gelipw, sizeof(geliargs.gelipw)); + bzero(gelipw, sizeof(gelipw)); __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK), MAKEBOOTDEV(dev_maj[dsk.type], dsk.part + 1, dsk.unit, 0xff), - 0, 0, 0, VTOP(&bootinfo)); + KARGS_FLAGS_EXTARG, 0, 0, VTOP(&bootinfo), geliargs); } static int @@ -434,6 +589,53 @@ static int dskread(void *buf, daddr_t lba, unsigned nblk) { + int err; + + err = drvread(&dsk, buf, lba + dsk.start, nblk); + +#ifdef GELI + if (err == 0 && is_geli(&dsk) == 0) { + /* Decrypt */ + if (geli_read(&dsk, lba * DEV_BSIZE, buf, nblk * DEV_BSIZE)) + return (err); + } +#endif - return drvread(&dsk, buf, lba + dsk.start, nblk); + return (err); +} + +#ifdef GELI +/* + * Read function compartible with the ZFS callback, required to keep the GELI + * Implementation the same for both UFS and ZFS + */ +static int +vdev_read(void *vdev __unused, void *priv, off_t off, void *buf, size_t bytes) +{ + char *p; + daddr_t lba; + unsigned int nb; + struct dsk *dskp = (struct dsk *) priv; + + if ((off & (DEV_BSIZE - 1)) || (bytes & (DEV_BSIZE - 1))) + return (-1); + + p = buf; + lba = off / DEV_BSIZE; + lba += dskp->start; + + while (bytes > 0) { + nb = bytes / DEV_BSIZE; + if (nb > VBLKSIZE / DEV_BSIZE) + nb = VBLKSIZE / DEV_BSIZE; + if (drvread(dskp, dmadat->blkbuf, lba, nb)) + return (-1); + memcpy(p, dmadat->blkbuf, nb * DEV_BSIZE); + p += nb * DEV_BSIZE; + lba += nb; + bytes -= nb * DEV_BSIZE; + } + + return (0); } +#endif Index: sys/boot/i386/gptzfsboot/Makefile =================================================================== --- sys/boot/i386/gptzfsboot/Makefile +++ sys/boot/i386/gptzfsboot/Makefile @@ -35,6 +35,12 @@ -Wpointer-arith -Wshadow -Wstrict-prototypes -Wwrite-strings \ -Winline +.if !defined(LOADER_NO_GELI_SUPPORT) +CFLAGS+= -DGELI +CFLAGS+= -I${.CURDIR}/../../geli +LIBGELIBOOT= ${.OBJDIR}/../../geli/libgeliboot.a +.endif + CFLAGS.gcc+= --param max-inline-insns-single=100 LD_FLAGS=-static -N --gc-sections @@ -65,7 +71,7 @@ ${OBJCOPY} -S -O binary gptzfsboot.out ${.TARGET} gptzfsboot.out: ${BTXCRT} zfsboot.o sio.o gpt.o drv.o cons.o util.o - ${LD} ${LD_FLAGS} -Ttext ${ORG2} -o ${.TARGET} ${.ALLSRC} ${LIBSTAND} + ${LD} ${LD_FLAGS} -Ttext ${ORG2} -o ${.TARGET} ${.ALLSRC} ${LIBSTAND} ${LIBGELIBOOT} zfsboot.o: ${.CURDIR}/../../zfs/zfsimpl.c Index: sys/boot/i386/libi386/Makefile =================================================================== --- sys/boot/i386/libi386/Makefile +++ sys/boot/i386/libi386/Makefile @@ -30,6 +30,12 @@ CFLAGS+= -DDISK_DEBUG .endif +.if !defined(LOADER_NO_GELI_SUPPORT) +# Decrypt encrypted drives +CFLAGS+= -DGELI +CFLAGS+= -I${.CURDIR}/../../geli +.endif + .if !defined(BOOT_HIDE_SERIAL_NUMBERS) # Export serial numbers, UUID, and asset tag from loader. CFLAGS+= -DSMBIOS_SERIAL_NUMBERS Index: sys/boot/i386/libi386/biosdisk.c =================================================================== --- sys/boot/i386/libi386/biosdisk.c +++ sys/boot/i386/libi386/biosdisk.c @@ -49,6 +49,34 @@ #include "disk.h" #include "libi386.h" +#ifdef GELI +#include "cons.h" +#include "drv.h" +#include "gpt.h" +#include "part.h" +#include +struct pentry { + struct ptable_entry part; + uint64_t flags; + union { + uint8_t bsd; + uint8_t mbr; + uuid_t gpt; + uint16_t vtoc8; + } type; + STAILQ_ENTRY(pentry) entry; +}; +struct ptable { + enum ptable_type type; + uint16_t sectorsize; + uint64_t sectors; + + STAILQ_HEAD(, pentry) entries; +}; + +#include "geliimpl.c" +#endif + CTASSERT(sizeof(struct i386_devdesc) >= sizeof(struct disk_devdesc)); #define BIOS_NUMDRIVES 0x475 @@ -108,6 +136,18 @@ static void bd_print(int verbose); static void bd_cleanup(void); +#ifdef GELI +static enum isgeli { + ISGELI_UNKNOWN, + ISGELI_NO, + ISGELI_YES +}; +static enum isgeli geli_status[MAXBDDEV][MAXTBLENTS]; + +int bios_read(void *vdev __unused, struct dsk *priv, off_t off, char *buf, + size_t bytes); +#endif + struct devsw biosdisk = { "disk", DEVT_DISK, @@ -154,6 +194,9 @@ { int base, unit, nfd = 0; +#ifdef GELI + geli_init(); +#endif /* sequence 0, 0x80 */ for (base = 0; base <= 0x80; base += 0x80) { for (unit = base; (nbdinfo < MAXBDDEV); unit++) { @@ -299,7 +342,8 @@ static int bd_open(struct open_file *f, ...) { - struct disk_devdesc *dev; + struct disk_devdesc *dev, rdev; + int err, g_err; va_list ap; va_start(ap, f); @@ -309,9 +353,95 @@ if (dev->d_unit < 0 || dev->d_unit >= nbdinfo) return (EIO); - return (disk_open(dev, BD(dev).bd_sectors * BD(dev).bd_sectorsize, + err = disk_open(dev, BD(dev).bd_sectors * BD(dev).bd_sectorsize, BD(dev).bd_sectorsize, (BD(dev).bd_flags & BD_FLOPPY) ? - DISK_F_NOCACHE: 0)); + DISK_F_NOCACHE: 0); + +#ifdef GELI + int r; + static char gelipw[256]; + char *passphrase; + + if (err) + return (err); + + /* if we already know there is no GELI, skip the rest */ + if (geli_status[dev->d_unit][dev->d_slice] != ISGELI_UNKNOWN) + return (err); + + struct dsk dskp; + struct ptable *table = NULL; + struct ptable_entry part; + struct pentry *entry; + int geli_part = 0; + + dskp.drive = bd_unit2bios(dev->d_unit); + dskp.type = dev->d_type; + dskp.unit = dev->d_unit; + dskp.slice = dev->d_slice; + dskp.part = dev->d_partition; + dskp.start = dev->d_offset; + + memcpy(&rdev, dev, sizeof(rdev)); + /* to read the GPT table, we need to read the first sector */ + rdev.d_offset = 0; + /* We need the LBA of the end of the partition */ + table = ptable_open(&rdev, BD(dev).bd_sectors, + BD(dev).bd_sectorsize, ptblread); + if (table == NULL) { + DEBUG("Can't read partition table"); + /* soft failure, return the exit status of disk_open */ + return (err); + } + + if (table->type == PTABLE_GPT) + dskp.part = 255; + + STAILQ_FOREACH(entry, &table->entries, entry) { + dskp.slice = entry->part.index; + dskp.start = entry->part.start; + if (is_geli(&dskp) == 0) { + geli_status[dev->d_unit][dskp.slice] = ISGELI_YES; + return (0); + } + if (geli_taste(bios_read, &dskp, + entry->part.end - entry->part.start) == 0) { + for (r = 0; r < 3; r++) { + if (r == 0 && (passphrase = + getenv("kern.geom.eli.passphrase")) + != NULL) { + /* Use the cached passphrase */ + } else { + printf("GELI Password for disk%dp%d: ", + dskp.unit, (dskp.slice > 0 ? + dskp.slice : dskp.part)); + getpwstr(gelipw, sizeof(gelipw)); + printf("\n"); + passphrase = &gelipw; + } + if (geli_attach(bios_read, &dskp, passphrase) + == 0) { + setenv("kern.geom.eli.passphrase", + passphrase, 1); + bzero(gelipw, sizeof(gelipw)); + geli_status[dev->d_unit][dskp.slice] = + ISGELI_YES; + geli_part++; + break; + } + } + } else + geli_status[dev->d_unit][dskp.slice] = ISGELI_NO; + } + + /* none of the partitions on this disk have GELI */ + if (geli_part == 0) { + /* found no GELI */ + geli_status[dev->d_unit][dev->d_slice] = ISGELI_NO; + } +#endif + + return (err); } static int @@ -586,6 +716,38 @@ static int bd_read(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest) { +#ifdef GELI + struct dsk dskp; + off_t p_off; + int err, n; + + /* 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); + if (err) + return (err); + + dskp.drive = bd_unit2bios(dev->d_unit); + dskp.type = dev->d_type; + dskp.unit = dev->d_unit; + dskp.slice = dev->d_slice; + dskp.part = dev->d_partition; + dskp.start = dev->d_offset; + + /* GELI needs the offset relative to the partition start */ + p_off = dblk - dskp.start; + + err = geli_read(&dskp, p_off * BIOSDISK_SECSIZE, dest, + blks * BIOSDISK_SECSIZE); + if (err) + return (err); + + return (0); + } +#endif return (bd_io(dev, dblk, blks, dest, 0)); } @@ -682,3 +844,25 @@ DEBUG("dev is 0x%x\n", rootdev); return(rootdev); } + +#ifdef GELI +int +bios_read(void *vdev __unused, struct dsk *priv, off_t off, char *buf, size_t bytes) +{ + struct disk_devdesc dev; + + dev.d_dev = &biosdisk; + dev.d_type = priv->type; + dev.d_unit = priv->unit; + dev.d_slice = priv->slice; + dev.d_partition = priv->part; + dev.d_offset = priv->start; + + off = off / BIOSDISK_SECSIZE; + /* GELI gives us the offset relative to the partition start */ + off += dev.d_offset; + bytes = bytes / BIOSDISK_SECSIZE; + + return (bd_io(&dev, off, bytes, buf, 0)); +} +#endif \ No newline at end of file Index: sys/boot/i386/loader/Makefile =================================================================== --- sys/boot/i386/loader/Makefile +++ sys/boot/i386/loader/Makefile @@ -58,6 +58,10 @@ .if defined(LOADER_NANDFS_SUPPORT) CFLAGS+= -DLOADER_NANDFS_SUPPORT .endif +.if !defined(LOADER_NO_GELI_SUPPORT) +CFLAGS+= -DGELI -DLOADER_GELI_SUPPORT +LIBGELIBOOT= ${.OBJDIR}/../../geli/libgeliboot.a +.endif # Always add MI sources .PATH: ${.CURDIR}/../../common @@ -116,8 +120,8 @@ # XXX crt0.o needs to be first for pxeboot(8) to work OBJS= ${BTXCRT} -DPADD= ${LIBFICL} ${LIBFIREWIRE} ${LIBZFSBOOT} ${LIBI386} ${LIBSTAND} -LDADD= ${LIBFICL} ${LIBFIREWIRE} ${LIBZFSBOOT} ${LIBI386} ${LIBSTAND} +DPADD= ${LIBFICL} ${LIBFIREWIRE} ${LIBZFSBOOT} ${LIBI386} ${LIBSTAND} ${LIBGELIBOOT} +LDADD= ${LIBFICL} ${LIBFIREWIRE} ${LIBZFSBOOT} ${LIBI386} ${LIBSTAND} ${LIBGELIBOOT} .include Index: sys/boot/i386/loader/main.c =================================================================== --- sys/boot/i386/loader/main.c +++ sys/boot/i386/loader/main.c @@ -68,7 +68,9 @@ static int isa_inb(int port); static void isa_outb(int port, int value); void exit(int code); +struct geli_boot_args *gargs; #ifdef LOADER_ZFS_SUPPORT +struct zfs_boot_args *zargs; static void i386_zfs_probe(void); #endif @@ -164,7 +166,31 @@ archsw.arch_isaoutb = isa_outb; #ifdef LOADER_ZFS_SUPPORT archsw.arch_zfs_probe = i386_zfs_probe; -#endif + +#ifdef LOADER_GELI_SUPPORT + if ((kargs->bootflags & KARGS_FLAGS_EXTARG) != 0) { + zargs = (struct zfs_boot_args *)(kargs + 1); + if (zargs != NULL && zargs->size >= offsetof(struct zfs_boot_args, gelipw)) { + if (zargs->gelipw[0] != '\0') { + setenv("kern.geom.eli.passphrase", zargs->gelipw, 1); + bzero(zargs->gelipw, sizeof(zargs->gelipw)); + } + } + } +#endif /* LOADER_GELI_SUPPORT */ +#else /* LOADER_ZFS_SUPPORT */ +#ifdef LOADER_GELI_SUPPORT + if ((kargs->bootflags & KARGS_FLAGS_EXTARG) != 0) { + gargs = (struct geli_boot_args *)(kargs + 1); + if (gargs != NULL && gargs->size >= offsetof(struct geli_boot_args, gelipw)) { + if (gargs->gelipw[0] != '\0') { + setenv("kern.geom.eli.passphrase", gargs->gelipw, 1); + bzero(gargs->gelipw, sizeof(gargs->gelipw)); + } + } + } +#endif /* LOADER_GELI_SUPPORT */ +#endif /* LOADER_ZFS_SUPPORT */ /* * March through the device switch probing for things. @@ -219,7 +245,6 @@ struct i386_devdesc new_currdev; #ifdef LOADER_ZFS_SUPPORT char buf[20]; - struct zfs_boot_args *zargs; #endif int biosdev = -1; Index: sys/boot/i386/zfsboot/Makefile =================================================================== --- sys/boot/i386/zfsboot/Makefile +++ sys/boot/i386/zfsboot/Makefile @@ -34,6 +34,14 @@ CFLAGS.gcc+= --param max-inline-insns-single=100 +.if !defined(LOADER_NO_GELI_SUPPORT) +CFLAGS+= -DGELI +CFLAGS+= -I${.CURDIR}/../../geli +LIBGELIBOOT= ${.OBJDIR}/../../geli/libgeliboot.a +# sys/ for rijndael +CFLAGS+= -I${.CURDIR}/../../../ +.endif + LD_FLAGS=-static -N --gc-sections LIBSTAND= ${.OBJDIR}/../../libstand32/libstand.a @@ -57,10 +65,10 @@ CLEANFILES+= zfsboot2 zfsboot.ld zfsboot.ldr zfsboot.bin zfsboot.out \ zfsboot.o zfsboot.s zfsboot.s.tmp sio.o cons.o drv.o util.o -# We currently allow 65536 bytes for zfsboot - in practice it could be +# We currently allow 131072 bytes for zfsboot - in practice it could be # any size up to 3.5Mb but keeping it fixed size simplifies zfsldr. # -BOOT2SIZE= 65536 +BOOT2SIZE= 131072 zfsboot2: zfsboot.ld @set -- `ls -l zfsboot.ld`; x=$$((${BOOT2SIZE}-$$5)); \ @@ -78,7 +86,7 @@ ${OBJCOPY} -S -O binary zfsboot.out ${.TARGET} zfsboot.out: ${BTXCRT} zfsboot.o sio.o drv.o cons.o util.o - ${LD} ${LD_FLAGS} -Ttext ${ORG2} -o ${.TARGET} ${.ALLSRC} ${LIBSTAND} + ${LD} ${LD_FLAGS} -Ttext ${ORG2} -o ${.TARGET} ${.ALLSRC} ${LIBSTAND} ${LIBGELIBOOT} SRCS= zfsboot.c Index: sys/boot/i386/zfsboot/zfsboot.c =================================================================== --- sys/boot/i386/zfsboot/zfsboot.c +++ sys/boot/i386/zfsboot/zfsboot.c @@ -94,6 +94,7 @@ static char cmddup[512]; static char kname[1024]; static char rootname[256]; +static char gelipw[256]; static int comspeed = SIOSPD; static struct bootinfo bootinfo; static uint32_t bootdev; @@ -134,10 +135,10 @@ printf("malloc failure\n"); for (;;) ; - return 0; + return (0); } heap_next += n; - return p; + return (p); } static char * @@ -145,9 +146,50 @@ { char *p = malloc(strlen(s) + 1); strcpy(p, s); - return p; + return (p); } +#ifdef GELI +#include "geliimpl.c" + +/* + * A wrapper for dskread that doesn't have to worry about whether the + * buffer pointer crosses a 64k boundary. Reads from an encrypted disk + */ +static int +geli_vdev_read(void *vdev __unused, void *priv, off_t off, void *buf, size_t bytes) +{ + char *p; + daddr_t lba; + unsigned int nb; + struct dsk *dsk = (struct dsk *) priv; + + if ((off & (DEV_BSIZE - 1)) || (bytes & (DEV_BSIZE - 1))) + return (-1); + + p = buf; + lba = off / DEV_BSIZE; + lba += dsk->start; + 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)) + return (-1); + /* decrypt */ + if (geli_read(dsk, ((lba - dsk->start) * DEV_BSIZE), + dmadat->rdbuf, nb * DEV_BSIZE)) + return (-1); + memcpy(p, dmadat->rdbuf, nb * DEV_BSIZE); + p += nb * DEV_BSIZE; + lba += nb; + bytes -= nb * DEV_BSIZE; + } + + return (0); +} +#endif + #include "zfsimpl.c" /* @@ -350,21 +392,52 @@ #ifdef GPT struct gpt_hdr hdr; struct gpt_ent *ent; - daddr_t slba, elba; unsigned part, entries_per_sec; #endif + daddr_t slba, elba; struct dos_partition *dp; char *sec; - unsigned i; + unsigned i, r; /* - * If we find a vdev on the whole disk, stop here. Otherwise dig - * out the partition table and probe each slice/partition - * in turn for a vdev. + * If we find a vdev on the whole disk, stop here. */ if (vdev_probe(vdev_read, dsk, NULL) == 0) return; +#ifdef GELI + /* + * Taste the disk, if it is GELI encrypted, decrypt it and check to see if + * it is a usable vdev then. Otherwise dig + * out the partition table and probe each slice/partition + * in turn for a vdev or GELI encrypted vdev. + */ + elba = drvsize(dsk); + if (elba > 0) { + elba--; + } + if (geli_taste(vdev_read, dsk, elba) == 0) { + for (r = 0; r < 3; r++) { + /* Try cached passphrase */ + if (r == 0 && gelipw[0] != '\0') { + if (geli_attach(vdev_read, dsk, &gelipw) == 0) { + if (vdev_probe(geli_vdev_read, dsk, NULL) == 0) { + return; + } + } + } + printf("GELI Password for disk%d: ", dsk->unit); + getpwstr(gelipw, sizeof(gelipw)); + printf("\n"); + if (geli_attach(vdev_read, dsk, &gelipw) == 0) { + if (vdev_probe(geli_vdev_read, dsk, NULL) == 0) { + return; + } + } + } + } +#endif + sec = dmadat->secbuf; dsk->start = 0; @@ -387,6 +460,8 @@ * return the spa_t for the first we find (if requested). This * will have the effect of booting from the first pool on the * disk. + * + * If no vdev is found, GELI decrypting the device and try again */ entries_per_sec = DEV_BSIZE / hdr.hdr_entsz; slba = hdr.hdr_lba_table; @@ -400,6 +475,8 @@ if (memcmp(&ent->ent_type, &freebsd_zfs_uuid, sizeof(uuid_t)) == 0) { dsk->start = ent->ent_lba_start; + dsk->slice = part + 1; + dsk->part = 255; if (vdev_probe(vdev_read, dsk, NULL) == 0) { /* * This slice had a vdev. We need a new dsk @@ -407,6 +484,39 @@ */ dsk = copy_dsk(dsk); } +#ifdef GELI + else if (geli_taste(vdev_read, dsk, ent->ent_lba_end - + ent->ent_lba_start) == 0) { + for (r = 0; r < 3; r++) { + /* Try cached passphrase */ + if (r == 0 && gelipw[0] != '\0') { + if (geli_attach(vdev_read, dsk, &gelipw) == 0) { + if (vdev_probe(geli_vdev_read, dsk, NULL) == 0) { + dsk = copy_dsk(dsk); + } + break; + } + } + printf("GELI Password for disk%dp%d: ", dsk->unit, + dsk->slice); + getpwstr(gelipw, sizeof(gelipw)); + printf("\n"); + if (geli_attach(vdev_read, dsk, &gelipw) == 0) { + /* + * This slice has GELI, check it for ZFS. + */ + if (vdev_probe(geli_vdev_read, dsk, NULL) == 0) { + /* + * This slice had a vdev. We need a new dsk + * structure now since the vdev now owns this one. + */ + dsk = copy_dsk(dsk); + } + break; + } + } + } +#endif } } slba++; @@ -423,13 +533,42 @@ if (!dp[i].dp_typ) continue; dsk->start = dp[i].dp_start; + dsk->slice = i + 1; if (vdev_probe(vdev_read, dsk, NULL) == 0) { - /* - * This slice had a vdev. We need a new dsk structure now - * since the vdev now owns this one. - */ dsk = copy_dsk(dsk); } +#ifdef GELI + else if (geli_taste(vdev_read, dsk, dp[i].dp_size - + dp[i].dp_start) == 0) { + for (i = 0; i < 3; i++) { + /* Try cached passphrase */ + if (r == 0 && gelipw[0] != '\0') { + if (geli_attach(vdev_read, dsk, &gelipw) == 0) { + if (vdev_probe(geli_vdev_read, dsk, NULL) == 0) { + dsk = copy_dsk(dsk); + } + break; + } + } + printf("GELI Password for disk%ds%d: ", dsk->unit, i); + getpwstr(gelipw, sizeof(gelipw)); + printf("\n"); + if (geli_attach(vdev_read, dsk, &gelipw) == 0) { + /* + * This slice has GELI, check it for ZFS. + */ + if (vdev_probe(geli_vdev_read, dsk, NULL) == 0) { + /* + * This slice had a vdev. We need a new dsk + * structure now since the vdev now owns this one. + */ + dsk = copy_dsk(dsk); + } + break; + } + } + } +#endif } } @@ -449,8 +588,8 @@ heap_end = PTOV(high_heap_base + high_heap_size); heap_next = PTOV(high_heap_base); } else { - heap_next = (char *) dmadat + sizeof(*dmadat); - heap_end = (char *) PTOV(bios_basemem); + heap_next = (char *)dmadat + sizeof(*dmadat); + heap_end = (char *)PTOV(bios_basemem); } dsk = malloc(sizeof(struct dsk)); @@ -476,6 +615,9 @@ autoboot = 1; +#ifdef GELI + geli_init(); +#endif zfs_init(); /* @@ -694,6 +836,8 @@ zfsargs.pool = zfsmount.spa->spa_guid; zfsargs.root = zfsmount.rootobj; zfsargs.primary_pool = primary_spa->spa_guid; + bcopy(gelipw, zfsargs.gelipw, sizeof(zfsargs.gelipw)); + bzero(gelipw, sizeof(gelipw)); if (primary_vdev != NULL) zfsargs.primary_vdev = primary_vdev->v_guid; else Index: sys/boot/zfs/libzfs.h =================================================================== --- sys/boot/zfs/libzfs.h +++ sys/boot/zfs/libzfs.h @@ -55,6 +55,7 @@ uint64_t root; uint64_t primary_pool; uint64_t primary_vdev; + char gelipw[256]; }; int zfs_parsedev(struct zfs_devdesc *dev, const char *devspec,