Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F149079751
D4593.id11389.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
60 KB
Referenced Files
None
Subscribers
None
D4593.id11389.diff
View Options
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 <uuid.h>
#include <drv.h>
+#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 <allanjude@FreeBSD.org>
+ * Copyright (c) 2005-2011 Pawel Jakub Dawidek <pawel@dawidek.net>
+ * 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 <sys/endian.h>
+#include <sys/queue.h>
+
+/*
+ * Stop rijndael and others from pulling in string.h
+ */
+#define _STRING_H_
+
+/* Pull in the sha256 and sha512 implementation */
+#include <sha512.h>
+#include <crypto/sha2/sha256.h>
+
+/*
+ * 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 <crypto/rijndael/rijndael-api-fst.h>
+
+#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 <allanjude@FreeBSD.org>
+ * 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 <allanjude@FreeBSD.org>
+ * Copyright (c) 2005-2011 Pawel Jakub Dawidek <pawel@dawidek.net>
+ * 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 <machine/bootinfo.h>
#include <machine/elf.h>
+#include <machine/pc/bios.h>
#include <machine/psl.h>
#include <stdarg.h>
@@ -31,6 +32,8 @@
#include <btxv86.h>
+#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 <uuid.h>
+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 <bsd.prog.mk>
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,
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Mar 23, 4:39 AM (5 h, 44 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
30160143
Default Alt Text
D4593.id11389.diff (60 KB)
Attached To
Mode
D4593: Implement a stripped down version of GELI (AES-XTS and AES-CBC only) in gptboot and gptzfsboot
Attached
Detach File
Event Timeline
Log In to Comment