geom_eli.c revision 159308
1/*-
2 * Copyright (c) 2004 Pawel Jakub Dawidek <pjd@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sbin/geom/class/eli/geom_eli.c 159308 2006-06-05 21:40:54Z pjd $");
29
30#include <stdio.h>
31#include <stdint.h>
32#include <stdlib.h>
33#include <unistd.h>
34#include <fcntl.h>
35#include <readpassphrase.h>
36#include <string.h>
37#include <strings.h>
38#include <libgeom.h>
39#include <paths.h>
40#include <errno.h>
41#include <assert.h>
42
43#include <sys/param.h>
44#include <sys/mman.h>
45#include <sys/resource.h>
46#include <opencrypto/cryptodev.h>
47#include <geom/eli/g_eli.h>
48#include <geom/eli/pkcs5v2.h>
49
50#include "core/geom.h"
51#include "misc/subr.h"
52
53
54uint32_t lib_version = G_LIB_VERSION;
55uint32_t version = G_ELI_VERSION;
56
57static char aalgo[] = "none";
58static char ealgo[] = "aes";
59static intmax_t keylen = 0;
60static intmax_t keyno = -1;
61static intmax_t iterations = -1;
62static intmax_t sectorsize = 0;
63static char keyfile[] = "", newkeyfile[] = "";
64
65static void eli_main(struct gctl_req *req, unsigned flags);
66static void eli_init(struct gctl_req *req);
67static void eli_attach(struct gctl_req *req);
68static void eli_setkey(struct gctl_req *req);
69static void eli_delkey(struct gctl_req *req);
70static void eli_kill(struct gctl_req *req);
71static void eli_backup(struct gctl_req *req);
72static void eli_restore(struct gctl_req *req);
73static void eli_clear(struct gctl_req *req);
74static void eli_dump(struct gctl_req *req);
75
76/*
77 * Available commands:
78 *
79 * init [-bhPv] [-a aalgo] [-e ealgo] [-i iterations] [-l keylen] [-K newkeyfile] prov
80 * label - alias for 'init'
81 * attach [-dpv] [-k keyfile] prov
82 * detach [-fl] prov ...
83 * stop - alias for 'detach'
84 * onetime [-d] [-a aalgo] [-e ealgo] [-l keylen] prov ...
85 * setkey [-pPv] [-n keyno] [-k keyfile] [-K newkeyfile] prov
86 * delkey [-afv] [-n keyno] prov
87 * kill [-av] [prov ...]
88 * backup [-v] prov file
89 * restore [-v] file prov
90 * clear [-v] prov ...
91 * dump [-v] prov ...
92 */
93struct g_command class_commands[] = {
94	{ "init", G_FLAG_VERBOSE, eli_main,
95	    {
96		{ 'a', "aalgo", aalgo, G_TYPE_STRING },
97		{ 'b', "boot", NULL, G_TYPE_NONE },
98		{ 'e', "ealgo", ealgo, G_TYPE_STRING },
99		{ 'i', "iterations", &iterations, G_TYPE_NUMBER },
100		{ 'K', "newkeyfile", newkeyfile, G_TYPE_STRING },
101		{ 'l', "keylen", &keylen, G_TYPE_NUMBER },
102		{ 'P', "nonewpassphrase", NULL, G_TYPE_NONE },
103		{ 's', "sectorsize", &sectorsize, G_TYPE_NUMBER },
104		G_OPT_SENTINEL
105	    },
106	    "[-bPv] [-a aalgo] [-e ealgo] [-i iterations] [-l keylen] [-K newkeyfile] [-s sectorsize] prov"
107	},
108	{ "label", G_FLAG_VERBOSE, eli_main,
109	    {
110		{ 'a', "aalgo", aalgo, G_TYPE_STRING },
111		{ 'b', "boot", NULL, G_TYPE_NONE },
112		{ 'e', "ealgo", ealgo, G_TYPE_STRING },
113		{ 'i', "iterations", &iterations, G_TYPE_NUMBER },
114		{ 'K', "newkeyfile", newkeyfile, G_TYPE_STRING },
115		{ 'l', "keylen", &keylen, G_TYPE_NUMBER },
116		{ 'P', "nonewpassphrase", NULL, G_TYPE_NONE },
117		{ 's', "sectorsize", &sectorsize, G_TYPE_NUMBER },
118		G_OPT_SENTINEL
119	    },
120	    "- an alias for 'init'"
121	},
122	{ "attach", G_FLAG_VERBOSE | G_FLAG_LOADKLD, eli_main,
123	    {
124		{ 'd', "detach", NULL, G_TYPE_NONE },
125		{ 'k', "keyfile", keyfile, G_TYPE_STRING },
126		{ 'p', "nopassphrase", NULL, G_TYPE_NONE },
127		G_OPT_SENTINEL
128	    },
129	    "[-dpv] [-k keyfile] prov"
130	},
131	{ "detach", 0, NULL,
132	    {
133		{ 'f', "force", NULL, G_TYPE_NONE },
134		{ 'l', "last", NULL, G_TYPE_NONE },
135		G_OPT_SENTINEL
136	    },
137	    "[-fl] prov ..."
138	},
139	{ "stop", 0, NULL,
140	    {
141		{ 'f', "force", NULL, G_TYPE_NONE },
142		{ 'l', "last", NULL, G_TYPE_NONE },
143		G_OPT_SENTINEL
144	    },
145	    "- an alias for 'detach'"
146	},
147	{ "onetime", G_FLAG_VERBOSE | G_FLAG_LOADKLD, NULL,
148	    {
149		{ 'a', "aalgo", aalgo, G_TYPE_STRING },
150		{ 'd', "detach", NULL, G_TYPE_NONE },
151		{ 'e', "ealgo", ealgo, G_TYPE_STRING },
152		{ 'l', "keylen", &keylen, G_TYPE_NUMBER },
153		{ 's', "sectorsize", &sectorsize, G_TYPE_NUMBER },
154		G_OPT_SENTINEL
155	    },
156	    "[-d] [-a aalgo] [-e ealgo] [-l keylen] [-s sectorsize] prov ..."
157	},
158	{ "setkey", G_FLAG_VERBOSE, eli_main,
159	    {
160		{ 'i', "iterations", &iterations, G_TYPE_NUMBER },
161		{ 'k', "keyfile", keyfile, G_TYPE_STRING },
162		{ 'K', "newkeyfile", newkeyfile, G_TYPE_STRING },
163		{ 'n', "keyno", &keyno, G_TYPE_NUMBER },
164		{ 'p', "nopassphrase", NULL, G_TYPE_NONE },
165		{ 'P', "nonewpassphrase", NULL, G_TYPE_NONE },
166		G_OPT_SENTINEL
167	    },
168	    "[-pPv] [-n keyno] [-i iterations] [-k keyfile] [-K newkeyfile] prov"
169	},
170	{ "delkey", G_FLAG_VERBOSE, eli_main,
171	    {
172		{ 'a', "all", NULL, G_TYPE_NONE },
173		{ 'f', "force", NULL, G_TYPE_NONE },
174		{ 'n', "keyno", &keyno, G_TYPE_NUMBER },
175		G_OPT_SENTINEL
176	    },
177	    "[-afv] [-n keyno] prov"
178	},
179	{ "kill", G_FLAG_VERBOSE, eli_main,
180	    {
181		{ 'a', "all", NULL, G_TYPE_NONE },
182		G_OPT_SENTINEL
183	    },
184	    "[-av] [prov ...]"
185	},
186	{ "backup", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS,
187	    "[-v] prov file"
188	},
189	{ "restore", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS,
190	    "[-v] file prov"
191	},
192	{ "clear", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS,
193	    "[-v] prov ..."
194	},
195	{ "dump", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS,
196	    "[-v] prov ..."
197	},
198	G_CMD_SENTINEL
199};
200
201static int verbose = 0;
202
203static int
204eli_protect(struct gctl_req *req)
205{
206	struct rlimit rl;
207
208	/* Disable core dumps. */
209	rl.rlim_cur = 0;
210	rl.rlim_max = 0;
211	if (setrlimit(RLIMIT_CORE, &rl) == -1) {
212		gctl_error(req, "Cannot disable core dumps: %s.",
213		    strerror(errno));
214		return (-1);
215	}
216	/* Disable swapping. */
217	if (mlockall(MCL_FUTURE) == -1) {
218		gctl_error(req, "Cannot lock memory: %s.", strerror(errno));
219		return (-1);
220	}
221	return (0);
222}
223
224static void
225eli_main(struct gctl_req *req, unsigned flags)
226{
227	const char *name;
228
229	if (eli_protect(req) == -1)
230		return;
231
232	if ((flags & G_FLAG_VERBOSE) != 0)
233		verbose = 1;
234
235	name = gctl_get_ascii(req, "verb");
236	if (name == NULL) {
237		gctl_error(req, "No '%s' argument.", "verb");
238		return;
239	}
240	if (strcmp(name, "init") == 0 || strcmp(name, "label") == 0)
241		eli_init(req);
242	else if (strcmp(name, "attach") == 0)
243		eli_attach(req);
244	else if (strcmp(name, "setkey") == 0)
245		eli_setkey(req);
246	else if (strcmp(name, "delkey") == 0)
247		eli_delkey(req);
248	else if (strcmp(name, "kill") == 0)
249		eli_kill(req);
250	else if (strcmp(name, "backup") == 0)
251		eli_backup(req);
252	else if (strcmp(name, "restore") == 0)
253		eli_restore(req);
254	else if (strcmp(name, "dump") == 0)
255		eli_dump(req);
256	else if (strcmp(name, "clear") == 0)
257		eli_clear(req);
258	else
259		gctl_error(req, "Unknown command: %s.", name);
260}
261
262static void
263arc4rand(unsigned char *buf, size_t size)
264{
265	uint32_t *buf4;
266	size_t size4;
267	unsigned i;
268
269	buf4 = (uint32_t *)buf;
270	size4 = size / 4;
271
272	for (i = 0; i < size4; i++)
273		buf4[i] = arc4random();
274	for (i *= 4; i < size; i++)
275		buf[i] = arc4random() % 0xff;
276}
277
278static int
279eli_is_attached(const char *prov)
280{
281	char name[MAXPATHLEN];
282	unsigned secsize;
283
284	/*
285	 * Not the best way to do it, but the easiest.
286	 * We try to open provider and check if it is a GEOM provider
287	 * by asking about its sectorsize.
288	 */
289	snprintf(name, sizeof(name), "%s%s", prov, G_ELI_SUFFIX);
290	secsize = g_get_sectorsize(name);
291	if (secsize > 0)
292		return (1);
293	return (0);
294}
295
296static unsigned char *
297eli_genkey(struct gctl_req *req, struct g_eli_metadata *md, unsigned char *key,
298    int new)
299{
300	struct hmac_ctx ctx;
301	const char *str;
302	int error, nopassphrase;
303
304	nopassphrase =
305	    gctl_get_int(req, new ? "nonewpassphrase" : "nopassphrase");
306
307	g_eli_crypto_hmac_init(&ctx, NULL, 0);
308
309	str = gctl_get_ascii(req, new ? "newkeyfile" : "keyfile");
310	if (str[0] == '\0' && nopassphrase) {
311		gctl_error(req, "No key components given.");
312		return (NULL);
313	} else if (str[0] != '\0') {
314		char buf[MAXPHYS];
315		ssize_t done;
316		int fd;
317
318		if (strcmp(str, "-") == 0)
319			fd = STDIN_FILENO;
320		else {
321			fd = open(str, O_RDONLY);
322			if (fd == -1) {
323				gctl_error(req, "Cannot open keyfile %s: %s.",
324				    str, strerror(errno));
325				return (NULL);
326			}
327		}
328		while ((done = read(fd, buf, sizeof(buf))) > 0)
329			g_eli_crypto_hmac_update(&ctx, buf, done);
330		error = errno;
331		if (strcmp(str, "-") != 0)
332			close(fd);
333		bzero(buf, sizeof(buf));
334		if (done == -1) {
335			gctl_error(req, "Cannot read keyfile %s: %s.", str,
336			    strerror(error));
337			return (NULL);
338		}
339	}
340
341	if (!nopassphrase) {
342		char buf1[BUFSIZ], buf2[BUFSIZ], *p;
343
344		if (!new && md->md_iterations == -1) {
345			gctl_error(req, "Missing -p flag.");
346			return (NULL);
347		}
348		for (;;) {
349			p = readpassphrase(
350			    new ? "Enter new passphrase:" : "Enter passphrase:",
351			    buf1, sizeof(buf1), RPP_ECHO_OFF | RPP_REQUIRE_TTY);
352			if (p == NULL) {
353				bzero(buf1, sizeof(buf1));
354				gctl_error(req, "Cannot read passphrase: %s.",
355				    strerror(errno));
356				return (NULL);
357			}
358
359			if (new) {
360				p = readpassphrase("Reenter new passphrase: ",
361				    buf2, sizeof(buf2),
362				    RPP_ECHO_OFF | RPP_REQUIRE_TTY);
363				if (p == NULL) {
364					bzero(buf1, sizeof(buf1));
365					gctl_error(req,
366					    "Cannot read passphrase: %s.",
367					    strerror(errno));
368					return (NULL);
369				}
370
371				if (strcmp(buf1, buf2) != 0) {
372					bzero(buf2, sizeof(buf2));
373					fprintf(stderr, "They didn't match.\n");
374					continue;
375				}
376				bzero(buf2, sizeof(buf2));
377			}
378			break;
379		}
380		/*
381		 * Field md_iterations equal to -1 means "choose some sane
382		 * value for me".
383		 */
384		if (md->md_iterations == -1) {
385			assert(new);
386			if (verbose)
387				printf("Calculating number of iterations...\n");
388			md->md_iterations = pkcs5v2_calculate(2000000);
389			assert(md->md_iterations > 0);
390			if (verbose) {
391				printf("Done, using %d iterations.\n",
392				    md->md_iterations);
393			}
394		}
395		/*
396		 * If md_iterations is equal to 0, user don't want PKCS5v2.
397		 */
398		if (md->md_iterations == 0) {
399			g_eli_crypto_hmac_update(&ctx, md->md_salt,
400			    sizeof(md->md_salt));
401			g_eli_crypto_hmac_update(&ctx, buf1, strlen(buf1));
402		} else /* if (md->md_iterations > 0) */ {
403			unsigned char dkey[G_ELI_USERKEYLEN];
404
405			pkcs5v2_genkey(dkey, sizeof(dkey), md->md_salt,
406			    sizeof(md->md_salt), buf1, md->md_iterations);
407			g_eli_crypto_hmac_update(&ctx, dkey, sizeof(dkey));
408			bzero(dkey, sizeof(dkey));
409		}
410		bzero(buf1, sizeof(buf1));
411	}
412	g_eli_crypto_hmac_final(&ctx, key, 0);
413	return (key);
414}
415
416static int
417eli_metadata_read(struct gctl_req *req, const char *prov,
418    struct g_eli_metadata *md)
419{
420	unsigned char sector[sizeof(struct g_eli_metadata)];
421	int error;
422
423	if (g_get_sectorsize(prov) == 0) {
424		int fd;
425
426		/* This is a file probably. */
427		fd = open(prov, O_RDONLY);
428		if (fd == -1) {
429			gctl_error(req, "Cannot open %s: %s.", prov,
430			    strerror(errno));
431			return (-1);
432		}
433		if (read(fd, sector, sizeof(sector)) != sizeof(sector)) {
434			gctl_error(req, "Cannot read metadata from %s: %s.",
435			    prov, strerror(errno));
436			close(fd);
437			return (-1);
438		}
439		close(fd);
440	} else {
441		/* This is a GEOM provider. */
442		error = g_metadata_read(prov, sector, sizeof(sector),
443		    G_ELI_MAGIC);
444		if (error != 0) {
445			gctl_error(req, "Cannot read metadata from %s: %s.",
446			    prov, strerror(error));
447			return (-1);
448		}
449	}
450	if (eli_metadata_decode(sector, md) != 0) {
451		gctl_error(req, "MD5 hash mismatch for %s.", prov);
452		return (-1);
453	}
454	return (0);
455}
456
457static int
458eli_metadata_store(struct gctl_req *req, const char *prov,
459    struct g_eli_metadata *md)
460{
461	unsigned char sector[sizeof(struct g_eli_metadata)];
462	int error;
463
464	eli_metadata_encode(md, sector);
465	if (g_get_sectorsize(prov) == 0) {
466		int fd;
467
468		/* This is a file probably. */
469		fd = open(prov, O_WRONLY | O_TRUNC);
470		if (fd == -1) {
471			gctl_error(req, "Cannot open %s: %s.", prov,
472			    strerror(errno));
473			bzero(sector, sizeof(sector));
474			return (-1);
475		}
476		if (write(fd, sector, sizeof(sector)) != sizeof(sector)) {
477			gctl_error(req, "Cannot write metadata to %s: %s.",
478			    prov, strerror(errno));
479			bzero(sector, sizeof(sector));
480			close(fd);
481			return (-1);
482		}
483		close(fd);
484	} else {
485		/* This is a GEOM provider. */
486		error = g_metadata_store(prov, sector, sizeof(sector));
487		if (error != 0) {
488			gctl_error(req, "Cannot write metadata to %s: %s.",
489			    prov, strerror(errno));
490			bzero(sector, sizeof(sector));
491			return (-1);
492		}
493	}
494	bzero(sector, sizeof(sector));
495	return (0);
496}
497
498static void
499eli_init(struct gctl_req *req)
500{
501	struct g_eli_metadata md;
502	unsigned char sector[sizeof(struct g_eli_metadata)];
503	unsigned char key[G_ELI_USERKEYLEN];
504	const char *str, *prov;
505	unsigned secsize;
506	off_t mediasize;
507	intmax_t val;
508	int error, nargs;
509
510	nargs = gctl_get_int(req, "nargs");
511	if (nargs != 1) {
512		gctl_error(req, "Invalid number of arguments.");
513		return;
514	}
515	prov = gctl_get_ascii(req, "arg0");
516	mediasize = g_get_mediasize(prov);
517	secsize = g_get_sectorsize(prov);
518	if (mediasize == 0 || secsize == 0) {
519		gctl_error(req, "Cannot get informations about %s: %s.", prov,
520		    strerror(errno));
521		return;
522	}
523
524	bzero(&md, sizeof(md));
525	strlcpy(md.md_magic, G_ELI_MAGIC, sizeof(md.md_magic));
526	md.md_version = G_ELI_VERSION;
527	md.md_flags = 0;
528	if (gctl_get_int(req, "boot"))
529		md.md_flags |= G_ELI_FLAG_BOOT;
530	str = gctl_get_ascii(req, "aalgo");
531	if (strcmp(str, "none") != 0) {
532		md.md_aalgo = g_eli_str2aalgo(str);
533		if (md.md_aalgo < CRYPTO_ALGORITHM_MIN ||
534		    md.md_aalgo > CRYPTO_ALGORITHM_MAX) {
535			gctl_error(req, "Invalid authentication algorithm.");
536			return;
537		}
538		md.md_flags |= G_ELI_FLAG_AUTH;
539	}
540	str = gctl_get_ascii(req, "ealgo");
541	md.md_ealgo = g_eli_str2ealgo(str);
542	if (md.md_ealgo < CRYPTO_ALGORITHM_MIN ||
543	    md.md_ealgo > CRYPTO_ALGORITHM_MAX) {
544		gctl_error(req, "Invalid encryption algorithm.");
545		return;
546	}
547	val = gctl_get_intmax(req, "keylen");
548	md.md_keylen = val;
549	md.md_keylen = g_eli_keylen(md.md_ealgo, md.md_keylen);
550	if (md.md_keylen == 0) {
551		gctl_error(req, "Invalid key length.");
552		return;
553	}
554	md.md_provsize = mediasize;
555
556	val = gctl_get_intmax(req, "iterations");
557	if (val != -1) {
558		int nonewpassphrase;
559
560		/*
561		 * Don't allow to set iterations when there will be no
562		 * passphrase.
563		 */
564		nonewpassphrase = gctl_get_int(req, "nonewpassphrase");
565		if (nonewpassphrase) {
566			gctl_error(req,
567			    "Options -i and -P are mutually exclusive.");
568			return;
569		}
570	}
571	md.md_iterations = val;
572
573	val = gctl_get_intmax(req, "sectorsize");
574	if (val == 0)
575		md.md_sectorsize = secsize;
576	else {
577		if (val < 0 || (val % secsize) != 0) {
578			gctl_error(req, "Invalid sector size.");
579			return;
580		}
581		md.md_sectorsize = val;
582	}
583
584	md.md_keys = 0x01;
585	arc4rand(md.md_salt, sizeof(md.md_salt));
586	arc4rand(md.md_mkeys, sizeof(md.md_mkeys));
587
588	/* Generate user key. */
589	if (eli_genkey(req, &md, key, 1) == NULL) {
590		bzero(key, sizeof(key));
591		bzero(&md, sizeof(md));
592		return;
593	}
594
595	/* Encrypt the first and the only Master Key. */
596	error = g_eli_mkey_encrypt(md.md_ealgo, key, md.md_keylen, md.md_mkeys);
597	bzero(key, sizeof(key));
598	if (error != 0) {
599		bzero(&md, sizeof(md));
600		gctl_error(req, "Cannot encrypt Master Key: %s.",
601		    strerror(error));
602		return;
603	}
604
605	eli_metadata_encode(&md, sector);
606	bzero(&md, sizeof(md));
607	error = g_metadata_store(prov, sector, sizeof(sector));
608	bzero(sector, sizeof(sector));
609	if (error != 0) {
610		gctl_error(req, "Cannot store metadata on %s: %s.", prov,
611		    strerror(error));
612		return;
613	}
614	if (verbose)
615		printf("Metadata value stored on %s.\n", prov);
616}
617
618static void
619eli_attach(struct gctl_req *req)
620{
621	struct g_eli_metadata md;
622	unsigned char key[G_ELI_USERKEYLEN];
623	const char *prov;
624	int nargs;
625
626	nargs = gctl_get_int(req, "nargs");
627	if (nargs != 1) {
628		gctl_error(req, "Invalid number of arguments.");
629		return;
630	}
631	prov = gctl_get_ascii(req, "arg0");
632
633	if (eli_metadata_read(req, prov, &md) == -1)
634		return;
635
636	if (eli_genkey(req, &md, key, 0) == NULL) {
637		bzero(key, sizeof(key));
638		return;
639	}
640
641	gctl_ro_param(req, "key", sizeof(key), key);
642	if (gctl_issue(req) == NULL) {
643		if (verbose)
644			printf("Attched to %s.\n", prov);
645	}
646	bzero(key, sizeof(key));
647}
648
649static void
650eli_setkey_attached(struct gctl_req *req, struct g_eli_metadata *md)
651{
652	unsigned char key[G_ELI_USERKEYLEN];
653	intmax_t val;
654
655	val = gctl_get_intmax(req, "iterations");
656	/* Check if iterations number should be changed. */
657	if (val != -1)
658		md->md_iterations = val;
659
660	/* Generate key for Master Key encryption. */
661	if (eli_genkey(req, md, key, 1) == NULL) {
662		bzero(key, sizeof(key));
663		return;
664	}
665
666	gctl_ro_param(req, "key", sizeof(key), key);
667	gctl_issue(req);
668	bzero(key, sizeof(key));
669}
670
671static void
672eli_setkey_detached(struct gctl_req *req, const char *prov,
673 struct g_eli_metadata *md)
674{
675	unsigned char key[G_ELI_USERKEYLEN], mkey[G_ELI_DATAIVKEYLEN];
676	unsigned char *mkeydst;
677	intmax_t val;
678	unsigned nkey;
679	int error;
680
681	if (md->md_keys == 0) {
682		gctl_error(req, "No valid keys on %s.", prov);
683		return;
684	}
685
686	/* Generate key for Master Key decryption. */
687	if (eli_genkey(req, md, key, 0) == NULL) {
688		bzero(key, sizeof(key));
689		return;
690	}
691
692	/* Decrypt Master Key. */
693	error = g_eli_mkey_decrypt(md, key, mkey, &nkey);
694	bzero(key, sizeof(key));
695	if (error != 0) {
696		bzero(md, sizeof(*md));
697		if (error == -1)
698			gctl_error(req, "Wrong key for %s.", prov);
699		else /* if (error > 0) */ {
700			gctl_error(req, "Cannot decrypt Master Key: %s.",
701			    strerror(error));
702		}
703		return;
704	}
705	if (verbose)
706		printf("Decrypted Master Key %u.\n", nkey);
707
708	val = gctl_get_intmax(req, "keyno");
709	if (val != -1)
710		nkey = val;
711#if 0
712	else
713		; /* Use the key number which was found during decryption. */
714#endif
715	if (nkey >= G_ELI_MAXMKEYS) {
716		gctl_error(req, "Invalid '%s' argument.", "keyno");
717		return;
718	}
719
720	val = gctl_get_intmax(req, "iterations");
721	/* Check if iterations number should and can be changed. */
722	if (val != -1) {
723		if (bitcount32(md->md_keys) != 1) {
724			gctl_error(req, "To be able to use '-i' option, only "
725			    "one key can be defined.");
726			return;
727		}
728		if (md->md_keys != (1 << nkey)) {
729			gctl_error(req, "Only already defined key can be "
730			    "changed when '-i' option is used.");
731			return;
732		}
733		md->md_iterations = val;
734	}
735
736	mkeydst = md->md_mkeys + nkey * G_ELI_MKEYLEN;
737	md->md_keys |= (1 << nkey);
738
739	bcopy(mkey, mkeydst, sizeof(mkey));
740	bzero(mkey, sizeof(mkey));
741
742	/* Generate key for Master Key encryption. */
743	if (eli_genkey(req, md, key, 1) == NULL) {
744		bzero(key, sizeof(key));
745		bzero(md, sizeof(*md));
746		return;
747	}
748
749	/* Encrypt the Master-Key with the new key. */
750	error = g_eli_mkey_encrypt(md->md_ealgo, key, md->md_keylen, mkeydst);
751	bzero(key, sizeof(key));
752	if (error != 0) {
753		bzero(md, sizeof(*md));
754		gctl_error(req, "Cannot encrypt Master Key: %s.",
755		    strerror(error));
756		return;
757	}
758
759	/* Store metadata with fresh key. */
760	eli_metadata_store(req, prov, md);
761	bzero(md, sizeof(*md));
762}
763
764static void
765eli_setkey(struct gctl_req *req)
766{
767	struct g_eli_metadata md;
768	const char *prov;
769	int nargs;
770
771	nargs = gctl_get_int(req, "nargs");
772	if (nargs != 1) {
773		gctl_error(req, "Invalid number of arguments.");
774		return;
775	}
776	prov = gctl_get_ascii(req, "arg0");
777
778	if (eli_metadata_read(req, prov, &md) == -1)
779		return;
780
781	if (eli_is_attached(prov))
782		eli_setkey_attached(req, &md);
783	else
784		eli_setkey_detached(req, prov, &md);
785}
786
787static void
788eli_delkey_attached(struct gctl_req *req, const char *prov __unused)
789{
790
791	gctl_issue(req);
792}
793
794static void
795eli_delkey_detached(struct gctl_req *req, const char *prov)
796{
797	struct g_eli_metadata md;
798	unsigned char *mkeydst;
799	intmax_t val;
800	unsigned nkey;
801	int all, force;
802
803	if (eli_metadata_read(req, prov, &md) == -1)
804		return;
805
806	all = gctl_get_int(req, "all");
807	if (all)
808		arc4rand(md.md_mkeys, sizeof(md.md_mkeys));
809	else {
810		force = gctl_get_int(req, "force");
811		val = gctl_get_intmax(req, "keyno");
812		if (val == -1) {
813			gctl_error(req, "Key number has to be specified.");
814			return;
815		}
816		nkey = val;
817		if (nkey >= G_ELI_MAXMKEYS) {
818			gctl_error(req, "Invalid '%s' argument.", "keyno");
819			return;
820		}
821		if (!(md.md_keys & (1 << nkey)) && !force) {
822			gctl_error(req, "Master Key %u is not set.", nkey);
823			return;
824		}
825		md.md_keys &= ~(1 << nkey);
826		if (md.md_keys == 0 && !force) {
827			gctl_error(req, "This is the last Master Key. Use '-f' "
828			    "option if you really want to remove it.");
829			return;
830		}
831		mkeydst = md.md_mkeys + nkey * G_ELI_MKEYLEN;
832		arc4rand(mkeydst, G_ELI_MKEYLEN);
833	}
834
835	eli_metadata_store(req, prov, &md);
836	bzero(&md, sizeof(md));
837}
838
839static void
840eli_delkey(struct gctl_req *req)
841{
842	const char *prov;
843	int nargs;
844
845	nargs = gctl_get_int(req, "nargs");
846	if (nargs != 1) {
847		gctl_error(req, "Invalid number of arguments.");
848		return;
849	}
850	prov = gctl_get_ascii(req, "arg0");
851
852	if (eli_is_attached(prov))
853		eli_delkey_attached(req, prov);
854	else
855		eli_delkey_detached(req, prov);
856}
857
858static void
859eli_kill_detached(struct gctl_req *req, const char *prov)
860{
861	struct g_eli_metadata md;
862	int error;
863
864	/*
865	 * NOTE: Maybe we should verify if this is geli provider first,
866	 *       but 'kill' command is quite critical so better don't waste
867	 *       the time.
868	 */
869#if 0
870	error = g_metadata_read(prov, (unsigned char *)&md, sizeof(md),
871	    G_ELI_MAGIC);
872	if (error != 0) {
873		gctl_error(req, "Cannot read metadata from %s: %s.", prov,
874		    strerror(error));
875		return;
876	}
877#endif
878
879	arc4rand((unsigned char *)&md, sizeof(md));
880	error = g_metadata_store(prov, (unsigned char *)&md, sizeof(md));
881	if (error != 0) {
882		gctl_error(req, "Cannot write metadata to %s: %s.", prov,
883		    strerror(error));
884	}
885}
886
887static void
888eli_kill(struct gctl_req *req)
889{
890	const char *prov;
891	int i, nargs, all;
892
893	nargs = gctl_get_int(req, "nargs");
894	all = gctl_get_int(req, "all");
895	if (!all && nargs == 0) {
896		gctl_error(req, "Too few arguments.");
897		return;
898	}
899	/*
900	 * How '-a' option combine with a list of providers:
901	 * Delete Master Keys from all attached providers:
902	 * geli kill -a
903	 * Delete Master Keys from all attached provider and from
904	 * detached da0 and da1:
905	 * geli kill -a da0 da1
906	 * Delete Master Keys from (attached or detached) da0 and da1:
907	 * geli kill da0 da1
908	 */
909
910	/*
911	 * First attached providers.
912	 */
913	gctl_issue(req);
914	/*
915	 * Now the rest.
916	 */
917	for (i = 0; i < nargs; i++) {
918		prov = gctl_get_ascii(req, "arg%d", i);
919		if (!eli_is_attached(prov))
920			eli_kill_detached(req, prov);
921	}
922}
923
924static void
925eli_backup(struct gctl_req *req)
926{
927	struct g_eli_metadata md;
928	const char *file, *prov;
929	unsigned secsize;
930	unsigned char *sector;
931	off_t mediasize;
932	int nargs, filefd, provfd;
933
934	nargs = gctl_get_int(req, "nargs");
935	if (nargs != 2) {
936		gctl_error(req, "Invalid number of arguments.");
937		return;
938	}
939	prov = gctl_get_ascii(req, "arg0");
940	file = gctl_get_ascii(req, "arg1");
941
942	provfd = filefd = -1;
943	sector = NULL;
944	secsize = 0;
945
946	provfd = open(prov, O_RDONLY);
947	if (provfd == -1 && errno == ENOENT && prov[0] != '/') {
948		char devprov[MAXPATHLEN];
949
950		snprintf(devprov, sizeof(devprov), "%s%s", _PATH_DEV, prov);
951		provfd = open(devprov, O_RDONLY);
952	}
953	if (provfd == -1) {
954		gctl_error(req, "Cannot open %s: %s.", prov, strerror(errno));
955		return;
956	}
957	filefd = open(file, O_WRONLY | O_TRUNC | O_CREAT, 0600);
958	if (filefd == -1) {
959		gctl_error(req, "Cannot open %s: %s.", file, strerror(errno));
960		goto out;
961	}
962
963	mediasize = g_get_mediasize(prov);
964	secsize = g_get_sectorsize(prov);
965	if (mediasize == 0 || secsize == 0) {
966		gctl_error(req, "Cannot get informations about %s: %s.", prov,
967		    strerror(errno));
968		return;
969	}
970
971	sector = malloc(secsize);
972	if (sector == NULL) {
973		gctl_error(req, "Cannot allocate memory.");
974		return;
975	}
976
977	/* Read metadata from the provider. */
978	if (pread(provfd, sector, secsize, mediasize - secsize) !=
979	    (ssize_t)secsize) {
980		gctl_error(req, "Cannot read metadata: %s.", strerror(errno));
981		goto out;
982	}
983	/* Check if this is geli provider. */
984	if (eli_metadata_decode(sector, &md) != 0) {
985		gctl_error(req, "MD5 hash mismatch: not a geli provider?");
986		goto out;
987	}
988	/* Write metadata to the destination file. */
989	if (write(filefd, sector, secsize) != (ssize_t)secsize) {
990		gctl_error(req, "Cannot write to %s: %s.", file,
991		    strerror(errno));
992		goto out;
993	}
994out:
995	if (provfd > 0)
996		close(provfd);
997	if (filefd > 0)
998		close(filefd);
999	if (sector != NULL) {
1000		bzero(sector, secsize);
1001		free(sector);
1002	}
1003}
1004
1005static void
1006eli_restore(struct gctl_req *req)
1007{
1008	struct g_eli_metadata md;
1009	const char *file, *prov;
1010	unsigned char *sector;
1011	unsigned secsize;
1012	off_t mediasize;
1013	int nargs, filefd, provfd;
1014
1015	nargs = gctl_get_int(req, "nargs");
1016	if (nargs != 2) {
1017		gctl_error(req, "Invalid number of arguments.");
1018		return;
1019	}
1020	file = gctl_get_ascii(req, "arg0");
1021	prov = gctl_get_ascii(req, "arg1");
1022
1023	provfd = filefd = -1;
1024	sector = NULL;
1025	secsize = 0;
1026
1027	filefd = open(file, O_RDONLY);
1028	if (filefd == -1) {
1029		gctl_error(req, "Cannot open %s: %s.", file, strerror(errno));
1030		goto out;
1031	}
1032	provfd = open(prov, O_WRONLY);
1033	if (provfd == -1 && errno == ENOENT && prov[0] != '/') {
1034		char devprov[MAXPATHLEN];
1035
1036		snprintf(devprov, sizeof(devprov), "%s%s", _PATH_DEV, prov);
1037		provfd = open(devprov, O_WRONLY);
1038	}
1039	if (provfd == -1) {
1040		gctl_error(req, "Cannot open %s: %s.", prov, strerror(errno));
1041		return;
1042	}
1043
1044	mediasize = g_get_mediasize(prov);
1045	secsize = g_get_sectorsize(prov);
1046	if (mediasize == 0 || secsize == 0) {
1047		gctl_error(req, "Cannot get informations about %s: %s.", prov,
1048		    strerror(errno));
1049		return;
1050	}
1051
1052	sector = malloc(secsize);
1053	if (sector == NULL) {
1054		gctl_error(req, "Cannot allocate memory.");
1055		return;
1056	}
1057
1058	/* Read metadata from the backup file. */
1059	if (read(filefd, sector, secsize) != (ssize_t)secsize) {
1060		gctl_error(req, "Cannot read from %s: %s.", file,
1061		    strerror(errno));
1062		goto out;
1063	}
1064	/* Check if this file contains geli metadata. */
1065	if (eli_metadata_decode(sector, &md) != 0) {
1066		gctl_error(req, "MD5 hash mismatch: not a geli backup file?");
1067		goto out;
1068	}
1069	/* Read metadata from the provider. */
1070	if (pwrite(provfd, sector, secsize, mediasize - secsize) !=
1071	    (ssize_t)secsize) {
1072		gctl_error(req, "Cannot write metadata: %s.", strerror(errno));
1073		goto out;
1074	}
1075out:
1076	if (provfd > 0)
1077		close(provfd);
1078	if (filefd > 0)
1079		close(filefd);
1080	if (sector != NULL) {
1081		bzero(sector, secsize);
1082		free(sector);
1083	}
1084}
1085
1086static void
1087eli_clear(struct gctl_req *req)
1088{
1089	const char *name;
1090	int error, i, nargs;
1091
1092	nargs = gctl_get_int(req, "nargs");
1093	if (nargs < 1) {
1094		gctl_error(req, "Too few arguments.");
1095		return;
1096	}
1097
1098	for (i = 0; i < nargs; i++) {
1099		name = gctl_get_ascii(req, "arg%d", i);
1100		error = g_metadata_clear(name, G_ELI_MAGIC);
1101		if (error != 0) {
1102			fprintf(stderr, "Cannot clear metadata on %s: %s.\n",
1103			    name, strerror(error));
1104			gctl_error(req, "Not fully done.");
1105			continue;
1106		}
1107		if (verbose)
1108			printf("Metadata cleared on %s.\n", name);
1109	}
1110}
1111
1112static void
1113eli_dump(struct gctl_req *req)
1114{
1115	struct g_eli_metadata md, tmpmd;
1116	const char *name;
1117	int error, i, nargs;
1118
1119	nargs = gctl_get_int(req, "nargs");
1120	if (nargs < 1) {
1121		gctl_error(req, "Too few arguments.");
1122		return;
1123	}
1124
1125	for (i = 0; i < nargs; i++) {
1126		name = gctl_get_ascii(req, "arg%d", i);
1127		error = g_metadata_read(name, (unsigned char *)&tmpmd,
1128		    sizeof(tmpmd), G_ELI_MAGIC);
1129		if (error != 0) {
1130			fprintf(stderr, "Cannot read metadata from %s: %s.\n",
1131			    name, strerror(error));
1132			gctl_error(req, "Not fully done.");
1133			continue;
1134		}
1135		if (eli_metadata_decode((unsigned char *)&tmpmd, &md) != 0) {
1136			fprintf(stderr, "MD5 hash mismatch for %s, skipping.\n",
1137			    name);
1138			gctl_error(req, "Not fully done.");
1139			continue;
1140		}
1141		printf("Metadata on %s:\n", name);
1142		eli_metadata_dump(&md);
1143		printf("\n");
1144	}
1145}
1146