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