svn commit: r286444 - in head: sbin/geom/class/eli sys/geom/eli

Pawel Jakub Dawidek pjd at FreeBSD.org
Sat Aug 8 09:51:41 UTC 2015


Author: pjd
Date: Sat Aug  8 09:51:38 2015
New Revision: 286444
URL: https://svnweb.freebsd.org/changeset/base/286444

Log:
  Enable BIO_DELETE passthru in GELI, so TRIM/UNMAP can work as expected when
  GELI is used on a SSD or inside virtual machine, so that guest can tell
  host that it is no longer using some of the storage.
  
  Enabling BIO_DELETE passthru comes with a small security consequence - an
  attacker can tell how much space is being really used on encrypted device and
  has less data no analyse then. This is why the -T option can be given to the
  init subcommand to turn off this behaviour and -t/T options for the configure
  subcommand can be used to adjust this setting later.
  
  PR:		198863
  Submitted by:	Matthew D. Fuller fullermd at over-yonder dot net
  
  This commit also includes a fix from Fabian Keil freebsd-listen at
  fabiankeil.de for 'configure' on onetime providers which is not strictly
  related, but is entangled in the same code, so would cause conflicts if
  separated out.

Modified:
  head/sbin/geom/class/eli/geli.8
  head/sbin/geom/class/eli/geom_eli.c
  head/sys/geom/eli/g_eli.c
  head/sys/geom/eli/g_eli.h
  head/sys/geom/eli/g_eli_ctl.c

Modified: head/sbin/geom/class/eli/geli.8
==============================================================================
--- head/sbin/geom/class/eli/geli.8	Sat Aug  8 08:40:36 2015	(r286443)
+++ head/sbin/geom/class/eli/geli.8	Sat Aug  8 09:51:38 2015	(r286444)
@@ -24,7 +24,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd June 2, 2015
+.Dd July 10, 2015
 .Dt GELI 8
 .Os
 .Sh NAME
@@ -51,7 +51,7 @@ utility:
 .Pp
 .Nm
 .Cm init
-.Op Fl bPv
+.Op Fl bPTv
 .Op Fl a Ar aalgo
 .Op Fl B Ar backupfile
 .Op Fl e Ar ealgo
@@ -80,7 +80,7 @@ utility:
 .Cm detach
 .Nm
 .Cm onetime
-.Op Fl d
+.Op Fl dT
 .Op Fl a Ar aalgo
 .Op Fl e Ar ealgo
 .Op Fl l Ar keylen
@@ -88,7 +88,7 @@ utility:
 .Ar prov
 .Nm
 .Cm configure
-.Op Fl bB
+.Op Fl bBtT
 .Ar prov ...
 .Nm
 .Cm setkey
@@ -351,6 +351,17 @@ Change decrypted provider's sector size.
 Increasing the sector size allows increased performance,
 because encryption/decryption which requires an initialization vector
 is done per sector; fewer sectors means less computational work.
+.It Fl T
+Don't pass through
+.Dv BIO_DELETE
+calls (i.e., TRIM/UNMAP).
+This can prevent an attacker from knowing how much space you're actually
+using and which sectors contain live data, but will also prevent the
+backing store (SSD, etc) from reclaiming space you're not using, which
+may degrade its performance and lifespan.
+The underlying provider may or may not actually obliterate the deleted
+sectors when TRIM is enabled, so it should not be considered to add any
+security.
 .It Fl V Ar version
 Metadata version to use.
 This option is helpful when creating a provider that may be used by older
@@ -456,6 +467,11 @@ Change decrypted provider's sector size.
 For more information, see the description of the
 .Cm init
 subcommand.
+.It Fl T
+Disable TRIM/UNMAP passthru.
+For more information, see the description of the
+.Cm init
+subcommand.
 .El
 .It Cm configure
 Change configuration of the given providers.
@@ -469,6 +485,13 @@ For more information, see the descriptio
 subcommand.
 .It Fl B
 Remove the BOOT flag from the given providers.
+.It Fl t
+Enable TRIM/UNMAP passthru.
+For more information, see the description of the
+.Cm init
+subcommand.
+.It Fl T
+Disable TRIM/UNMAP passthru.
 .El
 .It Cm setkey
 Install a copy of the Master Key into the selected slot, encrypted with

Modified: head/sbin/geom/class/eli/geom_eli.c
==============================================================================
--- head/sbin/geom/class/eli/geom_eli.c	Sat Aug  8 08:40:36 2015	(r286443)
+++ head/sbin/geom/class/eli/geom_eli.c	Sat Aug  8 09:51:38 2015	(r286444)
@@ -114,10 +114,11 @@ struct g_command class_commands[] = {
 		{ 'l', "keylen", "0", G_TYPE_NUMBER },
 		{ 'P', "nonewpassphrase", NULL, G_TYPE_BOOL },
 		{ 's', "sectorsize", "0", G_TYPE_NUMBER },
+		{ 'T', "notrim", NULL, G_TYPE_BOOL },
 		{ 'V', "mdversion", "-1", G_TYPE_NUMBER },
 		G_OPT_SENTINEL
 	    },
-	    "[-bPv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] [-s sectorsize] [-V version] prov"
+	    "[-bPTv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] [-s sectorsize] [-V version] prov"
 	},
 	{ "label", G_FLAG_VERBOSE, eli_main,
 	    {
@@ -170,17 +171,20 @@ struct g_command class_commands[] = {
 		{ 'e', "ealgo", GELI_ENC_ALGO, G_TYPE_STRING },
 		{ 'l', "keylen", "0", G_TYPE_NUMBER },
 		{ 's', "sectorsize", "0", G_TYPE_NUMBER },
+		{ 'T', "notrim", NULL, G_TYPE_BOOL },
 		G_OPT_SENTINEL
 	    },
-	    "[-d] [-a aalgo] [-e ealgo] [-l keylen] [-s sectorsize] prov"
+	    "[-dT] [-a aalgo] [-e ealgo] [-l keylen] [-s sectorsize] prov"
 	},
 	{ "configure", G_FLAG_VERBOSE, eli_main,
 	    {
 		{ 'b', "boot", NULL, G_TYPE_BOOL },
 		{ 'B', "noboot", NULL, G_TYPE_BOOL },
+		{ 't', "trim", NULL, G_TYPE_BOOL },
+		{ 'T', "notrim", NULL, G_TYPE_BOOL },
 		G_OPT_SENTINEL
 	    },
-	    "[-bB] prov ..."
+	    "[-bBtT] prov ..."
 	},
 	{ "setkey", G_FLAG_VERBOSE, eli_main,
 	    {
@@ -698,6 +702,8 @@ eli_init(struct gctl_req *req)
 	md.md_flags = 0;
 	if (gctl_get_int(req, "boot"))
 		md.md_flags |= G_ELI_FLAG_BOOT;
+	if (gctl_get_int(req, "notrim"))
+		md.md_flags |= G_ELI_FLAG_NODELETE;
 	md.md_ealgo = CRYPTO_ALGORITHM_MIN - 1;
 	str = gctl_get_ascii(req, "aalgo");
 	if (*str != '\0') {
@@ -899,26 +905,45 @@ eli_attach(struct gctl_req *req)
 }
 
 static void
-eli_configure_detached(struct gctl_req *req, const char *prov, bool boot)
+eli_configure_detached(struct gctl_req *req, const char *prov, int boot,
+ int trim)
 {
 	struct g_eli_metadata md;
+	bool changed = 0;
 
 	if (eli_metadata_read(req, prov, &md) == -1)
 		return;
 
-	if (boot && (md.md_flags & G_ELI_FLAG_BOOT)) {
+	if (boot == 1 && (md.md_flags & G_ELI_FLAG_BOOT)) {
 		if (verbose)
 			printf("BOOT flag already configured for %s.\n", prov);
-	} else if (!boot && !(md.md_flags & G_ELI_FLAG_BOOT)) {
+	} else if (boot == 0 && !(md.md_flags & G_ELI_FLAG_BOOT)) {
 		if (verbose)
 			printf("BOOT flag not configured for %s.\n", prov);
-	} else {
+	} else if (boot >= 0) {
 		if (boot)
 			md.md_flags |= G_ELI_FLAG_BOOT;
 		else
 			md.md_flags &= ~G_ELI_FLAG_BOOT;
-		eli_metadata_store(req, prov, &md);
+		changed = 1;
+	}
+
+	if (trim == 0 && (md.md_flags & G_ELI_FLAG_NODELETE)) {
+		if (verbose)
+			printf("TRIM disable flag already configured for %s.\n", prov);
+	} else if (trim == 1 && !(md.md_flags & G_ELI_FLAG_NODELETE)) {
+		if (verbose)
+			printf("TRIM disable flag not configured for %s.\n", prov);
+	} else if (trim >= 0) {
+		if (trim)
+			md.md_flags &= ~G_ELI_FLAG_NODELETE;
+		else
+			md.md_flags |= G_ELI_FLAG_NODELETE;
+		changed = 1;
 	}
+
+	if (changed)
+		eli_metadata_store(req, prov, &md);
 	bzero(&md, sizeof(md));
 }
 
@@ -926,7 +951,8 @@ static void
 eli_configure(struct gctl_req *req)
 {
 	const char *prov;
-	bool boot, noboot;
+	bool boot, noboot, trim, notrim;
+	int doboot, dotrim;
 	int i, nargs;
 
 	nargs = gctl_get_int(req, "nargs");
@@ -937,12 +963,30 @@ eli_configure(struct gctl_req *req)
 
 	boot = gctl_get_int(req, "boot");
 	noboot = gctl_get_int(req, "noboot");
+	trim = gctl_get_int(req, "trim");
+	notrim = gctl_get_int(req, "notrim");
 
+	doboot = -1;
 	if (boot && noboot) {
 		gctl_error(req, "Options -b and -B are mutually exclusive.");
 		return;
 	}
-	if (!boot && !noboot) {
+	if (boot)
+		doboot = 1;
+	else if (noboot)
+		doboot = 0;
+
+	dotrim = -1;
+	if (trim && notrim) {
+		gctl_error(req, "Options -t and -T are mutually exclusive.");
+		return;
+	}
+	if (trim)
+		dotrim = 1;
+	else if (notrim)
+		dotrim = 0;
+
+	if (doboot == -1 && dotrim == -1) {
 		gctl_error(req, "No option given.");
 		return;
 	}
@@ -953,7 +997,7 @@ eli_configure(struct gctl_req *req)
 	for (i = 0; i < nargs; i++) {
 		prov = gctl_get_ascii(req, "arg%d", i);
 		if (!eli_is_attached(prov))
-			eli_configure_detached(req, prov, boot);
+			eli_configure_detached(req, prov, doboot, dotrim);
 	}
 }
 

Modified: head/sys/geom/eli/g_eli.c
==============================================================================
--- head/sys/geom/eli/g_eli.c	Sat Aug  8 08:40:36 2015	(r286443)
+++ head/sys/geom/eli/g_eli.c	Sat Aug  8 09:51:38 2015	(r286444)
@@ -312,10 +312,15 @@ g_eli_start(struct bio *bp)
 		break;
 	case BIO_DELETE:
 		/*
-		 * We could eventually support BIO_DELETE request.
-		 * It could be done by overwritting requested sector with
-		 * random data g_eli_overwrites number of times.
+		 * If the user hasn't set the NODELETE flag, we just pass
+		 * it down the stack and let the layers beneath us do (or
+		 * not) whatever they do with it.  If they have, we
+		 * reject it.  A possible extension would be an
+		 * additional flag to take it as a hint to shred the data
+		 * with [multiple?] overwrites.
 		 */
+		if (!(sc->sc_flags & G_ELI_FLAG_NODELETE))
+			break;
 	default:
 		g_io_deliver(bp, EOPNOTSUPP);
 		return;
@@ -342,6 +347,7 @@ g_eli_start(struct bio *bp)
 		break;
 	case BIO_GETATTR:
 	case BIO_FLUSH:
+	case BIO_DELETE:
 		cbp->bio_done = g_std_done;
 		cp = LIST_FIRST(&sc->sc_geom->consumer);
 		cbp->bio_to = cp->provider;
@@ -1255,6 +1261,7 @@ g_eli_dumpconf(struct sbuf *sb, const ch
 		ADD_FLAG(G_ELI_FLAG_WOPEN, "W-OPEN");
 		ADD_FLAG(G_ELI_FLAG_DESTROY, "DESTROY");
 		ADD_FLAG(G_ELI_FLAG_RO, "READ-ONLY");
+		ADD_FLAG(G_ELI_FLAG_NODELETE, "NODELETE");
 #undef  ADD_FLAG
 	}
 	sbuf_printf(sb, "</Flags>\n");

Modified: head/sys/geom/eli/g_eli.h
==============================================================================
--- head/sys/geom/eli/g_eli.h	Sat Aug  8 08:40:36 2015	(r286443)
+++ head/sys/geom/eli/g_eli.h	Sat Aug  8 09:51:38 2015	(r286444)
@@ -94,6 +94,8 @@
 #define	G_ELI_FLAG_AUTH			0x00000010
 /* Provider is read-only, we should deny all write attempts. */
 #define	G_ELI_FLAG_RO			0x00000020
+/* Don't pass through BIO_DELETE requests. */
+#define	G_ELI_FLAG_NODELETE		0x00000040
 /* RUNTIME FLAGS. */
 /* Provider was open for writing. */
 #define	G_ELI_FLAG_WOPEN		0x00010000

Modified: head/sys/geom/eli/g_eli_ctl.c
==============================================================================
--- head/sys/geom/eli/g_eli_ctl.c	Sat Aug  8 08:40:36 2015	(r286443)
+++ head/sys/geom/eli/g_eli_ctl.c	Sat Aug  8 09:51:38 2015	(r286444)
@@ -236,7 +236,7 @@ g_eli_ctl_onetime(struct gctl_req *req, 
 	const char *name;
 	intmax_t *keylen, *sectorsize;
 	u_char mkey[G_ELI_DATAIVKEYLEN];
-	int *nargs, *detach;
+	int *nargs, *detach, *notrim;
 
 	g_topology_assert();
 	bzero(&md, sizeof(md));
@@ -251,17 +251,16 @@ g_eli_ctl_onetime(struct gctl_req *req, 
 		return;
 	}
 
-	detach = gctl_get_paraml(req, "detach", sizeof(*detach));
-	if (detach == NULL) {
-		gctl_error(req, "No '%s' argument.", "detach");
-		return;
-	}
-
 	strlcpy(md.md_magic, G_ELI_MAGIC, sizeof(md.md_magic));
 	md.md_version = G_ELI_VERSION;
 	md.md_flags |= G_ELI_FLAG_ONETIME;
-	if (*detach)
+
+	detach = gctl_get_paraml(req, "detach", sizeof(*detach));
+	if (detach != NULL && *detach)
 		md.md_flags |= G_ELI_FLAG_WO_DETACH;
+	notrim = gctl_get_paraml(req, "notrim", sizeof(*notrim));
+	if (notrim != NULL && *notrim)
+		md.md_flags |= G_ELI_FLAG_NODELETE;
 
 	md.md_ealgo = CRYPTO_ALGORITHM_MIN - 1;
 	name = gctl_get_asciiparam(req, "aalgo");
@@ -377,12 +376,15 @@ g_eli_ctl_configure(struct gctl_req *req
 	char param[16];
 	const char *prov;
 	u_char *sector;
-	int *nargs, *boot, *noboot;
-	int error;
+	int *nargs, *boot, *noboot, *trim, *notrim;
+	int zero, error, changed;
 	u_int i;
 
 	g_topology_assert();
 
+	changed = 0;
+	zero = 0;
+
 	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
 	if (nargs == NULL) {
 		gctl_error(req, "No '%s' argument.", "nargs");
@@ -394,20 +396,32 @@ g_eli_ctl_configure(struct gctl_req *req
 	}
 
 	boot = gctl_get_paraml(req, "boot", sizeof(*boot));
-	if (boot == NULL) {
-		gctl_error(req, "No '%s' argument.", "boot");
-		return;
-	}
+	if (boot == NULL)
+		boot = &zero;
 	noboot = gctl_get_paraml(req, "noboot", sizeof(*noboot));
-	if (noboot == NULL) {
-		gctl_error(req, "No '%s' argument.", "noboot");
-		return;
-	}
+	if (noboot == NULL)
+		noboot = &zero;
 	if (*boot && *noboot) {
 		gctl_error(req, "Options -b and -B are mutually exclusive.");
 		return;
 	}
-	if (!*boot && !*noboot) {
+	if (*boot || *noboot)
+		changed = 1;
+
+	trim = gctl_get_paraml(req, "trim", sizeof(*trim));
+	if (trim == NULL)
+		trim = &zero;
+	notrim = gctl_get_paraml(req, "notrim", sizeof(*notrim));
+	if (notrim == NULL)
+		notrim = &zero;
+	if (*trim && *notrim) {
+		gctl_error(req, "Options -t and -T are mutually exclusive.");
+		return;
+	}
+	if (*trim || *notrim)
+		changed = 1;
+
+	if (!changed) {
 		gctl_error(req, "No option given.");
 		return;
 	}
@@ -429,38 +443,72 @@ g_eli_ctl_configure(struct gctl_req *req
 			    "provider %s.", prov);
 			continue;
 		}
+		if (sc->sc_flags & G_ELI_FLAG_RO) {
+			gctl_error(req, "Cannot change configuration of "
+			    "read-only provider %s.", prov);
+			continue;
+		}
+
 		if (*boot && (sc->sc_flags & G_ELI_FLAG_BOOT)) {
 			G_ELI_DEBUG(1, "BOOT flag already configured for %s.",
 			    prov);
 			continue;
-		} else if (!*boot && !(sc->sc_flags & G_ELI_FLAG_BOOT)) {
+		} else if (*noboot && !(sc->sc_flags & G_ELI_FLAG_BOOT)) {
 			G_ELI_DEBUG(1, "BOOT flag not configured for %s.",
 			    prov);
 			continue;
 		}
-		if (sc->sc_flags & G_ELI_FLAG_RO) {
-			gctl_error(req, "Cannot change configuration of "
-			    "read-only provider %s.", prov);
+
+		if (*notrim && (sc->sc_flags & G_ELI_FLAG_NODELETE)) {
+			G_ELI_DEBUG(1, "TRIM disable flag already configured for %s.",
+			    prov);
 			continue;
-		}
-		cp = LIST_FIRST(&sc->sc_geom->consumer);
-		pp = cp->provider;
-		error = g_eli_read_metadata(mp, pp, &md);
-		if (error != 0) {
-			gctl_error(req,
-			    "Cannot read metadata from %s (error=%d).",
-			    prov, error);
+		} else if (*trim && !(sc->sc_flags & G_ELI_FLAG_NODELETE)) {
+			G_ELI_DEBUG(1, "TRIM disable flag not configured for %s.",
+			    prov);
 			continue;
 		}
 
+		if (!(sc->sc_flags & G_ELI_FLAG_ONETIME)) {
+			/*
+			 * ONETIME providers don't write metadata to
+			 * disk, so don't try reading it.  This means
+			 * we're bit-flipping uninitialized memory in md
+			 * below, but that's OK; we don't do anything
+			 * with it later.
+			 */
+			cp = LIST_FIRST(&sc->sc_geom->consumer);
+			pp = cp->provider;
+			error = g_eli_read_metadata(mp, pp, &md);
+			if (error != 0) {
+			    gctl_error(req,
+				"Cannot read metadata from %s (error=%d).",
+				prov, error);
+			    continue;
+			}
+		}
+
 		if (*boot) {
 			md.md_flags |= G_ELI_FLAG_BOOT;
 			sc->sc_flags |= G_ELI_FLAG_BOOT;
-		} else {
+		} else if (*noboot) {
 			md.md_flags &= ~G_ELI_FLAG_BOOT;
 			sc->sc_flags &= ~G_ELI_FLAG_BOOT;
 		}
 
+		if (*notrim) {
+			md.md_flags |= G_ELI_FLAG_NODELETE;
+			sc->sc_flags |= G_ELI_FLAG_NODELETE;
+		} else if (*trim) {
+			md.md_flags &= ~G_ELI_FLAG_NODELETE;
+			sc->sc_flags &= ~G_ELI_FLAG_NODELETE;
+		}
+
+		if (sc->sc_flags & G_ELI_FLAG_ONETIME) {
+			/* There's no metadata on disk so we are done here. */
+			continue;
+		}
+
 		sector = malloc(pp->sectorsize, M_ELI, M_WAITOK | M_ZERO);
 		eli_metadata_encode(&md, sector);
 		error = g_write_data(cp, pp->mediasize - pp->sectorsize, sector,


More information about the svn-src-head mailing list