cgdconfig.c revision 1.53 1 /* $NetBSD: cgdconfig.c,v 1.53 2021/11/22 14:34:35 nia Exp $ */
2
3 /*-
4 * Copyright (c) 2002, 2003 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 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __COPYRIGHT("@(#) Copyright (c) 2002, 2003\
35 The NetBSD Foundation, Inc. All rights reserved.");
36 __RCSID("$NetBSD: cgdconfig.c,v 1.53 2021/11/22 14:34:35 nia Exp $");
37 #endif
38
39 #ifdef HAVE_ARGON2
40 #include <argon2.h>
41 #endif
42 #include <err.h>
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <libgen.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <unistd.h>
50 #include <util.h>
51 #include <paths.h>
52 #include <dirent.h>
53
54 #include <sys/ioctl.h>
55 #include <sys/stat.h>
56 #include <sys/bootblock.h>
57 #include <sys/disklabel.h>
58 #include <sys/disklabel_gpt.h>
59 #include <sys/mman.h>
60 #include <sys/param.h>
61 #include <sys/resource.h>
62 #include <sys/statvfs.h>
63 #include <sys/bitops.h>
64
65 #include <dev/cgdvar.h>
66
67 #include <ufs/ffs/fs.h>
68
69 #include "params.h"
70 #include "pkcs5_pbkdf2.h"
71 #include "utils.h"
72 #include "cgdconfig.h"
73 #include "prog_ops.h"
74
75 #define CGDCONFIG_CFILE CGDCONFIG_DIR "/cgd.conf"
76
77 enum action {
78 ACTION_DEFAULT, /* default -> configure */
79 ACTION_CONFIGURE, /* configure, with paramsfile */
80 ACTION_UNCONFIGURE, /* unconfigure */
81 ACTION_GENERATE, /* generate a paramsfile */
82 ACTION_GENERATE_CONVERT, /* generate a ``dup'' paramsfile */
83 ACTION_CONFIGALL, /* configure all from config file */
84 ACTION_UNCONFIGALL, /* unconfigure all from config file */
85 ACTION_CONFIGSTDIN, /* configure, key from stdin */
86 ACTION_LIST /* list configured devices */
87 };
88
89 /* if nflag is set, do not configure/unconfigure the cgd's */
90
91 int nflag = 0;
92
93 /* if pflag is set to PFLAG_STDIN read from stdin rather than getpass(3) */
94
95 #define PFLAG_GETPASS 0x01
96 #define PFLAG_GETPASS_ECHO 0x02
97 #define PFLAG_GETPASS_MASK 0x03
98 #define PFLAG_STDIN 0x04
99 int pflag = PFLAG_GETPASS;
100
101 static int configure(int, char **, struct params *, int);
102 static int configure_stdin(struct params *, int argc, char **);
103 static int generate(struct params *, int, char **, const char *);
104 static int generate_convert(struct params *, int, char **, const char *);
105 static int unconfigure(int, char **, struct params *, int);
106 static int do_all(const char *, int, char **,
107 int (*)(int, char **, struct params *, int));
108 static int do_list(int, char **);
109
110 #define CONFIG_FLAGS_FROMALL 1 /* called from configure_all() */
111 #define CONFIG_FLAGS_FROMMAIN 2 /* called from main() */
112
113 static int configure_params(int, const char *, const char *,
114 struct params *);
115 static void eliminate_cores(void);
116 static bits_t *getkey(const char *, struct keygen *, size_t);
117 static bits_t *getkey_storedkey(const char *, struct keygen *, size_t);
118 static bits_t *getkey_randomkey(const char *, struct keygen *, size_t, int);
119 #ifdef HAVE_ARGON2
120 static bits_t *getkey_argon2id(const char *, struct keygen *, size_t);
121 #endif
122 static bits_t *getkey_pkcs5_pbkdf2(const char *, struct keygen *, size_t,
123 int);
124 static bits_t *getkey_shell_cmd(const char *, struct keygen *, size_t);
125 static char *maybe_getpass(char *);
126 static int opendisk_werror(const char *, char *, size_t);
127 static int unconfigure_fd(int);
128 static int verify(struct params *, int);
129 static int verify_disklabel(int);
130 static int verify_ffs(int);
131 static int verify_reenter(struct params *);
132 static int verify_mbr(int);
133 static int verify_gpt(int);
134
135 __dead static void usage(void);
136
137 /* Verbose Framework */
138 unsigned verbose = 0;
139
140 #define VERBOSE(x,y) if (verbose >= x) y
141 #define VPRINTF(x,y) if (verbose >= x) (void)printf y
142
143 static void
144 usage(void)
145 {
146
147 (void)fprintf(stderr, "usage: %s [-enpv] [-V vmeth] cgd dev "
148 "[paramsfile]\n", getprogname());
149 (void)fprintf(stderr, " %s -C [-enpv] [-f configfile]\n",
150 getprogname());
151 (void)fprintf(stderr, " %s -G [-enpv] [-i ivmeth] [-k kgmeth] "
152 "[-o outfile] paramsfile\n", getprogname());
153 (void)fprintf(stderr, " %s -g [-v] [-i ivmeth] [-k kgmeth] "
154 "[-o outfile] alg [keylen]\n", getprogname());
155 (void)fprintf(stderr, " %s -l [-v[v]] [cgd]\n", getprogname());
156 (void)fprintf(stderr, " %s -s [-nv] [-i ivmeth] cgd dev alg "
157 "[keylen]\n", getprogname());
158 (void)fprintf(stderr, " %s -U [-nv] [-f configfile]\n",
159 getprogname());
160 (void)fprintf(stderr, " %s -u [-nv] cgd\n", getprogname());
161 exit(EXIT_FAILURE);
162 }
163
164 static int
165 parse_size_t(const char *s, size_t *l)
166 {
167 char *endptr;
168 long v;
169
170 errno = 0;
171 v = strtol(s, &endptr, 10);
172 if ((v == LONG_MIN || v == LONG_MAX) && errno)
173 return -1;
174 if (v < INT_MIN || v > INT_MAX) {
175 errno = ERANGE;
176 return -1;
177 }
178 if (endptr == s) {
179 errno = EINVAL;
180 return -1;
181 }
182 *l = (size_t)v;
183 return 0;
184 }
185
186 static void
187 set_action(enum action *action, enum action value)
188 {
189 if (*action != ACTION_DEFAULT)
190 usage();
191 *action = value;
192 }
193
194 int
195 main(int argc, char **argv)
196 {
197 struct params *p;
198 struct params *tp;
199 struct keygen *kg;
200 enum action action = ACTION_DEFAULT;
201 int ch;
202 const char *cfile = NULL;
203 const char *outfile = NULL;
204
205 setprogname(*argv);
206 eliminate_cores();
207 if (mlockall(MCL_FUTURE))
208 err(EXIT_FAILURE, "Can't lock memory");
209 p = params_new();
210 kg = NULL;
211
212 while ((ch = getopt(argc, argv, "CGUV:b:ef:gi:k:lno:spuv")) != -1)
213 switch (ch) {
214 case 'C':
215 set_action(&action, ACTION_CONFIGALL);
216 break;
217 case 'G':
218 set_action(&action, ACTION_GENERATE_CONVERT);
219 break;
220 case 'U':
221 set_action(&action, ACTION_UNCONFIGALL);
222 break;
223 case 'V':
224 tp = params_verify_method(string_fromcharstar(optarg));
225 if (!tp)
226 usage();
227 p = params_combine(p, tp);
228 break;
229 case 'b':
230 {
231 size_t size;
232
233 if (parse_size_t(optarg, &size) == -1)
234 usage();
235 tp = params_bsize(size);
236 if (!tp)
237 usage();
238 p = params_combine(p, tp);
239 }
240 break;
241 case 'e':
242 pflag = PFLAG_GETPASS_ECHO;
243 break;
244 case 'f':
245 if (cfile)
246 usage();
247 cfile = estrdup(optarg);
248 break;
249 case 'g':
250 set_action(&action, ACTION_GENERATE);
251 break;
252 case 'i':
253 tp = params_ivmeth(string_fromcharstar(optarg));
254 p = params_combine(p, tp);
255 break;
256 case 'k':
257 kg = keygen_method(string_fromcharstar(optarg));
258 if (!kg)
259 usage();
260 keygen_addlist(&p->keygen, kg);
261 break;
262 case 'l':
263 set_action(&action, ACTION_LIST);
264 break;
265 case 'n':
266 nflag = 1;
267 break;
268 case 'o':
269 if (outfile)
270 usage();
271 outfile = estrdup(optarg);
272 break;
273 case 'p':
274 pflag = PFLAG_STDIN;
275 break;
276 case 's':
277 set_action(&action, ACTION_CONFIGSTDIN);
278 break;
279
280 case 'u':
281 set_action(&action, ACTION_UNCONFIGURE);
282 break;
283 case 'v':
284 verbose++;
285 break;
286 default:
287 usage();
288 /* NOTREACHED */
289 }
290
291 argc -= optind;
292 argv += optind;
293
294 if (!outfile)
295 outfile = "";
296 if (!cfile)
297 cfile = "";
298
299 if (prog_init && prog_init() == -1)
300 err(1, "init failed");
301
302 /* validate the consistency of the arguments */
303
304 switch (action) {
305 case ACTION_DEFAULT: /* ACTION_CONFIGURE is the default */
306 case ACTION_CONFIGURE:
307 return configure(argc, argv, p, CONFIG_FLAGS_FROMMAIN);
308 case ACTION_UNCONFIGURE:
309 return unconfigure(argc, argv, NULL, CONFIG_FLAGS_FROMMAIN);
310 case ACTION_GENERATE:
311 return generate(p, argc, argv, outfile);
312 case ACTION_GENERATE_CONVERT:
313 return generate_convert(p, argc, argv, outfile);
314 case ACTION_CONFIGALL:
315 return do_all(cfile, argc, argv, configure);
316 case ACTION_UNCONFIGALL:
317 return do_all(cfile, argc, argv, unconfigure);
318 case ACTION_CONFIGSTDIN:
319 return configure_stdin(p, argc, argv);
320 case ACTION_LIST:
321 return do_list(argc, argv);
322 default:
323 errx(EXIT_FAILURE, "undefined action");
324 /* NOTREACHED */
325 }
326 }
327
328 static bits_t *
329 getkey(const char *dev, struct keygen *kg, size_t len)
330 {
331 bits_t *ret = NULL;
332 bits_t *tmp;
333
334 VPRINTF(3, ("getkey(\"%s\", %p, %zu) called\n", dev, kg, len));
335 for (; kg; kg=kg->next) {
336 switch (kg->kg_method) {
337 case KEYGEN_STOREDKEY:
338 tmp = getkey_storedkey(dev, kg, len);
339 break;
340 case KEYGEN_RANDOMKEY:
341 tmp = getkey_randomkey(dev, kg, len, 1);
342 break;
343 case KEYGEN_URANDOMKEY:
344 tmp = getkey_randomkey(dev, kg, len, 0);
345 break;
346 #ifdef HAVE_ARGON2
347 case KEYGEN_ARGON2ID:
348 tmp = getkey_argon2id(dev, kg, len);
349 break;
350 #endif
351 case KEYGEN_PKCS5_PBKDF2_SHA1:
352 tmp = getkey_pkcs5_pbkdf2(dev, kg, len, 0);
353 break;
354 /* provide backwards compatibility for old config files */
355 case KEYGEN_PKCS5_PBKDF2_OLD:
356 tmp = getkey_pkcs5_pbkdf2(dev, kg, len, 1);
357 break;
358 case KEYGEN_SHELL_CMD:
359 tmp = getkey_shell_cmd(dev, kg, len);
360 break;
361 default:
362 warnx("unrecognised keygen method %d in getkey()",
363 kg->kg_method);
364 if (ret)
365 bits_free(ret);
366 return NULL;
367 }
368
369 if (ret)
370 ret = bits_xor_d(tmp, ret);
371 else
372 ret = tmp;
373 }
374
375 return ret;
376 }
377
378 /*ARGSUSED*/
379 static bits_t *
380 getkey_storedkey(const char *target, struct keygen *kg, size_t keylen)
381 {
382 return bits_dup(kg->kg_key);
383 }
384
385 /*ARGSUSED*/
386 static bits_t *
387 getkey_randomkey(const char *target, struct keygen *kg, size_t keylen, int hard)
388 {
389 return bits_getrandombits(keylen, hard);
390 }
391
392 static char *
393 maybe_getpass(char *prompt)
394 {
395 char buf[1024];
396 char *p = NULL;
397 char *tmp, *pass;
398
399 switch (pflag) {
400 case PFLAG_GETPASS:
401 p = getpass_r(prompt, buf, sizeof(buf));
402 break;
403
404 case PFLAG_GETPASS_ECHO:
405 p = getpassfd(prompt, buf, sizeof(buf), NULL,
406 GETPASS_ECHO|GETPASS_ECHO_NL|GETPASS_NEED_TTY, 0);
407 break;
408
409 case PFLAG_STDIN:
410 p = fgets(buf, sizeof(buf), stdin);
411 if (p) {
412 tmp = strchr(p, '\n');
413 if (tmp)
414 *tmp = '\0';
415 }
416 break;
417
418 default:
419 errx(EXIT_FAILURE, "pflag set inappropriately?");
420 }
421
422 if (!p)
423 err(EXIT_FAILURE, "failed to read passphrase");
424
425 pass = estrdup(p);
426 explicit_memset(buf, 0, sizeof(buf));
427
428 return pass;
429 }
430
431 /*ARGSUSED*/
432 /*
433 * XXX take, and pass through, a compat flag that indicates whether we
434 * provide backwards compatibility with a previous bug. The previous
435 * behaviour is indicated by the keygen method pkcs5_pbkdf2, and a
436 * non-zero compat flag. The new default, and correct keygen method is
437 * called pcks5_pbkdf2/sha1. When the old method is removed, so will
438 * be the compat argument.
439 */
440 static bits_t *
441 getkey_pkcs5_pbkdf2(const char *target, struct keygen *kg, size_t keylen,
442 int compat)
443 {
444 bits_t *ret;
445 char *passp;
446 char buf[1024];
447 u_int8_t *tmp;
448
449 snprintf(buf, sizeof(buf), "%s's passphrase%s:", target,
450 pflag & PFLAG_GETPASS_ECHO ? " (echo)" : "");
451 passp = maybe_getpass(buf);
452 if (pkcs5_pbkdf2(&tmp, BITS2BYTES(keylen), (uint8_t *)passp,
453 strlen(passp),
454 bits_getbuf(kg->kg_salt), BITS2BYTES(bits_len(kg->kg_salt)),
455 kg->kg_iterations, compat)) {
456 warnx("failed to generate PKCS#5 PBKDF2 key");
457 return NULL;
458 }
459
460 ret = bits_new(tmp, keylen);
461 kg->kg_key = bits_dup(ret);
462 explicit_memset(passp, 0, strlen(passp));
463 free(passp);
464 free(tmp);
465 return ret;
466 }
467
468 #ifdef HAVE_ARGON2
469 static bits_t *
470 getkey_argon2id(const char *target, struct keygen *kg, size_t keylen)
471 {
472 bits_t *ret;
473 char *passp;
474 char buf[1024];
475 uint8_t raw[256];
476 int err;
477
478 snprintf(buf, sizeof(buf), "%s's passphrase%s:", target,
479 pflag & PFLAG_GETPASS_ECHO ? " (echo)" : "");
480 passp = maybe_getpass(buf);
481 if ((err = argon2_hash(kg->kg_iterations, kg->kg_memory,
482 kg->kg_parallelism,
483 passp, strlen(passp),
484 bits_getbuf(kg->kg_salt),
485 BITS2BYTES(bits_len(kg->kg_salt)),
486 raw, sizeof(raw),
487 NULL, 0,
488 Argon2_id, kg->kg_version)) != ARGON2_OK) {
489 warnx("failed to generate Argon2id key, error code %d", err);
490 return NULL;
491 }
492
493 ret = bits_new(raw, keylen);
494 kg->kg_key = bits_dup(ret);
495 explicit_memset(passp, 0, strlen(passp));
496 explicit_memset(raw, 0, sizeof(raw));
497 free(passp);
498 return ret;
499 }
500 #endif
501
502 /*ARGSUSED*/
503 static bits_t *
504 getkey_shell_cmd(const char *target, struct keygen *kg, size_t keylen)
505 {
506 FILE *f;
507 bits_t *ret;
508 int status;
509
510 if ((f = popen(string_tocharstar(kg->kg_cmd), "r")) == NULL)
511 errx(1, "command failed");
512 if ((ret = bits_fget(f, keylen)) == NULL)
513 errx(1, "command output too short");
514 if ((status = pclose(f)) != 0)
515 err(1, "command failed with status %d", status);
516
517 return ret;
518 }
519
520 /*ARGSUSED*/
521 static int
522 unconfigure(int argc, char **argv, struct params *inparams, int flags)
523 {
524 int fd;
525 int ret;
526 char buf[MAXPATHLEN] = "";
527
528 /* only complain about additional arguments, if called from main() */
529 if (flags == CONFIG_FLAGS_FROMMAIN && argc != 1)
530 usage();
531
532 /* if called from do_all(), then ensure that 2 or 3 args exist */
533 if (flags == CONFIG_FLAGS_FROMALL && (argc < 2 || argc > 3))
534 return -1;
535
536 fd = opendisk1(*argv, O_RDWR, buf, sizeof(buf), 1, prog_open);
537 if (fd == -1) {
538 int saved_errno = errno;
539
540 warn("can't open cgd \"%s\", \"%s\"", *argv, buf);
541
542 /* this isn't fatal with nflag != 0 */
543 if (!nflag)
544 return saved_errno;
545 }
546
547 VPRINTF(1, ("%s (%s): clearing\n", *argv, buf));
548
549 if (nflag)
550 return 0;
551
552 ret = unconfigure_fd(fd);
553 (void)prog_close(fd);
554 return ret;
555 }
556
557 static int
558 unconfigure_fd(int fd)
559 {
560 struct cgd_ioctl ci;
561
562 if (prog_ioctl(fd, CGDIOCCLR, &ci) == -1) {
563 warn("ioctl");
564 return -1;
565 }
566
567 return 0;
568 }
569
570 /*ARGSUSED*/
571 static int
572 configure(int argc, char **argv, struct params *inparams, int flags)
573 {
574 struct params *p;
575 struct keygen *kg;
576 int fd;
577 int loop = 0;
578 int ret;
579 char cgdname[PATH_MAX];
580 char devicename[PATH_MAX];
581 const char *dev = NULL; /* XXX: gcc */
582
583 if (argc < 2 || argc > 3) {
584 /* print usage and exit, only if called from main() */
585 if (flags == CONFIG_FLAGS_FROMMAIN) {
586 warnx("wrong number of args");
587 usage();
588 }
589 return -1;
590 }
591
592 if ((
593 fd = opendisk1(*argv, O_RDWR, cgdname, sizeof(cgdname), 1, prog_open)
594 ) != -1) {
595 struct cgd_user cgu;
596
597 cgu.cgu_unit = -1;
598 if (prog_ioctl(fd, CGDIOCGET, &cgu) != -1 && cgu.cgu_dev != 0) {
599 warnx("device %s already in use", *argv);
600 prog_close(fd);
601 return -1;
602 }
603 prog_close(fd);
604 }
605
606 dev = getfsspecname(devicename, sizeof(devicename), argv[1]);
607 if (dev == NULL) {
608 warnx("getfsspecname failed: %s", devicename);
609 return -1;
610 }
611
612 if (argc == 2) {
613 char pfile[MAXPATHLEN];
614
615 /* make string writable for basename */
616 strlcpy(pfile, dev, sizeof(pfile));
617 p = params_cget(basename(pfile));
618 } else
619 p = params_cget(argv[2]);
620
621 if (!p)
622 return -1;
623
624 /*
625 * over-ride with command line specifications and fill in default
626 * values.
627 */
628
629 p = params_combine(p, inparams);
630 ret = params_filldefaults(p);
631 if (ret) {
632 params_free(p);
633 return ret;
634 }
635
636 if (!params_verify(p)) {
637 warnx("params invalid");
638 return -1;
639 }
640
641 /*
642 * loop over configuring the disk and checking to see if it
643 * verifies properly. We open and close the disk device each
644 * time, because if the user passes us the block device we
645 * need to flush the buffer cache.
646 *
647 * We only loop if one of the verification methods prompts for
648 * a password.
649 */
650
651 for (kg = p->keygen;
652 (pflag & PFLAG_GETPASS_MASK) && kg;
653 kg = kg->next)
654 if (kg->kg_method == KEYGEN_ARGON2ID ||
655 kg->kg_method == KEYGEN_PKCS5_PBKDF2_SHA1 ||
656 kg->kg_method == KEYGEN_PKCS5_PBKDF2_OLD) {
657 loop = 1;
658 break;
659 }
660
661 for (;;) {
662 fd = opendisk_werror(argv[0], cgdname, sizeof(cgdname));
663 if (fd == -1)
664 return -1;
665
666 if (p->key)
667 bits_free(p->key);
668
669 p->key = getkey(argv[1], p->keygen, p->keylen);
670 if (!p->key)
671 goto bail_err;
672
673 ret = configure_params(fd, cgdname, dev, p);
674 if (ret)
675 goto bail_err;
676
677 ret = verify(p, fd);
678 if (ret == -1) {
679 (void)unconfigure_fd(fd);
680 goto bail_err;
681 }
682 if (ret == 0) /* success */
683 break;
684
685 (void)unconfigure_fd(fd);
686 (void)prog_close(fd);
687
688 if (!loop) {
689 warnx("verification failed permanently");
690 goto bail_err;
691 }
692
693 warnx("verification failed, please reenter passphrase");
694 }
695
696 params_free(p);
697 (void)prog_close(fd);
698 return 0;
699
700 bail_err:;
701 params_free(p);
702 (void)prog_close(fd);
703 return -1;
704 }
705
706 static int
707 configure_stdin(struct params *p, int argc, char **argv)
708 {
709 int fd;
710 int ret;
711 char cgdname[PATH_MAX];
712 char devicename[PATH_MAX];
713 const char *dev;
714
715 if (argc < 3 || argc > 4)
716 usage();
717
718 dev = getfsspecname(devicename, sizeof(devicename), argv[1]);
719 if (dev == NULL) {
720 warnx("getfsspecname failed: %s", devicename);
721 return -1;
722 }
723
724 p->algorithm = string_fromcharstar(argv[2]);
725 if (argc > 3) {
726 size_t keylen;
727
728 if (parse_size_t(argv[3], &keylen) == -1) {
729 warn("failed to parse key length");
730 return -1;
731 }
732 p->keylen = keylen;
733 }
734
735 ret = params_filldefaults(p);
736 if (ret)
737 return ret;
738
739 fd = opendisk_werror(argv[0], cgdname, sizeof(cgdname));
740 if (fd == -1)
741 return -1;
742
743 p->key = bits_fget(stdin, p->keylen);
744 if (!p->key) {
745 warnx("failed to read key from stdin");
746 return -1;
747 }
748
749 return configure_params(fd, cgdname, dev, p);
750 }
751
752 static int
753 opendisk_werror(const char *cgd, char *buf, size_t buflen)
754 {
755 int fd;
756
757 VPRINTF(3, ("opendisk_werror(%s, %s, %zu) called.\n", cgd,buf,buflen));
758
759 /* sanity */
760 if (!cgd || !buf)
761 return -1;
762
763 if (nflag) {
764 if (strlcpy(buf, cgd, buflen) >= buflen)
765 return -1;
766 return 0;
767 }
768
769 fd = opendisk1(cgd, O_RDWR, buf, buflen, 0, prog_open);
770 if (fd == -1)
771 warnx("can't open cgd \"%s\", \"%s\"", cgd, buf);
772
773 return fd;
774 }
775
776 static int
777 configure_params(int fd, const char *cgd, const char *dev, struct params *p)
778 {
779 struct cgd_ioctl ci;
780
781 /* sanity */
782 if (!cgd || !dev)
783 return -1;
784
785 (void)memset(&ci, 0x0, sizeof(ci));
786 ci.ci_disk = dev;
787 ci.ci_alg = string_tocharstar(p->algorithm);
788 ci.ci_ivmethod = string_tocharstar(p->ivmeth);
789 ci.ci_key = bits_getbuf(p->key);
790 ci.ci_keylen = p->keylen;
791 ci.ci_blocksize = p->bsize;
792
793 VPRINTF(1, (" with alg %s keylen %zu blocksize %zu ivmethod %s\n",
794 string_tocharstar(p->algorithm), p->keylen, p->bsize,
795 string_tocharstar(p->ivmeth)));
796 VPRINTF(2, ("key: "));
797 VERBOSE(2, bits_fprint(stdout, p->key));
798 VPRINTF(2, ("\n"));
799
800 if (nflag)
801 return 0;
802
803 if (prog_ioctl(fd, CGDIOCSET, &ci) == -1) {
804 int saved_errno = errno;
805 warn("ioctl");
806 return saved_errno;
807 }
808
809 return 0;
810 }
811
812 /*
813 * verify returns 0 for success, -1 for unrecoverable error, or 1 for retry.
814 */
815
816 #define SCANSIZE 8192
817
818 static int
819 verify(struct params *p, int fd)
820 {
821
822 switch (p->verify_method) {
823 case VERIFY_NONE:
824 return 0;
825 case VERIFY_DISKLABEL:
826 return verify_disklabel(fd);
827 case VERIFY_FFS:
828 return verify_ffs(fd);
829 case VERIFY_REENTER:
830 return verify_reenter(p);
831 case VERIFY_MBR:
832 return verify_mbr(fd);
833 case VERIFY_GPT:
834 return verify_gpt(fd);
835 default:
836 warnx("unimplemented verification method");
837 return -1;
838 }
839 }
840
841 static int
842 verify_disklabel(int fd)
843 {
844 struct disklabel l;
845 ssize_t ret;
846 char buf[SCANSIZE];
847
848 /*
849 * we simply scan the first few blocks for a disklabel, ignoring
850 * any MBR/filecore sorts of logic. MSDOS and RiscOS can't read
851 * a cgd, anyway, so it is unlikely that there will be non-native
852 * partition information.
853 */
854
855 ret = prog_pread(fd, buf, SCANSIZE, 0);
856 if (ret < 0) {
857 warn("can't read disklabel area");
858 return -1;
859 }
860
861 /* now scan for the disklabel */
862
863 return disklabel_scan(&l, buf, (size_t)ret);
864 }
865
866 static int
867 verify_mbr(int fd)
868 {
869 struct mbr_sector mbr;
870 ssize_t ret;
871 char buf[SCANSIZE];
872
873 /*
874 * we read the first blocks to avoid sector size issues and
875 * verify the MBR in the beginning
876 */
877
878 ret = prog_pread(fd, buf, SCANSIZE, 0);
879 if (ret < 0) {
880 warn("can't read mbr area");
881 return -1;
882 }
883
884 memcpy(&mbr, buf, sizeof(mbr));
885 if (le16toh(mbr.mbr_magic) != MBR_MAGIC)
886 return 1;
887
888 return 0;
889 }
890
891 static uint32_t crc32_tab[] = {
892 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
893 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
894 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
895 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
896 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
897 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
898 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
899 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
900 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
901 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
902 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
903 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
904 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
905 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
906 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
907 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
908 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
909 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
910 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
911 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
912 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
913 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
914 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
915 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
916 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
917 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
918 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
919 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
920 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
921 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
922 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
923 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
924 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
925 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
926 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
927 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
928 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
929 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
930 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
931 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
932 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
933 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
934 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
935 };
936
937 static uint32_t
938 crc32(const void *buf, size_t size)
939 {
940 const uint8_t *p;
941 uint32_t crc;
942
943 p = buf;
944 crc = ~0U;
945
946 while (size--)
947 crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
948
949 return crc ^ ~0U;
950 }
951
952 static int
953 verify_gpt(int fd)
954 {
955 struct gpt_hdr hdr;
956 ssize_t ret;
957 char buf[SCANSIZE];
958 unsigned blksize;
959 size_t off;
960
961 /*
962 * we read the first blocks to avoid sector size issues and
963 * verify the GPT header.
964 */
965
966 ret = prog_pread(fd, buf, SCANSIZE, 0);
967 if (ret < 0) {
968 warn("can't read gpt area");
969 return -1;
970 }
971
972 ret = 1;
973 for (blksize = DEV_BSIZE;
974 (off = (blksize * GPT_HDR_BLKNO)) <= SCANSIZE - sizeof(hdr);
975 blksize <<= 1) {
976
977 memcpy(&hdr, &buf[off], sizeof(hdr));
978 if (memcmp(hdr.hdr_sig, GPT_HDR_SIG, sizeof(hdr.hdr_sig)) == 0
979 && le32toh(hdr.hdr_revision) == GPT_HDR_REVISION
980 && le32toh(hdr.hdr_size) == GPT_HDR_SIZE) {
981
982 hdr.hdr_crc_self = 0;
983 if (crc32(&hdr, sizeof(hdr))) {
984 ret = 0;
985 break;
986 }
987 }
988 }
989
990 return ret;
991 }
992
993 static off_t sblock_try[] = SBLOCKSEARCH;
994
995 static int
996 verify_ffs(int fd)
997 {
998 size_t i;
999
1000 for (i = 0; sblock_try[i] != -1; i++) {
1001 union {
1002 char buf[SBLOCKSIZE];
1003 struct fs fs;
1004 } u;
1005 ssize_t ret;
1006
1007 ret = prog_pread(fd, &u, sizeof(u), sblock_try[i]);
1008 if (ret < 0) {
1009 warn("pread");
1010 break;
1011 } else if ((size_t)ret < sizeof(u)) {
1012 warnx("pread: incomplete block");
1013 break;
1014 }
1015 switch (u.fs.fs_magic) {
1016 case FS_UFS1_MAGIC:
1017 case FS_UFS2_MAGIC:
1018 case FS_UFS1_MAGIC_SWAPPED:
1019 case FS_UFS2_MAGIC_SWAPPED:
1020 return 0;
1021 default:
1022 continue;
1023 }
1024 }
1025
1026 return 1; /* failure */
1027 }
1028
1029 static int
1030 verify_reenter(struct params *p)
1031 {
1032 struct keygen *kg;
1033 bits_t *orig_key, *key = NULL;
1034 int ret;
1035
1036 ret = 0;
1037 for (kg = p->keygen; kg && !ret; kg = kg->next) {
1038 if (kg->kg_method != KEYGEN_ARGON2ID &&
1039 kg->kg_method != KEYGEN_PKCS5_PBKDF2_SHA1 &&
1040 kg->kg_method != KEYGEN_PKCS5_PBKDF2_OLD)
1041 continue;
1042
1043 orig_key = kg->kg_key;
1044 kg->kg_key = NULL;
1045
1046 switch (kg->kg_method) {
1047 #ifdef HAVE_ARGON2
1048 case KEYGEN_ARGON2ID:
1049 key = getkey_argon2id("re-enter device", kg,
1050 bits_len(orig_key));
1051 break;
1052 #endif
1053 case KEYGEN_PKCS5_PBKDF2_SHA1:
1054 key = getkey_pkcs5_pbkdf2("re-enter device", kg,
1055 bits_len(orig_key), 0);
1056 break;
1057 case KEYGEN_PKCS5_PBKDF2_OLD:
1058 key = getkey_pkcs5_pbkdf2("re-enter device", kg,
1059 bits_len(orig_key), 1);
1060 break;
1061 default:
1062 warnx("unsupported keygen method");
1063 kg->kg_key = orig_key;
1064 return -1;
1065 }
1066
1067 ret = !bits_match(key, orig_key);
1068
1069 bits_free(key);
1070 bits_free(kg->kg_key);
1071 kg->kg_key = orig_key;
1072 }
1073
1074 return ret;
1075 }
1076
1077 static int
1078 generate(struct params *p, int argc, char **argv, const char *outfile)
1079 {
1080 int ret;
1081
1082 if (argc < 1 || argc > 2)
1083 usage();
1084
1085 p->algorithm = string_fromcharstar(argv[0]);
1086 if (argc > 1) {
1087 size_t keylen;
1088
1089 if (parse_size_t(argv[1], &keylen) == -1) {
1090 warn("Failed to parse key length");
1091 return -1;
1092 }
1093 p->keylen = keylen;
1094 }
1095
1096 ret = params_filldefaults(p);
1097 if (ret)
1098 return ret;
1099
1100 if (!p->keygen) {
1101 p->keygen = keygen_generate(KEYGEN_PKCS5_PBKDF2_SHA1);
1102 if (!p->keygen)
1103 return -1;
1104 }
1105
1106 if (keygen_filldefaults(p->keygen, p->keylen)) {
1107 warnx("Failed to generate defaults for keygen");
1108 return -1;
1109 }
1110
1111 if (!params_verify(p)) {
1112 warnx("invalid parameters generated");
1113 return -1;
1114 }
1115
1116 return params_cput(p, outfile);
1117 }
1118
1119 static int
1120 generate_convert(struct params *p, int argc, char **argv, const char *outfile)
1121 {
1122 struct params *oldp;
1123 struct keygen *kg;
1124
1125 if (argc != 1)
1126 usage();
1127
1128 oldp = params_cget(*argv);
1129 if (!oldp)
1130 return -1;
1131
1132 /* for sanity, we ensure that none of the keygens are randomkey */
1133 for (kg=p->keygen; kg; kg=kg->next)
1134 if ((kg->kg_method == KEYGEN_RANDOMKEY) ||
1135 (kg->kg_method == KEYGEN_URANDOMKEY)) {
1136 warnx("can't preserve randomly generated key");
1137 goto bail;
1138 }
1139 for (kg=oldp->keygen; kg; kg=kg->next)
1140 if ((kg->kg_method == KEYGEN_RANDOMKEY) ||
1141 (kg->kg_method == KEYGEN_URANDOMKEY)) {
1142 warnx("can't preserve randomly generated key");
1143 goto bail;
1144 }
1145
1146 if (!params_verify(oldp)) {
1147 warnx("invalid old parameters file \"%s\"", *argv);
1148 return -1;
1149 }
1150
1151 oldp->key = getkey("old file", oldp->keygen, oldp->keylen);
1152
1153 /* we copy across the non-keygen info, here. */
1154
1155 string_free(p->algorithm);
1156 string_free(p->ivmeth);
1157
1158 p->algorithm = string_dup(oldp->algorithm);
1159 p->ivmeth = string_dup(oldp->ivmeth);
1160 p->keylen = oldp->keylen;
1161 p->bsize = oldp->bsize;
1162 if (p->verify_method == VERIFY_UNKNOWN)
1163 p->verify_method = oldp->verify_method;
1164
1165 params_free(oldp);
1166
1167 if (!p->keygen) {
1168 p->keygen = keygen_generate(KEYGEN_PKCS5_PBKDF2_SHA1);
1169 if (!p->keygen)
1170 return -1;
1171 }
1172 (void)params_filldefaults(p);
1173 (void)keygen_filldefaults(p->keygen, p->keylen);
1174 p->key = getkey("new file", p->keygen, p->keylen);
1175
1176 kg = keygen_generate(KEYGEN_STOREDKEY);
1177 kg->kg_key = bits_xor(p->key, oldp->key);
1178 keygen_addlist(&p->keygen, kg);
1179
1180 if (!params_verify(p)) {
1181 warnx("can't generate new parameters file");
1182 return -1;
1183 }
1184
1185 return params_cput(p, outfile);
1186 bail:;
1187 params_free(oldp);
1188 return -1;
1189 }
1190
1191 static int
1192 /*ARGSUSED*/
1193 do_all(const char *cfile, int argc, char **argv,
1194 int (*conf)(int, char **, struct params *, int))
1195 {
1196 FILE *f;
1197 size_t len;
1198 size_t lineno;
1199 int my_argc;
1200 int ret;
1201 const char *fn;
1202 char *line;
1203 char **my_argv;
1204
1205 if (argc > 0)
1206 usage();
1207
1208 if (!cfile[0])
1209 fn = CGDCONFIG_CFILE;
1210 else
1211 fn = cfile;
1212
1213 f = fopen(fn, "r");
1214 if (f == NULL) {
1215 warn("could not open config file \"%s\"", fn);
1216 return -1;
1217 }
1218
1219 ret = 0;
1220 lineno = 0;
1221 for (;;) {
1222 line = fparseln(f, &len, &lineno, "\\\\#", FPARSELN_UNESCALL);
1223 if (!line)
1224 break;
1225 if (!*line)
1226 continue;
1227
1228 my_argv = words(line, &my_argc);
1229 ret = conf(my_argc, my_argv, NULL, CONFIG_FLAGS_FROMALL);
1230 if (ret) {
1231 warnx("action failed on \"%s\" line %lu", fn,
1232 (u_long)lineno);
1233 break;
1234 }
1235 words_free(my_argv, my_argc);
1236 }
1237 return ret;
1238 }
1239
1240 static const char *
1241 iv_method(int mode)
1242 {
1243
1244 switch (mode) {
1245 case CGD_CIPHER_CBC_ENCBLKNO8:
1246 return "encblkno8";
1247 case CGD_CIPHER_CBC_ENCBLKNO1:
1248 return "encblkno1";
1249 default:
1250 return "unknown";
1251 }
1252 }
1253
1254
1255 static void
1256 show(const char *dev) {
1257 char path[64];
1258 struct cgd_user cgu;
1259 int fd;
1260
1261 fd = opendisk(dev, O_RDONLY, path, sizeof(path), 0);
1262 if (fd == -1) {
1263 warn("open: %s", dev);
1264 return;
1265 }
1266
1267 cgu.cgu_unit = -1;
1268 if (prog_ioctl(fd, CGDIOCGET, &cgu) == -1) {
1269 close(fd);
1270 err(1, "CGDIOCGET");
1271 }
1272
1273 printf("%s: ", dev);
1274
1275 if (cgu.cgu_dev == 0) {
1276 printf("not in use");
1277 goto out;
1278 }
1279
1280 dev = devname(cgu.cgu_dev, S_IFBLK);
1281 if (dev != NULL)
1282 printf("%s ", dev);
1283 else
1284 printf("dev %llu,%llu ", (unsigned long long)major(cgu.cgu_dev),
1285 (unsigned long long)minor(cgu.cgu_dev));
1286
1287 if (verbose)
1288 printf("%s ", cgu.cgu_alg);
1289 if (verbose > 1) {
1290 printf("keylen %d ", cgu.cgu_keylen);
1291 printf("blksize %zd ", cgu.cgu_blocksize);
1292 printf("%s ", iv_method(cgu.cgu_mode));
1293 }
1294
1295 out:;
1296 putchar('\n');
1297 close(fd);
1298 }
1299
1300 static int
1301 do_list(int argc, char **argv)
1302 {
1303
1304 if (argc != 0 && argc != 1)
1305 usage();
1306
1307 if (argc) {
1308 show(argv[0]);
1309 return 0;
1310 }
1311
1312 DIR *dirp;
1313 struct dirent *dp;
1314 __BITMAP_TYPE(, uint32_t, 65536) bm;
1315
1316 __BITMAP_ZERO(&bm);
1317
1318 if ((dirp = opendir(_PATH_DEV)) == NULL)
1319 err(1, "opendir: %s", _PATH_DEV);
1320
1321 while ((dp = readdir(dirp)) != NULL) {
1322 char *ep;
1323 if (strncmp(dp->d_name, "rcgd", 4) != 0)
1324 continue;
1325 errno = 0;
1326 int n = (int)strtol(dp->d_name + 4, &ep, 0);
1327 if (ep == dp->d_name + 4 || errno != 0) {
1328 warnx("bad name %s", dp->d_name);
1329 continue;
1330 }
1331 *ep = '\0';
1332 if (__BITMAP_ISSET(n, &bm))
1333 continue;
1334 __BITMAP_SET(n, &bm);
1335 show(dp->d_name + 1);
1336 }
1337
1338 closedir(dirp);
1339 return 0;
1340 }
1341
1342 static void
1343 eliminate_cores(void)
1344 {
1345 struct rlimit rlp;
1346
1347 rlp.rlim_cur = 0;
1348 rlp.rlim_max = 0;
1349 if (setrlimit(RLIMIT_CORE, &rlp) == -1)
1350 err(EXIT_FAILURE, "Can't disable cores");
1351 }
1352