summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorpjd <pjd@FreeBSD.org>2015-08-08 09:51:38 +0000
committerpjd <pjd@FreeBSD.org>2015-08-08 09:51:38 +0000
commit2ae822a47d155274a8b019dffdb60f0992293ae4 (patch)
tree3d81b92c9c23fdf0dff04d772f0a3ac5e68d5526
parent349429fe8270f2579de42235749bb5ed010ee83a (diff)
downloadFreeBSD-src-2ae822a47d155274a8b019dffdb60f0992293ae4.zip
FreeBSD-src-2ae822a47d155274a8b019dffdb60f0992293ae4.tar.gz
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.
-rw-r--r--sbin/geom/class/eli/geli.831
-rw-r--r--sbin/geom/class/eli/geom_eli.c66
-rw-r--r--sys/geom/eli/g_eli.c13
-rw-r--r--sys/geom/eli/g_eli.h2
-rw-r--r--sys/geom/eli/g_eli_ctl.c112
5 files changed, 174 insertions, 50 deletions
diff --git a/sbin/geom/class/eli/geli.8 b/sbin/geom/class/eli/geli.8
index aac307f..ed425da 100644
--- a/sbin/geom/class/eli/geli.8
+++ b/sbin/geom/class/eli/geli.8
@@ -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 description of the
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
diff --git a/sbin/geom/class/eli/geom_eli.c b/sbin/geom/class/eli/geom_eli.c
index 502303d..0c6ca2c 100644
--- a/sbin/geom/class/eli/geom_eli.c
+++ b/sbin/geom/class/eli/geom_eli.c
@@ -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);
}
}
diff --git a/sys/geom/eli/g_eli.c b/sys/geom/eli/g_eli.c
index a094720..14fc17b 100644
--- a/sys/geom/eli/g_eli.c
+++ b/sys/geom/eli/g_eli.c
@@ -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 char *indent, struct g_geom *gp,
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");
diff --git a/sys/geom/eli/g_eli.h b/sys/geom/eli/g_eli.h
index 835a1b3..36ef555 100644
--- a/sys/geom/eli/g_eli.h
+++ b/sys/geom/eli/g_eli.h
@@ -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
diff --git a/sys/geom/eli/g_eli_ctl.c b/sys/geom/eli/g_eli_ctl.c
index 59aff9b..9de7ec3 100644
--- a/sys/geom/eli/g_eli_ctl.c
+++ b/sys/geom/eli/g_eli_ctl.c
@@ -236,7 +236,7 @@ g_eli_ctl_onetime(struct gctl_req *req, struct g_class *mp)
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, struct g_class *mp)
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, struct g_class *mp)
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, struct g_class *mp)
}
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, struct g_class *mp)
"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,
OpenPOWER on IntegriCloud