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,
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to