cgdconfig.c revision 1.1 1 /* $NetBSD: cgdconfig.c,v 1.1 2002/10/04 18:37:20 elric Exp $ */
2
3 /*-
4 * Copyright (c) 2002 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\
43 The NetBSD Foundation, Inc. All rights reserved.");
44 __RCSID("$NetBSD: cgdconfig.c,v 1.1 2002/10/04 18:37:20 elric Exp $");
45 #endif
46
47 #include <errno.h>
48 #include <fcntl.h>
49 #include <libgen.h>
50 #include <malloc.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/param.h>
59
60 #include <dev/cgdvar.h>
61
62 #include "params.h"
63 #include "pkcs5_pbkdf2.h"
64 #include "utils.h"
65
66 #define CGDCONFIG_DIR "/etc/cgd"
67 #define CGDCONFIG_CFILE CGDCONFIG_DIR "/cgd.conf"
68 #define DEFAULT_SALTLEN 128
69
70 #define ACTION_CONFIGURE 0x1 /* configure, with paramsfile */
71 #define ACTION_UNCONFIGURE 0x2 /* unconfigure */
72 #define ACTION_GENERATE 0x3 /* generate a paramsfile */
73 #define ACTION_CONFIGALL 0x4 /* configure all from config file */
74 #define ACTION_UNCONFIGALL 0x5 /* unconfigure all from config file */
75 #define ACTION_CONFIGSTDIN 0x6 /* configure, key from stdin */
76
77 /* if nflag is set, do not configure/unconfigure the cgd's */
78
79 int nflag = 0;
80
81 static int configure(int, char **, int);
82 static int configure_stdin(struct params *, int argc, char **);
83 static int generate(struct params *, int, char **, const char *);
84 static int unconfigure(int, char **, int);
85 static int do_all(const char *, int, char **, int (*)(int, char **, int));
86
87 #define CONFIG_FLAGS_FROMALL 1 /* called from configure_all() */
88 #define CONFIG_FLAGS_FROMMAIN 2 /* called from main() */
89
90 static int configure_params(const char *, const char *, struct params *);
91 static void key_print(FILE *, const u_int8_t *, int);
92 static char *getrandbits(int);
93 static int getkey(const char *, struct params *);
94 static int getkeyfrompassphrase(const char *, struct params *);
95 static int getkeyfromfile(FILE *, struct params *);
96
97 static void usage(void);
98
99 /* Verbose Framework */
100 int verbose = 0;
101
102 #define VERBOSE(x,y) if (verbose >= x) y
103 #define VPRINTF(x,y) if (verbose >= x) printf y
104
105 static void
106 usage(void)
107 {
108
109 fprintf(stderr, "usage: %s [-nv] cgd dev [paramsfile]\n",
110 getprogname());
111 fprintf(stderr, " %s -C [-nv] [-f configfile]\n", getprogname());
112 fprintf(stderr, " %s -U [-nv] [-f configfile]\n", getprogname());
113 fprintf(stderr, " %s -g [-nv] [-i ivmeth] [-k kgmeth] "
114 "[-o outfile] alg [keylen]\n", getprogname());
115 fprintf(stderr, " %s -s [-nv] [-i ivmeth] cgd dev alg "
116 "[keylen]\n", getprogname());
117 fprintf(stderr, " %s -u [-nv] cgd\n", getprogname());
118 exit(1);
119 }
120
121 int
122 main(int argc, char **argv)
123 {
124 struct params cf;
125 int action = ACTION_CONFIGURE;
126 int actions = 0;
127 int ch;
128 int ret;
129 char cfile[FILENAME_MAX] = "";
130 char outfile[FILENAME_MAX] = "";
131
132 setprogname(*argv);
133 params_init(&cf);
134
135 while ((ch = getopt(argc, argv, "CUb:f:gi:k:no:usv")) != -1)
136 switch (ch) {
137 case 'C':
138 action = ACTION_CONFIGALL;
139 actions++;
140 break;
141 case 'U':
142 action = ACTION_UNCONFIGALL;
143 actions++;
144 break;
145
146 case 'b':
147 ret = params_setbsize(&cf, atoi(optarg));
148 if (ret)
149 usage();
150 break;
151 case 'f':
152 strncpy(cfile, optarg, FILENAME_MAX);
153 break;
154 case 'g':
155 action = ACTION_GENERATE;
156 actions++;
157 break;
158 case 'i':
159 params_setivmeth(&cf, optarg);
160 break;
161 case 'k':
162 ret = params_setkeygen_method_str(&cf, optarg);
163 if (ret)
164 usage();
165 break;
166 case 'n':
167 nflag = 1;
168 break;
169 case 'o':
170 strncpy(outfile, optarg, FILENAME_MAX);
171 break;
172 case 's':
173 action = ACTION_CONFIGSTDIN;
174 actions++;
175 break;
176
177 case 'u':
178 action = ACTION_UNCONFIGURE;
179 actions++;
180 break;
181 case 'v':
182 verbose++;
183 break;
184 default:
185 usage();
186 /* NOTREACHED */
187 }
188
189 argc -= optind;
190 argv += optind;
191
192 /* validate the consistency of the arguments */
193
194 if (actions > 1)
195 usage();
196 if (action == ACTION_CONFIGURE && params_changed(&cf))
197 usage();
198
199 switch (action) {
200 case ACTION_CONFIGURE:
201 return configure(argc, argv, CONFIG_FLAGS_FROMMAIN);
202 case ACTION_UNCONFIGURE:
203 return unconfigure(argc, argv, CONFIG_FLAGS_FROMMAIN);
204 case ACTION_GENERATE:
205 return generate(&cf, argc, argv, outfile);
206 case ACTION_CONFIGALL:
207 return do_all(cfile, argc, argv, configure);
208 case ACTION_UNCONFIGALL:
209 return do_all(cfile, argc, argv, unconfigure);
210 case ACTION_CONFIGSTDIN:
211 return configure_stdin(&cf, argc, argv);
212 default:
213 fprintf(stderr, "undefined action\n");
214 return 1;
215 }
216 /* NOTREACHED */
217 }
218
219 static int
220 getkey(const char *target, struct params *p)
221 {
222
223 switch (p->keygen_method) {
224 case KEYGEN_RANDOMKEY:
225 p->key = getrandbits(p->keylen);
226 if (!p->key)
227 return -1;
228 return 0;
229 case KEYGEN_PKCS5_PBKDF2:
230 return getkeyfrompassphrase(target, p);
231 default:
232 fprintf(stderr, "getkey: unknown keygen_method\n");
233 return -1;
234 }
235 /* NOTREACHED */
236 }
237
238 static int
239 getkeyfromfile(FILE *f, struct params *p)
240 {
241 int ret;
242
243 /* XXXrcd: data hiding? */
244 p->key = malloc(p->keylen);
245 if (!p->key)
246 return -1;
247 ret = fread(p->key, p->keylen, 1, f);
248 if (ret < 1) {
249 fprintf(stderr, "failed to read key from stdin\n");
250 return -1;
251 }
252 return 0;
253 }
254
255 static int
256 getkeyfrompassphrase(const char *target, struct params *p)
257 {
258 int ret;
259 char *passp;
260 char buf[1024];
261
262 snprintf(buf, 1024, "%s's passphrase:", target);
263 passp = getpass(buf);
264 /* XXXrcd: data hiding ? we should be allocating the key here. */
265 ret = pkcs5_pbkdf2(&p->key, BITS2BYTES(p->keylen), passp,
266 strlen(passp), p->keygen_salt, BITS2BYTES(p->keygen_saltlen),
267 p->keygen_iterations);
268 if (p->xor_key)
269 memxor(p->key, p->xor_key, BITS2BYTES(p->keylen));
270 return ret;
271 }
272
273 static int
274 unconfigure(int argc, char **argv, int flags)
275 {
276 struct cgd_ioctl ci;
277 int fd;
278 int ret;
279 char buf[MAXPATHLEN] = "";
280
281 /* only complain about additional arguments, if called from main() */
282 if (flags == CONFIG_FLAGS_FROMMAIN && argc != 1)
283 usage();
284
285 /* if called from do_all(), then ensure that 2 or 3 args exist */
286 if (flags == CONFIG_FLAGS_FROMALL && (argc < 2 || argc > 3))
287 return -1;
288
289 fd = opendisk(*argv, O_RDWR, buf, sizeof(buf), 1);
290 if (fd == -1) {
291 fprintf(stderr, "can't open cgd \"%s\", \"%s\": %s\n",
292 *argv, buf, strerror(errno));
293
294 /* this isn't fatal with nflag != 0 */
295 if (!nflag)
296 return errno;
297 }
298
299 VPRINTF(1, ("%s (%s): clearing\n", *argv, buf));
300
301 if (nflag)
302 return 0;
303
304 ret = ioctl(fd, CGDIOCCLR, &ci);
305 if (ret == -1) {
306 perror("ioctl");
307 return errno;
308 }
309
310 return 0;
311 }
312
313 /* ARGSUSED */
314 static int
315 configure(int argc, char **argv, int flags)
316 {
317 struct params params;
318 int ret;
319 char pfile[FILENAME_MAX];
320
321 params_init(¶ms);
322
323 switch (argc) {
324 case 2:
325 strlcpy(pfile, CGDCONFIG_DIR, FILENAME_MAX);
326 strlcat(pfile, "/", FILENAME_MAX);
327 strlcat(pfile, basename(argv[1]), FILENAME_MAX);
328 break;
329 case 3:
330 strlcpy(pfile, argv[2], FILENAME_MAX);
331 break;
332 default:
333 /* print usage and exit, only if called from main() */
334 if (flags == CONFIG_FLAGS_FROMMAIN)
335 usage();
336 return -1;
337 /* NOTREACHED */
338 }
339
340 ret = params_cget(¶ms, pfile);
341 if (ret)
342 return ret;
343 ret = params_filldefaults(¶ms);
344 if (ret)
345 return ret;
346 ret = getkey(argv[1], ¶ms);
347 if (ret)
348 return ret;
349
350 ret = configure_params(argv[0], argv[1], ¶ms);
351 params_free(¶ms);
352 return ret;
353 }
354
355 static int
356 configure_stdin(struct params *p, int argc, char **argv)
357 {
358 int ret;
359
360 if (argc < 3 || argc > 4)
361 usage();
362
363 ret = params_setalgorithm(p, argv[2]);
364 if (ret)
365 return ret;
366 if (argc > 3) {
367 ret = params_setkeylen(p, atoi(argv[3]));
368 if (ret)
369 return ret;
370 }
371
372 ret = params_filldefaults(p);
373 if (ret)
374 return ret;
375
376 ret = getkeyfromfile(stdin, p);
377 if (ret)
378 return -1;
379
380 return configure_params(argv[0], argv[1], p);
381 }
382
383 static int
384 configure_params(const char *cgd, const char *dev, struct params *p)
385 {
386 struct cgd_ioctl ci;
387 int fd;
388 int ret;
389 char buf[MAXPATHLEN] = "";
390
391 /* sanity */
392 if (!cgd || !dev)
393 return -1;
394
395 memset(&ci, 0x0, sizeof(ci));
396 ci.ci_disk = (char *)dev;
397 ci.ci_alg = p->alg;
398 ci.ci_ivmethod = p->ivmeth;
399 ci.ci_key = p->key;
400 ci.ci_keylen = p->keylen;
401 ci.ci_blocksize = p->bsize;
402
403 fd = opendisk(cgd, O_RDWR, buf, sizeof(buf), 1);
404 if (fd == -1) {
405 fprintf(stderr, "can't open cgd \"%s\", \"%s\": %s\n",
406 cgd, buf, strerror(errno));
407
408 /* with nflag, this is not necessarily a fatal error */
409 if (!nflag)
410 return errno;
411 }
412
413 VPRINTF(1, ("attaching: %s (%s) attach to %s\n", cgd, buf, dev));
414 VPRINTF(1, (" with alg %s keylen %d blocksize %d ivmethod %s\n",
415 p->alg, p->keylen, p->bsize, p->ivmeth));
416 VERBOSE(2, key_print(stdout, p->key, p->keylen));
417
418 if (nflag)
419 return 0;
420
421 ret = ioctl(fd, CGDIOCSET, &ci);
422 if (ret == -1) {
423 perror("ioctl");
424 return errno;
425 }
426
427 return 0;
428 }
429
430 static int
431 generate(struct params *p, int argc, char **argv, const char *outfile)
432 {
433 FILE *f;
434 int ret;
435 char *tmp;
436
437 if (argc < 1 || argc > 2)
438 usage();
439
440 ret = params_setalgorithm(p, argv[0]);
441 if (ret)
442 return ret;
443 if (argc > 1) {
444 ret = params_setkeylen(p, atoi(argv[1]));
445 if (ret)
446 return ret;
447 }
448
449 ret = params_filldefaults(p);
450 if (ret)
451 return ret;
452
453 if (!p->keygen_method != KEYGEN_RANDOMKEY) {
454 tmp = getrandbits(DEFAULT_SALTLEN);
455 params_setkeygen_salt(p, tmp, DEFAULT_SALTLEN);
456 free(tmp);
457 tmp = getrandbits(p->keylen);
458 params_setxor_key(p, tmp, p->keylen);
459 free(tmp);
460
461 /* XXXrcd: generate key hash, if desired */
462 }
463
464 if (*outfile) {
465 f = fopen(outfile, "w");
466 if (!f) {
467 fprintf(stderr, "could not open outfile \"%s\": %s\n",
468 outfile, strerror(errno));
469 perror("fopen");
470 return -1;
471 }
472 } else {
473 f = stdout;
474 }
475
476 ret = params_fput(p, f);
477 params_free(p);
478 return ret;
479 }
480
481 static int
482 do_all(const char *cfile, int argc, char **argv,
483 int (*conf)(int, char **, int))
484 {
485 FILE *f;
486 size_t len;
487 size_t lineno;
488 int my_argc;
489 int ret;
490 const char *fn;
491 char *line;
492 char **my_argv;
493
494 if (argc > 0)
495 usage();
496
497 if (!cfile[0])
498 fn = CGDCONFIG_CFILE;
499 else
500 fn = cfile;
501
502 f = fopen(fn, "r");
503 if (!f) {
504 fprintf(stderr, "could not open config file \"%s\": %s\n",
505 fn, strerror(errno));
506 return -1;
507 }
508
509 ret = 0;
510 lineno = 0;
511 for (;;) {
512
513 line = fparseln(f, &len, &lineno, "\\\\#", FPARSELN_UNESCALL);
514 if (!line)
515 break;
516 if (!*line)
517 continue;
518
519 my_argv = words(line, &my_argc);
520 ret = conf(my_argc, my_argv, CONFIG_FLAGS_FROMALL);
521 if (ret) {
522 fprintf(stderr, "on \"%s\" line %lu\n", fn,
523 (u_long)lineno);
524 break;
525 }
526 words_free(my_argv, my_argc);
527 }
528 return ret;
529 }
530
531 /*
532 * XXX: key_print doesn't work quite exactly properly if the keylength
533 * is not evenly divisible by 8. If the key is not divisible by
534 * 8 then a few extra bits are printed.
535 */
536
537 static void
538 key_print(FILE *f, const u_int8_t *key, int len)
539 {
540 int i;
541 int col;
542
543 len = BITS2BYTES(len);
544 fprintf(f, "key: ");
545 for (i=0, col=5; i < len; i++, col+=2) {
546 fprintf(f, "%02x", key[i]);
547 if (col > 70) {
548 col = 5 - 2;
549 fprintf(f, "\n ");
550 }
551 }
552 fprintf(f, "\n");
553 }
554
555 static char *
556 getrandbits(int len)
557 {
558 FILE *f;
559 int ret;
560 char *res;
561
562 len = (len + 7) / 8;
563 res = malloc(len);
564 if (!res)
565 return NULL;
566 f = fopen("/dev/random", "r");
567 if (!f)
568 return NULL;
569 ret = fread(res, len, 1, f);
570 if (ret != 1) {
571 free(res);
572 return NULL;
573 }
574 return res;
575 }
576