diff --git a/sbin/geom/class/eli/Makefile b/sbin/geom/class/eli/Makefile new file mode 100644 index 000000000000..18f3869b6896 --- /dev/null +++ b/sbin/geom/class/eli/Makefile @@ -0,0 +1,17 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../misc ${.CURDIR}/../../../../sys/geom/eli ${.CURDIR}/../../../../sys/crypto/sha2 + +CLASS= eli +SRCS= g_eli_crypto.c +SRCS+= g_eli_key.c +SRCS+= pkcs5v2.c +SRCS+= sha2.c + +DPADD= ${LIBMD} ${LIBCRYPTO} +LDADD= -lmd -lcrypto + +NO_MAN= +CFLAGS+=-I${.CURDIR}/../../../../sys + +.include diff --git a/sbin/geom/class/eli/geli.8 b/sbin/geom/class/eli/geli.8 new file mode 100644 index 000000000000..edf11e743020 --- /dev/null +++ b/sbin/geom/class/eli/geli.8 @@ -0,0 +1,511 @@ +.\" Copyright (c) 2005 Pawel Jakub Dawidek +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd April 11, 2005 +.Dt GELI 8 +.Os +.Sh NAME +.Nm geli +.Nd "control utility for cryptographic GEOM class" +.Sh SYNOPSIS +To compile GEOM_ELI into your kernel, place the following lines in your kernel +configuration file: +.Bd -ragged -offset indent +.Cd "device crypto" +.Cd "device cryptodev" +.Cd "options GEOM_ELI" +.Ed +.Pp +Alternately, to load the GEOM_ELI module at boot time, place the following line +in your +.Xr loader.conf 5 : +.Bd -literal -offset indent +geom_eli_load="YES" +.Ed +.Pp +Usage of the +.Xr geli 8 +utility: +.Pp +.Nm +.Cm init +.Op Fl bPv +.Op Fl a Ar algo +.Op Fl i Ar iterations +.Op Fl K Ar newkeyfile +.Op Fl l Ar keylen +.Op Fl s Ar sectorsize +.Ar prov +.Nm +.Cm label - an alias for +.Cm init +.Nm +.Cm attach +.Op Fl dpv +.Op Fl k Ar keyfile +.Ar prov +.Nm +.Cm detach +.Op Fl fl +.Ar prov ... +.Nm +.Cm stop - an alias for +.Cm detach +.Nm +.Cm onetime +.Op Fl d +.Op Fl a Ar algo +.Op Fl l Ar keylen +.Op Fl s Ar sectorsize +.Ar prov ... +.Nm +.Cm setkey +.Op Fl pPv +.Op Fl k Ar keyfile +.Op Fl K Ar newkeyfile +.Op Fl n Ar keyno +.Ar prov +.Nm +.Cm delkey +.Op Fl afv +.Op Fl n Ar keyno +.Ar prov +.Nm +.Cm kill +.Op Fl av +.Op Ar prov ... +.Nm +.Cm backup +.Op Fl v +.Ar prov +.Ar file +.Nm +.Cm restore +.Op Fl v +.Ar file +.Ar prov +.Nm +.Cm clear +.Op Fl v +.Ar prov ... +.Nm +.Cm dump +.Op Fl v +.Ar prov ... +.Nm +.Cm list +.Nm +.Cm status +.Nm +.Cm load +.Nm +.Cm unload +.Sh DESCRIPTION +The +.Nm +utility is used to configure encryption on GEOM providers. +.Pp +Here is the list of the most important features: +.Pp +.Bl -bullet -offset indent -compact +.It +Utilize the +.Xr crypto 9 +framework, so when there is a crypto hardware available, +.Nm +will make use of it automatically. +If cryptography needs to be done in software, +a dedicated kernel thread will be started to do the crypto work in there. +.It +Supports many cryptographic algorithms (currently +.Nm AES , +.Nm Blowfish +and +.Nm 3DES ) . +.It +Can create a key from a couple of components (user entered passphrase, random +bits from a file, etc.). +.It +Allows to encrypt root partition - user will be asked for the passphrase before +root file system is mounted. +.It +User's passphrase is strengthen with: +.Rs +.%A B. Kaliski +.%T "PKCS #5: Password-Based Cryptography Specification, Version 2.0." +.%R RFC +.%N 2898 +.Re +.It +Allows to use two independent keys (e.g. +.Qq "user key" +and +.Qq "company key" ) . +.It +It is fast - +.Nm +performs simple sector-to-sector encryption. +.It +Allows to backup/restore Master Keys, so when user have to quickly destroy keys, +it is able to get the data back by restoring keys from the backup. +.It +Provider can be configured to automatically detach on last close (so user don't +have to remember to detach provider after unmounting file system). +.It +Allows to attach provider with a random, one-time keys - useful for swap +partitions and temporary file systems. +.El +.Pp +The first argument to +.Nm +indicates an action to be performed: +.Bl -tag -width ".Cm onetime" +.It Cm init +Initialize provider which needs to be encrypted. +Here you can setup cryptographic algorithm to use, key length, etc. +The last provider's sector is used to store metadata. +.Pp +Additional options include: +.Bl -tag -width ".Fl a Ar algo" +.It Fl a Ar algo +Encryption algorithm to use. +Currently supported algorithms are: +.Nm AES , +.Nm Blowfish +and +.Nm 3DES . +The default is +.Nm AES . +.It Fl b +Ask for the passphrase on boot, before root partition is mounted. +This allows to use encrypted root partition. +One will still need bootable unencrypted storage with +.Pa /boot/ +directory, which can be a CD-ROM disc or USB pen-drive, which can be removed +after boot. +.It Fl i Ar iterations +Number of iterations to use with PKCS#5v2. +If this option is not specified +.Nm +will find the number of iterations which is equal to 2 seconds of crypto work. +If 0 is given, PKCS#5v2 will not be used. +.It Fl K Ar newkeyfile +Specifies a file which contains part of the key. +If +.Ar newkeyfile +is given as -, standard input will be used. +Here is how more than one file with the key component can be used: +.Bd -literal -offset indent +# cat key1 key2 key3 | geli init -K - /dev/da0 +.Ed +.It Fl l Ar keylen +Key length to use with the given cryptographic algorithm. +If not given, the default key length for the given algorithm is used, which is: +128 for +.Nm AES , +128 for +.Nm Blowfish +and 192 for +.Nm 3DES . +.It Fl s Ar sectorsize +Change decrypted provider's sector size. +Increasing sector size allows to increase performance, because we need to +generate IV and do encrypt/decrypt for every single sector - less number +of sectors means less work to do. +.It Fl P +Do not use passphrase as the key component. +.El +.It Cm attach +Attach the given provider. The master key will be decrypted using the given +passphrase/keyfile and a new GEOM provider will be created using the given +provider's name with an +.Qq .eli +suffix. +.Pp +Additional options include: +.Bl -tag -width ".Fl a Ar algo" +.It Fl d +If specified, decrypted provider will be detached automatically on last close. +This can help with short memory - user doesn't have to remember to detach +provider after unmounting file system. +It only works when provider was opened for writing, so it will not work if +file system on the provider is mounted read-only. +Probably better choice is the +.Fl l +option for the +.Cm detach +subcommand. +.It Fl k Ar keyfile +Specifies a file which contains part of the key. +For more information see description of +.Fl K +option for the +.Cm init +subcommand. +.It Fl p +Do not use passphrase as the key component. +.El +.It Cm detach +Detach the given providers, which means remove devfs entry and clear the keys +from memory. +.Pp +Additional options include: +.Bl -tag -width ".Fl a Ar algo" +.It Fl f +Force detach - detach even if provider is open. +.It Fl l +Mark provider to detach on last close. +If this option is specified provider will not be detached until it is open, +but when it will be closed last time, it will be automatically detached (even +if it was only opened for reading). +.El +.It Cm onetime +Attach the given providers with a random, one-time keys. +The command can be used to encrypt swap partitions or temporary file systems. +.Pp +Additional options include: +.Bl -tag -width ".Fl a Ar algo" +.It Fl a Ar algo +Encryption algorithm to use. +For more information see description of the +.Cm init +subcommand. +.It Fl d +Detach on last close. +Note, the option is not usable for temporary file system, because provider will +be detached after creating file system on it. +It still can (and should be) used for swap partitions. +For more information see description of the +.Cm attach +subcommand. +.It Fl l Ar keylen +Key length to use with the given cryptographic algorithm. +For more information see description of the +.Cm init +subcommand. +.It Fl s Ar sectorsize +Change decrypted provider's sector size. +For more information see description of the +.Cm init +subcommand. +.El +.It Cm setkey +Change or setup (if not yet initialized) selected key. +There is one master key, which can be encrypted with two independent user keys. +With the +.Cm init +subcommand only key number 0 is initialized. +The key can be always changed: for attached provider, for detached provider or +on the backup file. +When provider is attached, user don't have to provide an old passphrase/keyfile. +.Pp +Additional options include: +.Bl -tag -width ".Fl a Ar algo" +.It Fl k Ar keyfile +Specifies a file which contains part of the old key. +.It Fl K Ar newkeyfile +Specifies a file which contains part of the new key. +.It Fl n Ar keyno +Specifies number of the key to change (could be 0 or 1). +If provider is attached and no key number is given, the key used for attaching +provider will be changed. +If provider is detached (or we're operating on a backup file) and no key number +is given, the key decrypted with passphrase/keyfile will be changed. +.It Fl p +Do not use passphrase as the old key component. +.It Fl P +Do not use passphrase as the new key component. +.El +.It Cm delkey +Destroy (overwrite with random data) selected key. +If one is destroying keys for an attached provider, provider won't be detached +even if all keys will be destroyed. +It can be even rescued with the +.Cm setkey +subcommand. +.Bl -tag -width ".Fl a Ar algo" +.It Fl a +Destroy all keys (doesn't need +.Fl f +option). +.It Fl f +Force key destruction. This option is needed to destroy the last key. +.It Fl n Ar keyno +Specifies the key number. +If provider is attached and no key number is given, the key used for attaching +provider will be destroyed. +If provider is detached (or we're operating on a backup file) the key number +has to be given. +.El +.It Cm kill +The command should be used in emergency situations. +It will destroy all keys on the given provider and will detach it forcibly +(if it is attached). +This is absolutely one-way command - if you don't have metadata backup, your data +is gone for good. +.Bl -tag -width ".Fl a Ar algo" +.It Fl a +If specified, all currently attached providers will be killed. +.El +.It Cm backup +Backup metadata from the given provider to the given file. +.It Cm restore +Restore metadata from the given file to the given provider. +.It Cm clear +Clear metadata from the given providers. +.It Cm dump +Dump metadata stored on the given providers. +.It Cm list +See +.Xr geom 8 . +.It Cm status +See +.Xr geom 8 . +.It Cm load +See +.Xr geom 8 . +.It Cm unload +See +.Xr geom 8 . +.El +.Pp +Additional options include: +.Bl -tag -width ".Fl v" +.It Fl v +Be more verbose. +.El +.Sh SYSCTL VARIABLES +The following +.Xr sysctl 8 +variables can be used to control the behavior of the +.Nm ELI +GEOM class. +The default value is shown next to each variable. +.Bl -tag -width indent +.It Va kern.geom.eli.debug : No 0 +Debug level of the +.Nm ELI +GEOM class. +This can be set to a number between 0 and 3 inclusive. +If set to 0 minimal debug information is printed, and if set to 3 the +maximum amount of debug information is printed. +This variable could be set in +.Pa /boot/loader.conf . +.It Va kern.geom.eli.tries : No 3 +Number of times user is asked for the passphrase. +This is only used for providers which should be attached on boot (before root +file system is mounted). +If set to 0, attaching providers on boot will be disabled. +This variable should be set in +.Pa /boot/loader.conf . +.It Va kern.geom.eli.overwrites : No 5 +Specifies how many times Master-Key will be overwriten with random values when +it is destroyed. After this operation it is filled with zeros. +.It Va kern.geom.eli.visible_passphrase : No 0 +If set to 1, passphrase entered on boot (before root file system is mounted) +will be visible. +This possibility should be used with caution as entered passphrase can be logged +and exposed via +.Xr dmesg 8 . +This variable should be set in +.Pa /boot/loader.conf . +.It Va kern.geom.eli.threads : No 1 +Specifies how many kernel threads should be used for doing software +cryptography. +It's purpose is to increase performance on SMP systems. +This variable could be set in +.Pa /boot/loader.conf . +.El +.Sh EXIT STATUS +Exit status is 0 on success, and 1 if the command fails. +.Sh EXAMPLES +Initialize provider which is going to be encrypted with a passphrase and random +data from a file on the user's pen drive. +Use 4kB sector size. +Attach the provider, create a file system and mount it. +Do the work. +Unmount provider and detach it: +.Bd -literal -offset indent +# dd if=/dev/random of=/mnt/pendrive/da2.key bs=64 count=1 +# geli init -s 4096 -K /mnt/pendrive/da2.key /dev/da2 +Enter new passphrase: +Reenter new passphrase: +# geli attach -k /mnt/pendrive/da2.key /dev/da2 +Enter passphrase: +# dd if=/dev/random of=/dev/da2.eli bs=1m +# newfs /dev/da2.eli +# mount /dev/da2.eli /mnt/secret +\&... +# umount /mnt/secret +# geli detach da2.eli +.Ed +.Pp +Create encrypted provider, but use two key: one for your girlfriend and one for +you (so there will be no tragedy if she forget her passphrase): +.Bd -literal -offset indent +# geli init /dev/da2 +Enter new passphrase: (enter your passphrase) +Reenter new passphrase: +# geli setkey -n 1 /dev/da2 +Enter passphrase: (enter your passphrase) +Enter new passphrase: (let your girlfriend to enter her passphrase ...) +Reenter new passphrase: (... twice) +.Ed +.Pp +You are security-person in your company. +Create encrypted provider for use by the user, but remember that users forget +their passphrases, so backup Master Key with your own random key: +.Bd -literal -offset indent +# dd if=/dev/random of=/mnt/pendrive/keys/`hostname` bs=64 count=1 +# geli init -P -K /mnt/pendrive/keys/`hostname` /dev/ad0s1e +# geli backup /dev/ad0s1e /mnt/pendrive/backups/`hostname` +(use key number 0, so encrypted Master Key by you will be overwriten) +# geli setkey -n 0 -k /mnt/pendrive/keys/`hostname` /dev/ad0s1e +(allow the user to enter his passphrase) +Enter new passphrase: +Reenter new passphrase: +.Ed +.Pp +Encrypted swap partition setup: +.Bd -literal -offset indent +# dd if=/dev/random of=/dev/ad0s1b bs=1m +# geli onetime -d -a 3des ad0s1b +# swapon /dev/ad0s1b.eli +.Ed +.Sh SEE ALSO +.Xr crypto 4 , +.Xr crypto 9 , +.Xr gbde 4 , +.Xr gbde 8 , +.Xr geom 4 , +.Xr geom 8 +.Sh HISTORY +The +.Nm +utility appeared in +.Fx 5.5 . +.Sh AUTHORS +.An Pawel Jakub Dawidek Aq pjd@FreeBSD.org diff --git a/sbin/geom/class/eli/geom_eli.c b/sbin/geom/class/eli/geom_eli.c new file mode 100644 index 000000000000..f01f55719c83 --- /dev/null +++ b/sbin/geom/class/eli/geom_eli.c @@ -0,0 +1,1246 @@ +/*- + * Copyright (c) 2004 Pawel Jakub Dawidek + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "core/geom.h" +#include "misc/subr.h" + + +uint32_t lib_version = G_LIB_VERSION; +uint32_t version = G_ELI_VERSION; + +static char algo[] = "aes"; +static intmax_t keylen = 0; +static intmax_t keyno = -1; +static intmax_t iterations = -1; +static intmax_t sectorsize = 0; +static char keyfile[] = "", newkeyfile[] = ""; + +static void eli_main(struct gctl_req *req, unsigned flags); +static void eli_init(struct gctl_req *req); +static void eli_attach(struct gctl_req *req); +static void eli_setkey(struct gctl_req *req); +static void eli_delkey(struct gctl_req *req); +static void eli_kill(struct gctl_req *req); +static void eli_backup(struct gctl_req *req); +static void eli_restore(struct gctl_req *req); +static void eli_clear(struct gctl_req *req); +static void eli_dump(struct gctl_req *req); + +/* + * Available commands: + * + * init [-bhPv] [-a algo] [-i iterations] [-l keylen] [-K newkeyfile] prov + * label - alias for 'init' + * attach [-dpv] [-k keyfile] prov + * detach [-fl] prov ... + * stop - alias for 'detach' + * onetime [-d] [-a algo] [-l keylen] prov ... + * setkey [-pPv] [-n keyno] [-k keyfile] [-K newkeyfile] prov + * delkey [-afv] [-n keyno] prov + * kill [-av] [prov ...] + * backup [-v] prov file + * restore [-v] file prov + * clear [-v] prov ... + * dump [-v] prov ... + */ +struct g_command class_commands[] = { + { "init", G_FLAG_VERBOSE, eli_main, + { + { 'a', "algo", algo, G_TYPE_STRING }, + { 'b', "boot", NULL, G_TYPE_NONE }, + { 'i', "iterations", &iterations, G_TYPE_NUMBER }, + { 'K', "newkeyfile", newkeyfile, G_TYPE_STRING }, + { 'l', "keylen", &keylen, G_TYPE_NUMBER }, + { 'P', "nonewpassphrase", NULL, G_TYPE_NONE }, + { 's', "sectorsize", §orsize, G_TYPE_NUMBER }, + G_OPT_SENTINEL + }, + "[-bPv] [-a algo] [-i iterations] [-l keylen] [-K newkeyfile] [-s sectorsize] prov" + }, + { "label", G_FLAG_VERBOSE, eli_main, + { + { 'a', "algo", algo, G_TYPE_STRING }, + { 'b', "boot", NULL, G_TYPE_NONE }, + { 'i', "iterations", &iterations, G_TYPE_NUMBER }, + { 'K', "newkeyfile", newkeyfile, G_TYPE_STRING }, + { 'l', "keylen", &keylen, G_TYPE_NUMBER }, + { 'P', "nonewpassphrase", NULL, G_TYPE_NONE }, + { 's', "sectorsize", §orsize, G_TYPE_NUMBER }, + G_OPT_SENTINEL + }, + "- an alias for 'init'" + }, + { "attach", G_FLAG_VERBOSE | G_FLAG_LOADKLD, eli_main, + { + { 'd', "detach", NULL, G_TYPE_NONE }, + { 'k', "keyfile", keyfile, G_TYPE_STRING }, + { 'p', "nopassphrase", NULL, G_TYPE_NONE }, + G_OPT_SENTINEL + }, + "[-dpv] [-k keyfile] prov" + }, + { "detach", 0, NULL, + { + { 'f', "force", NULL, G_TYPE_NONE }, + { 'l', "last", NULL, G_TYPE_NONE }, + G_OPT_SENTINEL + }, + "[-fl] prov ..." + }, + { "stop", 0, NULL, + { + { 'f', "force", NULL, G_TYPE_NONE }, + { 'l', "last", NULL, G_TYPE_NONE }, + G_OPT_SENTINEL + }, + "- an alias for 'detach'" + }, + { "onetime", G_FLAG_VERBOSE | G_FLAG_LOADKLD, NULL, + { + { 'a', "algo", algo, G_TYPE_STRING }, + { 'd', "detach", NULL, G_TYPE_NONE }, + { 'l', "keylen", &keylen, G_TYPE_NUMBER }, + { 's', "sectorsize", §orsize, G_TYPE_NUMBER }, + G_OPT_SENTINEL + }, + "[-d] [-a algo] [-l keylen] [-s sectorsize] prov ..." + }, + { "setkey", G_FLAG_VERBOSE, eli_main, + { + { 'k', "keyfile", keyfile, G_TYPE_STRING }, + { 'K', "newkeyfile", newkeyfile, G_TYPE_STRING }, + { 'n', "keyno", &keyno, G_TYPE_NUMBER }, + { 'p', "nopassphrase", NULL, G_TYPE_NONE }, + { 'P', "nonewpassphrase", NULL, G_TYPE_NONE }, + G_OPT_SENTINEL + }, + "[-pPv] [-n keyno] [-k keyfile] [-K newkeyfile] prov" + }, + { "delkey", G_FLAG_VERBOSE, eli_main, + { + { 'a', "all", NULL, G_TYPE_NONE }, + { 'f', "force", NULL, G_TYPE_NONE }, + { 'n', "keyno", &keyno, G_TYPE_NUMBER }, + G_OPT_SENTINEL + }, + "[-afv] [-n keyno] prov" + }, + { "kill", G_FLAG_VERBOSE, eli_main, + { + { 'a', "all", NULL, G_TYPE_NONE }, + G_OPT_SENTINEL + }, + "[-av] [prov ...]" + }, + { "backup", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS, + "[-v] prov file" + }, + { "restore", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS, + "[-v] file prov" + }, + { "clear", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS, + "[-v] prov ..." + }, + { "dump", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS, + "[-v] prov ..." + }, + G_CMD_SENTINEL +}; + +static int verbose = 0; + +static int +eli_protect(struct gctl_req *req) +{ + struct rlimit rl; + + /* Disable core dumps. */ + rl.rlim_cur = 0; + rl.rlim_max = 0; + if (setrlimit(RLIMIT_CORE, &rl) == -1) { + gctl_error(req, "Cannot disable core dumps: %s.", + strerror(errno)); + return (-1); + } + /* Disable swapping. */ + if (mlockall(MCL_FUTURE) == -1) { + gctl_error(req, "Cannot lock memory: %s.", strerror(errno)); + return (-1); + } + return (0); +} + +static void +eli_main(struct gctl_req *req, unsigned flags) +{ + const char *name; + + if (eli_protect(req) == -1) + return; + + if ((flags & G_FLAG_VERBOSE) != 0) + verbose = 1; + + name = gctl_get_asciiparam(req, "verb"); + if (name == NULL) { + gctl_error(req, "No '%s' argument.", "verb"); + return; + } + if (strcmp(name, "init") == 0 || strcmp(name, "label") == 0) + eli_init(req); + else if (strcmp(name, "attach") == 0) + eli_attach(req); + else if (strcmp(name, "setkey") == 0) + eli_setkey(req); + else if (strcmp(name, "delkey") == 0) + eli_delkey(req); + else if (strcmp(name, "kill") == 0) + eli_kill(req); + else if (strcmp(name, "backup") == 0) + eli_backup(req); + else if (strcmp(name, "restore") == 0) + eli_restore(req); + else if (strcmp(name, "dump") == 0) + eli_dump(req); + else if (strcmp(name, "clear") == 0) + eli_clear(req); + else + gctl_error(req, "Unknown command: %s.", name); +} + +static void +arc4rand(unsigned char *buf, size_t size) +{ + uint32_t *buf4; + size_t size4; + unsigned i; + + buf4 = (uint32_t *)buf; + size4 = size / 4; + + for (i = 0; i < size4; i++) + buf4[i] = arc4random(); + for (i *= 4; i < size; i++) + buf[i] = arc4random() % 0xff; +} + +static int +eli_is_attached(const char *prov) +{ + char name[MAXPATHLEN]; + unsigned secsize; + + /* + * Not the best way to do it, but the easiest. + * We try to open provider and check if it is a GEOM provider + * by asking about its sectorsize. + */ + snprintf(name, sizeof(name), "%s%s", prov, G_ELI_SUFFIX); + secsize = g_get_sectorsize(name); + if (secsize > 0) + return (1); + return (0); +} + +static unsigned char * +eli_genkey(struct gctl_req *req, struct g_eli_metadata *md, unsigned char *key, + int new) +{ + struct hmac_ctx ctx; + const char *str; + int *nopassphrase; + int error; + + nopassphrase = gctl_get_paraml(req, + new ? "nonewpassphrase" : "nopassphrase", sizeof(*nopassphrase)); + if (nopassphrase == NULL) { + gctl_error(req, "No '%s' argument.", + new ? "nonewpassphrase" : "nopassphrase"); + return (NULL); + } + + g_eli_crypto_hmac_init(&ctx, NULL, 0); + + str = gctl_get_asciiparam(req, new ? "newkeyfile" : "keyfile"); + if (str == NULL) { + gctl_error(req, "No '%s' argument.", + new ? "newkeyfile" : "keyfile"); + return (NULL); + } + if (str[0] != '\0') { + char buf[MAXPHYS]; + ssize_t done; + int fd; + + if (strcmp(str, "-") == 0) + fd = STDIN_FILENO; + else { + fd = open(str, O_RDONLY); + if (fd == -1) { + gctl_error(req, "Cannot open keyfile %s: %s.", + str, strerror(errno)); + return (NULL); + } + } + while ((done = read(fd, buf, sizeof(buf))) > 0) + g_eli_crypto_hmac_update(&ctx, buf, done); + error = errno; + if (strcmp(str, "-") != 0) + close(fd); + bzero(buf, sizeof(buf)); + if (done == -1) { + gctl_error(req, "Cannot read keyfile %s: %s.", str, + strerror(error)); + return (NULL); + } + } + + if (!*nopassphrase) { + char buf1[BUFSIZ], buf2[BUFSIZ], *p; + + for (;;) { + p = readpassphrase( + new ? "Enter new passphrase:" : "Enter passphrase:", + buf1, sizeof(buf1), RPP_ECHO_OFF | RPP_REQUIRE_TTY); + if (p == NULL) { + bzero(buf1, sizeof(buf1)); + gctl_error(req, "Cannot read passphrase: %s.", + strerror(errno)); + return (NULL); + } + + if (new) { + p = readpassphrase("Reenter new passphrase: ", + buf2, sizeof(buf2), + RPP_ECHO_OFF | RPP_REQUIRE_TTY); + if (p == NULL) { + bzero(buf1, sizeof(buf1)); + gctl_error(req, + "Cannot read passphrase: %s.", + strerror(errno)); + return (NULL); + } + + if (strcmp(buf1, buf2) != 0) { + bzero(buf2, sizeof(buf2)); + fprintf(stderr, "They didn't match.\n"); + continue; + } + bzero(buf2, sizeof(buf2)); + } + break; + } + /* + * Field md_iterations equal to -1 means "choose some sane + * value for me". + */ + if (md->md_iterations == -1) { + assert(new); + if (verbose) + printf("Calculating number of iterations...\n"); + md->md_iterations = pkcs5v2_calculate(2000000); + assert(md->md_iterations > 0); + if (verbose) { + printf("Done, using %d iterations.\n", + md->md_iterations); + } + } + /* + * If md_iterations is equal to 0, user don't want PKCS5v2. + */ + if (md->md_iterations == 0) { + g_eli_crypto_hmac_update(&ctx, md->md_salt, + sizeof(md->md_salt)); + g_eli_crypto_hmac_update(&ctx, buf1, strlen(buf1)); + } else /* if (md->md_iterations > 0) */ { + unsigned char dkey[G_ELI_USERKEYLEN]; + + pkcs5v2_genkey(dkey, sizeof(dkey), md->md_salt, + sizeof(md->md_salt), buf1, md->md_iterations); + g_eli_crypto_hmac_update(&ctx, dkey, sizeof(dkey)); + bzero(dkey, sizeof(dkey)); + } + bzero(buf1, sizeof(buf1)); + } + g_eli_crypto_hmac_final(&ctx, key, 0); + return (key); +} + +static int +eli_metadata_read(struct gctl_req *req, const char *prov, + struct g_eli_metadata *md) +{ + unsigned char sector[sizeof(struct g_eli_metadata)]; + int error; + + if (g_get_sectorsize(prov) == 0) { + int fd; + + /* This is a file probably. */ + fd = open(prov, O_RDONLY); + if (fd == -1) { + gctl_error(req, "Cannot open %s: %s.", prov, + strerror(errno)); + return (-1); + } + if (read(fd, sector, sizeof(sector)) != sizeof(sector)) { + gctl_error(req, "Cannot read metadata from %s: %s.", + prov, strerror(errno)); + close(fd); + return (-1); + } + close(fd); + } else { + /* This is a GEOM provider. */ + error = g_metadata_read(prov, sector, sizeof(sector), + G_ELI_MAGIC); + if (error != 0) { + gctl_error(req, "Cannot read metadata from %s: %s.", + prov, strerror(error)); + return (-1); + } + } + if (eli_metadata_decode(sector, md) != 0) { + gctl_error(req, "MD5 hash mismatch for %s.", prov); + return (-1); + } + return (0); +} + +static int +eli_metadata_store(struct gctl_req *req, const char *prov, + struct g_eli_metadata *md) +{ + unsigned char sector[sizeof(struct g_eli_metadata)]; + int error; + + eli_metadata_encode(md, sector); + if (g_get_sectorsize(prov) == 0) { + int fd; + + /* This is a file probably. */ + fd = open(prov, O_WRONLY | O_TRUNC); + if (fd == -1) { + gctl_error(req, "Cannot open %s: %s.", prov, + strerror(errno)); + bzero(sector, sizeof(sector)); + return (-1); + } + if (write(fd, sector, sizeof(sector)) != sizeof(sector)) { + gctl_error(req, "Cannot write metadata to %s: %s.", + prov, strerror(errno)); + bzero(sector, sizeof(sector)); + close(fd); + return (-1); + } + close(fd); + } else { + /* This is a GEOM provider. */ + error = g_metadata_store(prov, sector, sizeof(sector)); + if (error != 0) { + gctl_error(req, "Cannot write metadata to %s: %s.", + prov, strerror(errno)); + bzero(sector, sizeof(sector)); + return (-1); + } + } + bzero(sector, sizeof(sector)); + return (0); +} + +static void +eli_init(struct gctl_req *req) +{ + struct g_eli_metadata md; + unsigned char sector[sizeof(struct g_eli_metadata)]; + unsigned char key[G_ELI_USERKEYLEN]; + const char *str, *prov; + int *nargs, *boot; + unsigned secsize; + off_t mediasize; + intmax_t *valp; + int error; + + nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); + if (nargs == NULL) { + gctl_error(req, "No '%s' argument.", "nargs"); + return; + } + if (*nargs != 1) { + gctl_error(req, "Too few arguments."); + return; + } + prov = gctl_get_asciiparam(req, "arg0"); + if (prov == NULL) { + gctl_error(req, "No 'arg%u' argument.", 0); + return; + } + mediasize = g_get_mediasize(prov); + secsize = g_get_sectorsize(prov); + if (mediasize == 0 || secsize == 0) { + gctl_error(req, "Cannot get informations about %s: %s.", prov, + strerror(errno)); + return; + } + + bzero(&md, sizeof(md)); + strlcpy(md.md_magic, G_ELI_MAGIC, sizeof(md.md_magic)); + md.md_version = G_ELI_VERSION; + md.md_flags = 0; + boot = gctl_get_paraml(req, "boot", sizeof(*boot)); + if (boot == NULL) { + gctl_error(req, "No '%s' argument.", "boot"); + return; + } + if (*boot) { + int *nonewpassphrase; + + /* Part of key cannot be read on boot from a file. */ + str = gctl_get_asciiparam(req, "newkeyfile"); + if (str == NULL) { + gctl_error(req, "No '%s' argument.", "newkeyfile"); + return; + } + if (str[0] != '\0') { + gctl_error(req, + "Options -b and -K are mutually exclusive."); + return; + } + /* Key has to be given as a passphrase on boot. */ + nonewpassphrase = gctl_get_paraml(req, "nonewpassphrase", + sizeof(*nonewpassphrase)); + if (nonewpassphrase == NULL) { + gctl_error(req, "No '%s' argument.", "nonewpassphrase"); + return; + } + if (*nonewpassphrase) { + gctl_error(req, + "Options -b and -P are mutually exclusive."); + return; + } + md.md_flags |= G_ELI_FLAG_BOOT; + } + str = gctl_get_asciiparam(req, "algo"); + if (str == NULL) { + gctl_error(req, "No '%s' argument.", "algo"); + return; + } + md.md_algo = g_eli_str2algo(str); + if (md.md_algo < CRYPTO_ALGORITHM_MIN || + md.md_algo > CRYPTO_ALGORITHM_MAX) { + gctl_error(req, "Invalid encryption algorithm."); + return; + } + valp = gctl_get_paraml(req, "keylen", sizeof(*valp)); + if (valp == NULL) { + gctl_error(req, "No '%s' argument.", "keylen"); + return; + } + md.md_keylen = *valp; + md.md_keylen = g_eli_keylen(md.md_algo, md.md_keylen); + if (md.md_keylen == 0) { + gctl_error(req, "Invalid key length."); + return; + } + md.md_provsize = mediasize; + + valp = gctl_get_paraml(req, "iterations", sizeof(*valp)); + if (valp == NULL) { + gctl_error(req, "No '%s' argument.", "iterations"); + return; + } + md.md_iterations = *valp; + + valp = gctl_get_paraml(req, "sectorsize", sizeof(*valp)); + if (valp == NULL) { + gctl_error(req, "No '%s' argument.", "sectorsize"); + return; + } + if (*valp == 0) + md.md_sectorsize = secsize; + else { + if (*valp < 0 || (*valp % secsize) != 0) { + gctl_error(req, "Invalid sector size."); + return; + } + md.md_sectorsize = *valp; + } + + md.md_keys = 0x01; + arc4rand(md.md_salt, sizeof(md.md_salt)); + arc4rand(md.md_mkeys, sizeof(md.md_mkeys)); + + /* Generate user key. */ + if (eli_genkey(req, &md, key, 1) == NULL) { + bzero(key, sizeof(key)); + bzero(&md, sizeof(md)); + return; + } + + /* Encrypt the first and the only Master Key. */ + error = g_eli_mkey_encrypt(md.md_algo, key, md.md_keylen, md.md_mkeys); + bzero(key, sizeof(key)); + if (error != 0) { + bzero(&md, sizeof(md)); + gctl_error(req, "Cannot encrypt Master Key: %s.", + strerror(error)); + return; + } + + eli_metadata_encode(&md, sector); + bzero(&md, sizeof(md)); + error = g_metadata_store(prov, sector, sizeof(sector)); + bzero(sector, sizeof(sector)); + if (error != 0) { + gctl_error(req, "Cannot store metadata on %s: %s.", prov, + strerror(error)); + return; + } + if (verbose) + printf("Metadata value stored on %s.\n", prov); +} + +static void +eli_attach(struct gctl_req *req) +{ + struct g_eli_metadata md; + unsigned char key[G_ELI_USERKEYLEN]; + const char *prov; + int *nargs; + + nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); + if (nargs == NULL) { + gctl_error(req, "No '%s' argument.", "nargs"); + return; + } + if (*nargs != 1) { + gctl_error(req, "Too few arguments."); + return; + } + prov = gctl_get_asciiparam(req, "arg0"); + if (prov == NULL) { + gctl_error(req, "No 'arg%u' argument.", 0); + return; + } + + if (eli_metadata_read(req, prov, &md) == -1) + return; + + if (eli_genkey(req, &md, key, 0) == NULL) { + bzero(key, sizeof(key)); + return; + } + + gctl_ro_param(req, "key", sizeof(key), key); + if (gctl_issue(req) == NULL) { + if (verbose) + printf("Attched to %s.\n", prov); + } + bzero(key, sizeof(key)); +} + +static void +eli_setkey_attached(struct gctl_req *req, const char *prov) +{ + struct g_eli_metadata md; + unsigned char key[G_ELI_USERKEYLEN]; + + if (eli_metadata_read(req, prov, &md) == -1) + return; + + /* Generate key for Master Key encryption. */ + if (eli_genkey(req, &md, key, 1) == NULL) { + bzero(key, sizeof(key)); + return; + } + + gctl_ro_param(req, "key", sizeof(key), key); + gctl_issue(req); + bzero(key, sizeof(key)); +} + +static void +eli_setkey_detached(struct gctl_req *req, const char *prov) +{ + struct g_eli_metadata md; + unsigned char key[G_ELI_USERKEYLEN], mkey[G_ELI_DATAIVKEYLEN]; + unsigned char *mkeydst; + intmax_t *valp; + unsigned nkey; + int error; + + if (eli_metadata_read(req, prov, &md) == -1) + return; + + /* Generate key for Master Key decryption. */ + if (eli_genkey(req, &md, key, 0) == NULL) { + bzero(key, sizeof(key)); + return; + } + + /* Decrypt Master Key. */ + error = g_eli_mkey_decrypt(&md, key, mkey, &nkey); + bzero(key, sizeof(key)); + if (error != 0) { + bzero(&md, sizeof(md)); + if (error == -1) + gctl_error(req, "Wrong key for %s.", prov); + else /* if (error > 0) */ { + gctl_error(req, "Cannot decrypt Master Key: %s.", + strerror(error)); + } + return; + } + if (verbose) + printf("Decrypted Master Key %u.\n", nkey); + + valp = gctl_get_paraml(req, "keyno", sizeof(*valp)); + if (valp == NULL) { + gctl_error(req, "No '%s' argument.", "keyno"); + return; + } + if (*valp != -1) + nkey = *valp; +#if 0 + else + ; /* Use the key number which was found during decryption. */ +#endif + if (nkey >= G_ELI_MAXMKEYS) { + gctl_error(req, "Invalid '%s' argument.", "keyno"); + return; + } + + mkeydst = md.md_mkeys + nkey * G_ELI_MKEYLEN; + md.md_keys |= (1 << nkey); + + bcopy(mkey, mkeydst, sizeof(mkey)); + bzero(mkey, sizeof(mkey)); + + /* Generate key for Master Key encryption. */ + if (eli_genkey(req, &md, key, 1) == NULL) { + bzero(key, sizeof(key)); + bzero(&md, sizeof(md)); + return; + } + + /* Encrypt the Master-Key with the new key. */ + error = g_eli_mkey_encrypt(md.md_algo, key, md.md_keylen, mkeydst); + bzero(key, sizeof(key)); + if (error != 0) { + bzero(&md, sizeof(md)); + gctl_error(req, "Cannot encrypt Master Key: %s.", + strerror(error)); + return; + } + + /* Store metadata with fresh key. */ + eli_metadata_store(req, prov, &md); + bzero(&md, sizeof(md)); +} + +static void +eli_setkey(struct gctl_req *req) +{ + const char *prov; + int *nargs; + + nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); + if (nargs == NULL) { + gctl_error(req, "No '%s' argument.", "nargs"); + return; + } + if (*nargs != 1) { + gctl_error(req, "Too few arguments."); + return; + } + prov = gctl_get_asciiparam(req, "arg0"); + if (prov == NULL) { + gctl_error(req, "No 'arg%u' argument.", 0); + return; + } + + if (eli_is_attached(prov)) + eli_setkey_attached(req, prov); + else + eli_setkey_detached(req, prov); +} + +static void +eli_delkey_attached(struct gctl_req *req, const char *prov __unused) +{ + + gctl_issue(req); +} + +static void +eli_delkey_detached(struct gctl_req *req, const char *prov) +{ + struct g_eli_metadata md; + unsigned char *mkeydst; + intmax_t *valp; + unsigned nkey; + int *all, *force; + + if (eli_metadata_read(req, prov, &md) == -1) + return; + + all = gctl_get_paraml(req, "all", sizeof(*all)); + if (all == NULL) { + gctl_error(req, "No '%s' argument.", "all"); + return; + } + + if (*all) + arc4rand(md.md_mkeys, sizeof(md.md_mkeys)); + else { + force = gctl_get_paraml(req, "force", sizeof(*force)); + if (force == NULL) { + gctl_error(req, "No '%s' argument.", "force"); + return; + } + + valp = gctl_get_paraml(req, "keyno", sizeof(*valp)); + if (valp == NULL) { + gctl_error(req, "No '%s' argument.", "keyno"); + return; + } + if (*valp == -1) { + gctl_error(req, "Key number has to be specified."); + return; + } + nkey = *valp; + if (nkey >= G_ELI_MAXMKEYS) { + gctl_error(req, "Invalid '%s' argument.", "keyno"); + return; + } + if (!(md.md_keys & (1 << nkey)) && !*force) { + gctl_error(req, "Master Key %u is not set.", nkey); + return; + } + md.md_keys &= ~(1 << nkey); + if (md.md_keys == 0 && !*force) { + gctl_error(req, "This is the last Master Key. Use '-f' " + "option if you really want to remove it."); + return; + } + mkeydst = md.md_mkeys + nkey * G_ELI_MKEYLEN; + arc4rand(mkeydst, G_ELI_MKEYLEN); + } + + eli_metadata_store(req, prov, &md); + bzero(&md, sizeof(md)); +} + +static void +eli_delkey(struct gctl_req *req) +{ + const char *prov; + int *nargs; + + nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); + if (nargs == NULL) { + gctl_error(req, "No '%s' argument.", "nargs"); + return; + } + if (*nargs != 1) { + gctl_error(req, "Too few arguments."); + return; + } + prov = gctl_get_asciiparam(req, "arg0"); + if (prov == NULL) { + gctl_error(req, "No 'arg%u' argument.", 0); + return; + } + + if (eli_is_attached(prov)) + eli_delkey_attached(req, prov); + else + eli_delkey_detached(req, prov); +} + +static void +eli_kill_detached(struct gctl_req *req, const char *prov) +{ + struct g_eli_metadata md; + int error; + + /* + * NOTE: Maybe we should verify if this is geli provider first, + * but 'kill' command is quite critical so better don't waste + * the time. + */ +#if 0 + error = g_metadata_read(prov, (unsigned char *)&md, sizeof(md), + G_ELI_MAGIC); + if (error != 0) { + gctl_error(req, "Cannot read metadata from %s: %s.", prov, + strerror(error)); + return; + } +#endif + + arc4rand((unsigned char *)&md, sizeof(md)); + error = g_metadata_store(prov, (unsigned char *)&md, sizeof(md)); + if (error != 0) { + gctl_error(req, "Cannot write metadata to %s: %s.", prov, + strerror(error)); + } +} + +static void +eli_kill(struct gctl_req *req) +{ + const char *prov; + char param[16]; + unsigned i; + int *nargs, *all; + + nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); + if (nargs == NULL) { + gctl_error(req, "No '%s' argument.", "nargs"); + return; + } + all = gctl_get_paraml(req, "all", sizeof(*all)); + if (all == NULL) { + gctl_error(req, "No '%s' argument.", "all"); + return; + } + if (!*all && *nargs == 0) { + gctl_error(req, "Too few arguments."); + return; + } + /* + * How '-a' option combine with a list of providers: + * Delete Master Keys from all attached providers: + * geli kill -a + * Delete Master Keys from all attached provider and from + * detached da0 and da1: + * geli kill -a da0 da1 + * Delete Master Keys from (attached or detached) da0 and da1: + * geli kill da0 da1 + */ + + /* + * First attached providers. + */ + gctl_issue(req); + /* + * Now the rest. + */ + for (i = 0; i < (unsigned)*nargs; i++) { + snprintf(param, sizeof(param), "arg%u", i); + prov = gctl_get_asciiparam(req, param); + + if (!eli_is_attached(prov)) + eli_kill_detached(req, prov); + } +} + +static void +eli_backup(struct gctl_req *req) +{ + struct g_eli_metadata md; + const char *file, *prov; + unsigned secsize; + unsigned char *sector; + off_t mediasize; + int *nargs, filefd, provfd; + + nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); + if (nargs == NULL) { + gctl_error(req, "No '%s' argument.", "nargs"); + return; + } + if (*nargs != 2) { + gctl_error(req, "Invalid number of arguments."); + return; + } + + prov = gctl_get_asciiparam(req, "arg0"); + if (prov == NULL) { + gctl_error(req, "No 'arg%u' argument.", 0); + return; + } + file = gctl_get_asciiparam(req, "arg1"); + if (file == NULL) { + gctl_error(req, "No 'arg%u' argument.", 1); + return; + } + + provfd = filefd = -1; + sector = NULL; + secsize = 0; + + provfd = open(prov, O_RDONLY); + if (provfd == -1 && errno == ENOENT && prov[0] != '/') { + char devprov[MAXPATHLEN]; + + snprintf(devprov, sizeof(devprov), "%s%s", _PATH_DEV, prov); + provfd = open(devprov, O_RDONLY); + } + if (provfd == -1) { + gctl_error(req, "Cannot open %s: %s.", prov, strerror(errno)); + return; + } + filefd = open(file, O_WRONLY | O_TRUNC | O_CREAT, 0600); + if (filefd == -1) { + gctl_error(req, "Cannot open %s: %s.", file, strerror(errno)); + goto out; + } + + mediasize = g_get_mediasize(prov); + secsize = g_get_sectorsize(prov); + if (mediasize == 0 || secsize == 0) { + gctl_error(req, "Cannot get informations about %s: %s.", prov, + strerror(errno)); + return; + } + + sector = malloc(secsize); + if (sector == NULL) { + gctl_error(req, "Cannot allocate memory."); + return; + } + + /* Read metadata from the provider. */ + if (pread(provfd, sector, secsize, mediasize - secsize) != + (ssize_t)secsize) { + gctl_error(req, "Cannot read metadata: %s.", strerror(errno)); + goto out; + } + /* Check if this is geli provider. */ + if (eli_metadata_decode(sector, &md) != 0) { + gctl_error(req, "MD5 hash mismatch: not a geli provider?"); + goto out; + } + /* Write metadata to the destination file. */ + if (write(filefd, sector, secsize) != (ssize_t)secsize) { + gctl_error(req, "Cannot write to %s: %s.", file, + strerror(errno)); + goto out; + } +out: + if (provfd > 0) + close(provfd); + if (filefd > 0) + close(filefd); + if (sector != NULL) { + bzero(sector, secsize); + free(sector); + } +} + +static void +eli_restore(struct gctl_req *req) +{ + struct g_eli_metadata md; + const char *file, *prov; + unsigned char *sector; + unsigned secsize; + off_t mediasize; + int *nargs, filefd, provfd; + + nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); + if (nargs == NULL) { + gctl_error(req, "No '%s' argument.", "nargs"); + return; + } + if (*nargs != 2) { + gctl_error(req, "Invalid number of arguments."); + return; + } + + file = gctl_get_asciiparam(req, "arg0"); + if (file == NULL) { + gctl_error(req, "No 'arg%u' argument.", 1); + return; + } + prov = gctl_get_asciiparam(req, "arg1"); + if (prov == NULL) { + gctl_error(req, "No 'arg%u' argument.", 0); + return; + } + + provfd = filefd = -1; + sector = NULL; + secsize = 0; + + filefd = open(file, O_RDONLY); + if (filefd == -1) { + gctl_error(req, "Cannot open %s: %s.", file, strerror(errno)); + goto out; + } + provfd = open(prov, O_WRONLY); + if (provfd == -1 && errno == ENOENT && prov[0] != '/') { + char devprov[MAXPATHLEN]; + + snprintf(devprov, sizeof(devprov), "%s%s", _PATH_DEV, prov); + provfd = open(devprov, O_WRONLY); + } + if (provfd == -1) { + gctl_error(req, "Cannot open %s: %s.", prov, strerror(errno)); + return; + } + + mediasize = g_get_mediasize(prov); + secsize = g_get_sectorsize(prov); + if (mediasize == 0 || secsize == 0) { + gctl_error(req, "Cannot get informations about %s: %s.", prov, + strerror(errno)); + return; + } + + sector = malloc(secsize); + if (sector == NULL) { + gctl_error(req, "Cannot allocate memory."); + return; + } + + /* Read metadata from the backup file. */ + if (read(filefd, sector, secsize) != (ssize_t)secsize) { + gctl_error(req, "Cannot read from %s: %s.", file, + strerror(errno)); + goto out; + } + /* Check if this file contains geli metadata. */ + if (eli_metadata_decode(sector, &md) != 0) { + gctl_error(req, "MD5 hash mismatch: not a geli backup file?"); + goto out; + } + /* Read metadata from the provider. */ + if (pwrite(provfd, sector, secsize, mediasize - secsize) != + (ssize_t)secsize) { + gctl_error(req, "Cannot write metadata: %s.", strerror(errno)); + goto out; + } +out: + if (provfd > 0) + close(provfd); + if (filefd > 0) + close(filefd); + if (sector != NULL) { + bzero(sector, secsize); + free(sector); + } +} + +static void +eli_clear(struct gctl_req *req) +{ + const char *name; + char param[16]; + int *nargs, error, i; + + nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); + if (nargs == NULL) { + gctl_error(req, "No '%s' argument.", "nargs"); + return; + } + if (*nargs < 1) { + gctl_error(req, "Too few arguments."); + return; + } + + for (i = 0; i < *nargs; i++) { + snprintf(param, sizeof(param), "arg%u", i); + name = gctl_get_asciiparam(req, param); + + error = g_metadata_clear(name, G_ELI_MAGIC); + if (error != 0) { + fprintf(stderr, "Cannot clear metadata on %s: %s.\n", + name, strerror(error)); + gctl_error(req, "Not fully done."); + continue; + } + if (verbose) + printf("Metadata cleared on %s.\n", name); + } +} + +static void +eli_dump(struct gctl_req *req) +{ + struct g_eli_metadata md, tmpmd; + const char *name; + char param[16]; + int *nargs, error, i; + + nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); + if (nargs == NULL) { + gctl_error(req, "No '%s' argument.", "nargs"); + return; + } + if (*nargs < 1) { + gctl_error(req, "Too few arguments."); + return; + } + + for (i = 0; i < *nargs; i++) { + snprintf(param, sizeof(param), "arg%u", i); + name = gctl_get_asciiparam(req, param); + + error = g_metadata_read(name, (unsigned char *)&tmpmd, + sizeof(tmpmd), G_ELI_MAGIC); + if (error != 0) { + fprintf(stderr, "Cannot read metadata from %s: %s.\n", + name, strerror(error)); + gctl_error(req, "Not fully done."); + continue; + } + if (eli_metadata_decode((unsigned char *)&tmpmd, &md) != 0) { + fprintf(stderr, "MD5 hash mismatch for %s, skipping.\n", + name); + gctl_error(req, "Not fully done."); + continue; + } + printf("Metadata on %s:\n", name); + eli_metadata_dump(&md); + printf("\n"); + } +} diff --git a/sys/geom/eli/g_eli.c b/sys/geom/eli/g_eli.c new file mode 100644 index 000000000000..d837ce475800 --- /dev/null +++ b/sys/geom/eli/g_eli.c @@ -0,0 +1,1138 @@ +/*- + * Copyright (c) 2005 Pawel Jakub Dawidek + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + + +MALLOC_DEFINE(M_ELI, "eli data", "GEOM_ELI Data"); + +SYSCTL_DECL(_kern_geom); +SYSCTL_NODE(_kern_geom, OID_AUTO, eli, CTLFLAG_RW, 0, "GEOM_ELI stuff"); +u_int g_eli_debug = 1; +TUNABLE_INT("kern.geom.eli.debug", &g_eli_debug); +SYSCTL_UINT(_kern_geom_eli, OID_AUTO, debug, CTLFLAG_RW, &g_eli_debug, 0, + "Debug level"); +static u_int g_eli_tries = 3; +TUNABLE_INT("kern.geom.eli.tries", &g_eli_tries); +SYSCTL_UINT(_kern_geom_eli, OID_AUTO, tries, CTLFLAG_RW, &g_eli_tries, 0, + "Number of tries when asking for passphrase"); +static u_int g_eli_visible_passphrase = 0; +TUNABLE_INT("kern.geom.eli.visible_passphrase", &g_eli_visible_passphrase); +SYSCTL_UINT(_kern_geom_eli, OID_AUTO, visible_passphrase, CTLFLAG_RW, + &g_eli_visible_passphrase, 0, + "Turn on echo when entering passphrase (debug purposes only!!)"); +u_int g_eli_overwrites = 5; +SYSCTL_UINT(_kern_geom_eli, OID_AUTO, overwrites, CTLFLAG_RW, &g_eli_overwrites, + 0, "Number of overwrites on-disk keys when destroying"); +static u_int g_eli_threads = 1; +TUNABLE_INT("kern.geom.eli.threads", &g_eli_threads); +SYSCTL_UINT(_kern_geom_eli, OID_AUTO, threads, CTLFLAG_RW, &g_eli_threads, 0, + "Number of threads doing crypto work"); + +static int g_eli_do_taste = 0; + +static int g_eli_destroy_geom(struct gctl_req *req, struct g_class *mp, + struct g_geom *gp); +static int g_eli_crypto_run(struct g_eli_worker *wr, struct bio *bp); + +static g_taste_t g_eli_taste; +static g_dumpconf_t g_eli_dumpconf; + +struct g_class g_eli_class = { + .name = G_ELI_CLASS_NAME, + .version = G_VERSION, + .ctlreq = g_eli_config, + .taste = g_eli_taste, + .destroy_geom = g_eli_destroy_geom +}; + + +/* + * Code paths: + * BIO_READ: + * g_eli_start -> g_io_request -> g_eli_read_done -> g_eli_crypto_run -> g_eli_crypto_read_done -> g_io_deliver + * BIO_WRITE: + * g_eli_start -> g_eli_crypto_run -> g_eli_crypto_write_done -> g_io_request -> g_eli_write_done -> g_io_deliver + */ + + +/* + * EAGAIN from crypto(9) means, that we were probably balanced to another crypto + * accelerator or something like this. + * The function updates the SID and rerun the operation. + */ +static int +g_eli_crypto_rerun(struct cryptop *crp) +{ + struct g_eli_softc *sc; + struct g_eli_worker *wr; + struct bio *bp; + int error; + + bp = (struct bio *)crp->crp_opaque; + sc = bp->bio_to->geom->softc; + LIST_FOREACH(wr, &sc->sc_workers, w_next) { + if (wr->w_number == bp->bio_pflags) + break; + } + KASSERT(wr != NULL, ("Invalid worker (%u).", bp->bio_pflags)); + G_ELI_DEBUG(1, "Reruning crypto %s request (sid: %ju -> %ju).", + bp->bio_cmd == BIO_READ ? "READ" : "WRITE", (uintmax_t)wr->w_sid, + (uintmax_t)crp->crp_sid); + wr->w_sid = crp->crp_sid; + crp->crp_etype = 0; + error = crypto_dispatch(crp); + if (error == 0) + return (0); + G_ELI_DEBUG(1, "%s: crypto_dispatch() returned %d.", __func__, error); + crp->crp_etype = error; + return (error); +} + +/* + * The function is called afer reading encrypted data from the provider. + */ +static void +g_eli_read_done(struct bio *bp) +{ + struct g_eli_softc *sc; + struct bio *pbp; + int error; + + G_ELI_LOGREQ(2, bp, "Request done."); + pbp = bp->bio_parent; + if (pbp->bio_error == 0) + pbp->bio_error = bp->bio_error; + g_destroy_bio(bp); + if (pbp->bio_error != 0) { + G_ELI_LOGREQ(0, pbp, "%s() failed", __func__); + pbp->bio_completed = 0; + g_io_deliver(pbp, pbp->bio_error); + return; + } + sc = pbp->bio_to->geom->softc; + /* + * If we have hardware acceleration we can call g_eli_crypto_run() + * directly. If not, put it on the queue and wakeup worker thread, + * which will do the work for us, so we don't slow down g_up path. + */ + if (sc->sc_crypto == G_ELI_CRYPTO_HW) { + struct g_eli_worker *wr; + + wr = LIST_FIRST(&sc->sc_workers); + error = g_eli_crypto_run(wr, pbp); + if (error != 0) { + G_ELI_LOGREQ(0, pbp, + "g_eli_crypto_run() failed (error=%d).", error); + pbp->bio_completed = 0; + g_io_deliver(pbp, error); + } + } else { + mtx_lock(&sc->sc_queue_mtx); + bioq_insert_tail(&sc->sc_queue, pbp); + mtx_unlock(&sc->sc_queue_mtx); + wakeup(sc); + } +} + +/* + * The function is called after we read and decrypt data. + */ +static int +g_eli_crypto_read_done(struct cryptop *crp) +{ + struct bio *bp; + + if (crp->crp_etype == EAGAIN) { + if (g_eli_crypto_rerun(crp) == 0) + return (0); + } + bp = (struct bio *)crp->crp_opaque; + bp->bio_inbed++; + if (crp->crp_etype == 0) { + G_ELI_DEBUG(3, "Crypto READ request done (%d/%d).", + bp->bio_inbed, bp->bio_children); + bp->bio_completed += crp->crp_olen; + } else { + G_ELI_DEBUG(1, "Crypto READ request failed (%d/%d) error=%d.", + bp->bio_inbed, bp->bio_children, crp->crp_etype); + if (bp->bio_error == 0) + bp->bio_error = crp->crp_etype; + } + /* + * Do we have all sectors already? + */ + if (bp->bio_inbed < bp->bio_children) + return (0); + free(bp->bio_driver2, M_ELI); + bp->bio_driver2 = NULL; + if (bp->bio_error != 0) { + G_ELI_LOGREQ(0, bp, "Crypto READ request failed (error=%d).", + bp->bio_error); + bp->bio_completed = 0; + } + /* + * Read is finished, send it up. + */ + g_io_deliver(bp, bp->bio_error); + return (0); +} + +/* + * The function is called after we encrypt and write data. + */ +static void +g_eli_write_done(struct bio *bp) +{ + struct bio *pbp; + + G_ELI_LOGREQ(2, bp, "Request done."); + pbp = bp->bio_parent; + if (pbp->bio_error == 0) + pbp->bio_error = bp->bio_error; + free(pbp->bio_driver2, M_ELI); + pbp->bio_driver2 = NULL; + if (pbp->bio_error == 0) + pbp->bio_completed = pbp->bio_length; + else { + G_ELI_LOGREQ(0, pbp, "Crypto READ request failed (error=%d).", + pbp->bio_error); + pbp->bio_completed = 0; + } + g_destroy_bio(bp); + /* + * Write is finished, send it up. + */ + g_io_deliver(pbp, pbp->bio_error); +} + +/* + * The function is called after data encryption. + */ +static int +g_eli_crypto_write_done(struct cryptop *crp) +{ + struct g_geom *gp; + struct g_consumer *cp; + struct bio *bp, *cbp; + + if (crp->crp_etype == EAGAIN) { + if (g_eli_crypto_rerun(crp) == 0) + return (0); + } + bp = (struct bio *)crp->crp_opaque; + bp->bio_inbed++; + if (crp->crp_etype == 0) { + G_ELI_DEBUG(3, "Crypto WRITE request done (%d/%d).", + bp->bio_inbed, bp->bio_children); + } else { + G_ELI_DEBUG(1, "Crypto WRITE request failed (%d/%d) error=%d.", + bp->bio_inbed, bp->bio_children, crp->crp_etype); + if (bp->bio_error == 0) + bp->bio_error = crp->crp_etype; + } + /* + * All sectors are already encrypted? + */ + if (bp->bio_inbed < bp->bio_children) + return (0); + bp->bio_inbed = 0; + bp->bio_children = 1; + cbp = bp->bio_driver1; + bp->bio_driver1 = NULL; + if (bp->bio_error != 0) { + G_ELI_LOGREQ(0, bp, "Crypto WRITE request failed (error=%d).", + bp->bio_error); + free(bp->bio_driver2, M_ELI); + bp->bio_driver2 = NULL; + g_destroy_bio(cbp); + g_io_deliver(bp, bp->bio_error); + return (0); + } + cbp->bio_data = bp->bio_driver2; + cbp->bio_done = g_eli_write_done; + gp = bp->bio_to->geom; + cp = LIST_FIRST(&gp->consumer); + cbp->bio_to = cp->provider; + G_ELI_LOGREQ(2, cbp, "Sending request."); + /* + * Send encrypted data to the provider. + */ + g_io_request(cbp, cp); + return (0); +} + +/* + * This function should never be called, but GEOM made as it set ->orphan() + * method for every geom. + */ +static void +g_eli_orphan_spoil_assert(struct g_consumer *cp) +{ + + panic("Function %s() called for %s.", __func__, cp->geom->name); +} + +static void +g_eli_orphan(struct g_consumer *cp) +{ + struct g_eli_softc *sc; + + g_topology_assert(); + sc = cp->geom->softc; + if (sc == NULL) + return; + g_eli_destroy(sc, 1); +} + +static void +g_eli_start(struct bio *bp) +{ + struct g_eli_softc *sc; + struct bio *cbp; + + sc = bp->bio_to->geom->softc; + KASSERT(sc != NULL, + ("Provider's error should be set (error=%d)(device=%s).", + bp->bio_to->error, bp->bio_to->name)); + G_ELI_LOGREQ(2, bp, "Request received."); + + switch (bp->bio_cmd) { + case BIO_READ: + case BIO_WRITE: + break; + case BIO_DELETE: + /* + * We could eventually support BIO_DELETE request. + * It could be done by overwritting requested sector with + * random data g_eli_overwrites number of times. + */ + case BIO_GETATTR: + default: + g_io_deliver(bp, EOPNOTSUPP); + return; + } + cbp = g_clone_bio(bp); + if (cbp == NULL) { + g_io_deliver(bp, ENOMEM); + return; + } + if (bp->bio_cmd == BIO_READ) { + struct g_consumer *cp; + + cbp->bio_done = g_eli_read_done; + cp = LIST_FIRST(&sc->sc_geom->consumer); + cbp->bio_to = cp->provider; + G_ELI_LOGREQ(2, bp, "Sending request."); + /* + * Read encrypted data from provider. + */ + g_io_request(cbp, cp); + } else /* if (bp->bio_cmd == BIO_WRITE) */ { + struct g_eli_worker *wr; + int error; + + bp->bio_driver1 = cbp; + wr = LIST_FIRST(&sc->sc_workers); + /* + * If we have hardware acceleration we can call + * g_eli_crypto_run() directly. If not, put it on the queue and + * wakeup worker thread, which will do the work for us, so we + * don't slow down g_down path. + */ + if (sc->sc_crypto == G_ELI_CRYPTO_HW) { + error = g_eli_crypto_run(wr, bp); + if (error != 0) { + G_ELI_LOGREQ(0, bp, + "g_eli_crypto_run() failed (error=%d).", + error); + g_destroy_bio(cbp); + bp->bio_completed = 0; + g_io_deliver(bp, error); + } + } else { + mtx_lock(&sc->sc_queue_mtx); + bioq_insert_tail(&sc->sc_queue, bp); + mtx_unlock(&sc->sc_queue_mtx); + wakeup(sc); + } + } +} + +/* + * This is the main function for kernel worker thread when we don't have + * hardware acceleration and we have to do cryptography in software. + * Dedicated thread is needed, so we don't slow down g_up/g_down GEOM + * threads with crypto work. + */ +static void +g_eli_worker(void *arg) +{ + struct g_eli_softc *sc; + struct g_eli_worker *wr; + struct bio *bp; + int error; + + wr = arg; + sc = wr->w_softc; + mtx_lock_spin(&sched_lock); + sched_prio(curthread, PRIBIO); + mtx_unlock_spin(&sched_lock); + + G_ELI_DEBUG(1, "Thread %s started.", curthread->td_proc->p_comm); + + for (;;) { + mtx_lock(&sc->sc_queue_mtx); + bp = bioq_takefirst(&sc->sc_queue); + if (bp == NULL) { + if ((sc->sc_flags & G_ELI_FLAG_DESTROY) != 0) { + LIST_REMOVE(wr, w_next); + crypto_freesession(wr->w_sid); + free(wr, M_ELI); + G_ELI_DEBUG(1, "Thread %s exiting.", + curthread->td_proc->p_comm); + wakeup(&sc->sc_workers); + mtx_unlock(&sc->sc_queue_mtx); + kthread_exit(0); + } + msleep(sc, &sc->sc_queue_mtx, PRIBIO | PDROP, + "geli:w", 0); + continue; + } + mtx_unlock(&sc->sc_queue_mtx); + error = g_eli_crypto_run(wr, bp); + if (error != 0) { + G_ELI_LOGREQ(0, bp, + "g_eli_crypto_run() failed (error=%d).", error); + if (bp->bio_cmd == BIO_WRITE) { + g_destroy_bio(bp->bio_driver1); + bp->bio_driver1 = NULL; + } + bp->bio_driver2 = NULL; + bp->bio_completed = 0; + g_io_deliver(bp, error); + } + } +} + +/* + * Here we generate IV. It is unique for every sector. + */ +static void +g_eli_crypto_ivgen(struct g_eli_softc *sc, off_t offset, u_char *iv, + size_t size) +{ + u_char hash[SHA256_DIGEST_LENGTH]; + SHA256_CTX ctx; + + /* Copy precalculated SHA256 context for IV-Key. */ + bcopy(&sc->sc_ivctx, &ctx, sizeof(ctx)); + SHA256_Update(&ctx, (uint8_t *)&offset, sizeof(offset)); + SHA256_Final(hash, &ctx); + bcopy(hash, iv, size); +} + +/* + * This is the main function responsible for cryptography (ie. communication + * with crypto(9) subsystem). + */ +static int +g_eli_crypto_run(struct g_eli_worker *wr, struct bio *bp) +{ + struct g_eli_softc *sc; + struct cryptop *crp; + struct cryptodesc *crd; + struct uio *uio; + struct iovec *iov; + u_int i, nsec, add, secsize; + int err, error, flags; + size_t size; + u_char *p, *data; + + G_ELI_LOGREQ(3, bp, "%s", __func__); + + bp->bio_pflags = wr->w_number; + sc = wr->w_softc; + secsize = LIST_FIRST(&sc->sc_geom->provider)->sectorsize; + nsec = bp->bio_length / secsize; + + /* + * Calculate how much memory do we need. + * We need separate crypto operation for every single sector. + * It is much faster to calculate total amount of needed memory here and + * do the allocation once insteaf of allocate memory in pieces (many, + * many pieces). + */ + size = sizeof(*crp) * nsec; + size += sizeof(*crd) * nsec; + size += sizeof(*uio) * nsec; + size += sizeof(*iov) * nsec; + /* + * If we write the data we cannot destroy current bio_data content, + * so we need to allocate more memory for encrypted data. + */ + if (bp->bio_cmd == BIO_WRITE) + size += bp->bio_length; + p = malloc(size, M_ELI, M_NOWAIT | M_ZERO); + if (p == NULL) + return (ENOMEM); + + bp->bio_inbed = 0; + bp->bio_children = nsec; + bp->bio_driver2 = p; + + if (bp->bio_cmd == BIO_READ) + data = bp->bio_data; + else { + data = p; + p += bp->bio_length; + bcopy(bp->bio_data, data, bp->bio_length); + } + + error = 0; + for (i = 0, add = 0; i < nsec; i++, add += secsize) { + crp = (struct cryptop *)p; p += sizeof(*crp); + crd = (struct cryptodesc *)p; p += sizeof(*crd); + uio = (struct uio *)p; p += sizeof(*uio); + iov = (struct iovec *)p; p += sizeof(*iov); + + iov->iov_len = secsize; + iov->iov_base = data; + data += secsize; + + uio->uio_iov = iov; + uio->uio_iovcnt = 1; + uio->uio_segflg = UIO_SYSSPACE; + uio->uio_resid = secsize; + + crp->crp_sid = wr->w_sid; + crp->crp_ilen = secsize; + crp->crp_olen = secsize; + crp->crp_opaque = (void *)bp; + crp->crp_buf = (void *)uio; + if (bp->bio_cmd == BIO_WRITE) + crp->crp_callback = g_eli_crypto_write_done; + else /* if (bp->bio_cmd == BIO_READ) */ + crp->crp_callback = g_eli_crypto_read_done; + crp->crp_flags = CRYPTO_F_IOV | CRYPTO_F_CBIFSYNC | CRYPTO_F_REL; + crp->crp_desc = crd; + + crd->crd_skip = 0; + crd->crd_len = secsize; + crd->crd_flags = flags; + crd->crd_flags = + CRD_F_IV_EXPLICIT | CRD_F_IV_PRESENT | CRD_F_KEY_EXPLICIT; + if (bp->bio_cmd == BIO_WRITE) + crd->crd_flags |= CRD_F_ENCRYPT; + crd->crd_alg = sc->sc_algo; + crd->crd_key = sc->sc_datakey; + crd->crd_klen = sc->sc_keylen; + g_eli_crypto_ivgen(sc, bp->bio_offset + add, crd->crd_iv, + sizeof(crd->crd_iv)); + crd->crd_next = NULL; + + err = crypto_dispatch(crp); + if (err != 0) { + G_ELI_DEBUG(1, "crypto_dispatch() returned %d.", err); + bp->bio_children--; + if (error == 0) + error = err; + } + } + if (bp->bio_error == 0) + bp->bio_error = error; + if (bp->bio_children > 0) + error = 0; + else { + free(bp->bio_driver2, M_ELI); + bp->bio_driver2 = NULL; + } + return (error); +} + +int +g_eli_read_metadata(struct g_class *mp, struct g_provider *pp, + struct g_eli_metadata *md) +{ + struct g_geom *gp; + struct g_consumer *cp; + u_char *buf = NULL; + int error; + + g_topology_assert(); + + gp = g_new_geomf(mp, "eli:taste"); + gp->start = g_eli_start; + gp->access = g_std_access; + /* + * g_eli_read_metadata() is always called from the event thread. + * Our geom is created and destroyed in the same event, so there + * could be no orphan nor spoil event in the meantime. + */ + gp->orphan = g_eli_orphan_spoil_assert; + gp->spoiled = g_eli_orphan_spoil_assert; + cp = g_new_consumer(gp); + error = g_attach(cp, pp); + if (error != 0) + goto end; + error = g_access(cp, 1, 0, 0); + if (error != 0) + goto end; + g_topology_unlock(); + buf = g_read_data(cp, pp->mediasize - pp->sectorsize, pp->sectorsize, + &error); + g_topology_lock(); + if (error != 0) + goto end; + eli_metadata_decode(buf, md); +end: + if (buf != NULL) + g_free(buf); + if (cp->provider != NULL) { + if (cp->acr == 1) + g_access(cp, -1, 0, 0); + g_detach(cp); + } + g_destroy_consumer(cp); + g_destroy_geom(gp); + return (error); +} + +/* + * The function is called when we had last close on provider and user requested + * to close it when this situation occur. + */ +static void +g_eli_last_close(struct g_eli_softc *sc) +{ + struct g_geom *gp; + struct g_provider *pp; + char ppname[64]; + int error; + + g_topology_assert(); + gp = sc->sc_geom; + pp = LIST_FIRST(&gp->provider); + strlcpy(ppname, pp->name, sizeof(ppname)); + error = g_eli_destroy(sc, 1); + KASSERT(error == 0, ("Cannot detach %s on last close (error=%d).", + ppname, error)); + G_ELI_DEBUG(0, "Detached %s on last close.", ppname); +} + +int +g_eli_access(struct g_provider *pp, int dr, int dw, int de) +{ + struct g_eli_softc *sc; + struct g_geom *gp; + + gp = pp->geom; + sc = gp->softc; + + if (dw > 0) { + /* Someone is opening us for write, we need to remember that. */ + sc->sc_flags |= G_ELI_FLAG_WOPEN; + return (0); + } + /* Is this the last close? */ + if (pp->acr + dr > 0 || pp->acw + dw > 0 || pp->ace + de > 0) + return (0); + + /* + * Automatically detach on last close if requested. + */ + if ((sc->sc_flags & G_ELI_FLAG_RW_DETACH) || + (sc->sc_flags & G_ELI_FLAG_WOPEN)) { + g_eli_last_close(sc); + } + return (0); +} + +struct g_geom * +g_eli_create(struct gctl_req *req, struct g_class *mp, struct g_provider *bpp, + const struct g_eli_metadata *md, const u_char *mkey, int nkey) +{ + struct g_eli_softc *sc; + struct g_eli_worker *wr; + struct g_geom *gp; + struct g_provider *pp; + struct g_consumer *cp; + struct cryptoini cri; + u_int i, threads; + int error; + + G_ELI_DEBUG(1, "Creating device %s%s.", bpp->name, G_ELI_SUFFIX); + + gp = g_new_geomf(mp, "%s%s", bpp->name, G_ELI_SUFFIX); + gp->softc = NULL; /* for a moment */ + + sc = malloc(sizeof(*sc), M_ELI, M_WAITOK | M_ZERO); + gp->start = g_eli_start; + /* + * Spoiling cannot happen actually, because we keep provider open for + * writing all the time. + */ + gp->spoiled = g_eli_orphan_spoil_assert; + gp->orphan = g_eli_orphan; + /* + * If detach-on-last-close feature is not enabled, we can simply use + * g_std_access(). + */ + if (md->md_flags & G_ELI_FLAG_WO_DETACH) + gp->access = g_eli_access; + else + gp->access = g_std_access; + gp->dumpconf = g_eli_dumpconf; + + sc->sc_crypto = G_ELI_CRYPTO_SW; + sc->sc_flags = md->md_flags; + sc->sc_algo = md->md_algo; + sc->sc_nkey = nkey; + /* + * Remember the keys in our softc structure. + */ + bcopy(mkey, sc->sc_ivkey, sizeof(sc->sc_ivkey)); + mkey += sizeof(sc->sc_ivkey); + bcopy(mkey, sc->sc_datakey, sizeof(sc->sc_datakey)); + sc->sc_keylen = md->md_keylen; + + /* + * Precalculate SHA256 for IV generation. + * This is expensive operation and we can do it only once now or for + * every access to sector, so now will be much better. + */ + SHA256_Init(&sc->sc_ivctx); + SHA256_Update(&sc->sc_ivctx, sc->sc_ivkey, sizeof(sc->sc_ivkey)); + + gp->softc = sc; + sc->sc_geom = gp; + + bioq_init(&sc->sc_queue); + mtx_init(&sc->sc_queue_mtx, "geli:queue", NULL, MTX_DEF); + + pp = NULL; + cp = g_new_consumer(gp); + error = g_attach(cp, bpp); + if (error != 0) { + if (req != NULL) { + gctl_error(req, "Cannot attach to %s (error=%d).", + bpp->name, error); + } else { + G_ELI_DEBUG(1, "Cannot attach to %s (error=%d).", + bpp->name, error); + } + goto failed; + } + /* + * Keep provider open all the time, so we can run critical tasks, + * like Master Keys deletion, without wondering if we can open + * provider or not. + */ + error = g_access(cp, 1, 1, 1); + if (error != 0) { + if (req != NULL) { + gctl_error(req, "Cannot access %s (error=%d).", + bpp->name, error); + } else { + G_ELI_DEBUG(1, "Cannot access %s (error=%d).", + bpp->name, error); + } + goto failed; + } + + LIST_INIT(&sc->sc_workers); + + bzero(&cri, sizeof(cri)); + cri.cri_alg = sc->sc_algo; + cri.cri_klen = sc->sc_keylen; + cri.cri_key = sc->sc_datakey; + + threads = g_eli_threads; + /* There is really no need for too many worker threads. */ + if (threads > MAXCPU) { + G_ELI_DEBUG(0, "Reducing number of threads to %u.", MAXCPU); + threads = MAXCPU; + } + for (i = 0; i < threads; i++) { + wr = malloc(sizeof(*wr), M_ELI, M_WAITOK | M_ZERO); + wr->w_softc = sc; + wr->w_number = i; + + /* + * If this is the first pass, try to get hardware support. + * Use software cryptography, if we cannot get it. + */ + if (i == 0) { + error = crypto_newsession(&wr->w_sid, &cri, 1); + if (error == 0) { + sc->sc_crypto = G_ELI_CRYPTO_HW; + wr->w_proc = NULL; + LIST_INSERT_HEAD(&sc->sc_workers, wr, w_next); + break; + } + } + error = crypto_newsession(&wr->w_sid, &cri, 0); + if (error != 0) { + free(wr, M_ELI); + if (req != NULL) { + gctl_error(req, "Cannot setup crypto session " + "for %s (error=%d).", bpp->name, error); + } else { + G_ELI_DEBUG(1, "Cannot setup crypto session " + "for %s (error=%d).", bpp->name, error); + } + goto failed; + } + + /* + * If we cannot get hardware acceleration, create dedicated + * thread(s) and do the crypto work in there. + */ + + error = kthread_create(g_eli_worker, wr, &wr->w_proc, 0, 0, + "g_eli[%u] %s", i, bpp->name); + if (error != 0) { + crypto_freesession(wr->w_sid); + free(wr, M_ELI); + if (req != NULL) { + gctl_error(req, "Cannot create kernel " + "thread for %s (error=%d).", + bpp->name, error); + } else { + G_ELI_DEBUG(1, "Cannot create kernel " + "thread for %s (error=%d).", + bpp->name, error); + } + goto failed; + } + LIST_INSERT_HEAD(&sc->sc_workers, wr, w_next); + } + + /* + * Create decrypted provider. + */ + pp = g_new_providerf(gp, "%s%s", bpp->name, G_ELI_SUFFIX); + pp->sectorsize = md->md_sectorsize; + pp->mediasize = bpp->mediasize; + if ((sc->sc_flags & G_ELI_FLAG_ONETIME) == 0) + pp->mediasize -= bpp->sectorsize; + pp->mediasize -= (pp->mediasize % pp->sectorsize); + g_error_provider(pp, 0); + + G_ELI_DEBUG(0, "Device %s created.", pp->name); + G_ELI_DEBUG(0, " Cipher: %s", g_eli_algo2str(sc->sc_algo)); + G_ELI_DEBUG(0, "Key length: %u", sc->sc_keylen); + G_ELI_DEBUG(0, " Crypto: %s", + sc->sc_crypto == G_ELI_CRYPTO_SW ? "software" : "hardware"); + return (gp); +failed: + if (sc->sc_crypto == G_ELI_CRYPTO_SW) { + mtx_lock(&sc->sc_queue_mtx); + sc->sc_flags |= G_ELI_FLAG_DESTROY; + wakeup(sc); + /* + * Wait for kernel threads self destruction. + */ + while (!LIST_EMPTY(&sc->sc_workers)) { + msleep(&sc->sc_workers, &sc->sc_queue_mtx, PRIBIO, + "geli:destroy", 0); + } + } else if (sc->sc_crypto == G_ELI_CRYPTO_HW) { + wr = LIST_FIRST(&sc->sc_workers); + LIST_REMOVE(wr, w_next); + crypto_freesession(wr->w_sid); + free(wr, M_ELI); + } + mtx_destroy(&sc->sc_queue_mtx); + if (cp->provider != NULL) { + if (cp->acr == 1) + g_access(cp, -1, -1, -1); + g_detach(cp); + } + g_destroy_consumer(cp); + if (pp != NULL) + g_destroy_provider(pp); + g_destroy_geom(gp); + bzero(sc, sizeof(*sc)); + free(sc, M_ELI); + return (NULL); +} + +int +g_eli_destroy(struct g_eli_softc *sc, boolean_t force) +{ + struct g_eli_worker *wr; + struct g_geom *gp; + struct g_provider *pp; + + g_topology_assert(); + + if (sc == NULL) + return (ENXIO); + + gp = sc->sc_geom; + pp = LIST_FIRST(&gp->provider); + if (pp != NULL && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) { + if (force) { + G_ELI_DEBUG(1, "Device %s is still open, so it " + "can't be definitely removed.", pp->name); + } else { + G_ELI_DEBUG(1, + "Device %s is still open (r%dw%de%d).", pp->name, + pp->acr, pp->acw, pp->ace); + return (EBUSY); + } + } + + /* + * When we do cryptography in software, we have kernel thread hanging + * around, so we need to destroy it first. + */ + if (sc->sc_crypto == G_ELI_CRYPTO_SW) { + mtx_lock(&sc->sc_queue_mtx); + sc->sc_flags |= G_ELI_FLAG_DESTROY; + wakeup(sc); + while (!LIST_EMPTY(&sc->sc_workers)) { + msleep(&sc->sc_workers, &sc->sc_queue_mtx, PRIBIO, + "geli:destroy", 0); + } + } else /* if (sc->sc_crypto == G_ELI_CRYPTO_HW) */ { + wr = LIST_FIRST(&sc->sc_workers); + LIST_REMOVE(wr, w_next); + crypto_freesession(wr->w_sid); + free(wr, M_ELI); + } + mtx_destroy(&sc->sc_queue_mtx); + gp->softc = NULL; + bzero(sc, sizeof(*sc)); + free(sc, M_ELI); + + if (pp == NULL || (pp->acr == 0 && pp->acw == 0 && pp->ace == 0)) + G_ELI_DEBUG(0, "Device %s destroyed.", gp->name); + g_wither_geom_close(gp, ENXIO); + + return (0); +} + +static int +g_eli_destroy_geom(struct gctl_req *req __unused, + struct g_class *mp __unused, struct g_geom *gp) +{ + struct g_eli_softc *sc; + + sc = gp->softc; + return (g_eli_destroy(sc, 0)); +} + +/* + * Tasting is only made on boot. + * We detect providers which should be attached before root is mounted. + */ +static struct g_geom * +g_eli_taste(struct g_class *mp, struct g_provider *pp, int flags __unused) +{ + struct g_eli_metadata md; + struct g_geom *gp; + struct hmac_ctx ctx; + char passphrase[256]; + u_char key[G_ELI_USERKEYLEN], mkey[G_ELI_DATAIVKEYLEN]; + u_int nkey, i; + int error; + + g_trace(G_T_TOPOLOGY, "%s(%s, %s)", __func__, mp->name, pp->name); + g_topology_assert(); + + if (!g_eli_do_taste || g_eli_tries == 0) + return (NULL); + + G_ELI_DEBUG(3, "Tasting %s.", pp->name); + + error = g_eli_read_metadata(mp, pp, &md); + if (error != 0) + return (NULL); + gp = NULL; + + if (strcmp(md.md_magic, G_ELI_MAGIC) != 0) + return (NULL); + if (md.md_version > G_ELI_VERSION) { + printf("geom_eli.ko module is too old to handle %s.\n", + pp->name); + return (NULL); + } + if (md.md_provsize != pp->mediasize) + return (NULL); + /* Should we attach it on boot? */ + if ((md.md_flags & G_ELI_FLAG_BOOT) == 0) + return (NULL); + if (md.md_keys == 0x00) { + G_ELI_DEBUG(0, "No valid keys on %s.", pp->name); + return (NULL); + } + + /* + * Ask for the passphrase no more than g_eli_tries times. + */ + for (i = 0; i < g_eli_tries; i++) { + printf("Enter passphrase for %s: ", pp->name); + gets(passphrase, sizeof(passphrase), g_eli_visible_passphrase); + KASSERT(md.md_iterations >= 0, ("md_iterations = %d for %s", + (int)md.md_iterations, pp->name)); + /* + * Prepare Derived-Key from the user passphrase. + */ + g_eli_crypto_hmac_init(&ctx, NULL, 0); + if (md.md_iterations == 0) { + g_eli_crypto_hmac_update(&ctx, md.md_salt, + sizeof(md.md_salt)); + g_eli_crypto_hmac_update(&ctx, passphrase, + strlen(passphrase)); + } else { + u_char dkey[G_ELI_USERKEYLEN]; + + pkcs5v2_genkey(dkey, sizeof(dkey), md.md_salt, + sizeof(md.md_salt), passphrase, md.md_iterations); + g_eli_crypto_hmac_update(&ctx, dkey, sizeof(dkey)); + bzero(dkey, sizeof(dkey)); + } + g_eli_crypto_hmac_final(&ctx, key, 0); + + /* + * Decrypt Master-Key. + */ + error = g_eli_mkey_decrypt(&md, key, mkey, &nkey); + bzero(key, sizeof(key)); + if (error == -1) { + if (i == g_eli_tries - 1) { + i++; + break; + } + G_ELI_DEBUG(0, "Wrong key for %s. Tries left: %u.", + pp->name, g_eli_tries - i - 1); + /* Try again. */ + continue; + } else if (error > 0) { + G_ELI_DEBUG(0, "Cannot decrypt Master Key for %s (error=%d).", + pp->name, error); + return (NULL); + } + G_ELI_DEBUG(1, "Using Master Key %u for %s.", nkey, pp->name); + break; + } + if (i == g_eli_tries) { + G_ELI_DEBUG(0, "Wrong key for %s. No tries left.", pp->name); + return (NULL); + } + + /* + * We have correct key, let's attach provider. + */ + gp = g_eli_create(NULL, mp, pp, &md, mkey, nkey); + bzero(mkey, sizeof(mkey)); + bzero(&md, sizeof(md)); + if (gp == NULL) { + G_ELI_DEBUG(0, "Cannot create device %s%s.", pp->name, + G_ELI_SUFFIX); + return (NULL); + } + return (gp); +} + +static void +g_eli_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, + struct g_consumer *cp, struct g_provider *pp) +{ + struct g_eli_softc *sc; + + g_topology_assert(); + sc = gp->softc; + if (sc == NULL) + return; + if (pp != NULL || cp != NULL) + return; /* Nothing here. */ + sbuf_printf(sb, "%s", indent); + switch (sc->sc_crypto) { + case G_ELI_CRYPTO_HW: + sbuf_printf(sb, "hardware"); + break; + case G_ELI_CRYPTO_SW: + sbuf_printf(sb, "software"); + break; + default: + sbuf_printf(sb, "UNKNOWN"); + break; + } + sbuf_printf(sb, "\n"); +} + +static void +g_eli_on_boot_start(void *dummy __unused) +{ + + /* This prevents from tasting when module is loaded after boot. */ + if (cold) { + G_ELI_DEBUG(1, "Start tasting."); + g_eli_do_taste = 1; + } else { + G_ELI_DEBUG(1, "Tasting not started."); + } +} +SYSINIT(geli_boot_start, SI_SUB_TUNABLES, SI_ORDER_ANY, g_eli_on_boot_start, NULL) + +static void +g_eli_on_boot_end(void *dummy __unused) +{ + + if (g_eli_do_taste) { + G_ELI_DEBUG(1, "Tasting no more."); + g_eli_do_taste = 0; + } +} +SYSINIT(geli_boot_end, SI_SUB_RUN_SCHEDULER, SI_ORDER_ANY, g_eli_on_boot_end, NULL) + +DECLARE_GEOM_CLASS(g_eli_class, g_eli); +MODULE_DEPEND(geom_eli, crypto, 1, 1, 1); +MODULE_DEPEND(geom_eli, cryptodev, 1, 1, 1); diff --git a/sys/geom/eli/g_eli.h b/sys/geom/eli/g_eli.h new file mode 100644 index 000000000000..62bee40a9cdb --- /dev/null +++ b/sys/geom/eli/g_eli.h @@ -0,0 +1,366 @@ +/*- + * Copyright (c) 2005 Pawel Jakub Dawidek + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _G_ELI_H_ +#define _G_ELI_H_ + +#include +#include +#include +#include +#include +#ifdef _KERNEL +#include +#include +#include +#else +#include +#include +#endif +#ifndef _OpenSSL_ +#include +#endif + +#define G_ELI_CLASS_NAME "ELI" +#define G_ELI_MAGIC "GEOM::ELI" +#define G_ELI_SUFFIX ".eli" + +/* + * Version history: + * 0 - Initial version number. + */ +#define G_ELI_VERSION 0 + +/* Use random, onetime keys. */ +#define G_ELI_FLAG_ONETIME 0x00000001 +/* Ask for the passphrase from the kernel, before mounting root. */ +#define G_ELI_FLAG_BOOT 0x00000002 +/* Detach on last close, if we were open for writing. */ +#define G_ELI_FLAG_WO_DETACH 0x00000004 +/* Detach on last close. */ +#define G_ELI_FLAG_RW_DETACH 0x00000008 +/* Provider was open for writing. */ +#define G_ELI_FLAG_WOPEN 0x00010000 +/* Destroy device. */ +#define G_ELI_FLAG_DESTROY 0x00020000 + +#define SHA512_MDLEN 64 + +#define G_ELI_MAXMKEYS 2 +#define G_ELI_MAXKEYLEN 64 +#define G_ELI_USERKEYLEN G_ELI_MAXKEYLEN +#define G_ELI_DATAKEYLEN G_ELI_MAXKEYLEN +#define G_ELI_IVKEYLEN G_ELI_MAXKEYLEN +#define G_ELI_SALTLEN 64 +#define G_ELI_DATAIVKEYLEN (G_ELI_DATAKEYLEN + G_ELI_IVKEYLEN) +/* Data-Key, IV-Key, HMAC_SHA512(Derived-Key, Data-Key+IV-Key) */ +#define G_ELI_MKEYLEN (G_ELI_DATAIVKEYLEN + SHA512_MDLEN) + +#ifdef _KERNEL +extern u_int g_eli_debug; +extern u_int g_eli_overwrites; + +#define G_ELI_CRYPTO_HW 1 +#define G_ELI_CRYPTO_SW 2 + +#define G_ELI_DEBUG(lvl, ...) do { \ + if (g_eli_debug >= (lvl)) { \ + printf("GEOM_ELI"); \ + if (g_eli_debug > 0) \ + printf("[%u]", lvl); \ + printf(": "); \ + printf(__VA_ARGS__); \ + printf("\n"); \ + } \ +} while (0) +#define G_ELI_LOGREQ(lvl, bp, ...) do { \ + if (g_eli_debug >= (lvl)) { \ + printf("GEOM_ELI"); \ + if (g_eli_debug > 0) \ + printf("[%u]", lvl); \ + printf(": "); \ + printf(__VA_ARGS__); \ + printf(" "); \ + g_print_bio(bp); \ + printf("\n"); \ + } \ +} while (0) + +struct g_eli_worker { + struct g_eli_softc *w_softc; + struct proc *w_proc; + u_int w_number; + uint64_t w_sid; + LIST_ENTRY(g_eli_worker) w_next; +}; + +struct g_eli_softc { + struct g_geom *sc_geom; + u_int sc_crypto; + uint8_t sc_datakey[G_ELI_DATAKEYLEN]; + uint8_t sc_ivkey[G_ELI_IVKEYLEN]; + SHA256_CTX sc_ivctx; + u_int sc_algo; + u_int sc_keylen; + int sc_nkey; + uint32_t sc_flags; + + /* Only for software cryptography. */ + struct bio_queue_head sc_queue; + struct mtx sc_queue_mtx; + LIST_HEAD(, g_eli_worker) sc_workers; +}; +#define sc_name sc_geom->name +#endif /* _KERNEL */ + +struct g_eli_metadata { + char md_magic[16]; /* Magic value. */ + uint32_t md_version; /* Version number. */ + uint32_t md_flags; /* Additional flags. */ + uint16_t md_algo; /* Encryption algorithm. */ + uint16_t md_keylen; /* Key length. */ + uint64_t md_provsize; /* Provider's size. */ + uint32_t md_sectorsize; /* Sector size. */ + uint8_t md_keys; /* Available keys. */ + int32_t md_iterations; /* Number of iterations for PKCS#5v2 */ + uint8_t md_salt[G_ELI_SALTLEN]; /* Salt. */ + /* Encrypted master key (IV-key, Data-key, HMAC). */ + uint8_t md_mkeys[G_ELI_MAXMKEYS * G_ELI_MKEYLEN]; + u_char md_hash[16]; /* MD5 hash. */ +}; +#ifndef _OpenSSL_ +static __inline void +eli_metadata_encode(struct g_eli_metadata *md, u_char *data) +{ + MD5_CTX ctx; + u_char *p; + + p = data; + bcopy(md->md_magic, p, sizeof(md->md_magic)); p += sizeof(md->md_magic); + le32enc(p, md->md_version); p += sizeof(md->md_version); + le32enc(p, md->md_flags); p += sizeof(md->md_flags); + le16enc(p, md->md_algo); p += sizeof(md->md_algo); + le16enc(p, md->md_keylen); p += sizeof(md->md_keylen); + le64enc(p, md->md_provsize); p += sizeof(md->md_provsize); + le32enc(p, md->md_sectorsize); p += sizeof(md->md_sectorsize); + *p = md->md_keys; p += sizeof(md->md_keys); + le32enc(p, md->md_iterations); p += sizeof(md->md_iterations); + bcopy(md->md_salt, p, sizeof(md->md_salt)); p += sizeof(md->md_salt); + bcopy(md->md_mkeys, p, sizeof(md->md_mkeys)); p += sizeof(md->md_mkeys); + MD5Init(&ctx); + MD5Update(&ctx, data, p - data); + MD5Final(md->md_hash, &ctx); + bcopy(md->md_hash, p, sizeof(md->md_hash)); +} +static __inline int +eli_metadata_decode_v0(const u_char *data, struct g_eli_metadata *md) +{ + MD5_CTX ctx; + const u_char *p; + + p = data + sizeof(md->md_magic) + sizeof(md->md_version); + md->md_flags = le32dec(p); p += sizeof(md->md_flags); + md->md_algo = le16dec(p); p += sizeof(md->md_algo); + md->md_keylen = le16dec(p); p += sizeof(md->md_keylen); + md->md_provsize = le64dec(p); p += sizeof(md->md_provsize); + md->md_sectorsize = le32dec(p); p += sizeof(md->md_sectorsize); + md->md_keys = *p; p += sizeof(md->md_keys); + md->md_iterations = le32dec(p); p += sizeof(md->md_iterations); + bcopy(p, md->md_salt, sizeof(md->md_salt)); p += sizeof(md->md_salt); + bcopy(p, md->md_mkeys, sizeof(md->md_mkeys)); p += sizeof(md->md_mkeys); + MD5Init(&ctx); + MD5Update(&ctx, data, p - data); + MD5Final(md->md_hash, &ctx); + if (bcmp(md->md_hash, p, 16) != 0) + return (EINVAL); + return (0); +} +static __inline int +eli_metadata_decode(const u_char *data, struct g_eli_metadata *md) +{ + int error; + + bcopy(data, md->md_magic, sizeof(md->md_magic)); + md->md_version = le32dec(data + sizeof(md->md_magic)); + switch (md->md_version) { + case 0: + error = eli_metadata_decode_v0(data, md); + break; + default: + error = EINVAL; + break; + } + return (error); +} +#endif /* !_OpenSSL */ + +static __inline u_int +g_eli_str2algo(const char *name) +{ + + if (strcmp("null", name) == 0) + return (CRYPTO_NULL_CBC); + if (strcmp("aes", name) == 0) + return (CRYPTO_AES_CBC); + else if (strcmp("blowfish", name) == 0) + return (CRYPTO_BLF_CBC); + else if (strcmp("3des", name) == 0) + return (CRYPTO_3DES_CBC); + return (CRYPTO_ALGORITHM_MIN - 1); +} + +static __inline const char * +g_eli_algo2str(u_int algo) +{ + + switch (algo) { + case CRYPTO_NULL_CBC: + return ("NULL"); + case CRYPTO_AES_CBC: + return ("AES"); + case CRYPTO_BLF_CBC: + return ("Blowfish"); + case CRYPTO_3DES_CBC: + return ("3DES"); + } + return ("unknown"); +} + +static __inline void +eli_metadata_dump(const struct g_eli_metadata *md) +{ + static const char hex[] = "0123456789abcdef"; + char str[sizeof(md->md_mkeys) * 2 + 1]; + u_int i; + + printf(" magic: %s\n", md->md_magic); + printf(" version: %u\n", (u_int)md->md_version); + printf(" flags: 0x%x\n", (u_int)md->md_flags); + printf(" algo: %s\n", g_eli_algo2str(md->md_algo)); + printf(" keylen: %u\n", (u_int)md->md_keylen); + printf(" provsize: %ju\n", (uintmax_t)md->md_provsize); + printf("sectorsize: %u\n", (u_int)md->md_sectorsize); + printf(" keys: 0x%02x\n", (u_int)md->md_keys); + printf("iterations: %u\n", (u_int)md->md_iterations); + bzero(str, sizeof(str)); + for (i = 0; i < sizeof(md->md_salt); i++) { + str[i * 2] = hex[md->md_salt[i] >> 4]; + str[i * 2 + 1] = hex[md->md_salt[i] & 0x0f]; + } + printf(" Salt: %s\n", str); + bzero(str, sizeof(str)); + for (i = 0; i < sizeof(md->md_mkeys); i++) { + str[i * 2] = hex[md->md_mkeys[i] >> 4]; + str[i * 2 + 1] = hex[md->md_mkeys[i] & 0x0f]; + } + printf("Master Key: %s\n", str); + bzero(str, sizeof(str)); + for (i = 0; i < 16; i++) { + str[i * 2] = hex[md->md_hash[i] >> 4]; + str[i * 2 + 1] = hex[md->md_hash[i] & 0x0f]; + } + printf(" MD5 hash: %s\n", str); +} + +static __inline u_int +g_eli_keylen(u_int algo, u_int keylen) +{ + + switch (algo) { + case CRYPTO_NULL_CBC: + if (keylen == 0) + keylen = 64 * 8; + else { + if (keylen > 64 * 8) + keylen = 0; + } + return (keylen); + case CRYPTO_AES_CBC: + switch (keylen) { + case 0: + return (128); + case 128: + case 192: + case 256: + return (keylen); + default: + return (0); + } + case CRYPTO_BLF_CBC: + if (keylen == 0) + return (128); + if (keylen < 128 || keylen > 448) + return (0); + if ((keylen % 32) != 0) + return (0); + return (keylen); + case CRYPTO_3DES_CBC: + if (keylen == 0 || keylen == 192) + return (192); + return (0); + default: + return (0); + } +} + +#ifdef _KERNEL +int g_eli_read_metadata(struct g_class *mp, struct g_provider *pp, + struct g_eli_metadata *md); +struct g_geom *g_eli_create(struct gctl_req *req, struct g_class *mp, + struct g_provider *bpp, const struct g_eli_metadata *md, + const u_char *mkey, int nkey); +int g_eli_destroy(struct g_eli_softc *sc, boolean_t force); + +int g_eli_access(struct g_provider *pp, int dr, int dw, int de); +void g_eli_config(struct gctl_req *req, struct g_class *mp, const char *verb); +#endif + +void g_eli_mkey_hmac(unsigned char *mkey, const unsigned char *key); +int g_eli_mkey_decrypt(const struct g_eli_metadata *md, + const unsigned char *key, unsigned char *mkey, unsigned *nkeyp); +int g_eli_mkey_encrypt(unsigned algo, const unsigned char *key, unsigned keylen, + unsigned char *mkey); + +int g_eli_crypto_encrypt(u_int algo, u_char *data, size_t datasize, + const u_char *key, size_t keysize); +int g_eli_crypto_decrypt(u_int algo, u_char *data, size_t datasize, + const u_char *key, size_t keysize); + +struct hmac_ctx { + SHA512_CTX shactx; + u_char k_opad[128]; +}; + +void g_eli_crypto_hmac_init(struct hmac_ctx *ctx, const uint8_t *hkey, + size_t hkeylen); +void g_eli_crypto_hmac_update(struct hmac_ctx *ctx, const uint8_t *data, + size_t datasize); +void g_eli_crypto_hmac_final(struct hmac_ctx *ctx, uint8_t *md, size_t mdsize); +void g_eli_crypto_hmac(const uint8_t *hkey, size_t hkeysize, + const uint8_t *data, size_t datasize, uint8_t *md, size_t mdsize); +#endif /* !_G_ELI_H_ */ diff --git a/sys/geom/eli/g_eli_crypto.c b/sys/geom/eli/g_eli_crypto.c new file mode 100644 index 000000000000..d4ec8897d907 --- /dev/null +++ b/sys/geom/eli/g_eli_crypto.c @@ -0,0 +1,276 @@ +/*- + * Copyright (c) 2005 Pawel Jakub Dawidek + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#ifdef _KERNEL +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#define _OpenSSL_ +#endif +#include + +#ifdef _KERNEL +MALLOC_DECLARE(M_ELI); + +static int +g_eli_crypto_done(struct cryptop *crp) +{ + + crp->crp_opaque = (void *)crp; + wakeup(crp); + return (0); +} + +static int +g_eli_crypto_cipher(u_int algo, int enc, u_char *data, size_t datasize, + const u_char *key, size_t keysize) +{ + struct cryptoini cri; + struct cryptop *crp; + struct cryptodesc *crd; + struct uio *uio; + struct iovec *iov; + uint64_t sid; + u_char *p; + int error; + + bzero(&cri, sizeof(cri)); + cri.cri_alg = algo; + cri.cri_key = __DECONST(void *, key); + cri.cri_klen = keysize; + error = crypto_newsession(&sid, &cri, 0); + if (error != 0) + return (error); + p = malloc(sizeof(*crp) + sizeof(*crd) + sizeof(*uio) + sizeof(*iov), + M_ELI, M_NOWAIT | M_ZERO); + if (p == NULL) { + crypto_freesession(sid); + return (ENOMEM); + } + crp = (struct cryptop *)p; p += sizeof(*crp); + crd = (struct cryptodesc *)p; p += sizeof(*crd); + uio = (struct uio *)p; p += sizeof(*uio); + iov = (struct iovec *)p; p += sizeof(*iov); + + iov->iov_len = datasize; + iov->iov_base = data; + + uio->uio_iov = iov; + uio->uio_iovcnt = 1; + uio->uio_segflg = UIO_SYSSPACE; + uio->uio_resid = datasize; + + crd->crd_skip = 0; + crd->crd_len = datasize; + crd->crd_flags = CRD_F_IV_EXPLICIT | CRD_F_IV_PRESENT | CRD_F_KEY_EXPLICIT; + if (enc) + crd->crd_flags |= CRD_F_ENCRYPT; + crd->crd_alg = algo; + crd->crd_key = __DECONST(void *, key); + crd->crd_klen = keysize; + bzero(crd->crd_iv, sizeof(crd->crd_iv)); + crd->crd_next = NULL; + + crp->crp_sid = sid; + crp->crp_ilen = datasize; + crp->crp_olen = datasize; + crp->crp_opaque = NULL; + crp->crp_callback = g_eli_crypto_done; + crp->crp_buf = (void *)uio; + crp->crp_flags = CRYPTO_F_IOV | CRYPTO_F_CBIFSYNC | CRYPTO_F_REL; + crp->crp_desc = crd; + + error = crypto_dispatch(crp); + if (error == 0) { + while (crp->crp_opaque == NULL) + tsleep(crp, PRIBIO, "geli", hz / 5); + error = crp->crp_etype; + } + + free(crp, M_ELI); + crypto_freesession(sid); + return (error); +} +#else /* !_KERNEL */ +static int +g_eli_crypto_cipher(u_int algo, int enc, u_char *data, size_t datasize, + const u_char *key, size_t keysize) +{ + EVP_CIPHER_CTX ctx; + const EVP_CIPHER *type; + u_char iv[keysize]; + int outsize; + + switch (algo) { + case CRYPTO_NULL_CBC: + type = EVP_enc_null(); + break; + case CRYPTO_AES_CBC: + switch (keysize) { + case 128: + type = EVP_aes_128_cbc(); + break; + case 192: + type = EVP_aes_192_cbc(); + break; + case 256: + type = EVP_aes_256_cbc(); + break; + default: + return (EINVAL); + } + break; + case CRYPTO_BLF_CBC: + type = EVP_bf_cbc(); + break; + case CRYPTO_3DES_CBC: + type = EVP_des_ede3_cbc(); + break; + default: + return (EINVAL); + } + + EVP_CIPHER_CTX_init(&ctx); + + EVP_CipherInit_ex(&ctx, type, NULL, NULL, NULL, enc); + EVP_CIPHER_CTX_set_key_length(&ctx, keysize / 8); + EVP_CIPHER_CTX_set_padding(&ctx, 0); + bzero(iv, sizeof(iv)); + EVP_CipherInit_ex(&ctx, NULL, NULL, key, iv, enc); + + if (EVP_CipherUpdate(&ctx, data, &outsize, data, datasize) == 0) { + EVP_CIPHER_CTX_cleanup(&ctx); + return (EINVAL); + } + assert(outsize == (int)datasize); + + if (EVP_CipherFinal_ex(&ctx, data + outsize, &outsize) == 0) { + EVP_CIPHER_CTX_cleanup(&ctx); + return (EINVAL); + } + assert(outsize == 0); + + EVP_CIPHER_CTX_cleanup(&ctx); + return (0); +} +#endif /* !_KERNEL */ + +int +g_eli_crypto_encrypt(u_int algo, u_char *data, size_t datasize, + const u_char *key, size_t keysize) +{ + + return (g_eli_crypto_cipher(algo, 1, data, datasize, key, keysize)); +} + +int +g_eli_crypto_decrypt(u_int algo, u_char *data, size_t datasize, + const u_char *key, size_t keysize) +{ + + return (g_eli_crypto_cipher(algo, 0, data, datasize, key, keysize)); +} + +void +g_eli_crypto_hmac_init(struct hmac_ctx *ctx, const uint8_t *hkey, + size_t hkeylen) +{ + u_char k_ipad[128], key[128]; + SHA512_CTX lctx; + u_int i; + + bzero(key, sizeof(key)); + if (hkeylen == 0) + ; /* do nothing */ + else if (hkeylen <= 128) + bcopy(hkey, key, hkeylen); + else { + /* If key is longer than 128 bytes reset it to key = SHA512(key). */ + SHA512_Init(&lctx); + SHA512_Update(&lctx, hkey, hkeylen); + SHA512_Final(key, &lctx); + } + + /* XOR key with ipad and opad values. */ + for (i = 0; i < sizeof(key); i++) { + k_ipad[i] = key[i] ^ 0x36; + ctx->k_opad[i] = key[i] ^ 0x5c; + } + bzero(key, sizeof(key)); + /* Perform inner SHA512. */ + SHA512_Init(&ctx->shactx); + SHA512_Update(&ctx->shactx, k_ipad, sizeof(k_ipad)); +} + +void +g_eli_crypto_hmac_update(struct hmac_ctx *ctx, const uint8_t *data, + size_t datasize) +{ + + SHA512_Update(&ctx->shactx, data, datasize); +} + +void +g_eli_crypto_hmac_final(struct hmac_ctx *ctx, uint8_t *md, size_t mdsize) +{ + u_char digest[SHA512_MDLEN]; + SHA512_CTX lctx; + + SHA512_Final(digest, &ctx->shactx); + /* Perform outer SHA512. */ + SHA512_Init(&lctx); + SHA512_Update(&lctx, ctx->k_opad, sizeof(ctx->k_opad)); + bzero(ctx, sizeof(*ctx)); + SHA512_Update(&lctx, digest, sizeof(digest)); + SHA512_Final(digest, &lctx); + /* mdsize == 0 means "Give me the whole hash!" */ + if (mdsize == 0) + mdsize = SHA512_MDLEN; + bcopy(digest, md, mdsize); +} + +void +g_eli_crypto_hmac(const uint8_t *hkey, size_t hkeysize, const uint8_t *data, + size_t datasize, uint8_t *md, size_t mdsize) +{ + struct hmac_ctx ctx; + + g_eli_crypto_hmac_init(&ctx, hkey, hkeysize); + g_eli_crypto_hmac_update(&ctx, data, datasize); + g_eli_crypto_hmac_final(&ctx, md, mdsize); +} diff --git a/sys/geom/eli/g_eli_ctl.c b/sys/geom/eli/g_eli_ctl.c new file mode 100644 index 000000000000..d2ffdd97746d --- /dev/null +++ b/sys/geom/eli/g_eli_ctl.c @@ -0,0 +1,639 @@ +/*- + * Copyright (c) 2005 Pawel Jakub Dawidek + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + + +MALLOC_DECLARE(M_ELI); + + +static void +g_eli_ctl_attach(struct gctl_req *req, struct g_class *mp) +{ + struct g_eli_metadata md; + struct g_provider *pp; + const char *name; + u_char *key, mkey[G_ELI_DATAIVKEYLEN]; + int *nargs, *detach; + int keysize, error; + u_int nkey; + + g_topology_assert(); + + nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); + if (nargs == NULL) { + gctl_error(req, "No '%s' argument.", "nargs"); + return; + } + if (*nargs != 1) { + gctl_error(req, "Invalid number of arguments."); + return; + } + + detach = gctl_get_paraml(req, "detach", sizeof(*detach)); + if (detach == NULL) { + gctl_error(req, "No '%s' argument.", "detach"); + return; + } + + name = gctl_get_asciiparam(req, "arg0"); + if (name == NULL) { + gctl_error(req, "No 'arg%u' argument.", 0); + return; + } + if (strncmp(name, "/dev/", strlen("/dev/")) == 0) + name += strlen("/dev/"); + pp = g_provider_by_name(name); + if (pp == NULL) { + gctl_error(req, "Provider %s is invalid.", name); + return; + } + error = g_eli_read_metadata(mp, pp, &md); + if (error != 0) { + gctl_error(req, "Cannot read metadata from %s (error=%d).", + name, error); + return; + } + if (md.md_keys == 0x00) { + bzero(&md, sizeof(md)); + gctl_error(req, "No valid keys on %s.", pp->name); + return; + } + + key = gctl_get_param(req, "key", &keysize); + if (key == NULL || keysize != G_ELI_USERKEYLEN) { + bzero(&md, sizeof(md)); + gctl_error(req, "No '%s' argument.", "key"); + return; + } + + error = g_eli_mkey_decrypt(&md, key, mkey, &nkey); + bzero(key, keysize); + if (error == -1) { + bzero(&md, sizeof(md)); + gctl_error(req, "Wrong key for %s.", pp->name); + return; + } else if (error > 0) { + bzero(&md, sizeof(md)); + gctl_error(req, "Cannot decrypt Master Key for %s (error=%d).", + pp->name, error); + return; + } + G_ELI_DEBUG(1, "Using Master Key %u for %s.", nkey, pp->name); + + if (*detach) + md.md_flags |= G_ELI_FLAG_WO_DETACH; + g_eli_create(req, mp, pp, &md, mkey, nkey); + bzero(mkey, sizeof(mkey)); + bzero(&md, sizeof(md)); +} + +static struct g_eli_softc * +g_eli_find_device(struct g_class *mp, const char *prov) +{ + struct g_eli_softc *sc; + struct g_geom *gp; + struct g_provider *pp; + struct g_consumer *cp; + + if (strncmp(prov, "/dev/", strlen("/dev/")) == 0) + prov += strlen("/dev/"); + LIST_FOREACH(gp, &mp->geom, geom) { + sc = gp->softc; + if (sc == NULL) + continue; + pp = LIST_FIRST(&gp->provider); + if (pp != NULL && strcmp(pp->name, prov) == 0) + return (sc); + cp = LIST_FIRST(&gp->consumer); + if (cp != NULL && cp->provider != NULL && + strcmp(cp->provider->name, prov) == 0) { + return (sc); + } + } + return (NULL); +} + +static void +g_eli_ctl_detach(struct gctl_req *req, struct g_class *mp) +{ + struct g_eli_softc *sc; + int *force, *last, *nargs, error; + const char *prov; + char param[16]; + u_int i; + + g_topology_assert(); + + nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); + if (nargs == NULL) { + gctl_error(req, "No '%s' argument.", "nargs"); + return; + } + if (*nargs <= 0) { + gctl_error(req, "Missing device(s)."); + return; + } + force = gctl_get_paraml(req, "force", sizeof(*force)); + if (force == NULL) { + gctl_error(req, "No '%s' argument.", "force"); + return; + } + last = gctl_get_paraml(req, "last", sizeof(*last)); + if (last == NULL) { + gctl_error(req, "No '%s' argument.", "last"); + return; + } + + for (i = 0; i < (u_int)*nargs; i++) { + snprintf(param, sizeof(param), "arg%u", i); + prov = gctl_get_asciiparam(req, param); + if (prov == NULL) { + gctl_error(req, "No 'arg%u' argument.", i); + return; + } + sc = g_eli_find_device(mp, prov); + if (sc == NULL) { + gctl_error(req, "No such device: %s.", prov); + return; + } + if (*last) { + sc->sc_flags |= G_ELI_FLAG_RW_DETACH; + sc->sc_geom->access = g_eli_access; + } else { + error = g_eli_destroy(sc, *force); + if (error != 0) { + gctl_error(req, + "Cannot destroy device %s (error=%d).", + sc->sc_name, error); + return; + } + } + } +} + +static void +g_eli_ctl_onetime(struct gctl_req *req, struct g_class *mp) +{ + struct g_eli_metadata md; + struct g_provider *pp; + const char *name; + intmax_t *keylen, *sectorsize; + u_char mkey[G_ELI_DATAIVKEYLEN]; + int *nargs, *detach; + + g_topology_assert(); + bzero(&md, sizeof(md)); + + nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); + if (nargs == NULL) { + gctl_error(req, "No '%s' argument.", "nargs"); + return; + } + if (*nargs != 1) { + gctl_error(req, "Invalid number of arguments."); + return; + } + + detach = gctl_get_paraml(req, "detach", sizeof(*detach)); + if (detach == NULL) { + gctl_error(req, "No '%s' argument.", "detach"); + return; + } + + strlcpy(md.md_magic, G_ELI_MAGIC, sizeof(md.md_magic)); + md.md_version = G_ELI_VERSION; + md.md_flags |= G_ELI_FLAG_ONETIME; + if (*detach) + md.md_flags |= G_ELI_FLAG_WO_DETACH; + + name = gctl_get_asciiparam(req, "algo"); + if (name == NULL) { + gctl_error(req, "No '%s' argument.", "algo"); + return; + } + md.md_algo = g_eli_str2algo(name); + if (md.md_algo < CRYPTO_ALGORITHM_MIN || + md.md_algo > CRYPTO_ALGORITHM_MAX) { + gctl_error(req, "Invalid '%s' argument.", "algo"); + return; + } + + keylen = gctl_get_paraml(req, "keylen", sizeof(*keylen)); + if (keylen == NULL) { + gctl_error(req, "No '%s' argument.", "keylen"); + return; + } + md.md_keylen = g_eli_keylen(md.md_algo, *keylen); + if (md.md_keylen == 0) { + gctl_error(req, "Invalid '%s' argument.", "keylen"); + return; + } + + /* Not important here. */ + md.md_provsize = 0; + /* Not important here. */ + bzero(md.md_salt, sizeof(md.md_salt)); + + md.md_keys = 0x01; + arc4rand(mkey, sizeof(mkey), 0); + + /* Not important here. */ + bzero(md.md_hash, sizeof(md.md_hash)); + + name = gctl_get_asciiparam(req, "arg0"); + if (name == NULL) { + gctl_error(req, "No 'arg%u' argument.", 0); + return; + } + if (strncmp(name, "/dev/", strlen("/dev/")) == 0) + name += strlen("/dev/"); + pp = g_provider_by_name(name); + if (pp == NULL) { + gctl_error(req, "Provider %s is invalid.", name); + return; + } + + sectorsize = gctl_get_paraml(req, "sectorsize", sizeof(*sectorsize)); + if (sectorsize == NULL) { + gctl_error(req, "No '%s' argument.", "sectorsize"); + return; + } + if (*sectorsize == 0) + md.md_sectorsize = pp->sectorsize; + else { + if (*sectorsize < 0 || (*sectorsize % pp->sectorsize) != 0) { + gctl_error(req, "Invalid sector size."); + return; + } + md.md_sectorsize = *sectorsize; + } + + g_eli_create(req, mp, pp, &md, mkey, -1); + bzero(mkey, sizeof(mkey)); + bzero(&md, sizeof(md)); +} + +static void +g_eli_ctl_setkey(struct gctl_req *req, struct g_class *mp) +{ + struct g_eli_softc *sc; + struct g_eli_metadata md; + struct g_provider *pp; + struct g_consumer *cp; + const char *name; + u_char *key, *mkeydst, *sector; + intmax_t *valp; + int nkey; + int keysize, error; + + g_topology_assert(); + + name = gctl_get_asciiparam(req, "arg0"); + if (name == NULL) { + gctl_error(req, "No 'arg%u' argument.", 0); + return; + } + sc = g_eli_find_device(mp, name); + if (sc == NULL) { + gctl_error(req, "Provider %s is invalid.", name); + return; + } + cp = LIST_FIRST(&sc->sc_geom->consumer); + pp = cp->provider; + + error = g_eli_read_metadata(mp, pp, &md); + if (error != 0) { + gctl_error(req, "Cannot read metadata from %s (error=%d).", + name, error); + return; + } + + valp = gctl_get_paraml(req, "keyno", sizeof(*valp)); + if (valp == NULL) { + gctl_error(req, "No '%s' argument.", "keyno"); + return; + } + if (*valp != -1) + nkey = *valp; + else + nkey = sc->sc_nkey; + if (nkey < 0 || nkey >= G_ELI_MAXMKEYS) { + gctl_error(req, "Invalid '%s' argument.", "keyno"); + return; + } + + key = gctl_get_param(req, "key", &keysize); + if (key == NULL || keysize != G_ELI_USERKEYLEN) { + bzero(&md, sizeof(md)); + gctl_error(req, "No '%s' argument.", "key"); + return; + } + + mkeydst = md.md_mkeys + nkey * G_ELI_MKEYLEN; + md.md_keys |= (1 << nkey); + + bcopy(sc->sc_ivkey, mkeydst, sizeof(sc->sc_ivkey)); + bcopy(sc->sc_datakey, mkeydst + sizeof(sc->sc_ivkey), + sizeof(sc->sc_datakey)); + + /* Encrypt Master Key with the new key. */ + error = g_eli_mkey_encrypt(md.md_algo, key, md.md_keylen, mkeydst); + bzero(key, sizeof(key)); + if (error != 0) { + bzero(&md, sizeof(md)); + gctl_error(req, "Cannot encrypt Master Key (error=%d).", error); + return; + } + + sector = malloc(pp->sectorsize, M_ELI, M_WAITOK | M_ZERO); + /* Store metadata with fresh key. */ + eli_metadata_encode(&md, sector); + bzero(&md, sizeof(md)); + error = g_write_data(cp, pp->mediasize - pp->sectorsize, sector, + pp->sectorsize); + bzero(sector, sizeof(sector)); + free(sector, M_ELI); + if (error != 0) { + gctl_error(req, "Cannot store metadata on %s (error=%d).", + pp->name, error); + return; + } + G_ELI_DEBUG(1, "Key %u changed on %s.", nkey, pp->name); +} + +static void +g_eli_ctl_delkey(struct gctl_req *req, struct g_class *mp) +{ + struct g_eli_softc *sc; + struct g_eli_metadata md; + struct g_provider *pp; + struct g_consumer *cp; + const char *name; + u_char *mkeydst, *sector; + intmax_t *valp; + size_t keysize; + int error, nkey, *all, *force; + u_int i; + + g_topology_assert(); + + nkey = 0; /* fixes causeless gcc warning */ + + name = gctl_get_asciiparam(req, "arg0"); + if (name == NULL) { + gctl_error(req, "No 'arg%u' argument.", 0); + return; + } + sc = g_eli_find_device(mp, name); + if (sc == NULL) { + gctl_error(req, "Provider %s is invalid.", name); + return; + } + cp = LIST_FIRST(&sc->sc_geom->consumer); + pp = cp->provider; + + error = g_eli_read_metadata(mp, pp, &md); + if (error != 0) { + gctl_error(req, "Cannot read metadata from %s (error=%d).", + name, error); + return; + } + + all = gctl_get_paraml(req, "all", sizeof(*all)); + if (all == NULL) { + gctl_error(req, "No '%s' argument.", "all"); + return; + } + + if (*all) { + mkeydst = md.md_mkeys; + keysize = sizeof(md.md_mkeys); + } else { + force = gctl_get_paraml(req, "force", sizeof(*force)); + if (force == NULL) { + gctl_error(req, "No '%s' argument.", "force"); + return; + } + + valp = gctl_get_paraml(req, "keyno", sizeof(*valp)); + if (valp == NULL) { + gctl_error(req, "No '%s' argument.", "keyno"); + return; + } + if (*valp != -1) + nkey = *valp; + else + nkey = sc->sc_nkey; + if (nkey < 0 || nkey >= G_ELI_MAXMKEYS) { + gctl_error(req, "Invalid '%s' argument.", "keyno"); + return; + } + if (!(md.md_keys & (1 << nkey)) && !*force) { + gctl_error(req, "Master Key %u is not set.", nkey); + return; + } + md.md_keys &= ~(1 << nkey); + if (md.md_keys == 0 && !*force) { + gctl_error(req, "This is the last Master Key. Use '-f' " + "flag if you really want to remove it."); + return; + } + mkeydst = md.md_mkeys + nkey * G_ELI_MKEYLEN; + keysize = G_ELI_MKEYLEN; + } + + sector = malloc(pp->sectorsize, M_ELI, M_WAITOK | M_ZERO); + for (i = 0; i <= g_eli_overwrites; i++) { + if (i == g_eli_overwrites) + bzero(mkeydst, keysize); + else + arc4rand(mkeydst, keysize, 0); + /* Store metadata with destroyed key. */ + eli_metadata_encode(&md, sector); + error = g_write_data(cp, pp->mediasize - pp->sectorsize, sector, + pp->sectorsize); + if (error != 0) { + G_ELI_DEBUG(0, "Cannot store metadata on %s " + "(error=%d).", pp->name, error); + } + } + bzero(&md, sizeof(md)); + bzero(sector, sizeof(sector)); + free(sector, M_ELI); + if (*all) + G_ELI_DEBUG(1, "All keys removed from %s.", pp->name); + else + G_ELI_DEBUG(1, "Key %d removed from %s.", nkey, pp->name); +} + +static int +g_eli_kill_one(struct g_eli_softc *sc) +{ + struct g_provider *pp; + struct g_consumer *cp; + u_char *sector; + int err, error = 0; + u_int i; + + g_topology_assert(); + + if (sc == NULL) + return (ENOENT); + + pp = LIST_FIRST(&sc->sc_geom->provider); + g_error_provider(pp, ENXIO); + + cp = LIST_FIRST(&sc->sc_geom->consumer); + pp = cp->provider; + + sector = malloc(pp->sectorsize, M_ELI, M_WAITOK); + for (i = 0; i <= g_eli_overwrites; i++) { + if (i == g_eli_overwrites) + bzero(sector, pp->sectorsize); + else + arc4rand(sector, pp->sectorsize, 0); + err = g_write_data(cp, pp->mediasize - pp->sectorsize, sector, + pp->sectorsize); + if (err != 0) { + G_ELI_DEBUG(0, "Cannot erase metadata on %s " + "(error=%d).", pp->name, err); + if (error == 0) + error = err; + } + } + free(sector, M_ELI); + if (error == 0) + G_ELI_DEBUG(0, "%s has been killed.", pp->name); + g_eli_destroy(sc, 1); + return (error); +} + +static void +g_eli_ctl_kill(struct gctl_req *req, struct g_class *mp) +{ + int *all, *nargs; + int error; + + g_topology_assert(); + + nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); + if (nargs == NULL) { + gctl_error(req, "No '%s' argument.", "nargs"); + return; + } + all = gctl_get_paraml(req, "all", sizeof(*all)); + if (all == NULL) { + gctl_error(req, "No '%s' argument.", "all"); + return; + } + if (!*all && *nargs == 0) { + gctl_error(req, "Too few arguments."); + return; + } + + if (*all) { + struct g_geom *gp, *gp2; + + LIST_FOREACH_SAFE(gp, &mp->geom, geom, gp2) { + error = g_eli_kill_one(gp->softc); + if (error != 0) + gctl_error(req, "Not fully done."); + } + } else { + struct g_eli_softc *sc; + const char *prov; + char param[16]; + int i; + + for (i = 0; i < *nargs; i++) { + snprintf(param, sizeof(param), "arg%u", i); + prov = gctl_get_asciiparam(req, param); + + sc = g_eli_find_device(mp, prov); + if (sc == NULL) { + G_ELI_DEBUG(1, "No such provider: %s.", prov); + continue; + } + error = g_eli_kill_one(sc); + if (error != 0) + gctl_error(req, "Not fully done."); + } + } +} + +void +g_eli_config(struct gctl_req *req, struct g_class *mp, const char *verb) +{ + uint32_t *version; + + g_topology_assert(); + + version = gctl_get_paraml(req, "version", sizeof(*version)); + if (version == NULL) { + gctl_error(req, "No '%s' argument.", "version"); + return; + } + if (*version != G_ELI_VERSION) { + gctl_error(req, "Userland and kernel parts are out of sync."); + return; + } + + if (strcmp(verb, "attach") == 0) + g_eli_ctl_attach(req, mp); + else if (strcmp(verb, "detach") == 0 || strcmp(verb, "stop") == 0) + g_eli_ctl_detach(req, mp); + else if (strcmp(verb, "onetime") == 0) + g_eli_ctl_onetime(req, mp); + else if (strcmp(verb, "setkey") == 0) + g_eli_ctl_setkey(req, mp); + else if (strcmp(verb, "delkey") == 0) + g_eli_ctl_delkey(req, mp); + else if (strcmp(verb, "kill") == 0) + g_eli_ctl_kill(req, mp); + else + gctl_error(req, "Unknown verb."); +} diff --git a/sys/geom/eli/g_eli_key.c b/sys/geom/eli/g_eli_key.c new file mode 100644 index 000000000000..79a04e82307c --- /dev/null +++ b/sys/geom/eli/g_eli_key.c @@ -0,0 +1,179 @@ +/*- + * Copyright (c) 2005 Pawel Jakub Dawidek + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#ifdef _KERNEL +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#endif + +#include + + +/* + * Verify if the given 'key' is correct. + * Return 1 if it is correct and 0 otherwise. + */ +static int +g_eli_mkey_verify(const unsigned char *mkey, const unsigned char *key) +{ + const unsigned char *odhmac; /* On-disk HMAC. */ + unsigned char chmac[SHA512_MDLEN]; /* Calculated HMAC. */ + unsigned char hmkey[SHA512_MDLEN]; /* Key for HMAC. */ + + /* + * The key for HMAC calculations is: hmkey = HMAC_SHA512(Derived-Key, 0) + */ + g_eli_crypto_hmac(key, G_ELI_USERKEYLEN, "\x00", 1, hmkey, 0); + + odhmac = mkey + G_ELI_DATAIVKEYLEN; + + /* Calculate HMAC from Data-Key and IV-Key. */ + g_eli_crypto_hmac(hmkey, sizeof(hmkey), mkey, G_ELI_DATAIVKEYLEN, + chmac, 0); + + bzero(hmkey, sizeof(hmkey)); + + /* + * Compare calculated HMAC with HMAC from metadata. + * If two HMACs are equal, 'key' is correct. + */ + return (!bcmp(odhmac, chmac, SHA512_MDLEN)); +} + +/* + * Calculate HMAC from Data-Key and IV-Key. + */ +void +g_eli_mkey_hmac(unsigned char *mkey, const unsigned char *key) +{ + unsigned char hmkey[SHA512_MDLEN]; /* Key for HMAC. */ + unsigned char *odhmac; /* On-disk HMAC. */ + + /* + * The key for HMAC calculations is: hmkey = HMAC_SHA512(Derived-Key, 0) + */ + g_eli_crypto_hmac(key, G_ELI_USERKEYLEN, "\x00", 1, hmkey, 0); + + odhmac = mkey + G_ELI_DATAIVKEYLEN; + /* Calculate HMAC from Data-Key and IV-Key. */ + g_eli_crypto_hmac(hmkey, sizeof(hmkey), mkey, G_ELI_DATAIVKEYLEN, + odhmac, 0); + + bzero(hmkey, sizeof(hmkey)); +} + +/* + * Find and decrypt Master Key encrypted with 'key'. + * Return decrypted Master Key number in 'nkeyp' if not NULL. + * Return 0 on success, > 0 on failure, -1 on bad key. + */ +int +g_eli_mkey_decrypt(const struct g_eli_metadata *md, const unsigned char *key, + unsigned char *mkey, unsigned *nkeyp) +{ + unsigned char tmpmkey[G_ELI_MKEYLEN]; + unsigned char enckey[SHA512_MDLEN]; /* Key for encryption. */ + const unsigned char *mmkey; + int bit, error, nkey; + + if (nkeyp != NULL) + *nkeyp = -1; + + /* + * The key for encryption is: enckey = HMAC_SHA512(Derived-Key, 1) + */ + g_eli_crypto_hmac(key, G_ELI_USERKEYLEN, "\x01", 1, enckey, 0); + + mmkey = md->md_mkeys; + nkey = 0; + for (nkey = 0; nkey < G_ELI_MAXMKEYS; nkey++, mmkey += G_ELI_MKEYLEN) { + bit = (1 << nkey); + if ((md->md_keys & bit) == 0) + continue; + bcopy(mmkey, tmpmkey, G_ELI_MKEYLEN); + error = g_eli_crypto_decrypt(md->md_algo, tmpmkey, + G_ELI_MKEYLEN, enckey, md->md_keylen); + if (error != 0) { + bzero(tmpmkey, sizeof(tmpmkey)); + bzero(enckey, sizeof(enckey)); + return (error); + } + if (g_eli_mkey_verify(tmpmkey, key)) { + bcopy(tmpmkey, mkey, G_ELI_DATAIVKEYLEN); + bzero(tmpmkey, sizeof(tmpmkey)); + bzero(enckey, sizeof(enckey)); + if (nkeyp != NULL) + *nkeyp = nkey; + return (0); + } + } + bzero(enckey, sizeof(enckey)); + bzero(tmpmkey, sizeof(tmpmkey)); + return (-1); +} + +/* + * Encrypt the Master-Key and calculate HMAC to be able to verify it in the + * future. + */ +int +g_eli_mkey_encrypt(unsigned algo, const unsigned char *key, unsigned keylen, + unsigned char *mkey) +{ + unsigned char enckey[SHA512_MDLEN]; /* Key for encryption. */ + int error; + + /* + * To calculate HMAC, the whole key (G_ELI_USERKEYLEN bytes long) will + * be used. + */ + g_eli_mkey_hmac(mkey, key); + /* + * The key for encryption is: enckey = HMAC_SHA512(Derived-Key, 1) + */ + g_eli_crypto_hmac(key, G_ELI_USERKEYLEN, "\x01", 1, enckey, 0); + /* + * Encrypt the Master-Key and HMAC() result with the given key (this + * time only 'keylen' bits from the key are used). + */ + error = g_eli_crypto_encrypt(algo, mkey, G_ELI_MKEYLEN, enckey, keylen); + + bzero(enckey, sizeof(enckey)); + + return (error); +} diff --git a/sys/geom/eli/pkcs5v2.c b/sys/geom/eli/pkcs5v2.c new file mode 100644 index 000000000000..5fcd04793c3c --- /dev/null +++ b/sys/geom/eli/pkcs5v2.c @@ -0,0 +1,123 @@ +/*- + * Copyright (c) 2005 Pawel Jakub Dawidek + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#ifdef _KERNEL +#include +#include +#else +#include +#include +#include +#endif + +#include +#include + +static __inline void +xor(uint8_t *dst, const uint8_t *src, size_t size) +{ + + for (; size > 0; size--) + *dst++ ^= *src++; +} + +void +pkcs5v2_genkey(uint8_t *key, unsigned keylen, const uint8_t *salt, + size_t saltsize, const char *passphrase, u_int iterations) +{ + uint8_t md[SHA512_MDLEN], saltcount[saltsize + sizeof(uint32_t)]; + uint8_t *counter, *keyp; + u_int i, bsize, passlen; + uint32_t count; + + passlen = strlen(passphrase); + bzero(key, keylen); + bcopy(salt, saltcount, saltsize); + counter = saltcount + saltsize; + + keyp = key; + for (count = 1; keylen > 0; count++, keylen -= bsize, keyp += bsize) { + bsize = MIN(keylen, sizeof(md)); + + counter[0] = (count >> 24) & 0xff; + counter[1] = (count >> 16) & 0xff; + counter[2] = (count >> 8) & 0xff; + counter[3] = count & 0xff; + g_eli_crypto_hmac(passphrase, passlen, saltcount, + sizeof(saltcount), md, 0); + xor(keyp, md, bsize); + + for(i = 1; i < iterations; i++) { + g_eli_crypto_hmac(passphrase, passlen, md, sizeof(md), + md, 0); + xor(keyp, md, bsize); + } + } +} + +#ifndef _KERNEL +/* + * Return the number of microseconds needed for 'interations' iterations. + */ +static int +pkcs5v2_probe(int iterations) +{ + uint8_t key[G_ELI_USERKEYLEN], salt[G_ELI_SALTLEN]; + uint8_t passphrase[] = "passphrase"; + struct rusage start, end; + int usecs; + + getrusage(RUSAGE_SELF, &start); + pkcs5v2_genkey(key, sizeof(key), salt, sizeof(salt), passphrase, + iterations); + getrusage(RUSAGE_SELF, &end); + + usecs = end.ru_utime.tv_sec - start.ru_utime.tv_sec; + usecs *= 1000000; + usecs += end.ru_utime.tv_usec - start.ru_utime.tv_usec; + return (usecs); +} + +/* + * Return the number of iterations which takes 'usecs' microseconds. + */ +int +pkcs5v2_calculate(int usecs) +{ + int iterations, v; + + for (iterations = 1; ; iterations <<= 1) { + v = pkcs5v2_probe(iterations); + if (v > 2000000) + break; + } + return (((intmax_t)iterations * (intmax_t)usecs) / v); +} +#endif /* !_KERNEL */ diff --git a/sys/geom/eli/pkcs5v2.h b/sys/geom/eli/pkcs5v2.h new file mode 100644 index 000000000000..83691ac9f254 --- /dev/null +++ b/sys/geom/eli/pkcs5v2.h @@ -0,0 +1,36 @@ +/*- + * Copyright (c) 2005 Pawel Jakub Dawidek + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _PKCS5V2_H_ +#define _PKCS5V2_H_ +void pkcs5v2_genkey(uint8_t *key, unsigned keylen, const uint8_t *salt, + size_t saltsize, const char *passphrase, u_int iterations); +#ifndef _KERNEL +int pkcs5v2_calculate(int usecs); +#endif +#endif /* !_PKCS5V2_H_ */ diff --git a/sys/modules/geom/geom_eli/Makefile b/sys/modules/geom/geom_eli/Makefile new file mode 100644 index 000000000000..20db77d5b703 --- /dev/null +++ b/sys/modules/geom/geom_eli/Makefile @@ -0,0 +1,9 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../../geom/eli + +KMOD= geom_eli +SRCS= g_eli.c g_eli_crypto.c g_eli_ctl.c g_eli_key.c pkcs5v2.c +WARNS?= 2 + +.include