1e4b0a90eSBrooks Davis /*- 2e4b0a90eSBrooks Davis * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3e4b0a90eSBrooks Davis * 4*2f07cdf8SPawel Jakub Dawidek * Copyright (c) 2004-2019 Pawel Jakub Dawidek <pawel@dawidek.net> 5e4b0a90eSBrooks Davis * All rights reserved. 6e4b0a90eSBrooks Davis * 7e4b0a90eSBrooks Davis * Redistribution and use in source and binary forms, with or without 8e4b0a90eSBrooks Davis * modification, are permitted provided that the following conditions 9e4b0a90eSBrooks Davis * are met: 10e4b0a90eSBrooks Davis * 1. Redistributions of source code must retain the above copyright 11e4b0a90eSBrooks Davis * notice, this list of conditions and the following disclaimer. 12e4b0a90eSBrooks Davis * 2. Redistributions in binary form must reproduce the above copyright 13e4b0a90eSBrooks Davis * notice, this list of conditions and the following disclaimer in the 14e4b0a90eSBrooks Davis * documentation and/or other materials provided with the distribution. 15e4b0a90eSBrooks Davis * 16e4b0a90eSBrooks Davis * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 17e4b0a90eSBrooks Davis * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18e4b0a90eSBrooks Davis * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19e4b0a90eSBrooks Davis * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 20e4b0a90eSBrooks Davis * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21e4b0a90eSBrooks Davis * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22e4b0a90eSBrooks Davis * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23e4b0a90eSBrooks Davis * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24e4b0a90eSBrooks Davis * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25e4b0a90eSBrooks Davis * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26e4b0a90eSBrooks Davis * SUCH DAMAGE. 27e4b0a90eSBrooks Davis */ 28e4b0a90eSBrooks Davis 29e4b0a90eSBrooks Davis #include <sys/cdefs.h> 30e4b0a90eSBrooks Davis __FBSDID("$FreeBSD$"); 31e4b0a90eSBrooks Davis 32e4b0a90eSBrooks Davis #include <sys/param.h> 33e4b0a90eSBrooks Davis #include <sys/mman.h> 34e4b0a90eSBrooks Davis #include <sys/sysctl.h> 35e4b0a90eSBrooks Davis #include <sys/resource.h> 36e4b0a90eSBrooks Davis #include <opencrypto/cryptodev.h> 37e4b0a90eSBrooks Davis 38e4b0a90eSBrooks Davis #include <assert.h> 39e4b0a90eSBrooks Davis #include <err.h> 40e4b0a90eSBrooks Davis #include <errno.h> 41e4b0a90eSBrooks Davis #include <fcntl.h> 42e4b0a90eSBrooks Davis #include <libgeom.h> 43e4b0a90eSBrooks Davis #include <paths.h> 44e4b0a90eSBrooks Davis #include <readpassphrase.h> 45e4b0a90eSBrooks Davis #include <stdbool.h> 46e4b0a90eSBrooks Davis #include <stdint.h> 47e4b0a90eSBrooks Davis #include <stdio.h> 48e4b0a90eSBrooks Davis #include <stdlib.h> 49e4b0a90eSBrooks Davis #include <string.h> 50e4b0a90eSBrooks Davis #include <strings.h> 51e4b0a90eSBrooks Davis #include <unistd.h> 52e4b0a90eSBrooks Davis 53e4b0a90eSBrooks Davis #include <geom/eli/g_eli.h> 54e4b0a90eSBrooks Davis #include <geom/eli/pkcs5v2.h> 55e4b0a90eSBrooks Davis 56e4b0a90eSBrooks Davis #include "core/geom.h" 57e4b0a90eSBrooks Davis #include "misc/subr.h" 58e4b0a90eSBrooks Davis 59e4b0a90eSBrooks Davis 60e4b0a90eSBrooks Davis uint32_t lib_version = G_LIB_VERSION; 61e4b0a90eSBrooks Davis uint32_t version = G_ELI_VERSION; 62e4b0a90eSBrooks Davis 63e4b0a90eSBrooks Davis #define GELI_BACKUP_DIR "/var/backups/" 64e4b0a90eSBrooks Davis #define GELI_ENC_ALGO "aes" 65217df2daSBen Woods #define BUFSIZE 1024 66217df2daSBen Woods 67217df2daSBen Woods /* 68217df2daSBen Woods * Passphrase cached when attaching multiple providers, in order to be more 69217df2daSBen Woods * user-friendly if they are using the same passphrase. 70217df2daSBen Woods */ 71217df2daSBen Woods static char cached_passphrase[BUFSIZE] = ""; 72e4b0a90eSBrooks Davis 73e4b0a90eSBrooks Davis static void eli_main(struct gctl_req *req, unsigned flags); 74e4b0a90eSBrooks Davis static void eli_init(struct gctl_req *req); 75e4b0a90eSBrooks Davis static void eli_attach(struct gctl_req *req); 76e4b0a90eSBrooks Davis static void eli_configure(struct gctl_req *req); 77e4b0a90eSBrooks Davis static void eli_setkey(struct gctl_req *req); 78e4b0a90eSBrooks Davis static void eli_delkey(struct gctl_req *req); 79e4b0a90eSBrooks Davis static void eli_resume(struct gctl_req *req); 80e4b0a90eSBrooks Davis static void eli_kill(struct gctl_req *req); 81e4b0a90eSBrooks Davis static void eli_backup(struct gctl_req *req); 82e4b0a90eSBrooks Davis static void eli_restore(struct gctl_req *req); 83e4b0a90eSBrooks Davis static void eli_resize(struct gctl_req *req); 84e4b0a90eSBrooks Davis static void eli_version(struct gctl_req *req); 85e4b0a90eSBrooks Davis static void eli_clear(struct gctl_req *req); 86e4b0a90eSBrooks Davis static void eli_dump(struct gctl_req *req); 87e4b0a90eSBrooks Davis 88e4b0a90eSBrooks Davis static int eli_backup_create(struct gctl_req *req, const char *prov, 89e4b0a90eSBrooks Davis const char *file); 90e4b0a90eSBrooks Davis 91e4b0a90eSBrooks Davis /* 92e4b0a90eSBrooks Davis * Available commands: 93e4b0a90eSBrooks Davis * 94*2f07cdf8SPawel Jakub Dawidek * init [-bdgPRTv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] [-s sectorsize] [-V version] prov ... 95e4b0a90eSBrooks Davis * label - alias for 'init' 96217df2daSBen Woods * attach [-Cdprv] [-n keyno] [-j passfile] [-k keyfile] prov ... 97e4b0a90eSBrooks Davis * detach [-fl] prov ... 98e4b0a90eSBrooks Davis * stop - alias for 'detach' 99*2f07cdf8SPawel Jakub Dawidek * onetime [-dRT] [-a aalgo] [-e ealgo] [-l keylen] prov 100*2f07cdf8SPawel Jakub Dawidek * configure [-bBgGrRtT] prov ... 101e4b0a90eSBrooks Davis * setkey [-pPv] [-n keyno] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov 102e4b0a90eSBrooks Davis * delkey [-afv] [-n keyno] prov 103e4b0a90eSBrooks Davis * suspend [-v] -a | prov ... 104e4b0a90eSBrooks Davis * resume [-pv] [-j passfile] [-k keyfile] prov 105e4b0a90eSBrooks Davis * kill [-av] [prov ...] 106e4b0a90eSBrooks Davis * backup [-v] prov file 107e4b0a90eSBrooks Davis * restore [-fv] file prov 108e4b0a90eSBrooks Davis * resize [-v] -s oldsize prov 109e4b0a90eSBrooks Davis * version [prov ...] 110e4b0a90eSBrooks Davis * clear [-v] prov ... 111e4b0a90eSBrooks Davis * dump [-v] prov ... 112e4b0a90eSBrooks Davis */ 113e4b0a90eSBrooks Davis struct g_command class_commands[] = { 114e4b0a90eSBrooks Davis { "init", G_FLAG_VERBOSE, eli_main, 115e4b0a90eSBrooks Davis { 116e4b0a90eSBrooks Davis { 'a', "aalgo", "", G_TYPE_STRING }, 117e4b0a90eSBrooks Davis { 'b', "boot", NULL, G_TYPE_BOOL }, 118e4b0a90eSBrooks Davis { 'B', "backupfile", "", G_TYPE_STRING }, 119e4b0a90eSBrooks Davis { 'd', "displaypass", NULL, G_TYPE_BOOL }, 120e4b0a90eSBrooks Davis { 'e', "ealgo", "", G_TYPE_STRING }, 121e4b0a90eSBrooks Davis { 'g', "geliboot", NULL, G_TYPE_BOOL }, 122e4b0a90eSBrooks Davis { 'i', "iterations", "-1", G_TYPE_NUMBER }, 123e4b0a90eSBrooks Davis { 'J', "newpassfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 124e4b0a90eSBrooks Davis { 'K', "newkeyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 125e4b0a90eSBrooks Davis { 'l', "keylen", "0", G_TYPE_NUMBER }, 126e4b0a90eSBrooks Davis { 'P', "nonewpassphrase", NULL, G_TYPE_BOOL }, 127*2f07cdf8SPawel Jakub Dawidek { 'R', "noautoresize", NULL, G_TYPE_BOOL }, 128e4b0a90eSBrooks Davis { 's', "sectorsize", "0", G_TYPE_NUMBER }, 129e4b0a90eSBrooks Davis { 'T', "notrim", NULL, G_TYPE_BOOL }, 130e4b0a90eSBrooks Davis { 'V', "mdversion", "-1", G_TYPE_NUMBER }, 131e4b0a90eSBrooks Davis G_OPT_SENTINEL 132e4b0a90eSBrooks Davis }, 133*2f07cdf8SPawel Jakub Dawidek "[-bdgPRTv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] [-s sectorsize] [-V version] prov ..." 134e4b0a90eSBrooks Davis }, 135e4b0a90eSBrooks Davis { "label", G_FLAG_VERBOSE, eli_main, 136e4b0a90eSBrooks Davis { 137e4b0a90eSBrooks Davis { 'a', "aalgo", "", G_TYPE_STRING }, 138e4b0a90eSBrooks Davis { 'b', "boot", NULL, G_TYPE_BOOL }, 139e4b0a90eSBrooks Davis { 'B', "backupfile", "", G_TYPE_STRING }, 140e4b0a90eSBrooks Davis { 'd', "displaypass", NULL, G_TYPE_BOOL }, 141e4b0a90eSBrooks Davis { 'e', "ealgo", "", G_TYPE_STRING }, 142e4b0a90eSBrooks Davis { 'g', "geliboot", NULL, G_TYPE_BOOL }, 143e4b0a90eSBrooks Davis { 'i', "iterations", "-1", G_TYPE_NUMBER }, 144e4b0a90eSBrooks Davis { 'J', "newpassfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 145e4b0a90eSBrooks Davis { 'K', "newkeyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 146e4b0a90eSBrooks Davis { 'l', "keylen", "0", G_TYPE_NUMBER }, 147e4b0a90eSBrooks Davis { 'P', "nonewpassphrase", NULL, G_TYPE_BOOL }, 148*2f07cdf8SPawel Jakub Dawidek { 'R', "noautoresize", NULL, G_TYPE_BOOL }, 149e4b0a90eSBrooks Davis { 's', "sectorsize", "0", G_TYPE_NUMBER }, 150b4f850c0SPawel Jakub Dawidek { 'T', "notrim", NULL, G_TYPE_BOOL }, 151e4b0a90eSBrooks Davis { 'V', "mdversion", "-1", G_TYPE_NUMBER }, 152e4b0a90eSBrooks Davis G_OPT_SENTINEL 153e4b0a90eSBrooks Davis }, 154e4b0a90eSBrooks Davis "- an alias for 'init'" 155e4b0a90eSBrooks Davis }, 156e4b0a90eSBrooks Davis { "attach", G_FLAG_VERBOSE | G_FLAG_LOADKLD, eli_main, 157e4b0a90eSBrooks Davis { 158e4b0a90eSBrooks Davis { 'C', "dryrun", NULL, G_TYPE_BOOL }, 159e4b0a90eSBrooks Davis { 'd', "detach", NULL, G_TYPE_BOOL }, 160e4b0a90eSBrooks Davis { 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 161e4b0a90eSBrooks Davis { 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 162e4b0a90eSBrooks Davis { 'n', "keyno", "-1", G_TYPE_NUMBER }, 163e4b0a90eSBrooks Davis { 'p', "nopassphrase", NULL, G_TYPE_BOOL }, 164e4b0a90eSBrooks Davis { 'r', "readonly", NULL, G_TYPE_BOOL }, 165e4b0a90eSBrooks Davis G_OPT_SENTINEL 166e4b0a90eSBrooks Davis }, 167217df2daSBen Woods "[-Cdprv] [-n keyno] [-j passfile] [-k keyfile] prov ..." 168e4b0a90eSBrooks Davis }, 169e4b0a90eSBrooks Davis { "detach", 0, NULL, 170e4b0a90eSBrooks Davis { 171e4b0a90eSBrooks Davis { 'f', "force", NULL, G_TYPE_BOOL }, 172e4b0a90eSBrooks Davis { 'l', "last", NULL, G_TYPE_BOOL }, 173e4b0a90eSBrooks Davis G_OPT_SENTINEL 174e4b0a90eSBrooks Davis }, 175e4b0a90eSBrooks Davis "[-fl] prov ..." 176e4b0a90eSBrooks Davis }, 177e4b0a90eSBrooks Davis { "stop", 0, NULL, 178e4b0a90eSBrooks Davis { 179e4b0a90eSBrooks Davis { 'f', "force", NULL, G_TYPE_BOOL }, 180e4b0a90eSBrooks Davis { 'l', "last", NULL, G_TYPE_BOOL }, 181e4b0a90eSBrooks Davis G_OPT_SENTINEL 182e4b0a90eSBrooks Davis }, 183e4b0a90eSBrooks Davis "- an alias for 'detach'" 184e4b0a90eSBrooks Davis }, 185e4b0a90eSBrooks Davis { "onetime", G_FLAG_VERBOSE | G_FLAG_LOADKLD, NULL, 186e4b0a90eSBrooks Davis { 187e4b0a90eSBrooks Davis { 'a', "aalgo", "", G_TYPE_STRING }, 188e4b0a90eSBrooks Davis { 'd', "detach", NULL, G_TYPE_BOOL }, 189e4b0a90eSBrooks Davis { 'e', "ealgo", GELI_ENC_ALGO, G_TYPE_STRING }, 190e4b0a90eSBrooks Davis { 'l', "keylen", "0", G_TYPE_NUMBER }, 191*2f07cdf8SPawel Jakub Dawidek { 'R', "noautoresize", NULL, G_TYPE_BOOL }, 192e4b0a90eSBrooks Davis { 's', "sectorsize", "0", G_TYPE_NUMBER }, 193e4b0a90eSBrooks Davis { 'T', "notrim", NULL, G_TYPE_BOOL }, 194e4b0a90eSBrooks Davis G_OPT_SENTINEL 195e4b0a90eSBrooks Davis }, 196*2f07cdf8SPawel Jakub Dawidek "[-dRT] [-a aalgo] [-e ealgo] [-l keylen] [-s sectorsize] prov" 197e4b0a90eSBrooks Davis }, 198e4b0a90eSBrooks Davis { "configure", G_FLAG_VERBOSE, eli_main, 199e4b0a90eSBrooks Davis { 200e4b0a90eSBrooks Davis { 'b', "boot", NULL, G_TYPE_BOOL }, 201e4b0a90eSBrooks Davis { 'B', "noboot", NULL, G_TYPE_BOOL }, 202e4b0a90eSBrooks Davis { 'd', "displaypass", NULL, G_TYPE_BOOL }, 203e4b0a90eSBrooks Davis { 'D', "nodisplaypass", NULL, G_TYPE_BOOL }, 204e4b0a90eSBrooks Davis { 'g', "geliboot", NULL, G_TYPE_BOOL }, 205e4b0a90eSBrooks Davis { 'G', "nogeliboot", NULL, G_TYPE_BOOL }, 206*2f07cdf8SPawel Jakub Dawidek { 'r', "autoresize", NULL, G_TYPE_BOOL }, 207*2f07cdf8SPawel Jakub Dawidek { 'R', "noautoresize", NULL, G_TYPE_BOOL }, 208e4b0a90eSBrooks Davis { 't', "trim", NULL, G_TYPE_BOOL }, 209e4b0a90eSBrooks Davis { 'T', "notrim", NULL, G_TYPE_BOOL }, 210e4b0a90eSBrooks Davis G_OPT_SENTINEL 211e4b0a90eSBrooks Davis }, 212*2f07cdf8SPawel Jakub Dawidek "[-bBdDgGrRtT] prov ..." 213e4b0a90eSBrooks Davis }, 214e4b0a90eSBrooks Davis { "setkey", G_FLAG_VERBOSE, eli_main, 215e4b0a90eSBrooks Davis { 216e4b0a90eSBrooks Davis { 'i', "iterations", "-1", G_TYPE_NUMBER }, 217e4b0a90eSBrooks Davis { 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 218e4b0a90eSBrooks Davis { 'J', "newpassfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 219e4b0a90eSBrooks Davis { 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 220e4b0a90eSBrooks Davis { 'K', "newkeyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 221e4b0a90eSBrooks Davis { 'n', "keyno", "-1", G_TYPE_NUMBER }, 222e4b0a90eSBrooks Davis { 'p', "nopassphrase", NULL, G_TYPE_BOOL }, 223e4b0a90eSBrooks Davis { 'P', "nonewpassphrase", NULL, G_TYPE_BOOL }, 224e4b0a90eSBrooks Davis G_OPT_SENTINEL 225e4b0a90eSBrooks Davis }, 226e4b0a90eSBrooks Davis "[-pPv] [-n keyno] [-i iterations] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov" 227e4b0a90eSBrooks Davis }, 228e4b0a90eSBrooks Davis { "delkey", G_FLAG_VERBOSE, eli_main, 229e4b0a90eSBrooks Davis { 230e4b0a90eSBrooks Davis { 'a', "all", NULL, G_TYPE_BOOL }, 231e4b0a90eSBrooks Davis { 'f', "force", NULL, G_TYPE_BOOL }, 232e4b0a90eSBrooks Davis { 'n', "keyno", "-1", G_TYPE_NUMBER }, 233e4b0a90eSBrooks Davis G_OPT_SENTINEL 234e4b0a90eSBrooks Davis }, 235e4b0a90eSBrooks Davis "[-afv] [-n keyno] prov" 236e4b0a90eSBrooks Davis }, 237e4b0a90eSBrooks Davis { "suspend", G_FLAG_VERBOSE, NULL, 238e4b0a90eSBrooks Davis { 239e4b0a90eSBrooks Davis { 'a', "all", NULL, G_TYPE_BOOL }, 240e4b0a90eSBrooks Davis G_OPT_SENTINEL 241e4b0a90eSBrooks Davis }, 242e4b0a90eSBrooks Davis "[-v] -a | prov ..." 243e4b0a90eSBrooks Davis }, 244e4b0a90eSBrooks Davis { "resume", G_FLAG_VERBOSE, eli_main, 245e4b0a90eSBrooks Davis { 246e4b0a90eSBrooks Davis { 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 247e4b0a90eSBrooks Davis { 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 248e4b0a90eSBrooks Davis { 'p', "nopassphrase", NULL, G_TYPE_BOOL }, 249e4b0a90eSBrooks Davis G_OPT_SENTINEL 250e4b0a90eSBrooks Davis }, 251e4b0a90eSBrooks Davis "[-pv] [-j passfile] [-k keyfile] prov" 252e4b0a90eSBrooks Davis }, 253e4b0a90eSBrooks Davis { "kill", G_FLAG_VERBOSE, eli_main, 254e4b0a90eSBrooks Davis { 255e4b0a90eSBrooks Davis { 'a', "all", NULL, G_TYPE_BOOL }, 256e4b0a90eSBrooks Davis G_OPT_SENTINEL 257e4b0a90eSBrooks Davis }, 258e4b0a90eSBrooks Davis "[-av] [prov ...]" 259e4b0a90eSBrooks Davis }, 260e4b0a90eSBrooks Davis { "backup", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS, 261e4b0a90eSBrooks Davis "[-v] prov file" 262e4b0a90eSBrooks Davis }, 263e4b0a90eSBrooks Davis { "restore", G_FLAG_VERBOSE, eli_main, 264e4b0a90eSBrooks Davis { 265e4b0a90eSBrooks Davis { 'f', "force", NULL, G_TYPE_BOOL }, 266e4b0a90eSBrooks Davis G_OPT_SENTINEL 267e4b0a90eSBrooks Davis }, 268e4b0a90eSBrooks Davis "[-fv] file prov" 269e4b0a90eSBrooks Davis }, 270e4b0a90eSBrooks Davis { "resize", G_FLAG_VERBOSE, eli_main, 271e4b0a90eSBrooks Davis { 272e4b0a90eSBrooks Davis { 's', "oldsize", NULL, G_TYPE_NUMBER }, 273e4b0a90eSBrooks Davis G_OPT_SENTINEL 274e4b0a90eSBrooks Davis }, 275e4b0a90eSBrooks Davis "[-v] -s oldsize prov" 276e4b0a90eSBrooks Davis }, 277e4b0a90eSBrooks Davis { "version", G_FLAG_LOADKLD, eli_main, G_NULL_OPTS, 278e4b0a90eSBrooks Davis "[prov ...]" 279e4b0a90eSBrooks Davis }, 280e4b0a90eSBrooks Davis { "clear", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS, 281e4b0a90eSBrooks Davis "[-v] prov ..." 282e4b0a90eSBrooks Davis }, 283e4b0a90eSBrooks Davis { "dump", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS, 284e4b0a90eSBrooks Davis "[-v] prov ..." 285e4b0a90eSBrooks Davis }, 286e4b0a90eSBrooks Davis G_CMD_SENTINEL 287e4b0a90eSBrooks Davis }; 288e4b0a90eSBrooks Davis 289e4b0a90eSBrooks Davis static int verbose = 0; 290e4b0a90eSBrooks Davis 291e4b0a90eSBrooks Davis static int 292e4b0a90eSBrooks Davis eli_protect(struct gctl_req *req) 293e4b0a90eSBrooks Davis { 294e4b0a90eSBrooks Davis struct rlimit rl; 295e4b0a90eSBrooks Davis 296e4b0a90eSBrooks Davis /* Disable core dumps. */ 297e4b0a90eSBrooks Davis rl.rlim_cur = 0; 298e4b0a90eSBrooks Davis rl.rlim_max = 0; 299e4b0a90eSBrooks Davis if (setrlimit(RLIMIT_CORE, &rl) == -1) { 300e4b0a90eSBrooks Davis gctl_error(req, "Cannot disable core dumps: %s.", 301e4b0a90eSBrooks Davis strerror(errno)); 302e4b0a90eSBrooks Davis return (-1); 303e4b0a90eSBrooks Davis } 304e4b0a90eSBrooks Davis /* Disable swapping. */ 305e4b0a90eSBrooks Davis if (mlockall(MCL_FUTURE) == -1) { 306e4b0a90eSBrooks Davis gctl_error(req, "Cannot lock memory: %s.", strerror(errno)); 307e4b0a90eSBrooks Davis return (-1); 308e4b0a90eSBrooks Davis } 309e4b0a90eSBrooks Davis return (0); 310e4b0a90eSBrooks Davis } 311e4b0a90eSBrooks Davis 312e4b0a90eSBrooks Davis static void 313e4b0a90eSBrooks Davis eli_main(struct gctl_req *req, unsigned int flags) 314e4b0a90eSBrooks Davis { 315e4b0a90eSBrooks Davis const char *name; 316e4b0a90eSBrooks Davis 317e4b0a90eSBrooks Davis if (eli_protect(req) == -1) 318e4b0a90eSBrooks Davis return; 319e4b0a90eSBrooks Davis 320e4b0a90eSBrooks Davis if ((flags & G_FLAG_VERBOSE) != 0) 321e4b0a90eSBrooks Davis verbose = 1; 322e4b0a90eSBrooks Davis 323e4b0a90eSBrooks Davis name = gctl_get_ascii(req, "verb"); 324e4b0a90eSBrooks Davis if (name == NULL) { 325e4b0a90eSBrooks Davis gctl_error(req, "No '%s' argument.", "verb"); 326e4b0a90eSBrooks Davis return; 327e4b0a90eSBrooks Davis } 328e4b0a90eSBrooks Davis if (strcmp(name, "init") == 0 || strcmp(name, "label") == 0) 329e4b0a90eSBrooks Davis eli_init(req); 330e4b0a90eSBrooks Davis else if (strcmp(name, "attach") == 0) 331e4b0a90eSBrooks Davis eli_attach(req); 332e4b0a90eSBrooks Davis else if (strcmp(name, "configure") == 0) 333e4b0a90eSBrooks Davis eli_configure(req); 334e4b0a90eSBrooks Davis else if (strcmp(name, "setkey") == 0) 335e4b0a90eSBrooks Davis eli_setkey(req); 336e4b0a90eSBrooks Davis else if (strcmp(name, "delkey") == 0) 337e4b0a90eSBrooks Davis eli_delkey(req); 338e4b0a90eSBrooks Davis else if (strcmp(name, "resume") == 0) 339e4b0a90eSBrooks Davis eli_resume(req); 340e4b0a90eSBrooks Davis else if (strcmp(name, "kill") == 0) 341e4b0a90eSBrooks Davis eli_kill(req); 342e4b0a90eSBrooks Davis else if (strcmp(name, "backup") == 0) 343e4b0a90eSBrooks Davis eli_backup(req); 344e4b0a90eSBrooks Davis else if (strcmp(name, "restore") == 0) 345e4b0a90eSBrooks Davis eli_restore(req); 346e4b0a90eSBrooks Davis else if (strcmp(name, "resize") == 0) 347e4b0a90eSBrooks Davis eli_resize(req); 348e4b0a90eSBrooks Davis else if (strcmp(name, "version") == 0) 349e4b0a90eSBrooks Davis eli_version(req); 350e4b0a90eSBrooks Davis else if (strcmp(name, "dump") == 0) 351e4b0a90eSBrooks Davis eli_dump(req); 352e4b0a90eSBrooks Davis else if (strcmp(name, "clear") == 0) 353e4b0a90eSBrooks Davis eli_clear(req); 354e4b0a90eSBrooks Davis else 355e4b0a90eSBrooks Davis gctl_error(req, "Unknown command: %s.", name); 356e4b0a90eSBrooks Davis } 357e4b0a90eSBrooks Davis 358e4b0a90eSBrooks Davis static bool 359e4b0a90eSBrooks Davis eli_is_attached(const char *prov) 360e4b0a90eSBrooks Davis { 361e4b0a90eSBrooks Davis char name[MAXPATHLEN]; 362e4b0a90eSBrooks Davis 363e4b0a90eSBrooks Davis /* 364e4b0a90eSBrooks Davis * Not the best way to do it, but the easiest. 365e4b0a90eSBrooks Davis * We try to open provider and check if it is a GEOM provider 366e4b0a90eSBrooks Davis * by asking about its sectorsize. 367e4b0a90eSBrooks Davis */ 368e4b0a90eSBrooks Davis snprintf(name, sizeof(name), "%s%s", prov, G_ELI_SUFFIX); 369e4b0a90eSBrooks Davis return (g_get_sectorsize(name) > 0); 370e4b0a90eSBrooks Davis } 371e4b0a90eSBrooks Davis 372e4b0a90eSBrooks Davis static int 373e4b0a90eSBrooks Davis eli_genkey_files(struct gctl_req *req, bool new, const char *type, 374e4b0a90eSBrooks Davis struct hmac_ctx *ctxp, char *passbuf, size_t passbufsize) 375e4b0a90eSBrooks Davis { 376e4b0a90eSBrooks Davis char *p, buf[BUFSIZE], argname[16]; 377e4b0a90eSBrooks Davis const char *file; 378e4b0a90eSBrooks Davis int error, fd, i; 379e4b0a90eSBrooks Davis ssize_t done; 380e4b0a90eSBrooks Davis 381e4b0a90eSBrooks Davis assert((strcmp(type, "keyfile") == 0 && ctxp != NULL && 382e4b0a90eSBrooks Davis passbuf == NULL && passbufsize == 0) || 383e4b0a90eSBrooks Davis (strcmp(type, "passfile") == 0 && ctxp == NULL && 384e4b0a90eSBrooks Davis passbuf != NULL && passbufsize > 0)); 385e4b0a90eSBrooks Davis assert(strcmp(type, "keyfile") == 0 || passbuf[0] == '\0'); 386e4b0a90eSBrooks Davis 387e4b0a90eSBrooks Davis for (i = 0; ; i++) { 388e4b0a90eSBrooks Davis snprintf(argname, sizeof(argname), "%s%s%d", 389e4b0a90eSBrooks Davis new ? "new" : "", type, i); 390e4b0a90eSBrooks Davis 391e4b0a90eSBrooks Davis /* No more {key,pass}files? */ 392e4b0a90eSBrooks Davis if (!gctl_has_param(req, argname)) 393e4b0a90eSBrooks Davis return (i); 394e4b0a90eSBrooks Davis 395e4b0a90eSBrooks Davis file = gctl_get_ascii(req, "%s", argname); 396e4b0a90eSBrooks Davis assert(file != NULL); 397e4b0a90eSBrooks Davis 398e4b0a90eSBrooks Davis if (strcmp(file, "-") == 0) 399e4b0a90eSBrooks Davis fd = STDIN_FILENO; 400e4b0a90eSBrooks Davis else { 401e4b0a90eSBrooks Davis fd = open(file, O_RDONLY); 402e4b0a90eSBrooks Davis if (fd == -1) { 403e4b0a90eSBrooks Davis gctl_error(req, "Cannot open %s %s: %s.", 404e4b0a90eSBrooks Davis type, file, strerror(errno)); 405e4b0a90eSBrooks Davis return (-1); 406e4b0a90eSBrooks Davis } 407e4b0a90eSBrooks Davis } 408e4b0a90eSBrooks Davis if (strcmp(type, "keyfile") == 0) { 409e4b0a90eSBrooks Davis while ((done = read(fd, buf, sizeof(buf))) > 0) 410e4b0a90eSBrooks Davis g_eli_crypto_hmac_update(ctxp, buf, done); 411e4b0a90eSBrooks Davis } else /* if (strcmp(type, "passfile") == 0) */ { 412e4b0a90eSBrooks Davis assert(strcmp(type, "passfile") == 0); 413e4b0a90eSBrooks Davis 414e4b0a90eSBrooks Davis while ((done = read(fd, buf, sizeof(buf) - 1)) > 0) { 415e4b0a90eSBrooks Davis buf[done] = '\0'; 416e4b0a90eSBrooks Davis p = strchr(buf, '\n'); 417e4b0a90eSBrooks Davis if (p != NULL) { 418e4b0a90eSBrooks Davis *p = '\0'; 419e4b0a90eSBrooks Davis done = p - buf; 420e4b0a90eSBrooks Davis } 421e4b0a90eSBrooks Davis if (strlcat(passbuf, buf, passbufsize) >= 422e4b0a90eSBrooks Davis passbufsize) { 423e4b0a90eSBrooks Davis gctl_error(req, 424e4b0a90eSBrooks Davis "Passphrase in %s too long.", file); 425217df2daSBen Woods explicit_bzero(buf, sizeof(buf)); 426e4b0a90eSBrooks Davis return (-1); 427e4b0a90eSBrooks Davis } 428e4b0a90eSBrooks Davis if (p != NULL) 429e4b0a90eSBrooks Davis break; 430e4b0a90eSBrooks Davis } 431e4b0a90eSBrooks Davis } 432e4b0a90eSBrooks Davis error = errno; 433e4b0a90eSBrooks Davis if (strcmp(file, "-") != 0) 434e4b0a90eSBrooks Davis close(fd); 435217df2daSBen Woods explicit_bzero(buf, sizeof(buf)); 436e4b0a90eSBrooks Davis if (done == -1) { 437e4b0a90eSBrooks Davis gctl_error(req, "Cannot read %s %s: %s.", 438e4b0a90eSBrooks Davis type, file, strerror(error)); 439e4b0a90eSBrooks Davis return (-1); 440e4b0a90eSBrooks Davis } 441e4b0a90eSBrooks Davis } 442e4b0a90eSBrooks Davis /* NOTREACHED */ 443e4b0a90eSBrooks Davis } 444e4b0a90eSBrooks Davis 445e4b0a90eSBrooks Davis static int 446e4b0a90eSBrooks Davis eli_genkey_passphrase_prompt(struct gctl_req *req, bool new, char *passbuf, 447e4b0a90eSBrooks Davis size_t passbufsize) 448e4b0a90eSBrooks Davis { 449e4b0a90eSBrooks Davis char *p; 450e4b0a90eSBrooks Davis 451e4b0a90eSBrooks Davis for (;;) { 452e4b0a90eSBrooks Davis p = readpassphrase( 453e4b0a90eSBrooks Davis new ? "Enter new passphrase: " : "Enter passphrase: ", 454e4b0a90eSBrooks Davis passbuf, passbufsize, RPP_ECHO_OFF | RPP_REQUIRE_TTY); 455e4b0a90eSBrooks Davis if (p == NULL) { 456217df2daSBen Woods explicit_bzero(passbuf, passbufsize); 457e4b0a90eSBrooks Davis gctl_error(req, "Cannot read passphrase: %s.", 458e4b0a90eSBrooks Davis strerror(errno)); 459e4b0a90eSBrooks Davis return (-1); 460e4b0a90eSBrooks Davis } 461e4b0a90eSBrooks Davis 462e4b0a90eSBrooks Davis if (new) { 463e4b0a90eSBrooks Davis char tmpbuf[BUFSIZE]; 464e4b0a90eSBrooks Davis 465e4b0a90eSBrooks Davis p = readpassphrase("Reenter new passphrase: ", 466e4b0a90eSBrooks Davis tmpbuf, sizeof(tmpbuf), 467e4b0a90eSBrooks Davis RPP_ECHO_OFF | RPP_REQUIRE_TTY); 468e4b0a90eSBrooks Davis if (p == NULL) { 469217df2daSBen Woods explicit_bzero(passbuf, passbufsize); 470e4b0a90eSBrooks Davis gctl_error(req, 471e4b0a90eSBrooks Davis "Cannot read passphrase: %s.", 472e4b0a90eSBrooks Davis strerror(errno)); 473e4b0a90eSBrooks Davis return (-1); 474e4b0a90eSBrooks Davis } 475e4b0a90eSBrooks Davis 476e4b0a90eSBrooks Davis if (strcmp(passbuf, tmpbuf) != 0) { 477217df2daSBen Woods explicit_bzero(passbuf, passbufsize); 478e4b0a90eSBrooks Davis fprintf(stderr, "They didn't match.\n"); 479e4b0a90eSBrooks Davis continue; 480e4b0a90eSBrooks Davis } 481217df2daSBen Woods explicit_bzero(tmpbuf, sizeof(tmpbuf)); 482e4b0a90eSBrooks Davis } 483e4b0a90eSBrooks Davis return (0); 484e4b0a90eSBrooks Davis } 485e4b0a90eSBrooks Davis /* NOTREACHED */ 486e4b0a90eSBrooks Davis } 487e4b0a90eSBrooks Davis 488e4b0a90eSBrooks Davis static int 489e4b0a90eSBrooks Davis eli_genkey_passphrase(struct gctl_req *req, struct g_eli_metadata *md, bool new, 490e4b0a90eSBrooks Davis struct hmac_ctx *ctxp) 491e4b0a90eSBrooks Davis { 492e4b0a90eSBrooks Davis char passbuf[BUFSIZE]; 493e4b0a90eSBrooks Davis bool nopassphrase; 494e4b0a90eSBrooks Davis int nfiles; 495e4b0a90eSBrooks Davis 496217df2daSBen Woods /* 497217df2daSBen Woods * Return error if the 'do not use passphrase' flag was given but a 498217df2daSBen Woods * passfile was provided. 499217df2daSBen Woods */ 500e4b0a90eSBrooks Davis nopassphrase = 501e4b0a90eSBrooks Davis gctl_get_int(req, new ? "nonewpassphrase" : "nopassphrase"); 502e4b0a90eSBrooks Davis if (nopassphrase) { 503e4b0a90eSBrooks Davis if (gctl_has_param(req, new ? "newpassfile0" : "passfile0")) { 504e4b0a90eSBrooks Davis gctl_error(req, 505e4b0a90eSBrooks Davis "Options -%c and -%c are mutually exclusive.", 506e4b0a90eSBrooks Davis new ? 'J' : 'j', new ? 'P' : 'p'); 507e4b0a90eSBrooks Davis return (-1); 508e4b0a90eSBrooks Davis } 509e4b0a90eSBrooks Davis return (0); 510e4b0a90eSBrooks Davis } 511e4b0a90eSBrooks Davis 512217df2daSBen Woods /* 513217df2daSBen Woods * Return error if using a provider which does not require a passphrase 514217df2daSBen Woods * but the 'do not use passphrase' flag was not given. 515217df2daSBen Woods */ 516e4b0a90eSBrooks Davis if (!new && md->md_iterations == -1) { 517e4b0a90eSBrooks Davis gctl_error(req, "Missing -p flag."); 518e4b0a90eSBrooks Davis return (-1); 519e4b0a90eSBrooks Davis } 520e4b0a90eSBrooks Davis passbuf[0] = '\0'; 521217df2daSBen Woods 522217df2daSBen Woods /* Use cached passphrase if defined. */ 523217df2daSBen Woods if (strlen(cached_passphrase) > 0) { 524217df2daSBen Woods strlcpy(passbuf, cached_passphrase, sizeof(passbuf)); 525217df2daSBen Woods } else { 526e4b0a90eSBrooks Davis nfiles = eli_genkey_files(req, new, "passfile", NULL, passbuf, 527e4b0a90eSBrooks Davis sizeof(passbuf)); 528217df2daSBen Woods if (nfiles == -1) { 529e4b0a90eSBrooks Davis return (-1); 530217df2daSBen Woods } else if (nfiles == 0) { 531e4b0a90eSBrooks Davis if (eli_genkey_passphrase_prompt(req, new, passbuf, 532e4b0a90eSBrooks Davis sizeof(passbuf)) == -1) { 533e4b0a90eSBrooks Davis return (-1); 534e4b0a90eSBrooks Davis } 535e4b0a90eSBrooks Davis } 536217df2daSBen Woods /* Cache the passphrase for other providers. */ 537217df2daSBen Woods strlcpy(cached_passphrase, passbuf, sizeof(cached_passphrase)); 538217df2daSBen Woods } 539e4b0a90eSBrooks Davis /* 540e4b0a90eSBrooks Davis * Field md_iterations equal to -1 means "choose some sane 541e4b0a90eSBrooks Davis * value for me". 542e4b0a90eSBrooks Davis */ 543e4b0a90eSBrooks Davis if (md->md_iterations == -1) { 544e4b0a90eSBrooks Davis assert(new); 545e4b0a90eSBrooks Davis if (verbose) 546e4b0a90eSBrooks Davis printf("Calculating number of iterations...\n"); 547e4b0a90eSBrooks Davis md->md_iterations = pkcs5v2_calculate(2000000); 548e4b0a90eSBrooks Davis assert(md->md_iterations > 0); 549e4b0a90eSBrooks Davis if (verbose) { 550e4b0a90eSBrooks Davis printf("Done, using %d iterations.\n", 551e4b0a90eSBrooks Davis md->md_iterations); 552e4b0a90eSBrooks Davis } 553e4b0a90eSBrooks Davis } 554e4b0a90eSBrooks Davis /* 555e4b0a90eSBrooks Davis * If md_iterations is equal to 0, user doesn't want PKCS#5v2. 556e4b0a90eSBrooks Davis */ 557e4b0a90eSBrooks Davis if (md->md_iterations == 0) { 558e4b0a90eSBrooks Davis g_eli_crypto_hmac_update(ctxp, md->md_salt, 559e4b0a90eSBrooks Davis sizeof(md->md_salt)); 560e4b0a90eSBrooks Davis g_eli_crypto_hmac_update(ctxp, passbuf, strlen(passbuf)); 561e4b0a90eSBrooks Davis } else /* if (md->md_iterations > 0) */ { 562e4b0a90eSBrooks Davis unsigned char dkey[G_ELI_USERKEYLEN]; 563e4b0a90eSBrooks Davis 564e4b0a90eSBrooks Davis pkcs5v2_genkey(dkey, sizeof(dkey), md->md_salt, 565e4b0a90eSBrooks Davis sizeof(md->md_salt), passbuf, md->md_iterations); 566e4b0a90eSBrooks Davis g_eli_crypto_hmac_update(ctxp, dkey, sizeof(dkey)); 567217df2daSBen Woods explicit_bzero(dkey, sizeof(dkey)); 568e4b0a90eSBrooks Davis } 569217df2daSBen Woods explicit_bzero(passbuf, sizeof(passbuf)); 570e4b0a90eSBrooks Davis 571e4b0a90eSBrooks Davis return (0); 572e4b0a90eSBrooks Davis } 573e4b0a90eSBrooks Davis 574e4b0a90eSBrooks Davis static unsigned char * 575e4b0a90eSBrooks Davis eli_genkey(struct gctl_req *req, struct g_eli_metadata *md, unsigned char *key, 576e4b0a90eSBrooks Davis bool new) 577e4b0a90eSBrooks Davis { 578e4b0a90eSBrooks Davis struct hmac_ctx ctx; 579e4b0a90eSBrooks Davis bool nopassphrase; 580e4b0a90eSBrooks Davis int nfiles; 581e4b0a90eSBrooks Davis 582e4b0a90eSBrooks Davis nopassphrase = 583e4b0a90eSBrooks Davis gctl_get_int(req, new ? "nonewpassphrase" : "nopassphrase"); 584e4b0a90eSBrooks Davis 585e4b0a90eSBrooks Davis g_eli_crypto_hmac_init(&ctx, NULL, 0); 586e4b0a90eSBrooks Davis 587e4b0a90eSBrooks Davis nfiles = eli_genkey_files(req, new, "keyfile", &ctx, NULL, 0); 588e4b0a90eSBrooks Davis if (nfiles == -1) 589e4b0a90eSBrooks Davis return (NULL); 590e4b0a90eSBrooks Davis else if (nfiles == 0 && nopassphrase) { 591e4b0a90eSBrooks Davis gctl_error(req, "No key components given."); 592e4b0a90eSBrooks Davis return (NULL); 593e4b0a90eSBrooks Davis } 594e4b0a90eSBrooks Davis 595e4b0a90eSBrooks Davis if (eli_genkey_passphrase(req, md, new, &ctx) == -1) 596e4b0a90eSBrooks Davis return (NULL); 597e4b0a90eSBrooks Davis 598e4b0a90eSBrooks Davis g_eli_crypto_hmac_final(&ctx, key, 0); 599e4b0a90eSBrooks Davis 600e4b0a90eSBrooks Davis return (key); 601e4b0a90eSBrooks Davis } 602e4b0a90eSBrooks Davis 603e4b0a90eSBrooks Davis static int 604e4b0a90eSBrooks Davis eli_metadata_read(struct gctl_req *req, const char *prov, 605e4b0a90eSBrooks Davis struct g_eli_metadata *md) 606e4b0a90eSBrooks Davis { 607e4b0a90eSBrooks Davis unsigned char sector[sizeof(struct g_eli_metadata)]; 608e4b0a90eSBrooks Davis int error; 609e4b0a90eSBrooks Davis 610e4b0a90eSBrooks Davis if (g_get_sectorsize(prov) == 0) { 611e4b0a90eSBrooks Davis int fd; 612e4b0a90eSBrooks Davis 613e4b0a90eSBrooks Davis /* This is a file probably. */ 614e4b0a90eSBrooks Davis fd = open(prov, O_RDONLY); 615e4b0a90eSBrooks Davis if (fd == -1) { 616e4b0a90eSBrooks Davis gctl_error(req, "Cannot open %s: %s.", prov, 617e4b0a90eSBrooks Davis strerror(errno)); 618e4b0a90eSBrooks Davis return (-1); 619e4b0a90eSBrooks Davis } 620e4b0a90eSBrooks Davis if (read(fd, sector, sizeof(sector)) != sizeof(sector)) { 621e4b0a90eSBrooks Davis gctl_error(req, "Cannot read metadata from %s: %s.", 622e4b0a90eSBrooks Davis prov, strerror(errno)); 623e4b0a90eSBrooks Davis close(fd); 624e4b0a90eSBrooks Davis return (-1); 625e4b0a90eSBrooks Davis } 626e4b0a90eSBrooks Davis close(fd); 627e4b0a90eSBrooks Davis } else { 628e4b0a90eSBrooks Davis /* This is a GEOM provider. */ 629e4b0a90eSBrooks Davis error = g_metadata_read(prov, sector, sizeof(sector), 630e4b0a90eSBrooks Davis G_ELI_MAGIC); 631e4b0a90eSBrooks Davis if (error != 0) { 632e4b0a90eSBrooks Davis gctl_error(req, "Cannot read metadata from %s: %s.", 633e4b0a90eSBrooks Davis prov, strerror(error)); 634e4b0a90eSBrooks Davis return (-1); 635e4b0a90eSBrooks Davis } 636e4b0a90eSBrooks Davis } 637e4b0a90eSBrooks Davis error = eli_metadata_decode(sector, md); 638e4b0a90eSBrooks Davis switch (error) { 639e4b0a90eSBrooks Davis case 0: 640e4b0a90eSBrooks Davis break; 641e4b0a90eSBrooks Davis case EOPNOTSUPP: 642e4b0a90eSBrooks Davis gctl_error(req, 643e4b0a90eSBrooks Davis "Provider's %s metadata version %u is too new.\n" 644e4b0a90eSBrooks Davis "geli: The highest supported version is %u.", 645e4b0a90eSBrooks Davis prov, (unsigned int)md->md_version, G_ELI_VERSION); 646e4b0a90eSBrooks Davis return (-1); 647e4b0a90eSBrooks Davis case EINVAL: 648e4b0a90eSBrooks Davis gctl_error(req, "Inconsistent provider's %s metadata.", prov); 649e4b0a90eSBrooks Davis return (-1); 650e4b0a90eSBrooks Davis default: 651e4b0a90eSBrooks Davis gctl_error(req, 652e4b0a90eSBrooks Davis "Unexpected error while decoding provider's %s metadata: %s.", 653e4b0a90eSBrooks Davis prov, strerror(error)); 654e4b0a90eSBrooks Davis return (-1); 655e4b0a90eSBrooks Davis } 656e4b0a90eSBrooks Davis return (0); 657e4b0a90eSBrooks Davis } 658e4b0a90eSBrooks Davis 659e4b0a90eSBrooks Davis static int 660e4b0a90eSBrooks Davis eli_metadata_store(struct gctl_req *req, const char *prov, 661e4b0a90eSBrooks Davis struct g_eli_metadata *md) 662e4b0a90eSBrooks Davis { 663e4b0a90eSBrooks Davis unsigned char sector[sizeof(struct g_eli_metadata)]; 664e4b0a90eSBrooks Davis int error; 665e4b0a90eSBrooks Davis 666e4b0a90eSBrooks Davis eli_metadata_encode(md, sector); 667e4b0a90eSBrooks Davis if (g_get_sectorsize(prov) == 0) { 668e4b0a90eSBrooks Davis int fd; 669e4b0a90eSBrooks Davis 670e4b0a90eSBrooks Davis /* This is a file probably. */ 671e4b0a90eSBrooks Davis fd = open(prov, O_WRONLY | O_TRUNC); 672e4b0a90eSBrooks Davis if (fd == -1) { 673e4b0a90eSBrooks Davis gctl_error(req, "Cannot open %s: %s.", prov, 674e4b0a90eSBrooks Davis strerror(errno)); 675217df2daSBen Woods explicit_bzero(sector, sizeof(sector)); 676e4b0a90eSBrooks Davis return (-1); 677e4b0a90eSBrooks Davis } 678e4b0a90eSBrooks Davis if (write(fd, sector, sizeof(sector)) != sizeof(sector)) { 679e4b0a90eSBrooks Davis gctl_error(req, "Cannot write metadata to %s: %s.", 680e4b0a90eSBrooks Davis prov, strerror(errno)); 681217df2daSBen Woods explicit_bzero(sector, sizeof(sector)); 682e4b0a90eSBrooks Davis close(fd); 683e4b0a90eSBrooks Davis return (-1); 684e4b0a90eSBrooks Davis } 685e4b0a90eSBrooks Davis close(fd); 686e4b0a90eSBrooks Davis } else { 687e4b0a90eSBrooks Davis /* This is a GEOM provider. */ 688e4b0a90eSBrooks Davis error = g_metadata_store(prov, sector, sizeof(sector)); 689e4b0a90eSBrooks Davis if (error != 0) { 690e4b0a90eSBrooks Davis gctl_error(req, "Cannot write metadata to %s: %s.", 691e4b0a90eSBrooks Davis prov, strerror(errno)); 692217df2daSBen Woods explicit_bzero(sector, sizeof(sector)); 693e4b0a90eSBrooks Davis return (-1); 694e4b0a90eSBrooks Davis } 695e4b0a90eSBrooks Davis } 696217df2daSBen Woods explicit_bzero(sector, sizeof(sector)); 697e4b0a90eSBrooks Davis return (0); 698e4b0a90eSBrooks Davis } 699e4b0a90eSBrooks Davis 700e4b0a90eSBrooks Davis static void 701e4b0a90eSBrooks Davis eli_init(struct gctl_req *req) 702e4b0a90eSBrooks Davis { 703e4b0a90eSBrooks Davis struct g_eli_metadata md; 704368455ecSBen Woods struct gctl_req *r; 705e4b0a90eSBrooks Davis unsigned char sector[sizeof(struct g_eli_metadata)] __aligned(4); 706e4b0a90eSBrooks Davis unsigned char key[G_ELI_USERKEYLEN]; 707e4b0a90eSBrooks Davis char backfile[MAXPATHLEN]; 708e4b0a90eSBrooks Davis const char *str, *prov; 709e4b0a90eSBrooks Davis unsigned int secsize, version; 710e4b0a90eSBrooks Davis off_t mediasize; 711e4b0a90eSBrooks Davis intmax_t val; 712368455ecSBen Woods int error, i, nargs, nparams, param; 713368455ecSBen Woods const int one = 1; 714e4b0a90eSBrooks Davis 715e4b0a90eSBrooks Davis nargs = gctl_get_int(req, "nargs"); 716368455ecSBen Woods if (nargs <= 0) { 717368455ecSBen Woods gctl_error(req, "Too few arguments."); 718e4b0a90eSBrooks Davis return; 719e4b0a90eSBrooks Davis } 720e4b0a90eSBrooks Davis 721368455ecSBen Woods /* Start generating metadata for provider(s) being initialized. */ 722217df2daSBen Woods explicit_bzero(&md, sizeof(md)); 723e4b0a90eSBrooks Davis strlcpy(md.md_magic, G_ELI_MAGIC, sizeof(md.md_magic)); 724e4b0a90eSBrooks Davis val = gctl_get_intmax(req, "mdversion"); 725e4b0a90eSBrooks Davis if (val == -1) { 726e4b0a90eSBrooks Davis version = G_ELI_VERSION; 727e4b0a90eSBrooks Davis } else if (val < 0 || val > G_ELI_VERSION) { 728e4b0a90eSBrooks Davis gctl_error(req, 729e4b0a90eSBrooks Davis "Invalid version specified should be between %u and %u.", 730e4b0a90eSBrooks Davis G_ELI_VERSION_00, G_ELI_VERSION); 731e4b0a90eSBrooks Davis return; 732e4b0a90eSBrooks Davis } else { 733e4b0a90eSBrooks Davis version = val; 734e4b0a90eSBrooks Davis } 735e4b0a90eSBrooks Davis md.md_version = version; 736*2f07cdf8SPawel Jakub Dawidek md.md_flags = G_ELI_FLAG_AUTORESIZE; 737e4b0a90eSBrooks Davis if (gctl_get_int(req, "boot")) 738e4b0a90eSBrooks Davis md.md_flags |= G_ELI_FLAG_BOOT; 739e4b0a90eSBrooks Davis if (gctl_get_int(req, "geliboot")) 740e4b0a90eSBrooks Davis md.md_flags |= G_ELI_FLAG_GELIBOOT; 741e4b0a90eSBrooks Davis if (gctl_get_int(req, "displaypass")) 742e4b0a90eSBrooks Davis md.md_flags |= G_ELI_FLAG_GELIDISPLAYPASS; 743e4b0a90eSBrooks Davis if (gctl_get_int(req, "notrim")) 744e4b0a90eSBrooks Davis md.md_flags |= G_ELI_FLAG_NODELETE; 745*2f07cdf8SPawel Jakub Dawidek if (gctl_get_int(req, "noautoresize")) 746*2f07cdf8SPawel Jakub Dawidek md.md_flags &= ~G_ELI_FLAG_AUTORESIZE; 747e4b0a90eSBrooks Davis md.md_ealgo = CRYPTO_ALGORITHM_MIN - 1; 748e4b0a90eSBrooks Davis str = gctl_get_ascii(req, "aalgo"); 749e4b0a90eSBrooks Davis if (*str != '\0') { 750e4b0a90eSBrooks Davis if (version < G_ELI_VERSION_01) { 751e4b0a90eSBrooks Davis gctl_error(req, 752e4b0a90eSBrooks Davis "Data authentication is supported starting from version %u.", 753e4b0a90eSBrooks Davis G_ELI_VERSION_01); 754e4b0a90eSBrooks Davis return; 755e4b0a90eSBrooks Davis } 756e4b0a90eSBrooks Davis md.md_aalgo = g_eli_str2aalgo(str); 757e4b0a90eSBrooks Davis if (md.md_aalgo >= CRYPTO_ALGORITHM_MIN && 758e4b0a90eSBrooks Davis md.md_aalgo <= CRYPTO_ALGORITHM_MAX) { 759e4b0a90eSBrooks Davis md.md_flags |= G_ELI_FLAG_AUTH; 760e4b0a90eSBrooks Davis } else { 761e4b0a90eSBrooks Davis /* 762e4b0a90eSBrooks Davis * For backward compatibility, check if the -a option 763e4b0a90eSBrooks Davis * was used to provide encryption algorithm. 764e4b0a90eSBrooks Davis */ 765e4b0a90eSBrooks Davis md.md_ealgo = g_eli_str2ealgo(str); 766e4b0a90eSBrooks Davis if (md.md_ealgo < CRYPTO_ALGORITHM_MIN || 767e4b0a90eSBrooks Davis md.md_ealgo > CRYPTO_ALGORITHM_MAX) { 768e4b0a90eSBrooks Davis gctl_error(req, 769e4b0a90eSBrooks Davis "Invalid authentication algorithm."); 770e4b0a90eSBrooks Davis return; 771e4b0a90eSBrooks Davis } else { 772e4b0a90eSBrooks Davis fprintf(stderr, "warning: The -e option, not " 773e4b0a90eSBrooks Davis "the -a option is now used to specify " 774e4b0a90eSBrooks Davis "encryption algorithm to use.\n"); 775e4b0a90eSBrooks Davis } 776e4b0a90eSBrooks Davis } 777e4b0a90eSBrooks Davis } 778e4b0a90eSBrooks Davis if (md.md_ealgo < CRYPTO_ALGORITHM_MIN || 779e4b0a90eSBrooks Davis md.md_ealgo > CRYPTO_ALGORITHM_MAX) { 780e4b0a90eSBrooks Davis str = gctl_get_ascii(req, "ealgo"); 781e4b0a90eSBrooks Davis if (*str == '\0') { 782e4b0a90eSBrooks Davis if (version < G_ELI_VERSION_05) 783e4b0a90eSBrooks Davis str = "aes-cbc"; 784e4b0a90eSBrooks Davis else 785e4b0a90eSBrooks Davis str = GELI_ENC_ALGO; 786e4b0a90eSBrooks Davis } 787e4b0a90eSBrooks Davis md.md_ealgo = g_eli_str2ealgo(str); 788e4b0a90eSBrooks Davis if (md.md_ealgo < CRYPTO_ALGORITHM_MIN || 789e4b0a90eSBrooks Davis md.md_ealgo > CRYPTO_ALGORITHM_MAX) { 790e4b0a90eSBrooks Davis gctl_error(req, "Invalid encryption algorithm."); 791e4b0a90eSBrooks Davis return; 792e4b0a90eSBrooks Davis } 793e4b0a90eSBrooks Davis if (md.md_ealgo == CRYPTO_CAMELLIA_CBC && 794e4b0a90eSBrooks Davis version < G_ELI_VERSION_04) { 795e4b0a90eSBrooks Davis gctl_error(req, 796e4b0a90eSBrooks Davis "Camellia-CBC algorithm is supported starting from version %u.", 797e4b0a90eSBrooks Davis G_ELI_VERSION_04); 798e4b0a90eSBrooks Davis return; 799e4b0a90eSBrooks Davis } 800e4b0a90eSBrooks Davis if (md.md_ealgo == CRYPTO_AES_XTS && 801e4b0a90eSBrooks Davis version < G_ELI_VERSION_05) { 802e4b0a90eSBrooks Davis gctl_error(req, 803e4b0a90eSBrooks Davis "AES-XTS algorithm is supported starting from version %u.", 804e4b0a90eSBrooks Davis G_ELI_VERSION_05); 805e4b0a90eSBrooks Davis return; 806e4b0a90eSBrooks Davis } 807e4b0a90eSBrooks Davis } 808e4b0a90eSBrooks Davis val = gctl_get_intmax(req, "keylen"); 809e4b0a90eSBrooks Davis md.md_keylen = val; 810e4b0a90eSBrooks Davis md.md_keylen = g_eli_keylen(md.md_ealgo, md.md_keylen); 811e4b0a90eSBrooks Davis if (md.md_keylen == 0) { 812e4b0a90eSBrooks Davis gctl_error(req, "Invalid key length."); 813e4b0a90eSBrooks Davis return; 814e4b0a90eSBrooks Davis } 815e4b0a90eSBrooks Davis 816e4b0a90eSBrooks Davis val = gctl_get_intmax(req, "iterations"); 817e4b0a90eSBrooks Davis if (val != -1) { 818e4b0a90eSBrooks Davis int nonewpassphrase; 819e4b0a90eSBrooks Davis 820e4b0a90eSBrooks Davis /* 821e4b0a90eSBrooks Davis * Don't allow to set iterations when there will be no 822e4b0a90eSBrooks Davis * passphrase. 823e4b0a90eSBrooks Davis */ 824e4b0a90eSBrooks Davis nonewpassphrase = gctl_get_int(req, "nonewpassphrase"); 825e4b0a90eSBrooks Davis if (nonewpassphrase) { 826e4b0a90eSBrooks Davis gctl_error(req, 827e4b0a90eSBrooks Davis "Options -i and -P are mutually exclusive."); 828e4b0a90eSBrooks Davis return; 829e4b0a90eSBrooks Davis } 830e4b0a90eSBrooks Davis } 831e4b0a90eSBrooks Davis md.md_iterations = val; 832e4b0a90eSBrooks Davis 833e4b0a90eSBrooks Davis val = gctl_get_intmax(req, "sectorsize"); 834e4b0a90eSBrooks Davis if (val > sysconf(_SC_PAGE_SIZE)) { 835e4b0a90eSBrooks Davis fprintf(stderr, 836e4b0a90eSBrooks Davis "warning: Using sectorsize bigger than the page size!\n"); 837e4b0a90eSBrooks Davis } 838368455ecSBen Woods 839368455ecSBen Woods md.md_keys = 0x01; 840368455ecSBen Woods 841368455ecSBen Woods /* 842368455ecSBen Woods * Determine number of parameters in the parent geom request before the 843368455ecSBen Woods * nargs parameter and list of providers. 844368455ecSBen Woods */ 845368455ecSBen Woods nparams = req->narg - nargs - 1; 846368455ecSBen Woods 847368455ecSBen Woods /* Create new child request for each provider and issue to kernel */ 848368455ecSBen Woods for (i = 0; i < nargs; i++) { 849368455ecSBen Woods r = gctl_get_handle(); 850368455ecSBen Woods 851368455ecSBen Woods /* Copy each parameter from the parent request to the child */ 852368455ecSBen Woods for (param = 0; param < nparams; param++) { 853368455ecSBen Woods gctl_ro_param(r, req->arg[param].name, 854368455ecSBen Woods req->arg[param].len, req->arg[param].value); 855368455ecSBen Woods } 856368455ecSBen Woods 857368455ecSBen Woods /* Add a single provider to the parameter list of the child */ 858368455ecSBen Woods gctl_ro_param(r, "nargs", sizeof(one), &one); 859368455ecSBen Woods prov = gctl_get_ascii(req, "arg%d", i); 860368455ecSBen Woods gctl_ro_param(r, "arg0", -1, prov); 861368455ecSBen Woods 862368455ecSBen Woods mediasize = g_get_mediasize(prov); 863368455ecSBen Woods secsize = g_get_sectorsize(prov); 864368455ecSBen Woods if (mediasize == 0 || secsize == 0) { 865368455ecSBen Woods gctl_error(r, "Cannot get information about %s: %s.", 866368455ecSBen Woods prov, strerror(errno)); 867368455ecSBen Woods goto out; 868368455ecSBen Woods } 869368455ecSBen Woods 870368455ecSBen Woods md.md_provsize = mediasize; 871368455ecSBen Woods 872368455ecSBen Woods val = gctl_get_intmax(r, "sectorsize"); 873368455ecSBen Woods if (val == 0) { 874368455ecSBen Woods md.md_sectorsize = secsize; 875368455ecSBen Woods } else { 876368455ecSBen Woods if (val < 0 || (val % secsize) != 0 || !powerof2(val)) { 877368455ecSBen Woods gctl_error(r, "Invalid sector size."); 878368455ecSBen Woods goto out; 879368455ecSBen Woods } 880e4b0a90eSBrooks Davis md.md_sectorsize = val; 881e4b0a90eSBrooks Davis } 882e4b0a90eSBrooks Davis 883368455ecSBen Woods /* Use different salt and Master Key for each provider. */ 884e4b0a90eSBrooks Davis arc4random_buf(md.md_salt, sizeof(md.md_salt)); 885e4b0a90eSBrooks Davis arc4random_buf(md.md_mkeys, sizeof(md.md_mkeys)); 886e4b0a90eSBrooks Davis 887e4b0a90eSBrooks Davis /* Generate user key. */ 888368455ecSBen Woods if (eli_genkey(r, &md, key, true) == NULL) { 889368455ecSBen Woods /* 890368455ecSBen Woods * Error generating key - details added to geom request 891368455ecSBen Woods * by eli_genkey(). 892368455ecSBen Woods */ 893368455ecSBen Woods goto out; 894e4b0a90eSBrooks Davis } 895e4b0a90eSBrooks Davis 896e4b0a90eSBrooks Davis /* Encrypt the first and the only Master Key. */ 897368455ecSBen Woods error = g_eli_mkey_encrypt(md.md_ealgo, key, md.md_keylen, 898368455ecSBen Woods md.md_mkeys); 899e4b0a90eSBrooks Davis if (error != 0) { 900368455ecSBen Woods gctl_error(r, "Cannot encrypt Master Key: %s.", 901e4b0a90eSBrooks Davis strerror(error)); 902368455ecSBen Woods goto out; 903e4b0a90eSBrooks Davis } 904e4b0a90eSBrooks Davis 905e45068e3SBen Woods /* Convert metadata to on-disk format. */ 906e4b0a90eSBrooks Davis eli_metadata_encode(&md, sector); 907368455ecSBen Woods 908e45068e3SBen Woods /* Store metadata to disk. */ 909e4b0a90eSBrooks Davis error = g_metadata_store(prov, sector, sizeof(sector)); 910e4b0a90eSBrooks Davis if (error != 0) { 911368455ecSBen Woods gctl_error(r, "Cannot store metadata on %s: %s.", prov, 912e4b0a90eSBrooks Davis strerror(error)); 913368455ecSBen Woods goto out; 914e4b0a90eSBrooks Davis } 915e4b0a90eSBrooks Davis if (verbose) 916e4b0a90eSBrooks Davis printf("Metadata value stored on %s.\n", prov); 917368455ecSBen Woods 918e4b0a90eSBrooks Davis /* Backup metadata to a file. */ 919368455ecSBen Woods const char *p = prov; 920368455ecSBen Woods unsigned int j; 921368455ecSBen Woods 922368455ecSBen Woods /* 923368455ecSBen Woods * Check if provider string includes the devfs mountpoint 924368455ecSBen Woods * (typically /dev/). 925368455ecSBen Woods */ 926368455ecSBen Woods if (strncmp(p, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) { 927368455ecSBen Woods /* Skip forward to the device filename only. */ 928368455ecSBen Woods p += sizeof(_PATH_DEV) - 1; 929368455ecSBen Woods } 930368455ecSBen Woods 931368455ecSBen Woods str = gctl_get_ascii(r, "backupfile"); 932e4b0a90eSBrooks Davis if (str[0] != '\0') { 933368455ecSBen Woods /* Backupfile given by the user, just copy it. */ 934e4b0a90eSBrooks Davis strlcpy(backfile, str, sizeof(backfile)); 935368455ecSBen Woods 936e45068e3SBen Woods /* If multiple providers have been initialized in one 937e45068e3SBen Woods * command, and the backup filename has been specified 938e45068e3SBen Woods * as anything other than "none", make the backup 939e45068e3SBen Woods * filename unique for each provider. */ 940e45068e3SBen Woods if (nargs > 1 && strcmp(backfile, "none") != 0) { 941368455ecSBen Woods /* 942368455ecSBen Woods * Replace first occurrence of "PROV" with 943368455ecSBen Woods * provider name. 944368455ecSBen Woods */ 945368455ecSBen Woods str = strnstr(backfile, "PROV", 946368455ecSBen Woods sizeof(backfile)); 947368455ecSBen Woods if (str != NULL) { 948368455ecSBen Woods char suffix[MAXPATHLEN]; 949368455ecSBen Woods j = str - backfile; 950368455ecSBen Woods strlcpy(suffix, &backfile[j+4], 951368455ecSBen Woods sizeof(suffix)); 952368455ecSBen Woods backfile[j] = '\0'; 953368455ecSBen Woods strlcat(backfile, p, sizeof(backfile)); 954368455ecSBen Woods strlcat(backfile, suffix, 955368455ecSBen Woods sizeof(backfile)); 956368455ecSBen Woods } else { 957368455ecSBen Woods /* 958368455ecSBen Woods * "PROV" not found in backfile, append 959368455ecSBen Woods * provider name. 960368455ecSBen Woods */ 961368455ecSBen Woods strlcat(backfile, "-", 962368455ecSBen Woods sizeof(backfile)); 963368455ecSBen Woods strlcat(backfile, p, sizeof(backfile)); 964368455ecSBen Woods } 965368455ecSBen Woods } 966e4b0a90eSBrooks Davis } else { 967e4b0a90eSBrooks Davis /* Generate filename automatically. */ 968e4b0a90eSBrooks Davis snprintf(backfile, sizeof(backfile), "%s%s.eli", 969e4b0a90eSBrooks Davis GELI_BACKUP_DIR, p); 970e4b0a90eSBrooks Davis /* Replace all / with _. */ 971368455ecSBen Woods for (j = strlen(GELI_BACKUP_DIR); backfile[j] != '\0'; 972368455ecSBen Woods j++) { 973368455ecSBen Woods if (backfile[j] == '/') 974368455ecSBen Woods backfile[j] = '_'; 975e4b0a90eSBrooks Davis } 976e4b0a90eSBrooks Davis } 977e4b0a90eSBrooks Davis if (strcmp(backfile, "none") != 0 && 978368455ecSBen Woods eli_backup_create(r, prov, backfile) == 0) { 979368455ecSBen Woods printf("\nMetadata backup for provider %s can be found " 980368455ecSBen Woods "in %s\n", prov, backfile); 981368455ecSBen Woods printf("and can be restored with the following " 982368455ecSBen Woods "command:\n"); 983e4b0a90eSBrooks Davis printf("\n\t# geli restore %s %s\n\n", backfile, prov); 984e4b0a90eSBrooks Davis } 985368455ecSBen Woods 986368455ecSBen Woods out: 987368455ecSBen Woods /* 988368455ecSBen Woods * Print error for this request, and set parent request error 989368455ecSBen Woods * message. 990368455ecSBen Woods */ 991368455ecSBen Woods if (r->error != NULL && r->error[0] != '\0') { 992368455ecSBen Woods warnx("%s", r->error); 993368455ecSBen Woods gctl_error(req, "There was an error with at least one " 994368455ecSBen Woods "provider."); 995368455ecSBen Woods } 996368455ecSBen Woods 997368455ecSBen Woods gctl_free(r); 998368455ecSBen Woods 999368455ecSBen Woods /* 1000e45068e3SBen Woods * Erase sensitive and provider specific data from memory. 1001368455ecSBen Woods */ 1002368455ecSBen Woods explicit_bzero(key, sizeof(key)); 1003e45068e3SBen Woods explicit_bzero(sector, sizeof(sector)); 1004e45068e3SBen Woods explicit_bzero(&md.md_provsize, sizeof(md.md_provsize)); 1005e45068e3SBen Woods explicit_bzero(&md.md_sectorsize, sizeof(md.md_sectorsize)); 1006e45068e3SBen Woods explicit_bzero(&md.md_salt, sizeof(md.md_salt)); 1007e45068e3SBen Woods explicit_bzero(&md.md_mkeys, sizeof(md.md_mkeys)); 1008368455ecSBen Woods } 1009368455ecSBen Woods 1010368455ecSBen Woods /* Clear the cached metadata, including keys. */ 1011368455ecSBen Woods explicit_bzero(&md, sizeof(md)); 1012e4b0a90eSBrooks Davis } 1013e4b0a90eSBrooks Davis 1014e4b0a90eSBrooks Davis static void 1015e4b0a90eSBrooks Davis eli_attach(struct gctl_req *req) 1016e4b0a90eSBrooks Davis { 1017e4b0a90eSBrooks Davis struct g_eli_metadata md; 1018217df2daSBen Woods struct gctl_req *r; 1019e4b0a90eSBrooks Davis const char *prov; 1020e4b0a90eSBrooks Davis off_t mediasize; 1021217df2daSBen Woods int i, nargs, nparams, param; 1022217df2daSBen Woods const int one = 1; 1023e4b0a90eSBrooks Davis 1024e4b0a90eSBrooks Davis nargs = gctl_get_int(req, "nargs"); 1025368455ecSBen Woods if (nargs <= 0) { 1026217df2daSBen Woods gctl_error(req, "Too few arguments."); 1027e4b0a90eSBrooks Davis return; 1028e4b0a90eSBrooks Davis } 1029e4b0a90eSBrooks Davis 1030217df2daSBen Woods unsigned char key[G_ELI_USERKEYLEN]; 1031217df2daSBen Woods 1032217df2daSBen Woods /* 1033217df2daSBen Woods * Determine number of parameters in the parent geom request before the 1034217df2daSBen Woods * nargs parameter and list of providers. 1035217df2daSBen Woods */ 1036217df2daSBen Woods nparams = req->narg - nargs - 1; 1037217df2daSBen Woods 1038368455ecSBen Woods /* Create new child request for each provider and issue to kernel */ 1039217df2daSBen Woods for (i = 0; i < nargs; i++) { 1040217df2daSBen Woods r = gctl_get_handle(); 1041217df2daSBen Woods 1042368455ecSBen Woods /* Copy each parameter from the parent request to the child */ 1043217df2daSBen Woods for (param = 0; param < nparams; param++) { 1044368455ecSBen Woods gctl_ro_param(r, req->arg[param].name, 1045368455ecSBen Woods req->arg[param].len, req->arg[param].value); 1046217df2daSBen Woods } 1047217df2daSBen Woods 1048217df2daSBen Woods /* Add a single provider to the parameter list of the child */ 1049217df2daSBen Woods gctl_ro_param(r, "nargs", sizeof(one), &one); 1050217df2daSBen Woods prov = gctl_get_ascii(req, "arg%d", i); 1051217df2daSBen Woods gctl_ro_param(r, "arg0", -1, prov); 1052217df2daSBen Woods 10534b8e4d53SBen Woods if (eli_metadata_read(r, prov, &md) == -1) { 10544b8e4d53SBen Woods /* 10554b8e4d53SBen Woods * Error reading metadata - details added to geom 10564b8e4d53SBen Woods * request by eli_metadata_read(). 10574b8e4d53SBen Woods */ 10584b8e4d53SBen Woods goto out; 10594b8e4d53SBen Woods } 1060e4b0a90eSBrooks Davis 1061e4b0a90eSBrooks Davis mediasize = g_get_mediasize(prov); 1062e4b0a90eSBrooks Davis if (md.md_provsize != (uint64_t)mediasize) { 1063217df2daSBen Woods gctl_error(r, "Provider size mismatch."); 10644b8e4d53SBen Woods goto out; 1065e4b0a90eSBrooks Davis } 1066e4b0a90eSBrooks Davis 1067217df2daSBen Woods if (eli_genkey(r, &md, key, false) == NULL) { 10684b8e4d53SBen Woods /* 10694b8e4d53SBen Woods * Error generating key - details added to geom request 10704b8e4d53SBen Woods * by eli_genkey(). 10714b8e4d53SBen Woods */ 10724b8e4d53SBen Woods goto out; 1073e4b0a90eSBrooks Davis } 1074e4b0a90eSBrooks Davis 1075217df2daSBen Woods gctl_ro_param(r, "key", sizeof(key), key); 1076217df2daSBen Woods 1077217df2daSBen Woods if (gctl_issue(r) == NULL) { 1078e4b0a90eSBrooks Davis if (verbose) 1079e4b0a90eSBrooks Davis printf("Attached to %s.\n", prov); 1080e4b0a90eSBrooks Davis } 1081217df2daSBen Woods 10824b8e4d53SBen Woods out: 1083368455ecSBen Woods /* 1084368455ecSBen Woods * Print error for this request, and set parent request error 1085368455ecSBen Woods * message. 1086368455ecSBen Woods */ 1087217df2daSBen Woods if (r->error != NULL && r->error[0] != '\0') { 1088217df2daSBen Woods warnx("%s", r->error); 1089368455ecSBen Woods gctl_error(req, "There was an error with at least one " 1090368455ecSBen Woods "provider."); 1091217df2daSBen Woods } 1092217df2daSBen Woods 1093217df2daSBen Woods gctl_free(r); 1094217df2daSBen Woods 10954b8e4d53SBen Woods /* Clear sensitive data from memory. */ 1096217df2daSBen Woods explicit_bzero(key, sizeof(key)); 1097217df2daSBen Woods } 1098217df2daSBen Woods 10994b8e4d53SBen Woods /* Clear sensitive data from memory. */ 1100217df2daSBen Woods explicit_bzero(cached_passphrase, sizeof(cached_passphrase)); 1101e4b0a90eSBrooks Davis } 1102e4b0a90eSBrooks Davis 1103e4b0a90eSBrooks Davis static void 1104e4b0a90eSBrooks Davis eli_configure_detached(struct gctl_req *req, const char *prov, int boot, 1105*2f07cdf8SPawel Jakub Dawidek int geliboot, int displaypass, int trim, int autoresize) 1106e4b0a90eSBrooks Davis { 1107e4b0a90eSBrooks Davis struct g_eli_metadata md; 1108e4b0a90eSBrooks Davis bool changed = 0; 1109e4b0a90eSBrooks Davis 1110e4b0a90eSBrooks Davis if (eli_metadata_read(req, prov, &md) == -1) 1111e4b0a90eSBrooks Davis return; 1112e4b0a90eSBrooks Davis 1113e4b0a90eSBrooks Davis if (boot == 1 && (md.md_flags & G_ELI_FLAG_BOOT)) { 1114e4b0a90eSBrooks Davis if (verbose) 1115e4b0a90eSBrooks Davis printf("BOOT flag already configured for %s.\n", prov); 1116e4b0a90eSBrooks Davis } else if (boot == 0 && !(md.md_flags & G_ELI_FLAG_BOOT)) { 1117e4b0a90eSBrooks Davis if (verbose) 1118e4b0a90eSBrooks Davis printf("BOOT flag not configured for %s.\n", prov); 1119e4b0a90eSBrooks Davis } else if (boot >= 0) { 1120e4b0a90eSBrooks Davis if (boot) 1121e4b0a90eSBrooks Davis md.md_flags |= G_ELI_FLAG_BOOT; 1122e4b0a90eSBrooks Davis else 1123e4b0a90eSBrooks Davis md.md_flags &= ~G_ELI_FLAG_BOOT; 1124e4b0a90eSBrooks Davis changed = 1; 1125e4b0a90eSBrooks Davis } 1126e4b0a90eSBrooks Davis 1127e4b0a90eSBrooks Davis if (geliboot == 1 && (md.md_flags & G_ELI_FLAG_GELIBOOT)) { 1128e4b0a90eSBrooks Davis if (verbose) 1129e4b0a90eSBrooks Davis printf("GELIBOOT flag already configured for %s.\n", prov); 1130e4b0a90eSBrooks Davis } else if (geliboot == 0 && !(md.md_flags & G_ELI_FLAG_GELIBOOT)) { 1131e4b0a90eSBrooks Davis if (verbose) 1132e4b0a90eSBrooks Davis printf("GELIBOOT flag not configured for %s.\n", prov); 1133e4b0a90eSBrooks Davis } else if (geliboot >= 0) { 1134e4b0a90eSBrooks Davis if (geliboot) 1135e4b0a90eSBrooks Davis md.md_flags |= G_ELI_FLAG_GELIBOOT; 1136e4b0a90eSBrooks Davis else 1137e4b0a90eSBrooks Davis md.md_flags &= ~G_ELI_FLAG_GELIBOOT; 1138e4b0a90eSBrooks Davis changed = 1; 1139e4b0a90eSBrooks Davis } 1140e4b0a90eSBrooks Davis 1141e4b0a90eSBrooks Davis if (displaypass == 1 && (md.md_flags & G_ELI_FLAG_GELIDISPLAYPASS)) { 1142e4b0a90eSBrooks Davis if (verbose) 1143e4b0a90eSBrooks Davis printf("GELIDISPLAYPASS flag already configured for %s.\n", prov); 1144e4b0a90eSBrooks Davis } else if (displaypass == 0 && 1145e4b0a90eSBrooks Davis !(md.md_flags & G_ELI_FLAG_GELIDISPLAYPASS)) { 1146e4b0a90eSBrooks Davis if (verbose) 1147e4b0a90eSBrooks Davis printf("GELIDISPLAYPASS flag not configured for %s.\n", prov); 1148e4b0a90eSBrooks Davis } else if (displaypass >= 0) { 1149e4b0a90eSBrooks Davis if (displaypass) 1150e4b0a90eSBrooks Davis md.md_flags |= G_ELI_FLAG_GELIDISPLAYPASS; 1151e4b0a90eSBrooks Davis else 1152e4b0a90eSBrooks Davis md.md_flags &= ~G_ELI_FLAG_GELIDISPLAYPASS; 1153e4b0a90eSBrooks Davis changed = 1; 1154e4b0a90eSBrooks Davis } 1155e4b0a90eSBrooks Davis 1156e4b0a90eSBrooks Davis if (trim == 0 && (md.md_flags & G_ELI_FLAG_NODELETE)) { 1157e4b0a90eSBrooks Davis if (verbose) 1158e4b0a90eSBrooks Davis printf("TRIM disable flag already configured for %s.\n", prov); 1159e4b0a90eSBrooks Davis } else if (trim == 1 && !(md.md_flags & G_ELI_FLAG_NODELETE)) { 1160e4b0a90eSBrooks Davis if (verbose) 1161e4b0a90eSBrooks Davis printf("TRIM disable flag not configured for %s.\n", prov); 1162e4b0a90eSBrooks Davis } else if (trim >= 0) { 1163e4b0a90eSBrooks Davis if (trim) 1164e4b0a90eSBrooks Davis md.md_flags &= ~G_ELI_FLAG_NODELETE; 1165e4b0a90eSBrooks Davis else 1166e4b0a90eSBrooks Davis md.md_flags |= G_ELI_FLAG_NODELETE; 1167e4b0a90eSBrooks Davis changed = 1; 1168e4b0a90eSBrooks Davis } 1169e4b0a90eSBrooks Davis 1170*2f07cdf8SPawel Jakub Dawidek if (autoresize == 1 && (md.md_flags & G_ELI_FLAG_AUTORESIZE)) { 1171*2f07cdf8SPawel Jakub Dawidek if (verbose) 1172*2f07cdf8SPawel Jakub Dawidek printf("AUTORESIZE flag already configured for %s.\n", prov); 1173*2f07cdf8SPawel Jakub Dawidek } else if (autoresize == 0 && !(md.md_flags & G_ELI_FLAG_AUTORESIZE)) { 1174*2f07cdf8SPawel Jakub Dawidek if (verbose) 1175*2f07cdf8SPawel Jakub Dawidek printf("AUTORESIZE flag not configured for %s.\n", prov); 1176*2f07cdf8SPawel Jakub Dawidek } else if (autoresize >= 0) { 1177*2f07cdf8SPawel Jakub Dawidek if (autoresize) 1178*2f07cdf8SPawel Jakub Dawidek md.md_flags |= G_ELI_FLAG_AUTORESIZE; 1179*2f07cdf8SPawel Jakub Dawidek else 1180*2f07cdf8SPawel Jakub Dawidek md.md_flags &= ~G_ELI_FLAG_AUTORESIZE; 1181*2f07cdf8SPawel Jakub Dawidek changed = 1; 1182*2f07cdf8SPawel Jakub Dawidek } 1183*2f07cdf8SPawel Jakub Dawidek 1184e4b0a90eSBrooks Davis if (changed) 1185e4b0a90eSBrooks Davis eli_metadata_store(req, prov, &md); 1186217df2daSBen Woods explicit_bzero(&md, sizeof(md)); 1187e4b0a90eSBrooks Davis } 1188e4b0a90eSBrooks Davis 1189e4b0a90eSBrooks Davis static void 1190e4b0a90eSBrooks Davis eli_configure(struct gctl_req *req) 1191e4b0a90eSBrooks Davis { 1192e4b0a90eSBrooks Davis const char *prov; 1193e4b0a90eSBrooks Davis bool boot, noboot, geliboot, nogeliboot, displaypass, nodisplaypass; 1194*2f07cdf8SPawel Jakub Dawidek bool autoresize, noautoresize, trim, notrim; 1195*2f07cdf8SPawel Jakub Dawidek int doboot, dogeliboot, dodisplaypass, dotrim, doautoresize; 1196e4b0a90eSBrooks Davis int i, nargs; 1197e4b0a90eSBrooks Davis 1198e4b0a90eSBrooks Davis nargs = gctl_get_int(req, "nargs"); 1199e4b0a90eSBrooks Davis if (nargs == 0) { 1200e4b0a90eSBrooks Davis gctl_error(req, "Too few arguments."); 1201e4b0a90eSBrooks Davis return; 1202e4b0a90eSBrooks Davis } 1203e4b0a90eSBrooks Davis 1204e4b0a90eSBrooks Davis boot = gctl_get_int(req, "boot"); 1205e4b0a90eSBrooks Davis noboot = gctl_get_int(req, "noboot"); 1206e4b0a90eSBrooks Davis geliboot = gctl_get_int(req, "geliboot"); 1207e4b0a90eSBrooks Davis nogeliboot = gctl_get_int(req, "nogeliboot"); 1208e4b0a90eSBrooks Davis displaypass = gctl_get_int(req, "displaypass"); 1209e4b0a90eSBrooks Davis nodisplaypass = gctl_get_int(req, "nodisplaypass"); 1210e4b0a90eSBrooks Davis trim = gctl_get_int(req, "trim"); 1211e4b0a90eSBrooks Davis notrim = gctl_get_int(req, "notrim"); 1212*2f07cdf8SPawel Jakub Dawidek autoresize = gctl_get_int(req, "autoresize"); 1213*2f07cdf8SPawel Jakub Dawidek noautoresize = gctl_get_int(req, "noautoresize"); 1214e4b0a90eSBrooks Davis 1215e4b0a90eSBrooks Davis doboot = -1; 1216e4b0a90eSBrooks Davis if (boot && noboot) { 1217e4b0a90eSBrooks Davis gctl_error(req, "Options -b and -B are mutually exclusive."); 1218e4b0a90eSBrooks Davis return; 1219e4b0a90eSBrooks Davis } 1220e4b0a90eSBrooks Davis if (boot) 1221e4b0a90eSBrooks Davis doboot = 1; 1222e4b0a90eSBrooks Davis else if (noboot) 1223e4b0a90eSBrooks Davis doboot = 0; 1224e4b0a90eSBrooks Davis 1225e4b0a90eSBrooks Davis dogeliboot = -1; 1226e4b0a90eSBrooks Davis if (geliboot && nogeliboot) { 1227e4b0a90eSBrooks Davis gctl_error(req, "Options -g and -G are mutually exclusive."); 1228e4b0a90eSBrooks Davis return; 1229e4b0a90eSBrooks Davis } 1230e4b0a90eSBrooks Davis if (geliboot) 1231e4b0a90eSBrooks Davis dogeliboot = 1; 1232e4b0a90eSBrooks Davis else if (nogeliboot) 1233e4b0a90eSBrooks Davis dogeliboot = 0; 1234e4b0a90eSBrooks Davis 1235e4b0a90eSBrooks Davis dodisplaypass = -1; 1236e4b0a90eSBrooks Davis if (displaypass && nodisplaypass) { 1237e4b0a90eSBrooks Davis gctl_error(req, "Options -d and -D are mutually exclusive."); 1238e4b0a90eSBrooks Davis return; 1239e4b0a90eSBrooks Davis } 1240e4b0a90eSBrooks Davis if (displaypass) 1241e4b0a90eSBrooks Davis dodisplaypass = 1; 1242e4b0a90eSBrooks Davis else if (nodisplaypass) 1243e4b0a90eSBrooks Davis dodisplaypass = 0; 1244e4b0a90eSBrooks Davis 1245e4b0a90eSBrooks Davis dotrim = -1; 1246e4b0a90eSBrooks Davis if (trim && notrim) { 1247e4b0a90eSBrooks Davis gctl_error(req, "Options -t and -T are mutually exclusive."); 1248e4b0a90eSBrooks Davis return; 1249e4b0a90eSBrooks Davis } 1250e4b0a90eSBrooks Davis if (trim) 1251e4b0a90eSBrooks Davis dotrim = 1; 1252e4b0a90eSBrooks Davis else if (notrim) 1253e4b0a90eSBrooks Davis dotrim = 0; 1254e4b0a90eSBrooks Davis 1255*2f07cdf8SPawel Jakub Dawidek doautoresize = -1; 1256*2f07cdf8SPawel Jakub Dawidek if (autoresize && noautoresize) { 1257*2f07cdf8SPawel Jakub Dawidek gctl_error(req, "Options -r and -R are mutually exclusive."); 1258*2f07cdf8SPawel Jakub Dawidek return; 1259*2f07cdf8SPawel Jakub Dawidek } 1260*2f07cdf8SPawel Jakub Dawidek if (autoresize) 1261*2f07cdf8SPawel Jakub Dawidek doautoresize = 1; 1262*2f07cdf8SPawel Jakub Dawidek else if (noautoresize) 1263*2f07cdf8SPawel Jakub Dawidek doautoresize = 0; 1264*2f07cdf8SPawel Jakub Dawidek 1265e4b0a90eSBrooks Davis if (doboot == -1 && dogeliboot == -1 && dodisplaypass == -1 && 1266*2f07cdf8SPawel Jakub Dawidek dotrim == -1 && doautoresize == -1) { 1267e4b0a90eSBrooks Davis gctl_error(req, "No option given."); 1268e4b0a90eSBrooks Davis return; 1269e4b0a90eSBrooks Davis } 1270e4b0a90eSBrooks Davis 1271e4b0a90eSBrooks Davis /* First attached providers. */ 1272e4b0a90eSBrooks Davis gctl_issue(req); 1273e4b0a90eSBrooks Davis /* Now the rest. */ 1274e4b0a90eSBrooks Davis for (i = 0; i < nargs; i++) { 1275e4b0a90eSBrooks Davis prov = gctl_get_ascii(req, "arg%d", i); 1276e4b0a90eSBrooks Davis if (!eli_is_attached(prov)) { 1277e4b0a90eSBrooks Davis eli_configure_detached(req, prov, doboot, dogeliboot, 1278*2f07cdf8SPawel Jakub Dawidek dodisplaypass, dotrim, doautoresize); 1279e4b0a90eSBrooks Davis } 1280e4b0a90eSBrooks Davis } 1281e4b0a90eSBrooks Davis } 1282e4b0a90eSBrooks Davis 1283e4b0a90eSBrooks Davis static void 1284e4b0a90eSBrooks Davis eli_setkey_attached(struct gctl_req *req, struct g_eli_metadata *md) 1285e4b0a90eSBrooks Davis { 1286e4b0a90eSBrooks Davis unsigned char key[G_ELI_USERKEYLEN]; 1287e4b0a90eSBrooks Davis intmax_t val, old = 0; 1288e4b0a90eSBrooks Davis int error; 1289e4b0a90eSBrooks Davis 1290e4b0a90eSBrooks Davis val = gctl_get_intmax(req, "iterations"); 1291e4b0a90eSBrooks Davis /* Check if iterations number should be changed. */ 1292e4b0a90eSBrooks Davis if (val != -1) 1293e4b0a90eSBrooks Davis md->md_iterations = val; 1294e4b0a90eSBrooks Davis else 1295e4b0a90eSBrooks Davis old = md->md_iterations; 1296e4b0a90eSBrooks Davis 1297e4b0a90eSBrooks Davis /* Generate key for Master Key encryption. */ 1298e4b0a90eSBrooks Davis if (eli_genkey(req, md, key, true) == NULL) { 1299217df2daSBen Woods explicit_bzero(key, sizeof(key)); 1300e4b0a90eSBrooks Davis return; 1301e4b0a90eSBrooks Davis } 1302e4b0a90eSBrooks Davis /* 1303e4b0a90eSBrooks Davis * If number of iterations has changed, but wasn't given as a 1304e4b0a90eSBrooks Davis * command-line argument, update the request. 1305e4b0a90eSBrooks Davis */ 1306e4b0a90eSBrooks Davis if (val == -1 && md->md_iterations != old) { 1307e4b0a90eSBrooks Davis error = gctl_change_param(req, "iterations", sizeof(intmax_t), 1308e4b0a90eSBrooks Davis &md->md_iterations); 1309e4b0a90eSBrooks Davis assert(error == 0); 1310e4b0a90eSBrooks Davis } 1311e4b0a90eSBrooks Davis 1312e4b0a90eSBrooks Davis gctl_ro_param(req, "key", sizeof(key), key); 1313e4b0a90eSBrooks Davis gctl_issue(req); 1314217df2daSBen Woods explicit_bzero(key, sizeof(key)); 1315e4b0a90eSBrooks Davis } 1316e4b0a90eSBrooks Davis 1317e4b0a90eSBrooks Davis static void 1318e4b0a90eSBrooks Davis eli_setkey_detached(struct gctl_req *req, const char *prov, 1319e4b0a90eSBrooks Davis struct g_eli_metadata *md) 1320e4b0a90eSBrooks Davis { 1321e4b0a90eSBrooks Davis unsigned char key[G_ELI_USERKEYLEN], mkey[G_ELI_DATAIVKEYLEN]; 1322e4b0a90eSBrooks Davis unsigned char *mkeydst; 1323e4b0a90eSBrooks Davis unsigned int nkey; 1324e4b0a90eSBrooks Davis intmax_t val; 1325e4b0a90eSBrooks Davis int error; 1326e4b0a90eSBrooks Davis 1327e4b0a90eSBrooks Davis if (md->md_keys == 0) { 1328e4b0a90eSBrooks Davis gctl_error(req, "No valid keys on %s.", prov); 1329e4b0a90eSBrooks Davis return; 1330e4b0a90eSBrooks Davis } 1331e4b0a90eSBrooks Davis 1332e4b0a90eSBrooks Davis /* Generate key for Master Key decryption. */ 1333e4b0a90eSBrooks Davis if (eli_genkey(req, md, key, false) == NULL) { 1334217df2daSBen Woods explicit_bzero(key, sizeof(key)); 1335e4b0a90eSBrooks Davis return; 1336e4b0a90eSBrooks Davis } 1337e4b0a90eSBrooks Davis 1338e4b0a90eSBrooks Davis /* Decrypt Master Key. */ 1339e4b0a90eSBrooks Davis error = g_eli_mkey_decrypt_any(md, key, mkey, &nkey); 1340217df2daSBen Woods explicit_bzero(key, sizeof(key)); 1341e4b0a90eSBrooks Davis if (error != 0) { 1342217df2daSBen Woods explicit_bzero(md, sizeof(*md)); 1343e4b0a90eSBrooks Davis if (error == -1) 1344e4b0a90eSBrooks Davis gctl_error(req, "Wrong key for %s.", prov); 1345e4b0a90eSBrooks Davis else /* if (error > 0) */ { 1346e4b0a90eSBrooks Davis gctl_error(req, "Cannot decrypt Master Key: %s.", 1347e4b0a90eSBrooks Davis strerror(error)); 1348e4b0a90eSBrooks Davis } 1349e4b0a90eSBrooks Davis return; 1350e4b0a90eSBrooks Davis } 1351e4b0a90eSBrooks Davis if (verbose) 1352e4b0a90eSBrooks Davis printf("Decrypted Master Key %u.\n", nkey); 1353e4b0a90eSBrooks Davis 1354e4b0a90eSBrooks Davis val = gctl_get_intmax(req, "keyno"); 1355e4b0a90eSBrooks Davis if (val != -1) 1356e4b0a90eSBrooks Davis nkey = val; 1357e4b0a90eSBrooks Davis #if 0 1358e4b0a90eSBrooks Davis else 1359e4b0a90eSBrooks Davis ; /* Use the key number which was found during decryption. */ 1360e4b0a90eSBrooks Davis #endif 1361e4b0a90eSBrooks Davis if (nkey >= G_ELI_MAXMKEYS) { 1362e4b0a90eSBrooks Davis gctl_error(req, "Invalid '%s' argument.", "keyno"); 1363e4b0a90eSBrooks Davis return; 1364e4b0a90eSBrooks Davis } 1365e4b0a90eSBrooks Davis 1366e4b0a90eSBrooks Davis val = gctl_get_intmax(req, "iterations"); 1367e4b0a90eSBrooks Davis /* Check if iterations number should and can be changed. */ 1368e4b0a90eSBrooks Davis if (val != -1 && md->md_iterations == -1) { 1369e4b0a90eSBrooks Davis md->md_iterations = val; 1370e4b0a90eSBrooks Davis } else if (val != -1 && val != md->md_iterations) { 1371e4b0a90eSBrooks Davis if (bitcount32(md->md_keys) != 1) { 1372e4b0a90eSBrooks Davis gctl_error(req, "To be able to use '-i' option, only " 1373e4b0a90eSBrooks Davis "one key can be defined."); 1374e4b0a90eSBrooks Davis return; 1375e4b0a90eSBrooks Davis } 1376e4b0a90eSBrooks Davis if (md->md_keys != (1 << nkey)) { 1377e4b0a90eSBrooks Davis gctl_error(req, "Only already defined key can be " 1378e4b0a90eSBrooks Davis "changed when '-i' option is used."); 1379e4b0a90eSBrooks Davis return; 1380e4b0a90eSBrooks Davis } 1381e4b0a90eSBrooks Davis md->md_iterations = val; 1382e4b0a90eSBrooks Davis } 1383e4b0a90eSBrooks Davis 1384e4b0a90eSBrooks Davis mkeydst = md->md_mkeys + nkey * G_ELI_MKEYLEN; 1385e4b0a90eSBrooks Davis md->md_keys |= (1 << nkey); 1386e4b0a90eSBrooks Davis 1387e4b0a90eSBrooks Davis bcopy(mkey, mkeydst, sizeof(mkey)); 1388217df2daSBen Woods explicit_bzero(mkey, sizeof(mkey)); 1389e4b0a90eSBrooks Davis 1390e4b0a90eSBrooks Davis /* Generate key for Master Key encryption. */ 1391e4b0a90eSBrooks Davis if (eli_genkey(req, md, key, true) == NULL) { 1392217df2daSBen Woods explicit_bzero(key, sizeof(key)); 1393217df2daSBen Woods explicit_bzero(md, sizeof(*md)); 1394e4b0a90eSBrooks Davis return; 1395e4b0a90eSBrooks Davis } 1396e4b0a90eSBrooks Davis 1397e4b0a90eSBrooks Davis /* Encrypt the Master-Key with the new key. */ 1398e4b0a90eSBrooks Davis error = g_eli_mkey_encrypt(md->md_ealgo, key, md->md_keylen, mkeydst); 1399217df2daSBen Woods explicit_bzero(key, sizeof(key)); 1400e4b0a90eSBrooks Davis if (error != 0) { 1401217df2daSBen Woods explicit_bzero(md, sizeof(*md)); 1402e4b0a90eSBrooks Davis gctl_error(req, "Cannot encrypt Master Key: %s.", 1403e4b0a90eSBrooks Davis strerror(error)); 1404e4b0a90eSBrooks Davis return; 1405e4b0a90eSBrooks Davis } 1406e4b0a90eSBrooks Davis 1407e4b0a90eSBrooks Davis /* Store metadata with fresh key. */ 1408e4b0a90eSBrooks Davis eli_metadata_store(req, prov, md); 1409217df2daSBen Woods explicit_bzero(md, sizeof(*md)); 1410e4b0a90eSBrooks Davis } 1411e4b0a90eSBrooks Davis 1412e4b0a90eSBrooks Davis static void 1413e4b0a90eSBrooks Davis eli_setkey(struct gctl_req *req) 1414e4b0a90eSBrooks Davis { 1415e4b0a90eSBrooks Davis struct g_eli_metadata md; 1416e4b0a90eSBrooks Davis const char *prov; 1417e4b0a90eSBrooks Davis int nargs; 1418e4b0a90eSBrooks Davis 1419e4b0a90eSBrooks Davis nargs = gctl_get_int(req, "nargs"); 1420e4b0a90eSBrooks Davis if (nargs != 1) { 1421e4b0a90eSBrooks Davis gctl_error(req, "Invalid number of arguments."); 1422e4b0a90eSBrooks Davis return; 1423e4b0a90eSBrooks Davis } 1424e4b0a90eSBrooks Davis prov = gctl_get_ascii(req, "arg0"); 1425e4b0a90eSBrooks Davis 1426e4b0a90eSBrooks Davis if (eli_metadata_read(req, prov, &md) == -1) 1427e4b0a90eSBrooks Davis return; 1428e4b0a90eSBrooks Davis 1429e4b0a90eSBrooks Davis if (eli_is_attached(prov)) 1430e4b0a90eSBrooks Davis eli_setkey_attached(req, &md); 1431e4b0a90eSBrooks Davis else 1432e4b0a90eSBrooks Davis eli_setkey_detached(req, prov, &md); 1433e4b0a90eSBrooks Davis 1434e4b0a90eSBrooks Davis if (req->error == NULL || req->error[0] == '\0') { 1435e4b0a90eSBrooks Davis printf("Note, that the master key encrypted with old keys " 1436e4b0a90eSBrooks Davis "and/or passphrase may still exists in a metadata backup " 1437e4b0a90eSBrooks Davis "file.\n"); 1438e4b0a90eSBrooks Davis } 1439e4b0a90eSBrooks Davis } 1440e4b0a90eSBrooks Davis 1441e4b0a90eSBrooks Davis static void 1442e4b0a90eSBrooks Davis eli_delkey_attached(struct gctl_req *req, const char *prov __unused) 1443e4b0a90eSBrooks Davis { 1444e4b0a90eSBrooks Davis 1445e4b0a90eSBrooks Davis gctl_issue(req); 1446e4b0a90eSBrooks Davis } 1447e4b0a90eSBrooks Davis 1448e4b0a90eSBrooks Davis static void 1449e4b0a90eSBrooks Davis eli_delkey_detached(struct gctl_req *req, const char *prov) 1450e4b0a90eSBrooks Davis { 1451e4b0a90eSBrooks Davis struct g_eli_metadata md; 1452e4b0a90eSBrooks Davis unsigned char *mkeydst; 1453e4b0a90eSBrooks Davis unsigned int nkey; 1454e4b0a90eSBrooks Davis intmax_t val; 1455e4b0a90eSBrooks Davis bool all, force; 1456e4b0a90eSBrooks Davis 1457e4b0a90eSBrooks Davis if (eli_metadata_read(req, prov, &md) == -1) 1458e4b0a90eSBrooks Davis return; 1459e4b0a90eSBrooks Davis 1460e4b0a90eSBrooks Davis all = gctl_get_int(req, "all"); 1461e4b0a90eSBrooks Davis if (all) 1462e4b0a90eSBrooks Davis arc4random_buf(md.md_mkeys, sizeof(md.md_mkeys)); 1463e4b0a90eSBrooks Davis else { 1464e4b0a90eSBrooks Davis force = gctl_get_int(req, "force"); 1465e4b0a90eSBrooks Davis val = gctl_get_intmax(req, "keyno"); 1466e4b0a90eSBrooks Davis if (val == -1) { 1467e4b0a90eSBrooks Davis gctl_error(req, "Key number has to be specified."); 1468e4b0a90eSBrooks Davis return; 1469e4b0a90eSBrooks Davis } 1470e4b0a90eSBrooks Davis nkey = val; 1471e4b0a90eSBrooks Davis if (nkey >= G_ELI_MAXMKEYS) { 1472e4b0a90eSBrooks Davis gctl_error(req, "Invalid '%s' argument.", "keyno"); 1473e4b0a90eSBrooks Davis return; 1474e4b0a90eSBrooks Davis } 1475e4b0a90eSBrooks Davis if (!(md.md_keys & (1 << nkey)) && !force) { 1476e4b0a90eSBrooks Davis gctl_error(req, "Master Key %u is not set.", nkey); 1477e4b0a90eSBrooks Davis return; 1478e4b0a90eSBrooks Davis } 1479e4b0a90eSBrooks Davis md.md_keys &= ~(1 << nkey); 1480e4b0a90eSBrooks Davis if (md.md_keys == 0 && !force) { 1481e4b0a90eSBrooks Davis gctl_error(req, "This is the last Master Key. Use '-f' " 1482e4b0a90eSBrooks Davis "option if you really want to remove it."); 1483e4b0a90eSBrooks Davis return; 1484e4b0a90eSBrooks Davis } 1485e4b0a90eSBrooks Davis mkeydst = md.md_mkeys + nkey * G_ELI_MKEYLEN; 1486e4b0a90eSBrooks Davis arc4random_buf(mkeydst, G_ELI_MKEYLEN); 1487e4b0a90eSBrooks Davis } 1488e4b0a90eSBrooks Davis 1489e4b0a90eSBrooks Davis eli_metadata_store(req, prov, &md); 1490217df2daSBen Woods explicit_bzero(&md, sizeof(md)); 1491e4b0a90eSBrooks Davis } 1492e4b0a90eSBrooks Davis 1493e4b0a90eSBrooks Davis static void 1494e4b0a90eSBrooks Davis eli_delkey(struct gctl_req *req) 1495e4b0a90eSBrooks Davis { 1496e4b0a90eSBrooks Davis const char *prov; 1497e4b0a90eSBrooks Davis int nargs; 1498e4b0a90eSBrooks Davis 1499e4b0a90eSBrooks Davis nargs = gctl_get_int(req, "nargs"); 1500e4b0a90eSBrooks Davis if (nargs != 1) { 1501e4b0a90eSBrooks Davis gctl_error(req, "Invalid number of arguments."); 1502e4b0a90eSBrooks Davis return; 1503e4b0a90eSBrooks Davis } 1504e4b0a90eSBrooks Davis prov = gctl_get_ascii(req, "arg0"); 1505e4b0a90eSBrooks Davis 1506e4b0a90eSBrooks Davis if (eli_is_attached(prov)) 1507e4b0a90eSBrooks Davis eli_delkey_attached(req, prov); 1508e4b0a90eSBrooks Davis else 1509e4b0a90eSBrooks Davis eli_delkey_detached(req, prov); 1510e4b0a90eSBrooks Davis } 1511e4b0a90eSBrooks Davis 1512e4b0a90eSBrooks Davis static void 1513e4b0a90eSBrooks Davis eli_resume(struct gctl_req *req) 1514e4b0a90eSBrooks Davis { 1515e4b0a90eSBrooks Davis struct g_eli_metadata md; 1516e4b0a90eSBrooks Davis unsigned char key[G_ELI_USERKEYLEN]; 1517e4b0a90eSBrooks Davis const char *prov; 1518e4b0a90eSBrooks Davis off_t mediasize; 1519e4b0a90eSBrooks Davis int nargs; 1520e4b0a90eSBrooks Davis 1521e4b0a90eSBrooks Davis nargs = gctl_get_int(req, "nargs"); 1522e4b0a90eSBrooks Davis if (nargs != 1) { 1523e4b0a90eSBrooks Davis gctl_error(req, "Invalid number of arguments."); 1524e4b0a90eSBrooks Davis return; 1525e4b0a90eSBrooks Davis } 1526e4b0a90eSBrooks Davis prov = gctl_get_ascii(req, "arg0"); 1527e4b0a90eSBrooks Davis 1528e4b0a90eSBrooks Davis if (eli_metadata_read(req, prov, &md) == -1) 1529e4b0a90eSBrooks Davis return; 1530e4b0a90eSBrooks Davis 1531e4b0a90eSBrooks Davis mediasize = g_get_mediasize(prov); 1532e4b0a90eSBrooks Davis if (md.md_provsize != (uint64_t)mediasize) { 1533e4b0a90eSBrooks Davis gctl_error(req, "Provider size mismatch."); 1534e4b0a90eSBrooks Davis return; 1535e4b0a90eSBrooks Davis } 1536e4b0a90eSBrooks Davis 1537e4b0a90eSBrooks Davis if (eli_genkey(req, &md, key, false) == NULL) { 1538217df2daSBen Woods explicit_bzero(key, sizeof(key)); 1539e4b0a90eSBrooks Davis return; 1540e4b0a90eSBrooks Davis } 1541e4b0a90eSBrooks Davis 1542e4b0a90eSBrooks Davis gctl_ro_param(req, "key", sizeof(key), key); 1543e4b0a90eSBrooks Davis if (gctl_issue(req) == NULL) { 1544e4b0a90eSBrooks Davis if (verbose) 1545e4b0a90eSBrooks Davis printf("Resumed %s.\n", prov); 1546e4b0a90eSBrooks Davis } 1547217df2daSBen Woods explicit_bzero(key, sizeof(key)); 1548e4b0a90eSBrooks Davis } 1549e4b0a90eSBrooks Davis 1550e4b0a90eSBrooks Davis static int 1551e4b0a90eSBrooks Davis eli_trash_metadata(struct gctl_req *req, const char *prov, int fd, off_t offset) 1552e4b0a90eSBrooks Davis { 1553e4b0a90eSBrooks Davis unsigned int overwrites; 1554e4b0a90eSBrooks Davis unsigned char *sector; 1555e4b0a90eSBrooks Davis ssize_t size; 1556e4b0a90eSBrooks Davis int error; 1557e4b0a90eSBrooks Davis 1558e4b0a90eSBrooks Davis size = sizeof(overwrites); 1559e4b0a90eSBrooks Davis if (sysctlbyname("kern.geom.eli.overwrites", &overwrites, &size, 1560e4b0a90eSBrooks Davis NULL, 0) == -1 || overwrites == 0) { 1561e4b0a90eSBrooks Davis overwrites = G_ELI_OVERWRITES; 1562e4b0a90eSBrooks Davis } 1563e4b0a90eSBrooks Davis 1564e4b0a90eSBrooks Davis size = g_sectorsize(fd); 1565e4b0a90eSBrooks Davis if (size <= 0) { 1566e4b0a90eSBrooks Davis gctl_error(req, "Cannot obtain provider sector size %s: %s.", 1567e4b0a90eSBrooks Davis prov, strerror(errno)); 1568e4b0a90eSBrooks Davis return (-1); 1569e4b0a90eSBrooks Davis } 1570e4b0a90eSBrooks Davis sector = malloc(size); 1571e4b0a90eSBrooks Davis if (sector == NULL) { 1572e4b0a90eSBrooks Davis gctl_error(req, "Cannot allocate %zd bytes of memory.", size); 1573e4b0a90eSBrooks Davis return (-1); 1574e4b0a90eSBrooks Davis } 1575e4b0a90eSBrooks Davis 1576e4b0a90eSBrooks Davis error = 0; 1577e4b0a90eSBrooks Davis do { 1578e4b0a90eSBrooks Davis arc4random_buf(sector, size); 1579e4b0a90eSBrooks Davis if (pwrite(fd, sector, size, offset) != size) { 1580e4b0a90eSBrooks Davis if (error == 0) 1581e4b0a90eSBrooks Davis error = errno; 1582e4b0a90eSBrooks Davis } 1583e4b0a90eSBrooks Davis (void)g_flush(fd); 1584e4b0a90eSBrooks Davis } while (--overwrites > 0); 1585e4b0a90eSBrooks Davis free(sector); 1586e4b0a90eSBrooks Davis if (error != 0) { 1587e4b0a90eSBrooks Davis gctl_error(req, "Cannot trash metadata on provider %s: %s.", 1588e4b0a90eSBrooks Davis prov, strerror(error)); 1589e4b0a90eSBrooks Davis return (-1); 1590e4b0a90eSBrooks Davis } 1591e4b0a90eSBrooks Davis return (0); 1592e4b0a90eSBrooks Davis } 1593e4b0a90eSBrooks Davis 1594e4b0a90eSBrooks Davis static void 1595e4b0a90eSBrooks Davis eli_kill_detached(struct gctl_req *req, const char *prov) 1596e4b0a90eSBrooks Davis { 1597e4b0a90eSBrooks Davis off_t offset; 1598e4b0a90eSBrooks Davis int fd; 1599e4b0a90eSBrooks Davis 1600e4b0a90eSBrooks Davis /* 1601e4b0a90eSBrooks Davis * NOTE: Maybe we should verify if this is geli provider first, 1602e4b0a90eSBrooks Davis * but 'kill' command is quite critical so better don't waste 1603e4b0a90eSBrooks Davis * the time. 1604e4b0a90eSBrooks Davis */ 1605e4b0a90eSBrooks Davis #if 0 1606e4b0a90eSBrooks Davis error = g_metadata_read(prov, (unsigned char *)&md, sizeof(md), 1607e4b0a90eSBrooks Davis G_ELI_MAGIC); 1608e4b0a90eSBrooks Davis if (error != 0) { 1609e4b0a90eSBrooks Davis gctl_error(req, "Cannot read metadata from %s: %s.", prov, 1610e4b0a90eSBrooks Davis strerror(error)); 1611e4b0a90eSBrooks Davis return; 1612e4b0a90eSBrooks Davis } 1613e4b0a90eSBrooks Davis #endif 1614e4b0a90eSBrooks Davis 1615e4b0a90eSBrooks Davis fd = g_open(prov, 1); 1616e4b0a90eSBrooks Davis if (fd == -1) { 1617e4b0a90eSBrooks Davis gctl_error(req, "Cannot open provider %s: %s.", prov, 1618e4b0a90eSBrooks Davis strerror(errno)); 1619e4b0a90eSBrooks Davis return; 1620e4b0a90eSBrooks Davis } 1621e4b0a90eSBrooks Davis offset = g_mediasize(fd) - g_sectorsize(fd); 1622e4b0a90eSBrooks Davis if (offset <= 0) { 1623e4b0a90eSBrooks Davis gctl_error(req, 1624e4b0a90eSBrooks Davis "Cannot obtain media size or sector size for provider %s: %s.", 1625e4b0a90eSBrooks Davis prov, strerror(errno)); 1626e4b0a90eSBrooks Davis (void)g_close(fd); 1627e4b0a90eSBrooks Davis return; 1628e4b0a90eSBrooks Davis } 1629e4b0a90eSBrooks Davis (void)eli_trash_metadata(req, prov, fd, offset); 1630e4b0a90eSBrooks Davis (void)g_close(fd); 1631e4b0a90eSBrooks Davis } 1632e4b0a90eSBrooks Davis 1633e4b0a90eSBrooks Davis static void 1634e4b0a90eSBrooks Davis eli_kill(struct gctl_req *req) 1635e4b0a90eSBrooks Davis { 1636e4b0a90eSBrooks Davis const char *prov; 1637e4b0a90eSBrooks Davis int i, nargs, all; 1638e4b0a90eSBrooks Davis 1639e4b0a90eSBrooks Davis nargs = gctl_get_int(req, "nargs"); 1640e4b0a90eSBrooks Davis all = gctl_get_int(req, "all"); 1641e4b0a90eSBrooks Davis if (!all && nargs == 0) { 1642e4b0a90eSBrooks Davis gctl_error(req, "Too few arguments."); 1643e4b0a90eSBrooks Davis return; 1644e4b0a90eSBrooks Davis } 1645e4b0a90eSBrooks Davis /* 1646e4b0a90eSBrooks Davis * How '-a' option combine with a list of providers: 1647e4b0a90eSBrooks Davis * Delete Master Keys from all attached providers: 1648e4b0a90eSBrooks Davis * geli kill -a 1649e4b0a90eSBrooks Davis * Delete Master Keys from all attached providers and from 1650e4b0a90eSBrooks Davis * detached da0 and da1: 1651e4b0a90eSBrooks Davis * geli kill -a da0 da1 1652e4b0a90eSBrooks Davis * Delete Master Keys from (attached or detached) da0 and da1: 1653e4b0a90eSBrooks Davis * geli kill da0 da1 1654e4b0a90eSBrooks Davis */ 1655e4b0a90eSBrooks Davis 1656e4b0a90eSBrooks Davis /* First detached providers. */ 1657e4b0a90eSBrooks Davis for (i = 0; i < nargs; i++) { 1658e4b0a90eSBrooks Davis prov = gctl_get_ascii(req, "arg%d", i); 1659e4b0a90eSBrooks Davis if (!eli_is_attached(prov)) 1660e4b0a90eSBrooks Davis eli_kill_detached(req, prov); 1661e4b0a90eSBrooks Davis } 1662e4b0a90eSBrooks Davis /* Now attached providers. */ 1663e4b0a90eSBrooks Davis gctl_issue(req); 1664e4b0a90eSBrooks Davis } 1665e4b0a90eSBrooks Davis 1666e4b0a90eSBrooks Davis static int 1667e4b0a90eSBrooks Davis eli_backup_create(struct gctl_req *req, const char *prov, const char *file) 1668e4b0a90eSBrooks Davis { 1669e4b0a90eSBrooks Davis unsigned char *sector; 1670e4b0a90eSBrooks Davis ssize_t secsize; 1671e4b0a90eSBrooks Davis int error, filefd, ret; 1672e4b0a90eSBrooks Davis 1673e4b0a90eSBrooks Davis ret = -1; 1674e4b0a90eSBrooks Davis filefd = -1; 1675e4b0a90eSBrooks Davis sector = NULL; 1676e4b0a90eSBrooks Davis secsize = 0; 1677e4b0a90eSBrooks Davis 1678e4b0a90eSBrooks Davis secsize = g_get_sectorsize(prov); 1679e4b0a90eSBrooks Davis if (secsize == 0) { 1680e4b0a90eSBrooks Davis gctl_error(req, "Cannot get informations about %s: %s.", prov, 1681e4b0a90eSBrooks Davis strerror(errno)); 1682e4b0a90eSBrooks Davis goto out; 1683e4b0a90eSBrooks Davis } 1684e4b0a90eSBrooks Davis sector = malloc(secsize); 1685e4b0a90eSBrooks Davis if (sector == NULL) { 1686e4b0a90eSBrooks Davis gctl_error(req, "Cannot allocate memory."); 1687e4b0a90eSBrooks Davis goto out; 1688e4b0a90eSBrooks Davis } 1689e4b0a90eSBrooks Davis /* Read metadata from the provider. */ 1690e4b0a90eSBrooks Davis error = g_metadata_read(prov, sector, secsize, G_ELI_MAGIC); 1691e4b0a90eSBrooks Davis if (error != 0) { 1692e4b0a90eSBrooks Davis gctl_error(req, "Unable to read metadata from %s: %s.", prov, 1693e4b0a90eSBrooks Davis strerror(error)); 1694e4b0a90eSBrooks Davis goto out; 1695e4b0a90eSBrooks Davis } 1696e4b0a90eSBrooks Davis 1697e4b0a90eSBrooks Davis filefd = open(file, O_WRONLY | O_TRUNC | O_CREAT, 0600); 1698e4b0a90eSBrooks Davis if (filefd == -1) { 1699e4b0a90eSBrooks Davis gctl_error(req, "Unable to open %s: %s.", file, 1700e4b0a90eSBrooks Davis strerror(errno)); 1701e4b0a90eSBrooks Davis goto out; 1702e4b0a90eSBrooks Davis } 1703e4b0a90eSBrooks Davis /* Write metadata to the destination file. */ 1704e4b0a90eSBrooks Davis if (write(filefd, sector, secsize) != secsize) { 1705e4b0a90eSBrooks Davis gctl_error(req, "Unable to write to %s: %s.", file, 1706e4b0a90eSBrooks Davis strerror(errno)); 1707e4b0a90eSBrooks Davis (void)close(filefd); 1708e4b0a90eSBrooks Davis (void)unlink(file); 1709e4b0a90eSBrooks Davis goto out; 1710e4b0a90eSBrooks Davis } 1711e4b0a90eSBrooks Davis (void)fsync(filefd); 1712e4b0a90eSBrooks Davis (void)close(filefd); 1713e4b0a90eSBrooks Davis /* Success. */ 1714e4b0a90eSBrooks Davis ret = 0; 1715e4b0a90eSBrooks Davis out: 1716e4b0a90eSBrooks Davis if (sector != NULL) { 1717217df2daSBen Woods explicit_bzero(sector, secsize); 1718e4b0a90eSBrooks Davis free(sector); 1719e4b0a90eSBrooks Davis } 1720e4b0a90eSBrooks Davis return (ret); 1721e4b0a90eSBrooks Davis } 1722e4b0a90eSBrooks Davis 1723e4b0a90eSBrooks Davis static void 1724e4b0a90eSBrooks Davis eli_backup(struct gctl_req *req) 1725e4b0a90eSBrooks Davis { 1726e4b0a90eSBrooks Davis const char *file, *prov; 1727e4b0a90eSBrooks Davis int nargs; 1728e4b0a90eSBrooks Davis 1729e4b0a90eSBrooks Davis nargs = gctl_get_int(req, "nargs"); 1730e4b0a90eSBrooks Davis if (nargs != 2) { 1731e4b0a90eSBrooks Davis gctl_error(req, "Invalid number of arguments."); 1732e4b0a90eSBrooks Davis return; 1733e4b0a90eSBrooks Davis } 1734e4b0a90eSBrooks Davis prov = gctl_get_ascii(req, "arg0"); 1735e4b0a90eSBrooks Davis file = gctl_get_ascii(req, "arg1"); 1736e4b0a90eSBrooks Davis 1737e4b0a90eSBrooks Davis eli_backup_create(req, prov, file); 1738e4b0a90eSBrooks Davis } 1739e4b0a90eSBrooks Davis 1740e4b0a90eSBrooks Davis static void 1741e4b0a90eSBrooks Davis eli_restore(struct gctl_req *req) 1742e4b0a90eSBrooks Davis { 1743e4b0a90eSBrooks Davis struct g_eli_metadata md; 1744e4b0a90eSBrooks Davis const char *file, *prov; 1745e4b0a90eSBrooks Davis off_t mediasize; 1746e4b0a90eSBrooks Davis int nargs; 1747e4b0a90eSBrooks Davis 1748e4b0a90eSBrooks Davis nargs = gctl_get_int(req, "nargs"); 1749e4b0a90eSBrooks Davis if (nargs != 2) { 1750e4b0a90eSBrooks Davis gctl_error(req, "Invalid number of arguments."); 1751e4b0a90eSBrooks Davis return; 1752e4b0a90eSBrooks Davis } 1753e4b0a90eSBrooks Davis file = gctl_get_ascii(req, "arg0"); 1754e4b0a90eSBrooks Davis prov = gctl_get_ascii(req, "arg1"); 1755e4b0a90eSBrooks Davis 1756e4b0a90eSBrooks Davis /* Read metadata from the backup file. */ 1757e4b0a90eSBrooks Davis if (eli_metadata_read(req, file, &md) == -1) 1758e4b0a90eSBrooks Davis return; 1759e4b0a90eSBrooks Davis /* Obtain provider's mediasize. */ 1760e4b0a90eSBrooks Davis mediasize = g_get_mediasize(prov); 1761e4b0a90eSBrooks Davis if (mediasize == 0) { 1762e4b0a90eSBrooks Davis gctl_error(req, "Cannot get informations about %s: %s.", prov, 1763e4b0a90eSBrooks Davis strerror(errno)); 1764e4b0a90eSBrooks Davis return; 1765e4b0a90eSBrooks Davis } 1766e4b0a90eSBrooks Davis /* Check if the provider size has changed since we did the backup. */ 1767e4b0a90eSBrooks Davis if (md.md_provsize != (uint64_t)mediasize) { 1768e4b0a90eSBrooks Davis if (gctl_get_int(req, "force")) { 1769e4b0a90eSBrooks Davis md.md_provsize = mediasize; 1770e4b0a90eSBrooks Davis } else { 1771e4b0a90eSBrooks Davis gctl_error(req, "Provider size mismatch: " 1772e4b0a90eSBrooks Davis "wrong backup file?"); 1773e4b0a90eSBrooks Davis return; 1774e4b0a90eSBrooks Davis } 1775e4b0a90eSBrooks Davis } 1776e4b0a90eSBrooks Davis /* Write metadata to the provider. */ 1777e4b0a90eSBrooks Davis (void)eli_metadata_store(req, prov, &md); 1778e4b0a90eSBrooks Davis } 1779e4b0a90eSBrooks Davis 1780e4b0a90eSBrooks Davis static void 1781e4b0a90eSBrooks Davis eli_resize(struct gctl_req *req) 1782e4b0a90eSBrooks Davis { 1783e4b0a90eSBrooks Davis struct g_eli_metadata md; 1784e4b0a90eSBrooks Davis const char *prov; 1785e4b0a90eSBrooks Davis unsigned char *sector; 1786e4b0a90eSBrooks Davis ssize_t secsize; 1787e4b0a90eSBrooks Davis off_t mediasize, oldsize; 1788e4b0a90eSBrooks Davis int error, nargs, provfd; 1789e4b0a90eSBrooks Davis 1790e4b0a90eSBrooks Davis nargs = gctl_get_int(req, "nargs"); 1791e4b0a90eSBrooks Davis if (nargs != 1) { 1792e4b0a90eSBrooks Davis gctl_error(req, "Invalid number of arguments."); 1793e4b0a90eSBrooks Davis return; 1794e4b0a90eSBrooks Davis } 1795e4b0a90eSBrooks Davis prov = gctl_get_ascii(req, "arg0"); 1796e4b0a90eSBrooks Davis 1797e4b0a90eSBrooks Davis provfd = -1; 1798e4b0a90eSBrooks Davis sector = NULL; 1799e4b0a90eSBrooks Davis secsize = 0; 1800e4b0a90eSBrooks Davis 1801e4b0a90eSBrooks Davis provfd = g_open(prov, 1); 1802e4b0a90eSBrooks Davis if (provfd == -1) { 1803e4b0a90eSBrooks Davis gctl_error(req, "Cannot open %s: %s.", prov, strerror(errno)); 1804e4b0a90eSBrooks Davis goto out; 1805e4b0a90eSBrooks Davis } 1806e4b0a90eSBrooks Davis 1807e4b0a90eSBrooks Davis mediasize = g_mediasize(provfd); 1808e4b0a90eSBrooks Davis secsize = g_sectorsize(provfd); 1809e4b0a90eSBrooks Davis if (mediasize == -1 || secsize == -1) { 1810e4b0a90eSBrooks Davis gctl_error(req, "Cannot get information about %s: %s.", prov, 1811e4b0a90eSBrooks Davis strerror(errno)); 1812e4b0a90eSBrooks Davis goto out; 1813e4b0a90eSBrooks Davis } 1814e4b0a90eSBrooks Davis 1815e4b0a90eSBrooks Davis sector = malloc(secsize); 1816e4b0a90eSBrooks Davis if (sector == NULL) { 1817e4b0a90eSBrooks Davis gctl_error(req, "Cannot allocate memory."); 1818e4b0a90eSBrooks Davis goto out; 1819e4b0a90eSBrooks Davis } 1820e4b0a90eSBrooks Davis 1821e4b0a90eSBrooks Davis oldsize = gctl_get_intmax(req, "oldsize"); 1822e4b0a90eSBrooks Davis if (oldsize < 0 || oldsize > mediasize) { 1823e4b0a90eSBrooks Davis gctl_error(req, "Invalid oldsize: Out of range."); 1824e4b0a90eSBrooks Davis goto out; 1825e4b0a90eSBrooks Davis } 1826e4b0a90eSBrooks Davis if (oldsize == mediasize) { 1827e4b0a90eSBrooks Davis gctl_error(req, "Size hasn't changed."); 1828e4b0a90eSBrooks Davis goto out; 1829e4b0a90eSBrooks Davis } 1830e4b0a90eSBrooks Davis 1831e4b0a90eSBrooks Davis /* Read metadata from the 'oldsize' offset. */ 1832e4b0a90eSBrooks Davis if (pread(provfd, sector, secsize, oldsize - secsize) != secsize) { 1833e4b0a90eSBrooks Davis gctl_error(req, "Cannot read old metadata: %s.", 1834e4b0a90eSBrooks Davis strerror(errno)); 1835e4b0a90eSBrooks Davis goto out; 1836e4b0a90eSBrooks Davis } 1837e4b0a90eSBrooks Davis 1838e4b0a90eSBrooks Davis /* Check if this sector contains geli metadata. */ 1839e4b0a90eSBrooks Davis error = eli_metadata_decode(sector, &md); 1840e4b0a90eSBrooks Davis switch (error) { 1841e4b0a90eSBrooks Davis case 0: 1842e4b0a90eSBrooks Davis break; 1843e4b0a90eSBrooks Davis case EOPNOTSUPP: 1844e4b0a90eSBrooks Davis gctl_error(req, 1845e4b0a90eSBrooks Davis "Provider's %s metadata version %u is too new.\n" 1846e4b0a90eSBrooks Davis "geli: The highest supported version is %u.", 1847e4b0a90eSBrooks Davis prov, (unsigned int)md.md_version, G_ELI_VERSION); 1848e4b0a90eSBrooks Davis goto out; 1849e4b0a90eSBrooks Davis case EINVAL: 1850e4b0a90eSBrooks Davis gctl_error(req, "Inconsistent provider's %s metadata.", prov); 1851e4b0a90eSBrooks Davis goto out; 1852e4b0a90eSBrooks Davis default: 1853e4b0a90eSBrooks Davis gctl_error(req, 1854e4b0a90eSBrooks Davis "Unexpected error while decoding provider's %s metadata: %s.", 1855e4b0a90eSBrooks Davis prov, strerror(error)); 1856e4b0a90eSBrooks Davis goto out; 1857e4b0a90eSBrooks Davis } 1858e4b0a90eSBrooks Davis 1859e4b0a90eSBrooks Davis /* 1860e4b0a90eSBrooks Davis * If the old metadata doesn't have a correct provider size, refuse 1861e4b0a90eSBrooks Davis * to resize. 1862e4b0a90eSBrooks Davis */ 1863e4b0a90eSBrooks Davis if (md.md_provsize != (uint64_t)oldsize) { 1864e4b0a90eSBrooks Davis gctl_error(req, "Provider size mismatch at oldsize."); 1865e4b0a90eSBrooks Davis goto out; 1866e4b0a90eSBrooks Davis } 1867e4b0a90eSBrooks Davis 1868e4b0a90eSBrooks Davis /* 1869e4b0a90eSBrooks Davis * Update the old metadata with the current provider size and write 1870e4b0a90eSBrooks Davis * it back to the correct place on the provider. 1871e4b0a90eSBrooks Davis */ 1872e4b0a90eSBrooks Davis md.md_provsize = mediasize; 1873e4b0a90eSBrooks Davis /* Write metadata to the provider. */ 1874e4b0a90eSBrooks Davis (void)eli_metadata_store(req, prov, &md); 1875e4b0a90eSBrooks Davis /* Now trash the old metadata. */ 1876e4b0a90eSBrooks Davis (void)eli_trash_metadata(req, prov, provfd, oldsize - secsize); 1877e4b0a90eSBrooks Davis out: 1878e4b0a90eSBrooks Davis if (provfd != -1) 1879e4b0a90eSBrooks Davis (void)g_close(provfd); 1880e4b0a90eSBrooks Davis if (sector != NULL) { 1881217df2daSBen Woods explicit_bzero(sector, secsize); 1882e4b0a90eSBrooks Davis free(sector); 1883e4b0a90eSBrooks Davis } 1884e4b0a90eSBrooks Davis } 1885e4b0a90eSBrooks Davis 1886e4b0a90eSBrooks Davis static void 1887e4b0a90eSBrooks Davis eli_version(struct gctl_req *req) 1888e4b0a90eSBrooks Davis { 1889e4b0a90eSBrooks Davis struct g_eli_metadata md; 1890e4b0a90eSBrooks Davis const char *name; 1891e4b0a90eSBrooks Davis unsigned int version; 1892e4b0a90eSBrooks Davis int error, i, nargs; 1893e4b0a90eSBrooks Davis 1894e4b0a90eSBrooks Davis nargs = gctl_get_int(req, "nargs"); 1895e4b0a90eSBrooks Davis 1896e4b0a90eSBrooks Davis if (nargs == 0) { 1897e4b0a90eSBrooks Davis unsigned int kernver; 1898e4b0a90eSBrooks Davis ssize_t size; 1899e4b0a90eSBrooks Davis 1900e4b0a90eSBrooks Davis size = sizeof(kernver); 1901e4b0a90eSBrooks Davis if (sysctlbyname("kern.geom.eli.version", &kernver, &size, 1902e4b0a90eSBrooks Davis NULL, 0) == -1) { 1903e4b0a90eSBrooks Davis warn("Unable to obtain GELI kernel version"); 1904e4b0a90eSBrooks Davis } else { 1905e4b0a90eSBrooks Davis printf("kernel: %u\n", kernver); 1906e4b0a90eSBrooks Davis } 1907e4b0a90eSBrooks Davis printf("userland: %u\n", G_ELI_VERSION); 1908e4b0a90eSBrooks Davis return; 1909e4b0a90eSBrooks Davis } 1910e4b0a90eSBrooks Davis 1911e4b0a90eSBrooks Davis for (i = 0; i < nargs; i++) { 1912e4b0a90eSBrooks Davis name = gctl_get_ascii(req, "arg%d", i); 1913e4b0a90eSBrooks Davis error = g_metadata_read(name, (unsigned char *)&md, 1914e4b0a90eSBrooks Davis sizeof(md), G_ELI_MAGIC); 1915e4b0a90eSBrooks Davis if (error != 0) { 1916e4b0a90eSBrooks Davis warn("%s: Unable to read metadata: %s.", name, 1917e4b0a90eSBrooks Davis strerror(error)); 1918e4b0a90eSBrooks Davis gctl_error(req, "Not fully done."); 1919e4b0a90eSBrooks Davis continue; 1920e4b0a90eSBrooks Davis } 1921e4b0a90eSBrooks Davis version = le32dec(&md.md_version); 1922e4b0a90eSBrooks Davis printf("%s: %u\n", name, version); 1923e4b0a90eSBrooks Davis } 1924e4b0a90eSBrooks Davis } 1925e4b0a90eSBrooks Davis 1926e4b0a90eSBrooks Davis static void 1927e4b0a90eSBrooks Davis eli_clear(struct gctl_req *req) 1928e4b0a90eSBrooks Davis { 1929e4b0a90eSBrooks Davis const char *name; 1930e4b0a90eSBrooks Davis int error, i, nargs; 1931e4b0a90eSBrooks Davis 1932e4b0a90eSBrooks Davis nargs = gctl_get_int(req, "nargs"); 1933e4b0a90eSBrooks Davis if (nargs < 1) { 1934e4b0a90eSBrooks Davis gctl_error(req, "Too few arguments."); 1935e4b0a90eSBrooks Davis return; 1936e4b0a90eSBrooks Davis } 1937e4b0a90eSBrooks Davis 1938e4b0a90eSBrooks Davis for (i = 0; i < nargs; i++) { 1939e4b0a90eSBrooks Davis name = gctl_get_ascii(req, "arg%d", i); 1940e4b0a90eSBrooks Davis error = g_metadata_clear(name, G_ELI_MAGIC); 1941e4b0a90eSBrooks Davis if (error != 0) { 1942e4b0a90eSBrooks Davis fprintf(stderr, "Cannot clear metadata on %s: %s.\n", 1943e4b0a90eSBrooks Davis name, strerror(error)); 1944e4b0a90eSBrooks Davis gctl_error(req, "Not fully done."); 1945e4b0a90eSBrooks Davis continue; 1946e4b0a90eSBrooks Davis } 1947e4b0a90eSBrooks Davis if (verbose) 1948e4b0a90eSBrooks Davis printf("Metadata cleared on %s.\n", name); 1949e4b0a90eSBrooks Davis } 1950e4b0a90eSBrooks Davis } 1951e4b0a90eSBrooks Davis 1952e4b0a90eSBrooks Davis static void 1953e4b0a90eSBrooks Davis eli_dump(struct gctl_req *req) 1954e4b0a90eSBrooks Davis { 1955e4b0a90eSBrooks Davis struct g_eli_metadata md; 1956e4b0a90eSBrooks Davis const char *name; 1957e4b0a90eSBrooks Davis int i, nargs; 1958e4b0a90eSBrooks Davis 1959e4b0a90eSBrooks Davis nargs = gctl_get_int(req, "nargs"); 1960e4b0a90eSBrooks Davis if (nargs < 1) { 1961e4b0a90eSBrooks Davis gctl_error(req, "Too few arguments."); 1962e4b0a90eSBrooks Davis return; 1963e4b0a90eSBrooks Davis } 1964e4b0a90eSBrooks Davis 1965e4b0a90eSBrooks Davis for (i = 0; i < nargs; i++) { 1966e4b0a90eSBrooks Davis name = gctl_get_ascii(req, "arg%d", i); 1967e4b0a90eSBrooks Davis if (eli_metadata_read(NULL, name, &md) == -1) { 1968e4b0a90eSBrooks Davis gctl_error(req, "Not fully done."); 1969e4b0a90eSBrooks Davis continue; 1970e4b0a90eSBrooks Davis } 1971e4b0a90eSBrooks Davis printf("Metadata on %s:\n", name); 1972e4b0a90eSBrooks Davis eli_metadata_dump(&md); 1973e4b0a90eSBrooks Davis printf("\n"); 1974e4b0a90eSBrooks Davis } 1975e4b0a90eSBrooks Davis } 1976