diff --git a/sbin/geom/class/eli/geli.8 b/sbin/geom/class/eli/geli.8 index d3ada239a6a2..268e8e645337 100644 --- a/sbin/geom/class/eli/geli.8 +++ b/sbin/geom/class/eli/geli.8 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd September 23, 2010 +.Dd September 25, 2010 .Dt GELI 8 .Os .Sh NAME @@ -56,6 +56,7 @@ utility: .Op Fl B Ar backupfile .Op Fl e Ar ealgo .Op Fl i Ar iterations +.Op Fl J Ar newpassfile .Op Fl K Ar newkeyfile .Op Fl l Ar keylen .Op Fl s Ar sectorsize @@ -66,6 +67,7 @@ utility: .Nm .Cm attach .Op Fl dprv +.Op Fl j Ar passfile .Op Fl k Ar keyfile .Ar prov .Nm @@ -91,6 +93,8 @@ utility: .Cm setkey .Op Fl pPv .Op Fl i Ar iterations +.Op Fl j Ar passfile +.Op Fl J Ar newpassfile .Op Fl k Ar keyfile .Op Fl K Ar newkeyfile .Op Fl n Ar keyno @@ -223,7 +227,7 @@ The metadata can be recovered with the subcommand described below. .Pp Additional options include: -.Bl -tag -width ".Fl a Ar newkeyfile" +.Bl -tag -width ".Fl J Ar newpassfile" .It Fl a Ar aalgo Enable data integrity verification (authentication) using the given algorithm. This will reduce size of available storage and also reduce speed. @@ -272,15 +276,19 @@ If this option is not specified, .Nm will find the number of iterations which is equal to 2 seconds of crypto work. If 0 is given, PKCS#5v2 will not be used. +.It Fl J Ar newpassfile +Specifies a file which contains the passphrase or its part. +If +.Ar newpassfile +is given as -, standard input will be used. +Only the first line (excluding new-line character) is taken from the given file. +This argument can be specified multiple times. .It Fl K Ar newkeyfile Specifies a file which contains part of the key. If .Ar newkeyfile is given as -, standard input will be used. -Here is how more than one file with a key component can be used: -.Bd -literal -offset indent -# cat key1 key2 key3 | geli init -K - /dev/da0 -.Ed +This argument can be specified multiple times. .It Fl l Ar keylen Key length to use with the given cryptographic algorithm. If not given, the default key length for the given algorithm is used, which is: @@ -309,7 +317,7 @@ provider's name with an suffix. .Pp Additional options include: -.Bl -tag -width ".Fl a Ar keyfile" +.Bl -tag -width ".Fl j Ar passfile" .It Fl d If specified, a decrypted provider will be detached automatically on last close. This can help with short memory - user does not have to remember to detach the @@ -321,6 +329,13 @@ Probably a better choice is the option for the .Cm detach subcommand. +.It Fl j Ar passfile +Specifies a file which contains the passphrase or its part. +For more information see the description of the +.Fl J +option for the +.Cm init +subcommand. .It Fl k Ar keyfile Specifies a file which contains part of the key. For more information see the description of the @@ -409,13 +424,17 @@ When a provider is attached, the user does not have to provide an old passphrase/keyfile. .Pp Additional options include: -.Bl -tag -width ".Fl a Ar iterations" +.Bl -tag -width ".Fl J Ar newpassfile" .It Fl i Ar iterations Number of iterations to use with PKCS#5v2. If 0 is given, PKCS#5v2 will not be used. To be able to use this option with .Cm setkey subcommand, only one key have to be defined and this key has to be changed. +.It Fl j Ar passfile +Specifies a file which contains the old passphrase or its part. +.It Fl J Ar newpassfile +Specifies a file which contains the new passphrase or its part. .It Fl k Ar keyfile Specifies a file which contains part of the old key. .It Fl K Ar newkeyfile @@ -638,7 +657,7 @@ keyfile: # dd if=/dev/random of=/boot/keys/da0.key0 bs=32k count=1 # dd if=/dev/random of=/boot/keys/da0.key1 bs=32k count=1 # dd if=/dev/random of=/boot/keys/da0.key2 bs=32k count=1 -# cat /boot/keys/da0.key0 /boot/keys/da0.key1 /boot/keys/da0.key2 | geli init -b -K - da0 +# geli init -b -K /boot/keys/da0.key0 -K /boot/keys/da0.key1 -K /boot/keys/da0.key2 da0 Enter new passphrase: Reenter new passphrase: # dd if=/dev/random of=/dev/da1s3a bs=1m @@ -711,6 +730,22 @@ update the metadata: # geli resize -s 1g ada0p1 # geli attach -k keyfile -p ada0p1 .Ed +.Pp +Initialize provider with passphrase split into two files. +The provider can be attached by giving those two files or by giving +.Dq foobar +passphrase on +.Nm +prompt: +.Bd -literal -offset indent +# echo foo > da0.pass0 +# echo bar > da0.pass1 +# geli init -J da0.pass0 -J da0.pass1 da0 +# geli attach -j da0.pass0 -j da0.pass1 da0 +# geli detach da0 +# geli attach da0 +Enter passphrase: foobar +.Ed .Sh ENCRYPTION MODES .Nm supports two encryption modes: diff --git a/sbin/geom/class/eli/geom_eli.c b/sbin/geom/class/eli/geom_eli.c index 1735d7a42bc3..d7097216b602 100644 --- a/sbin/geom/class/eli/geom_eli.c +++ b/sbin/geom/class/eli/geom_eli.c @@ -30,6 +30,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -79,14 +80,14 @@ static int eli_backup_create(struct gctl_req *req, const char *prov, /* * Available commands: * - * init [-bhPv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-K newkeyfile] prov + * init [-bhPv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] prov * label - alias for 'init' - * attach [-dprv] [-k keyfile] prov + * attach [-dprv] [-j passfile] [-k keyfile] prov * detach [-fl] prov ... * stop - alias for 'detach' * onetime [-d] [-a aalgo] [-e ealgo] [-l keylen] prov * configure [-bB] prov ... - * setkey [-pPv] [-n keyno] [-k keyfile] [-K newkeyfile] prov + * setkey [-pPv] [-n keyno] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov * delkey [-afv] [-n keyno] prov * kill [-av] [prov ...] * backup [-v] prov file @@ -103,13 +104,14 @@ struct g_command class_commands[] = { { 'B', "backupfile", "", G_TYPE_STRING }, { 'e', "ealgo", GELI_ENC_ALGO, G_TYPE_STRING }, { 'i', "iterations", "-1", G_TYPE_NUMBER }, - { 'K', "newkeyfile", "", G_TYPE_STRING }, + { 'J', "newpassfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, + { 'K', "newkeyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, { 'l', "keylen", "0", G_TYPE_NUMBER }, { 'P', "nonewpassphrase", NULL, G_TYPE_BOOL }, { 's', "sectorsize", "0", G_TYPE_NUMBER }, G_OPT_SENTINEL }, - "[-bPv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-K newkeyfile] [-s sectorsize] prov" + "[-bPv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] [-s sectorsize] prov" }, { "label", G_FLAG_VERBOSE, eli_main, { @@ -118,7 +120,8 @@ struct g_command class_commands[] = { { 'B', "backupfile", "", G_TYPE_STRING }, { 'e', "ealgo", GELI_ENC_ALGO, G_TYPE_STRING }, { 'i', "iterations", "-1", G_TYPE_NUMBER }, - { 'K', "newkeyfile", "", G_TYPE_STRING }, + { 'J', "newpassfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, + { 'K', "newkeyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, { 'l', "keylen", "0", G_TYPE_NUMBER }, { 'P', "nonewpassphrase", NULL, G_TYPE_BOOL }, { 's', "sectorsize", "0", G_TYPE_NUMBER }, @@ -129,12 +132,13 @@ struct g_command class_commands[] = { { "attach", G_FLAG_VERBOSE | G_FLAG_LOADKLD, eli_main, { { 'd', "detach", NULL, G_TYPE_BOOL }, - { 'k', "keyfile", "", G_TYPE_STRING }, + { 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, + { 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, { 'p', "nopassphrase", NULL, G_TYPE_BOOL }, { 'r', "readonly", NULL, G_TYPE_BOOL }, G_OPT_SENTINEL }, - "[-dprv] [-k keyfile] prov" + "[-dprv] [-j passfile] [-k keyfile] prov" }, { "detach", 0, NULL, { @@ -174,14 +178,16 @@ struct g_command class_commands[] = { { "setkey", G_FLAG_VERBOSE, eli_main, { { 'i', "iterations", "-1", G_TYPE_NUMBER }, - { 'k', "keyfile", "", G_TYPE_STRING }, - { 'K', "newkeyfile", "", G_TYPE_STRING }, + { 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, + { 'J', "newpassfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, + { 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, + { 'K', "newkeyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, { 'n', "keyno", "-1", G_TYPE_NUMBER }, { 'p', "nopassphrase", NULL, G_TYPE_BOOL }, { 'P', "nonewpassphrase", NULL, G_TYPE_BOOL }, G_OPT_SENTINEL }, - "[-pPv] [-n keyno] [-i iterations] [-k keyfile] [-K newkeyfile] prov" + "[-pPv] [-n keyno] [-i iterations] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov" }, { "delkey", G_FLAG_VERBOSE, eli_main, { @@ -249,7 +255,7 @@ eli_protect(struct gctl_req *req) } static void -eli_main(struct gctl_req *req, unsigned flags) +eli_main(struct gctl_req *req, unsigned int flags) { const char *name; @@ -295,7 +301,7 @@ arc4rand(unsigned char *buf, size_t size) { uint32_t *buf4; size_t size4; - unsigned i; + unsigned int i; buf4 = (uint32_t *)buf; size4 = size / 4; @@ -324,123 +330,216 @@ eli_is_attached(const char *prov) return (0); } +static int +eli_genkey_files(struct gctl_req *req, bool new, const char *type, + struct hmac_ctx *ctxp, char *passbuf, size_t passbufsize) +{ + char *p, buf[MAXPHYS], argname[16]; + const char *file; + int error, fd, i; + ssize_t done; + + assert((strcmp(type, "keyfile") == 0 && ctxp != NULL && + passbuf == NULL && passbufsize == 0) || + (strcmp(type, "passfile") == 0 && ctxp == NULL && + passbuf != NULL && passbufsize > 0)); + assert(strcmp(type, "keyfile") == 0 || passbuf[0] == '\0'); + + for (i = 0; ; i++) { + snprintf(argname, sizeof(argname), "%s%s%d", + new ? "new" : "", type, i); + + /* No more {key,pass}files? */ + if (!gctl_has_param(req, argname)) + return (i); + + file = gctl_get_ascii(req, argname); + assert(file != NULL); + + if (strcmp(file, "-") == 0) + fd = STDIN_FILENO; + else { + fd = open(file, O_RDONLY); + if (fd == -1) { + gctl_error(req, "Cannot open %s %s: %s.", + type, file, strerror(errno)); + return (-1); + } + } + if (strcmp(type, "keyfile") == 0) { + while ((done = read(fd, buf, sizeof(buf))) > 0) + g_eli_crypto_hmac_update(ctxp, buf, done); + } else /* if (strcmp(type, "passfile") == 0) */ { + while ((done = read(fd, buf, sizeof(buf) - 1)) > 0) { + buf[done] = '\0'; + p = strchr(buf, '\n'); + if (p != NULL) { + *p = '\0'; + done = p - buf; + } + if (strlcat(passbuf, buf, passbufsize) >= + passbufsize) { + gctl_error(req, + "Passphrase in %s too long.", file); + bzero(buf, sizeof(buf)); + return (-1); + } + if (p != NULL) + break; + } + } + error = errno; + if (strcmp(file, "-") != 0) + close(fd); + bzero(buf, sizeof(buf)); + if (done == -1) { + gctl_error(req, "Cannot read %s %s: %s.", + type, file, strerror(error)); + return (-1); + } + } + /* NOTREACHED */ +} + +static int +eli_genkey_passphrase_prompt(struct gctl_req *req, bool new, char *passbuf, + size_t passbufsize) +{ + char *p; + + for (;;) { + p = readpassphrase( + new ? "Enter new passphrase:" : "Enter passphrase:", + passbuf, passbufsize, RPP_ECHO_OFF | RPP_REQUIRE_TTY); + if (p == NULL) { + bzero(passbuf, passbufsize); + gctl_error(req, "Cannot read passphrase: %s.", + strerror(errno)); + return (-1); + } + + if (new) { + char tmpbuf[BUFSIZ]; + + p = readpassphrase("Reenter new passphrase: ", + tmpbuf, sizeof(tmpbuf), + RPP_ECHO_OFF | RPP_REQUIRE_TTY); + if (p == NULL) { + bzero(passbuf, passbufsize); + gctl_error(req, + "Cannot read passphrase: %s.", + strerror(errno)); + return (-1); + } + + if (strcmp(passbuf, tmpbuf) != 0) { + bzero(passbuf, passbufsize); + fprintf(stderr, "They didn't match.\n"); + continue; + } + bzero(tmpbuf, sizeof(tmpbuf)); + } + return (0); + } + /* NOTREACHED */ +} + +static int +eli_genkey_passphrase(struct gctl_req *req, struct g_eli_metadata *md, bool new, + struct hmac_ctx *ctxp) +{ + char passbuf[MAXPHYS]; + bool nopassphrase; + int nfiles; + + nopassphrase = + gctl_get_int(req, new ? "nonewpassphrase" : "nopassphrase"); + if (nopassphrase) { + if (gctl_has_param(req, new ? "newpassfile0" : "passfile0")) { + gctl_error(req, + "Options -%c and -%c are mutually exclusive.", + new ? 'J' : 'j', new ? 'P' : 'p'); + return (-1); + } + return (0); + } + + if (!new && md->md_iterations == -1) { + gctl_error(req, "Missing -p flag."); + return (-1); + } + passbuf[0] = '\0'; + nfiles = eli_genkey_files(req, new, "passfile", NULL, passbuf, + sizeof(passbuf)); + if (nfiles == -1) + return (-1); + else if (nfiles == 0) { + if (eli_genkey_passphrase_prompt(req, new, passbuf, + sizeof(passbuf)) == -1) { + return (-1); + } + } + /* + * Field md_iterations equal to -1 means "choose some sane + * value for me". + */ + if (md->md_iterations == -1) { + assert(new); + if (verbose) + printf("Calculating number of iterations...\n"); + md->md_iterations = pkcs5v2_calculate(2000000); + assert(md->md_iterations > 0); + if (verbose) { + printf("Done, using %d iterations.\n", + md->md_iterations); + } + } + /* + * If md_iterations is equal to 0, user doesn't want PKCS#5v2. + */ + if (md->md_iterations == 0) { + g_eli_crypto_hmac_update(ctxp, md->md_salt, + sizeof(md->md_salt)); + g_eli_crypto_hmac_update(ctxp, passbuf, strlen(passbuf)); + } else /* if (md->md_iterations > 0) */ { + unsigned char dkey[G_ELI_USERKEYLEN]; + + pkcs5v2_genkey(dkey, sizeof(dkey), md->md_salt, + sizeof(md->md_salt), passbuf, md->md_iterations); + g_eli_crypto_hmac_update(ctxp, dkey, sizeof(dkey)); + bzero(dkey, sizeof(dkey)); + } + bzero(passbuf, sizeof(passbuf)); + + return (0); +} + static unsigned char * eli_genkey(struct gctl_req *req, struct g_eli_metadata *md, unsigned char *key, - int new) + bool new) { struct hmac_ctx ctx; - const char *str; - int error, nopassphrase; + bool nopassphrase; + int nfiles; nopassphrase = gctl_get_int(req, new ? "nonewpassphrase" : "nopassphrase"); g_eli_crypto_hmac_init(&ctx, NULL, 0); - str = gctl_get_ascii(req, new ? "newkeyfile" : "keyfile"); - if (str[0] == '\0' && nopassphrase) { + nfiles = eli_genkey_files(req, new, "keyfile", &ctx, NULL, 0); + if (nfiles == -1) + return (NULL); + else if (nfiles == 0 && nopassphrase) { gctl_error(req, "No key components given."); return (NULL); - } else if (str[0] != '\0') { - char buf[MAXPHYS]; - ssize_t done; - int fd; - - if (strcmp(str, "-") == 0) - fd = STDIN_FILENO; - else { - fd = open(str, O_RDONLY); - if (fd == -1) { - gctl_error(req, "Cannot open keyfile %s: %s.", - str, strerror(errno)); - return (NULL); - } - } - while ((done = read(fd, buf, sizeof(buf))) > 0) - g_eli_crypto_hmac_update(&ctx, buf, done); - error = errno; - if (strcmp(str, "-") != 0) - close(fd); - bzero(buf, sizeof(buf)); - if (done == -1) { - gctl_error(req, "Cannot read keyfile %s: %s.", str, - strerror(error)); - return (NULL); - } } - if (!nopassphrase) { - char buf1[BUFSIZ], buf2[BUFSIZ], *p; + if (eli_genkey_passphrase(req, md, new, &ctx) == -1) + return (NULL); - if (!new && md->md_iterations == -1) { - gctl_error(req, "Missing -p flag."); - return (NULL); - } - for (;;) { - p = readpassphrase( - new ? "Enter new passphrase:" : "Enter passphrase:", - buf1, sizeof(buf1), RPP_ECHO_OFF | RPP_REQUIRE_TTY); - if (p == NULL) { - bzero(buf1, sizeof(buf1)); - gctl_error(req, "Cannot read passphrase: %s.", - strerror(errno)); - return (NULL); - } - - if (new) { - p = readpassphrase("Reenter new passphrase: ", - buf2, sizeof(buf2), - RPP_ECHO_OFF | RPP_REQUIRE_TTY); - if (p == NULL) { - bzero(buf1, sizeof(buf1)); - gctl_error(req, - "Cannot read passphrase: %s.", - strerror(errno)); - return (NULL); - } - - if (strcmp(buf1, buf2) != 0) { - bzero(buf2, sizeof(buf2)); - fprintf(stderr, "They didn't match.\n"); - continue; - } - bzero(buf2, sizeof(buf2)); - } - break; - } - /* - * Field md_iterations equal to -1 means "choose some sane - * value for me". - */ - if (md->md_iterations == -1) { - assert(new); - if (verbose) - printf("Calculating number of iterations...\n"); - md->md_iterations = pkcs5v2_calculate(2000000); - assert(md->md_iterations > 0); - if (verbose) { - printf("Done, using %d iterations.\n", - md->md_iterations); - } - } - /* - * If md_iterations is equal to 0, user don't want PKCS#5v2. - */ - if (md->md_iterations == 0) { - g_eli_crypto_hmac_update(&ctx, md->md_salt, - sizeof(md->md_salt)); - g_eli_crypto_hmac_update(&ctx, buf1, strlen(buf1)); - } else /* if (md->md_iterations > 0) */ { - unsigned char dkey[G_ELI_USERKEYLEN]; - - pkcs5v2_genkey(dkey, sizeof(dkey), md->md_salt, - sizeof(md->md_salt), buf1, md->md_iterations); - g_eli_crypto_hmac_update(&ctx, dkey, sizeof(dkey)); - bzero(dkey, sizeof(dkey)); - } - bzero(buf1, sizeof(buf1)); - } g_eli_crypto_hmac_final(&ctx, key, 0); + return (key); } @@ -640,7 +739,7 @@ eli_init(struct gctl_req *req) arc4rand(md.md_mkeys, sizeof(md.md_mkeys)); /* Generate user key. */ - if (eli_genkey(req, &md, key, 1) == NULL) { + if (eli_genkey(req, &md, key, true) == NULL) { bzero(key, sizeof(key)); bzero(&md, sizeof(md)); return; @@ -720,7 +819,7 @@ eli_attach(struct gctl_req *req) return; } - if (eli_genkey(req, &md, key, 0) == NULL) { + if (eli_genkey(req, &md, key, false) == NULL) { bzero(key, sizeof(key)); return; } @@ -734,7 +833,7 @@ eli_attach(struct gctl_req *req) } static void -eli_configure_detached(struct gctl_req *req, const char *prov, int boot) +eli_configure_detached(struct gctl_req *req, const char *prov, bool boot) { struct g_eli_metadata md; @@ -761,7 +860,8 @@ static void eli_configure(struct gctl_req *req) { const char *prov; - int i, nargs, boot, noboot; + bool boot, noboot; + int i, nargs; nargs = gctl_get_int(req, "nargs"); if (nargs == 0) { @@ -806,7 +906,7 @@ eli_setkey_attached(struct gctl_req *req, struct g_eli_metadata *md) old = md->md_iterations; /* Generate key for Master Key encryption. */ - if (eli_genkey(req, md, key, 1) == NULL) { + if (eli_genkey(req, md, key, true) == NULL) { bzero(key, sizeof(key)); return; } @@ -831,8 +931,8 @@ eli_setkey_detached(struct gctl_req *req, const char *prov, { unsigned char key[G_ELI_USERKEYLEN], mkey[G_ELI_DATAIVKEYLEN]; unsigned char *mkeydst; + unsigned int nkey; intmax_t val; - unsigned nkey; int error; if (md->md_keys == 0) { @@ -841,7 +941,7 @@ eli_setkey_detached(struct gctl_req *req, const char *prov, } /* Generate key for Master Key decryption. */ - if (eli_genkey(req, md, key, 0) == NULL) { + if (eli_genkey(req, md, key, false) == NULL) { bzero(key, sizeof(key)); return; } @@ -897,7 +997,7 @@ eli_setkey_detached(struct gctl_req *req, const char *prov, bzero(mkey, sizeof(mkey)); /* Generate key for Master Key encryption. */ - if (eli_genkey(req, md, key, 1) == NULL) { + if (eli_genkey(req, md, key, true) == NULL) { bzero(key, sizeof(key)); bzero(md, sizeof(*md)); return; @@ -959,9 +1059,9 @@ eli_delkey_detached(struct gctl_req *req, const char *prov) { struct g_eli_metadata md; unsigned char *mkeydst; + unsigned int nkey; intmax_t val; - unsigned nkey; - int all, force; + bool all, force; if (eli_metadata_read(req, prov, &md) == -1) return;