svn commit: r214405 - in stable/8: sbin/geom/class/eli sbin/geom/class/sched sbin/geom/core sys/geom/eli

Pawel Jakub Dawidek pjd at FreeBSD.org
Tue Oct 26 23:06:54 UTC 2010


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 at 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 at FreeBSD.org>
+.\" Copyright (c) 2005-2010 Pawel Jakub Dawidek <pjd at 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 at FreeBSD.org>
+ * Copyright (c) 2004-2010 Pawel Jakub Dawidek <pjd at 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 ***


More information about the svn-src-all mailing list