gbde.c revision 111298
1249423Sdim/*-
2193323Sed * Copyright (c) 2002 Poul-Henning Kamp
3353358Sdim * Copyright (c) 2002 Networks Associates Technology, Inc.
4353358Sdim * All rights reserved.
5353358Sdim *
6193323Sed * This software was developed for the FreeBSD Project by Poul-Henning Kamp
7193323Sed * and NAI Labs, the Security Research Division of Network Associates, Inc.
8327952Sdim * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
9296417Sdim * DARPA CHATS research program.
10296417Sdim *
11296417Sdim * Redistribution and use in source and binary forms, with or without
12327952Sdim * modification, are permitted provided that the following conditions
13193323Sed * are met:
14193323Sed * 1. Redistributions of source code must retain the above copyright
15309124Sdim *    notice, this list of conditions and the following disclaimer.
16234353Sdim * 2. Redistributions in binary form must reproduce the above copyright
17327952Sdim *    notice, this list of conditions and the following disclaimer in the
18243830Sdim *    documentation and/or other materials provided with the distribution.
19327952Sdim *
20327952Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21193323Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22249423Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23296417Sdim * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24296417Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25327952Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26249423Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27249423Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28249423Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29327952Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30353358Sdim * SUCH DAMAGE.
31327952Sdim *
32296417Sdim * $FreeBSD: head/sbin/gbde/gbde.c 111298 2003-02-23 07:37:47Z tjr $
33327952Sdim */
34327952Sdim
35327952Sdim#include <sys/types.h>
36327952Sdim#include <sys/queue.h>
37327952Sdim#include <sys/mutex.h>
38327952Sdim#include <md5.h>
39327952Sdim#include <readpassphrase.h>
40276479Sdim#include <string.h>
41327952Sdim#include <stdint.h>
42327952Sdim#include <unistd.h>
43327952Sdim#include <fcntl.h>
44344779Sdim#include <paths.h>
45327952Sdim#include <strings.h>
46327952Sdim#include <stdlib.h>
47327952Sdim#include <err.h>
48327952Sdim#include <stdio.h>
49327952Sdim#include <libutil.h>
50327952Sdim#include <sys/errno.h>
51360784Sdim#include <sys/disk.h>
52327952Sdim#include <sys/stat.h>
53327952Sdim#include <crypto/rijndael/rijndael.h>
54327952Sdim#include <crypto/sha2/sha2.h>
55327952Sdim
56296417Sdim#define KASSERT(foo, bar) do { if(!(foo)) { warn bar ; exit (1); } } while (0)
57327952Sdim
58296417Sdim#include <geom/geom.h>
59321369Sdim#include <geom/bde/g_bde.h>
60327952Sdim
61327952Sdimextern const char template[];
62327952Sdim
63327952Sdim
64327952Sdim#if 0
65193323Sedstatic void
66193323Sedg_hexdump(void *ptr, int length)
67276479Sdim{
68276479Sdim	int i, j, k;
69193323Sed	unsigned char *cp;
70193323Sed
71344779Sdim	cp = ptr;
72193323Sed	for (i = 0; i < length; i+= 16) {
73314564Sdim		printf("%04x  ", i);
74261991Sdim		for (j = 0; j < 16; j++) {
75261991Sdim			k = i + j;
76193323Sed			if (k < length)
77296417Sdim				printf(" %02x", cp[k]);
78296417Sdim			else
79341825Sdim				printf("   ");
80353358Sdim		}
81193323Sed		printf("  |");
82321369Sdim		for (j = 0; j < 16; j++) {
83360784Sdim			k = i + j;
84321369Sdim			if (k >= length)
85321369Sdim				printf(" ");
86321369Sdim			else if (cp[k] >= ' ' && cp[k] <= '~')
87341825Sdim				printf("%c", cp[k]);
88341825Sdim			else
89341825Sdim				printf(".");
90341825Sdim		}
91353358Sdim		printf("|\n");
92353358Sdim	}
93353358Sdim}
94353358Sdim#endif
95193323Sed
96193323Sedstatic void __dead2
97327952Sdimusage(const char *reason)
98327952Sdim{
99327952Sdim	const char *p;
100327952Sdim
101321369Sdim	p = getprogname();
102321369Sdim	fprintf(stderr, "Usage error: %s", reason);
103321369Sdim	fprintf(stderr, "Usage:\n");
104321369Sdim	fprintf(stderr, "\t%s attach dest [-l lockfile]\n", p);
105321369Sdim	fprintf(stderr, "\t%s detach dest\n", p);
106321369Sdim	fprintf(stderr, "\t%s init /dev/dest [-i] [-f filename] [-L lockfile]\n", p);
107321369Sdim	fprintf(stderr, "\t%s setkey dest [-n key] [-l lockfile] [-L lockfile]\n", p);
108321369Sdim	fprintf(stderr, "\t%s destroy dest [-n key] [-l lockfile] [-L lockfile]\n", p);
109321369Sdim	exit (1);
110321369Sdim}
111296417Sdim
112296417Sdimvoid *
113296417Sdimg_read_data(struct g_consumer *cp, off_t offset, off_t length, int *error)
114296417Sdim{
115296417Sdim	void *p;
116249423Sdim	int fd, i;
117321369Sdim	off_t o2;
118296417Sdim
119296417Sdim	p = malloc(length);
120249423Sdim	if (p == NULL)
121344779Sdim		err(1, "malloc");
122344779Sdim	fd = *(int *)cp;
123344779Sdim	o2 = lseek(fd, offset, SEEK_SET);
124344779Sdim	if (o2 != offset)
125296417Sdim		err(1, "lseek");
126296417Sdim	i = read(fd, p, length);
127249423Sdim	if (i != length)
128296417Sdim		err(1, "read");
129296417Sdim	if (error != NULL)
130344779Sdim		error = 0;
131296417Sdim	return (p);
132296417Sdim}
133261991Sdim
134296417Sdimstatic void
135296417Sdimrandom_bits(void *p, u_int len)
136344779Sdim{
137309124Sdim	static int fdr = -1;
138309124Sdim	int i;
139309124Sdim
140309124Sdim	if (fdr < 0) {
141344779Sdim		fdr = open("/dev/urandom", O_RDONLY);
142344779Sdim		if (fdr < 0)
143296417Sdim			err(1, "/dev/urandom");
144344779Sdim	}
145327952Sdim
146249423Sdim	i = read(fdr, p, len);
147296417Sdim	if (i != (int)len)
148327952Sdim		err(1, "read from /dev/urandom");
149296417Sdim}
150249423Sdim
151296417Sdim/* XXX: not nice */
152344779Sdimstatic u_char sha2[SHA512_DIGEST_LENGTH];
153327952Sdim
154344779Sdimstatic void
155296417Sdimreset_passphrase(struct g_bde_softc *sc)
156327952Sdim{
157296417Sdim
158296417Sdim	memcpy(sc->sha2, sha2, SHA512_DIGEST_LENGTH);
159296417Sdim}
160249423Sdim
161296417Sdimstatic void
162296417Sdimsetup_passphrase(struct g_bde_softc *sc, int sure, const char *input)
163344779Sdim{
164296417Sdim	char buf1[BUFSIZ], buf2[BUFSIZ], *p;
165296417Sdim
166296417Sdim	if (input != NULL) {
167296417Sdim		g_bde_hash_pass(sc, input, strlen(input));
168193323Sed		memcpy(sha2, sc->sha2, SHA512_DIGEST_LENGTH);
169296417Sdim		return;
170296417Sdim	}
171344779Sdim	for (;;) {
172193323Sed		p = readpassphrase(
173296417Sdim		    sure ? "Enter new passphrase:" : "Enter passphrase: ",
174296417Sdim		    buf1, sizeof buf1,
175296417Sdim		    RPP_ECHO_OFF | RPP_REQUIRE_TTY);
176296417Sdim		if (p == NULL)
177193323Sed			err(1, "readpassphrase");
178327952Sdim
179344779Sdim		if (sure) {
180344779Sdim			p = readpassphrase("Reenter new passphrase: ",
181327952Sdim			    buf2, sizeof buf2,
182296417Sdim			    RPP_ECHO_OFF | RPP_REQUIRE_TTY);
183296417Sdim			if (p == NULL)
184296417Sdim				err(1, "readpassphrase");
185296417Sdim
186296417Sdim			if (strcmp(buf1, buf2)) {
187296417Sdim				printf("They didn't match.\n");
188296417Sdim				continue;
189296417Sdim			}
190296417Sdim		}
191296417Sdim		if (strlen(buf1) < 3) {
192296417Sdim			printf("Too short passphrase.\n");
193296417Sdim			continue;
194296417Sdim		}
195296417Sdim		break;
196296417Sdim	}
197296417Sdim	g_bde_hash_pass(sc, buf1, strlen(buf1));
198296417Sdim	memcpy(sha2, sc->sha2, SHA512_DIGEST_LENGTH);
199296417Sdim}
200296417Sdim
201296417Sdimstatic void
202296417Sdimencrypt_sector(void *d, int len, int klen, void *key)
203296417Sdim{
204296417Sdim	keyInstance ki;
205296417Sdim	cipherInstance ci;
206193323Sed	int error;
207296417Sdim
208296417Sdim	error = rijndael_cipherInit(&ci, MODE_CBC, NULL);
209344779Sdim	if (error <= 0)
210344779Sdim		errx(1, "rijndael_cipherInit=%d", error);
211344779Sdim	error = rijndael_makeKey(&ki, DIR_ENCRYPT, klen, key);
212193323Sed	if (error <= 0)
213296417Sdim		errx(1, "rijndael_makeKeY=%d", error);
214296417Sdim	error = rijndael_blockEncrypt(&ci, &ki, d, len * 8, d);
215296417Sdim	if (error <= 0)
216193323Sed		errx(1, "rijndael_blockEncrypt=%d", error);
217344779Sdim}
218344779Sdim
219344779Sdimstatic void
220344779Sdimcmd_attach(const struct g_bde_softc *sc, const char *dest, const char *lfile)
221344779Sdim{
222344779Sdim	int gfd, i, ffd;
223344779Sdim	struct geomconfiggeom gcg;
224296417Sdim	u_char buf[256 + 16];
225296417Sdim
226193323Sed	gfd = open("/dev/geom.ctl", O_RDWR);
227321369Sdim	if (gfd < 0)
228321369Sdim		err(1, "/dev/geom.ctl");
229321369Sdim	memset(&gcg, 0, sizeof gcg);
230321369Sdim	gcg.class.u.name = "BDE";
231321369Sdim	gcg.class.len = strlen(gcg.class.u.name);
232296417Sdim	gcg.provider.u.name = dest;
233296417Sdim	gcg.provider.len = strlen(gcg.provider.u.name);
234321369Sdim	gcg.flag = GCFG_CREATE;
235193323Sed	gcg.len = sizeof buf;
236193323Sed	gcg.ptr = buf;
237193323Sed
238344779Sdim	if (lfile != NULL) {
239296417Sdim		ffd = open(lfile, O_RDONLY, 0);
240296417Sdim		if (ffd < 0)
241296417Sdim			err(1, "%s", lfile);
242193323Sed		read(ffd, buf + sizeof(sc->sha2), 16);
243321369Sdim		close(ffd);
244321369Sdim	} else {
245321369Sdim		memset(buf + sizeof(sc->sha2), 0, 16);
246321369Sdim	}
247321369Sdim	memcpy(buf, sc->sha2, sizeof(sc->sha2));
248296417Sdim
249193323Sed	i = ioctl(gfd, GEOMCONFIGGEOM, &gcg);
250296417Sdim	if (i != 0)
251193323Sed		err(1, "ioctl(GEOMCONFIGGEOM)");
252296417Sdim	exit (0);
253344779Sdim}
254344779Sdim
255344779Sdimstatic void
256296417Sdimcmd_detach(const char *dest)
257296417Sdim{
258296417Sdim	int i, gfd;
259193323Sed	struct geomconfiggeom gcg;
260193323Sed
261193323Sed	gfd = open("/dev/geom.ctl", O_RDWR);
262353358Sdim	if (gfd < 0)
263353358Sdim		err(1, "/dev/geom.ctl");
264353358Sdim	memset(&gcg, 0, sizeof gcg);
265353358Sdim	gcg.class.u.name = "BDE";
266353358Sdim	gcg.class.len = strlen(gcg.class.u.name);
267193323Sed	gcg.provider.u.name = dest;
268193323Sed	gcg.provider.len = strlen(gcg.provider.u.name);
269193323Sed	gcg.flag = GCFG_DISMANTLE;
270344779Sdim
271296417Sdim	i = ioctl(gfd, GEOMCONFIGGEOM, &gcg);
272193323Sed	if (i != 0)
273193323Sed		err(1, "ioctl(GEOMCONFIGGEOM)");
274193323Sed	exit (0);
275193323Sed}
276193323Sed
277193323Sedstatic void
278193323Sedcmd_open(struct g_bde_softc *sc, int dfd , const char *l_opt, u_int *nkey)
279193323Sed{
280344779Sdim	int error;
281344779Sdim	int ffd;
282344779Sdim	u_char keyloc[16];
283193323Sed	u_int sectorsize;
284193323Sed	off_t mediasize;
285193323Sed	struct stat st;
286321369Sdim
287321369Sdim	error = ioctl(dfd, DIOCGSECTORSIZE, &sectorsize);
288344779Sdim	if (error)
289193323Sed		sectorsize = 512;
290344779Sdim	error = ioctl(dfd, DIOCGMEDIASIZE, &mediasize);
291344779Sdim	if (error) {
292344779Sdim		error = fstat(dfd, &st);
293344779Sdim		if (error == 0 && S_ISREG(st.st_mode))
294344779Sdim			mediasize = st.st_size;
295344779Sdim		else
296344779Sdim			error = ENOENT;
297193323Sed	}
298344779Sdim	if (error)
299344779Sdim		mediasize = (off_t)-1;
300344779Sdim	if (l_opt != NULL) {
301344779Sdim		ffd = open(l_opt, O_RDONLY, 0);
302193323Sed		if (ffd < 0)
303344779Sdim			err(1, "%s", l_opt);
304344779Sdim		read(ffd, keyloc, sizeof keyloc);
305344779Sdim		close(ffd);
306193323Sed	} else {
307193323Sed		memset(keyloc, 0, sizeof keyloc);
308193323Sed	}
309193323Sed
310193323Sed	error = g_bde_decrypt_lock(sc, sc->sha2, keyloc, mediasize,
311193323Sed	    sectorsize, nkey);
312193323Sed	if (error == ENOENT)
313193323Sed		errx(1, "Lock was destroyed.");
314234353Sdim	if (error == ESRCH)
315327952Sdim		errx(1, "Lock was nuked.");
316296417Sdim	if (error == ENOTDIR)
317296417Sdim		errx(1, "Lock not found");
318296417Sdim	if (error != 0)
319296417Sdim		errx(1, "Error %d decrypting lock", error);
320296417Sdim	if (nkey)
321296417Sdim		printf("Opened with key %u\n", *nkey);
322296417Sdim	return;
323234353Sdim}
324296417Sdim
325296417Sdimstatic void
326296417Sdimcmd_nuke(struct g_bde_key *gl, int dfd , int key)
327327952Sdim{
328234353Sdim	int i;
329296417Sdim	u_char *sbuf;
330234353Sdim	off_t offset, offset2;
331296417Sdim
332296417Sdim	sbuf = malloc(gl->sectorsize);
333296417Sdim	memset(sbuf, 0, gl->sectorsize);
334296417Sdim	offset = (gl->lsector[key] & ~(gl->sectorsize - 1));
335296417Sdim	offset2 = lseek(dfd, offset, SEEK_SET);
336296417Sdim	if (offset2 != offset)
337296417Sdim		err(1, "lseek");
338234353Sdim	i = write(dfd, sbuf, gl->sectorsize);
339296417Sdim	if (i != (int)gl->sectorsize)
340296417Sdim		err(1, "write");
341234353Sdim	printf("Nuked key %d\n", key);
342327952Sdim}
343234353Sdim
344296417Sdimstatic void
345296417Sdimcmd_write(struct g_bde_key *gl, struct g_bde_softc *sc, int dfd , int key, const char *l_opt)
346296417Sdim{
347234353Sdim	int i, ffd;
348296417Sdim	uint64_t off[2];
349296417Sdim	u_char keyloc[16];
350296417Sdim	u_char *sbuf, *q;
351296417Sdim	off_t offset, offset2;
352296417Sdim
353296417Sdim	sbuf = malloc(gl->sectorsize);
354296417Sdim	/*
355234353Sdim	 * Find the byte-offset in the lock sector where we will put the lock
356296417Sdim	 * data structure.  We can put it any random place as long as the
357296417Sdim	 * structure fits.
358296417Sdim	 */
359296417Sdim	for(;;) {
360327952Sdim		random_bits(off, sizeof off);
361234353Sdim		off[0] &= (gl->sectorsize - 1);
362296417Sdim		if (off[0] + G_BDE_LOCKSIZE > gl->sectorsize)
363234353Sdim			continue;
364296417Sdim		break;
365296417Sdim	}
366296417Sdim
367296417Sdim	/* Add the sector offset in bytes */
368296417Sdim	off[0] += (gl->lsector[key] & ~(gl->sectorsize - 1));
369296417Sdim	gl->lsector[key] = off[0];
370234353Sdim
371296417Sdim	i = g_bde_keyloc_encrypt(sc, off, keyloc);
372309124Sdim	if (i)
373296417Sdim		errx(1, "g_bde_keyloc_encrypt()");
374296417Sdim	if (l_opt != NULL) {
375296417Sdim		ffd = open(l_opt, O_WRONLY | O_CREAT | O_TRUNC, 0600);
376234353Sdim		if (ffd < 0)
377296417Sdim			err(1, "%s", l_opt);
378296417Sdim		write(ffd, keyloc, sizeof keyloc);
379296417Sdim		close(ffd);
380296417Sdim	} else if (gl->flags & 1) {
381296417Sdim		offset2 = lseek(dfd, 0, SEEK_SET);
382296417Sdim		if (offset2 != 0)
383296417Sdim			err(1, "lseek");
384296417Sdim		i = read(dfd, sbuf, gl->sectorsize);
385296417Sdim		if (i != (int)gl->sectorsize)
386296417Sdim			err(1, "read");
387296417Sdim		memcpy(sbuf + key * 16, keyloc, sizeof keyloc);
388296417Sdim		offset2 = lseek(dfd, 0, SEEK_SET);
389296417Sdim		if (offset2 != 0)
390296417Sdim			err(1, "lseek");
391296417Sdim		i = write(dfd, sbuf, gl->sectorsize);
392296417Sdim		if (i != (int)gl->sectorsize)
393296417Sdim			err(1, "write");
394296417Sdim	} else {
395296417Sdim		errx(1, "No -L option and no space in sector 0 for lockfile");
396296417Sdim	}
397234353Sdim
398234353Sdim	/* Allocate a sectorbuffer and fill it with random junk */
399296417Sdim	if (sbuf == NULL)
400296417Sdim		err(1, "malloc");
401296417Sdim	random_bits(sbuf, gl->sectorsize);
402296417Sdim
403296417Sdim	/* Fill random bits in the spare field */
404234353Sdim	random_bits(gl->spare, sizeof(gl->spare));
405296417Sdim
406296417Sdim	/* Encode the structure where we want it */
407296417Sdim	q = sbuf + (off[0] % gl->sectorsize);
408296417Sdim	i = g_bde_encode_lock(sc, gl, q);
409327952Sdim	if (i < 0)
410327952Sdim		errx(1, "programming error encoding lock");
411296417Sdim
412327952Sdim	encrypt_sector(q, G_BDE_LOCKSIZE, 256, sc->sha2 + 16);
413327952Sdim	offset = gl->lsector[key] & ~(gl->sectorsize - 1);
414327952Sdim	offset2 = lseek(dfd, offset, SEEK_SET);
415296417Sdim	if (offset2 != offset)
416296417Sdim		err(1, "lseek");
417234353Sdim	i = write(dfd, sbuf, gl->sectorsize);
418327952Sdim	if (i != (int)gl->sectorsize)
419327952Sdim		err(1, "write");
420234353Sdim	printf("Wrote key %d at %jd\n", key, (intmax_t)offset);
421327952Sdim#if 0
422296417Sdim	printf("s0 = %jd\n", (intmax_t)gl->sector0);
423327952Sdim	printf("sN = %jd\n", (intmax_t)gl->sectorN);
424327952Sdim	printf("l[0] = %jd\n", (intmax_t)gl->lsector[0]);
425234353Sdim	printf("l[1] = %jd\n", (intmax_t)gl->lsector[1]);
426314564Sdim	printf("l[2] = %jd\n", (intmax_t)gl->lsector[2]);
427314564Sdim	printf("l[3] = %jd\n", (intmax_t)gl->lsector[3]);
428314564Sdim	printf("k = %jd\n", (intmax_t)gl->keyoffset);
429296417Sdim	printf("ss = %jd\n", (intmax_t)gl->sectorsize);
430327952Sdim#endif
431296417Sdim}
432296417Sdim
433314564Sdimstatic void
434327952Sdimcmd_destroy(struct g_bde_key *gl, int nkey)
435296417Sdim{
436296417Sdim	int i;
437296417Sdim
438327952Sdim	bzero(&gl->sector0, sizeof gl->sector0);
439296417Sdim	bzero(&gl->sectorN, sizeof gl->sectorN);
440296417Sdim	bzero(&gl->keyoffset, sizeof gl->keyoffset);
441234353Sdim	bzero(&gl->flags, sizeof gl->flags);
442327952Sdim	bzero(gl->mkey, sizeof gl->mkey);
443327952Sdim	for (i = 0; i < G_BDE_MAXKEYS; i++)
444296417Sdim		if (i != nkey)
445261991Sdim			gl->lsector[i] = ~0;
446261991Sdim}
447296417Sdim
448296417Sdimstatic int
449341825Sdimsorthelp(const void *a, const void *b)
450296417Sdim{
451276479Sdim	const off_t *oa, *ob;
452276479Sdim
453276479Sdim	oa = a;
454276479Sdim	ob = b;
455261991Sdim	return (*oa - *ob);
456261991Sdim}
457261991Sdim
458276479Sdimstatic void
459276479Sdimcmd_init(struct g_bde_key *gl, int dfd, const char *f_opt, int i_opt, const char *l_opt)
460276479Sdim{
461261991Sdim	int i;
462261991Sdim	u_char *buf;
463261991Sdim	unsigned sector_size;
464261991Sdim	uint64_t	first_sector;
465261991Sdim	uint64_t	last_sector;
466261991Sdim	uint64_t	total_sectors;
467261991Sdim	off_t	off, off2;
468261991Sdim	unsigned nkeys;
469261991Sdim	const char *p;
470261991Sdim	char *q, cbuf[BUFSIZ];
471261991Sdim	unsigned u, u2;
472276479Sdim	uint64_t o;
473261991Sdim	properties	params;
474276479Sdim
475280031Sdim	bzero(gl, sizeof *gl);
476276479Sdim	if (f_opt != NULL) {
477261991Sdim		i = open(f_opt, O_RDONLY);
478261991Sdim		if (i < 0)
479261991Sdim			err(1, "%s", f_opt);
480261991Sdim		params = properties_read(i);
481276479Sdim		close (i);
482276479Sdim	} else {
483276479Sdim		/* XXX: Polish */
484276479Sdim		q = strdup("/tmp/temp.XXXXXXXXXX");
485276479Sdim		i = mkstemp(q);
486276479Sdim		if (i < 0)
487276479Sdim			err(1, "%s", q);
488276479Sdim		write(i, template, strlen(template));
489280031Sdim		close (i);
490276479Sdim		if (i_opt) {
491276479Sdim			p = getenv("EDITOR");
492276479Sdim			if (p == NULL)
493261991Sdim				p = "vi";
494276479Sdim			if (snprintf(cbuf, sizeof(cbuf), "%s %s\n", p, q) >=
495276479Sdim			    (ssize_t)sizeof(cbuf))
496261991Sdim				errx(1, "EDITOR is too long");
497276479Sdim			system(cbuf);
498261991Sdim		}
499261991Sdim		i = open(q, O_RDONLY);
500261991Sdim		if (i < 0)
501261991Sdim			err(1, "%s", f_opt);
502261991Sdim		params = properties_read(i);
503276479Sdim		close (i);
504261991Sdim		unlink(q);
505261991Sdim	}
506261991Sdim
507261991Sdim	/* <sector_size> */
508261991Sdim	p = property_find(params, "sector_size");
509296417Sdim	i = ioctl(dfd, DIOCGSECTORSIZE, &u);
510296417Sdim	if (p != NULL) {
511296417Sdim		sector_size = strtoul(p, &q, 0);
512296417Sdim		if (!*p || *q)
513296417Sdim			errx(1, "sector_size not a proper number");
514296417Sdim	} else if (i == 0) {
515296417Sdim		sector_size = u;
516296417Sdim	} else {
517296417Sdim		errx(1, "Missing sector_size property");
518296417Sdim	}
519296417Sdim	if (sector_size & (sector_size - 1))
520296417Sdim		errx(1, "sector_size not a power of 2");
521296417Sdim	if (sector_size < 512)
522296417Sdim		errx(1, "sector_size is smaller than 512");
523296417Sdim	buf = malloc(sector_size);
524296417Sdim	if (buf == NULL)
525296417Sdim		err(1, "Failed to malloc sector buffer");
526296417Sdim	gl->sectorsize = sector_size;
527261991Sdim
528296417Sdim	i = ioctl(dfd, DIOCGMEDIASIZE, &off);
529296417Sdim	if (i == 0) {
530296417Sdim		first_sector = 0;
531296417Sdim		total_sectors = off / sector_size;
532296417Sdim		last_sector = total_sectors - 1;
533296417Sdim	} else {
534296417Sdim		first_sector = 0;
535314564Sdim		last_sector = 0;
536314564Sdim		total_sectors = 0;
537296417Sdim	}
538296417Sdim
539296417Sdim	/* <first_sector> */
540296417Sdim	p = property_find(params, "first_sector");
541296417Sdim	if (p != NULL) {
542296417Sdim		first_sector = strtoul(p, &q, 0);
543296417Sdim		if (!*p || *q)
544296417Sdim			errx(1, "first_sector not a proper number");
545296417Sdim	}
546296417Sdim
547276479Sdim	/* <last_sector> */
548261991Sdim	p = property_find(params, "last_sector");
549261991Sdim	if (p != NULL) {
550261991Sdim		last_sector = strtoul(p, &q, 0);
551261991Sdim		if (!*p || *q)
552309124Sdim			errx(1, "last_sector not a proper number");
553309124Sdim		if (last_sector <= first_sector)
554309124Sdim			errx(1, "last_sector not larger than first_sector");
555309124Sdim		total_sectors = last_sector + 1;
556309124Sdim	}
557261991Sdim
558261991Sdim	/* <total_sectors> */
559261991Sdim	p = property_find(params, "total_sectors");
560261991Sdim	if (p != NULL) {
561261991Sdim		total_sectors = strtoul(p, &q, 0);
562261991Sdim		if (!*p || *q)
563261991Sdim			errx(1, "total_sectors not a proper number");
564261991Sdim		if (last_sector == 0)
565261991Sdim			last_sector = first_sector + total_sectors - 1;
566261991Sdim	}
567261991Sdim
568261991Sdim	if (l_opt == NULL && first_sector != 0)
569261991Sdim		errx(1, "No -L new-lockfile argument and first_sector != 0");
570261991Sdim	else if (l_opt == NULL) {
571261991Sdim		first_sector++;
572314564Sdim		total_sectors--;
573314564Sdim		gl->flags |= 1;
574314564Sdim	}
575314564Sdim	gl->sector0 = first_sector * gl->sectorsize;
576314564Sdim
577314564Sdim	if (total_sectors != (last_sector - first_sector) + 1)
578314564Sdim		errx(1, "total_sectors disagree with first_sector and last_sector");
579314564Sdim	if (total_sectors == 0)
580314564Sdim		errx(1, "missing last_sector or total_sectors");
581314564Sdim
582314564Sdim	gl->sectorN = (last_sector + 1) * gl->sectorsize;
583314564Sdim
584314564Sdim	/* Find a random keyoffset */
585314564Sdim	random_bits(&o, sizeof o);
586314564Sdim	o %= (gl->sectorN - gl->sector0);
587314564Sdim	o &= ~(gl->sectorsize - 1);
588327952Sdim	gl->keyoffset = o;
589327952Sdim
590314564Sdim	/* <number_of_keys> */
591314564Sdim	p = property_find(params, "number_of_keys");
592314564Sdim	if (p == NULL)
593314564Sdim		errx(1, "Missing number_of_keys property");
594314564Sdim	nkeys = strtoul(p, &q, 0);
595314564Sdim	if (!*p || *q)
596314564Sdim		errx(1, "number_of_keys not a proper number");
597314564Sdim	if (nkeys < 1 || nkeys > G_BDE_MAXKEYS)
598314564Sdim		errx(1, "number_of_keys out of range");
599314564Sdim	for (u = 0; u < nkeys; u++) {
600314564Sdim		for(;;) {
601314564Sdim			do {
602314564Sdim				random_bits(&o, sizeof o);
603314564Sdim				o %= gl->sectorN;
604314564Sdim				o &= ~(gl->sectorsize - 1);
605314564Sdim			} while(o < gl->sector0);
606314564Sdim			for (u2 = 0; u2 < u; u2++)
607314564Sdim				if (o == gl->lsector[u2])
608314564Sdim					break;
609314564Sdim			if (u2 < u)
610314564Sdim				continue;
611314564Sdim			break;
612314564Sdim		}
613321369Sdim		gl->lsector[u] = o;
614314564Sdim	}
615314564Sdim	for (; u < G_BDE_MAXKEYS; u++) {
616314564Sdim		do
617314564Sdim			random_bits(&o, sizeof o);
618314564Sdim		while (o < gl->sectorN);
619314564Sdim		gl->lsector[u] = o;
620314564Sdim	}
621314564Sdim	qsort(gl->lsector, G_BDE_MAXKEYS, sizeof gl->lsector[0], sorthelp);
622321369Sdim
623321369Sdim	/* Flush sector zero if we use it for lockfile data */
624321369Sdim	if (gl->flags & 1) {
625321369Sdim		off2 = lseek(dfd, 0, SEEK_SET);
626321369Sdim		if (off2 != 0)
627321369Sdim			err(1, "lseek(2) to sector 0");
628321369Sdim		random_bits(buf, sector_size);
629321369Sdim		i = write(dfd, buf, sector_size);
630321369Sdim		if (i != (int)sector_size)
631321369Sdim			err(1, "write sector 0");
632321369Sdim	}
633321369Sdim
634321369Sdim	/* <random_flush> */
635321369Sdim	p = property_find(params, "random_flush");
636321369Sdim	if (p != NULL) {
637321369Sdim		off = first_sector * sector_size;
638321369Sdim		off2 = lseek(dfd, off, SEEK_SET);
639321369Sdim		if (off2 != off)
640321369Sdim			err(1, "lseek(2) to first_sector");
641321369Sdim		off2 = last_sector * sector_size;
642321369Sdim		while (off <= off2) {
643321369Sdim			random_bits(buf, sector_size);
644321369Sdim			i = write(dfd, buf, sector_size);
645321369Sdim			if (i != (int)sector_size)
646321369Sdim				err(1, "write to $device_name");
647321369Sdim			off += sector_size;
648321369Sdim		}
649321369Sdim	}
650321369Sdim
651321369Sdim	random_bits(gl->mkey, sizeof gl->mkey);
652321369Sdim	random_bits(gl->salt, sizeof gl->salt);
653321369Sdim
654321369Sdim	return;
655321369Sdim}
656321369Sdim
657321369Sdimstatic enum action {
658321369Sdim	ACT_HUH,
659321369Sdim	ACT_ATTACH, ACT_DETACH,
660321369Sdim	ACT_INIT, ACT_SETKEY, ACT_DESTROY, ACT_NUKE
661341825Sdim} action;
662321369Sdim
663321369Sdimint
664321369Sdimmain(int argc, char **argv)
665360784Sdim{
666360784Sdim	const char *opts;
667360784Sdim	const char *l_opt, *L_opt;
668360784Sdim	const char *p_opt, *P_opt;
669360784Sdim	const char *f_opt;
670360784Sdim	char *dest;
671360784Sdim	int i_opt, n_opt, ch, dfd, doopen;
672360784Sdim	u_int nkey;
673360784Sdim	int i;
674360784Sdim	char *q, buf[BUFSIZ];
675360784Sdim	struct g_bde_key *gl;
676360784Sdim	struct g_bde_softc sc;
677360784Sdim
678360784Sdim	if (argc < 3)
679360784Sdim		usage("Too few arguments\n");
680360784Sdim
681360784Sdim	doopen = 0;
682360784Sdim	if (!strcmp(argv[1], "attach")) {
683360784Sdim		action = ACT_ATTACH;
684296417Sdim		opts = "l:p:";
685296417Sdim	} else if (!strcmp(argv[1], "detach")) {
686193323Sed		action = ACT_DETACH;
687193323Sed		opts = "";
688234353Sdim	} else if (!strcmp(argv[1], "init")) {
689234353Sdim		action = ACT_INIT;
690193323Sed		doopen = 1;
691193323Sed		opts = "f:iL:P:";
692296417Sdim	} else if (!strcmp(argv[1], "setkey")) {
693309124Sdim		action = ACT_SETKEY;
694309124Sdim		doopen = 1;
695309124Sdim		opts = "n:l:L:p:P:";
696309124Sdim	} else if (!strcmp(argv[1], "destroy")) {
697193323Sed		action = ACT_DESTROY;
698193323Sed		doopen = 1;
699321369Sdim		opts = "l:p:";
700321369Sdim	} else if (!strcmp(argv[1], "nuke")) {
701234353Sdim		action = ACT_NUKE;
702234353Sdim		doopen = 1;
703234353Sdim		opts = "l:p:n:";
704234353Sdim	} else {
705296417Sdim		usage("Unknown sub command\n");
706296417Sdim	}
707234353Sdim	argc--;
708321369Sdim	argv++;
709234353Sdim
710234353Sdim	dest = strdup(argv[1]);
711234353Sdim	argc--;
712234353Sdim	argv++;
713234353Sdim
714234353Sdim	p_opt = NULL;
715234353Sdim	P_opt = NULL;
716296417Sdim	l_opt = NULL;
717296417Sdim	L_opt = NULL;
718296417Sdim	f_opt = NULL;
719296417Sdim	n_opt = 0;
720261991Sdim	i_opt = 0;
721261991Sdim
722234353Sdim	while((ch = getopt(argc, argv, opts)) != -1)
723296417Sdim		switch (ch) {
724234353Sdim		case 'f':
725234353Sdim			f_opt = optarg;
726234353Sdim			break;
727321369Sdim		case 'i':
728234353Sdim			i_opt = !i_opt;
729234353Sdim		case 'l':
730234353Sdim			l_opt = optarg;
731234353Sdim			break;
732234353Sdim		case 'L':
733234353Sdim			L_opt = optarg;
734296417Sdim			break;
735309124Sdim		case 'p':
736309124Sdim			p_opt = optarg;
737309124Sdim			break;
738261991Sdim		case 'P':
739261991Sdim			P_opt = optarg;
740234353Sdim			break;
741234353Sdim		case 'n':
742234353Sdim			n_opt = strtoul(optarg, &q, 0);
743234353Sdim			if (!*optarg || *q)
744261991Sdim				usage("-n argument not numeric\n");
745261991Sdim			if (n_opt < -1 || n_opt > G_BDE_MAXKEYS)
746261991Sdim				usage("-n argument out of range\n"); break;
747261991Sdim		default:
748261991Sdim			usage("Invalid option\n");
749296417Sdim		}
750296417Sdim
751296417Sdim	if (doopen) {
752360784Sdim		dfd = open(dest, O_RDWR | O_CREAT, 0644);
753360784Sdim		if (dfd < 0) {
754261991Sdim			if (snprintf(buf, sizeof(buf), "%s%s",
755261991Sdim			    _PATH_DEV, dest) >= (ssize_t)sizeof(buf))
756234353Sdim				errno = ENAMETOOLONG;
757234353Sdim			else
758234353Sdim				dfd = open(buf, O_RDWR | O_CREAT, 0644);
759234353Sdim		}
760234353Sdim		if (dfd < 0)
761234353Sdim			err(1, "%s", dest);
762234353Sdim	} else {
763234353Sdim		if (!memcmp(dest, _PATH_DEV, strlen(_PATH_DEV)))
764234353Sdim			strcpy(dest, dest + strlen(_PATH_DEV));
765296417Sdim		if (strchr(dest, '/'))
766276479Sdim			usage("\"dest\" argument must be geom-name\n");
767234353Sdim	}
768296417Sdim
769296417Sdim	memset(&sc, 0, sizeof sc);
770234353Sdim	sc.consumer = (void *)&dfd;
771234353Sdim	gl = &sc.key;
772234353Sdim	switch(action) {
773234353Sdim	case ACT_ATTACH:
774261991Sdim		setup_passphrase(&sc, 0, p_opt);
775321369Sdim		cmd_attach(&sc, dest, l_opt);
776193323Sed		break;
777193323Sed	case ACT_DETACH:
778193323Sed		cmd_detach(dest);
779234353Sdim		break;
780234353Sdim	case ACT_INIT:
781234353Sdim		cmd_init(gl, dfd, f_opt, i_opt, L_opt);
782234353Sdim		setup_passphrase(&sc, 1, P_opt);
783276479Sdim		cmd_write(gl, &sc, dfd, 0, L_opt);
784276479Sdim		break;
785234353Sdim	case ACT_SETKEY:
786234353Sdim		setup_passphrase(&sc, 0, p_opt);
787234353Sdim		cmd_open(&sc, dfd, l_opt, &nkey);
788234353Sdim		if (n_opt == 0)
789234353Sdim			n_opt = nkey + 1;
790234353Sdim		setup_passphrase(&sc, 1, P_opt);
791296417Sdim		cmd_write(gl, &sc, dfd, n_opt - 1, L_opt);
792296417Sdim		break;
793234353Sdim	case ACT_DESTROY:
794296417Sdim		setup_passphrase(&sc, 0, p_opt);
795234353Sdim		cmd_open(&sc, dfd, l_opt, &nkey);
796234353Sdim		cmd_destroy(gl, nkey);
797309124Sdim		reset_passphrase(&sc);
798309124Sdim		cmd_write(gl, &sc, dfd, nkey, l_opt);
799234353Sdim		break;
800234353Sdim	case ACT_NUKE:
801276479Sdim		setup_passphrase(&sc, 0, p_opt);
802276479Sdim		cmd_open(&sc, dfd, l_opt, &nkey);
803234353Sdim		if (n_opt == 0)
804309124Sdim			n_opt = nkey + 1;
805309124Sdim		if (n_opt == -1) {
806234353Sdim			for(i = 0; i < G_BDE_MAXKEYS; i++)
807234353Sdim				cmd_nuke(gl, dfd, i);
808234353Sdim		} else {
809234353Sdim				cmd_nuke(gl, dfd, n_opt - 1);
810234353Sdim		}
811234353Sdim		break;
812296417Sdim	default:
813296417Sdim		usage("Internal error\n");
814234353Sdim	}
815234353Sdim
816234353Sdim	return(0);
817321369Sdim}
818234353Sdim