Hide length of geli passphrase during boot.

Introduce additional flag to the geli which allows to restore previous
behavior.

Reviewed by:	AllanJude@, cem@ (previous version)
MFC:		1 month
Relnotes:	yes
Differential Revision:	https://reviews.freebsd.org/D11751
This commit is contained in:
Mariusz Zaborski 2017-08-26 14:07:24 +00:00
parent b92e8635f9
commit 3453dc72ad
9 changed files with 211 additions and 119 deletions

View File

@ -51,6 +51,10 @@ NOTE TO PEOPLE WHO THINK THAT FreeBSD 12.x IS SLOW:
****************************** SPECIAL WARNING: ******************************
20170826:
During boot the geli passphrase will be hidden. To restore previous
behavior see geli(8) configuration options.
20170825:
Move PMTUD blackhole counters to TCPSTATS and remove them from bare
sysctl values. Minor nit, but requires a rebuild of both world/kernel

View File

@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd August 3, 2016
.Dd August 26, 2017
.Dt GELI 8
.Os
.Sh NAME
@ -51,7 +51,7 @@ utility:
.Pp
.Nm
.Cm init
.Op Fl bgPTv
.Op Fl bdgPTv
.Op Fl a Ar aalgo
.Op Fl B Ar backupfile
.Op Fl e Ar ealgo
@ -88,7 +88,7 @@ utility:
.Ar prov
.Nm
.Cm configure
.Op Fl bBgGtT
.Op Fl bBdDgGtT
.Ar prov ...
.Nm
.Cm setkey
@ -279,6 +279,9 @@ To inhibit backups, you can use
.Pa none
as the
.Ar backupfile .
.It Fl d
While booting from this encrypted root filesystem enable visibility of
passphrase length.
.It Fl e Ar ealgo
Encryption algorithm to use.
Currently supported algorithms are:
@ -490,6 +493,12 @@ For more information, see the description of the
subcommand.
.It Fl B
Remove the BOOT flag from the given providers.
.It Fl d
While booting from this encrypted root filesystem enable visibility of
passphrase length.
.It Fl D
While booting from this encrypted root filesystem disable visibility of
passphrase length.
.It Fl g
Enable booting from this encrypted root filesystem.
The boot loader prompts for the passphrase and loads

View File

@ -82,7 +82,7 @@ static int eli_backup_create(struct gctl_req *req, const char *prov,
/*
* Available commands:
*
* init [-bgPTv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] [-s sectorsize] [-V version] prov
* init [-bdgPTv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] [-s sectorsize] [-V version] prov
* label - alias for 'init'
* attach [-dprv] [-j passfile] [-k keyfile] prov
* detach [-fl] prov ...
@ -107,6 +107,7 @@ struct g_command class_commands[] = {
{ 'a', "aalgo", "", G_TYPE_STRING },
{ 'b', "boot", NULL, G_TYPE_BOOL },
{ 'B', "backupfile", "", G_TYPE_STRING },
{ 'd', "displaypass", NULL, G_TYPE_BOOL },
{ 'e', "ealgo", "", G_TYPE_STRING },
{ 'g', "geliboot", NULL, G_TYPE_BOOL },
{ 'i', "iterations", "-1", G_TYPE_NUMBER },
@ -119,13 +120,14 @@ struct g_command class_commands[] = {
{ 'V', "mdversion", "-1", G_TYPE_NUMBER },
G_OPT_SENTINEL
},
"[-bgPTv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] [-s sectorsize] [-V version] prov"
"[-bdgPTv] [-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,
{
{ 'a', "aalgo", "", G_TYPE_STRING },
{ 'b', "boot", NULL, G_TYPE_BOOL },
{ 'B', "backupfile", "", G_TYPE_STRING },
{ 'd', "displaypass", NULL, G_TYPE_BOOL },
{ 'e', "ealgo", "", G_TYPE_STRING },
{ 'g', "geliboot", NULL, G_TYPE_BOOL },
{ 'i', "iterations", "-1", G_TYPE_NUMBER },
@ -182,13 +184,15 @@ struct g_command class_commands[] = {
{
{ 'b', "boot", NULL, G_TYPE_BOOL },
{ 'B', "noboot", NULL, G_TYPE_BOOL },
{ 'd', "displaypass", NULL, G_TYPE_BOOL },
{ 'D', "nodisplaypass", NULL, G_TYPE_BOOL },
{ 'g', "geliboot", NULL, G_TYPE_BOOL },
{ 'G', "nogeliboot", NULL, G_TYPE_BOOL },
{ 't', "trim", NULL, G_TYPE_BOOL },
{ 'T', "notrim", NULL, G_TYPE_BOOL },
G_OPT_SENTINEL
},
"[-bBgGtT] prov ..."
"[-bBdDgGtT] prov ..."
},
{ "setkey", G_FLAG_VERBOSE, eli_main,
{
@ -708,6 +712,8 @@ eli_init(struct gctl_req *req)
md.md_flags |= G_ELI_FLAG_BOOT;
if (gctl_get_int(req, "geliboot"))
md.md_flags |= G_ELI_FLAG_GELIBOOT;
if (gctl_get_int(req, "displaypass"))
md.md_flags |= G_ELI_FLAG_GELIDISPLAYPASS;
if (gctl_get_int(req, "notrim"))
md.md_flags |= G_ELI_FLAG_NODELETE;
md.md_ealgo = CRYPTO_ALGORITHM_MIN - 1;
@ -912,7 +918,7 @@ eli_attach(struct gctl_req *req)
static void
eli_configure_detached(struct gctl_req *req, const char *prov, int boot,
int geliboot, int trim)
int geliboot, int displaypass, int trim)
{
struct g_eli_metadata md;
bool changed = 0;
@ -948,6 +954,21 @@ eli_configure_detached(struct gctl_req *req, const char *prov, int boot,
changed = 1;
}
if (displaypass == 1 && (md.md_flags & G_ELI_FLAG_GELIDISPLAYPASS)) {
if (verbose)
printf("GELIDISPLAYPASS flag already configured for %s.\n", prov);
} else if (displaypass == 0 &&
!(md.md_flags & G_ELI_FLAG_GELIDISPLAYPASS)) {
if (verbose)
printf("GELIDISPLAYPASS flag not configured for %s.\n", prov);
} else if (displaypass >= 0) {
if (displaypass)
md.md_flags |= G_ELI_FLAG_GELIDISPLAYPASS;
else
md.md_flags &= ~G_ELI_FLAG_GELIDISPLAYPASS;
changed = 1;
}
if (trim == 0 && (md.md_flags & G_ELI_FLAG_NODELETE)) {
if (verbose)
printf("TRIM disable flag already configured for %s.\n", prov);
@ -971,8 +992,9 @@ static void
eli_configure(struct gctl_req *req)
{
const char *prov;
bool boot, noboot, geliboot, nogeliboot, trim, notrim;
int doboot, dogeliboot, dotrim;
bool boot, noboot, geliboot, nogeliboot, displaypass, nodisplaypass;
bool trim, notrim;
int doboot, dogeliboot, dodisplaypass, dotrim;
int i, nargs;
nargs = gctl_get_int(req, "nargs");
@ -985,6 +1007,8 @@ eli_configure(struct gctl_req *req)
noboot = gctl_get_int(req, "noboot");
geliboot = gctl_get_int(req, "geliboot");
nogeliboot = gctl_get_int(req, "nogeliboot");
displaypass = gctl_get_int(req, "displaypass");
nodisplaypass = gctl_get_int(req, "nodisplaypass");
trim = gctl_get_int(req, "trim");
notrim = gctl_get_int(req, "notrim");
@ -1008,6 +1032,16 @@ eli_configure(struct gctl_req *req)
else if (nogeliboot)
dogeliboot = 0;
dodisplaypass = -1;
if (displaypass && nodisplaypass) {
gctl_error(req, "Options -d and -D are mutually exclusive.");
return;
}
if (displaypass)
dodisplaypass = 1;
else if (nodisplaypass)
dodisplaypass = 0;
dotrim = -1;
if (trim && notrim) {
gctl_error(req, "Options -t and -T are mutually exclusive.");
@ -1018,7 +1052,8 @@ eli_configure(struct gctl_req *req)
else if (notrim)
dotrim = 0;
if (doboot == -1 && dogeliboot == -1 && dotrim == -1) {
if (doboot == -1 && dogeliboot == -1 && dodisplaypass == -1 &&
dotrim == -1) {
gctl_error(req, "No option given.");
return;
}
@ -1028,8 +1063,10 @@ eli_configure(struct gctl_req *req)
/* Now the rest. */
for (i = 0; i < nargs; i++) {
prov = gctl_get_ascii(req, "arg%d", i);
if (!eli_is_attached(prov))
eli_configure_detached(req, prov, doboot, dogeliboot, dotrim);
if (!eli_is_attached(prov)) {
eli_configure_detached(req, prov, doboot, dogeliboot,
dodisplaypass, dotrim);
}
}
}

View File

@ -220,8 +220,9 @@ geli_taste(int read_func(void *vdev, void *priv, off_t off, void *buf,
/*
* Attempt to decrypt the device
*/
int
geli_attach(struct dsk *dskp, const char *passphrase, const u_char *mkeyp)
static int
geli_attach(struct geli_entry *ge, struct dsk *dskp, const char *passphrase,
const u_char *mkeyp)
{
u_char key[G_ELI_USERKEYLEN], mkey[G_ELI_DATAIVKEYLEN], *mkp;
u_int keynum;
@ -233,92 +234,83 @@ geli_attach(struct dsk *dskp, const char *passphrase, const u_char *mkeyp)
explicit_bzero(mkeyp, G_ELI_DATAIVKEYLEN);
}
SLIST_FOREACH_SAFE(geli_e, &geli_head, entries, geli_e_tmp) {
if (geli_same_device(geli_e, dskp) != 0) {
continue;
}
if (mkeyp != NULL || geli_findkey(geli_e, dskp, mkey) == 0) {
goto found_key;
}
g_eli_crypto_hmac_init(&ctx, NULL, 0);
/*
* Prepare Derived-Key from the user passphrase.
*/
if (geli_e->md.md_iterations < 0) {
/* XXX TODO: Support loading key files. */
return (1);
} else if (geli_e->md.md_iterations == 0) {
g_eli_crypto_hmac_update(&ctx, geli_e->md.md_salt,
sizeof(geli_e->md.md_salt));
g_eli_crypto_hmac_update(&ctx, passphrase,
strlen(passphrase));
} else if (geli_e->md.md_iterations > 0) {
printf("Calculating GELI Decryption Key disk%dp%d @ %d"
" iterations...\n", dskp->unit,
(dskp->slice > 0 ? dskp->slice : dskp->part),
geli_e->md.md_iterations);
u_char dkey[G_ELI_USERKEYLEN];
pkcs5v2_genkey(dkey, sizeof(dkey), geli_e->md.md_salt,
sizeof(geli_e->md.md_salt), passphrase,
geli_e->md.md_iterations);
g_eli_crypto_hmac_update(&ctx, dkey, sizeof(dkey));
explicit_bzero(dkey, sizeof(dkey));
}
g_eli_crypto_hmac_final(&ctx, key, 0);
error = g_eli_mkey_decrypt(&geli_e->md, key, mkey, &keynum);
if (error == -1) {
explicit_bzero(mkey, sizeof(mkey));
explicit_bzero(key, sizeof(key));
printf("Bad GELI key: bad password?\n");
return (error);
} else if (error != 0) {
explicit_bzero(mkey, sizeof(mkey));
explicit_bzero(key, sizeof(key));
printf("Failed to decrypt GELI master key: %d\n", error);
return (error);
} else {
/* Add key to keychain */
save_key(key);
explicit_bzero(&key, sizeof(key));
}
found_key:
/* Store the keys */
bcopy(mkey, geli_e->sc.sc_mkey, sizeof(geli_e->sc.sc_mkey));
bcopy(mkey, geli_e->sc.sc_ivkey, sizeof(geli_e->sc.sc_ivkey));
mkp = mkey + sizeof(geli_e->sc.sc_ivkey);
if ((geli_e->sc.sc_flags & G_ELI_FLAG_AUTH) == 0) {
bcopy(mkp, geli_e->sc.sc_ekey, G_ELI_DATAKEYLEN);
} else {
/*
* The encryption key is: ekey = HMAC_SHA512(Data-Key, 0x10)
*/
g_eli_crypto_hmac(mkp, G_ELI_MAXKEYLEN, "\x10", 1,
geli_e->sc.sc_ekey, 0);
}
explicit_bzero(mkey, sizeof(mkey));
/* Initialize the per-sector IV. */
switch (geli_e->sc.sc_ealgo) {
case CRYPTO_AES_XTS:
break;
default:
SHA256_Init(&geli_e->sc.sc_ivctx);
SHA256_Update(&geli_e->sc.sc_ivctx, geli_e->sc.sc_ivkey,
sizeof(geli_e->sc.sc_ivkey));
break;
}
return (0);
if (mkeyp != NULL || geli_findkey(ge, dskp, mkey) == 0) {
goto found_key;
}
/* Disk not found. */
return (2);
g_eli_crypto_hmac_init(&ctx, NULL, 0);
/*
* Prepare Derived-Key from the user passphrase.
*/
if (geli_e->md.md_iterations < 0) {
/* XXX TODO: Support loading key files. */
return (1);
} else if (geli_e->md.md_iterations == 0) {
g_eli_crypto_hmac_update(&ctx, geli_e->md.md_salt,
sizeof(geli_e->md.md_salt));
g_eli_crypto_hmac_update(&ctx, passphrase,
strlen(passphrase));
} else if (geli_e->md.md_iterations > 0) {
printf("Calculating GELI Decryption Key disk%dp%d @ %d"
" iterations...\n", dskp->unit,
(dskp->slice > 0 ? dskp->slice : dskp->part),
geli_e->md.md_iterations);
u_char dkey[G_ELI_USERKEYLEN];
pkcs5v2_genkey(dkey, sizeof(dkey), geli_e->md.md_salt,
sizeof(geli_e->md.md_salt), passphrase,
geli_e->md.md_iterations);
g_eli_crypto_hmac_update(&ctx, dkey, sizeof(dkey));
explicit_bzero(dkey, sizeof(dkey));
}
g_eli_crypto_hmac_final(&ctx, key, 0);
error = g_eli_mkey_decrypt(&geli_e->md, key, mkey, &keynum);
if (error == -1) {
explicit_bzero(mkey, sizeof(mkey));
explicit_bzero(key, sizeof(key));
printf("Bad GELI key: bad password?\n");
return (error);
} else if (error != 0) {
explicit_bzero(mkey, sizeof(mkey));
explicit_bzero(key, sizeof(key));
printf("Failed to decrypt GELI master key: %d\n", error);
return (error);
} else {
/* Add key to keychain */
save_key(key);
explicit_bzero(&key, sizeof(key));
}
found_key:
/* Store the keys */
bcopy(mkey, geli_e->sc.sc_mkey, sizeof(geli_e->sc.sc_mkey));
bcopy(mkey, geli_e->sc.sc_ivkey, sizeof(geli_e->sc.sc_ivkey));
mkp = mkey + sizeof(geli_e->sc.sc_ivkey);
if ((geli_e->sc.sc_flags & G_ELI_FLAG_AUTH) == 0) {
bcopy(mkp, geli_e->sc.sc_ekey, G_ELI_DATAKEYLEN);
} else {
/*
* The encryption key is: ekey = HMAC_SHA512(Data-Key, 0x10)
*/
g_eli_crypto_hmac(mkp, G_ELI_MAXKEYLEN, "\x10", 1,
geli_e->sc.sc_ekey, 0);
}
explicit_bzero(mkey, sizeof(mkey));
/* Initialize the per-sector IV. */
switch (geli_e->sc.sc_ealgo) {
case CRYPTO_AES_XTS:
break;
default:
SHA256_Init(&geli_e->sc.sc_ivctx);
SHA256_Update(&geli_e->sc.sc_ivctx, geli_e->sc.sc_ivkey,
sizeof(geli_e->sc.sc_ivkey));
break;
}
return (0);
}
int
@ -402,7 +394,7 @@ geli_havekey(struct dsk *dskp)
}
if (geli_findkey(geli_e, dskp, mkey) == 0) {
if (geli_attach(dskp, NULL, mkey) == 0) {
if (geli_attach(geli_e, dskp, NULL, mkey) == 0) {
return (0);
}
}
@ -417,20 +409,28 @@ geli_passphrase(char *pw, int disk, int parttype, int part, struct dsk *dskp)
{
int i;
/* TODO: Implement GELI keyfile(s) support */
for (i = 0; i < 3; i++) {
/* Try cached passphrase */
if (i == 0 && pw[0] != '\0') {
if (geli_attach(dskp, pw, NULL) == 0) {
SLIST_FOREACH_SAFE(geli_e, &geli_head, entries, geli_e_tmp) {
if (geli_same_device(geli_e, dskp) != 0) {
continue;
}
/* TODO: Implement GELI keyfile(s) support */
for (i = 0; i < 3; i++) {
/* Try cached passphrase */
if (i == 0 && pw[0] != '\0') {
if (geli_attach(geli_e, dskp, pw, NULL) == 0) {
return (0);
}
}
printf("GELI Passphrase for disk%d%c%d: ", disk,
parttype, part);
pwgets(pw, GELI_PW_MAXLEN,
(geli_e->md.md_flags & G_ELI_FLAG_GELIDISPLAYPASS) == 0);
printf("\n");
if (geli_attach(geli_e, dskp, pw, NULL) == 0) {
return (0);
}
}
printf("GELI Passphrase for disk%d%c%d: ", disk, parttype, part);
pwgets(pw, GELI_PW_MAXLEN);
printf("\n");
if (geli_attach(dskp, pw, NULL) == 0) {
return (0);
}
}
return (1);

View File

@ -46,12 +46,11 @@
#define GELI_MAX_KEYS 64
#define GELI_PW_MAXLEN 256
extern void pwgets(char *buf, int n);
extern void pwgets(char *buf, int n, int hide);
void geli_init(void);
int geli_taste(int read_func(void *vdev, void *priv, off_t off,
void *buf, size_t bytes), struct dsk *dsk, daddr_t lastsector);
int geli_attach(struct dsk *dskp, const char *passphrase, const u_char *mkeyp);
int is_geli(struct dsk *dsk);
int geli_read(struct dsk *dsk, off_t offset, u_char *buf, size_t bytes);
int geli_decrypt(u_int algo, u_char *data, size_t datasize,

View File

@ -39,7 +39,7 @@ __FBSDID("$FreeBSD$");
/* gets() with constrained input length, for passwords */
void
pwgets(char *buf, int n)
pwgets(char *buf, int n, int hide)
{
int c;
char *lp;
@ -55,9 +55,11 @@ pwgets(char *buf, int n)
case '\177':
if (lp > buf) {
lp--;
putchar('\b');
putchar(' ');
putchar('\b');
if (hide == 0) {
putchar('\b');
putchar(' ');
putchar('\b');
}
}
break;
case 'u'&037:
@ -68,7 +70,9 @@ pwgets(char *buf, int n)
default:
if ((n < 1) || ((lp - buf) < n - 1)) {
*lp++ = c;
putchar('*');
if (hide == 0) {
putchar('*');
}
}
}
/*NOTREACHED*/

View File

@ -1023,7 +1023,7 @@ g_eli_taste(struct g_class *mp, struct g_provider *pp, int flags __unused)
struct hmac_ctx ctx;
char passphrase[256];
u_char key[G_ELI_USERKEYLEN], mkey[G_ELI_DATAIVKEYLEN];
u_int i, nkey, nkeyfiles, tries;
u_int i, nkey, nkeyfiles, tries, showpass;
int error;
struct keybuf *keybuf;
@ -1112,8 +1112,11 @@ g_eli_taste(struct g_class *mp, struct g_provider *pp, int flags __unused)
sizeof(passphrase));
} else {
printf("Enter passphrase for %s: ", pp->name);
showpass = g_eli_visible_passphrase;
if ((md.md_flags & G_ELI_FLAG_GELIDISPLAYPASS) != 0)
showpass = GETS_ECHOPASS;
cngets(passphrase, sizeof(passphrase),
g_eli_visible_passphrase);
showpass);
memcpy(cached_passphrase, passphrase,
sizeof(passphrase));
}
@ -1232,6 +1235,7 @@ g_eli_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
ADD_FLAG(G_ELI_FLAG_RO, "READ-ONLY");
ADD_FLAG(G_ELI_FLAG_NODELETE, "NODELETE");
ADD_FLAG(G_ELI_FLAG_GELIBOOT, "GELIBOOT");
ADD_FLAG(G_ELI_FLAG_GELIDISPLAYPASS, "GELIDISPLAYPASS");
#undef ADD_FLAG
}
sbuf_printf(sb, "</Flags>\n");

View File

@ -100,6 +100,8 @@
#define G_ELI_FLAG_NODELETE 0x00000040
/* This GELI supports GELIBoot */
#define G_ELI_FLAG_GELIBOOT 0x00000080
/* Hide passphrase length in GELIboot. */
#define G_ELI_FLAG_GELIDISPLAYPASS 0x00000100
/* RUNTIME FLAGS. */
/* Provider was open for writing. */
#define G_ELI_FLAG_WOPEN 0x00010000

View File

@ -377,6 +377,7 @@ g_eli_ctl_configure(struct gctl_req *req, struct g_class *mp)
const char *prov;
u_char *sector;
int *nargs, *boot, *noboot, *trim, *notrim, *geliboot, *nogeliboot;
int *displaypass, *nodisplaypass;
int zero, error, changed;
u_int i;
@ -434,6 +435,19 @@ g_eli_ctl_configure(struct gctl_req *req, struct g_class *mp)
if (*geliboot || *nogeliboot)
changed = 1;
displaypass = gctl_get_paraml(req, "displaypass", sizeof(*displaypass));
if (displaypass == NULL)
displaypass = &zero;
nodisplaypass = gctl_get_paraml(req, "nodisplaypass", sizeof(*nodisplaypass));
if (nodisplaypass == NULL)
nodisplaypass = &zero;
if (*displaypass && *nodisplaypass) {
gctl_error(req, "Options -d and -D are mutually exclusive.");
return;
}
if (*displaypass || *nodisplaypass)
changed = 1;
if (!changed) {
gctl_error(req, "No option given.");
return;
@ -492,6 +506,17 @@ g_eli_ctl_configure(struct gctl_req *req, struct g_class *mp)
continue;
}
if (*displaypass && (sc->sc_flags & G_ELI_FLAG_GELIDISPLAYPASS)) {
G_ELI_DEBUG(1, "GELIDISPLAYPASS flag already configured for %s.",
prov);
continue;
} else if (*nodisplaypass &&
!(sc->sc_flags & G_ELI_FLAG_GELIDISPLAYPASS)) {
G_ELI_DEBUG(1, "GELIDISPLAYPASS flag not configured for %s.",
prov);
continue;
}
if (!(sc->sc_flags & G_ELI_FLAG_ONETIME)) {
/*
* ONETIME providers don't write metadata to
@ -535,6 +560,14 @@ g_eli_ctl_configure(struct gctl_req *req, struct g_class *mp)
sc->sc_flags &= ~G_ELI_FLAG_GELIBOOT;
}
if (*displaypass) {
md.md_flags |= G_ELI_FLAG_GELIDISPLAYPASS;
sc->sc_flags |= G_ELI_FLAG_GELIDISPLAYPASS;
} else if (*nodisplaypass) {
md.md_flags &= ~G_ELI_FLAG_GELIDISPLAYPASS;
sc->sc_flags &= ~G_ELI_FLAG_GELIDISPLAYPASS;
}
if (sc->sc_flags & G_ELI_FLAG_ONETIME) {
/* There's no metadata on disk so we are done here. */
continue;