summaryrefslogtreecommitdiffstats
path: root/sbin
diff options
context:
space:
mode:
authorpjd <pjd@FreeBSD.org>2005-07-27 21:43:37 +0000
committerpjd <pjd@FreeBSD.org>2005-07-27 21:43:37 +0000
commit57922fa5cc21f455226d4725d6b6d303710b8534 (patch)
tree08d6bf97fadbec3f76872d8ae88345ff02534c94 /sbin
parent52bcb6f38c0a472abe10e970cbfe0b6d5e4d94bc (diff)
downloadFreeBSD-src-57922fa5cc21f455226d4725d6b6d303710b8534.zip
FreeBSD-src-57922fa5cc21f455226d4725d6b6d303710b8534.tar.gz
Add GEOM_ELI class which provides GEOM providers encryption.
For features list and usage see manual page: geli(8). Sponsored by: Wheel Sp. z o.o. http://www.wheel.pl MFC after: 1 week
Diffstat (limited to 'sbin')
-rw-r--r--sbin/geom/class/eli/Makefile17
-rw-r--r--sbin/geom/class/eli/geli.8511
-rw-r--r--sbin/geom/class/eli/geom_eli.c1246
3 files changed, 1774 insertions, 0 deletions
diff --git a/sbin/geom/class/eli/Makefile b/sbin/geom/class/eli/Makefile
new file mode 100644
index 0000000..18f3869
--- /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 <bsd.lib.mk>
diff --git a/sbin/geom/class/eli/geli.8 b/sbin/geom/class/eli/geli.8
new file mode 100644
index 0000000..edf11e7
--- /dev/null
+++ b/sbin/geom/class/eli/geli.8
@@ -0,0 +1,511 @@
+.\" Copyright (c) 2005 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.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 0000000..f01f557
--- /dev/null
+++ b/sbin/geom/class/eli/geom_eli.c
@@ -0,0 +1,1246 @@
+/*-
+ * Copyright (c) 2004 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <readpassphrase.h>
+#include <string.h>
+#include <strings.h>
+#include <libgeom.h>
+#include <paths.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <opencrypto/cryptodev.h>
+#include <geom/eli/g_eli.h>
+#include <geom/eli/pkcs5v2.h>
+
+#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", &sectorsize, 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", &sectorsize, 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", &sectorsize, 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");
+ }
+}
OpenPOWER on IntegriCloud