Remove GBDE source files

This commit is contained in:
Poul-Henning Kamp 2024-05-07 07:31:09 +00:00
parent 984764d796
commit 8d2d1d6516
12 changed files with 0 additions and 6741 deletions

View File

@ -1,31 +0,0 @@
PACKAGE=geom
PROG= gbde
SRCS= gbde.c template.c
SRCS+= rijndael-alg-fst.c
SRCS+= rijndael-api-fst.c
SRCS+= g_bde_lock.c
# rijndael-fst.c does evil casting things which can results in warnings,
# the test-vectors check out however, so it works right.
NO_WCAST_ALIGN=
NO_WMISSING_VARIABLE_DECLARATIONS=
CFLAGS+= -I${SRCTOP}/sys
.PATH: ${SRCTOP}/sys/geom/bde \
${SRCTOP}/sys/crypto/rijndael \
${SRCTOP}/sys/crypto/sha2
CLEANFILES+= template.c
MAN= gbde.8
LIBADD= md util geom
template.c: template.txt
file2c 'const char template[] = {' ',0};' \
< ${.CURDIR}/template.txt > template.c
test: ${PROG}
sh ${.CURDIR}/test.sh ${.CURDIR}
.include <bsd.prog.mk>

View File

@ -1,18 +0,0 @@
# Autogenerated - do NOT edit!
DIRDEPS = \
include \
include/xlocale \
lib/${CSU_DIR} \
lib/libc \
lib/libcompiler_rt \
lib/libgeom \
lib/libmd \
lib/libutil \
.include <dirdeps.mk>
.if ${DEP_RELDIR} == ${_DEP_RELDIR}
# local dependencies - needed for -jN in clean tree
.endif

View File

@ -1,271 +0,0 @@
.\"
.\" 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.
.\"
.\" 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.
.\"
.Dd October 3, 2016
.Dt GBDE 8
.Os
.Sh NAME
.Nm gbde
.Nd operation and management utility for Geom Based Disk Encryption
.Sh SYNOPSIS
.Nm
.Cm attach
.Ar destination
.Op Fl k Ar keyfile
.Op Fl l Ar lockfile
.Op Fl p Ar pass-phrase
.Nm
.Cm detach
.Ar destination
.Nm
.Cm init
.Ar destination
.Op Fl i
.Op Fl f Ar filename
.Op Fl K Ar new-keyfile
.Op Fl L Ar new-lockfile
.Op Fl P Ar new-pass-phrase
.Nm
.Cm setkey
.Ar destination
.Op Fl n Ar key
.Op Fl k Ar keyfile
.Op Fl l Ar lockfile
.Op Fl p Ar pass-phrase
.Op Fl K Ar new-keyfile
.Op Fl L Ar new-lockfile
.Op Fl P Ar new-pass-phrase
.Nm
.Cm nuke
.Ar destination
.Op Fl n Ar key
.Op Fl k Ar keyfile
.Op Fl l Ar lockfile
.Op Fl p Ar pass-phrase
.Nm
.Cm destroy
.Ar destination
.Op Fl k Ar keyfile
.Op Fl l Ar lockfile
.Op Fl p Ar pass-phrase
.Sh DESCRIPTION
.Bf -symbolic
NOTICE:
Please be aware that this code has not yet received much review
and analysis by qualified cryptographers and therefore should be considered
a slightly suspect experimental facility.
.Pp
We cannot at this point guarantee that the on-disk format will not change
in response to reviews or bug-fixes, so potential users are advised to
be prepared that
.Xr dump 8 Ns / Ns
.Xr restore 8
based migrations may be called for in the future.
.Ef
.Pp
The
.Nm
utility is the only official operation and management interface for the
.Xr gbde 4
.Tn GEOM
based disk encryption kernel facility.
The interaction between the
.Nm
utility and the kernel part is not a published interface.
.Pp
The operational aspect consists of two subcommands:
one to open and attach
a device to the in-kernel cryptographic
.Nm
module
.Pq Cm attach ,
and one to close and detach a device
.Pq Cm detach .
.Pp
The management part allows initialization of the master key and lock sectors
on a device
.Pq Cm init ,
initialization and replacement of pass-phrases
.Pq Cm setkey ,
and key invalidation
.Pq Cm nuke
and blackening
.Pq Cm destroy
functions.
.Pp
The
.Fl l Ar lockfile
argument is used to supply the lock selector data.
If no
.Fl l
option is specified, the first sector is used for this purpose.
.Pp
The
.Fl L Ar new-lockfile
argument
specifies the lock selector file for the key
initialized with the
.Cm init
subcommand
or modified with the
.Cm setkey
subcommand.
.Pp
The
.Fl n Ar key
argument can be used to specify to which of the four keys
the operation applies.
A value of 1 to 4 selects the specified key, a value of 0 (the default)
means
.Dq "this key"
(i.e., the key used to gain access to the device)
and a value of \-1 means
.Dq "all keys" .
.Pp
The
.Fl f Ar filename
specifies an optional parameter file for use under initialization.
.Pp
Alternatively, the
.Fl i
option toggles an interactive mode where a template file with descriptions
of the parameters can be interactively edited.
.Pp
The
.Fl p Ar pass-phrase
argument
specifies the pass-phrase used for opening the device.
If not specified, the controlling terminal will be used to prompt the user
for the pass-phrase.
Be aware that using this option may expose the pass-phrase to other
users who happen to run
.Xr ps 1
or similar while the command is running.
.Pp
The
.Fl P Ar new-pass-phrase
argument
can be used to specify the new pass-phrase to the
.Cm init
and
.Cm setkey
subcommands.
If not specified, the user is prompted for the new pass-phrase on the
controlling terminal.
Be aware that using this option may expose the pass-phrase to other
users who happen to run
.Xr ps 1
or similar while the command is running.
.Pp
The
.Fl k Ar keyfile
argument specifies a key file to be used in combination with the
pass-phrase (whether the pass-phrase is specified on the command line
or entered from the terminal) for opening the device.
The device will only be opened if the contents of the key file and the
pass-phrase are both correct.
.Pp
The
.Fl K Ar new-keyfile
argument can be used to specify a new key file to the
.Cm init
and
.Cm setkey
subcommands.
If not specified, no key file will be used (even if one was previously
used).
.Sh EXAMPLES
To initialize a device, using default parameters:
.Pp
.Dl "gbde init /dev/ada0s1f -L /etc/ada0s1f.lock"
.Pp
To attach an encrypted device:
.Pp
.Dl "gbde attach ada0s1f -l /etc/ada0s1f.lock"
.Pp
The encrypted device has the suffix
.Pa .bde
so a typical
command to create and mount a file system would be:
.Pp
.Dl "newfs /dev/ada0s1f.bde"
.Dl "mount /dev/ada0s1f.bde /secret"
.Pp
To detach an encrypted device:
.Pp
.Dl "gbde detach ada0s1f"
.Pp
Please notice that detaching an encrypted device corresponds to
physically removing it, do not forget to unmount the file system first.
.Pp
To initialize the second key using a detached lockfile and a trivial
pass-phrase:
.Pp
.Dl "gbde setkey ada0s1f -n 2 -P foo -L key2.lockfile"
.Pp
To invalidate your own masterkey:
.Pp
.Dl "gbde nuke ada0s1f"
.Pp
This will overwrite your masterkey sector with zeros, and results in
a diagnostic if you try to use the key again.
You can also destroy the other three copies of the masterkey with the
-n argument.
.Pp
You can also invalidate your masterkey without leaving a tell-tale sector
full of zeros:
.Pp
.Dl "gbde destroy ada0s1f"
.Pp
This will overwrite the information fields in your masterkey sector,
encrypt it and write it back.
You get a (different) diagnostic if you try to use it.
.Sh SEE ALSO
.Xr gbde 4 ,
.Xr geom 4
.Sh HISTORY
This software was developed for the
.Fx
Project by
.An Poul-Henning Kamp
and NAI Labs, the Security Research Division of Network Associates, Inc.\&
under DARPA/SPAWAR contract N66001-01-C-8035
.Pq Dq CBOSS ,
as part of the
DARPA CHATS research program.
.Nm
first appeared in
.Fx 5.0 .
.Sh AUTHORS
.An Poul-Henning Kamp Aq Mt phk@FreeBSD.org
.Sh BUGS
The cryptographic algorithms and the overall design have not been
attacked mercilessly for over 10 years by a gang of cryptoanalysts.

View File

@ -1,895 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* 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.
*
* 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.
*
* XXX: Future stuff
*
* Replace the template file options (-i & -f) with command-line variables
* "-v property=foo"
*
* Introduce -e, extra entropy source (XOR with /dev/random)
*
* Introduce -E, alternate entropy source (instead of /dev/random)
*
* Introduce -i take IV from keyboard or
*
* Introduce -I take IV from file/cmd
*
* Introduce -m/-M store encrypted+encoded masterkey in file
*
* Introduce -k/-K get pass-phrase part from file/cmd
*
* Introduce -d add more dest-devices to worklist.
*
* Add key-option: selfdestruct bit.
*
* New/changed verbs:
* "onetime" attach with onetime nonstored locksector
* "key"/"unkey" to blast memory copy of key without orphaning
* "nuke" blow away everything attached, crash/halt/power-off if possible.
* "blast" destroy all copies of the masterkey
* "destroy" destroy one copy of the masterkey
* "backup"/"restore" of masterkey sectors.
*
* Make all verbs work on both attached/detached devices.
*
*/
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/mutex.h>
#include <md5.h>
#include <readpassphrase.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <paths.h>
#include <strings.h>
#include <stdlib.h>
#include <err.h>
#include <stdio.h>
#include <libutil.h>
#include <libgeom.h>
#include <sys/errno.h>
#include <sys/disk.h>
#include <sys/stat.h>
#include <crypto/rijndael/rijndael-api-fst.h>
#include <crypto/sha2/sha512.h>
#include <sys/param.h>
#include <sys/linker.h>
#define GBDEMOD "geom_bde"
#define KASSERT(foo, bar) do { if(!(foo)) { warn bar ; exit (1); } } while (0)
#include <geom/geom.h>
#include <geom/bde/g_bde.h>
extern const char template[];
#if 0
static void
g_hexdump(void *ptr, int length)
{
int i, j, k;
unsigned char *cp;
cp = ptr;
for (i = 0; i < length; i+= 16) {
printf("%04x ", i);
for (j = 0; j < 16; j++) {
k = i + j;
if (k < length)
printf(" %02x", cp[k]);
else
printf(" ");
}
printf(" |");
for (j = 0; j < 16; j++) {
k = i + j;
if (k >= length)
printf(" ");
else if (cp[k] >= ' ' && cp[k] <= '~')
printf("%c", cp[k]);
else
printf(".");
}
printf("|\n");
}
}
#endif
static void __dead2
usage(void)
{
(void)fprintf(stderr,
"usage: gbde attach destination [-k keyfile] [-l lockfile] [-p pass-phrase]\n"
" gbde detach destination\n"
" gbde init destination [-i] [-f filename] [-K new-keyfile]\n"
" [-L new-lockfile] [-P new-pass-phrase]\n"
" gbde setkey destination [-n key]\n"
" [-k keyfile] [-l lockfile] [-p pass-phrase]\n"
" [-K new-keyfile] [-L new-lockfile] [-P new-pass-phrase]\n"
" gbde nuke destination [-n key]\n"
" [-k keyfile] [-l lockfile] [-p pass-phrase]\n"
" gbde destroy destination [-k keyfile] [-l lockfile] [-p pass-phrase]\n");
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)
{
arc4random_buf(p, len);
}
/* XXX: not nice */
static u_char sha2[SHA512_DIGEST_LENGTH];
static void
reset_passphrase(struct g_bde_softc *sc)
{
memcpy(sc->sha2, sha2, SHA512_DIGEST_LENGTH);
}
static void
setup_passphrase(struct g_bde_softc *sc, int sure, const char *input,
const char *keyfile)
{
char buf1[BUFSIZ + SHA512_DIGEST_LENGTH];
char buf2[BUFSIZ + SHA512_DIGEST_LENGTH];
char *p;
int kfd, klen, bpos = 0;
if (keyfile != NULL) {
/* Read up to BUFSIZ bytes from keyfile */
kfd = open(keyfile, O_RDONLY, 0);
if (kfd < 0)
err(1, "%s", keyfile);
klen = read(kfd, buf1, BUFSIZ);
if (klen == -1)
err(1, "%s", keyfile);
close(kfd);
/* Prepend the passphrase with the hash of the key read */
g_bde_hash_pass(sc, buf1, klen);
memcpy(buf1, sc->sha2, SHA512_DIGEST_LENGTH);
memcpy(buf2, sc->sha2, SHA512_DIGEST_LENGTH);
bpos = SHA512_DIGEST_LENGTH;
}
if (input != NULL) {
if (strlen(input) >= BUFSIZ)
errx(1, "Passphrase too long");
strcpy(buf1 + bpos, input);
g_bde_hash_pass(sc, buf1, strlen(buf1 + bpos) + bpos);
memcpy(sha2, sc->sha2, SHA512_DIGEST_LENGTH);
return;
}
for (;;) {
p = readpassphrase(
sure ? "Enter new passphrase:" : "Enter passphrase: ",
buf1 + bpos, sizeof buf1 - bpos,
RPP_ECHO_OFF | RPP_REQUIRE_TTY);
if (p == NULL)
err(1, "readpassphrase");
if (sure) {
p = readpassphrase("Reenter new passphrase: ",
buf2 + bpos, sizeof buf2 - bpos,
RPP_ECHO_OFF | RPP_REQUIRE_TTY);
if (p == NULL)
err(1, "readpassphrase");
if (strcmp(buf1 + bpos, buf2 + bpos)) {
printf("They didn't match.\n");
continue;
}
}
if (strlen(buf1 + bpos) < 3) {
printf("Too short passphrase.\n");
continue;
}
break;
}
g_bde_hash_pass(sc, buf1, strlen(buf1 + bpos) + bpos);
memcpy(sha2, sc->sha2, SHA512_DIGEST_LENGTH);
}
static void
encrypt_sector(void *d, int len, int klen, 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, klen, 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 ffd;
u_char buf[16];
struct gctl_req *r;
const char *errstr;
r = gctl_get_handle();
gctl_ro_param(r, "verb", -1, "create geom");
gctl_ro_param(r, "class", -1, "BDE");
gctl_ro_param(r, "provider", -1, dest);
gctl_ro_param(r, "pass", SHA512_DIGEST_LENGTH, sc->sha2);
if (lfile != NULL) {
ffd = open(lfile, O_RDONLY, 0);
if (ffd < 0)
err(1, "%s", lfile);
read(ffd, buf, 16);
gctl_ro_param(r, "key", 16, buf);
close(ffd);
}
errstr = gctl_issue(r);
if (errstr != NULL)
errx(1, "Attach to %s failed: %s", dest, errstr);
exit (0);
}
static void
cmd_detach(const char *dest)
{
struct gctl_req *r;
const char *errstr;
char buf[BUFSIZ];
r = gctl_get_handle();
gctl_ro_param(r, "verb", -1, "destroy geom");
gctl_ro_param(r, "class", -1, "BDE");
sprintf(buf, "%s.bde", dest);
gctl_ro_param(r, "geom", -1, buf);
/* gctl_dump(r, stdout); */
errstr = gctl_issue(r);
if (errstr != NULL)
errx(1, "Detach of %s failed: %s", dest, errstr);
exit (0);
}
static void
cmd_open(struct g_bde_softc *sc, int dfd , const char *l_opt, u_int *nkey)
{
int error;
int ffd;
u_char keyloc[16];
u_int sectorsize;
off_t mediasize;
struct stat st;
error = ioctl(dfd, DIOCGSECTORSIZE, &sectorsize);
if (error)
sectorsize = 512;
error = ioctl(dfd, DIOCGMEDIASIZE, &mediasize);
if (error) {
error = fstat(dfd, &st);
if (error == 0 && S_ISREG(st.st_mode))
mediasize = st.st_size;
else
error = ENOENT;
}
if (error)
mediasize = (off_t)-1;
if (l_opt != NULL) {
ffd = open(l_opt, O_RDONLY, 0);
if (ffd < 0)
err(1, "%s", l_opt);
read(ffd, keyloc, sizeof keyloc);
close(ffd);
} else {
memset(keyloc, 0, sizeof keyloc);
}
error = g_bde_decrypt_lock(sc, sc->sha2, keyloc, mediasize,
sectorsize, 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", 1 + *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);
free(sbuf);
if (i != (int)gl->sectorsize)
err(1, "write");
printf("Nuked key %d\n", 1 + key);
}
static void
cmd_write(struct g_bde_key *gl, struct g_bde_softc *sc, int dfd , int key, const char *l_opt)
{
int i, ffd;
uint64_t off[2];
u_char keyloc[16];
u_char *sbuf, *q;
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->sha2, off[0], off[1], 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, "%s", l_opt);
write(ffd, keyloc, sizeof keyloc);
close(ffd);
} else if (gl->flags & GBDE_F_SECT0) {
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 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);
i = g_bde_encode_lock(sc->sha2, gl, q);
if (i < 0)
errx(1, "programming error encoding lock");
encrypt_sector(q, G_BDE_LOCKSIZE, 256, sc->sha2 + 16);
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");
free(sbuf);
#if 0
printf("Wrote key %d at %jd\n", key, (intmax_t)offset);
printf("s0 = %jd\n", (intmax_t)gl->sector0);
printf("sN = %jd\n", (intmax_t)gl->sectorN);
printf("l[0] = %jd\n", (intmax_t)gl->lsector[0]);
printf("l[1] = %jd\n", (intmax_t)gl->lsector[1]);
printf("l[2] = %jd\n", (intmax_t)gl->lsector[2]);
printf("l[3] = %jd\n", (intmax_t)gl->lsector[3]);
printf("k = %jd\n", (intmax_t)gl->keyoffset);
printf("ss = %jd\n", (intmax_t)gl->sectorsize);
#endif
}
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);
gl->flags &= GBDE_F_SECT0;
bzero(gl->mkey, sizeof gl->mkey);
for (i = 0; i < G_BDE_MAXKEYS; i++)
if (i != nkey)
gl->lsector[i] = ~0;
}
static int
sorthelp(const void *a, const void *b)
{
const uint64_t *oa, *ob;
oa = a;
ob = b;
if (*oa > *ob)
return 1;
if (*oa < *ob)
return -1;
return 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, "%s", f_opt);
params = properties_read(i);
close (i);
} else if (i_opt) {
/* XXX: Polish */
asprintf(&q, "%stemp.XXXXXXXXXX", _PATH_TMP);
if (q == NULL)
err(1, "asprintf");
i = mkstemp(q);
if (i < 0)
err(1, "%s", q);
write(i, template, strlen(template));
close (i);
p = getenv("EDITOR");
if (p == NULL)
p = "vi";
if (snprintf(cbuf, sizeof(cbuf), "%s %s\n", p, q) >=
(ssize_t)sizeof(cbuf)) {
unlink(q);
errx(1, "EDITOR is too long");
}
system(cbuf);
i = open(q, O_RDONLY);
if (i < 0)
err(1, "%s", f_opt);
params = properties_read(i);
close (i);
unlink(q);
free(q);
} else {
/* XXX: Hack */
i = open(_PATH_DEVNULL, O_RDONLY);
if (i < 0)
err(1, "%s", _PATH_DEVNULL);
params = properties_read(i);
close (i);
}
/* <sector_size> */
p = property_find(params, "sector_size");
i = ioctl(dfd, DIOCGSECTORSIZE, &u);
if (p != NULL) {
sector_size = strtoul(p, &q, 0);
if (!*p || *q)
errx(1, "sector_size not a proper number");
} else if (i == 0) {
sector_size = u;
} else {
errx(1, "Missing sector_size property");
}
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;
}
/* <first_sector> */
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");
}
/* <last_sector> */
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;
}
/* <total_sectors> */
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 |= GBDE_F_SECT0;
}
gl->sector0 = first_sector * gl->sectorsize;
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;
/* <number_of_keys> */
p = property_find(params, "number_of_keys");
if (p != NULL) {
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");
} else {
nkeys = 4;
}
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;
}
qsort(gl->lsector, G_BDE_MAXKEYS, sizeof gl->lsector[0], sorthelp);
/* Flush sector zero if we use it for lockfile data */
if (gl->flags & GBDE_F_SECT0) {
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");
}
/* <random_flush> */
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->mkey, sizeof gl->mkey);
random_bits(gl->salt, sizeof gl->salt);
return;
}
static enum action {
ACT_HUH,
ACT_ATTACH, ACT_DETACH,
ACT_INIT, ACT_SETKEY, ACT_DESTROY, ACT_NUKE
} action;
int
main(int argc, char **argv)
{
const char *opts;
const char *k_opt, *K_opt;
const char *l_opt, *L_opt;
const char *p_opt, *P_opt;
const char *f_opt;
char *dest;
int i_opt, n_opt, ch, dfd, doopen;
u_int nkey;
int i;
char *q, buf[BUFSIZ];
struct g_bde_key *gl;
struct g_bde_softc sc;
fprintf(stderr, "GBDE disk-encryption is deprecated,\n");
fprintf(stderr, "and will be removed in FreeBSD 15.0\n");
fprintf(stderr, "(continuing in 5 seconds)\n");
sleep(5);
if (argc < 3)
usage();
if (modfind("g_bde") < 0) {
/* need to load the gbde module */
if (kldload(GBDEMOD) < 0 || modfind("g_bde") < 0)
err(1, GBDEMOD ": Kernel module not available");
}
doopen = 0;
if (!strcmp(argv[1], "attach")) {
action = ACT_ATTACH;
opts = "k:l:p:";
} else if (!strcmp(argv[1], "detach")) {
action = ACT_DETACH;
opts = "";
} else if (!strcmp(argv[1], "init")) {
action = ACT_INIT;
doopen = 1;
opts = "f:iK:L:P:";
} else if (!strcmp(argv[1], "setkey")) {
action = ACT_SETKEY;
doopen = 1;
opts = "k:K:l:L:n:p:P:";
} else if (!strcmp(argv[1], "destroy")) {
action = ACT_DESTROY;
doopen = 1;
opts = "k:l:p:";
} else if (!strcmp(argv[1], "nuke")) {
action = ACT_NUKE;
doopen = 1;
opts = "k:l:n:p:";
} else {
usage();
}
argc--;
argv++;
dest = strdup(argv[1]);
argc--;
argv++;
p_opt = NULL;
P_opt = NULL;
k_opt = NULL;
K_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;
break;
case 'k':
k_opt = optarg;
break;
case 'K':
K_opt = optarg;
break;
case 'l':
l_opt = optarg;
break;
case 'L':
L_opt = optarg;
break;
case 'n':
n_opt = strtoul(optarg, &q, 0);
if (!*optarg || *q)
errx(1, "-n argument not numeric");
if (n_opt < -1 || n_opt > G_BDE_MAXKEYS)
errx(1, "-n argument out of range");
break;
case 'p':
p_opt = optarg;
break;
case 'P':
P_opt = optarg;
break;
default:
usage();
}
if (doopen) {
dfd = open(dest, O_RDWR);
if (dfd < 0 && dest[0] != '/') {
if (snprintf(buf, sizeof(buf), "%s%s",
_PATH_DEV, dest) >= (ssize_t)sizeof(buf))
errno = ENAMETOOLONG;
else
dfd = open(buf, O_RDWR);
}
if (dfd < 0)
err(1, "%s", dest);
} else {
if (!memcmp(dest, _PATH_DEV, strlen(_PATH_DEV)))
strcpy(dest, dest + strlen(_PATH_DEV));
}
memset(&sc, 0, sizeof sc);
sc.consumer = (void *)&dfd;
gl = &sc.key;
switch(action) {
case ACT_ATTACH:
setup_passphrase(&sc, 0, p_opt, k_opt);
cmd_attach(&sc, dest, l_opt);
break;
case ACT_DETACH:
cmd_detach(dest);
break;
case ACT_INIT:
cmd_init(gl, dfd, f_opt, i_opt, L_opt);
setup_passphrase(&sc, 1, P_opt, K_opt);
cmd_write(gl, &sc, dfd, 0, L_opt);
break;
case ACT_SETKEY:
setup_passphrase(&sc, 0, p_opt, k_opt);
cmd_open(&sc, dfd, l_opt, &nkey);
if (n_opt == 0)
n_opt = nkey + 1;
setup_passphrase(&sc, 1, P_opt, K_opt);
cmd_write(gl, &sc, dfd, n_opt - 1, L_opt);
break;
case ACT_DESTROY:
setup_passphrase(&sc, 0, p_opt, k_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, k_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:
errx(1, "internal error");
}
return(0);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,31 +0,0 @@
#
# 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 =

View File

@ -1,66 +0,0 @@
#!/bin/sh
set -e
MD=99
mdconfig -d -u $MD > /dev/null 2>&1 || 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
rm -f /tmp/_l1 /tmp/_l2 /tmp/_l3 /tmp/_l4
./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
mdconfig -a -t malloc -s 1m -u $MD
if [ -f image.uu ] ; then
uudecode -p image.uu | bzcat > $D
else
uudecode -p ${1}/image.uu | bzcat > $D
fi
if [ `md5 < $D` != "a4066a739338d451b919e63f9ee4a12c" ] ; then
echo "Failed to set up md(4) device correctly"
exit 2
fi
./gbde attach $D -p foo
fsck_ffs ${D}.bde
./gbde detach $D
mdconfig -d -u $MD
echo "***********"
echo "Test passed"
echo "***********"
exit 0

View File

@ -1,296 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* 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.
*
* 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.
*
*/
#include <sys/param.h>
#include <sys/bio.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/malloc.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/kthread.h>
#include <sys/sysctl.h>
#include <crypto/rijndael/rijndael-api-fst.h>
#include <crypto/sha2/sha512.h>
#include <geom/geom.h>
#include <geom/bde/g_bde.h>
#define BDE_CLASS_NAME "BDE"
FEATURE(geom_bde, "GEOM-based Disk Encryption");
static void
g_bde_start(struct bio *bp)
{
switch (bp->bio_cmd) {
case BIO_DELETE:
case BIO_READ:
case BIO_WRITE:
g_bde_start1(bp);
break;
case BIO_GETATTR:
g_io_deliver(bp, EOPNOTSUPP);
break;
default:
g_io_deliver(bp, EOPNOTSUPP);
return;
}
return;
}
static void
g_bde_orphan(struct g_consumer *cp)
{
struct g_geom *gp;
struct g_provider *pp;
struct g_bde_softc *sc;
g_trace(G_T_TOPOLOGY, "g_bde_orphan(%p/%s)", cp, cp->provider->name);
g_topology_assert();
gp = cp->geom;
sc = gp->softc;
gp->flags |= G_GEOM_WITHER;
LIST_FOREACH(pp, &gp->provider, provider)
g_wither_provider(pp, ENXIO);
explicit_bzero(sc, sizeof(struct g_bde_softc)); /* destroy evidence */
return;
}
static int
g_bde_access(struct g_provider *pp, int dr, int dw, int de)
{
struct g_geom *gp;
struct g_consumer *cp;
gp = pp->geom;
cp = LIST_FIRST(&gp->consumer);
if (cp->acr == 0 && cp->acw == 0 && cp->ace == 0) {
de++;
dr++;
}
/* ... and let go of it on last close */
if ((cp->acr + dr) == 0 && (cp->acw + dw) == 0 && (cp->ace + de) == 1) {
de--;
dr--;
}
return (g_access(cp, dr, dw, de));
}
static void
g_bde_create_geom(struct gctl_req *req, struct g_class *mp, struct g_provider *pp)
{
struct g_geom *gp;
struct g_consumer *cp;
struct g_bde_key *kp;
int error, i;
u_int sectorsize;
off_t mediasize;
struct g_bde_softc *sc;
void *pass;
void *key;
g_trace(G_T_TOPOLOGY, "g_bde_create_geom(%s, %s)", mp->name, pp->name);
g_topology_assert();
gp = NULL;
gp = g_new_geomf(mp, "%s.bde", pp->name);
cp = g_new_consumer(gp);
error = g_attach(cp, pp);
if (error != 0) {
g_destroy_consumer(cp);
g_destroy_geom(gp);
gctl_error(req, "could not attach consumer");
return;
}
error = g_access(cp, 1, 1, 1);
if (error) {
g_detach(cp);
g_destroy_consumer(cp);
g_destroy_geom(gp);
gctl_error(req, "could not access consumer");
return;
}
pass = NULL;
key = NULL;
do {
pass = gctl_get_param(req, "pass", &i);
if (pass == NULL || i != SHA512_DIGEST_LENGTH) {
gctl_error(req, "No usable key presented");
break;
}
key = gctl_get_param(req, "key", &i);
if (key != NULL && i != 16) {
gctl_error(req, "Invalid key presented");
break;
}
sectorsize = cp->provider->sectorsize;
mediasize = cp->provider->mediasize;
sc = g_malloc(sizeof(struct g_bde_softc), M_WAITOK | M_ZERO);
gp->softc = sc;
sc->geom = gp;
sc->consumer = cp;
error = g_bde_decrypt_lock(sc, pass, key,
mediasize, sectorsize, NULL);
explicit_bzero(sc->sha2, sizeof sc->sha2);
if (error)
break;
kp = &sc->key;
/* Initialize helper-fields */
kp->keys_per_sector = kp->sectorsize / G_BDE_SKEYLEN;
kp->zone_cont = kp->keys_per_sector * kp->sectorsize;
kp->zone_width = kp->zone_cont + kp->sectorsize;
kp->media_width = kp->sectorN - kp->sector0 -
G_BDE_MAXKEYS * kp->sectorsize;
/* Our external parameters */
sc->zone_cont = kp->zone_cont;
sc->mediasize = g_bde_max_sector(kp);
sc->sectorsize = kp->sectorsize;
TAILQ_INIT(&sc->freelist);
TAILQ_INIT(&sc->worklist);
mtx_init(&sc->worklist_mutex, "g_bde_worklist", NULL, MTX_DEF);
/* XXX: error check */
kproc_create(g_bde_worker, gp, &sc->thread, 0, 0,
"g_bde %s", gp->name);
pp = g_new_providerf(gp, "%s", gp->name);
pp->stripesize = kp->zone_cont;
pp->stripeoffset = 0;
pp->mediasize = sc->mediasize;
pp->sectorsize = sc->sectorsize;
g_error_provider(pp, 0);
break;
} while (0);
if (pass != NULL)
explicit_bzero(pass, SHA512_DIGEST_LENGTH);
if (key != NULL)
explicit_bzero(key, 16);
if (error == 0)
return;
g_access(cp, -1, -1, -1);
g_detach(cp);
g_destroy_consumer(cp);
g_free(gp->softc);
g_destroy_geom(gp);
switch (error) {
case ENOENT:
gctl_error(req, "Lock was destroyed");
break;
case ESRCH:
gctl_error(req, "Lock was nuked");
break;
case EINVAL:
gctl_error(req, "Could not open lock");
break;
case ENOTDIR:
gctl_error(req, "Lock not found");
break;
default:
gctl_error(req, "Could not open lock (%d)", error);
break;
}
return;
}
static int
g_bde_destroy_geom(struct gctl_req *req, struct g_class *mp, struct g_geom *gp)
{
struct g_consumer *cp;
struct g_provider *pp;
struct g_bde_softc *sc;
g_trace(G_T_TOPOLOGY, "g_bde_destroy_geom(%s, %s)", mp->name, gp->name);
g_topology_assert();
/*
* Orderly detachment.
*/
KASSERT(gp != NULL, ("NULL geom"));
pp = LIST_FIRST(&gp->provider);
KASSERT(pp != NULL, ("NULL provider"));
if (pp->acr > 0 || pp->acw > 0 || pp->ace > 0)
return (EBUSY);
sc = gp->softc;
cp = LIST_FIRST(&gp->consumer);
KASSERT(cp != NULL, ("NULL consumer"));
sc->dead = 1;
wakeup(sc);
g_access(cp, -1, -1, -1);
g_detach(cp);
g_destroy_consumer(cp);
while (sc->dead != 2 && !LIST_EMPTY(&pp->consumers))
tsleep(sc, PRIBIO, "g_bdedie", hz);
mtx_destroy(&sc->worklist_mutex);
explicit_bzero(&sc->key, sizeof sc->key);
g_free(sc);
g_wither_geom(gp, ENXIO);
return (0);
}
static void
g_bde_ctlreq(struct gctl_req *req, struct g_class *mp, char const *verb)
{
struct g_geom *gp;
struct g_provider *pp;
if (!strcmp(verb, "create geom")) {
pp = gctl_get_provider(req, "provider");
if (pp != NULL)
g_bde_create_geom(req, mp, pp);
} else if (!strcmp(verb, "destroy geom")) {
gp = gctl_get_geom(req, mp, "geom");
if (gp != NULL)
g_bde_destroy_geom(req, mp, gp);
} else {
gctl_error(req, "unknown verb");
}
}
static struct g_class g_bde_class = {
.name = BDE_CLASS_NAME,
.version = G_VERSION,
.destroy_geom = g_bde_destroy_geom,
.ctlreq = g_bde_ctlreq,
.start = g_bde_start,
.orphan = g_bde_orphan,
.access = g_bde_access,
.spoiled = g_std_spoiled,
};
DECLARE_GEOM_CLASS(g_bde_class, g_bde);
MODULE_VERSION(geom_bde, 0);

View File

@ -1,215 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* 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.
*
* 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.
*/
#ifndef _SYS_GEOM_BDE_G_BDE_H_
#define _SYS_GEOM_BDE_G_BDE_H_ 1
/*
* These are quite, but not entirely unlike constants.
*
* They are not commented in details here, to prevent unadvisable
* experimentation. Please consult the code where they are used before you
* even think about modifying these.
*/
#define G_BDE_MKEYLEN (2048/8)
#define G_BDE_SKEYBITS 128
#define G_BDE_SKEYLEN (G_BDE_SKEYBITS/8)
#define G_BDE_KKEYBITS 128
#define G_BDE_KKEYLEN (G_BDE_KKEYBITS/8)
#define G_BDE_MAXKEYS 4
#define G_BDE_LOCKSIZE 384
#define NLOCK_FIELDS 13
/* This just needs to be "large enough" */
#define G_BDE_KEYBYTES 304
/* This file is being included by userspace. */
#ifndef __diagused
#define __diagused
#endif
struct g_bde_work;
struct g_bde_softc;
struct g_bde_sector {
struct g_bde_work *owner;
struct g_bde_softc *softc;
off_t offset;
u_int size;
u_int ref;
void *data;
TAILQ_ENTRY(g_bde_sector) list;
u_char valid;
u_char malloc;
enum {JUNK, IO, VALID} state;
int error;
time_t used;
};
struct g_bde_work {
struct mtx mutex;
off_t offset;
off_t length;
void *data;
struct bio *bp;
struct g_bde_softc *softc;
off_t so;
off_t kso;
u_int ko;
struct g_bde_sector *sp;
struct g_bde_sector *ksp;
TAILQ_ENTRY(g_bde_work) list;
enum {SETUP, WAIT, FINISH} state;
int error;
};
/*
* The decrypted contents of the lock sectors. Notice that this is not
* the same as the on-disk layout. The on-disk layout is dynamic and
* dependent on the pass-phrase.
*/
struct g_bde_key {
uint64_t sector0;
/* Physical byte offset of 1st byte used */
uint64_t sectorN;
/* Physical byte offset of 1st byte not used */
uint64_t keyoffset;
/* Number of bytes the disk image is skewed. */
uint64_t lsector[G_BDE_MAXKEYS];
/* Physical byte offsets of lock sectors */
uint32_t sectorsize;
/* Our "logical" sector size */
uint32_t flags;
#define GBDE_F_SECT0 1
uint8_t salt[16];
/* Used to frustate the kkey generation */
uint8_t spare[32];
/* For future use, random contents */
uint8_t mkey[G_BDE_MKEYLEN];
/* Our masterkey. */
/* Non-stored help-fields */
uint64_t zone_width; /* On-disk width of zone */
uint64_t zone_cont; /* Payload width of zone */
uint64_t media_width; /* Non-magic width of zone */
u_int keys_per_sector;
};
struct g_bde_softc {
off_t mediasize;
u_int sectorsize;
uint64_t zone_cont;
struct g_geom *geom;
struct g_consumer *consumer;
TAILQ_HEAD(, g_bde_sector) freelist;
TAILQ_HEAD(, g_bde_work) worklist;
struct mtx worklist_mutex;
struct proc *thread;
struct g_bde_key key;
int dead;
u_int nwork;
u_int nsect;
u_int ncache;
u_char sha2[SHA512_DIGEST_LENGTH];
};
/* g_bde_crypt.c */
void g_bde_crypt_delete(struct g_bde_work *wp);
void g_bde_crypt_read(struct g_bde_work *wp);
void g_bde_crypt_write(struct g_bde_work *wp);
/* g_bde_key.c */
void g_bde_zap_key(struct g_bde_softc *sc);
int g_bde_get_key(struct g_bde_softc *sc, void *ptr, int len);
int g_bde_init_keybytes(struct g_bde_softc *sc, char *passp, int len);
/* g_bde_lock .c */
int g_bde_encode_lock(u_char *sha2, struct g_bde_key *gl, u_char *ptr);
int g_bde_decode_lock(struct g_bde_softc *sc, struct g_bde_key *gl, u_char *ptr);
int g_bde_keyloc_encrypt(u_char *sha2, uint64_t v0, uint64_t v1, void *output);
int g_bde_keyloc_decrypt(u_char *sha2, void *input, uint64_t *output);
int g_bde_decrypt_lock(struct g_bde_softc *sc, u_char *keymat, u_char *meta, off_t mediasize, u_int sectorsize, u_int *nkey);
void g_bde_hash_pass(struct g_bde_softc *sc, const void *input, u_int len);
/* g_bde_math .c */
uint64_t g_bde_max_sector(struct g_bde_key *lp);
void g_bde_map_sector(struct g_bde_work *wp);
/* g_bde_work.c */
void g_bde_start1(struct bio *bp);
void g_bde_worker(void *arg);
/*
* These four functions wrap the raw Rijndael functions and make sure we
* explode if something fails which shouldn't.
*/
static __inline void
AES_init(cipherInstance *ci)
{
int error __diagused;
error = rijndael_cipherInit(ci, MODE_CBC, NULL);
KASSERT(error > 0, ("rijndael_cipherInit %d", error));
}
static __inline void
AES_makekey(keyInstance *ki, int dir, u_int len, const void *key)
{
int error __diagused;
error = rijndael_makeKey(ki, dir, len, key);
KASSERT(error > 0, ("rijndael_makeKey %d", error));
}
static __inline void
AES_encrypt(cipherInstance *ci, keyInstance *ki, const void *in, void *out, u_int len)
{
int error __diagused;
error = rijndael_blockEncrypt(ci, ki, in, len * 8, out);
KASSERT(error > 0, ("rijndael_blockEncrypt %d", error));
}
static __inline void
AES_decrypt(cipherInstance *ci, keyInstance *ki, const void *in, void *out, u_int len)
{
int error __diagused;
error = rijndael_blockDecrypt(ci, ki, in, len * 8, out);
KASSERT(error > 0, ("rijndael_blockDecrypt %d", error));
}
#endif /* _SYS_GEOM_BDE_G_BDE_H_ */

View File

@ -1,358 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* 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.
*
* 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.
*/
/* This source file contains the functions responsible for the crypto, keying
* and mapping operations on the I/O requests.
*
*/
#include <sys/param.h>
#include <sys/bio.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/queue.h>
#include <sys/malloc.h>
#include <sys/libkern.h>
#include <sys/endian.h>
#include <sys/md5.h>
#include <crypto/rijndael/rijndael-api-fst.h>
#include <crypto/sha2/sha512.h>
#include <geom/geom.h>
#include <geom/bde/g_bde.h>
/*
* XXX: Debugging DO NOT ENABLE
*/
#undef MD5_KEY
/*
* Derive kkey from mkey + sector offset.
*
* Security objective: Derive a potentially very large number of distinct skeys
* from the comparatively small key material in our mkey, in such a way that
* if one, more or even many of the kkeys are compromised, this does not
* significantly help an attack on other kkeys and in particular does not
* weaken or compromise the mkey.
*
* First we MD5 hash the sectornumber with the salt from the lock sector.
* The salt prevents the precalculation and statistical analysis of the MD5
* output which would be possible if we only gave it the sectornumber.
*
* The MD5 hash is used to pick out 16 bytes from the masterkey, which
* are then hashed with MD5 together with the sector number.
*
* The resulting MD5 hash is the kkey.
*/
static void
g_bde_kkey(struct g_bde_softc *sc, keyInstance *ki, int dir, off_t sector)
{
u_int t;
MD5_CTX ct;
u_char buf[16];
u_char buf2[8];
/* We have to be architecture neutral */
le64enc(buf2, sector);
MD5Init(&ct);
MD5Update(&ct, sc->key.salt, 8);
MD5Update(&ct, buf2, sizeof buf2);
MD5Update(&ct, sc->key.salt + 8, 8);
MD5Final(buf, &ct);
MD5Init(&ct);
for (t = 0; t < 16; t++) {
MD5Update(&ct, &sc->key.mkey[buf[t]], 1);
if (t == 8)
MD5Update(&ct, buf2, sizeof buf2);
}
bzero(buf2, sizeof buf2);
MD5Final(buf, &ct);
bzero(&ct, sizeof ct);
AES_makekey(ki, dir, G_BDE_KKEYBITS, buf);
bzero(buf, sizeof buf);
}
/*
* Encryption work for read operation.
*
* Security objective: Find the kkey, find the skey, decrypt the sector data.
*/
void
g_bde_crypt_read(struct g_bde_work *wp)
{
struct g_bde_softc *sc;
u_char *d;
u_int n;
off_t o;
u_char skey[G_BDE_SKEYLEN];
keyInstance ki;
cipherInstance ci;
AES_init(&ci);
sc = wp->softc;
o = 0;
for (n = 0; o < wp->length; n++, o += sc->sectorsize) {
d = (u_char *)wp->ksp->data + wp->ko + n * G_BDE_SKEYLEN;
g_bde_kkey(sc, &ki, DIR_DECRYPT, wp->offset + o);
AES_decrypt(&ci, &ki, d, skey, sizeof skey);
d = (u_char *)wp->data + o;
AES_makekey(&ki, DIR_DECRYPT, G_BDE_SKEYBITS, skey);
AES_decrypt(&ci, &ki, d, d, sc->sectorsize);
}
bzero(skey, sizeof skey);
bzero(&ci, sizeof ci);
bzero(&ki, sizeof ki);
}
/*
* Encryption work for write operation.
*
* Security objective: Create random skey, encrypt sector data,
* encrypt skey with the kkey.
*/
void
g_bde_crypt_write(struct g_bde_work *wp)
{
u_char *s, *d;
struct g_bde_softc *sc;
u_int n;
off_t o;
u_char skey[G_BDE_SKEYLEN];
keyInstance ki;
cipherInstance ci;
sc = wp->softc;
AES_init(&ci);
o = 0;
for (n = 0; o < wp->length; n++, o += sc->sectorsize) {
s = (u_char *)wp->data + o;
d = (u_char *)wp->sp->data + o;
arc4rand(skey, sizeof skey, 0);
AES_makekey(&ki, DIR_ENCRYPT, G_BDE_SKEYBITS, skey);
AES_encrypt(&ci, &ki, s, d, sc->sectorsize);
d = (u_char *)wp->ksp->data + wp->ko + n * G_BDE_SKEYLEN;
g_bde_kkey(sc, &ki, DIR_ENCRYPT, wp->offset + o);
AES_encrypt(&ci, &ki, skey, d, sizeof skey);
bzero(skey, sizeof skey);
}
bzero(skey, sizeof skey);
bzero(&ci, sizeof ci);
bzero(&ki, sizeof ki);
}
/*
* Encryption work for delete operation.
*
* Security objective: Write random data to the sectors.
*
* XXX: At a hit in performance we would trash the encrypted skey as well.
* XXX: This would add frustration to the cleaning lady attack by making
* XXX: deletes look like writes.
*/
void
g_bde_crypt_delete(struct g_bde_work *wp)
{
struct g_bde_softc *sc;
u_char *d;
off_t o;
u_char skey[G_BDE_SKEYLEN];
keyInstance ki;
cipherInstance ci;
sc = wp->softc;
d = wp->sp->data;
AES_init(&ci);
/*
* Do not unroll this loop!
* Our zone may be significantly wider than the amount of random
* bytes arc4rand likes to give in one reseeding, whereas our
* sectorsize is far more likely to be in the same range.
*/
for (o = 0; o < wp->length; o += sc->sectorsize) {
arc4rand(d, sc->sectorsize, 0);
arc4rand(skey, sizeof skey, 0);
AES_makekey(&ki, DIR_ENCRYPT, G_BDE_SKEYBITS, skey);
AES_encrypt(&ci, &ki, d, d, sc->sectorsize);
d += sc->sectorsize;
}
/*
* Having written a long random sequence to disk here, we want to
* force a reseed, to avoid weakening the next time we use random
* data for something important.
*/
arc4rand(&o, sizeof o, 1);
}
/*
* Calculate the total payload size of the encrypted device.
*
* Security objectives: none.
*
* This function needs to agree with g_bde_map_sector() about things.
*/
uint64_t
g_bde_max_sector(struct g_bde_key *kp)
{
uint64_t maxsect;
maxsect = kp->media_width;
maxsect /= kp->zone_width;
maxsect *= kp->zone_cont;
return (maxsect);
}
/*
* Convert an unencrypted side offset to offsets on the encrypted side.
*
* Security objective: Make it harder to identify what sectors contain what
* on a "cold" disk image.
*
* We do this by adding the "keyoffset" from the lock to the physical sector
* number modulus the available number of sectors. Since all physical sectors
* presumably look the same cold, this will do.
*
* As part of the mapping we have to skip the lock sectors which we know
* the physical address off. We also truncate the work packet, respecting
* zone boundaries and lock sectors, so that we end up with a sequence of
* sectors which are physically contiguous.
*
* Shuffling things further is an option, but the incremental frustration is
* not currently deemed worth the run-time performance hit resulting from the
* increased number of disk arm movements it would incur.
*
* This function offers nothing but a trivial diversion for an attacker able
* to do "the cleaning lady attack" in its current static mapping form.
*/
void
g_bde_map_sector(struct g_bde_work *wp)
{
u_int zone, zoff, u, len;
uint64_t ko;
struct g_bde_softc *sc;
struct g_bde_key *kp;
sc = wp->softc;
kp = &sc->key;
/* find which zone and the offset in it */
zone = wp->offset / kp->zone_cont;
zoff = wp->offset % kp->zone_cont;
/* Calculate the offset of the key in the key sector */
wp->ko = (zoff / kp->sectorsize) * G_BDE_SKEYLEN;
/* restrict length to that zone */
len = kp->zone_cont - zoff;
/* ... and in general */
if (len > DFLTPHYS)
len = DFLTPHYS;
if (len < wp->length)
wp->length = len;
/* Find physical sector address */
wp->so = zone * kp->zone_width + zoff;
wp->so += kp->keyoffset;
wp->so %= kp->media_width;
if (wp->so + wp->length > kp->media_width)
wp->length = kp->media_width - wp->so;
wp->so += kp->sector0;
/* The key sector is the last in this zone. */
wp->kso = zone * kp->zone_width + kp->zone_cont;
wp->kso += kp->keyoffset;
wp->kso %= kp->media_width;
wp->kso += kp->sector0;
/* Compensate for lock sectors */
for (u = 0; u < G_BDE_MAXKEYS; u++) {
/* Find the start of this lock sector */
ko = rounddown2(kp->lsector[u], (uint64_t)kp->sectorsize);
if (wp->kso >= ko)
wp->kso += kp->sectorsize;
if (wp->so >= ko) {
/* lock sector before work packet */
wp->so += kp->sectorsize;
} else if ((wp->so + wp->length) > ko) {
/* lock sector in work packet, truncate */
wp->length = ko - wp->so;
}
}
#if 0
printf("off %jd len %jd so %jd ko %jd kso %u\n",
(intmax_t)wp->offset,
(intmax_t)wp->length,
(intmax_t)wp->so,
(intmax_t)wp->kso,
wp->ko);
#endif
KASSERT(wp->so + wp->length <= kp->sectorN,
("wp->so (%jd) + wp->length (%jd) > EOM (%jd), offset = %jd",
(intmax_t)wp->so,
(intmax_t)wp->length,
(intmax_t)kp->sectorN,
(intmax_t)wp->offset));
KASSERT(wp->kso + kp->sectorsize <= kp->sectorN,
("wp->kso (%jd) + kp->sectorsize > EOM (%jd), offset = %jd",
(intmax_t)wp->kso,
(intmax_t)kp->sectorN,
(intmax_t)wp->offset));
KASSERT(wp->so >= kp->sector0,
("wp->so (%jd) < BOM (%jd), offset = %jd",
(intmax_t)wp->so,
(intmax_t)kp->sector0,
(intmax_t)wp->offset));
KASSERT(wp->kso >= kp->sector0,
("wp->kso (%jd) <BOM (%jd), offset = %jd",
(intmax_t)wp->kso,
(intmax_t)kp->sector0,
(intmax_t)wp->offset));
}

View File

@ -1,478 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* 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.
*
* 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.
*/
/* This souce file contains routines which operates on the lock sectors, both
* for the kernel and the userland program gbde(1).
*
*/
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/endian.h>
#include <sys/md5.h>
#ifdef _KERNEL
#include <sys/malloc.h>
#include <sys/systm.h>
#else
#include <err.h>
#define CTASSERT(foo)
#define KASSERT(foo, bar) do { if(!(foo)) { warn bar ; exit (1); } } while (0)
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#define g_free(foo) free(foo)
#endif
#include <crypto/rijndael/rijndael-api-fst.h>
#include <crypto/sha2/sha512.h>
#include <geom/geom.h>
#include <geom/bde/g_bde.h>
/*
* Hash the raw pass-phrase.
*
* Security objectives: produce from the pass-phrase a fixed length
* bytesequence with PRN like properties in a reproducible way retaining
* as much entropy from the pass-phrase as possible.
*
* SHA2-512 makes this easy.
*/
void
g_bde_hash_pass(struct g_bde_softc *sc, const void *input, u_int len)
{
SHA512_CTX cx;
SHA512_Init(&cx);
SHA512_Update(&cx, input, len);
SHA512_Final(sc->sha2, &cx);
}
/*
* Encode/Decode the lock structure in byte-sequence format.
*
* Security objectives: Store in pass-phrase dependent variant format.
*
* C-structure packing and byte-endianess depends on architecture, compiler
* and compiler options. Writing raw structures to disk is therefore a bad
* idea in these enlightend days.
*
* We spend a fraction of the key-material on shuffling the fields around
* so they will be stored in an unpredictable sequence.
*
* For each byte of the key-material we derive two field indexes, and swap
* the position of those two fields.
*
* I have not worked out the statistical properties of this shuffle, but
* given that the key-material has PRN properties, the primary objective
* of making it hard to figure out which bits are where in the lock sector
* is sufficiently fulfilled.
*
* We include (and shuffle) an extra hash field in the stored version for
* identification and versioning purposes. This field contains the MD5 hash
* of a version identifier (currently "0000") followed by the stored lock
* sector byte-sequence substituting zero bytes for the hash field.
*
* The stored keysequence is protected by AES/256/CBC elsewhere in the code
* so the fact that the generated byte sequence has a much higher than
* average density of zero bits (from the numeric fields) is not currently
* a concern.
*
* Should this later become a concern, a simple software update and
* pass-phrase change can remedy the situation. One possible solution
* could be to XOR the numeric fields with a key-material derived PRN.
*
* The chosen shuffle algorithm only works as long as we have no more than 16
* fields in the stored part of the lock structure (hence the CTASSERT below).
*/
CTASSERT(NLOCK_FIELDS <= 16);
static void
g_bde_shuffle_lock(u_char *sha2, int *buf)
{
int j, k, l;
u_int u;
/* Assign the fields sequential positions */
for(u = 0; u < NLOCK_FIELDS; u++)
buf[u] = u;
/* Then mix it all up */
for(u = 48; u < SHA512_DIGEST_LENGTH; u++) {
j = sha2[u] % NLOCK_FIELDS;
k = (sha2[u] / NLOCK_FIELDS) % NLOCK_FIELDS;
l = buf[j];
buf[j] = buf[k];
buf[k] = l;
}
}
int
g_bde_encode_lock(u_char *sha2, struct g_bde_key *gl, u_char *ptr)
{
int shuffle[NLOCK_FIELDS];
u_char *hash, *p;
int i;
MD5_CTX c;
p = ptr;
hash = NULL;
g_bde_shuffle_lock(sha2, shuffle);
for (i = 0; i < NLOCK_FIELDS; i++) {
switch(shuffle[i]) {
case 0:
le64enc(p, gl->sector0);
p += 8;
break;
case 1:
le64enc(p, gl->sectorN);
p += 8;
break;
case 2:
le64enc(p, gl->keyoffset);
p += 8;
break;
case 3:
le32enc(p, gl->sectorsize);
p += 4;
break;
case 4:
le32enc(p, gl->flags);
p += 4;
break;
case 5:
case 6:
case 7:
case 8:
le64enc(p, gl->lsector[shuffle[i] - 5]);
p += 8;
break;
case 9:
bcopy(gl->spare, p, sizeof gl->spare);
p += sizeof gl->spare;
break;
case 10:
bcopy(gl->salt, p, sizeof gl->salt);
p += sizeof gl->salt;
break;
case 11:
bcopy(gl->mkey, p, sizeof gl->mkey);
p += sizeof gl->mkey;
break;
case 12:
bzero(p, 16);
hash = p;
p += 16;
break;
}
}
if(ptr + G_BDE_LOCKSIZE != p)
return(-1);
if (hash == NULL)
return(-1);
MD5Init(&c);
MD5Update(&c, "0000", 4); /* Versioning */
MD5Update(&c, ptr, G_BDE_LOCKSIZE);
MD5Final(hash, &c);
return(0);
}
int
g_bde_decode_lock(struct g_bde_softc *sc, struct g_bde_key *gl, u_char *ptr)
{
int shuffle[NLOCK_FIELDS];
u_char *p;
u_char hash[16], hash2[16];
MD5_CTX c;
int i;
p = ptr;
g_bde_shuffle_lock(sc->sha2, shuffle);
for (i = 0; i < NLOCK_FIELDS; i++) {
switch(shuffle[i]) {
case 0:
gl->sector0 = le64dec(p);
p += 8;
break;
case 1:
gl->sectorN = le64dec(p);
p += 8;
break;
case 2:
gl->keyoffset = le64dec(p);
p += 8;
break;
case 3:
gl->sectorsize = le32dec(p);
p += 4;
break;
case 4:
gl->flags = le32dec(p);
p += 4;
break;
case 5:
case 6:
case 7:
case 8:
gl->lsector[shuffle[i] - 5] = le64dec(p);
p += 8;
break;
case 9:
bcopy(p, gl->spare, sizeof gl->spare);
p += sizeof gl->spare;
break;
case 10:
bcopy(p, gl->salt, sizeof gl->salt);
p += sizeof gl->salt;
break;
case 11:
bcopy(p, gl->mkey, sizeof gl->mkey);
p += sizeof gl->mkey;
break;
case 12:
bcopy(p, hash2, sizeof hash2);
bzero(p, sizeof hash2);
p += sizeof hash2;
break;
}
}
if(ptr + G_BDE_LOCKSIZE != p)
return(-1);
MD5Init(&c);
MD5Update(&c, "0000", 4); /* Versioning */
MD5Update(&c, ptr, G_BDE_LOCKSIZE);
MD5Final(hash, &c);
if (bcmp(hash, hash2, sizeof hash2))
return (1);
return (0);
}
/*
* Encode/Decode the locksector address ("metadata") with key-material.
*
* Security objectives: Encode/Decode the metadata encrypted by key-material.
*
* A simple AES/128/CBC will do. We take care to always store the metadata
* in the same endianness to make it MI.
*
* In the typical case the metadata is stored in encrypted format in sector
* zero on the media, but at the users discretion or if the piece of the
* device used (sector0...sectorN) does not contain sector zero, it can
* be stored in a filesystem or on a PostIt.
*
* The inability to easily locate the lock sectors makes an attack on a
* cold disk much less attractive, without unduly inconveniencing the
* legitimate user who can feasibly do a brute-force scan if the metadata
* was lost.
*/
int
g_bde_keyloc_encrypt(u_char *sha2, uint64_t v0, uint64_t v1, void *output)
{
u_char buf[16];
keyInstance ki;
cipherInstance ci;
le64enc(buf, v0);
le64enc(buf + 8, v1);
AES_init(&ci);
AES_makekey(&ki, DIR_ENCRYPT, G_BDE_KKEYBITS, sha2 + 0);
AES_encrypt(&ci, &ki, buf, output, sizeof buf);
explicit_bzero(buf, sizeof buf);
explicit_bzero(&ci, sizeof ci);
explicit_bzero(&ki, sizeof ki);
return (0);
}
int
g_bde_keyloc_decrypt(u_char *sha2, void *input, uint64_t *output)
{
keyInstance ki;
cipherInstance ci;
u_char buf[16];
AES_init(&ci);
AES_makekey(&ki, DIR_DECRYPT, G_BDE_KKEYBITS, sha2 + 0);
AES_decrypt(&ci, &ki, input, buf, sizeof buf);
*output = le64dec(buf);
explicit_bzero(buf, sizeof buf);
explicit_bzero(&ci, sizeof ci);
explicit_bzero(&ki, sizeof ki);
return(0);
}
/*
* Find and Encode/Decode lock sectors.
*
* Security objective: given the pass-phrase, find, decrypt, decode and
* validate the lock sector contents.
*
* For ondisk metadata we cannot know beforehand which of the lock sectors
* a given pass-phrase opens so we must try each of the metadata copies in
* sector zero in turn. If metadata was passed as an argument, we don't
* have this problem.
*
*/
static int
g_bde_decrypt_lockx(struct g_bde_softc *sc, u_char *meta, off_t mediasize, u_int sectorsize, u_int *nkey)
{
u_char *buf, *q;
struct g_bde_key *gl;
uint64_t off, q1;
int error, m, i;
keyInstance ki;
cipherInstance ci;
gl = &sc->key;
/* Try to decrypt the metadata */
error = g_bde_keyloc_decrypt(sc->sha2, meta, &off);
if (error)
return (error);
/* If it points into thin blue air, forget it */
if (off + G_BDE_LOCKSIZE > (uint64_t)mediasize) {
off = 0;
return (EINVAL);
}
/* The lock data may span two physical sectors. */
m = 1;
if (off % sectorsize > sectorsize - G_BDE_LOCKSIZE)
m++;
/* Read the suspected sector(s) */
buf = g_read_data(sc->consumer,
off - (off % sectorsize),
m * sectorsize, &error);
if (buf == NULL) {
off = 0;
return(error);
}
/* Find the byte-offset of the stored byte sequence */
q = buf + off % sectorsize;
/* If it is all zero, somebody nuked our lock sector */
q1 = 0;
for (i = 0; i < G_BDE_LOCKSIZE; i++)
q1 += q[i];
if (q1 == 0) {
off = 0;
g_free(buf);
return (ESRCH);
}
/* Decrypt the byte-sequence in place */
AES_init(&ci);
AES_makekey(&ki, DIR_DECRYPT, 256, sc->sha2 + 16);
AES_decrypt(&ci, &ki, q, q, G_BDE_LOCKSIZE);
/* Decode the byte-sequence */
i = g_bde_decode_lock(sc, gl, q);
q = NULL;
if (i < 0) {
off = 0;
return (EDOOFUS); /* Programming error */
} else if (i > 0) {
off = 0;
return (ENOTDIR); /* Hash didn't match */
}
bzero(buf, sectorsize * m);
g_free(buf);
/* If the masterkey is all zeros, user destroyed it */
q1 = 0;
for (i = 0; i < (int)sizeof(gl->mkey); i++)
q1 += gl->mkey[i];
if (q1 == 0)
return (ENOENT);
/* If we have an unsorted lock-sequence, refuse */
for (i = 0; i < G_BDE_MAXKEYS - 1; i++)
if (gl->lsector[i] >= gl->lsector[i + 1])
return (EINVAL);
/* Finally, find out which key was used by matching the byte offset */
for (i = 0; i < G_BDE_MAXKEYS; i++)
if (nkey != NULL && off == gl->lsector[i])
*nkey = i;
off = 0;
return (0);
}
int
g_bde_decrypt_lock(struct g_bde_softc *sc, u_char *keymat, u_char *meta, off_t mediasize, u_int sectorsize, u_int *nkey)
{
u_char *buf, buf1[16];
int error, e, i;
/* set up the key-material */
bcopy(keymat, sc->sha2, SHA512_DIGEST_LENGTH);
/* If passed-in metadata is non-zero, use it */
bzero(buf1, sizeof buf1);
if (meta != NULL && bcmp(buf1, meta, sizeof buf1))
return (g_bde_decrypt_lockx(sc, meta, mediasize,
sectorsize, nkey));
/* Read sector zero */
buf = g_read_data(sc->consumer, 0, sectorsize, &error);
if (buf == NULL)
return(error);
/* Try each index in turn, save indicative errors for final result */
error = EINVAL;
for (i = 0; i < G_BDE_MAXKEYS; i++) {
e = g_bde_decrypt_lockx(sc, buf + i * 16, mediasize,
sectorsize, nkey);
/* Success or destroyed master key terminates */
if (e == 0 || e == ENOENT) {
error = e;
break;
}
if (e != 0 && error == EINVAL)
error = e;
}
g_free(buf);
return (error);
}

View File

@ -1,778 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* 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.
*
* 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.
*/
/*
* This source file contains the state-engine which makes things happen in the
* right order.
*
* Outline:
* 1) g_bde_start1()
* Break the struct bio into multiple work packets one per zone.
* 2) g_bde_start2()
* Setup the necessary sector buffers and start those read operations
* which we can start at this time and put the item on the work-list.
* 3) g_bde_worker()
* Scan the work-list for items which are ready for crypto processing
* and call the matching crypto function in g_bde_crypt.c and schedule
* any writes needed. Read operations finish here by releasing the
* sector buffers and delivering the original bio request.
* 4) g_bde_write_done()
* Release sector buffers and deliver the original bio request.
*
* Because of the C-scope rules, the functions are almost perfectly in the
* opposite order in this source file.
*
* XXX: A switch to the hardware assisted crypto in src/sys/opencrypto will add
* XXX: additional states to this state-engine. Since no hardware available
* XXX: at this time has AES support, implementing this has been postponed
* XXX: until such time as it would result in a benefit.
*/
#include <sys/param.h>
#include <sys/bio.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/queue.h>
#include <sys/malloc.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/sysctl.h>
#include <sys/proc.h>
#include <sys/kthread.h>
#include <crypto/rijndael/rijndael-api-fst.h>
#include <crypto/sha2/sha512.h>
#include <geom/geom.h>
#include <geom/bde/g_bde.h>
/*
* FIXME: This used to call malloc_last_fail which in practice was almost
* guaranteed to return time_uptime even in face of severe memory shortage.
* As GBDE is the only consumer the kludge below was added to facilitate the
* removal with minimial changes. The code should be fixed to respond to memory
* pressure (e.g., by using lowmem eventhandler) instead.
*/
static int
g_bde_malloc_last_fail(void)
{
return (time_uptime);
}
static void g_bde_delete_sector(struct g_bde_softc *wp, struct g_bde_sector *sp);
static struct g_bde_sector * g_bde_new_sector(struct g_bde_work *wp, u_int len);
static void g_bde_release_keysector(struct g_bde_work *wp);
static struct g_bde_sector *g_bde_get_keysector(struct g_bde_work *wp);
static int g_bde_start_read(struct g_bde_sector *sp);
static void g_bde_purge_sector(struct g_bde_softc *sc, int fraction);
/*
* Work item allocation.
*
* C++ would call these constructors and destructors.
*/
static u_int g_bde_nwork;
SYSCTL_UINT(_debug, OID_AUTO, gbde_nwork, CTLFLAG_RD, &g_bde_nwork, 0, "");
static MALLOC_DEFINE(M_GBDE, "gbde", "GBDE data structures");
static struct g_bde_work *
g_bde_new_work(struct g_bde_softc *sc)
{
struct g_bde_work *wp;
wp = malloc(sizeof *wp, M_GBDE, M_NOWAIT | M_ZERO);
if (wp == NULL)
return (wp);
wp->state = SETUP;
wp->softc = sc;
g_bde_nwork++;
sc->nwork++;
TAILQ_INSERT_TAIL(&sc->worklist, wp, list);
return (wp);
}
static void
g_bde_delete_work(struct g_bde_work *wp)
{
struct g_bde_softc *sc;
sc = wp->softc;
g_bde_nwork--;
sc->nwork--;
TAILQ_REMOVE(&sc->worklist, wp, list);
free(wp, M_GBDE);
}
/*
* Sector buffer allocation
*
* These two functions allocate and free back variable sized sector buffers
*/
static u_int g_bde_nsect;
SYSCTL_UINT(_debug, OID_AUTO, gbde_nsect, CTLFLAG_RD, &g_bde_nsect, 0, "");
static void
g_bde_delete_sector(struct g_bde_softc *sc, struct g_bde_sector *sp)
{
g_bde_nsect--;
sc->nsect--;
if (sp->malloc)
free(sp->data, M_GBDE);
free(sp, M_GBDE);
}
static struct g_bde_sector *
g_bde_new_sector(struct g_bde_work *wp, u_int len)
{
struct g_bde_sector *sp;
sp = malloc(sizeof *sp, M_GBDE, M_NOWAIT | M_ZERO);
if (sp == NULL)
return (sp);
if (len > 0) {
sp->data = malloc(len, M_GBDE, M_NOWAIT | M_ZERO);
if (sp->data == NULL) {
free(sp, M_GBDE);
return (NULL);
}
sp->malloc = 1;
}
g_bde_nsect++;
wp->softc->nsect++;
sp->size = len;
sp->softc = wp->softc;
sp->ref = 1;
sp->owner = wp;
sp->offset = wp->so;
sp->state = JUNK;
return (sp);
}
/*
* Skey sector cache.
*
* Nothing prevents two separate I/O requests from addressing the same zone
* and thereby needing the same skey sector. We therefore need to sequence
* I/O operations to the skey sectors. A certain amount of caching is also
* desirable, although the extent of benefit from this is not at this point
* determined.
*
* XXX: GEOM may be able to grow a generic caching facility at some point
* XXX: to support such needs.
*/
static u_int g_bde_ncache;
SYSCTL_UINT(_debug, OID_AUTO, gbde_ncache, CTLFLAG_RD, &g_bde_ncache, 0, "");
static void
g_bde_purge_one_sector(struct g_bde_softc *sc, struct g_bde_sector *sp)
{
g_trace(G_T_TOPOLOGY, "g_bde_purge_one_sector(%p, %p)", sc, sp);
if (sp->ref != 0)
return;
TAILQ_REMOVE(&sc->freelist, sp, list);
g_bde_ncache--;
sc->ncache--;
bzero(sp->data, sp->size);
g_bde_delete_sector(sc, sp);
}
static struct g_bde_sector *
g_bde_get_keysector(struct g_bde_work *wp)
{
struct g_bde_sector *sp;
struct g_bde_softc *sc;
off_t offset;
offset = wp->kso;
g_trace(G_T_TOPOLOGY, "g_bde_get_keysector(%p, %jd)", wp, (intmax_t)offset);
sc = wp->softc;
if (g_bde_malloc_last_fail() < g_bde_ncache)
g_bde_purge_sector(sc, -1);
sp = TAILQ_FIRST(&sc->freelist);
if (sp != NULL && sp->ref == 0 && sp->used + 300 < time_uptime)
g_bde_purge_one_sector(sc, sp);
TAILQ_FOREACH(sp, &sc->freelist, list) {
if (sp->offset == offset)
break;
}
if (sp != NULL) {
sp->ref++;
KASSERT(sp->offset == offset, ("wrong offset"));
KASSERT(sp->softc == wp->softc, ("wrong softc"));
if (sp->ref == 1)
sp->owner = wp;
} else {
if (g_bde_malloc_last_fail() < g_bde_ncache) {
TAILQ_FOREACH(sp, &sc->freelist, list)
if (sp->ref == 0)
break;
}
if (sp == NULL && !TAILQ_EMPTY(&sc->freelist))
sp = TAILQ_FIRST(&sc->freelist);
if (sp != NULL && sp->ref > 0)
sp = NULL;
if (sp == NULL) {
sp = g_bde_new_sector(wp, sc->sectorsize);
if (sp != NULL) {
g_bde_ncache++;
sc->ncache++;
TAILQ_INSERT_TAIL(&sc->freelist, sp, list);
sp->malloc = 2;
}
}
if (sp != NULL) {
sp->offset = offset;
sp->softc = wp->softc;
sp->ref = 1;
sp->owner = wp;
sp->state = JUNK;
sp->error = 0;
}
}
if (sp != NULL) {
TAILQ_REMOVE(&sc->freelist, sp, list);
TAILQ_INSERT_TAIL(&sc->freelist, sp, list);
sp->used = time_uptime;
}
wp->ksp = sp;
return(sp);
}
static void
g_bde_release_keysector(struct g_bde_work *wp)
{
struct g_bde_softc *sc;
struct g_bde_work *wp2;
struct g_bde_sector *sp;
sp = wp->ksp;
g_trace(G_T_TOPOLOGY, "g_bde_release_keysector(%p)", sp);
KASSERT(sp->malloc == 2, ("Wrong sector released"));
sc = sp->softc;
KASSERT(sc != NULL, ("NULL sp->softc"));
KASSERT(wp == sp->owner, ("Releasing, not owner"));
sp->owner = NULL;
wp->ksp = NULL;
sp->ref--;
if (sp->ref > 0) {
TAILQ_REMOVE(&sc->freelist, sp, list);
TAILQ_INSERT_TAIL(&sc->freelist, sp, list);
TAILQ_FOREACH(wp2, &sc->worklist, list) {
if (wp2->ksp == sp) {
KASSERT(wp2 != wp, ("Self-reowning"));
sp->owner = wp2;
wakeup(sp->softc);
break;
}
}
KASSERT(wp2 != NULL, ("Failed to pick up owner for %p\n", sp));
} else if (sp->error != 0) {
sp->offset = ~0;
sp->error = 0;
sp->state = JUNK;
}
TAILQ_REMOVE(&sc->freelist, sp, list);
TAILQ_INSERT_HEAD(&sc->freelist, sp, list);
}
static void
g_bde_purge_sector(struct g_bde_softc *sc, int fraction)
{
struct g_bde_sector *sp;
int n;
g_trace(G_T_TOPOLOGY, "g_bde_purge_sector(%p)", sc);
if (fraction > 0)
n = sc->ncache / fraction + 1;
else
n = g_bde_ncache - g_bde_malloc_last_fail();
if (n < 0)
return;
if (n > sc->ncache)
n = sc->ncache;
while(n--) {
TAILQ_FOREACH(sp, &sc->freelist, list) {
if (sp->ref != 0)
continue;
TAILQ_REMOVE(&sc->freelist, sp, list);
g_bde_ncache--;
sc->ncache--;
bzero(sp->data, sp->size);
g_bde_delete_sector(sc, sp);
break;
}
}
}
static struct g_bde_sector *
g_bde_read_keysector(struct g_bde_softc *sc, struct g_bde_work *wp)
{
struct g_bde_sector *sp;
g_trace(G_T_TOPOLOGY, "g_bde_read_keysector(%p)", wp);
sp = g_bde_get_keysector(wp);
if (sp == NULL) {
g_bde_purge_sector(sc, -1);
sp = g_bde_get_keysector(wp);
}
if (sp == NULL)
return (sp);
if (sp->owner != wp)
return (sp);
if (sp->state == VALID)
return (sp);
if (g_bde_start_read(sp) == 0)
return (sp);
g_bde_release_keysector(wp);
return (NULL);
}
/*
* Contribute to the completion of the original bio request.
*
* We have no simple way to tell how many bits the original bio request has
* been segmented into, so the easiest way to determine when we can deliver
* it is to keep track of the number of bytes we have completed. We keep
* track of any errors underway and latch onto the first one.
*
* We always report "nothing done" in case of error, because random bits here
* and there may be completed and returning a number of completed bytes does
* not convey any useful information about which bytes they were. If some
* piece of broken code somewhere interprets this to mean that nothing has
* changed on the underlying media they deserve the lossage headed for them.
*
* A single mutex per g_bde instance is used to prevent contention.
*/
static void
g_bde_contribute(struct bio *bp, off_t bytes, int error)
{
g_trace(G_T_TOPOLOGY, "g_bde_contribute bp %p bytes %jd error %d",
bp, (intmax_t)bytes, error);
if (bp->bio_error == 0)
bp->bio_error = error;
bp->bio_completed += bytes;
KASSERT(bp->bio_completed <= bp->bio_length, ("Too large contribution"));
if (bp->bio_completed == bp->bio_length) {
if (bp->bio_error != 0)
bp->bio_completed = 0;
g_io_deliver(bp, bp->bio_error);
}
}
/*
* This is the common case "we're done with this work package" function
*/
static void
g_bde_work_done(struct g_bde_work *wp, int error)
{
g_bde_contribute(wp->bp, wp->length, error);
if (wp->sp != NULL)
g_bde_delete_sector(wp->softc, wp->sp);
if (wp->ksp != NULL)
g_bde_release_keysector(wp);
g_bde_delete_work(wp);
}
/*
* A write operation has finished. When we have all expected cows in the
* barn close the door and call it a day.
*/
static void
g_bde_write_done(struct bio *bp)
{
struct g_bde_sector *sp;
struct g_bde_work *wp;
struct g_bde_softc *sc;
sp = bp->bio_caller1;
sc = bp->bio_caller2;
mtx_lock(&sc->worklist_mutex);
KASSERT(sp != NULL, ("NULL sp"));
KASSERT(sc != NULL, ("NULL sc"));
KASSERT(sp->owner != NULL, ("NULL sp->owner"));
g_trace(G_T_TOPOLOGY, "g_bde_write_done(%p)", sp);
if (bp->bio_error == 0 && bp->bio_completed != sp->size)
bp->bio_error = EIO;
sp->error = bp->bio_error;
g_destroy_bio(bp);
wp = sp->owner;
if (wp->error == 0)
wp->error = sp->error;
if (wp->bp->bio_cmd == BIO_DELETE) {
KASSERT(sp == wp->sp, ("trashed delete op"));
g_bde_work_done(wp, wp->error);
mtx_unlock(&sc->worklist_mutex);
return;
}
KASSERT(wp->bp->bio_cmd == BIO_WRITE, ("Confused in g_bde_write_done()"));
KASSERT(sp == wp->sp || sp == wp->ksp, ("trashed write op"));
if (wp->sp == sp) {
g_bde_delete_sector(sc, wp->sp);
wp->sp = NULL;
} else {
sp->state = VALID;
}
if (wp->sp == NULL && wp->ksp != NULL && wp->ksp->state == VALID)
g_bde_work_done(wp, wp->error);
mtx_unlock(&sc->worklist_mutex);
return;
}
/*
* Send a write request for the given sector down the pipeline.
*/
static int
g_bde_start_write(struct g_bde_sector *sp)
{
struct bio *bp;
struct g_bde_softc *sc;
g_trace(G_T_TOPOLOGY, "g_bde_start_write(%p)", sp);
sc = sp->softc;
KASSERT(sc != NULL, ("NULL sc in g_bde_start_write"));
KASSERT(sp->owner != NULL, ("NULL sp->owner in g_bde_start_write"));
bp = g_new_bio();
if (bp == NULL)
return (ENOMEM);
bp->bio_cmd = BIO_WRITE;
bp->bio_offset = sp->offset;
bp->bio_data = sp->data;
bp->bio_length = sp->size;
bp->bio_done = g_bde_write_done;
bp->bio_caller1 = sp;
bp->bio_caller2 = sc;
sp->state = IO;
g_io_request(bp, sc->consumer);
return(0);
}
/*
* A read operation has finished. Mark the sector no longer iobusy and
* wake up the worker thread and let it do its thing.
*/
static void
g_bde_read_done(struct bio *bp)
{
struct g_bde_sector *sp;
struct g_bde_softc *sc;
sp = bp->bio_caller1;
g_trace(G_T_TOPOLOGY, "g_bde_read_done(%p)", sp);
sc = bp->bio_caller2;
mtx_lock(&sc->worklist_mutex);
if (bp->bio_error == 0 && bp->bio_completed != sp->size)
bp->bio_error = EIO;
sp->error = bp->bio_error;
if (sp->error == 0)
sp->state = VALID;
else
sp->state = JUNK;
wakeup(sc);
g_destroy_bio(bp);
mtx_unlock(&sc->worklist_mutex);
}
/*
* Send a read request for the given sector down the pipeline.
*/
static int
g_bde_start_read(struct g_bde_sector *sp)
{
struct bio *bp;
struct g_bde_softc *sc;
g_trace(G_T_TOPOLOGY, "g_bde_start_read(%p)", sp);
sc = sp->softc;
KASSERT(sc != NULL, ("Null softc in sp %p", sp));
bp = g_new_bio();
if (bp == NULL)
return (ENOMEM);
bp->bio_cmd = BIO_READ;
bp->bio_offset = sp->offset;
bp->bio_data = sp->data;
bp->bio_length = sp->size;
bp->bio_done = g_bde_read_done;
bp->bio_caller1 = sp;
bp->bio_caller2 = sc;
sp->state = IO;
g_io_request(bp, sc->consumer);
return(0);
}
/*
* The worker thread.
*
* The up/down path of GEOM is not allowed to sleep or do any major work
* so we use this thread to do the actual crypto operations and to push
* the state engine onwards.
*
* XXX: if we switch to the src/sys/opencrypt hardware assisted encryption
* XXX: using a thread here is probably not needed.
*/
void
g_bde_worker(void *arg)
{
struct g_bde_softc *sc;
struct g_bde_work *wp, *twp;
struct g_geom *gp;
int restart, error;
gp = arg;
sc = gp->softc;
mtx_lock(&sc->worklist_mutex);
for (;;) {
restart = 0;
g_trace(G_T_TOPOLOGY, "g_bde_worker scan");
TAILQ_FOREACH_SAFE(wp, &sc->worklist, list, twp) {
KASSERT(wp != NULL, ("NULL wp"));
KASSERT(wp->softc != NULL, ("NULL wp->softc"));
if (wp->state != WAIT)
continue; /* Not interesting here */
KASSERT(wp->bp != NULL, ("NULL wp->bp"));
KASSERT(wp->sp != NULL, ("NULL wp->sp"));
if (wp->ksp != NULL) {
if (wp->ksp->owner != wp)
continue;
if (wp->ksp->state == IO)
continue;
KASSERT(wp->ksp->state == VALID,
("Illegal sector state (%d)",
wp->ksp->state));
}
if (wp->bp->bio_cmd == BIO_READ && wp->sp->state == IO)
continue;
if (wp->ksp != NULL && wp->ksp->error != 0) {
g_bde_work_done(wp, wp->ksp->error);
continue;
}
switch(wp->bp->bio_cmd) {
case BIO_READ:
if (wp->ksp == NULL) {
KASSERT(wp->error != 0,
("BIO_READ, no ksp and no error"));
g_bde_work_done(wp, wp->error);
break;
}
if (wp->sp->error != 0) {
g_bde_work_done(wp, wp->sp->error);
break;
}
mtx_unlock(&sc->worklist_mutex);
g_bde_crypt_read(wp);
mtx_lock(&sc->worklist_mutex);
restart++;
g_bde_work_done(wp, wp->sp->error);
break;
case BIO_WRITE:
wp->state = FINISH;
KASSERT(wp->sp->owner == wp,
("Write not owner sp"));
KASSERT(wp->ksp->owner == wp,
("Write not owner ksp"));
mtx_unlock(&sc->worklist_mutex);
g_bde_crypt_write(wp);
mtx_lock(&sc->worklist_mutex);
restart++;
error = g_bde_start_write(wp->sp);
if (error) {
g_bde_work_done(wp, error);
break;
}
error = g_bde_start_write(wp->ksp);
if (wp->error != 0)
wp->error = error;
break;
case BIO_DELETE:
wp->state = FINISH;
mtx_unlock(&sc->worklist_mutex);
g_bde_crypt_delete(wp);
mtx_lock(&sc->worklist_mutex);
restart++;
g_bde_start_write(wp->sp);
break;
}
if (restart)
break;
}
if (!restart) {
/*
* We don't look for our death-warrant until we are
* idle. Shouldn't make a difference in practice.
*/
if (sc->dead)
break;
g_trace(G_T_TOPOLOGY, "g_bde_worker sleep");
error = msleep(sc, &sc->worklist_mutex,
PRIBIO, "-", hz);
if (error == EWOULDBLOCK) {
/*
* Lose our skey cache in an orderly fashion.
* The exact rate can be tuned to be less
* aggressive if this is desirable. 10% per
* second means that the cache is gone in a
* few minutes.
*/
g_bde_purge_sector(sc, 10);
}
}
}
g_trace(G_T_TOPOLOGY, "g_bde_worker die");
g_bde_purge_sector(sc, 1);
KASSERT(sc->nwork == 0, ("Dead but %d work remaining", sc->nwork));
KASSERT(sc->ncache == 0, ("Dead but %d cache remaining", sc->ncache));
KASSERT(sc->nsect == 0, ("Dead but %d sect remaining", sc->nsect));
mtx_unlock(&sc->worklist_mutex);
sc->dead = 2;
wakeup(sc);
kproc_exit(0);
}
/*
* g_bde_start1 has chopped the incoming request up so all the requests
* we see here are inside a single zone. Map the data and key locations
* grab the buffers we need and fire off the first volley of read requests.
*/
static void
g_bde_start2(struct g_bde_work *wp)
{
struct g_bde_softc *sc;
KASSERT(wp != NULL, ("NULL wp in g_bde_start2"));
KASSERT(wp->softc != NULL, ("NULL wp->softc"));
g_trace(G_T_TOPOLOGY, "g_bde_start2(%p)", wp);
sc = wp->softc;
switch (wp->bp->bio_cmd) {
case BIO_READ:
wp->sp = g_bde_new_sector(wp, 0);
if (wp->sp == NULL) {
g_bde_work_done(wp, ENOMEM);
return;
}
wp->sp->size = wp->length;
wp->sp->data = wp->data;
if (g_bde_start_read(wp->sp) != 0) {
g_bde_work_done(wp, ENOMEM);
return;
}
g_bde_read_keysector(sc, wp);
if (wp->ksp == NULL)
wp->error = ENOMEM;
break;
case BIO_DELETE:
wp->sp = g_bde_new_sector(wp, wp->length);
if (wp->sp == NULL) {
g_bde_work_done(wp, ENOMEM);
return;
}
break;
case BIO_WRITE:
wp->sp = g_bde_new_sector(wp, wp->length);
if (wp->sp == NULL) {
g_bde_work_done(wp, ENOMEM);
return;
}
g_bde_read_keysector(sc, wp);
if (wp->ksp == NULL) {
g_bde_work_done(wp, ENOMEM);
return;
}
break;
default:
KASSERT(0 == 1,
("Wrong bio_cmd %d in g_bde_start2", wp->bp->bio_cmd));
}
wp->state = WAIT;
wakeup(sc);
}
/*
* Create a sequence of work structures, and have g_bde_map_sector() determine
* how long they each can be. Feed them to g_bde_start2().
*/
void
g_bde_start1(struct bio *bp)
{
struct g_bde_softc *sc;
struct g_bde_work *wp;
off_t done;
sc = bp->bio_to->geom->softc;
bp->bio_driver1 = sc;
mtx_lock(&sc->worklist_mutex);
for(done = 0; done < bp->bio_length; ) {
wp = g_bde_new_work(sc);
if (wp != NULL) {
wp->bp = bp;
wp->offset = bp->bio_offset + done;
wp->data = bp->bio_data + done;
wp->length = bp->bio_length - done;
g_bde_map_sector(wp);
done += wp->length;
g_bde_start2(wp);
}
if (wp == NULL || bp->bio_error != 0) {
g_bde_contribute(bp, bp->bio_length - done, ENOMEM);
break;
}
}
mtx_unlock(&sc->worklist_mutex);
return;
}