From owner-svn-src-head@freebsd.org Mon Jul 23 23:04:44 2018 Return-Path: Delivered-To: svn-src-head@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id A87771056BFC; Mon, 23 Jul 2018 23:04:44 +0000 (UTC) (envelope-from woodsb02@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 582137231B; Mon, 23 Jul 2018 23:04:44 +0000 (UTC) (envelope-from woodsb02@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 38ECD22F85; Mon, 23 Jul 2018 23:04:44 +0000 (UTC) (envelope-from woodsb02@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id w6NN4iTM089529; Mon, 23 Jul 2018 23:04:44 GMT (envelope-from woodsb02@FreeBSD.org) Received: (from woodsb02@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id w6NN4hUh089528; Mon, 23 Jul 2018 23:04:43 GMT (envelope-from woodsb02@FreeBSD.org) Message-Id: <201807232304.w6NN4hUh089528@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: woodsb02 set sender to woodsb02@FreeBSD.org using -f From: Ben Woods Date: Mon, 23 Jul 2018 23:04:43 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r336659 - head/lib/geom/eli X-SVN-Group: head X-SVN-Commit-Author: woodsb02 X-SVN-Commit-Paths: head/lib/geom/eli X-SVN-Commit-Revision: 336659 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.27 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 23 Jul 2018 23:04:44 -0000 Author: woodsb02 (ports committer) Date: Mon Jul 23 23:04:43 2018 New Revision: 336659 URL: https://svnweb.freebsd.org/changeset/base/336659 Log: geli init: Allow initialization of multiple geli providers at once if they use same passphrase and keyfiles. Unique salt will be randomly generated for each provider to ensure the Master Key for each is unique. This change follows on from r335673 and r336602, which allowed multiple providers to be attached in a single command. Reviewed by: asomers Approved by: sobomax Differential Revision: https://reviews.freebsd.org/D16115 Modified: head/lib/geom/eli/geli.8 head/lib/geom/eli/geom_eli.c Modified: head/lib/geom/eli/geli.8 ============================================================================== --- head/lib/geom/eli/geli.8 Mon Jul 23 22:15:36 2018 (r336658) +++ head/lib/geom/eli/geli.8 Mon Jul 23 23:04:43 2018 (r336659) @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd June 27, 2018 +.Dd July 24, 2018 .Dt GELI 8 .Os .Sh NAME @@ -61,7 +61,7 @@ utility: .Op Fl l Ar keylen .Op Fl s Ar sectorsize .Op Fl V Ar version -.Ar prov +.Ar prov ... .Nm .Cm label - an alias for .Cm init @@ -233,10 +233,14 @@ The first argument to indicates an action to be performed: .Bl -tag -width ".Cm configure" .It Cm init -Initialize the provider which needs to be encrypted. +Initialize providers which need to be encrypted. +If multiple providers are listed as arguments, they will all be initialized +with the same passphrase and/or User Key. +A unique salt will be randomly generated for each provider to ensure the +Master Key for each is unique. Here you can set up the cryptographic algorithm to use, Data Key length, etc. -The last sector of the provider is used to store metadata. +The last sector of the providers is used to store metadata. The .Cm init subcommand also automatically writes metadata backups to @@ -279,6 +283,16 @@ To inhibit backups, you can use .Pa none as the .Ar backupfile . +If multiple providers were initialized in the one command, you can use +.Pa PROV +(all upper-case) in the file name, and it will be replaced with the provider +name. +If +.Pa PROV +is not found in the file name and multiple providers were initialized in the +one command, +.Pa - +will be appended to the end of the file name specified. .It Fl d When entering the passphrase to boot from this encrypted root filesystem, echo .Ql * Modified: head/lib/geom/eli/geom_eli.c ============================================================================== --- head/lib/geom/eli/geom_eli.c Mon Jul 23 22:15:36 2018 (r336658) +++ head/lib/geom/eli/geom_eli.c Mon Jul 23 23:04:43 2018 (r336659) @@ -91,7 +91,7 @@ static int eli_backup_create(struct gctl_req *req, con /* * Available commands: * - * init [-bdgPTv] [-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 [-Cdprv] [-n keyno] [-j passfile] [-k keyfile] prov ... * detach [-fl] prov ... @@ -129,7 +129,7 @@ struct g_command class_commands[] = { { 'V', "mdversion", "-1", G_TYPE_NUMBER }, G_OPT_SENTINEL }, - "[-bdgPTv] [-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, { @@ -695,6 +695,7 @@ static void eli_init(struct gctl_req *req) { struct g_eli_metadata md; + struct gctl_req *r; unsigned char sector[sizeof(struct g_eli_metadata)] __aligned(4); unsigned char key[G_ELI_USERKEYLEN]; char backfile[MAXPATHLEN]; @@ -702,22 +703,16 @@ eli_init(struct gctl_req *req) unsigned int secsize, version; off_t mediasize; intmax_t val; - int error, nargs; + int error, i, nargs, nparams, param; + const int one = 1; nargs = gctl_get_int(req, "nargs"); - if (nargs != 1) { - gctl_error(req, "Invalid number of arguments."); + if (nargs <= 0) { + gctl_error(req, "Too few arguments."); return; } - prov = gctl_get_ascii(req, "arg0"); - mediasize = g_get_mediasize(prov); - secsize = g_get_sectorsize(prov); - if (mediasize == 0 || secsize == 0) { - gctl_error(req, "Cannot get informations about %s: %s.", prov, - strerror(errno)); - return; - } + /* Start generating metadata for provider(s) being initialized. */ explicit_bzero(&md, sizeof(md)); strlcpy(md.md_magic, G_ELI_MAGIC, sizeof(md.md_magic)); val = gctl_get_intmax(req, "mdversion"); @@ -809,7 +804,6 @@ eli_init(struct gctl_req *req) gctl_error(req, "Invalid key length."); return; } - md.md_provsize = mediasize; val = gctl_get_intmax(req, "iterations"); if (val != -1) { @@ -829,78 +823,191 @@ eli_init(struct gctl_req *req) md.md_iterations = val; val = gctl_get_intmax(req, "sectorsize"); - if (val == 0) - md.md_sectorsize = secsize; - else { - if (val < 0 || (val % secsize) != 0 || !powerof2(val)) { - gctl_error(req, "Invalid sector size."); - return; - } - if (val > sysconf(_SC_PAGE_SIZE)) { - fprintf(stderr, - "warning: Using sectorsize bigger than the page size!\n"); - } - md.md_sectorsize = val; + if (val > sysconf(_SC_PAGE_SIZE)) { + fprintf(stderr, + "warning: Using sectorsize bigger than the page size!\n"); } md.md_keys = 0x01; - arc4random_buf(md.md_salt, sizeof(md.md_salt)); - arc4random_buf(md.md_mkeys, sizeof(md.md_mkeys)); - /* Generate user key. */ - if (eli_genkey(req, &md, key, true) == NULL) { + /* + * Determine number of parameters in the parent geom request before the + * nargs parameter and list of providers. + */ + nparams = req->narg - nargs - 1; + + /* Create new child request for each provider and issue to kernel */ + for (i = 0; i < nargs; i++) { + r = gctl_get_handle(); + + /* Copy each parameter from the parent request to the child */ + for (param = 0; param < nparams; param++) { + gctl_ro_param(r, req->arg[param].name, + req->arg[param].len, req->arg[param].value); + } + + /* Add a single provider to the parameter list of the child */ + gctl_ro_param(r, "nargs", sizeof(one), &one); + prov = gctl_get_ascii(req, "arg%d", i); + gctl_ro_param(r, "arg0", -1, prov); + + mediasize = g_get_mediasize(prov); + secsize = g_get_sectorsize(prov); + if (mediasize == 0 || secsize == 0) { + gctl_error(r, "Cannot get information about %s: %s.", + prov, strerror(errno)); + goto out; + } + + md.md_provsize = mediasize; + + val = gctl_get_intmax(r, "sectorsize"); + if (val == 0) { + md.md_sectorsize = secsize; + } else { + if (val < 0 || (val % secsize) != 0 || !powerof2(val)) { + gctl_error(r, "Invalid sector size."); + goto out; + } + md.md_sectorsize = val; + } + + /* Use different salt and Master Key for each provider. */ + arc4random_buf(md.md_salt, sizeof(md.md_salt)); + arc4random_buf(md.md_mkeys, sizeof(md.md_mkeys)); + + /* Generate user key. */ + if (eli_genkey(r, &md, key, true) == NULL) { + /* + * Error generating key - details added to geom request + * by eli_genkey(). + */ + goto out; + } + + /* Encrypt the first and the only Master Key. */ + error = g_eli_mkey_encrypt(md.md_ealgo, key, md.md_keylen, + md.md_mkeys); explicit_bzero(key, sizeof(key)); - explicit_bzero(&md, sizeof(md)); - return; - } + if (error != 0) { + gctl_error(r, "Cannot encrypt Master Key: %s.", + strerror(error)); + goto out; + } - /* Encrypt the first and the only Master Key. */ - error = g_eli_mkey_encrypt(md.md_ealgo, key, md.md_keylen, md.md_mkeys); - explicit_bzero(key, sizeof(key)); - if (error != 0) { - explicit_bzero(&md, sizeof(md)); - gctl_error(req, "Cannot encrypt Master Key: %s.", - strerror(error)); - return; - } + /* + * Convert metadata to on-disk format and then immediately erase + * sensitive data from the metadata struct. + */ + eli_metadata_encode(&md, sector); + explicit_bzero(&md.md_provsize, sizeof(md.md_provsize)); + explicit_bzero(&md.md_sectorsize, sizeof(md.md_sectorsize)); + explicit_bzero(&md.md_salt, sizeof(md.md_salt)); + explicit_bzero(&md.md_mkeys, sizeof(md.md_mkeys)); - eli_metadata_encode(&md, sector); - explicit_bzero(&md, sizeof(md)); - error = g_metadata_store(prov, sector, sizeof(sector)); - explicit_bzero(sector, sizeof(sector)); - if (error != 0) { - gctl_error(req, "Cannot store metadata on %s: %s.", prov, - strerror(error)); - return; - } - if (verbose) - printf("Metadata value stored on %s.\n", prov); - /* Backup metadata to a file. */ - str = gctl_get_ascii(req, "backupfile"); - if (str[0] != '\0') { - /* Backupfile given be the user, just copy it. */ - strlcpy(backfile, str, sizeof(backfile)); - } else { - /* Generate file name automatically. */ + /* + * Store metadata to disk and then immediately erase sensitive + * data from memory. + */ + error = g_metadata_store(prov, sector, sizeof(sector)); + explicit_bzero(sector, sizeof(sector)); + if (error != 0) { + gctl_error(r, "Cannot store metadata on %s: %s.", prov, + strerror(error)); + goto out; + } + if (verbose) + printf("Metadata value stored on %s.\n", prov); + + /* Backup metadata to a file. */ const char *p = prov; - unsigned int i; + unsigned int j; - if (strncmp(p, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) + /* + * Check if provider string includes the devfs mountpoint + * (typically /dev/). + */ + if (strncmp(p, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) { + /* Skip forward to the device filename only. */ p += sizeof(_PATH_DEV) - 1; - snprintf(backfile, sizeof(backfile), "%s%s.eli", - GELI_BACKUP_DIR, p); - /* Replace all / with _. */ - for (i = strlen(GELI_BACKUP_DIR); backfile[i] != '\0'; i++) { - if (backfile[i] == '/') - backfile[i] = '_'; } + + str = gctl_get_ascii(r, "backupfile"); + if (str[0] != '\0') { + /* Backupfile given by the user, just copy it. */ + strlcpy(backfile, str, sizeof(backfile)); + + /* Make the backup filename unique if multiple providers + * initialized in one command. */ + if (nargs > 1) { + /* + * Replace first occurrence of "PROV" with + * provider name. + */ + str = strnstr(backfile, "PROV", + sizeof(backfile)); + if (str != NULL) { + char suffix[MAXPATHLEN]; + j = str - backfile; + strlcpy(suffix, &backfile[j+4], + sizeof(suffix)); + backfile[j] = '\0'; + strlcat(backfile, p, sizeof(backfile)); + strlcat(backfile, suffix, + sizeof(backfile)); + } else { + /* + * "PROV" not found in backfile, append + * provider name. + */ + strlcat(backfile, "-", + sizeof(backfile)); + strlcat(backfile, p, sizeof(backfile)); + } + } + } else { + /* Generate filename automatically. */ + snprintf(backfile, sizeof(backfile), "%s%s.eli", + GELI_BACKUP_DIR, p); + /* Replace all / with _. */ + for (j = strlen(GELI_BACKUP_DIR); backfile[j] != '\0'; + j++) { + if (backfile[j] == '/') + backfile[j] = '_'; + } + } + if (strcmp(backfile, "none") != 0 && + eli_backup_create(r, prov, backfile) == 0) { + printf("\nMetadata backup for provider %s can be found " + "in %s\n", prov, backfile); + printf("and can be restored with the following " + "command:\n"); + printf("\n\t# geli restore %s %s\n\n", backfile, prov); + } + +out: + /* + * Print error for this request, and set parent request error + * message. + */ + if (r->error != NULL && r->error[0] != '\0') { + warnx("%s", r->error); + gctl_error(req, "There was an error with at least one " + "provider."); + } + + gctl_free(r); + + /* + * Erase sensitive data from memory, and ensure subsequent + * providers are initialized with unique metadata. + */ + explicit_bzero(key, sizeof(key)); + explicit_bzero(&md, sizeof(md)); } - if (strcmp(backfile, "none") != 0 && - eli_backup_create(req, prov, backfile) == 0) { - printf("\nMetadata backup can be found in %s and\n", backfile); - printf("can be restored with the following command:\n"); - printf("\n\t# geli restore %s %s\n\n", backfile, prov); - } + + /* Clear the cached metadata, including keys. */ + explicit_bzero(&md, sizeof(md)); } static void @@ -914,7 +1021,7 @@ eli_attach(struct gctl_req *req) const int one = 1; nargs = gctl_get_int(req, "nargs"); - if (nargs == 0) { + if (nargs <= 0) { gctl_error(req, "Too few arguments."); return; } @@ -927,13 +1034,14 @@ eli_attach(struct gctl_req *req) */ nparams = req->narg - nargs - 1; - /* Create new child geom request for each provider and issue to kernel */ + /* Create new child request for each provider and issue to kernel */ for (i = 0; i < nargs; i++) { r = gctl_get_handle(); - /* Copy each parameter from the parent request to the child request */ + /* Copy each parameter from the parent request to the child */ for (param = 0; param < nparams; param++) { - gctl_ro_param(r, req->arg[param].name, req->arg[param].len, req->arg[param].value); + gctl_ro_param(r, req->arg[param].name, + req->arg[param].len, req->arg[param].value); } /* Add a single provider to the parameter list of the child */ @@ -971,10 +1079,14 @@ eli_attach(struct gctl_req *req) } out: - /* Print error for this request, and set parent request error message */ + /* + * Print error for this request, and set parent request error + * message. + */ if (r->error != NULL && r->error[0] != '\0') { warnx("%s", r->error); - gctl_error(req, "There was an error with at least one provider."); + gctl_error(req, "There was an error with at least one " + "provider."); } gctl_free(r);