Date: Wed, 16 Mar 2016 23:12:19 +0000 (UTC) From: Allan Jude <allanjude@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r296963 - in head/sys/boot: . common geli i386/common i386/gptboot i386/gptzfsboot i386/libi386 i386/loader i386/zfsboot zfs Message-ID: <201603162312.u2GNCJdO058299@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: allanjude Date: Wed Mar 16 23:12:19 2016 New Revision: 296963 URL: https://svnweb.freebsd.org/changeset/base/296963 Log: Implement GELI (AES-XTS and AES-CBC only) in gptboot and gptzfsboot Allows booting from a GELI encrypted root file system, via UFS or ZFS Reviewed by: gnn, smh (previous version), delphij (previous version) Relnotes: yes Sponsored by: ScaleEngine Inc. Differential Revision: https://reviews.freebsd.org/D4593 Added: head/sys/boot/geli/ head/sys/boot/geli/Makefile (contents, props changed) head/sys/boot/geli/geliboot.c (contents, props changed) head/sys/boot/geli/geliboot.h (contents, props changed) head/sys/boot/geli/geliboot_crypto.c (contents, props changed) head/sys/boot/geli/pwgets.c (contents, props changed) - copied, changed from r292837, head/lib/libstand/gets.c Modified: head/sys/boot/Makefile.amd64 head/sys/boot/Makefile.i386 head/sys/boot/common/disk.c head/sys/boot/common/disk.h head/sys/boot/common/gpt.c head/sys/boot/common/gpt.h head/sys/boot/i386/common/bootargs.h head/sys/boot/i386/common/cons.c head/sys/boot/i386/common/drv.c head/sys/boot/i386/common/drv.h head/sys/boot/i386/gptboot/Makefile head/sys/boot/i386/gptboot/gptboot.c head/sys/boot/i386/gptzfsboot/Makefile head/sys/boot/i386/libi386/Makefile head/sys/boot/i386/libi386/biosdisk.c head/sys/boot/i386/loader/Makefile head/sys/boot/i386/loader/main.c head/sys/boot/i386/zfsboot/zfsboot.c head/sys/boot/zfs/libzfs.h Modified: head/sys/boot/Makefile.amd64 ============================================================================== --- head/sys/boot/Makefile.amd64 Wed Mar 16 23:06:34 2016 (r296962) +++ head/sys/boot/Makefile.amd64 Wed Mar 16 23:12:19 2016 (r296963) @@ -3,6 +3,7 @@ SUBDIR+= efi SUBDIR+= libstand32 SUBDIR+= zfs +SUBDIR+= geli SUBDIR+= userboot .if ${MK_FORTH} != "no" Modified: head/sys/boot/Makefile.i386 ============================================================================== --- head/sys/boot/Makefile.i386 Wed Mar 16 23:06:34 2016 (r296962) +++ head/sys/boot/Makefile.i386 Wed Mar 16 23:12:19 2016 (r296963) @@ -3,3 +3,4 @@ SUBDIR+= efi SUBDIR+= libstand32 SUBDIR+= zfs +SUBDIR+= geli Modified: head/sys/boot/common/disk.c ============================================================================== --- head/sys/boot/common/disk.c Wed Mar 16 23:06:34 2016 (r296962) +++ head/sys/boot/common/disk.c Wed Mar 16 23:12:19 2016 (r296963) @@ -170,7 +170,7 @@ display_size(uint64_t size, u_int sector return (buf); } -static int +int ptblread(void *d, void *buf, size_t blocks, off_t offset) { struct disk_devdesc *dev; Modified: head/sys/boot/common/disk.h ============================================================================== --- head/sys/boot/common/disk.h Wed Mar 16 23:06:34 2016 (r296962) +++ head/sys/boot/common/disk.h Wed Mar 16 23:12:19 2016 (r296963) @@ -107,6 +107,7 @@ extern int disk_read(struct disk_devdesc 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. Modified: head/sys/boot/common/gpt.c ============================================================================== --- head/sys/boot/common/gpt.c Wed Mar 16 23:06:34 2016 (r296962) +++ head/sys/boot/common/gpt.c Wed Mar 16 23:12:19 2016 (r296963) @@ -39,8 +39,6 @@ __FBSDID("$FreeBSD$"); #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]; Modified: head/sys/boot/common/gpt.h ============================================================================== --- head/sys/boot/common/gpt.h Wed Mar 16 23:06:34 2016 (r296962) +++ head/sys/boot/common/gpt.h Wed Mar 16 23:12:19 2016 (r296963) @@ -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); Added: head/sys/boot/geli/Makefile ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/boot/geli/Makefile Wed Mar 16 23:12:19 2016 (r296963) @@ -0,0 +1,52 @@ +# $FreeBSD$ +# libgeliboot + +MAN= + +.include <src.opts.mk> +MK_SSP= no + +LIB= geliboot +INTERNALLIB= +MK_PROFILE= no +NO_PIC= + +.if ${MACHINE_CPUARCH} == "i386" || ${MACHINE_CPUARCH} == "amd64" +CFLAGS+= -march=i386 +.endif +.if ${MACHINE_ARCH} == "amd64" || ${MACHINE_ARCH} == "powerpc64" +CFLAGS+= -m32 +.endif + +WARNS?= 0 + +# string functions from libc +.PATH: ${.CURDIR}/../../../lib/libc/string +SRCS+= bcmp.c bcopy.c bzero.c + +# Our password input method +SRCS+= pwgets.c + +# sha256 and sha512 from sys/crypto +.PATH: ${.CURDIR}/../../crypto/sha2 +CFLAGS+= -DWEAK_REFS +SRCS+= sha256c.c sha512c.c + +# md5 from libmd +.PATH: ${.CURDIR}/../../../lib/libmd +SRCS+= md5c.c + +# AES implementation from sys/crypto +.PATH: ${.CURDIR}/../../crypto/rijndael +CFLAGS+= -I${.CURDIR}/../../ +# Remove asserts +CFLAGS+= -DNDEBUG +SRCS+= rijndael-alg-fst.c rijndael-api-fst.c rijndael-api.c + +# local GELI Implementation +.PATH: ${.CURDIR}/../../geom/eli +CFLAGS+= -D_STAND +SRCS+= geliboot_crypto.c g_eli_hmac.c g_eli_key.c g_eli_key_cache.c pkcs5v2.c + +.include <bsd.stand.mk> +.include <bsd.lib.mk> Added: head/sys/boot/geli/geliboot.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/boot/geli/geliboot.c Wed Mar 16 23:12:19 2016 (r296963) @@ -0,0 +1,284 @@ +/*- + * 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 "geliboot.h" + +SLIST_HEAD(geli_list, geli_entry) geli_head = SLIST_HEAD_INITIALIZER(geli_head); +struct geli_list *geli_headp; + +static int +geli_same_device(struct geli_entry *ge, struct dsk *dskp) +{ + + if (geli_e->dsk->drive == dskp->drive && + dskp->part == 255 && geli_e->dsk->part == dskp->slice) { + /* + * Sometimes slice = slice, and sometimes part = slice + * If the incoming struct dsk has part=255, it means look at + * the slice instead of the part number + */ + return (0); + } + + /* Is this the same device? */ + if (geli_e->dsk->drive != dskp->drive || + geli_e->dsk->slice != dskp->slice || + geli_e->dsk->part != dskp->part) { + return (1); + } + + return (0); +} + +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 + */ +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; + eli_metadata_softc(&geli_e->sc, &md, DEV_BSIZE, + (lastsector + DEV_BSIZE) * DEV_BSIZE); + + SLIST_INSERT_HEAD(&geli_head, geli_e, entries); + geli_count++; + + return (0); +} + +/* + * Attempt to decrypt the device + */ +int +geli_attach(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_same_device(geli_e, dskp) != 0) { + continue; + } + + g_eli_crypto_hmac_init(&ctx, NULL, 0); + /* + * Prepare Derived-Key from the user passphrase. + */ + if (geli_e->md.md_iterations == 0) { + g_eli_crypto_hmac_update(&ctx, geli_e->md.md_salt, + sizeof(geli_e->md.md_salt)); + g_eli_crypto_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); + g_eli_crypto_hmac_update(&ctx, dkey, sizeof(dkey)); + bzero(&dkey, sizeof(dkey)); + } + + g_eli_crypto_hmac_final(&ctx, key, 0); + + error = g_eli_mkey_decrypt(&geli_e->md, key, mkey, &keynum); + bzero(&key, sizeof(key)); + if (error == -1) { + bzero(&mkey, sizeof(mkey)); + printf("Bad GELI key: %d\n", error); + return (error); + } else if (error != 0) { + bzero(&mkey, sizeof(mkey)); + printf("Failed to decrypt GELI master key: %d\n", error); + return (error); + } + + /* Store the keys */ + bcopy(mkey, geli_e->sc.sc_mkey, sizeof(geli_e->sc.sc_mkey)); + bcopy(mkey, geli_e->sc.sc_ivkey, sizeof(geli_e->sc.sc_ivkey)); + mkp = mkey + sizeof(geli_e->sc.sc_ivkey); + if ((geli_e->sc.sc_flags & G_ELI_FLAG_AUTH) == 0) { + bcopy(mkp, geli_e->sc.sc_ekey, G_ELI_DATAKEYLEN); + } else { + /* + * The encryption key is: ekey = HMAC_SHA512(Data-Key, 0x10) + */ + g_eli_crypto_hmac(mkp, G_ELI_MAXKEYLEN, "\x10", 1, + geli_e->sc.sc_ekey, 0); + } + bzero(&mkey, sizeof(mkey)); + + /* Initialize the per-sector IV */ + switch (geli_e->sc.sc_ealgo) { + case CRYPTO_AES_XTS: + break; + default: + SHA256_Init(&geli_e->sc.sc_ivctx); + SHA256_Update(&geli_e->sc.sc_ivctx, geli_e->sc.sc_ivkey, + sizeof(geli_e->sc.sc_ivkey)); + break; + } + + return (0); + } + + /* Disk not found */ + return (2); +} + +int +is_geli(struct dsk *dskp) +{ + SLIST_FOREACH_SAFE(geli_e, &geli_head, entries, geli_e_tmp) { + if (geli_same_device(geli_e, dskp) == 0) { + return (0); + } + } + + return (1); +} + +int +geli_read(struct dsk *dskp, off_t offset, u_char *buf, size_t bytes) +{ + u_char iv[G_ELI_IVKEYLEN]; + u_char *pbuf; + int error; + off_t os; + uint64_t keyno; + size_t n, nb; + struct g_eli_key gkey; + + SLIST_FOREACH_SAFE(geli_e, &geli_head, entries, geli_e_tmp) { + if (geli_same_device(geli_e, dskp) != 0) { + continue; + } + + nb = bytes / DEV_BSIZE; + for (n = 0; n < nb; n++) { + os = offset + (n * DEV_BSIZE); + pbuf = buf + (n * DEV_BSIZE); + + g_eli_crypto_ivgen(&geli_e->sc, os, iv, G_ELI_IVKEYLEN); + + /* Get the key that corresponds to this offset */ + keyno = (os >> G_ELI_KEY_SHIFT) / DEV_BSIZE; + g_eli_key_fill(&geli_e->sc, &gkey, keyno); + + error = geliboot_crypt(geli_e->sc.sc_ealgo, 0, pbuf, + DEV_BSIZE, gkey.gek_key, geli_e->sc.sc_ekeylen, iv); + + if (error != 0) { + bzero(&gkey, sizeof(gkey)); + printf("Failed to decrypt in geli_read()!"); + return (error); + } + } + bzero(&gkey, sizeof(gkey)); + return (0); + } + + printf("GELI provider not found\n"); + return (1); +} + +int +geli_passphrase(char *pw, int disk, int parttype, int part, struct dsk *dskp) +{ + int i; + + /* TODO: Implement GELI keyfile(s) support */ + for (i = 0; i < 3; i++) { + /* Try cached passphrase */ + if (i == 0 && pw[0] != '\0') { + if (geli_attach(dskp, pw) == 0) { + return (0); + } + } + printf("GELI Passphrase for disk%d%c%d: ", disk, parttype, part); + pwgets(pw, GELI_PW_MAXLEN); + printf("\n"); + if (geli_attach(dskp, pw) == 0) { + return (0); + } + } + + return (1); +} Added: head/sys/boot/geli/geliboot.h ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/boot/geli/geliboot.h Wed Mar 16 23:12:19 2016 (r296963) @@ -0,0 +1,86 @@ +/*- + * 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> + +#ifndef _GELIBOOT_H_ +#define _GELIBOOT_H_ + +#define _STRING_H_ +#define _STRINGS_H_ +#define _STDIO_H_ +#include <geom/eli/g_eli.h> +#include <geom/eli/pkcs5v2.h> + +/* Pull in the md5, sha256, and sha512 implementations */ +#include <md5.h> +#include <crypto/sha2/sha256.h> +#include <crypto/sha2/sha512.h> + +/* Pull in AES implementation */ +#include <crypto/rijndael/rijndael-api-fst.h> + +/* AES-XTS implementation */ +#define _STAND +#define STAND_H /* We don't want stand.h in {gpt,zfs,gptzfs}boot */ +#include <opencrypto/xform_enc.h> + +#ifndef DEV_BSIZE +#define DEV_BSIZE 512 +#endif + +#ifndef MIN +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +#define GELI_PW_MAXLEN 256 +extern void pwgets(char *buf, int n); + +struct geli_entry { + struct dsk *dsk; + off_t part_end; + struct g_eli_softc sc; + struct g_eli_metadata md; + SLIST_ENTRY(geli_entry) entries; +} *geli_e, *geli_e_tmp; + +int geli_count; + +void geli_init(void); +int geli_taste(int read_func(void *vdev, void *priv, off_t off, + void *buf, size_t bytes), struct dsk *dsk, daddr_t lastsector); +int geli_attach(struct dsk *dskp, const char *passphrase); +int is_geli(struct dsk *dsk); +int geli_read(struct dsk *dsk, off_t offset, u_char *buf, size_t bytes); +int geli_decrypt(u_int algo, u_char *data, size_t datasize, + const u_char *key, size_t keysize, const uint8_t* iv); +int geli_passphrase(char *pw, int disk, int parttype, int part, struct dsk *dskp); + +#endif /* _GELIBOOT_H_ */ Added: head/sys/boot/geli/geliboot_crypto.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/boot/geli/geliboot_crypto.c Wed Mar 16 23:12:19 2016 (r296963) @@ -0,0 +1,135 @@ +/*- + * Copyright (c) 2005-2010 Pawel Jakub Dawidek <pjd@FreeBSD.org> + * 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$ + */ + +#include "geliboot.h" + +int +geliboot_crypt(u_int algo, int enc, u_char *data, size_t datasize, + const u_char *key, size_t keysize, u_char *iv) +{ + keyInstance aeskey; + cipherInstance cipher; + struct aes_xts_ctx xtsctx, *ctxp; + size_t xts_len; + int err, blks, i; + + switch (algo) { + case CRYPTO_AES_CBC: + err = rijndael_makeKey(&aeskey, !enc, keysize, + (const char *)key); + if (err < 0) { + printf("Failed to setup decryption keys: %d\n", err); + return (err); + } + + err = rijndael_cipherInit(&cipher, MODE_CBC, iv); + if (err < 0) { + printf("Failed to setup IV: %d\n", err); + return (err); + } + + switch (enc) { + case 0: /* decrypt */ + blks = rijndael_blockDecrypt(&cipher, &aeskey, data, + datasize * 8, data); + break; + case 1: /* encrypt */ + blks = rijndael_blockEncrypt(&cipher, &aeskey, data, + datasize * 8, data); + break; + } + if (datasize != (blks / 8)) { + printf("Failed to decrypt the entire input: " + "%u != %u\n", blks, datasize); + return (1); + } + break; + case CRYPTO_AES_XTS: + xts_len = keysize << 1; + ctxp = &xtsctx; + + rijndael_set_key(&ctxp->key1, key, xts_len / 2); + rijndael_set_key(&ctxp->key2, key + (xts_len / 16), xts_len / 2); + + enc_xform_aes_xts.reinit(ctxp, iv); + + switch (enc) { + case 0: /* decrypt */ + for (i = 0; i < datasize; i += AES_XTS_BLOCKSIZE) { + enc_xform_aes_xts.decrypt(ctxp, data + i); + } + break; + case 1: /* encrypt */ + for (i = 0; i < datasize; i += AES_XTS_BLOCKSIZE) { + enc_xform_aes_xts.encrypt(ctxp, data + i); + } + break; + } + break; + default: + printf("Unsupported crypto algorithm #%d\n", algo); + return (1); + } + + return (0); +} + +static int +g_eli_crypto_cipher(u_int algo, int enc, u_char *data, size_t datasize, + const u_char *key, size_t keysize) +{ + u_char iv[keysize]; + + bzero(iv, sizeof(iv)); + return (geliboot_crypt(algo, enc, data, datasize, key, keysize, iv)); +} + +int +g_eli_crypto_encrypt(u_int algo, u_char *data, size_t datasize, + const u_char *key, size_t keysize) +{ + + /* We prefer AES-CBC for metadata protection. */ + if (algo == CRYPTO_AES_XTS) + algo = CRYPTO_AES_CBC; + + return (g_eli_crypto_cipher(algo, 1, data, datasize, key, keysize)); +} + +int +g_eli_crypto_decrypt(u_int algo, u_char *data, size_t datasize, + const u_char *key, size_t keysize) +{ + + /* We prefer AES-CBC for metadata protection. */ + if (algo == CRYPTO_AES_XTS) + algo = CRYPTO_AES_CBC; + + return (g_eli_crypto_cipher(algo, 0, data, datasize, key, keysize)); +} Copied and modified: head/sys/boot/geli/pwgets.c (from r292837, head/lib/libstand/gets.c) ============================================================================== --- head/lib/libstand/gets.c Mon Dec 28 18:36:00 2015 (r292837, copy source) +++ head/sys/boot/geli/pwgets.c Wed Mar 16 23:12:19 2016 (r296963) @@ -36,10 +36,10 @@ __FBSDID("$FreeBSD$"); #include "stand.h" -/* gets() with constrained input length */ +/* gets() with constrained input length, for passwords */ void -ngets(char *buf, int n) +pwgets(char *buf, int n) { int c; char *lp; @@ -76,37 +76,8 @@ ngets(char *buf, int n) default: if ((n < 1) || ((lp - buf) < n - 1)) { *lp++ = c; - putchar(c); + putchar('*'); } } /*NOTREACHED*/ } - -int -fgetstr(char *buf, int size, int fd) -{ - char c; - int err, len; - - size--; /* leave space for terminator */ - len = 0; - while (size != 0) { - err = read(fd, &c, sizeof(c)); - if (err < 0) /* read error */ - return(-1); - if (err == 0) { /* EOF */ - if (len == 0) - return(-1); /* nothing to read */ - break; - } - if ((c == '\r') || /* line terminators */ - (c == '\n')) - break; - *buf++ = c; /* keep char */ - size--; - len++; - } - *buf = 0; - return(len); -} - Modified: head/sys/boot/i386/common/bootargs.h ============================================================================== --- head/sys/boot/i386/common/bootargs.h Wed Mar 16 23:06:34 2016 (r296962) +++ head/sys/boot/i386/common/bootargs.h Wed Mar 16 23:12:19 2016 (r296963) @@ -64,6 +64,12 @@ struct bootargs */ }; +struct geli_boot_args +{ + uint32_t size; + char gelipw[256]; +}; + #endif /*__ASSEMBLER__*/ #endif /* !_BOOT_I386_ARGS_H_ */ Modified: head/sys/boot/i386/common/cons.c ============================================================================== --- head/sys/boot/i386/common/cons.c Wed Mar 16 23:06:34 2016 (r296962) +++ head/sys/boot/i386/common/cons.c Wed Mar 16 23:12:19 2016 (r296963) @@ -97,6 +97,13 @@ xgetc(int fn) } int +getchar(void) +{ + + return (xgetc(0)); +} + +int keyhit(unsigned int secs) { uint32_t t0, t1; Modified: head/sys/boot/i386/common/drv.c ============================================================================== --- head/sys/boot/i386/common/drv.c Wed Mar 16 23:06:34 2016 (r296962) +++ head/sys/boot/i386/common/drv.c Wed Mar 16 23:12:19 2016 (r296963) @@ -29,7 +29,6 @@ __FBSDID("$FreeBSD$"); #include "xreadorg.h" #endif -#ifdef GPT static struct edd_params params; uint64_t @@ -50,7 +49,6 @@ drvsize(struct dsk *dskp) } return (params.sectors); } -#endif /* GPT */ #ifndef USE_XREAD static struct edd_packet packet; Modified: head/sys/boot/i386/common/drv.h ============================================================================== --- head/sys/boot/i386/common/drv.h Wed Mar 16 23:06:34 2016 (r296962) +++ head/sys/boot/i386/common/drv.h Wed Mar 16 23:12:19 2016 (r296963) @@ -42,7 +42,7 @@ struct dsk { 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_ */ Modified: head/sys/boot/i386/gptboot/Makefile ============================================================================== --- head/sys/boot/i386/gptboot/Makefile Wed Mar 16 23:06:34 2016 (r296962) +++ head/sys/boot/i386/gptboot/Makefile Wed Mar 16 23:12:19 2016 (r296963) @@ -39,6 +39,14 @@ CFLAGS= -DBOOTPROG=\"gptboot\" \ CFLAGS.gcc+= --param max-inline-insns-single=100 +.if !defined(LOADER_NO_GELI_SUPPORT) +CFLAGS+= -DLOADER_GELI_SUPPORT +CFLAGS+= -I${.CURDIR}/../../geli +LIBGELIBOOT= ${.OBJDIR}/../../geli/libgeliboot.a +.PATH: ${.CURDIR}/../../../opencrypto +OPENCRYPTO_XTS= xform_aes_xts.o +.endif + LD_FLAGS=-static -N --gc-sections LIBSTAND= ${.OBJDIR}/../../libstand32/libstand.a @@ -60,14 +68,14 @@ gptldr.bin: gptldr.out 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 \ - cons.o util.o +CLEANFILES+= gptboot.bin gptboot.out gptboot.o sio.o crc32.o drv.o \ + cons.o util.o ${OPENCRYPTO_XTS} 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 ${OPENCRYPTO_XTS} + ${LD} ${LD_FLAGS} -Ttext ${ORG2} -o ${.TARGET} ${.ALLSRC} ${LIBSTAND} ${LIBGELIBOOT} gptboot.o: ${.CURDIR}/../../common/ufsread.c Modified: head/sys/boot/i386/gptboot/gptboot.c ============================================================================== --- head/sys/boot/i386/gptboot/gptboot.c Wed Mar 16 23:06:34 2016 (r296962) +++ head/sys/boot/i386/gptboot/gptboot.c Wed Mar 16 23:12:19 2016 (r296963) @@ -23,6 +23,7 @@ __FBSDID("$FreeBSD$"); #include <machine/bootinfo.h> #include <machine/elf.h> +#include <machine/pc/bios.h> #include <machine/psl.h> #include <stdarg.h> @@ -31,6 +32,7 @@ __FBSDID("$FreeBSD$"); #include <btxv86.h> +#include "bootargs.h" #include "lib.h" #include "rbx.h" #include "drv.h" @@ -82,14 +84,60 @@ static struct dsk dsk; static char kname[1024]; 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); +void *malloc(size_t n); +void free(void *ptr); +#ifdef LOADER_GELI_SUPPORT +static int vdev_read(void *vdev __unused, void *priv, off_t off, void *buf, + size_t bytes); +#endif + +void * +malloc(size_t n) +{ + char *p = heap_next; + if (p + n > heap_end) { + printf("malloc failure\n"); + for (;;) + ; + /* NOTREACHED */ + return (0); + } + heap_next += n; + return (p); +} + +void +free(void *ptr) +{ + + return; +} #include "ufsread.c" +#include "gpt.c" +#ifdef LOADER_GELI_SUPPORT +#include "geliboot.c" +static char gelipw[GELI_PW_MAXLEN]; +#endif static inline int xfsread(ufs_ino_t inode, void *buf, size_t nbyte) @@ -102,14 +150,90 @@ xfsread(ufs_ino_t inode, void *buf, size return (0); } -static inline uint32_t -memsize(void) +static void +bios_getmem(void) { + uint64_t size; - v86.addr = MEM_EXT; + /* 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; + } + + /* 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 @@ -124,6 +248,16 @@ gptinit(void) printf("%s: no UFS partition was found\n", BOOTPROG); return (-1); } +#ifdef LOADER_GELI_SUPPORT + if (geli_taste(vdev_read, &dsk, (gpttable[curent].ent_lba_end - + gpttable[curent].ent_lba_start)) == 0) { + if (geli_passphrase(&gelipw, dsk.unit, 'p', curent + 1, &dsk) != 0) { + printf("%s: unable to decrypt GELI key\n", BOOTPROG); + return (-1); + } + } +#endif + dsk_meta = 0; return (0); } @@ -137,6 +271,17 @@ main(void) 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); @@ -146,10 +291,14 @@ main(void) 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; *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201603162312.u2GNCJdO058299>