cgdconfig.c revision 1.5 1 /* $NetBSD: cgdconfig.c,v 1.5 2003/03/24 02:02:50 elric Exp $ */
2
3 /*-
4 * Copyright (c) 2002, 2003 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Roland C. Dowdeswell.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 #include <sys/cdefs.h>
40 #ifndef lint
41 __COPYRIGHT(
42 "@(#) Copyright (c) 2002, 2003\
43 The NetBSD Foundation, Inc. All rights reserved.");
44 __RCSID("$NetBSD: cgdconfig.c,v 1.5 2003/03/24 02:02:50 elric Exp $");
45 #endif
46
47 #include <err.h>
48 #include <errno.h>
49 #include <fcntl.h>
50 #include <libgen.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <unistd.h>
55 #include <util.h>
56
57 #include <sys/ioctl.h>
58 #include <sys/disklabel.h>
59 #include <sys/param.h>
60
61 #include <dev/cgdvar.h>
62
63 #include <ufs/ffs/fs.h>
64
65 #include "params.h"
66 #include "pkcs5_pbkdf2.h"
67 #include "utils.h"
68
69 #define CGDCONFIG_DIR "/etc/cgd"
70 #define CGDCONFIG_CFILE CGDCONFIG_DIR "/cgd.conf"
71
72 #define ACTION_CONFIGURE 0x1 /* configure, with paramsfile */
73 #define ACTION_UNCONFIGURE 0x2 /* unconfigure */
74 #define ACTION_GENERATE 0x3 /* generate a paramsfile */
75 #define ACTION_GENERATE_CONVERT 0x4 /* generate a ``dup'' paramsfile */
76 #define ACTION_CONFIGALL 0x5 /* configure all from config file */
77 #define ACTION_UNCONFIGALL 0x6 /* unconfigure all from config file */
78 #define ACTION_CONFIGSTDIN 0x7 /* configure, key from stdin */
79
80 /* if nflag is set, do not configure/unconfigure the cgd's */
81
82 int nflag = 0;
83
84 static int configure(int, char **, struct params *, int);
85 static int configure_stdin(struct params *, int argc, char **);
86 static int generate(struct params *, int, char **, const char *);
87 static int generate_convert(struct params *, int, char **, const char *);
88 static int unconfigure(int, char **, struct params *, int);
89 static int do_all(const char *, int, char **,
90 int (*)(int, char **, struct params *, int));
91
92 #define CONFIG_FLAGS_FROMALL 1 /* called from configure_all() */
93 #define CONFIG_FLAGS_FROMMAIN 2 /* called from main() */
94
95 static int configure_params(int, const char *, const char *,
96 struct params *);
97 static bits_t *getkey(const char *, struct keygen *, int);
98 static bits_t *getkey_storedkey(const char *, struct keygen *, int);
99 static bits_t *getkey_randomkey(const char *, struct keygen *, int);
100 static bits_t *getkey_pkcs5_pbkdf2(const char *, struct keygen *, int);
101 static int opendisk_werror(const char *, char *, int);
102 static int unconfigure_fd(int);
103 static int verify(struct params *, int);
104 static int verify_disklabel(int);
105 static int verify_ffs(int);
106
107 static void usage(void);
108
109 /* Verbose Framework */
110 int verbose = 0;
111
112 #define VERBOSE(x,y) if (verbose >= x) y
113 #define VPRINTF(x,y) if (verbose >= x) printf y
114
115 static void
116 usage(void)
117 {
118
119 fprintf(stderr, "usage: %s [-nv] [-V vmeth] cgd dev [paramsfile]\n",
120 getprogname());
121 fprintf(stderr, " %s -C [-nv] [-f configfile]\n", getprogname());
122 fprintf(stderr, " %s -U [-nv] [-f configfile]\n", getprogname());
123 fprintf(stderr, " %s -G [-nv] [-i ivmeth] [-k kgmeth] "
124 "[-o outfile] paramsfile\n", getprogname());
125 fprintf(stderr, " %s -g [-nv] [-i ivmeth] [-k kgmeth] "
126 "[-o outfile] alg [keylen]\n", getprogname());
127 fprintf(stderr, " %s -s [-nv] [-i ivmeth] cgd dev alg "
128 "[keylen]\n", getprogname());
129 fprintf(stderr, " %s -u [-nv] cgd\n", getprogname());
130 exit(1);
131 }
132
133 int
134 main(int argc, char **argv)
135 {
136 struct params *p;
137 struct params *tp;
138 struct keygen *kg;
139 int action = ACTION_CONFIGURE;
140 int actions = 0;
141 int ch;
142 char cfile[FILENAME_MAX] = "";
143 char outfile[FILENAME_MAX] = "";
144
145 setprogname(*argv);
146 p = params_new();
147 kg = NULL;
148
149 while ((ch = getopt(argc, argv, "CGUV:b:f:gi:k:no:usv")) != -1)
150 switch (ch) {
151 case 'C':
152 action = ACTION_CONFIGALL;
153 actions++;
154 break;
155 case 'G':
156 action = ACTION_GENERATE_CONVERT;
157 actions++;
158 break;
159 case 'U':
160 action = ACTION_UNCONFIGALL;
161 actions++;
162 break;
163 case 'V':
164 tp = params_verify_method(string_fromcharstar(optarg));
165 if (!tp)
166 usage();
167 p = params_combine(p, tp);
168 break;
169 case 'b':
170 tp = params_bsize(atoi(optarg));
171 if (!tp)
172 usage();
173 p = params_combine(p, tp);
174 break;
175 case 'f':
176 strncpy(cfile, optarg, FILENAME_MAX);
177 break;
178 case 'g':
179 action = ACTION_GENERATE;
180 actions++;
181 break;
182 case 'i':
183 tp = params_ivmeth(string_fromcharstar(optarg));
184 p = params_combine(p, tp);
185 break;
186 case 'k':
187 kg = keygen_method(string_fromcharstar(optarg));
188 if (!kg)
189 usage();
190 keygen_addlist(&p->keygen, kg);
191 break;
192 case 'n':
193 nflag = 1;
194 break;
195 case 'o':
196 strncpy(outfile, optarg, FILENAME_MAX);
197 break;
198 case 's':
199 action = ACTION_CONFIGSTDIN;
200 actions++;
201 break;
202
203 case 'u':
204 action = ACTION_UNCONFIGURE;
205 actions++;
206 break;
207 case 'v':
208 verbose++;
209 break;
210 default:
211 usage();
212 /* NOTREACHED */
213 }
214
215 argc -= optind;
216 argv += optind;
217
218 /* validate the consistency of the arguments */
219
220 if (actions > 1)
221 usage();
222
223 switch (action) {
224 case ACTION_CONFIGURE:
225 return configure(argc, argv, p, CONFIG_FLAGS_FROMMAIN);
226 case ACTION_UNCONFIGURE:
227 return unconfigure(argc, argv, NULL, CONFIG_FLAGS_FROMMAIN);
228 case ACTION_GENERATE:
229 return generate(p, argc, argv, outfile);
230 case ACTION_GENERATE_CONVERT:
231 return generate_convert(p, argc, argv, outfile);
232 case ACTION_CONFIGALL:
233 return do_all(cfile, argc, argv, configure);
234 case ACTION_UNCONFIGALL:
235 return do_all(cfile, argc, argv, unconfigure);
236 case ACTION_CONFIGSTDIN:
237 return configure_stdin(p, argc, argv);
238 default:
239 errx(EXIT_FAILURE, "undefined action");
240 }
241 /* NOTREACHED */
242 }
243
244 static bits_t *
245 getkey(const char *dev, struct keygen *kg, int len)
246 {
247 bits_t *ret = NULL;
248 bits_t *tmp;
249
250 VPRINTF(3, ("getkey(\"%s\", %p, %d) called\n", dev, kg, len));
251 for (; kg; kg=kg->next) {
252 switch (kg->kg_method) {
253 case KEYGEN_STOREDKEY:
254 tmp = getkey_storedkey(dev, kg, len);
255 break;
256 case KEYGEN_RANDOMKEY:
257 tmp = getkey_randomkey(dev, kg, len);
258 break;
259 case KEYGEN_PKCS5_PBKDF2:
260 tmp = getkey_pkcs5_pbkdf2(dev, kg, len);
261 break;
262 default:
263 warnx("unrecognised keygen method %d in getkey()",
264 kg->kg_method);
265 if (ret)
266 bits_free(ret);
267 return NULL;
268 }
269
270 if (ret)
271 ret = bits_xor_d(tmp, ret);
272 else
273 ret = tmp;
274 }
275
276 return ret;
277 }
278
279 /*ARGSUSED*/
280 static bits_t *
281 getkey_storedkey(const char *target, struct keygen *kg, int keylen)
282 {
283
284 return bits_dup(kg->kg_key);
285 }
286
287 /*ARGSUSED*/
288 static bits_t *
289 getkey_randomkey(const char *target, struct keygen *kg, int keylen)
290 {
291
292 return bits_getrandombits(keylen);
293 }
294
295 /*ARGSUSED*/
296 static bits_t *
297 getkey_pkcs5_pbkdf2(const char *target, struct keygen *kg, int keylen)
298 {
299 bits_t *ret;
300 char *passp;
301 char buf[1024];
302 u_int8_t *tmp;
303
304 snprintf(buf, sizeof(buf), "%s's passphrase:", target);
305 passp = getpass(buf);
306 if (pkcs5_pbkdf2(&tmp, BITS2BYTES(keylen), passp, strlen(passp),
307 bits_getbuf(kg->kg_salt), BITS2BYTES(bits_len(kg->kg_salt)),
308 kg->kg_iterations)) {
309 warnx("failed to generate PKCS#5 PBKDF2 key");
310 return NULL;
311 }
312
313 ret = bits_new(tmp, keylen);
314 free(tmp);
315 return ret;
316 }
317
318 /*ARGSUSED*/
319 static int
320 unconfigure(int argc, char **argv, struct params *inparams, int flags)
321 {
322 int fd;
323 int ret;
324 char buf[MAXPATHLEN] = "";
325
326 /* only complain about additional arguments, if called from main() */
327 if (flags == CONFIG_FLAGS_FROMMAIN && argc != 1)
328 usage();
329
330 /* if called from do_all(), then ensure that 2 or 3 args exist */
331 if (flags == CONFIG_FLAGS_FROMALL && (argc < 2 || argc > 3))
332 return -1;
333
334 fd = opendisk(*argv, O_RDWR, buf, sizeof(buf), 1);
335 if (fd == -1) {
336 warn("can't open cgd \"%s\", \"%s\"", *argv, buf);
337
338 /* this isn't fatal with nflag != 0 */
339 if (!nflag)
340 return errno;
341 }
342
343 VPRINTF(1, ("%s (%s): clearing\n", *argv, buf));
344
345 if (nflag)
346 return 0;
347
348 ret = unconfigure_fd(fd);
349 close(fd);
350 return ret;
351 }
352
353 static int
354 unconfigure_fd(int fd)
355 {
356 struct cgd_ioctl ci;
357 int ret;
358
359 ret = ioctl(fd, CGDIOCCLR, &ci);
360 if (ret == -1) {
361 perror("ioctl");
362 return -1;
363 }
364
365 return 0;
366 }
367
368 /*ARGSUSED*/
369 static int
370 configure(int argc, char **argv, struct params *inparams, int flags)
371 {
372 struct params *p;
373 int fd;
374 int ret;
375 char pfile[FILENAME_MAX];
376 char cgdname[PATH_MAX];
377
378 switch (argc) {
379 case 2:
380 strlcpy(pfile, CGDCONFIG_DIR, FILENAME_MAX);
381 strlcat(pfile, "/", FILENAME_MAX);
382 strlcat(pfile, basename(argv[1]), FILENAME_MAX);
383 break;
384 case 3:
385 strlcpy(pfile, argv[2], FILENAME_MAX);
386 break;
387 default:
388 /* print usage and exit, only if called from main() */
389 if (flags == CONFIG_FLAGS_FROMMAIN) {
390 warnx("wrong number of args");
391 usage();
392 }
393 return -1;
394 /* NOTREACHED */
395 }
396
397 p = params_cget(pfile);
398 if (!p)
399 return -1;
400
401 /*
402 * over-ride with command line specifications and fill in default
403 * values.
404 */
405
406 p = params_combine(p, inparams);
407 ret = params_filldefaults(p);
408 if (ret) {
409 params_free(p);
410 return ret;
411 }
412
413 if (!params_verify(p)) {
414 warnx("params invalid");
415 return -1;
416 }
417
418 /*
419 * loop over configuring the disk and checking to see if it
420 * verifies properly. We open and close the disk device each
421 * time, because if the user passes us the block device we
422 * need to flush the buffer cache.
423 */
424
425 for (;;) {
426 fd = opendisk_werror(argv[0], cgdname, sizeof(cgdname));
427 if (fd == -1)
428 return -1;
429
430 if (p->key)
431 bits_free(p->key);
432
433 p->key = getkey(argv[1], p->keygen, p->keylen);
434 if (!p->key)
435 goto bail_err;
436
437 ret = configure_params(fd, cgdname, argv[1], p);
438 if (ret)
439 goto bail_err;
440
441 ret = verify(p, fd);
442 if (ret == -1)
443 goto bail_err;
444 if (!ret)
445 break;
446
447 fprintf(stderr, "verification failed, please reenter "
448 "passphrase\n");
449
450 unconfigure_fd(fd);
451 close(fd);
452 }
453
454 params_free(p);
455 close(fd);
456 return 0;
457 bail_err:
458 params_free(p);
459 close(fd);
460 return -1;
461 }
462
463 static int
464 configure_stdin(struct params *p, int argc, char **argv)
465 {
466 int fd;
467 int ret;
468 char cgdname[PATH_MAX];
469
470 if (argc < 3 || argc > 4)
471 usage();
472
473 p->algorithm = string_fromcharstar(argv[2]);
474 if (argc > 3)
475 p->keylen = atoi(argv[3]);
476
477 ret = params_filldefaults(p);
478 if (ret)
479 return ret;
480
481 fd = opendisk_werror(argv[0], cgdname, sizeof(cgdname));
482 if (fd == -1)
483 return -1;
484
485 p->key = bits_fget(stdin, p->keylen);
486 if (!p->key) {
487 warnx("failed to read key from stdin");
488 return -1;
489 }
490
491 return configure_params(fd, cgdname, argv[1], p);
492 }
493
494 static int
495 opendisk_werror(const char *cgd, char *buf, int buflen)
496 {
497 int fd;
498
499 VPRINTF(3, ("opendisk_werror(%s, %s, %d) called.\n", cgd, buf, buflen));
500
501 /* sanity */
502 if (!cgd || !buf)
503 return -1;
504
505 if (nflag) {
506 strncpy(buf, cgd, buflen);
507 return 0;
508 }
509
510 fd = opendisk(cgd, O_RDWR, buf, buflen, 0);
511 if (fd == -1)
512 warnx("can't open cgd \"%s\", \"%s\"", cgd, buf);
513
514 return fd;
515 }
516
517 static int
518 configure_params(int fd, const char *cgd, const char *dev, struct params *p)
519 {
520 struct cgd_ioctl ci;
521 int ret;
522
523 /* sanity */
524 if (!cgd || !dev)
525 return -1;
526
527 memset(&ci, 0x0, sizeof(ci));
528 ci.ci_disk = (char *)dev;
529 ci.ci_alg = (char *)string_tocharstar(p->algorithm);
530 ci.ci_ivmethod = (char *)string_tocharstar(p->ivmeth);
531 ci.ci_key = (char *)bits_getbuf(p->key);
532 ci.ci_keylen = p->keylen;
533 ci.ci_blocksize = p->bsize;
534
535 VPRINTF(1, (" with alg %s keylen %d blocksize %d ivmethod %s\n",
536 string_tocharstar(p->algorithm), p->keylen, p->bsize,
537 string_tocharstar(p->ivmeth)));
538 VPRINTF(2, ("key: "));
539 VERBOSE(2, bits_fprint(stdout, p->key));
540 VPRINTF(2, ("\n"));
541
542 if (nflag)
543 return 0;
544
545 ret = ioctl(fd, CGDIOCSET, &ci);
546 if (ret == -1) {
547 perror("ioctl");
548 return errno;
549 }
550
551 return 0;
552 }
553
554 /*
555 * verify returns 0 for success, -1 for unrecoverable error, or 1 for retry.
556 */
557
558 #define SCANSIZE 8192
559
560 static int
561 verify(struct params *p, int fd)
562 {
563
564 switch (p->verify_method) {
565 case VERIFY_NONE:
566 return 0;
567 case VERIFY_DISKLABEL:
568 return verify_disklabel(fd);
569 case VERIFY_FFS:
570 return verify_ffs(fd);
571 default:
572 warnx("unimplemented verification method");
573 return -1;
574 }
575 }
576
577 static int
578 verify_disklabel(int fd)
579 {
580 struct disklabel l;
581 int ret;
582 char buf[SCANSIZE];
583
584 /*
585 * we simply scan the first few blocks for a disklabel, ignoring
586 * any MBR/filecore sorts of logic. MSDOS and RiscOS can't read
587 * a cgd, anyway, so it is unlikely that there will be non-native
588 * partition information.
589 */
590
591 ret = pread(fd, buf, 8192, 0);
592 if (ret == -1) {
593 warn("can't read disklabel area");
594 return -1;
595 }
596
597 /* now scan for the disklabel */
598
599 return disklabel_scan(&l, buf, sizeof(buf));
600 }
601
602 static int
603 verify_ffs(int fd)
604 {
605 struct fs *fs;
606 int ret;
607 char buf[SBSIZE];
608
609 ret = pread(fd, buf, SBSIZE, SBOFF);
610 fs = (struct fs *)buf;
611 if (ret == -1) {
612 warn("pread");
613 return 0;
614 }
615 if (fs->fs_magic == FS_MAGIC)
616 return 0;
617 if (fs->fs_magic == bswap32(FS_MAGIC))
618 return 0;
619 return 1;
620 }
621
622 static int
623 generate(struct params *p, int argc, char **argv, const char *outfile)
624 {
625 int ret;
626
627 if (argc < 1 || argc > 2)
628 usage();
629
630 p->algorithm = string_fromcharstar(argv[0]);
631 if (argc > 1)
632 p->keylen = atoi(argv[1]);
633
634 ret = params_filldefaults(p);
635 if (ret)
636 return ret;
637
638 if (!p->keygen) {
639 p->keygen = keygen_generate(KEYGEN_PKCS5_PBKDF2);
640 if (!p->keygen)
641 return -1;
642 }
643
644 if (keygen_filldefaults(p->keygen, p->keylen)) {
645 warnx("Failed to generate defaults for keygen");
646 return -1;
647 }
648
649 if (!params_verify(p)) {
650 warnx("invalid parameters generated");
651 return -1;
652 }
653
654 return params_cput(p, outfile);
655 }
656
657 static int
658 generate_convert(struct params *p, int argc, char **argv, const char *outfile)
659 {
660 struct params *oldp;
661 struct keygen *kg;
662
663 if (argc != 1)
664 usage();
665
666 oldp = params_cget(*argv);
667 if (!oldp)
668 return -1;
669
670 /* for sanity, we ensure that none of the keygens are randomkey */
671 for (kg=p->keygen; kg; kg=kg->next)
672 if (kg->kg_method == KEYGEN_RANDOMKEY)
673 goto bail;
674 for (kg=oldp->keygen; kg; kg=kg->next)
675 if (kg->kg_method == KEYGEN_RANDOMKEY)
676 goto bail;
677
678 if (!params_verify(oldp)) {
679 warnx("invalid old parameters file \"%s\"", *argv);
680 return -1;
681 }
682
683 oldp->key = getkey("old file", oldp->keygen, oldp->keylen);
684
685 /* we copy across the non-keygen info, here. */
686
687 string_free(p->algorithm);
688 string_free(p->ivmeth);
689
690 p->algorithm = string_dup(oldp->algorithm);
691 p->ivmeth = string_dup(oldp->ivmeth);
692 p->keylen = oldp->keylen;
693 p->bsize = oldp->bsize;
694 if (p->verify_method == VERIFY_UNKNOWN)
695 p->verify_method = oldp->verify_method;
696
697 params_free(oldp);
698
699 if (!p->keygen) {
700 p->keygen = keygen_generate(KEYGEN_PKCS5_PBKDF2);
701 if (!p->keygen)
702 return -1;
703 keygen_filldefaults(p->keygen, p->keylen);
704 }
705 params_filldefaults(p);
706 p->key = getkey("new file", p->keygen, p->keylen);
707
708 kg = keygen_generate(KEYGEN_STOREDKEY);
709 kg->kg_key = bits_xor(p->key, oldp->key);
710 keygen_addlist(&p->keygen, kg);
711
712 if (!params_verify(p)) {
713 warnx("can't generate new parameters file");
714 return -1;
715 }
716
717 return params_cput(p, outfile);
718 bail:
719 params_free(oldp);
720 return -1;
721 }
722
723 static int
724 do_all(const char *cfile, int argc, char **argv,
725 int (*conf)(int, char **, struct params *, int))
726 {
727 FILE *f;
728 size_t len;
729 size_t lineno;
730 int my_argc;
731 int ret;
732 const char *fn;
733 char *line;
734 char **my_argv;
735
736 if (argc > 0)
737 usage();
738
739 if (!cfile[0])
740 fn = CGDCONFIG_CFILE;
741 else
742 fn = cfile;
743
744 f = fopen(fn, "r");
745 if (!f) {
746 warn("could not open config file \"%s\"", fn);
747 return -1;
748 }
749
750 ret = chdir(CGDCONFIG_DIR);
751 if (ret == -1)
752 warn("could not chdir to %s", CGDCONFIG_DIR);
753
754 ret = 0;
755 lineno = 0;
756 for (;;) {
757 line = fparseln(f, &len, &lineno, "\\\\#", FPARSELN_UNESCALL);
758 if (!line)
759 break;
760 if (!*line)
761 continue;
762
763 my_argv = words(line, &my_argc);
764 ret = conf(my_argc, my_argv, NULL, CONFIG_FLAGS_FROMALL);
765 if (ret) {
766 warnx("action failed on \"%s\" line %lu", fn,
767 (u_long)lineno);
768 break;
769 }
770 words_free(my_argv, my_argc);
771 }
772 return ret;
773 }
774