geom_eli.c revision 348588
1254219Scy/*- 2254219Scy * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3254219Scy * 4254219Scy * Copyright (c) 2004-2010 Pawel Jakub Dawidek <pjd@FreeBSD.org> 5254219Scy * All rights reserved. 6254219Scy * 7254219Scy * Redistribution and use in source and binary forms, with or without 8254219Scy * modification, are permitted provided that the following conditions 9254219Scy * are met: 10254219Scy * 1. Redistributions of source code must retain the above copyright 11254219Scy * notice, this list of conditions and the following disclaimer. 12254219Scy * 2. Redistributions in binary form must reproduce the above copyright 13254219Scy * notice, this list of conditions and the following disclaimer in the 14254219Scy * documentation and/or other materials provided with the distribution. 15254219Scy * 16254219Scy * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 17254219Scy * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18254219Scy * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19254219Scy * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 20254219Scy * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21254219Scy * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22254219Scy * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23254219Scy * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24254219Scy * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25254219Scy * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26254219Scy * SUCH DAMAGE. 27254219Scy */ 28254219Scy 29254219Scy#include <sys/cdefs.h> 30254219Scy__FBSDID("$FreeBSD: stable/11/sbin/geom/class/eli/geom_eli.c 348588 2019-06-03 21:04:23Z jhb $"); 31254219Scy 32254219Scy#include <sys/param.h> 33254219Scy#include <sys/mman.h> 34254219Scy#include <sys/sysctl.h> 35254219Scy#include <sys/resource.h> 36254219Scy#include <opencrypto/cryptodev.h> 37254219Scy 38254219Scy#include <assert.h> 39254219Scy#include <err.h> 40254219Scy#include <errno.h> 41254219Scy#include <fcntl.h> 42254219Scy#include <libgeom.h> 43254219Scy#include <paths.h> 44254219Scy#include <readpassphrase.h> 45254219Scy#include <stdbool.h> 46254219Scy#include <stdint.h> 47254219Scy#include <stdio.h> 48254219Scy#include <stdlib.h> 49254219Scy#include <string.h> 50254219Scy#include <strings.h> 51254219Scy#include <unistd.h> 52254219Scy 53254219Scy#include <geom/eli/g_eli.h> 54254219Scy#include <geom/eli/pkcs5v2.h> 55254219Scy 56254219Scy#include "core/geom.h" 57254219Scy#include "misc/subr.h" 58254219Scy 59254219Scy 60254219Scyuint32_t lib_version = G_LIB_VERSION; 61254219Scyuint32_t version = G_ELI_VERSION; 62254219Scy 63254219Scy#define GELI_BACKUP_DIR "/var/backups/" 64254219Scy#define GELI_ENC_ALGO "aes" 65254219Scy 66254219Scystatic void eli_main(struct gctl_req *req, unsigned flags); 67254219Scystatic void eli_init(struct gctl_req *req); 68254219Scystatic void eli_attach(struct gctl_req *req); 69254219Scystatic void eli_configure(struct gctl_req *req); 70254219Scystatic void eli_setkey(struct gctl_req *req); 71254219Scystatic void eli_delkey(struct gctl_req *req); 72254219Scystatic void eli_resume(struct gctl_req *req); 73254219Scystatic void eli_kill(struct gctl_req *req); 74254219Scystatic void eli_backup(struct gctl_req *req); 75254219Scystatic void eli_restore(struct gctl_req *req); 76254219Scystatic void eli_resize(struct gctl_req *req); 77254219Scystatic void eli_version(struct gctl_req *req); 78254219Scystatic void eli_clear(struct gctl_req *req); 79254219Scystatic void eli_dump(struct gctl_req *req); 80254219Scy 81254219Scystatic int eli_backup_create(struct gctl_req *req, const char *prov, 82254219Scy const char *file); 83254219Scy 84254219Scy/* 85254219Scy * Available commands: 86254219Scy * 87254219Scy * init [-bdgPTv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] [-s sectorsize] [-V version] prov 88254219Scy * label - alias for 'init' 89254219Scy * attach [-Cdprv] [-n keyno] [-j passfile] [-k keyfile] prov 90254219Scy * detach [-fl] prov ... 91254219Scy * stop - alias for 'detach' 92254219Scy * onetime [-d] [-a aalgo] [-e ealgo] [-l keylen] prov 93254219Scy * configure [-bBgGtT] prov ... 94254219Scy * setkey [-pPv] [-n keyno] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov 95254219Scy * delkey [-afv] [-n keyno] prov 96254219Scy * suspend [-v] -a | prov ... 97254219Scy * resume [-pv] [-j passfile] [-k keyfile] prov 98254219Scy * kill [-av] [prov ...] 99254219Scy * backup [-v] prov file 100254219Scy * restore [-fv] file prov 101254219Scy * resize [-v] -s oldsize prov 102254219Scy * version [prov ...] 103254219Scy * clear [-v] prov ... 104254219Scy * dump [-v] prov ... 105254219Scy */ 106254219Scystruct g_command class_commands[] = { 107254219Scy { "init", G_FLAG_VERBOSE, eli_main, 108254219Scy { 109254219Scy { 'a', "aalgo", "", G_TYPE_STRING }, 110254219Scy { 'b', "boot", NULL, G_TYPE_BOOL }, 111254219Scy { 'B', "backupfile", "", G_TYPE_STRING }, 112254219Scy { 'd', "displaypass", NULL, G_TYPE_BOOL }, 113254219Scy { 'e', "ealgo", "", G_TYPE_STRING }, 114254219Scy { 'g', "geliboot", NULL, G_TYPE_BOOL }, 115254219Scy { 'i', "iterations", "-1", G_TYPE_NUMBER }, 116254219Scy { 'J', "newpassfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 117254219Scy { 'K', "newkeyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 118254219Scy { 'l', "keylen", "0", G_TYPE_NUMBER }, 119254219Scy { 'P', "nonewpassphrase", NULL, G_TYPE_BOOL }, 120254219Scy { 's', "sectorsize", "0", G_TYPE_NUMBER }, 121254219Scy { 'T', "notrim", NULL, G_TYPE_BOOL }, 122254219Scy { 'V', "mdversion", "-1", G_TYPE_NUMBER }, 123254219Scy G_OPT_SENTINEL 124254219Scy }, 125254219Scy "[-bdgPTv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] [-s sectorsize] [-V version] prov" 126254219Scy }, 127254219Scy { "label", G_FLAG_VERBOSE, eli_main, 128254219Scy { 129254219Scy { 'a', "aalgo", "", G_TYPE_STRING }, 130254219Scy { 'b', "boot", NULL, G_TYPE_BOOL }, 131254219Scy { 'B', "backupfile", "", G_TYPE_STRING }, 132254219Scy { 'd', "displaypass", NULL, G_TYPE_BOOL }, 133254219Scy { 'e', "ealgo", "", G_TYPE_STRING }, 134254219Scy { 'g', "geliboot", NULL, G_TYPE_BOOL }, 135254219Scy { 'i', "iterations", "-1", G_TYPE_NUMBER }, 136254219Scy { 'J', "newpassfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 137254219Scy { 'K', "newkeyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 138254219Scy { 'l', "keylen", "0", G_TYPE_NUMBER }, 139254219Scy { 'P', "nonewpassphrase", NULL, G_TYPE_BOOL }, 140254219Scy { 's', "sectorsize", "0", G_TYPE_NUMBER }, 141254219Scy { 'V', "mdversion", "-1", G_TYPE_NUMBER }, 142254219Scy G_OPT_SENTINEL 143254219Scy }, 144254219Scy "- an alias for 'init'" 145254219Scy }, 146254219Scy { "attach", G_FLAG_VERBOSE | G_FLAG_LOADKLD, eli_main, 147254219Scy { 148254219Scy { 'C', "dryrun", NULL, G_TYPE_BOOL }, 149254219Scy { 'd', "detach", NULL, G_TYPE_BOOL }, 150254219Scy { 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 151254219Scy { 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 152254219Scy { 'n', "keyno", "-1", G_TYPE_NUMBER }, 153254219Scy { 'p', "nopassphrase", NULL, G_TYPE_BOOL }, 154254219Scy { 'r', "readonly", NULL, G_TYPE_BOOL }, 155254219Scy G_OPT_SENTINEL 156254219Scy }, 157254219Scy "[-Cdprv] [-n keyno] [-j passfile] [-k keyfile] prov" 158254219Scy }, 159254219Scy { "detach", 0, NULL, 160254219Scy { 161254219Scy { 'f', "force", NULL, G_TYPE_BOOL }, 162254219Scy { 'l', "last", NULL, G_TYPE_BOOL }, 163254219Scy G_OPT_SENTINEL 164254219Scy }, 165254219Scy "[-fl] prov ..." 166254219Scy }, 167254219Scy { "stop", 0, NULL, 168254219Scy { 169254219Scy { 'f', "force", NULL, G_TYPE_BOOL }, 170254219Scy { 'l', "last", NULL, G_TYPE_BOOL }, 171254219Scy G_OPT_SENTINEL 172254219Scy }, 173254219Scy "- an alias for 'detach'" 174254219Scy }, 175254219Scy { "onetime", G_FLAG_VERBOSE | G_FLAG_LOADKLD, NULL, 176254219Scy { 177254219Scy { 'a', "aalgo", "", G_TYPE_STRING }, 178254219Scy { 'd', "detach", NULL, G_TYPE_BOOL }, 179254219Scy { 'e', "ealgo", GELI_ENC_ALGO, G_TYPE_STRING }, 180254219Scy { 'l', "keylen", "0", G_TYPE_NUMBER }, 181254219Scy { 's', "sectorsize", "0", G_TYPE_NUMBER }, 182254219Scy { 'T', "notrim", NULL, G_TYPE_BOOL }, 183254219Scy G_OPT_SENTINEL 184254219Scy }, 185254219Scy "[-dT] [-a aalgo] [-e ealgo] [-l keylen] [-s sectorsize] prov" 186254219Scy }, 187254219Scy { "configure", G_FLAG_VERBOSE, eli_main, 188254219Scy { 189254219Scy { 'b', "boot", NULL, G_TYPE_BOOL }, 190254219Scy { 'B', "noboot", NULL, G_TYPE_BOOL }, 191254219Scy { 'd', "displaypass", NULL, G_TYPE_BOOL }, 192254219Scy { 'D', "nodisplaypass", NULL, G_TYPE_BOOL }, 193254219Scy { 'g', "geliboot", NULL, G_TYPE_BOOL }, 194254219Scy { 'G', "nogeliboot", NULL, G_TYPE_BOOL }, 195254219Scy { 't', "trim", NULL, G_TYPE_BOOL }, 196254219Scy { 'T', "notrim", NULL, G_TYPE_BOOL }, 197254219Scy G_OPT_SENTINEL 198254219Scy }, 199254219Scy "[-bBdDgGtT] prov ..." 200254219Scy }, 201254219Scy { "setkey", G_FLAG_VERBOSE, eli_main, 202254219Scy { 203254219Scy { 'i', "iterations", "-1", G_TYPE_NUMBER }, 204254219Scy { 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 205254219Scy { 'J', "newpassfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 206254219Scy { 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 207254219Scy { 'K', "newkeyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 208254219Scy { 'n', "keyno", "-1", G_TYPE_NUMBER }, 209254219Scy { 'p', "nopassphrase", NULL, G_TYPE_BOOL }, 210254219Scy { 'P', "nonewpassphrase", NULL, G_TYPE_BOOL }, 211254219Scy G_OPT_SENTINEL 212254219Scy }, 213254219Scy "[-pPv] [-n keyno] [-i iterations] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov" 214254219Scy }, 215254219Scy { "delkey", G_FLAG_VERBOSE, eli_main, 216254219Scy { 217254219Scy { 'a', "all", NULL, G_TYPE_BOOL }, 218254219Scy { 'f', "force", NULL, G_TYPE_BOOL }, 219254219Scy { 'n', "keyno", "-1", G_TYPE_NUMBER }, 220254219Scy G_OPT_SENTINEL 221254219Scy }, 222254219Scy "[-afv] [-n keyno] prov" 223254219Scy }, 224254219Scy { "suspend", G_FLAG_VERBOSE, NULL, 225254219Scy { 226254219Scy { 'a', "all", NULL, G_TYPE_BOOL }, 227254219Scy G_OPT_SENTINEL 228254219Scy }, 229254219Scy "[-v] -a | prov ..." 230254219Scy }, 231254219Scy { "resume", G_FLAG_VERBOSE, eli_main, 232254219Scy { 233254219Scy { 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 234254219Scy { 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 235254219Scy { 'p', "nopassphrase", NULL, G_TYPE_BOOL }, 236254219Scy G_OPT_SENTINEL 237254219Scy }, 238254219Scy "[-pv] [-j passfile] [-k keyfile] prov" 239254219Scy }, 240254219Scy { "kill", G_FLAG_VERBOSE, eli_main, 241254219Scy { 242254219Scy { 'a', "all", NULL, G_TYPE_BOOL }, 243254219Scy G_OPT_SENTINEL 244254219Scy }, 245254219Scy "[-av] [prov ...]" 246254219Scy }, 247254219Scy { "backup", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS, 248254219Scy "[-v] prov file" 249254219Scy }, 250254219Scy { "restore", G_FLAG_VERBOSE, eli_main, 251254219Scy { 252254219Scy { 'f', "force", NULL, G_TYPE_BOOL }, 253254219Scy G_OPT_SENTINEL 254254219Scy }, 255254219Scy "[-fv] file prov" 256254219Scy }, 257254219Scy { "resize", G_FLAG_VERBOSE, eli_main, 258254219Scy { 259254219Scy { 's', "oldsize", NULL, G_TYPE_NUMBER }, 260254219Scy G_OPT_SENTINEL 261254219Scy }, 262254219Scy "[-v] -s oldsize prov" 263254219Scy }, 264254219Scy { "version", G_FLAG_LOADKLD, eli_main, G_NULL_OPTS, 265254219Scy "[prov ...]" 266254219Scy }, 267254219Scy { "clear", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS, 268254219Scy "[-v] prov ..." 269254219Scy }, 270254219Scy { "dump", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS, 271254219Scy "[-v] prov ..." 272254219Scy }, 273254219Scy G_CMD_SENTINEL 274254219Scy}; 275254219Scy 276254219Scystatic int verbose = 0; 277254219Scy 278254219Scy#define BUFSIZE 1024 279254219Scy 280254219Scystatic int 281254219Scyeli_protect(struct gctl_req *req) 282254219Scy{ 283254219Scy struct rlimit rl; 284254219Scy 285254219Scy /* Disable core dumps. */ 286254219Scy rl.rlim_cur = 0; 287254219Scy rl.rlim_max = 0; 288254219Scy if (setrlimit(RLIMIT_CORE, &rl) == -1) { 289254219Scy gctl_error(req, "Cannot disable core dumps: %s.", 290254219Scy strerror(errno)); 291254219Scy return (-1); 292254219Scy } 293254219Scy /* Disable swapping. */ 294254219Scy if (mlockall(MCL_FUTURE) == -1) { 295254219Scy gctl_error(req, "Cannot lock memory: %s.", strerror(errno)); 296254219Scy return (-1); 297254219Scy } 298254219Scy return (0); 299254219Scy} 300254219Scy 301254219Scystatic void 302254219Scyeli_main(struct gctl_req *req, unsigned int flags) 303254219Scy{ 304254219Scy const char *name; 305254219Scy 306254219Scy if (eli_protect(req) == -1) 307254219Scy return; 308254219Scy 309254219Scy if ((flags & G_FLAG_VERBOSE) != 0) 310254219Scy verbose = 1; 311254219Scy 312254219Scy name = gctl_get_ascii(req, "verb"); 313254219Scy if (name == NULL) { 314254219Scy gctl_error(req, "No '%s' argument.", "verb"); 315254219Scy return; 316254219Scy } 317254219Scy if (strcmp(name, "init") == 0 || strcmp(name, "label") == 0) 318254219Scy eli_init(req); 319254219Scy else if (strcmp(name, "attach") == 0) 320254219Scy eli_attach(req); 321254219Scy else if (strcmp(name, "configure") == 0) 322254219Scy eli_configure(req); 323254219Scy else if (strcmp(name, "setkey") == 0) 324254219Scy eli_setkey(req); 325254219Scy else if (strcmp(name, "delkey") == 0) 326254219Scy eli_delkey(req); 327254219Scy else if (strcmp(name, "resume") == 0) 328254219Scy eli_resume(req); 329254219Scy else if (strcmp(name, "kill") == 0) 330254219Scy eli_kill(req); 331254219Scy else if (strcmp(name, "backup") == 0) 332254219Scy eli_backup(req); 333254219Scy else if (strcmp(name, "restore") == 0) 334254219Scy eli_restore(req); 335254219Scy else if (strcmp(name, "resize") == 0) 336254219Scy eli_resize(req); 337254219Scy else if (strcmp(name, "version") == 0) 338254219Scy eli_version(req); 339254219Scy else if (strcmp(name, "dump") == 0) 340254219Scy eli_dump(req); 341254219Scy else if (strcmp(name, "clear") == 0) 342254219Scy eli_clear(req); 343254219Scy else 344254219Scy gctl_error(req, "Unknown command: %s.", name); 345254219Scy} 346254219Scy 347254219Scystatic bool 348254219Scyeli_is_attached(const char *prov) 349254219Scy{ 350254219Scy char name[MAXPATHLEN]; 351254219Scy 352254219Scy /* 353254219Scy * Not the best way to do it, but the easiest. 354254219Scy * We try to open provider and check if it is a GEOM provider 355254219Scy * by asking about its sectorsize. 356254219Scy */ 357254219Scy snprintf(name, sizeof(name), "%s%s", prov, G_ELI_SUFFIX); 358254219Scy return (g_get_sectorsize(name) > 0); 359254219Scy} 360254219Scy 361254219Scystatic int 362254219Scyeli_genkey_files(struct gctl_req *req, bool new, const char *type, 363254219Scy struct hmac_ctx *ctxp, char *passbuf, size_t passbufsize) 364254219Scy{ 365254219Scy char *p, buf[BUFSIZE], argname[16]; 366254219Scy const char *file; 367254219Scy int error, fd, i; 368254219Scy ssize_t done; 369254219Scy 370254219Scy assert((strcmp(type, "keyfile") == 0 && ctxp != NULL && 371254219Scy passbuf == NULL && passbufsize == 0) || 372254219Scy (strcmp(type, "passfile") == 0 && ctxp == NULL && 373254219Scy passbuf != NULL && passbufsize > 0)); 374254219Scy assert(strcmp(type, "keyfile") == 0 || passbuf[0] == '\0'); 375254219Scy 376254219Scy for (i = 0; ; i++) { 377254219Scy snprintf(argname, sizeof(argname), "%s%s%d", 378254219Scy new ? "new" : "", type, i); 379254219Scy 380254219Scy /* No more {key,pass}files? */ 381254219Scy if (!gctl_has_param(req, argname)) 382254219Scy return (i); 383254219Scy 384254219Scy file = gctl_get_ascii(req, "%s", argname); 385254219Scy assert(file != NULL); 386254219Scy 387254219Scy if (strcmp(file, "-") == 0) 388254219Scy fd = STDIN_FILENO; 389254219Scy else { 390254219Scy fd = open(file, O_RDONLY); 391254219Scy if (fd == -1) { 392254219Scy gctl_error(req, "Cannot open %s %s: %s.", 393254219Scy type, file, strerror(errno)); 394254219Scy return (-1); 395254219Scy } 396254219Scy } 397254219Scy if (strcmp(type, "keyfile") == 0) { 398254219Scy while ((done = read(fd, buf, sizeof(buf))) > 0) 399254219Scy g_eli_crypto_hmac_update(ctxp, buf, done); 400254219Scy } else /* if (strcmp(type, "passfile") == 0) */ { 401254219Scy assert(strcmp(type, "passfile") == 0); 402254219Scy 403254219Scy while ((done = read(fd, buf, sizeof(buf) - 1)) > 0) { 404254219Scy buf[done] = '\0'; 405254219Scy p = strchr(buf, '\n'); 406254219Scy if (p != NULL) { 407254219Scy *p = '\0'; 408254219Scy done = p - buf; 409254219Scy } 410254219Scy if (strlcat(passbuf, buf, passbufsize) >= 411254219Scy passbufsize) { 412254219Scy gctl_error(req, 413254219Scy "Passphrase in %s too long.", file); 414254219Scy bzero(buf, sizeof(buf)); 415254219Scy return (-1); 416254219Scy } 417254219Scy if (p != NULL) 418254219Scy break; 419254219Scy } 420254219Scy } 421254219Scy error = errno; 422254219Scy if (strcmp(file, "-") != 0) 423254219Scy close(fd); 424254219Scy bzero(buf, sizeof(buf)); 425254219Scy if (done == -1) { 426254219Scy gctl_error(req, "Cannot read %s %s: %s.", 427254219Scy type, file, strerror(error)); 428254219Scy return (-1); 429254219Scy } 430254219Scy } 431254219Scy /* NOTREACHED */ 432254219Scy} 433254219Scy 434254219Scystatic int 435254219Scyeli_genkey_passphrase_prompt(struct gctl_req *req, bool new, char *passbuf, 436254219Scy size_t passbufsize) 437254219Scy{ 438254219Scy char *p; 439254219Scy 440254219Scy for (;;) { 441254219Scy p = readpassphrase( 442254219Scy new ? "Enter new passphrase: " : "Enter passphrase: ", 443254219Scy passbuf, passbufsize, RPP_ECHO_OFF | RPP_REQUIRE_TTY); 444254219Scy if (p == NULL) { 445254219Scy bzero(passbuf, passbufsize); 446254219Scy gctl_error(req, "Cannot read passphrase: %s.", 447254219Scy strerror(errno)); 448254219Scy return (-1); 449254219Scy } 450254219Scy 451254219Scy if (new) { 452254219Scy char tmpbuf[BUFSIZE]; 453254219Scy 454254219Scy p = readpassphrase("Reenter new passphrase: ", 455254219Scy tmpbuf, sizeof(tmpbuf), 456254219Scy RPP_ECHO_OFF | RPP_REQUIRE_TTY); 457254219Scy if (p == NULL) { 458254219Scy bzero(passbuf, passbufsize); 459254219Scy gctl_error(req, 460254219Scy "Cannot read passphrase: %s.", 461254219Scy strerror(errno)); 462254219Scy return (-1); 463254219Scy } 464254219Scy 465254219Scy if (strcmp(passbuf, tmpbuf) != 0) { 466254219Scy bzero(passbuf, passbufsize); 467254219Scy fprintf(stderr, "They didn't match.\n"); 468254219Scy continue; 469254219Scy } 470254219Scy bzero(tmpbuf, sizeof(tmpbuf)); 471254219Scy } 472254219Scy return (0); 473254219Scy } 474254219Scy /* NOTREACHED */ 475254219Scy} 476254219Scy 477254219Scystatic int 478254219Scyeli_genkey_passphrase(struct gctl_req *req, struct g_eli_metadata *md, bool new, 479254219Scy struct hmac_ctx *ctxp) 480254219Scy{ 481254219Scy char passbuf[BUFSIZE]; 482254219Scy bool nopassphrase; 483254219Scy int nfiles; 484254219Scy 485254219Scy nopassphrase = 486254219Scy gctl_get_int(req, new ? "nonewpassphrase" : "nopassphrase"); 487254219Scy if (nopassphrase) { 488254219Scy if (gctl_has_param(req, new ? "newpassfile0" : "passfile0")) { 489254219Scy gctl_error(req, 490254219Scy "Options -%c and -%c are mutually exclusive.", 491254219Scy new ? 'J' : 'j', new ? 'P' : 'p'); 492254219Scy return (-1); 493254219Scy } 494254219Scy return (0); 495254219Scy } 496254219Scy 497254219Scy if (!new && md->md_iterations == -1) { 498254219Scy gctl_error(req, "Missing -p flag."); 499254219Scy return (-1); 500254219Scy } 501254219Scy passbuf[0] = '\0'; 502254219Scy nfiles = eli_genkey_files(req, new, "passfile", NULL, passbuf, 503254219Scy sizeof(passbuf)); 504254219Scy if (nfiles == -1) 505254219Scy return (-1); 506254219Scy else if (nfiles == 0) { 507254219Scy if (eli_genkey_passphrase_prompt(req, new, passbuf, 508254219Scy sizeof(passbuf)) == -1) { 509254219Scy return (-1); 510254219Scy } 511254219Scy } 512254219Scy /* 513254219Scy * Field md_iterations equal to -1 means "choose some sane 514254219Scy * value for me". 515254219Scy */ 516254219Scy if (md->md_iterations == -1) { 517254219Scy assert(new); 518254219Scy if (verbose) 519254219Scy printf("Calculating number of iterations...\n"); 520254219Scy md->md_iterations = pkcs5v2_calculate(2000000); 521254219Scy assert(md->md_iterations > 0); 522254219Scy if (verbose) { 523254219Scy printf("Done, using %d iterations.\n", 524254219Scy md->md_iterations); 525254219Scy } 526254219Scy } 527254219Scy /* 528254219Scy * If md_iterations is equal to 0, user doesn't want PKCS#5v2. 529254219Scy */ 530254219Scy if (md->md_iterations == 0) { 531254219Scy g_eli_crypto_hmac_update(ctxp, md->md_salt, 532254219Scy sizeof(md->md_salt)); 533254219Scy g_eli_crypto_hmac_update(ctxp, passbuf, strlen(passbuf)); 534254219Scy } else /* if (md->md_iterations > 0) */ { 535254219Scy unsigned char dkey[G_ELI_USERKEYLEN]; 536254219Scy 537254219Scy pkcs5v2_genkey(dkey, sizeof(dkey), md->md_salt, 538254219Scy sizeof(md->md_salt), passbuf, md->md_iterations); 539254219Scy g_eli_crypto_hmac_update(ctxp, dkey, sizeof(dkey)); 540254219Scy bzero(dkey, sizeof(dkey)); 541254219Scy } 542254219Scy bzero(passbuf, sizeof(passbuf)); 543254219Scy 544254219Scy return (0); 545254219Scy} 546254219Scy 547254219Scystatic unsigned char * 548254219Scyeli_genkey(struct gctl_req *req, struct g_eli_metadata *md, unsigned char *key, 549254219Scy bool new) 550254219Scy{ 551254219Scy struct hmac_ctx ctx; 552254219Scy bool nopassphrase; 553254219Scy int nfiles; 554254219Scy 555254219Scy nopassphrase = 556254219Scy gctl_get_int(req, new ? "nonewpassphrase" : "nopassphrase"); 557254219Scy 558254219Scy g_eli_crypto_hmac_init(&ctx, NULL, 0); 559254219Scy 560254219Scy nfiles = eli_genkey_files(req, new, "keyfile", &ctx, NULL, 0); 561254219Scy if (nfiles == -1) 562254219Scy return (NULL); 563254219Scy else if (nfiles == 0 && nopassphrase) { 564254219Scy gctl_error(req, "No key components given."); 565254219Scy return (NULL); 566254219Scy } 567254219Scy 568254219Scy if (eli_genkey_passphrase(req, md, new, &ctx) == -1) 569254219Scy return (NULL); 570254219Scy 571254219Scy g_eli_crypto_hmac_final(&ctx, key, 0); 572254219Scy 573254219Scy return (key); 574254219Scy} 575254219Scy 576254219Scystatic int 577254219Scyeli_metadata_read(struct gctl_req *req, const char *prov, 578254219Scy struct g_eli_metadata *md) 579254219Scy{ 580254219Scy unsigned char sector[sizeof(struct g_eli_metadata)]; 581254219Scy int error; 582254219Scy 583254219Scy if (g_get_sectorsize(prov) == 0) { 584254219Scy int fd; 585254219Scy 586254219Scy /* This is a file probably. */ 587254219Scy fd = open(prov, O_RDONLY); 588254219Scy if (fd == -1) { 589254219Scy gctl_error(req, "Cannot open %s: %s.", prov, 590254219Scy strerror(errno)); 591254219Scy return (-1); 592254219Scy } 593254219Scy if (read(fd, sector, sizeof(sector)) != sizeof(sector)) { 594254219Scy gctl_error(req, "Cannot read metadata from %s: %s.", 595254219Scy prov, strerror(errno)); 596254219Scy close(fd); 597254219Scy return (-1); 598254219Scy } 599254219Scy close(fd); 600254219Scy } else { 601254219Scy /* This is a GEOM provider. */ 602254219Scy error = g_metadata_read(prov, sector, sizeof(sector), 603254219Scy G_ELI_MAGIC); 604254219Scy if (error != 0) { 605254219Scy gctl_error(req, "Cannot read metadata from %s: %s.", 606254219Scy prov, strerror(error)); 607254219Scy return (-1); 608254219Scy } 609254219Scy } 610254219Scy error = eli_metadata_decode(sector, md); 611254219Scy switch (error) { 612254219Scy case 0: 613254219Scy break; 614254219Scy case EOPNOTSUPP: 615254219Scy gctl_error(req, 616254219Scy "Provider's %s metadata version %u is too new.\n" 617254219Scy "geli: The highest supported version is %u.", 618254219Scy prov, (unsigned int)md->md_version, G_ELI_VERSION); 619254219Scy return (-1); 620254219Scy case EINVAL: 621254219Scy gctl_error(req, "Inconsistent provider's %s metadata.", prov); 622254219Scy return (-1); 623254219Scy default: 624254219Scy gctl_error(req, 625254219Scy "Unexpected error while decoding provider's %s metadata: %s.", 626254219Scy prov, strerror(error)); 627254219Scy return (-1); 628254219Scy } 629254219Scy return (0); 630254219Scy} 631254219Scy 632254219Scystatic int 633254219Scyeli_metadata_store(struct gctl_req *req, const char *prov, 634254219Scy struct g_eli_metadata *md) 635254219Scy{ 636254219Scy unsigned char sector[sizeof(struct g_eli_metadata)]; 637254219Scy int error; 638254219Scy 639254219Scy eli_metadata_encode(md, sector); 640254219Scy if (g_get_sectorsize(prov) == 0) { 641254219Scy int fd; 642254219Scy 643254219Scy /* This is a file probably. */ 644254219Scy fd = open(prov, O_WRONLY | O_TRUNC); 645254219Scy if (fd == -1) { 646254219Scy gctl_error(req, "Cannot open %s: %s.", prov, 647254219Scy strerror(errno)); 648254219Scy bzero(sector, sizeof(sector)); 649254219Scy return (-1); 650254219Scy } 651254219Scy if (write(fd, sector, sizeof(sector)) != sizeof(sector)) { 652254219Scy gctl_error(req, "Cannot write metadata to %s: %s.", 653254219Scy prov, strerror(errno)); 654254219Scy bzero(sector, sizeof(sector)); 655254219Scy close(fd); 656254219Scy return (-1); 657254219Scy } 658254219Scy close(fd); 659254219Scy } else { 660254219Scy /* This is a GEOM provider. */ 661254219Scy error = g_metadata_store(prov, sector, sizeof(sector)); 662254219Scy if (error != 0) { 663254219Scy gctl_error(req, "Cannot write metadata to %s: %s.", 664254219Scy prov, strerror(errno)); 665254219Scy bzero(sector, sizeof(sector)); 666254219Scy return (-1); 667254219Scy } 668254219Scy } 669254219Scy bzero(sector, sizeof(sector)); 670254219Scy return (0); 671254219Scy} 672254219Scy 673254219Scystatic void 674254219Scyeli_init(struct gctl_req *req) 675254219Scy{ 676254219Scy struct g_eli_metadata md; 677254219Scy unsigned char sector[sizeof(struct g_eli_metadata)] __aligned(4); 678254219Scy unsigned char key[G_ELI_USERKEYLEN]; 679254219Scy char backfile[MAXPATHLEN]; 680254219Scy const char *str, *prov; 681254219Scy unsigned int secsize, version; 682254219Scy off_t mediasize; 683254219Scy intmax_t val; 684254219Scy int error, nargs; 685254219Scy 686254219Scy nargs = gctl_get_int(req, "nargs"); 687254219Scy if (nargs != 1) { 688254219Scy gctl_error(req, "Invalid number of arguments."); 689254219Scy return; 690254219Scy } 691254219Scy prov = gctl_get_ascii(req, "arg0"); 692254219Scy mediasize = g_get_mediasize(prov); 693254219Scy secsize = g_get_sectorsize(prov); 694254219Scy if (mediasize == 0 || secsize == 0) { 695254219Scy gctl_error(req, "Cannot get informations about %s: %s.", prov, 696254219Scy strerror(errno)); 697254219Scy return; 698254219Scy } 699254219Scy 700254219Scy bzero(&md, sizeof(md)); 701254219Scy strlcpy(md.md_magic, G_ELI_MAGIC, sizeof(md.md_magic)); 702254219Scy val = gctl_get_intmax(req, "mdversion"); 703254219Scy if (val == -1) { 704254219Scy version = G_ELI_VERSION; 705254219Scy } else if (val < 0 || val > G_ELI_VERSION) { 706254219Scy gctl_error(req, 707254219Scy "Invalid version specified should be between %u and %u.", 708254219Scy G_ELI_VERSION_00, G_ELI_VERSION); 709254219Scy return; 710254219Scy } else { 711254219Scy version = val; 712254219Scy } 713254219Scy md.md_version = version; 714254219Scy md.md_flags = 0; 715254219Scy if (gctl_get_int(req, "boot")) 716254219Scy md.md_flags |= G_ELI_FLAG_BOOT; 717254219Scy if (gctl_get_int(req, "geliboot")) 718254219Scy md.md_flags |= G_ELI_FLAG_GELIBOOT; 719254219Scy if (gctl_get_int(req, "displaypass")) 720254219Scy md.md_flags |= G_ELI_FLAG_GELIDISPLAYPASS; 721254219Scy if (gctl_get_int(req, "notrim")) 722254219Scy md.md_flags |= G_ELI_FLAG_NODELETE; 723254219Scy md.md_ealgo = CRYPTO_ALGORITHM_MIN - 1; 724254219Scy str = gctl_get_ascii(req, "aalgo"); 725254219Scy if (*str != '\0') { 726254219Scy if (version < G_ELI_VERSION_01) { 727254219Scy gctl_error(req, 728254219Scy "Data authentication is supported starting from version %u.", 729254219Scy G_ELI_VERSION_01); 730254219Scy return; 731254219Scy } 732254219Scy md.md_aalgo = g_eli_str2aalgo(str); 733254219Scy if (md.md_aalgo >= CRYPTO_ALGORITHM_MIN && 734254219Scy md.md_aalgo <= CRYPTO_ALGORITHM_MAX) { 735254219Scy md.md_flags |= G_ELI_FLAG_AUTH; 736254219Scy } else { 737254219Scy /* 738254219Scy * For backward compatibility, check if the -a option 739254219Scy * was used to provide encryption algorithm. 740254219Scy */ 741254219Scy md.md_ealgo = g_eli_str2ealgo(str); 742254219Scy if (md.md_ealgo < CRYPTO_ALGORITHM_MIN || 743254219Scy md.md_ealgo > CRYPTO_ALGORITHM_MAX) { 744254219Scy gctl_error(req, 745254219Scy "Invalid authentication algorithm."); 746254219Scy return; 747254219Scy } else { 748254219Scy fprintf(stderr, "warning: The -e option, not " 749254219Scy "the -a option is now used to specify " 750254219Scy "encryption algorithm to use.\n"); 751254219Scy } 752254219Scy } 753254219Scy } 754254219Scy if (md.md_ealgo < CRYPTO_ALGORITHM_MIN || 755254219Scy md.md_ealgo > CRYPTO_ALGORITHM_MAX) { 756254219Scy str = gctl_get_ascii(req, "ealgo"); 757254219Scy if (*str == '\0') { 758254219Scy if (version < G_ELI_VERSION_05) 759254219Scy str = "aes-cbc"; 760254219Scy else 761254219Scy str = GELI_ENC_ALGO; 762254219Scy } 763254219Scy md.md_ealgo = g_eli_str2ealgo(str); 764254219Scy if (md.md_ealgo < CRYPTO_ALGORITHM_MIN || 765254219Scy md.md_ealgo > CRYPTO_ALGORITHM_MAX) { 766254219Scy gctl_error(req, "Invalid encryption algorithm."); 767254219Scy return; 768254219Scy } 769254219Scy if (md.md_ealgo == CRYPTO_CAMELLIA_CBC && 770254219Scy version < G_ELI_VERSION_04) { 771254219Scy gctl_error(req, 772254219Scy "Camellia-CBC algorithm is supported starting from version %u.", 773254219Scy G_ELI_VERSION_04); 774254219Scy return; 775254219Scy } 776254219Scy if (md.md_ealgo == CRYPTO_AES_XTS && 777254219Scy version < G_ELI_VERSION_05) { 778254219Scy gctl_error(req, 779254219Scy "AES-XTS algorithm is supported starting from version %u.", 780254219Scy G_ELI_VERSION_05); 781254219Scy return; 782254219Scy } 783254219Scy } 784254219Scy if (md.md_flags & G_ELI_FLAG_AUTH) { 785254219Scy switch (md.md_aalgo) { 786254219Scy case CRYPTO_MD5_HMAC: 787254219Scy gctl_error(req, 788254219Scy "The %s authentication algorithm is deprecated.", 789254219Scy g_eli_algo2str(md.md_aalgo)); 790254219Scy return; 791254219Scy } 792254219Scy } 793254219Scy switch (md.md_ealgo) { 794254219Scy case CRYPTO_3DES_CBC: 795254219Scy case CRYPTO_BLF_CBC: 796254219Scy gctl_error(req, "The %s encryption algorithm is deprecated.", 797254219Scy g_eli_algo2str(md.md_ealgo)); 798254219Scy return; 799254219Scy } 800254219Scy val = gctl_get_intmax(req, "keylen"); 801254219Scy md.md_keylen = val; 802254219Scy md.md_keylen = g_eli_keylen(md.md_ealgo, md.md_keylen); 803254219Scy if (md.md_keylen == 0) { 804254219Scy gctl_error(req, "Invalid key length."); 805254219Scy return; 806254219Scy } 807254219Scy md.md_provsize = mediasize; 808254219Scy 809254219Scy val = gctl_get_intmax(req, "iterations"); 810254219Scy if (val != -1) { 811254219Scy int nonewpassphrase; 812254219Scy 813254219Scy /* 814254219Scy * Don't allow to set iterations when there will be no 815254219Scy * passphrase. 816254219Scy */ 817254219Scy nonewpassphrase = gctl_get_int(req, "nonewpassphrase"); 818254219Scy if (nonewpassphrase) { 819254219Scy gctl_error(req, 820254219Scy "Options -i and -P are mutually exclusive."); 821254219Scy return; 822254219Scy } 823254219Scy } 824254219Scy md.md_iterations = val; 825254219Scy 826254219Scy val = gctl_get_intmax(req, "sectorsize"); 827254219Scy if (val == 0) 828254219Scy md.md_sectorsize = secsize; 829254219Scy else { 830254219Scy if (val < 0 || (val % secsize) != 0 || !powerof2(val)) { 831254219Scy gctl_error(req, "Invalid sector size."); 832254219Scy return; 833254219Scy } 834254219Scy if (val > sysconf(_SC_PAGE_SIZE)) { 835254219Scy fprintf(stderr, 836254219Scy "warning: Using sectorsize bigger than the page size!\n"); 837254219Scy } 838254219Scy md.md_sectorsize = val; 839254219Scy } 840254219Scy 841254219Scy md.md_keys = 0x01; 842254219Scy arc4random_buf(md.md_salt, sizeof(md.md_salt)); 843254219Scy arc4random_buf(md.md_mkeys, sizeof(md.md_mkeys)); 844254219Scy 845254219Scy /* Generate user key. */ 846254219Scy if (eli_genkey(req, &md, key, true) == NULL) { 847254219Scy bzero(key, sizeof(key)); 848254219Scy bzero(&md, sizeof(md)); 849254219Scy return; 850254219Scy } 851254219Scy 852254219Scy /* Encrypt the first and the only Master Key. */ 853254219Scy error = g_eli_mkey_encrypt(md.md_ealgo, key, md.md_keylen, md.md_mkeys); 854254219Scy bzero(key, sizeof(key)); 855254219Scy if (error != 0) { 856254219Scy bzero(&md, sizeof(md)); 857254219Scy gctl_error(req, "Cannot encrypt Master Key: %s.", 858254219Scy strerror(error)); 859254219Scy return; 860254219Scy } 861254219Scy 862254219Scy eli_metadata_encode(&md, sector); 863254219Scy bzero(&md, sizeof(md)); 864254219Scy error = g_metadata_store(prov, sector, sizeof(sector)); 865254219Scy bzero(sector, sizeof(sector)); 866254219Scy if (error != 0) { 867254219Scy gctl_error(req, "Cannot store metadata on %s: %s.", prov, 868254219Scy strerror(error)); 869254219Scy return; 870254219Scy } 871254219Scy if (verbose) 872254219Scy printf("Metadata value stored on %s.\n", prov); 873254219Scy /* Backup metadata to a file. */ 874254219Scy str = gctl_get_ascii(req, "backupfile"); 875254219Scy if (str[0] != '\0') { 876254219Scy /* Backupfile given be the user, just copy it. */ 877254219Scy strlcpy(backfile, str, sizeof(backfile)); 878254219Scy } else { 879254219Scy /* Generate file name automatically. */ 880254219Scy const char *p = prov; 881254219Scy unsigned int i; 882254219Scy 883254219Scy if (strncmp(p, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) 884254219Scy p += sizeof(_PATH_DEV) - 1; 885254219Scy snprintf(backfile, sizeof(backfile), "%s%s.eli", 886254219Scy GELI_BACKUP_DIR, p); 887254219Scy /* Replace all / with _. */ 888254219Scy for (i = strlen(GELI_BACKUP_DIR); backfile[i] != '\0'; i++) { 889254219Scy if (backfile[i] == '/') 890254219Scy backfile[i] = '_'; 891254219Scy } 892254219Scy } 893254219Scy if (strcmp(backfile, "none") != 0 && 894254219Scy eli_backup_create(req, prov, backfile) == 0) { 895254219Scy printf("\nMetadata backup can be found in %s and\n", backfile); 896254219Scy printf("can be restored with the following command:\n"); 897254219Scy printf("\n\t# geli restore %s %s\n\n", backfile, prov); 898254219Scy } 899254219Scy} 900254219Scy 901254219Scystatic void 902254219Scyeli_attach(struct gctl_req *req) 903254219Scy{ 904254219Scy struct g_eli_metadata md; 905254219Scy unsigned char key[G_ELI_USERKEYLEN]; 906254219Scy const char *prov; 907254219Scy off_t mediasize; 908254219Scy int nargs; 909254219Scy 910254219Scy nargs = gctl_get_int(req, "nargs"); 911254219Scy if (nargs != 1) { 912254219Scy gctl_error(req, "Invalid number of arguments."); 913254219Scy return; 914254219Scy } 915254219Scy prov = gctl_get_ascii(req, "arg0"); 916254219Scy 917254219Scy if (eli_metadata_read(req, prov, &md) == -1) 918254219Scy return; 919254219Scy 920254219Scy mediasize = g_get_mediasize(prov); 921254219Scy if (md.md_provsize != (uint64_t)mediasize) { 922254219Scy gctl_error(req, "Provider size mismatch."); 923254219Scy return; 924254219Scy } 925254219Scy 926254219Scy if (eli_genkey(req, &md, key, false) == NULL) { 927254219Scy bzero(key, sizeof(key)); 928254219Scy return; 929254219Scy } 930254219Scy 931254219Scy gctl_ro_param(req, "key", sizeof(key), key); 932254219Scy if (gctl_issue(req) == NULL) { 933254219Scy if (verbose) 934254219Scy printf("Attached to %s.\n", prov); 935254219Scy } 936254219Scy bzero(key, sizeof(key)); 937254219Scy} 938254219Scy 939254219Scystatic void 940254219Scyeli_configure_detached(struct gctl_req *req, const char *prov, int boot, 941254219Scy int geliboot, int displaypass, int trim) 942254219Scy{ 943254219Scy struct g_eli_metadata md; 944254219Scy bool changed = 0; 945254219Scy 946254219Scy if (eli_metadata_read(req, prov, &md) == -1) 947254219Scy return; 948254219Scy 949254219Scy if (boot == 1 && (md.md_flags & G_ELI_FLAG_BOOT)) { 950254219Scy if (verbose) 951254219Scy printf("BOOT flag already configured for %s.\n", prov); 952254219Scy } else if (boot == 0 && !(md.md_flags & G_ELI_FLAG_BOOT)) { 953254219Scy if (verbose) 954254219Scy printf("BOOT flag not configured for %s.\n", prov); 955254219Scy } else if (boot >= 0) { 956254219Scy if (boot) 957254219Scy md.md_flags |= G_ELI_FLAG_BOOT; 958254219Scy else 959254219Scy md.md_flags &= ~G_ELI_FLAG_BOOT; 960254219Scy changed = 1; 961254219Scy } 962254219Scy 963254219Scy if (geliboot == 1 && (md.md_flags & G_ELI_FLAG_GELIBOOT)) { 964254219Scy if (verbose) 965254219Scy printf("GELIBOOT flag already configured for %s.\n", prov); 966254219Scy } else if (geliboot == 0 && !(md.md_flags & G_ELI_FLAG_GELIBOOT)) { 967254219Scy if (verbose) 968254219Scy printf("GELIBOOT flag not configured for %s.\n", prov); 969254219Scy } else if (geliboot >= 0) { 970254219Scy if (geliboot) 971254219Scy md.md_flags |= G_ELI_FLAG_GELIBOOT; 972254219Scy else 973254219Scy md.md_flags &= ~G_ELI_FLAG_GELIBOOT; 974254219Scy changed = 1; 975254219Scy } 976254219Scy 977254219Scy if (displaypass == 1 && (md.md_flags & G_ELI_FLAG_GELIDISPLAYPASS)) { 978254219Scy if (verbose) 979254219Scy printf("GELIDISPLAYPASS flag already configured for %s.\n", prov); 980254219Scy } else if (displaypass == 0 && 981254219Scy !(md.md_flags & G_ELI_FLAG_GELIDISPLAYPASS)) { 982254219Scy if (verbose) 983254219Scy printf("GELIDISPLAYPASS flag not configured for %s.\n", prov); 984254219Scy } else if (displaypass >= 0) { 985254219Scy if (displaypass) 986254219Scy md.md_flags |= G_ELI_FLAG_GELIDISPLAYPASS; 987254219Scy else 988254219Scy md.md_flags &= ~G_ELI_FLAG_GELIDISPLAYPASS; 989254219Scy changed = 1; 990254219Scy } 991254219Scy 992254219Scy if (trim == 0 && (md.md_flags & G_ELI_FLAG_NODELETE)) { 993254219Scy if (verbose) 994254219Scy printf("TRIM disable flag already configured for %s.\n", prov); 995254219Scy } else if (trim == 1 && !(md.md_flags & G_ELI_FLAG_NODELETE)) { 996254219Scy if (verbose) 997254219Scy printf("TRIM disable flag not configured for %s.\n", prov); 998254219Scy } else if (trim >= 0) { 999254219Scy if (trim) 1000254219Scy md.md_flags &= ~G_ELI_FLAG_NODELETE; 1001254219Scy else 1002254219Scy md.md_flags |= G_ELI_FLAG_NODELETE; 1003254219Scy changed = 1; 1004254219Scy } 1005254219Scy 1006254219Scy if (changed) 1007254219Scy eli_metadata_store(req, prov, &md); 1008254219Scy bzero(&md, sizeof(md)); 1009254219Scy} 1010254219Scy 1011254219Scystatic void 1012254219Scyeli_configure(struct gctl_req *req) 1013254219Scy{ 1014254219Scy const char *prov; 1015254219Scy bool boot, noboot, geliboot, nogeliboot, displaypass, nodisplaypass; 1016254219Scy bool trim, notrim; 1017254219Scy int doboot, dogeliboot, dodisplaypass, dotrim; 1018254219Scy int i, nargs; 1019254219Scy 1020254219Scy nargs = gctl_get_int(req, "nargs"); 1021254219Scy if (nargs == 0) { 1022254219Scy gctl_error(req, "Too few arguments."); 1023254219Scy return; 1024254219Scy } 1025254219Scy 1026254219Scy boot = gctl_get_int(req, "boot"); 1027254219Scy noboot = gctl_get_int(req, "noboot"); 1028254219Scy geliboot = gctl_get_int(req, "geliboot"); 1029254219Scy nogeliboot = gctl_get_int(req, "nogeliboot"); 1030254219Scy displaypass = gctl_get_int(req, "displaypass"); 1031254219Scy nodisplaypass = gctl_get_int(req, "nodisplaypass"); 1032254219Scy trim = gctl_get_int(req, "trim"); 1033254219Scy notrim = gctl_get_int(req, "notrim"); 1034254219Scy 1035254219Scy doboot = -1; 1036254219Scy if (boot && noboot) { 1037254219Scy gctl_error(req, "Options -b and -B are mutually exclusive."); 1038254219Scy return; 1039254219Scy } 1040254219Scy if (boot) 1041254219Scy doboot = 1; 1042254219Scy else if (noboot) 1043254219Scy doboot = 0; 1044254219Scy 1045254219Scy dogeliboot = -1; 1046254219Scy if (geliboot && nogeliboot) { 1047254219Scy gctl_error(req, "Options -g and -G are mutually exclusive."); 1048254219Scy return; 1049254219Scy } 1050254219Scy if (geliboot) 1051254219Scy dogeliboot = 1; 1052254219Scy else if (nogeliboot) 1053254219Scy dogeliboot = 0; 1054254219Scy 1055254219Scy dodisplaypass = -1; 1056254219Scy if (displaypass && nodisplaypass) { 1057254219Scy gctl_error(req, "Options -d and -D are mutually exclusive."); 1058254219Scy return; 1059254219Scy } 1060254219Scy if (displaypass) 1061254219Scy dodisplaypass = 1; 1062254219Scy else if (nodisplaypass) 1063254219Scy dodisplaypass = 0; 1064254219Scy 1065254219Scy dotrim = -1; 1066254219Scy if (trim && notrim) { 1067254219Scy gctl_error(req, "Options -t and -T are mutually exclusive."); 1068254219Scy return; 1069254219Scy } 1070254219Scy if (trim) 1071254219Scy dotrim = 1; 1072254219Scy else if (notrim) 1073254219Scy dotrim = 0; 1074254219Scy 1075254219Scy if (doboot == -1 && dogeliboot == -1 && dodisplaypass == -1 && 1076254219Scy dotrim == -1) { 1077254219Scy gctl_error(req, "No option given."); 1078254219Scy return; 1079254219Scy } 1080254219Scy 1081254219Scy /* First attached providers. */ 1082254219Scy gctl_issue(req); 1083254219Scy /* Now the rest. */ 1084254219Scy for (i = 0; i < nargs; i++) { 1085254219Scy prov = gctl_get_ascii(req, "arg%d", i); 1086254219Scy if (!eli_is_attached(prov)) { 1087254219Scy eli_configure_detached(req, prov, doboot, dogeliboot, 1088254219Scy dodisplaypass, dotrim); 1089254219Scy } 1090254219Scy } 1091254219Scy} 1092254219Scy 1093254219Scystatic void 1094254219Scyeli_setkey_attached(struct gctl_req *req, struct g_eli_metadata *md) 1095254219Scy{ 1096254219Scy unsigned char key[G_ELI_USERKEYLEN]; 1097254219Scy intmax_t val, old = 0; 1098254219Scy int error; 1099254219Scy 1100254219Scy val = gctl_get_intmax(req, "iterations"); 1101254219Scy /* Check if iterations number should be changed. */ 1102254219Scy if (val != -1) 1103254219Scy md->md_iterations = val; 1104254219Scy else 1105254219Scy old = md->md_iterations; 1106254219Scy 1107254219Scy /* Generate key for Master Key encryption. */ 1108254219Scy if (eli_genkey(req, md, key, true) == NULL) { 1109254219Scy bzero(key, sizeof(key)); 1110254219Scy return; 1111254219Scy } 1112254219Scy /* 1113254219Scy * If number of iterations has changed, but wasn't given as a 1114254219Scy * command-line argument, update the request. 1115254219Scy */ 1116254219Scy if (val == -1 && md->md_iterations != old) { 1117254219Scy error = gctl_change_param(req, "iterations", sizeof(intmax_t), 1118254219Scy &md->md_iterations); 1119254219Scy assert(error == 0); 1120254219Scy } 1121254219Scy 1122254219Scy gctl_ro_param(req, "key", sizeof(key), key); 1123254219Scy gctl_issue(req); 1124254219Scy bzero(key, sizeof(key)); 1125254219Scy} 1126254219Scy 1127254219Scystatic void 1128254219Scyeli_setkey_detached(struct gctl_req *req, const char *prov, 1129254219Scy struct g_eli_metadata *md) 1130254219Scy{ 1131254219Scy unsigned char key[G_ELI_USERKEYLEN], mkey[G_ELI_DATAIVKEYLEN]; 1132254219Scy unsigned char *mkeydst; 1133254219Scy unsigned int nkey; 1134254219Scy intmax_t val; 1135254219Scy int error; 1136254219Scy 1137254219Scy if (md->md_keys == 0) { 1138254219Scy gctl_error(req, "No valid keys on %s.", prov); 1139254219Scy return; 1140254219Scy } 1141254219Scy 1142254219Scy /* Generate key for Master Key decryption. */ 1143254219Scy if (eli_genkey(req, md, key, false) == NULL) { 1144254219Scy bzero(key, sizeof(key)); 1145254219Scy return; 1146254219Scy } 1147254219Scy 1148254219Scy /* Decrypt Master Key. */ 1149254219Scy error = g_eli_mkey_decrypt_any(md, key, mkey, &nkey); 1150254219Scy bzero(key, sizeof(key)); 1151254219Scy if (error != 0) { 1152254219Scy bzero(md, sizeof(*md)); 1153254219Scy if (error == -1) 1154254219Scy gctl_error(req, "Wrong key for %s.", prov); 1155254219Scy else /* if (error > 0) */ { 1156254219Scy gctl_error(req, "Cannot decrypt Master Key: %s.", 1157254219Scy strerror(error)); 1158254219Scy } 1159254219Scy return; 1160254219Scy } 1161254219Scy if (verbose) 1162254219Scy printf("Decrypted Master Key %u.\n", nkey); 1163254219Scy 1164254219Scy val = gctl_get_intmax(req, "keyno"); 1165254219Scy if (val != -1) 1166254219Scy nkey = val; 1167254219Scy#if 0 1168254219Scy else 1169254219Scy ; /* Use the key number which was found during decryption. */ 1170254219Scy#endif 1171254219Scy if (nkey >= G_ELI_MAXMKEYS) { 1172254219Scy gctl_error(req, "Invalid '%s' argument.", "keyno"); 1173254219Scy return; 1174254219Scy } 1175254219Scy 1176254219Scy val = gctl_get_intmax(req, "iterations"); 1177254219Scy /* Check if iterations number should and can be changed. */ 1178254219Scy if (val != -1 && md->md_iterations == -1) { 1179254219Scy md->md_iterations = val; 1180254219Scy } else if (val != -1 && val != md->md_iterations) { 1181254219Scy if (bitcount32(md->md_keys) != 1) { 1182254219Scy gctl_error(req, "To be able to use '-i' option, only " 1183254219Scy "one key can be defined."); 1184254219Scy return; 1185254219Scy } 1186254219Scy if (md->md_keys != (1 << nkey)) { 1187254219Scy gctl_error(req, "Only already defined key can be " 1188254219Scy "changed when '-i' option is used."); 1189254219Scy return; 1190254219Scy } 1191254219Scy md->md_iterations = val; 1192254219Scy } 1193254219Scy 1194254219Scy mkeydst = md->md_mkeys + nkey * G_ELI_MKEYLEN; 1195254219Scy md->md_keys |= (1 << nkey); 1196254219Scy 1197254219Scy bcopy(mkey, mkeydst, sizeof(mkey)); 1198254219Scy bzero(mkey, sizeof(mkey)); 1199254219Scy 1200254219Scy /* Generate key for Master Key encryption. */ 1201254219Scy if (eli_genkey(req, md, key, true) == NULL) { 1202254219Scy bzero(key, sizeof(key)); 1203254219Scy bzero(md, sizeof(*md)); 1204254219Scy return; 1205254219Scy } 1206254219Scy 1207254219Scy /* Encrypt the Master-Key with the new key. */ 1208254219Scy error = g_eli_mkey_encrypt(md->md_ealgo, key, md->md_keylen, mkeydst); 1209254219Scy bzero(key, sizeof(key)); 1210254219Scy if (error != 0) { 1211254219Scy bzero(md, sizeof(*md)); 1212254219Scy gctl_error(req, "Cannot encrypt Master Key: %s.", 1213254219Scy strerror(error)); 1214254219Scy return; 1215254219Scy } 1216254219Scy 1217254219Scy /* Store metadata with fresh key. */ 1218254219Scy eli_metadata_store(req, prov, md); 1219254219Scy bzero(md, sizeof(*md)); 1220254219Scy} 1221254219Scy 1222254219Scystatic void 1223254219Scyeli_setkey(struct gctl_req *req) 1224254219Scy{ 1225254219Scy struct g_eli_metadata md; 1226254219Scy const char *prov; 1227254219Scy int nargs; 1228254219Scy 1229254219Scy nargs = gctl_get_int(req, "nargs"); 1230254219Scy if (nargs != 1) { 1231254219Scy gctl_error(req, "Invalid number of arguments."); 1232254219Scy return; 1233254219Scy } 1234254219Scy prov = gctl_get_ascii(req, "arg0"); 1235254219Scy 1236254219Scy if (eli_metadata_read(req, prov, &md) == -1) 1237254219Scy return; 1238254219Scy 1239254219Scy if (eli_is_attached(prov)) 1240254219Scy eli_setkey_attached(req, &md); 1241254219Scy else 1242254219Scy eli_setkey_detached(req, prov, &md); 1243254219Scy 1244254219Scy if (req->error == NULL || req->error[0] == '\0') { 1245254219Scy printf("Note, that the master key encrypted with old keys " 1246254219Scy "and/or passphrase may still exists in a metadata backup " 1247254219Scy "file.\n"); 1248254219Scy } 1249254219Scy} 1250254219Scy 1251254219Scystatic void 1252254219Scyeli_delkey_attached(struct gctl_req *req, const char *prov __unused) 1253254219Scy{ 1254254219Scy 1255254219Scy gctl_issue(req); 1256254219Scy} 1257254219Scy 1258254219Scystatic void 1259254219Scyeli_delkey_detached(struct gctl_req *req, const char *prov) 1260254219Scy{ 1261254219Scy struct g_eli_metadata md; 1262254219Scy unsigned char *mkeydst; 1263254219Scy unsigned int nkey; 1264254219Scy intmax_t val; 1265254219Scy bool all, force; 1266254219Scy 1267254219Scy if (eli_metadata_read(req, prov, &md) == -1) 1268254219Scy return; 1269254219Scy 1270254219Scy all = gctl_get_int(req, "all"); 1271254219Scy if (all) 1272254219Scy arc4random_buf(md.md_mkeys, sizeof(md.md_mkeys)); 1273254219Scy else { 1274254219Scy force = gctl_get_int(req, "force"); 1275254219Scy val = gctl_get_intmax(req, "keyno"); 1276254219Scy if (val == -1) { 1277254219Scy gctl_error(req, "Key number has to be specified."); 1278254219Scy return; 1279254219Scy } 1280254219Scy nkey = val; 1281254219Scy if (nkey >= G_ELI_MAXMKEYS) { 1282254219Scy gctl_error(req, "Invalid '%s' argument.", "keyno"); 1283254219Scy return; 1284254219Scy } 1285254219Scy if (!(md.md_keys & (1 << nkey)) && !force) { 1286254219Scy gctl_error(req, "Master Key %u is not set.", nkey); 1287254219Scy return; 1288254219Scy } 1289254219Scy md.md_keys &= ~(1 << nkey); 1290254219Scy if (md.md_keys == 0 && !force) { 1291254219Scy gctl_error(req, "This is the last Master Key. Use '-f' " 1292254219Scy "option if you really want to remove it."); 1293254219Scy return; 1294254219Scy } 1295254219Scy mkeydst = md.md_mkeys + nkey * G_ELI_MKEYLEN; 1296254219Scy arc4random_buf(mkeydst, G_ELI_MKEYLEN); 1297254219Scy } 1298254219Scy 1299254219Scy eli_metadata_store(req, prov, &md); 1300254219Scy bzero(&md, sizeof(md)); 1301254219Scy} 1302254219Scy 1303254219Scystatic void 1304254219Scyeli_delkey(struct gctl_req *req) 1305254219Scy{ 1306254219Scy const char *prov; 1307254219Scy int nargs; 1308254219Scy 1309254219Scy nargs = gctl_get_int(req, "nargs"); 1310254219Scy if (nargs != 1) { 1311254219Scy gctl_error(req, "Invalid number of arguments."); 1312254219Scy return; 1313254219Scy } 1314254219Scy prov = gctl_get_ascii(req, "arg0"); 1315254219Scy 1316254219Scy if (eli_is_attached(prov)) 1317254219Scy eli_delkey_attached(req, prov); 1318254219Scy else 1319254219Scy eli_delkey_detached(req, prov); 1320254219Scy} 1321254219Scy 1322254219Scystatic void 1323254219Scyeli_resume(struct gctl_req *req) 1324254219Scy{ 1325254219Scy struct g_eli_metadata md; 1326254219Scy unsigned char key[G_ELI_USERKEYLEN]; 1327254219Scy const char *prov; 1328254219Scy off_t mediasize; 1329254219Scy int nargs; 1330254219Scy 1331254219Scy nargs = gctl_get_int(req, "nargs"); 1332254219Scy if (nargs != 1) { 1333254219Scy gctl_error(req, "Invalid number of arguments."); 1334254219Scy return; 1335254219Scy } 1336254219Scy prov = gctl_get_ascii(req, "arg0"); 1337254219Scy 1338254219Scy if (eli_metadata_read(req, prov, &md) == -1) 1339254219Scy return; 1340254219Scy 1341254219Scy mediasize = g_get_mediasize(prov); 1342254219Scy if (md.md_provsize != (uint64_t)mediasize) { 1343254219Scy gctl_error(req, "Provider size mismatch."); 1344254219Scy return; 1345254219Scy } 1346254219Scy 1347254219Scy if (eli_genkey(req, &md, key, false) == NULL) { 1348254219Scy bzero(key, sizeof(key)); 1349254219Scy return; 1350254219Scy } 1351254219Scy 1352254219Scy gctl_ro_param(req, "key", sizeof(key), key); 1353254219Scy if (gctl_issue(req) == NULL) { 1354254219Scy if (verbose) 1355254219Scy printf("Resumed %s.\n", prov); 1356254219Scy } 1357254219Scy bzero(key, sizeof(key)); 1358254219Scy} 1359254219Scy 1360254219Scystatic int 1361254219Scyeli_trash_metadata(struct gctl_req *req, const char *prov, int fd, off_t offset) 1362254219Scy{ 1363254219Scy unsigned int overwrites; 1364254219Scy unsigned char *sector; 1365254219Scy ssize_t size; 1366254219Scy int error; 1367254219Scy 1368254219Scy size = sizeof(overwrites); 1369254219Scy if (sysctlbyname("kern.geom.eli.overwrites", &overwrites, &size, 1370254219Scy NULL, 0) == -1 || overwrites == 0) { 1371254219Scy overwrites = G_ELI_OVERWRITES; 1372254219Scy } 1373254219Scy 1374254219Scy size = g_sectorsize(fd); 1375254219Scy if (size <= 0) { 1376254219Scy gctl_error(req, "Cannot obtain provider sector size %s: %s.", 1377254219Scy prov, strerror(errno)); 1378254219Scy return (-1); 1379254219Scy } 1380254219Scy sector = malloc(size); 1381254219Scy if (sector == NULL) { 1382254219Scy gctl_error(req, "Cannot allocate %zd bytes of memory.", size); 1383254219Scy return (-1); 1384254219Scy } 1385254219Scy 1386254219Scy error = 0; 1387254219Scy do { 1388254219Scy arc4random_buf(sector, size); 1389254219Scy if (pwrite(fd, sector, size, offset) != size) { 1390254219Scy if (error == 0) 1391254219Scy error = errno; 1392254219Scy } 1393254219Scy (void)g_flush(fd); 1394254219Scy } while (--overwrites > 0); 1395254219Scy free(sector); 1396254219Scy if (error != 0) { 1397254219Scy gctl_error(req, "Cannot trash metadata on provider %s: %s.", 1398254219Scy prov, strerror(error)); 1399254219Scy return (-1); 1400254219Scy } 1401254219Scy return (0); 1402254219Scy} 1403254219Scy 1404254219Scystatic void 1405254219Scyeli_kill_detached(struct gctl_req *req, const char *prov) 1406254219Scy{ 1407254219Scy off_t offset; 1408254219Scy int fd; 1409254219Scy 1410254219Scy /* 1411254219Scy * NOTE: Maybe we should verify if this is geli provider first, 1412254219Scy * but 'kill' command is quite critical so better don't waste 1413254219Scy * the time. 1414254219Scy */ 1415254219Scy#if 0 1416254219Scy error = g_metadata_read(prov, (unsigned char *)&md, sizeof(md), 1417254219Scy G_ELI_MAGIC); 1418254219Scy if (error != 0) { 1419254219Scy gctl_error(req, "Cannot read metadata from %s: %s.", prov, 1420254219Scy strerror(error)); 1421254219Scy return; 1422254219Scy } 1423254219Scy#endif 1424254219Scy 1425254219Scy fd = g_open(prov, 1); 1426254219Scy if (fd == -1) { 1427254219Scy gctl_error(req, "Cannot open provider %s: %s.", prov, 1428254219Scy strerror(errno)); 1429254219Scy return; 1430254219Scy } 1431254219Scy offset = g_mediasize(fd) - g_sectorsize(fd); 1432254219Scy if (offset <= 0) { 1433254219Scy gctl_error(req, 1434254219Scy "Cannot obtain media size or sector size for provider %s: %s.", 1435254219Scy prov, strerror(errno)); 1436254219Scy (void)g_close(fd); 1437254219Scy return; 1438254219Scy } 1439254219Scy (void)eli_trash_metadata(req, prov, fd, offset); 1440254219Scy (void)g_close(fd); 1441254219Scy} 1442254219Scy 1443254219Scystatic void 1444254219Scyeli_kill(struct gctl_req *req) 1445254219Scy{ 1446254219Scy const char *prov; 1447254219Scy int i, nargs, all; 1448254219Scy 1449254219Scy nargs = gctl_get_int(req, "nargs"); 1450254219Scy all = gctl_get_int(req, "all"); 1451254219Scy if (!all && nargs == 0) { 1452254219Scy gctl_error(req, "Too few arguments."); 1453254219Scy return; 1454254219Scy } 1455254219Scy /* 1456254219Scy * How '-a' option combine with a list of providers: 1457254219Scy * Delete Master Keys from all attached providers: 1458254219Scy * geli kill -a 1459254219Scy * Delete Master Keys from all attached providers and from 1460254219Scy * detached da0 and da1: 1461254219Scy * geli kill -a da0 da1 1462254219Scy * Delete Master Keys from (attached or detached) da0 and da1: 1463254219Scy * geli kill da0 da1 1464254219Scy */ 1465254219Scy 1466254219Scy /* First detached providers. */ 1467254219Scy for (i = 0; i < nargs; i++) { 1468254219Scy prov = gctl_get_ascii(req, "arg%d", i); 1469254219Scy if (!eli_is_attached(prov)) 1470254219Scy eli_kill_detached(req, prov); 1471254219Scy } 1472254219Scy /* Now attached providers. */ 1473254219Scy gctl_issue(req); 1474254219Scy} 1475254219Scy 1476254219Scystatic int 1477254219Scyeli_backup_create(struct gctl_req *req, const char *prov, const char *file) 1478254219Scy{ 1479254219Scy unsigned char *sector; 1480254219Scy ssize_t secsize; 1481254219Scy int error, filefd, ret; 1482254219Scy 1483254219Scy ret = -1; 1484254219Scy filefd = -1; 1485254219Scy sector = NULL; 1486254219Scy secsize = 0; 1487254219Scy 1488254219Scy secsize = g_get_sectorsize(prov); 1489254219Scy if (secsize == 0) { 1490254219Scy gctl_error(req, "Cannot get informations about %s: %s.", prov, 1491254219Scy strerror(errno)); 1492254219Scy goto out; 1493254219Scy } 1494254219Scy sector = malloc(secsize); 1495254219Scy if (sector == NULL) { 1496254219Scy gctl_error(req, "Cannot allocate memory."); 1497254219Scy goto out; 1498254219Scy } 1499254219Scy /* Read metadata from the provider. */ 1500254219Scy error = g_metadata_read(prov, sector, secsize, G_ELI_MAGIC); 1501254219Scy if (error != 0) { 1502254219Scy gctl_error(req, "Unable to read metadata from %s: %s.", prov, 1503254219Scy strerror(error)); 1504254219Scy goto out; 1505254219Scy } 1506254219Scy 1507254219Scy filefd = open(file, O_WRONLY | O_TRUNC | O_CREAT, 0600); 1508254219Scy if (filefd == -1) { 1509254219Scy gctl_error(req, "Unable to open %s: %s.", file, 1510254219Scy strerror(errno)); 1511254219Scy goto out; 1512254219Scy } 1513254219Scy /* Write metadata to the destination file. */ 1514254219Scy if (write(filefd, sector, secsize) != secsize) { 1515254219Scy gctl_error(req, "Unable to write to %s: %s.", file, 1516254219Scy strerror(errno)); 1517254219Scy (void)close(filefd); 1518254219Scy (void)unlink(file); 1519254219Scy goto out; 1520254219Scy } 1521254219Scy (void)fsync(filefd); 1522254219Scy (void)close(filefd); 1523254219Scy /* Success. */ 1524254219Scy ret = 0; 1525254219Scyout: 1526254219Scy if (sector != NULL) { 1527254219Scy bzero(sector, secsize); 1528254219Scy free(sector); 1529 } 1530 return (ret); 1531} 1532 1533static void 1534eli_backup(struct gctl_req *req) 1535{ 1536 const char *file, *prov; 1537 int nargs; 1538 1539 nargs = gctl_get_int(req, "nargs"); 1540 if (nargs != 2) { 1541 gctl_error(req, "Invalid number of arguments."); 1542 return; 1543 } 1544 prov = gctl_get_ascii(req, "arg0"); 1545 file = gctl_get_ascii(req, "arg1"); 1546 1547 eli_backup_create(req, prov, file); 1548} 1549 1550static void 1551eli_restore(struct gctl_req *req) 1552{ 1553 struct g_eli_metadata md; 1554 const char *file, *prov; 1555 off_t mediasize; 1556 int nargs; 1557 1558 nargs = gctl_get_int(req, "nargs"); 1559 if (nargs != 2) { 1560 gctl_error(req, "Invalid number of arguments."); 1561 return; 1562 } 1563 file = gctl_get_ascii(req, "arg0"); 1564 prov = gctl_get_ascii(req, "arg1"); 1565 1566 /* Read metadata from the backup file. */ 1567 if (eli_metadata_read(req, file, &md) == -1) 1568 return; 1569 /* Obtain provider's mediasize. */ 1570 mediasize = g_get_mediasize(prov); 1571 if (mediasize == 0) { 1572 gctl_error(req, "Cannot get informations about %s: %s.", prov, 1573 strerror(errno)); 1574 return; 1575 } 1576 /* Check if the provider size has changed since we did the backup. */ 1577 if (md.md_provsize != (uint64_t)mediasize) { 1578 if (gctl_get_int(req, "force")) { 1579 md.md_provsize = mediasize; 1580 } else { 1581 gctl_error(req, "Provider size mismatch: " 1582 "wrong backup file?"); 1583 return; 1584 } 1585 } 1586 /* Write metadata to the provider. */ 1587 (void)eli_metadata_store(req, prov, &md); 1588} 1589 1590static void 1591eli_resize(struct gctl_req *req) 1592{ 1593 struct g_eli_metadata md; 1594 const char *prov; 1595 unsigned char *sector; 1596 ssize_t secsize; 1597 off_t mediasize, oldsize; 1598 int error, nargs, provfd; 1599 1600 nargs = gctl_get_int(req, "nargs"); 1601 if (nargs != 1) { 1602 gctl_error(req, "Invalid number of arguments."); 1603 return; 1604 } 1605 prov = gctl_get_ascii(req, "arg0"); 1606 1607 provfd = -1; 1608 sector = NULL; 1609 secsize = 0; 1610 1611 provfd = g_open(prov, 1); 1612 if (provfd == -1) { 1613 gctl_error(req, "Cannot open %s: %s.", prov, strerror(errno)); 1614 goto out; 1615 } 1616 1617 mediasize = g_mediasize(provfd); 1618 secsize = g_sectorsize(provfd); 1619 if (mediasize == -1 || secsize == -1) { 1620 gctl_error(req, "Cannot get information about %s: %s.", prov, 1621 strerror(errno)); 1622 goto out; 1623 } 1624 1625 sector = malloc(secsize); 1626 if (sector == NULL) { 1627 gctl_error(req, "Cannot allocate memory."); 1628 goto out; 1629 } 1630 1631 oldsize = gctl_get_intmax(req, "oldsize"); 1632 if (oldsize < 0 || oldsize > mediasize) { 1633 gctl_error(req, "Invalid oldsize: Out of range."); 1634 goto out; 1635 } 1636 if (oldsize == mediasize) { 1637 gctl_error(req, "Size hasn't changed."); 1638 goto out; 1639 } 1640 1641 /* Read metadata from the 'oldsize' offset. */ 1642 if (pread(provfd, sector, secsize, oldsize - secsize) != secsize) { 1643 gctl_error(req, "Cannot read old metadata: %s.", 1644 strerror(errno)); 1645 goto out; 1646 } 1647 1648 /* Check if this sector contains geli metadata. */ 1649 error = eli_metadata_decode(sector, &md); 1650 switch (error) { 1651 case 0: 1652 break; 1653 case EOPNOTSUPP: 1654 gctl_error(req, 1655 "Provider's %s metadata version %u is too new.\n" 1656 "geli: The highest supported version is %u.", 1657 prov, (unsigned int)md.md_version, G_ELI_VERSION); 1658 goto out; 1659 case EINVAL: 1660 gctl_error(req, "Inconsistent provider's %s metadata.", prov); 1661 goto out; 1662 default: 1663 gctl_error(req, 1664 "Unexpected error while decoding provider's %s metadata: %s.", 1665 prov, strerror(error)); 1666 goto out; 1667 } 1668 1669 /* 1670 * If the old metadata doesn't have a correct provider size, refuse 1671 * to resize. 1672 */ 1673 if (md.md_provsize != (uint64_t)oldsize) { 1674 gctl_error(req, "Provider size mismatch at oldsize."); 1675 goto out; 1676 } 1677 1678 /* 1679 * Update the old metadata with the current provider size and write 1680 * it back to the correct place on the provider. 1681 */ 1682 md.md_provsize = mediasize; 1683 /* Write metadata to the provider. */ 1684 (void)eli_metadata_store(req, prov, &md); 1685 /* Now trash the old metadata. */ 1686 (void)eli_trash_metadata(req, prov, provfd, oldsize - secsize); 1687out: 1688 if (provfd != -1) 1689 (void)g_close(provfd); 1690 if (sector != NULL) { 1691 bzero(sector, secsize); 1692 free(sector); 1693 } 1694} 1695 1696static void 1697eli_version(struct gctl_req *req) 1698{ 1699 struct g_eli_metadata md; 1700 const char *name; 1701 unsigned int version; 1702 int error, i, nargs; 1703 1704 nargs = gctl_get_int(req, "nargs"); 1705 1706 if (nargs == 0) { 1707 unsigned int kernver; 1708 ssize_t size; 1709 1710 size = sizeof(kernver); 1711 if (sysctlbyname("kern.geom.eli.version", &kernver, &size, 1712 NULL, 0) == -1) { 1713 warn("Unable to obtain GELI kernel version"); 1714 } else { 1715 printf("kernel: %u\n", kernver); 1716 } 1717 printf("userland: %u\n", G_ELI_VERSION); 1718 return; 1719 } 1720 1721 for (i = 0; i < nargs; i++) { 1722 name = gctl_get_ascii(req, "arg%d", i); 1723 error = g_metadata_read(name, (unsigned char *)&md, 1724 sizeof(md), G_ELI_MAGIC); 1725 if (error != 0) { 1726 warn("%s: Unable to read metadata: %s.", name, 1727 strerror(error)); 1728 gctl_error(req, "Not fully done."); 1729 continue; 1730 } 1731 version = le32dec(&md.md_version); 1732 printf("%s: %u\n", name, version); 1733 } 1734} 1735 1736static void 1737eli_clear(struct gctl_req *req) 1738{ 1739 const char *name; 1740 int error, i, nargs; 1741 1742 nargs = gctl_get_int(req, "nargs"); 1743 if (nargs < 1) { 1744 gctl_error(req, "Too few arguments."); 1745 return; 1746 } 1747 1748 for (i = 0; i < nargs; i++) { 1749 name = gctl_get_ascii(req, "arg%d", i); 1750 error = g_metadata_clear(name, G_ELI_MAGIC); 1751 if (error != 0) { 1752 fprintf(stderr, "Cannot clear metadata on %s: %s.\n", 1753 name, strerror(error)); 1754 gctl_error(req, "Not fully done."); 1755 continue; 1756 } 1757 if (verbose) 1758 printf("Metadata cleared on %s.\n", name); 1759 } 1760} 1761 1762static void 1763eli_dump(struct gctl_req *req) 1764{ 1765 struct g_eli_metadata md; 1766 const char *name; 1767 int i, nargs; 1768 1769 nargs = gctl_get_int(req, "nargs"); 1770 if (nargs < 1) { 1771 gctl_error(req, "Too few arguments."); 1772 return; 1773 } 1774 1775 for (i = 0; i < nargs; i++) { 1776 name = gctl_get_ascii(req, "arg%d", i); 1777 if (eli_metadata_read(NULL, name, &md) == -1) { 1778 gctl_error(req, "Not fully done."); 1779 continue; 1780 } 1781 printf("Metadata on %s:\n", name); 1782 eli_metadata_dump(&md); 1783 printf("\n"); 1784 } 1785} 1786