Index: sbin/geom/class/eli/geli.8 =================================================================== --- sbin/geom/class/eli/geli.8 (revision 285365) +++ sbin/geom/class/eli/geli.8 (working copy) @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd June 2, 2015 +.Dd July 10, 2015 .Dt GELI 8 .Os .Sh NAME @@ -51,7 +51,7 @@ .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 @@ .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 @@ .Ar prov .Nm .Cm configure -.Op Fl bB +.Op Fl bBtT .Ar prov ... .Nm .Cm setkey @@ -351,6 +351,17 @@ 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 @@ 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 @@ 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 Index: sbin/geom/class/eli/geom_eli.c =================================================================== --- sbin/geom/class/eli/geom_eli.c (revision 285365) +++ sbin/geom/class/eli/geom_eli.c (working copy) @@ -114,10 +114,11 @@ { '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 @@ { '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 @@ 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 @@ } 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; + 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 @@ 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 @@ 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 @@ 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); } } Index: sys/geom/eli/g_eli.c =================================================================== --- sys/geom/eli/g_eli.c (revision 285365) +++ sys/geom/eli/g_eli.c (working copy) @@ -312,10 +312,15 @@ 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 @@ 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 @@ 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, "\n"); Index: sys/geom/eli/g_eli.h =================================================================== --- sys/geom/eli/g_eli.h (revision 285365) +++ sys/geom/eli/g_eli.h (working copy) @@ -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 Index: sys/geom/eli/g_eli_ctl.c =================================================================== --- sys/geom/eli/g_eli_ctl.c (revision 285365) +++ sys/geom/eli/g_eli_ctl.c (working copy) @@ -236,7 +236,7 @@ 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 @@ 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 @@ 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 @@ } 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 @@ "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,