diff options
author | Pawel Jakub Dawidek <pjd@FreeBSD.org> | 2015-08-08 09:51:38 +0000 |
---|---|---|
committer | Pawel Jakub Dawidek <pjd@FreeBSD.org> | 2015-08-08 09:51:38 +0000 |
commit | 46e34470269793e5da58b001897e830a0f5de825 (patch) | |
tree | 3d81b92c9c23fdf0dff04d772f0a3ac5e68d5526 /sys/geom/eli | |
parent | 079672cb07e224e283a5d7d032e5b7090e1f4cc9 (diff) | |
download | src-46e34470269793e5da58b001897e830a0f5de825.tar.gz src-46e34470269793e5da58b001897e830a0f5de825.zip |
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.
Notes
Notes:
svn path=/head/; revision=286444
Diffstat (limited to 'sys/geom/eli')
-rw-r--r-- | sys/geom/eli/g_eli.c | 13 | ||||
-rw-r--r-- | sys/geom/eli/g_eli.h | 2 | ||||
-rw-r--r-- | sys/geom/eli/g_eli_ctl.c | 112 |
3 files changed, 92 insertions, 35 deletions
diff --git a/sys/geom/eli/g_eli.c b/sys/geom/eli/g_eli.c index a09472052b48..14fc17bc2323 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 835a1b316326..36ef5551a191 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 59aff9b684bd..9de7ec3d55cb 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, |