xref: /netbsd-src/sbin/cgdconfig/cgdconfig.c (revision 413d532bcc3f62d122e56d92e13ac64825a40baf)
1 /* $NetBSD: cgdconfig.c,v 1.35 2013/06/09 18:37:40 christos 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.35 2013/06/09 18:37:40 christos Exp $");
37 #endif
38 
39 #include <err.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <libgen.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
47 #include <util.h>
48 #include <paths.h>
49 #include <dirent.h>
50 
51 #include <sys/ioctl.h>
52 #include <sys/disklabel.h>
53 #include <sys/mman.h>
54 #include <sys/param.h>
55 #include <sys/resource.h>
56 #include <sys/statvfs.h>
57 #include <sys/bitops.h>
58 
59 #include <dev/cgdvar.h>
60 
61 #include <ufs/ffs/fs.h>
62 
63 #include "params.h"
64 #include "pkcs5_pbkdf2.h"
65 #include "utils.h"
66 #include "cgdconfig.h"
67 #include "prog_ops.h"
68 
69 #define CGDCONFIG_DIR		"/etc/cgd"
70 #define CGDCONFIG_CFILE		CGDCONFIG_DIR "/cgd.conf"
71 
72 enum action {
73 	 ACTION_DEFAULT,		/* default -> configure */
74 	 ACTION_CONFIGURE,		/* configure, with paramsfile */
75 	 ACTION_UNCONFIGURE,		/* unconfigure */
76 	 ACTION_GENERATE,		/* generate a paramsfile */
77 	 ACTION_GENERATE_CONVERT,	/* generate a ``dup'' paramsfile */
78 	 ACTION_CONFIGALL,		/* configure all from config file */
79 	 ACTION_UNCONFIGALL,		/* unconfigure all from config file */
80 	 ACTION_CONFIGSTDIN,		/* configure, key from stdin */
81 	 ACTION_LIST			/* list configured devices */
82 };
83 
84 /* if nflag is set, do not configure/unconfigure the cgd's */
85 
86 int	nflag = 0;
87 
88 /* if pflag is set to PFLAG_STDIN read from stdin rather than getpass(3) */
89 
90 #define	PFLAG_GETPASS	0x01
91 #define	PFLAG_STDIN	0x02
92 int	pflag = PFLAG_GETPASS;
93 
94 static int	configure(int, char **, struct params *, int);
95 static int	configure_stdin(struct params *, int argc, char **);
96 static int	generate(struct params *, int, char **, const char *);
97 static int	generate_convert(struct params *, int, char **, const char *);
98 static int	unconfigure(int, char **, struct params *, int);
99 static int	do_all(const char *, int, char **,
100 		       int (*)(int, char **, struct params *, int));
101 static int	do_list(int, char **);
102 
103 #define CONFIG_FLAGS_FROMALL	1	/* called from configure_all() */
104 #define CONFIG_FLAGS_FROMMAIN	2	/* called from main() */
105 
106 static int	 configure_params(int, const char *, const char *,
107 				  struct params *);
108 static void	 eliminate_cores(void);
109 static bits_t	*getkey(const char *, struct keygen *, size_t);
110 static bits_t	*getkey_storedkey(const char *, struct keygen *, size_t);
111 static bits_t	*getkey_randomkey(const char *, struct keygen *, size_t, int);
112 static bits_t	*getkey_pkcs5_pbkdf2(const char *, struct keygen *, size_t,
113 				     int);
114 static bits_t	*getkey_shell_cmd(const char *, struct keygen *, size_t);
115 static char	*maybe_getpass(char *);
116 static int	 opendisk_werror(const char *, char *, size_t);
117 static int	 unconfigure_fd(int);
118 static int	 verify(struct params *, int);
119 static int	 verify_disklabel(int);
120 static int	 verify_ffs(int);
121 static int	 verify_reenter(struct params *);
122 
123 __dead static void	 usage(void);
124 
125 /* Verbose Framework */
126 unsigned	verbose = 0;
127 
128 #define VERBOSE(x,y)	if (verbose >= x) y
129 #define VPRINTF(x,y)	if (verbose >= x) (void)printf y
130 
131 static void
132 usage(void)
133 {
134 
135 	(void)fprintf(stderr, "usage: %s [-nv] [-V vmeth] cgd dev [paramsfile]\n",
136 	    getprogname());
137 	(void)fprintf(stderr, "       %s -C [-nv] [-f configfile]\n", getprogname());
138 	(void)fprintf(stderr, "       %s -G [-nv] [-i ivmeth] [-k kgmeth] "
139 	    "[-o outfile] paramsfile\n", getprogname());
140 	(void)fprintf(stderr, "       %s -g [-nv] [-i ivmeth] [-k kgmeth] "
141 	    "[-o outfile] alg [keylen]\n", getprogname());
142 	(void)fprintf(stderr, "       %s -l\n", getprogname());
143 	(void)fprintf(stderr, "       %s -s [-nv] [-i ivmeth] cgd dev alg "
144 	    "[keylen]\n", getprogname());
145 	(void)fprintf(stderr, "       %s -U [-nv] [-f configfile]\n", getprogname());
146 	(void)fprintf(stderr, "       %s -u [-nv] cgd\n", getprogname());
147 	exit(EXIT_FAILURE);
148 }
149 
150 static int
151 parse_size_t(const char *s, size_t *l)
152 {
153 	char *endptr;
154 	long v;
155 
156 	errno = 0;
157 	v = strtol(s, &endptr, 10);
158 	if ((v == LONG_MIN || v == LONG_MAX) && errno)
159 		return -1;
160 	if (v < INT_MIN || v > INT_MAX) {
161 		errno = ERANGE;
162 		return -1;
163 	}
164 	if (endptr == s) {
165 		errno = EINVAL;
166 		return -1;
167 	}
168 	*l = (size_t)v;
169 	return 0;
170 }
171 
172 static void
173 set_action(enum action *action, enum action value)
174 {
175 	if (*action != ACTION_DEFAULT)
176 		usage();
177 	*action = value;
178 }
179 
180 int
181 main(int argc, char **argv)
182 {
183 	struct params *p;
184 	struct params *tp;
185 	struct keygen *kg;
186 	enum action action = ACTION_DEFAULT;
187 	int	ch;
188 	const char	*cfile = NULL;
189 	const char	*outfile = NULL;
190 
191 	setprogname(*argv);
192 	eliminate_cores();
193 	if (mlockall(MCL_FUTURE))
194 		err(EXIT_FAILURE, "Can't lock memory");
195 	p = params_new();
196 	kg = NULL;
197 
198 	while ((ch = getopt(argc, argv, "CGUV:b:f:gi:k:lno:spuv")) != -1)
199 		switch (ch) {
200 		case 'C':
201 			set_action(&action, ACTION_CONFIGALL);
202 			break;
203 		case 'G':
204 			set_action(&action, ACTION_GENERATE_CONVERT);
205 			break;
206 		case 'U':
207 			set_action(&action, ACTION_UNCONFIGALL);
208 			break;
209 		case 'V':
210 			tp = params_verify_method(string_fromcharstar(optarg));
211 			if (!tp)
212 				usage();
213 			p = params_combine(p, tp);
214 			break;
215 		case 'b':
216 			{
217 				size_t size;
218 
219 				if (parse_size_t(optarg, &size) == -1)
220 					usage();
221 				tp = params_bsize(size);
222 				if (!tp)
223 					usage();
224 				p = params_combine(p, tp);
225 			}
226 			break;
227 		case 'f':
228 			if (cfile)
229 				usage();
230 			cfile = estrdup(optarg);
231 			break;
232 		case 'g':
233 			set_action(&action, ACTION_GENERATE);
234 			break;
235 		case 'i':
236 			tp = params_ivmeth(string_fromcharstar(optarg));
237 			p = params_combine(p, tp);
238 			break;
239 		case 'k':
240 			kg = keygen_method(string_fromcharstar(optarg));
241 			if (!kg)
242 				usage();
243 			keygen_addlist(&p->keygen, kg);
244 			break;
245 		case 'l':
246 			set_action(&action, ACTION_LIST);
247 			break;
248 		case 'n':
249 			nflag = 1;
250 			break;
251 		case 'o':
252 			if (outfile)
253 				usage();
254 			outfile = estrdup(optarg);
255 			break;
256 		case 'p':
257 			pflag = PFLAG_STDIN;
258 			break;
259 		case 's':
260 			set_action(&action, ACTION_CONFIGSTDIN);
261 			break;
262 
263 		case 'u':
264 			set_action(&action, ACTION_UNCONFIGURE);
265 			break;
266 		case 'v':
267 			verbose++;
268 			break;
269 		default:
270 			usage();
271 			/* NOTREACHED */
272 		}
273 
274 	argc -= optind;
275 	argv += optind;
276 
277 	if (!outfile)
278 		outfile = "";
279 	if (!cfile)
280 		cfile = "";
281 
282 	if (prog_init && prog_init() == -1)
283 		err(1, "init failed");
284 
285 	/* validate the consistency of the arguments */
286 
287 	switch (action) {
288 	case ACTION_DEFAULT:	/* ACTION_CONFIGURE is the default */
289 	case ACTION_CONFIGURE:
290 		return configure(argc, argv, p, CONFIG_FLAGS_FROMMAIN);
291 	case ACTION_UNCONFIGURE:
292 		return unconfigure(argc, argv, NULL, CONFIG_FLAGS_FROMMAIN);
293 	case ACTION_GENERATE:
294 		return generate(p, argc, argv, outfile);
295 	case ACTION_GENERATE_CONVERT:
296 		return generate_convert(p, argc, argv, outfile);
297 	case ACTION_CONFIGALL:
298 		return do_all(cfile, argc, argv, configure);
299 	case ACTION_UNCONFIGALL:
300 		return do_all(cfile, argc, argv, unconfigure);
301 	case ACTION_CONFIGSTDIN:
302 		return configure_stdin(p, argc, argv);
303 	case ACTION_LIST:
304 		return do_list(argc, argv);
305 	default:
306 		errx(EXIT_FAILURE, "undefined action");
307 		/* NOTREACHED */
308 	}
309 }
310 
311 static bits_t *
312 getkey(const char *dev, struct keygen *kg, size_t len)
313 {
314 	bits_t	*ret = NULL;
315 	bits_t	*tmp;
316 
317 	VPRINTF(3, ("getkey(\"%s\", %p, %zu) called\n", dev, kg, len));
318 	for (; kg; kg=kg->next) {
319 		switch (kg->kg_method) {
320 		case KEYGEN_STOREDKEY:
321 			tmp = getkey_storedkey(dev, kg, len);
322 			break;
323 		case KEYGEN_RANDOMKEY:
324 			tmp = getkey_randomkey(dev, kg, len, 1);
325 			break;
326 		case KEYGEN_URANDOMKEY:
327 			tmp = getkey_randomkey(dev, kg, len, 0);
328 			break;
329 		case KEYGEN_PKCS5_PBKDF2_SHA1:
330 			tmp = getkey_pkcs5_pbkdf2(dev, kg, len, 0);
331 			break;
332 		/* provide backwards compatibility for old config files */
333 		case KEYGEN_PKCS5_PBKDF2_OLD:
334 			tmp = getkey_pkcs5_pbkdf2(dev, kg, len, 1);
335 			break;
336 		case KEYGEN_SHELL_CMD:
337 			tmp = getkey_shell_cmd(dev, kg, len);
338 			break;
339 		default:
340 			warnx("unrecognised keygen method %d in getkey()",
341 			    kg->kg_method);
342 			if (ret)
343 				bits_free(ret);
344 			return NULL;
345 		}
346 
347 		if (ret)
348 			ret = bits_xor_d(tmp, ret);
349 		else
350 			ret = tmp;
351 	}
352 
353 	return ret;
354 }
355 
356 /*ARGSUSED*/
357 static bits_t *
358 getkey_storedkey(const char *target, struct keygen *kg, size_t keylen)
359 {
360 	return bits_dup(kg->kg_key);
361 }
362 
363 /*ARGSUSED*/
364 static bits_t *
365 getkey_randomkey(const char *target, struct keygen *kg, size_t keylen, int hard)
366 {
367 	return bits_getrandombits(keylen, hard);
368 }
369 
370 static char *
371 maybe_getpass(char *prompt)
372 {
373 	char	 buf[1024];
374 	char	*p = buf;
375 	char	*tmp;
376 
377 	switch (pflag) {
378 	case PFLAG_GETPASS:
379 		p = getpass(prompt);
380 		break;
381 
382 	case PFLAG_STDIN:
383 		p = fgets(buf, sizeof(buf), stdin);
384 		if (p) {
385 			tmp = strchr(p, '\n');
386 			if (tmp)
387 				*tmp = '\0';
388 		}
389 		break;
390 
391 	default:
392 		errx(EXIT_FAILURE, "pflag set inappropriately?");
393 	}
394 
395 	if (!p)
396 		err(EXIT_FAILURE, "failed to read passphrase");
397 
398 	return estrdup(p);
399 }
400 
401 /*ARGSUSED*/
402 /*
403  * XXX take, and pass through, a compat flag that indicates whether we
404  * provide backwards compatibility with a previous bug.  The previous
405  * behaviour is indicated by the keygen method pkcs5_pbkdf2, and a
406  * non-zero compat flag. The new default, and correct keygen method is
407  * called pcks5_pbkdf2/sha1.  When the old method is removed, so will
408  * be the compat argument.
409  */
410 static bits_t *
411 getkey_pkcs5_pbkdf2(const char *target, struct keygen *kg, size_t keylen,
412     int compat)
413 {
414 	bits_t		*ret;
415 	char		*passp;
416 	char		 buf[1024];
417 	u_int8_t	*tmp;
418 
419 	snprintf(buf, sizeof(buf), "%s's passphrase:", target);
420 	passp = maybe_getpass(buf);
421 	if (pkcs5_pbkdf2(&tmp, BITS2BYTES(keylen), (uint8_t *)passp,
422 	    strlen(passp),
423 	    bits_getbuf(kg->kg_salt), BITS2BYTES(bits_len(kg->kg_salt)),
424 	    kg->kg_iterations, compat)) {
425 		warnx("failed to generate PKCS#5 PBKDF2 key");
426 		return NULL;
427 	}
428 
429 	ret = bits_new(tmp, keylen);
430 	kg->kg_key = bits_dup(ret);
431 	memset(passp, 0, strlen(passp));
432 	free(passp);
433 	free(tmp);
434 	return ret;
435 }
436 
437 /*ARGSUSED*/
438 static bits_t *
439 getkey_shell_cmd(const char *target, struct keygen *kg, size_t keylen)
440 {
441 	FILE	*f;
442 	bits_t	*ret;
443 
444 	f = popen(string_tocharstar(kg->kg_cmd), "r");
445 	ret = bits_fget(f, keylen);
446 	pclose(f);
447 
448 	return ret;
449 }
450 
451 /*ARGSUSED*/
452 static int
453 unconfigure(int argc, char **argv, struct params *inparams, int flags)
454 {
455 	int	fd;
456 	int	ret;
457 	char	buf[MAXPATHLEN] = "";
458 
459 	/* only complain about additional arguments, if called from main() */
460 	if (flags == CONFIG_FLAGS_FROMMAIN && argc != 1)
461 		usage();
462 
463 	/* if called from do_all(), then ensure that 2 or 3 args exist */
464 	if (flags == CONFIG_FLAGS_FROMALL && (argc < 2 || argc > 3))
465 		return -1;
466 
467 	fd = opendisk1(*argv, O_RDWR, buf, sizeof(buf), 1, prog_open);
468 	if (fd == -1) {
469 		int saved_errno = errno;
470 
471 		warn("can't open cgd \"%s\", \"%s\"", *argv, buf);
472 
473 		/* this isn't fatal with nflag != 0 */
474 		if (!nflag)
475 			return saved_errno;
476 	}
477 
478 	VPRINTF(1, ("%s (%s): clearing\n", *argv, buf));
479 
480 	if (nflag)
481 		return 0;
482 
483 	ret = unconfigure_fd(fd);
484 	(void)prog_close(fd);
485 	return ret;
486 }
487 
488 static int
489 unconfigure_fd(int fd)
490 {
491 	struct	cgd_ioctl ci;
492 
493 	if (prog_ioctl(fd, CGDIOCCLR, &ci) == -1) {
494 		warn("ioctl");
495 		return -1;
496 	}
497 
498 	return 0;
499 }
500 
501 /*ARGSUSED*/
502 static int
503 configure(int argc, char **argv, struct params *inparams, int flags)
504 {
505 	struct params	*p;
506 	struct keygen	*kg;
507 	int		 fd;
508 	int		 loop = 0;
509 	int		 ret;
510 	char		 cgdname[PATH_MAX];
511 
512 	if (argc == 2) {
513 		char *pfile;
514 
515 		if (asprintf(&pfile, "%s/%s",
516 		    CGDCONFIG_DIR, basename(argv[1])) == -1)
517 			return -1;
518 
519 		p = params_cget(pfile);
520 		free(pfile);
521 	} else if (argc == 3) {
522 		p = params_cget(argv[2]);
523 	} else {
524 		/* print usage and exit, only if called from main() */
525 		if (flags == CONFIG_FLAGS_FROMMAIN) {
526 			warnx("wrong number of args");
527 			usage();
528 		}
529 		return -1;
530 	}
531 
532 	if (!p)
533 		return -1;
534 
535 	/*
536 	 * over-ride with command line specifications and fill in default
537 	 * values.
538 	 */
539 
540 	p = params_combine(p, inparams);
541 	ret = params_filldefaults(p);
542 	if (ret) {
543 		params_free(p);
544 		return ret;
545 	}
546 
547 	if (!params_verify(p)) {
548 		warnx("params invalid");
549 		return -1;
550 	}
551 
552 	/*
553 	 * loop over configuring the disk and checking to see if it
554 	 * verifies properly.  We open and close the disk device each
555 	 * time, because if the user passes us the block device we
556 	 * need to flush the buffer cache.
557 	 *
558 	 * We only loop if one of the verification methods prompts for
559 	 * a password.
560 	 */
561 
562 	for (kg = p->keygen; pflag == PFLAG_GETPASS && kg; kg = kg->next)
563 		if ((kg->kg_method == KEYGEN_PKCS5_PBKDF2_SHA1) ||
564 		    (kg->kg_method == KEYGEN_PKCS5_PBKDF2_OLD )) {
565 			loop = 1;
566 			break;
567 		}
568 
569 	for (;;) {
570 		fd = opendisk_werror(argv[0], cgdname, sizeof(cgdname));
571 		if (fd == -1)
572 			return -1;
573 
574 		if (p->key)
575 			bits_free(p->key);
576 
577 		p->key = getkey(argv[1], p->keygen, p->keylen);
578 		if (!p->key)
579 			goto bail_err;
580 
581 		ret = configure_params(fd, cgdname, argv[1], p);
582 		if (ret)
583 			goto bail_err;
584 
585 		ret = verify(p, fd);
586 		if (ret == -1)
587 			goto bail_err;
588 		if (!ret)
589 			break;
590 
591 		(void)unconfigure_fd(fd);
592 		(void)prog_close(fd);
593 
594 		if (!loop) {
595 			warnx("verification failed permanently");
596 			goto bail_err;
597 		}
598 
599 		warnx("verification failed, please reenter passphrase");
600 	}
601 
602 	params_free(p);
603 	(void)prog_close(fd);
604 	return 0;
605 bail_err:
606 	params_free(p);
607 	(void)prog_close(fd);
608 	return -1;
609 }
610 
611 static int
612 configure_stdin(struct params *p, int argc, char **argv)
613 {
614 	int	fd;
615 	int	ret;
616 	char	cgdname[PATH_MAX];
617 
618 	if (argc < 3 || argc > 4)
619 		usage();
620 
621 	p->algorithm = string_fromcharstar(argv[2]);
622 	if (argc > 3) {
623 		size_t keylen;
624 
625 		if (parse_size_t(argv[3], &keylen) == -1) {
626 			warn("failed to parse key length");
627 			return -1;
628 		}
629 		p->keylen = keylen;
630 	}
631 
632 	ret = params_filldefaults(p);
633 	if (ret)
634 		return ret;
635 
636 	fd = opendisk_werror(argv[0], cgdname, sizeof(cgdname));
637 	if (fd == -1)
638 		return -1;
639 
640 	p->key = bits_fget(stdin, p->keylen);
641 	if (!p->key) {
642 		warnx("failed to read key from stdin");
643 		return -1;
644 	}
645 
646 	return configure_params(fd, cgdname, argv[1], p);
647 }
648 
649 static int
650 opendisk_werror(const char *cgd, char *buf, size_t buflen)
651 {
652 	int	fd;
653 
654 	VPRINTF(3, ("opendisk_werror(%s, %s, %zu) called.\n", cgd, buf, buflen));
655 
656 	/* sanity */
657 	if (!cgd || !buf)
658 		return -1;
659 
660 	if (nflag) {
661 		if (strlcpy(buf, cgd, buflen) >= buflen)
662 			return -1;
663 		return 0;
664 	}
665 
666 	fd = opendisk1(cgd, O_RDWR, buf, buflen, 0, prog_open);
667 	if (fd == -1)
668 		warnx("can't open cgd \"%s\", \"%s\"", cgd, buf);
669 
670 	return fd;
671 }
672 
673 static int
674 configure_params(int fd, const char *cgd, const char *dev, struct params *p)
675 {
676 	struct cgd_ioctl ci;
677 
678 	/* sanity */
679 	if (!cgd || !dev)
680 		return -1;
681 
682 	(void)memset(&ci, 0x0, sizeof(ci));
683 	ci.ci_disk = dev;
684 	ci.ci_alg = string_tocharstar(p->algorithm);
685 	ci.ci_ivmethod = string_tocharstar(p->ivmeth);
686 	ci.ci_key = bits_getbuf(p->key);
687 	ci.ci_keylen = p->keylen;
688 	ci.ci_blocksize = p->bsize;
689 
690 	VPRINTF(1, ("    with alg %s keylen %zu blocksize %zu ivmethod %s\n",
691 	    string_tocharstar(p->algorithm), p->keylen, p->bsize,
692 	    string_tocharstar(p->ivmeth)));
693 	VPRINTF(2, ("key: "));
694 	VERBOSE(2, bits_fprint(stdout, p->key));
695 	VPRINTF(2, ("\n"));
696 
697 	if (nflag)
698 		return 0;
699 
700 	if (prog_ioctl(fd, CGDIOCSET, &ci) == -1) {
701 		int saved_errno = errno;
702 		warn("ioctl");
703 		return saved_errno;
704 	}
705 
706 	return 0;
707 }
708 
709 /*
710  * verify returns 0 for success, -1 for unrecoverable error, or 1 for retry.
711  */
712 
713 #define SCANSIZE	8192
714 
715 static int
716 verify(struct params *p, int fd)
717 {
718 
719 	switch (p->verify_method) {
720 	case VERIFY_NONE:
721 		return 0;
722 	case VERIFY_DISKLABEL:
723 		return verify_disklabel(fd);
724 	case VERIFY_FFS:
725 		return verify_ffs(fd);
726 	case VERIFY_REENTER:
727 		return verify_reenter(p);
728 	default:
729 		warnx("unimplemented verification method");
730 		return -1;
731 	}
732 }
733 
734 static int
735 verify_disklabel(int fd)
736 {
737 	struct	disklabel l;
738 	ssize_t	ret;
739 	char	buf[SCANSIZE];
740 
741 	/*
742 	 * we simply scan the first few blocks for a disklabel, ignoring
743 	 * any MBR/filecore sorts of logic.  MSDOS and RiscOS can't read
744 	 * a cgd, anyway, so it is unlikely that there will be non-native
745 	 * partition information.
746 	 */
747 
748 	ret = prog_pread(fd, buf, 8192, 0);
749 	if (ret < 0) {
750 		warn("can't read disklabel area");
751 		return -1;
752 	}
753 
754 	/* now scan for the disklabel */
755 
756 	return disklabel_scan(&l, buf, (size_t)ret);
757 }
758 
759 static off_t sblock_try[] = SBLOCKSEARCH;
760 
761 static int
762 verify_ffs(int fd)
763 {
764 	size_t	i;
765 
766 	for (i = 0; sblock_try[i] != -1; i++) {
767 		union {
768 		    char	buf[SBLOCKSIZE];
769 		    struct	fs fs;
770 		} u;
771 		ssize_t ret;
772 
773 		ret = prog_pread(fd, &u, sizeof(u), sblock_try[i]);
774 		if (ret < 0) {
775 			warn("pread");
776 			break;
777 		} else if ((size_t)ret < sizeof(u)) {
778 			warnx("pread: incomplete block");
779 			break;
780 		}
781 		switch (u.fs.fs_magic) {
782 		case FS_UFS1_MAGIC:
783 		case FS_UFS2_MAGIC:
784 		case FS_UFS1_MAGIC_SWAPPED:
785 		case FS_UFS2_MAGIC_SWAPPED:
786 			return 0;
787 		default:
788 			continue;
789 		}
790 	}
791 
792 	return 1;	/* failure */
793 }
794 
795 static int
796 verify_reenter(struct params *p)
797 {
798 	struct keygen *kg;
799 	bits_t *orig_key, *key;
800 	int ret;
801 
802 	ret = 0;
803 	for (kg = p->keygen; kg && !ret; kg = kg->next) {
804 		if ((kg->kg_method != KEYGEN_PKCS5_PBKDF2_SHA1) &&
805 		    (kg->kg_method != KEYGEN_PKCS5_PBKDF2_OLD ))
806 			continue;
807 
808 		orig_key = kg->kg_key;
809 		kg->kg_key = NULL;
810 
811 		/* add a compat flag till the _OLD method goes away */
812 		key = getkey_pkcs5_pbkdf2("re-enter device", kg,
813 			bits_len(orig_key), kg->kg_method == KEYGEN_PKCS5_PBKDF2_OLD);
814 		ret = !bits_match(key, orig_key);
815 
816 		bits_free(key);
817 		bits_free(kg->kg_key);
818 		kg->kg_key = orig_key;
819 	}
820 
821 	return ret;
822 }
823 
824 static int
825 generate(struct params *p, int argc, char **argv, const char *outfile)
826 {
827 	int	 ret;
828 
829 	if (argc < 1 || argc > 2)
830 		usage();
831 
832 	p->algorithm = string_fromcharstar(argv[0]);
833 	if (argc > 1) {
834 		size_t keylen;
835 
836 		if (parse_size_t(argv[1], &keylen) == -1) {
837 			warn("Failed to parse key length");
838 			return -1;
839 		}
840 		p->keylen = keylen;
841 	}
842 
843 	ret = params_filldefaults(p);
844 	if (ret)
845 		return ret;
846 
847 	if (!p->keygen) {
848 		p->keygen = keygen_generate(KEYGEN_PKCS5_PBKDF2_SHA1);
849 		if (!p->keygen)
850 			return -1;
851 	}
852 
853 	if (keygen_filldefaults(p->keygen, p->keylen)) {
854 		warnx("Failed to generate defaults for keygen");
855 		return -1;
856 	}
857 
858 	if (!params_verify(p)) {
859 		warnx("invalid parameters generated");
860 		return -1;
861 	}
862 
863 	return params_cput(p, outfile);
864 }
865 
866 static int
867 generate_convert(struct params *p, int argc, char **argv, const char *outfile)
868 {
869 	struct params	*oldp;
870 	struct keygen	*kg;
871 
872 	if (argc != 1)
873 		usage();
874 
875 	oldp = params_cget(*argv);
876 	if (!oldp)
877 		return -1;
878 
879 	/* for sanity, we ensure that none of the keygens are randomkey */
880 	for (kg=p->keygen; kg; kg=kg->next)
881 		if ((kg->kg_method == KEYGEN_RANDOMKEY) ||
882 		    (kg->kg_method == KEYGEN_URANDOMKEY)) {
883 			warnx("can't preserve randomly generated key");
884 			goto bail;
885 		}
886 	for (kg=oldp->keygen; kg; kg=kg->next)
887 		if ((kg->kg_method == KEYGEN_RANDOMKEY) ||
888 		    (kg->kg_method == KEYGEN_URANDOMKEY)) {
889 			warnx("can't preserve randomly generated key");
890 			goto bail;
891 		}
892 
893 	if (!params_verify(oldp)) {
894 		warnx("invalid old parameters file \"%s\"", *argv);
895 		return -1;
896 	}
897 
898 	oldp->key = getkey("old file", oldp->keygen, oldp->keylen);
899 
900 	/* we copy across the non-keygen info, here. */
901 
902 	string_free(p->algorithm);
903 	string_free(p->ivmeth);
904 
905 	p->algorithm = string_dup(oldp->algorithm);
906 	p->ivmeth = string_dup(oldp->ivmeth);
907 	p->keylen = oldp->keylen;
908 	p->bsize = oldp->bsize;
909 	if (p->verify_method == VERIFY_UNKNOWN)
910 		p->verify_method = oldp->verify_method;
911 
912 	params_free(oldp);
913 
914 	if (!p->keygen) {
915 		p->keygen = keygen_generate(KEYGEN_PKCS5_PBKDF2_SHA1);
916 		if (!p->keygen)
917 			return -1;
918 	}
919 	(void)params_filldefaults(p);
920 	(void)keygen_filldefaults(p->keygen, p->keylen);
921 	p->key = getkey("new file", p->keygen, p->keylen);
922 
923 	kg = keygen_generate(KEYGEN_STOREDKEY);
924 	kg->kg_key = bits_xor(p->key, oldp->key);
925 	keygen_addlist(&p->keygen, kg);
926 
927 	if (!params_verify(p)) {
928 		warnx("can't generate new parameters file");
929 		return -1;
930 	}
931 
932 	return params_cput(p, outfile);
933 bail:
934 	params_free(oldp);
935 	return -1;
936 }
937 
938 static int
939 /*ARGSUSED*/
940 do_all(const char *cfile, int argc, char **argv,
941        int (*conf)(int, char **, struct params *, int))
942 {
943 	FILE		 *f;
944 	size_t		  len;
945 	size_t		  lineno;
946 	int		  my_argc;
947 	int		  ret;
948 	const char	 *fn;
949 	char		 *line;
950 	char		**my_argv;
951 
952 	if (argc > 0)
953 		usage();
954 
955 	if (!cfile[0])
956 		fn = CGDCONFIG_CFILE;
957 	else
958 		fn = cfile;
959 
960 	f = fopen(fn, "r");
961 	if (!f) {
962 		warn("could not open config file \"%s\"", fn);
963 		return -1;
964 	}
965 
966 	ret = chdir(CGDCONFIG_DIR);
967 	if (ret == -1)
968 		warn("could not chdir to %s", CGDCONFIG_DIR);
969 
970 	ret = 0;
971 	lineno = 0;
972 	for (;;) {
973 		line = fparseln(f, &len, &lineno, "\\\\#", FPARSELN_UNESCALL);
974 		if (!line)
975 			break;
976 		if (!*line)
977 			continue;
978 
979 		my_argv = words(line, &my_argc);
980 		ret = conf(my_argc, my_argv, NULL, CONFIG_FLAGS_FROMALL);
981 		if (ret) {
982 			warnx("action failed on \"%s\" line %lu", fn,
983 			    (u_long)lineno);
984 			break;
985 		}
986 		words_free(my_argv, my_argc);
987 	}
988 	return ret;
989 }
990 
991 static const char *
992 iv_method(int mode)
993 {
994 
995 	switch (mode) {
996 	case CGD_CIPHER_CBC_ENCBLKNO8:
997 		return "encblkno8";
998 	case CGD_CIPHER_CBC_ENCBLKNO1:
999 		return "encblkno1";
1000 	default:
1001 		return "unknown";
1002 	}
1003 }
1004 
1005 
1006 static void
1007 show(const char *dev) {
1008 	char path[64];
1009 	struct cgd_user cgu;
1010 	int fd;
1011 
1012 	fd = opendisk(dev, O_RDONLY, path, sizeof(path), 0);
1013 	if (fd == -1) {
1014 		warn("open: %s", dev);
1015 		return;
1016 	}
1017 
1018 	cgu.cgu_unit = -1;
1019 	if (prog_ioctl(fd, CGDIOCGET, &cgu) == -1) {
1020 		close(fd);
1021 		err(1, "CGDIOCGET");
1022 	}
1023 
1024 	printf("%s: ", dev);
1025 
1026 	if (cgu.cgu_dev == 0) {
1027 		printf("not in use");
1028 		goto out;
1029 	}
1030 
1031 	dev = devname(cgu.cgu_dev, S_IFBLK);
1032 	if (dev != NULL)
1033 		printf("%s ", dev);
1034 	else
1035 		printf("dev %llu,%llu ", (unsigned long long)major(cgu.cgu_dev),
1036 		    (unsigned long long)minor(cgu.cgu_dev));
1037 
1038 	if (verbose)
1039 		printf("%s ", cgu.cgu_alg);
1040 	if (verbose > 1) {
1041 		printf("keylen %d ", cgu.cgu_keylen);
1042 		printf("blksize %zd ", cgu.cgu_blocksize);
1043 		printf("%s ", iv_method(cgu.cgu_mode));
1044 	}
1045 
1046 out:
1047 	putchar('\n');
1048 	close(fd);
1049 }
1050 
1051 static int
1052 do_list(int argc, char **argv)
1053 {
1054 
1055 	if (argc != 0 && argc != 1)
1056 		usage();
1057 
1058 	if (argc) {
1059 		show(argv[0]);
1060 		return 0;
1061 	}
1062 
1063 	DIR *dirp;
1064 	struct dirent *dp;
1065 	__BITMAP_TYPE(, uint32_t, 65536) bm;
1066 
1067 	__BITMAP_ZERO(&bm);
1068 
1069 	if ((dirp = opendir(_PATH_DEV)) == NULL)
1070 		err(1, "opendir: %s", _PATH_DEV);
1071 
1072 	while ((dp = readdir(dirp)) != NULL) {
1073 		char *ep;
1074 		if (strncmp(dp->d_name, "rcgd", 4) != 0)
1075 			continue;
1076 		errno = 0;
1077 		int n = (int)strtol(dp->d_name + 4, &ep, 0);
1078 		if (ep == dp->d_name + 4 || errno != 0) {
1079 			warnx("bad name %s", dp->d_name);
1080 			continue;
1081 		}
1082 		*ep = '\0';
1083 		if (__BITMAP_ISSET(n, &bm))
1084 			continue;
1085 		__BITMAP_SET(n, &bm);
1086 		show(dp->d_name + 1);
1087 	}
1088 
1089 	closedir(dirp);
1090 	return 0;
1091 }
1092 
1093 static void
1094 eliminate_cores(void)
1095 {
1096 	struct rlimit	rlp;
1097 
1098 	rlp.rlim_cur = 0;
1099 	rlp.rlim_max = 0;
1100 	if (setrlimit(RLIMIT_CORE, &rlp) == -1)
1101 		err(EXIT_FAILURE, "Can't disable cores");
1102 }
1103