]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/gbde/gbde.c
Fix ipfw2 panics on 64-bit platforms.
[FreeBSD/FreeBSD.git] / sbin / gbde / gbde.c
1 /*-
2  * Copyright (c) 2002 Poul-Henning Kamp
3  * Copyright (c) 2002 Networks Associates Technology, Inc.
4  * All rights reserved.
5  *
6  * This software was developed for the FreeBSD Project by Poul-Henning Kamp
7  * and NAI Labs, the Security Research Division of Network Associates, Inc.
8  * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
9  * DARPA CHATS research program.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. The names of the authors may not be used to endorse or promote
20  *    products derived from this software without specific prior written
21  *    permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  * $FreeBSD$
36  */
37
38 #include <sys/types.h>
39 #include <sys/queue.h>
40 #include <sys/mutex.h>
41 #include <md5.h>
42 #include <readpassphrase.h>
43 #include <string.h>
44 #include <stdint.h>
45 #include <unistd.h>
46 #include <fcntl.h>
47 #include <strings.h>
48 #include <stdlib.h>
49 #include <err.h>
50 #include <stdio.h>
51 #include <libutil.h>
52 #include <sys/errno.h>
53 #include <sys/disk.h>
54 #include <crypto/rijndael/rijndael.h>
55
56 #include <geom/geom.h>
57 #include <geom/bde/g_bde.h>
58
59 extern const char template[];
60
61 static void __dead2
62 usage(const char *reason)
63 {
64         const char *p;
65
66         p = getprogname();
67         fprintf(stderr, "Usage error: %s", reason);
68         fprintf(stderr, "Usage:\n");
69         fprintf(stderr, "\t%s attach dest -l filename\n", p);
70         fprintf(stderr, "\t%s detach dest\n", p);
71         fprintf(stderr, "\t%s init dest [-i] [-f filename] -l filename\n", p);
72         fprintf(stderr, "\t%s setkey dest [-n key] -l filename\n", p);
73         fprintf(stderr, "\t%s destroy dest [-n key] -l filename\n", p);
74         exit (1);
75 }
76
77 void *
78 g_read_data(struct g_consumer *cp, off_t offset, off_t length, int *error)
79 {
80         void *p;
81         int fd, i;
82         off_t o2;
83
84         p = malloc(length);
85         if (p == NULL)
86                 err(1, "malloc");
87         fd = *(int *)cp;
88         o2 = lseek(fd, offset, SEEK_SET);
89         if (o2 != offset)
90                 err(1, "lseek");
91         i = read(fd, p, length);
92         if (i != length)
93                 err(1, "read");
94         if (error != NULL)
95                 error = 0;
96         return (p);
97 }
98
99 static void
100 random_bits(void *p, u_int len)
101 {
102         static int fdr = -1;
103         int i;
104
105         if (fdr < 0) {
106                 fdr = open("/dev/urandom", O_RDONLY);
107                 if (fdr < 0)
108                         err(1, "/dev/urandom");
109         }
110                 
111         i = read(fdr, p, len);
112         if (i != (int)len)
113                 err(1, "read from /dev/urandom");
114 }
115
116 /* XXX: not nice */
117 static u_char sbox[256];
118
119 static void
120 reset_passphrase(struct g_bde_softc *sc)
121 {
122
123         memcpy(sc->arc4_sbox, sbox, 256);
124         sc->arc4_i = sc->arc4_j = 0;
125 }
126
127 static void
128 setup_passphrase(struct g_bde_softc *sc, int sure, const char *input)
129 {
130         char buf1[BUFSIZ], buf2[BUFSIZ], *p;
131
132         if (input != NULL) {
133                 g_bde_arc4_seed(sc, input, strlen(input));
134                 memcpy(sbox, sc->arc4_sbox, 256);
135                 return;
136         }
137         for (;;) {
138                 p = readpassphrase(
139                     sure ? "Enter new passphrase:" : "Enter passphrase: ",
140                     buf1, sizeof buf1,
141                     RPP_ECHO_OFF | RPP_REQUIRE_TTY);
142                 if (p == NULL)
143                         err(1, "readpassphrase");
144
145                 if (sure) {
146                         p = readpassphrase("Reenter new passphrase: ",
147                             buf2, sizeof buf2,
148                             RPP_ECHO_OFF | RPP_REQUIRE_TTY);
149                         if (p == NULL)
150                                 err(1, "readpassphrase");
151
152                         if (strcmp(buf1, buf2)) {
153                                 printf("They didn't match.\n");
154                                 continue;
155                         }
156                 }
157                 if (strlen(buf1) < 3) {
158                         printf("Too short passphrase.\n");
159                         continue;
160                 }
161                 break;
162         }
163         g_bde_arc4_seed(sc, buf1, strlen(buf1));
164         memcpy(sbox, sc->arc4_sbox, 256);
165 }
166
167 static void
168 encrypt_sector(void *d, int len, void *key)
169 {
170         keyInstance ki;
171         cipherInstance ci;
172         int error;
173         
174         error = rijndael_cipherInit(&ci, MODE_CBC, NULL);
175         if (error <= 0)
176                 errx(1, "rijndael_cipherInit=%d", error);
177         error = rijndael_makeKey(&ki, DIR_ENCRYPT, 128, key);
178         if (error <= 0)
179                 errx(1, "rijndael_makeKeY=%d", error);
180         error = rijndael_blockEncrypt(&ci, &ki, d, len * 8, d);
181         if (error <= 0)
182                 errx(1, "rijndael_blockEncrypt=%d", error);
183 }
184
185 static void
186 cmd_attach(const struct g_bde_softc *sc, const char *dest, const char *lfile)
187 {
188         int gfd, i, ffd;
189         struct geomconfiggeom gcg;
190         u_char buf[256 + 16];
191
192         gfd = open("/dev/geom.ctl", O_RDWR);
193         if (gfd < 0)
194                 err(1, "/dev/geom.ctl");
195         memset(&gcg, 0, sizeof gcg);
196         gcg.class.u.name = "BDE";
197         gcg.class.len = strlen(gcg.class.u.name);
198         gcg.provider.u.name = dest;
199         gcg.provider.len = strlen(gcg.provider.u.name);
200         gcg.flag = 0;
201         gcg.len = sizeof buf;
202         gcg.ptr = buf;
203         
204         if (lfile != NULL) {
205                 ffd = open(lfile, O_RDONLY, 0);
206                 if (ffd < 0)
207                         err(1, lfile);
208                 read(ffd, buf + 256, 16);
209                 close(ffd);
210         } else {
211                 memset(buf + 256, 0, 16);
212         }
213         memcpy(buf, sc->arc4_sbox, 256);
214
215         i = ioctl(gfd, GEOMCONFIGGEOM, &gcg);
216         if (i != 0)
217                 err(1, "ioctl(GEOMCONFIGGEOM)");
218         exit (0);
219 }
220
221 static void
222 cmd_detach(const char *dest)
223 {
224         int i, gfd;
225         struct geomconfiggeom gcg;
226
227         gfd = open("/dev/geom.ctl", O_RDWR);
228         if (gfd < 0)
229                 err(1, "/dev/geom.ctl");
230         memset(&gcg, 0, sizeof gcg);
231         gcg.class.u.name = "BDE";
232         gcg.class.len = strlen(gcg.class.u.name);
233         gcg.provider.u.name = dest;
234         gcg.provider.len = strlen(gcg.provider.u.name);
235         gcg.flag = 1;
236         
237         i = ioctl(gfd, GEOMCONFIGGEOM, &gcg);
238         if (i != 0)
239                 err(1, "ioctl(GEOMCONFIGGEOM)");
240         exit (0);
241 }
242
243 static void
244 cmd_open(struct g_bde_softc *sc, int dfd __unused, const char *l_opt, u_int *nkey)
245 {
246         int error;
247         int ffd;
248         u_char keyloc[16];
249
250         if (l_opt != NULL) {
251                 ffd = open(l_opt, O_RDONLY, 0);
252                 if (ffd < 0)
253                         err(1, l_opt);
254                 read(ffd, keyloc, sizeof keyloc);
255                 close(ffd);
256         } else {
257                 memset(keyloc, 0, sizeof keyloc);
258         }
259
260         error = g_bde_decrypt_lock(sc, sbox, keyloc, 0xffffffff,
261             512, nkey);
262         if (error == ENOENT)
263                 errx(1, "Lock was destroyed.");
264         if (error == ESRCH)
265                 errx(1, "Lock was nuked.");
266         if (error == ENOTDIR)
267                 errx(1, "Lock not found");
268         if (error != 0)
269                 errx(1, "Error %d decrypting lock", error);
270         if (nkey)
271                 printf("Opened with key %u\n", *nkey);
272         return;
273 }
274
275 static void
276 cmd_nuke(struct g_bde_key *gl, int dfd , int key)
277 {
278         int i;
279         u_char *sbuf;
280         off_t offset, offset2;
281
282         sbuf = malloc(gl->sectorsize);
283         memset(sbuf, 0, gl->sectorsize);
284         offset = (gl->lsector[key] & ~(gl->sectorsize - 1));
285         offset2 = lseek(dfd, offset, SEEK_SET);
286         if (offset2 != offset)
287                 err(1, "lseek");
288         i = write(dfd, sbuf, gl->sectorsize);
289         if (i != (int)gl->sectorsize)
290                 err(1, "write");
291         printf("Nuked key %d\n", key);
292 }
293
294 static void
295 cmd_write(struct g_bde_key *gl, struct g_bde_softc *sc, int dfd , int key, const char *l_opt)
296 {
297         char buf[BUFSIZ];
298         int i, ffd;
299         uint64_t off[2];
300         u_char keyloc[16];
301         u_char *sbuf, *q;
302         MD5_CTX c;
303         off_t offset, offset2;
304
305         sbuf = malloc(gl->sectorsize);
306         /*
307          * Find the byte-offset in the lock sector where we will put the lock
308          * data structure.  We can put it any random place as long as the
309          * structure fits.
310          */
311         for(;;) {
312                 random_bits(off, sizeof off);
313                 off[0] &= (gl->sectorsize - 1);
314                 if (off[0] + G_BDE_LOCKSIZE > gl->sectorsize)
315                         continue;
316                 break;
317         }
318
319         /* Add the sector offset in bytes */
320         off[0] += (gl->lsector[key] & ~(gl->sectorsize - 1));
321         gl->lsector[key] = off[0];
322
323         i = g_bde_keyloc_encrypt(sc, off, keyloc);
324         if (i)
325                 errx(1, "g_bde_keyloc_encrypt()");
326         if (l_opt != NULL) {
327                 ffd = open(l_opt, O_WRONLY | O_CREAT | O_TRUNC, 0600);
328                 if (ffd < 0)
329                         err(1, l_opt);
330                 write(ffd, keyloc, sizeof keyloc);
331                 close(ffd);
332         } else if (gl->flags & 1) {
333                 offset2 = lseek(dfd, 0, SEEK_SET);
334                 if (offset2 != 0)
335                         err(1, "lseek");
336                 i = read(dfd, sbuf, gl->sectorsize);
337                 if (i != (int)gl->sectorsize)
338                         err(1, "read");
339                 memcpy(sbuf + key * 16, keyloc, sizeof keyloc);
340                 offset2 = lseek(dfd, 0, SEEK_SET);
341                 if (offset2 != 0)
342                         err(1, "lseek");
343                 i = write(dfd, sbuf, gl->sectorsize);
344                 if (i != (int)gl->sectorsize)
345                         err(1, "write");
346         } else {
347                 errx(1, "No -L option and no space in sector 0 for lockfile");
348         }
349
350         /* Allocate a sectorbuffer and fill it with random junk */
351         if (sbuf == NULL)
352                 err(1, "malloc");
353         random_bits(sbuf, gl->sectorsize);
354
355         /* Fill in the hash field with something we can recognize again */
356         g_bde_arc4_seq(sc, buf, 16);
357         MD5Init(&c);
358         MD5Update(&c, "0000", 4);       /* XXX: for future versioning */
359         MD5Update(&c, buf, 16);
360         MD5Final(gl->hash, &c);
361
362         /* Fill random bits in the spare field */
363         random_bits(gl->spare, sizeof(gl->spare));
364
365         /* Encode the structure where we want it */
366         q = sbuf + (off[0] % gl->sectorsize);
367         g_bde_encode_lock(gl, q);
368
369         /*
370          * The encoded structure likely contains long sequences of zeros
371          * which stick out as a sore thumb, so we XOR with key-material
372          * to make it harder to recognize in a brute-force attack
373          */
374         g_bde_arc4_seq(sc, buf, G_BDE_LOCKSIZE);
375         for (i = 0; i < G_BDE_LOCKSIZE; i++)
376                 q[i] ^= buf[i];
377
378         g_bde_arc4_seq(sc, buf, 16);
379
380         encrypt_sector(q, G_BDE_LOCKSIZE, buf);
381         offset = gl->lsector[key] & ~(gl->sectorsize - 1);
382         offset2 = lseek(dfd, offset, SEEK_SET);
383         if (offset2 != offset)
384                 err(1, "lseek");
385         i = write(dfd, sbuf, gl->sectorsize);
386         if (i != (int)gl->sectorsize)
387                 err(1, "write");
388         printf("Wrote key %d at %jd\n", key, (intmax_t)offset);
389
390 }
391
392 static void
393 cmd_destroy(struct g_bde_key *gl, int nkey)
394 {
395         int i;
396
397         bzero(&gl->sector0, sizeof gl->sector0);
398         bzero(&gl->sectorN, sizeof gl->sectorN);
399         bzero(&gl->keyoffset, sizeof gl->keyoffset);
400         bzero(&gl->flags, sizeof gl->flags);
401         bzero(gl->key, sizeof gl->key);
402         for (i = 0; i < G_BDE_MAXKEYS; i++)
403                 if (i != nkey)
404                         gl->lsector[i] = ~0;
405 }
406
407 static void
408 cmd_init(struct g_bde_key *gl, int dfd, const char *f_opt, int i_opt, const char *l_opt)
409 {
410         int i;
411         u_char *buf;
412         unsigned sector_size;
413         uint64_t        first_sector;
414         uint64_t        last_sector;
415         uint64_t        total_sectors;
416         off_t   off, off2;
417         unsigned nkeys;
418         const char *p;
419         char *q, cbuf[BUFSIZ];
420         unsigned u, u2;
421         uint64_t o;
422         properties      params;
423
424         bzero(gl, sizeof *gl);
425         if (f_opt != NULL) {
426                 i = open(f_opt, O_RDONLY);
427                 if (i < 0)
428                         err(1, f_opt);
429                 params = properties_read(i);
430                 close (i);
431         } else {
432                 /* XXX: Polish */
433                 q = strdup("/tmp/temp.XXXXXXXXXX");
434                 i = mkstemp(q);
435                 if (i < 0)
436                         err(1, q);
437                 write(i, template, strlen(template));
438                 close (i);
439                 if (i_opt) {
440                         p = getenv("EDITOR");
441                         if (p == NULL)
442                                 p = "vi";
443                         sprintf(cbuf, "%s %s\n", p, q);
444                         system(cbuf);
445                 }
446                 i = open(q, O_RDONLY);
447                 if (i < 0)
448                         err(1, f_opt);
449                 params = properties_read(i);
450                 close (i);
451                 unlink(q);
452         }
453
454         /* <sector_size> */
455         p = property_find(params, "sector_size");
456         i = ioctl(dfd, DIOCGSECTORSIZE, &u);
457         if (i == 0)
458                 sector_size = u;
459         else if (p == NULL)
460                 errx(1, "Missing sector_size property");
461         if (p != NULL) {
462                 sector_size = strtoul(p, &q, 0);
463                 if (!*p || *q)
464                         errx(1, "sector_size not a proper number");
465         }
466         if (sector_size & (sector_size - 1))
467                 errx(1, "sector_size not a power of 2");
468         if (sector_size < 512)
469                 errx(1, "sector_size is smaller than 512");
470         buf = malloc(sector_size);
471         if (buf == NULL)
472                 err(1, "Failed to malloc sector buffer");
473         gl->sectorsize = sector_size;
474
475         i = ioctl(dfd, DIOCGMEDIASIZE, &off);
476         if (i == 0) {
477                 first_sector = 0;
478                 total_sectors = off / sector_size;
479                 last_sector = total_sectors - 1;
480         } else {
481                 first_sector = 0;
482                 last_sector = 0;
483                 total_sectors = 0;
484         }
485
486         /* <first_sector> */
487         p = property_find(params, "first_sector");
488         if (p != NULL) {
489                 first_sector = strtoul(p, &q, 0);
490                 if (!*p || *q)
491                         errx(1, "first_sector not a proper number");
492         }
493         gl->sector0 = first_sector * gl->sectorsize;
494
495         /* <last_sector> */
496         p = property_find(params, "last_sector");
497         if (p != NULL) {
498                 last_sector = strtoul(p, &q, 0);
499                 if (!*p || *q)
500                         errx(1, "last_sector not a proper number");
501                 if (last_sector <= first_sector)
502                         errx(1, "last_sector not larger than first_sector");
503                 total_sectors = last_sector + 1;
504         }
505
506         /* <total_sectors> */
507         p = property_find(params, "total_sectors");
508         if (p != NULL) {
509                 total_sectors = strtoul(p, &q, 0);
510                 if (!*p || *q)
511                         errx(1, "total_sectors not a proper number");
512                 if (last_sector == 0) 
513                         last_sector = first_sector + total_sectors - 1;
514         }
515
516         if (l_opt == NULL && first_sector != 0)
517                 errx(1, "No -L new-lockfile argument and first_sector != 0");
518         else if (l_opt == NULL) {
519                 first_sector++;
520                 total_sectors--;
521                 gl->flags |= 1;
522         }
523
524         if (total_sectors != (last_sector - first_sector) + 1)
525                 errx(1, "total_sectors disagree with first_sector and last_sector");
526         if (total_sectors == 0)
527                 errx(1, "missing last_sector or total_sectors");
528
529         gl->sectorN = (last_sector + 1) * gl->sectorsize;
530
531         /* Find a random keyoffset */
532         random_bits(&o, sizeof o);
533         o %= (gl->sectorN - gl->sector0);
534         o &= ~(gl->sectorsize - 1);
535         gl->keyoffset = o;
536
537         /* <number_of_keys> */
538         p = property_find(params, "number_of_keys");
539         if (p == NULL)
540                 errx(1, "Missing number_of_keys property");
541         nkeys = strtoul(p, &q, 0);
542         if (!*p || *q)
543                 errx(1, "number_of_keys not a proper number");
544         if (nkeys < 1 || nkeys > G_BDE_MAXKEYS)
545                 errx(1, "number_of_keys out of range");
546         for (u = 0; u < nkeys; u++) {
547                 for(;;) {
548                         do {
549                                 random_bits(&o, sizeof o);
550                                 o %= gl->sectorN;
551                                 o &= ~(gl->sectorsize - 1);
552                         } while(o < gl->sector0);
553                         for (u2 = 0; u2 < u; u2++)
554                                 if (o == gl->lsector[u2])
555                                         break;
556                         if (u2 < u)
557                                 continue;
558                         break;
559                 }
560                 gl->lsector[u] = o;
561         }       
562         for (; u < G_BDE_MAXKEYS; u++) {
563                 do 
564                         random_bits(&o, sizeof o);
565                 while (o < gl->sectorN);
566                 gl->lsector[u] = o;
567         }
568
569         /* Flush sector zero if we use it for lockfile data */
570         if (gl->flags & 1) {
571                 off2 = lseek(dfd, 0, SEEK_SET);
572                 if (off2 != 0)
573                         err(1, "lseek(2) to sector 0");
574                 random_bits(buf, sector_size);
575                 i = write(dfd, buf, sector_size);
576                 if (i != (int)sector_size)
577                         err(1, "write sector 0");
578         }
579
580         /* <random_flush> */
581         p = property_find(params, "random_flush");
582         if (p != NULL) {
583                 off = first_sector * sector_size;
584                 off2 = lseek(dfd, off, SEEK_SET);
585                 if (off2 != off)
586                         err(1, "lseek(2) to first_sector");
587                 off2 = last_sector * sector_size;
588                 while (off <= off2) {
589                         random_bits(buf, sector_size);
590                         i = write(dfd, buf, sector_size);
591                         if (i != (int)sector_size)
592                                 err(1, "write to $device_name");
593                         off += sector_size;
594                 }
595         }
596
597         random_bits(gl->key, sizeof gl->key);
598         
599         return;
600 }
601
602 static enum action {
603         ACT_HUH,
604         ACT_ATTACH, ACT_DETACH,
605         ACT_INIT, ACT_SETKEY, ACT_DESTROY, ACT_NUKE
606 } action;
607
608 int
609 main(int argc, char **argv)
610 {
611         const char *opts;
612         const char *l_opt, *L_opt;
613         const char *p_opt, *P_opt;
614         const char *f_opt;
615         const char *dest;
616         int i_opt, n_opt, ch, dfd, nkey, doopen;
617         int i;
618         char *q;
619         struct g_bde_key *gl;
620         struct g_bde_softc sc;
621
622         if (argc < 3)
623                 usage("Too few arguments\n");
624
625         doopen = 0;
626         if (!strcmp(argv[1], "attach")) {
627                 action = ACT_ATTACH;
628                 opts = "l:p:";
629         } else if (!strcmp(argv[1], "detach")) {
630                 action = ACT_DETACH;
631                 opts = "";
632         } else if (!strcmp(argv[1], "init")) {
633                 action = ACT_INIT;
634                 doopen = 1;
635                 opts = "f:iL:P:";
636         } else if (!strcmp(argv[1], "setkey")) {
637                 action = ACT_SETKEY;
638                 doopen = 1;
639                 opts = "n:l:L:p:P:";
640         } else if (!strcmp(argv[1], "destroy")) {
641                 action = ACT_DESTROY;
642                 doopen = 1;
643                 opts = "l:p:";
644         } else if (!strcmp(argv[1], "nuke")) {
645                 action = ACT_NUKE;
646                 doopen = 1;
647                 opts = "l:p:n:";
648         } else {
649                 usage("Unknown sub command\n");
650         }
651         argc--;
652         argv++;
653
654         dest = argv[1];
655         argc--;
656         argv++;
657
658         p_opt = NULL;
659         P_opt = NULL;
660         l_opt = NULL;
661         L_opt = NULL;
662         f_opt = NULL;
663         n_opt = 0;
664         i_opt = 0;
665
666         while((ch = getopt(argc, argv, opts)) != -1)
667                 switch (ch) {
668                 case 'f':
669                         f_opt = optarg;
670                         break;
671                 case 'i':
672                         i_opt = !i_opt;
673                 case 'l':
674                         l_opt = optarg;
675                         break;
676                 case 'L':
677                         L_opt = optarg;
678                         break;
679                 case 'p':
680                         p_opt = optarg;
681                         break;
682                 case 'P':
683                         P_opt = optarg;
684                         break;
685                 case 'n':
686                         n_opt = strtoul(optarg, &q, 0);
687                         if (!*optarg || *q)
688                                 usage("-n argument not numeric\n");
689                         if (n_opt < -1 || n_opt > G_BDE_MAXKEYS)
690                                 usage("-n argument out of range\n");
691                         break;
692                 default:
693                         usage("Invalid option\n");
694                 }
695
696         if (doopen) {
697                 dfd = open(dest, O_RDWR | O_CREAT, 0644);
698                 if (dfd < 0)
699                         err(1, dest);
700         }
701
702         memset(&sc, 0, sizeof sc);
703         sc.consumer = (struct g_consumer *)&dfd;
704         gl = &sc.key;
705         switch(action) {
706         case ACT_ATTACH:
707                 setup_passphrase(&sc, 0, p_opt);
708                 cmd_attach(&sc, dest, l_opt);
709                 break;
710         case ACT_DETACH:
711                 cmd_detach(dest);
712                 break;
713         case ACT_INIT:
714                 cmd_init(gl, dfd, f_opt, i_opt, L_opt);
715                 setup_passphrase(&sc, 1, P_opt);
716                 cmd_write(gl, &sc, dfd, 0, L_opt);
717                 break;
718         case ACT_SETKEY:
719                 setup_passphrase(&sc, 0, p_opt);
720                 cmd_open(&sc, dfd, l_opt, &nkey);
721                 if (n_opt == 0)
722                         n_opt = nkey + 1;
723                 setup_passphrase(&sc, 1, P_opt);
724                 cmd_write(gl, &sc, dfd, n_opt - 1, L_opt);
725                 break;
726         case ACT_DESTROY:
727                 setup_passphrase(&sc, 0, p_opt);
728                 cmd_open(&sc, dfd, l_opt, &nkey);
729                 cmd_destroy(gl, nkey);
730                 reset_passphrase(&sc);
731                 cmd_write(gl, &sc, dfd, nkey, l_opt);
732                 break;
733         case ACT_NUKE:
734                 setup_passphrase(&sc, 0, p_opt);
735                 cmd_open(&sc, dfd, l_opt, &nkey);
736                 if (n_opt == 0)
737                         n_opt = nkey + 1;
738                 if (n_opt == -1) {
739                         for(i = 0; i < G_BDE_MAXKEYS; i++)
740                                 cmd_nuke(gl, dfd, i);
741                 } else {
742                                 cmd_nuke(gl, dfd, n_opt - 1);
743                 }
744                 break;
745         default:
746                 usage("Internal error\n");
747         }
748
749         return(0);
750 }