1184054Slulf/*- 2186743Slulf * SPDX-License-Identifier: BSD-2-Clause 3184054Slulf * 4184054Slulf * Copyright (c) 2004-2019 Pawel Jakub Dawidek <pawel@dawidek.net> 5184054Slulf * All rights reserved. 6184054Slulf * 7184054Slulf * Redistribution and use in source and binary forms, with or without 8184054Slulf * modification, are permitted provided that the following conditions 9184054Slulf * are met: 10184054Slulf * 1. Redistributions of source code must retain the above copyright 11184054Slulf * notice, this list of conditions and the following disclaimer. 12184054Slulf * 2. Redistributions in binary form must reproduce the above copyright 13184054Slulf * notice, this list of conditions and the following disclaimer in the 14184054Slulf * documentation and/or other materials provided with the distribution. 15184054Slulf * 16184054Slulf * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 17184054Slulf * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18184054Slulf * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19184054Slulf * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 20184054Slulf * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21184054Slulf * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22184054Slulf * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23184054Slulf * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24184054Slulf * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25184054Slulf * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26184054Slulf * SUCH DAMAGE. 27184054Slulf */ 28184054Slulf 29184054Slulf#include <sys/param.h> 30185134Slulf#include <sys/mman.h> 31185134Slulf#include <sys/sysctl.h> 32184054Slulf#include <sys/resource.h> 33184054Slulf#include <opencrypto/cryptodev.h> 34184054Slulf 35185134Slulf#include <assert.h> 36184054Slulf#include <err.h> 37185134Slulf#include <errno.h> 38184054Slulf#include <fcntl.h> 39185134Slulf#include <libgeom.h> 40185134Slulf#include <paths.h> 41184054Slulf#include <readpassphrase.h> 42184054Slulf#include <stdbool.h> 43184054Slulf#include <stdint.h> 44184054Slulf#include <stdio.h> 45185134Slulf#include <stdlib.h> 46185134Slulf#include <string.h> 47184054Slulf#include <strings.h> 48184054Slulf#include <unistd.h> 49184054Slulf 50184054Slulf#include <geom/eli/g_eli.h> 51184054Slulf#include <geom/eli/pkcs5v2.h> 52184054Slulf 53184054Slulf#include "core/geom.h" 54184054Slulf#include "misc/subr.h" 55184054Slulf 56184054Slulf 57184054Slulfuint32_t lib_version = G_LIB_VERSION; 58184054Slulfuint32_t version = G_ELI_VERSION; 59184054Slulf 60184054Slulf#define GELI_BACKUP_DIR "/var/backups/" 61184054Slulf#define GELI_ENC_ALGO "aes" 62184054Slulf#define BUFSIZE 1024 63184054Slulf 64184054Slulf/* 65184054Slulf * Passphrase cached when attaching multiple providers, in order to be more 66184054Slulf * user-friendly if they are using the same passphrase. 67184054Slulf */ 68184054Slulfstatic char cached_passphrase[BUFSIZE] = ""; 69184054Slulf 70184054Slulfstatic void eli_main(struct gctl_req *req, unsigned flags); 71184054Slulfstatic void eli_init(struct gctl_req *req); 72184054Slulfstatic void eli_attach(struct gctl_req *req); 73184054Slulfstatic void eli_configure(struct gctl_req *req); 74184054Slulfstatic void eli_setkey(struct gctl_req *req); 75184054Slulfstatic void eli_delkey(struct gctl_req *req); 76184054Slulfstatic void eli_resume(struct gctl_req *req); 77184054Slulfstatic void eli_kill(struct gctl_req *req); 78184054Slulfstatic void eli_backup(struct gctl_req *req); 79184054Slulfstatic void eli_restore(struct gctl_req *req); 80184054Slulfstatic void eli_resize(struct gctl_req *req); 81184054Slulfstatic void eli_version(struct gctl_req *req); 82184054Slulfstatic void eli_clear(struct gctl_req *req); 83184054Slulfstatic void eli_dump(struct gctl_req *req); 84184054Slulf 85184054Slulfstatic int eli_backup_create(struct gctl_req *req, const char *prov, 86184054Slulf const char *file); 87184054Slulf 88184054Slulf/* 89184054Slulf * Available commands: 90184054Slulf * 91184054Slulf * init [-bdgPRTv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] [-s sectorsize] [-V version] prov ... 92186727Slulf * label - alias for 'init' 93184054Slulf * attach [-Cdprv] [-n keyno] [-j passfile] [-k keyfile] prov ... 94184054Slulf * detach [-fl] prov ... 95184054Slulf * stop - alias for 'detach' 96184054Slulf * onetime [-dRT] [-a aalgo] [-e ealgo] [-l keylen] prov 97184054Slulf * configure [-bBgGrRtT] prov ... 98184054Slulf * setkey [-pPv] [-n keyno] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov 99184054Slulf * delkey [-afv] [-n keyno] prov 100184054Slulf * suspend [-v] -a | prov ... 101184054Slulf * resume [-pv] [-j passfile] [-k keyfile] prov 102184054Slulf * kill [-av] [prov ...] 103186727Slulf * backup [-v] prov file 104184054Slulf * restore [-fv] file prov 105184054Slulf * resize [-v] -s oldsize prov 106184054Slulf * version [prov ...] 107184054Slulf * clear [-v] prov ... 108184054Slulf * dump [-v] prov ... 109184054Slulf */ 110184054Slulfstruct g_command class_commands[] = { 111184054Slulf { "init", G_FLAG_VERBOSE, eli_main, 112184054Slulf { 113184054Slulf { 'a', "aalgo", "", G_TYPE_STRING }, 114184054Slulf { 'b', "boot", NULL, G_TYPE_BOOL }, 115184054Slulf { 'B', "backupfile", "", G_TYPE_STRING }, 116184054Slulf { 'd', "displaypass", NULL, G_TYPE_BOOL }, 117184054Slulf { 'e', "ealgo", "", G_TYPE_STRING }, 118184054Slulf { 'g', "geliboot", NULL, G_TYPE_BOOL }, 119184054Slulf { 'i', "iterations", "-1", G_TYPE_NUMBER }, 120184054Slulf { 'J', "newpassfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 121184054Slulf { 'K', "newkeyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 122186700Slulf { 'l', "keylen", "0", G_TYPE_NUMBER }, 123184054Slulf { 'P', "nonewpassphrase", NULL, G_TYPE_BOOL }, 124184054Slulf { 'R', "noautoresize", NULL, G_TYPE_BOOL }, 125184054Slulf { 's', "sectorsize", "0", G_TYPE_NUMBER }, 126184054Slulf { 'T', "notrim", NULL, G_TYPE_BOOL }, 127184054Slulf { 'V', "mdversion", "-1", G_TYPE_NUMBER }, 128184054Slulf G_OPT_SENTINEL 129184054Slulf }, 130184054Slulf "[-bdgPRTv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] [-s sectorsize] [-V version] prov ..." 131184054Slulf }, 132184054Slulf { "label", G_FLAG_VERBOSE, eli_main, 133184054Slulf { 134184054Slulf { 'a', "aalgo", "", G_TYPE_STRING }, 135184054Slulf { 'b', "boot", NULL, G_TYPE_BOOL }, 136184054Slulf { 'B', "backupfile", "", G_TYPE_STRING }, 137184054Slulf { 'd', "displaypass", NULL, G_TYPE_BOOL }, 138184054Slulf { 'e', "ealgo", "", G_TYPE_STRING }, 139184054Slulf { 'g', "geliboot", NULL, G_TYPE_BOOL }, 140184054Slulf { 'i', "iterations", "-1", G_TYPE_NUMBER }, 141184054Slulf { 'J', "newpassfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 142184054Slulf { 'K', "newkeyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 143190422Slulf { 'l', "keylen", "0", G_TYPE_NUMBER }, 144186727Slulf { 'P', "nonewpassphrase", NULL, G_TYPE_BOOL }, 145184054Slulf { 'R', "noautoresize", NULL, G_TYPE_BOOL }, 146184054Slulf { 's', "sectorsize", "0", G_TYPE_NUMBER }, 147184054Slulf { 'T', "notrim", NULL, G_TYPE_BOOL }, 148184054Slulf { 'V', "mdversion", "-1", G_TYPE_NUMBER }, 149184054Slulf G_OPT_SENTINEL 150184054Slulf }, 151184054Slulf "- an alias for 'init'" 152184054Slulf }, 153184054Slulf { "attach", G_FLAG_VERBOSE | G_FLAG_LOADKLD, eli_main, 154184054Slulf { 155184054Slulf { 'C', "dryrun", NULL, G_TYPE_BOOL }, 156186700Slulf { 'd', "detach", NULL, G_TYPE_BOOL }, 157184054Slulf { 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 158184054Slulf { 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 159184054Slulf { 'n', "keyno", "-1", G_TYPE_NUMBER }, 160184054Slulf { 'p', "nopassphrase", NULL, G_TYPE_BOOL }, 161184054Slulf { 'r', "readonly", NULL, G_TYPE_BOOL }, 162184054Slulf G_OPT_SENTINEL 163184054Slulf }, 164184054Slulf "[-Cdprv] [-n keyno] [-j passfile] [-k keyfile] prov ..." 165184054Slulf }, 166184054Slulf { "detach", 0, NULL, 167184054Slulf { 168185134Slulf { 'f', "force", NULL, G_TYPE_BOOL }, 169184054Slulf { 'l', "last", NULL, G_TYPE_BOOL }, 170184054Slulf G_OPT_SENTINEL 171185134Slulf }, 172184054Slulf "[-fl] prov ..." 173184054Slulf }, 174184054Slulf { "stop", 0, NULL, 175184054Slulf { 176184054Slulf { 'f', "force", NULL, G_TYPE_BOOL }, 177184054Slulf { 'l', "last", NULL, G_TYPE_BOOL }, 178186700Slulf G_OPT_SENTINEL 179184054Slulf }, 180185134Slulf "- an alias for 'detach'" 181184054Slulf }, 182184054Slulf { "onetime", G_FLAG_VERBOSE | G_FLAG_LOADKLD, NULL, 183184054Slulf { 184184054Slulf { 'a', "aalgo", "", G_TYPE_STRING }, 185184054Slulf { 'd', "detach", NULL, G_TYPE_BOOL }, 186184054Slulf { 'e', "ealgo", GELI_ENC_ALGO, G_TYPE_STRING }, 187184054Slulf { 'l', "keylen", "0", G_TYPE_NUMBER }, 188184054Slulf { 'R', "noautoresize", NULL, G_TYPE_BOOL }, 189184054Slulf { 's', "sectorsize", "0", G_TYPE_NUMBER }, 190184054Slulf { 'T', "notrim", NULL, G_TYPE_BOOL }, 191184054Slulf G_OPT_SENTINEL 192184054Slulf }, 193184054Slulf "[-dRT] [-a aalgo] [-e ealgo] [-l keylen] [-s sectorsize] prov" 194185134Slulf }, 195184054Slulf { "configure", G_FLAG_VERBOSE, eli_main, 196184054Slulf { 197184054Slulf { 'b', "boot", NULL, G_TYPE_BOOL }, 198184054Slulf { 'B', "noboot", NULL, G_TYPE_BOOL }, 199184054Slulf { 'd', "displaypass", NULL, G_TYPE_BOOL }, 200184054Slulf { 'D', "nodisplaypass", NULL, G_TYPE_BOOL }, 201184054Slulf { 'g', "geliboot", NULL, G_TYPE_BOOL }, 202184054Slulf { 'G', "nogeliboot", NULL, G_TYPE_BOOL }, 203184054Slulf { 'r', "autoresize", NULL, G_TYPE_BOOL }, 204184054Slulf { 'R', "noautoresize", NULL, G_TYPE_BOOL }, 205184054Slulf { 't', "trim", NULL, G_TYPE_BOOL }, 206184054Slulf { 'T', "notrim", NULL, G_TYPE_BOOL }, 207184054Slulf G_OPT_SENTINEL 208185592Slulf }, 209184054Slulf "[-bBdDgGrRtT] prov ..." 210186700Slulf }, 211184054Slulf { "setkey", G_FLAG_VERBOSE, eli_main, 212184054Slulf { 213184054Slulf { 'i', "iterations", "-1", G_TYPE_NUMBER }, 214184054Slulf { 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 215184054Slulf { 'J', "newpassfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 216184054Slulf { 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 217184054Slulf { 'K', "newkeyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 218186700Slulf { 'n', "keyno", "-1", G_TYPE_NUMBER }, 219184054Slulf { 'p', "nopassphrase", NULL, G_TYPE_BOOL }, 220184054Slulf { 'P', "nonewpassphrase", NULL, G_TYPE_BOOL }, 221184054Slulf G_OPT_SENTINEL 222184054Slulf }, 223184054Slulf "[-pPv] [-n keyno] [-i iterations] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov" 224184054Slulf }, 225184054Slulf { "delkey", G_FLAG_VERBOSE, eli_main, 226184054Slulf { 227184054Slulf { 'a', "all", NULL, G_TYPE_BOOL }, 228184054Slulf { 'f', "force", NULL, G_TYPE_BOOL }, 229184054Slulf { 'n', "keyno", "-1", G_TYPE_NUMBER }, 230184054Slulf G_OPT_SENTINEL 231184054Slulf }, 232184054Slulf "[-afv] [-n keyno] prov" 233184054Slulf }, 234184054Slulf { "suspend", G_FLAG_VERBOSE, NULL, 235184054Slulf { 236184054Slulf { 'a', "all", NULL, G_TYPE_BOOL }, 237185811Slulf G_OPT_SENTINEL 238184054Slulf }, 239184054Slulf "[-v] -a | prov ..." 240184054Slulf }, 241184054Slulf { "resume", G_FLAG_VERBOSE, eli_main, 242184054Slulf { 243184054Slulf { 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 244184054Slulf { 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 245184054Slulf { 'p', "nopassphrase", NULL, G_TYPE_BOOL }, 246184054Slulf G_OPT_SENTINEL 247184054Slulf }, 248184054Slulf "[-pv] [-j passfile] [-k keyfile] prov" 249184054Slulf }, 250184054Slulf { "kill", G_FLAG_VERBOSE, eli_main, 251184054Slulf { 252184054Slulf { 'a', "all", NULL, G_TYPE_BOOL }, 253184054Slulf G_OPT_SENTINEL 254184054Slulf }, 255184054Slulf "[-av] [prov ...]" 256184054Slulf }, 257184054Slulf { "backup", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS, 258184054Slulf "[-v] prov file" 259184054Slulf }, 260184054Slulf { "restore", G_FLAG_VERBOSE, eli_main, 261184054Slulf { 262184054Slulf { 'f', "force", NULL, G_TYPE_BOOL }, 263184054Slulf G_OPT_SENTINEL 264184054Slulf }, 265184054Slulf "[-fv] file prov" 266184054Slulf }, 267184054Slulf { "resize", G_FLAG_VERBOSE, eli_main, 268184054Slulf { 269185592Slulf { 's', "oldsize", NULL, G_TYPE_NUMBER }, 270185811Slulf G_OPT_SENTINEL 271185811Slulf }, 272185811Slulf "[-v] -s oldsize prov" 273185811Slulf }, 274185811Slulf { "version", G_FLAG_LOADKLD, eli_main, G_NULL_OPTS, 275185811Slulf "[prov ...]" 276185811Slulf }, 277184054Slulf { "clear", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS, 278184054Slulf "[-v] prov ..." 279184054Slulf }, 280184054Slulf { "dump", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS, 281184054Slulf "[-v] prov ..." 282184054Slulf }, 283184054Slulf G_CMD_SENTINEL 284184054Slulf}; 285184054Slulf 286184054Slulfstatic int verbose = 0; 287184054Slulf 288184054Slulfstatic int 289184054Slulfeli_protect(struct gctl_req *req) 290184054Slulf{ 291184054Slulf struct rlimit rl; 292184054Slulf 293184054Slulf /* Disable core dumps. */ 294184054Slulf rl.rlim_cur = 0; 295184054Slulf rl.rlim_max = 0; 296184054Slulf if (setrlimit(RLIMIT_CORE, &rl) == -1) { 297184054Slulf gctl_error(req, "Cannot disable core dumps: %s.", 298184054Slulf strerror(errno)); 299184054Slulf return (-1); 300184054Slulf } 301184054Slulf /* Disable swapping. */ 302184054Slulf if (mlockall(MCL_FUTURE) == -1) { 303184054Slulf gctl_error(req, "Cannot lock memory: %s.", strerror(errno)); 304184054Slulf return (-1); 305184054Slulf } 306184054Slulf return (0); 307184054Slulf} 308184054Slulf 309184054Slulfstatic void 310184054Slulfeli_main(struct gctl_req *req, unsigned int flags) 311190422Slulf{ 312190422Slulf const char *name; 313184054Slulf 314184054Slulf if (eli_protect(req) == -1) 315190422Slulf return; 316190422Slulf 317190422Slulf if ((flags & G_FLAG_VERBOSE) != 0) 318190422Slulf verbose = 1; 319190422Slulf 320184054Slulf name = gctl_get_ascii(req, "verb"); 321184054Slulf if (name == NULL) { 322190422Slulf gctl_error(req, "No '%s' argument.", "verb"); 323190422Slulf return; 324184054Slulf } 325184054Slulf if (strcmp(name, "init") == 0 || strcmp(name, "label") == 0) 326184054Slulf eli_init(req); 327184054Slulf else if (strcmp(name, "attach") == 0) 328184054Slulf eli_attach(req); 329184054Slulf else if (strcmp(name, "configure") == 0) 330184054Slulf eli_configure(req); 331184054Slulf else if (strcmp(name, "setkey") == 0) 332190422Slulf eli_setkey(req); 333190422Slulf else if (strcmp(name, "delkey") == 0) 334184054Slulf eli_delkey(req); 335184054Slulf else if (strcmp(name, "resume") == 0) 336190422Slulf eli_resume(req); 337190422Slulf else if (strcmp(name, "kill") == 0) 338184054Slulf eli_kill(req); 339184054Slulf else if (strcmp(name, "backup") == 0) 340190422Slulf eli_backup(req); 341190422Slulf else if (strcmp(name, "restore") == 0) 342190422Slulf eli_restore(req); 343184054Slulf else if (strcmp(name, "resize") == 0) 344184054Slulf eli_resize(req); 345184054Slulf else if (strcmp(name, "version") == 0) 346184054Slulf eli_version(req); 347190422Slulf else if (strcmp(name, "dump") == 0) 348190422Slulf eli_dump(req); 349190422Slulf else if (strcmp(name, "clear") == 0) 350190422Slulf eli_clear(req); 351190422Slulf else 352190422Slulf gctl_error(req, "Unknown command: %s.", name); 353190422Slulf} 354190422Slulf 355184054Slulfstatic bool 356184054Slulfeli_is_attached(const char *prov) 357190422Slulf{ 358190422Slulf char name[MAXPATHLEN]; 359190422Slulf 360190422Slulf /* 361190422Slulf * Not the best way to do it, but the easiest. 362190422Slulf * We try to open provider and check if it is a GEOM provider 363190422Slulf * by asking about its sectorsize. 364190422Slulf */ 365190422Slulf snprintf(name, sizeof(name), "%s%s", prov, G_ELI_SUFFIX); 366190422Slulf return (g_get_sectorsize(name) > 0); 367184054Slulf} 368190422Slulf 369190422Slulfstatic int 370184054Slulfeli_genkey_files(struct gctl_req *req, bool new, const char *type, 371185134Slulf struct hmac_ctx *ctxp, char *passbuf, size_t passbufsize) 372184054Slulf{ 373184054Slulf char *p, buf[BUFSIZE], argname[16]; 374184054Slulf const char *file; 375184054Slulf int error, fd, i; 376184054Slulf ssize_t done; 377184054Slulf 378184054Slulf assert((strcmp(type, "keyfile") == 0 && ctxp != NULL && 379184054Slulf passbuf == NULL && passbufsize == 0) || 380184054Slulf (strcmp(type, "passfile") == 0 && ctxp == NULL && 381184054Slulf passbuf != NULL && passbufsize > 0)); 382184054Slulf assert(strcmp(type, "keyfile") == 0 || passbuf[0] == '\0'); 383184054Slulf 384190422Slulf for (i = 0; ; i++) { 385190422Slulf snprintf(argname, sizeof(argname), "%s%s%d", 386190422Slulf new ? "new" : "", type, i); 387184054Slulf 388190422Slulf /* No more {key,pass}files? */ 389190422Slulf if (!gctl_has_param(req, argname)) 390190422Slulf return (i); 391190422Slulf 392190422Slulf file = gctl_get_ascii(req, "%s", argname); 393190422Slulf assert(file != NULL); 394190422Slulf 395190422Slulf if (strcmp(file, "-") == 0) 396184054Slulf fd = STDIN_FILENO; 397184054Slulf else { 398184054Slulf fd = open(file, O_RDONLY); 399184054Slulf if (fd == -1) { 400184054Slulf gctl_error(req, "Cannot open %s %s: %s.", 401186727Slulf type, file, strerror(errno)); 402184054Slulf return (-1); 403184054Slulf } 404184054Slulf } 405184054Slulf if (strcmp(type, "keyfile") == 0) { 406184054Slulf while ((done = read(fd, buf, sizeof(buf))) > 0) 407184054Slulf g_eli_crypto_hmac_update(ctxp, buf, done); 408184054Slulf } else /* if (strcmp(type, "passfile") == 0) */ { 409190422Slulf assert(strcmp(type, "passfile") == 0); 410190422Slulf 411190422Slulf while ((done = read(fd, buf, sizeof(buf) - 1)) > 0) { 412190422Slulf buf[done] = '\0'; 413190422Slulf p = strchr(buf, '\n'); 414190422Slulf if (p != NULL) { 415190422Slulf *p = '\0'; 416184054Slulf done = p - buf; 417184054Slulf } 418190422Slulf if (strlcat(passbuf, buf, passbufsize) >= 419190422Slulf passbufsize) { 420184054Slulf gctl_error(req, 421184054Slulf "Passphrase in %s too long.", file); 422184054Slulf explicit_bzero(buf, sizeof(buf)); 423190422Slulf return (-1); 424190422Slulf } 425184054Slulf if (p != NULL) 426184054Slulf break; 427190422Slulf } 428190422Slulf } 429184054Slulf error = errno; 430184054Slulf if (strcmp(file, "-") != 0) 431190422Slulf close(fd); 432190422Slulf explicit_bzero(buf, sizeof(buf)); 433184054Slulf if (done == -1) { 434184054Slulf gctl_error(req, "Cannot read %s %s: %s.", 435184054Slulf type, file, strerror(error)); 436184054Slulf return (-1); 437190422Slulf } 438190422Slulf } 439184054Slulf /* NOTREACHED */ 440184054Slulf} 441184054Slulf 442184054Slulfstatic int 443184054Slulfeli_genkey_passphrase_prompt(struct gctl_req *req, bool new, char *passbuf, 444184054Slulf size_t passbufsize) 445184054Slulf{ 446184054Slulf char *p; 447184054Slulf 448184054Slulf for (;;) { 449184054Slulf p = readpassphrase( 450185134Slulf new ? "Enter new passphrase: " : "Enter passphrase: ", 451184054Slulf passbuf, passbufsize, RPP_ECHO_OFF | RPP_REQUIRE_TTY); 452184054Slulf if (p == NULL) { 453185134Slulf explicit_bzero(passbuf, passbufsize); 454184054Slulf gctl_error(req, "Cannot read passphrase: %s.", 455184054Slulf strerror(errno)); 456184054Slulf return (-1); 457184054Slulf } 458184054Slulf 459184054Slulf if (new) { 460184054Slulf char tmpbuf[BUFSIZE]; 461184054Slulf 462184054Slulf p = readpassphrase("Reenter new passphrase: ", 463184054Slulf tmpbuf, sizeof(tmpbuf), 464184054Slulf RPP_ECHO_OFF | RPP_REQUIRE_TTY); 465184054Slulf if (p == NULL) { 466184054Slulf explicit_bzero(passbuf, passbufsize); 467184054Slulf gctl_error(req, 468190422Slulf "Cannot read passphrase: %s.", 469190422Slulf strerror(errno)); 470190422Slulf return (-1); 471190422Slulf } 472184054Slulf 473184054Slulf if (strcmp(passbuf, tmpbuf) != 0) { 474184054Slulf explicit_bzero(passbuf, passbufsize); 475190422Slulf fprintf(stderr, "They didn't match.\n"); 476190422Slulf continue; 477184054Slulf } 478184054Slulf explicit_bzero(tmpbuf, sizeof(tmpbuf)); 479184054Slulf } 480190422Slulf return (0); 481190422Slulf } 482184054Slulf /* NOTREACHED */ 483184054Slulf} 484184054Slulf 485190422Slulfstatic int 486190422Slulfeli_genkey_passphrase(struct gctl_req *req, struct g_eli_metadata *md, bool new, 487184054Slulf struct hmac_ctx *ctxp) 488184054Slulf{ 489184054Slulf char passbuf[BUFSIZE]; 490184054Slulf bool nopassphrase; 491184054Slulf int nfiles; 492184054Slulf 493184054Slulf /* 494184054Slulf * Return error if the 'do not use passphrase' flag was given but a 495184054Slulf * passfile was provided. 496184054Slulf */ 497185134Slulf nopassphrase = 498185134Slulf gctl_get_int(req, new ? "nonewpassphrase" : "nopassphrase"); 499184054Slulf if (nopassphrase) { 500184054Slulf if (gctl_has_param(req, new ? "newpassfile0" : "passfile0")) { 501184054Slulf gctl_error(req, 502184054Slulf "Options -%c and -%c are mutually exclusive.", 503184054Slulf new ? 'J' : 'j', new ? 'P' : 'p'); 504184054Slulf return (-1); 505184054Slulf } 506184054Slulf return (0); 507184054Slulf } 508186727Slulf 509184054Slulf /* 510184054Slulf * Return error if using a provider which does not require a passphrase 511184054Slulf * but the 'do not use passphrase' flag was not given. 512184054Slulf */ 513184054Slulf if (!new && md->md_iterations == -1) { 514184054Slulf gctl_error(req, "Missing -p flag."); 515184054Slulf return (-1); 516184054Slulf } 517186724Slulf passbuf[0] = '\0'; 518185134Slulf 519185134Slulf /* Use cached passphrase if defined. */ 520184054Slulf if (strlen(cached_passphrase) > 0) { 521184054Slulf strlcpy(passbuf, cached_passphrase, sizeof(passbuf)); 522184054Slulf } else { 523184054Slulf nfiles = eli_genkey_files(req, new, "passfile", NULL, passbuf, 524184054Slulf sizeof(passbuf)); 525186724Slulf if (nfiles == -1) { 526184054Slulf return (-1); 527184054Slulf } else if (nfiles == 0) { 528184054Slulf if (eli_genkey_passphrase_prompt(req, new, passbuf, 529184054Slulf sizeof(passbuf)) == -1) { 530184054Slulf return (-1); 531185134Slulf } 532184054Slulf } 533184054Slulf /* Cache the passphrase for other providers. */ 534184054Slulf strlcpy(cached_passphrase, passbuf, sizeof(cached_passphrase)); 535184054Slulf } 536184054Slulf /* 537184054Slulf * Field md_iterations equal to -1 means "choose some sane 538184054Slulf * value for me". 539184054Slulf */ 540184054Slulf if (md->md_iterations == -1) { 541184054Slulf assert(new); 542184054Slulf if (verbose) 543184054Slulf printf("Calculating number of iterations...\n"); 544184054Slulf md->md_iterations = pkcs5v2_calculate(2000000); 545184054Slulf assert(md->md_iterations > 0); 546184054Slulf if (verbose) { 547184054Slulf printf("Done, using %d iterations.\n", 548184054Slulf md->md_iterations); 549184054Slulf } 550184054Slulf } 551184054Slulf /* 552184054Slulf * If md_iterations is equal to 0, user doesn't want PKCS#5v2. 553184054Slulf */ 554185134Slulf if (md->md_iterations == 0) { 555184054Slulf g_eli_crypto_hmac_update(ctxp, md->md_salt, 556184054Slulf sizeof(md->md_salt)); 557184054Slulf g_eli_crypto_hmac_update(ctxp, passbuf, strlen(passbuf)); 558184054Slulf } else /* if (md->md_iterations > 0) */ { 559184054Slulf unsigned char dkey[G_ELI_USERKEYLEN]; 560184054Slulf 561184054Slulf pkcs5v2_genkey(dkey, sizeof(dkey), md->md_salt, 562184054Slulf sizeof(md->md_salt), passbuf, md->md_iterations); 563184054Slulf g_eli_crypto_hmac_update(ctxp, dkey, sizeof(dkey)); 564184054Slulf explicit_bzero(dkey, sizeof(dkey)); 565184054Slulf } 566184054Slulf explicit_bzero(passbuf, sizeof(passbuf)); 567184054Slulf 568184054Slulf return (0); 569190422Slulf} 570190422Slulf 571190422Slulfstatic bool 572190422Slulfeli_init_key_hmac_ctx(struct gctl_req *req, struct hmac_ctx *ctx, bool new) 573184054Slulf{ 574184054Slulf int nfiles; 575184054Slulf bool nopassphrase; 576184054Slulf 577184054Slulf nopassphrase = 578184054Slulf gctl_get_int(req, new ? "nonewpassphrase" : "nopassphrase"); 579184054Slulf 580184054Slulf g_eli_crypto_hmac_init(ctx, NULL, 0); 581184054Slulf nfiles = eli_genkey_files(req, new, "keyfile", ctx, NULL, 0); 582184054Slulf if (nfiles == -1) { 583184054Slulf return (false); 584184054Slulf } else if (nfiles == 0 && nopassphrase) { 585184054Slulf gctl_error(req, "No key components given."); 586190422Slulf return (false); 587190422Slulf } 588190422Slulf 589190422Slulf return (true); 590184054Slulf} 591184054Slulf 592184054Slulfstatic unsigned char * 593185134Slulfeli_genkey(struct gctl_req *req, const struct hmac_ctx *ctxtemplate, 594184054Slulf struct g_eli_metadata *md, unsigned char *key, bool new) 595184054Slulf{ 596184054Slulf struct hmac_ctx ctx; 597184054Slulf 598184054Slulf memcpy(&ctx, ctxtemplate, sizeof(ctx)); 599184054Slulf 600184054Slulf if (eli_genkey_passphrase(req, md, new, &ctx) == -1) 601184054Slulf return (NULL); 602184054Slulf 603184054Slulf g_eli_crypto_hmac_final(&ctx, key, 0); 604184054Slulf 605184054Slulf return (key); 606184054Slulf} 607184054Slulf 608184054Slulfstatic unsigned char * 609184054Slulfeli_genkey_single(struct gctl_req *req, struct g_eli_metadata *md, 610184054Slulf unsigned char *key, bool new) 611185592Slulf{ 612184054Slulf struct hmac_ctx ctx; 613185592Slulf unsigned char *rkey; 614184054Slulf 615184054Slulf if (!eli_init_key_hmac_ctx(req, &ctx, new)) { 616184054Slulf return (NULL); 617185134Slulf } 618184054Slulf rkey = eli_genkey(req, &ctx, md, key, new); 619184054Slulf explicit_bzero(&ctx, sizeof(ctx)); 620184054Slulf 621184054Slulf return (rkey); 622184054Slulf} 623184054Slulf 624184054Slulfstatic int 625184054Slulfeli_metadata_read(struct gctl_req *req, const char *prov, 626184054Slulf struct g_eli_metadata *md) 627184054Slulf{ 628184054Slulf unsigned char sector[sizeof(struct g_eli_metadata)]; 629184054Slulf int error; 630184054Slulf 631184054Slulf if (g_get_sectorsize(prov) == 0) { 632184054Slulf int fd; 633184054Slulf 634184054Slulf /* This is a file probably. */ 635184054Slulf fd = open(prov, O_RDONLY); 636184054Slulf if (fd == -1) { 637184054Slulf gctl_error(req, "Cannot open %s: %s.", prov, 638184054Slulf strerror(errno)); 639184054Slulf return (-1); 640184054Slulf } 641184054Slulf if (read(fd, sector, sizeof(sector)) != sizeof(sector)) { 642184054Slulf gctl_error(req, "Cannot read metadata from %s: %s.", 643184054Slulf prov, strerror(errno)); 644184054Slulf close(fd); 645184054Slulf return (-1); 646184054Slulf } 647185134Slulf close(fd); 648185134Slulf } else { 649185134Slulf /* This is a GEOM provider. */ 650185134Slulf error = g_metadata_read(prov, sector, sizeof(sector), 651184054Slulf G_ELI_MAGIC); 652184054Slulf if (error != 0) { 653184054Slulf gctl_error(req, "Cannot read metadata from %s: %s.", 654184054Slulf prov, strerror(error)); 655184054Slulf return (-1); 656184054Slulf } 657184054Slulf } 658184054Slulf error = eli_metadata_decode(sector, md); 659184054Slulf switch (error) { 660184054Slulf case 0: 661184054Slulf break; 662184054Slulf case EOPNOTSUPP: 663184054Slulf gctl_error(req, 664184054Slulf "Provider's %s metadata version %u is too new.\n" 665184054Slulf "geli: The highest supported version is %u.", 666185134Slulf prov, (unsigned int)md->md_version, G_ELI_VERSION); 667184054Slulf return (-1); 668184054Slulf case EINVAL: 669184054Slulf gctl_error(req, "Inconsistent provider's %s metadata.", prov); 670184054Slulf return (-1); 671184054Slulf default: 672184054Slulf gctl_error(req, 673184054Slulf "Unexpected error while decoding provider's %s metadata: %s.", 674184054Slulf prov, strerror(error)); 675184054Slulf return (-1); 676185592Slulf } 677184054Slulf return (0); 678184054Slulf} 679184054Slulf 680184054Slulfstatic int 681184054Slulfeli_metadata_store(struct gctl_req *req, const char *prov, 682184054Slulf struct g_eli_metadata *md) 683184054Slulf{ 684184054Slulf unsigned char sector[sizeof(struct g_eli_metadata)]; 685184054Slulf int error; 686184054Slulf 687184054Slulf eli_metadata_encode(md, sector); 688184054Slulf if (g_get_sectorsize(prov) == 0) { 689184054Slulf int fd; 690184054Slulf 691184054Slulf /* This is a file probably. */ 692184054Slulf fd = open(prov, O_WRONLY | O_TRUNC); 693184054Slulf if (fd == -1) { 694184054Slulf gctl_error(req, "Cannot open %s: %s.", prov, 695184054Slulf strerror(errno)); 696184054Slulf explicit_bzero(sector, sizeof(sector)); 697184054Slulf return (-1); 698184054Slulf } 699184054Slulf if (write(fd, sector, sizeof(sector)) != sizeof(sector)) { 700184054Slulf gctl_error(req, "Cannot write metadata to %s: %s.", 701184054Slulf prov, strerror(errno)); 702184054Slulf explicit_bzero(sector, sizeof(sector)); 703184054Slulf close(fd); 704185134Slulf return (-1); 705184054Slulf } 706184054Slulf close(fd); 707185134Slulf } else { 708184054Slulf /* This is a GEOM provider. */ 709185134Slulf error = g_metadata_store(prov, sector, sizeof(sector)); 710184054Slulf if (error != 0) { 711185134Slulf gctl_error(req, "Cannot write metadata to %s: %s.", 712184054Slulf prov, strerror(errno)); 713185134Slulf explicit_bzero(sector, sizeof(sector)); 714185134Slulf return (-1); 715185134Slulf } 716185134Slulf } 717185134Slulf explicit_bzero(sector, sizeof(sector)); 718184054Slulf return (0); 719184054Slulf} 720184054Slulf 721185134Slulfstatic void 722184054Slulfeli_init(struct gctl_req *req) 723185134Slulf{ 724184054Slulf struct g_eli_metadata md; 725185134Slulf struct gctl_req *r; 726185134Slulf unsigned char sector[sizeof(struct g_eli_metadata)] __aligned(4); 727184054Slulf unsigned char key[G_ELI_USERKEYLEN]; 728184054Slulf char backfile[MAXPATHLEN]; 729184054Slulf const char *str, *prov; 730185134Slulf unsigned int secsize, eli_version; 731184054Slulf off_t mediasize; 732185134Slulf intmax_t val; 733186700Slulf int error, i, nargs, nparams, param; 734185134Slulf const int one = 1; 735184054Slulf struct hmac_ctx ctxtemplate; 736184054Slulf 737184054Slulf nargs = gctl_get_int(req, "nargs"); 738185134Slulf if (nargs <= 0) { 739184054Slulf gctl_error(req, "Too few arguments."); 740185134Slulf return; 741184054Slulf } 742185134Slulf 743184054Slulf /* Start generating metadata for provider(s) being initialized. */ 744185134Slulf explicit_bzero(&md, sizeof(md)); 745184054Slulf strlcpy(md.md_magic, G_ELI_MAGIC, sizeof(md.md_magic)); 746185134Slulf val = gctl_get_intmax(req, "mdversion"); 747184054Slulf if (val == -1) { 748185134Slulf eli_version = G_ELI_VERSION; 749184054Slulf } else if (val < 0 || val > G_ELI_VERSION) { 750184054Slulf gctl_error(req, 751184054Slulf "Invalid version specified should be between %u and %u.", 752184054Slulf G_ELI_VERSION_00, G_ELI_VERSION); 753184054Slulf return; 754184054Slulf } else { 755184054Slulf eli_version = val; 756185134Slulf } 757184054Slulf md.md_version = eli_version; 758184054Slulf md.md_flags = G_ELI_FLAG_AUTORESIZE; 759184054Slulf if (gctl_get_int(req, "boot")) 760185134Slulf md.md_flags |= G_ELI_FLAG_BOOT; 761184054Slulf if (gctl_get_int(req, "geliboot")) 762184054Slulf md.md_flags |= G_ELI_FLAG_GELIBOOT; 763184054Slulf if (gctl_get_int(req, "displaypass")) 764184054Slulf md.md_flags |= G_ELI_FLAG_GELIDISPLAYPASS; 765184054Slulf if (gctl_get_int(req, "notrim")) 766184054Slulf md.md_flags |= G_ELI_FLAG_NODELETE; 767184054Slulf if (gctl_get_int(req, "noautoresize")) 768184054Slulf md.md_flags &= ~G_ELI_FLAG_AUTORESIZE; 769184054Slulf md.md_ealgo = CRYPTO_ALGORITHM_MIN - 1; 770184054Slulf str = gctl_get_ascii(req, "aalgo"); 771184054Slulf if (*str != '\0') { 772184054Slulf if (eli_version < G_ELI_VERSION_01) { 773184054Slulf gctl_error(req, 774184054Slulf "Data authentication is supported starting from version %u.", 775184054Slulf G_ELI_VERSION_01); 776184054Slulf return; 777184054Slulf } 778184054Slulf md.md_aalgo = g_eli_str2aalgo(str); 779184054Slulf if (md.md_aalgo >= CRYPTO_ALGORITHM_MIN && 780184054Slulf md.md_aalgo <= CRYPTO_ALGORITHM_MAX) { 781184054Slulf md.md_flags |= G_ELI_FLAG_AUTH; 782184054Slulf } else { 783184054Slulf /* 784184054Slulf * For backward compatibility, check if the -a option 785184054Slulf * was used to provide encryption algorithm. 786184054Slulf */ 787184054Slulf md.md_ealgo = g_eli_str2ealgo(str); 788184054Slulf if (md.md_ealgo < CRYPTO_ALGORITHM_MIN || 789184054Slulf md.md_ealgo > CRYPTO_ALGORITHM_MAX) { 790184054Slulf gctl_error(req, 791184054Slulf "Invalid authentication algorithm."); 792184054Slulf return; 793184054Slulf } else { 794184054Slulf fprintf(stderr, "warning: The -e option, not " 795184054Slulf "the -a option is now used to specify " 796184054Slulf "encryption algorithm to use.\n"); 797184054Slulf } 798184054Slulf } 799184054Slulf } 800184054Slulf if (md.md_ealgo < CRYPTO_ALGORITHM_MIN || 801184054Slulf md.md_ealgo > CRYPTO_ALGORITHM_MAX) { 802184054Slulf str = gctl_get_ascii(req, "ealgo"); 803184054Slulf if (*str == '\0') { 804184054Slulf if (eli_version < G_ELI_VERSION_05) 805184054Slulf str = "aes-cbc"; 806184054Slulf else 807184054Slulf str = GELI_ENC_ALGO; 808186700Slulf } 809186700Slulf md.md_ealgo = g_eli_str2ealgo(str); 810184054Slulf if (md.md_ealgo < CRYPTO_ALGORITHM_MIN || 811184054Slulf md.md_ealgo > CRYPTO_ALGORITHM_MAX) { 812184054Slulf gctl_error(req, "Invalid encryption algorithm."); 813184054Slulf return; 814184054Slulf } 815184054Slulf if (md.md_ealgo == CRYPTO_CAMELLIA_CBC && 816184054Slulf eli_version < G_ELI_VERSION_04) { 817184054Slulf gctl_error(req, 818184054Slulf "Camellia-CBC algorithm is supported starting from version %u.", 819184054Slulf G_ELI_VERSION_04); 820184054Slulf return; 821184054Slulf } 822184054Slulf if (md.md_ealgo == CRYPTO_AES_XTS && 823184054Slulf eli_version < G_ELI_VERSION_05) { 824184054Slulf gctl_error(req, 825184054Slulf "AES-XTS algorithm is supported starting from version %u.", 826184054Slulf G_ELI_VERSION_05); 827184054Slulf return; 828184054Slulf } 829184054Slulf } 830184054Slulf val = gctl_get_intmax(req, "keylen"); 831184054Slulf md.md_keylen = val; 832184054Slulf md.md_keylen = g_eli_keylen(md.md_ealgo, md.md_keylen); 833184054Slulf if (md.md_keylen == 0) { 834184054Slulf gctl_error(req, "Invalid key length."); 835184054Slulf return; 836184054Slulf } 837184054Slulf 838184054Slulf val = gctl_get_intmax(req, "iterations"); 839184054Slulf if (val != -1) { 840184054Slulf int nonewpassphrase; 841184054Slulf 842184054Slulf /* 843184054Slulf * Don't allow to set iterations when there will be no 844184054Slulf * passphrase. 845184054Slulf */ 846184054Slulf nonewpassphrase = gctl_get_int(req, "nonewpassphrase"); 847186727Slulf if (nonewpassphrase) { 848186727Slulf gctl_error(req, 849186727Slulf "Options -i and -P are mutually exclusive."); 850184054Slulf return; 851184054Slulf } 852184054Slulf } 853184054Slulf md.md_iterations = val; 854184054Slulf 855184054Slulf val = gctl_get_intmax(req, "sectorsize"); 856184054Slulf if (val > sysconf(_SC_PAGE_SIZE)) { 857184054Slulf fprintf(stderr, 858184054Slulf "warning: Using sectorsize bigger than the page size!\n"); 859184054Slulf } 860184054Slulf 861184054Slulf md.md_keys = 0x01; 862184054Slulf 863184054Slulf /* 864184054Slulf * Determine number of parameters in the parent geom request before the 865184054Slulf * nargs parameter and list of providers. 866184054Slulf */ 867184054Slulf nparams = req->narg - nargs - 1; 868184054Slulf 869184054Slulf /* Generate HMAC context template. */ 870184054Slulf if (!eli_init_key_hmac_ctx(req, &ctxtemplate, true)) 871184054Slulf return; 872184054Slulf 873184054Slulf /* Create new child request for each provider and issue to kernel */ 874184054Slulf for (i = 0; i < nargs; i++) { 875184054Slulf r = gctl_get_handle(); 876184054Slulf 877184054Slulf /* Copy each parameter from the parent request to the child */ 878184054Slulf for (param = 0; param < nparams; param++) { 879184054Slulf gctl_ro_param(r, req->arg[param].name, 880184054Slulf req->arg[param].len, req->arg[param].value); 881184054Slulf } 882184054Slulf 883184054Slulf /* Add a single provider to the parameter list of the child */ 884184054Slulf gctl_ro_param(r, "nargs", sizeof(one), &one); 885184054Slulf prov = gctl_get_ascii(req, "arg%d", i); 886184054Slulf gctl_ro_param(r, "arg0", -1, prov); 887184054Slulf 888184054Slulf mediasize = g_get_mediasize(prov); 889184054Slulf secsize = g_get_sectorsize(prov); 890184054Slulf if (mediasize == 0 || secsize == 0) { 891184054Slulf gctl_error(r, "Cannot get information about %s: %s.", 892184054Slulf prov, strerror(errno)); 893184054Slulf goto out; 894184054Slulf } 895184054Slulf 896184054Slulf md.md_provsize = mediasize; 897184054Slulf 898184054Slulf val = gctl_get_intmax(r, "sectorsize"); 899184054Slulf if (val == 0) { 900184054Slulf md.md_sectorsize = secsize; 901184054Slulf } else { 902184054Slulf if (val < 0 || (val % secsize) != 0 || !powerof2(val)) { 903184054Slulf gctl_error(r, "Invalid sector size."); 904184054Slulf goto out; 905184054Slulf } 906184054Slulf md.md_sectorsize = val; 907186700Slulf } 908186700Slulf 909184054Slulf /* Use different salt and Master Key for each provider. */ 910184054Slulf arc4random_buf(md.md_salt, sizeof(md.md_salt)); 911184054Slulf arc4random_buf(md.md_mkeys, sizeof(md.md_mkeys)); 912184054Slulf 913184054Slulf /* Generate user key. */ 914184054Slulf if (eli_genkey(r, &ctxtemplate, &md, key, true) == NULL) { 915184054Slulf /* 916184054Slulf * Error generating key - details added to geom request 917184054Slulf * by eli_genkey(). 918184054Slulf */ 919184054Slulf goto out; 920184054Slulf } 921184054Slulf 922184054Slulf /* Encrypt the first and the only Master Key. */ 923184054Slulf error = g_eli_mkey_encrypt(md.md_ealgo, key, md.md_keylen, 924184054Slulf md.md_mkeys); 925184054Slulf if (error != 0) { 926184054Slulf gctl_error(r, "Cannot encrypt Master Key: %s.", 927184054Slulf strerror(error)); 928184054Slulf goto out; 929184054Slulf } 930184054Slulf 931186741Slulf /* Convert metadata to on-disk format. */ 932184054Slulf eli_metadata_encode(&md, sector); 933184054Slulf 934184054Slulf /* Store metadata to disk. */ 935184054Slulf error = g_metadata_store(prov, sector, sizeof(sector)); 936184054Slulf if (error != 0) { 937184054Slulf gctl_error(r, "Cannot store metadata on %s: %s.", prov, 938184054Slulf strerror(error)); 939184054Slulf goto out; 940184054Slulf } 941184054Slulf if (verbose) 942184054Slulf printf("Metadata value stored on %s.\n", prov); 943184054Slulf 944184054Slulf /* Backup metadata to a file. */ 945184054Slulf const char *p = prov; 946184054Slulf unsigned int j; 947184054Slulf 948184054Slulf /* 949184054Slulf * Check if provider string includes the devfs mountpoint 950186718Slulf * (typically /dev/). 951184054Slulf */ 952184054Slulf if (strncmp(p, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) { 953186741Slulf /* Skip forward to the device filename only. */ 954186741Slulf p += sizeof(_PATH_DEV) - 1; 955186741Slulf } 956186741Slulf 957184054Slulf str = gctl_get_ascii(r, "backupfile"); 958186741Slulf if (str[0] != '\0') { 959186741Slulf /* Backupfile given by the user, just copy it. */ 960186741Slulf strlcpy(backfile, str, sizeof(backfile)); 961186741Slulf 962184054Slulf /* If multiple providers have been initialized in one 963186741Slulf * command, and the backup filename has been specified 964186741Slulf * as anything other than "none", make the backup 965186741Slulf * filename unique for each provider. */ 966184054Slulf if (nargs > 1 && strcmp(backfile, "none") != 0) { 967186741Slulf /* 968186741Slulf * Replace first occurrence of "PROV" with 969186741Slulf * provider name. 970186741Slulf */ 971184054Slulf str = strnstr(backfile, "PROV", 972186741Slulf sizeof(backfile)); 973186741Slulf if (str != NULL) { 974186741Slulf char suffix[MAXPATHLEN]; 975186741Slulf j = str - backfile; 976186741Slulf strlcpy(suffix, &backfile[j+4], 977184054Slulf sizeof(suffix)); 978186741Slulf backfile[j] = '\0'; 979186741Slulf strlcat(backfile, p, sizeof(backfile)); 980186741Slulf strlcat(backfile, suffix, 981186741Slulf sizeof(backfile)); 982184054Slulf } else { 983186741Slulf /* 984186741Slulf * "PROV" not found in backfile, append 985184054Slulf * provider name. 986184054Slulf */ 987184054Slulf strlcat(backfile, "-", 988184054Slulf sizeof(backfile)); 989184054Slulf strlcat(backfile, p, sizeof(backfile)); 990184054Slulf } 991184054Slulf } 992184054Slulf } else { 993184054Slulf /* Generate filename automatically. */ 994184054Slulf snprintf(backfile, sizeof(backfile), "%s%s.eli", 995184054Slulf GELI_BACKUP_DIR, p); 996184054Slulf /* Replace all / with _. */ 997184054Slulf for (j = strlen(GELI_BACKUP_DIR); backfile[j] != '\0'; 998184054Slulf j++) { 999184054Slulf if (backfile[j] == '/') 1000185134Slulf backfile[j] = '_'; 1001185134Slulf } 1002184054Slulf } 1003184054Slulf if (strcmp(backfile, "none") != 0 && 1004186727Slulf eli_backup_create(r, prov, backfile) == 0) { 1005184054Slulf printf("\nMetadata backup for provider %s can be found " 1006184054Slulf "in %s\n", prov, backfile); 1007184054Slulf printf("and can be restored with the following " 1008184054Slulf "command:\n"); 1009184054Slulf printf("\n\t# geli restore %s %s\n\n", backfile, prov); 1010184054Slulf } 1011184054Slulf 1012184054Slulfout: 1013184054Slulf /* 1014184054Slulf * Print error for this request, and set parent request error 1015184054Slulf * message. 1016184054Slulf */ 1017184054Slulf if (r->error != NULL && r->error[0] != '\0') { 1018184054Slulf warnx("%s", r->error); 1019184054Slulf gctl_error(req, "There was an error with at least one " 1020184054Slulf "provider."); 1021184054Slulf } 1022184054Slulf 1023184054Slulf gctl_free(r); 1024184054Slulf 1025184054Slulf /* 1026184054Slulf * Erase sensitive and provider specific data from memory. 1027184054Slulf */ 1028184054Slulf explicit_bzero(key, sizeof(key)); 1029184054Slulf explicit_bzero(sector, sizeof(sector)); 1030184054Slulf explicit_bzero(&md.md_provsize, sizeof(md.md_provsize)); 1031185134Slulf explicit_bzero(&md.md_sectorsize, sizeof(md.md_sectorsize)); 1032185134Slulf explicit_bzero(&md.md_salt, sizeof(md.md_salt)); 1033184054Slulf explicit_bzero(&md.md_mkeys, sizeof(md.md_mkeys)); 1034184054Slulf } 1035184054Slulf 1036184054Slulf /* Clear the cached metadata, including keys. */ 1037184054Slulf explicit_bzero(&md, sizeof(md)); 1038184054Slulf explicit_bzero(&ctxtemplate, sizeof(ctxtemplate)); 1039184054Slulf} 1040184054Slulf 1041184054Slulfstatic void 1042184054Slulfeli_attach(struct gctl_req *req) 1043184054Slulf{ 1044184054Slulf struct g_eli_metadata md; 1045184054Slulf struct gctl_req *r; 1046184054Slulf const char *prov; 1047184054Slulf off_t mediasize; 1048184054Slulf int i, nargs, nparams, param; 1049184054Slulf const int one = 1; 1050184054Slulf struct hmac_ctx ctxtemplate; 1051184054Slulf 1052184054Slulf nargs = gctl_get_int(req, "nargs"); 1053184054Slulf if (nargs <= 0) { 1054186727Slulf gctl_error(req, "Too few arguments."); 1055184054Slulf return; 1056184054Slulf } 1057184054Slulf 1058184054Slulf unsigned char key[G_ELI_USERKEYLEN]; 1059184054Slulf 1060184054Slulf /* 1061184054Slulf * Determine number of parameters in the parent geom request before the 1062184054Slulf * nargs parameter and list of providers. 1063184054Slulf */ 1064184054Slulf nparams = req->narg - nargs - 1; 1065184054Slulf 1066184054Slulf /* Generate HMAC context template. */ 1067184054Slulf if (!eli_init_key_hmac_ctx(req, &ctxtemplate, false)) 1068184054Slulf return; 1069184054Slulf 1070184054Slulf /* Create new child request for each provider and issue to kernel */ 1071184054Slulf for (i = 0; i < nargs; i++) { 1072184054Slulf r = gctl_get_handle(); 1073184054Slulf 1074184054Slulf /* Copy each parameter from the parent request to the child */ 1075184054Slulf for (param = 0; param < nparams; param++) { 1076184054Slulf gctl_ro_param(r, req->arg[param].name, 1077184054Slulf req->arg[param].len, req->arg[param].value); 1078184054Slulf } 1079184054Slulf 1080184054Slulf /* Add a single provider to the parameter list of the child */ 1081184054Slulf gctl_ro_param(r, "nargs", sizeof(one), &one); 1082184054Slulf prov = gctl_get_ascii(req, "arg%d", i); 1083184054Slulf gctl_ro_param(r, "arg0", -1, prov); 1084184054Slulf 1085184054Slulf if (eli_metadata_read(r, prov, &md) == -1) { 1086184054Slulf /* 1087184054Slulf * Error reading metadata - details added to geom 1088184054Slulf * request by eli_metadata_read(). 1089184054Slulf */ 1090184054Slulf goto out; 1091184054Slulf } 1092184054Slulf 1093184054Slulf mediasize = g_get_mediasize(prov); 1094184054Slulf if (md.md_provsize != (uint64_t)mediasize) { 1095184054Slulf gctl_error(r, "Provider size mismatch."); 1096184054Slulf goto out; 1097184054Slulf } 1098184054Slulf 1099184054Slulf if (eli_genkey(r, &ctxtemplate, &md, key, false) == NULL) { 1100184054Slulf /* 1101184054Slulf * Error generating key - details added to geom request 1102184054Slulf * by eli_genkey(). 1103184054Slulf */ 1104184054Slulf goto out; 1105184054Slulf } 1106186700Slulf 1107186700Slulf gctl_ro_param(r, "key", sizeof(key), key); 1108186700Slulf 1109186700Slulf if (gctl_issue(r) == NULL) { 1110186700Slulf if (verbose) 1111186700Slulf printf("Attached to %s.\n", prov); 1112186700Slulf } 1113186700Slulf 1114186700Slulfout: 1115186700Slulf /* 1116186700Slulf * Print error for this request, and set parent request error 1117184054Slulf * message. 1118184054Slulf */ 1119184054Slulf if (r->error != NULL && r->error[0] != '\0') { 1120184054Slulf warnx("%s", r->error); 1121184054Slulf gctl_error(req, "There was an error with at least one " 1122184054Slulf "provider."); 1123184054Slulf } 1124184054Slulf 1125184054Slulf gctl_free(r); 1126184054Slulf 1127184054Slulf /* Clear sensitive data from memory. */ 1128184054Slulf explicit_bzero(key, sizeof(key)); 1129184054Slulf } 1130184054Slulf 1131184054Slulf /* Clear sensitive data from memory. */ 1132184054Slulf explicit_bzero(cached_passphrase, sizeof(cached_passphrase)); 1133184054Slulf explicit_bzero(&ctxtemplate, sizeof(ctxtemplate)); 1134184054Slulf} 1135184054Slulf 1136184054Slulfstatic void 1137184054Slulfeli_configure_detached(struct gctl_req *req, const char *prov, int boot, 1138184054Slulf int geliboot, int displaypass, int trim, int autoresize) 1139184054Slulf{ 1140184054Slulf struct g_eli_metadata md; 1141186727Slulf bool changed = 0; 1142184054Slulf 1143184054Slulf if (eli_metadata_read(req, prov, &md) == -1) 1144184054Slulf return; 1145184054Slulf 1146184054Slulf if (boot == 1 && (md.md_flags & G_ELI_FLAG_BOOT)) { 1147184054Slulf if (verbose) 1148184054Slulf printf("BOOT flag already configured for %s.\n", prov); 1149184054Slulf } else if (boot == 0 && !(md.md_flags & G_ELI_FLAG_BOOT)) { 1150184054Slulf if (verbose) 1151186741Slulf printf("BOOT flag not configured for %s.\n", prov); 1152184054Slulf } else if (boot >= 0) { 1153184054Slulf if (boot) 1154184054Slulf md.md_flags |= G_ELI_FLAG_BOOT; 1155184054Slulf else 1156184054Slulf md.md_flags &= ~G_ELI_FLAG_BOOT; 1157184054Slulf changed = 1; 1158184054Slulf } 1159184054Slulf 1160184054Slulf if (geliboot == 1 && (md.md_flags & G_ELI_FLAG_GELIBOOT)) { 1161184054Slulf if (verbose) 1162184054Slulf printf("GELIBOOT flag already configured for %s.\n", prov); 1163184054Slulf } else if (geliboot == 0 && !(md.md_flags & G_ELI_FLAG_GELIBOOT)) { 1164184054Slulf if (verbose) 1165184054Slulf printf("GELIBOOT flag not configured for %s.\n", prov); 1166184054Slulf } else if (geliboot >= 0) { 1167184054Slulf if (geliboot) 1168184054Slulf md.md_flags |= G_ELI_FLAG_GELIBOOT; 1169184054Slulf else 1170184054Slulf md.md_flags &= ~G_ELI_FLAG_GELIBOOT; 1171184054Slulf changed = 1; 1172184054Slulf } 1173184054Slulf 1174184054Slulf if (displaypass == 1 && (md.md_flags & G_ELI_FLAG_GELIDISPLAYPASS)) { 1175184054Slulf if (verbose) 1176184054Slulf printf("GELIDISPLAYPASS flag already configured for %s.\n", prov); 1177184054Slulf } else if (displaypass == 0 && 1178184054Slulf !(md.md_flags & G_ELI_FLAG_GELIDISPLAYPASS)) { 1179184054Slulf if (verbose) 1180184054Slulf printf("GELIDISPLAYPASS flag not configured for %s.\n", prov); 1181184054Slulf } else if (displaypass >= 0) { 1182184054Slulf if (displaypass) 1183184054Slulf md.md_flags |= G_ELI_FLAG_GELIDISPLAYPASS; 1184184054Slulf else 1185184054Slulf md.md_flags &= ~G_ELI_FLAG_GELIDISPLAYPASS; 1186184054Slulf changed = 1; 1187185134Slulf } 1188184054Slulf 1189184054Slulf if (trim == 0 && (md.md_flags & G_ELI_FLAG_NODELETE)) { 1190184054Slulf if (verbose) 1191184054Slulf printf("TRIM disable flag already configured for %s.\n", prov); 1192184054Slulf } else if (trim == 1 && !(md.md_flags & G_ELI_FLAG_NODELETE)) { 1193186727Slulf if (verbose) 1194184054Slulf printf("TRIM disable flag not configured for %s.\n", prov); 1195184054Slulf } else if (trim >= 0) { 1196184054Slulf if (trim) 1197184054Slulf md.md_flags &= ~G_ELI_FLAG_NODELETE; 1198184054Slulf else 1199184054Slulf md.md_flags |= G_ELI_FLAG_NODELETE; 1200184054Slulf changed = 1; 1201184054Slulf } 1202184054Slulf 1203186727Slulf if (autoresize == 1 && (md.md_flags & G_ELI_FLAG_AUTORESIZE)) { 1204186727Slulf if (verbose) 1205186727Slulf printf("AUTORESIZE flag already configured for %s.\n", prov); 1206186727Slulf } else if (autoresize == 0 && !(md.md_flags & G_ELI_FLAG_AUTORESIZE)) { 1207186727Slulf if (verbose) 1208186727Slulf printf("AUTORESIZE flag not configured for %s.\n", prov); 1209186727Slulf } else if (autoresize >= 0) { 1210186727Slulf if (autoresize) 1211186727Slulf md.md_flags |= G_ELI_FLAG_AUTORESIZE; 1212186727Slulf else 1213186727Slulf md.md_flags &= ~G_ELI_FLAG_AUTORESIZE; 1214186727Slulf changed = 1; 1215186727Slulf } 1216186727Slulf 1217186727Slulf if (changed) 1218186727Slulf eli_metadata_store(req, prov, &md); 1219186727Slulf explicit_bzero(&md, sizeof(md)); 1220186727Slulf} 1221186727Slulf 1222186727Slulfstatic void 1223186727Slulfeli_configure(struct gctl_req *req) 1224186727Slulf{ 1225186727Slulf const char *prov; 1226186727Slulf bool boot, noboot, geliboot, nogeliboot, displaypass, nodisplaypass; 1227186727Slulf bool autoresize, noautoresize, trim, notrim; 1228186700Slulf int doboot, dogeliboot, dodisplaypass, dotrim, doautoresize; 1229184054Slulf int i, nargs; 1230184054Slulf 1231184054Slulf nargs = gctl_get_int(req, "nargs"); 1232184054Slulf if (nargs == 0) { 1233184054Slulf gctl_error(req, "Too few arguments."); 1234184054Slulf return; 1235184054Slulf } 1236184054Slulf 1237184054Slulf boot = gctl_get_int(req, "boot"); 1238184054Slulf noboot = gctl_get_int(req, "noboot"); 1239184054Slulf geliboot = gctl_get_int(req, "geliboot"); 1240184054Slulf nogeliboot = gctl_get_int(req, "nogeliboot"); 1241184054Slulf displaypass = gctl_get_int(req, "displaypass"); 1242184054Slulf nodisplaypass = gctl_get_int(req, "nodisplaypass"); 1243184054Slulf trim = gctl_get_int(req, "trim"); 1244184054Slulf notrim = gctl_get_int(req, "notrim"); 1245184054Slulf autoresize = gctl_get_int(req, "autoresize"); 1246184054Slulf noautoresize = gctl_get_int(req, "noautoresize"); 1247184054Slulf 1248184054Slulf doboot = -1; 1249184054Slulf if (boot && noboot) { 1250184054Slulf gctl_error(req, "Options -b and -B are mutually exclusive."); 1251184054Slulf return; 1252184054Slulf } 1253184054Slulf if (boot) 1254184054Slulf doboot = 1; 1255184054Slulf else if (noboot) 1256184054Slulf doboot = 0; 1257184054Slulf 1258184054Slulf dogeliboot = -1; 1259184054Slulf if (geliboot && nogeliboot) { 1260184054Slulf gctl_error(req, "Options -g and -G are mutually exclusive."); 1261184054Slulf return; 1262184054Slulf } 1263184054Slulf if (geliboot) 1264184054Slulf dogeliboot = 1; 1265184054Slulf else if (nogeliboot) 1266184054Slulf dogeliboot = 0; 1267184054Slulf 1268184054Slulf dodisplaypass = -1; 1269184054Slulf if (displaypass && nodisplaypass) { 1270184054Slulf gctl_error(req, "Options -d and -D are mutually exclusive."); 1271184054Slulf return; 1272184054Slulf } 1273184054Slulf if (displaypass) 1274184054Slulf dodisplaypass = 1; 1275184054Slulf else if (nodisplaypass) 1276184054Slulf dodisplaypass = 0; 1277184054Slulf 1278184054Slulf dotrim = -1; 1279184054Slulf if (trim && notrim) { 1280184054Slulf gctl_error(req, "Options -t and -T are mutually exclusive."); 1281184054Slulf return; 1282184054Slulf } 1283186744Slulf if (trim) 1284184054Slulf dotrim = 1; 1285184054Slulf else if (notrim) 1286184054Slulf dotrim = 0; 1287184054Slulf 1288184054Slulf doautoresize = -1; 1289184054Slulf if (autoresize && noautoresize) { 1290184054Slulf gctl_error(req, "Options -r and -R are mutually exclusive."); 1291184054Slulf return; 1292184054Slulf } 1293184054Slulf if (autoresize) 1294184054Slulf doautoresize = 1; 1295184054Slulf else if (noautoresize) 1296184054Slulf doautoresize = 0; 1297184054Slulf 1298185592Slulf if (doboot == -1 && dogeliboot == -1 && dodisplaypass == -1 && 1299184054Slulf dotrim == -1 && doautoresize == -1) { 1300184054Slulf gctl_error(req, "No option given."); 1301190422Slulf return; 1302184054Slulf } 1303184054Slulf 1304185592Slulf /* First attached providers. */ 1305184054Slulf gctl_issue(req); 1306185592Slulf /* Now the rest. */ 1307185592Slulf for (i = 0; i < nargs; i++) { 1308184054Slulf prov = gctl_get_ascii(req, "arg%d", i); 1309190422Slulf if (!eli_is_attached(prov)) { 1310184054Slulf eli_configure_detached(req, prov, doboot, dogeliboot, 1311190422Slulf dodisplaypass, dotrim, doautoresize); 1312184054Slulf } 1313184054Slulf } 1314184054Slulf} 1315184054Slulf 1316185592Slulfstatic void 1317184054Slulfeli_setkey_attached(struct gctl_req *req, struct g_eli_metadata *md) 1318184054Slulf{ 1319190422Slulf unsigned char key[G_ELI_USERKEYLEN]; 1320184054Slulf intmax_t val, old = 0; 1321184054Slulf int error; 1322185592Slulf 1323184054Slulf val = gctl_get_intmax(req, "iterations"); 1324185592Slulf /* Check if iterations number should be changed. */ 1325185592Slulf if (val != -1) 1326184054Slulf md->md_iterations = val; 1327184054Slulf else 1328190422Slulf old = md->md_iterations; 1329184054Slulf 1330190422Slulf /* Generate key for Master Key encryption. */ 1331184054Slulf if (eli_genkey_single(req, md, key, true) == NULL) { 1332184054Slulf explicit_bzero(key, sizeof(key)); 1333184054Slulf return; 1334190422Slulf } 1335184054Slulf /* 1336184054Slulf * If number of iterations has changed, but wasn't given as a 1337184054Slulf * command-line argument, update the request. 1338190422Slulf */ 1339184054Slulf if (val == -1 && md->md_iterations != old) { 1340184054Slulf error = gctl_change_param(req, "iterations", sizeof(intmax_t), 1341184054Slulf &md->md_iterations); 1342190422Slulf assert(error == 0); 1343184054Slulf } 1344190422Slulf 1345184054Slulf gctl_ro_param(req, "key", sizeof(key), key); 1346184054Slulf gctl_issue(req); 1347184054Slulf explicit_bzero(key, sizeof(key)); 1348190422Slulf} 1349184054Slulf 1350184054Slulfstatic void 1351184054Slulfeli_setkey_detached(struct gctl_req *req, const char *prov, 1352190422Slulf struct g_eli_metadata *md) 1353185134Slulf{ 1354185134Slulf unsigned char key[G_ELI_USERKEYLEN], mkey[G_ELI_DATAIVKEYLEN]; 1355185134Slulf unsigned char *mkeydst; 1356190422Slulf unsigned int nkey; 1357185134Slulf intmax_t val; 1358190422Slulf int error; 1359185134Slulf 1360185134Slulf if (md->md_keys == 0) { 1361190422Slulf gctl_error(req, "No valid keys on %s.", prov); 1362185134Slulf return; 1363185134Slulf } 1364184054Slulf 1365184054Slulf /* Generate key for Master Key decryption. */ 1366184054Slulf if (eli_genkey_single(req, md, key, false) == NULL) { 1367184054Slulf explicit_bzero(key, sizeof(key)); 1368184054Slulf return; 1369184054Slulf } 1370184054Slulf 1371184054Slulf /* Decrypt Master Key. */ 1372184054Slulf error = g_eli_mkey_decrypt_any(md, key, mkey, &nkey); 1373184054Slulf explicit_bzero(key, sizeof(key)); 1374184054Slulf if (error != 0) { 1375184054Slulf explicit_bzero(md, sizeof(*md)); 1376184054Slulf if (error == -1) 1377184054Slulf gctl_error(req, "Wrong key for %s.", prov); 1378190422Slulf else /* if (error > 0) */ { 1379190422Slulf gctl_error(req, "Cannot decrypt Master Key: %s.", 1380184054Slulf strerror(error)); 1381190422Slulf } 1382184054Slulf return; 1383184054Slulf } 1384184054Slulf if (verbose) 1385184054Slulf printf("Decrypted Master Key %u.\n", nkey); 1386184054Slulf 1387184054Slulf val = gctl_get_intmax(req, "keyno"); 1388184054Slulf if (val != -1) 1389184054Slulf nkey = val; 1390184054Slulf#if 0 1391184054Slulf else 1392184054Slulf ; /* Use the key number which was found during decryption. */ 1393184054Slulf#endif 1394184054Slulf if (nkey >= G_ELI_MAXMKEYS) { 1395184054Slulf gctl_error(req, "Invalid '%s' argument.", "keyno"); 1396184054Slulf return; 1397184054Slulf } 1398184054Slulf 1399184054Slulf val = gctl_get_intmax(req, "iterations"); 1400184054Slulf /* Check if iterations number should and can be changed. */ 1401184054Slulf if (val != -1 && md->md_iterations == -1) { 1402184054Slulf md->md_iterations = val; 1403184054Slulf } else if (val != -1 && val != md->md_iterations) { 1404184054Slulf if (bitcount32(md->md_keys) != 1) { 1405184054Slulf gctl_error(req, "To be able to use '-i' option, only " 1406184054Slulf "one key can be defined."); 1407184054Slulf return; 1408184054Slulf } 1409184054Slulf if (md->md_keys != (1 << nkey)) { 1410184054Slulf gctl_error(req, "Only already defined key can be " 1411184054Slulf "changed when '-i' option is used."); 1412184054Slulf return; 1413 } 1414 md->md_iterations = val; 1415 } 1416 1417 mkeydst = md->md_mkeys + nkey * G_ELI_MKEYLEN; 1418 md->md_keys |= (1 << nkey); 1419 1420 bcopy(mkey, mkeydst, sizeof(mkey)); 1421 explicit_bzero(mkey, sizeof(mkey)); 1422 1423 /* 1424 * The previous eli_genkey() set cached_passphrase, we do not want to 1425 * use that for the new passphrase so always prompt for it 1426 */ 1427 explicit_bzero(cached_passphrase, sizeof(cached_passphrase)); 1428 1429 /* Generate key for Master Key encryption. */ 1430 if (eli_genkey_single(req, md, key, true) == NULL) { 1431 explicit_bzero(key, sizeof(key)); 1432 explicit_bzero(md, sizeof(*md)); 1433 return; 1434 } 1435 1436 /* Encrypt the Master-Key with the new key. */ 1437 error = g_eli_mkey_encrypt(md->md_ealgo, key, md->md_keylen, mkeydst); 1438 explicit_bzero(key, sizeof(key)); 1439 if (error != 0) { 1440 explicit_bzero(md, sizeof(*md)); 1441 gctl_error(req, "Cannot encrypt Master Key: %s.", 1442 strerror(error)); 1443 return; 1444 } 1445 1446 /* Store metadata with fresh key. */ 1447 eli_metadata_store(req, prov, md); 1448 explicit_bzero(md, sizeof(*md)); 1449} 1450 1451static void 1452eli_setkey(struct gctl_req *req) 1453{ 1454 struct g_eli_metadata md; 1455 const char *prov; 1456 int nargs; 1457 1458 nargs = gctl_get_int(req, "nargs"); 1459 if (nargs != 1) { 1460 gctl_error(req, "Invalid number of arguments."); 1461 return; 1462 } 1463 prov = gctl_get_ascii(req, "arg0"); 1464 1465 if (eli_metadata_read(req, prov, &md) == -1) 1466 return; 1467 1468 if (eli_is_attached(prov)) 1469 eli_setkey_attached(req, &md); 1470 else 1471 eli_setkey_detached(req, prov, &md); 1472 1473 if (req->error == NULL || req->error[0] == '\0') { 1474 printf("Note, that the master key encrypted with old keys " 1475 "and/or passphrase may still exist in a metadata backup " 1476 "file.\n"); 1477 } 1478} 1479 1480static void 1481eli_delkey_attached(struct gctl_req *req, const char *prov __unused) 1482{ 1483 1484 gctl_issue(req); 1485} 1486 1487static void 1488eli_delkey_detached(struct gctl_req *req, const char *prov) 1489{ 1490 struct g_eli_metadata md; 1491 unsigned char *mkeydst; 1492 unsigned int nkey; 1493 intmax_t val; 1494 bool all, force; 1495 1496 if (eli_metadata_read(req, prov, &md) == -1) 1497 return; 1498 1499 all = gctl_get_int(req, "all"); 1500 if (all) 1501 arc4random_buf(md.md_mkeys, sizeof(md.md_mkeys)); 1502 else { 1503 force = gctl_get_int(req, "force"); 1504 val = gctl_get_intmax(req, "keyno"); 1505 if (val == -1) { 1506 gctl_error(req, "Key number has to be specified."); 1507 return; 1508 } 1509 nkey = val; 1510 if (nkey >= G_ELI_MAXMKEYS) { 1511 gctl_error(req, "Invalid '%s' argument.", "keyno"); 1512 return; 1513 } 1514 if (!(md.md_keys & (1 << nkey)) && !force) { 1515 gctl_error(req, "Master Key %u is not set.", nkey); 1516 return; 1517 } 1518 md.md_keys &= ~(1 << nkey); 1519 if (md.md_keys == 0 && !force) { 1520 gctl_error(req, "This is the last Master Key. Use '-f' " 1521 "option if you really want to remove it."); 1522 return; 1523 } 1524 mkeydst = md.md_mkeys + nkey * G_ELI_MKEYLEN; 1525 arc4random_buf(mkeydst, G_ELI_MKEYLEN); 1526 } 1527 1528 eli_metadata_store(req, prov, &md); 1529 explicit_bzero(&md, sizeof(md)); 1530} 1531 1532static void 1533eli_delkey(struct gctl_req *req) 1534{ 1535 const char *prov; 1536 int nargs; 1537 1538 nargs = gctl_get_int(req, "nargs"); 1539 if (nargs != 1) { 1540 gctl_error(req, "Invalid number of arguments."); 1541 return; 1542 } 1543 prov = gctl_get_ascii(req, "arg0"); 1544 1545 if (eli_is_attached(prov)) 1546 eli_delkey_attached(req, prov); 1547 else 1548 eli_delkey_detached(req, prov); 1549} 1550 1551static void 1552eli_resume(struct gctl_req *req) 1553{ 1554 struct g_eli_metadata md; 1555 unsigned char key[G_ELI_USERKEYLEN]; 1556 const char *prov; 1557 off_t mediasize; 1558 int nargs; 1559 1560 nargs = gctl_get_int(req, "nargs"); 1561 if (nargs != 1) { 1562 gctl_error(req, "Invalid number of arguments."); 1563 return; 1564 } 1565 prov = gctl_get_ascii(req, "arg0"); 1566 1567 if (eli_metadata_read(req, prov, &md) == -1) 1568 return; 1569 1570 mediasize = g_get_mediasize(prov); 1571 if (md.md_provsize != (uint64_t)mediasize) { 1572 gctl_error(req, "Provider size mismatch."); 1573 return; 1574 } 1575 1576 if (eli_genkey_single(req, &md, key, false) == NULL) { 1577 explicit_bzero(key, sizeof(key)); 1578 return; 1579 } 1580 1581 gctl_ro_param(req, "key", sizeof(key), key); 1582 if (gctl_issue(req) == NULL) { 1583 if (verbose) 1584 printf("Resumed %s.\n", prov); 1585 } 1586 explicit_bzero(key, sizeof(key)); 1587} 1588 1589static int 1590eli_trash_metadata(struct gctl_req *req, const char *prov, int fd, off_t offset) 1591{ 1592 unsigned int overwrites; 1593 unsigned char *sector; 1594 ssize_t size; 1595 int error; 1596 1597 size = sizeof(overwrites); 1598 if (sysctlbyname("kern.geom.eli.overwrites", &overwrites, &size, 1599 NULL, 0) == -1 || overwrites == 0) { 1600 overwrites = G_ELI_OVERWRITES; 1601 } 1602 1603 size = g_sectorsize(fd); 1604 if (size <= 0) { 1605 gctl_error(req, "Cannot obtain provider sector size %s: %s.", 1606 prov, strerror(errno)); 1607 return (-1); 1608 } 1609 sector = malloc(size); 1610 if (sector == NULL) { 1611 gctl_error(req, "Cannot allocate %zd bytes of memory.", size); 1612 return (-1); 1613 } 1614 1615 error = 0; 1616 do { 1617 arc4random_buf(sector, size); 1618 if (pwrite(fd, sector, size, offset) != size) { 1619 if (error == 0) 1620 error = errno; 1621 } 1622 (void)g_flush(fd); 1623 } while (--overwrites > 0); 1624 free(sector); 1625 if (error != 0) { 1626 gctl_error(req, "Cannot trash metadata on provider %s: %s.", 1627 prov, strerror(error)); 1628 return (-1); 1629 } 1630 return (0); 1631} 1632 1633static void 1634eli_kill_detached(struct gctl_req *req, const char *prov) 1635{ 1636 off_t offset; 1637 int fd; 1638 1639 /* 1640 * NOTE: Maybe we should verify if this is geli provider first, 1641 * but 'kill' command is quite critical so better don't waste 1642 * the time. 1643 */ 1644#if 0 1645 error = g_metadata_read(prov, (unsigned char *)&md, sizeof(md), 1646 G_ELI_MAGIC); 1647 if (error != 0) { 1648 gctl_error(req, "Cannot read metadata from %s: %s.", prov, 1649 strerror(error)); 1650 return; 1651 } 1652#endif 1653 1654 fd = g_open(prov, 1); 1655 if (fd == -1) { 1656 gctl_error(req, "Cannot open provider %s: %s.", prov, 1657 strerror(errno)); 1658 return; 1659 } 1660 offset = g_mediasize(fd) - g_sectorsize(fd); 1661 if (offset <= 0) { 1662 gctl_error(req, 1663 "Cannot obtain media size or sector size for provider %s: %s.", 1664 prov, strerror(errno)); 1665 (void)g_close(fd); 1666 return; 1667 } 1668 (void)eli_trash_metadata(req, prov, fd, offset); 1669 (void)g_close(fd); 1670} 1671 1672static void 1673eli_kill(struct gctl_req *req) 1674{ 1675 const char *prov; 1676 int i, nargs, all; 1677 1678 nargs = gctl_get_int(req, "nargs"); 1679 all = gctl_get_int(req, "all"); 1680 if (!all && nargs == 0) { 1681 gctl_error(req, "Too few arguments."); 1682 return; 1683 } 1684 /* 1685 * How '-a' option combine with a list of providers: 1686 * Delete Master Keys from all attached providers: 1687 * geli kill -a 1688 * Delete Master Keys from all attached providers and from 1689 * detached da0 and da1: 1690 * geli kill -a da0 da1 1691 * Delete Master Keys from (attached or detached) da0 and da1: 1692 * geli kill da0 da1 1693 */ 1694 1695 /* First detached providers. */ 1696 for (i = 0; i < nargs; i++) { 1697 prov = gctl_get_ascii(req, "arg%d", i); 1698 if (!eli_is_attached(prov)) 1699 eli_kill_detached(req, prov); 1700 } 1701 /* Now attached providers. */ 1702 gctl_issue(req); 1703} 1704 1705static int 1706eli_backup_create(struct gctl_req *req, const char *prov, const char *file) 1707{ 1708 unsigned char *sector; 1709 ssize_t secsize; 1710 int error, filefd, ret; 1711 1712 ret = -1; 1713 filefd = -1; 1714 sector = NULL; 1715 secsize = 0; 1716 1717 secsize = g_get_sectorsize(prov); 1718 if (secsize == 0) { 1719 gctl_error(req, "Cannot get informations about %s: %s.", prov, 1720 strerror(errno)); 1721 goto out; 1722 } 1723 sector = malloc(secsize); 1724 if (sector == NULL) { 1725 gctl_error(req, "Cannot allocate memory."); 1726 goto out; 1727 } 1728 /* Read metadata from the provider. */ 1729 error = g_metadata_read(prov, sector, secsize, G_ELI_MAGIC); 1730 if (error != 0) { 1731 gctl_error(req, "Unable to read metadata from %s: %s.", prov, 1732 strerror(error)); 1733 goto out; 1734 } 1735 1736 filefd = open(file, O_WRONLY | O_TRUNC | O_CREAT, 0600); 1737 if (filefd == -1) { 1738 gctl_error(req, "Unable to open %s: %s.", file, 1739 strerror(errno)); 1740 goto out; 1741 } 1742 /* Write metadata to the destination file. */ 1743 if (write(filefd, sector, secsize) != secsize) { 1744 gctl_error(req, "Unable to write to %s: %s.", file, 1745 strerror(errno)); 1746 (void)close(filefd); 1747 (void)unlink(file); 1748 goto out; 1749 } 1750 (void)fsync(filefd); 1751 (void)close(filefd); 1752 /* Success. */ 1753 ret = 0; 1754out: 1755 if (sector != NULL) { 1756 explicit_bzero(sector, secsize); 1757 free(sector); 1758 } 1759 return (ret); 1760} 1761 1762static void 1763eli_backup(struct gctl_req *req) 1764{ 1765 const char *file, *prov; 1766 int nargs; 1767 1768 nargs = gctl_get_int(req, "nargs"); 1769 if (nargs != 2) { 1770 gctl_error(req, "Invalid number of arguments."); 1771 return; 1772 } 1773 prov = gctl_get_ascii(req, "arg0"); 1774 file = gctl_get_ascii(req, "arg1"); 1775 1776 eli_backup_create(req, prov, file); 1777} 1778 1779static void 1780eli_restore(struct gctl_req *req) 1781{ 1782 struct g_eli_metadata md; 1783 const char *file, *prov; 1784 off_t mediasize; 1785 int nargs; 1786 1787 nargs = gctl_get_int(req, "nargs"); 1788 if (nargs != 2) { 1789 gctl_error(req, "Invalid number of arguments."); 1790 return; 1791 } 1792 file = gctl_get_ascii(req, "arg0"); 1793 prov = gctl_get_ascii(req, "arg1"); 1794 1795 /* Read metadata from the backup file. */ 1796 if (eli_metadata_read(req, file, &md) == -1) 1797 return; 1798 /* Obtain provider's mediasize. */ 1799 mediasize = g_get_mediasize(prov); 1800 if (mediasize == 0) { 1801 gctl_error(req, "Cannot get informations about %s: %s.", prov, 1802 strerror(errno)); 1803 return; 1804 } 1805 /* Check if the provider size has changed since we did the backup. */ 1806 if (md.md_provsize != (uint64_t)mediasize) { 1807 if (gctl_get_int(req, "force")) { 1808 md.md_provsize = mediasize; 1809 } else { 1810 gctl_error(req, "Provider size mismatch: " 1811 "wrong backup file?"); 1812 return; 1813 } 1814 } 1815 /* Write metadata to the provider. */ 1816 (void)eli_metadata_store(req, prov, &md); 1817} 1818 1819static void 1820eli_resize(struct gctl_req *req) 1821{ 1822 struct g_eli_metadata md; 1823 const char *prov; 1824 unsigned char *sector; 1825 ssize_t secsize; 1826 off_t mediasize, oldsize; 1827 int error, nargs, provfd; 1828 1829 nargs = gctl_get_int(req, "nargs"); 1830 if (nargs != 1) { 1831 gctl_error(req, "Invalid number of arguments."); 1832 return; 1833 } 1834 prov = gctl_get_ascii(req, "arg0"); 1835 1836 provfd = -1; 1837 sector = NULL; 1838 secsize = 0; 1839 1840 provfd = g_open(prov, 1); 1841 if (provfd == -1) { 1842 gctl_error(req, "Cannot open %s: %s.", prov, strerror(errno)); 1843 goto out; 1844 } 1845 1846 mediasize = g_mediasize(provfd); 1847 secsize = g_sectorsize(provfd); 1848 if (mediasize == -1 || secsize == -1) { 1849 gctl_error(req, "Cannot get information about %s: %s.", prov, 1850 strerror(errno)); 1851 goto out; 1852 } 1853 1854 sector = malloc(secsize); 1855 if (sector == NULL) { 1856 gctl_error(req, "Cannot allocate memory."); 1857 goto out; 1858 } 1859 1860 oldsize = gctl_get_intmax(req, "oldsize"); 1861 if (oldsize < 0 || oldsize > mediasize) { 1862 gctl_error(req, "Invalid oldsize: Out of range."); 1863 goto out; 1864 } 1865 1866 /* Read metadata from the 'oldsize' offset. */ 1867 if (pread(provfd, sector, secsize, oldsize - secsize) != secsize) { 1868 gctl_error(req, "Cannot read old metadata: %s.", 1869 strerror(errno)); 1870 goto out; 1871 } 1872 1873 /* Check if this sector contains geli metadata. */ 1874 error = eli_metadata_decode(sector, &md); 1875 switch (error) { 1876 case 0: 1877 break; 1878 case EOPNOTSUPP: 1879 gctl_error(req, 1880 "Provider's %s metadata version %u is too new.\n" 1881 "geli: The highest supported version is %u.", 1882 prov, (unsigned int)md.md_version, G_ELI_VERSION); 1883 goto out; 1884 case EINVAL: 1885 gctl_error(req, "Inconsistent provider's %s metadata.", prov); 1886 goto out; 1887 default: 1888 gctl_error(req, 1889 "Unexpected error while decoding provider's %s metadata: %s.", 1890 prov, strerror(error)); 1891 goto out; 1892 } 1893 1894 /* 1895 * If the old metadata doesn't have a correct provider size, refuse 1896 * to resize. 1897 */ 1898 if (md.md_provsize != (uint64_t)oldsize) { 1899 gctl_error(req, "Provider size mismatch at oldsize."); 1900 goto out; 1901 } 1902 1903 /* The metadata is valid and nothing has changed. Just exit. */ 1904 if (oldsize == mediasize) 1905 goto out; 1906 1907 /* 1908 * Update the old metadata with the current provider size and write 1909 * it back to the correct place on the provider. 1910 */ 1911 md.md_provsize = mediasize; 1912 /* Write metadata to the provider. */ 1913 (void)eli_metadata_store(req, prov, &md); 1914 /* Now trash the old metadata. */ 1915 (void)eli_trash_metadata(req, prov, provfd, oldsize - secsize); 1916out: 1917 if (provfd != -1) 1918 (void)g_close(provfd); 1919 if (sector != NULL) { 1920 explicit_bzero(sector, secsize); 1921 free(sector); 1922 } 1923} 1924 1925static void 1926eli_version(struct gctl_req *req) 1927{ 1928 struct g_eli_metadata md; 1929 const char *name; 1930 unsigned int eli_version; 1931 int error, i, nargs; 1932 1933 nargs = gctl_get_int(req, "nargs"); 1934 1935 if (nargs == 0) { 1936 unsigned int kernver; 1937 ssize_t size; 1938 1939 size = sizeof(kernver); 1940 if (sysctlbyname("kern.geom.eli.version", &kernver, &size, 1941 NULL, 0) == -1) { 1942 warn("Unable to obtain GELI kernel version"); 1943 } else { 1944 printf("kernel: %u\n", kernver); 1945 } 1946 printf("userland: %u\n", G_ELI_VERSION); 1947 return; 1948 } 1949 1950 for (i = 0; i < nargs; i++) { 1951 name = gctl_get_ascii(req, "arg%d", i); 1952 error = g_metadata_read(name, (unsigned char *)&md, 1953 sizeof(md), G_ELI_MAGIC); 1954 if (error != 0) { 1955 warn("%s: Unable to read metadata: %s.", name, 1956 strerror(error)); 1957 gctl_error(req, "Not fully done."); 1958 continue; 1959 } 1960 eli_version = le32dec(&md.md_version); 1961 printf("%s: %u\n", name, eli_version); 1962 } 1963} 1964 1965static void 1966eli_clear(struct gctl_req *req) 1967{ 1968 const char *name; 1969 int error, i, nargs; 1970 1971 nargs = gctl_get_int(req, "nargs"); 1972 if (nargs < 1) { 1973 gctl_error(req, "Too few arguments."); 1974 return; 1975 } 1976 1977 for (i = 0; i < nargs; i++) { 1978 name = gctl_get_ascii(req, "arg%d", i); 1979 error = g_metadata_clear(name, G_ELI_MAGIC); 1980 if (error != 0) { 1981 fprintf(stderr, "Cannot clear metadata on %s: %s.\n", 1982 name, strerror(error)); 1983 gctl_error(req, "Not fully done."); 1984 continue; 1985 } 1986 if (verbose) 1987 printf("Metadata cleared on %s.\n", name); 1988 } 1989} 1990 1991static void 1992eli_dump(struct gctl_req *req) 1993{ 1994 struct g_eli_metadata md; 1995 const char *name; 1996 int i, nargs; 1997 1998 nargs = gctl_get_int(req, "nargs"); 1999 if (nargs < 1) { 2000 gctl_error(req, "Too few arguments."); 2001 return; 2002 } 2003 2004 for (i = 0; i < nargs; i++) { 2005 name = gctl_get_ascii(req, "arg%d", i); 2006 if (eli_metadata_read(NULL, name, &md) == -1) { 2007 gctl_error(req, "Not fully done."); 2008 continue; 2009 } 2010 printf("Metadata on %s:\n", name); 2011 eli_metadata_dump(&md); 2012 printf("\n"); 2013 } 2014} 2015