gbde.c revision 125387
1105513Sphk/*-
2105513Sphk * Copyright (c) 2002 Poul-Henning Kamp
3105513Sphk * Copyright (c) 2002 Networks Associates Technology, Inc.
4105513Sphk * All rights reserved.
5105513Sphk *
6105513Sphk * This software was developed for the FreeBSD Project by Poul-Henning Kamp
7105513Sphk * and NAI Labs, the Security Research Division of Network Associates, Inc.
8105513Sphk * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
9105513Sphk * DARPA CHATS research program.
10105513Sphk *
11105513Sphk * Redistribution and use in source and binary forms, with or without
12105513Sphk * modification, are permitted provided that the following conditions
13105513Sphk * are met:
14105513Sphk * 1. Redistributions of source code must retain the above copyright
15105513Sphk *    notice, this list of conditions and the following disclaimer.
16105513Sphk * 2. Redistributions in binary form must reproduce the above copyright
17105513Sphk *    notice, this list of conditions and the following disclaimer in the
18105513Sphk *    documentation and/or other materials provided with the distribution.
19105513Sphk *
20105513Sphk * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21105513Sphk * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22105513Sphk * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23105513Sphk * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24105513Sphk * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25105513Sphk * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26105513Sphk * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27105513Sphk * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28105513Sphk * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29105513Sphk * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30105513Sphk * SUCH DAMAGE.
31105513Sphk *
32105513Sphk * $FreeBSD: head/sbin/gbde/gbde.c 125387 2004-02-03 11:12:29Z des $
33121073Sphk *
34121073Sphk * XXX: Future stuff
35125387Sdes *
36121073Sphk * Replace the template file options (-i & -f) with command-line variables
37121073Sphk * "-v property=foo"
38121073Sphk *
39121073Sphk * Introduce -e, extra entropy source (XOR with /dev/random)
40121073Sphk *
41121073Sphk * Introduce -E, alternate entropy source (instead of /dev/random)
42121073Sphk *
43125387Sdes * Introduce -i take IV from keyboard or
44121073Sphk *
45121073Sphk * Introduce -I take IV from file/cmd
46121073Sphk *
47121073Sphk * Introduce -m/-M store encrypted+encoded masterkey in file
48121073Sphk *
49121073Sphk * Introduce -k/-K get pass-phrase part from file/cmd
50121073Sphk *
51121073Sphk * Introduce -d add more dest-devices to worklist.
52121073Sphk *
53121073Sphk * Add key-option: selfdestruct bit.
54121073Sphk *
55121073Sphk * New/changed verbs:
56121073Sphk *	"onetime"	attach with onetime nonstored locksector
57121073Sphk *	"key"/"unkey" to blast memory copy of key without orphaning
58121073Sphk *	"nuke" blow away everything attached, crash/halt/power-off if possible.
59121073Sphk *	"blast" destroy all copies of the masterkey
60121073Sphk *	"destroy" destroy one copy of the masterkey
61121073Sphk *	"backup"/"restore" of masterkey sectors.
62121073Sphk *
63121073Sphk * Make all verbs work on both attached/detached devices.
64121073Sphk *
65105513Sphk */
66105513Sphk
67105513Sphk#include <sys/types.h>
68105513Sphk#include <sys/queue.h>
69105513Sphk#include <sys/mutex.h>
70105513Sphk#include <md5.h>
71105513Sphk#include <readpassphrase.h>
72105513Sphk#include <string.h>
73105513Sphk#include <stdint.h>
74105513Sphk#include <unistd.h>
75105513Sphk#include <fcntl.h>
76107455Sphk#include <paths.h>
77105513Sphk#include <strings.h>
78105513Sphk#include <stdlib.h>
79105513Sphk#include <err.h>
80105513Sphk#include <stdio.h>
81105513Sphk#include <libutil.h>
82112877Sphk#include <libgeom.h>
83105513Sphk#include <sys/errno.h>
84105513Sphk#include <sys/disk.h>
85106407Sphk#include <sys/stat.h>
86105513Sphk#include <crypto/rijndael/rijndael.h>
87106407Sphk#include <crypto/sha2/sha2.h>
88120877Sphk#include <sys/param.h>
89120877Sphk#include <sys/linker.h>
90105513Sphk
91120877Sphk#define GBDEMOD "geom_bde"
92106407Sphk#define KASSERT(foo, bar) do { if(!(foo)) { warn bar ; exit (1); } } while (0)
93106407Sphk
94105513Sphk#include <geom/geom.h>
95105513Sphk#include <geom/bde/g_bde.h>
96105513Sphk
97105513Sphkextern const char template[];
98105513Sphk
99106407Sphk
100106407Sphk#if 0
101106407Sphkstatic void
102106407Sphkg_hexdump(void *ptr, int length)
103106407Sphk{
104106407Sphk	int i, j, k;
105106407Sphk	unsigned char *cp;
106106407Sphk
107106407Sphk	cp = ptr;
108106407Sphk	for (i = 0; i < length; i+= 16) {
109106407Sphk		printf("%04x  ", i);
110106407Sphk		for (j = 0; j < 16; j++) {
111106407Sphk			k = i + j;
112106407Sphk			if (k < length)
113106407Sphk				printf(" %02x", cp[k]);
114106407Sphk			else
115106407Sphk				printf("   ");
116106407Sphk		}
117106407Sphk		printf("  |");
118106407Sphk		for (j = 0; j < 16; j++) {
119106407Sphk			k = i + j;
120106407Sphk			if (k >= length)
121106407Sphk				printf(" ");
122106407Sphk			else if (cp[k] >= ' ' && cp[k] <= '~')
123106407Sphk				printf("%c", cp[k]);
124106407Sphk			else
125106407Sphk				printf(".");
126106407Sphk		}
127106407Sphk		printf("|\n");
128106407Sphk	}
129106407Sphk}
130106407Sphk#endif
131106407Sphk
132105513Sphkstatic void __dead2
133105513Sphkusage(const char *reason)
134105513Sphk{
135105513Sphk	const char *p;
136105513Sphk
137105513Sphk	p = getprogname();
138105513Sphk	fprintf(stderr, "Usage error: %s", reason);
139105513Sphk	fprintf(stderr, "Usage:\n");
140107455Sphk	fprintf(stderr, "\t%s attach dest [-l lockfile]\n", p);
141105541Sphk	fprintf(stderr, "\t%s detach dest\n", p);
142107455Sphk	fprintf(stderr, "\t%s init /dev/dest [-i] [-f filename] [-L lockfile]\n", p);
143107455Sphk	fprintf(stderr, "\t%s setkey dest [-n key] [-l lockfile] [-L lockfile]\n", p);
144107455Sphk	fprintf(stderr, "\t%s destroy dest [-n key] [-l lockfile] [-L lockfile]\n", p);
145105513Sphk	exit (1);
146105513Sphk}
147105513Sphk
148105513Sphkvoid *
149105513Sphkg_read_data(struct g_consumer *cp, off_t offset, off_t length, int *error)
150105513Sphk{
151105513Sphk	void *p;
152105513Sphk	int fd, i;
153105513Sphk	off_t o2;
154105513Sphk
155105513Sphk	p = malloc(length);
156105513Sphk	if (p == NULL)
157105513Sphk		err(1, "malloc");
158105513Sphk	fd = *(int *)cp;
159105513Sphk	o2 = lseek(fd, offset, SEEK_SET);
160105513Sphk	if (o2 != offset)
161105513Sphk		err(1, "lseek");
162105513Sphk	i = read(fd, p, length);
163105513Sphk	if (i != length)
164105513Sphk		err(1, "read");
165105513Sphk	if (error != NULL)
166105513Sphk		error = 0;
167105513Sphk	return (p);
168105513Sphk}
169105513Sphk
170105513Sphkstatic void
171105513Sphkrandom_bits(void *p, u_int len)
172105513Sphk{
173105513Sphk	static int fdr = -1;
174105513Sphk	int i;
175105513Sphk
176105513Sphk	if (fdr < 0) {
177105513Sphk		fdr = open("/dev/urandom", O_RDONLY);
178105513Sphk		if (fdr < 0)
179105513Sphk			err(1, "/dev/urandom");
180105513Sphk	}
181125387Sdes
182105513Sphk	i = read(fdr, p, len);
183105513Sphk	if (i != (int)len)
184105513Sphk		err(1, "read from /dev/urandom");
185105513Sphk}
186105513Sphk
187105513Sphk/* XXX: not nice */
188106407Sphkstatic u_char sha2[SHA512_DIGEST_LENGTH];
189105513Sphk
190105513Sphkstatic void
191105513Sphkreset_passphrase(struct g_bde_softc *sc)
192105513Sphk{
193105513Sphk
194106407Sphk	memcpy(sc->sha2, sha2, SHA512_DIGEST_LENGTH);
195105513Sphk}
196105513Sphk
197105513Sphkstatic void
198105513Sphksetup_passphrase(struct g_bde_softc *sc, int sure, const char *input)
199105513Sphk{
200105513Sphk	char buf1[BUFSIZ], buf2[BUFSIZ], *p;
201105513Sphk
202105513Sphk	if (input != NULL) {
203106407Sphk		g_bde_hash_pass(sc, input, strlen(input));
204106407Sphk		memcpy(sha2, sc->sha2, SHA512_DIGEST_LENGTH);
205105513Sphk		return;
206105513Sphk	}
207105513Sphk	for (;;) {
208105513Sphk		p = readpassphrase(
209105513Sphk		    sure ? "Enter new passphrase:" : "Enter passphrase: ",
210105513Sphk		    buf1, sizeof buf1,
211105513Sphk		    RPP_ECHO_OFF | RPP_REQUIRE_TTY);
212105513Sphk		if (p == NULL)
213105513Sphk			err(1, "readpassphrase");
214105513Sphk
215105513Sphk		if (sure) {
216105513Sphk			p = readpassphrase("Reenter new passphrase: ",
217105513Sphk			    buf2, sizeof buf2,
218105513Sphk			    RPP_ECHO_OFF | RPP_REQUIRE_TTY);
219105513Sphk			if (p == NULL)
220105513Sphk				err(1, "readpassphrase");
221105513Sphk
222105513Sphk			if (strcmp(buf1, buf2)) {
223105513Sphk				printf("They didn't match.\n");
224105513Sphk				continue;
225105513Sphk			}
226105513Sphk		}
227105513Sphk		if (strlen(buf1) < 3) {
228105513Sphk			printf("Too short passphrase.\n");
229105513Sphk			continue;
230105513Sphk		}
231105513Sphk		break;
232105513Sphk	}
233106407Sphk	g_bde_hash_pass(sc, buf1, strlen(buf1));
234106407Sphk	memcpy(sha2, sc->sha2, SHA512_DIGEST_LENGTH);
235105513Sphk}
236105513Sphk
237105513Sphkstatic void
238106407Sphkencrypt_sector(void *d, int len, int klen, void *key)
239105513Sphk{
240105513Sphk	keyInstance ki;
241105513Sphk	cipherInstance ci;
242105513Sphk	int error;
243125387Sdes
244105513Sphk	error = rijndael_cipherInit(&ci, MODE_CBC, NULL);
245105513Sphk	if (error <= 0)
246105513Sphk		errx(1, "rijndael_cipherInit=%d", error);
247106407Sphk	error = rijndael_makeKey(&ki, DIR_ENCRYPT, klen, key);
248105513Sphk	if (error <= 0)
249105513Sphk		errx(1, "rijndael_makeKeY=%d", error);
250105513Sphk	error = rijndael_blockEncrypt(&ci, &ki, d, len * 8, d);
251105513Sphk	if (error <= 0)
252105513Sphk		errx(1, "rijndael_blockEncrypt=%d", error);
253105513Sphk}
254105513Sphk
255105513Sphkstatic void
256105513Sphkcmd_attach(const struct g_bde_softc *sc, const char *dest, const char *lfile)
257105513Sphk{
258112877Sphk	int ffd;
259112877Sphk	u_char buf[16];
260112877Sphk	struct gctl_req *r;
261112877Sphk	const char *errstr;
262105513Sphk
263115624Sphk	r = gctl_get_handle();
264115624Sphk	gctl_ro_param(r, "verb", -1, "create geom");
265112877Sphk	gctl_ro_param(r, "class", -1, "BDE");
266112877Sphk	gctl_ro_param(r, "provider", -1, dest);
267112877Sphk	gctl_ro_param(r, "pass", SHA512_DIGEST_LENGTH, sc->sha2);
268105513Sphk	if (lfile != NULL) {
269105513Sphk		ffd = open(lfile, O_RDONLY, 0);
270105513Sphk		if (ffd < 0)
271111296Stjr			err(1, "%s", lfile);
272112877Sphk		read(ffd, buf, 16);
273112877Sphk		gctl_ro_param(r, "key", 16, buf);
274105513Sphk		close(ffd);
275105513Sphk	}
276112877Sphk	/* gctl_dump(r, stdout); */
277112877Sphk	errstr = gctl_issue(r);
278112877Sphk	if (errstr != NULL)
279125386Sdes		errx(1, "Attach to %s failed: %s", dest, errstr);
280105513Sphk
281105513Sphk	exit (0);
282105513Sphk}
283105513Sphk
284105513Sphkstatic void
285105541Sphkcmd_detach(const char *dest)
286105513Sphk{
287112877Sphk	struct gctl_req *r;
288112877Sphk	const char *errstr;
289112877Sphk	char buf[BUFSIZ];
290105513Sphk
291115624Sphk	r = gctl_get_handle();
292115624Sphk	gctl_ro_param(r, "verb", -1, "destroy geom");
293112877Sphk	gctl_ro_param(r, "class", -1, "BDE");
294112877Sphk	sprintf(buf, "%s.bde", dest);
295112877Sphk	gctl_ro_param(r, "geom", -1, buf);
296112877Sphk	/* gctl_dump(r, stdout); */
297112877Sphk	errstr = gctl_issue(r);
298112877Sphk	if (errstr != NULL)
299125386Sdes		errx(1, "Detach of %s failed: %s", dest, errstr);
300105513Sphk	exit (0);
301105513Sphk}
302105513Sphk
303105513Sphkstatic void
304106407Sphkcmd_open(struct g_bde_softc *sc, int dfd , const char *l_opt, u_int *nkey)
305105513Sphk{
306105513Sphk	int error;
307105513Sphk	int ffd;
308105513Sphk	u_char keyloc[16];
309106407Sphk	u_int sectorsize;
310106407Sphk	off_t mediasize;
311106407Sphk	struct stat st;
312105513Sphk
313106407Sphk	error = ioctl(dfd, DIOCGSECTORSIZE, &sectorsize);
314106407Sphk	if (error)
315106407Sphk		sectorsize = 512;
316106407Sphk	error = ioctl(dfd, DIOCGMEDIASIZE, &mediasize);
317106407Sphk	if (error) {
318106407Sphk		error = fstat(dfd, &st);
319106407Sphk		if (error == 0 && S_ISREG(st.st_mode))
320106407Sphk			mediasize = st.st_size;
321106407Sphk		else
322106407Sphk			error = ENOENT;
323106407Sphk	}
324106407Sphk	if (error)
325106407Sphk		mediasize = (off_t)-1;
326105513Sphk	if (l_opt != NULL) {
327105513Sphk		ffd = open(l_opt, O_RDONLY, 0);
328105513Sphk		if (ffd < 0)
329111296Stjr			err(1, "%s", l_opt);
330105513Sphk		read(ffd, keyloc, sizeof keyloc);
331105513Sphk		close(ffd);
332105513Sphk	} else {
333105513Sphk		memset(keyloc, 0, sizeof keyloc);
334105513Sphk	}
335105513Sphk
336106407Sphk	error = g_bde_decrypt_lock(sc, sc->sha2, keyloc, mediasize,
337106407Sphk	    sectorsize, nkey);
338105513Sphk	if (error == ENOENT)
339105513Sphk		errx(1, "Lock was destroyed.");
340105513Sphk	if (error == ESRCH)
341105513Sphk		errx(1, "Lock was nuked.");
342105513Sphk	if (error == ENOTDIR)
343105513Sphk		errx(1, "Lock not found");
344105513Sphk	if (error != 0)
345105513Sphk		errx(1, "Error %d decrypting lock", error);
346105513Sphk	if (nkey)
347105513Sphk		printf("Opened with key %u\n", *nkey);
348105513Sphk	return;
349105513Sphk}
350105513Sphk
351105513Sphkstatic void
352105513Sphkcmd_nuke(struct g_bde_key *gl, int dfd , int key)
353105513Sphk{
354105513Sphk	int i;
355105513Sphk	u_char *sbuf;
356105513Sphk	off_t offset, offset2;
357105513Sphk
358105513Sphk	sbuf = malloc(gl->sectorsize);
359105513Sphk	memset(sbuf, 0, gl->sectorsize);
360105513Sphk	offset = (gl->lsector[key] & ~(gl->sectorsize - 1));
361105513Sphk	offset2 = lseek(dfd, offset, SEEK_SET);
362105513Sphk	if (offset2 != offset)
363105513Sphk		err(1, "lseek");
364105513Sphk	i = write(dfd, sbuf, gl->sectorsize);
365105513Sphk	if (i != (int)gl->sectorsize)
366105513Sphk		err(1, "write");
367105513Sphk	printf("Nuked key %d\n", key);
368105513Sphk}
369105513Sphk
370105513Sphkstatic void
371105513Sphkcmd_write(struct g_bde_key *gl, struct g_bde_softc *sc, int dfd , int key, const char *l_opt)
372105513Sphk{
373105513Sphk	int i, ffd;
374105513Sphk	uint64_t off[2];
375105513Sphk	u_char keyloc[16];
376105513Sphk	u_char *sbuf, *q;
377105513Sphk	off_t offset, offset2;
378105513Sphk
379105513Sphk	sbuf = malloc(gl->sectorsize);
380105513Sphk	/*
381105513Sphk	 * Find the byte-offset in the lock sector where we will put the lock
382105513Sphk	 * data structure.  We can put it any random place as long as the
383105513Sphk	 * structure fits.
384105513Sphk	 */
385105513Sphk	for(;;) {
386105513Sphk		random_bits(off, sizeof off);
387105513Sphk		off[0] &= (gl->sectorsize - 1);
388105513Sphk		if (off[0] + G_BDE_LOCKSIZE > gl->sectorsize)
389105513Sphk			continue;
390105513Sphk		break;
391105513Sphk	}
392105513Sphk
393105513Sphk	/* Add the sector offset in bytes */
394105513Sphk	off[0] += (gl->lsector[key] & ~(gl->sectorsize - 1));
395105513Sphk	gl->lsector[key] = off[0];
396105513Sphk
397120876Sphk	i = g_bde_keyloc_encrypt(sc->sha2, off[0], off[1], keyloc);
398105513Sphk	if (i)
399105513Sphk		errx(1, "g_bde_keyloc_encrypt()");
400105513Sphk	if (l_opt != NULL) {
401105513Sphk		ffd = open(l_opt, O_WRONLY | O_CREAT | O_TRUNC, 0600);
402105513Sphk		if (ffd < 0)
403111296Stjr			err(1, "%s", l_opt);
404105513Sphk		write(ffd, keyloc, sizeof keyloc);
405105513Sphk		close(ffd);
406120876Sphk	} else if (gl->flags & GBDE_F_SECT0) {
407105513Sphk		offset2 = lseek(dfd, 0, SEEK_SET);
408105513Sphk		if (offset2 != 0)
409105513Sphk			err(1, "lseek");
410105513Sphk		i = read(dfd, sbuf, gl->sectorsize);
411105513Sphk		if (i != (int)gl->sectorsize)
412105513Sphk			err(1, "read");
413105513Sphk		memcpy(sbuf + key * 16, keyloc, sizeof keyloc);
414105513Sphk		offset2 = lseek(dfd, 0, SEEK_SET);
415105513Sphk		if (offset2 != 0)
416105513Sphk			err(1, "lseek");
417105513Sphk		i = write(dfd, sbuf, gl->sectorsize);
418105513Sphk		if (i != (int)gl->sectorsize)
419105513Sphk			err(1, "write");
420105513Sphk	} else {
421105513Sphk		errx(1, "No -L option and no space in sector 0 for lockfile");
422105513Sphk	}
423105513Sphk
424105513Sphk	/* Allocate a sectorbuffer and fill it with random junk */
425105513Sphk	if (sbuf == NULL)
426105513Sphk		err(1, "malloc");
427105513Sphk	random_bits(sbuf, gl->sectorsize);
428105513Sphk
429105513Sphk	/* Fill random bits in the spare field */
430105513Sphk	random_bits(gl->spare, sizeof(gl->spare));
431105513Sphk
432105513Sphk	/* Encode the structure where we want it */
433105513Sphk	q = sbuf + (off[0] % gl->sectorsize);
434120876Sphk	i = g_bde_encode_lock(sc->sha2, gl, q);
435106407Sphk	if (i < 0)
436106407Sphk		errx(1, "programming error encoding lock");
437105513Sphk
438106407Sphk	encrypt_sector(q, G_BDE_LOCKSIZE, 256, sc->sha2 + 16);
439105513Sphk	offset = gl->lsector[key] & ~(gl->sectorsize - 1);
440105513Sphk	offset2 = lseek(dfd, offset, SEEK_SET);
441105513Sphk	if (offset2 != offset)
442105513Sphk		err(1, "lseek");
443105513Sphk	i = write(dfd, sbuf, gl->sectorsize);
444105513Sphk	if (i != (int)gl->sectorsize)
445105513Sphk		err(1, "write");
446105513Sphk	printf("Wrote key %d at %jd\n", key, (intmax_t)offset);
447108052Sphk#if 0
448108052Sphk	printf("s0 = %jd\n", (intmax_t)gl->sector0);
449108052Sphk	printf("sN = %jd\n", (intmax_t)gl->sectorN);
450108052Sphk	printf("l[0] = %jd\n", (intmax_t)gl->lsector[0]);
451108052Sphk	printf("l[1] = %jd\n", (intmax_t)gl->lsector[1]);
452108052Sphk	printf("l[2] = %jd\n", (intmax_t)gl->lsector[2]);
453108052Sphk	printf("l[3] = %jd\n", (intmax_t)gl->lsector[3]);
454108052Sphk	printf("k = %jd\n", (intmax_t)gl->keyoffset);
455108052Sphk	printf("ss = %jd\n", (intmax_t)gl->sectorsize);
456108052Sphk#endif
457105513Sphk}
458105513Sphk
459105513Sphkstatic void
460105513Sphkcmd_destroy(struct g_bde_key *gl, int nkey)
461105513Sphk{
462105513Sphk	int i;
463105513Sphk
464105513Sphk	bzero(&gl->sector0, sizeof gl->sector0);
465105513Sphk	bzero(&gl->sectorN, sizeof gl->sectorN);
466105513Sphk	bzero(&gl->keyoffset, sizeof gl->keyoffset);
467105513Sphk	bzero(&gl->flags, sizeof gl->flags);
468106227Sphk	bzero(gl->mkey, sizeof gl->mkey);
469105513Sphk	for (i = 0; i < G_BDE_MAXKEYS; i++)
470105513Sphk		if (i != nkey)
471105513Sphk			gl->lsector[i] = ~0;
472105513Sphk}
473105513Sphk
474108052Sphkstatic int
475108052Sphksorthelp(const void *a, const void *b)
476108052Sphk{
477108052Sphk	const off_t *oa, *ob;
478108052Sphk
479108052Sphk	oa = a;
480108052Sphk	ob = b;
481113466Sphk	return (*oa > *ob);
482108052Sphk}
483108052Sphk
484105513Sphkstatic void
485105513Sphkcmd_init(struct g_bde_key *gl, int dfd, const char *f_opt, int i_opt, const char *l_opt)
486105513Sphk{
487105513Sphk	int i;
488105513Sphk	u_char *buf;
489105513Sphk	unsigned sector_size;
490105513Sphk	uint64_t	first_sector;
491105513Sphk	uint64_t	last_sector;
492105513Sphk	uint64_t	total_sectors;
493105513Sphk	off_t	off, off2;
494105513Sphk	unsigned nkeys;
495105513Sphk	const char *p;
496105513Sphk	char *q, cbuf[BUFSIZ];
497105513Sphk	unsigned u, u2;
498105513Sphk	uint64_t o;
499105513Sphk	properties	params;
500105513Sphk
501105513Sphk	bzero(gl, sizeof *gl);
502105513Sphk	if (f_opt != NULL) {
503105513Sphk		i = open(f_opt, O_RDONLY);
504105513Sphk		if (i < 0)
505111296Stjr			err(1, "%s", f_opt);
506105513Sphk		params = properties_read(i);
507105513Sphk		close (i);
508105513Sphk	} else {
509105513Sphk		/* XXX: Polish */
510105513Sphk		q = strdup("/tmp/temp.XXXXXXXXXX");
511105513Sphk		i = mkstemp(q);
512105513Sphk		if (i < 0)
513111296Stjr			err(1, "%s", q);
514105513Sphk		write(i, template, strlen(template));
515105513Sphk		close (i);
516105513Sphk		if (i_opt) {
517105513Sphk			p = getenv("EDITOR");
518105513Sphk			if (p == NULL)
519105513Sphk				p = "vi";
520111298Stjr			if (snprintf(cbuf, sizeof(cbuf), "%s %s\n", p, q) >=
521111298Stjr			    (ssize_t)sizeof(cbuf))
522111298Stjr				errx(1, "EDITOR is too long");
523105513Sphk			system(cbuf);
524105513Sphk		}
525105513Sphk		i = open(q, O_RDONLY);
526105513Sphk		if (i < 0)
527111296Stjr			err(1, "%s", f_opt);
528105513Sphk		params = properties_read(i);
529105513Sphk		close (i);
530105513Sphk		unlink(q);
531105513Sphk	}
532105513Sphk
533105513Sphk	/* <sector_size> */
534105513Sphk	p = property_find(params, "sector_size");
535105513Sphk	i = ioctl(dfd, DIOCGSECTORSIZE, &u);
536105513Sphk	if (p != NULL) {
537105513Sphk		sector_size = strtoul(p, &q, 0);
538105513Sphk		if (!*p || *q)
539105513Sphk			errx(1, "sector_size not a proper number");
540108020Sphk	} else if (i == 0) {
541108020Sphk		sector_size = u;
542108020Sphk	} else {
543108020Sphk		errx(1, "Missing sector_size property");
544105513Sphk	}
545105513Sphk	if (sector_size & (sector_size - 1))
546105513Sphk		errx(1, "sector_size not a power of 2");
547105513Sphk	if (sector_size < 512)
548105513Sphk		errx(1, "sector_size is smaller than 512");
549105513Sphk	buf = malloc(sector_size);
550105513Sphk	if (buf == NULL)
551105513Sphk		err(1, "Failed to malloc sector buffer");
552105513Sphk	gl->sectorsize = sector_size;
553105513Sphk
554105513Sphk	i = ioctl(dfd, DIOCGMEDIASIZE, &off);
555105513Sphk	if (i == 0) {
556105513Sphk		first_sector = 0;
557105513Sphk		total_sectors = off / sector_size;
558105513Sphk		last_sector = total_sectors - 1;
559105513Sphk	} else {
560105513Sphk		first_sector = 0;
561105513Sphk		last_sector = 0;
562105513Sphk		total_sectors = 0;
563105513Sphk	}
564105513Sphk
565105513Sphk	/* <first_sector> */
566105513Sphk	p = property_find(params, "first_sector");
567105513Sphk	if (p != NULL) {
568105513Sphk		first_sector = strtoul(p, &q, 0);
569105513Sphk		if (!*p || *q)
570105513Sphk			errx(1, "first_sector not a proper number");
571105513Sphk	}
572105513Sphk
573105513Sphk	/* <last_sector> */
574105513Sphk	p = property_find(params, "last_sector");
575105513Sphk	if (p != NULL) {
576105513Sphk		last_sector = strtoul(p, &q, 0);
577105513Sphk		if (!*p || *q)
578105513Sphk			errx(1, "last_sector not a proper number");
579105513Sphk		if (last_sector <= first_sector)
580105513Sphk			errx(1, "last_sector not larger than first_sector");
581105513Sphk		total_sectors = last_sector + 1;
582105513Sphk	}
583105513Sphk
584105513Sphk	/* <total_sectors> */
585105513Sphk	p = property_find(params, "total_sectors");
586105513Sphk	if (p != NULL) {
587105513Sphk		total_sectors = strtoul(p, &q, 0);
588105513Sphk		if (!*p || *q)
589105513Sphk			errx(1, "total_sectors not a proper number");
590125387Sdes		if (last_sector == 0)
591105513Sphk			last_sector = first_sector + total_sectors - 1;
592105513Sphk	}
593105513Sphk
594105513Sphk	if (l_opt == NULL && first_sector != 0)
595105513Sphk		errx(1, "No -L new-lockfile argument and first_sector != 0");
596105513Sphk	else if (l_opt == NULL) {
597105513Sphk		first_sector++;
598105513Sphk		total_sectors--;
599120876Sphk		gl->flags |= GBDE_F_SECT0;
600105513Sphk	}
601108060Sphk	gl->sector0 = first_sector * gl->sectorsize;
602105513Sphk
603105513Sphk	if (total_sectors != (last_sector - first_sector) + 1)
604105513Sphk		errx(1, "total_sectors disagree with first_sector and last_sector");
605105513Sphk	if (total_sectors == 0)
606105513Sphk		errx(1, "missing last_sector or total_sectors");
607105513Sphk
608105513Sphk	gl->sectorN = (last_sector + 1) * gl->sectorsize;
609105513Sphk
610105513Sphk	/* Find a random keyoffset */
611105513Sphk	random_bits(&o, sizeof o);
612105513Sphk	o %= (gl->sectorN - gl->sector0);
613105513Sphk	o &= ~(gl->sectorsize - 1);
614105513Sphk	gl->keyoffset = o;
615105513Sphk
616105513Sphk	/* <number_of_keys> */
617105513Sphk	p = property_find(params, "number_of_keys");
618105513Sphk	if (p == NULL)
619105513Sphk		errx(1, "Missing number_of_keys property");
620105513Sphk	nkeys = strtoul(p, &q, 0);
621105513Sphk	if (!*p || *q)
622105513Sphk		errx(1, "number_of_keys not a proper number");
623105513Sphk	if (nkeys < 1 || nkeys > G_BDE_MAXKEYS)
624105513Sphk		errx(1, "number_of_keys out of range");
625105513Sphk	for (u = 0; u < nkeys; u++) {
626105513Sphk		for(;;) {
627105513Sphk			do {
628105513Sphk				random_bits(&o, sizeof o);
629105513Sphk				o %= gl->sectorN;
630105513Sphk				o &= ~(gl->sectorsize - 1);
631105513Sphk			} while(o < gl->sector0);
632105513Sphk			for (u2 = 0; u2 < u; u2++)
633105513Sphk				if (o == gl->lsector[u2])
634105513Sphk					break;
635105513Sphk			if (u2 < u)
636105513Sphk				continue;
637105513Sphk			break;
638105513Sphk		}
639105513Sphk		gl->lsector[u] = o;
640125387Sdes	}
641105513Sphk	for (; u < G_BDE_MAXKEYS; u++) {
642125387Sdes		do
643105513Sphk			random_bits(&o, sizeof o);
644105513Sphk		while (o < gl->sectorN);
645105513Sphk		gl->lsector[u] = o;
646105513Sphk	}
647108052Sphk	qsort(gl->lsector, G_BDE_MAXKEYS, sizeof gl->lsector[0], sorthelp);
648105513Sphk
649105513Sphk	/* Flush sector zero if we use it for lockfile data */
650120876Sphk	if (gl->flags & GBDE_F_SECT0) {
651105513Sphk		off2 = lseek(dfd, 0, SEEK_SET);
652105513Sphk		if (off2 != 0)
653105513Sphk			err(1, "lseek(2) to sector 0");
654105513Sphk		random_bits(buf, sector_size);
655105513Sphk		i = write(dfd, buf, sector_size);
656105513Sphk		if (i != (int)sector_size)
657105513Sphk			err(1, "write sector 0");
658105513Sphk	}
659105513Sphk
660105513Sphk	/* <random_flush> */
661105513Sphk	p = property_find(params, "random_flush");
662105513Sphk	if (p != NULL) {
663105513Sphk		off = first_sector * sector_size;
664105513Sphk		off2 = lseek(dfd, off, SEEK_SET);
665105513Sphk		if (off2 != off)
666105513Sphk			err(1, "lseek(2) to first_sector");
667105513Sphk		off2 = last_sector * sector_size;
668105513Sphk		while (off <= off2) {
669105513Sphk			random_bits(buf, sector_size);
670105513Sphk			i = write(dfd, buf, sector_size);
671105513Sphk			if (i != (int)sector_size)
672105513Sphk				err(1, "write to $device_name");
673105513Sphk			off += sector_size;
674105513Sphk		}
675105513Sphk	}
676105513Sphk
677106227Sphk	random_bits(gl->mkey, sizeof gl->mkey);
678106227Sphk	random_bits(gl->salt, sizeof gl->salt);
679125387Sdes
680105513Sphk	return;
681105513Sphk}
682105513Sphk
683105513Sphkstatic enum action {
684105513Sphk	ACT_HUH,
685105541Sphk	ACT_ATTACH, ACT_DETACH,
686105513Sphk	ACT_INIT, ACT_SETKEY, ACT_DESTROY, ACT_NUKE
687105513Sphk} action;
688105513Sphk
689105513Sphkint
690105513Sphkmain(int argc, char **argv)
691105513Sphk{
692105513Sphk	const char *opts;
693105513Sphk	const char *l_opt, *L_opt;
694105513Sphk	const char *p_opt, *P_opt;
695105513Sphk	const char *f_opt;
696107455Sphk	char *dest;
697107455Sphk	int i_opt, n_opt, ch, dfd, doopen;
698107455Sphk	u_int nkey;
699105513Sphk	int i;
700107455Sphk	char *q, buf[BUFSIZ];
701105513Sphk	struct g_bde_key *gl;
702105513Sphk	struct g_bde_softc sc;
703105513Sphk
704105513Sphk	if (argc < 3)
705105513Sphk		usage("Too few arguments\n");
706105513Sphk
707120969Sphk       if ((i = modfind("g_bde")) < 0) {
708125387Sdes	       /* need to load the gbde module */
709125387Sdes	       if (kldload(GBDEMOD) < 0 || modfind("g_bde") < 0) {
710125387Sdes		       usage(GBDEMOD ": Kernel module not available");
711125387Sdes	       }
712120877Sphk       }
713105513Sphk	doopen = 0;
714105513Sphk	if (!strcmp(argv[1], "attach")) {
715105513Sphk		action = ACT_ATTACH;
716105513Sphk		opts = "l:p:";
717105541Sphk	} else if (!strcmp(argv[1], "detach")) {
718105541Sphk		action = ACT_DETACH;
719105513Sphk		opts = "";
720105513Sphk	} else if (!strcmp(argv[1], "init")) {
721105513Sphk		action = ACT_INIT;
722105513Sphk		doopen = 1;
723105513Sphk		opts = "f:iL:P:";
724105513Sphk	} else if (!strcmp(argv[1], "setkey")) {
725105513Sphk		action = ACT_SETKEY;
726105513Sphk		doopen = 1;
727105513Sphk		opts = "n:l:L:p:P:";
728105513Sphk	} else if (!strcmp(argv[1], "destroy")) {
729105513Sphk		action = ACT_DESTROY;
730105513Sphk		doopen = 1;
731105513Sphk		opts = "l:p:";
732105513Sphk	} else if (!strcmp(argv[1], "nuke")) {
733105513Sphk		action = ACT_NUKE;
734105513Sphk		doopen = 1;
735105513Sphk		opts = "l:p:n:";
736105513Sphk	} else {
737105513Sphk		usage("Unknown sub command\n");
738105513Sphk	}
739105513Sphk	argc--;
740105513Sphk	argv++;
741105513Sphk
742107455Sphk	dest = strdup(argv[1]);
743105513Sphk	argc--;
744105513Sphk	argv++;
745105513Sphk
746105513Sphk	p_opt = NULL;
747105513Sphk	P_opt = NULL;
748105513Sphk	l_opt = NULL;
749105513Sphk	L_opt = NULL;
750105513Sphk	f_opt = NULL;
751105513Sphk	n_opt = 0;
752105513Sphk	i_opt = 0;
753105513Sphk
754105513Sphk	while((ch = getopt(argc, argv, opts)) != -1)
755105513Sphk		switch (ch) {
756105513Sphk		case 'f':
757105513Sphk			f_opt = optarg;
758105513Sphk			break;
759105513Sphk		case 'i':
760105513Sphk			i_opt = !i_opt;
761105513Sphk		case 'l':
762105513Sphk			l_opt = optarg;
763105513Sphk			break;
764105513Sphk		case 'L':
765105513Sphk			L_opt = optarg;
766105513Sphk			break;
767105513Sphk		case 'p':
768105513Sphk			p_opt = optarg;
769105513Sphk			break;
770105513Sphk		case 'P':
771105513Sphk			P_opt = optarg;
772105513Sphk			break;
773105513Sphk		case 'n':
774105513Sphk			n_opt = strtoul(optarg, &q, 0);
775105513Sphk			if (!*optarg || *q)
776105513Sphk				usage("-n argument not numeric\n");
777105513Sphk			if (n_opt < -1 || n_opt > G_BDE_MAXKEYS)
778107982Sphk				usage("-n argument out of range\n"); break;
779105513Sphk		default:
780105513Sphk			usage("Invalid option\n");
781105513Sphk		}
782105513Sphk
783105513Sphk	if (doopen) {
784105513Sphk		dfd = open(dest, O_RDWR | O_CREAT, 0644);
785107455Sphk		if (dfd < 0) {
786111298Stjr			if (snprintf(buf, sizeof(buf), "%s%s",
787111298Stjr			    _PATH_DEV, dest) >= (ssize_t)sizeof(buf))
788111298Stjr				errno = ENAMETOOLONG;
789111298Stjr			else
790111298Stjr				dfd = open(buf, O_RDWR | O_CREAT, 0644);
791107455Sphk		}
792105513Sphk		if (dfd < 0)
793111296Stjr			err(1, "%s", dest);
794107455Sphk	} else {
795107455Sphk		if (!memcmp(dest, _PATH_DEV, strlen(_PATH_DEV)))
796107455Sphk			strcpy(dest, dest + strlen(_PATH_DEV));
797107455Sphk		if (strchr(dest, '/'))
798107455Sphk			usage("\"dest\" argument must be geom-name\n");
799105513Sphk	}
800105513Sphk
801105513Sphk	memset(&sc, 0, sizeof sc);
802107982Sphk	sc.consumer = (void *)&dfd;
803105513Sphk	gl = &sc.key;
804105513Sphk	switch(action) {
805105513Sphk	case ACT_ATTACH:
806105513Sphk		setup_passphrase(&sc, 0, p_opt);
807105513Sphk		cmd_attach(&sc, dest, l_opt);
808105513Sphk		break;
809105541Sphk	case ACT_DETACH:
810105541Sphk		cmd_detach(dest);
811105513Sphk		break;
812105513Sphk	case ACT_INIT:
813105513Sphk		cmd_init(gl, dfd, f_opt, i_opt, L_opt);
814105513Sphk		setup_passphrase(&sc, 1, P_opt);
815105513Sphk		cmd_write(gl, &sc, dfd, 0, L_opt);
816105513Sphk		break;
817105513Sphk	case ACT_SETKEY:
818105513Sphk		setup_passphrase(&sc, 0, p_opt);
819105513Sphk		cmd_open(&sc, dfd, l_opt, &nkey);
820105513Sphk		if (n_opt == 0)
821105513Sphk			n_opt = nkey + 1;
822105513Sphk		setup_passphrase(&sc, 1, P_opt);
823105513Sphk		cmd_write(gl, &sc, dfd, n_opt - 1, L_opt);
824105513Sphk		break;
825105513Sphk	case ACT_DESTROY:
826105513Sphk		setup_passphrase(&sc, 0, p_opt);
827105513Sphk		cmd_open(&sc, dfd, l_opt, &nkey);
828105513Sphk		cmd_destroy(gl, nkey);
829105513Sphk		reset_passphrase(&sc);
830105513Sphk		cmd_write(gl, &sc, dfd, nkey, l_opt);
831105513Sphk		break;
832105513Sphk	case ACT_NUKE:
833105513Sphk		setup_passphrase(&sc, 0, p_opt);
834105513Sphk		cmd_open(&sc, dfd, l_opt, &nkey);
835105513Sphk		if (n_opt == 0)
836105513Sphk			n_opt = nkey + 1;
837105513Sphk		if (n_opt == -1) {
838105513Sphk			for(i = 0; i < G_BDE_MAXKEYS; i++)
839105513Sphk				cmd_nuke(gl, dfd, i);
840105513Sphk		} else {
841105513Sphk				cmd_nuke(gl, dfd, n_opt - 1);
842105513Sphk		}
843105513Sphk		break;
844105513Sphk	default:
845105513Sphk		usage("Internal error\n");
846105513Sphk	}
847105513Sphk
848105513Sphk	return(0);
849105513Sphk}
850