Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 26 Jun 2018 18:07:17 +0000 (UTC)
From:      Ben Woods <woodsb02@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r335673 - in head: etc/defaults etc/rc.d lib/geom/eli share/man/man5
Message-ID:  <201806261807.w5QI7Hod072088@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: woodsb02 (ports committer)
Date: Tue Jun 26 18:07:16 2018
New Revision: 335673
URL: https://svnweb.freebsd.org/changeset/base/335673

Log:
  geli attach multiple providers
  
  Allow attaching of multiple geli providers at once if they use same
  passphrase and keyfiles.
  
  This is helpful when the providers being attached are not used for boot,
  and therefore the existing code to first try the cached password when
  tasting the providers during boot does not apply.
  
  Multiple providers with the same passphrase and keyfiles can be attached
  at the same time during system start-up by adding the following to
  rc.conf:
    geli_groups="storage backup"
    geli_storage_flags="-k /etc/geli/storage.keys"
    geli_storage_devices="ada0 ada1"
    geli_backup_flags="-j /etc/geli/backup.passfile -k /etc/geli/backup.keys"
    geli_backup_devices="ada2 ada3"
  
  Reviewed by:	wblock, delphij, jilles
  Approved by:	sobomax (src), bcr (doc)
  Differential Revision:	https://reviews.freebsd.org/D12644

Modified:
  head/etc/defaults/rc.conf
  head/etc/rc.d/geli
  head/lib/geom/eli/geli.8
  head/lib/geom/eli/geom_eli.c
  head/share/man/man5/rc.conf.5

Modified: head/etc/defaults/rc.conf
==============================================================================
--- head/etc/defaults/rc.conf	Tue Jun 26 16:50:41 2018	(r335672)
+++ head/etc/defaults/rc.conf	Tue Jun 26 18:07:16 2018	(r335673)
@@ -79,6 +79,8 @@ gbde_lockdir="/etc"	# Where to look for gbde lockfiles
 # GELI disk encryption configuration.
 geli_devices=""		# List of devices to automatically attach in addition to
 			# GELI devices listed in /etc/fstab.
+geli_groups=""		# List of groups containing devices to automatically
+			# attach with the same keyfiles and passphrase
 geli_tries=""		# Number of times to attempt attaching geli device.
 			# If empty, kern.geom.eli.tries will be used.
 geli_default_flags=""	# Default flags for geli(8).
@@ -90,6 +92,11 @@ geli_autodetach="YES"	# Automatically detach on last c
 #geli_da1_flags="-p -k /etc/geli/da1.keys"
 #geli_da1_autodetach="NO"
 #geli_mirror_home_flags="-k /etc/geli/home.keys"
+#geli_groups="storage backup"
+#geli_storage_flags="-k /etc/geli/storage.keys"
+#geli_storage_devices="ada0 ada1"
+#geli_backup_flags="-j /etc/geli/backup.passfile -k /etc/geli/backup.keys"
+#geli_backup_devices="ada2 ada3"
 
 root_rw_mount="YES"	# Set to NO to inhibit remounting root read-write.
 root_hold_delay="30"	# Time to wait for root mount hold release.

Modified: head/etc/rc.d/geli
==============================================================================
--- head/etc/rc.d/geli	Tue Jun 26 16:50:41 2018	(r335672)
+++ head/etc/rc.d/geli	Tue Jun 26 18:07:16 2018	(r335673)
@@ -34,7 +34,7 @@
 
 name="geli"
 desc="GELI disk encryption"
-start_precmd='[ -n "$(geli_make_list)" ]'
+start_precmd='[ -n "$(geli_make_list)" -o -n "${geli_groups}" ]'
 start_cmd="geli_start"
 stop_cmd="geli_stop"
 required_modules="geom_eli:g_eli"
@@ -72,11 +72,47 @@ geli_start()
 			done
 		fi
 	done
+
+	for group in ${geli_groups}; do
+		group_=`ltr ${group} '/-' '_'`
+
+		eval "flags=\${geli_${group_}_flags}"
+		if [ -z "${flags}" ]; then
+			flags=${geli_default_flags}
+		fi
+
+		eval "providers=\${geli_${group_}_devices}"
+		if [ -z "${providers}" ]; then
+			echo "No devices listed in geli group ${group}."
+			continue
+		fi
+
+		if [ -e "/dev/${providers%% *}" -a ! -e "/dev/${providers%% *}.eli" ]; then
+			echo "Configuring Disk Encryption for geli group ${group}, containing ${providers}."
+			count=1
+			while [ ${count} -le ${geli_tries} ]; do
+				geli attach ${flags} ${providers}
+				if [ -e "/dev/${providers%% *}.eli" ]; then
+					break
+				fi
+				echo "Attach failed; attempt ${count} of ${geli_tries}."
+				count=$((count+1))
+			done
+		fi
+	done
 }
 
 geli_stop()
 {
 	devices=`geli_make_list`
+
+	for group in ${geli_groups}; do
+		group_=`ltr ${group} '/-' '_'`
+
+		eval "providers=\${geli_${group_}_devices}"
+
+		devices="${devices} ${providers}"
+	done
 
 	for provider in ${devices}; do
 		if [ -e "/dev/${provider}.eli" ]; then

Modified: head/lib/geom/eli/geli.8
==============================================================================
--- head/lib/geom/eli/geli.8	Tue Jun 26 16:50:41 2018	(r335672)
+++ head/lib/geom/eli/geli.8	Tue Jun 26 18:07:16 2018	(r335673)
@@ -24,7 +24,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd May 9, 2018
+.Dd June 27, 2018
 .Dt GELI 8
 .Os
 .Sh NAME
@@ -71,7 +71,7 @@ utility:
 .Op Fl n Ar keyno
 .Op Fl j Ar passfile
 .Op Fl k Ar keyfile
-.Ar prov
+.Ar prov ...
 .Nm
 .Cm detach
 .Op Fl fl
@@ -192,8 +192,7 @@ entered via prompt or read from one or more passfiles;
 one or more files.
 .It
 Allows encryption of the root partition.
-The user will be asked for the
-passphrase before the root file system is mounted.
+The user is asked for the passphrase before the root filesystem is mounted.
 .It
 Strengthens the passphrase component of the User Key with:
 .Rs
@@ -217,12 +216,12 @@ so that if a user has to quickly destroy key material,
 it is possible to get the data back by restoring keys from
 backup.
 .It
-Providers can be configured to automatically detach on last close
-(so users do not have to remember to detach providers after unmounting
-the file systems).
+Providers can be configured to automatically detach on last close,
+so users do not have to remember to detach providers after unmounting
+the filesystems.
 .It
-Allows attaching a provider with a random, one-time Master Key -
-useful for swap partitions and temporary file systems.
+Allows attaching a provider with a random, one-time Master Key,
+which is useful for swap partitions and temporary filesystems.
 .It
 Allows verification of data integrity (data authentication).
 .It
@@ -380,17 +379,23 @@ This option is helpful when creating a provider that m
 versions.
 Consult the
 .Sx HISTORY
-section to find which metadata version is supported by which FreeBSD version.
+section to find which metadata version is supported by which
+.Fx
+version.
 Note that using an older version of metadata may limit the number of
 features available.
 .El
 .It Cm attach
-Attach the given provider.
-The encrypted Master Key will be loaded from the metadata and decrypted
-using the given passphrase/keyfile and a new GEOM provider will be created
-using the given provider's name with an
+Attach the given providers.
+The encrypted Master Keys are loaded from the metadata and decrypted
+using the given passphrase/keyfile and new GEOM providers are created
+using the specified provider names.
+A
 .Qq .eli
-suffix.
+suffix is added to the user specified provider names.
+Multiple providers can only be attached with a single
+.Cm attach
+command if they all have the same passphrase and keyfiles.
 .Pp
 Additional options include:
 .Bl -tag -width ".Fl j Ar passfile"
@@ -398,11 +403,11 @@ Additional options include:
 Do a dry-run decryption.
 This is useful to verify passphrase and keyfile without decrypting the device.
 .It Fl d
-If specified, a decrypted provider will be detached automatically on last close.
-This can help with scarce memory so the user does not have to remember to detach the
-provider after unmounting the file system.
-It only works when the provider was opened for writing, so it will not work if
-the file system on the provider is mounted read-only.
+If specified, the decrypted providers are detached automatically on last close,
+so the user does not have to remember to detach
+providers after unmounting the filesystems.
+This only works when providers were opened for writing, and will not work if
+the filesystems on the providers were mounted read-only.
 Probably a better choice is the
 .Fl l
 option for the
@@ -419,6 +424,7 @@ For more information see the description of the
 option for the
 .Cm init
 subcommand.
+The same passfiles are used for all listed providers.
 .It Fl k Ar keyfile
 Specifies a file which contains the keyfile component of the User Key
 (or part of it).
@@ -427,14 +433,15 @@ For more information see the description of the
 option for the
 .Cm init
 subcommand.
+The same keyfiles are used for all listed providers.
 .It Fl p
-Do not use a passphrase as a component of the User Key.
+Do not use a passphrase as a component of the User Keys.
 Cannot be combined with the
 .Fl j
 option.
 .It Fl r
-Attach read-only provider.
-It will not be opened for writing.
+Attach read-only providers.
+They are not opened for writing.
 .El
 .It Cm detach
 Detach the given providers, which means remove the devfs entry
@@ -445,14 +452,15 @@ Additional options include:
 .It Fl f
 Force detach - detach even if the provider is open.
 .It Fl l
-Mark provider to detach on last close.
+Mark provider to detach on last close, after the last filesystem has been
+unmounted.
 If this option is specified, the provider will not be detached
 while it is open, but will be automatically detached when it is closed for the
 last time even if it was only opened for reading.
 .El
 .It Cm onetime
 Attach the given providers with a random, one-time (ephemeral) Master Key.
-The command can be used to encrypt swap partitions or temporary file systems.
+The command can be used to encrypt swap partitions or temporary filesystems.
 .Pp
 Additional options include:
 .Bl -tag -width ".Fl a Ar sectorsize"
@@ -467,10 +475,10 @@ For more information, see the description of the
 .Cm init
 subcommand.
 .It Fl d
-Detach on last close.
-Note: this option is not usable for temporary file systems as the provider will
-be detached after creating the file system on it.
-It still can (and should be) used for swap partitions.
+Detach on last close, after the last filesystem has been unmounted.
+Note: this option is not usable for temporary filesystems as the provider is
+detached after the filesystem has been created.
+It still can, and should, be used for swap partitions.
 For more information, see the description of the
 .Cm attach
 subcommand.
@@ -643,17 +651,17 @@ and
 .El
 .It Cm suspend
 Suspend device by waiting for all inflight requests to finish, clearing all
-sensitive information (like the Master Key and Data Keys) from kernel memory,
+sensitive information such as the Master Key and Data Keys from kernel memory,
 and blocking all further I/O requests until the
 .Cm resume
 subcommand is executed.
-This functionality is useful for laptops: when one wants to suspend a
-laptop, one does not want to leave an encrypted device attached.
-Instead of closing all files and directories opened from a file system located
-on an encrypted device, unmounting the file system, and detaching the device,
-the
+This functionality is useful for laptops.
+Suspending a laptop should not leave an encrypted device attached.
+The
 .Cm suspend
-subcommand can be used.
+subcommand can be used rather than closing all files and directories from
+filesystems on the encrypted device, unmounting the filesystem, and
+detaching the device.
 Any access to the encrypted device will be blocked until the Master Key is
 reloaded through the
 .Cm resume
@@ -664,8 +672,8 @@ The
 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 an encrypted device due to the file system cache, etc.
+Please note that sensitive data might still be present in memory locations
+such as the filesystem cache after suspending an encrypted device.
 .Pp
 Additional options include:
 .Bl -tag -width ".Fl a"
@@ -678,23 +686,23 @@ devices.
 Resume previously suspended device.
 The caller must ensure that executing this subcommand does not access the
 suspended device, leading to a deadlock.
-For example suspending a device which contains the file system where the
+For example, suspending a device which contains the filesystem where the
 .Nm
-utility is stored is bad idea.
+utility is stored is a 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 component of the User Key
-(or part of it).
+Specifies a file which contains the passphrase component of the User Key,
+or part of it.
 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 the keyfile component of the User Key
-(or part of it).
+Specifies a file which contains the keyfile component of the User Key,
+or part of it.
 For more information see the description of the
 .Fl K
 option for the
@@ -810,18 +818,17 @@ If set to 3, the
 maximum amount of debug information is printed.
 .It Va kern.geom.eli.tries : No 3
 Number of times a user is asked for the passphrase.
-This is only used for providers which are attached on boot
-(before the root file system is mounted).
+This is only used for providers which are attached on boot,
+before the root filesystem 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 the Master Key will be overwritten
+Specifies how many times the Master Key is overwritten
 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, the passphrase entered on boot (before the root
-file system is mounted) will be visible.
+If set to 1, the passphrase entered on boot will be visible.
 This alternative should be used with caution as the entered
 passphrase can be logged and exposed via
 .Xr dmesg 8 .
@@ -860,7 +867,7 @@ Exit status is 0 on success, and 1 if the command fail
 Initialize a 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.
+Attach the provider, create a filesystem, and mount it.
 Do the work.
 Unmount the provider and detach it:
 .Bd -literal -offset indent
@@ -915,7 +922,7 @@ Encrypted swap partition setup:
 .Ed
 .Pp
 The example below shows how to configure two providers which will be attached
-on boot (before the root file system is mounted).
+on boot, before the root filesystem is mounted.
 One of them is using passphrase and three keyfile parts and the other is
 using only a keyfile in one part:
 .Bd -literal -offset indent
@@ -993,7 +1000,7 @@ geli: Cannot read metadata from /dev/da0: Invalid argu
 Enter passphrase:
 .Ed
 .Pp
-If an encrypted file system is extended, it is necessary to relocate and
+If an encrypted filesystem is extended, it is necessary to relocate and
 update the metadata:
 .Bd -literal -offset indent
 # gpart create -s GPT ada0

Modified: head/lib/geom/eli/geom_eli.c
==============================================================================
--- head/lib/geom/eli/geom_eli.c	Tue Jun 26 16:50:41 2018	(r335672)
+++ head/lib/geom/eli/geom_eli.c	Tue Jun 26 18:07:16 2018	(r335673)
@@ -62,7 +62,14 @@ uint32_t version = G_ELI_VERSION;
 
 #define	GELI_BACKUP_DIR	"/var/backups/"
 #define	GELI_ENC_ALGO	"aes"
+#define	BUFSIZE		1024
 
+/*
+ * Passphrase cached when attaching multiple providers, in order to be more
+ * user-friendly if they are using the same passphrase.
+ */
+static char cached_passphrase[BUFSIZE] = "";
+
 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);
@@ -86,7 +93,7 @@ static int eli_backup_create(struct gctl_req *req, con
  *
  * init [-bdgPTv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] [-s sectorsize] [-V version] prov
  * label - alias for 'init'
- * attach [-Cdprv] [-n keyno] [-j passfile] [-k keyfile] prov
+ * attach [-Cdprv] [-n keyno] [-j passfile] [-k keyfile] prov ...
  * detach [-fl] prov ...
  * stop - alias for 'detach'
  * onetime [-d] [-a aalgo] [-e ealgo] [-l keylen] prov
@@ -154,7 +161,7 @@ struct g_command class_commands[] = {
 		{ 'r', "readonly", NULL, G_TYPE_BOOL },
 		G_OPT_SENTINEL
 	    },
-	    "[-Cdprv] [-n keyno] [-j passfile] [-k keyfile] prov"
+	    "[-Cdprv] [-n keyno] [-j passfile] [-k keyfile] prov ..."
 	},
 	{ "detach", 0, NULL,
 	    {
@@ -275,8 +282,6 @@ struct g_command class_commands[] = {
 
 static int verbose = 0;
 
-#define	BUFSIZE	1024
-
 static int
 eli_protect(struct gctl_req *req)
 {
@@ -411,7 +416,7 @@ eli_genkey_files(struct gctl_req *req, bool new, const
 				    passbufsize) {
 					gctl_error(req,
 					    "Passphrase in %s too long.", file);
-					bzero(buf, sizeof(buf));
+					explicit_bzero(buf, sizeof(buf));
 					return (-1);
 				}
 				if (p != NULL)
@@ -421,7 +426,7 @@ eli_genkey_files(struct gctl_req *req, bool new, const
 		error = errno;
 		if (strcmp(file, "-") != 0)
 			close(fd);
-		bzero(buf, sizeof(buf));
+		explicit_bzero(buf, sizeof(buf));
 		if (done == -1) {
 			gctl_error(req, "Cannot read %s %s: %s.",
 			    type, file, strerror(error));
@@ -442,7 +447,7 @@ eli_genkey_passphrase_prompt(struct gctl_req *req, boo
 		    new ? "Enter new passphrase: " : "Enter passphrase: ",
 		    passbuf, passbufsize, RPP_ECHO_OFF | RPP_REQUIRE_TTY);
 		if (p == NULL) {
-			bzero(passbuf, passbufsize);
+			explicit_bzero(passbuf, passbufsize);
 			gctl_error(req, "Cannot read passphrase: %s.",
 			    strerror(errno));
 			return (-1);
@@ -455,7 +460,7 @@ eli_genkey_passphrase_prompt(struct gctl_req *req, boo
 			    tmpbuf, sizeof(tmpbuf),
 			    RPP_ECHO_OFF | RPP_REQUIRE_TTY);
 			if (p == NULL) {
-				bzero(passbuf, passbufsize);
+				explicit_bzero(passbuf, passbufsize);
 				gctl_error(req,
 				    "Cannot read passphrase: %s.",
 				    strerror(errno));
@@ -463,11 +468,11 @@ eli_genkey_passphrase_prompt(struct gctl_req *req, boo
 			}
 
 			if (strcmp(passbuf, tmpbuf) != 0) {
-				bzero(passbuf, passbufsize);
+				explicit_bzero(passbuf, passbufsize);
 				fprintf(stderr, "They didn't match.\n");
 				continue;
 			}
-			bzero(tmpbuf, sizeof(tmpbuf));
+			explicit_bzero(tmpbuf, sizeof(tmpbuf));
 		}
 		return (0);
 	}
@@ -482,6 +487,10 @@ eli_genkey_passphrase(struct gctl_req *req, struct g_e
 	bool nopassphrase;
 	int nfiles;
 
+	/*
+	 * Return error if the 'do not use passphrase' flag was given but a
+	 * passfile was provided.
+	 */
 	nopassphrase =
 	    gctl_get_int(req, new ? "nonewpassphrase" : "nopassphrase");
 	if (nopassphrase) {
@@ -494,20 +503,32 @@ eli_genkey_passphrase(struct gctl_req *req, struct g_e
 		return (0);
 	}
 
+	/*
+	 * Return error if using a provider which does not require a passphrase
+	 * but the 'do not use passphrase' flag was not given.
+	 */
 	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) {
+
+	/* Use cached passphrase if defined. */
+	if (strlen(cached_passphrase) > 0) {
+		strlcpy(passbuf, cached_passphrase, sizeof(passbuf));
+	} else {
+		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);
+			}
 		}
+		/* Cache the passphrase for other providers. */
+		strlcpy(cached_passphrase, passbuf, sizeof(cached_passphrase));
 	}
 	/*
 	 * Field md_iterations equal to -1 means "choose some sane
@@ -537,9 +558,9 @@ eli_genkey_passphrase(struct gctl_req *req, struct g_e
 		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));
+		explicit_bzero(dkey, sizeof(dkey));
 	}
-	bzero(passbuf, sizeof(passbuf));
+	explicit_bzero(passbuf, sizeof(passbuf));
 
 	return (0);
 }
@@ -645,13 +666,13 @@ eli_metadata_store(struct gctl_req *req, const char *p
 		if (fd == -1) {
 			gctl_error(req, "Cannot open %s: %s.", prov,
 			    strerror(errno));
-			bzero(sector, sizeof(sector));
+			explicit_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));
+			explicit_bzero(sector, sizeof(sector));
 			close(fd);
 			return (-1);
 		}
@@ -662,11 +683,11 @@ eli_metadata_store(struct gctl_req *req, const char *p
 		if (error != 0) {
 			gctl_error(req, "Cannot write metadata to %s: %s.",
 			    prov, strerror(errno));
-			bzero(sector, sizeof(sector));
+			explicit_bzero(sector, sizeof(sector));
 			return (-1);
 		}
 	}
-	bzero(sector, sizeof(sector));
+	explicit_bzero(sector, sizeof(sector));
 	return (0);
 }
 
@@ -697,7 +718,7 @@ eli_init(struct gctl_req *req)
 		return;
 	}
 
-	bzero(&md, sizeof(md));
+	explicit_bzero(&md, sizeof(md));
 	strlcpy(md.md_magic, G_ELI_MAGIC, sizeof(md.md_magic));
 	val = gctl_get_intmax(req, "mdversion");
 	if (val == -1) {
@@ -828,25 +849,25 @@ eli_init(struct gctl_req *req)
 
 	/* Generate user key. */
 	if (eli_genkey(req, &md, key, true) == NULL) {
-		bzero(key, sizeof(key));
-		bzero(&md, sizeof(md));
+		explicit_bzero(key, sizeof(key));
+		explicit_bzero(&md, sizeof(md));
 		return;
 	}
 
 	/* Encrypt the first and the only Master Key. */
 	error = g_eli_mkey_encrypt(md.md_ealgo, key, md.md_keylen, md.md_mkeys);
-	bzero(key, sizeof(key));
+	explicit_bzero(key, sizeof(key));
 	if (error != 0) {
-		bzero(&md, sizeof(md));
+		explicit_bzero(&md, sizeof(md));
 		gctl_error(req, "Cannot encrypt Master Key: %s.",
 		    strerror(error));
 		return;
 	}
 
 	eli_metadata_encode(&md, sector);
-	bzero(&md, sizeof(md));
+	explicit_bzero(&md, sizeof(md));
 	error = g_metadata_store(prov, sector, sizeof(sector));
-	bzero(sector, sizeof(sector));
+	explicit_bzero(sector, sizeof(sector));
 	if (error != 0) {
 		gctl_error(req, "Cannot store metadata on %s: %s.", prov,
 		    strerror(error));
@@ -886,38 +907,75 @@ static void
 eli_attach(struct gctl_req *req)
 {
 	struct g_eli_metadata md;
-	unsigned char key[G_ELI_USERKEYLEN];
+	struct gctl_req *r;
 	const char *prov;
 	off_t mediasize;
-	int nargs;
+	int i, nargs, nparams, param;
+	const int one = 1;
 
 	nargs = gctl_get_int(req, "nargs");
-	if (nargs != 1) {
-		gctl_error(req, "Invalid number of arguments.");
+	if (nargs == 0) {
+		gctl_error(req, "Too few arguments.");
 		return;
 	}
-	prov = gctl_get_ascii(req, "arg0");
 
-	if (eli_metadata_read(req, prov, &md) == -1)
-		return;
+	unsigned char key[G_ELI_USERKEYLEN];
 
-	mediasize = g_get_mediasize(prov);
-	if (md.md_provsize != (uint64_t)mediasize) {
-		gctl_error(req, "Provider size mismatch.");
-		return;
-	}
+	/*
+	 * Determine number of parameters in the parent geom request before the
+	 * nargs parameter and list of providers.
+	 */
+	nparams = req->narg - nargs - 1;
 
-	if (eli_genkey(req, &md, key, false) == NULL) {
-		bzero(key, sizeof(key));
-		return;
-	}
+	/* Create new child geom request for each provider and issue to kernel */
+	for (i = 0; i < nargs; i++) {
+		r = gctl_get_handle();
 
-	gctl_ro_param(req, "key", sizeof(key), key);
-	if (gctl_issue(req) == NULL) {
-		if (verbose)
-			printf("Attached to %s.\n", prov);
+		/* Copy each parameter from the parent request to the child request */
+		for (param = 0; param < nparams; param++) {
+			gctl_ro_param(r, req->arg[param].name, req->arg[param].len, req->arg[param].value);
+		}
+
+		/* Add a single provider to the parameter list of the child */
+		gctl_ro_param(r, "nargs", sizeof(one), &one);
+		prov = gctl_get_ascii(req, "arg%d", i);
+		gctl_ro_param(r, "arg0", -1, prov);
+
+		if (eli_metadata_read(r, prov, &md) == -1)
+			return;
+
+		mediasize = g_get_mediasize(prov);
+		if (md.md_provsize != (uint64_t)mediasize) {
+			gctl_error(r, "Provider size mismatch.");
+			return;
+		}
+
+		if (eli_genkey(r, &md, key, false) == NULL) {
+			explicit_bzero(key, sizeof(key));
+			return;
+		}
+
+		gctl_ro_param(r, "key", sizeof(key), key);
+
+		if (gctl_issue(r) == NULL) {
+			if (verbose)
+				printf("Attached to %s.\n", prov);
+		}
+
+		/* Print error for this request, and set parent request error message */
+	        if (r->error != NULL && r->error[0] != '\0') {
+	                warnx("%s", r->error);
+			gctl_error(req, "At least one provider failed to attach.");
+	        }
+
+		gctl_free(r);
+
+		/* Clear the derived key */
+		explicit_bzero(key, sizeof(key));
 	}
-	bzero(key, sizeof(key));
+
+	/* Clear the cached passphrase */
+	explicit_bzero(cached_passphrase, sizeof(cached_passphrase));
 }
 
 static void
@@ -989,7 +1047,7 @@ eli_configure_detached(struct gctl_req *req, const cha
 
 	if (changed)
 		eli_metadata_store(req, prov, &md);
-	bzero(&md, sizeof(md));
+	explicit_bzero(&md, sizeof(md));
 }
 
 static void
@@ -1090,7 +1148,7 @@ eli_setkey_attached(struct gctl_req *req, struct g_eli
 
 	/* Generate key for Master Key encryption. */
 	if (eli_genkey(req, md, key, true) == NULL) {
-		bzero(key, sizeof(key));
+		explicit_bzero(key, sizeof(key));
 		return;
 	}
 	/*
@@ -1105,7 +1163,7 @@ eli_setkey_attached(struct gctl_req *req, struct g_eli
 
 	gctl_ro_param(req, "key", sizeof(key), key);
 	gctl_issue(req);
-	bzero(key, sizeof(key));
+	explicit_bzero(key, sizeof(key));
 }
 
 static void
@@ -1125,15 +1183,15 @@ eli_setkey_detached(struct gctl_req *req, const char *
 
 	/* Generate key for Master Key decryption. */
 	if (eli_genkey(req, md, key, false) == NULL) {
-		bzero(key, sizeof(key));
+		explicit_bzero(key, sizeof(key));
 		return;
 	}
 
 	/* Decrypt Master Key. */
 	error = g_eli_mkey_decrypt_any(md, key, mkey, &nkey);
-	bzero(key, sizeof(key));
+	explicit_bzero(key, sizeof(key));
 	if (error != 0) {
-		bzero(md, sizeof(*md));
+		explicit_bzero(md, sizeof(*md));
 		if (error == -1)
 			gctl_error(req, "Wrong key for %s.", prov);
 		else /* if (error > 0) */ {
@@ -1179,20 +1237,20 @@ eli_setkey_detached(struct gctl_req *req, const char *
 	md->md_keys |= (1 << nkey);
 
 	bcopy(mkey, mkeydst, sizeof(mkey));
-	bzero(mkey, sizeof(mkey));
+	explicit_bzero(mkey, sizeof(mkey));
 
 	/* Generate key for Master Key encryption. */
 	if (eli_genkey(req, md, key, true) == NULL) {
-		bzero(key, sizeof(key));
-		bzero(md, sizeof(*md));
+		explicit_bzero(key, sizeof(key));
+		explicit_bzero(md, sizeof(*md));
 		return;
 	}
 
 	/* Encrypt the Master-Key with the new key. */
 	error = g_eli_mkey_encrypt(md->md_ealgo, key, md->md_keylen, mkeydst);
-	bzero(key, sizeof(key));
+	explicit_bzero(key, sizeof(key));
 	if (error != 0) {
-		bzero(md, sizeof(*md));
+		explicit_bzero(md, sizeof(*md));
 		gctl_error(req, "Cannot encrypt Master Key: %s.",
 		    strerror(error));
 		return;
@@ -1200,7 +1258,7 @@ eli_setkey_detached(struct gctl_req *req, const char *
 
 	/* Store metadata with fresh key. */
 	eli_metadata_store(req, prov, md);
-	bzero(md, sizeof(*md));
+	explicit_bzero(md, sizeof(*md));
 }
 
 static void
@@ -1281,7 +1339,7 @@ eli_delkey_detached(struct gctl_req *req, const char *
 	}
 
 	eli_metadata_store(req, prov, &md);
-	bzero(&md, sizeof(md));
+	explicit_bzero(&md, sizeof(md));
 }
 
 static void
@@ -1329,7 +1387,7 @@ eli_resume(struct gctl_req *req)
 	}
 
 	if (eli_genkey(req, &md, key, false) == NULL) {
-		bzero(key, sizeof(key));
+		explicit_bzero(key, sizeof(key));
 		return;
 	}
 
@@ -1338,7 +1396,7 @@ eli_resume(struct gctl_req *req)
 		if (verbose)
 			printf("Resumed %s.\n", prov);
 	}
-	bzero(key, sizeof(key));
+	explicit_bzero(key, sizeof(key));
 }
 
 static int
@@ -1508,7 +1566,7 @@ eli_backup_create(struct gctl_req *req, const char *pr
 	ret = 0;
 out:
 	if (sector != NULL) {
-		bzero(sector, secsize);
+		explicit_bzero(sector, secsize);
 		free(sector);
 	}
 	return (ret);
@@ -1672,7 +1730,7 @@ out:
 	if (provfd != -1)
 		(void)g_close(provfd);
 	if (sector != NULL) {
-		bzero(sector, secsize);
+		explicit_bzero(sector, secsize);
 		free(sector);
 	}
 }

Modified: head/share/man/man5/rc.conf.5
==============================================================================
--- head/share/man/man5/rc.conf.5	Tue Jun 26 16:50:41 2018	(r335672)
+++ head/share/man/man5/rc.conf.5	Tue Jun 26 18:07:16 2018	(r335673)
@@ -24,7 +24,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd April 21, 2018
+.Dd June 27, 2018
 .Dt RC.CONF 5
 .Os
 .Sh NAME
@@ -2011,6 +2011,13 @@ List of devices to automatically attach on boot.
 Note that .eli devices from
 .Pa /etc/fstab
 are automatically appended to this list.
+.It Va geli_groups
+.Pq Vt str
+List of groups containing devices to automatically attach on boot with the same
+keyfiles and passphrase.
+This must be accompanied with a corresponding
+.Va geli_ Ns Ao Ar group Ac Ns Va _devices
+variable.
 .It Va geli_tries
 .Pq Vt int
 Number of times user is asked for the pass-phrase.
@@ -2022,8 +2029,10 @@ sysctl variable.
 Default flags to use by
 .Xr geli 8
 when configuring disk encryption.
-Flags can be configured for every device separately by defining
+Flags can be configured for every device separately by defining the
 .Va geli_ Ns Ao Ar device Ac Ns Va _flags
+variable, and for every group separately by defining the
+.Va geli_ Ns Ao Ar group Ac Ns Va _flags
 variable.
 .It Va geli_autodetach
 .Pq Vt str
@@ -2031,7 +2040,7 @@ Specifies if GELI devices should be marked for detach 
 file systems are mounted.
 Default is
 .Dq Li YES .
-This can be changed for every device separately by defining
+This can be changed for every device separately by defining the
 .Va geli_ Ns Ao Ar device Ac Ns Va _autodetach
 variable.
 .It Va root_rw_mount



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201806261807.w5QI7Hod072088>