geom_eli.c revision 330449
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 330449 2018-03-05 07:26:05Z eadler $"); 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' 89213172Spjd * attach [-dprv] [-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 { 148162868Spjd { 'd', "detach", NULL, G_TYPE_BOOL }, 149213172Spjd { 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 150213172Spjd { 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 151162868Spjd { 'p', "nopassphrase", NULL, G_TYPE_BOOL }, 152162868Spjd { 'r', "readonly", NULL, G_TYPE_BOOL }, 153148456Spjd G_OPT_SENTINEL 154148456Spjd }, 155213172Spjd "[-dprv] [-j passfile] [-k keyfile] prov" 156148456Spjd }, 157148456Spjd { "detach", 0, NULL, 158148456Spjd { 159162868Spjd { 'f', "force", NULL, G_TYPE_BOOL }, 160162868Spjd { 'l', "last", NULL, G_TYPE_BOOL }, 161148456Spjd G_OPT_SENTINEL 162148456Spjd }, 163212554Spjd "[-fl] prov ..." 164148456Spjd }, 165148456Spjd { "stop", 0, NULL, 166148456Spjd { 167162868Spjd { 'f', "force", NULL, G_TYPE_BOOL }, 168162868Spjd { 'l', "last", NULL, G_TYPE_BOOL }, 169148456Spjd G_OPT_SENTINEL 170148456Spjd }, 171212554Spjd "- an alias for 'detach'" 172148456Spjd }, 173148456Spjd { "onetime", G_FLAG_VERBOSE | G_FLAG_LOADKLD, NULL, 174148456Spjd { 175212547Spjd { 'a', "aalgo", "", G_TYPE_STRING }, 176162868Spjd { 'd', "detach", NULL, G_TYPE_BOOL }, 177212547Spjd { 'e', "ealgo", GELI_ENC_ALGO, G_TYPE_STRING }, 178212554Spjd { 'l', "keylen", "0", G_TYPE_NUMBER }, 179212554Spjd { 's', "sectorsize", "0", G_TYPE_NUMBER }, 180286444Spjd { 'T', "notrim", NULL, G_TYPE_BOOL }, 181148456Spjd G_OPT_SENTINEL 182148456Spjd }, 183286444Spjd "[-dT] [-a aalgo] [-e ealgo] [-l keylen] [-s sectorsize] prov" 184148456Spjd }, 185162353Spjd { "configure", G_FLAG_VERBOSE, eli_main, 186162353Spjd { 187162868Spjd { 'b', "boot", NULL, G_TYPE_BOOL }, 188162868Spjd { 'B', "noboot", NULL, G_TYPE_BOOL }, 189329114Skevans { 'd', "displaypass", NULL, G_TYPE_BOOL }, 190329114Skevans { 'D', "nodisplaypass", NULL, G_TYPE_BOOL }, 191297691Sallanjude { 'g', "geliboot", NULL, G_TYPE_BOOL }, 192297691Sallanjude { 'G', "nogeliboot", NULL, G_TYPE_BOOL }, 193286444Spjd { 't', "trim", NULL, G_TYPE_BOOL }, 194286444Spjd { 'T', "notrim", NULL, G_TYPE_BOOL }, 195162353Spjd G_OPT_SENTINEL 196162353Spjd }, 197329114Skevans "[-bBdDgGtT] prov ..." 198162353Spjd }, 199148456Spjd { "setkey", G_FLAG_VERBOSE, eli_main, 200148456Spjd { 201212554Spjd { 'i', "iterations", "-1", G_TYPE_NUMBER }, 202213172Spjd { 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 203213172Spjd { 'J', "newpassfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 204213172Spjd { 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 205213172Spjd { 'K', "newkeyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 206212554Spjd { 'n', "keyno", "-1", G_TYPE_NUMBER }, 207162868Spjd { 'p', "nopassphrase", NULL, G_TYPE_BOOL }, 208162868Spjd { 'P', "nonewpassphrase", NULL, G_TYPE_BOOL }, 209148456Spjd G_OPT_SENTINEL 210148456Spjd }, 211213172Spjd "[-pPv] [-n keyno] [-i iterations] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov" 212148456Spjd }, 213148456Spjd { "delkey", G_FLAG_VERBOSE, eli_main, 214148456Spjd { 215162868Spjd { 'a', "all", NULL, G_TYPE_BOOL }, 216162868Spjd { 'f', "force", NULL, G_TYPE_BOOL }, 217212554Spjd { 'n', "keyno", "-1", G_TYPE_NUMBER }, 218148456Spjd G_OPT_SENTINEL 219148456Spjd }, 220212554Spjd "[-afv] [-n keyno] prov" 221148456Spjd }, 222214118Spjd { "suspend", G_FLAG_VERBOSE, NULL, 223214118Spjd { 224214118Spjd { 'a', "all", NULL, G_TYPE_BOOL }, 225214118Spjd G_OPT_SENTINEL 226214118Spjd }, 227214118Spjd "[-v] -a | prov ..." 228214118Spjd }, 229214118Spjd { "resume", G_FLAG_VERBOSE, eli_main, 230214118Spjd { 231214118Spjd { 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 232214118Spjd { 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 233214118Spjd { 'p', "nopassphrase", NULL, G_TYPE_BOOL }, 234214118Spjd G_OPT_SENTINEL 235214118Spjd }, 236214118Spjd "[-pv] [-j passfile] [-k keyfile] prov" 237214118Spjd }, 238148456Spjd { "kill", G_FLAG_VERBOSE, eli_main, 239148456Spjd { 240162868Spjd { 'a', "all", NULL, G_TYPE_BOOL }, 241148456Spjd G_OPT_SENTINEL 242148456Spjd }, 243212554Spjd "[-av] [prov ...]" 244148456Spjd }, 245212554Spjd { "backup", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS, 246148456Spjd "[-v] prov file" 247148456Spjd }, 248212934Sbrian { "restore", G_FLAG_VERBOSE, eli_main, 249212934Sbrian { 250212934Sbrian { 'f', "force", NULL, G_TYPE_BOOL }, 251212934Sbrian G_OPT_SENTINEL 252212934Sbrian }, 253212934Sbrian "[-fv] file prov" 254148456Spjd }, 255212934Sbrian { "resize", G_FLAG_VERBOSE, eli_main, 256212934Sbrian { 257212934Sbrian { 's', "oldsize", NULL, G_TYPE_NUMBER }, 258212934Sbrian G_OPT_SENTINEL 259212934Sbrian }, 260212934Sbrian "[-v] -s oldsize prov" 261212934Sbrian }, 262226723Spjd { "version", G_FLAG_LOADKLD, eli_main, G_NULL_OPTS, 263226723Spjd "[prov ...]" 264226723Spjd }, 265212554Spjd { "clear", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS, 266148456Spjd "[-v] prov ..." 267148456Spjd }, 268212554Spjd { "dump", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS, 269148456Spjd "[-v] prov ..." 270148456Spjd }, 271148456Spjd G_CMD_SENTINEL 272148456Spjd}; 273148456Spjd 274148456Spjdstatic int verbose = 0; 275148456Spjd 276248475Spjd#define BUFSIZE 1024 277248475Spjd 278148456Spjdstatic int 279148456Spjdeli_protect(struct gctl_req *req) 280148456Spjd{ 281148456Spjd struct rlimit rl; 282148456Spjd 283148456Spjd /* Disable core dumps. */ 284148456Spjd rl.rlim_cur = 0; 285148456Spjd rl.rlim_max = 0; 286148456Spjd if (setrlimit(RLIMIT_CORE, &rl) == -1) { 287148456Spjd gctl_error(req, "Cannot disable core dumps: %s.", 288148456Spjd strerror(errno)); 289148456Spjd return (-1); 290148456Spjd } 291148456Spjd /* Disable swapping. */ 292148456Spjd if (mlockall(MCL_FUTURE) == -1) { 293148456Spjd gctl_error(req, "Cannot lock memory: %s.", strerror(errno)); 294148456Spjd return (-1); 295148456Spjd } 296148456Spjd return (0); 297148456Spjd} 298148456Spjd 299148456Spjdstatic void 300213172Spjdeli_main(struct gctl_req *req, unsigned int flags) 301148456Spjd{ 302148456Spjd const char *name; 303148456Spjd 304148456Spjd if (eli_protect(req) == -1) 305148456Spjd return; 306148456Spjd 307148456Spjd if ((flags & G_FLAG_VERBOSE) != 0) 308148456Spjd verbose = 1; 309148456Spjd 310153190Spjd name = gctl_get_ascii(req, "verb"); 311148456Spjd if (name == NULL) { 312148456Spjd gctl_error(req, "No '%s' argument.", "verb"); 313148456Spjd return; 314148456Spjd } 315148456Spjd if (strcmp(name, "init") == 0 || strcmp(name, "label") == 0) 316148456Spjd eli_init(req); 317148456Spjd else if (strcmp(name, "attach") == 0) 318148456Spjd eli_attach(req); 319162353Spjd else if (strcmp(name, "configure") == 0) 320162353Spjd eli_configure(req); 321148456Spjd else if (strcmp(name, "setkey") == 0) 322148456Spjd eli_setkey(req); 323148456Spjd else if (strcmp(name, "delkey") == 0) 324148456Spjd eli_delkey(req); 325214118Spjd else if (strcmp(name, "resume") == 0) 326214118Spjd eli_resume(req); 327148456Spjd else if (strcmp(name, "kill") == 0) 328148456Spjd eli_kill(req); 329148456Spjd else if (strcmp(name, "backup") == 0) 330148456Spjd eli_backup(req); 331148456Spjd else if (strcmp(name, "restore") == 0) 332148456Spjd eli_restore(req); 333212934Sbrian else if (strcmp(name, "resize") == 0) 334212934Sbrian eli_resize(req); 335226723Spjd else if (strcmp(name, "version") == 0) 336226723Spjd eli_version(req); 337148456Spjd else if (strcmp(name, "dump") == 0) 338148456Spjd eli_dump(req); 339148456Spjd else if (strcmp(name, "clear") == 0) 340148456Spjd eli_clear(req); 341148456Spjd else 342148456Spjd gctl_error(req, "Unknown command: %s.", name); 343148456Spjd} 344148456Spjd 345226717Spjdstatic bool 346148456Spjdeli_is_attached(const char *prov) 347148456Spjd{ 348148456Spjd char name[MAXPATHLEN]; 349148456Spjd 350148456Spjd /* 351148456Spjd * Not the best way to do it, but the easiest. 352148456Spjd * We try to open provider and check if it is a GEOM provider 353148456Spjd * by asking about its sectorsize. 354148456Spjd */ 355148456Spjd snprintf(name, sizeof(name), "%s%s", prov, G_ELI_SUFFIX); 356226717Spjd return (g_get_sectorsize(name) > 0); 357148456Spjd} 358148456Spjd 359213172Spjdstatic int 360213172Spjdeli_genkey_files(struct gctl_req *req, bool new, const char *type, 361213172Spjd struct hmac_ctx *ctxp, char *passbuf, size_t passbufsize) 362148456Spjd{ 363248475Spjd char *p, buf[BUFSIZE], argname[16]; 364213172Spjd const char *file; 365213172Spjd int error, fd, i; 366213172Spjd ssize_t done; 367148456Spjd 368213172Spjd assert((strcmp(type, "keyfile") == 0 && ctxp != NULL && 369213172Spjd passbuf == NULL && passbufsize == 0) || 370213172Spjd (strcmp(type, "passfile") == 0 && ctxp == NULL && 371213172Spjd passbuf != NULL && passbufsize > 0)); 372213172Spjd assert(strcmp(type, "keyfile") == 0 || passbuf[0] == '\0'); 373148456Spjd 374213172Spjd for (i = 0; ; i++) { 375213172Spjd snprintf(argname, sizeof(argname), "%s%s%d", 376213172Spjd new ? "new" : "", type, i); 377148456Spjd 378213172Spjd /* No more {key,pass}files? */ 379213172Spjd if (!gctl_has_param(req, argname)) 380213172Spjd return (i); 381148456Spjd 382215704Sbrucec file = gctl_get_ascii(req, "%s", argname); 383213172Spjd assert(file != NULL); 384213172Spjd 385213172Spjd if (strcmp(file, "-") == 0) 386148456Spjd fd = STDIN_FILENO; 387148456Spjd else { 388213172Spjd fd = open(file, O_RDONLY); 389148456Spjd if (fd == -1) { 390213172Spjd gctl_error(req, "Cannot open %s %s: %s.", 391213172Spjd type, file, strerror(errno)); 392213172Spjd return (-1); 393148456Spjd } 394148456Spjd } 395213172Spjd if (strcmp(type, "keyfile") == 0) { 396213172Spjd while ((done = read(fd, buf, sizeof(buf))) > 0) 397213172Spjd g_eli_crypto_hmac_update(ctxp, buf, done); 398213172Spjd } else /* if (strcmp(type, "passfile") == 0) */ { 399246621Spjd assert(strcmp(type, "passfile") == 0); 400246621Spjd 401213172Spjd while ((done = read(fd, buf, sizeof(buf) - 1)) > 0) { 402213172Spjd buf[done] = '\0'; 403213172Spjd p = strchr(buf, '\n'); 404213172Spjd if (p != NULL) { 405213172Spjd *p = '\0'; 406213172Spjd done = p - buf; 407213172Spjd } 408213172Spjd if (strlcat(passbuf, buf, passbufsize) >= 409213172Spjd passbufsize) { 410213172Spjd gctl_error(req, 411213172Spjd "Passphrase in %s too long.", file); 412213172Spjd bzero(buf, sizeof(buf)); 413213172Spjd return (-1); 414213172Spjd } 415213172Spjd if (p != NULL) 416213172Spjd break; 417213172Spjd } 418213172Spjd } 419148456Spjd error = errno; 420213172Spjd if (strcmp(file, "-") != 0) 421148456Spjd close(fd); 422148456Spjd bzero(buf, sizeof(buf)); 423148456Spjd if (done == -1) { 424213172Spjd gctl_error(req, "Cannot read %s %s: %s.", 425213172Spjd type, file, strerror(error)); 426213172Spjd return (-1); 427148456Spjd } 428148456Spjd } 429213172Spjd /* NOTREACHED */ 430213172Spjd} 431148456Spjd 432213172Spjdstatic int 433213172Spjdeli_genkey_passphrase_prompt(struct gctl_req *req, bool new, char *passbuf, 434213172Spjd size_t passbufsize) 435213172Spjd{ 436213172Spjd char *p; 437148456Spjd 438213172Spjd for (;;) { 439213172Spjd p = readpassphrase( 440284250Sbrueffer new ? "Enter new passphrase: " : "Enter passphrase: ", 441213172Spjd passbuf, passbufsize, RPP_ECHO_OFF | RPP_REQUIRE_TTY); 442213172Spjd if (p == NULL) { 443213172Spjd bzero(passbuf, passbufsize); 444213172Spjd gctl_error(req, "Cannot read passphrase: %s.", 445213172Spjd strerror(errno)); 446213172Spjd return (-1); 447149047Spjd } 448213172Spjd 449213172Spjd if (new) { 450248475Spjd char tmpbuf[BUFSIZE]; 451213172Spjd 452213172Spjd p = readpassphrase("Reenter new passphrase: ", 453213172Spjd tmpbuf, sizeof(tmpbuf), 454213172Spjd RPP_ECHO_OFF | RPP_REQUIRE_TTY); 455148456Spjd if (p == NULL) { 456213172Spjd bzero(passbuf, passbufsize); 457213172Spjd gctl_error(req, 458213172Spjd "Cannot read passphrase: %s.", 459148456Spjd strerror(errno)); 460213172Spjd return (-1); 461148456Spjd } 462213172Spjd 463213172Spjd if (strcmp(passbuf, tmpbuf) != 0) { 464213172Spjd bzero(passbuf, passbufsize); 465213172Spjd fprintf(stderr, "They didn't match.\n"); 466213172Spjd continue; 467148456Spjd } 468213172Spjd bzero(tmpbuf, sizeof(tmpbuf)); 469148456Spjd } 470213172Spjd return (0); 471213172Spjd } 472213172Spjd /* NOTREACHED */ 473213172Spjd} 474213172Spjd 475213172Spjdstatic int 476213172Spjdeli_genkey_passphrase(struct gctl_req *req, struct g_eli_metadata *md, bool new, 477213172Spjd struct hmac_ctx *ctxp) 478213172Spjd{ 479248475Spjd char passbuf[BUFSIZE]; 480213172Spjd bool nopassphrase; 481213172Spjd int nfiles; 482213172Spjd 483213172Spjd nopassphrase = 484213172Spjd gctl_get_int(req, new ? "nonewpassphrase" : "nopassphrase"); 485213172Spjd if (nopassphrase) { 486213172Spjd if (gctl_has_param(req, new ? "newpassfile0" : "passfile0")) { 487213172Spjd gctl_error(req, 488213172Spjd "Options -%c and -%c are mutually exclusive.", 489213172Spjd new ? 'J' : 'j', new ? 'P' : 'p'); 490213172Spjd return (-1); 491148456Spjd } 492213172Spjd return (0); 493213172Spjd } 494148456Spjd 495213172Spjd if (!new && md->md_iterations == -1) { 496213172Spjd gctl_error(req, "Missing -p flag."); 497213172Spjd return (-1); 498213172Spjd } 499213172Spjd passbuf[0] = '\0'; 500213172Spjd nfiles = eli_genkey_files(req, new, "passfile", NULL, passbuf, 501213172Spjd sizeof(passbuf)); 502213172Spjd if (nfiles == -1) 503213172Spjd return (-1); 504213172Spjd else if (nfiles == 0) { 505213172Spjd if (eli_genkey_passphrase_prompt(req, new, passbuf, 506213172Spjd sizeof(passbuf)) == -1) { 507213172Spjd return (-1); 508148456Spjd } 509148456Spjd } 510213172Spjd /* 511213172Spjd * Field md_iterations equal to -1 means "choose some sane 512213172Spjd * value for me". 513213172Spjd */ 514213172Spjd if (md->md_iterations == -1) { 515213172Spjd assert(new); 516213172Spjd if (verbose) 517213172Spjd printf("Calculating number of iterations...\n"); 518213172Spjd md->md_iterations = pkcs5v2_calculate(2000000); 519213172Spjd assert(md->md_iterations > 0); 520213172Spjd if (verbose) { 521213172Spjd printf("Done, using %d iterations.\n", 522213172Spjd md->md_iterations); 523213172Spjd } 524213172Spjd } 525213172Spjd /* 526213172Spjd * If md_iterations is equal to 0, user doesn't want PKCS#5v2. 527213172Spjd */ 528213172Spjd if (md->md_iterations == 0) { 529213172Spjd g_eli_crypto_hmac_update(ctxp, md->md_salt, 530213172Spjd sizeof(md->md_salt)); 531213172Spjd g_eli_crypto_hmac_update(ctxp, passbuf, strlen(passbuf)); 532213172Spjd } else /* if (md->md_iterations > 0) */ { 533213172Spjd unsigned char dkey[G_ELI_USERKEYLEN]; 534213172Spjd 535213172Spjd pkcs5v2_genkey(dkey, sizeof(dkey), md->md_salt, 536213172Spjd sizeof(md->md_salt), passbuf, md->md_iterations); 537213172Spjd g_eli_crypto_hmac_update(ctxp, dkey, sizeof(dkey)); 538213172Spjd bzero(dkey, sizeof(dkey)); 539213172Spjd } 540213172Spjd bzero(passbuf, sizeof(passbuf)); 541213172Spjd 542213172Spjd return (0); 543213172Spjd} 544213172Spjd 545213172Spjdstatic unsigned char * 546213172Spjdeli_genkey(struct gctl_req *req, struct g_eli_metadata *md, unsigned char *key, 547213172Spjd bool new) 548213172Spjd{ 549213172Spjd struct hmac_ctx ctx; 550213172Spjd bool nopassphrase; 551213172Spjd int nfiles; 552213172Spjd 553213172Spjd nopassphrase = 554213172Spjd gctl_get_int(req, new ? "nonewpassphrase" : "nopassphrase"); 555213172Spjd 556213172Spjd g_eli_crypto_hmac_init(&ctx, NULL, 0); 557213172Spjd 558213172Spjd nfiles = eli_genkey_files(req, new, "keyfile", &ctx, NULL, 0); 559213172Spjd if (nfiles == -1) 560213172Spjd return (NULL); 561213172Spjd else if (nfiles == 0 && nopassphrase) { 562213172Spjd gctl_error(req, "No key components given."); 563213172Spjd return (NULL); 564213172Spjd } 565213172Spjd 566213172Spjd if (eli_genkey_passphrase(req, md, new, &ctx) == -1) 567213172Spjd return (NULL); 568213172Spjd 569148456Spjd g_eli_crypto_hmac_final(&ctx, key, 0); 570213172Spjd 571148456Spjd return (key); 572148456Spjd} 573148456Spjd 574148456Spjdstatic int 575148456Spjdeli_metadata_read(struct gctl_req *req, const char *prov, 576148456Spjd struct g_eli_metadata *md) 577148456Spjd{ 578148456Spjd unsigned char sector[sizeof(struct g_eli_metadata)]; 579148456Spjd int error; 580148456Spjd 581148456Spjd if (g_get_sectorsize(prov) == 0) { 582148456Spjd int fd; 583148456Spjd 584148456Spjd /* This is a file probably. */ 585148456Spjd fd = open(prov, O_RDONLY); 586148456Spjd if (fd == -1) { 587148456Spjd gctl_error(req, "Cannot open %s: %s.", prov, 588148456Spjd strerror(errno)); 589148456Spjd return (-1); 590148456Spjd } 591148456Spjd if (read(fd, sector, sizeof(sector)) != sizeof(sector)) { 592148456Spjd gctl_error(req, "Cannot read metadata from %s: %s.", 593148456Spjd prov, strerror(errno)); 594148456Spjd close(fd); 595148456Spjd return (-1); 596148456Spjd } 597148456Spjd close(fd); 598148456Spjd } else { 599148456Spjd /* This is a GEOM provider. */ 600148456Spjd error = g_metadata_read(prov, sector, sizeof(sector), 601148456Spjd G_ELI_MAGIC); 602148456Spjd if (error != 0) { 603148456Spjd gctl_error(req, "Cannot read metadata from %s: %s.", 604148456Spjd prov, strerror(error)); 605148456Spjd return (-1); 606148456Spjd } 607148456Spjd } 608226722Spjd error = eli_metadata_decode(sector, md); 609226722Spjd switch (error) { 610226722Spjd case 0: 611226722Spjd break; 612226722Spjd case EOPNOTSUPP: 613226722Spjd gctl_error(req, 614226722Spjd "Provider's %s metadata version %u is too new.\n" 615226722Spjd "geli: The highest supported version is %u.", 616226722Spjd prov, (unsigned int)md->md_version, G_ELI_VERSION); 617148456Spjd return (-1); 618226722Spjd case EINVAL: 619226722Spjd gctl_error(req, "Inconsistent provider's %s metadata.", prov); 620226722Spjd return (-1); 621226722Spjd default: 622226722Spjd gctl_error(req, 623226722Spjd "Unexpected error while decoding provider's %s metadata: %s.", 624226722Spjd prov, strerror(error)); 625226722Spjd return (-1); 626148456Spjd } 627148456Spjd return (0); 628148456Spjd} 629148456Spjd 630148456Spjdstatic int 631148456Spjdeli_metadata_store(struct gctl_req *req, const char *prov, 632148456Spjd struct g_eli_metadata *md) 633148456Spjd{ 634148456Spjd unsigned char sector[sizeof(struct g_eli_metadata)]; 635148456Spjd int error; 636148456Spjd 637148456Spjd eli_metadata_encode(md, sector); 638148456Spjd if (g_get_sectorsize(prov) == 0) { 639148456Spjd int fd; 640148456Spjd 641148456Spjd /* This is a file probably. */ 642148456Spjd fd = open(prov, O_WRONLY | O_TRUNC); 643148456Spjd if (fd == -1) { 644148456Spjd gctl_error(req, "Cannot open %s: %s.", prov, 645148456Spjd strerror(errno)); 646148456Spjd bzero(sector, sizeof(sector)); 647148456Spjd return (-1); 648148456Spjd } 649148456Spjd if (write(fd, sector, sizeof(sector)) != sizeof(sector)) { 650148456Spjd gctl_error(req, "Cannot write metadata to %s: %s.", 651148456Spjd prov, strerror(errno)); 652148456Spjd bzero(sector, sizeof(sector)); 653148456Spjd close(fd); 654148456Spjd return (-1); 655148456Spjd } 656148456Spjd close(fd); 657148456Spjd } else { 658148456Spjd /* This is a GEOM provider. */ 659148456Spjd error = g_metadata_store(prov, sector, sizeof(sector)); 660148456Spjd if (error != 0) { 661148456Spjd gctl_error(req, "Cannot write metadata to %s: %s.", 662148456Spjd prov, strerror(errno)); 663148456Spjd bzero(sector, sizeof(sector)); 664148456Spjd return (-1); 665148456Spjd } 666148456Spjd } 667148456Spjd bzero(sector, sizeof(sector)); 668148456Spjd return (0); 669148456Spjd} 670148456Spjd 671148456Spjdstatic void 672148456Spjdeli_init(struct gctl_req *req) 673148456Spjd{ 674148456Spjd struct g_eli_metadata md; 675148456Spjd unsigned char sector[sizeof(struct g_eli_metadata)]; 676148456Spjd unsigned char key[G_ELI_USERKEYLEN]; 677182452Spjd char backfile[MAXPATHLEN]; 678148456Spjd const char *str, *prov; 679226733Spjd unsigned int secsize, version; 680148456Spjd off_t mediasize; 681153190Spjd intmax_t val; 682155536Spjd int error, nargs; 683148456Spjd 684153190Spjd nargs = gctl_get_int(req, "nargs"); 685153190Spjd if (nargs != 1) { 686158214Spjd gctl_error(req, "Invalid number of arguments."); 687148456Spjd return; 688148456Spjd } 689153190Spjd prov = gctl_get_ascii(req, "arg0"); 690148456Spjd mediasize = g_get_mediasize(prov); 691148456Spjd secsize = g_get_sectorsize(prov); 692148456Spjd if (mediasize == 0 || secsize == 0) { 693148456Spjd gctl_error(req, "Cannot get informations about %s: %s.", prov, 694148456Spjd strerror(errno)); 695148456Spjd return; 696148456Spjd } 697148456Spjd 698148456Spjd bzero(&md, sizeof(md)); 699148456Spjd strlcpy(md.md_magic, G_ELI_MAGIC, sizeof(md.md_magic)); 700226733Spjd val = gctl_get_intmax(req, "mdversion"); 701226733Spjd if (val == -1) { 702226733Spjd version = G_ELI_VERSION; 703226733Spjd } else if (val < 0 || val > G_ELI_VERSION) { 704226733Spjd gctl_error(req, 705226733Spjd "Invalid version specified should be between %u and %u.", 706226733Spjd G_ELI_VERSION_00, G_ELI_VERSION); 707226733Spjd return; 708226733Spjd } else { 709226733Spjd version = val; 710226733Spjd } 711226733Spjd md.md_version = version; 712148456Spjd md.md_flags = 0; 713155536Spjd if (gctl_get_int(req, "boot")) 714148456Spjd md.md_flags |= G_ELI_FLAG_BOOT; 715297691Sallanjude if (gctl_get_int(req, "geliboot")) 716297691Sallanjude md.md_flags |= G_ELI_FLAG_GELIBOOT; 717329114Skevans if (gctl_get_int(req, "displaypass")) 718329114Skevans md.md_flags |= G_ELI_FLAG_GELIDISPLAYPASS; 719286444Spjd if (gctl_get_int(req, "notrim")) 720286444Spjd md.md_flags |= G_ELI_FLAG_NODELETE; 721159361Spjd md.md_ealgo = CRYPTO_ALGORITHM_MIN - 1; 722159308Spjd str = gctl_get_ascii(req, "aalgo"); 723212547Spjd if (*str != '\0') { 724226733Spjd if (version < G_ELI_VERSION_01) { 725226733Spjd gctl_error(req, 726226733Spjd "Data authentication is supported starting from version %u.", 727226733Spjd G_ELI_VERSION_01); 728226733Spjd return; 729226733Spjd } 730159308Spjd md.md_aalgo = g_eli_str2aalgo(str); 731159361Spjd if (md.md_aalgo >= CRYPTO_ALGORITHM_MIN && 732159361Spjd md.md_aalgo <= CRYPTO_ALGORITHM_MAX) { 733159361Spjd md.md_flags |= G_ELI_FLAG_AUTH; 734159361Spjd } else { 735159361Spjd /* 736159361Spjd * For backward compatibility, check if the -a option 737159361Spjd * was used to provide encryption algorithm. 738159361Spjd */ 739159361Spjd md.md_ealgo = g_eli_str2ealgo(str); 740159361Spjd if (md.md_ealgo < CRYPTO_ALGORITHM_MIN || 741159361Spjd md.md_ealgo > CRYPTO_ALGORITHM_MAX) { 742159361Spjd gctl_error(req, 743159361Spjd "Invalid authentication algorithm."); 744159361Spjd return; 745159361Spjd } else { 746159361Spjd fprintf(stderr, "warning: The -e option, not " 747159361Spjd "the -a option is now used to specify " 748159361Spjd "encryption algorithm to use.\n"); 749159361Spjd } 750159308Spjd } 751159308Spjd } 752159308Spjd if (md.md_ealgo < CRYPTO_ALGORITHM_MIN || 753159308Spjd md.md_ealgo > CRYPTO_ALGORITHM_MAX) { 754159361Spjd str = gctl_get_ascii(req, "ealgo"); 755226733Spjd if (*str == '\0') { 756226733Spjd if (version < G_ELI_VERSION_05) 757226733Spjd str = "aes-cbc"; 758226733Spjd else 759226733Spjd str = GELI_ENC_ALGO; 760226733Spjd } 761159361Spjd md.md_ealgo = g_eli_str2ealgo(str); 762159361Spjd if (md.md_ealgo < CRYPTO_ALGORITHM_MIN || 763159361Spjd md.md_ealgo > CRYPTO_ALGORITHM_MAX) { 764159361Spjd gctl_error(req, "Invalid encryption algorithm."); 765159361Spjd return; 766159361Spjd } 767226733Spjd if (md.md_ealgo == CRYPTO_CAMELLIA_CBC && 768226733Spjd version < G_ELI_VERSION_04) { 769226733Spjd gctl_error(req, 770226733Spjd "Camellia-CBC algorithm is supported starting from version %u.", 771226733Spjd G_ELI_VERSION_04); 772226733Spjd return; 773226733Spjd } 774226733Spjd if (md.md_ealgo == CRYPTO_AES_XTS && 775226733Spjd version < G_ELI_VERSION_05) { 776226733Spjd gctl_error(req, 777226733Spjd "AES-XTS algorithm is supported starting from version %u.", 778226733Spjd G_ELI_VERSION_05); 779226733Spjd return; 780226733Spjd } 781148456Spjd } 782153190Spjd val = gctl_get_intmax(req, "keylen"); 783153190Spjd md.md_keylen = val; 784159308Spjd md.md_keylen = g_eli_keylen(md.md_ealgo, md.md_keylen); 785148456Spjd if (md.md_keylen == 0) { 786148456Spjd gctl_error(req, "Invalid key length."); 787148456Spjd return; 788148456Spjd } 789148456Spjd md.md_provsize = mediasize; 790148456Spjd 791153190Spjd val = gctl_get_intmax(req, "iterations"); 792155536Spjd if (val != -1) { 793155536Spjd int nonewpassphrase; 794155536Spjd 795155536Spjd /* 796155536Spjd * Don't allow to set iterations when there will be no 797155536Spjd * passphrase. 798155536Spjd */ 799155536Spjd nonewpassphrase = gctl_get_int(req, "nonewpassphrase"); 800155536Spjd if (nonewpassphrase) { 801155536Spjd gctl_error(req, 802155536Spjd "Options -i and -P are mutually exclusive."); 803155536Spjd return; 804155536Spjd } 805155536Spjd } 806153190Spjd md.md_iterations = val; 807148456Spjd 808153190Spjd val = gctl_get_intmax(req, "sectorsize"); 809153190Spjd if (val == 0) 810148456Spjd md.md_sectorsize = secsize; 811148456Spjd else { 812260254Spjd if (val < 0 || (val % secsize) != 0 || !powerof2(val)) { 813148456Spjd gctl_error(req, "Invalid sector size."); 814148456Spjd return; 815148456Spjd } 816167229Spjd if (val > sysconf(_SC_PAGE_SIZE)) { 817214404Spjd fprintf(stderr, 818214404Spjd "warning: Using sectorsize bigger than the page size!\n"); 819167229Spjd } 820153190Spjd md.md_sectorsize = val; 821148456Spjd } 822148456Spjd 823148456Spjd md.md_keys = 0x01; 824246620Spjd arc4random_buf(md.md_salt, sizeof(md.md_salt)); 825246620Spjd arc4random_buf(md.md_mkeys, sizeof(md.md_mkeys)); 826148456Spjd 827148456Spjd /* Generate user key. */ 828213172Spjd if (eli_genkey(req, &md, key, true) == NULL) { 829148456Spjd bzero(key, sizeof(key)); 830148456Spjd bzero(&md, sizeof(md)); 831148456Spjd return; 832148456Spjd } 833148456Spjd 834148456Spjd /* Encrypt the first and the only Master Key. */ 835159308Spjd error = g_eli_mkey_encrypt(md.md_ealgo, key, md.md_keylen, md.md_mkeys); 836148456Spjd bzero(key, sizeof(key)); 837148456Spjd if (error != 0) { 838148456Spjd bzero(&md, sizeof(md)); 839148456Spjd gctl_error(req, "Cannot encrypt Master Key: %s.", 840148456Spjd strerror(error)); 841148456Spjd return; 842148456Spjd } 843148456Spjd 844148456Spjd eli_metadata_encode(&md, sector); 845148456Spjd bzero(&md, sizeof(md)); 846148456Spjd error = g_metadata_store(prov, sector, sizeof(sector)); 847148456Spjd bzero(sector, sizeof(sector)); 848148456Spjd if (error != 0) { 849148456Spjd gctl_error(req, "Cannot store metadata on %s: %s.", prov, 850148456Spjd strerror(error)); 851148456Spjd return; 852148456Spjd } 853148456Spjd if (verbose) 854148456Spjd printf("Metadata value stored on %s.\n", prov); 855182452Spjd /* Backup metadata to a file. */ 856182452Spjd str = gctl_get_ascii(req, "backupfile"); 857182452Spjd if (str[0] != '\0') { 858182452Spjd /* Backupfile given be the user, just copy it. */ 859182452Spjd strlcpy(backfile, str, sizeof(backfile)); 860182452Spjd } else { 861182452Spjd /* Generate file name automatically. */ 862182452Spjd const char *p = prov; 863182452Spjd unsigned int i; 864182452Spjd 865213662Sae if (strncmp(p, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) 866213662Sae p += sizeof(_PATH_DEV) - 1; 867182452Spjd snprintf(backfile, sizeof(backfile), "%s%s.eli", 868182452Spjd GELI_BACKUP_DIR, p); 869182452Spjd /* Replace all / with _. */ 870182452Spjd for (i = strlen(GELI_BACKUP_DIR); backfile[i] != '\0'; i++) { 871182452Spjd if (backfile[i] == '/') 872182452Spjd backfile[i] = '_'; 873182452Spjd } 874182452Spjd } 875182452Spjd if (strcmp(backfile, "none") != 0 && 876182452Spjd eli_backup_create(req, prov, backfile) == 0) { 877182452Spjd printf("\nMetadata backup can be found in %s and\n", backfile); 878182452Spjd printf("can be restored with the following command:\n"); 879182452Spjd printf("\n\t# geli restore %s %s\n\n", backfile, prov); 880182452Spjd } 881148456Spjd} 882148456Spjd 883148456Spjdstatic void 884148456Spjdeli_attach(struct gctl_req *req) 885148456Spjd{ 886148456Spjd struct g_eli_metadata md; 887148456Spjd unsigned char key[G_ELI_USERKEYLEN]; 888148456Spjd const char *prov; 889212934Sbrian off_t mediasize; 890153190Spjd int nargs; 891148456Spjd 892153190Spjd nargs = gctl_get_int(req, "nargs"); 893153190Spjd if (nargs != 1) { 894158214Spjd gctl_error(req, "Invalid number of arguments."); 895148456Spjd return; 896148456Spjd } 897153190Spjd prov = gctl_get_ascii(req, "arg0"); 898148456Spjd 899148456Spjd if (eli_metadata_read(req, prov, &md) == -1) 900148456Spjd return; 901148456Spjd 902212934Sbrian mediasize = g_get_mediasize(prov); 903212934Sbrian if (md.md_provsize != (uint64_t)mediasize) { 904212934Sbrian gctl_error(req, "Provider size mismatch."); 905212934Sbrian return; 906212934Sbrian } 907212934Sbrian 908213172Spjd if (eli_genkey(req, &md, key, false) == NULL) { 909148456Spjd bzero(key, sizeof(key)); 910148456Spjd return; 911148456Spjd } 912148456Spjd 913148456Spjd gctl_ro_param(req, "key", sizeof(key), key); 914148456Spjd if (gctl_issue(req) == NULL) { 915148456Spjd if (verbose) 916166892Spjd printf("Attached to %s.\n", prov); 917148456Spjd } 918148456Spjd bzero(key, sizeof(key)); 919148456Spjd} 920148456Spjd 921148456Spjdstatic void 922286444Spjdeli_configure_detached(struct gctl_req *req, const char *prov, int boot, 923329114Skevans int geliboot, int displaypass, int trim) 924162353Spjd{ 925162353Spjd struct g_eli_metadata md; 926286444Spjd bool changed = 0; 927162353Spjd 928162353Spjd if (eli_metadata_read(req, prov, &md) == -1) 929162353Spjd return; 930162353Spjd 931286444Spjd if (boot == 1 && (md.md_flags & G_ELI_FLAG_BOOT)) { 932162353Spjd if (verbose) 933162353Spjd printf("BOOT flag already configured for %s.\n", prov); 934286444Spjd } else if (boot == 0 && !(md.md_flags & G_ELI_FLAG_BOOT)) { 935162353Spjd if (verbose) 936162353Spjd printf("BOOT flag not configured for %s.\n", prov); 937286444Spjd } else if (boot >= 0) { 938162353Spjd if (boot) 939162353Spjd md.md_flags |= G_ELI_FLAG_BOOT; 940162353Spjd else 941162353Spjd md.md_flags &= ~G_ELI_FLAG_BOOT; 942286444Spjd changed = 1; 943286444Spjd } 944286444Spjd 945297691Sallanjude if (geliboot == 1 && (md.md_flags & G_ELI_FLAG_GELIBOOT)) { 946297691Sallanjude if (verbose) 947297691Sallanjude printf("GELIBOOT flag already configured for %s.\n", prov); 948297691Sallanjude } else if (geliboot == 0 && !(md.md_flags & G_ELI_FLAG_GELIBOOT)) { 949297691Sallanjude if (verbose) 950297691Sallanjude printf("GELIBOOT flag not configured for %s.\n", prov); 951297691Sallanjude } else if (geliboot >= 0) { 952297691Sallanjude if (geliboot) 953297691Sallanjude md.md_flags |= G_ELI_FLAG_GELIBOOT; 954297691Sallanjude else 955297691Sallanjude md.md_flags &= ~G_ELI_FLAG_GELIBOOT; 956297691Sallanjude changed = 1; 957297691Sallanjude } 958297691Sallanjude 959329114Skevans if (displaypass == 1 && (md.md_flags & G_ELI_FLAG_GELIDISPLAYPASS)) { 960329114Skevans if (verbose) 961329114Skevans printf("GELIDISPLAYPASS flag already configured for %s.\n", prov); 962329114Skevans } else if (displaypass == 0 && 963329114Skevans !(md.md_flags & G_ELI_FLAG_GELIDISPLAYPASS)) { 964329114Skevans if (verbose) 965329114Skevans printf("GELIDISPLAYPASS flag not configured for %s.\n", prov); 966329114Skevans } else if (displaypass >= 0) { 967329114Skevans if (displaypass) 968329114Skevans md.md_flags |= G_ELI_FLAG_GELIDISPLAYPASS; 969329114Skevans else 970329114Skevans md.md_flags &= ~G_ELI_FLAG_GELIDISPLAYPASS; 971329114Skevans changed = 1; 972329114Skevans } 973329114Skevans 974286444Spjd if (trim == 0 && (md.md_flags & G_ELI_FLAG_NODELETE)) { 975286444Spjd if (verbose) 976286444Spjd printf("TRIM disable flag already configured for %s.\n", prov); 977286444Spjd } else if (trim == 1 && !(md.md_flags & G_ELI_FLAG_NODELETE)) { 978286444Spjd if (verbose) 979286444Spjd printf("TRIM disable flag not configured for %s.\n", prov); 980286444Spjd } else if (trim >= 0) { 981286444Spjd if (trim) 982286444Spjd md.md_flags &= ~G_ELI_FLAG_NODELETE; 983286444Spjd else 984286444Spjd md.md_flags |= G_ELI_FLAG_NODELETE; 985286444Spjd changed = 1; 986286444Spjd } 987286444Spjd 988286444Spjd if (changed) 989162353Spjd eli_metadata_store(req, prov, &md); 990162353Spjd bzero(&md, sizeof(md)); 991162353Spjd} 992162353Spjd 993162353Spjdstatic void 994162353Spjdeli_configure(struct gctl_req *req) 995162353Spjd{ 996162353Spjd const char *prov; 997329114Skevans bool boot, noboot, geliboot, nogeliboot, displaypass, nodisplaypass; 998329114Skevans bool trim, notrim; 999329114Skevans int doboot, dogeliboot, dodisplaypass, dotrim; 1000213172Spjd int i, nargs; 1001162353Spjd 1002162353Spjd nargs = gctl_get_int(req, "nargs"); 1003162353Spjd if (nargs == 0) { 1004162353Spjd gctl_error(req, "Too few arguments."); 1005162353Spjd return; 1006162353Spjd } 1007162353Spjd 1008162353Spjd boot = gctl_get_int(req, "boot"); 1009162353Spjd noboot = gctl_get_int(req, "noboot"); 1010297691Sallanjude geliboot = gctl_get_int(req, "geliboot"); 1011297691Sallanjude nogeliboot = gctl_get_int(req, "nogeliboot"); 1012329114Skevans displaypass = gctl_get_int(req, "displaypass"); 1013329114Skevans nodisplaypass = gctl_get_int(req, "nodisplaypass"); 1014286444Spjd trim = gctl_get_int(req, "trim"); 1015286444Spjd notrim = gctl_get_int(req, "notrim"); 1016162353Spjd 1017286444Spjd doboot = -1; 1018162353Spjd if (boot && noboot) { 1019162353Spjd gctl_error(req, "Options -b and -B are mutually exclusive."); 1020162353Spjd return; 1021162353Spjd } 1022286444Spjd if (boot) 1023286444Spjd doboot = 1; 1024286444Spjd else if (noboot) 1025286444Spjd doboot = 0; 1026286444Spjd 1027297691Sallanjude dogeliboot = -1; 1028297691Sallanjude if (geliboot && nogeliboot) { 1029297691Sallanjude gctl_error(req, "Options -g and -G are mutually exclusive."); 1030297691Sallanjude return; 1031297691Sallanjude } 1032297691Sallanjude if (geliboot) 1033297691Sallanjude dogeliboot = 1; 1034297691Sallanjude else if (nogeliboot) 1035297691Sallanjude dogeliboot = 0; 1036297691Sallanjude 1037329114Skevans dodisplaypass = -1; 1038329114Skevans if (displaypass && nodisplaypass) { 1039329114Skevans gctl_error(req, "Options -d and -D are mutually exclusive."); 1040329114Skevans return; 1041329114Skevans } 1042329114Skevans if (displaypass) 1043329114Skevans dodisplaypass = 1; 1044329114Skevans else if (nodisplaypass) 1045329114Skevans dodisplaypass = 0; 1046329114Skevans 1047286444Spjd dotrim = -1; 1048286444Spjd if (trim && notrim) { 1049286444Spjd gctl_error(req, "Options -t and -T are mutually exclusive."); 1050286444Spjd return; 1051286444Spjd } 1052286444Spjd if (trim) 1053286444Spjd dotrim = 1; 1054286444Spjd else if (notrim) 1055286444Spjd dotrim = 0; 1056286444Spjd 1057329114Skevans if (doboot == -1 && dogeliboot == -1 && dodisplaypass == -1 && 1058329114Skevans dotrim == -1) { 1059162353Spjd gctl_error(req, "No option given."); 1060162353Spjd return; 1061162353Spjd } 1062162353Spjd 1063162353Spjd /* First attached providers. */ 1064162353Spjd gctl_issue(req); 1065162353Spjd /* Now the rest. */ 1066162353Spjd for (i = 0; i < nargs; i++) { 1067162353Spjd prov = gctl_get_ascii(req, "arg%d", i); 1068329114Skevans if (!eli_is_attached(prov)) { 1069329114Skevans eli_configure_detached(req, prov, doboot, dogeliboot, 1070329114Skevans dodisplaypass, dotrim); 1071329114Skevans } 1072162353Spjd } 1073162353Spjd} 1074162353Spjd 1075162353Spjdstatic void 1076155101Spjdeli_setkey_attached(struct gctl_req *req, struct g_eli_metadata *md) 1077148456Spjd{ 1078148456Spjd unsigned char key[G_ELI_USERKEYLEN]; 1079166216Spjd intmax_t val, old = 0; 1080166216Spjd int error; 1081148456Spjd 1082153190Spjd val = gctl_get_intmax(req, "iterations"); 1083149304Spjd /* Check if iterations number should be changed. */ 1084153190Spjd if (val != -1) 1085153190Spjd md->md_iterations = val; 1086166216Spjd else 1087166216Spjd old = md->md_iterations; 1088148456Spjd 1089148456Spjd /* Generate key for Master Key encryption. */ 1090213172Spjd if (eli_genkey(req, md, key, true) == NULL) { 1091148456Spjd bzero(key, sizeof(key)); 1092148456Spjd return; 1093148456Spjd } 1094166216Spjd /* 1095166216Spjd * If number of iterations has changed, but wasn't given as a 1096166216Spjd * command-line argument, update the request. 1097166216Spjd */ 1098166216Spjd if (val == -1 && md->md_iterations != old) { 1099166216Spjd error = gctl_change_param(req, "iterations", sizeof(intmax_t), 1100166216Spjd &md->md_iterations); 1101166216Spjd assert(error == 0); 1102166216Spjd } 1103148456Spjd 1104148456Spjd gctl_ro_param(req, "key", sizeof(key), key); 1105148456Spjd gctl_issue(req); 1106148456Spjd bzero(key, sizeof(key)); 1107148456Spjd} 1108148456Spjd 1109148456Spjdstatic void 1110149304Spjdeli_setkey_detached(struct gctl_req *req, const char *prov, 1111149304Spjd struct g_eli_metadata *md) 1112148456Spjd{ 1113148456Spjd unsigned char key[G_ELI_USERKEYLEN], mkey[G_ELI_DATAIVKEYLEN]; 1114148456Spjd unsigned char *mkeydst; 1115213172Spjd unsigned int nkey; 1116153190Spjd intmax_t val; 1117148456Spjd int error; 1118148456Spjd 1119149928Spjd if (md->md_keys == 0) { 1120149928Spjd gctl_error(req, "No valid keys on %s.", prov); 1121149928Spjd return; 1122149928Spjd } 1123149928Spjd 1124148456Spjd /* Generate key for Master Key decryption. */ 1125213172Spjd if (eli_genkey(req, md, key, false) == NULL) { 1126148456Spjd bzero(key, sizeof(key)); 1127148456Spjd return; 1128148456Spjd } 1129148456Spjd 1130148456Spjd /* Decrypt Master Key. */ 1131149304Spjd error = g_eli_mkey_decrypt(md, key, mkey, &nkey); 1132148456Spjd bzero(key, sizeof(key)); 1133148456Spjd if (error != 0) { 1134149304Spjd bzero(md, sizeof(*md)); 1135148456Spjd if (error == -1) 1136148456Spjd gctl_error(req, "Wrong key for %s.", prov); 1137148456Spjd else /* if (error > 0) */ { 1138148456Spjd gctl_error(req, "Cannot decrypt Master Key: %s.", 1139148456Spjd strerror(error)); 1140148456Spjd } 1141148456Spjd return; 1142148456Spjd } 1143148456Spjd if (verbose) 1144148456Spjd printf("Decrypted Master Key %u.\n", nkey); 1145148456Spjd 1146153190Spjd val = gctl_get_intmax(req, "keyno"); 1147153190Spjd if (val != -1) 1148153190Spjd nkey = val; 1149148456Spjd#if 0 1150148456Spjd else 1151148456Spjd ; /* Use the key number which was found during decryption. */ 1152148456Spjd#endif 1153148456Spjd if (nkey >= G_ELI_MAXMKEYS) { 1154148456Spjd gctl_error(req, "Invalid '%s' argument.", "keyno"); 1155148456Spjd return; 1156148456Spjd } 1157148456Spjd 1158153190Spjd val = gctl_get_intmax(req, "iterations"); 1159149304Spjd /* Check if iterations number should and can be changed. */ 1160317858Smav if (val != -1 && md->md_iterations == -1) { 1161317858Smav md->md_iterations = val; 1162317858Smav } else if (val != -1 && val != md->md_iterations) { 1163149304Spjd if (bitcount32(md->md_keys) != 1) { 1164149304Spjd gctl_error(req, "To be able to use '-i' option, only " 1165149304Spjd "one key can be defined."); 1166149304Spjd return; 1167149304Spjd } 1168149304Spjd if (md->md_keys != (1 << nkey)) { 1169149304Spjd gctl_error(req, "Only already defined key can be " 1170149304Spjd "changed when '-i' option is used."); 1171149304Spjd return; 1172149304Spjd } 1173153190Spjd md->md_iterations = val; 1174149304Spjd } 1175148456Spjd 1176149304Spjd mkeydst = md->md_mkeys + nkey * G_ELI_MKEYLEN; 1177149304Spjd md->md_keys |= (1 << nkey); 1178149304Spjd 1179148456Spjd bcopy(mkey, mkeydst, sizeof(mkey)); 1180148456Spjd bzero(mkey, sizeof(mkey)); 1181148456Spjd 1182148456Spjd /* Generate key for Master Key encryption. */ 1183213172Spjd if (eli_genkey(req, md, key, true) == NULL) { 1184148456Spjd bzero(key, sizeof(key)); 1185149304Spjd bzero(md, sizeof(*md)); 1186148456Spjd return; 1187148456Spjd } 1188148456Spjd 1189148456Spjd /* Encrypt the Master-Key with the new key. */ 1190159308Spjd error = g_eli_mkey_encrypt(md->md_ealgo, key, md->md_keylen, mkeydst); 1191148456Spjd bzero(key, sizeof(key)); 1192148456Spjd if (error != 0) { 1193149304Spjd bzero(md, sizeof(*md)); 1194148456Spjd gctl_error(req, "Cannot encrypt Master Key: %s.", 1195148456Spjd strerror(error)); 1196148456Spjd return; 1197148456Spjd } 1198148456Spjd 1199148456Spjd /* Store metadata with fresh key. */ 1200149304Spjd eli_metadata_store(req, prov, md); 1201149304Spjd bzero(md, sizeof(*md)); 1202148456Spjd} 1203148456Spjd 1204148456Spjdstatic void 1205148456Spjdeli_setkey(struct gctl_req *req) 1206148456Spjd{ 1207149304Spjd struct g_eli_metadata md; 1208148456Spjd const char *prov; 1209153190Spjd int nargs; 1210148456Spjd 1211153190Spjd nargs = gctl_get_int(req, "nargs"); 1212153190Spjd if (nargs != 1) { 1213158214Spjd gctl_error(req, "Invalid number of arguments."); 1214148456Spjd return; 1215148456Spjd } 1216153190Spjd prov = gctl_get_ascii(req, "arg0"); 1217148456Spjd 1218149304Spjd if (eli_metadata_read(req, prov, &md) == -1) 1219149304Spjd return; 1220149304Spjd 1221148456Spjd if (eli_is_attached(prov)) 1222155101Spjd eli_setkey_attached(req, &md); 1223148456Spjd else 1224149304Spjd eli_setkey_detached(req, prov, &md); 1225182452Spjd 1226182452Spjd if (req->error == NULL || req->error[0] == '\0') { 1227182452Spjd printf("Note, that the master key encrypted with old keys " 1228182452Spjd "and/or passphrase may still exists in a metadata backup " 1229182452Spjd "file.\n"); 1230182452Spjd } 1231148456Spjd} 1232148456Spjd 1233148456Spjdstatic void 1234148456Spjdeli_delkey_attached(struct gctl_req *req, const char *prov __unused) 1235148456Spjd{ 1236148456Spjd 1237148456Spjd gctl_issue(req); 1238148456Spjd} 1239148456Spjd 1240148456Spjdstatic void 1241148456Spjdeli_delkey_detached(struct gctl_req *req, const char *prov) 1242148456Spjd{ 1243148456Spjd struct g_eli_metadata md; 1244148456Spjd unsigned char *mkeydst; 1245213172Spjd unsigned int nkey; 1246153190Spjd intmax_t val; 1247213172Spjd bool all, force; 1248148456Spjd 1249148456Spjd if (eli_metadata_read(req, prov, &md) == -1) 1250148456Spjd return; 1251148456Spjd 1252153190Spjd all = gctl_get_int(req, "all"); 1253153190Spjd if (all) 1254246620Spjd arc4random_buf(md.md_mkeys, sizeof(md.md_mkeys)); 1255148456Spjd else { 1256153190Spjd force = gctl_get_int(req, "force"); 1257153190Spjd val = gctl_get_intmax(req, "keyno"); 1258153190Spjd if (val == -1) { 1259148456Spjd gctl_error(req, "Key number has to be specified."); 1260148456Spjd return; 1261148456Spjd } 1262153190Spjd nkey = val; 1263148456Spjd if (nkey >= G_ELI_MAXMKEYS) { 1264148456Spjd gctl_error(req, "Invalid '%s' argument.", "keyno"); 1265148456Spjd return; 1266148456Spjd } 1267153190Spjd if (!(md.md_keys & (1 << nkey)) && !force) { 1268148456Spjd gctl_error(req, "Master Key %u is not set.", nkey); 1269148456Spjd return; 1270148456Spjd } 1271148456Spjd md.md_keys &= ~(1 << nkey); 1272153190Spjd if (md.md_keys == 0 && !force) { 1273148456Spjd gctl_error(req, "This is the last Master Key. Use '-f' " 1274148456Spjd "option if you really want to remove it."); 1275148456Spjd return; 1276148456Spjd } 1277148456Spjd mkeydst = md.md_mkeys + nkey * G_ELI_MKEYLEN; 1278246620Spjd arc4random_buf(mkeydst, G_ELI_MKEYLEN); 1279148456Spjd } 1280148456Spjd 1281148456Spjd eli_metadata_store(req, prov, &md); 1282148456Spjd bzero(&md, sizeof(md)); 1283148456Spjd} 1284148456Spjd 1285148456Spjdstatic void 1286148456Spjdeli_delkey(struct gctl_req *req) 1287148456Spjd{ 1288148456Spjd const char *prov; 1289153190Spjd int nargs; 1290148456Spjd 1291153190Spjd nargs = gctl_get_int(req, "nargs"); 1292153190Spjd if (nargs != 1) { 1293158214Spjd gctl_error(req, "Invalid number of arguments."); 1294148456Spjd return; 1295148456Spjd } 1296153190Spjd prov = gctl_get_ascii(req, "arg0"); 1297148456Spjd 1298148456Spjd if (eli_is_attached(prov)) 1299148456Spjd eli_delkey_attached(req, prov); 1300148456Spjd else 1301148456Spjd eli_delkey_detached(req, prov); 1302148456Spjd} 1303148456Spjd 1304214118Spjdstatic void 1305214118Spjdeli_resume(struct gctl_req *req) 1306214118Spjd{ 1307214118Spjd struct g_eli_metadata md; 1308214118Spjd unsigned char key[G_ELI_USERKEYLEN]; 1309214118Spjd const char *prov; 1310214118Spjd off_t mediasize; 1311214118Spjd int nargs; 1312214118Spjd 1313214118Spjd nargs = gctl_get_int(req, "nargs"); 1314214118Spjd if (nargs != 1) { 1315214118Spjd gctl_error(req, "Invalid number of arguments."); 1316214118Spjd return; 1317214118Spjd } 1318214118Spjd prov = gctl_get_ascii(req, "arg0"); 1319214118Spjd 1320214118Spjd if (eli_metadata_read(req, prov, &md) == -1) 1321214118Spjd return; 1322214118Spjd 1323214118Spjd mediasize = g_get_mediasize(prov); 1324214118Spjd if (md.md_provsize != (uint64_t)mediasize) { 1325214118Spjd gctl_error(req, "Provider size mismatch."); 1326214118Spjd return; 1327214118Spjd } 1328214118Spjd 1329214118Spjd if (eli_genkey(req, &md, key, false) == NULL) { 1330214118Spjd bzero(key, sizeof(key)); 1331214118Spjd return; 1332214118Spjd } 1333214118Spjd 1334214118Spjd gctl_ro_param(req, "key", sizeof(key), key); 1335214118Spjd if (gctl_issue(req) == NULL) { 1336214118Spjd if (verbose) 1337214118Spjd printf("Resumed %s.\n", prov); 1338214118Spjd } 1339214118Spjd bzero(key, sizeof(key)); 1340214118Spjd} 1341214118Spjd 1342213060Spjdstatic int 1343213060Spjdeli_trash_metadata(struct gctl_req *req, const char *prov, int fd, off_t offset) 1344213060Spjd{ 1345213060Spjd unsigned int overwrites; 1346213060Spjd unsigned char *sector; 1347213060Spjd ssize_t size; 1348213060Spjd int error; 1349213060Spjd 1350213060Spjd size = sizeof(overwrites); 1351213060Spjd if (sysctlbyname("kern.geom.eli.overwrites", &overwrites, &size, 1352213060Spjd NULL, 0) == -1 || overwrites == 0) { 1353213060Spjd overwrites = G_ELI_OVERWRITES; 1354213060Spjd } 1355213060Spjd 1356213060Spjd size = g_sectorsize(fd); 1357213060Spjd if (size <= 0) { 1358213060Spjd gctl_error(req, "Cannot obtain provider sector size %s: %s.", 1359213060Spjd prov, strerror(errno)); 1360213060Spjd return (-1); 1361213060Spjd } 1362213060Spjd sector = malloc(size); 1363213060Spjd if (sector == NULL) { 1364213060Spjd gctl_error(req, "Cannot allocate %zd bytes of memory.", size); 1365213060Spjd return (-1); 1366213060Spjd } 1367213060Spjd 1368213060Spjd error = 0; 1369213060Spjd do { 1370246620Spjd arc4random_buf(sector, size); 1371213060Spjd if (pwrite(fd, sector, size, offset) != size) { 1372213060Spjd if (error == 0) 1373213060Spjd error = errno; 1374213060Spjd } 1375213060Spjd (void)g_flush(fd); 1376213060Spjd } while (--overwrites > 0); 1377246622Spjd free(sector); 1378213060Spjd if (error != 0) { 1379213060Spjd gctl_error(req, "Cannot trash metadata on provider %s: %s.", 1380213060Spjd prov, strerror(error)); 1381213060Spjd return (-1); 1382213060Spjd } 1383213060Spjd return (0); 1384213060Spjd} 1385213060Spjd 1386148456Spjdstatic void 1387148456Spjdeli_kill_detached(struct gctl_req *req, const char *prov) 1388148456Spjd{ 1389213060Spjd off_t offset; 1390213060Spjd int fd; 1391148456Spjd 1392148456Spjd /* 1393148456Spjd * NOTE: Maybe we should verify if this is geli provider first, 1394148456Spjd * but 'kill' command is quite critical so better don't waste 1395148456Spjd * the time. 1396148456Spjd */ 1397148456Spjd#if 0 1398148456Spjd error = g_metadata_read(prov, (unsigned char *)&md, sizeof(md), 1399148456Spjd G_ELI_MAGIC); 1400148456Spjd if (error != 0) { 1401148456Spjd gctl_error(req, "Cannot read metadata from %s: %s.", prov, 1402148456Spjd strerror(error)); 1403148456Spjd return; 1404148456Spjd } 1405148456Spjd#endif 1406148456Spjd 1407213060Spjd fd = g_open(prov, 1); 1408213060Spjd if (fd == -1) { 1409213060Spjd gctl_error(req, "Cannot open provider %s: %s.", prov, 1410213060Spjd strerror(errno)); 1411213060Spjd return; 1412148456Spjd } 1413213060Spjd offset = g_mediasize(fd) - g_sectorsize(fd); 1414213060Spjd if (offset <= 0) { 1415213060Spjd gctl_error(req, 1416213060Spjd "Cannot obtain media size or sector size for provider %s: %s.", 1417213060Spjd prov, strerror(errno)); 1418213060Spjd (void)g_close(fd); 1419213060Spjd return; 1420213060Spjd } 1421213060Spjd (void)eli_trash_metadata(req, prov, fd, offset); 1422213060Spjd (void)g_close(fd); 1423148456Spjd} 1424148456Spjd 1425148456Spjdstatic void 1426148456Spjdeli_kill(struct gctl_req *req) 1427148456Spjd{ 1428148456Spjd const char *prov; 1429153190Spjd int i, nargs, all; 1430148456Spjd 1431153190Spjd nargs = gctl_get_int(req, "nargs"); 1432153190Spjd all = gctl_get_int(req, "all"); 1433153190Spjd if (!all && nargs == 0) { 1434148456Spjd gctl_error(req, "Too few arguments."); 1435148456Spjd return; 1436148456Spjd } 1437148456Spjd /* 1438148456Spjd * How '-a' option combine with a list of providers: 1439148456Spjd * Delete Master Keys from all attached providers: 1440148456Spjd * geli kill -a 1441169312Spjd * Delete Master Keys from all attached providers and from 1442148456Spjd * detached da0 and da1: 1443148456Spjd * geli kill -a da0 da1 1444148456Spjd * Delete Master Keys from (attached or detached) da0 and da1: 1445148456Spjd * geli kill da0 da1 1446148456Spjd */ 1447148456Spjd 1448169312Spjd /* First detached providers. */ 1449153190Spjd for (i = 0; i < nargs; i++) { 1450153190Spjd prov = gctl_get_ascii(req, "arg%d", i); 1451148456Spjd if (!eli_is_attached(prov)) 1452148456Spjd eli_kill_detached(req, prov); 1453148456Spjd } 1454162347Spjd /* Now attached providers. */ 1455162347Spjd gctl_issue(req); 1456148456Spjd} 1457148456Spjd 1458182452Spjdstatic int 1459182452Spjdeli_backup_create(struct gctl_req *req, const char *prov, const char *file) 1460148456Spjd{ 1461148456Spjd unsigned char *sector; 1462213059Spjd ssize_t secsize; 1463226716Spjd int error, filefd, ret; 1464148456Spjd 1465182452Spjd ret = -1; 1466226716Spjd filefd = -1; 1467148456Spjd sector = NULL; 1468148456Spjd secsize = 0; 1469148456Spjd 1470226716Spjd secsize = g_get_sectorsize(prov); 1471226716Spjd if (secsize == 0) { 1472148456Spjd gctl_error(req, "Cannot get informations about %s: %s.", prov, 1473148456Spjd strerror(errno)); 1474169193Spjd goto out; 1475148456Spjd } 1476148456Spjd sector = malloc(secsize); 1477148456Spjd if (sector == NULL) { 1478148456Spjd gctl_error(req, "Cannot allocate memory."); 1479169193Spjd goto out; 1480148456Spjd } 1481148456Spjd /* Read metadata from the provider. */ 1482226716Spjd error = g_metadata_read(prov, sector, secsize, G_ELI_MAGIC); 1483226716Spjd if (error != 0) { 1484226716Spjd gctl_error(req, "Unable to read metadata from %s: %s.", prov, 1485226716Spjd strerror(error)); 1486148456Spjd goto out; 1487148456Spjd } 1488226716Spjd 1489226716Spjd filefd = open(file, O_WRONLY | O_TRUNC | O_CREAT, 0600); 1490226716Spjd if (filefd == -1) { 1491226716Spjd gctl_error(req, "Unable to open %s: %s.", file, 1492226716Spjd strerror(errno)); 1493148456Spjd goto out; 1494148456Spjd } 1495148456Spjd /* Write metadata to the destination file. */ 1496213059Spjd if (write(filefd, sector, secsize) != secsize) { 1497226716Spjd gctl_error(req, "Unable to write to %s: %s.", file, 1498148456Spjd strerror(errno)); 1499226716Spjd (void)close(filefd); 1500226716Spjd (void)unlink(file); 1501148456Spjd goto out; 1502148456Spjd } 1503213059Spjd (void)fsync(filefd); 1504226716Spjd (void)close(filefd); 1505182452Spjd /* Success. */ 1506182452Spjd ret = 0; 1507148456Spjdout: 1508148456Spjd if (sector != NULL) { 1509148456Spjd bzero(sector, secsize); 1510148456Spjd free(sector); 1511148456Spjd } 1512182452Spjd return (ret); 1513148456Spjd} 1514148456Spjd 1515148456Spjdstatic void 1516182452Spjdeli_backup(struct gctl_req *req) 1517182452Spjd{ 1518182452Spjd const char *file, *prov; 1519182452Spjd int nargs; 1520182452Spjd 1521182452Spjd nargs = gctl_get_int(req, "nargs"); 1522182452Spjd if (nargs != 2) { 1523182452Spjd gctl_error(req, "Invalid number of arguments."); 1524182452Spjd return; 1525182452Spjd } 1526182452Spjd prov = gctl_get_ascii(req, "arg0"); 1527182452Spjd file = gctl_get_ascii(req, "arg1"); 1528182452Spjd 1529182452Spjd eli_backup_create(req, prov, file); 1530182452Spjd} 1531182452Spjd 1532182452Spjdstatic void 1533148456Spjdeli_restore(struct gctl_req *req) 1534148456Spjd{ 1535148456Spjd struct g_eli_metadata md; 1536148456Spjd const char *file, *prov; 1537148456Spjd off_t mediasize; 1538226716Spjd int nargs; 1539148456Spjd 1540153190Spjd nargs = gctl_get_int(req, "nargs"); 1541153190Spjd if (nargs != 2) { 1542148456Spjd gctl_error(req, "Invalid number of arguments."); 1543148456Spjd return; 1544148456Spjd } 1545153190Spjd file = gctl_get_ascii(req, "arg0"); 1546153190Spjd prov = gctl_get_ascii(req, "arg1"); 1547148456Spjd 1548226716Spjd /* Read metadata from the backup file. */ 1549226716Spjd if (eli_metadata_read(req, file, &md) == -1) 1550226716Spjd return; 1551226716Spjd /* Obtain provider's mediasize. */ 1552226716Spjd mediasize = g_get_mediasize(prov); 1553226716Spjd if (mediasize == 0) { 1554148456Spjd gctl_error(req, "Cannot get informations about %s: %s.", prov, 1555148456Spjd strerror(errno)); 1556226716Spjd return; 1557148456Spjd } 1558212934Sbrian /* Check if the provider size has changed since we did the backup. */ 1559212934Sbrian if (md.md_provsize != (uint64_t)mediasize) { 1560212934Sbrian if (gctl_get_int(req, "force")) { 1561212934Sbrian md.md_provsize = mediasize; 1562212934Sbrian } else { 1563212934Sbrian gctl_error(req, "Provider size mismatch: " 1564212934Sbrian "wrong backup file?"); 1565226716Spjd return; 1566212934Sbrian } 1567212934Sbrian } 1568226716Spjd /* Write metadata to the provider. */ 1569226716Spjd (void)eli_metadata_store(req, prov, &md); 1570148456Spjd} 1571148456Spjd 1572148456Spjdstatic void 1573212934Sbrianeli_resize(struct gctl_req *req) 1574212934Sbrian{ 1575212934Sbrian struct g_eli_metadata md; 1576212934Sbrian const char *prov; 1577212934Sbrian unsigned char *sector; 1578213056Spjd ssize_t secsize; 1579212934Sbrian off_t mediasize, oldsize; 1580226722Spjd int error, nargs, provfd; 1581212934Sbrian 1582212934Sbrian nargs = gctl_get_int(req, "nargs"); 1583212934Sbrian if (nargs != 1) { 1584212934Sbrian gctl_error(req, "Invalid number of arguments."); 1585212934Sbrian return; 1586212934Sbrian } 1587212934Sbrian prov = gctl_get_ascii(req, "arg0"); 1588212934Sbrian 1589212934Sbrian provfd = -1; 1590212934Sbrian sector = NULL; 1591212934Sbrian secsize = 0; 1592212934Sbrian 1593213056Spjd provfd = g_open(prov, 1); 1594212934Sbrian if (provfd == -1) { 1595212934Sbrian gctl_error(req, "Cannot open %s: %s.", prov, strerror(errno)); 1596212934Sbrian goto out; 1597212934Sbrian } 1598212934Sbrian 1599213056Spjd mediasize = g_mediasize(provfd); 1600213056Spjd secsize = g_sectorsize(provfd); 1601213056Spjd if (mediasize == -1 || secsize == -1) { 1602212934Sbrian gctl_error(req, "Cannot get information about %s: %s.", prov, 1603212934Sbrian strerror(errno)); 1604212934Sbrian goto out; 1605212934Sbrian } 1606212934Sbrian 1607212934Sbrian sector = malloc(secsize); 1608212934Sbrian if (sector == NULL) { 1609212934Sbrian gctl_error(req, "Cannot allocate memory."); 1610212934Sbrian goto out; 1611212934Sbrian } 1612212934Sbrian 1613212934Sbrian oldsize = gctl_get_intmax(req, "oldsize"); 1614212934Sbrian if (oldsize < 0 || oldsize > mediasize) { 1615212934Sbrian gctl_error(req, "Invalid oldsize: Out of range."); 1616212934Sbrian goto out; 1617212934Sbrian } 1618213058Spjd if (oldsize == mediasize) { 1619213058Spjd gctl_error(req, "Size hasn't changed."); 1620213058Spjd goto out; 1621213058Spjd } 1622212934Sbrian 1623212934Sbrian /* Read metadata from the 'oldsize' offset. */ 1624213056Spjd if (pread(provfd, sector, secsize, oldsize - secsize) != secsize) { 1625212934Sbrian gctl_error(req, "Cannot read old metadata: %s.", 1626212934Sbrian strerror(errno)); 1627212934Sbrian goto out; 1628212934Sbrian } 1629212934Sbrian 1630212934Sbrian /* Check if this sector contains geli metadata. */ 1631226722Spjd error = eli_metadata_decode(sector, &md); 1632226722Spjd switch (error) { 1633226722Spjd case 0: 1634226722Spjd break; 1635226722Spjd case EOPNOTSUPP: 1636226722Spjd gctl_error(req, 1637226722Spjd "Provider's %s metadata version %u is too new.\n" 1638226722Spjd "geli: The highest supported version is %u.", 1639226722Spjd prov, (unsigned int)md.md_version, G_ELI_VERSION); 1640212934Sbrian goto out; 1641226722Spjd case EINVAL: 1642226722Spjd gctl_error(req, "Inconsistent provider's %s metadata.", prov); 1643226722Spjd goto out; 1644226722Spjd default: 1645226722Spjd gctl_error(req, 1646226722Spjd "Unexpected error while decoding provider's %s metadata: %s.", 1647226722Spjd prov, strerror(error)); 1648226722Spjd goto out; 1649212934Sbrian } 1650212934Sbrian 1651212934Sbrian /* 1652212934Sbrian * If the old metadata doesn't have a correct provider size, refuse 1653212934Sbrian * to resize. 1654212934Sbrian */ 1655212934Sbrian if (md.md_provsize != (uint64_t)oldsize) { 1656212934Sbrian gctl_error(req, "Provider size mismatch at oldsize."); 1657212934Sbrian goto out; 1658212934Sbrian } 1659212934Sbrian 1660212934Sbrian /* 1661212934Sbrian * Update the old metadata with the current provider size and write 1662212934Sbrian * it back to the correct place on the provider. 1663212934Sbrian */ 1664212934Sbrian md.md_provsize = mediasize; 1665226720Spjd /* Write metadata to the provider. */ 1666226720Spjd (void)eli_metadata_store(req, prov, &md); 1667212934Sbrian /* Now trash the old metadata. */ 1668226720Spjd (void)eli_trash_metadata(req, prov, provfd, oldsize - secsize); 1669212934Sbrianout: 1670226720Spjd if (provfd != -1) 1671213056Spjd (void)g_close(provfd); 1672212934Sbrian if (sector != NULL) { 1673212934Sbrian bzero(sector, secsize); 1674212934Sbrian free(sector); 1675212934Sbrian } 1676212934Sbrian} 1677212934Sbrian 1678212934Sbrianstatic void 1679226723Spjdeli_version(struct gctl_req *req) 1680226723Spjd{ 1681226723Spjd struct g_eli_metadata md; 1682226723Spjd const char *name; 1683226723Spjd unsigned int version; 1684226723Spjd int error, i, nargs; 1685226723Spjd 1686226723Spjd nargs = gctl_get_int(req, "nargs"); 1687226723Spjd 1688226723Spjd if (nargs == 0) { 1689226723Spjd unsigned int kernver; 1690226723Spjd ssize_t size; 1691226723Spjd 1692226723Spjd size = sizeof(kernver); 1693226723Spjd if (sysctlbyname("kern.geom.eli.version", &kernver, &size, 1694226723Spjd NULL, 0) == -1) { 1695226723Spjd warn("Unable to obtain GELI kernel version"); 1696226723Spjd } else { 1697226723Spjd printf("kernel: %u\n", kernver); 1698226723Spjd } 1699226723Spjd printf("userland: %u\n", G_ELI_VERSION); 1700226723Spjd return; 1701226723Spjd } 1702226723Spjd 1703226723Spjd for (i = 0; i < nargs; i++) { 1704226723Spjd name = gctl_get_ascii(req, "arg%d", i); 1705226723Spjd error = g_metadata_read(name, (unsigned char *)&md, 1706226723Spjd sizeof(md), G_ELI_MAGIC); 1707226723Spjd if (error != 0) { 1708226723Spjd warn("%s: Unable to read metadata: %s.", name, 1709226723Spjd strerror(error)); 1710226723Spjd gctl_error(req, "Not fully done."); 1711226723Spjd continue; 1712226723Spjd } 1713226723Spjd version = le32dec(&md.md_version); 1714226723Spjd printf("%s: %u\n", name, version); 1715226723Spjd } 1716226723Spjd} 1717226723Spjd 1718226723Spjdstatic void 1719148456Spjdeli_clear(struct gctl_req *req) 1720148456Spjd{ 1721148456Spjd const char *name; 1722153190Spjd int error, i, nargs; 1723148456Spjd 1724153190Spjd nargs = gctl_get_int(req, "nargs"); 1725153190Spjd if (nargs < 1) { 1726148456Spjd gctl_error(req, "Too few arguments."); 1727148456Spjd return; 1728148456Spjd } 1729148456Spjd 1730153190Spjd for (i = 0; i < nargs; i++) { 1731153190Spjd name = gctl_get_ascii(req, "arg%d", i); 1732148456Spjd error = g_metadata_clear(name, G_ELI_MAGIC); 1733148456Spjd if (error != 0) { 1734148456Spjd fprintf(stderr, "Cannot clear metadata on %s: %s.\n", 1735148456Spjd name, strerror(error)); 1736148456Spjd gctl_error(req, "Not fully done."); 1737148456Spjd continue; 1738148456Spjd } 1739148456Spjd if (verbose) 1740155175Spjd printf("Metadata cleared on %s.\n", name); 1741148456Spjd } 1742148456Spjd} 1743148456Spjd 1744148456Spjdstatic void 1745148456Spjdeli_dump(struct gctl_req *req) 1746148456Spjd{ 1747226719Spjd struct g_eli_metadata md; 1748148456Spjd const char *name; 1749226719Spjd int i, nargs; 1750148456Spjd 1751153190Spjd nargs = gctl_get_int(req, "nargs"); 1752153190Spjd if (nargs < 1) { 1753148456Spjd gctl_error(req, "Too few arguments."); 1754148456Spjd return; 1755148456Spjd } 1756148456Spjd 1757153190Spjd for (i = 0; i < nargs; i++) { 1758153190Spjd name = gctl_get_ascii(req, "arg%d", i); 1759226719Spjd if (eli_metadata_read(NULL, name, &md) == -1) { 1760148456Spjd gctl_error(req, "Not fully done."); 1761148456Spjd continue; 1762148456Spjd } 1763148456Spjd printf("Metadata on %s:\n", name); 1764148456Spjd eli_metadata_dump(&md); 1765148456Spjd printf("\n"); 1766148456Spjd } 1767148456Spjd} 1768