geom_eli.c revision 226723
1/*- 2 * Copyright (c) 2004-2010 Pawel Jakub Dawidek <pjd@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD: head/sbin/geom/class/eli/geom_eli.c 226723 2011-10-25 08:03:51Z pjd $"); 29 30#include <sys/param.h> 31#include <sys/mman.h> 32#include <sys/sysctl.h> 33#include <sys/resource.h> 34#include <opencrypto/cryptodev.h> 35 36#include <assert.h> 37#include <err.h> 38#include <errno.h> 39#include <fcntl.h> 40#include <libgeom.h> 41#include <paths.h> 42#include <readpassphrase.h> 43#include <stdbool.h> 44#include <stdint.h> 45#include <stdio.h> 46#include <stdlib.h> 47#include <string.h> 48#include <strings.h> 49#include <unistd.h> 50 51#include <geom/eli/g_eli.h> 52#include <geom/eli/pkcs5v2.h> 53 54#include "core/geom.h" 55#include "misc/subr.h" 56 57 58uint32_t lib_version = G_LIB_VERSION; 59uint32_t version = G_ELI_VERSION; 60 61#define GELI_BACKUP_DIR "/var/backups/" 62#define GELI_ENC_ALGO "aes" 63 64static void eli_main(struct gctl_req *req, unsigned flags); 65static void eli_init(struct gctl_req *req); 66static void eli_attach(struct gctl_req *req); 67static void eli_configure(struct gctl_req *req); 68static void eli_setkey(struct gctl_req *req); 69static void eli_delkey(struct gctl_req *req); 70static void eli_resume(struct gctl_req *req); 71static void eli_kill(struct gctl_req *req); 72static void eli_backup(struct gctl_req *req); 73static void eli_restore(struct gctl_req *req); 74static void eli_resize(struct gctl_req *req); 75static void eli_version(struct gctl_req *req); 76static void eli_clear(struct gctl_req *req); 77static void eli_dump(struct gctl_req *req); 78 79static int eli_backup_create(struct gctl_req *req, const char *prov, 80 const char *file); 81 82/* 83 * Available commands: 84 * 85 * init [-bhPv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] prov 86 * label - alias for 'init' 87 * attach [-dprv] [-j passfile] [-k keyfile] prov 88 * detach [-fl] prov ... 89 * stop - alias for 'detach' 90 * onetime [-d] [-a aalgo] [-e ealgo] [-l keylen] prov 91 * configure [-bB] prov ... 92 * setkey [-pPv] [-n keyno] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov 93 * delkey [-afv] [-n keyno] prov 94 * suspend [-v] -a | prov ... 95 * resume [-pv] [-j passfile] [-k keyfile] prov 96 * kill [-av] [prov ...] 97 * backup [-v] prov file 98 * restore [-fv] file prov 99 * resize [-v] -s oldsize prov 100 * version [prov ...] 101 * clear [-v] prov ... 102 * dump [-v] prov ... 103 */ 104struct g_command class_commands[] = { 105 { "init", G_FLAG_VERBOSE, eli_main, 106 { 107 { 'a', "aalgo", "", G_TYPE_STRING }, 108 { 'b', "boot", NULL, G_TYPE_BOOL }, 109 { 'B', "backupfile", "", G_TYPE_STRING }, 110 { 'e', "ealgo", GELI_ENC_ALGO, G_TYPE_STRING }, 111 { 'i', "iterations", "-1", G_TYPE_NUMBER }, 112 { 'J', "newpassfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 113 { 'K', "newkeyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 114 { 'l', "keylen", "0", G_TYPE_NUMBER }, 115 { 'P', "nonewpassphrase", NULL, G_TYPE_BOOL }, 116 { 's', "sectorsize", "0", G_TYPE_NUMBER }, 117 G_OPT_SENTINEL 118 }, 119 "[-bPv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] [-s sectorsize] prov" 120 }, 121 { "label", G_FLAG_VERBOSE, eli_main, 122 { 123 { 'a', "aalgo", "", G_TYPE_STRING }, 124 { 'b', "boot", NULL, G_TYPE_BOOL }, 125 { 'B', "backupfile", "", G_TYPE_STRING }, 126 { 'e', "ealgo", GELI_ENC_ALGO, G_TYPE_STRING }, 127 { 'i', "iterations", "-1", G_TYPE_NUMBER }, 128 { 'J', "newpassfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 129 { 'K', "newkeyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 130 { 'l', "keylen", "0", G_TYPE_NUMBER }, 131 { 'P', "nonewpassphrase", NULL, G_TYPE_BOOL }, 132 { 's', "sectorsize", "0", G_TYPE_NUMBER }, 133 G_OPT_SENTINEL 134 }, 135 "- an alias for 'init'" 136 }, 137 { "attach", G_FLAG_VERBOSE | G_FLAG_LOADKLD, eli_main, 138 { 139 { 'd', "detach", NULL, G_TYPE_BOOL }, 140 { 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 141 { 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 142 { 'p', "nopassphrase", NULL, G_TYPE_BOOL }, 143 { 'r', "readonly", NULL, G_TYPE_BOOL }, 144 G_OPT_SENTINEL 145 }, 146 "[-dprv] [-j passfile] [-k keyfile] prov" 147 }, 148 { "detach", 0, NULL, 149 { 150 { 'f', "force", NULL, G_TYPE_BOOL }, 151 { 'l', "last", NULL, G_TYPE_BOOL }, 152 G_OPT_SENTINEL 153 }, 154 "[-fl] prov ..." 155 }, 156 { "stop", 0, NULL, 157 { 158 { 'f', "force", NULL, G_TYPE_BOOL }, 159 { 'l', "last", NULL, G_TYPE_BOOL }, 160 G_OPT_SENTINEL 161 }, 162 "- an alias for 'detach'" 163 }, 164 { "onetime", G_FLAG_VERBOSE | G_FLAG_LOADKLD, NULL, 165 { 166 { 'a', "aalgo", "", G_TYPE_STRING }, 167 { 'd', "detach", NULL, G_TYPE_BOOL }, 168 { 'e', "ealgo", GELI_ENC_ALGO, G_TYPE_STRING }, 169 { 'l', "keylen", "0", G_TYPE_NUMBER }, 170 { 's', "sectorsize", "0", G_TYPE_NUMBER }, 171 G_OPT_SENTINEL 172 }, 173 "[-d] [-a aalgo] [-e ealgo] [-l keylen] [-s sectorsize] prov" 174 }, 175 { "configure", G_FLAG_VERBOSE, eli_main, 176 { 177 { 'b', "boot", NULL, G_TYPE_BOOL }, 178 { 'B', "noboot", NULL, G_TYPE_BOOL }, 179 G_OPT_SENTINEL 180 }, 181 "[-bB] prov ..." 182 }, 183 { "setkey", G_FLAG_VERBOSE, eli_main, 184 { 185 { 'i', "iterations", "-1", G_TYPE_NUMBER }, 186 { 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 187 { 'J', "newpassfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 188 { 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 189 { 'K', "newkeyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 190 { 'n', "keyno", "-1", G_TYPE_NUMBER }, 191 { 'p', "nopassphrase", NULL, G_TYPE_BOOL }, 192 { 'P', "nonewpassphrase", NULL, G_TYPE_BOOL }, 193 G_OPT_SENTINEL 194 }, 195 "[-pPv] [-n keyno] [-i iterations] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov" 196 }, 197 { "delkey", G_FLAG_VERBOSE, eli_main, 198 { 199 { 'a', "all", NULL, G_TYPE_BOOL }, 200 { 'f', "force", NULL, G_TYPE_BOOL }, 201 { 'n', "keyno", "-1", G_TYPE_NUMBER }, 202 G_OPT_SENTINEL 203 }, 204 "[-afv] [-n keyno] prov" 205 }, 206 { "suspend", G_FLAG_VERBOSE, NULL, 207 { 208 { 'a', "all", NULL, G_TYPE_BOOL }, 209 G_OPT_SENTINEL 210 }, 211 "[-v] -a | prov ..." 212 }, 213 { "resume", G_FLAG_VERBOSE, eli_main, 214 { 215 { 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 216 { 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI }, 217 { 'p', "nopassphrase", NULL, G_TYPE_BOOL }, 218 G_OPT_SENTINEL 219 }, 220 "[-pv] [-j passfile] [-k keyfile] prov" 221 }, 222 { "kill", G_FLAG_VERBOSE, eli_main, 223 { 224 { 'a', "all", NULL, G_TYPE_BOOL }, 225 G_OPT_SENTINEL 226 }, 227 "[-av] [prov ...]" 228 }, 229 { "backup", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS, 230 "[-v] prov file" 231 }, 232 { "restore", G_FLAG_VERBOSE, eli_main, 233 { 234 { 'f', "force", NULL, G_TYPE_BOOL }, 235 G_OPT_SENTINEL 236 }, 237 "[-fv] file prov" 238 }, 239 { "resize", G_FLAG_VERBOSE, eli_main, 240 { 241 { 's', "oldsize", NULL, G_TYPE_NUMBER }, 242 G_OPT_SENTINEL 243 }, 244 "[-v] -s oldsize prov" 245 }, 246 { "version", G_FLAG_LOADKLD, eli_main, G_NULL_OPTS, 247 "[prov ...]" 248 }, 249 { "clear", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS, 250 "[-v] prov ..." 251 }, 252 { "dump", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS, 253 "[-v] prov ..." 254 }, 255 G_CMD_SENTINEL 256}; 257 258static int verbose = 0; 259 260static int 261eli_protect(struct gctl_req *req) 262{ 263 struct rlimit rl; 264 265 /* Disable core dumps. */ 266 rl.rlim_cur = 0; 267 rl.rlim_max = 0; 268 if (setrlimit(RLIMIT_CORE, &rl) == -1) { 269 gctl_error(req, "Cannot disable core dumps: %s.", 270 strerror(errno)); 271 return (-1); 272 } 273 /* Disable swapping. */ 274 if (mlockall(MCL_FUTURE) == -1) { 275 gctl_error(req, "Cannot lock memory: %s.", strerror(errno)); 276 return (-1); 277 } 278 return (0); 279} 280 281static void 282eli_main(struct gctl_req *req, unsigned int flags) 283{ 284 const char *name; 285 286 if (eli_protect(req) == -1) 287 return; 288 289 if ((flags & G_FLAG_VERBOSE) != 0) 290 verbose = 1; 291 292 name = gctl_get_ascii(req, "verb"); 293 if (name == NULL) { 294 gctl_error(req, "No '%s' argument.", "verb"); 295 return; 296 } 297 if (strcmp(name, "init") == 0 || strcmp(name, "label") == 0) 298 eli_init(req); 299 else if (strcmp(name, "attach") == 0) 300 eli_attach(req); 301 else if (strcmp(name, "configure") == 0) 302 eli_configure(req); 303 else if (strcmp(name, "setkey") == 0) 304 eli_setkey(req); 305 else if (strcmp(name, "delkey") == 0) 306 eli_delkey(req); 307 else if (strcmp(name, "resume") == 0) 308 eli_resume(req); 309 else if (strcmp(name, "kill") == 0) 310 eli_kill(req); 311 else if (strcmp(name, "backup") == 0) 312 eli_backup(req); 313 else if (strcmp(name, "restore") == 0) 314 eli_restore(req); 315 else if (strcmp(name, "resize") == 0) 316 eli_resize(req); 317 else if (strcmp(name, "version") == 0) 318 eli_version(req); 319 else if (strcmp(name, "dump") == 0) 320 eli_dump(req); 321 else if (strcmp(name, "clear") == 0) 322 eli_clear(req); 323 else 324 gctl_error(req, "Unknown command: %s.", name); 325} 326 327static void 328arc4rand(unsigned char *buf, size_t size) 329{ 330 uint32_t *buf4; 331 size_t size4; 332 unsigned int i; 333 334 buf4 = (uint32_t *)buf; 335 size4 = size / 4; 336 337 for (i = 0; i < size4; i++) 338 buf4[i] = arc4random(); 339 for (i *= 4; i < size; i++) 340 buf[i] = arc4random() % 0xff; 341} 342 343static bool 344eli_is_attached(const char *prov) 345{ 346 char name[MAXPATHLEN]; 347 348 /* 349 * Not the best way to do it, but the easiest. 350 * We try to open provider and check if it is a GEOM provider 351 * by asking about its sectorsize. 352 */ 353 snprintf(name, sizeof(name), "%s%s", prov, G_ELI_SUFFIX); 354 return (g_get_sectorsize(name) > 0); 355} 356 357static int 358eli_genkey_files(struct gctl_req *req, bool new, const char *type, 359 struct hmac_ctx *ctxp, char *passbuf, size_t passbufsize) 360{ 361 char *p, buf[MAXPHYS], argname[16]; 362 const char *file; 363 int error, fd, i; 364 ssize_t done; 365 366 assert((strcmp(type, "keyfile") == 0 && ctxp != NULL && 367 passbuf == NULL && passbufsize == 0) || 368 (strcmp(type, "passfile") == 0 && ctxp == NULL && 369 passbuf != NULL && passbufsize > 0)); 370 assert(strcmp(type, "keyfile") == 0 || passbuf[0] == '\0'); 371 372 for (i = 0; ; i++) { 373 snprintf(argname, sizeof(argname), "%s%s%d", 374 new ? "new" : "", type, i); 375 376 /* No more {key,pass}files? */ 377 if (!gctl_has_param(req, argname)) 378 return (i); 379 380 file = gctl_get_ascii(req, "%s", argname); 381 assert(file != NULL); 382 383 if (strcmp(file, "-") == 0) 384 fd = STDIN_FILENO; 385 else { 386 fd = open(file, O_RDONLY); 387 if (fd == -1) { 388 gctl_error(req, "Cannot open %s %s: %s.", 389 type, file, strerror(errno)); 390 return (-1); 391 } 392 } 393 if (strcmp(type, "keyfile") == 0) { 394 while ((done = read(fd, buf, sizeof(buf))) > 0) 395 g_eli_crypto_hmac_update(ctxp, buf, done); 396 } else /* if (strcmp(type, "passfile") == 0) */ { 397 while ((done = read(fd, buf, sizeof(buf) - 1)) > 0) { 398 buf[done] = '\0'; 399 p = strchr(buf, '\n'); 400 if (p != NULL) { 401 *p = '\0'; 402 done = p - buf; 403 } 404 if (strlcat(passbuf, buf, passbufsize) >= 405 passbufsize) { 406 gctl_error(req, 407 "Passphrase in %s too long.", file); 408 bzero(buf, sizeof(buf)); 409 return (-1); 410 } 411 if (p != NULL) 412 break; 413 } 414 } 415 error = errno; 416 if (strcmp(file, "-") != 0) 417 close(fd); 418 bzero(buf, sizeof(buf)); 419 if (done == -1) { 420 gctl_error(req, "Cannot read %s %s: %s.", 421 type, file, strerror(error)); 422 return (-1); 423 } 424 } 425 /* NOTREACHED */ 426} 427 428static int 429eli_genkey_passphrase_prompt(struct gctl_req *req, bool new, char *passbuf, 430 size_t passbufsize) 431{ 432 char *p; 433 434 for (;;) { 435 p = readpassphrase( 436 new ? "Enter new passphrase:" : "Enter passphrase:", 437 passbuf, passbufsize, RPP_ECHO_OFF | RPP_REQUIRE_TTY); 438 if (p == NULL) { 439 bzero(passbuf, passbufsize); 440 gctl_error(req, "Cannot read passphrase: %s.", 441 strerror(errno)); 442 return (-1); 443 } 444 445 if (new) { 446 char tmpbuf[BUFSIZ]; 447 448 p = readpassphrase("Reenter new passphrase: ", 449 tmpbuf, sizeof(tmpbuf), 450 RPP_ECHO_OFF | RPP_REQUIRE_TTY); 451 if (p == NULL) { 452 bzero(passbuf, passbufsize); 453 gctl_error(req, 454 "Cannot read passphrase: %s.", 455 strerror(errno)); 456 return (-1); 457 } 458 459 if (strcmp(passbuf, tmpbuf) != 0) { 460 bzero(passbuf, passbufsize); 461 fprintf(stderr, "They didn't match.\n"); 462 continue; 463 } 464 bzero(tmpbuf, sizeof(tmpbuf)); 465 } 466 return (0); 467 } 468 /* NOTREACHED */ 469} 470 471static int 472eli_genkey_passphrase(struct gctl_req *req, struct g_eli_metadata *md, bool new, 473 struct hmac_ctx *ctxp) 474{ 475 char passbuf[MAXPHYS]; 476 bool nopassphrase; 477 int nfiles; 478 479 nopassphrase = 480 gctl_get_int(req, new ? "nonewpassphrase" : "nopassphrase"); 481 if (nopassphrase) { 482 if (gctl_has_param(req, new ? "newpassfile0" : "passfile0")) { 483 gctl_error(req, 484 "Options -%c and -%c are mutually exclusive.", 485 new ? 'J' : 'j', new ? 'P' : 'p'); 486 return (-1); 487 } 488 return (0); 489 } 490 491 if (!new && md->md_iterations == -1) { 492 gctl_error(req, "Missing -p flag."); 493 return (-1); 494 } 495 passbuf[0] = '\0'; 496 nfiles = eli_genkey_files(req, new, "passfile", NULL, passbuf, 497 sizeof(passbuf)); 498 if (nfiles == -1) 499 return (-1); 500 else if (nfiles == 0) { 501 if (eli_genkey_passphrase_prompt(req, new, passbuf, 502 sizeof(passbuf)) == -1) { 503 return (-1); 504 } 505 } 506 /* 507 * Field md_iterations equal to -1 means "choose some sane 508 * value for me". 509 */ 510 if (md->md_iterations == -1) { 511 assert(new); 512 if (verbose) 513 printf("Calculating number of iterations...\n"); 514 md->md_iterations = pkcs5v2_calculate(2000000); 515 assert(md->md_iterations > 0); 516 if (verbose) { 517 printf("Done, using %d iterations.\n", 518 md->md_iterations); 519 } 520 } 521 /* 522 * If md_iterations is equal to 0, user doesn't want PKCS#5v2. 523 */ 524 if (md->md_iterations == 0) { 525 g_eli_crypto_hmac_update(ctxp, md->md_salt, 526 sizeof(md->md_salt)); 527 g_eli_crypto_hmac_update(ctxp, passbuf, strlen(passbuf)); 528 } else /* if (md->md_iterations > 0) */ { 529 unsigned char dkey[G_ELI_USERKEYLEN]; 530 531 pkcs5v2_genkey(dkey, sizeof(dkey), md->md_salt, 532 sizeof(md->md_salt), passbuf, md->md_iterations); 533 g_eli_crypto_hmac_update(ctxp, dkey, sizeof(dkey)); 534 bzero(dkey, sizeof(dkey)); 535 } 536 bzero(passbuf, sizeof(passbuf)); 537 538 return (0); 539} 540 541static unsigned char * 542eli_genkey(struct gctl_req *req, struct g_eli_metadata *md, unsigned char *key, 543 bool new) 544{ 545 struct hmac_ctx ctx; 546 bool nopassphrase; 547 int nfiles; 548 549 nopassphrase = 550 gctl_get_int(req, new ? "nonewpassphrase" : "nopassphrase"); 551 552 g_eli_crypto_hmac_init(&ctx, NULL, 0); 553 554 nfiles = eli_genkey_files(req, new, "keyfile", &ctx, NULL, 0); 555 if (nfiles == -1) 556 return (NULL); 557 else if (nfiles == 0 && nopassphrase) { 558 gctl_error(req, "No key components given."); 559 return (NULL); 560 } 561 562 if (eli_genkey_passphrase(req, md, new, &ctx) == -1) 563 return (NULL); 564 565 g_eli_crypto_hmac_final(&ctx, key, 0); 566 567 return (key); 568} 569 570static int 571eli_metadata_read(struct gctl_req *req, const char *prov, 572 struct g_eli_metadata *md) 573{ 574 unsigned char sector[sizeof(struct g_eli_metadata)]; 575 int error; 576 577 if (g_get_sectorsize(prov) == 0) { 578 int fd; 579 580 /* This is a file probably. */ 581 fd = open(prov, O_RDONLY); 582 if (fd == -1) { 583 gctl_error(req, "Cannot open %s: %s.", prov, 584 strerror(errno)); 585 return (-1); 586 } 587 if (read(fd, sector, sizeof(sector)) != sizeof(sector)) { 588 gctl_error(req, "Cannot read metadata from %s: %s.", 589 prov, strerror(errno)); 590 close(fd); 591 return (-1); 592 } 593 close(fd); 594 } else { 595 /* This is a GEOM provider. */ 596 error = g_metadata_read(prov, sector, sizeof(sector), 597 G_ELI_MAGIC); 598 if (error != 0) { 599 gctl_error(req, "Cannot read metadata from %s: %s.", 600 prov, strerror(error)); 601 return (-1); 602 } 603 } 604 error = eli_metadata_decode(sector, md); 605 switch (error) { 606 case 0: 607 break; 608 case EOPNOTSUPP: 609 gctl_error(req, 610 "Provider's %s metadata version %u is too new.\n" 611 "geli: The highest supported version is %u.", 612 prov, (unsigned int)md->md_version, G_ELI_VERSION); 613 return (-1); 614 case EINVAL: 615 gctl_error(req, "Inconsistent provider's %s metadata.", prov); 616 return (-1); 617 default: 618 gctl_error(req, 619 "Unexpected error while decoding provider's %s metadata: %s.", 620 prov, strerror(error)); 621 return (-1); 622 } 623 return (0); 624} 625 626static int 627eli_metadata_store(struct gctl_req *req, const char *prov, 628 struct g_eli_metadata *md) 629{ 630 unsigned char sector[sizeof(struct g_eli_metadata)]; 631 int error; 632 633 eli_metadata_encode(md, sector); 634 if (g_get_sectorsize(prov) == 0) { 635 int fd; 636 637 /* This is a file probably. */ 638 fd = open(prov, O_WRONLY | O_TRUNC); 639 if (fd == -1) { 640 gctl_error(req, "Cannot open %s: %s.", prov, 641 strerror(errno)); 642 bzero(sector, sizeof(sector)); 643 return (-1); 644 } 645 if (write(fd, sector, sizeof(sector)) != sizeof(sector)) { 646 gctl_error(req, "Cannot write metadata to %s: %s.", 647 prov, strerror(errno)); 648 bzero(sector, sizeof(sector)); 649 close(fd); 650 return (-1); 651 } 652 close(fd); 653 } else { 654 /* This is a GEOM provider. */ 655 error = g_metadata_store(prov, sector, sizeof(sector)); 656 if (error != 0) { 657 gctl_error(req, "Cannot write metadata to %s: %s.", 658 prov, strerror(errno)); 659 bzero(sector, sizeof(sector)); 660 return (-1); 661 } 662 } 663 bzero(sector, sizeof(sector)); 664 return (0); 665} 666 667static void 668eli_init(struct gctl_req *req) 669{ 670 struct g_eli_metadata md; 671 unsigned char sector[sizeof(struct g_eli_metadata)]; 672 unsigned char key[G_ELI_USERKEYLEN]; 673 char backfile[MAXPATHLEN]; 674 const char *str, *prov; 675 unsigned secsize; 676 off_t mediasize; 677 intmax_t val; 678 int error, nargs; 679 680 nargs = gctl_get_int(req, "nargs"); 681 if (nargs != 1) { 682 gctl_error(req, "Invalid number of arguments."); 683 return; 684 } 685 prov = gctl_get_ascii(req, "arg0"); 686 mediasize = g_get_mediasize(prov); 687 secsize = g_get_sectorsize(prov); 688 if (mediasize == 0 || secsize == 0) { 689 gctl_error(req, "Cannot get informations about %s: %s.", prov, 690 strerror(errno)); 691 return; 692 } 693 694 bzero(&md, sizeof(md)); 695 strlcpy(md.md_magic, G_ELI_MAGIC, sizeof(md.md_magic)); 696 md.md_version = G_ELI_VERSION; 697 md.md_flags = 0; 698 if (gctl_get_int(req, "boot")) 699 md.md_flags |= G_ELI_FLAG_BOOT; 700 md.md_ealgo = CRYPTO_ALGORITHM_MIN - 1; 701 str = gctl_get_ascii(req, "aalgo"); 702 if (*str != '\0') { 703 md.md_aalgo = g_eli_str2aalgo(str); 704 if (md.md_aalgo >= CRYPTO_ALGORITHM_MIN && 705 md.md_aalgo <= CRYPTO_ALGORITHM_MAX) { 706 md.md_flags |= G_ELI_FLAG_AUTH; 707 } else { 708 /* 709 * For backward compatibility, check if the -a option 710 * was used to provide encryption algorithm. 711 */ 712 md.md_ealgo = g_eli_str2ealgo(str); 713 if (md.md_ealgo < CRYPTO_ALGORITHM_MIN || 714 md.md_ealgo > CRYPTO_ALGORITHM_MAX) { 715 gctl_error(req, 716 "Invalid authentication algorithm."); 717 return; 718 } else { 719 fprintf(stderr, "warning: The -e option, not " 720 "the -a option is now used to specify " 721 "encryption algorithm to use.\n"); 722 } 723 } 724 } 725 if (md.md_ealgo < CRYPTO_ALGORITHM_MIN || 726 md.md_ealgo > CRYPTO_ALGORITHM_MAX) { 727 str = gctl_get_ascii(req, "ealgo"); 728 md.md_ealgo = g_eli_str2ealgo(str); 729 if (md.md_ealgo < CRYPTO_ALGORITHM_MIN || 730 md.md_ealgo > CRYPTO_ALGORITHM_MAX) { 731 gctl_error(req, "Invalid encryption algorithm."); 732 return; 733 } 734 } 735 val = gctl_get_intmax(req, "keylen"); 736 md.md_keylen = val; 737 md.md_keylen = g_eli_keylen(md.md_ealgo, md.md_keylen); 738 if (md.md_keylen == 0) { 739 gctl_error(req, "Invalid key length."); 740 return; 741 } 742 md.md_provsize = mediasize; 743 744 val = gctl_get_intmax(req, "iterations"); 745 if (val != -1) { 746 int nonewpassphrase; 747 748 /* 749 * Don't allow to set iterations when there will be no 750 * passphrase. 751 */ 752 nonewpassphrase = gctl_get_int(req, "nonewpassphrase"); 753 if (nonewpassphrase) { 754 gctl_error(req, 755 "Options -i and -P are mutually exclusive."); 756 return; 757 } 758 } 759 md.md_iterations = val; 760 761 val = gctl_get_intmax(req, "sectorsize"); 762 if (val == 0) 763 md.md_sectorsize = secsize; 764 else { 765 if (val < 0 || (val % secsize) != 0) { 766 gctl_error(req, "Invalid sector size."); 767 return; 768 } 769 if (val > sysconf(_SC_PAGE_SIZE)) { 770 fprintf(stderr, 771 "warning: Using sectorsize bigger than the page size!\n"); 772 } 773 md.md_sectorsize = val; 774 } 775 776 md.md_keys = 0x01; 777 arc4rand(md.md_salt, sizeof(md.md_salt)); 778 arc4rand(md.md_mkeys, sizeof(md.md_mkeys)); 779 780 /* Generate user key. */ 781 if (eli_genkey(req, &md, key, true) == NULL) { 782 bzero(key, sizeof(key)); 783 bzero(&md, sizeof(md)); 784 return; 785 } 786 787 /* Encrypt the first and the only Master Key. */ 788 error = g_eli_mkey_encrypt(md.md_ealgo, key, md.md_keylen, md.md_mkeys); 789 bzero(key, sizeof(key)); 790 if (error != 0) { 791 bzero(&md, sizeof(md)); 792 gctl_error(req, "Cannot encrypt Master Key: %s.", 793 strerror(error)); 794 return; 795 } 796 797 eli_metadata_encode(&md, sector); 798 bzero(&md, sizeof(md)); 799 error = g_metadata_store(prov, sector, sizeof(sector)); 800 bzero(sector, sizeof(sector)); 801 if (error != 0) { 802 gctl_error(req, "Cannot store metadata on %s: %s.", prov, 803 strerror(error)); 804 return; 805 } 806 if (verbose) 807 printf("Metadata value stored on %s.\n", prov); 808 /* Backup metadata to a file. */ 809 str = gctl_get_ascii(req, "backupfile"); 810 if (str[0] != '\0') { 811 /* Backupfile given be the user, just copy it. */ 812 strlcpy(backfile, str, sizeof(backfile)); 813 } else { 814 /* Generate file name automatically. */ 815 const char *p = prov; 816 unsigned int i; 817 818 if (strncmp(p, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) 819 p += sizeof(_PATH_DEV) - 1; 820 snprintf(backfile, sizeof(backfile), "%s%s.eli", 821 GELI_BACKUP_DIR, p); 822 /* Replace all / with _. */ 823 for (i = strlen(GELI_BACKUP_DIR); backfile[i] != '\0'; i++) { 824 if (backfile[i] == '/') 825 backfile[i] = '_'; 826 } 827 } 828 if (strcmp(backfile, "none") != 0 && 829 eli_backup_create(req, prov, backfile) == 0) { 830 printf("\nMetadata backup can be found in %s and\n", backfile); 831 printf("can be restored with the following command:\n"); 832 printf("\n\t# geli restore %s %s\n\n", backfile, prov); 833 } 834} 835 836static void 837eli_attach(struct gctl_req *req) 838{ 839 struct g_eli_metadata md; 840 unsigned char key[G_ELI_USERKEYLEN]; 841 const char *prov; 842 off_t mediasize; 843 int nargs; 844 845 nargs = gctl_get_int(req, "nargs"); 846 if (nargs != 1) { 847 gctl_error(req, "Invalid number of arguments."); 848 return; 849 } 850 prov = gctl_get_ascii(req, "arg0"); 851 852 if (eli_metadata_read(req, prov, &md) == -1) 853 return; 854 855 mediasize = g_get_mediasize(prov); 856 if (md.md_provsize != (uint64_t)mediasize) { 857 gctl_error(req, "Provider size mismatch."); 858 return; 859 } 860 861 if (eli_genkey(req, &md, key, false) == NULL) { 862 bzero(key, sizeof(key)); 863 return; 864 } 865 866 gctl_ro_param(req, "key", sizeof(key), key); 867 if (gctl_issue(req) == NULL) { 868 if (verbose) 869 printf("Attached to %s.\n", prov); 870 } 871 bzero(key, sizeof(key)); 872} 873 874static void 875eli_configure_detached(struct gctl_req *req, const char *prov, bool boot) 876{ 877 struct g_eli_metadata md; 878 879 if (eli_metadata_read(req, prov, &md) == -1) 880 return; 881 882 if (boot && (md.md_flags & G_ELI_FLAG_BOOT)) { 883 if (verbose) 884 printf("BOOT flag already configured for %s.\n", prov); 885 } else if (!boot && !(md.md_flags & G_ELI_FLAG_BOOT)) { 886 if (verbose) 887 printf("BOOT flag not configured for %s.\n", prov); 888 } else { 889 if (boot) 890 md.md_flags |= G_ELI_FLAG_BOOT; 891 else 892 md.md_flags &= ~G_ELI_FLAG_BOOT; 893 eli_metadata_store(req, prov, &md); 894 } 895 bzero(&md, sizeof(md)); 896} 897 898static void 899eli_configure(struct gctl_req *req) 900{ 901 const char *prov; 902 bool boot, noboot; 903 int i, nargs; 904 905 nargs = gctl_get_int(req, "nargs"); 906 if (nargs == 0) { 907 gctl_error(req, "Too few arguments."); 908 return; 909 } 910 911 boot = gctl_get_int(req, "boot"); 912 noboot = gctl_get_int(req, "noboot"); 913 914 if (boot && noboot) { 915 gctl_error(req, "Options -b and -B are mutually exclusive."); 916 return; 917 } 918 if (!boot && !noboot) { 919 gctl_error(req, "No option given."); 920 return; 921 } 922 923 /* First attached providers. */ 924 gctl_issue(req); 925 /* Now the rest. */ 926 for (i = 0; i < nargs; i++) { 927 prov = gctl_get_ascii(req, "arg%d", i); 928 if (!eli_is_attached(prov)) 929 eli_configure_detached(req, prov, boot); 930 } 931} 932 933static void 934eli_setkey_attached(struct gctl_req *req, struct g_eli_metadata *md) 935{ 936 unsigned char key[G_ELI_USERKEYLEN]; 937 intmax_t val, old = 0; 938 int error; 939 940 val = gctl_get_intmax(req, "iterations"); 941 /* Check if iterations number should be changed. */ 942 if (val != -1) 943 md->md_iterations = val; 944 else 945 old = md->md_iterations; 946 947 /* Generate key for Master Key encryption. */ 948 if (eli_genkey(req, md, key, true) == NULL) { 949 bzero(key, sizeof(key)); 950 return; 951 } 952 /* 953 * If number of iterations has changed, but wasn't given as a 954 * command-line argument, update the request. 955 */ 956 if (val == -1 && md->md_iterations != old) { 957 error = gctl_change_param(req, "iterations", sizeof(intmax_t), 958 &md->md_iterations); 959 assert(error == 0); 960 } 961 962 gctl_ro_param(req, "key", sizeof(key), key); 963 gctl_issue(req); 964 bzero(key, sizeof(key)); 965} 966 967static void 968eli_setkey_detached(struct gctl_req *req, const char *prov, 969 struct g_eli_metadata *md) 970{ 971 unsigned char key[G_ELI_USERKEYLEN], mkey[G_ELI_DATAIVKEYLEN]; 972 unsigned char *mkeydst; 973 unsigned int nkey; 974 intmax_t val; 975 int error; 976 977 if (md->md_keys == 0) { 978 gctl_error(req, "No valid keys on %s.", prov); 979 return; 980 } 981 982 /* Generate key for Master Key decryption. */ 983 if (eli_genkey(req, md, key, false) == NULL) { 984 bzero(key, sizeof(key)); 985 return; 986 } 987 988 /* Decrypt Master Key. */ 989 error = g_eli_mkey_decrypt(md, key, mkey, &nkey); 990 bzero(key, sizeof(key)); 991 if (error != 0) { 992 bzero(md, sizeof(*md)); 993 if (error == -1) 994 gctl_error(req, "Wrong key for %s.", prov); 995 else /* if (error > 0) */ { 996 gctl_error(req, "Cannot decrypt Master Key: %s.", 997 strerror(error)); 998 } 999 return; 1000 } 1001 if (verbose) 1002 printf("Decrypted Master Key %u.\n", nkey); 1003 1004 val = gctl_get_intmax(req, "keyno"); 1005 if (val != -1) 1006 nkey = val; 1007#if 0 1008 else 1009 ; /* Use the key number which was found during decryption. */ 1010#endif 1011 if (nkey >= G_ELI_MAXMKEYS) { 1012 gctl_error(req, "Invalid '%s' argument.", "keyno"); 1013 return; 1014 } 1015 1016 val = gctl_get_intmax(req, "iterations"); 1017 /* Check if iterations number should and can be changed. */ 1018 if (val != -1) { 1019 if (bitcount32(md->md_keys) != 1) { 1020 gctl_error(req, "To be able to use '-i' option, only " 1021 "one key can be defined."); 1022 return; 1023 } 1024 if (md->md_keys != (1 << nkey)) { 1025 gctl_error(req, "Only already defined key can be " 1026 "changed when '-i' option is used."); 1027 return; 1028 } 1029 md->md_iterations = val; 1030 } 1031 1032 mkeydst = md->md_mkeys + nkey * G_ELI_MKEYLEN; 1033 md->md_keys |= (1 << nkey); 1034 1035 bcopy(mkey, mkeydst, sizeof(mkey)); 1036 bzero(mkey, sizeof(mkey)); 1037 1038 /* Generate key for Master Key encryption. */ 1039 if (eli_genkey(req, md, key, true) == NULL) { 1040 bzero(key, sizeof(key)); 1041 bzero(md, sizeof(*md)); 1042 return; 1043 } 1044 1045 /* Encrypt the Master-Key with the new key. */ 1046 error = g_eli_mkey_encrypt(md->md_ealgo, key, md->md_keylen, mkeydst); 1047 bzero(key, sizeof(key)); 1048 if (error != 0) { 1049 bzero(md, sizeof(*md)); 1050 gctl_error(req, "Cannot encrypt Master Key: %s.", 1051 strerror(error)); 1052 return; 1053 } 1054 1055 /* Store metadata with fresh key. */ 1056 eli_metadata_store(req, prov, md); 1057 bzero(md, sizeof(*md)); 1058} 1059 1060static void 1061eli_setkey(struct gctl_req *req) 1062{ 1063 struct g_eli_metadata md; 1064 const char *prov; 1065 int nargs; 1066 1067 nargs = gctl_get_int(req, "nargs"); 1068 if (nargs != 1) { 1069 gctl_error(req, "Invalid number of arguments."); 1070 return; 1071 } 1072 prov = gctl_get_ascii(req, "arg0"); 1073 1074 if (eli_metadata_read(req, prov, &md) == -1) 1075 return; 1076 1077 if (eli_is_attached(prov)) 1078 eli_setkey_attached(req, &md); 1079 else 1080 eli_setkey_detached(req, prov, &md); 1081 1082 if (req->error == NULL || req->error[0] == '\0') { 1083 printf("Note, that the master key encrypted with old keys " 1084 "and/or passphrase may still exists in a metadata backup " 1085 "file.\n"); 1086 } 1087} 1088 1089static void 1090eli_delkey_attached(struct gctl_req *req, const char *prov __unused) 1091{ 1092 1093 gctl_issue(req); 1094} 1095 1096static void 1097eli_delkey_detached(struct gctl_req *req, const char *prov) 1098{ 1099 struct g_eli_metadata md; 1100 unsigned char *mkeydst; 1101 unsigned int nkey; 1102 intmax_t val; 1103 bool all, force; 1104 1105 if (eli_metadata_read(req, prov, &md) == -1) 1106 return; 1107 1108 all = gctl_get_int(req, "all"); 1109 if (all) 1110 arc4rand(md.md_mkeys, sizeof(md.md_mkeys)); 1111 else { 1112 force = gctl_get_int(req, "force"); 1113 val = gctl_get_intmax(req, "keyno"); 1114 if (val == -1) { 1115 gctl_error(req, "Key number has to be specified."); 1116 return; 1117 } 1118 nkey = val; 1119 if (nkey >= G_ELI_MAXMKEYS) { 1120 gctl_error(req, "Invalid '%s' argument.", "keyno"); 1121 return; 1122 } 1123 if (!(md.md_keys & (1 << nkey)) && !force) { 1124 gctl_error(req, "Master Key %u is not set.", nkey); 1125 return; 1126 } 1127 md.md_keys &= ~(1 << nkey); 1128 if (md.md_keys == 0 && !force) { 1129 gctl_error(req, "This is the last Master Key. Use '-f' " 1130 "option if you really want to remove it."); 1131 return; 1132 } 1133 mkeydst = md.md_mkeys + nkey * G_ELI_MKEYLEN; 1134 arc4rand(mkeydst, G_ELI_MKEYLEN); 1135 } 1136 1137 eli_metadata_store(req, prov, &md); 1138 bzero(&md, sizeof(md)); 1139} 1140 1141static void 1142eli_delkey(struct gctl_req *req) 1143{ 1144 const char *prov; 1145 int nargs; 1146 1147 nargs = gctl_get_int(req, "nargs"); 1148 if (nargs != 1) { 1149 gctl_error(req, "Invalid number of arguments."); 1150 return; 1151 } 1152 prov = gctl_get_ascii(req, "arg0"); 1153 1154 if (eli_is_attached(prov)) 1155 eli_delkey_attached(req, prov); 1156 else 1157 eli_delkey_detached(req, prov); 1158} 1159 1160static void 1161eli_resume(struct gctl_req *req) 1162{ 1163 struct g_eli_metadata md; 1164 unsigned char key[G_ELI_USERKEYLEN]; 1165 const char *prov; 1166 off_t mediasize; 1167 int nargs; 1168 1169 nargs = gctl_get_int(req, "nargs"); 1170 if (nargs != 1) { 1171 gctl_error(req, "Invalid number of arguments."); 1172 return; 1173 } 1174 prov = gctl_get_ascii(req, "arg0"); 1175 1176 if (eli_metadata_read(req, prov, &md) == -1) 1177 return; 1178 1179 mediasize = g_get_mediasize(prov); 1180 if (md.md_provsize != (uint64_t)mediasize) { 1181 gctl_error(req, "Provider size mismatch."); 1182 return; 1183 } 1184 1185 if (eli_genkey(req, &md, key, false) == NULL) { 1186 bzero(key, sizeof(key)); 1187 return; 1188 } 1189 1190 gctl_ro_param(req, "key", sizeof(key), key); 1191 if (gctl_issue(req) == NULL) { 1192 if (verbose) 1193 printf("Resumed %s.\n", prov); 1194 } 1195 bzero(key, sizeof(key)); 1196} 1197 1198static int 1199eli_trash_metadata(struct gctl_req *req, const char *prov, int fd, off_t offset) 1200{ 1201 unsigned int overwrites; 1202 unsigned char *sector; 1203 ssize_t size; 1204 int error; 1205 1206 size = sizeof(overwrites); 1207 if (sysctlbyname("kern.geom.eli.overwrites", &overwrites, &size, 1208 NULL, 0) == -1 || overwrites == 0) { 1209 overwrites = G_ELI_OVERWRITES; 1210 } 1211 1212 size = g_sectorsize(fd); 1213 if (size <= 0) { 1214 gctl_error(req, "Cannot obtain provider sector size %s: %s.", 1215 prov, strerror(errno)); 1216 return (-1); 1217 } 1218 sector = malloc(size); 1219 if (sector == NULL) { 1220 gctl_error(req, "Cannot allocate %zd bytes of memory.", size); 1221 return (-1); 1222 } 1223 1224 error = 0; 1225 do { 1226 arc4rand(sector, size); 1227 if (pwrite(fd, sector, size, offset) != size) { 1228 if (error == 0) 1229 error = errno; 1230 } 1231 (void)g_flush(fd); 1232 } while (--overwrites > 0); 1233 if (error != 0) { 1234 gctl_error(req, "Cannot trash metadata on provider %s: %s.", 1235 prov, strerror(error)); 1236 return (-1); 1237 } 1238 return (0); 1239} 1240 1241static void 1242eli_kill_detached(struct gctl_req *req, const char *prov) 1243{ 1244 off_t offset; 1245 int fd; 1246 1247 /* 1248 * NOTE: Maybe we should verify if this is geli provider first, 1249 * but 'kill' command is quite critical so better don't waste 1250 * the time. 1251 */ 1252#if 0 1253 error = g_metadata_read(prov, (unsigned char *)&md, sizeof(md), 1254 G_ELI_MAGIC); 1255 if (error != 0) { 1256 gctl_error(req, "Cannot read metadata from %s: %s.", prov, 1257 strerror(error)); 1258 return; 1259 } 1260#endif 1261 1262 fd = g_open(prov, 1); 1263 if (fd == -1) { 1264 gctl_error(req, "Cannot open provider %s: %s.", prov, 1265 strerror(errno)); 1266 return; 1267 } 1268 offset = g_mediasize(fd) - g_sectorsize(fd); 1269 if (offset <= 0) { 1270 gctl_error(req, 1271 "Cannot obtain media size or sector size for provider %s: %s.", 1272 prov, strerror(errno)); 1273 (void)g_close(fd); 1274 return; 1275 } 1276 (void)eli_trash_metadata(req, prov, fd, offset); 1277 (void)g_close(fd); 1278} 1279 1280static void 1281eli_kill(struct gctl_req *req) 1282{ 1283 const char *prov; 1284 int i, nargs, all; 1285 1286 nargs = gctl_get_int(req, "nargs"); 1287 all = gctl_get_int(req, "all"); 1288 if (!all && nargs == 0) { 1289 gctl_error(req, "Too few arguments."); 1290 return; 1291 } 1292 /* 1293 * How '-a' option combine with a list of providers: 1294 * Delete Master Keys from all attached providers: 1295 * geli kill -a 1296 * Delete Master Keys from all attached providers and from 1297 * detached da0 and da1: 1298 * geli kill -a da0 da1 1299 * Delete Master Keys from (attached or detached) da0 and da1: 1300 * geli kill da0 da1 1301 */ 1302 1303 /* First detached providers. */ 1304 for (i = 0; i < nargs; i++) { 1305 prov = gctl_get_ascii(req, "arg%d", i); 1306 if (!eli_is_attached(prov)) 1307 eli_kill_detached(req, prov); 1308 } 1309 /* Now attached providers. */ 1310 gctl_issue(req); 1311} 1312 1313static int 1314eli_backup_create(struct gctl_req *req, const char *prov, const char *file) 1315{ 1316 unsigned char *sector; 1317 ssize_t secsize; 1318 int error, filefd, ret; 1319 1320 ret = -1; 1321 filefd = -1; 1322 sector = NULL; 1323 secsize = 0; 1324 1325 secsize = g_get_sectorsize(prov); 1326 if (secsize == 0) { 1327 gctl_error(req, "Cannot get informations about %s: %s.", prov, 1328 strerror(errno)); 1329 goto out; 1330 } 1331 sector = malloc(secsize); 1332 if (sector == NULL) { 1333 gctl_error(req, "Cannot allocate memory."); 1334 goto out; 1335 } 1336 /* Read metadata from the provider. */ 1337 error = g_metadata_read(prov, sector, secsize, G_ELI_MAGIC); 1338 if (error != 0) { 1339 gctl_error(req, "Unable to read metadata from %s: %s.", prov, 1340 strerror(error)); 1341 goto out; 1342 } 1343 1344 filefd = open(file, O_WRONLY | O_TRUNC | O_CREAT, 0600); 1345 if (filefd == -1) { 1346 gctl_error(req, "Unable to open %s: %s.", file, 1347 strerror(errno)); 1348 goto out; 1349 } 1350 /* Write metadata to the destination file. */ 1351 if (write(filefd, sector, secsize) != secsize) { 1352 gctl_error(req, "Unable to write to %s: %s.", file, 1353 strerror(errno)); 1354 (void)close(filefd); 1355 (void)unlink(file); 1356 goto out; 1357 } 1358 (void)fsync(filefd); 1359 (void)close(filefd); 1360 /* Success. */ 1361 ret = 0; 1362out: 1363 if (sector != NULL) { 1364 bzero(sector, secsize); 1365 free(sector); 1366 } 1367 return (ret); 1368} 1369 1370static void 1371eli_backup(struct gctl_req *req) 1372{ 1373 const char *file, *prov; 1374 int nargs; 1375 1376 nargs = gctl_get_int(req, "nargs"); 1377 if (nargs != 2) { 1378 gctl_error(req, "Invalid number of arguments."); 1379 return; 1380 } 1381 prov = gctl_get_ascii(req, "arg0"); 1382 file = gctl_get_ascii(req, "arg1"); 1383 1384 eli_backup_create(req, prov, file); 1385} 1386 1387static void 1388eli_restore(struct gctl_req *req) 1389{ 1390 struct g_eli_metadata md; 1391 const char *file, *prov; 1392 off_t mediasize; 1393 int nargs; 1394 1395 nargs = gctl_get_int(req, "nargs"); 1396 if (nargs != 2) { 1397 gctl_error(req, "Invalid number of arguments."); 1398 return; 1399 } 1400 file = gctl_get_ascii(req, "arg0"); 1401 prov = gctl_get_ascii(req, "arg1"); 1402 1403 /* Read metadata from the backup file. */ 1404 if (eli_metadata_read(req, file, &md) == -1) 1405 return; 1406 /* Obtain provider's mediasize. */ 1407 mediasize = g_get_mediasize(prov); 1408 if (mediasize == 0) { 1409 gctl_error(req, "Cannot get informations about %s: %s.", prov, 1410 strerror(errno)); 1411 return; 1412 } 1413 /* Check if the provider size has changed since we did the backup. */ 1414 if (md.md_provsize != (uint64_t)mediasize) { 1415 if (gctl_get_int(req, "force")) { 1416 md.md_provsize = mediasize; 1417 } else { 1418 gctl_error(req, "Provider size mismatch: " 1419 "wrong backup file?"); 1420 return; 1421 } 1422 } 1423 /* Write metadata to the provider. */ 1424 (void)eli_metadata_store(req, prov, &md); 1425} 1426 1427static void 1428eli_resize(struct gctl_req *req) 1429{ 1430 struct g_eli_metadata md; 1431 const char *prov; 1432 unsigned char *sector; 1433 ssize_t secsize; 1434 off_t mediasize, oldsize; 1435 int error, nargs, provfd; 1436 1437 nargs = gctl_get_int(req, "nargs"); 1438 if (nargs != 1) { 1439 gctl_error(req, "Invalid number of arguments."); 1440 return; 1441 } 1442 prov = gctl_get_ascii(req, "arg0"); 1443 1444 provfd = -1; 1445 sector = NULL; 1446 secsize = 0; 1447 1448 provfd = g_open(prov, 1); 1449 if (provfd == -1) { 1450 gctl_error(req, "Cannot open %s: %s.", prov, strerror(errno)); 1451 goto out; 1452 } 1453 1454 mediasize = g_mediasize(provfd); 1455 secsize = g_sectorsize(provfd); 1456 if (mediasize == -1 || secsize == -1) { 1457 gctl_error(req, "Cannot get information about %s: %s.", prov, 1458 strerror(errno)); 1459 goto out; 1460 } 1461 1462 sector = malloc(secsize); 1463 if (sector == NULL) { 1464 gctl_error(req, "Cannot allocate memory."); 1465 goto out; 1466 } 1467 1468 oldsize = gctl_get_intmax(req, "oldsize"); 1469 if (oldsize < 0 || oldsize > mediasize) { 1470 gctl_error(req, "Invalid oldsize: Out of range."); 1471 goto out; 1472 } 1473 if (oldsize == mediasize) { 1474 gctl_error(req, "Size hasn't changed."); 1475 goto out; 1476 } 1477 1478 /* Read metadata from the 'oldsize' offset. */ 1479 if (pread(provfd, sector, secsize, oldsize - secsize) != secsize) { 1480 gctl_error(req, "Cannot read old metadata: %s.", 1481 strerror(errno)); 1482 goto out; 1483 } 1484 1485 /* Check if this sector contains geli metadata. */ 1486 error = eli_metadata_decode(sector, &md); 1487 switch (error) { 1488 case 0: 1489 break; 1490 case EOPNOTSUPP: 1491 gctl_error(req, 1492 "Provider's %s metadata version %u is too new.\n" 1493 "geli: The highest supported version is %u.", 1494 prov, (unsigned int)md.md_version, G_ELI_VERSION); 1495 goto out; 1496 case EINVAL: 1497 gctl_error(req, "Inconsistent provider's %s metadata.", prov); 1498 goto out; 1499 default: 1500 gctl_error(req, 1501 "Unexpected error while decoding provider's %s metadata: %s.", 1502 prov, strerror(error)); 1503 goto out; 1504 } 1505 1506 /* 1507 * If the old metadata doesn't have a correct provider size, refuse 1508 * to resize. 1509 */ 1510 if (md.md_provsize != (uint64_t)oldsize) { 1511 gctl_error(req, "Provider size mismatch at oldsize."); 1512 goto out; 1513 } 1514 1515 /* 1516 * Update the old metadata with the current provider size and write 1517 * it back to the correct place on the provider. 1518 */ 1519 md.md_provsize = mediasize; 1520 /* Write metadata to the provider. */ 1521 (void)eli_metadata_store(req, prov, &md); 1522 /* Now trash the old metadata. */ 1523 (void)eli_trash_metadata(req, prov, provfd, oldsize - secsize); 1524out: 1525 if (provfd != -1) 1526 (void)g_close(provfd); 1527 if (sector != NULL) { 1528 bzero(sector, secsize); 1529 free(sector); 1530 } 1531} 1532 1533static void 1534eli_version(struct gctl_req *req) 1535{ 1536 struct g_eli_metadata md; 1537 const char *name; 1538 unsigned int version; 1539 int error, i, nargs; 1540 1541 nargs = gctl_get_int(req, "nargs"); 1542 1543 if (nargs == 0) { 1544 unsigned int kernver; 1545 ssize_t size; 1546 1547 size = sizeof(kernver); 1548 if (sysctlbyname("kern.geom.eli.version", &kernver, &size, 1549 NULL, 0) == -1) { 1550 warn("Unable to obtain GELI kernel version"); 1551 } else { 1552 printf("kernel: %u\n", kernver); 1553 } 1554 printf("userland: %u\n", G_ELI_VERSION); 1555 return; 1556 } 1557 1558 for (i = 0; i < nargs; i++) { 1559 name = gctl_get_ascii(req, "arg%d", i); 1560 error = g_metadata_read(name, (unsigned char *)&md, 1561 sizeof(md), G_ELI_MAGIC); 1562 if (error != 0) { 1563 warn("%s: Unable to read metadata: %s.", name, 1564 strerror(error)); 1565 gctl_error(req, "Not fully done."); 1566 continue; 1567 } 1568 version = le32dec(&md.md_version); 1569 printf("%s: %u\n", name, version); 1570 } 1571} 1572 1573static void 1574eli_clear(struct gctl_req *req) 1575{ 1576 const char *name; 1577 int error, i, nargs; 1578 1579 nargs = gctl_get_int(req, "nargs"); 1580 if (nargs < 1) { 1581 gctl_error(req, "Too few arguments."); 1582 return; 1583 } 1584 1585 for (i = 0; i < nargs; i++) { 1586 name = gctl_get_ascii(req, "arg%d", i); 1587 error = g_metadata_clear(name, G_ELI_MAGIC); 1588 if (error != 0) { 1589 fprintf(stderr, "Cannot clear metadata on %s: %s.\n", 1590 name, strerror(error)); 1591 gctl_error(req, "Not fully done."); 1592 continue; 1593 } 1594 if (verbose) 1595 printf("Metadata cleared on %s.\n", name); 1596 } 1597} 1598 1599static void 1600eli_dump(struct gctl_req *req) 1601{ 1602 struct g_eli_metadata md; 1603 const char *name; 1604 int i, nargs; 1605 1606 nargs = gctl_get_int(req, "nargs"); 1607 if (nargs < 1) { 1608 gctl_error(req, "Too few arguments."); 1609 return; 1610 } 1611 1612 for (i = 0; i < nargs; i++) { 1613 name = gctl_get_ascii(req, "arg%d", i); 1614 if (eli_metadata_read(NULL, name, &md) == -1) { 1615 gctl_error(req, "Not fully done."); 1616 continue; 1617 } 1618 printf("Metadata on %s:\n", name); 1619 eli_metadata_dump(&md); 1620 printf("\n"); 1621 } 1622} 1623