xref: /openbsd/sbin/bioctl/bioctl.c (revision 178701b6)
1 /* $OpenBSD: bioctl.c,v 1.158 2024/07/15 05:36:08 jmc Exp $ */
2 
3 /*
4  * Copyright (c) 2004, 2005 Marco Peereboom
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
20  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  */
29 
30 #include <sys/param.h>	/* NODEV */
31 #include <sys/ioctl.h>
32 #include <sys/dkio.h>
33 #include <sys/stat.h>
34 #include <dev/softraidvar.h>
35 #include <dev/biovar.h>
36 
37 #include <errno.h>
38 #include <err.h>
39 #include <fcntl.h>
40 #include <util.h>
41 #include <ctype.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <time.h>
46 #include <unistd.h>
47 #include <limits.h>
48 #include <vis.h>
49 #include <readpassphrase.h>
50 
51 struct locator {
52 	int		channel;
53 	int		target;
54 	int		lun;
55 };
56 
57 struct timing {
58 	int		interval;
59 	int		start;
60 };
61 
62 static void __dead	usage(void);
63 const char 		*str2locator(const char *, struct locator *);
64 const char 		*str2patrol(const char *, struct timing *);
65 void			bio_status(struct bio_status *);
66 int			bio_parse_devlist(char *, dev_t *);
67 void			bio_kdf_derive(struct sr_crypto_kdfinfo *,
68 			    struct sr_crypto_pbkdf *, char *, int);
69 void			bio_kdf_generate(struct sr_crypto_kdfinfo *);
70 int			bcrypt_pbkdf_autorounds(void);
71 void			derive_key(u_int32_t, int, u_int8_t *, size_t,
72 			    u_int8_t *, size_t, char *, int);
73 
74 void			bio_inq(char *);
75 void			bio_alarm(char *);
76 int			bio_getvolbyname(char *);
77 void			bio_setstate(char *, int, char *);
78 void			bio_setblink(char *, char *, int);
79 void			bio_blink(char *, int, int);
80 void			bio_createraid(u_int16_t, char *, char *);
81 void			bio_deleteraid(char *);
82 void			bio_changepass(char *);
83 u_int32_t		bio_createflags(char *);
84 char			*bio_vis(char *);
85 void			bio_diskinq(char *);
86 void			bio_patrol(char *);
87 
88 int			devh = -1;
89 int			human;
90 int			verbose;
91 u_int32_t		cflags = 0;
92 int			rflag = -1;	/* auto */
93 char			*passfile;
94 
95 void			*bio_cookie;
96 
97 int interactive = 1;
98 
99 int
main(int argc,char * argv[])100 main(int argc, char *argv[])
101 {
102 	struct bio_locate	bl;
103 	u_int64_t		func = 0;
104 	char			*devicename = NULL;
105 	char			*realname = NULL, *al_arg = NULL;
106 	char			*bl_arg = NULL, *dev_list = NULL;
107 	char			*key_disk = NULL;
108 	const char		*errstr;
109 	int			ch, blink = 0, changepass = 0, diskinq = 0;
110 	int			ss_func = 0;
111 	u_int16_t		cr_level = 0;
112 	int			biodev = 0;
113 
114 	if (argc < 2)
115 		usage();
116 
117 	while ((ch = getopt(argc, argv, "a:b:C:c:dH:hik:l:O:Pp:qr:R:st:u:v")) !=
118 	    -1) {
119 		switch (ch) {
120 		case 'a': /* alarm */
121 			func |= BIOC_ALARM;
122 			al_arg = optarg;
123 			break;
124 		case 'b': /* blink */
125 			func |= BIOC_BLINK;
126 			blink = BIOC_SBBLINK;
127 			bl_arg = optarg;
128 			break;
129 		case 'C': /* creation flags */
130 			cflags = bio_createflags(optarg);
131 			break;
132 		case 'c': /* create */
133 			func |= BIOC_CREATERAID;
134 			if (strcmp(optarg, "1C") == 0) {
135 				cr_level = 0x1C;
136 			} else if (isdigit((unsigned char)*optarg)) {
137 				cr_level = strtonum(optarg, 0, 10, &errstr);
138 				if (errstr != NULL)
139 					errx(1, "Invalid RAID level");
140 			} else if (strlen(optarg) == 1) {
141 				cr_level = *optarg;
142 			} else {
143 				errx(1, "Invalid RAID level");
144 			}
145 			break;
146 		case 'd':
147 			/* delete volume */
148 			func |= BIOC_DELETERAID;
149 			break;
150 		case 'u': /* unblink */
151 			func |= BIOC_BLINK;
152 			blink = BIOC_SBUNBLINK;
153 			bl_arg = optarg;
154 			break;
155 		case 'H': /* set hotspare */
156 			func |= BIOC_SETSTATE;
157 			ss_func = BIOC_SSHOTSPARE;
158 			al_arg = optarg;
159 			break;
160 		case 'h':
161 			human = 1;
162 			break;
163 		case 'i': /* inquiry */
164 			func |= BIOC_INQ;
165 			break;
166 		case 'k': /* Key disk. */
167 			key_disk = optarg;
168 			break;
169 		case 'l': /* device list */
170 			func |= BIOC_DEVLIST;
171 			dev_list = optarg;
172 			break;
173 		case 'P':
174 			/* Change passphrase. */
175 			changepass = 1;
176 			break;
177 		case 'p':
178 			passfile = optarg;
179 			break;
180 		case 'r':
181 			if (strcmp(optarg, "auto") == 0) {
182 				rflag = -1;
183 				break;
184 			}
185 			rflag = strtonum(optarg, 16, 1<<30, &errstr);
186 			if (errstr != NULL)
187 				errx(1, "number of KDF rounds is %s: %s",
188 				    errstr, optarg);
189 			break;
190 		case 'O':
191 			/* set a chunk to offline */
192 			func |= BIOC_SETSTATE;
193 			ss_func = BIOC_SSOFFLINE;
194 			al_arg = optarg;
195 			break;
196 		case 'R':
197 			/* rebuild to provided chunk/CTL */
198 			func |= BIOC_SETSTATE;
199 			ss_func = BIOC_SSREBUILD;
200 			al_arg = optarg;
201 			break;
202 		case 's':
203 			interactive = 0;
204 			break;
205 		case 't': /* patrol */
206 			func |= BIOC_PATROL;
207 			al_arg = optarg;
208 			break;
209 		case 'v':
210 			verbose = 1;
211 			break;
212 		case 'q':
213 			diskinq = 1;
214 			break;
215 		default:
216 			usage();
217 			/* NOTREACHED */
218 		}
219 	}
220 	argc -= optind;
221 	argv += optind;
222 
223 	if (argc != 1 || (changepass && func != 0))
224 		usage();
225 
226 	if (func == 0)
227 		func |= BIOC_INQ;
228 
229 	devicename = argv[0];
230 	if (devicename == NULL)
231 		errx(1, "need device");
232 
233 	devh = opendev(devicename, O_RDWR, OPENDEV_PART, &realname);
234 	if (devh == -1) {
235 		devh = open("/dev/bio", O_RDWR);
236 		if (devh == -1)
237 			err(1, "Can't open %s", "/dev/bio");
238 
239 		memset(&bl, 0, sizeof(bl));
240 		bl.bl_name = devicename;
241 		if (ioctl(devh, BIOCLOCATE, &bl) == -1)
242 			errx(1, "Can't locate %s device via %s",
243 			    bl.bl_name, "/dev/bio");
244 
245 		bio_cookie = bl.bl_bio.bio_cookie;
246 		biodev = 1;
247 		devicename = NULL;
248 	}
249 
250 	if (diskinq) {
251 		bio_diskinq(devicename);
252 	} else if (changepass && !biodev) {
253 		bio_changepass(devicename);
254 	} else if (func & BIOC_INQ) {
255 		bio_inq(devicename);
256 	} else if (func == BIOC_ALARM) {
257 		bio_alarm(al_arg);
258 	} else if (func == BIOC_BLINK) {
259 		bio_setblink(devicename, bl_arg, blink);
260 	} else if (func == BIOC_PATROL) {
261 		bio_patrol(al_arg);
262 	} else if (func == BIOC_SETSTATE) {
263 		bio_setstate(al_arg, ss_func, argv[0]);
264 	} else if (func == BIOC_DELETERAID && !biodev) {
265 		bio_deleteraid(devicename);
266 	} else if (func & BIOC_CREATERAID || func & BIOC_DEVLIST) {
267 		if (!(func & BIOC_CREATERAID))
268 			errx(1, "need -c parameter");
269 		if (!(func & BIOC_DEVLIST))
270 			errx(1, "need -l parameter");
271 		if (!biodev)
272 			errx(1, "must use bio device");
273 		bio_createraid(cr_level, dev_list, key_disk);
274 	}
275 
276 	return (0);
277 }
278 
279 static void __dead
usage(void)280 usage(void)
281 {
282 	extern char		*__progname;
283 
284 	fprintf(stderr,
285 		"usage: %s [-hiqv] [-a alarm-function] "
286 		"[-b channel:target[.lun]]\n"
287 		"\t[-H channel:target[.lun]] "
288 		"[-R chunk | channel:target[.lun]]\n"
289 		"\t[-t patrol-function] "
290 		"[-u channel:target[.lun]] "
291 		"device\n\n"
292 		"       %s [-dhiPqsv] "
293 		"[-C flag[,...]] [-c raidlevel] [-k keydisk]\n"
294 		"\t[-l chunk[,...]] "
295 		"[-O device | channel:target[.lun]] [-p passfile]\n"
296 		"\t[-R chunk | channel:target[.lun]] [-r rounds] "
297 		"device\n", __progname, __progname);
298 
299 	exit(1);
300 }
301 
302 const char *
str2locator(const char * string,struct locator * location)303 str2locator(const char *string, struct locator *location)
304 {
305 	const char		*errstr;
306 	char			parse[80], *targ, *lun;
307 
308 	strlcpy(parse, string, sizeof parse);
309 	targ = strchr(parse, ':');
310 	if (targ == NULL)
311 		return ("target not specified");
312 	*targ++ = '\0';
313 
314 	lun = strchr(targ, '.');
315 	if (lun != NULL) {
316 		*lun++ = '\0';
317 		location->lun = strtonum(lun, 0, 256, &errstr);
318 		if (errstr)
319 			return (errstr);
320 	} else
321 		location->lun = 0;
322 
323 	location->target = strtonum(targ, 0, 256, &errstr);
324 	if (errstr)
325 		return (errstr);
326 	location->channel = strtonum(parse, 0, 256, &errstr);
327 	if (errstr)
328 		return (errstr);
329 	return (NULL);
330 }
331 
332 const char *
str2patrol(const char * string,struct timing * timing)333 str2patrol(const char *string, struct timing *timing)
334 {
335 	const char		*errstr;
336 	char			parse[80], *interval = NULL, *start = NULL;
337 
338 	timing->interval = 0;
339 	timing->start = 0;
340 
341 	strlcpy(parse, string, sizeof parse);
342 
343 	interval = strchr(parse, '.');
344 	if (interval != NULL) {
345 		*interval++ = '\0';
346 		start = strchr(interval, '.');
347 		if (start != NULL)
348 			*start++ = '\0';
349 	}
350 	if (interval != NULL) {
351 		/* -1 == continuously */
352 		timing->interval = strtonum(interval, -1, INT_MAX, &errstr);
353 		if (errstr)
354 			return (errstr);
355 	}
356 	if (start != NULL) {
357 		timing->start = strtonum(start, 0, INT_MAX, &errstr);
358 		if (errstr)
359 			return (errstr);
360 	}
361 
362 	return (NULL);
363 }
364 
365 void
bio_status(struct bio_status * bs)366 bio_status(struct bio_status *bs)
367 {
368 	extern char		*__progname;
369 	char			*prefix;
370 	int			i;
371 
372 	if (strlen(bs->bs_controller))
373 		prefix = bs->bs_controller;
374 	else
375 		prefix = __progname;
376 
377 	for (i = 0; i < bs->bs_msg_count; i++)
378 		fprintf(bs->bs_msgs[i].bm_type == BIO_MSG_INFO ?
379 		    stdout : stderr, "%s: %s\n", prefix, bs->bs_msgs[i].bm_msg);
380 
381 	if (bs->bs_status == BIO_STATUS_ERROR) {
382 		if (bs->bs_msg_count == 0)
383 			errx(1, "unknown error");
384 		else
385 			exit(1);
386 	}
387 }
388 
389 void
bio_inq(char * name)390 bio_inq(char *name)
391 {
392 	char 			*status, *cache;
393 	char			size[64], scsiname[16], volname[32];
394 	char			percent[20], seconds[20];
395 	int			i, d, volheader, hotspare, unused;
396 	char			encname[16], serial[32];
397 	struct bioc_inq		bi;
398 	struct bioc_vol		bv;
399 	struct bioc_disk	bd;
400 
401 	memset(&bi, 0, sizeof(bi));
402 
403 	bi.bi_bio.bio_cookie = bio_cookie;
404 
405 	if (ioctl(devh, BIOCINQ, &bi) == -1) {
406 		if (errno == ENOTTY)
407 			bio_diskinq(name);
408 		else
409 			err(1, "BIOCINQ");
410 		return;
411 	}
412 
413 	bio_status(&bi.bi_bio.bio_status);
414 
415 	volheader = 0;
416 	for (i = 0; i < bi.bi_novol; i++) {
417 		memset(&bv, 0, sizeof(bv));
418 		bv.bv_bio.bio_cookie = bio_cookie;
419 		bv.bv_volid = i;
420 		bv.bv_percent = -1;
421 		bv.bv_seconds = 0;
422 
423 		if (ioctl(devh, BIOCVOL, &bv) == -1)
424 			err(1, "BIOCVOL");
425 
426 		bio_status(&bv.bv_bio.bio_status);
427 
428 		if (name && strcmp(name, bv.bv_dev) != 0)
429 			continue;
430 
431 		if (!volheader) {
432 			volheader = 1;
433 			printf("%-11s %-10s %14s %-8s\n",
434 			    "Volume", "Status", "Size", "Device");
435 		}
436 
437 		percent[0] = '\0';
438 		seconds[0] = '\0';
439 		if (bv.bv_percent != -1)
440 			snprintf(percent, sizeof percent,
441 			    " %d%% done", bv.bv_percent);
442 		if (bv.bv_seconds)
443 			snprintf(seconds, sizeof seconds,
444 			    " %u seconds", bv.bv_seconds);
445 		switch (bv.bv_status) {
446 		case BIOC_SVONLINE:
447 			status = BIOC_SVONLINE_S;
448 			break;
449 		case BIOC_SVOFFLINE:
450 			status = BIOC_SVOFFLINE_S;
451 			break;
452 		case BIOC_SVDEGRADED:
453 			status = BIOC_SVDEGRADED_S;
454 			break;
455 		case BIOC_SVBUILDING:
456 			status = BIOC_SVBUILDING_S;
457 			break;
458 		case BIOC_SVREBUILD:
459 			status = BIOC_SVREBUILD_S;
460 			break;
461 		case BIOC_SVSCRUB:
462 			status = BIOC_SVSCRUB_S;
463 			break;
464 		case BIOC_SVINVALID:
465 		default:
466 			status = BIOC_SVINVALID_S;
467 		}
468 		switch (bv.bv_cache) {
469 		case BIOC_CVWRITEBACK:
470 			cache = BIOC_CVWRITEBACK_S;
471 			break;
472 		case BIOC_CVWRITETHROUGH:
473 			cache = BIOC_CVWRITETHROUGH_S;
474 			break;
475 		case BIOC_CVUNKNOWN:
476 		default:
477 			cache = BIOC_CVUNKNOWN_S;
478 		}
479 
480 		snprintf(volname, sizeof volname, "%s %u",
481 		    bi.bi_dev, bv.bv_volid);
482 
483 		unused = 0;
484 		hotspare = 0;
485 		if (bv.bv_level == -1 && bv.bv_nodisk == 1)
486 			hotspare = 1;
487 		else if (bv.bv_level == -2 && bv.bv_nodisk == 1)
488 			unused = 1;
489 		else {
490 			if (human)
491 				fmt_scaled(bv.bv_size, size);
492 			else
493 				snprintf(size, sizeof size, "%14llu",
494 				    bv.bv_size);
495 			printf("%11s %-10s %14s %-7s ",
496 			    volname, status, size, bv.bv_dev);
497 			switch (bv.bv_level) {
498 			case 'C':
499 				printf("CRYPTO%s%s\n",
500 				    percent, seconds);
501 				break;
502 			case 'c':
503 				printf("CONCAT%s%s\n",
504 				    percent, seconds);
505 				break;
506 			case 0x1C:
507 			case 0x1E:
508 				printf("RAID%X%s%s %s\n",
509 				    bv.bv_level, percent, seconds, cache);
510 				break;
511 			default:
512 				printf("RAID%u%s%s %s\n",
513 				    bv.bv_level, percent, seconds, cache);
514 				break;
515 			}
516 
517 		}
518 
519 		for (d = 0; d < bv.bv_nodisk; d++) {
520 			memset(&bd, 0, sizeof(bd));
521 			bd.bd_bio.bio_cookie = bio_cookie;
522 			bd.bd_diskid = d;
523 			bd.bd_volid = i;
524 			bd.bd_patrol.bdp_percent = -1;
525 			bd.bd_patrol.bdp_seconds = 0;
526 
527 			if (ioctl(devh, BIOCDISK, &bd) == -1)
528 				err(1, "BIOCDISK");
529 
530 			bio_status(&bd.bd_bio.bio_status);
531 
532 			switch (bd.bd_status) {
533 			case BIOC_SDONLINE:
534 				status = BIOC_SDONLINE_S;
535 				break;
536 			case BIOC_SDOFFLINE:
537 				status = BIOC_SDOFFLINE_S;
538 				break;
539 			case BIOC_SDFAILED:
540 				status = BIOC_SDFAILED_S;
541 				break;
542 			case BIOC_SDREBUILD:
543 				status = BIOC_SDREBUILD_S;
544 				break;
545 			case BIOC_SDHOTSPARE:
546 				status = BIOC_SDHOTSPARE_S;
547 				break;
548 			case BIOC_SDUNUSED:
549 				status = BIOC_SDUNUSED_S;
550 				break;
551 			case BIOC_SDSCRUB:
552 				status = BIOC_SDSCRUB_S;
553 				break;
554 			case BIOC_SDINVALID:
555 			default:
556 				status = BIOC_SDINVALID_S;
557 			}
558 
559 			if (hotspare || unused)
560 				;	/* use volname from parent volume */
561 			else
562 				snprintf(volname, sizeof volname, "    %3u",
563 				    bd.bd_diskid);
564 
565 			if (bv.bv_level == 'C' && bd.bd_size == 0)
566 				snprintf(size, sizeof size, "%14s", "key disk");
567 			else if (human)
568 				fmt_scaled(bd.bd_size, size);
569 			else
570 				snprintf(size, sizeof size, "%14llu",
571 				    bd.bd_size);
572 			snprintf(scsiname, sizeof scsiname,
573 			    "%u:%u.%u",
574 			    bd.bd_channel, bd.bd_target, bd.bd_lun);
575 			if (bd.bd_procdev[0])
576 				strlcpy(encname, bd.bd_procdev, sizeof encname);
577 			else
578 				strlcpy(encname, "noencl", sizeof encname);
579 			if (bd.bd_serial[0])
580 				strlcpy(serial, bd.bd_serial, sizeof serial);
581 			else
582 				strlcpy(serial, "unknown serial", sizeof serial);
583 
584 			percent[0] = '\0';
585 			seconds[0] = '\0';
586 			if (bd.bd_patrol.bdp_percent != -1)
587 				snprintf(percent, sizeof percent,
588 				    " patrol %d%% done", bd.bd_patrol.bdp_percent);
589 			if (bd.bd_patrol.bdp_seconds)
590 				snprintf(seconds, sizeof seconds,
591 				    " %u seconds", bd.bd_patrol.bdp_seconds);
592 
593 			printf("%11s %-10s %14s %-7s %-6s <%s>\n",
594 			    volname, status, size, scsiname, encname,
595 			    bd.bd_vendor);
596 			if (verbose)
597 				printf("%11s %-10s %14s %-7s %-6s '%s'%s%s\n",
598 				    "", "", "", "", "", serial, percent, seconds);
599 		}
600 	}
601 }
602 
603 void
bio_alarm(char * arg)604 bio_alarm(char *arg)
605 {
606 	struct bioc_alarm	ba;
607 
608 	memset(&ba, 0, sizeof(ba));
609 	ba.ba_bio.bio_cookie = bio_cookie;
610 
611 	switch (arg[0]) {
612 	case 'q': /* silence alarm */
613 		/* FALLTHROUGH */
614 	case 's':
615 		ba.ba_opcode = BIOC_SASILENCE;
616 		break;
617 
618 	case 'e': /* enable alarm */
619 		ba.ba_opcode = BIOC_SAENABLE;
620 		break;
621 
622 	case 'd': /* disable alarm */
623 		ba.ba_opcode = BIOC_SADISABLE;
624 		break;
625 
626 	case 't': /* test alarm */
627 		ba.ba_opcode = BIOC_SATEST;
628 		break;
629 
630 	case 'g': /* get alarm state */
631 		ba.ba_opcode = BIOC_GASTATUS;
632 		break;
633 
634 	default:
635 		errx(1, "invalid alarm function: %s", arg);
636 	}
637 
638 	if (ioctl(devh, BIOCALARM, &ba) == -1)
639 		err(1, "BIOCALARM");
640 
641 	bio_status(&ba.ba_bio.bio_status);
642 
643 	if (arg[0] == 'g')
644 		printf("alarm is currently %s\n",
645 		    ba.ba_status ? "enabled" : "disabled");
646 }
647 
648 int
bio_getvolbyname(char * name)649 bio_getvolbyname(char *name)
650 {
651 	int			id = -1, i;
652 	struct bioc_inq		bi;
653 	struct bioc_vol		bv;
654 
655 	memset(&bi, 0, sizeof(bi));
656 	bi.bi_bio.bio_cookie = bio_cookie;
657 	if (ioctl(devh, BIOCINQ, &bi) == -1)
658 		err(1, "BIOCINQ");
659 
660 	bio_status(&bi.bi_bio.bio_status);
661 
662 	for (i = 0; i < bi.bi_novol; i++) {
663 		memset(&bv, 0, sizeof(bv));
664 		bv.bv_bio.bio_cookie = bio_cookie;
665 		bv.bv_volid = i;
666 		if (ioctl(devh, BIOCVOL, &bv) == -1)
667 			err(1, "BIOCVOL");
668 
669 		bio_status(&bv.bv_bio.bio_status);
670 
671 		if (name && strcmp(name, bv.bv_dev) != 0)
672 			continue;
673 		id = i;
674 		break;
675 	}
676 
677 	return (id);
678 }
679 
680 void
bio_setstate(char * arg,int status,char * devicename)681 bio_setstate(char *arg, int status, char *devicename)
682 {
683 	struct bioc_setstate	bs;
684 	struct locator		location;
685 	struct stat		sb;
686 	const char		*errstr;
687 
688 	memset(&bs, 0, sizeof(bs));
689 	if (stat(arg, &sb) == -1) {
690 		/* use CTL */
691 		errstr = str2locator(arg, &location);
692 		if (errstr)
693 			errx(1, "Target %s: %s", arg, errstr);
694 		bs.bs_channel = location.channel;
695 		bs.bs_target = location.target;
696 		bs.bs_lun = location.lun;
697 	} else {
698 		/* use other id */
699 		bs.bs_other_id = sb.st_rdev;
700 		bs.bs_other_id_type = BIOC_SSOTHER_DEVT;
701 	}
702 
703 	bs.bs_bio.bio_cookie = bio_cookie;
704 	bs.bs_status = status;
705 
706 	if (status != BIOC_SSHOTSPARE) {
707 		/* make sure user supplied a sd device */
708 		bs.bs_volid = bio_getvolbyname(devicename);
709 		if (bs.bs_volid == -1)
710 			errx(1, "invalid device %s", devicename);
711 	}
712 
713 	if (ioctl(devh, BIOCSETSTATE, &bs) == -1)
714 		err(1, "BIOCSETSTATE");
715 
716 	bio_status(&bs.bs_bio.bio_status);
717 }
718 
719 void
bio_setblink(char * name,char * arg,int blink)720 bio_setblink(char *name, char *arg, int blink)
721 {
722 	struct locator		location;
723 	struct bioc_blink	bb;
724 	struct bioc_inq		bi;
725 	struct bioc_vol		bv;
726 	struct bioc_disk	bd;
727 	const char		*errstr;
728 	int			v, d, rv;
729 
730 	errstr = str2locator(arg, &location);
731 	if (errstr)
732 		errx(1, "Target %s: %s", arg, errstr);
733 
734 	/* try setting blink on the device directly */
735 	memset(&bb, 0, sizeof(bb));
736 	bb.bb_bio.bio_cookie = bio_cookie;
737 	bb.bb_status = blink;
738 	bb.bb_target = location.target;
739 	bb.bb_channel = location.channel;
740 	rv = ioctl(devh, BIOCBLINK, &bb);
741 
742 	if (rv == 0 && bb.bb_bio.bio_status.bs_status == BIO_STATUS_UNKNOWN)
743 		return;
744 
745 	if (rv == 0 && bb.bb_bio.bio_status.bs_status == BIO_STATUS_SUCCESS) {
746 		bio_status(&bb.bb_bio.bio_status);
747 		return;
748 	}
749 
750 	/* if the blink didn't work, try to find something that will */
751 
752 	memset(&bi, 0, sizeof(bi));
753 	bi.bi_bio.bio_cookie = bio_cookie;
754 	if (ioctl(devh, BIOCINQ, &bi) == -1)
755 		err(1, "BIOCINQ");
756 
757 	bio_status(&bi.bi_bio.bio_status);
758 
759 	for (v = 0; v < bi.bi_novol; v++) {
760 		memset(&bv, 0, sizeof(bv));
761 		bv.bv_bio.bio_cookie = bio_cookie;
762 		bv.bv_volid = v;
763 		if (ioctl(devh, BIOCVOL, &bv) == -1)
764 			err(1, "BIOCVOL");
765 
766 		bio_status(&bv.bv_bio.bio_status);
767 
768 		if (name && strcmp(name, bv.bv_dev) != 0)
769 			continue;
770 
771 		for (d = 0; d < bv.bv_nodisk; d++) {
772 			memset(&bd, 0, sizeof(bd));
773 			bd.bd_bio.bio_cookie = bio_cookie;
774 			bd.bd_volid = v;
775 			bd.bd_diskid = d;
776 
777 			if (ioctl(devh, BIOCDISK, &bd) == -1)
778 				err(1, "BIOCDISK");
779 
780 			bio_status(&bd.bd_bio.bio_status);
781 
782 			if (bd.bd_channel == location.channel &&
783 			    bd.bd_target == location.target &&
784 			    bd.bd_lun == location.lun) {
785 				if (bd.bd_procdev[0] != '\0')
786 					bio_blink(bd.bd_procdev,
787 					    location.target, blink);
788 				else
789 					warnx("Disk %s is not in an enclosure",
790 					    arg);
791 				return;
792 			}
793 		}
794 	}
795 
796 	warnx("Disk %s does not exist", arg);
797 }
798 
799 void
bio_blink(char * enclosure,int target,int blinktype)800 bio_blink(char *enclosure, int target, int blinktype)
801 {
802 	int			bioh;
803 	struct bio_locate	bl;
804 	struct bioc_blink	blink;
805 
806 	bioh = open("/dev/bio", O_RDWR);
807 	if (bioh == -1)
808 		err(1, "Can't open %s", "/dev/bio");
809 
810 	memset(&bl, 0, sizeof(bl));
811 	bl.bl_name = enclosure;
812 	if (ioctl(bioh, BIOCLOCATE, &bl) == -1)
813 		errx(1, "Can't locate %s device via %s", enclosure, "/dev/bio");
814 
815 	memset(&blink, 0, sizeof(blink));
816 	blink.bb_bio.bio_cookie = bio_cookie;
817 	blink.bb_status = blinktype;
818 	blink.bb_target = target;
819 
820 	if (ioctl(bioh, BIOCBLINK, &blink) == -1)
821 		err(1, "BIOCBLINK");
822 
823 	bio_status(&blink.bb_bio.bio_status);
824 
825 	close(bioh);
826 }
827 
828 void
bio_createraid(u_int16_t level,char * dev_list,char * key_disk)829 bio_createraid(u_int16_t level, char *dev_list, char *key_disk)
830 {
831 	struct bioc_createraid	create;
832 	struct sr_crypto_kdfinfo kdfinfo;
833 	struct sr_crypto_pbkdf	kdfhint;
834 	struct stat		sb;
835 	int			rv, no_dev, fd;
836 	dev_t			*dt;
837 	u_int16_t		min_disks = 0;
838 
839 	if (!dev_list)
840 		errx(1, "no devices specified");
841 
842 	dt = calloc(1, BIOC_CRMAXLEN);
843 	if (!dt)
844 		err(1, "not enough memory for dev_t list");
845 
846 	no_dev = bio_parse_devlist(dev_list, dt);
847 
848 	switch (level) {
849 	case 0:
850 		min_disks = 2;
851 		break;
852 	case 1:
853 		min_disks = 2;
854 		break;
855 	case 5:
856 		min_disks = 3;
857 		break;
858 	case 'C':
859 	case 0x1C:
860 		min_disks = 1;
861 		break;
862 	case 'c':
863 		min_disks = 1;
864 		break;
865 	default:
866 		errx(1, "unsupported RAID level");
867 	}
868 
869 	if (no_dev < min_disks)
870 		errx(1, "not enough disks");
871 
872 	/* for crypto raid we only allow one single chunk */
873 	if (level == 'C' && no_dev != min_disks)
874 		errx(1, "not exactly one partition");
875 
876 	memset(&create, 0, sizeof(create));
877 	create.bc_bio.bio_cookie = bio_cookie;
878 	create.bc_level = level;
879 	create.bc_dev_list_len = no_dev * sizeof(dev_t);
880 	create.bc_dev_list = dt;
881 	create.bc_flags = BIOC_SCDEVT | cflags;
882 	create.bc_key_disk = NODEV;
883 
884 	if ((level == 'C' || level == 0x1C) && key_disk == NULL) {
885 
886 		memset(&kdfinfo, 0, sizeof(kdfinfo));
887 		memset(&kdfhint, 0, sizeof(kdfhint));
888 
889 		create.bc_flags |= BIOC_SCNOAUTOASSEMBLE;
890 
891 		create.bc_opaque = &kdfhint;
892 		create.bc_opaque_size = sizeof(kdfhint);
893 		create.bc_opaque_flags = BIOC_SOOUT;
894 
895 		/* try to get KDF hint */
896 		if (ioctl(devh, BIOCCREATERAID, &create) == -1)
897 			err(1, "ioctl");
898 
899 		bio_status(&create.bc_bio.bio_status);
900 
901 		if (create.bc_opaque_status == BIOC_SOINOUT_OK) {
902 			bio_kdf_derive(&kdfinfo, &kdfhint, "Passphrase: ", 0);
903 			memset(&kdfhint, 0, sizeof(kdfhint));
904 		} else {
905 			bio_kdf_generate(&kdfinfo);
906 		}
907 
908 		create.bc_opaque = &kdfinfo;
909 		create.bc_opaque_size = sizeof(kdfinfo);
910 		create.bc_opaque_flags = BIOC_SOIN;
911 
912 	} else if ((level == 'C' || level == 0x1C) && key_disk != NULL) {
913 
914 		/* Get device number for key disk. */
915 		fd = opendev(key_disk, O_RDONLY, OPENDEV_BLCK, NULL);
916 		if (fd == -1)
917 			err(1, "could not open %s", key_disk);
918 		if (fstat(fd, &sb) == -1) {
919 			int saved_errno = errno;
920 			close(fd);
921 			errc(1, saved_errno, "could not stat %s", key_disk);
922 		}
923 		close(fd);
924 		create.bc_key_disk = sb.st_rdev;
925 
926 		memset(&kdfinfo, 0, sizeof(kdfinfo));
927 
928 		kdfinfo.genkdf.len = sizeof(kdfinfo.genkdf);
929 		kdfinfo.genkdf.type = SR_CRYPTOKDFT_KEYDISK;
930 		kdfinfo.len = sizeof(kdfinfo);
931 		kdfinfo.flags = SR_CRYPTOKDF_HINT;
932 
933 		create.bc_opaque = &kdfinfo;
934 		create.bc_opaque_size = sizeof(kdfinfo);
935 		create.bc_opaque_flags = BIOC_SOIN;
936 
937 	}
938 
939 	rv = ioctl(devh, BIOCCREATERAID, &create);
940 	explicit_bzero(&kdfinfo, sizeof(kdfinfo));
941 	if (rv == -1)
942 		err(1, "BIOCCREATERAID");
943 
944 	bio_status(&create.bc_bio.bio_status);
945 
946 	free(dt);
947 }
948 
949 void
bio_kdf_derive(struct sr_crypto_kdfinfo * kdfinfo,struct sr_crypto_pbkdf * kdfhint,char * prompt,int verify)950 bio_kdf_derive(struct sr_crypto_kdfinfo *kdfinfo, struct sr_crypto_pbkdf
951     *kdfhint, char* prompt, int verify)
952 {
953 	if (!kdfinfo)
954 		errx(1, "invalid KDF info");
955 	if (!kdfhint)
956 		errx(1, "invalid KDF hint");
957 
958 	if (kdfhint->generic.len != sizeof(*kdfhint))
959 		errx(1, "KDF hint has invalid size");
960 
961 	kdfinfo->flags = SR_CRYPTOKDF_KEY;
962 	kdfinfo->len = sizeof(*kdfinfo);
963 
964 	derive_key(kdfhint->generic.type, kdfhint->rounds,
965 	    kdfinfo->maskkey, sizeof(kdfinfo->maskkey),
966 	    kdfhint->salt, sizeof(kdfhint->salt),
967 	    prompt, verify);
968 }
969 
970 void
bio_kdf_generate(struct sr_crypto_kdfinfo * kdfinfo)971 bio_kdf_generate(struct sr_crypto_kdfinfo *kdfinfo)
972 {
973 	if (!kdfinfo)
974 		errx(1, "invalid KDF info");
975 
976 	if (rflag == -1)
977 		rflag = bcrypt_pbkdf_autorounds();
978 
979 	kdfinfo->pbkdf.generic.len = sizeof(kdfinfo->pbkdf);
980 	kdfinfo->pbkdf.generic.type = SR_CRYPTOKDFT_BCRYPT_PBKDF;
981 	kdfinfo->pbkdf.rounds = rflag;
982 
983 	kdfinfo->flags = SR_CRYPTOKDF_KEY | SR_CRYPTOKDF_HINT;
984 	kdfinfo->len = sizeof(*kdfinfo);
985 
986 	/* generate salt */
987 	arc4random_buf(kdfinfo->pbkdf.salt, sizeof(kdfinfo->pbkdf.salt));
988 
989 	derive_key(kdfinfo->pbkdf.generic.type, kdfinfo->pbkdf.rounds,
990 	    kdfinfo->maskkey, sizeof(kdfinfo->maskkey),
991 	    kdfinfo->pbkdf.salt, sizeof(kdfinfo->pbkdf.salt),
992 	    "New passphrase: ", interactive);
993 }
994 
995 int
bio_parse_devlist(char * lst,dev_t * dt)996 bio_parse_devlist(char *lst, dev_t *dt)
997 {
998 	char			*s, *e;
999 	u_int32_t		sz = 0;
1000 	int			no_dev = 0, i, x;
1001 	struct stat		sb;
1002 	char			dev[PATH_MAX];
1003 	int			fd;
1004 
1005 	if (!lst)
1006 		errx(1, "invalid device list");
1007 
1008 	s = e = lst;
1009 	/* make sure we have a valid device list like /dev/sdNa,/dev/sdNNa */
1010 	while (*e != '\0') {
1011 		if (*e == ',')
1012 			s = e + 1;
1013 		else if (*(e + 1) == '\0' || *(e + 1) == ',') {
1014 			/* got one */
1015 			sz = e - s + 1;
1016 			strlcpy(dev, s, sz + 1);
1017 			fd = opendev(dev, O_RDONLY, OPENDEV_BLCK, NULL);
1018 			if (fd == -1)
1019 				err(1, "could not open %s", dev);
1020 			if (fstat(fd, &sb) == -1) {
1021 				int saved_errno = errno;
1022 				close(fd);
1023 				errc(1, saved_errno, "could not stat %s", dev);
1024 			}
1025 			close(fd);
1026 			dt[no_dev] = sb.st_rdev;
1027 			no_dev++;
1028 			if (no_dev > (int)(BIOC_CRMAXLEN / sizeof(dev_t)))
1029 				errx(1, "too many devices on device list");
1030 		}
1031 		e++;
1032 	}
1033 
1034 	for (i = 0; i < no_dev; i++)
1035 		for (x = 0; x < no_dev; x++)
1036 			if (dt[i] == dt[x] && x != i)
1037 				errx(1, "duplicate device in list");
1038 
1039 	return (no_dev);
1040 }
1041 
1042 u_int32_t
bio_createflags(char * lst)1043 bio_createflags(char *lst)
1044 {
1045 	char			*s, *e, fs[32];
1046 	u_int32_t		sz = 0;
1047 	u_int32_t		flags = 0;
1048 
1049 	if (!lst)
1050 		errx(1, "invalid flags list");
1051 
1052 	s = e = lst;
1053 	/* make sure we have a valid flags list like force,noassemeble */
1054 	while (*e != '\0') {
1055 		if (*e == ',')
1056 			s = e + 1;
1057 		else if (*(e + 1) == '\0' || *(e + 1) == ',') {
1058 			/* got one */
1059 			sz = e - s + 1;
1060 			switch (s[0]) {
1061 			case 'f':
1062 				flags |= BIOC_SCFORCE;
1063 				break;
1064 			case 'n':
1065 				flags |= BIOC_SCNOAUTOASSEMBLE;
1066 				break;
1067 			default:
1068 				strlcpy(fs, s, sz + 1);
1069 				errx(1, "invalid flag %s", fs);
1070 			}
1071 		}
1072 		e++;
1073 	}
1074 
1075 	return (flags);
1076 }
1077 
1078 void
bio_deleteraid(char * dev)1079 bio_deleteraid(char *dev)
1080 {
1081 	struct bioc_deleteraid	bd;
1082 	memset(&bd, 0, sizeof(bd));
1083 
1084 	bd.bd_bio.bio_cookie = bio_cookie;
1085 	/* XXX make this a dev_t instead of a string */
1086 	strlcpy(bd.bd_dev, dev, sizeof bd.bd_dev);
1087 	if (ioctl(devh, BIOCDELETERAID, &bd) == -1)
1088 		err(1, "BIOCDELETERAID");
1089 
1090 	bio_status(&bd.bd_bio.bio_status);
1091 }
1092 
1093 void
bio_changepass(char * dev)1094 bio_changepass(char *dev)
1095 {
1096 	struct bioc_discipline bd;
1097 	struct sr_crypto_kdfpair kdfpair;
1098 	struct sr_crypto_kdfinfo kdfinfo1, kdfinfo2;
1099 	struct sr_crypto_pbkdf kdfhint;
1100 	int rv;
1101 
1102 	memset(&bd, 0, sizeof(bd));
1103 	memset(&kdfhint, 0, sizeof(kdfhint));
1104 	memset(&kdfinfo1, 0, sizeof(kdfinfo1));
1105 	memset(&kdfinfo2, 0, sizeof(kdfinfo2));
1106 
1107 	/* XXX use dev_t instead of string. */
1108 	strlcpy(bd.bd_dev, dev, sizeof(bd.bd_dev));
1109 	bd.bd_cmd = SR_IOCTL_GET_KDFHINT;
1110 	bd.bd_size = sizeof(kdfhint);
1111 	bd.bd_data = &kdfhint;
1112 
1113 	if (ioctl(devh, BIOCDISCIPLINE, &bd) == -1)
1114 		err(1, "BIOCDISCIPLINE");
1115 
1116 	bio_status(&bd.bd_bio.bio_status);
1117 
1118 	/* Current passphrase. */
1119 	bio_kdf_derive(&kdfinfo1, &kdfhint, "Old passphrase: ", 0);
1120 
1121 	if (rflag == -1) {
1122 		rflag = bcrypt_pbkdf_autorounds();
1123 
1124 		/* Use previous number of rounds for the same KDF if higher. */
1125 		if (kdfhint.generic.type == SR_CRYPTOKDFT_BCRYPT_PBKDF &&
1126 		    rflag < kdfhint.rounds)
1127 			rflag = kdfhint.rounds;
1128 	}
1129 
1130 	/* New passphrase. */
1131 	bio_kdf_generate(&kdfinfo2);
1132 
1133 	kdfpair.kdfinfo1 = &kdfinfo1;
1134 	kdfpair.kdfsize1 = sizeof(kdfinfo1);
1135 	kdfpair.kdfinfo2 = &kdfinfo2;
1136 	kdfpair.kdfsize2 = sizeof(kdfinfo2);
1137 
1138 	bd.bd_cmd = SR_IOCTL_CHANGE_PASSPHRASE;
1139 	bd.bd_size = sizeof(kdfpair);
1140 	bd.bd_data = &kdfpair;
1141 
1142 	rv = ioctl(devh, BIOCDISCIPLINE, &bd);
1143 
1144 	memset(&kdfhint, 0, sizeof(kdfhint));
1145 	explicit_bzero(&kdfinfo1, sizeof(kdfinfo1));
1146 	explicit_bzero(&kdfinfo2, sizeof(kdfinfo2));
1147 
1148 	if (rv == -1)
1149 		err(1, "BIOCDISCIPLINE");
1150 
1151 	bio_status(&bd.bd_bio.bio_status);
1152 }
1153 
1154 #define BIOCTL_VIS_NBUF		4
1155 #define BIOCTL_VIS_BUFLEN	80
1156 
1157 char *
bio_vis(char * s)1158 bio_vis(char *s)
1159 {
1160 	static char	 rbuf[BIOCTL_VIS_NBUF][BIOCTL_VIS_BUFLEN];
1161 	static uint	 idx = 0;
1162 	char		*buf;
1163 
1164 	buf = rbuf[idx++];
1165 	if (idx == BIOCTL_VIS_NBUF)
1166 		idx = 0;
1167 
1168 	strnvis(buf, s, BIOCTL_VIS_BUFLEN, VIS_NL|VIS_CSTYLE);
1169 	return (buf);
1170 }
1171 
1172 void
bio_diskinq(char * sd_dev)1173 bio_diskinq(char *sd_dev)
1174 {
1175 	struct dk_inquiry	di;
1176 
1177 	if (ioctl(devh, DIOCINQ, &di) == -1)
1178 		err(1, "DIOCINQ");
1179 
1180 	printf("%s: <%s, %s, %s>, serial %s\n", sd_dev, bio_vis(di.vendor),
1181 	    bio_vis(di.product), bio_vis(di.revision), bio_vis(di.serial));
1182 }
1183 
1184 void
bio_patrol(char * arg)1185 bio_patrol(char *arg)
1186 {
1187 	struct bioc_patrol	bp;
1188 	struct timing		timing;
1189 	const char		*errstr;
1190 
1191 	memset(&bp, 0, sizeof(bp));
1192 	bp.bp_bio.bio_cookie = bio_cookie;
1193 
1194 	switch (arg[0]) {
1195 	case 'a':
1196 		bp.bp_opcode = BIOC_SPAUTO;
1197 		break;
1198 
1199 	case 'm':
1200 		bp.bp_opcode = BIOC_SPMANUAL;
1201 		break;
1202 
1203 	case 'd':
1204 		bp.bp_opcode = BIOC_SPDISABLE;
1205 		break;
1206 
1207 	case 'g': /* get patrol state */
1208 		bp.bp_opcode = BIOC_GPSTATUS;
1209 		break;
1210 
1211 	case 's': /* start/stop patrol */
1212 		if (strncmp("sta", arg, 3) == 0)
1213 			bp.bp_opcode = BIOC_SPSTART;
1214 		else
1215 			bp.bp_opcode = BIOC_SPSTOP;
1216 		break;
1217 
1218 	default:
1219 		errx(1, "invalid patrol function: %s", arg);
1220 	}
1221 
1222 	switch (arg[0]) {
1223 	case 'a':
1224 		errstr = str2patrol(arg, &timing);
1225 		if (errstr)
1226 			errx(1, "Patrol %s: %s", arg, errstr);
1227 		bp.bp_autoival = timing.interval;
1228 		bp.bp_autonext = timing.start;
1229 		break;
1230 	}
1231 
1232 	if (ioctl(devh, BIOCPATROL, &bp) == -1)
1233 		err(1, "BIOCPATROL");
1234 
1235 	bio_status(&bp.bp_bio.bio_status);
1236 
1237 	if (arg[0] == 'g') {
1238 		const char *mode, *status;
1239 		char interval[40];
1240 
1241 		interval[0] = '\0';
1242 
1243 		switch (bp.bp_mode) {
1244 		case BIOC_SPMAUTO:
1245 			mode = "auto";
1246 			snprintf(interval, sizeof interval,
1247 			    " interval=%d next=%d", bp.bp_autoival,
1248 			    bp.bp_autonext - bp.bp_autonow);
1249 			break;
1250 		case BIOC_SPMMANUAL:
1251 			mode = "manual";
1252 			break;
1253 		case BIOC_SPMDISABLED:
1254 			mode = "disabled";
1255 			break;
1256 		default:
1257 			mode = "unknown";
1258 			break;
1259 		}
1260 		switch (bp.bp_status) {
1261 		case BIOC_SPSSTOPPED:
1262 			status = "stopped";
1263 			break;
1264 		case BIOC_SPSREADY:
1265 			status = "ready";
1266 			break;
1267 		case BIOC_SPSACTIVE:
1268 			status = "active";
1269 			break;
1270 		case BIOC_SPSABORTED:
1271 			status = "aborted";
1272 			break;
1273 		default:
1274 			status = "unknown";
1275 			break;
1276 		}
1277 		printf("patrol mode: %s%s\n", mode, interval);
1278 		printf("patrol status: %s\n", status);
1279 	}
1280 }
1281 
1282 /*
1283  * Measure this system's performance by measuring the time for 100 rounds.
1284  * We are aiming for something that takes around 1s.
1285  */
1286 int
bcrypt_pbkdf_autorounds(void)1287 bcrypt_pbkdf_autorounds(void)
1288 {
1289 	struct timespec before, after;
1290 	char buf[SR_CRYPTO_MAXKEYBYTES], salt[128];
1291 	int r = 100;
1292 	int duration;
1293 
1294 	clock_gettime(CLOCK_THREAD_CPUTIME_ID, &before);
1295 	if (bcrypt_pbkdf("testpassword", strlen("testpassword"),
1296 	    salt, sizeof(salt), buf, sizeof(buf), r) != 0)
1297 		errx(1, "bcrypt pbkdf failed");
1298 	clock_gettime(CLOCK_THREAD_CPUTIME_ID, &after);
1299 
1300 	duration = after.tv_sec - before.tv_sec;
1301 	duration *= 1000000;
1302 	duration += (after.tv_nsec - before.tv_nsec) / 1000;
1303 
1304 	duration /= r;
1305 	r = 1000000 / duration;
1306 
1307 	if (r < 16)
1308 		r = 16;
1309 
1310 	return r;
1311 }
1312 
1313 void
derive_key(u_int32_t type,int rounds,u_int8_t * key,size_t keysz,u_int8_t * salt,size_t saltsz,char * prompt,int verify)1314 derive_key(u_int32_t type, int rounds, u_int8_t *key, size_t keysz,
1315     u_int8_t *salt, size_t saltsz, char *prompt, int verify)
1316 {
1317 	FILE		*f;
1318 	size_t		pl;
1319 	struct stat	sb;
1320 	char		passphrase[1024], verifybuf[1024];
1321 	int		rpp_flag = RPP_ECHO_OFF;
1322 
1323 	if (!key)
1324 		errx(1, "Invalid key");
1325 	if (!salt)
1326 		errx(1, "Invalid salt");
1327 
1328 	if (type != SR_CRYPTOKDFT_PKCS5_PBKDF2 &&
1329 	    type != SR_CRYPTOKDFT_BCRYPT_PBKDF)
1330 		errx(1, "unknown KDF type %d", type);
1331 
1332 	if (rounds < (type == SR_CRYPTOKDFT_PKCS5_PBKDF2 ? 1000 : 16))
1333 		errx(1, "number of KDF rounds is too small: %d", rounds);
1334 
1335 	/* get passphrase */
1336 	if (passfile) {
1337 		if ((f = fopen(passfile, "r")) == NULL)
1338 			err(1, "invalid passphrase file");
1339 
1340 		if (fstat(fileno(f), &sb) == -1)
1341 			err(1, "can't stat passphrase file");
1342 		if (sb.st_uid != 0)
1343 			errx(1, "passphrase file must be owned by root");
1344 		if ((sb.st_mode & ~S_IFMT) != (S_IRUSR | S_IWUSR))
1345 			errx(1, "passphrase file has the wrong permissions");
1346 
1347 		if (fgets(passphrase, sizeof(passphrase), f) == NULL)
1348 			err(1, "can't read passphrase file");
1349 		pl = strlen(passphrase);
1350 		if (pl > 0 && passphrase[pl - 1] == '\n')
1351 			passphrase[pl - 1] = '\0';
1352 		else
1353 			errx(1, "invalid passphrase length");
1354 
1355 		fclose(f);
1356 	} else {
1357 		rpp_flag |= interactive ? RPP_REQUIRE_TTY : RPP_STDIN;
1358 
1359  retry:
1360 		if (readpassphrase(prompt, passphrase, sizeof(passphrase),
1361 		    rpp_flag) == NULL)
1362 			err(1, "unable to read passphrase");
1363 		if (*passphrase == '\0') {
1364 			warnx("invalid passphrase length");
1365 			if (interactive)
1366 				goto retry;
1367 			exit(1);
1368 		}
1369 	}
1370 
1371 	if (verify && !passfile) {
1372 		/* request user to re-type it */
1373 		if (readpassphrase("Re-type passphrase: ", verifybuf,
1374 		    sizeof(verifybuf), rpp_flag) == NULL) {
1375 			explicit_bzero(passphrase, sizeof(passphrase));
1376 			err(1, "unable to read passphrase");
1377 		}
1378 		if ((strlen(passphrase) != strlen(verifybuf)) ||
1379 		    (strcmp(passphrase, verifybuf) != 0)) {
1380 			explicit_bzero(passphrase, sizeof(passphrase));
1381 			explicit_bzero(verifybuf, sizeof(verifybuf));
1382 			if (interactive) {
1383 				warnx("Passphrases did not match, try again");
1384 				goto retry;
1385 			}
1386 			errx(1, "Passphrases did not match");
1387 		}
1388 		/* forget the re-typed one */
1389 		explicit_bzero(verifybuf, sizeof(verifybuf));
1390 	}
1391 
1392 	/* derive key from passphrase */
1393 	if (type == SR_CRYPTOKDFT_PKCS5_PBKDF2) {
1394 		if (verbose)
1395 			printf("Deriving key using PKCS#5 PBKDF2 with %i rounds...\n",
1396 			    rounds);
1397 		if (pkcs5_pbkdf2(passphrase, strlen(passphrase), salt, saltsz,
1398 		    key, keysz, rounds) != 0)
1399 			errx(1, "pkcs5_pbkdf2 failed");
1400 	} else if (type == SR_CRYPTOKDFT_BCRYPT_PBKDF) {
1401 		if (verbose)
1402 			printf("Deriving key using bcrypt PBKDF with %i rounds...\n",
1403 			    rounds);
1404 		if (bcrypt_pbkdf(passphrase, strlen(passphrase), salt, saltsz,
1405 		    key, keysz, rounds) != 0)
1406 			errx(1, "bcrypt_pbkdf failed");
1407 	} else {
1408 		errx(1, "unknown KDF type %d", type);
1409 	}
1410 
1411 	/* forget passphrase */
1412 	explicit_bzero(passphrase, sizeof(passphrase));
1413 }
1414