1 /* $NetBSD: cgdconfig.c,v 1.4 2002/10/28 05:46:01 elric Exp $ */ 2 3 /*- 4 * Copyright (c) 2002 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Roland C. Dowdeswell. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 #include <sys/cdefs.h> 40 #ifndef lint 41 __COPYRIGHT( 42 "@(#) Copyright (c) 2002\ 43 The NetBSD Foundation, Inc. All rights reserved."); 44 __RCSID("$NetBSD: cgdconfig.c,v 1.4 2002/10/28 05:46:01 elric Exp $"); 45 #endif 46 47 #include <errno.h> 48 #include <fcntl.h> 49 #include <libgen.h> 50 #include <malloc.h> 51 #include <stdio.h> 52 #include <stdlib.h> 53 #include <string.h> 54 #include <unistd.h> 55 #include <util.h> 56 57 #include <sys/ioctl.h> 58 #include <sys/disklabel.h> 59 #include <sys/param.h> 60 61 #include <dev/cgdvar.h> 62 63 #include "params.h" 64 #include "pkcs5_pbkdf2.h" 65 #include "utils.h" 66 67 #define CGDCONFIG_DIR "/etc/cgd" 68 #define CGDCONFIG_CFILE CGDCONFIG_DIR "/cgd.conf" 69 #define DEFAULT_SALTLEN 128 70 71 #define ACTION_CONFIGURE 0x1 /* configure, with paramsfile */ 72 #define ACTION_UNCONFIGURE 0x2 /* unconfigure */ 73 #define ACTION_GENERATE 0x3 /* generate a paramsfile */ 74 #define ACTION_CONFIGALL 0x4 /* configure all from config file */ 75 #define ACTION_UNCONFIGALL 0x5 /* unconfigure all from config file */ 76 #define ACTION_CONFIGSTDIN 0x6 /* configure, key from stdin */ 77 78 /* if nflag is set, do not configure/unconfigure the cgd's */ 79 80 int nflag = 0; 81 82 static int configure(int, char **, struct params *, int); 83 static int configure_stdin(struct params *, int argc, char **); 84 static int generate(struct params *, int, char **, const char *); 85 static int unconfigure(int, char **, struct params *, int); 86 static int do_all(const char *, int, char **, 87 int (*)(int, char **, struct params *, int)); 88 89 #define CONFIG_FLAGS_FROMALL 1 /* called from configure_all() */ 90 #define CONFIG_FLAGS_FROMMAIN 2 /* called from main() */ 91 92 static int configure_params(int, const char *, const char *, 93 struct params *); 94 static void key_print(FILE *, const u_int8_t *, int); 95 static char *getrandbits(int); 96 static int getkey(const char *, struct params *); 97 static int getkeyfrompassphrase(const char *, struct params *); 98 static int getkeyfromfile(FILE *, struct params *); 99 static int opendisk_werror(const char *, char *, int); 100 static int unconfigure_fd(int); 101 static int verify(struct params *, int); 102 103 static void usage(void); 104 105 /* Verbose Framework */ 106 int verbose = 0; 107 108 #define VERBOSE(x,y) if (verbose >= x) y 109 #define VPRINTF(x,y) if (verbose >= x) printf y 110 111 static void 112 usage(void) 113 { 114 115 fprintf(stderr, "usage: %s [-nv] [-V vmeth] cgd dev [paramsfile]\n", 116 getprogname()); 117 fprintf(stderr, " %s -C [-nv] [-f configfile]\n", getprogname()); 118 fprintf(stderr, " %s -U [-nv] [-f configfile]\n", getprogname()); 119 fprintf(stderr, " %s -g [-nv] [-i ivmeth] [-k kgmeth] " 120 "[-o outfile] [-V vmeth] alg [keylen]\n", getprogname()); 121 fprintf(stderr, " %s -s [-nv] [-i ivmeth] cgd dev alg " 122 "[keylen]\n", getprogname()); 123 fprintf(stderr, " %s -u [-nv] cgd\n", getprogname()); 124 exit(1); 125 } 126 127 int 128 main(int argc, char **argv) 129 { 130 struct params cf; 131 int action = ACTION_CONFIGURE; 132 int actions = 0; 133 int ch; 134 int ret; 135 char cfile[FILENAME_MAX] = ""; 136 char outfile[FILENAME_MAX] = ""; 137 138 setprogname(*argv); 139 params_init(&cf); 140 141 while ((ch = getopt(argc, argv, "CUV:b:f:gi:k:no:usv")) != -1) 142 switch (ch) { 143 case 'C': 144 action = ACTION_CONFIGALL; 145 actions++; 146 break; 147 case 'U': 148 action = ACTION_UNCONFIGALL; 149 actions++; 150 break; 151 case 'V': 152 ret = params_setverify_method_str(&cf, optarg); 153 if (ret) 154 usage(); 155 break; 156 case 'b': 157 ret = params_setbsize(&cf, atoi(optarg)); 158 if (ret) 159 usage(); 160 break; 161 case 'f': 162 strncpy(cfile, optarg, FILENAME_MAX); 163 break; 164 case 'g': 165 action = ACTION_GENERATE; 166 actions++; 167 break; 168 case 'i': 169 params_setivmeth(&cf, optarg); 170 break; 171 case 'k': 172 ret = params_setkeygen_method_str(&cf, optarg); 173 if (ret) 174 usage(); 175 break; 176 case 'n': 177 nflag = 1; 178 break; 179 case 'o': 180 strncpy(outfile, optarg, FILENAME_MAX); 181 break; 182 case 's': 183 action = ACTION_CONFIGSTDIN; 184 actions++; 185 break; 186 187 case 'u': 188 action = ACTION_UNCONFIGURE; 189 actions++; 190 break; 191 case 'v': 192 verbose++; 193 break; 194 default: 195 usage(); 196 /* NOTREACHED */ 197 } 198 199 argc -= optind; 200 argv += optind; 201 202 /* validate the consistency of the arguments */ 203 204 if (actions > 1) 205 usage(); 206 if (action == ACTION_CONFIGURE && params_changed(&cf)) 207 usage(); 208 209 switch (action) { 210 case ACTION_CONFIGURE: 211 return configure(argc, argv, &cf, CONFIG_FLAGS_FROMMAIN); 212 case ACTION_UNCONFIGURE: 213 return unconfigure(argc, argv, NULL, CONFIG_FLAGS_FROMMAIN); 214 case ACTION_GENERATE: 215 return generate(&cf, argc, argv, outfile); 216 case ACTION_CONFIGALL: 217 return do_all(cfile, argc, argv, configure); 218 case ACTION_UNCONFIGALL: 219 return do_all(cfile, argc, argv, unconfigure); 220 case ACTION_CONFIGSTDIN: 221 return configure_stdin(&cf, argc, argv); 222 default: 223 fprintf(stderr, "undefined action\n"); 224 return 1; 225 } 226 /* NOTREACHED */ 227 } 228 229 static int 230 getkey(const char *target, struct params *p) 231 { 232 233 switch (p->keygen_method) { 234 case KEYGEN_RANDOMKEY: 235 p->key = getrandbits(p->keylen); 236 if (!p->key) 237 return -1; 238 return 0; 239 case KEYGEN_PKCS5_PBKDF2: 240 return getkeyfrompassphrase(target, p); 241 default: 242 fprintf(stderr, "getkey: unknown keygen_method\n"); 243 return -1; 244 } 245 /* NOTREACHED */ 246 } 247 248 static int 249 getkeyfromfile(FILE *f, struct params *p) 250 { 251 int ret; 252 253 /* XXXrcd: data hiding? */ 254 p->key = malloc(p->keylen); 255 if (!p->key) 256 return -1; 257 ret = fread(p->key, p->keylen, 1, f); 258 if (ret < 1) { 259 fprintf(stderr, "failed to read key from stdin\n"); 260 return -1; 261 } 262 return 0; 263 } 264 265 static int 266 getkeyfrompassphrase(const char *target, struct params *p) 267 { 268 int ret; 269 char *passp; 270 char buf[1024]; 271 272 snprintf(buf, 1024, "%s's passphrase:", target); 273 passp = getpass(buf); 274 /* XXXrcd: data hiding ? we should be allocating the key here. */ 275 if (!p->key) 276 free(p->key); 277 ret = pkcs5_pbkdf2(&p->key, BITS2BYTES(p->keylen), passp, 278 strlen(passp), p->keygen_salt, BITS2BYTES(p->keygen_saltlen), 279 p->keygen_iterations); 280 if (p->xor_key) 281 memxor(p->key, p->xor_key, BITS2BYTES(p->keylen)); 282 return ret; 283 } 284 285 /* ARGSUSED */ 286 static int 287 unconfigure(int argc, char **argv, struct params *inparams, int flags) 288 { 289 int fd; 290 int ret; 291 char buf[MAXPATHLEN] = ""; 292 293 /* only complain about additional arguments, if called from main() */ 294 if (flags == CONFIG_FLAGS_FROMMAIN && argc != 1) 295 usage(); 296 297 /* if called from do_all(), then ensure that 2 or 3 args exist */ 298 if (flags == CONFIG_FLAGS_FROMALL && (argc < 2 || argc > 3)) 299 return -1; 300 301 fd = opendisk(*argv, O_RDWR, buf, sizeof(buf), 1); 302 if (fd == -1) { 303 fprintf(stderr, "can't open cgd \"%s\", \"%s\": %s\n", 304 *argv, buf, strerror(errno)); 305 306 /* this isn't fatal with nflag != 0 */ 307 if (!nflag) 308 return errno; 309 } 310 311 VPRINTF(1, ("%s (%s): clearing\n", *argv, buf)); 312 313 if (nflag) 314 return 0; 315 316 ret = unconfigure_fd(fd); 317 close(fd); 318 return ret; 319 } 320 321 static int 322 unconfigure_fd(int fd) 323 { 324 struct cgd_ioctl ci; 325 int ret; 326 327 ret = ioctl(fd, CGDIOCCLR, &ci); 328 if (ret == -1) { 329 perror("ioctl"); 330 return -1; 331 } 332 333 return 0; 334 } 335 336 /* ARGSUSED */ 337 static int 338 configure(int argc, char **argv, struct params *inparams, int flags) 339 { 340 struct params params; 341 int fd; 342 int ret; 343 char pfile[FILENAME_MAX]; 344 char cgdname[PATH_MAX]; 345 346 params_init(¶ms); 347 348 switch (argc) { 349 case 2: 350 strlcpy(pfile, CGDCONFIG_DIR, FILENAME_MAX); 351 strlcat(pfile, "/", FILENAME_MAX); 352 strlcat(pfile, basename(argv[1]), FILENAME_MAX); 353 break; 354 case 3: 355 strlcpy(pfile, argv[2], FILENAME_MAX); 356 break; 357 default: 358 /* print usage and exit, only if called from main() */ 359 if (flags == CONFIG_FLAGS_FROMMAIN) 360 usage(); 361 return -1; 362 /* NOTREACHED */ 363 } 364 365 ret = params_cget(¶ms, pfile); 366 if (ret) 367 return ret; 368 ret = params_filldefaults(¶ms); 369 if (ret) 370 return ret; 371 372 /* 373 * over-ride the verify method with that specified on the 374 * command line 375 */ 376 377 if (inparams && inparams->verify_method != VERIFY_UNKNOWN) 378 params.verify_method = inparams->verify_method; 379 380 /* 381 * loop over configuring the disk and checking to see if it 382 * verifies properly. We open and close the disk device each 383 * time, because if the user passes us the block device we 384 * need to flush the buffer cache. 385 */ 386 387 for (;;) { 388 fd = opendisk_werror(argv[0], cgdname, sizeof(cgdname)); 389 if (fd == -1) 390 return -1; 391 392 ret = getkey(argv[1], ¶ms); 393 if (ret) 394 goto bail_err; 395 396 ret = configure_params(fd, cgdname, argv[1], ¶ms); 397 if (ret) 398 goto bail_err; 399 400 ret = verify(¶ms, fd); 401 if (ret == -1) 402 goto bail_err; 403 if (!ret) 404 break; 405 406 fprintf(stderr, "verification failed, please reenter " 407 "passphrase\n"); 408 409 unconfigure_fd(fd); 410 close(fd); 411 } 412 413 params_free(¶ms); 414 close(fd); 415 return 0; 416 bail_err: 417 close(fd); 418 return -1; 419 } 420 421 static int 422 configure_stdin(struct params *p, int argc, char **argv) 423 { 424 int fd; 425 int ret; 426 char cgdname[PATH_MAX]; 427 428 if (argc < 3 || argc > 4) 429 usage(); 430 431 ret = params_setalgorithm(p, argv[2]); 432 if (ret) 433 return ret; 434 if (argc > 3) { 435 ret = params_setkeylen(p, atoi(argv[3])); 436 if (ret) 437 return ret; 438 } 439 440 ret = params_filldefaults(p); 441 if (ret) 442 return ret; 443 444 fd = opendisk_werror(argv[0], cgdname, sizeof(cgdname)); 445 if (fd == -1) 446 return -1; 447 448 ret = getkeyfromfile(stdin, p); 449 if (ret) 450 return -1; 451 452 return configure_params(fd, cgdname, argv[1], p); 453 } 454 455 static int 456 opendisk_werror(const char *cgd, char *buf, int buflen) 457 { 458 int fd; 459 460 /* sanity */ 461 if (!cgd || !buf) 462 return -1; 463 464 if (nflag) { 465 strncpy(buf, cgd, buflen); 466 return 0; 467 } 468 469 fd = opendisk(cgd, O_RDWR, buf, buflen, 0); 470 if (fd == -1) 471 fprintf(stderr, "can't open cgd \"%s\", \"%s\": %s\n", 472 cgd, buf, strerror(errno)); 473 return fd; 474 } 475 476 static int 477 configure_params(int fd, const char *cgd, const char *dev, struct params *p) 478 { 479 struct cgd_ioctl ci; 480 int ret; 481 482 /* sanity */ 483 if (!cgd || !dev) 484 return -1; 485 486 memset(&ci, 0x0, sizeof(ci)); 487 ci.ci_disk = (char *)dev; 488 ci.ci_alg = p->alg; 489 ci.ci_ivmethod = p->ivmeth; 490 ci.ci_key = p->key; 491 ci.ci_keylen = p->keylen; 492 ci.ci_blocksize = p->bsize; 493 494 VPRINTF(1, ("attaching: %s attach to %s\n", cgd, dev)); 495 VPRINTF(1, (" with alg %s keylen %d blocksize %d ivmethod %s\n", 496 p->alg, p->keylen, p->bsize, p->ivmeth)); 497 VERBOSE(2, key_print(stdout, p->key, p->keylen)); 498 499 if (nflag) 500 return 0; 501 502 ret = ioctl(fd, CGDIOCSET, &ci); 503 if (ret == -1) { 504 perror("ioctl"); 505 return errno; 506 } 507 508 return 0; 509 } 510 511 /* 512 * verify returns 0 for success, -1 for unrecoverable error, or 1 for retry. 513 */ 514 515 #define SCANSIZE 8192 516 517 static int 518 verify(struct params *p, int fd) 519 { 520 struct disklabel l; 521 int ret; 522 char buf[SCANSIZE]; 523 524 switch (p->verify_method) { 525 case VERIFY_NONE: 526 return 0; 527 case VERIFY_DISKLABEL: 528 /* 529 * for now this is the only method, so we just perform it 530 * in this function. 531 */ 532 break; 533 default: 534 fprintf(stderr, "verify: unimplemented verification method\n"); 535 return -1; 536 } 537 538 /* 539 * we simply scan the first few blocks for a disklabel, ignoring 540 * any MBR/filecore sorts of logic. MSDOS and RiscOS can't read 541 * a cgd, anyway, so it is unlikely that there will be non-native 542 * partition information. 543 */ 544 545 ret = pread(fd, buf, 8192, 0); 546 if (ret == -1) { 547 fprintf(stderr, "verify: can't read disklabel area\n"); 548 return -1; 549 } 550 551 /* now scan for the disklabel */ 552 553 return disklabel_scan(&l, buf, sizeof(buf)); 554 } 555 556 static int 557 generate(struct params *p, int argc, char **argv, const char *outfile) 558 { 559 FILE *f; 560 int ret; 561 char *tmp; 562 563 if (argc < 1 || argc > 2) 564 usage(); 565 566 ret = params_setalgorithm(p, argv[0]); 567 if (ret) 568 return ret; 569 if (argc > 1) { 570 ret = params_setkeylen(p, atoi(argv[1])); 571 if (ret) 572 return ret; 573 } 574 575 ret = params_filldefaults(p); 576 if (ret) 577 return ret; 578 579 if (p->keygen_method != KEYGEN_RANDOMKEY) { 580 tmp = getrandbits(DEFAULT_SALTLEN); 581 params_setkeygen_salt(p, tmp, DEFAULT_SALTLEN); 582 free(tmp); 583 tmp = getrandbits(p->keylen); 584 params_setxor_key(p, tmp, p->keylen); 585 free(tmp); 586 587 /* XXXrcd: generate key hash, if desired */ 588 } 589 590 if (*outfile) { 591 f = fopen(outfile, "w"); 592 if (!f) { 593 fprintf(stderr, "could not open outfile \"%s\": %s\n", 594 outfile, strerror(errno)); 595 perror("fopen"); 596 return -1; 597 } 598 } else { 599 f = stdout; 600 } 601 602 ret = params_fput(p, f); 603 params_free(p); 604 return ret; 605 } 606 607 static int 608 do_all(const char *cfile, int argc, char **argv, 609 int (*conf)(int, char **, struct params *, int)) 610 { 611 FILE *f; 612 size_t len; 613 size_t lineno; 614 int my_argc; 615 int ret; 616 const char *fn; 617 char *line; 618 char **my_argv; 619 620 if (argc > 0) 621 usage(); 622 623 if (!cfile[0]) 624 fn = CGDCONFIG_CFILE; 625 else 626 fn = cfile; 627 628 f = fopen(fn, "r"); 629 if (!f) { 630 fprintf(stderr, "could not open config file \"%s\": %s\n", 631 fn, strerror(errno)); 632 return -1; 633 } 634 635 ret = 0; 636 lineno = 0; 637 for (;;) { 638 639 line = fparseln(f, &len, &lineno, "\\\\#", FPARSELN_UNESCALL); 640 if (!line) 641 break; 642 if (!*line) 643 continue; 644 645 my_argv = words(line, &my_argc); 646 ret = conf(my_argc, my_argv, NULL, CONFIG_FLAGS_FROMALL); 647 if (ret) { 648 fprintf(stderr, "on \"%s\" line %lu\n", fn, 649 (u_long)lineno); 650 break; 651 } 652 words_free(my_argv, my_argc); 653 } 654 return ret; 655 } 656 657 /* 658 * XXX: key_print doesn't work quite exactly properly if the keylength 659 * is not evenly divisible by 8. If the key is not divisible by 660 * 8 then a few extra bits are printed. 661 */ 662 663 static void 664 key_print(FILE *f, const u_int8_t *key, int len) 665 { 666 int i; 667 int col; 668 669 len = BITS2BYTES(len); 670 fprintf(f, "key: "); 671 for (i=0, col=5; i < len; i++, col+=2) { 672 fprintf(f, "%02x", key[i]); 673 if (col > 70) { 674 col = 5 - 2; 675 fprintf(f, "\n "); 676 } 677 } 678 fprintf(f, "\n"); 679 } 680 681 static char * 682 getrandbits(int len) 683 { 684 FILE *f; 685 int ret; 686 char *res; 687 688 len = (len + 7) / 8; 689 res = malloc(len); 690 if (!res) 691 return NULL; 692 f = fopen("/dev/random", "r"); 693 if (!f) 694 return NULL; 695 ret = fread(res, len, 1, f); 696 if (ret != 1) { 697 free(res); 698 return NULL; 699 } 700 return res; 701 } 702