diff --git a/sbin/Makefile b/sbin/Makefile index 5085f4ae3bb9..1600de60ade6 100644 --- a/sbin/Makefile +++ b/sbin/Makefile @@ -24,6 +24,7 @@ SUBDIR= adjkerntz \ fsck_msdosfs \ fsdb \ fsirand \ + gbde \ growfs \ ifconfig \ init \ diff --git a/sbin/gbde/Makefile b/sbin/gbde/Makefile new file mode 100644 index 000000000000..f23ca62a6760 --- /dev/null +++ b/sbin/gbde/Makefile @@ -0,0 +1,25 @@ +# $FreeBSD$ + +PROG= gbde +SRCS= gbde.c template.c +SRCS+= geom_enc.c +SRCS+= rijndael-alg-fst.c +SRCS+= rijndael-api-fst.c +SRCS+= g_bde_lock.c + +CFLAGS+= -I/sys +VPATH += ${.CURDIR}/../../sys/geom:${.CURDIR}/../../sys/geom/bde:${.CURDIR}/../../sys/crypto/rijndael + +CLEANFILES+= template.c + +MAN= gbde.8 +LDADD= -lmd -lutil + +.include + +template.c: template.txt + file2c 'const char template[] = {' ',0};' \ + < ${.CURDIR}/template.txt > template.c + +test: ${PROG} + sh ${.CURDIR}/test.sh diff --git a/sbin/gbde/gbde.8 b/sbin/gbde/gbde.8 new file mode 100644 index 000000000000..bc5b29c2488f --- /dev/null +++ b/sbin/gbde/gbde.8 @@ -0,0 +1,157 @@ +.\" +.\" Copyright (c) 2002 Poul-Henning Kamp +.\" Copyright (c) 2002 Networks Associates Technology, Inc. +.\" All rights reserved. +.\" +.\" This software was developed for the FreeBSD Project by Poul-Henning Kamp +.\" and NAI Labs, the Security Research Division of Network Associates, Inc. +.\" under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the +.\" DARPA CHATS research program. +.\" +.\" 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. +.\" 3. The names of the authors may not be used to endorse or promote +.\" products derived from this software without specific prior written +.\" permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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$ +.\" +.Dd October 19, 2002 +.Os +.Dt gbde 9 +.Sh NAME +.Nm gbde +.Nd Operation and management utility for Geom Based Disk Encryption. +.Sh SYNOPSIS +.Nm +attach +.Ar destination +.Op Fl l Ar lockfile +.Op Fl p Ar pass-phrase +.Nm +dettach +.Ar destination +.Nm +init +.Ar destination +.Op Fl i +.Op Fl f Ar filename +.Op Fl L Ar lockfile +.Op Fl P Ar pass-phrase +.Nm +setkey +.Ar destination +.Op Fl n Ar key +.Op Fl l Ar lockfile +.Op Fl p Ar pass-phrase +.Op Fl L Ar new-lockfile +.Op Fl P Ar new-pass-phrase +.Nm +destroy +.Ar destination +.Op Fl n Ar key +.Op Fl l Ar lockfile +.Op Fl L Ar lockfile +.Sh DESCRIPTION +The +.Nm +program is the only official operation and management interface for the +.Xr gbde 4 +GEOM based disk encryption kernel facility. +The interaction between the +.Nm +program and the kernel part is not a published interface. +.Pp +The operational aspect consists of two subcommands, one to open and attach +a device and one to close and dettach +a device to the in-kernel cryptographic gbde module. +.Pp +The management part allows initialization of the master key and lock sectors +on a device, initialization and replacement of pass-phrases and +key invalidation and blackening functions. +.Pp +The +.Fl l Ar lockfile +argument is used to supply the lock selector data. +If no +.Fl l +argument is specified, the first sector is used for this purpose. +.Pp +.Fl L Ar new-lockfile +specifies the lock selector file for the key modified with the +.Ar setkey subcommand. +.Pp +The +.Fl n Ar key +argument can be used to specify which of the four keys the operation applies to. +A value of 1 to 4 selects the specified key, a value of 0 (the default) means "this key" (ie, the key used to gain access to the device) and a value of -1 means "all keys". +.Pp +The +.Fl f Ar filename +specifies an optional parameter file for use under initialization. +.Pp +Alternatively the +.Fl i +optional toggles an interactive mode where a template file with descriptions +of the parameters can be interactively edited. +.Pp +.Fl p Ar pass-phrase +specifies the pass-phrase used to opening the device. +If not specified the controlling terminal will be used to prompt the user +for the pass-phrase. +.Pp +.Fl P Ar new-pass-phrase +can be used to specify the new pass-phrase to the +.Ar setkey +subcommand. +If not specified, the user is prompted for the new pass-phrase on the +controlling terminal. +.Sh EXAMPLES +To initialize a device, using default parameters: +.Dl # gbde init /dev/ad0s1f -l /etc/ad0s1f.lock +.Pp +To attach an encrypted device: +.Dl # gbde attach ad0s1f -l /etc/ad0s1f.lock +.Pp +To dettach an encrypted device: +.Dl # gbde dettach ad0s1f +.Pp +To initialize the second key using a dettached lockfile and a trivial +pass-phrase: +.Dl # gbde setkey ad0s1f -n 2 -P foo -L key2.lockfile +.Pp +To destroy all copies of the masterkey: +.Dl # gbde destroy ad0s1f -n -1 +.Sh SEE ALSO +.Xr gbde 4 , +.Xr geom 4 . +.Rs +.%A Poul-Henning Kamp +.%T "Making sure data is lost: Spook-strength encryption of on-disk data" +.%R "Refereed paper, NORDU2003 conference" +.Re +.Sh HISTORY +This software was developed for the FreeBSD Project by Poul-Henning Kamp +and NAI Labs, the Security Research Division of Network Associates, Inc. +under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the +DARPA CHATS research program. +.Sh AUTHORS +.An "Poul-Henning Kamp" Aq phk@FreeBSD.org diff --git a/sbin/gbde/gbde.c b/sbin/gbde/gbde.c new file mode 100644 index 000000000000..2df31c4bc69f --- /dev/null +++ b/sbin/gbde/gbde.c @@ -0,0 +1,750 @@ +/*- + * Copyright (c) 2002 Poul-Henning Kamp + * Copyright (c) 2002 Networks Associates Technology, Inc. + * All rights reserved. + * + * This software was developed for the FreeBSD Project by Poul-Henning Kamp + * and NAI Labs, the Security Research Division of Network Associates, Inc. + * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the + * DARPA CHATS research program. + * + * 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. + * 3. The names of the authors may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +extern const char template[]; + +static void __dead2 +usage(const char *reason) +{ + const char *p; + + p = getprogname(); + fprintf(stderr, "Usage error: %s", reason); + fprintf(stderr, "Usage:\n"); + fprintf(stderr, "\t%s attach dest -l filename\n", p); + fprintf(stderr, "\t%s dettach dest\n", p); + fprintf(stderr, "\t%s init dest [-i] [-f filename] -l filename\n", p); + fprintf(stderr, "\t%s setkey dest [-n key] -l filename\n", p); + fprintf(stderr, "\t%s destroy dest [-n key] -l filename\n", p); + exit (1); +} + +void * +g_read_data(struct g_consumer *cp, off_t offset, off_t length, int *error) +{ + void *p; + int fd, i; + off_t o2; + + p = malloc(length); + if (p == NULL) + err(1, "malloc"); + fd = *(int *)cp; + o2 = lseek(fd, offset, SEEK_SET); + if (o2 != offset) + err(1, "lseek"); + i = read(fd, p, length); + if (i != length) + err(1, "read"); + if (error != NULL) + error = 0; + return (p); +} + +static void +random_bits(void *p, u_int len) +{ + static int fdr = -1; + int i; + + if (fdr < 0) { + fdr = open("/dev/urandom", O_RDONLY); + if (fdr < 0) + err(1, "/dev/urandom"); + } + + i = read(fdr, p, len); + if (i != (int)len) + err(1, "read from /dev/urandom"); +} + +/* XXX: not nice */ +static u_char sbox[256]; + +static void +reset_passphrase(struct g_bde_softc *sc) +{ + + memcpy(sc->arc4_sbox, sbox, 256); + sc->arc4_i = sc->arc4_j = 0; +} + +static void +setup_passphrase(struct g_bde_softc *sc, int sure, const char *input) +{ + char buf1[BUFSIZ], buf2[BUFSIZ], *p; + + if (input != NULL) { + g_bde_arc4_seed(sc, input, strlen(input)); + memcpy(sbox, sc->arc4_sbox, 256); + return; + } + for (;;) { + p = readpassphrase( + sure ? "Enter new passphrase:" : "Enter passphrase: ", + buf1, sizeof buf1, + RPP_ECHO_OFF | RPP_REQUIRE_TTY); + if (p == NULL) + err(1, "readpassphrase"); + + if (sure) { + p = readpassphrase("Reenter new passphrase: ", + buf2, sizeof buf2, + RPP_ECHO_OFF | RPP_REQUIRE_TTY); + if (p == NULL) + err(1, "readpassphrase"); + + if (strcmp(buf1, buf2)) { + printf("They didn't match.\n"); + continue; + } + } + if (strlen(buf1) < 3) { + printf("Too short passphrase.\n"); + continue; + } + break; + } + g_bde_arc4_seed(sc, buf1, strlen(buf1)); + memcpy(sbox, sc->arc4_sbox, 256); +} + +static void +encrypt_sector(void *d, int len, void *key) +{ + keyInstance ki; + cipherInstance ci; + int error; + + error = rijndael_cipherInit(&ci, MODE_CBC, NULL); + if (error <= 0) + errx(1, "rijndael_cipherInit=%d", error); + error = rijndael_makeKey(&ki, DIR_ENCRYPT, 128, key); + if (error <= 0) + errx(1, "rijndael_makeKeY=%d", error); + error = rijndael_blockEncrypt(&ci, &ki, d, len * 8, d); + if (error <= 0) + errx(1, "rijndael_blockEncrypt=%d", error); +} + +static void +cmd_attach(const struct g_bde_softc *sc, const char *dest, const char *lfile) +{ + int gfd, i, ffd; + struct geomconfiggeom gcg; + u_char buf[256 + 16]; + + gfd = open("/dev/geom.ctl", O_RDWR); + if (gfd < 0) + err(1, "/dev/geom.ctl"); + memset(&gcg, 0, sizeof gcg); + gcg.class.u.name = "BDE"; + gcg.class.len = strlen(gcg.class.u.name); + gcg.provider.u.name = dest; + gcg.provider.len = strlen(gcg.provider.u.name); + gcg.flag = 0; + gcg.len = sizeof buf; + gcg.ptr = buf; + + if (lfile != NULL) { + ffd = open(lfile, O_RDONLY, 0); + if (ffd < 0) + err(1, lfile); + read(ffd, buf + 256, 16); + close(ffd); + } else { + memset(buf + 256, 0, 16); + } + memcpy(buf, sc->arc4_sbox, 256); + + i = ioctl(gfd, GEOMCONFIGGEOM, &gcg); + if (i != 0) + err(1, "ioctl(GEOMCONFIGGEOM)"); + exit (0); +} + +static void +cmd_dettach(const char *dest) +{ + int i, gfd; + struct geomconfiggeom gcg; + + gfd = open("/dev/geom.ctl", O_RDWR); + if (gfd < 0) + err(1, "/dev/geom.ctl"); + memset(&gcg, 0, sizeof gcg); + gcg.class.u.name = "BDE"; + gcg.class.len = strlen(gcg.class.u.name); + gcg.provider.u.name = dest; + gcg.provider.len = strlen(gcg.provider.u.name); + gcg.flag = 1; + + i = ioctl(gfd, GEOMCONFIGGEOM, &gcg); + if (i != 0) + err(1, "ioctl(GEOMCONFIGGEOM)"); + exit (0); +} + +static void +cmd_open(struct g_bde_softc *sc, int dfd __unused, const char *l_opt, u_int *nkey) +{ + int error; + int ffd; + u_char keyloc[16]; + + if (l_opt != NULL) { + ffd = open(l_opt, O_RDONLY, 0); + if (ffd < 0) + err(1, l_opt); + read(ffd, keyloc, sizeof keyloc); + close(ffd); + } else { + memset(keyloc, 0, sizeof keyloc); + } + + error = g_bde_decrypt_lock(sc, sbox, keyloc, 0xffffffff, + 512, nkey); + if (error == ENOENT) + errx(1, "Lock was destroyed."); + if (error == ESRCH) + errx(1, "Lock was nuked."); + if (error == ENOTDIR) + errx(1, "Lock not found"); + if (error != 0) + errx(1, "Error %d decrypting lock", error); + if (nkey) + printf("Opened with key %u\n", *nkey); + return; +} + +static void +cmd_nuke(struct g_bde_key *gl, int dfd , int key) +{ + int i; + u_char *sbuf; + off_t offset, offset2; + + sbuf = malloc(gl->sectorsize); + memset(sbuf, 0, gl->sectorsize); + offset = (gl->lsector[key] & ~(gl->sectorsize - 1)); + offset2 = lseek(dfd, offset, SEEK_SET); + if (offset2 != offset) + err(1, "lseek"); + i = write(dfd, sbuf, gl->sectorsize); + if (i != (int)gl->sectorsize) + err(1, "write"); + printf("Nuked key %d\n", key); +} + +static void +cmd_write(struct g_bde_key *gl, struct g_bde_softc *sc, int dfd , int key, const char *l_opt) +{ + char buf[BUFSIZ]; + int i, ffd; + uint64_t off[2]; + u_char keyloc[16]; + u_char *sbuf, *q; + MD5_CTX c; + off_t offset, offset2; + + sbuf = malloc(gl->sectorsize); + /* + * Find the byte-offset in the lock sector where we will put the lock + * data structure. We can put it any random place as long as the + * structure fits. + */ + for(;;) { + random_bits(off, sizeof off); + off[0] &= (gl->sectorsize - 1); + if (off[0] + G_BDE_LOCKSIZE > gl->sectorsize) + continue; + break; + } + + /* Add the sector offset in bytes */ + off[0] += (gl->lsector[key] & ~(gl->sectorsize - 1)); + gl->lsector[key] = off[0]; + + i = g_bde_keyloc_encrypt(sc, off, keyloc); + if (i) + errx(1, "g_bde_keyloc_encrypt()"); + if (l_opt != NULL) { + ffd = open(l_opt, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (ffd < 0) + err(1, l_opt); + write(ffd, keyloc, sizeof keyloc); + close(ffd); + } else if (gl->flags & 1) { + offset2 = lseek(dfd, 0, SEEK_SET); + if (offset2 != 0) + err(1, "lseek"); + i = read(dfd, sbuf, gl->sectorsize); + if (i != (int)gl->sectorsize) + err(1, "read"); + memcpy(sbuf + key * 16, keyloc, sizeof keyloc); + offset2 = lseek(dfd, 0, SEEK_SET); + if (offset2 != 0) + err(1, "lseek"); + i = write(dfd, sbuf, gl->sectorsize); + if (i != (int)gl->sectorsize) + err(1, "write"); + } else { + errx(1, "No -L option and no space in sector 0 for lockfile"); + } + + /* Allocate a sectorbuffer and fill it with random junk */ + if (sbuf == NULL) + err(1, "malloc"); + random_bits(sbuf, gl->sectorsize); + + /* Fill in the hash field with something we can recognize again */ + g_bde_arc4_seq(sc, buf, 16); + MD5Init(&c); + MD5Update(&c, "0000", 4); /* XXX: for future versioning */ + MD5Update(&c, buf, 16); + MD5Final(gl->hash, &c); + + /* Fill random bits in the spare field */ + random_bits(gl->spare, sizeof(gl->spare)); + + /* Encode the structure where we want it */ + q = sbuf + (off[0] % gl->sectorsize); + g_bde_encode_lock(gl, q); + + /* + * The encoded structure likely contains long sequences of zeros + * which stick out as a sore thumb, so we XOR with key-material + * to make it harder to recognize in a brute-force attack + */ + g_bde_arc4_seq(sc, buf, G_BDE_LOCKSIZE); + for (i = 0; i < G_BDE_LOCKSIZE; i++) + q[i] ^= buf[i]; + + g_bde_arc4_seq(sc, buf, 16); + + encrypt_sector(q, G_BDE_LOCKSIZE, buf); + offset = gl->lsector[key] & ~(gl->sectorsize - 1); + offset2 = lseek(dfd, offset, SEEK_SET); + if (offset2 != offset) + err(1, "lseek"); + i = write(dfd, sbuf, gl->sectorsize); + if (i != (int)gl->sectorsize) + err(1, "write"); + printf("Wrote key %d at %jd\n", key, (intmax_t)offset); + +} + +static void +cmd_destroy(struct g_bde_key *gl, int nkey) +{ + int i; + + bzero(&gl->sector0, sizeof gl->sector0); + bzero(&gl->sectorN, sizeof gl->sectorN); + bzero(&gl->keyoffset, sizeof gl->keyoffset); + bzero(&gl->flags, sizeof gl->flags); + bzero(gl->key, sizeof gl->key); + for (i = 0; i < G_BDE_MAXKEYS; i++) + if (i != nkey) + gl->lsector[i] = ~0; +} + +static void +cmd_init(struct g_bde_key *gl, int dfd, const char *f_opt, int i_opt, const char *l_opt) +{ + int i; + u_char *buf; + unsigned sector_size; + uint64_t first_sector; + uint64_t last_sector; + uint64_t total_sectors; + off_t off, off2; + unsigned nkeys; + const char *p; + char *q, cbuf[BUFSIZ]; + unsigned u, u2; + uint64_t o; + properties params; + + bzero(gl, sizeof *gl); + if (f_opt != NULL) { + i = open(f_opt, O_RDONLY); + if (i < 0) + err(1, f_opt); + params = properties_read(i); + close (i); + } else { + /* XXX: Polish */ + q = strdup("/tmp/temp.XXXXXXXXXX"); + i = mkstemp(q); + if (i < 0) + err(1, q); + write(i, template, strlen(template)); + close (i); + if (i_opt) { + p = getenv("EDITOR"); + if (p == NULL) + p = "vi"; + sprintf(cbuf, "%s %s\n", p, q); + system(cbuf); + } + i = open(q, O_RDONLY); + if (i < 0) + err(1, f_opt); + params = properties_read(i); + close (i); + unlink(q); + } + + /* */ + p = property_find(params, "sector_size"); + i = ioctl(dfd, DIOCGSECTORSIZE, &u); + if (i == 0) + sector_size = u; + else if (p == NULL) + errx(1, "Missing sector_size property"); + if (p != NULL) { + sector_size = strtoul(p, &q, 0); + if (!*p || *q) + errx(1, "sector_size not a proper number"); + } + if (sector_size & (sector_size - 1)) + errx(1, "sector_size not a power of 2"); + if (sector_size < 512) + errx(1, "sector_size is smaller than 512"); + buf = malloc(sector_size); + if (buf == NULL) + err(1, "Failed to malloc sector buffer"); + gl->sectorsize = sector_size; + + i = ioctl(dfd, DIOCGMEDIASIZE, &off); + if (i == 0) { + first_sector = 0; + total_sectors = off / sector_size; + last_sector = total_sectors - 1; + } else { + first_sector = 0; + last_sector = 0; + total_sectors = 0; + } + + /* */ + p = property_find(params, "first_sector"); + if (p != NULL) { + first_sector = strtoul(p, &q, 0); + if (!*p || *q) + errx(1, "first_sector not a proper number"); + } + gl->sector0 = first_sector * gl->sectorsize; + + /* */ + p = property_find(params, "last_sector"); + if (p != NULL) { + last_sector = strtoul(p, &q, 0); + if (!*p || *q) + errx(1, "last_sector not a proper number"); + if (last_sector <= first_sector) + errx(1, "last_sector not larger than first_sector"); + total_sectors = last_sector + 1; + } + + /* */ + p = property_find(params, "total_sectors"); + if (p != NULL) { + total_sectors = strtoul(p, &q, 0); + if (!*p || *q) + errx(1, "total_sectors not a proper number"); + if (last_sector == 0) + last_sector = first_sector + total_sectors - 1; + } + + if (l_opt == NULL && first_sector != 0) + errx(1, "No -L new-lockfile argument and first_sector != 0"); + else if (l_opt == NULL) { + first_sector++; + total_sectors--; + gl->flags |= 1; + } + + if (total_sectors != (last_sector - first_sector) + 1) + errx(1, "total_sectors disagree with first_sector and last_sector"); + if (total_sectors == 0) + errx(1, "missing last_sector or total_sectors"); + + gl->sectorN = (last_sector + 1) * gl->sectorsize; + + /* Find a random keyoffset */ + random_bits(&o, sizeof o); + o %= (gl->sectorN - gl->sector0); + o &= ~(gl->sectorsize - 1); + gl->keyoffset = o; + + /* */ + p = property_find(params, "number_of_keys"); + if (p == NULL) + errx(1, "Missing number_of_keys property"); + nkeys = strtoul(p, &q, 0); + if (!*p || *q) + errx(1, "number_of_keys not a proper number"); + if (nkeys < 1 || nkeys > G_BDE_MAXKEYS) + errx(1, "number_of_keys out of range"); + for (u = 0; u < nkeys; u++) { + for(;;) { + do { + random_bits(&o, sizeof o); + o %= gl->sectorN; + o &= ~(gl->sectorsize - 1); + } while(o < gl->sector0); + for (u2 = 0; u2 < u; u2++) + if (o == gl->lsector[u2]) + break; + if (u2 < u) + continue; + break; + } + gl->lsector[u] = o; + } + for (; u < G_BDE_MAXKEYS; u++) { + do + random_bits(&o, sizeof o); + while (o < gl->sectorN); + gl->lsector[u] = o; + } + + /* Flush sector zero if we use it for lockfile data */ + if (gl->flags & 1) { + off2 = lseek(dfd, 0, SEEK_SET); + if (off2 != 0) + err(1, "lseek(2) to sector 0"); + random_bits(buf, sector_size); + i = write(dfd, buf, sector_size); + if (i != (int)sector_size) + err(1, "write sector 0"); + } + + /* */ + p = property_find(params, "random_flush"); + if (p != NULL) { + off = first_sector * sector_size; + off2 = lseek(dfd, off, SEEK_SET); + if (off2 != off) + err(1, "lseek(2) to first_sector"); + off2 = last_sector * sector_size; + while (off <= off2) { + random_bits(buf, sector_size); + i = write(dfd, buf, sector_size); + if (i != (int)sector_size) + err(1, "write to $device_name"); + off += sector_size; + } + } + + random_bits(gl->key, sizeof gl->key); + + return; +} + +static enum action { + ACT_HUH, + ACT_ATTACH, ACT_DETTACH, + ACT_INIT, ACT_SETKEY, ACT_DESTROY, ACT_NUKE +} action; + +int +main(int argc, char **argv) +{ + const char *opts; + const char *l_opt, *L_opt; + const char *p_opt, *P_opt; + const char *f_opt; + const char *dest; + int i_opt, n_opt, ch, dfd, nkey, doopen; + int i; + char *q; + struct g_bde_key *gl; + struct g_bde_softc sc; + + if (argc < 3) + usage("Too few arguments\n"); + + doopen = 0; + if (!strcmp(argv[1], "attach")) { + action = ACT_ATTACH; + opts = "l:p:"; + } else if (!strcmp(argv[1], "dettach")) { + action = ACT_DETTACH; + opts = ""; + } else if (!strcmp(argv[1], "init")) { + action = ACT_INIT; + doopen = 1; + opts = "f:iL:P:"; + } else if (!strcmp(argv[1], "setkey")) { + action = ACT_SETKEY; + doopen = 1; + opts = "n:l:L:p:P:"; + } else if (!strcmp(argv[1], "destroy")) { + action = ACT_DESTROY; + doopen = 1; + opts = "l:p:"; + } else if (!strcmp(argv[1], "nuke")) { + action = ACT_NUKE; + doopen = 1; + opts = "l:p:n:"; + } else { + usage("Unknown sub command\n"); + } + argc--; + argv++; + + dest = argv[1]; + argc--; + argv++; + + p_opt = NULL; + P_opt = NULL; + l_opt = NULL; + L_opt = NULL; + f_opt = NULL; + n_opt = 0; + i_opt = 0; + + while((ch = getopt(argc, argv, opts)) != -1) + switch (ch) { + case 'f': + f_opt = optarg; + break; + case 'i': + i_opt = !i_opt; + case 'l': + l_opt = optarg; + break; + case 'L': + L_opt = optarg; + break; + case 'p': + p_opt = optarg; + break; + case 'P': + P_opt = optarg; + break; + case 'n': + n_opt = strtoul(optarg, &q, 0); + if (!*optarg || *q) + usage("-n argument not numeric\n"); + if (n_opt < -1 || n_opt > G_BDE_MAXKEYS) + usage("-n argument out of range\n"); + break; + default: + usage("Invalid option\n"); + } + + if (doopen) { + dfd = open(dest, O_RDWR | O_CREAT, 0644); + if (dfd < 0) + err(1, dest); + } + + memset(&sc, 0, sizeof sc); + sc.consumer = (struct g_consumer *)&dfd; + gl = &sc.key; + switch(action) { + case ACT_ATTACH: + setup_passphrase(&sc, 0, p_opt); + cmd_attach(&sc, dest, l_opt); + break; + case ACT_DETTACH: + cmd_dettach(dest); + break; + case ACT_INIT: + cmd_init(gl, dfd, f_opt, i_opt, L_opt); + setup_passphrase(&sc, 1, P_opt); + cmd_write(gl, &sc, dfd, 0, L_opt); + break; + case ACT_SETKEY: + setup_passphrase(&sc, 0, p_opt); + cmd_open(&sc, dfd, l_opt, &nkey); + if (n_opt == 0) + n_opt = nkey + 1; + setup_passphrase(&sc, 1, P_opt); + cmd_write(gl, &sc, dfd, n_opt - 1, L_opt); + break; + case ACT_DESTROY: + setup_passphrase(&sc, 0, p_opt); + cmd_open(&sc, dfd, l_opt, &nkey); + cmd_destroy(gl, nkey); + reset_passphrase(&sc); + cmd_write(gl, &sc, dfd, nkey, l_opt); + break; + case ACT_NUKE: + setup_passphrase(&sc, 0, p_opt); + cmd_open(&sc, dfd, l_opt, &nkey); + if (n_opt == 0) + n_opt = nkey + 1; + if (n_opt == -1) { + for(i = 0; i < G_BDE_MAXKEYS; i++) + cmd_nuke(gl, dfd, i); + } else { + cmd_nuke(gl, dfd, n_opt - 1); + } + break; + default: + usage("Internal error\n"); + } + + return(0); +} diff --git a/sbin/gbde/template.txt b/sbin/gbde/template.txt new file mode 100644 index 000000000000..3d22007befd9 --- /dev/null +++ b/sbin/gbde/template.txt @@ -0,0 +1,32 @@ +# $FreeBSD$ +# +# Sector size is the smallest unit of data which can be read or written. +# Making it too small decreases performance and decreases available space. +# Making it too large may prevent filesystems from working. 512 is the +# minimum and always safe. For UFS, use the fragment size +# +sector_size = 512 + +# +# Start and end of the encrypted section of the partition. Specify in +# sector numbers. If none specified, "all" will be assumed, to the +# extent the value of this can be established. +# +#first_sector = 0 +#last_sector = 2879 +#total_sectors = 2880 + +# +# An encrypted partition can have more than one key. It may be a good idea +# to make at least two keys, and save one of them for "just in case" use. +# The minimum is obviously one and the maximum is 4. +# +number_of_keys = 4 + +# +# Flushing the partition with random bytes prevents a brute-force attack +# from skipping sectors which obviously contains un-encrypted data. +# NB: This variable is boolean, if it is present it means "yes" even if +# you set it to the value "no" +# +#random_flush = diff --git a/sbin/gbde/test.sh b/sbin/gbde/test.sh new file mode 100644 index 000000000000..0fe384d47785 --- /dev/null +++ b/sbin/gbde/test.sh @@ -0,0 +1,46 @@ +#!/bin/sh +# $FreeBSD$ + +set -e + +MD=99 +mdconfig -d -u $MD || true + +mdconfig -a -t malloc -s 1m -u $MD + +D=/dev/md$MD + +./gbde init $D -P foo -L /tmp/_l1 +./gbde setkey $D -p foo -l /tmp/_l1 -P bar -L /tmp/_l1 +./gbde setkey $D -p bar -l /tmp/_l1 -P foo -L /tmp/_l1 + +./gbde setkey $D -p foo -l /tmp/_l1 -n 2 -P foo2 -L /tmp/_l2 +./gbde setkey $D -p foo2 -l /tmp/_l2 -n 3 -P foo3 -L /tmp/_l3 +./gbde setkey $D -p foo3 -l /tmp/_l3 -n 4 -P foo4 -L /tmp/_l4 +./gbde setkey $D -p foo4 -l /tmp/_l4 -n 1 -P foo1 -L /tmp/_l1 + +./gbde nuke $D -p foo1 -l /tmp/_l1 -n 4 +if ./gbde nuke $D -p foo4 -l /tmp/_l4 -n 3 ; then false ; fi +./gbde destroy $D -p foo2 -l /tmp/_l2 +if ./gbde destroy $D -p foo2 -l /tmp/_l2 ; then false ; fi + +./gbde nuke $D -p foo1 -l /tmp/_l1 -n -1 +if ./gbde nuke $D -p foo1 -l /tmp/_l1 -n -1 ; then false ; fi +if ./gbde nuke $D -p foo2 -l /tmp/_l2 -n -1 ; then false ; fi +if ./gbde nuke $D -p foo3 -l /tmp/_l3 -n -1 ; then false ; fi +if ./gbde nuke $D -p foo4 -l /tmp/_l4 -n -1 ; then false ; fi + +./gbde init $D -P foo +./gbde setkey $D -p foo -P bar +./gbde setkey $D -p bar -P foo + +./gbde setkey $D -p foo -n 2 -P foo2 +./gbde setkey $D -p foo2 -n 3 -P foo3 +./gbde setkey $D -p foo3 -n 4 -P foo4 +./gbde setkey $D -p foo4 -n 1 -P foo1 + +mdconfig -d -u $MD || true +echo "***********" +echo "Test passed" +echo "***********" +exit 0