1148456Spjd/*- 2330449Seadler * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3330449Seadler * 4213073Spjd * Copyright (c) 2004-2010 Pawel Jakub Dawidek <pjd@FreeBSD.org> 5148456Spjd * All rights reserved. 6148456Spjd * 7148456Spjd * Redistribution and use in source and binary forms, with or without 8148456Spjd * modification, are permitted provided that the following conditions 9148456Spjd * are met: 10148456Spjd * 1. Redistributions of source code must retain the above copyright 11148456Spjd * notice, this list of conditions and the following disclaimer. 12148456Spjd * 2. Redistributions in binary form must reproduce the above copyright 13148456Spjd * notice, this list of conditions and the following disclaimer in the 14148456Spjd * documentation and/or other materials provided with the distribution. 15155175Spjd * 16148456Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 17148456Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18148456Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19148456Spjd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 20148456Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21148456Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22148456Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23148456Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24148456Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25148456Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26148456Spjd * SUCH DAMAGE. 27148456Spjd */ 28148456Spjd 29148456Spjd#include <sys/cdefs.h> 30148456Spjd__FBSDID("$FreeBSD: stable/11/sbin/geom/class/eli/geom_eli.c 348588 2019-06-03 21:04:23Z jhb $"); 31148456Spjd 32226715Spjd#include <sys/param.h> 33226715Spjd#include <sys/mman.h> 34213060Spjd#include <sys/sysctl.h> 35226715Spjd#include <sys/resource.h> 36226715Spjd#include <opencrypto/cryptodev.h> 37213060Spjd 38226715Spjd#include <assert.h> 39226715Spjd#include <err.h> 40226715Spjd#include <errno.h> 41226715Spjd#include <fcntl.h> 42226715Spjd#include <libgeom.h> 43226715Spjd#include <paths.h> 44226715Spjd#include <readpassphrase.h> 45213172Spjd#include <stdbool.h> 46226715Spjd#include <stdint.h> 47148456Spjd#include <stdio.h> 48148456Spjd#include <stdlib.h> 49148456Spjd#include <string.h> 50148456Spjd#include <strings.h> 51226715Spjd#include <unistd.h> 52148456Spjd 53148456Spjd#include <geom/eli/g_eli.h> 54148456Spjd#include <geom/eli/pkcs5v2.h> 55148456Spjd 56148456Spjd#include "core/geom.h" 57148456Spjd#include "misc/subr.h" 58148456Spjd 59148456Spjd 60148456Spjduint32_t lib_version = G_LIB_VERSION; 61148456Spjduint32_t version = G_ELI_VERSION; 62148456Spjd 63182452Spjd#define GELI_BACKUP_DIR "/var/backups/" 64212547Spjd#define GELI_ENC_ALGO "aes" 65182452Spjd 66148456Spjdstatic void eli_main(struct gctl_req *req, unsigned flags); 67148456Spjdstatic void eli_init(struct gctl_req *req); 68148456Spjdstatic void eli_attach(struct gctl_req *req); 69162353Spjdstatic void eli_configure(struct gctl_req *req); 70148456Spjdstatic void eli_setkey(struct gctl_req *req); 71148456Spjdstatic void eli_delkey(struct gctl_req *req); 72214118Spjdstatic void eli_resume(struct gctl_req *req); 73148456Spjdstatic void eli_kill(struct gctl_req *req); 74148456Spjdstatic void eli_backup(struct gctl_req *req); 75148456Spjdstatic void eli_restore(struct gctl_req *req); 76212934Sbrianstatic void eli_resize(struct gctl_req *req); 77226723Spjdstatic void eli_version(struct gctl_req *req); 78148456Spjdstatic void eli_clear(struct gctl_req *req); 79148456Spjdstatic void eli_dump(struct gctl_req *req); 80148456Spjd 81182452Spjdstatic int eli_backup_create(struct gctl_req *req, const char *prov, 82182452Spjd const char *file); 83182452Spjd 84148456Spjd/* 85148456Spjd * Available commands: 86148456Spjd * 87329114Skevans * init [-bdgPTv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] [-s sectorsize] [-V version] prov 88148456Spjd * label - alias for 'init' 89344397Skevans * attach [-Cdprv] [-n keyno] [-j passfile] [-k keyfile] prov 90148456Spjd * detach [-fl] prov ... 91148456Spjd * stop - alias for 'detach' 92181639Spjd * onetime [-d] [-a aalgo] [-e ealgo] [-l keylen] prov 93297691Sallanjude * configure [-bBgGtT] prov ... 94213172Spjd * setkey [-pPv] [-n keyno] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov 95148456Spjd * delkey [-afv] [-n keyno] prov 96214118Spjd * suspend [-v] -a | prov ... 97214118Spjd * resume [-pv] [-j passfile] [-k keyfile] prov 98148456Spjd * kill [-av] [prov ...] 99148456Spjd * backup [-v] prov file 100212934Sbrian * restore [-fv] file prov 101212934Sbrian * resize [-v] -s oldsize prov 102226723Spjd * version [prov ...] 103148456Spjd * clear [-v] prov ... 104148456Spjd * dump [-v] prov ... 105148456Spjd */ 106148456Spjdstruct g_command class_commands[] = { 107148456Spjd { "init", G_FLAG_VERBOSE, eli_main, 108148456Spjd { 109212547Spjd { 'a', "aalgo", "", G_TYPE_STRING }, 110162868Spjd { 'b', "boot", NULL, G_TYPE_BOOL }, 111212547Spjd { 'B', "backupfile", "", G_TYPE_STRING }, 112329114Skevans { 'd', "displaypass", NULL, G_TYPE_BOOL }, 113226733Spjd { 'e', "ealgo", "", G_TYPE_STRING }, 114297691Sallanjude { 'g', "geliboot", NULL, G_TYPE_BOOL }, 115212554Spjd { 'i', "iterations", "-1", G_TYPE_NUMBER }, 116213172Spjd { 'J', "newpassfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 117213172Spjd { 'K', "newkeyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 118212554Spjd { 'l', "keylen", "0", G_TYPE_NUMBER }, 119162868Spjd { 'P', "nonewpassphrase", NULL, G_TYPE_BOOL }, 120212554Spjd { 's', "sectorsize", "0", G_TYPE_NUMBER }, 121286444Spjd { 'T', "notrim", NULL, G_TYPE_BOOL }, 122226733Spjd { 'V', "mdversion", "-1", G_TYPE_NUMBER }, 123148456Spjd G_OPT_SENTINEL 124148456Spjd }, 125329114Skevans "[-bdgPTv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] [-s sectorsize] [-V version] prov" 126148456Spjd }, 127148456Spjd { "label", G_FLAG_VERBOSE, eli_main, 128148456Spjd { 129212547Spjd { 'a', "aalgo", "", G_TYPE_STRING }, 130162868Spjd { 'b', "boot", NULL, G_TYPE_BOOL }, 131212547Spjd { 'B', "backupfile", "", G_TYPE_STRING }, 132329114Skevans { 'd', "displaypass", NULL, G_TYPE_BOOL }, 133226733Spjd { 'e', "ealgo", "", G_TYPE_STRING }, 134297691Sallanjude { 'g', "geliboot", NULL, G_TYPE_BOOL }, 135212554Spjd { 'i', "iterations", "-1", G_TYPE_NUMBER }, 136213172Spjd { 'J', "newpassfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 137213172Spjd { 'K', "newkeyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 138212554Spjd { 'l', "keylen", "0", G_TYPE_NUMBER }, 139162868Spjd { 'P', "nonewpassphrase", NULL, G_TYPE_BOOL }, 140212554Spjd { 's', "sectorsize", "0", G_TYPE_NUMBER }, 141226733Spjd { 'V', "mdversion", "-1", G_TYPE_NUMBER }, 142148456Spjd G_OPT_SENTINEL 143148456Spjd }, 144212554Spjd "- an alias for 'init'" 145148456Spjd }, 146148456Spjd { "attach", G_FLAG_VERBOSE | G_FLAG_LOADKLD, eli_main, 147148456Spjd { 148344397Skevans { 'C', "dryrun", NULL, G_TYPE_BOOL }, 149162868Spjd { 'd', "detach", NULL, G_TYPE_BOOL }, 150213172Spjd { 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 151213172Spjd { 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 152344397Skevans { 'n', "keyno", "-1", G_TYPE_NUMBER }, 153162868Spjd { 'p', "nopassphrase", NULL, G_TYPE_BOOL }, 154162868Spjd { 'r', "readonly", NULL, G_TYPE_BOOL }, 155148456Spjd G_OPT_SENTINEL 156148456Spjd }, 157344397Skevans "[-Cdprv] [-n keyno] [-j passfile] [-k keyfile] prov" 158148456Spjd }, 159148456Spjd { "detach", 0, NULL, 160148456Spjd { 161162868Spjd { 'f', "force", NULL, G_TYPE_BOOL }, 162162868Spjd { 'l', "last", NULL, G_TYPE_BOOL }, 163148456Spjd G_OPT_SENTINEL 164148456Spjd }, 165212554Spjd "[-fl] prov ..." 166148456Spjd }, 167148456Spjd { "stop", 0, NULL, 168148456Spjd { 169162868Spjd { 'f', "force", NULL, G_TYPE_BOOL }, 170162868Spjd { 'l', "last", NULL, G_TYPE_BOOL }, 171148456Spjd G_OPT_SENTINEL 172148456Spjd }, 173212554Spjd "- an alias for 'detach'" 174148456Spjd }, 175148456Spjd { "onetime", G_FLAG_VERBOSE | G_FLAG_LOADKLD, NULL, 176148456Spjd { 177212547Spjd { 'a', "aalgo", "", G_TYPE_STRING }, 178162868Spjd { 'd', "detach", NULL, G_TYPE_BOOL }, 179212547Spjd { 'e', "ealgo", GELI_ENC_ALGO, G_TYPE_STRING }, 180212554Spjd { 'l', "keylen", "0", G_TYPE_NUMBER }, 181212554Spjd { 's', "sectorsize", "0", G_TYPE_NUMBER }, 182286444Spjd { 'T', "notrim", NULL, G_TYPE_BOOL }, 183148456Spjd G_OPT_SENTINEL 184148456Spjd }, 185286444Spjd "[-dT] [-a aalgo] [-e ealgo] [-l keylen] [-s sectorsize] prov" 186148456Spjd }, 187162353Spjd { "configure", G_FLAG_VERBOSE, eli_main, 188162353Spjd { 189162868Spjd { 'b', "boot", NULL, G_TYPE_BOOL }, 190162868Spjd { 'B', "noboot", NULL, G_TYPE_BOOL }, 191329114Skevans { 'd', "displaypass", NULL, G_TYPE_BOOL }, 192329114Skevans { 'D', "nodisplaypass", NULL, G_TYPE_BOOL }, 193297691Sallanjude { 'g', "geliboot", NULL, G_TYPE_BOOL }, 194297691Sallanjude { 'G', "nogeliboot", NULL, G_TYPE_BOOL }, 195286444Spjd { 't', "trim", NULL, G_TYPE_BOOL }, 196286444Spjd { 'T', "notrim", NULL, G_TYPE_BOOL }, 197162353Spjd G_OPT_SENTINEL 198162353Spjd }, 199329114Skevans "[-bBdDgGtT] prov ..." 200162353Spjd }, 201148456Spjd { "setkey", G_FLAG_VERBOSE, eli_main, 202148456Spjd { 203212554Spjd { 'i', "iterations", "-1", G_TYPE_NUMBER }, 204213172Spjd { 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 205213172Spjd { 'J', "newpassfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 206213172Spjd { 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 207213172Spjd { 'K', "newkeyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 208212554Spjd { 'n', "keyno", "-1", G_TYPE_NUMBER }, 209162868Spjd { 'p', "nopassphrase", NULL, G_TYPE_BOOL }, 210162868Spjd { 'P', "nonewpassphrase", NULL, G_TYPE_BOOL }, 211148456Spjd G_OPT_SENTINEL 212148456Spjd }, 213213172Spjd "[-pPv] [-n keyno] [-i iterations] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov" 214148456Spjd }, 215148456Spjd { "delkey", G_FLAG_VERBOSE, eli_main, 216148456Spjd { 217162868Spjd { 'a', "all", NULL, G_TYPE_BOOL }, 218162868Spjd { 'f', "force", NULL, G_TYPE_BOOL }, 219212554Spjd { 'n', "keyno", "-1", G_TYPE_NUMBER }, 220148456Spjd G_OPT_SENTINEL 221148456Spjd }, 222212554Spjd "[-afv] [-n keyno] prov" 223148456Spjd }, 224214118Spjd { "suspend", G_FLAG_VERBOSE, NULL, 225214118Spjd { 226214118Spjd { 'a', "all", NULL, G_TYPE_BOOL }, 227214118Spjd G_OPT_SENTINEL 228214118Spjd }, 229214118Spjd "[-v] -a | prov ..." 230214118Spjd }, 231214118Spjd { "resume", G_FLAG_VERBOSE, eli_main, 232214118Spjd { 233214118Spjd { 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 234214118Spjd { 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 235214118Spjd { 'p', "nopassphrase", NULL, G_TYPE_BOOL }, 236214118Spjd G_OPT_SENTINEL 237214118Spjd }, 238214118Spjd "[-pv] [-j passfile] [-k keyfile] prov" 239214118Spjd }, 240148456Spjd { "kill", G_FLAG_VERBOSE, eli_main, 241148456Spjd { 242162868Spjd { 'a', "all", NULL, G_TYPE_BOOL }, 243148456Spjd G_OPT_SENTINEL 244148456Spjd }, 245212554Spjd "[-av] [prov ...]" 246148456Spjd }, 247212554Spjd { "backup", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS, 248148456Spjd "[-v] prov file" 249148456Spjd }, 250212934Sbrian { "restore", G_FLAG_VERBOSE, eli_main, 251212934Sbrian { 252212934Sbrian { 'f', "force", NULL, G_TYPE_BOOL }, 253212934Sbrian G_OPT_SENTINEL 254212934Sbrian }, 255212934Sbrian "[-fv] file prov" 256148456Spjd }, 257212934Sbrian { "resize", G_FLAG_VERBOSE, eli_main, 258212934Sbrian { 259212934Sbrian { 's', "oldsize", NULL, G_TYPE_NUMBER }, 260212934Sbrian G_OPT_SENTINEL 261212934Sbrian }, 262212934Sbrian "[-v] -s oldsize prov" 263212934Sbrian }, 264226723Spjd { "version", G_FLAG_LOADKLD, eli_main, G_NULL_OPTS, 265226723Spjd "[prov ...]" 266226723Spjd }, 267212554Spjd { "clear", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS, 268148456Spjd "[-v] prov ..." 269148456Spjd }, 270212554Spjd { "dump", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS, 271148456Spjd "[-v] prov ..." 272148456Spjd }, 273148456Spjd G_CMD_SENTINEL 274148456Spjd}; 275148456Spjd 276148456Spjdstatic int verbose = 0; 277148456Spjd 278248475Spjd#define BUFSIZE 1024 279248475Spjd 280148456Spjdstatic int 281148456Spjdeli_protect(struct gctl_req *req) 282148456Spjd{ 283148456Spjd struct rlimit rl; 284148456Spjd 285148456Spjd /* Disable core dumps. */ 286148456Spjd rl.rlim_cur = 0; 287148456Spjd rl.rlim_max = 0; 288148456Spjd if (setrlimit(RLIMIT_CORE, &rl) == -1) { 289148456Spjd gctl_error(req, "Cannot disable core dumps: %s.", 290148456Spjd strerror(errno)); 291148456Spjd return (-1); 292148456Spjd } 293148456Spjd /* Disable swapping. */ 294148456Spjd if (mlockall(MCL_FUTURE) == -1) { 295148456Spjd gctl_error(req, "Cannot lock memory: %s.", strerror(errno)); 296148456Spjd return (-1); 297148456Spjd } 298148456Spjd return (0); 299148456Spjd} 300148456Spjd 301148456Spjdstatic void 302213172Spjdeli_main(struct gctl_req *req, unsigned int flags) 303148456Spjd{ 304148456Spjd const char *name; 305148456Spjd 306148456Spjd if (eli_protect(req) == -1) 307148456Spjd return; 308148456Spjd 309148456Spjd if ((flags & G_FLAG_VERBOSE) != 0) 310148456Spjd verbose = 1; 311148456Spjd 312153190Spjd name = gctl_get_ascii(req, "verb"); 313148456Spjd if (name == NULL) { 314148456Spjd gctl_error(req, "No '%s' argument.", "verb"); 315148456Spjd return; 316148456Spjd } 317148456Spjd if (strcmp(name, "init") == 0 || strcmp(name, "label") == 0) 318148456Spjd eli_init(req); 319148456Spjd else if (strcmp(name, "attach") == 0) 320148456Spjd eli_attach(req); 321162353Spjd else if (strcmp(name, "configure") == 0) 322162353Spjd eli_configure(req); 323148456Spjd else if (strcmp(name, "setkey") == 0) 324148456Spjd eli_setkey(req); 325148456Spjd else if (strcmp(name, "delkey") == 0) 326148456Spjd eli_delkey(req); 327214118Spjd else if (strcmp(name, "resume") == 0) 328214118Spjd eli_resume(req); 329148456Spjd else if (strcmp(name, "kill") == 0) 330148456Spjd eli_kill(req); 331148456Spjd else if (strcmp(name, "backup") == 0) 332148456Spjd eli_backup(req); 333148456Spjd else if (strcmp(name, "restore") == 0) 334148456Spjd eli_restore(req); 335212934Sbrian else if (strcmp(name, "resize") == 0) 336212934Sbrian eli_resize(req); 337226723Spjd else if (strcmp(name, "version") == 0) 338226723Spjd eli_version(req); 339148456Spjd else if (strcmp(name, "dump") == 0) 340148456Spjd eli_dump(req); 341148456Spjd else if (strcmp(name, "clear") == 0) 342148456Spjd eli_clear(req); 343148456Spjd else 344148456Spjd gctl_error(req, "Unknown command: %s.", name); 345148456Spjd} 346148456Spjd 347226717Spjdstatic bool 348148456Spjdeli_is_attached(const char *prov) 349148456Spjd{ 350148456Spjd char name[MAXPATHLEN]; 351148456Spjd 352148456Spjd /* 353148456Spjd * Not the best way to do it, but the easiest. 354148456Spjd * We try to open provider and check if it is a GEOM provider 355148456Spjd * by asking about its sectorsize. 356148456Spjd */ 357148456Spjd snprintf(name, sizeof(name), "%s%s", prov, G_ELI_SUFFIX); 358226717Spjd return (g_get_sectorsize(name) > 0); 359148456Spjd} 360148456Spjd 361213172Spjdstatic int 362213172Spjdeli_genkey_files(struct gctl_req *req, bool new, const char *type, 363213172Spjd struct hmac_ctx *ctxp, char *passbuf, size_t passbufsize) 364148456Spjd{ 365248475Spjd char *p, buf[BUFSIZE], argname[16]; 366213172Spjd const char *file; 367213172Spjd int error, fd, i; 368213172Spjd ssize_t done; 369148456Spjd 370213172Spjd assert((strcmp(type, "keyfile") == 0 && ctxp != NULL && 371213172Spjd passbuf == NULL && passbufsize == 0) || 372213172Spjd (strcmp(type, "passfile") == 0 && ctxp == NULL && 373213172Spjd passbuf != NULL && passbufsize > 0)); 374213172Spjd assert(strcmp(type, "keyfile") == 0 || passbuf[0] == '\0'); 375148456Spjd 376213172Spjd for (i = 0; ; i++) { 377213172Spjd snprintf(argname, sizeof(argname), "%s%s%d", 378213172Spjd new ? "new" : "", type, i); 379148456Spjd 380213172Spjd /* No more {key,pass}files? */ 381213172Spjd if (!gctl_has_param(req, argname)) 382213172Spjd return (i); 383148456Spjd 384215704Sbrucec file = gctl_get_ascii(req, "%s", argname); 385213172Spjd assert(file != NULL); 386213172Spjd 387213172Spjd if (strcmp(file, "-") == 0) 388148456Spjd fd = STDIN_FILENO; 389148456Spjd else { 390213172Spjd fd = open(file, O_RDONLY); 391148456Spjd if (fd == -1) { 392213172Spjd gctl_error(req, "Cannot open %s %s: %s.", 393213172Spjd type, file, strerror(errno)); 394213172Spjd return (-1); 395148456Spjd } 396148456Spjd } 397213172Spjd if (strcmp(type, "keyfile") == 0) { 398213172Spjd while ((done = read(fd, buf, sizeof(buf))) > 0) 399213172Spjd g_eli_crypto_hmac_update(ctxp, buf, done); 400213172Spjd } else /* if (strcmp(type, "passfile") == 0) */ { 401246621Spjd assert(strcmp(type, "passfile") == 0); 402246621Spjd 403213172Spjd while ((done = read(fd, buf, sizeof(buf) - 1)) > 0) { 404213172Spjd buf[done] = '\0'; 405213172Spjd p = strchr(buf, '\n'); 406213172Spjd if (p != NULL) { 407213172Spjd *p = '\0'; 408213172Spjd done = p - buf; 409213172Spjd } 410213172Spjd if (strlcat(passbuf, buf, passbufsize) >= 411213172Spjd passbufsize) { 412213172Spjd gctl_error(req, 413213172Spjd "Passphrase in %s too long.", file); 414213172Spjd bzero(buf, sizeof(buf)); 415213172Spjd return (-1); 416213172Spjd } 417213172Spjd if (p != NULL) 418213172Spjd break; 419213172Spjd } 420213172Spjd } 421148456Spjd error = errno; 422213172Spjd if (strcmp(file, "-") != 0) 423148456Spjd close(fd); 424148456Spjd bzero(buf, sizeof(buf)); 425148456Spjd if (done == -1) { 426213172Spjd gctl_error(req, "Cannot read %s %s: %s.", 427213172Spjd type, file, strerror(error)); 428213172Spjd return (-1); 429148456Spjd } 430148456Spjd } 431213172Spjd /* NOTREACHED */ 432213172Spjd} 433148456Spjd 434213172Spjdstatic int 435213172Spjdeli_genkey_passphrase_prompt(struct gctl_req *req, bool new, char *passbuf, 436213172Spjd size_t passbufsize) 437213172Spjd{ 438213172Spjd char *p; 439148456Spjd 440213172Spjd for (;;) { 441213172Spjd p = readpassphrase( 442284250Sbrueffer new ? "Enter new passphrase: " : "Enter passphrase: ", 443213172Spjd passbuf, passbufsize, RPP_ECHO_OFF | RPP_REQUIRE_TTY); 444213172Spjd if (p == NULL) { 445213172Spjd bzero(passbuf, passbufsize); 446213172Spjd gctl_error(req, "Cannot read passphrase: %s.", 447213172Spjd strerror(errno)); 448213172Spjd return (-1); 449149047Spjd } 450213172Spjd 451213172Spjd if (new) { 452248475Spjd char tmpbuf[BUFSIZE]; 453213172Spjd 454213172Spjd p = readpassphrase("Reenter new passphrase: ", 455213172Spjd tmpbuf, sizeof(tmpbuf), 456213172Spjd RPP_ECHO_OFF | RPP_REQUIRE_TTY); 457148456Spjd if (p == NULL) { 458213172Spjd bzero(passbuf, passbufsize); 459213172Spjd gctl_error(req, 460213172Spjd "Cannot read passphrase: %s.", 461148456Spjd strerror(errno)); 462213172Spjd return (-1); 463148456Spjd } 464213172Spjd 465213172Spjd if (strcmp(passbuf, tmpbuf) != 0) { 466213172Spjd bzero(passbuf, passbufsize); 467213172Spjd fprintf(stderr, "They didn't match.\n"); 468213172Spjd continue; 469148456Spjd } 470213172Spjd bzero(tmpbuf, sizeof(tmpbuf)); 471148456Spjd } 472213172Spjd return (0); 473213172Spjd } 474213172Spjd /* NOTREACHED */ 475213172Spjd} 476213172Spjd 477213172Spjdstatic int 478213172Spjdeli_genkey_passphrase(struct gctl_req *req, struct g_eli_metadata *md, bool new, 479213172Spjd struct hmac_ctx *ctxp) 480213172Spjd{ 481248475Spjd char passbuf[BUFSIZE]; 482213172Spjd bool nopassphrase; 483213172Spjd int nfiles; 484213172Spjd 485213172Spjd nopassphrase = 486213172Spjd gctl_get_int(req, new ? "nonewpassphrase" : "nopassphrase"); 487213172Spjd if (nopassphrase) { 488213172Spjd if (gctl_has_param(req, new ? "newpassfile0" : "passfile0")) { 489213172Spjd gctl_error(req, 490213172Spjd "Options -%c and -%c are mutually exclusive.", 491213172Spjd new ? 'J' : 'j', new ? 'P' : 'p'); 492213172Spjd return (-1); 493148456Spjd } 494213172Spjd return (0); 495213172Spjd } 496148456Spjd 497213172Spjd if (!new && md->md_iterations == -1) { 498213172Spjd gctl_error(req, "Missing -p flag."); 499213172Spjd return (-1); 500213172Spjd } 501213172Spjd passbuf[0] = '\0'; 502213172Spjd nfiles = eli_genkey_files(req, new, "passfile", NULL, passbuf, 503213172Spjd sizeof(passbuf)); 504213172Spjd if (nfiles == -1) 505213172Spjd return (-1); 506213172Spjd else if (nfiles == 0) { 507213172Spjd if (eli_genkey_passphrase_prompt(req, new, passbuf, 508213172Spjd sizeof(passbuf)) == -1) { 509213172Spjd return (-1); 510148456Spjd } 511148456Spjd } 512213172Spjd /* 513213172Spjd * Field md_iterations equal to -1 means "choose some sane 514213172Spjd * value for me". 515213172Spjd */ 516213172Spjd if (md->md_iterations == -1) { 517213172Spjd assert(new); 518213172Spjd if (verbose) 519213172Spjd printf("Calculating number of iterations...\n"); 520213172Spjd md->md_iterations = pkcs5v2_calculate(2000000); 521213172Spjd assert(md->md_iterations > 0); 522213172Spjd if (verbose) { 523213172Spjd printf("Done, using %d iterations.\n", 524213172Spjd md->md_iterations); 525213172Spjd } 526213172Spjd } 527213172Spjd /* 528213172Spjd * If md_iterations is equal to 0, user doesn't want PKCS#5v2. 529213172Spjd */ 530213172Spjd if (md->md_iterations == 0) { 531213172Spjd g_eli_crypto_hmac_update(ctxp, md->md_salt, 532213172Spjd sizeof(md->md_salt)); 533213172Spjd g_eli_crypto_hmac_update(ctxp, passbuf, strlen(passbuf)); 534213172Spjd } else /* if (md->md_iterations > 0) */ { 535213172Spjd unsigned char dkey[G_ELI_USERKEYLEN]; 536213172Spjd 537213172Spjd pkcs5v2_genkey(dkey, sizeof(dkey), md->md_salt, 538213172Spjd sizeof(md->md_salt), passbuf, md->md_iterations); 539213172Spjd g_eli_crypto_hmac_update(ctxp, dkey, sizeof(dkey)); 540213172Spjd bzero(dkey, sizeof(dkey)); 541213172Spjd } 542213172Spjd bzero(passbuf, sizeof(passbuf)); 543213172Spjd 544213172Spjd return (0); 545213172Spjd} 546213172Spjd 547213172Spjdstatic unsigned char * 548213172Spjdeli_genkey(struct gctl_req *req, struct g_eli_metadata *md, unsigned char *key, 549213172Spjd bool new) 550213172Spjd{ 551213172Spjd struct hmac_ctx ctx; 552213172Spjd bool nopassphrase; 553213172Spjd int nfiles; 554213172Spjd 555213172Spjd nopassphrase = 556213172Spjd gctl_get_int(req, new ? "nonewpassphrase" : "nopassphrase"); 557213172Spjd 558213172Spjd g_eli_crypto_hmac_init(&ctx, NULL, 0); 559213172Spjd 560213172Spjd nfiles = eli_genkey_files(req, new, "keyfile", &ctx, NULL, 0); 561213172Spjd if (nfiles == -1) 562213172Spjd return (NULL); 563213172Spjd else if (nfiles == 0 && nopassphrase) { 564213172Spjd gctl_error(req, "No key components given."); 565213172Spjd return (NULL); 566213172Spjd } 567213172Spjd 568213172Spjd if (eli_genkey_passphrase(req, md, new, &ctx) == -1) 569213172Spjd return (NULL); 570213172Spjd 571148456Spjd g_eli_crypto_hmac_final(&ctx, key, 0); 572213172Spjd 573148456Spjd return (key); 574148456Spjd} 575148456Spjd 576148456Spjdstatic int 577148456Spjdeli_metadata_read(struct gctl_req *req, const char *prov, 578148456Spjd struct g_eli_metadata *md) 579148456Spjd{ 580148456Spjd unsigned char sector[sizeof(struct g_eli_metadata)]; 581148456Spjd int error; 582148456Spjd 583148456Spjd if (g_get_sectorsize(prov) == 0) { 584148456Spjd int fd; 585148456Spjd 586148456Spjd /* This is a file probably. */ 587148456Spjd fd = open(prov, O_RDONLY); 588148456Spjd if (fd == -1) { 589148456Spjd gctl_error(req, "Cannot open %s: %s.", prov, 590148456Spjd strerror(errno)); 591148456Spjd return (-1); 592148456Spjd } 593148456Spjd if (read(fd, sector, sizeof(sector)) != sizeof(sector)) { 594148456Spjd gctl_error(req, "Cannot read metadata from %s: %s.", 595148456Spjd prov, strerror(errno)); 596148456Spjd close(fd); 597148456Spjd return (-1); 598148456Spjd } 599148456Spjd close(fd); 600148456Spjd } else { 601148456Spjd /* This is a GEOM provider. */ 602148456Spjd error = g_metadata_read(prov, sector, sizeof(sector), 603148456Spjd G_ELI_MAGIC); 604148456Spjd if (error != 0) { 605148456Spjd gctl_error(req, "Cannot read metadata from %s: %s.", 606148456Spjd prov, strerror(error)); 607148456Spjd return (-1); 608148456Spjd } 609148456Spjd } 610226722Spjd error = eli_metadata_decode(sector, md); 611226722Spjd switch (error) { 612226722Spjd case 0: 613226722Spjd break; 614226722Spjd case EOPNOTSUPP: 615226722Spjd gctl_error(req, 616226722Spjd "Provider's %s metadata version %u is too new.\n" 617226722Spjd "geli: The highest supported version is %u.", 618226722Spjd prov, (unsigned int)md->md_version, G_ELI_VERSION); 619148456Spjd return (-1); 620226722Spjd case EINVAL: 621226722Spjd gctl_error(req, "Inconsistent provider's %s metadata.", prov); 622226722Spjd return (-1); 623226722Spjd default: 624226722Spjd gctl_error(req, 625226722Spjd "Unexpected error while decoding provider's %s metadata: %s.", 626226722Spjd prov, strerror(error)); 627226722Spjd return (-1); 628148456Spjd } 629148456Spjd return (0); 630148456Spjd} 631148456Spjd 632148456Spjdstatic int 633148456Spjdeli_metadata_store(struct gctl_req *req, const char *prov, 634148456Spjd struct g_eli_metadata *md) 635148456Spjd{ 636148456Spjd unsigned char sector[sizeof(struct g_eli_metadata)]; 637148456Spjd int error; 638148456Spjd 639148456Spjd eli_metadata_encode(md, sector); 640148456Spjd if (g_get_sectorsize(prov) == 0) { 641148456Spjd int fd; 642148456Spjd 643148456Spjd /* This is a file probably. */ 644148456Spjd fd = open(prov, O_WRONLY | O_TRUNC); 645148456Spjd if (fd == -1) { 646148456Spjd gctl_error(req, "Cannot open %s: %s.", prov, 647148456Spjd strerror(errno)); 648148456Spjd bzero(sector, sizeof(sector)); 649148456Spjd return (-1); 650148456Spjd } 651148456Spjd if (write(fd, sector, sizeof(sector)) != sizeof(sector)) { 652148456Spjd gctl_error(req, "Cannot write metadata to %s: %s.", 653148456Spjd prov, strerror(errno)); 654148456Spjd bzero(sector, sizeof(sector)); 655148456Spjd close(fd); 656148456Spjd return (-1); 657148456Spjd } 658148456Spjd close(fd); 659148456Spjd } else { 660148456Spjd /* This is a GEOM provider. */ 661148456Spjd error = g_metadata_store(prov, sector, sizeof(sector)); 662148456Spjd if (error != 0) { 663148456Spjd gctl_error(req, "Cannot write metadata to %s: %s.", 664148456Spjd prov, strerror(errno)); 665148456Spjd bzero(sector, sizeof(sector)); 666148456Spjd return (-1); 667148456Spjd } 668148456Spjd } 669148456Spjd bzero(sector, sizeof(sector)); 670148456Spjd return (0); 671148456Spjd} 672148456Spjd 673148456Spjdstatic void 674148456Spjdeli_init(struct gctl_req *req) 675148456Spjd{ 676148456Spjd struct g_eli_metadata md; 677332522Skevans unsigned char sector[sizeof(struct g_eli_metadata)] __aligned(4); 678148456Spjd unsigned char key[G_ELI_USERKEYLEN]; 679182452Spjd char backfile[MAXPATHLEN]; 680148456Spjd const char *str, *prov; 681226733Spjd unsigned int secsize, version; 682148456Spjd off_t mediasize; 683153190Spjd intmax_t val; 684155536Spjd int error, nargs; 685148456Spjd 686153190Spjd nargs = gctl_get_int(req, "nargs"); 687153190Spjd if (nargs != 1) { 688158214Spjd gctl_error(req, "Invalid number of arguments."); 689148456Spjd return; 690148456Spjd } 691153190Spjd prov = gctl_get_ascii(req, "arg0"); 692148456Spjd mediasize = g_get_mediasize(prov); 693148456Spjd secsize = g_get_sectorsize(prov); 694148456Spjd if (mediasize == 0 || secsize == 0) { 695148456Spjd gctl_error(req, "Cannot get informations about %s: %s.", prov, 696148456Spjd strerror(errno)); 697148456Spjd return; 698148456Spjd } 699148456Spjd 700148456Spjd bzero(&md, sizeof(md)); 701148456Spjd strlcpy(md.md_magic, G_ELI_MAGIC, sizeof(md.md_magic)); 702226733Spjd val = gctl_get_intmax(req, "mdversion"); 703226733Spjd if (val == -1) { 704226733Spjd version = G_ELI_VERSION; 705226733Spjd } else if (val < 0 || val > G_ELI_VERSION) { 706226733Spjd gctl_error(req, 707226733Spjd "Invalid version specified should be between %u and %u.", 708226733Spjd G_ELI_VERSION_00, G_ELI_VERSION); 709226733Spjd return; 710226733Spjd } else { 711226733Spjd version = val; 712226733Spjd } 713226733Spjd md.md_version = version; 714148456Spjd md.md_flags = 0; 715155536Spjd if (gctl_get_int(req, "boot")) 716148456Spjd md.md_flags |= G_ELI_FLAG_BOOT; 717297691Sallanjude if (gctl_get_int(req, "geliboot")) 718297691Sallanjude md.md_flags |= G_ELI_FLAG_GELIBOOT; 719329114Skevans if (gctl_get_int(req, "displaypass")) 720329114Skevans md.md_flags |= G_ELI_FLAG_GELIDISPLAYPASS; 721286444Spjd if (gctl_get_int(req, "notrim")) 722286444Spjd md.md_flags |= G_ELI_FLAG_NODELETE; 723159361Spjd md.md_ealgo = CRYPTO_ALGORITHM_MIN - 1; 724159308Spjd str = gctl_get_ascii(req, "aalgo"); 725212547Spjd if (*str != '\0') { 726226733Spjd if (version < G_ELI_VERSION_01) { 727226733Spjd gctl_error(req, 728226733Spjd "Data authentication is supported starting from version %u.", 729226733Spjd G_ELI_VERSION_01); 730226733Spjd return; 731226733Spjd } 732159308Spjd md.md_aalgo = g_eli_str2aalgo(str); 733159361Spjd if (md.md_aalgo >= CRYPTO_ALGORITHM_MIN && 734159361Spjd md.md_aalgo <= CRYPTO_ALGORITHM_MAX) { 735159361Spjd md.md_flags |= G_ELI_FLAG_AUTH; 736159361Spjd } else { 737159361Spjd /* 738159361Spjd * For backward compatibility, check if the -a option 739159361Spjd * was used to provide encryption algorithm. 740159361Spjd */ 741159361Spjd md.md_ealgo = g_eli_str2ealgo(str); 742159361Spjd if (md.md_ealgo < CRYPTO_ALGORITHM_MIN || 743159361Spjd md.md_ealgo > CRYPTO_ALGORITHM_MAX) { 744159361Spjd gctl_error(req, 745159361Spjd "Invalid authentication algorithm."); 746159361Spjd return; 747159361Spjd } else { 748159361Spjd fprintf(stderr, "warning: The -e option, not " 749159361Spjd "the -a option is now used to specify " 750159361Spjd "encryption algorithm to use.\n"); 751159361Spjd } 752159308Spjd } 753159308Spjd } 754159308Spjd if (md.md_ealgo < CRYPTO_ALGORITHM_MIN || 755159308Spjd md.md_ealgo > CRYPTO_ALGORITHM_MAX) { 756159361Spjd str = gctl_get_ascii(req, "ealgo"); 757226733Spjd if (*str == '\0') { 758226733Spjd if (version < G_ELI_VERSION_05) 759226733Spjd str = "aes-cbc"; 760226733Spjd else 761226733Spjd str = GELI_ENC_ALGO; 762226733Spjd } 763159361Spjd md.md_ealgo = g_eli_str2ealgo(str); 764159361Spjd if (md.md_ealgo < CRYPTO_ALGORITHM_MIN || 765159361Spjd md.md_ealgo > CRYPTO_ALGORITHM_MAX) { 766159361Spjd gctl_error(req, "Invalid encryption algorithm."); 767159361Spjd return; 768159361Spjd } 769226733Spjd if (md.md_ealgo == CRYPTO_CAMELLIA_CBC && 770226733Spjd version < G_ELI_VERSION_04) { 771226733Spjd gctl_error(req, 772226733Spjd "Camellia-CBC algorithm is supported starting from version %u.", 773226733Spjd G_ELI_VERSION_04); 774226733Spjd return; 775226733Spjd } 776226733Spjd if (md.md_ealgo == CRYPTO_AES_XTS && 777226733Spjd version < G_ELI_VERSION_05) { 778226733Spjd gctl_error(req, 779226733Spjd "AES-XTS algorithm is supported starting from version %u.", 780226733Spjd G_ELI_VERSION_05); 781226733Spjd return; 782226733Spjd } 783148456Spjd } 784348588Sjhb if (md.md_flags & G_ELI_FLAG_AUTH) { 785348588Sjhb switch (md.md_aalgo) { 786348588Sjhb case CRYPTO_MD5_HMAC: 787348588Sjhb gctl_error(req, 788348588Sjhb "The %s authentication algorithm is deprecated.", 789348588Sjhb g_eli_algo2str(md.md_aalgo)); 790348588Sjhb return; 791348588Sjhb } 792348588Sjhb } 793348588Sjhb switch (md.md_ealgo) { 794348588Sjhb case CRYPTO_3DES_CBC: 795348588Sjhb case CRYPTO_BLF_CBC: 796348588Sjhb gctl_error(req, "The %s encryption algorithm is deprecated.", 797348588Sjhb g_eli_algo2str(md.md_ealgo)); 798348588Sjhb return; 799348588Sjhb } 800153190Spjd val = gctl_get_intmax(req, "keylen"); 801153190Spjd md.md_keylen = val; 802159308Spjd md.md_keylen = g_eli_keylen(md.md_ealgo, md.md_keylen); 803148456Spjd if (md.md_keylen == 0) { 804148456Spjd gctl_error(req, "Invalid key length."); 805148456Spjd return; 806148456Spjd } 807148456Spjd md.md_provsize = mediasize; 808148456Spjd 809153190Spjd val = gctl_get_intmax(req, "iterations"); 810155536Spjd if (val != -1) { 811155536Spjd int nonewpassphrase; 812155536Spjd 813155536Spjd /* 814155536Spjd * Don't allow to set iterations when there will be no 815155536Spjd * passphrase. 816155536Spjd */ 817155536Spjd nonewpassphrase = gctl_get_int(req, "nonewpassphrase"); 818155536Spjd if (nonewpassphrase) { 819155536Spjd gctl_error(req, 820155536Spjd "Options -i and -P are mutually exclusive."); 821155536Spjd return; 822155536Spjd } 823155536Spjd } 824153190Spjd md.md_iterations = val; 825148456Spjd 826153190Spjd val = gctl_get_intmax(req, "sectorsize"); 827153190Spjd if (val == 0) 828148456Spjd md.md_sectorsize = secsize; 829148456Spjd else { 830260254Spjd if (val < 0 || (val % secsize) != 0 || !powerof2(val)) { 831148456Spjd gctl_error(req, "Invalid sector size."); 832148456Spjd return; 833148456Spjd } 834167229Spjd if (val > sysconf(_SC_PAGE_SIZE)) { 835214404Spjd fprintf(stderr, 836214404Spjd "warning: Using sectorsize bigger than the page size!\n"); 837167229Spjd } 838153190Spjd md.md_sectorsize = val; 839148456Spjd } 840148456Spjd 841148456Spjd md.md_keys = 0x01; 842246620Spjd arc4random_buf(md.md_salt, sizeof(md.md_salt)); 843246620Spjd arc4random_buf(md.md_mkeys, sizeof(md.md_mkeys)); 844148456Spjd 845148456Spjd /* Generate user key. */ 846213172Spjd if (eli_genkey(req, &md, key, true) == NULL) { 847148456Spjd bzero(key, sizeof(key)); 848148456Spjd bzero(&md, sizeof(md)); 849148456Spjd return; 850148456Spjd } 851148456Spjd 852148456Spjd /* Encrypt the first and the only Master Key. */ 853159308Spjd error = g_eli_mkey_encrypt(md.md_ealgo, key, md.md_keylen, md.md_mkeys); 854148456Spjd bzero(key, sizeof(key)); 855148456Spjd if (error != 0) { 856148456Spjd bzero(&md, sizeof(md)); 857148456Spjd gctl_error(req, "Cannot encrypt Master Key: %s.", 858148456Spjd strerror(error)); 859148456Spjd return; 860148456Spjd } 861148456Spjd 862148456Spjd eli_metadata_encode(&md, sector); 863148456Spjd bzero(&md, sizeof(md)); 864148456Spjd error = g_metadata_store(prov, sector, sizeof(sector)); 865148456Spjd bzero(sector, sizeof(sector)); 866148456Spjd if (error != 0) { 867148456Spjd gctl_error(req, "Cannot store metadata on %s: %s.", prov, 868148456Spjd strerror(error)); 869148456Spjd return; 870148456Spjd } 871148456Spjd if (verbose) 872148456Spjd printf("Metadata value stored on %s.\n", prov); 873182452Spjd /* Backup metadata to a file. */ 874182452Spjd str = gctl_get_ascii(req, "backupfile"); 875182452Spjd if (str[0] != '\0') { 876182452Spjd /* Backupfile given be the user, just copy it. */ 877182452Spjd strlcpy(backfile, str, sizeof(backfile)); 878182452Spjd } else { 879182452Spjd /* Generate file name automatically. */ 880182452Spjd const char *p = prov; 881182452Spjd unsigned int i; 882182452Spjd 883213662Sae if (strncmp(p, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) 884213662Sae p += sizeof(_PATH_DEV) - 1; 885182452Spjd snprintf(backfile, sizeof(backfile), "%s%s.eli", 886182452Spjd GELI_BACKUP_DIR, p); 887182452Spjd /* Replace all / with _. */ 888182452Spjd for (i = strlen(GELI_BACKUP_DIR); backfile[i] != '\0'; i++) { 889182452Spjd if (backfile[i] == '/') 890182452Spjd backfile[i] = '_'; 891182452Spjd } 892182452Spjd } 893182452Spjd if (strcmp(backfile, "none") != 0 && 894182452Spjd eli_backup_create(req, prov, backfile) == 0) { 895182452Spjd printf("\nMetadata backup can be found in %s and\n", backfile); 896182452Spjd printf("can be restored with the following command:\n"); 897182452Spjd printf("\n\t# geli restore %s %s\n\n", backfile, prov); 898182452Spjd } 899148456Spjd} 900148456Spjd 901148456Spjdstatic void 902148456Spjdeli_attach(struct gctl_req *req) 903148456Spjd{ 904148456Spjd struct g_eli_metadata md; 905148456Spjd unsigned char key[G_ELI_USERKEYLEN]; 906148456Spjd const char *prov; 907212934Sbrian off_t mediasize; 908153190Spjd int nargs; 909148456Spjd 910153190Spjd nargs = gctl_get_int(req, "nargs"); 911153190Spjd if (nargs != 1) { 912158214Spjd gctl_error(req, "Invalid number of arguments."); 913148456Spjd return; 914148456Spjd } 915153190Spjd prov = gctl_get_ascii(req, "arg0"); 916148456Spjd 917148456Spjd if (eli_metadata_read(req, prov, &md) == -1) 918148456Spjd return; 919148456Spjd 920212934Sbrian mediasize = g_get_mediasize(prov); 921212934Sbrian if (md.md_provsize != (uint64_t)mediasize) { 922212934Sbrian gctl_error(req, "Provider size mismatch."); 923212934Sbrian return; 924212934Sbrian } 925212934Sbrian 926213172Spjd if (eli_genkey(req, &md, key, false) == NULL) { 927148456Spjd bzero(key, sizeof(key)); 928148456Spjd return; 929148456Spjd } 930148456Spjd 931148456Spjd gctl_ro_param(req, "key", sizeof(key), key); 932148456Spjd if (gctl_issue(req) == NULL) { 933148456Spjd if (verbose) 934166892Spjd printf("Attached to %s.\n", prov); 935148456Spjd } 936148456Spjd bzero(key, sizeof(key)); 937148456Spjd} 938148456Spjd 939148456Spjdstatic void 940286444Spjdeli_configure_detached(struct gctl_req *req, const char *prov, int boot, 941329114Skevans int geliboot, int displaypass, int trim) 942162353Spjd{ 943162353Spjd struct g_eli_metadata md; 944286444Spjd bool changed = 0; 945162353Spjd 946162353Spjd if (eli_metadata_read(req, prov, &md) == -1) 947162353Spjd return; 948162353Spjd 949286444Spjd if (boot == 1 && (md.md_flags & G_ELI_FLAG_BOOT)) { 950162353Spjd if (verbose) 951162353Spjd printf("BOOT flag already configured for %s.\n", prov); 952286444Spjd } else if (boot == 0 && !(md.md_flags & G_ELI_FLAG_BOOT)) { 953162353Spjd if (verbose) 954162353Spjd printf("BOOT flag not configured for %s.\n", prov); 955286444Spjd } else if (boot >= 0) { 956162353Spjd if (boot) 957162353Spjd md.md_flags |= G_ELI_FLAG_BOOT; 958162353Spjd else 959162353Spjd md.md_flags &= ~G_ELI_FLAG_BOOT; 960286444Spjd changed = 1; 961286444Spjd } 962286444Spjd 963297691Sallanjude if (geliboot == 1 && (md.md_flags & G_ELI_FLAG_GELIBOOT)) { 964297691Sallanjude if (verbose) 965297691Sallanjude printf("GELIBOOT flag already configured for %s.\n", prov); 966297691Sallanjude } else if (geliboot == 0 && !(md.md_flags & G_ELI_FLAG_GELIBOOT)) { 967297691Sallanjude if (verbose) 968297691Sallanjude printf("GELIBOOT flag not configured for %s.\n", prov); 969297691Sallanjude } else if (geliboot >= 0) { 970297691Sallanjude if (geliboot) 971297691Sallanjude md.md_flags |= G_ELI_FLAG_GELIBOOT; 972297691Sallanjude else 973297691Sallanjude md.md_flags &= ~G_ELI_FLAG_GELIBOOT; 974297691Sallanjude changed = 1; 975297691Sallanjude } 976297691Sallanjude 977329114Skevans if (displaypass == 1 && (md.md_flags & G_ELI_FLAG_GELIDISPLAYPASS)) { 978329114Skevans if (verbose) 979329114Skevans printf("GELIDISPLAYPASS flag already configured for %s.\n", prov); 980329114Skevans } else if (displaypass == 0 && 981329114Skevans !(md.md_flags & G_ELI_FLAG_GELIDISPLAYPASS)) { 982329114Skevans if (verbose) 983329114Skevans printf("GELIDISPLAYPASS flag not configured for %s.\n", prov); 984329114Skevans } else if (displaypass >= 0) { 985329114Skevans if (displaypass) 986329114Skevans md.md_flags |= G_ELI_FLAG_GELIDISPLAYPASS; 987329114Skevans else 988329114Skevans md.md_flags &= ~G_ELI_FLAG_GELIDISPLAYPASS; 989329114Skevans changed = 1; 990329114Skevans } 991329114Skevans 992286444Spjd if (trim == 0 && (md.md_flags & G_ELI_FLAG_NODELETE)) { 993286444Spjd if (verbose) 994286444Spjd printf("TRIM disable flag already configured for %s.\n", prov); 995286444Spjd } else if (trim == 1 && !(md.md_flags & G_ELI_FLAG_NODELETE)) { 996286444Spjd if (verbose) 997286444Spjd printf("TRIM disable flag not configured for %s.\n", prov); 998286444Spjd } else if (trim >= 0) { 999286444Spjd if (trim) 1000286444Spjd md.md_flags &= ~G_ELI_FLAG_NODELETE; 1001286444Spjd else 1002286444Spjd md.md_flags |= G_ELI_FLAG_NODELETE; 1003286444Spjd changed = 1; 1004286444Spjd } 1005286444Spjd 1006286444Spjd if (changed) 1007162353Spjd eli_metadata_store(req, prov, &md); 1008162353Spjd bzero(&md, sizeof(md)); 1009162353Spjd} 1010162353Spjd 1011162353Spjdstatic void 1012162353Spjdeli_configure(struct gctl_req *req) 1013162353Spjd{ 1014162353Spjd const char *prov; 1015329114Skevans bool boot, noboot, geliboot, nogeliboot, displaypass, nodisplaypass; 1016329114Skevans bool trim, notrim; 1017329114Skevans int doboot, dogeliboot, dodisplaypass, dotrim; 1018213172Spjd int i, nargs; 1019162353Spjd 1020162353Spjd nargs = gctl_get_int(req, "nargs"); 1021162353Spjd if (nargs == 0) { 1022162353Spjd gctl_error(req, "Too few arguments."); 1023162353Spjd return; 1024162353Spjd } 1025162353Spjd 1026162353Spjd boot = gctl_get_int(req, "boot"); 1027162353Spjd noboot = gctl_get_int(req, "noboot"); 1028297691Sallanjude geliboot = gctl_get_int(req, "geliboot"); 1029297691Sallanjude nogeliboot = gctl_get_int(req, "nogeliboot"); 1030329114Skevans displaypass = gctl_get_int(req, "displaypass"); 1031329114Skevans nodisplaypass = gctl_get_int(req, "nodisplaypass"); 1032286444Spjd trim = gctl_get_int(req, "trim"); 1033286444Spjd notrim = gctl_get_int(req, "notrim"); 1034162353Spjd 1035286444Spjd doboot = -1; 1036162353Spjd if (boot && noboot) { 1037162353Spjd gctl_error(req, "Options -b and -B are mutually exclusive."); 1038162353Spjd return; 1039162353Spjd } 1040286444Spjd if (boot) 1041286444Spjd doboot = 1; 1042286444Spjd else if (noboot) 1043286444Spjd doboot = 0; 1044286444Spjd 1045297691Sallanjude dogeliboot = -1; 1046297691Sallanjude if (geliboot && nogeliboot) { 1047297691Sallanjude gctl_error(req, "Options -g and -G are mutually exclusive."); 1048297691Sallanjude return; 1049297691Sallanjude } 1050297691Sallanjude if (geliboot) 1051297691Sallanjude dogeliboot = 1; 1052297691Sallanjude else if (nogeliboot) 1053297691Sallanjude dogeliboot = 0; 1054297691Sallanjude 1055329114Skevans dodisplaypass = -1; 1056329114Skevans if (displaypass && nodisplaypass) { 1057329114Skevans gctl_error(req, "Options -d and -D are mutually exclusive."); 1058329114Skevans return; 1059329114Skevans } 1060329114Skevans if (displaypass) 1061329114Skevans dodisplaypass = 1; 1062329114Skevans else if (nodisplaypass) 1063329114Skevans dodisplaypass = 0; 1064329114Skevans 1065286444Spjd dotrim = -1; 1066286444Spjd if (trim && notrim) { 1067286444Spjd gctl_error(req, "Options -t and -T are mutually exclusive."); 1068286444Spjd return; 1069286444Spjd } 1070286444Spjd if (trim) 1071286444Spjd dotrim = 1; 1072286444Spjd else if (notrim) 1073286444Spjd dotrim = 0; 1074286444Spjd 1075329114Skevans if (doboot == -1 && dogeliboot == -1 && dodisplaypass == -1 && 1076329114Skevans dotrim == -1) { 1077162353Spjd gctl_error(req, "No option given."); 1078162353Spjd return; 1079162353Spjd } 1080162353Spjd 1081162353Spjd /* First attached providers. */ 1082162353Spjd gctl_issue(req); 1083162353Spjd /* Now the rest. */ 1084162353Spjd for (i = 0; i < nargs; i++) { 1085162353Spjd prov = gctl_get_ascii(req, "arg%d", i); 1086329114Skevans if (!eli_is_attached(prov)) { 1087329114Skevans eli_configure_detached(req, prov, doboot, dogeliboot, 1088329114Skevans dodisplaypass, dotrim); 1089329114Skevans } 1090162353Spjd } 1091162353Spjd} 1092162353Spjd 1093162353Spjdstatic void 1094155101Spjdeli_setkey_attached(struct gctl_req *req, struct g_eli_metadata *md) 1095148456Spjd{ 1096148456Spjd unsigned char key[G_ELI_USERKEYLEN]; 1097166216Spjd intmax_t val, old = 0; 1098166216Spjd int error; 1099148456Spjd 1100153190Spjd val = gctl_get_intmax(req, "iterations"); 1101149304Spjd /* Check if iterations number should be changed. */ 1102153190Spjd if (val != -1) 1103153190Spjd md->md_iterations = val; 1104166216Spjd else 1105166216Spjd old = md->md_iterations; 1106148456Spjd 1107148456Spjd /* Generate key for Master Key encryption. */ 1108213172Spjd if (eli_genkey(req, md, key, true) == NULL) { 1109148456Spjd bzero(key, sizeof(key)); 1110148456Spjd return; 1111148456Spjd } 1112166216Spjd /* 1113166216Spjd * If number of iterations has changed, but wasn't given as a 1114166216Spjd * command-line argument, update the request. 1115166216Spjd */ 1116166216Spjd if (val == -1 && md->md_iterations != old) { 1117166216Spjd error = gctl_change_param(req, "iterations", sizeof(intmax_t), 1118166216Spjd &md->md_iterations); 1119166216Spjd assert(error == 0); 1120166216Spjd } 1121148456Spjd 1122148456Spjd gctl_ro_param(req, "key", sizeof(key), key); 1123148456Spjd gctl_issue(req); 1124148456Spjd bzero(key, sizeof(key)); 1125148456Spjd} 1126148456Spjd 1127148456Spjdstatic void 1128149304Spjdeli_setkey_detached(struct gctl_req *req, const char *prov, 1129149304Spjd struct g_eli_metadata *md) 1130148456Spjd{ 1131148456Spjd unsigned char key[G_ELI_USERKEYLEN], mkey[G_ELI_DATAIVKEYLEN]; 1132148456Spjd unsigned char *mkeydst; 1133213172Spjd unsigned int nkey; 1134153190Spjd intmax_t val; 1135148456Spjd int error; 1136148456Spjd 1137149928Spjd if (md->md_keys == 0) { 1138149928Spjd gctl_error(req, "No valid keys on %s.", prov); 1139149928Spjd return; 1140149928Spjd } 1141149928Spjd 1142148456Spjd /* Generate key for Master Key decryption. */ 1143213172Spjd if (eli_genkey(req, md, key, false) == NULL) { 1144148456Spjd bzero(key, sizeof(key)); 1145148456Spjd return; 1146148456Spjd } 1147148456Spjd 1148148456Spjd /* Decrypt Master Key. */ 1149344397Skevans error = g_eli_mkey_decrypt_any(md, key, mkey, &nkey); 1150148456Spjd bzero(key, sizeof(key)); 1151148456Spjd if (error != 0) { 1152149304Spjd bzero(md, sizeof(*md)); 1153148456Spjd if (error == -1) 1154148456Spjd gctl_error(req, "Wrong key for %s.", prov); 1155148456Spjd else /* if (error > 0) */ { 1156148456Spjd gctl_error(req, "Cannot decrypt Master Key: %s.", 1157148456Spjd strerror(error)); 1158148456Spjd } 1159148456Spjd return; 1160148456Spjd } 1161148456Spjd if (verbose) 1162148456Spjd printf("Decrypted Master Key %u.\n", nkey); 1163148456Spjd 1164153190Spjd val = gctl_get_intmax(req, "keyno"); 1165153190Spjd if (val != -1) 1166153190Spjd nkey = val; 1167148456Spjd#if 0 1168148456Spjd else 1169148456Spjd ; /* Use the key number which was found during decryption. */ 1170148456Spjd#endif 1171148456Spjd if (nkey >= G_ELI_MAXMKEYS) { 1172148456Spjd gctl_error(req, "Invalid '%s' argument.", "keyno"); 1173148456Spjd return; 1174148456Spjd } 1175148456Spjd 1176153190Spjd val = gctl_get_intmax(req, "iterations"); 1177149304Spjd /* Check if iterations number should and can be changed. */ 1178317858Smav if (val != -1 && md->md_iterations == -1) { 1179317858Smav md->md_iterations = val; 1180317858Smav } else if (val != -1 && val != md->md_iterations) { 1181149304Spjd if (bitcount32(md->md_keys) != 1) { 1182149304Spjd gctl_error(req, "To be able to use '-i' option, only " 1183149304Spjd "one key can be defined."); 1184149304Spjd return; 1185149304Spjd } 1186149304Spjd if (md->md_keys != (1 << nkey)) { 1187149304Spjd gctl_error(req, "Only already defined key can be " 1188149304Spjd "changed when '-i' option is used."); 1189149304Spjd return; 1190149304Spjd } 1191153190Spjd md->md_iterations = val; 1192149304Spjd } 1193148456Spjd 1194149304Spjd mkeydst = md->md_mkeys + nkey * G_ELI_MKEYLEN; 1195149304Spjd md->md_keys |= (1 << nkey); 1196149304Spjd 1197148456Spjd bcopy(mkey, mkeydst, sizeof(mkey)); 1198148456Spjd bzero(mkey, sizeof(mkey)); 1199148456Spjd 1200148456Spjd /* Generate key for Master Key encryption. */ 1201213172Spjd if (eli_genkey(req, md, key, true) == NULL) { 1202148456Spjd bzero(key, sizeof(key)); 1203149304Spjd bzero(md, sizeof(*md)); 1204148456Spjd return; 1205148456Spjd } 1206148456Spjd 1207148456Spjd /* Encrypt the Master-Key with the new key. */ 1208159308Spjd error = g_eli_mkey_encrypt(md->md_ealgo, key, md->md_keylen, mkeydst); 1209148456Spjd bzero(key, sizeof(key)); 1210148456Spjd if (error != 0) { 1211149304Spjd bzero(md, sizeof(*md)); 1212148456Spjd gctl_error(req, "Cannot encrypt Master Key: %s.", 1213148456Spjd strerror(error)); 1214148456Spjd return; 1215148456Spjd } 1216148456Spjd 1217148456Spjd /* Store metadata with fresh key. */ 1218149304Spjd eli_metadata_store(req, prov, md); 1219149304Spjd bzero(md, sizeof(*md)); 1220148456Spjd} 1221148456Spjd 1222148456Spjdstatic void 1223148456Spjdeli_setkey(struct gctl_req *req) 1224148456Spjd{ 1225149304Spjd struct g_eli_metadata md; 1226148456Spjd const char *prov; 1227153190Spjd int nargs; 1228148456Spjd 1229153190Spjd nargs = gctl_get_int(req, "nargs"); 1230153190Spjd if (nargs != 1) { 1231158214Spjd gctl_error(req, "Invalid number of arguments."); 1232148456Spjd return; 1233148456Spjd } 1234153190Spjd prov = gctl_get_ascii(req, "arg0"); 1235148456Spjd 1236149304Spjd if (eli_metadata_read(req, prov, &md) == -1) 1237149304Spjd return; 1238149304Spjd 1239148456Spjd if (eli_is_attached(prov)) 1240155101Spjd eli_setkey_attached(req, &md); 1241148456Spjd else 1242149304Spjd eli_setkey_detached(req, prov, &md); 1243182452Spjd 1244182452Spjd if (req->error == NULL || req->error[0] == '\0') { 1245182452Spjd printf("Note, that the master key encrypted with old keys " 1246182452Spjd "and/or passphrase may still exists in a metadata backup " 1247182452Spjd "file.\n"); 1248182452Spjd } 1249148456Spjd} 1250148456Spjd 1251148456Spjdstatic void 1252148456Spjdeli_delkey_attached(struct gctl_req *req, const char *prov __unused) 1253148456Spjd{ 1254148456Spjd 1255148456Spjd gctl_issue(req); 1256148456Spjd} 1257148456Spjd 1258148456Spjdstatic void 1259148456Spjdeli_delkey_detached(struct gctl_req *req, const char *prov) 1260148456Spjd{ 1261148456Spjd struct g_eli_metadata md; 1262148456Spjd unsigned char *mkeydst; 1263213172Spjd unsigned int nkey; 1264153190Spjd intmax_t val; 1265213172Spjd bool all, force; 1266148456Spjd 1267148456Spjd if (eli_metadata_read(req, prov, &md) == -1) 1268148456Spjd return; 1269148456Spjd 1270153190Spjd all = gctl_get_int(req, "all"); 1271153190Spjd if (all) 1272246620Spjd arc4random_buf(md.md_mkeys, sizeof(md.md_mkeys)); 1273148456Spjd else { 1274153190Spjd force = gctl_get_int(req, "force"); 1275153190Spjd val = gctl_get_intmax(req, "keyno"); 1276153190Spjd if (val == -1) { 1277148456Spjd gctl_error(req, "Key number has to be specified."); 1278148456Spjd return; 1279148456Spjd } 1280153190Spjd nkey = val; 1281148456Spjd if (nkey >= G_ELI_MAXMKEYS) { 1282148456Spjd gctl_error(req, "Invalid '%s' argument.", "keyno"); 1283148456Spjd return; 1284148456Spjd } 1285153190Spjd if (!(md.md_keys & (1 << nkey)) && !force) { 1286148456Spjd gctl_error(req, "Master Key %u is not set.", nkey); 1287148456Spjd return; 1288148456Spjd } 1289148456Spjd md.md_keys &= ~(1 << nkey); 1290153190Spjd if (md.md_keys == 0 && !force) { 1291148456Spjd gctl_error(req, "This is the last Master Key. Use '-f' " 1292148456Spjd "option if you really want to remove it."); 1293148456Spjd return; 1294148456Spjd } 1295148456Spjd mkeydst = md.md_mkeys + nkey * G_ELI_MKEYLEN; 1296246620Spjd arc4random_buf(mkeydst, G_ELI_MKEYLEN); 1297148456Spjd } 1298148456Spjd 1299148456Spjd eli_metadata_store(req, prov, &md); 1300148456Spjd bzero(&md, sizeof(md)); 1301148456Spjd} 1302148456Spjd 1303148456Spjdstatic void 1304148456Spjdeli_delkey(struct gctl_req *req) 1305148456Spjd{ 1306148456Spjd const char *prov; 1307153190Spjd int nargs; 1308148456Spjd 1309153190Spjd nargs = gctl_get_int(req, "nargs"); 1310153190Spjd if (nargs != 1) { 1311158214Spjd gctl_error(req, "Invalid number of arguments."); 1312148456Spjd return; 1313148456Spjd } 1314153190Spjd prov = gctl_get_ascii(req, "arg0"); 1315148456Spjd 1316148456Spjd if (eli_is_attached(prov)) 1317148456Spjd eli_delkey_attached(req, prov); 1318148456Spjd else 1319148456Spjd eli_delkey_detached(req, prov); 1320148456Spjd} 1321148456Spjd 1322214118Spjdstatic void 1323214118Spjdeli_resume(struct gctl_req *req) 1324214118Spjd{ 1325214118Spjd struct g_eli_metadata md; 1326214118Spjd unsigned char key[G_ELI_USERKEYLEN]; 1327214118Spjd const char *prov; 1328214118Spjd off_t mediasize; 1329214118Spjd int nargs; 1330214118Spjd 1331214118Spjd nargs = gctl_get_int(req, "nargs"); 1332214118Spjd if (nargs != 1) { 1333214118Spjd gctl_error(req, "Invalid number of arguments."); 1334214118Spjd return; 1335214118Spjd } 1336214118Spjd prov = gctl_get_ascii(req, "arg0"); 1337214118Spjd 1338214118Spjd if (eli_metadata_read(req, prov, &md) == -1) 1339214118Spjd return; 1340214118Spjd 1341214118Spjd mediasize = g_get_mediasize(prov); 1342214118Spjd if (md.md_provsize != (uint64_t)mediasize) { 1343214118Spjd gctl_error(req, "Provider size mismatch."); 1344214118Spjd return; 1345214118Spjd } 1346214118Spjd 1347214118Spjd if (eli_genkey(req, &md, key, false) == NULL) { 1348214118Spjd bzero(key, sizeof(key)); 1349214118Spjd return; 1350214118Spjd } 1351214118Spjd 1352214118Spjd gctl_ro_param(req, "key", sizeof(key), key); 1353214118Spjd if (gctl_issue(req) == NULL) { 1354214118Spjd if (verbose) 1355214118Spjd printf("Resumed %s.\n", prov); 1356214118Spjd } 1357214118Spjd bzero(key, sizeof(key)); 1358214118Spjd} 1359214118Spjd 1360213060Spjdstatic int 1361213060Spjdeli_trash_metadata(struct gctl_req *req, const char *prov, int fd, off_t offset) 1362213060Spjd{ 1363213060Spjd unsigned int overwrites; 1364213060Spjd unsigned char *sector; 1365213060Spjd ssize_t size; 1366213060Spjd int error; 1367213060Spjd 1368213060Spjd size = sizeof(overwrites); 1369213060Spjd if (sysctlbyname("kern.geom.eli.overwrites", &overwrites, &size, 1370213060Spjd NULL, 0) == -1 || overwrites == 0) { 1371213060Spjd overwrites = G_ELI_OVERWRITES; 1372213060Spjd } 1373213060Spjd 1374213060Spjd size = g_sectorsize(fd); 1375213060Spjd if (size <= 0) { 1376213060Spjd gctl_error(req, "Cannot obtain provider sector size %s: %s.", 1377213060Spjd prov, strerror(errno)); 1378213060Spjd return (-1); 1379213060Spjd } 1380213060Spjd sector = malloc(size); 1381213060Spjd if (sector == NULL) { 1382213060Spjd gctl_error(req, "Cannot allocate %zd bytes of memory.", size); 1383213060Spjd return (-1); 1384213060Spjd } 1385213060Spjd 1386213060Spjd error = 0; 1387213060Spjd do { 1388246620Spjd arc4random_buf(sector, size); 1389213060Spjd if (pwrite(fd, sector, size, offset) != size) { 1390213060Spjd if (error == 0) 1391213060Spjd error = errno; 1392213060Spjd } 1393213060Spjd (void)g_flush(fd); 1394213060Spjd } while (--overwrites > 0); 1395246622Spjd free(sector); 1396213060Spjd if (error != 0) { 1397213060Spjd gctl_error(req, "Cannot trash metadata on provider %s: %s.", 1398213060Spjd prov, strerror(error)); 1399213060Spjd return (-1); 1400213060Spjd } 1401213060Spjd return (0); 1402213060Spjd} 1403213060Spjd 1404148456Spjdstatic void 1405148456Spjdeli_kill_detached(struct gctl_req *req, const char *prov) 1406148456Spjd{ 1407213060Spjd off_t offset; 1408213060Spjd int fd; 1409148456Spjd 1410148456Spjd /* 1411148456Spjd * NOTE: Maybe we should verify if this is geli provider first, 1412148456Spjd * but 'kill' command is quite critical so better don't waste 1413148456Spjd * the time. 1414148456Spjd */ 1415148456Spjd#if 0 1416148456Spjd error = g_metadata_read(prov, (unsigned char *)&md, sizeof(md), 1417148456Spjd G_ELI_MAGIC); 1418148456Spjd if (error != 0) { 1419148456Spjd gctl_error(req, "Cannot read metadata from %s: %s.", prov, 1420148456Spjd strerror(error)); 1421148456Spjd return; 1422148456Spjd } 1423148456Spjd#endif 1424148456Spjd 1425213060Spjd fd = g_open(prov, 1); 1426213060Spjd if (fd == -1) { 1427213060Spjd gctl_error(req, "Cannot open provider %s: %s.", prov, 1428213060Spjd strerror(errno)); 1429213060Spjd return; 1430148456Spjd } 1431213060Spjd offset = g_mediasize(fd) - g_sectorsize(fd); 1432213060Spjd if (offset <= 0) { 1433213060Spjd gctl_error(req, 1434213060Spjd "Cannot obtain media size or sector size for provider %s: %s.", 1435213060Spjd prov, strerror(errno)); 1436213060Spjd (void)g_close(fd); 1437213060Spjd return; 1438213060Spjd } 1439213060Spjd (void)eli_trash_metadata(req, prov, fd, offset); 1440213060Spjd (void)g_close(fd); 1441148456Spjd} 1442148456Spjd 1443148456Spjdstatic void 1444148456Spjdeli_kill(struct gctl_req *req) 1445148456Spjd{ 1446148456Spjd const char *prov; 1447153190Spjd int i, nargs, all; 1448148456Spjd 1449153190Spjd nargs = gctl_get_int(req, "nargs"); 1450153190Spjd all = gctl_get_int(req, "all"); 1451153190Spjd if (!all && nargs == 0) { 1452148456Spjd gctl_error(req, "Too few arguments."); 1453148456Spjd return; 1454148456Spjd } 1455148456Spjd /* 1456148456Spjd * How '-a' option combine with a list of providers: 1457148456Spjd * Delete Master Keys from all attached providers: 1458148456Spjd * geli kill -a 1459169312Spjd * Delete Master Keys from all attached providers and from 1460148456Spjd * detached da0 and da1: 1461148456Spjd * geli kill -a da0 da1 1462148456Spjd * Delete Master Keys from (attached or detached) da0 and da1: 1463148456Spjd * geli kill da0 da1 1464148456Spjd */ 1465148456Spjd 1466169312Spjd /* First detached providers. */ 1467153190Spjd for (i = 0; i < nargs; i++) { 1468153190Spjd prov = gctl_get_ascii(req, "arg%d", i); 1469148456Spjd if (!eli_is_attached(prov)) 1470148456Spjd eli_kill_detached(req, prov); 1471148456Spjd } 1472162347Spjd /* Now attached providers. */ 1473162347Spjd gctl_issue(req); 1474148456Spjd} 1475148456Spjd 1476182452Spjdstatic int 1477182452Spjdeli_backup_create(struct gctl_req *req, const char *prov, const char *file) 1478148456Spjd{ 1479148456Spjd unsigned char *sector; 1480213059Spjd ssize_t secsize; 1481226716Spjd int error, filefd, ret; 1482148456Spjd 1483182452Spjd ret = -1; 1484226716Spjd filefd = -1; 1485148456Spjd sector = NULL; 1486148456Spjd secsize = 0; 1487148456Spjd 1488226716Spjd secsize = g_get_sectorsize(prov); 1489226716Spjd if (secsize == 0) { 1490148456Spjd gctl_error(req, "Cannot get informations about %s: %s.", prov, 1491148456Spjd strerror(errno)); 1492169193Spjd goto out; 1493148456Spjd } 1494148456Spjd sector = malloc(secsize); 1495148456Spjd if (sector == NULL) { 1496148456Spjd gctl_error(req, "Cannot allocate memory."); 1497169193Spjd goto out; 1498148456Spjd } 1499148456Spjd /* Read metadata from the provider. */ 1500226716Spjd error = g_metadata_read(prov, sector, secsize, G_ELI_MAGIC); 1501226716Spjd if (error != 0) { 1502226716Spjd gctl_error(req, "Unable to read metadata from %s: %s.", prov, 1503226716Spjd strerror(error)); 1504148456Spjd goto out; 1505148456Spjd } 1506226716Spjd 1507226716Spjd filefd = open(file, O_WRONLY | O_TRUNC | O_CREAT, 0600); 1508226716Spjd if (filefd == -1) { 1509226716Spjd gctl_error(req, "Unable to open %s: %s.", file, 1510226716Spjd strerror(errno)); 1511148456Spjd goto out; 1512148456Spjd } 1513148456Spjd /* Write metadata to the destination file. */ 1514213059Spjd if (write(filefd, sector, secsize) != secsize) { 1515226716Spjd gctl_error(req, "Unable to write to %s: %s.", file, 1516148456Spjd strerror(errno)); 1517226716Spjd (void)close(filefd); 1518226716Spjd (void)unlink(file); 1519148456Spjd goto out; 1520148456Spjd } 1521213059Spjd (void)fsync(filefd); 1522226716Spjd (void)close(filefd); 1523182452Spjd /* Success. */ 1524182452Spjd ret = 0; 1525148456Spjdout: 1526148456Spjd if (sector != NULL) { 1527148456Spjd bzero(sector, secsize); 1528148456Spjd free(sector); 1529148456Spjd } 1530182452Spjd return (ret); 1531148456Spjd} 1532148456Spjd 1533148456Spjdstatic void 1534182452Spjdeli_backup(struct gctl_req *req) 1535182452Spjd{ 1536182452Spjd const char *file, *prov; 1537182452Spjd int nargs; 1538182452Spjd 1539182452Spjd nargs = gctl_get_int(req, "nargs"); 1540182452Spjd if (nargs != 2) { 1541182452Spjd gctl_error(req, "Invalid number of arguments."); 1542182452Spjd return; 1543182452Spjd } 1544182452Spjd prov = gctl_get_ascii(req, "arg0"); 1545182452Spjd file = gctl_get_ascii(req, "arg1"); 1546182452Spjd 1547182452Spjd eli_backup_create(req, prov, file); 1548182452Spjd} 1549182452Spjd 1550182452Spjdstatic void 1551148456Spjdeli_restore(struct gctl_req *req) 1552148456Spjd{ 1553148456Spjd struct g_eli_metadata md; 1554148456Spjd const char *file, *prov; 1555148456Spjd off_t mediasize; 1556226716Spjd int nargs; 1557148456Spjd 1558153190Spjd nargs = gctl_get_int(req, "nargs"); 1559153190Spjd if (nargs != 2) { 1560148456Spjd gctl_error(req, "Invalid number of arguments."); 1561148456Spjd return; 1562148456Spjd } 1563153190Spjd file = gctl_get_ascii(req, "arg0"); 1564153190Spjd prov = gctl_get_ascii(req, "arg1"); 1565148456Spjd 1566226716Spjd /* Read metadata from the backup file. */ 1567226716Spjd if (eli_metadata_read(req, file, &md) == -1) 1568226716Spjd return; 1569226716Spjd /* Obtain provider's mediasize. */ 1570226716Spjd mediasize = g_get_mediasize(prov); 1571226716Spjd if (mediasize == 0) { 1572148456Spjd gctl_error(req, "Cannot get informations about %s: %s.", prov, 1573148456Spjd strerror(errno)); 1574226716Spjd return; 1575148456Spjd } 1576212934Sbrian /* Check if the provider size has changed since we did the backup. */ 1577212934Sbrian if (md.md_provsize != (uint64_t)mediasize) { 1578212934Sbrian if (gctl_get_int(req, "force")) { 1579212934Sbrian md.md_provsize = mediasize; 1580212934Sbrian } else { 1581212934Sbrian gctl_error(req, "Provider size mismatch: " 1582212934Sbrian "wrong backup file?"); 1583226716Spjd return; 1584212934Sbrian } 1585212934Sbrian } 1586226716Spjd /* Write metadata to the provider. */ 1587226716Spjd (void)eli_metadata_store(req, prov, &md); 1588148456Spjd} 1589148456Spjd 1590148456Spjdstatic void 1591212934Sbrianeli_resize(struct gctl_req *req) 1592212934Sbrian{ 1593212934Sbrian struct g_eli_metadata md; 1594212934Sbrian const char *prov; 1595212934Sbrian unsigned char *sector; 1596213056Spjd ssize_t secsize; 1597212934Sbrian off_t mediasize, oldsize; 1598226722Spjd int error, nargs, provfd; 1599212934Sbrian 1600212934Sbrian nargs = gctl_get_int(req, "nargs"); 1601212934Sbrian if (nargs != 1) { 1602212934Sbrian gctl_error(req, "Invalid number of arguments."); 1603212934Sbrian return; 1604212934Sbrian } 1605212934Sbrian prov = gctl_get_ascii(req, "arg0"); 1606212934Sbrian 1607212934Sbrian provfd = -1; 1608212934Sbrian sector = NULL; 1609212934Sbrian secsize = 0; 1610212934Sbrian 1611213056Spjd provfd = g_open(prov, 1); 1612212934Sbrian if (provfd == -1) { 1613212934Sbrian gctl_error(req, "Cannot open %s: %s.", prov, strerror(errno)); 1614212934Sbrian goto out; 1615212934Sbrian } 1616212934Sbrian 1617213056Spjd mediasize = g_mediasize(provfd); 1618213056Spjd secsize = g_sectorsize(provfd); 1619213056Spjd if (mediasize == -1 || secsize == -1) { 1620212934Sbrian gctl_error(req, "Cannot get information about %s: %s.", prov, 1621212934Sbrian strerror(errno)); 1622212934Sbrian goto out; 1623212934Sbrian } 1624212934Sbrian 1625212934Sbrian sector = malloc(secsize); 1626212934Sbrian if (sector == NULL) { 1627212934Sbrian gctl_error(req, "Cannot allocate memory."); 1628212934Sbrian goto out; 1629212934Sbrian } 1630212934Sbrian 1631212934Sbrian oldsize = gctl_get_intmax(req, "oldsize"); 1632212934Sbrian if (oldsize < 0 || oldsize > mediasize) { 1633212934Sbrian gctl_error(req, "Invalid oldsize: Out of range."); 1634212934Sbrian goto out; 1635212934Sbrian } 1636213058Spjd if (oldsize == mediasize) { 1637213058Spjd gctl_error(req, "Size hasn't changed."); 1638213058Spjd goto out; 1639213058Spjd } 1640212934Sbrian 1641212934Sbrian /* Read metadata from the 'oldsize' offset. */ 1642213056Spjd if (pread(provfd, sector, secsize, oldsize - secsize) != secsize) { 1643212934Sbrian gctl_error(req, "Cannot read old metadata: %s.", 1644212934Sbrian strerror(errno)); 1645212934Sbrian goto out; 1646212934Sbrian } 1647212934Sbrian 1648212934Sbrian /* Check if this sector contains geli metadata. */ 1649226722Spjd error = eli_metadata_decode(sector, &md); 1650226722Spjd switch (error) { 1651226722Spjd case 0: 1652226722Spjd break; 1653226722Spjd case EOPNOTSUPP: 1654226722Spjd gctl_error(req, 1655226722Spjd "Provider's %s metadata version %u is too new.\n" 1656226722Spjd "geli: The highest supported version is %u.", 1657226722Spjd prov, (unsigned int)md.md_version, G_ELI_VERSION); 1658212934Sbrian goto out; 1659226722Spjd case EINVAL: 1660226722Spjd gctl_error(req, "Inconsistent provider's %s metadata.", prov); 1661226722Spjd goto out; 1662226722Spjd default: 1663226722Spjd gctl_error(req, 1664226722Spjd "Unexpected error while decoding provider's %s metadata: %s.", 1665226722Spjd prov, strerror(error)); 1666226722Spjd goto out; 1667212934Sbrian } 1668212934Sbrian 1669212934Sbrian /* 1670212934Sbrian * If the old metadata doesn't have a correct provider size, refuse 1671212934Sbrian * to resize. 1672212934Sbrian */ 1673212934Sbrian if (md.md_provsize != (uint64_t)oldsize) { 1674212934Sbrian gctl_error(req, "Provider size mismatch at oldsize."); 1675212934Sbrian goto out; 1676212934Sbrian } 1677212934Sbrian 1678212934Sbrian /* 1679212934Sbrian * Update the old metadata with the current provider size and write 1680212934Sbrian * it back to the correct place on the provider. 1681212934Sbrian */ 1682212934Sbrian md.md_provsize = mediasize; 1683226720Spjd /* Write metadata to the provider. */ 1684226720Spjd (void)eli_metadata_store(req, prov, &md); 1685212934Sbrian /* Now trash the old metadata. */ 1686226720Spjd (void)eli_trash_metadata(req, prov, provfd, oldsize - secsize); 1687212934Sbrianout: 1688226720Spjd if (provfd != -1) 1689213056Spjd (void)g_close(provfd); 1690212934Sbrian if (sector != NULL) { 1691212934Sbrian bzero(sector, secsize); 1692212934Sbrian free(sector); 1693212934Sbrian } 1694212934Sbrian} 1695212934Sbrian 1696212934Sbrianstatic void 1697226723Spjdeli_version(struct gctl_req *req) 1698226723Spjd{ 1699226723Spjd struct g_eli_metadata md; 1700226723Spjd const char *name; 1701226723Spjd unsigned int version; 1702226723Spjd int error, i, nargs; 1703226723Spjd 1704226723Spjd nargs = gctl_get_int(req, "nargs"); 1705226723Spjd 1706226723Spjd if (nargs == 0) { 1707226723Spjd unsigned int kernver; 1708226723Spjd ssize_t size; 1709226723Spjd 1710226723Spjd size = sizeof(kernver); 1711226723Spjd if (sysctlbyname("kern.geom.eli.version", &kernver, &size, 1712226723Spjd NULL, 0) == -1) { 1713226723Spjd warn("Unable to obtain GELI kernel version"); 1714226723Spjd } else { 1715226723Spjd printf("kernel: %u\n", kernver); 1716226723Spjd } 1717226723Spjd printf("userland: %u\n", G_ELI_VERSION); 1718226723Spjd return; 1719226723Spjd } 1720226723Spjd 1721226723Spjd for (i = 0; i < nargs; i++) { 1722226723Spjd name = gctl_get_ascii(req, "arg%d", i); 1723226723Spjd error = g_metadata_read(name, (unsigned char *)&md, 1724226723Spjd sizeof(md), G_ELI_MAGIC); 1725226723Spjd if (error != 0) { 1726226723Spjd warn("%s: Unable to read metadata: %s.", name, 1727226723Spjd strerror(error)); 1728226723Spjd gctl_error(req, "Not fully done."); 1729226723Spjd continue; 1730226723Spjd } 1731226723Spjd version = le32dec(&md.md_version); 1732226723Spjd printf("%s: %u\n", name, version); 1733226723Spjd } 1734226723Spjd} 1735226723Spjd 1736226723Spjdstatic void 1737148456Spjdeli_clear(struct gctl_req *req) 1738148456Spjd{ 1739148456Spjd const char *name; 1740153190Spjd int error, i, nargs; 1741148456Spjd 1742153190Spjd nargs = gctl_get_int(req, "nargs"); 1743153190Spjd if (nargs < 1) { 1744148456Spjd gctl_error(req, "Too few arguments."); 1745148456Spjd return; 1746148456Spjd } 1747148456Spjd 1748153190Spjd for (i = 0; i < nargs; i++) { 1749153190Spjd name = gctl_get_ascii(req, "arg%d", i); 1750148456Spjd error = g_metadata_clear(name, G_ELI_MAGIC); 1751148456Spjd if (error != 0) { 1752148456Spjd fprintf(stderr, "Cannot clear metadata on %s: %s.\n", 1753148456Spjd name, strerror(error)); 1754148456Spjd gctl_error(req, "Not fully done."); 1755148456Spjd continue; 1756148456Spjd } 1757148456Spjd if (verbose) 1758155175Spjd printf("Metadata cleared on %s.\n", name); 1759148456Spjd } 1760148456Spjd} 1761148456Spjd 1762148456Spjdstatic void 1763148456Spjdeli_dump(struct gctl_req *req) 1764148456Spjd{ 1765226719Spjd struct g_eli_metadata md; 1766148456Spjd const char *name; 1767226719Spjd int i, nargs; 1768148456Spjd 1769153190Spjd nargs = gctl_get_int(req, "nargs"); 1770153190Spjd if (nargs < 1) { 1771148456Spjd gctl_error(req, "Too few arguments."); 1772148456Spjd return; 1773148456Spjd } 1774148456Spjd 1775153190Spjd for (i = 0; i < nargs; i++) { 1776153190Spjd name = gctl_get_ascii(req, "arg%d", i); 1777226719Spjd if (eli_metadata_read(NULL, name, &md) == -1) { 1778148456Spjd gctl_error(req, "Not fully done."); 1779148456Spjd continue; 1780148456Spjd } 1781148456Spjd printf("Metadata on %s:\n", name); 1782148456Spjd eli_metadata_dump(&md); 1783148456Spjd printf("\n"); 1784148456Spjd } 1785148456Spjd} 1786