1*178701b6Sjmc /* $OpenBSD: bioctl.c,v 1.158 2024/07/15 05:36:08 jmc Exp $ */
2cf6503d7Sderaadt
33af9de98Smarco /*
42f39728eSdlg * Copyright (c) 2004, 2005 Marco Peereboom
53af9de98Smarco * All rights reserved.
63af9de98Smarco *
73af9de98Smarco * Redistribution and use in source and binary forms, with or without
83af9de98Smarco * modification, are permitted provided that the following conditions
93af9de98Smarco * are met:
103af9de98Smarco * 1. Redistributions of source code must retain the above copyright
113af9de98Smarco * notice, this list of conditions and the following disclaimer.
123af9de98Smarco * 2. Redistributions in binary form must reproduce the above copyright
133af9de98Smarco * notice, this list of conditions and the following disclaimer in the
143af9de98Smarco * documentation and/or other materials provided with the distribution.
153af9de98Smarco *
163af9de98Smarco * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
173af9de98Smarco * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
183af9de98Smarco * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
193af9de98Smarco * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
203af9de98Smarco * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
213af9de98Smarco * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
223af9de98Smarco * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
233af9de98Smarco * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
243af9de98Smarco * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
253af9de98Smarco * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
263af9de98Smarco * SUCH DAMAGE.
273af9de98Smarco *
283af9de98Smarco */
293af9de98Smarco
30b9fc9a72Sderaadt #include <sys/param.h> /* NODEV */
31c2126c9aSmarco #include <sys/ioctl.h>
3291f4f7d8Sdlg #include <sys/dkio.h>
3346bc198bSmarco #include <sys/stat.h>
34aef7fe28Shshoexer #include <dev/softraidvar.h>
35e7d4f752Sderaadt #include <dev/biovar.h>
363af9de98Smarco
37db2730c1Smarco #include <errno.h>
38db2730c1Smarco #include <err.h>
39db2730c1Smarco #include <fcntl.h>
408ccdd032Sderaadt #include <util.h>
41934f30d8Sderaadt #include <ctype.h>
42db2730c1Smarco #include <stdio.h>
43db2730c1Smarco #include <stdlib.h>
44db2730c1Smarco #include <string.h>
45ef0eb24eSjsing #include <time.h>
46db2730c1Smarco #include <unistd.h>
47b9fc9a72Sderaadt #include <limits.h>
4803b2dfbfShenning #include <vis.h>
499e8c6f5bShshoexer #include <readpassphrase.h>
50db2730c1Smarco
516de960dcSmarco struct locator {
526de960dcSmarco int channel;
536de960dcSmarco int target;
546de960dcSmarco int lun;
556de960dcSmarco };
566de960dcSmarco
57d865b7d2Suebayasi struct timing {
58d865b7d2Suebayasi int interval;
59d865b7d2Suebayasi int start;
60d865b7d2Suebayasi };
61d865b7d2Suebayasi
621f610680Stb static void __dead usage(void);
6341eccc89Sderaadt const char *str2locator(const char *, struct locator *);
64d865b7d2Suebayasi const char *str2patrol(const char *, struct timing *);
65d313c28bSjsing void bio_status(struct bio_status *);
6646bc198bSmarco int bio_parse_devlist(char *, dev_t *);
67aef7fe28Shshoexer void bio_kdf_derive(struct sr_crypto_kdfinfo *,
683487a6b1Sjsing struct sr_crypto_pbkdf *, char *, int);
69aef7fe28Shshoexer void bio_kdf_generate(struct sr_crypto_kdfinfo *);
70ef0eb24eSjsing int bcrypt_pbkdf_autorounds(void);
713487a6b1Sjsing void derive_key(u_int32_t, int, u_int8_t *, size_t,
7261f93244Sjsing u_int8_t *, size_t, char *, int);
738ccdd032Sderaadt
748ccdd032Sderaadt void bio_inq(char *);
758ccdd032Sderaadt void bio_alarm(char *);
76a15048bbSmarco int bio_getvolbyname(char *);
77a15048bbSmarco void bio_setstate(char *, int, char *);
78a928c459Sderaadt void bio_setblink(char *, char *, int);
79a928c459Sderaadt void bio_blink(char *, int, int);
800054cd36Sjsing void bio_createraid(u_int16_t, char *, char *);
81c7c3e8aaSmarco void bio_deleteraid(char *);
82c6446370Sjsing void bio_changepass(char *);
83e8a57fdeSmarco u_int32_t bio_createflags(char *);
8403b2dfbfShenning char *bio_vis(char *);
8503b2dfbfShenning void bio_diskinq(char *);
86d865b7d2Suebayasi void bio_patrol(char *);
873af9de98Smarco
883af9de98Smarco int devh = -1;
89abe9d68eSderaadt int human;
90abe9d68eSderaadt int verbose;
91e8a57fdeSmarco u_int32_t cflags = 0;
92b4f9a699Skn int rflag = -1; /* auto */
93dd81669fSkn char *passfile;
943af9de98Smarco
95545c4d7fSjsing void *bio_cookie;
96545c4d7fSjsing
975f219970Skn int interactive = 1;
983af9de98Smarco
993af9de98Smarco int
main(int argc,char * argv[])1003af9de98Smarco main(int argc, char *argv[])
1013af9de98Smarco {
102545c4d7fSjsing struct bio_locate bl;
103db2730c1Smarco u_int64_t func = 0;
104e63d8b1dSdtucker char *devicename = NULL;
105cf6503d7Sderaadt char *realname = NULL, *al_arg = NULL;
1067195049bSmarco char *bl_arg = NULL, *dev_list = NULL;
1070054cd36Sjsing char *key_disk = NULL;
108aedd4f07Sdjm const char *errstr;
109d313c28bSjsing int ch, blink = 0, changepass = 0, diskinq = 0;
110c6446370Sjsing int ss_func = 0;
1119ecba717Sderaadt u_int16_t cr_level = 0;
112e37c64dbSjsing int biodev = 0;
1133af9de98Smarco
1143af9de98Smarco if (argc < 2)
1153af9de98Smarco usage();
1163af9de98Smarco
117d865b7d2Suebayasi while ((ch = getopt(argc, argv, "a:b:C:c:dH:hik:l:O:Pp:qr:R:st:u:v")) !=
118c6446370Sjsing -1) {
1193af9de98Smarco switch (ch) {
120edfd9792Smarco case 'a': /* alarm */
121edfd9792Smarco func |= BIOC_ALARM;
122edfd9792Smarco al_arg = optarg;
123edfd9792Smarco break;
124c55617f1Sdlg case 'b': /* blink */
125c55617f1Sdlg func |= BIOC_BLINK;
126a928c459Sderaadt blink = BIOC_SBBLINK;
127a928c459Sderaadt bl_arg = optarg;
128a928c459Sderaadt break;
129e8a57fdeSmarco case 'C': /* creation flags */
130e8a57fdeSmarco cflags = bio_createflags(optarg);
131e8a57fdeSmarco break;
1327195049bSmarco case 'c': /* create */
1337195049bSmarco func |= BIOC_CREATERAID;
13401de20b5Sstsp if (strcmp(optarg, "1C") == 0) {
13501de20b5Sstsp cr_level = 0x1C;
13601de20b5Sstsp } else if (isdigit((unsigned char)*optarg)) {
13792d21b5cStedu cr_level = strtonum(optarg, 0, 10, &errstr);
13892d21b5cStedu if (errstr != NULL)
13992d21b5cStedu errx(1, "Invalid RAID level");
14024b6f6bcSkn } else if (strlen(optarg) == 1) {
14198b750e4Stedu cr_level = *optarg;
14224b6f6bcSkn } else {
14324b6f6bcSkn errx(1, "Invalid RAID level");
14424b6f6bcSkn }
1457195049bSmarco break;
146c7c3e8aaSmarco case 'd':
147c7c3e8aaSmarco /* delete volume */
148c7c3e8aaSmarco func |= BIOC_DELETERAID;
149c7c3e8aaSmarco break;
150a928c459Sderaadt case 'u': /* unblink */
151a928c459Sderaadt func |= BIOC_BLINK;
152a928c459Sderaadt blink = BIOC_SBUNBLINK;
153c55617f1Sdlg bl_arg = optarg;
154c55617f1Sdlg break;
1556de960dcSmarco case 'H': /* set hotspare */
1566de960dcSmarco func |= BIOC_SETSTATE;
157a15048bbSmarco ss_func = BIOC_SSHOTSPARE;
1586de960dcSmarco al_arg = optarg;
1596de960dcSmarco break;
1608ccdd032Sderaadt case 'h':
1618ccdd032Sderaadt human = 1;
1628ccdd032Sderaadt break;
163db2730c1Smarco case 'i': /* inquiry */
164db2730c1Smarco func |= BIOC_INQ;
1653af9de98Smarco break;
1660054cd36Sjsing case 'k': /* Key disk. */
1670054cd36Sjsing key_disk = optarg;
1680054cd36Sjsing break;
1697195049bSmarco case 'l': /* device list */
1707195049bSmarco func |= BIOC_DEVLIST;
1717195049bSmarco dev_list = optarg;
1727195049bSmarco break;
173c6446370Sjsing case 'P':
174c6446370Sjsing /* Change passphrase. */
175c6446370Sjsing changepass = 1;
176c6446370Sjsing break;
17786735da2Smarco case 'p':
178dd81669fSkn passfile = optarg;
17986735da2Smarco break;
180aedd4f07Sdjm case 'r':
181ef0eb24eSjsing if (strcmp(optarg, "auto") == 0) {
182ef0eb24eSjsing rflag = -1;
183ef0eb24eSjsing break;
184ef0eb24eSjsing }
185b4f9a699Skn rflag = strtonum(optarg, 16, 1<<30, &errstr);
186aedd4f07Sdjm if (errstr != NULL)
18761f93244Sjsing errx(1, "number of KDF rounds is %s: %s",
188aedd4f07Sdjm errstr, optarg);
189aedd4f07Sdjm break;
19050e55a42Sjsing case 'O':
19150e55a42Sjsing /* set a chunk to offline */
19250e55a42Sjsing func |= BIOC_SETSTATE;
19350e55a42Sjsing ss_func = BIOC_SSOFFLINE;
19450e55a42Sjsing al_arg = optarg;
19550e55a42Sjsing break;
196a15048bbSmarco case 'R':
197a15048bbSmarco /* rebuild to provided chunk/CTL */
198a15048bbSmarco func |= BIOC_SETSTATE;
199a15048bbSmarco ss_func = BIOC_SSREBUILD;
200a15048bbSmarco al_arg = optarg;
201a15048bbSmarco break;
202b96c6ce2Sckuethe case 's':
2035f219970Skn interactive = 0;
204b96c6ce2Sckuethe break;
205d865b7d2Suebayasi case 't': /* patrol */
206d865b7d2Suebayasi func |= BIOC_PATROL;
207d865b7d2Suebayasi al_arg = optarg;
208d865b7d2Suebayasi break;
209abe9d68eSderaadt case 'v':
210abe9d68eSderaadt verbose = 1;
211abe9d68eSderaadt break;
21203b2dfbfShenning case 'q':
21303b2dfbfShenning diskinq = 1;
21403b2dfbfShenning break;
2153af9de98Smarco default:
2163af9de98Smarco usage();
2173af9de98Smarco /* NOTREACHED */
2183af9de98Smarco }
2193af9de98Smarco }
220cf6503d7Sderaadt argc -= optind;
221cf6503d7Sderaadt argv += optind;
2223af9de98Smarco
223c6446370Sjsing if (argc != 1 || (changepass && func != 0))
224cf6503d7Sderaadt usage();
225cf6503d7Sderaadt
226dcbaf4c8Sderaadt if (func == 0)
227dcbaf4c8Sderaadt func |= BIOC_INQ;
228dcbaf4c8Sderaadt
229e63d8b1dSdtucker devicename = argv[0];
230e63d8b1dSdtucker if (devicename == NULL)
231e37c64dbSjsing errx(1, "need device");
23210b411a7Smarco
233e63d8b1dSdtucker devh = opendev(devicename, O_RDWR, OPENDEV_PART, &realname);
234e37c64dbSjsing if (devh == -1) {
23541eccc89Sderaadt devh = open("/dev/bio", O_RDWR);
2363af9de98Smarco if (devh == -1)
23741eccc89Sderaadt err(1, "Can't open %s", "/dev/bio");
2383af9de98Smarco
239281272dcSpatrick memset(&bl, 0, sizeof(bl));
240e63d8b1dSdtucker bl.bl_name = devicename;
241df69c215Sderaadt if (ioctl(devh, BIOCLOCATE, &bl) == -1)
24210b411a7Smarco errx(1, "Can't locate %s device via %s",
24341eccc89Sderaadt bl.bl_name, "/dev/bio");
244d313c28bSjsing
245545c4d7fSjsing bio_cookie = bl.bl_bio.bio_cookie;
246e37c64dbSjsing biodev = 1;
247e63d8b1dSdtucker devicename = NULL;
248e37c64dbSjsing }
2493af9de98Smarco
25003b2dfbfShenning if (diskinq) {
251e63d8b1dSdtucker bio_diskinq(devicename);
252e37c64dbSjsing } else if (changepass && !biodev) {
253e63d8b1dSdtucker bio_changepass(devicename);
25403b2dfbfShenning } else if (func & BIOC_INQ) {
255e63d8b1dSdtucker bio_inq(devicename);
256edfd9792Smarco } else if (func == BIOC_ALARM) {
257edfd9792Smarco bio_alarm(al_arg);
258c55617f1Sdlg } else if (func == BIOC_BLINK) {
259e63d8b1dSdtucker bio_setblink(devicename, bl_arg, blink);
260d865b7d2Suebayasi } else if (func == BIOC_PATROL) {
261d865b7d2Suebayasi bio_patrol(al_arg);
2626de960dcSmarco } else if (func == BIOC_SETSTATE) {
263a15048bbSmarco bio_setstate(al_arg, ss_func, argv[0]);
264e37c64dbSjsing } else if (func == BIOC_DELETERAID && !biodev) {
265e63d8b1dSdtucker bio_deleteraid(devicename);
2667195049bSmarco } else if (func & BIOC_CREATERAID || func & BIOC_DEVLIST) {
2677195049bSmarco if (!(func & BIOC_CREATERAID))
2687195049bSmarco errx(1, "need -c parameter");
2697195049bSmarco if (!(func & BIOC_DEVLIST))
2707195049bSmarco errx(1, "need -l parameter");
271e37c64dbSjsing if (!biodev)
272e37c64dbSjsing errx(1, "must use bio device");
2730054cd36Sjsing bio_createraid(cr_level, dev_list, key_disk);
2743af9de98Smarco }
2753af9de98Smarco
2763af9de98Smarco return (0);
2773af9de98Smarco }
2783af9de98Smarco
2791f610680Stb static void __dead
usage(void)2803af9de98Smarco usage(void)
2813af9de98Smarco {
2823af9de98Smarco extern char *__progname;
2833af9de98Smarco
284d90b5d8bSjmc fprintf(stderr,
285d0b772c8Sjmc "usage: %s [-hiqv] [-a alarm-function] "
286d90b5d8bSjmc "[-b channel:target[.lun]]\n"
287d0b772c8Sjmc "\t[-H channel:target[.lun]] "
2881e4c8201Sschwarze "[-R chunk | channel:target[.lun]]\n"
2892e6b119fSjmc "\t[-t patrol-function] "
2902e6b119fSjmc "[-u channel:target[.lun]] "
291*178701b6Sjmc "device\n\n"
292b96c6ce2Sckuethe " %s [-dhiPqsv] "
293889853deSkn "[-C flag[,...]] [-c raidlevel] [-k keydisk]\n"
294889853deSkn "\t[-l chunk[,...]] "
295*178701b6Sjmc "[-O device | channel:target[.lun]] [-p passfile]\n"
296*178701b6Sjmc "\t[-R chunk | channel:target[.lun]] [-r rounds] "
297d0b772c8Sjmc "device\n", __progname, __progname);
298d90b5d8bSjmc
2993af9de98Smarco exit(1);
3003af9de98Smarco }
3013af9de98Smarco
30241eccc89Sderaadt const char *
str2locator(const char * string,struct locator * location)3036de960dcSmarco str2locator(const char *string, struct locator *location)
3046de960dcSmarco {
30550d3c4dcSdlg const char *errstr;
30641eccc89Sderaadt char parse[80], *targ, *lun;
3076de960dcSmarco
30841eccc89Sderaadt strlcpy(parse, string, sizeof parse);
30941eccc89Sderaadt targ = strchr(parse, ':');
3106de960dcSmarco if (targ == NULL)
31141eccc89Sderaadt return ("target not specified");
3126de960dcSmarco *targ++ = '\0';
3136de960dcSmarco
31450d3c4dcSdlg lun = strchr(targ, '.');
3156de960dcSmarco if (lun != NULL) {
3166de960dcSmarco *lun++ = '\0';
31750d3c4dcSdlg location->lun = strtonum(lun, 0, 256, &errstr);
31850d3c4dcSdlg if (errstr)
31941eccc89Sderaadt return (errstr);
3206de960dcSmarco } else
3216de960dcSmarco location->lun = 0;
3226de960dcSmarco
32350d3c4dcSdlg location->target = strtonum(targ, 0, 256, &errstr);
32450d3c4dcSdlg if (errstr)
32541eccc89Sderaadt return (errstr);
32641eccc89Sderaadt location->channel = strtonum(parse, 0, 256, &errstr);
32750d3c4dcSdlg if (errstr)
32841eccc89Sderaadt return (errstr);
32941eccc89Sderaadt return (NULL);
3306de960dcSmarco }
3316de960dcSmarco
332d865b7d2Suebayasi const char *
str2patrol(const char * string,struct timing * timing)333d865b7d2Suebayasi str2patrol(const char *string, struct timing *timing)
334d865b7d2Suebayasi {
335d865b7d2Suebayasi const char *errstr;
336d865b7d2Suebayasi char parse[80], *interval = NULL, *start = NULL;
337d865b7d2Suebayasi
338d865b7d2Suebayasi timing->interval = 0;
339d865b7d2Suebayasi timing->start = 0;
340d865b7d2Suebayasi
341d865b7d2Suebayasi strlcpy(parse, string, sizeof parse);
342d865b7d2Suebayasi
343d865b7d2Suebayasi interval = strchr(parse, '.');
344d865b7d2Suebayasi if (interval != NULL) {
345d865b7d2Suebayasi *interval++ = '\0';
346d865b7d2Suebayasi start = strchr(interval, '.');
347d865b7d2Suebayasi if (start != NULL)
348d865b7d2Suebayasi *start++ = '\0';
349d865b7d2Suebayasi }
350d865b7d2Suebayasi if (interval != NULL) {
351d865b7d2Suebayasi /* -1 == continuously */
352d865b7d2Suebayasi timing->interval = strtonum(interval, -1, INT_MAX, &errstr);
353d865b7d2Suebayasi if (errstr)
354d865b7d2Suebayasi return (errstr);
355d865b7d2Suebayasi }
356d865b7d2Suebayasi if (start != NULL) {
357d865b7d2Suebayasi timing->start = strtonum(start, 0, INT_MAX, &errstr);
358d865b7d2Suebayasi if (errstr)
359d865b7d2Suebayasi return (errstr);
360d865b7d2Suebayasi }
361d865b7d2Suebayasi
362d865b7d2Suebayasi return (NULL);
363d865b7d2Suebayasi }
364d865b7d2Suebayasi
3653af9de98Smarco void
bio_status(struct bio_status * bs)366d313c28bSjsing bio_status(struct bio_status *bs)
367d313c28bSjsing {
3680a69bfccSjsing extern char *__progname;
3690a69bfccSjsing char *prefix;
370d313c28bSjsing int i;
371d313c28bSjsing
372d313c28bSjsing if (strlen(bs->bs_controller))
3730a69bfccSjsing prefix = bs->bs_controller;
3740a69bfccSjsing else
3750a69bfccSjsing prefix = __progname;
376d313c28bSjsing
3770a69bfccSjsing for (i = 0; i < bs->bs_msg_count; i++)
3784bfefca2Skn fprintf(bs->bs_msgs[i].bm_type == BIO_MSG_INFO ?
3794bfefca2Skn stdout : stderr, "%s: %s\n", prefix, bs->bs_msgs[i].bm_msg);
3800a69bfccSjsing
3810a69bfccSjsing if (bs->bs_status == BIO_STATUS_ERROR) {
3820a69bfccSjsing if (bs->bs_msg_count == 0)
3830a69bfccSjsing errx(1, "unknown error");
3840a69bfccSjsing else
385d313c28bSjsing exit(1);
386d313c28bSjsing }
3870a69bfccSjsing }
388d313c28bSjsing
389d313c28bSjsing void
bio_inq(char * name)3908ccdd032Sderaadt bio_inq(char *name)
391d4546a56Sdlg {
392cc711184Skettenis char *status, *cache;
393cc711184Skettenis char size[64], scsiname[16], volname[32];
394d865b7d2Suebayasi char percent[20], seconds[20];
395d313c28bSjsing int i, d, volheader, hotspare, unused;
396aa65acf1Sderaadt char encname[16], serial[32];
3978ccdd032Sderaadt struct bioc_inq bi;
3988ccdd032Sderaadt struct bioc_vol bv;
399c2b1f828Sderaadt struct bioc_disk bd;
400db2730c1Smarco
401db2730c1Smarco memset(&bi, 0, sizeof(bi));
4023af9de98Smarco
403545c4d7fSjsing bi.bi_bio.bio_cookie = bio_cookie;
4043af9de98Smarco
405df69c215Sderaadt if (ioctl(devh, BIOCINQ, &bi) == -1) {
40603b2dfbfShenning if (errno == ENOTTY)
40703b2dfbfShenning bio_diskinq(name);
40803b2dfbfShenning else
409da3b0664Shenning err(1, "BIOCINQ");
4103af9de98Smarco return;
4113af9de98Smarco }
4123af9de98Smarco
413d313c28bSjsing bio_status(&bi.bi_bio.bio_status);
414d313c28bSjsing
4158ccdd032Sderaadt volheader = 0;
4168ccdd032Sderaadt for (i = 0; i < bi.bi_novol; i++) {
417db2730c1Smarco memset(&bv, 0, sizeof(bv));
418545c4d7fSjsing bv.bv_bio.bio_cookie = bio_cookie;
4198ccdd032Sderaadt bv.bv_volid = i;
4200a92ff65Sderaadt bv.bv_percent = -1;
4219017fb97Sderaadt bv.bv_seconds = 0;
42270a2ae7bSmarco
423df69c215Sderaadt if (ioctl(devh, BIOCVOL, &bv) == -1)
424da3b0664Shenning err(1, "BIOCVOL");
4253af9de98Smarco
426d313c28bSjsing bio_status(&bv.bv_bio.bio_status);
427d313c28bSjsing
4288ccdd032Sderaadt if (name && strcmp(name, bv.bv_dev) != 0)
4298ccdd032Sderaadt continue;
4308ccdd032Sderaadt
4318ccdd032Sderaadt if (!volheader) {
4328ccdd032Sderaadt volheader = 1;
433150c22bbSjcs printf("%-11s %-10s %14s %-8s\n",
4348ccdd032Sderaadt "Volume", "Status", "Size", "Device");
4358ccdd032Sderaadt }
4368ccdd032Sderaadt
4370a92ff65Sderaadt percent[0] = '\0';
4389017fb97Sderaadt seconds[0] = '\0';
4390a92ff65Sderaadt if (bv.bv_percent != -1)
4400a92ff65Sderaadt snprintf(percent, sizeof percent,
4410a92ff65Sderaadt " %d%% done", bv.bv_percent);
4429017fb97Sderaadt if (bv.bv_seconds)
4439017fb97Sderaadt snprintf(seconds, sizeof seconds,
4449017fb97Sderaadt " %u seconds", bv.bv_seconds);
4458ccdd032Sderaadt switch (bv.bv_status) {
446db2730c1Smarco case BIOC_SVONLINE:
4478ccdd032Sderaadt status = BIOC_SVONLINE_S;
448db2730c1Smarco break;
449db2730c1Smarco case BIOC_SVOFFLINE:
4508ccdd032Sderaadt status = BIOC_SVOFFLINE_S;
451db2730c1Smarco break;
452db2730c1Smarco case BIOC_SVDEGRADED:
4538ccdd032Sderaadt status = BIOC_SVDEGRADED_S;
454db2730c1Smarco break;
4550a92ff65Sderaadt case BIOC_SVBUILDING:
4560a92ff65Sderaadt status = BIOC_SVBUILDING_S;
4570a92ff65Sderaadt break;
4580a92ff65Sderaadt case BIOC_SVREBUILD:
4590a92ff65Sderaadt status = BIOC_SVREBUILD_S;
4600a92ff65Sderaadt break;
4610a92ff65Sderaadt case BIOC_SVSCRUB:
4620a92ff65Sderaadt status = BIOC_SVSCRUB_S;
4630a92ff65Sderaadt break;
464db2730c1Smarco case BIOC_SVINVALID:
465db2730c1Smarco default:
4668ccdd032Sderaadt status = BIOC_SVINVALID_S;
467d4546a56Sdlg }
468cc711184Skettenis switch (bv.bv_cache) {
469cc711184Skettenis case BIOC_CVWRITEBACK:
470cc711184Skettenis cache = BIOC_CVWRITEBACK_S;
471cc711184Skettenis break;
472cc711184Skettenis case BIOC_CVWRITETHROUGH:
473cc711184Skettenis cache = BIOC_CVWRITETHROUGH_S;
474cc711184Skettenis break;
475cc711184Skettenis case BIOC_CVUNKNOWN:
476cc711184Skettenis default:
477cc711184Skettenis cache = BIOC_CVUNKNOWN_S;
478cc711184Skettenis }
4793af9de98Smarco
480aa65acf1Sderaadt snprintf(volname, sizeof volname, "%s %u",
4818ccdd032Sderaadt bi.bi_dev, bv.bv_volid);
482b9950701Smarco
4839ecba717Sderaadt unused = 0;
4849ecba717Sderaadt hotspare = 0;
485aa65acf1Sderaadt if (bv.bv_level == -1 && bv.bv_nodisk == 1)
486aa65acf1Sderaadt hotspare = 1;
487b9950701Smarco else if (bv.bv_level == -2 && bv.bv_nodisk == 1)
488b9950701Smarco unused = 1;
489aa65acf1Sderaadt else {
4908ccdd032Sderaadt if (human)
4918ccdd032Sderaadt fmt_scaled(bv.bv_size, size);
4928ccdd032Sderaadt else
4938ccdd032Sderaadt snprintf(size, sizeof size, "%14llu",
4948ccdd032Sderaadt bv.bv_size);
495d5e6461fSkn printf("%11s %-10s %14s %-7s ",
496d5e6461fSkn volname, status, size, bv.bv_dev);
497da935596Stodd switch (bv.bv_level) {
498da935596Stodd case 'C':
499d5e6461fSkn printf("CRYPTO%s%s\n",
500da935596Stodd percent, seconds);
501da935596Stodd break;
5022b5fc845Sjsing case 'c':
503d5e6461fSkn printf("CONCAT%s%s\n",
5042b5fc845Sjsing percent, seconds);
5052b5fc845Sjsing break;
50601de20b5Sstsp case 0x1C:
50733c65cd8Skn case 0x1E:
508d5e6461fSkn printf("RAID%X%s%s %s\n",
50901de20b5Sstsp bv.bv_level, percent, seconds, cache);
51001de20b5Sstsp break;
511da935596Stodd default:
512d5e6461fSkn printf("RAID%u%s%s %s\n",
513cc711184Skettenis bv.bv_level, percent, seconds, cache);
514da935596Stodd break;
515da935596Stodd }
516da935596Stodd
517aa65acf1Sderaadt }
5188ccdd032Sderaadt
5198ccdd032Sderaadt for (d = 0; d < bv.bv_nodisk; d++) {
520db2730c1Smarco memset(&bd, 0, sizeof(bd));
521545c4d7fSjsing bd.bd_bio.bio_cookie = bio_cookie;
5228ccdd032Sderaadt bd.bd_diskid = d;
5238ccdd032Sderaadt bd.bd_volid = i;
524d865b7d2Suebayasi bd.bd_patrol.bdp_percent = -1;
525d865b7d2Suebayasi bd.bd_patrol.bdp_seconds = 0;
5263af9de98Smarco
527df69c215Sderaadt if (ioctl(devh, BIOCDISK, &bd) == -1)
528da3b0664Shenning err(1, "BIOCDISK");
5293af9de98Smarco
530d313c28bSjsing bio_status(&bd.bd_bio.bio_status);
531d313c28bSjsing
5328ccdd032Sderaadt switch (bd.bd_status) {
533db2730c1Smarco case BIOC_SDONLINE:
5348ccdd032Sderaadt status = BIOC_SDONLINE_S;
535d4546a56Sdlg break;
536db2730c1Smarco case BIOC_SDOFFLINE:
5378ccdd032Sderaadt status = BIOC_SDOFFLINE_S;
538d4546a56Sdlg break;
539db2730c1Smarco case BIOC_SDFAILED:
5408ccdd032Sderaadt status = BIOC_SDFAILED_S;
541db2730c1Smarco break;
542db2730c1Smarco case BIOC_SDREBUILD:
5438ccdd032Sderaadt status = BIOC_SDREBUILD_S;
544db2730c1Smarco break;
545db2730c1Smarco case BIOC_SDHOTSPARE:
5468ccdd032Sderaadt status = BIOC_SDHOTSPARE_S;
547db2730c1Smarco break;
548db2730c1Smarco case BIOC_SDUNUSED:
5498ccdd032Sderaadt status = BIOC_SDUNUSED_S;
550db2730c1Smarco break;
551e1dfb373Sderaadt case BIOC_SDSCRUB:
552e1dfb373Sderaadt status = BIOC_SDSCRUB_S;
553e1dfb373Sderaadt break;
554db2730c1Smarco case BIOC_SDINVALID:
555d4546a56Sdlg default:
5568ccdd032Sderaadt status = BIOC_SDINVALID_S;
557d4546a56Sdlg }
558aa65acf1Sderaadt
559b9950701Smarco if (hotspare || unused)
560aa65acf1Sderaadt ; /* use volname from parent volume */
561aa65acf1Sderaadt else
562aa65acf1Sderaadt snprintf(volname, sizeof volname, " %3u",
563aa65acf1Sderaadt bd.bd_diskid);
564aa65acf1Sderaadt
5650054cd36Sjsing if (bv.bv_level == 'C' && bd.bd_size == 0)
5660054cd36Sjsing snprintf(size, sizeof size, "%14s", "key disk");
5670054cd36Sjsing else if (human)
5688ccdd032Sderaadt fmt_scaled(bd.bd_size, size);
5698ccdd032Sderaadt else
5708ccdd032Sderaadt snprintf(size, sizeof size, "%14llu",
5718ccdd032Sderaadt bd.bd_size);
5728ccdd032Sderaadt snprintf(scsiname, sizeof scsiname,
57343d61178Sderaadt "%u:%u.%u",
57443d61178Sderaadt bd.bd_channel, bd.bd_target, bd.bd_lun);
5755978b28dSderaadt if (bd.bd_procdev[0])
576abe9d68eSderaadt strlcpy(encname, bd.bd_procdev, sizeof encname);
5775978b28dSderaadt else
578abe9d68eSderaadt strlcpy(encname, "noencl", sizeof encname);
579abe9d68eSderaadt if (bd.bd_serial[0])
580abe9d68eSderaadt strlcpy(serial, bd.bd_serial, sizeof serial);
581abe9d68eSderaadt else
582abe9d68eSderaadt strlcpy(serial, "unknown serial", sizeof serial);
5838ccdd032Sderaadt
584d865b7d2Suebayasi percent[0] = '\0';
585d865b7d2Suebayasi seconds[0] = '\0';
586d865b7d2Suebayasi if (bd.bd_patrol.bdp_percent != -1)
587d865b7d2Suebayasi snprintf(percent, sizeof percent,
588d865b7d2Suebayasi " patrol %d%% done", bd.bd_patrol.bdp_percent);
589d865b7d2Suebayasi if (bd.bd_patrol.bdp_seconds)
590d865b7d2Suebayasi snprintf(seconds, sizeof seconds,
591d865b7d2Suebayasi " %u seconds", bd.bd_patrol.bdp_seconds);
592d865b7d2Suebayasi
593150c22bbSjcs printf("%11s %-10s %14s %-7s %-6s <%s>\n",
594aa65acf1Sderaadt volname, status, size, scsiname, encname,
5958ccdd032Sderaadt bd.bd_vendor);
596abe9d68eSderaadt if (verbose)
597d865b7d2Suebayasi printf("%11s %-10s %14s %-7s %-6s '%s'%s%s\n",
598d865b7d2Suebayasi "", "", "", "", "", serial, percent, seconds);
599d4546a56Sdlg }
600d4546a56Sdlg }
601d4546a56Sdlg }
602edfd9792Smarco
603edfd9792Smarco void
bio_alarm(char * arg)604edfd9792Smarco bio_alarm(char *arg)
605edfd9792Smarco {
6068ccdd032Sderaadt struct bioc_alarm ba;
607edfd9792Smarco
608c2b1f828Sderaadt memset(&ba, 0, sizeof(ba));
609545c4d7fSjsing ba.ba_bio.bio_cookie = bio_cookie;
610edfd9792Smarco
611edfd9792Smarco switch (arg[0]) {
612edfd9792Smarco case 'q': /* silence alarm */
613edfd9792Smarco /* FALLTHROUGH */
614edfd9792Smarco case 's':
6158ccdd032Sderaadt ba.ba_opcode = BIOC_SASILENCE;
616edfd9792Smarco break;
617edfd9792Smarco
618edfd9792Smarco case 'e': /* enable alarm */
6198ccdd032Sderaadt ba.ba_opcode = BIOC_SAENABLE;
620edfd9792Smarco break;
621edfd9792Smarco
622edfd9792Smarco case 'd': /* disable alarm */
6238ccdd032Sderaadt ba.ba_opcode = BIOC_SADISABLE;
624edfd9792Smarco break;
625edfd9792Smarco
626edfd9792Smarco case 't': /* test alarm */
6278ccdd032Sderaadt ba.ba_opcode = BIOC_SATEST;
628edfd9792Smarco break;
629edfd9792Smarco
630edfd9792Smarco case 'g': /* get alarm state */
6318ccdd032Sderaadt ba.ba_opcode = BIOC_GASTATUS;
632edfd9792Smarco break;
633edfd9792Smarco
634edfd9792Smarco default:
635da3b0664Shenning errx(1, "invalid alarm function: %s", arg);
636edfd9792Smarco }
637edfd9792Smarco
638df69c215Sderaadt if (ioctl(devh, BIOCALARM, &ba) == -1)
639da3b0664Shenning err(1, "BIOCALARM");
640edfd9792Smarco
641d313c28bSjsing bio_status(&ba.ba_bio.bio_status);
642d313c28bSjsing
643d313c28bSjsing if (arg[0] == 'g')
644edfd9792Smarco printf("alarm is currently %s\n",
6458ccdd032Sderaadt ba.ba_status ? "enabled" : "disabled");
646edfd9792Smarco }
6476de960dcSmarco
648a15048bbSmarco int
bio_getvolbyname(char * name)649a15048bbSmarco bio_getvolbyname(char *name)
650a15048bbSmarco {
651d313c28bSjsing int id = -1, i;
652a15048bbSmarco struct bioc_inq bi;
653a15048bbSmarco struct bioc_vol bv;
654a15048bbSmarco
655a15048bbSmarco memset(&bi, 0, sizeof(bi));
656545c4d7fSjsing bi.bi_bio.bio_cookie = bio_cookie;
657df69c215Sderaadt if (ioctl(devh, BIOCINQ, &bi) == -1)
658a15048bbSmarco err(1, "BIOCINQ");
659a15048bbSmarco
660d313c28bSjsing bio_status(&bi.bi_bio.bio_status);
661d313c28bSjsing
662a15048bbSmarco for (i = 0; i < bi.bi_novol; i++) {
663a15048bbSmarco memset(&bv, 0, sizeof(bv));
664545c4d7fSjsing bv.bv_bio.bio_cookie = bio_cookie;
665a15048bbSmarco bv.bv_volid = i;
666df69c215Sderaadt if (ioctl(devh, BIOCVOL, &bv) == -1)
667a15048bbSmarco err(1, "BIOCVOL");
668a15048bbSmarco
669d313c28bSjsing bio_status(&bv.bv_bio.bio_status);
670d313c28bSjsing
671a15048bbSmarco if (name && strcmp(name, bv.bv_dev) != 0)
672a15048bbSmarco continue;
673a15048bbSmarco id = i;
674a15048bbSmarco break;
675a15048bbSmarco }
676a15048bbSmarco
677a15048bbSmarco return (id);
678a15048bbSmarco }
679a15048bbSmarco
680ebaf584eSderaadt void
bio_setstate(char * arg,int status,char * devicename)6818d8693a2Sdtucker bio_setstate(char *arg, int status, char *devicename)
6826de960dcSmarco {
6836de960dcSmarco struct bioc_setstate bs;
6846de960dcSmarco struct locator location;
685a15048bbSmarco struct stat sb;
68641eccc89Sderaadt const char *errstr;
6876de960dcSmarco
688a15048bbSmarco memset(&bs, 0, sizeof(bs));
689a15048bbSmarco if (stat(arg, &sb) == -1) {
690a15048bbSmarco /* use CTL */
69141eccc89Sderaadt errstr = str2locator(arg, &location);
69241eccc89Sderaadt if (errstr)
69341eccc89Sderaadt errx(1, "Target %s: %s", arg, errstr);
6946de960dcSmarco bs.bs_channel = location.channel;
6956de960dcSmarco bs.bs_target = location.target;
6966de960dcSmarco bs.bs_lun = location.lun;
697a15048bbSmarco } else {
698a15048bbSmarco /* use other id */
699a15048bbSmarco bs.bs_other_id = sb.st_rdev;
700a15048bbSmarco bs.bs_other_id_type = BIOC_SSOTHER_DEVT;
701a15048bbSmarco }
702a15048bbSmarco
703545c4d7fSjsing bs.bs_bio.bio_cookie = bio_cookie;
704a15048bbSmarco bs.bs_status = status;
705a15048bbSmarco
706d2647ac1Sjsing if (status != BIOC_SSHOTSPARE) {
707a15048bbSmarco /* make sure user supplied a sd device */
7088d8693a2Sdtucker bs.bs_volid = bio_getvolbyname(devicename);
709a15048bbSmarco if (bs.bs_volid == -1)
7108d8693a2Sdtucker errx(1, "invalid device %s", devicename);
711d2647ac1Sjsing }
7126de960dcSmarco
713df69c215Sderaadt if (ioctl(devh, BIOCSETSTATE, &bs) == -1)
714da3b0664Shenning err(1, "BIOCSETSTATE");
715d313c28bSjsing
716d313c28bSjsing bio_status(&bs.bs_bio.bio_status);
7176de960dcSmarco }
718c55617f1Sdlg
719c55617f1Sdlg void
bio_setblink(char * name,char * arg,int blink)720a928c459Sderaadt bio_setblink(char *name, char *arg, int blink)
721c55617f1Sdlg {
72250d3c4dcSdlg struct locator location;
723c2b1f828Sderaadt struct bioc_blink bb;
72450d3c4dcSdlg struct bioc_inq bi;
72550d3c4dcSdlg struct bioc_vol bv;
72650d3c4dcSdlg struct bioc_disk bd;
72741eccc89Sderaadt const char *errstr;
72850d3c4dcSdlg int v, d, rv;
729c55617f1Sdlg
73041eccc89Sderaadt errstr = str2locator(arg, &location);
73141eccc89Sderaadt if (errstr)
73241eccc89Sderaadt errx(1, "Target %s: %s", arg, errstr);
73350d3c4dcSdlg
7340505205bSdlg /* try setting blink on the device directly */
7350505205bSdlg memset(&bb, 0, sizeof(bb));
736545c4d7fSjsing bb.bb_bio.bio_cookie = bio_cookie;
7370505205bSdlg bb.bb_status = blink;
7380505205bSdlg bb.bb_target = location.target;
739649724a4Smarco bb.bb_channel = location.channel;
7400505205bSdlg rv = ioctl(devh, BIOCBLINK, &bb);
741d313c28bSjsing
742d313c28bSjsing if (rv == 0 && bb.bb_bio.bio_status.bs_status == BIO_STATUS_UNKNOWN)
7430505205bSdlg return;
7440505205bSdlg
745d313c28bSjsing if (rv == 0 && bb.bb_bio.bio_status.bs_status == BIO_STATUS_SUCCESS) {
746d313c28bSjsing bio_status(&bb.bb_bio.bio_status);
747d313c28bSjsing return;
748d313c28bSjsing }
749d313c28bSjsing
750855d4e83Ssobrado /* if the blink didn't work, try to find something that will */
7510505205bSdlg
75250d3c4dcSdlg memset(&bi, 0, sizeof(bi));
753545c4d7fSjsing bi.bi_bio.bio_cookie = bio_cookie;
754df69c215Sderaadt if (ioctl(devh, BIOCINQ, &bi) == -1)
755da3b0664Shenning err(1, "BIOCINQ");
75650d3c4dcSdlg
757d313c28bSjsing bio_status(&bi.bi_bio.bio_status);
758d313c28bSjsing
75950d3c4dcSdlg for (v = 0; v < bi.bi_novol; v++) {
76050d3c4dcSdlg memset(&bv, 0, sizeof(bv));
761545c4d7fSjsing bv.bv_bio.bio_cookie = bio_cookie;
76250d3c4dcSdlg bv.bv_volid = v;
763df69c215Sderaadt if (ioctl(devh, BIOCVOL, &bv) == -1)
764da3b0664Shenning err(1, "BIOCVOL");
76550d3c4dcSdlg
766d313c28bSjsing bio_status(&bv.bv_bio.bio_status);
767d313c28bSjsing
76850d3c4dcSdlg if (name && strcmp(name, bv.bv_dev) != 0)
76950d3c4dcSdlg continue;
77050d3c4dcSdlg
77150d3c4dcSdlg for (d = 0; d < bv.bv_nodisk; d++) {
77250d3c4dcSdlg memset(&bd, 0, sizeof(bd));
773545c4d7fSjsing bd.bd_bio.bio_cookie = bio_cookie;
77450d3c4dcSdlg bd.bd_volid = v;
77550d3c4dcSdlg bd.bd_diskid = d;
77650d3c4dcSdlg
777df69c215Sderaadt if (ioctl(devh, BIOCDISK, &bd) == -1)
778da3b0664Shenning err(1, "BIOCDISK");
77950d3c4dcSdlg
780d313c28bSjsing bio_status(&bd.bd_bio.bio_status);
781d313c28bSjsing
78250d3c4dcSdlg if (bd.bd_channel == location.channel &&
78350d3c4dcSdlg bd.bd_target == location.target &&
78450d3c4dcSdlg bd.bd_lun == location.lun) {
785d313c28bSjsing if (bd.bd_procdev[0] != '\0')
78650d3c4dcSdlg bio_blink(bd.bd_procdev,
787a928c459Sderaadt location.target, blink);
788d313c28bSjsing else
789d313c28bSjsing warnx("Disk %s is not in an enclosure",
790d313c28bSjsing arg);
79150d3c4dcSdlg return;
79250d3c4dcSdlg }
79350d3c4dcSdlg }
79450d3c4dcSdlg }
79550d3c4dcSdlg
79641eccc89Sderaadt warnx("Disk %s does not exist", arg);
79750d3c4dcSdlg }
79850d3c4dcSdlg
79950d3c4dcSdlg void
bio_blink(char * enclosure,int target,int blinktype)800a928c459Sderaadt bio_blink(char *enclosure, int target, int blinktype)
80150d3c4dcSdlg {
80250d3c4dcSdlg int bioh;
803d313c28bSjsing struct bio_locate bl;
80450d3c4dcSdlg struct bioc_blink blink;
80550d3c4dcSdlg
80641eccc89Sderaadt bioh = open("/dev/bio", O_RDWR);
80750d3c4dcSdlg if (bioh == -1)
80841eccc89Sderaadt err(1, "Can't open %s", "/dev/bio");
80950d3c4dcSdlg
810c2b1f828Sderaadt memset(&bl, 0, sizeof(bl));
811d313c28bSjsing bl.bl_name = enclosure;
812df69c215Sderaadt if (ioctl(bioh, BIOCLOCATE, &bl) == -1)
81341eccc89Sderaadt errx(1, "Can't locate %s device via %s", enclosure, "/dev/bio");
814c55617f1Sdlg
815c55617f1Sdlg memset(&blink, 0, sizeof(blink));
816545c4d7fSjsing blink.bb_bio.bio_cookie = bio_cookie;
817a928c459Sderaadt blink.bb_status = blinktype;
818c55617f1Sdlg blink.bb_target = target;
819c55617f1Sdlg
820df69c215Sderaadt if (ioctl(bioh, BIOCBLINK, &blink) == -1)
821da3b0664Shenning err(1, "BIOCBLINK");
82250d3c4dcSdlg
823d313c28bSjsing bio_status(&blink.bb_bio.bio_status);
824d313c28bSjsing
82550d3c4dcSdlg close(bioh);
826c55617f1Sdlg }
8277195049bSmarco
8287195049bSmarco void
bio_createraid(u_int16_t level,char * dev_list,char * key_disk)8290054cd36Sjsing bio_createraid(u_int16_t level, char *dev_list, char *key_disk)
8307195049bSmarco {
8317195049bSmarco struct bioc_createraid create;
832aef7fe28Shshoexer struct sr_crypto_kdfinfo kdfinfo;
8333487a6b1Sjsing struct sr_crypto_pbkdf kdfhint;
8340054cd36Sjsing struct stat sb;
8358b0d0f28Sjsing int rv, no_dev, fd;
8367f8eae2bSnicm dev_t *dt;
8377195049bSmarco u_int16_t min_disks = 0;
8387195049bSmarco
8397195049bSmarco if (!dev_list)
8407195049bSmarco errx(1, "no devices specified");
8417195049bSmarco
842f9b0dfcfStedu dt = calloc(1, BIOC_CRMAXLEN);
84346bc198bSmarco if (!dt)
84446bc198bSmarco err(1, "not enough memory for dev_t list");
84546bc198bSmarco
84646bc198bSmarco no_dev = bio_parse_devlist(dev_list, dt);
84746bc198bSmarco
8487195049bSmarco switch (level) {
8497195049bSmarco case 0:
85084e48fabSmarco min_disks = 2;
8517195049bSmarco break;
8527195049bSmarco case 1:
8537195049bSmarco min_disks = 2;
8547195049bSmarco break;
855e717853eSmarco case 5:
856e717853eSmarco min_disks = 3;
857e717853eSmarco break;
85884e48fabSmarco case 'C':
85901de20b5Sstsp case 0x1C:
860aef7fe28Shshoexer min_disks = 1;
86184e48fabSmarco break;
86298b750e4Stedu case 'c':
863b346a95bSkrw min_disks = 1;
86498b750e4Stedu break;
8657195049bSmarco default:
86624b6f6bcSkn errx(1, "unsupported RAID level");
8677195049bSmarco }
8687195049bSmarco
86984e48fabSmarco if (no_dev < min_disks)
87084e48fabSmarco errx(1, "not enough disks");
87184e48fabSmarco
872aef7fe28Shshoexer /* for crypto raid we only allow one single chunk */
873aef7fe28Shshoexer if (level == 'C' && no_dev != min_disks)
874818b0595Shalex errx(1, "not exactly one partition");
875aef7fe28Shshoexer
8767195049bSmarco memset(&create, 0, sizeof(create));
877545c4d7fSjsing create.bc_bio.bio_cookie = bio_cookie;
8787195049bSmarco create.bc_level = level;
87946bc198bSmarco create.bc_dev_list_len = no_dev * sizeof(dev_t);
88046bc198bSmarco create.bc_dev_list = dt;
881e8a57fdeSmarco create.bc_flags = BIOC_SCDEVT | cflags;
8820054cd36Sjsing create.bc_key_disk = NODEV;
8837195049bSmarco
88401de20b5Sstsp if ((level == 'C' || level == 0x1C) && key_disk == NULL) {
8850054cd36Sjsing
886aef7fe28Shshoexer memset(&kdfinfo, 0, sizeof(kdfinfo));
887aef7fe28Shshoexer memset(&kdfhint, 0, sizeof(kdfhint));
888aef7fe28Shshoexer
8890054cd36Sjsing create.bc_flags |= BIOC_SCNOAUTOASSEMBLE;
8900054cd36Sjsing
891aef7fe28Shshoexer create.bc_opaque = &kdfhint;
892aef7fe28Shshoexer create.bc_opaque_size = sizeof(kdfhint);
893aef7fe28Shshoexer create.bc_opaque_flags = BIOC_SOOUT;
894aef7fe28Shshoexer
895aef7fe28Shshoexer /* try to get KDF hint */
896df69c215Sderaadt if (ioctl(devh, BIOCCREATERAID, &create) == -1)
89783e979edShshoexer err(1, "ioctl");
89883e979edShshoexer
899d313c28bSjsing bio_status(&create.bc_bio.bio_status);
900d313c28bSjsing
90183e979edShshoexer if (create.bc_opaque_status == BIOC_SOINOUT_OK) {
902c6446370Sjsing bio_kdf_derive(&kdfinfo, &kdfhint, "Passphrase: ", 0);
903aef7fe28Shshoexer memset(&kdfhint, 0, sizeof(kdfhint));
904aef7fe28Shshoexer } else {
905aef7fe28Shshoexer bio_kdf_generate(&kdfinfo);
906aef7fe28Shshoexer }
907aef7fe28Shshoexer
908aef7fe28Shshoexer create.bc_opaque = &kdfinfo;
909aef7fe28Shshoexer create.bc_opaque_size = sizeof(kdfinfo);
910aef7fe28Shshoexer create.bc_opaque_flags = BIOC_SOIN;
9110054cd36Sjsing
91201de20b5Sstsp } else if ((level == 'C' || level == 0x1C) && key_disk != NULL) {
9130054cd36Sjsing
9148b0d0f28Sjsing /* Get device number for key disk. */
9158b0d0f28Sjsing fd = opendev(key_disk, O_RDONLY, OPENDEV_BLCK, NULL);
9168b0d0f28Sjsing if (fd == -1)
9178b0d0f28Sjsing err(1, "could not open %s", key_disk);
9188b0d0f28Sjsing if (fstat(fd, &sb) == -1) {
919ffb4dd05Sguenther int saved_errno = errno;
9208b0d0f28Sjsing close(fd);
921ffb4dd05Sguenther errc(1, saved_errno, "could not stat %s", key_disk);
9228b0d0f28Sjsing }
9238b0d0f28Sjsing close(fd);
9240054cd36Sjsing create.bc_key_disk = sb.st_rdev;
9250054cd36Sjsing
9260054cd36Sjsing memset(&kdfinfo, 0, sizeof(kdfinfo));
9270054cd36Sjsing
9280054cd36Sjsing kdfinfo.genkdf.len = sizeof(kdfinfo.genkdf);
9290054cd36Sjsing kdfinfo.genkdf.type = SR_CRYPTOKDFT_KEYDISK;
9300054cd36Sjsing kdfinfo.len = sizeof(kdfinfo);
9310054cd36Sjsing kdfinfo.flags = SR_CRYPTOKDF_HINT;
9320054cd36Sjsing
9330054cd36Sjsing create.bc_opaque = &kdfinfo;
9340054cd36Sjsing create.bc_opaque_size = sizeof(kdfinfo);
9350054cd36Sjsing create.bc_opaque_flags = BIOC_SOIN;
9360054cd36Sjsing
937aef7fe28Shshoexer }
938aef7fe28Shshoexer
9397195049bSmarco rv = ioctl(devh, BIOCCREATERAID, &create);
9400a488504Spelikan explicit_bzero(&kdfinfo, sizeof(kdfinfo));
941d313c28bSjsing if (rv == -1)
942da3b0664Shenning err(1, "BIOCCREATERAID");
943d313c28bSjsing
944d313c28bSjsing bio_status(&create.bc_bio.bio_status);
94546bc198bSmarco
94646bc198bSmarco free(dt);
94746bc198bSmarco }
94846bc198bSmarco
949aef7fe28Shshoexer void
bio_kdf_derive(struct sr_crypto_kdfinfo * kdfinfo,struct sr_crypto_pbkdf * kdfhint,char * prompt,int verify)9503487a6b1Sjsing bio_kdf_derive(struct sr_crypto_kdfinfo *kdfinfo, struct sr_crypto_pbkdf
951c6446370Sjsing *kdfhint, char* prompt, int verify)
952aef7fe28Shshoexer {
953aef7fe28Shshoexer if (!kdfinfo)
954aef7fe28Shshoexer errx(1, "invalid KDF info");
955aef7fe28Shshoexer if (!kdfhint)
956aef7fe28Shshoexer errx(1, "invalid KDF hint");
957aef7fe28Shshoexer
9583487a6b1Sjsing if (kdfhint->generic.len != sizeof(*kdfhint))
959aef7fe28Shshoexer errx(1, "KDF hint has invalid size");
960aef7fe28Shshoexer
961aef7fe28Shshoexer kdfinfo->flags = SR_CRYPTOKDF_KEY;
962aef7fe28Shshoexer kdfinfo->len = sizeof(*kdfinfo);
963aef7fe28Shshoexer
9643487a6b1Sjsing derive_key(kdfhint->generic.type, kdfhint->rounds,
965aef7fe28Shshoexer kdfinfo->maskkey, sizeof(kdfinfo->maskkey),
96661f93244Sjsing kdfhint->salt, sizeof(kdfhint->salt),
96761f93244Sjsing prompt, verify);
968aef7fe28Shshoexer }
969aef7fe28Shshoexer
970aef7fe28Shshoexer void
bio_kdf_generate(struct sr_crypto_kdfinfo * kdfinfo)971aef7fe28Shshoexer bio_kdf_generate(struct sr_crypto_kdfinfo *kdfinfo)
972aef7fe28Shshoexer {
973aef7fe28Shshoexer if (!kdfinfo)
974aef7fe28Shshoexer errx(1, "invalid KDF info");
975aef7fe28Shshoexer
976ef0eb24eSjsing if (rflag == -1)
977ef0eb24eSjsing rflag = bcrypt_pbkdf_autorounds();
978ef0eb24eSjsing
9793487a6b1Sjsing kdfinfo->pbkdf.generic.len = sizeof(kdfinfo->pbkdf);
9802ba69c71Sjsing kdfinfo->pbkdf.generic.type = SR_CRYPTOKDFT_BCRYPT_PBKDF;
981b4f9a699Skn kdfinfo->pbkdf.rounds = rflag;
98261f93244Sjsing
9830054cd36Sjsing kdfinfo->flags = SR_CRYPTOKDF_KEY | SR_CRYPTOKDF_HINT;
98461f93244Sjsing kdfinfo->len = sizeof(*kdfinfo);
985aef7fe28Shshoexer
986aef7fe28Shshoexer /* generate salt */
9873487a6b1Sjsing arc4random_buf(kdfinfo->pbkdf.salt, sizeof(kdfinfo->pbkdf.salt));
988aef7fe28Shshoexer
9893487a6b1Sjsing derive_key(kdfinfo->pbkdf.generic.type, kdfinfo->pbkdf.rounds,
990aef7fe28Shshoexer kdfinfo->maskkey, sizeof(kdfinfo->maskkey),
9913487a6b1Sjsing kdfinfo->pbkdf.salt, sizeof(kdfinfo->pbkdf.salt),
9925f219970Skn "New passphrase: ", interactive);
993aef7fe28Shshoexer }
994aef7fe28Shshoexer
99546bc198bSmarco int
bio_parse_devlist(char * lst,dev_t * dt)99646bc198bSmarco bio_parse_devlist(char *lst, dev_t *dt)
99746bc198bSmarco {
99846bc198bSmarco char *s, *e;
99946bc198bSmarco u_int32_t sz = 0;
100046bc198bSmarco int no_dev = 0, i, x;
100146bc198bSmarco struct stat sb;
1002b9fc9a72Sderaadt char dev[PATH_MAX];
1003e37c64dbSjsing int fd;
100446bc198bSmarco
100546bc198bSmarco if (!lst)
100646bc198bSmarco errx(1, "invalid device list");
100746bc198bSmarco
100846bc198bSmarco s = e = lst;
100946bc198bSmarco /* make sure we have a valid device list like /dev/sdNa,/dev/sdNNa */
101046bc198bSmarco while (*e != '\0') {
101146bc198bSmarco if (*e == ',')
101246bc198bSmarco s = e + 1;
101346bc198bSmarco else if (*(e + 1) == '\0' || *(e + 1) == ',') {
101446bc198bSmarco /* got one */
101546bc198bSmarco sz = e - s + 1;
10165c1f8f6bSdjm strlcpy(dev, s, sz + 1);
1017e37c64dbSjsing fd = opendev(dev, O_RDONLY, OPENDEV_BLCK, NULL);
1018e37c64dbSjsing if (fd == -1)
1019e37c64dbSjsing err(1, "could not open %s", dev);
1020e37c64dbSjsing if (fstat(fd, &sb) == -1) {
1021ffb4dd05Sguenther int saved_errno = errno;
1022e37c64dbSjsing close(fd);
1023ffb4dd05Sguenther errc(1, saved_errno, "could not stat %s", dev);
1024e37c64dbSjsing }
1025e37c64dbSjsing close(fd);
102646bc198bSmarco dt[no_dev] = sb.st_rdev;
102746bc198bSmarco no_dev++;
10285c1f8f6bSdjm if (no_dev > (int)(BIOC_CRMAXLEN / sizeof(dev_t)))
102946bc198bSmarco errx(1, "too many devices on device list");
103046bc198bSmarco }
103146bc198bSmarco e++;
103246bc198bSmarco }
103346bc198bSmarco
103446bc198bSmarco for (i = 0; i < no_dev; i++)
103546bc198bSmarco for (x = 0; x < no_dev; x++)
103646bc198bSmarco if (dt[i] == dt[x] && x != i)
103746bc198bSmarco errx(1, "duplicate device in list");
103846bc198bSmarco
103946bc198bSmarco return (no_dev);
10407195049bSmarco }
1041e8a57fdeSmarco
1042e8a57fdeSmarco u_int32_t
bio_createflags(char * lst)1043e8a57fdeSmarco bio_createflags(char *lst)
1044e8a57fdeSmarco {
1045e8a57fdeSmarco char *s, *e, fs[32];
1046e8a57fdeSmarco u_int32_t sz = 0;
1047e8a57fdeSmarco u_int32_t flags = 0;
1048e8a57fdeSmarco
1049e8a57fdeSmarco if (!lst)
1050e8a57fdeSmarco errx(1, "invalid flags list");
1051e8a57fdeSmarco
1052e8a57fdeSmarco s = e = lst;
1053e8a57fdeSmarco /* make sure we have a valid flags list like force,noassemeble */
1054e8a57fdeSmarco while (*e != '\0') {
1055e8a57fdeSmarco if (*e == ',')
1056e8a57fdeSmarco s = e + 1;
1057e8a57fdeSmarco else if (*(e + 1) == '\0' || *(e + 1) == ',') {
1058e8a57fdeSmarco /* got one */
1059e8a57fdeSmarco sz = e - s + 1;
1060e8a57fdeSmarco switch (s[0]) {
1061e8a57fdeSmarco case 'f':
1062e8a57fdeSmarco flags |= BIOC_SCFORCE;
1063e8a57fdeSmarco break;
1064e8a57fdeSmarco case 'n':
1065e8a57fdeSmarco flags |= BIOC_SCNOAUTOASSEMBLE;
1066e8a57fdeSmarco break;
1067e8a57fdeSmarco default:
1068e8a57fdeSmarco strlcpy(fs, s, sz + 1);
1069e8a57fdeSmarco errx(1, "invalid flag %s", fs);
1070e8a57fdeSmarco }
1071e8a57fdeSmarco }
1072e8a57fdeSmarco e++;
1073e8a57fdeSmarco }
1074e8a57fdeSmarco
1075e8a57fdeSmarco return (flags);
1076e8a57fdeSmarco }
107703b2dfbfShenning
1078c7c3e8aaSmarco void
bio_deleteraid(char * dev)1079c7c3e8aaSmarco bio_deleteraid(char *dev)
1080c7c3e8aaSmarco {
1081c7c3e8aaSmarco struct bioc_deleteraid bd;
1082c7c3e8aaSmarco memset(&bd, 0, sizeof(bd));
1083c7c3e8aaSmarco
1084545c4d7fSjsing bd.bd_bio.bio_cookie = bio_cookie;
1085a15048bbSmarco /* XXX make this a dev_t instead of a string */
1086c7c3e8aaSmarco strlcpy(bd.bd_dev, dev, sizeof bd.bd_dev);
1087df69c215Sderaadt if (ioctl(devh, BIOCDELETERAID, &bd) == -1)
1088d313c28bSjsing err(1, "BIOCDELETERAID");
1089d313c28bSjsing
1090d313c28bSjsing bio_status(&bd.bd_bio.bio_status);
1091c7c3e8aaSmarco }
1092c7c3e8aaSmarco
1093c6446370Sjsing void
bio_changepass(char * dev)1094c6446370Sjsing bio_changepass(char *dev)
1095c6446370Sjsing {
1096c6446370Sjsing struct bioc_discipline bd;
1097c6446370Sjsing struct sr_crypto_kdfpair kdfpair;
1098c6446370Sjsing struct sr_crypto_kdfinfo kdfinfo1, kdfinfo2;
10993487a6b1Sjsing struct sr_crypto_pbkdf kdfhint;
1100c6446370Sjsing int rv;
1101c6446370Sjsing
1102c6446370Sjsing memset(&bd, 0, sizeof(bd));
1103c6446370Sjsing memset(&kdfhint, 0, sizeof(kdfhint));
1104c6446370Sjsing memset(&kdfinfo1, 0, sizeof(kdfinfo1));
1105c6446370Sjsing memset(&kdfinfo2, 0, sizeof(kdfinfo2));
1106c6446370Sjsing
1107c6446370Sjsing /* XXX use dev_t instead of string. */
1108c6446370Sjsing strlcpy(bd.bd_dev, dev, sizeof(bd.bd_dev));
1109c6446370Sjsing bd.bd_cmd = SR_IOCTL_GET_KDFHINT;
1110c6446370Sjsing bd.bd_size = sizeof(kdfhint);
1111c6446370Sjsing bd.bd_data = &kdfhint;
1112c6446370Sjsing
1113df69c215Sderaadt if (ioctl(devh, BIOCDISCIPLINE, &bd) == -1)
1114d313c28bSjsing err(1, "BIOCDISCIPLINE");
1115d313c28bSjsing
1116d313c28bSjsing bio_status(&bd.bd_bio.bio_status);
1117c6446370Sjsing
1118c6446370Sjsing /* Current passphrase. */
1119c6446370Sjsing bio_kdf_derive(&kdfinfo1, &kdfhint, "Old passphrase: ", 0);
1120c6446370Sjsing
1121b4f9a699Skn if (rflag == -1) {
1122b4f9a699Skn rflag = bcrypt_pbkdf_autorounds();
1123b4f9a699Skn
1124b4f9a699Skn /* Use previous number of rounds for the same KDF if higher. */
1125b4f9a699Skn if (kdfhint.generic.type == SR_CRYPTOKDFT_BCRYPT_PBKDF &&
1126b4f9a699Skn rflag < kdfhint.rounds)
11271f1fa78aSjsing rflag = kdfhint.rounds;
1128b4f9a699Skn }
11291f1fa78aSjsing
1130c6446370Sjsing /* New passphrase. */
11312c98a0f7Sjsing bio_kdf_generate(&kdfinfo2);
1132c6446370Sjsing
1133c6446370Sjsing kdfpair.kdfinfo1 = &kdfinfo1;
1134c6446370Sjsing kdfpair.kdfsize1 = sizeof(kdfinfo1);
1135c6446370Sjsing kdfpair.kdfinfo2 = &kdfinfo2;
1136c6446370Sjsing kdfpair.kdfsize2 = sizeof(kdfinfo2);
1137c6446370Sjsing
1138c6446370Sjsing bd.bd_cmd = SR_IOCTL_CHANGE_PASSPHRASE;
1139c6446370Sjsing bd.bd_size = sizeof(kdfpair);
1140c6446370Sjsing bd.bd_data = &kdfpair;
1141c6446370Sjsing
1142c6446370Sjsing rv = ioctl(devh, BIOCDISCIPLINE, &bd);
1143c6446370Sjsing
1144c6446370Sjsing memset(&kdfhint, 0, sizeof(kdfhint));
11450a488504Spelikan explicit_bzero(&kdfinfo1, sizeof(kdfinfo1));
11460a488504Spelikan explicit_bzero(&kdfinfo2, sizeof(kdfinfo2));
1147c6446370Sjsing
1148df69c215Sderaadt if (rv == -1)
1149d313c28bSjsing err(1, "BIOCDISCIPLINE");
1150d313c28bSjsing
1151d313c28bSjsing bio_status(&bd.bd_bio.bio_status);
1152c6446370Sjsing }
1153c6446370Sjsing
115403b2dfbfShenning #define BIOCTL_VIS_NBUF 4
115503b2dfbfShenning #define BIOCTL_VIS_BUFLEN 80
115603b2dfbfShenning
115703b2dfbfShenning char *
bio_vis(char * s)115803b2dfbfShenning bio_vis(char *s)
115903b2dfbfShenning {
116003b2dfbfShenning static char rbuf[BIOCTL_VIS_NBUF][BIOCTL_VIS_BUFLEN];
116103b2dfbfShenning static uint idx = 0;
116203b2dfbfShenning char *buf;
116303b2dfbfShenning
116403b2dfbfShenning buf = rbuf[idx++];
116503b2dfbfShenning if (idx == BIOCTL_VIS_NBUF)
116603b2dfbfShenning idx = 0;
116703b2dfbfShenning
116803b2dfbfShenning strnvis(buf, s, BIOCTL_VIS_BUFLEN, VIS_NL|VIS_CSTYLE);
116903b2dfbfShenning return (buf);
117003b2dfbfShenning }
117103b2dfbfShenning
117203b2dfbfShenning void
bio_diskinq(char * sd_dev)117303b2dfbfShenning bio_diskinq(char *sd_dev)
117403b2dfbfShenning {
117503b2dfbfShenning struct dk_inquiry di;
117603b2dfbfShenning
1177da3b0664Shenning if (ioctl(devh, DIOCINQ, &di) == -1)
1178da3b0664Shenning err(1, "DIOCINQ");
117903b2dfbfShenning
118003b2dfbfShenning printf("%s: <%s, %s, %s>, serial %s\n", sd_dev, bio_vis(di.vendor),
118103b2dfbfShenning bio_vis(di.product), bio_vis(di.revision), bio_vis(di.serial));
118203b2dfbfShenning }
1183aef7fe28Shshoexer
1184aef7fe28Shshoexer void
bio_patrol(char * arg)1185d865b7d2Suebayasi bio_patrol(char *arg)
1186d865b7d2Suebayasi {
1187d865b7d2Suebayasi struct bioc_patrol bp;
1188d865b7d2Suebayasi struct timing timing;
1189d865b7d2Suebayasi const char *errstr;
1190d865b7d2Suebayasi
1191d865b7d2Suebayasi memset(&bp, 0, sizeof(bp));
1192d865b7d2Suebayasi bp.bp_bio.bio_cookie = bio_cookie;
1193d865b7d2Suebayasi
1194d865b7d2Suebayasi switch (arg[0]) {
1195d865b7d2Suebayasi case 'a':
1196d865b7d2Suebayasi bp.bp_opcode = BIOC_SPAUTO;
1197d865b7d2Suebayasi break;
1198d865b7d2Suebayasi
1199d865b7d2Suebayasi case 'm':
1200d865b7d2Suebayasi bp.bp_opcode = BIOC_SPMANUAL;
1201d865b7d2Suebayasi break;
1202d865b7d2Suebayasi
1203d865b7d2Suebayasi case 'd':
1204d865b7d2Suebayasi bp.bp_opcode = BIOC_SPDISABLE;
1205d865b7d2Suebayasi break;
1206d865b7d2Suebayasi
1207d865b7d2Suebayasi case 'g': /* get patrol state */
1208d865b7d2Suebayasi bp.bp_opcode = BIOC_GPSTATUS;
1209d865b7d2Suebayasi break;
1210d865b7d2Suebayasi
1211d865b7d2Suebayasi case 's': /* start/stop patrol */
1212d865b7d2Suebayasi if (strncmp("sta", arg, 3) == 0)
1213d865b7d2Suebayasi bp.bp_opcode = BIOC_SPSTART;
1214d865b7d2Suebayasi else
1215d865b7d2Suebayasi bp.bp_opcode = BIOC_SPSTOP;
1216d865b7d2Suebayasi break;
1217d865b7d2Suebayasi
1218d865b7d2Suebayasi default:
1219d865b7d2Suebayasi errx(1, "invalid patrol function: %s", arg);
1220d865b7d2Suebayasi }
1221d865b7d2Suebayasi
1222d865b7d2Suebayasi switch (arg[0]) {
1223d865b7d2Suebayasi case 'a':
1224d865b7d2Suebayasi errstr = str2patrol(arg, &timing);
1225d865b7d2Suebayasi if (errstr)
1226d865b7d2Suebayasi errx(1, "Patrol %s: %s", arg, errstr);
1227d865b7d2Suebayasi bp.bp_autoival = timing.interval;
1228d865b7d2Suebayasi bp.bp_autonext = timing.start;
1229d865b7d2Suebayasi break;
1230d865b7d2Suebayasi }
1231d865b7d2Suebayasi
1232df69c215Sderaadt if (ioctl(devh, BIOCPATROL, &bp) == -1)
1233d865b7d2Suebayasi err(1, "BIOCPATROL");
1234d865b7d2Suebayasi
1235d865b7d2Suebayasi bio_status(&bp.bp_bio.bio_status);
1236d865b7d2Suebayasi
1237d865b7d2Suebayasi if (arg[0] == 'g') {
1238d865b7d2Suebayasi const char *mode, *status;
1239d865b7d2Suebayasi char interval[40];
1240d865b7d2Suebayasi
1241d865b7d2Suebayasi interval[0] = '\0';
1242d865b7d2Suebayasi
1243d865b7d2Suebayasi switch (bp.bp_mode) {
1244d865b7d2Suebayasi case BIOC_SPMAUTO:
1245d865b7d2Suebayasi mode = "auto";
1246d865b7d2Suebayasi snprintf(interval, sizeof interval,
1247d865b7d2Suebayasi " interval=%d next=%d", bp.bp_autoival,
1248d865b7d2Suebayasi bp.bp_autonext - bp.bp_autonow);
1249d865b7d2Suebayasi break;
1250d865b7d2Suebayasi case BIOC_SPMMANUAL:
1251d865b7d2Suebayasi mode = "manual";
1252d865b7d2Suebayasi break;
1253d865b7d2Suebayasi case BIOC_SPMDISABLED:
1254d865b7d2Suebayasi mode = "disabled";
1255d865b7d2Suebayasi break;
1256d865b7d2Suebayasi default:
12572ad5ec6fSuebayasi mode = "unknown";
1258d865b7d2Suebayasi break;
1259d865b7d2Suebayasi }
1260d865b7d2Suebayasi switch (bp.bp_status) {
1261d865b7d2Suebayasi case BIOC_SPSSTOPPED:
1262d865b7d2Suebayasi status = "stopped";
1263d865b7d2Suebayasi break;
1264d865b7d2Suebayasi case BIOC_SPSREADY:
1265d865b7d2Suebayasi status = "ready";
1266d865b7d2Suebayasi break;
1267d865b7d2Suebayasi case BIOC_SPSACTIVE:
1268d865b7d2Suebayasi status = "active";
1269d865b7d2Suebayasi break;
1270d865b7d2Suebayasi case BIOC_SPSABORTED:
1271d865b7d2Suebayasi status = "aborted";
1272d865b7d2Suebayasi break;
1273d865b7d2Suebayasi default:
1274d865b7d2Suebayasi status = "unknown";
1275d865b7d2Suebayasi break;
1276d865b7d2Suebayasi }
1277d865b7d2Suebayasi printf("patrol mode: %s%s\n", mode, interval);
1278d865b7d2Suebayasi printf("patrol status: %s\n", status);
1279d865b7d2Suebayasi }
1280d865b7d2Suebayasi }
1281d865b7d2Suebayasi
1282ef0eb24eSjsing /*
1283ef0eb24eSjsing * Measure this system's performance by measuring the time for 100 rounds.
1284ef0eb24eSjsing * We are aiming for something that takes around 1s.
1285ef0eb24eSjsing */
1286ef0eb24eSjsing int
bcrypt_pbkdf_autorounds(void)1287ef0eb24eSjsing bcrypt_pbkdf_autorounds(void)
1288ef0eb24eSjsing {
1289ef0eb24eSjsing struct timespec before, after;
1290ef0eb24eSjsing char buf[SR_CRYPTO_MAXKEYBYTES], salt[128];
1291ef0eb24eSjsing int r = 100;
1292ef0eb24eSjsing int duration;
1293ef0eb24eSjsing
1294ef0eb24eSjsing clock_gettime(CLOCK_THREAD_CPUTIME_ID, &before);
1295ef0eb24eSjsing if (bcrypt_pbkdf("testpassword", strlen("testpassword"),
1296ef0eb24eSjsing salt, sizeof(salt), buf, sizeof(buf), r) != 0)
1297ef0eb24eSjsing errx(1, "bcrypt pbkdf failed");
1298ef0eb24eSjsing clock_gettime(CLOCK_THREAD_CPUTIME_ID, &after);
1299ef0eb24eSjsing
1300ef0eb24eSjsing duration = after.tv_sec - before.tv_sec;
1301ef0eb24eSjsing duration *= 1000000;
1302ef0eb24eSjsing duration += (after.tv_nsec - before.tv_nsec) / 1000;
1303ef0eb24eSjsing
1304ef0eb24eSjsing duration /= r;
1305ef0eb24eSjsing r = 1000000 / duration;
1306ef0eb24eSjsing
1307ef0eb24eSjsing if (r < 16)
1308ef0eb24eSjsing r = 16;
1309ef0eb24eSjsing
1310ef0eb24eSjsing return r;
1311ef0eb24eSjsing }
1312ef0eb24eSjsing
1313d865b7d2Suebayasi 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)13143487a6b1Sjsing derive_key(u_int32_t type, int rounds, u_int8_t *key, size_t keysz,
131561f93244Sjsing u_int8_t *salt, size_t saltsz, char *prompt, int verify)
1316aef7fe28Shshoexer {
131786735da2Smarco FILE *f;
131886735da2Smarco size_t pl;
131986735da2Smarco struct stat sb;
13209e8c6f5bShshoexer char passphrase[1024], verifybuf[1024];
13215f219970Skn int rpp_flag = RPP_ECHO_OFF;
1322aef7fe28Shshoexer
1323aef7fe28Shshoexer if (!key)
1324aef7fe28Shshoexer errx(1, "Invalid key");
1325aef7fe28Shshoexer if (!salt)
1326aef7fe28Shshoexer errx(1, "Invalid salt");
132761f93244Sjsing
13281a8c43f6Sjsing if (type != SR_CRYPTOKDFT_PKCS5_PBKDF2 &&
13291a8c43f6Sjsing type != SR_CRYPTOKDFT_BCRYPT_PBKDF)
133061f93244Sjsing errx(1, "unknown KDF type %d", type);
13311a8c43f6Sjsing
1332b4f9a699Skn if (rounds < (type == SR_CRYPTOKDFT_PKCS5_PBKDF2 ? 1000 : 16))
133361f93244Sjsing errx(1, "number of KDF rounds is too small: %d", rounds);
1334aef7fe28Shshoexer
1335aef7fe28Shshoexer /* get passphrase */
1336dd81669fSkn if (passfile) {
1337dd81669fSkn if ((f = fopen(passfile, "r")) == NULL)
133886735da2Smarco err(1, "invalid passphrase file");
133986735da2Smarco
134086735da2Smarco if (fstat(fileno(f), &sb) == -1)
134186735da2Smarco err(1, "can't stat passphrase file");
134286735da2Smarco if (sb.st_uid != 0)
134386735da2Smarco errx(1, "passphrase file must be owned by root");
134486735da2Smarco if ((sb.st_mode & ~S_IFMT) != (S_IRUSR | S_IWUSR))
134586735da2Smarco errx(1, "passphrase file has the wrong permissions");
134686735da2Smarco
134786735da2Smarco if (fgets(passphrase, sizeof(passphrase), f) == NULL)
134886735da2Smarco err(1, "can't read passphrase file");
134986735da2Smarco pl = strlen(passphrase);
135086735da2Smarco if (pl > 0 && passphrase[pl - 1] == '\n')
135186735da2Smarco passphrase[pl - 1] = '\0';
135286735da2Smarco else
135386735da2Smarco errx(1, "invalid passphrase length");
135486735da2Smarco
135586735da2Smarco fclose(f);
1356ba3d8661Smarco } else {
13575f219970Skn rpp_flag |= interactive ? RPP_REQUIRE_TTY : RPP_STDIN;
13585f219970Skn
1359954c7a34Skn retry:
1360c6446370Sjsing if (readpassphrase(prompt, passphrase, sizeof(passphrase),
1361b96c6ce2Sckuethe rpp_flag) == NULL)
13627eef0726Stedu err(1, "unable to read passphrase");
13631302b329Skn if (*passphrase == '\0') {
13641302b329Skn warnx("invalid passphrase length");
13651302b329Skn if (interactive)
13661302b329Skn goto retry;
13671302b329Skn exit(1);
13681302b329Skn }
1369ba3d8661Smarco }
13709e8c6f5bShshoexer
1371dd81669fSkn if (verify && !passfile) {
13729e8c6f5bShshoexer /* request user to re-type it */
13739e8c6f5bShshoexer if (readpassphrase("Re-type passphrase: ", verifybuf,
1374b96c6ce2Sckuethe sizeof(verifybuf), rpp_flag) == NULL) {
13750a488504Spelikan explicit_bzero(passphrase, sizeof(passphrase));
13767eef0726Stedu err(1, "unable to read passphrase");
13779e8c6f5bShshoexer }
13789e8c6f5bShshoexer if ((strlen(passphrase) != strlen(verifybuf)) ||
13799e8c6f5bShshoexer (strcmp(passphrase, verifybuf) != 0)) {
13800a488504Spelikan explicit_bzero(passphrase, sizeof(passphrase));
13810a488504Spelikan explicit_bzero(verifybuf, sizeof(verifybuf));
1382954c7a34Skn if (interactive) {
1383954c7a34Skn warnx("Passphrases did not match, try again");
1384954c7a34Skn goto retry;
1385954c7a34Skn }
13869e8c6f5bShshoexer errx(1, "Passphrases did not match");
13879e8c6f5bShshoexer }
13889e8c6f5bShshoexer /* forget the re-typed one */
13890a488504Spelikan explicit_bzero(verifybuf, sizeof(verifybuf));
13909e8c6f5bShshoexer }
1391aef7fe28Shshoexer
1392aef7fe28Shshoexer /* derive key from passphrase */
13931a8c43f6Sjsing if (type == SR_CRYPTOKDFT_PKCS5_PBKDF2) {
1394ef0eb24eSjsing if (verbose)
1395ef0eb24eSjsing printf("Deriving key using PKCS#5 PBKDF2 with %i rounds...\n",
1396ef0eb24eSjsing rounds);
13975c1f8f6bSdjm if (pkcs5_pbkdf2(passphrase, strlen(passphrase), salt, saltsz,
13985c1f8f6bSdjm key, keysz, rounds) != 0)
13991a8c43f6Sjsing errx(1, "pkcs5_pbkdf2 failed");
14001a8c43f6Sjsing } else if (type == SR_CRYPTOKDFT_BCRYPT_PBKDF) {
1401ef0eb24eSjsing if (verbose)
1402ef0eb24eSjsing printf("Deriving key using bcrypt PBKDF with %i rounds...\n",
1403ef0eb24eSjsing rounds);
14041a8c43f6Sjsing if (bcrypt_pbkdf(passphrase, strlen(passphrase), salt, saltsz,
14051a8c43f6Sjsing key, keysz, rounds) != 0)
14061a8c43f6Sjsing errx(1, "bcrypt_pbkdf failed");
14071a8c43f6Sjsing } else {
14081a8c43f6Sjsing errx(1, "unknown KDF type %d", type);
14091a8c43f6Sjsing }
1410aef7fe28Shshoexer
1411aef7fe28Shshoexer /* forget passphrase */
14120a488504Spelikan explicit_bzero(passphrase, sizeof(passphrase));
1413aef7fe28Shshoexer }
1414