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