Date: Tue, 26 Oct 2010 23:06:54 +0000 (UTC) From: Pawel Jakub Dawidek <pjd@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-8@freebsd.org Subject: svn commit: r214405 - in stable/8: sbin/geom/class/eli sbin/geom/class/sched sbin/geom/core sys/geom/eli Message-ID: <201010262306.o9QN6sWp076792@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: pjd Date: Tue Oct 26 23:06:53 2010 New Revision: 214405 URL: http://svn.freebsd.org/changeset/base/214405 Log: MFC r202976,r211927,r212845,r212846,r212934,r213055,r213056,r213057,r213058, r213059,r213060,r213062,r213063,r213067,r213070,r213071,r213072,r213073, r213164,r213165,r213172,r214116,r214118,r214133,r214163,r214225,r214226, r214227,r214228,r214229,r214404: r202976 (by trasz): Remove pointless assignment. Found with: clang r211927: Correct offset conversion to little endian. It was implemented in version 2, but because of a bug it was a no-op, so we were still using offsets in native byte order for the host. Do it properly this time, bump version to 4 and set the G_ELI_FLAG_NATIVE_BYTE_ORDER flag when version is under 4. Reported by: ivoras r212845 (by brian): Support attaching version 4 metadata Reviewed by: pjd r212846: Fix indent. r212934 (by brian): Add a geli resize subcommand to resize encrypted filesystems prior to growing the filesystem. Refuse to attach providers where the metadata provider size is wrong. This makes post-boot attaches behave consistently with pre-boot attaches. Also refuse to restore metadata to a provider of the wrong size without the new -f switch. The new -f switch forces the metadata restoration despite the provider size, and updates the provider size in the restored metadata to the correct value. Helped by: pjd Reviewed by: pjd r213055: When trashing metadata, flush after each write. r213056: Simplify code a bit by using g_*() API from libgeom. r213057: - Make use of g_*() API. - Flush cache after writing metadata. r213058: Because we first write metadata into new place and then trash old place we don't want situation where old size is equal to new size, as we will trash newly written metadata. r213059: - Use g_*() API when doing backups. - fsync() created files. r213060: - When trashing metadata, repeat overwrite kern.geom.eli.overwrites times. - Flush write cache after each write. r213062: Define default overwrite count, so that userland can use it. r213063: Make the code similar to the code in g_eli_integrity.c. r213067: Implement switching of data encryption key every 2^20 blocks. This ensures the same encryption key won't be used for more than 2^20 blocks (sectors). This will be the default now. r213070: Add support for AES-XTS. This will be the default now. r213071: Document AES-XTS. r213072: Update copyright years. r213073: Update copyright years. r213164: Ignore errors from BIO_FLUSH. It might confuse users that provider wasn't really killed. What we really care about are write errors only. r213165: Change g_eli_debug to int, so one can turn off any GELI output by setting kern.geom.eli.debug sysctl to -1. r213172: - Add support for loading passphrase from a file (-J and -j options). This is especially useful for things like installers, where regular geli prompt can't be used. - Add support for specifing multiple -K or -k options, so there is no need to cat all keyfiles and read them from standard input. Requested by: Kris Moore <kris@pcbsd.org>, thompsa r214116: - Add missing comments. - Make a comment consistent with others. r214118: Bring in geli suspend/resume functionality (finally). Before this change if you wanted to suspend your laptop and be sure that your encryption keys are safe, you had to stop all processes that use file system stored on encrypted device, unmount the file system and detach geli provider. This isn't very handy. If you are a lucky user of a laptop where suspend/resume actually works with FreeBSD (I'm not!) you most likely want to suspend your laptop, because you don't want to start everything over again when you turn your laptop back on. And this is where geli suspend/resume steps in. When you execute: # geli suspend -a geli will wait for all in-flight I/O requests, suspend new I/O requests, remove all geli sensitive data from the kernel memory (like encryption keys) and will wait for either 'geli resume' or 'geli detach'. Now with no keys in memory you can suspend your laptop without stopping any processes or unmounting any file systems. When you resume your laptop you have to resume geli devices using 'geli resume' command. You need to provide your passphrase, etc. again so the keys can be restored and suspended I/O requests released. Of course you need to remember that 'geli suspend' won't clear file system cache and other places where data from your geli-encrypted file system might be present. But to get rid of those stopping processes and unmounting file system won't help either - you have to turn your laptop off. Be warned. Also note, that suspending geli device which contains file system with geli utility (or anything used by 'geli resume') is not very good idea, as you won't be able to resume it - when you execute geli(8), the kernel will try to read it and this read I/O request will be suspended. r214133: Fix a bug introduced in r213067 where we use authentication key before initializing it. r214163: Free opencrypto sessions on suspend, as they also might keep encryption keys. r214225: Move sc_akeyctx and sc_ivctx initialization to the g_eli_mkey_propagate() function which eliminates code duplication and will ensure proper order of operation. r214226: Encryption keys array might be NULL if device is suspended. Check for this, so we don't panic when we detach suspended device. r214227: Add State tag, so 'geli status' will report active/suspended status, eg: # geli status Name Status Components da0.eli SUSPENDED da0 da1.eli ACTIVE da1 r214228: Close a race between checking if device is already suspended and suspending it. r214229: - Improve error messages, so instead of 'Not fully done', the user will get information that device is already suspended or that device is using one-time key and suspend is not supported. - 'geli suspend -a' silently skips devices that use one-time key, this is fine, but because we log which device were suspended on the console, log also which devices were skipped. r214404: Use fprintf(stderr) instead of gctl_error() to print a warning about too big sector size. When gctl error is set gctl_has_param() always returns 'false', which prevents geli(8) from finding some arguments and also masks an error, which is generates in such case. Modified: stable/8/sbin/geom/class/eli/geli.8 stable/8/sbin/geom/class/eli/geom_eli.c stable/8/sbin/geom/core/geom.c stable/8/sys/geom/eli/g_eli.c stable/8/sys/geom/eli/g_eli.h stable/8/sys/geom/eli/g_eli_crypto.c stable/8/sys/geom/eli/g_eli_ctl.c stable/8/sys/geom/eli/g_eli_integrity.c stable/8/sys/geom/eli/g_eli_key.c stable/8/sys/geom/eli/g_eli_privacy.c Directory Properties: stable/8/sbin/geom/ (props changed) stable/8/sbin/geom/class/part/ (props changed) stable/8/sbin/geom/class/sched/gsched.8 (props changed) stable/8/sbin/geom/class/stripe/ (props changed) stable/8/sys/ (props changed) stable/8/sys/amd64/include/xen/ (props changed) stable/8/sys/cddl/contrib/opensolaris/ (props changed) stable/8/sys/contrib/dev/acpica/ (props changed) stable/8/sys/contrib/pf/ (props changed) stable/8/sys/dev/xen/xenpci/ (props changed) Modified: stable/8/sbin/geom/class/eli/geli.8 ============================================================================== --- stable/8/sbin/geom/class/eli/geli.8 Tue Oct 26 22:46:15 2010 (r214404) +++ stable/8/sbin/geom/class/eli/geli.8 Tue Oct 26 23:06:53 2010 (r214405) @@ -1,4 +1,4 @@ -.\" Copyright (c) 2005-2008 Pawel Jakub Dawidek <pjd@FreeBSD.org> +.\" Copyright (c) 2005-2010 Pawel Jakub Dawidek <pjd@FreeBSD.org> .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd August 29, 2008 +.Dd October 20, 2010 .Dt GELI 8 .Os .Sh NAME @@ -56,6 +56,7 @@ utility: .Op Fl B Ar backupfile .Op Fl e Ar ealgo .Op Fl i Ar iterations +.Op Fl J Ar newpassfile .Op Fl K Ar newkeyfile .Op Fl l Ar keylen .Op Fl s Ar sectorsize @@ -66,6 +67,7 @@ utility: .Nm .Cm attach .Op Fl dprv +.Op Fl j Ar passfile .Op Fl k Ar keyfile .Ar prov .Nm @@ -91,6 +93,8 @@ utility: .Cm setkey .Op Fl pPv .Op Fl i Ar iterations +.Op Fl j Ar passfile +.Op Fl J Ar newpassfile .Op Fl k Ar keyfile .Op Fl K Ar newkeyfile .Op Fl n Ar keyno @@ -111,10 +115,25 @@ utility: .Ar file .Nm .Cm restore -.Op Fl v +.Op Fl fv .Ar file .Ar prov .Nm +.Cm suspend +.Op Fl v +.Fl a | Ar prov ... +.Nm +.Cm resume +.Op Fl pv +.Op Fl j Ar passfile +.Op Fl k Ar keyfile +.Ar prov +.Nm +.Cm resize +.Op Fl v +.Fl s Ar oldsize +.Ar prov +.Nm .Cm clear .Op Fl v .Ar prov ... @@ -146,11 +165,12 @@ framework, so when there is crypto hardw will make use of it automatically. .It Supports many cryptographic algorithms (currently -.Nm AES , -.Nm Blowfish , -.Nm Camellia +.Nm AES-XTS , +.Nm AES-CBC , +.Nm Blowfish-CBC , +.Nm Camellia-CBC and -.Nm 3DES ) . +.Nm 3DES-CBC ) . .It Can optionally perform data authentication (integrity verification) utilizing one of the following algorithms: @@ -197,6 +217,8 @@ Allows to attach a provider with a rando partitions and temporary file systems. .It Allows to verify data integrity (data authentication). +.It +Allows to suspend and resume encrypted devices. .El .Pp The first argument to @@ -217,7 +239,7 @@ The metadata can be recovered with the subcommand described below. .Pp Additional options include: -.Bl -tag -width ".Fl a Ar aalgo" +.Bl -tag -width ".Fl J Ar newpassfile" .It Fl a Ar aalgo Enable data integrity verification (authentication) using the given algorithm. This will reduce size of available storage and also reduce speed. @@ -252,39 +274,44 @@ as the .It Fl e Ar ealgo Encryption algorithm to use. Currently supported algorithms are: -.Nm AES , -.Nm Blowfish , -.Nm Camellia +.Nm AES-XTS , +.Nm AES-CBC , +.Nm Blowfish-CBC , +.Nm Camellia-CBC and -.Nm 3DES . +.Nm 3DES-CBC . The default and recommended algorithm is -.Nm AES . +.Nm AES-XTS . .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 J Ar newpassfile +Specifies a file which contains the passphrase or its part. +If +.Ar newpassfile +is given as -, standard input will be used. +Only the first line (excluding new-line character) is taken from the given file. +This argument can be specified multiple times. .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 a key component can be used: -.Bd -literal -offset indent -# cat key1 key2 key3 | geli init -K - /dev/da0 -.Ed +This argument can be specified multiple times. .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 , -128 for -.Nm Camellia +.Nm AES-XTS , +.Nm AES-CBC , +.Nm Blowfish-CBC +and +.Nm Camellia-CBC and 192 for -.Nm 3DES . +.Nm 3DES-CBC . .It Fl P Do not use passphrase as the key component. .It Fl s Ar sectorsize @@ -302,7 +329,7 @@ provider's name with an suffix. .Pp Additional options include: -.Bl -tag -width ".Fl a Ar algo" +.Bl -tag -width ".Fl j Ar passfile" .It Fl d If specified, a decrypted provider will be detached automatically on last close. This can help with short memory - user does not have to remember to detach the @@ -314,6 +341,13 @@ Probably a better choice is the option for the .Cm detach subcommand. +.It Fl j Ar passfile +Specifies a file which contains the passphrase or its part. +For more information see the description of the +.Fl J +option for the +.Cm init +subcommand. .It Fl k Ar keyfile Specifies a file which contains part of the key. For more information see the description of the @@ -332,7 +366,7 @@ Detach the given providers, which means and clear the keys from memory. .Pp Additional options include: -.Bl -tag -width ".Fl a Ar algo" +.Bl -tag -width ".Fl f" .It Fl f Force detach - detach even if the provider is open. .It Fl l @@ -347,7 +381,7 @@ Attach the given providers with random, The command can be used to encrypt swap partitions or temporary file systems. .Pp Additional options include: -.Bl -tag -width ".Fl a Ar aalgo" +.Bl -tag -width ".Fl a Ar sectorsize" .It Fl a Ar aalgo Enable data integrity verification (authentication). For more information, see the description of the @@ -402,13 +436,17 @@ When a provider is attached, the user do an old passphrase/keyfile. .Pp Additional options include: -.Bl -tag -width ".Fl a Ar algo" +.Bl -tag -width ".Fl J Ar newpassfile" .It Fl i Ar iterations Number of iterations to use with PKCS#5v2. If 0 is given, PKCS#5v2 will not be used. To be able to use this option with .Cm setkey subcommand, only one key have to be defined and this key has to be changed. +.It Fl j Ar passfile +Specifies a file which contains the old passphrase or its part. +.It Fl J Ar newpassfile +Specifies a file which contains the new passphrase or its part. .It Fl k Ar keyfile Specifies a file which contains part of the old key. .It Fl K Ar newkeyfile @@ -432,7 +470,9 @@ will not be detached even if all keys wi It can be even rescued with the .Cm setkey subcommand. -.Bl -tag -width ".Fl a Ar algo" +.Pp +Additional options include: +.Bl -tag -width ".Fl a Ar keyno" .It Fl a Destroy all keys (does not need .Fl f @@ -456,7 +496,9 @@ backup, your data is gone for good. In case the provider was attached with the .Fl r flag, the keys will not be destroyed, only the provider will be detached. -.Bl -tag -width ".Fl a Ar algo" +.Pp +Additional options include: +.Bl -tag -width ".Fl a" .It Fl a If specified, all currently attached providers will be killed. .El @@ -464,6 +506,97 @@ If specified, all currently attached pro Backup metadata from the given provider to the given file. .It Cm restore Restore metadata from the given file to the given provider. +.Pp +Additional options include: +.Bl -tag -width ".Fl f" +.It Fl f +Metadata contains the size of the provider to ensure that the correct +partition or slice is attached. +If an attempt is made to restore metadata to a provider that has a different +size, +.Nm +will refuse to restore the data unless the +.Fl f +switch is used. +If the partition or slice has been grown, the +.Cm resize +subcommand should be used rather than attempting to relocate the metadata +through +.Cm backup +and +.Cm restore . +.El +.It Cm suspend +Suspend device by waiting for all inflight request to finish, clearing all +sensitive informations (like keys) from the kernel memory and blocking all +further I/O requests until the +.Cm resume +subcommand is executed. +This functionality is useful for eg. laptops - when one wants to suspend a +laptop, one does not want to leave encrypted device attached. +Instead of closing all files and directories opened from a file system placed +on an encrypted device, unmounting the file system and detaching the device, +the +.Cm suspend +subcommand can be used. +Any access to the encrypted device will be blocked until the keys are +recovered through +.Cm resume +subcommand, thus there is no need to close nor unmount anything. +The +.Cm suspend +subcommand does not work with devices created with the +.Cm onetime +subcommand. +Please note that sensitive data might still be present in memory after +suspending encrypted device, because of file system cache, etc. +.Pp +Additional options include: +.Bl -tag -width ".Fl a" +.It Fl a +Suspend all +.Nm +devices. +.El +.It Cm resume +Resume previously suspended device. +The caller must ensure that executing this subcommand won't try to access +suspended device, which will lead to a deadlock. +For example suspending device, which contains file system where the +.Nm +utility is stored is bad idea. +.Pp +Additional options include: +.Bl -tag -width ".Fl j Ar passfile" +.It Fl j Ar passfile +Specifies a file which contains the passphrase or its part. +For more information see the description of the +.Fl J +option for the +.Cm init +subcommand. +.It Fl k Ar keyfile +Specifies a file which contains part of the key. +For more information see the description of the +.Fl K +option for the +.Cm init +subcommand. +.It Fl p +Do not use passphrase as the key component. +.El +.It Cm resize +Inform +.Nm +that the provider has been resized. +The old metadata block is relocated to the correct position at the end of the +provider and the provider size is updated. +.Pp +Additional options include: +.Bl -tag -width ".Fl s Ar oldsize" +.It Fl s Ar oldsize +The size of the provider before it was resized. +.El .It Cm clear Clear metadata from the given providers. .It Cm dump @@ -603,7 +736,7 @@ keyfile: # dd if=/dev/random of=/boot/keys/da0.key0 bs=32k count=1 # dd if=/dev/random of=/boot/keys/da0.key1 bs=32k count=1 # dd if=/dev/random of=/boot/keys/da0.key2 bs=32k count=1 -# cat /boot/keys/da0.key0 /boot/keys/da0.key1 /boot/keys/da0.key2 | geli init -b -K - da0 +# geli init -b -K /boot/keys/da0.key0 -K /boot/keys/da0.key1 -K /boot/keys/da0.key2 da0 Enter new passphrase: Reenter new passphrase: # dd if=/dev/random of=/dev/da1s3a bs=1m @@ -665,6 +798,61 @@ geli: Cannot read metadata from /dev/da0 # geli attach /dev/da0 Enter passphrase: .Ed +.Pp +If an encrypted filesystem is extended, it is necessary to relocate and +update the metadata: +.Bd -literal -offset indent +# gpart create -s GPT ada0 +# gpart add -s 1g -t freebsd-ufs -i 1 ada0 +# geli init -K keyfile -P ada0p1 +# gpart resize -s 2g -i 1 ada0 +# geli resize -s 1g ada0p1 +# geli attach -k keyfile -p ada0p1 +.Ed +.Pp +Initialize provider with passphrase split into two files. +The provider can be attached by giving those two files or by giving +.Dq foobar +passphrase on +.Nm +prompt: +.Bd -literal -offset indent +# echo foo > da0.pass0 +# echo bar > da0.pass1 +# geli init -J da0.pass0 -J da0.pass1 da0 +# geli attach -j da0.pass0 -j da0.pass1 da0 +# geli detach da0 +# geli attach da0 +Enter passphrase: foobar +.Ed +.Pp +Suspend all +.Nm +devices, suspend a laptop, then resume devices one by one after resuming a +laptop: +.Bd -literal -offset indent +# geli suspend -a +# zzz +<resume your laptop> +# geli resume -p -k keyfile gpt/secret +# geli resume gpt/private +Enter passphrase: +.Ed +.Sh ENCRYPTION MODES +.Nm +supports two encryption modes: +.Nm XTS , +which was standarized as +.Nm IEE P1619 +and +.Nm CBC +with unpredictable IV. +The +.Nm CBC +mode used by +.Nm +is very similar to the mode +.Nm ESSIV . .Sh DATA AUTHENTICATION .Nm can verify data integrity when an authentication algorithm is specified. Modified: stable/8/sbin/geom/class/eli/geom_eli.c ============================================================================== --- stable/8/sbin/geom/class/eli/geom_eli.c Tue Oct 26 22:46:15 2010 (r214404) +++ stable/8/sbin/geom/class/eli/geom_eli.c Tue Oct 26 23:06:53 2010 (r214405) @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2004-2008 Pawel Jakub Dawidek <pjd@FreeBSD.org> + * Copyright (c) 2004-2010 Pawel Jakub Dawidek <pjd@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -27,6 +27,10 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); +#include <sys/types.h> +#include <sys/sysctl.h> + +#include <stdbool.h> #include <stdio.h> #include <stdint.h> #include <stdlib.h> @@ -63,9 +67,11 @@ static void eli_attach(struct gctl_req * static void eli_configure(struct gctl_req *req); static void eli_setkey(struct gctl_req *req); static void eli_delkey(struct gctl_req *req); +static void eli_resume(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_resize(struct gctl_req *req); static void eli_clear(struct gctl_req *req); static void eli_dump(struct gctl_req *req); @@ -75,18 +81,21 @@ static int eli_backup_create(struct gctl /* * Available commands: * - * init [-bhPv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-K newkeyfile] prov + * init [-bhPv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] prov * label - alias for 'init' - * attach [-dprv] [-k keyfile] prov + * attach [-dprv] [-j passfile] [-k keyfile] prov * detach [-fl] prov ... * stop - alias for 'detach' * onetime [-d] [-a aalgo] [-e ealgo] [-l keylen] prov * configure [-bB] prov ... - * setkey [-pPv] [-n keyno] [-k keyfile] [-K newkeyfile] prov + * setkey [-pPv] [-n keyno] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov * delkey [-afv] [-n keyno] prov + * suspend [-v] -a | prov ... + * resume [-pv] [-j passfile] [-k keyfile] prov * kill [-av] [prov ...] * backup [-v] prov file - * restore [-v] file prov + * restore [-fv] file prov + * resize [-v] -s oldsize prov * clear [-v] prov ... * dump [-v] prov ... */ @@ -98,13 +107,15 @@ struct g_command class_commands[] = { { 'B', "backupfile", "", G_TYPE_STRING }, { 'e', "ealgo", GELI_ENC_ALGO, G_TYPE_STRING }, { 'i', "iterations", "-1", G_TYPE_NUMBER }, - { 'K', "newkeyfile", "", G_TYPE_STRING }, + { 'J', "newpassfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, + { 'K', "newkeyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, { 'l', "keylen", "0", G_TYPE_NUMBER }, { 'P', "nonewpassphrase", NULL, G_TYPE_BOOL }, { 's', "sectorsize", "0", G_TYPE_NUMBER }, G_OPT_SENTINEL }, - NULL, "[-bPv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-K newkeyfile] [-s sectorsize] prov" + NULL, + "[-bPv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] [-s sectorsize] prov" }, { "label", G_FLAG_VERBOSE, eli_main, { @@ -113,23 +124,27 @@ struct g_command class_commands[] = { { 'B', "backupfile", "", G_TYPE_STRING }, { 'e', "ealgo", GELI_ENC_ALGO, G_TYPE_STRING }, { 'i', "iterations", "-1", G_TYPE_NUMBER }, - { 'K', "newkeyfile", "", G_TYPE_STRING }, + { 'J', "newpassfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, + { 'K', "newkeyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, { 'l', "keylen", "0", G_TYPE_NUMBER }, { 'P', "nonewpassphrase", NULL, G_TYPE_BOOL }, { 's', "sectorsize", "0", G_TYPE_NUMBER }, G_OPT_SENTINEL }, - NULL, "- an alias for 'init'" + NULL, + "- an alias for 'init'" }, { "attach", G_FLAG_VERBOSE | G_FLAG_LOADKLD, eli_main, { { 'd', "detach", NULL, G_TYPE_BOOL }, - { 'k', "keyfile", "", G_TYPE_STRING }, + { 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, + { 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, { 'p', "nopassphrase", NULL, G_TYPE_BOOL }, { 'r', "readonly", NULL, G_TYPE_BOOL }, G_OPT_SENTINEL }, - NULL, "[-dprv] [-k keyfile] prov" + NULL, + "[-dprv] [-j passfile] [-k keyfile] prov" }, { "detach", 0, NULL, { @@ -137,7 +152,8 @@ struct g_command class_commands[] = { { 'l', "last", NULL, G_TYPE_BOOL }, G_OPT_SENTINEL }, - NULL, "[-fl] prov ..." + NULL, + "[-fl] prov ..." }, { "stop", 0, NULL, { @@ -145,7 +161,8 @@ struct g_command class_commands[] = { { 'l', "last", NULL, G_TYPE_BOOL }, G_OPT_SENTINEL }, - NULL, "- an alias for 'detach'" + NULL, + "- an alias for 'detach'" }, { "onetime", G_FLAG_VERBOSE | G_FLAG_LOADKLD, NULL, { @@ -156,7 +173,8 @@ struct g_command class_commands[] = { { 's', "sectorsize", "0", G_TYPE_NUMBER }, G_OPT_SENTINEL }, - NULL, "[-d] [-a aalgo] [-e ealgo] [-l keylen] [-s sectorsize] prov" + NULL, + "[-d] [-a aalgo] [-e ealgo] [-l keylen] [-s sectorsize] prov" }, { "configure", G_FLAG_VERBOSE, eli_main, { @@ -164,19 +182,23 @@ struct g_command class_commands[] = { { 'B', "noboot", NULL, G_TYPE_BOOL }, G_OPT_SENTINEL }, - NULL, "[-bB] prov ..." + NULL, + "[-bB] prov ..." }, { "setkey", G_FLAG_VERBOSE, eli_main, { { 'i', "iterations", "-1", G_TYPE_NUMBER }, - { 'k', "keyfile", "", G_TYPE_STRING }, - { 'K', "newkeyfile", "", G_TYPE_STRING }, + { 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, + { 'J', "newpassfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, + { 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, + { 'K', "newkeyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, { 'n', "keyno", "-1", G_TYPE_NUMBER }, { 'p', "nopassphrase", NULL, G_TYPE_BOOL }, { 'P', "nonewpassphrase", NULL, G_TYPE_BOOL }, G_OPT_SENTINEL }, - NULL, "[-pPv] [-n keyno] [-i iterations] [-k keyfile] [-K newkeyfile] prov" + NULL, + "[-pPv] [-n keyno] [-i iterations] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov" }, { "delkey", G_FLAG_VERBOSE, eli_main, { @@ -185,20 +207,53 @@ struct g_command class_commands[] = { { 'n', "keyno", "-1", G_TYPE_NUMBER }, G_OPT_SENTINEL }, - NULL, "[-afv] [-n keyno] prov" + NULL, + "[-afv] [-n keyno] prov" + }, + { "suspend", G_FLAG_VERBOSE, NULL, + { + { 'a', "all", NULL, G_TYPE_BOOL }, + G_OPT_SENTINEL + }, + NULL, + "[-v] -a | prov ..." + }, + { "resume", G_FLAG_VERBOSE, eli_main, + { + { 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, + { 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, + { 'p', "nopassphrase", NULL, G_TYPE_BOOL }, + G_OPT_SENTINEL + }, + NULL, + "[-pv] [-j passfile] [-k keyfile] prov" }, { "kill", G_FLAG_VERBOSE, eli_main, { { 'a', "all", NULL, G_TYPE_BOOL }, G_OPT_SENTINEL }, - NULL, "[-av] [prov ...]" + NULL, + "[-av] [prov ...]" }, { "backup", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS, NULL, "[-v] prov file" }, - { "restore", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS, NULL, - "[-v] file prov" + { "restore", G_FLAG_VERBOSE, eli_main, + { + { 'f', "force", NULL, G_TYPE_BOOL }, + G_OPT_SENTINEL + }, + NULL, + "[-fv] file prov" + }, + { "resize", G_FLAG_VERBOSE, eli_main, + { + { 's', "oldsize", NULL, G_TYPE_NUMBER }, + G_OPT_SENTINEL + }, + NULL, + "[-v] -s oldsize prov" }, { "clear", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS, NULL, "[-v] prov ..." @@ -233,7 +288,7 @@ eli_protect(struct gctl_req *req) } static void -eli_main(struct gctl_req *req, unsigned flags) +eli_main(struct gctl_req *req, unsigned int flags) { const char *name; @@ -258,12 +313,16 @@ eli_main(struct gctl_req *req, unsigned eli_setkey(req); else if (strcmp(name, "delkey") == 0) eli_delkey(req); + else if (strcmp(name, "resume") == 0) + eli_resume(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, "resize") == 0) + eli_resize(req); else if (strcmp(name, "dump") == 0) eli_dump(req); else if (strcmp(name, "clear") == 0) @@ -277,7 +336,7 @@ arc4rand(unsigned char *buf, size_t size { uint32_t *buf4; size_t size4; - unsigned i; + unsigned int i; buf4 = (uint32_t *)buf; size4 = size / 4; @@ -306,123 +365,216 @@ eli_is_attached(const char *prov) return (0); } -static unsigned char * -eli_genkey(struct gctl_req *req, struct g_eli_metadata *md, unsigned char *key, - int new) +static int +eli_genkey_files(struct gctl_req *req, bool new, const char *type, + struct hmac_ctx *ctxp, char *passbuf, size_t passbufsize) { - struct hmac_ctx ctx; - const char *str; - int error, nopassphrase; + char *p, buf[MAXPHYS], argname[16]; + const char *file; + int error, fd, i; + ssize_t done; + + assert((strcmp(type, "keyfile") == 0 && ctxp != NULL && + passbuf == NULL && passbufsize == 0) || + (strcmp(type, "passfile") == 0 && ctxp == NULL && + passbuf != NULL && passbufsize > 0)); + assert(strcmp(type, "keyfile") == 0 || passbuf[0] == '\0'); + + for (i = 0; ; i++) { + snprintf(argname, sizeof(argname), "%s%s%d", + new ? "new" : "", type, i); + + /* No more {key,pass}files? */ + if (!gctl_has_param(req, argname)) + return (i); - nopassphrase = - gctl_get_int(req, new ? "nonewpassphrase" : "nopassphrase"); - - g_eli_crypto_hmac_init(&ctx, NULL, 0); - - str = gctl_get_ascii(req, new ? "newkeyfile" : "keyfile"); - if (str[0] == '\0' && nopassphrase) { - gctl_error(req, "No key components given."); - return (NULL); - } else if (str[0] != '\0') { - char buf[MAXPHYS]; - ssize_t done; - int fd; + file = gctl_get_ascii(req, argname); + assert(file != NULL); - if (strcmp(str, "-") == 0) + if (strcmp(file, "-") == 0) fd = STDIN_FILENO; else { - fd = open(str, O_RDONLY); + fd = open(file, O_RDONLY); if (fd == -1) { - gctl_error(req, "Cannot open keyfile %s: %s.", - str, strerror(errno)); - return (NULL); + gctl_error(req, "Cannot open %s %s: %s.", + type, file, strerror(errno)); + return (-1); + } + } + if (strcmp(type, "keyfile") == 0) { + while ((done = read(fd, buf, sizeof(buf))) > 0) + g_eli_crypto_hmac_update(ctxp, buf, done); + } else /* if (strcmp(type, "passfile") == 0) */ { + while ((done = read(fd, buf, sizeof(buf) - 1)) > 0) { + buf[done] = '\0'; + p = strchr(buf, '\n'); + if (p != NULL) { + *p = '\0'; + done = p - buf; + } + if (strlcat(passbuf, buf, passbufsize) >= + passbufsize) { + gctl_error(req, + "Passphrase in %s too long.", file); + bzero(buf, sizeof(buf)); + return (-1); + } + if (p != NULL) + break; } } - while ((done = read(fd, buf, sizeof(buf))) > 0) - g_eli_crypto_hmac_update(&ctx, buf, done); error = errno; - if (strcmp(str, "-") != 0) + if (strcmp(file, "-") != 0) close(fd); bzero(buf, sizeof(buf)); if (done == -1) { - gctl_error(req, "Cannot read keyfile %s: %s.", str, - strerror(error)); - return (NULL); + gctl_error(req, "Cannot read %s %s: %s.", + type, file, strerror(error)); + return (-1); } } + /* NOTREACHED */ +} - if (!nopassphrase) { - char buf1[BUFSIZ], buf2[BUFSIZ], *p; +static int +eli_genkey_passphrase_prompt(struct gctl_req *req, bool new, char *passbuf, + size_t passbufsize) +{ + char *p; - if (!new && md->md_iterations == -1) { - gctl_error(req, "Missing -p flag."); - return (NULL); + for (;;) { + p = readpassphrase( + new ? "Enter new passphrase:" : "Enter passphrase:", + passbuf, passbufsize, RPP_ECHO_OFF | RPP_REQUIRE_TTY); + if (p == NULL) { + bzero(passbuf, passbufsize); + gctl_error(req, "Cannot read passphrase: %s.", + strerror(errno)); + return (-1); } - for (;;) { - p = readpassphrase( - new ? "Enter new passphrase:" : "Enter passphrase:", - buf1, sizeof(buf1), RPP_ECHO_OFF | RPP_REQUIRE_TTY); + + if (new) { + char tmpbuf[BUFSIZ]; + + p = readpassphrase("Reenter new passphrase: ", + tmpbuf, sizeof(tmpbuf), + RPP_ECHO_OFF | RPP_REQUIRE_TTY); if (p == NULL) { - bzero(buf1, sizeof(buf1)); - gctl_error(req, "Cannot read passphrase: %s.", + bzero(passbuf, passbufsize); + gctl_error(req, + "Cannot read passphrase: %s.", strerror(errno)); - return (NULL); + return (-1); } - - 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)); + + if (strcmp(passbuf, tmpbuf) != 0) { + bzero(passbuf, passbufsize); + fprintf(stderr, "They didn't match.\n"); + continue; } - break; + bzero(tmpbuf, sizeof(tmpbuf)); } - /* - * 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); - } + return (0); + } + /* NOTREACHED */ +} + +static int +eli_genkey_passphrase(struct gctl_req *req, struct g_eli_metadata *md, bool new, + struct hmac_ctx *ctxp) +{ + char passbuf[MAXPHYS]; + bool nopassphrase; + int nfiles; + + nopassphrase = + gctl_get_int(req, new ? "nonewpassphrase" : "nopassphrase"); + if (nopassphrase) { + if (gctl_has_param(req, new ? "newpassfile0" : "passfile0")) { + gctl_error(req, + "Options -%c and -%c are mutually exclusive.", + new ? 'J' : 'j', new ? 'P' : 'p'); + return (-1); } - /* - * If md_iterations is equal to 0, user don't want PKCS#5v2. - */ - 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)); + return (0); + } + + if (!new && md->md_iterations == -1) { + gctl_error(req, "Missing -p flag."); + return (-1); + } + passbuf[0] = '\0'; + nfiles = eli_genkey_files(req, new, "passfile", NULL, passbuf, + sizeof(passbuf)); + if (nfiles == -1) + return (-1); + else if (nfiles == 0) { + if (eli_genkey_passphrase_prompt(req, new, passbuf, + sizeof(passbuf)) == -1) { + return (-1); + } + } + /* + * 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); } - bzero(buf1, sizeof(buf1)); } + /* + * If md_iterations is equal to 0, user doesn't want PKCS#5v2. + */ + if (md->md_iterations == 0) { + g_eli_crypto_hmac_update(ctxp, md->md_salt, + sizeof(md->md_salt)); + g_eli_crypto_hmac_update(ctxp, passbuf, strlen(passbuf)); + } else /* if (md->md_iterations > 0) */ { + unsigned char dkey[G_ELI_USERKEYLEN]; + + pkcs5v2_genkey(dkey, sizeof(dkey), md->md_salt, + sizeof(md->md_salt), passbuf, md->md_iterations); + g_eli_crypto_hmac_update(ctxp, dkey, sizeof(dkey)); + bzero(dkey, sizeof(dkey)); + } + bzero(passbuf, sizeof(passbuf)); + + return (0); +} + +static unsigned char * +eli_genkey(struct gctl_req *req, struct g_eli_metadata *md, unsigned char *key, + bool new) +{ + struct hmac_ctx ctx; + bool nopassphrase; + int nfiles; + + nopassphrase = + gctl_get_int(req, new ? "nonewpassphrase" : "nopassphrase"); + + g_eli_crypto_hmac_init(&ctx, NULL, 0); + + nfiles = eli_genkey_files(req, new, "keyfile", &ctx, NULL, 0); + if (nfiles == -1) + return (NULL); + else if (nfiles == 0 && nopassphrase) { + gctl_error(req, "No key components given."); + return (NULL); + } + + if (eli_genkey_passphrase(req, md, new, &ctx) == -1) + return (NULL); + g_eli_crypto_hmac_final(&ctx, key, 0); + return (key); } @@ -611,8 +763,8 @@ eli_init(struct gctl_req *req) return; } if (val > sysconf(_SC_PAGE_SIZE)) { - gctl_error(req, "warning: Using sectorsize bigger than " - "the page size!"); + fprintf(stderr, + "warning: Using sectorsize bigger than the page size!\n"); } md.md_sectorsize = val; } @@ -622,7 +774,7 @@ eli_init(struct gctl_req *req) arc4rand(md.md_mkeys, sizeof(md.md_mkeys)); /* Generate user key. */ - if (eli_genkey(req, &md, key, 1) == NULL) { + if (eli_genkey(req, &md, key, true) == NULL) { bzero(key, sizeof(key)); *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201010262306.o9QN6sWp076792>