geom_eli.c revision 226722
1289715Sglebius/*-
2289715Sglebius * Copyright (c) 2004-2010 Pawel Jakub Dawidek <pjd@FreeBSD.org>
3289715Sglebius * All rights reserved.
4289715Sglebius *
5289715Sglebius * Redistribution and use in source and binary forms, with or without
6289715Sglebius * modification, are permitted provided that the following conditions
7289715Sglebius * are met:
8289715Sglebius * 1. Redistributions of source code must retain the above copyright
9293894Sglebius *    notice, this list of conditions and the following disclaimer.
10289715Sglebius * 2. Redistributions in binary form must reproduce the above copyright
11289715Sglebius *    notice, this list of conditions and the following disclaimer in the
12293894Sglebius *    documentation and/or other materials provided with the distribution.
13289715Sglebius *
14289715Sglebius * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
15293894Sglebius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16293894Sglebius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17293894Sglebius * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
18293894Sglebius * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19293894Sglebius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20293894Sglebius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21289715Sglebius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22289715Sglebius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23293894Sglebius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24293894Sglebius * SUCH DAMAGE.
25293894Sglebius */
26289715Sglebius
27293894Sglebius#include <sys/cdefs.h>
28293894Sglebius__FBSDID("$FreeBSD: head/sbin/geom/class/eli/geom_eli.c 226722 2011-10-25 07:56:27Z pjd $");
29293894Sglebius
30289715Sglebius#include <sys/param.h>
31289715Sglebius#include <sys/mman.h>
32289715Sglebius#include <sys/sysctl.h>
33289715Sglebius#include <sys/resource.h>
34293894Sglebius#include <opencrypto/cryptodev.h>
35293894Sglebius
36293894Sglebius#include <assert.h>
37289715Sglebius#include <err.h>
38293894Sglebius#include <errno.h>
39289715Sglebius#include <fcntl.h>
40293894Sglebius#include <libgeom.h>
41289715Sglebius#include <paths.h>
42289715Sglebius#include <readpassphrase.h>
43293894Sglebius#include <stdbool.h>
44293894Sglebius#include <stdint.h>
45289715Sglebius#include <stdio.h>
46289715Sglebius#include <stdlib.h>
47289715Sglebius#include <string.h>
48289715Sglebius#include <strings.h>
49289715Sglebius#include <unistd.h>
50293894Sglebius
51293894Sglebius#include <geom/eli/g_eli.h>
52293894Sglebius#include <geom/eli/pkcs5v2.h>
53289715Sglebius
54289715Sglebius#include "core/geom.h"
55289715Sglebius#include "misc/subr.h"
56293894Sglebius
57289715Sglebius
58289715Sglebiusuint32_t lib_version = G_LIB_VERSION;
59289715Sglebiusuint32_t version = G_ELI_VERSION;
60289715Sglebius
61289715Sglebius#define	GELI_BACKUP_DIR	"/var/backups/"
62293894Sglebius#define	GELI_ENC_ALGO	"aes"
63293894Sglebius
64289715Sglebiusstatic void eli_main(struct gctl_req *req, unsigned flags);
65289715Sglebiusstatic void eli_init(struct gctl_req *req);
66289715Sglebiusstatic void eli_attach(struct gctl_req *req);
67293894Sglebiusstatic void eli_configure(struct gctl_req *req);
68293894Sglebiusstatic void eli_setkey(struct gctl_req *req);
69289715Sglebiusstatic void eli_delkey(struct gctl_req *req);
70293894Sglebiusstatic void eli_resume(struct gctl_req *req);
71293894Sglebiusstatic void eli_kill(struct gctl_req *req);
72293894Sglebiusstatic void eli_backup(struct gctl_req *req);
73289715Sglebiusstatic void eli_restore(struct gctl_req *req);
74293894Sglebiusstatic void eli_resize(struct gctl_req *req);
75289715Sglebiusstatic void eli_clear(struct gctl_req *req);
76293894Sglebiusstatic void eli_dump(struct gctl_req *req);
77289715Sglebius
78289715Sglebiusstatic int eli_backup_create(struct gctl_req *req, const char *prov,
79289715Sglebius    const char *file);
80289715Sglebius
81289715Sglebius/*
82293894Sglebius * Available commands:
83289715Sglebius *
84289715Sglebius * init [-bhPv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] prov
85293894Sglebius * label - alias for 'init'
86293894Sglebius * attach [-dprv] [-j passfile] [-k keyfile] prov
87289715Sglebius * detach [-fl] prov ...
88 * stop - alias for 'detach'
89 * onetime [-d] [-a aalgo] [-e ealgo] [-l keylen] prov
90 * configure [-bB] prov ...
91 * setkey [-pPv] [-n keyno] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov
92 * delkey [-afv] [-n keyno] prov
93 * suspend [-v] -a | prov ...
94 * resume [-pv] [-j passfile] [-k keyfile] prov
95 * kill [-av] [prov ...]
96 * backup [-v] prov file
97 * restore [-fv] file prov
98 * resize [-v] -s oldsize prov
99 * clear [-v] prov ...
100 * dump [-v] prov ...
101 */
102struct g_command class_commands[] = {
103	{ "init", G_FLAG_VERBOSE, eli_main,
104	    {
105		{ 'a', "aalgo", "", G_TYPE_STRING },
106		{ 'b', "boot", NULL, G_TYPE_BOOL },
107		{ 'B', "backupfile", "", G_TYPE_STRING },
108		{ 'e', "ealgo", GELI_ENC_ALGO, G_TYPE_STRING },
109		{ 'i', "iterations", "-1", G_TYPE_NUMBER },
110		{ 'J', "newpassfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
111		{ 'K', "newkeyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
112		{ 'l', "keylen", "0", G_TYPE_NUMBER },
113		{ 'P', "nonewpassphrase", NULL, G_TYPE_BOOL },
114		{ 's', "sectorsize", "0", G_TYPE_NUMBER },
115		G_OPT_SENTINEL
116	    },
117	    "[-bPv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] [-s sectorsize] prov"
118	},
119	{ "label", G_FLAG_VERBOSE, eli_main,
120	    {
121		{ 'a', "aalgo", "", G_TYPE_STRING },
122		{ 'b', "boot", NULL, G_TYPE_BOOL },
123		{ 'B', "backupfile", "", G_TYPE_STRING },
124		{ 'e', "ealgo", GELI_ENC_ALGO, G_TYPE_STRING },
125		{ 'i', "iterations", "-1", G_TYPE_NUMBER },
126		{ 'J', "newpassfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
127		{ 'K', "newkeyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
128		{ 'l', "keylen", "0", G_TYPE_NUMBER },
129		{ 'P', "nonewpassphrase", NULL, G_TYPE_BOOL },
130		{ 's', "sectorsize", "0", G_TYPE_NUMBER },
131		G_OPT_SENTINEL
132	    },
133	    "- an alias for 'init'"
134	},
135	{ "attach", G_FLAG_VERBOSE | G_FLAG_LOADKLD, eli_main,
136	    {
137		{ 'd', "detach", NULL, G_TYPE_BOOL },
138		{ 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
139		{ 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
140		{ 'p', "nopassphrase", NULL, G_TYPE_BOOL },
141		{ 'r', "readonly", NULL, G_TYPE_BOOL },
142		G_OPT_SENTINEL
143	    },
144	    "[-dprv] [-j passfile] [-k keyfile] prov"
145	},
146	{ "detach", 0, NULL,
147	    {
148		{ 'f', "force", NULL, G_TYPE_BOOL },
149		{ 'l', "last", NULL, G_TYPE_BOOL },
150		G_OPT_SENTINEL
151	    },
152	    "[-fl] prov ..."
153	},
154	{ "stop", 0, NULL,
155	    {
156		{ 'f', "force", NULL, G_TYPE_BOOL },
157		{ 'l', "last", NULL, G_TYPE_BOOL },
158		G_OPT_SENTINEL
159	    },
160	    "- an alias for 'detach'"
161	},
162	{ "onetime", G_FLAG_VERBOSE | G_FLAG_LOADKLD, NULL,
163	    {
164		{ 'a', "aalgo", "", G_TYPE_STRING },
165		{ 'd', "detach", NULL, G_TYPE_BOOL },
166		{ 'e', "ealgo", GELI_ENC_ALGO, G_TYPE_STRING },
167		{ 'l', "keylen", "0", G_TYPE_NUMBER },
168		{ 's', "sectorsize", "0", G_TYPE_NUMBER },
169		G_OPT_SENTINEL
170	    },
171	    "[-d] [-a aalgo] [-e ealgo] [-l keylen] [-s sectorsize] prov"
172	},
173	{ "configure", G_FLAG_VERBOSE, eli_main,
174	    {
175		{ 'b', "boot", NULL, G_TYPE_BOOL },
176		{ 'B', "noboot", NULL, G_TYPE_BOOL },
177		G_OPT_SENTINEL
178	    },
179	    "[-bB] prov ..."
180	},
181	{ "setkey", G_FLAG_VERBOSE, eli_main,
182	    {
183		{ 'i', "iterations", "-1", G_TYPE_NUMBER },
184		{ 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
185		{ 'J', "newpassfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
186		{ 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
187		{ 'K', "newkeyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
188		{ 'n', "keyno", "-1", G_TYPE_NUMBER },
189		{ 'p', "nopassphrase", NULL, G_TYPE_BOOL },
190		{ 'P', "nonewpassphrase", NULL, G_TYPE_BOOL },
191		G_OPT_SENTINEL
192	    },
193	    "[-pPv] [-n keyno] [-i iterations] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov"
194	},
195	{ "delkey", G_FLAG_VERBOSE, eli_main,
196	    {
197		{ 'a', "all", NULL, G_TYPE_BOOL },
198		{ 'f', "force", NULL, G_TYPE_BOOL },
199		{ 'n', "keyno", "-1", G_TYPE_NUMBER },
200		G_OPT_SENTINEL
201	    },
202	    "[-afv] [-n keyno] prov"
203	},
204	{ "suspend", G_FLAG_VERBOSE, NULL,
205	    {
206		{ 'a', "all", NULL, G_TYPE_BOOL },
207		G_OPT_SENTINEL
208	    },
209	    "[-v] -a | prov ..."
210	},
211	{ "resume", G_FLAG_VERBOSE, eli_main,
212	    {
213		{ 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
214		{ 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
215		{ 'p', "nopassphrase", NULL, G_TYPE_BOOL },
216		G_OPT_SENTINEL
217	    },
218	    "[-pv] [-j passfile] [-k keyfile] prov"
219	},
220	{ "kill", G_FLAG_VERBOSE, eli_main,
221	    {
222		{ 'a', "all", NULL, G_TYPE_BOOL },
223		G_OPT_SENTINEL
224	    },
225	    "[-av] [prov ...]"
226	},
227	{ "backup", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS,
228	    "[-v] prov file"
229	},
230	{ "restore", G_FLAG_VERBOSE, eli_main,
231	    {
232		{ 'f', "force", NULL, G_TYPE_BOOL },
233		G_OPT_SENTINEL
234	    },
235	    "[-fv] file prov"
236	},
237	{ "resize", G_FLAG_VERBOSE, eli_main,
238	    {
239		{ 's', "oldsize", NULL, G_TYPE_NUMBER },
240		G_OPT_SENTINEL
241	    },
242	    "[-v] -s oldsize prov"
243	},
244	{ "clear", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS,
245	    "[-v] prov ..."
246	},
247	{ "dump", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS,
248	    "[-v] prov ..."
249	},
250	G_CMD_SENTINEL
251};
252
253static int verbose = 0;
254
255static int
256eli_protect(struct gctl_req *req)
257{
258	struct rlimit rl;
259
260	/* Disable core dumps. */
261	rl.rlim_cur = 0;
262	rl.rlim_max = 0;
263	if (setrlimit(RLIMIT_CORE, &rl) == -1) {
264		gctl_error(req, "Cannot disable core dumps: %s.",
265		    strerror(errno));
266		return (-1);
267	}
268	/* Disable swapping. */
269	if (mlockall(MCL_FUTURE) == -1) {
270		gctl_error(req, "Cannot lock memory: %s.", strerror(errno));
271		return (-1);
272	}
273	return (0);
274}
275
276static void
277eli_main(struct gctl_req *req, unsigned int flags)
278{
279	const char *name;
280
281	if (eli_protect(req) == -1)
282		return;
283
284	if ((flags & G_FLAG_VERBOSE) != 0)
285		verbose = 1;
286
287	name = gctl_get_ascii(req, "verb");
288	if (name == NULL) {
289		gctl_error(req, "No '%s' argument.", "verb");
290		return;
291	}
292	if (strcmp(name, "init") == 0 || strcmp(name, "label") == 0)
293		eli_init(req);
294	else if (strcmp(name, "attach") == 0)
295		eli_attach(req);
296	else if (strcmp(name, "configure") == 0)
297		eli_configure(req);
298	else if (strcmp(name, "setkey") == 0)
299		eli_setkey(req);
300	else if (strcmp(name, "delkey") == 0)
301		eli_delkey(req);
302	else if (strcmp(name, "resume") == 0)
303		eli_resume(req);
304	else if (strcmp(name, "kill") == 0)
305		eli_kill(req);
306	else if (strcmp(name, "backup") == 0)
307		eli_backup(req);
308	else if (strcmp(name, "restore") == 0)
309		eli_restore(req);
310	else if (strcmp(name, "resize") == 0)
311		eli_resize(req);
312	else if (strcmp(name, "dump") == 0)
313		eli_dump(req);
314	else if (strcmp(name, "clear") == 0)
315		eli_clear(req);
316	else
317		gctl_error(req, "Unknown command: %s.", name);
318}
319
320static void
321arc4rand(unsigned char *buf, size_t size)
322{
323	uint32_t *buf4;
324	size_t size4;
325	unsigned int i;
326
327	buf4 = (uint32_t *)buf;
328	size4 = size / 4;
329
330	for (i = 0; i < size4; i++)
331		buf4[i] = arc4random();
332	for (i *= 4; i < size; i++)
333		buf[i] = arc4random() % 0xff;
334}
335
336static bool
337eli_is_attached(const char *prov)
338{
339	char name[MAXPATHLEN];
340
341	/*
342	 * Not the best way to do it, but the easiest.
343	 * We try to open provider and check if it is a GEOM provider
344	 * by asking about its sectorsize.
345	 */
346	snprintf(name, sizeof(name), "%s%s", prov, G_ELI_SUFFIX);
347	return (g_get_sectorsize(name) > 0);
348}
349
350static int
351eli_genkey_files(struct gctl_req *req, bool new, const char *type,
352    struct hmac_ctx *ctxp, char *passbuf, size_t passbufsize)
353{
354	char *p, buf[MAXPHYS], argname[16];
355	const char *file;
356	int error, fd, i;
357	ssize_t done;
358
359	assert((strcmp(type, "keyfile") == 0 && ctxp != NULL &&
360	    passbuf == NULL && passbufsize == 0) ||
361	    (strcmp(type, "passfile") == 0 && ctxp == NULL &&
362	    passbuf != NULL && passbufsize > 0));
363	assert(strcmp(type, "keyfile") == 0 || passbuf[0] == '\0');
364
365	for (i = 0; ; i++) {
366		snprintf(argname, sizeof(argname), "%s%s%d",
367		    new ? "new" : "", type, i);
368
369		/* No more {key,pass}files? */
370		if (!gctl_has_param(req, argname))
371			return (i);
372
373		file = gctl_get_ascii(req, "%s", argname);
374		assert(file != NULL);
375
376		if (strcmp(file, "-") == 0)
377			fd = STDIN_FILENO;
378		else {
379			fd = open(file, O_RDONLY);
380			if (fd == -1) {
381				gctl_error(req, "Cannot open %s %s: %s.",
382				    type, file, strerror(errno));
383				return (-1);
384			}
385		}
386		if (strcmp(type, "keyfile") == 0) {
387			while ((done = read(fd, buf, sizeof(buf))) > 0)
388				g_eli_crypto_hmac_update(ctxp, buf, done);
389		} else /* if (strcmp(type, "passfile") == 0) */ {
390			while ((done = read(fd, buf, sizeof(buf) - 1)) > 0) {
391				buf[done] = '\0';
392				p = strchr(buf, '\n');
393				if (p != NULL) {
394					*p = '\0';
395					done = p - buf;
396				}
397				if (strlcat(passbuf, buf, passbufsize) >=
398				    passbufsize) {
399					gctl_error(req,
400					    "Passphrase in %s too long.", file);
401					bzero(buf, sizeof(buf));
402					return (-1);
403				}
404				if (p != NULL)
405					break;
406			}
407		}
408		error = errno;
409		if (strcmp(file, "-") != 0)
410			close(fd);
411		bzero(buf, sizeof(buf));
412		if (done == -1) {
413			gctl_error(req, "Cannot read %s %s: %s.",
414			    type, file, strerror(error));
415			return (-1);
416		}
417	}
418	/* NOTREACHED */
419}
420
421static int
422eli_genkey_passphrase_prompt(struct gctl_req *req, bool new, char *passbuf,
423    size_t passbufsize)
424{
425	char *p;
426
427	for (;;) {
428		p = readpassphrase(
429		    new ? "Enter new passphrase:" : "Enter passphrase:",
430		    passbuf, passbufsize, RPP_ECHO_OFF | RPP_REQUIRE_TTY);
431		if (p == NULL) {
432			bzero(passbuf, passbufsize);
433			gctl_error(req, "Cannot read passphrase: %s.",
434			    strerror(errno));
435			return (-1);
436		}
437
438		if (new) {
439			char tmpbuf[BUFSIZ];
440
441			p = readpassphrase("Reenter new passphrase: ",
442			    tmpbuf, sizeof(tmpbuf),
443			    RPP_ECHO_OFF | RPP_REQUIRE_TTY);
444			if (p == NULL) {
445				bzero(passbuf, passbufsize);
446				gctl_error(req,
447				    "Cannot read passphrase: %s.",
448				    strerror(errno));
449				return (-1);
450			}
451
452			if (strcmp(passbuf, tmpbuf) != 0) {
453				bzero(passbuf, passbufsize);
454				fprintf(stderr, "They didn't match.\n");
455				continue;
456			}
457			bzero(tmpbuf, sizeof(tmpbuf));
458		}
459		return (0);
460	}
461	/* NOTREACHED */
462}
463
464static int
465eli_genkey_passphrase(struct gctl_req *req, struct g_eli_metadata *md, bool new,
466    struct hmac_ctx *ctxp)
467{
468	char passbuf[MAXPHYS];
469	bool nopassphrase;
470	int nfiles;
471
472	nopassphrase =
473	    gctl_get_int(req, new ? "nonewpassphrase" : "nopassphrase");
474	if (nopassphrase) {
475		if (gctl_has_param(req, new ? "newpassfile0" : "passfile0")) {
476			gctl_error(req,
477			    "Options -%c and -%c are mutually exclusive.",
478			    new ? 'J' : 'j', new ? 'P' : 'p');
479			return (-1);
480		}
481		return (0);
482	}
483
484	if (!new && md->md_iterations == -1) {
485		gctl_error(req, "Missing -p flag.");
486		return (-1);
487	}
488	passbuf[0] = '\0';
489	nfiles = eli_genkey_files(req, new, "passfile", NULL, passbuf,
490	    sizeof(passbuf));
491	if (nfiles == -1)
492		return (-1);
493	else if (nfiles == 0) {
494		if (eli_genkey_passphrase_prompt(req, new, passbuf,
495		    sizeof(passbuf)) == -1) {
496			return (-1);
497		}
498	}
499	/*
500	 * Field md_iterations equal to -1 means "choose some sane
501	 * value for me".
502	 */
503	if (md->md_iterations == -1) {
504		assert(new);
505		if (verbose)
506			printf("Calculating number of iterations...\n");
507		md->md_iterations = pkcs5v2_calculate(2000000);
508		assert(md->md_iterations > 0);
509		if (verbose) {
510			printf("Done, using %d iterations.\n",
511			    md->md_iterations);
512		}
513	}
514	/*
515	 * If md_iterations is equal to 0, user doesn't want PKCS#5v2.
516	 */
517	if (md->md_iterations == 0) {
518		g_eli_crypto_hmac_update(ctxp, md->md_salt,
519		    sizeof(md->md_salt));
520		g_eli_crypto_hmac_update(ctxp, passbuf, strlen(passbuf));
521	} else /* if (md->md_iterations > 0) */ {
522		unsigned char dkey[G_ELI_USERKEYLEN];
523
524		pkcs5v2_genkey(dkey, sizeof(dkey), md->md_salt,
525		    sizeof(md->md_salt), passbuf, md->md_iterations);
526		g_eli_crypto_hmac_update(ctxp, dkey, sizeof(dkey));
527		bzero(dkey, sizeof(dkey));
528	}
529	bzero(passbuf, sizeof(passbuf));
530
531	return (0);
532}
533
534static unsigned char *
535eli_genkey(struct gctl_req *req, struct g_eli_metadata *md, unsigned char *key,
536    bool new)
537{
538	struct hmac_ctx ctx;
539	bool nopassphrase;
540	int nfiles;
541
542	nopassphrase =
543	    gctl_get_int(req, new ? "nonewpassphrase" : "nopassphrase");
544
545	g_eli_crypto_hmac_init(&ctx, NULL, 0);
546
547	nfiles = eli_genkey_files(req, new, "keyfile", &ctx, NULL, 0);
548	if (nfiles == -1)
549		return (NULL);
550	else if (nfiles == 0 && nopassphrase) {
551		gctl_error(req, "No key components given.");
552		return (NULL);
553	}
554
555	if (eli_genkey_passphrase(req, md, new, &ctx) == -1)
556		return (NULL);
557
558	g_eli_crypto_hmac_final(&ctx, key, 0);
559
560	return (key);
561}
562
563static int
564eli_metadata_read(struct gctl_req *req, const char *prov,
565    struct g_eli_metadata *md)
566{
567	unsigned char sector[sizeof(struct g_eli_metadata)];
568	int error;
569
570	if (g_get_sectorsize(prov) == 0) {
571		int fd;
572
573		/* This is a file probably. */
574		fd = open(prov, O_RDONLY);
575		if (fd == -1) {
576			gctl_error(req, "Cannot open %s: %s.", prov,
577			    strerror(errno));
578			return (-1);
579		}
580		if (read(fd, sector, sizeof(sector)) != sizeof(sector)) {
581			gctl_error(req, "Cannot read metadata from %s: %s.",
582			    prov, strerror(errno));
583			close(fd);
584			return (-1);
585		}
586		close(fd);
587	} else {
588		/* This is a GEOM provider. */
589		error = g_metadata_read(prov, sector, sizeof(sector),
590		    G_ELI_MAGIC);
591		if (error != 0) {
592			gctl_error(req, "Cannot read metadata from %s: %s.",
593			    prov, strerror(error));
594			return (-1);
595		}
596	}
597	error = eli_metadata_decode(sector, md);
598	switch (error) {
599	case 0:
600		break;
601	case EOPNOTSUPP:
602		gctl_error(req,
603		    "Provider's %s metadata version %u is too new.\n"
604		    "geli: The highest supported version is %u.",
605		    prov, (unsigned int)md->md_version, G_ELI_VERSION);
606		return (-1);
607	case EINVAL:
608		gctl_error(req, "Inconsistent provider's %s metadata.", prov);
609		return (-1);
610	default:
611		gctl_error(req,
612		    "Unexpected error while decoding provider's %s metadata: %s.",
613		    prov, strerror(error));
614		return (-1);
615	}
616	return (0);
617}
618
619static int
620eli_metadata_store(struct gctl_req *req, const char *prov,
621    struct g_eli_metadata *md)
622{
623	unsigned char sector[sizeof(struct g_eli_metadata)];
624	int error;
625
626	eli_metadata_encode(md, sector);
627	if (g_get_sectorsize(prov) == 0) {
628		int fd;
629
630		/* This is a file probably. */
631		fd = open(prov, O_WRONLY | O_TRUNC);
632		if (fd == -1) {
633			gctl_error(req, "Cannot open %s: %s.", prov,
634			    strerror(errno));
635			bzero(sector, sizeof(sector));
636			return (-1);
637		}
638		if (write(fd, sector, sizeof(sector)) != sizeof(sector)) {
639			gctl_error(req, "Cannot write metadata to %s: %s.",
640			    prov, strerror(errno));
641			bzero(sector, sizeof(sector));
642			close(fd);
643			return (-1);
644		}
645		close(fd);
646	} else {
647		/* This is a GEOM provider. */
648		error = g_metadata_store(prov, sector, sizeof(sector));
649		if (error != 0) {
650			gctl_error(req, "Cannot write metadata to %s: %s.",
651			    prov, strerror(errno));
652			bzero(sector, sizeof(sector));
653			return (-1);
654		}
655	}
656	bzero(sector, sizeof(sector));
657	return (0);
658}
659
660static void
661eli_init(struct gctl_req *req)
662{
663	struct g_eli_metadata md;
664	unsigned char sector[sizeof(struct g_eli_metadata)];
665	unsigned char key[G_ELI_USERKEYLEN];
666	char backfile[MAXPATHLEN];
667	const char *str, *prov;
668	unsigned secsize;
669	off_t mediasize;
670	intmax_t val;
671	int error, nargs;
672
673	nargs = gctl_get_int(req, "nargs");
674	if (nargs != 1) {
675		gctl_error(req, "Invalid number of arguments.");
676		return;
677	}
678	prov = gctl_get_ascii(req, "arg0");
679	mediasize = g_get_mediasize(prov);
680	secsize = g_get_sectorsize(prov);
681	if (mediasize == 0 || secsize == 0) {
682		gctl_error(req, "Cannot get informations about %s: %s.", prov,
683		    strerror(errno));
684		return;
685	}
686
687	bzero(&md, sizeof(md));
688	strlcpy(md.md_magic, G_ELI_MAGIC, sizeof(md.md_magic));
689	md.md_version = G_ELI_VERSION;
690	md.md_flags = 0;
691	if (gctl_get_int(req, "boot"))
692		md.md_flags |= G_ELI_FLAG_BOOT;
693	md.md_ealgo = CRYPTO_ALGORITHM_MIN - 1;
694	str = gctl_get_ascii(req, "aalgo");
695	if (*str != '\0') {
696		md.md_aalgo = g_eli_str2aalgo(str);
697		if (md.md_aalgo >= CRYPTO_ALGORITHM_MIN &&
698		    md.md_aalgo <= CRYPTO_ALGORITHM_MAX) {
699			md.md_flags |= G_ELI_FLAG_AUTH;
700		} else {
701			/*
702			 * For backward compatibility, check if the -a option
703			 * was used to provide encryption algorithm.
704			 */
705			md.md_ealgo = g_eli_str2ealgo(str);
706			if (md.md_ealgo < CRYPTO_ALGORITHM_MIN ||
707			    md.md_ealgo > CRYPTO_ALGORITHM_MAX) {
708				gctl_error(req,
709				    "Invalid authentication algorithm.");
710				return;
711			} else {
712				fprintf(stderr, "warning: The -e option, not "
713				    "the -a option is now used to specify "
714				    "encryption algorithm to use.\n");
715			}
716		}
717	}
718	if (md.md_ealgo < CRYPTO_ALGORITHM_MIN ||
719	    md.md_ealgo > CRYPTO_ALGORITHM_MAX) {
720		str = gctl_get_ascii(req, "ealgo");
721		md.md_ealgo = g_eli_str2ealgo(str);
722		if (md.md_ealgo < CRYPTO_ALGORITHM_MIN ||
723		    md.md_ealgo > CRYPTO_ALGORITHM_MAX) {
724			gctl_error(req, "Invalid encryption algorithm.");
725			return;
726		}
727	}
728	val = gctl_get_intmax(req, "keylen");
729	md.md_keylen = val;
730	md.md_keylen = g_eli_keylen(md.md_ealgo, md.md_keylen);
731	if (md.md_keylen == 0) {
732		gctl_error(req, "Invalid key length.");
733		return;
734	}
735	md.md_provsize = mediasize;
736
737	val = gctl_get_intmax(req, "iterations");
738	if (val != -1) {
739		int nonewpassphrase;
740
741		/*
742		 * Don't allow to set iterations when there will be no
743		 * passphrase.
744		 */
745		nonewpassphrase = gctl_get_int(req, "nonewpassphrase");
746		if (nonewpassphrase) {
747			gctl_error(req,
748			    "Options -i and -P are mutually exclusive.");
749			return;
750		}
751	}
752	md.md_iterations = val;
753
754	val = gctl_get_intmax(req, "sectorsize");
755	if (val == 0)
756		md.md_sectorsize = secsize;
757	else {
758		if (val < 0 || (val % secsize) != 0) {
759			gctl_error(req, "Invalid sector size.");
760			return;
761		}
762		if (val > sysconf(_SC_PAGE_SIZE)) {
763			fprintf(stderr,
764			    "warning: Using sectorsize bigger than the page size!\n");
765		}
766		md.md_sectorsize = val;
767	}
768
769	md.md_keys = 0x01;
770	arc4rand(md.md_salt, sizeof(md.md_salt));
771	arc4rand(md.md_mkeys, sizeof(md.md_mkeys));
772
773	/* Generate user key. */
774	if (eli_genkey(req, &md, key, true) == NULL) {
775		bzero(key, sizeof(key));
776		bzero(&md, sizeof(md));
777		return;
778	}
779
780	/* Encrypt the first and the only Master Key. */
781	error = g_eli_mkey_encrypt(md.md_ealgo, key, md.md_keylen, md.md_mkeys);
782	bzero(key, sizeof(key));
783	if (error != 0) {
784		bzero(&md, sizeof(md));
785		gctl_error(req, "Cannot encrypt Master Key: %s.",
786		    strerror(error));
787		return;
788	}
789
790	eli_metadata_encode(&md, sector);
791	bzero(&md, sizeof(md));
792	error = g_metadata_store(prov, sector, sizeof(sector));
793	bzero(sector, sizeof(sector));
794	if (error != 0) {
795		gctl_error(req, "Cannot store metadata on %s: %s.", prov,
796		    strerror(error));
797		return;
798	}
799	if (verbose)
800		printf("Metadata value stored on %s.\n", prov);
801	/* Backup metadata to a file. */
802	str = gctl_get_ascii(req, "backupfile");
803	if (str[0] != '\0') {
804		/* Backupfile given be the user, just copy it. */
805		strlcpy(backfile, str, sizeof(backfile));
806	} else {
807		/* Generate file name automatically. */
808		const char *p = prov;
809		unsigned int i;
810
811		if (strncmp(p, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
812			p += sizeof(_PATH_DEV) - 1;
813		snprintf(backfile, sizeof(backfile), "%s%s.eli",
814		    GELI_BACKUP_DIR, p);
815		/* Replace all / with _. */
816		for (i = strlen(GELI_BACKUP_DIR); backfile[i] != '\0'; i++) {
817			if (backfile[i] == '/')
818				backfile[i] = '_';
819		}
820	}
821	if (strcmp(backfile, "none") != 0 &&
822	    eli_backup_create(req, prov, backfile) == 0) {
823		printf("\nMetadata backup can be found in %s and\n", backfile);
824		printf("can be restored with the following command:\n");
825		printf("\n\t# geli restore %s %s\n\n", backfile, prov);
826	}
827}
828
829static void
830eli_attach(struct gctl_req *req)
831{
832	struct g_eli_metadata md;
833	unsigned char key[G_ELI_USERKEYLEN];
834	const char *prov;
835	off_t mediasize;
836	int nargs;
837
838	nargs = gctl_get_int(req, "nargs");
839	if (nargs != 1) {
840		gctl_error(req, "Invalid number of arguments.");
841		return;
842	}
843	prov = gctl_get_ascii(req, "arg0");
844
845	if (eli_metadata_read(req, prov, &md) == -1)
846		return;
847
848	mediasize = g_get_mediasize(prov);
849	if (md.md_provsize != (uint64_t)mediasize) {
850		gctl_error(req, "Provider size mismatch.");
851		return;
852	}
853
854	if (eli_genkey(req, &md, key, false) == NULL) {
855		bzero(key, sizeof(key));
856		return;
857	}
858
859	gctl_ro_param(req, "key", sizeof(key), key);
860	if (gctl_issue(req) == NULL) {
861		if (verbose)
862			printf("Attached to %s.\n", prov);
863	}
864	bzero(key, sizeof(key));
865}
866
867static void
868eli_configure_detached(struct gctl_req *req, const char *prov, bool boot)
869{
870	struct g_eli_metadata md;
871
872	if (eli_metadata_read(req, prov, &md) == -1)
873		return;
874
875	if (boot && (md.md_flags & G_ELI_FLAG_BOOT)) {
876		if (verbose)
877			printf("BOOT flag already configured for %s.\n", prov);
878	} else if (!boot && !(md.md_flags & G_ELI_FLAG_BOOT)) {
879		if (verbose)
880			printf("BOOT flag not configured for %s.\n", prov);
881	} else {
882		if (boot)
883			md.md_flags |= G_ELI_FLAG_BOOT;
884		else
885			md.md_flags &= ~G_ELI_FLAG_BOOT;
886		eli_metadata_store(req, prov, &md);
887	}
888	bzero(&md, sizeof(md));
889}
890
891static void
892eli_configure(struct gctl_req *req)
893{
894	const char *prov;
895	bool boot, noboot;
896	int i, nargs;
897
898	nargs = gctl_get_int(req, "nargs");
899	if (nargs == 0) {
900		gctl_error(req, "Too few arguments.");
901		return;
902	}
903
904	boot = gctl_get_int(req, "boot");
905	noboot = gctl_get_int(req, "noboot");
906
907	if (boot && noboot) {
908		gctl_error(req, "Options -b and -B are mutually exclusive.");
909		return;
910	}
911	if (!boot && !noboot) {
912		gctl_error(req, "No option given.");
913		return;
914	}
915
916	/* First attached providers. */
917	gctl_issue(req);
918	/* Now the rest. */
919	for (i = 0; i < nargs; i++) {
920		prov = gctl_get_ascii(req, "arg%d", i);
921		if (!eli_is_attached(prov))
922			eli_configure_detached(req, prov, boot);
923	}
924}
925
926static void
927eli_setkey_attached(struct gctl_req *req, struct g_eli_metadata *md)
928{
929	unsigned char key[G_ELI_USERKEYLEN];
930	intmax_t val, old = 0;
931	int error;
932
933	val = gctl_get_intmax(req, "iterations");
934	/* Check if iterations number should be changed. */
935	if (val != -1)
936		md->md_iterations = val;
937	else
938		old = md->md_iterations;
939
940	/* Generate key for Master Key encryption. */
941	if (eli_genkey(req, md, key, true) == NULL) {
942		bzero(key, sizeof(key));
943		return;
944	}
945	/*
946	 * If number of iterations has changed, but wasn't given as a
947	 * command-line argument, update the request.
948	 */
949	if (val == -1 && md->md_iterations != old) {
950		error = gctl_change_param(req, "iterations", sizeof(intmax_t),
951		    &md->md_iterations);
952		assert(error == 0);
953	}
954
955	gctl_ro_param(req, "key", sizeof(key), key);
956	gctl_issue(req);
957	bzero(key, sizeof(key));
958}
959
960static void
961eli_setkey_detached(struct gctl_req *req, const char *prov,
962 struct g_eli_metadata *md)
963{
964	unsigned char key[G_ELI_USERKEYLEN], mkey[G_ELI_DATAIVKEYLEN];
965	unsigned char *mkeydst;
966	unsigned int nkey;
967	intmax_t val;
968	int error;
969
970	if (md->md_keys == 0) {
971		gctl_error(req, "No valid keys on %s.", prov);
972		return;
973	}
974
975	/* Generate key for Master Key decryption. */
976	if (eli_genkey(req, md, key, false) == NULL) {
977		bzero(key, sizeof(key));
978		return;
979	}
980
981	/* Decrypt Master Key. */
982	error = g_eli_mkey_decrypt(md, key, mkey, &nkey);
983	bzero(key, sizeof(key));
984	if (error != 0) {
985		bzero(md, sizeof(*md));
986		if (error == -1)
987			gctl_error(req, "Wrong key for %s.", prov);
988		else /* if (error > 0) */ {
989			gctl_error(req, "Cannot decrypt Master Key: %s.",
990			    strerror(error));
991		}
992		return;
993	}
994	if (verbose)
995		printf("Decrypted Master Key %u.\n", nkey);
996
997	val = gctl_get_intmax(req, "keyno");
998	if (val != -1)
999		nkey = val;
1000#if 0
1001	else
1002		; /* Use the key number which was found during decryption. */
1003#endif
1004	if (nkey >= G_ELI_MAXMKEYS) {
1005		gctl_error(req, "Invalid '%s' argument.", "keyno");
1006		return;
1007	}
1008
1009	val = gctl_get_intmax(req, "iterations");
1010	/* Check if iterations number should and can be changed. */
1011	if (val != -1) {
1012		if (bitcount32(md->md_keys) != 1) {
1013			gctl_error(req, "To be able to use '-i' option, only "
1014			    "one key can be defined.");
1015			return;
1016		}
1017		if (md->md_keys != (1 << nkey)) {
1018			gctl_error(req, "Only already defined key can be "
1019			    "changed when '-i' option is used.");
1020			return;
1021		}
1022		md->md_iterations = val;
1023	}
1024
1025	mkeydst = md->md_mkeys + nkey * G_ELI_MKEYLEN;
1026	md->md_keys |= (1 << nkey);
1027
1028	bcopy(mkey, mkeydst, sizeof(mkey));
1029	bzero(mkey, sizeof(mkey));
1030
1031	/* Generate key for Master Key encryption. */
1032	if (eli_genkey(req, md, key, true) == NULL) {
1033		bzero(key, sizeof(key));
1034		bzero(md, sizeof(*md));
1035		return;
1036	}
1037
1038	/* Encrypt the Master-Key with the new key. */
1039	error = g_eli_mkey_encrypt(md->md_ealgo, key, md->md_keylen, mkeydst);
1040	bzero(key, sizeof(key));
1041	if (error != 0) {
1042		bzero(md, sizeof(*md));
1043		gctl_error(req, "Cannot encrypt Master Key: %s.",
1044		    strerror(error));
1045		return;
1046	}
1047
1048	/* Store metadata with fresh key. */
1049	eli_metadata_store(req, prov, md);
1050	bzero(md, sizeof(*md));
1051}
1052
1053static void
1054eli_setkey(struct gctl_req *req)
1055{
1056	struct g_eli_metadata md;
1057	const char *prov;
1058	int nargs;
1059
1060	nargs = gctl_get_int(req, "nargs");
1061	if (nargs != 1) {
1062		gctl_error(req, "Invalid number of arguments.");
1063		return;
1064	}
1065	prov = gctl_get_ascii(req, "arg0");
1066
1067	if (eli_metadata_read(req, prov, &md) == -1)
1068		return;
1069
1070	if (eli_is_attached(prov))
1071		eli_setkey_attached(req, &md);
1072	else
1073		eli_setkey_detached(req, prov, &md);
1074
1075	if (req->error == NULL || req->error[0] == '\0') {
1076		printf("Note, that the master key encrypted with old keys "
1077		    "and/or passphrase may still exists in a metadata backup "
1078		    "file.\n");
1079	}
1080}
1081
1082static void
1083eli_delkey_attached(struct gctl_req *req, const char *prov __unused)
1084{
1085
1086	gctl_issue(req);
1087}
1088
1089static void
1090eli_delkey_detached(struct gctl_req *req, const char *prov)
1091{
1092	struct g_eli_metadata md;
1093	unsigned char *mkeydst;
1094	unsigned int nkey;
1095	intmax_t val;
1096	bool all, force;
1097
1098	if (eli_metadata_read(req, prov, &md) == -1)
1099		return;
1100
1101	all = gctl_get_int(req, "all");
1102	if (all)
1103		arc4rand(md.md_mkeys, sizeof(md.md_mkeys));
1104	else {
1105		force = gctl_get_int(req, "force");
1106		val = gctl_get_intmax(req, "keyno");
1107		if (val == -1) {
1108			gctl_error(req, "Key number has to be specified.");
1109			return;
1110		}
1111		nkey = val;
1112		if (nkey >= G_ELI_MAXMKEYS) {
1113			gctl_error(req, "Invalid '%s' argument.", "keyno");
1114			return;
1115		}
1116		if (!(md.md_keys & (1 << nkey)) && !force) {
1117			gctl_error(req, "Master Key %u is not set.", nkey);
1118			return;
1119		}
1120		md.md_keys &= ~(1 << nkey);
1121		if (md.md_keys == 0 && !force) {
1122			gctl_error(req, "This is the last Master Key. Use '-f' "
1123			    "option if you really want to remove it.");
1124			return;
1125		}
1126		mkeydst = md.md_mkeys + nkey * G_ELI_MKEYLEN;
1127		arc4rand(mkeydst, G_ELI_MKEYLEN);
1128	}
1129
1130	eli_metadata_store(req, prov, &md);
1131	bzero(&md, sizeof(md));
1132}
1133
1134static void
1135eli_delkey(struct gctl_req *req)
1136{
1137	const char *prov;
1138	int nargs;
1139
1140	nargs = gctl_get_int(req, "nargs");
1141	if (nargs != 1) {
1142		gctl_error(req, "Invalid number of arguments.");
1143		return;
1144	}
1145	prov = gctl_get_ascii(req, "arg0");
1146
1147	if (eli_is_attached(prov))
1148		eli_delkey_attached(req, prov);
1149	else
1150		eli_delkey_detached(req, prov);
1151}
1152
1153static void
1154eli_resume(struct gctl_req *req)
1155{
1156	struct g_eli_metadata md;
1157	unsigned char key[G_ELI_USERKEYLEN];
1158	const char *prov;
1159	off_t mediasize;
1160	int nargs;
1161
1162	nargs = gctl_get_int(req, "nargs");
1163	if (nargs != 1) {
1164		gctl_error(req, "Invalid number of arguments.");
1165		return;
1166	}
1167	prov = gctl_get_ascii(req, "arg0");
1168
1169	if (eli_metadata_read(req, prov, &md) == -1)
1170		return;
1171
1172	mediasize = g_get_mediasize(prov);
1173	if (md.md_provsize != (uint64_t)mediasize) {
1174		gctl_error(req, "Provider size mismatch.");
1175		return;
1176	}
1177
1178	if (eli_genkey(req, &md, key, false) == NULL) {
1179		bzero(key, sizeof(key));
1180		return;
1181	}
1182
1183	gctl_ro_param(req, "key", sizeof(key), key);
1184	if (gctl_issue(req) == NULL) {
1185		if (verbose)
1186			printf("Resumed %s.\n", prov);
1187	}
1188	bzero(key, sizeof(key));
1189}
1190
1191static int
1192eli_trash_metadata(struct gctl_req *req, const char *prov, int fd, off_t offset)
1193{
1194	unsigned int overwrites;
1195	unsigned char *sector;
1196	ssize_t size;
1197	int error;
1198
1199	size = sizeof(overwrites);
1200	if (sysctlbyname("kern.geom.eli.overwrites", &overwrites, &size,
1201	    NULL, 0) == -1 || overwrites == 0) {
1202		overwrites = G_ELI_OVERWRITES;
1203	}
1204
1205	size = g_sectorsize(fd);
1206	if (size <= 0) {
1207		gctl_error(req, "Cannot obtain provider sector size %s: %s.",
1208		    prov, strerror(errno));
1209		return (-1);
1210	}
1211	sector = malloc(size);
1212	if (sector == NULL) {
1213		gctl_error(req, "Cannot allocate %zd bytes of memory.", size);
1214		return (-1);
1215	}
1216
1217	error = 0;
1218	do {
1219		arc4rand(sector, size);
1220		if (pwrite(fd, sector, size, offset) != size) {
1221			if (error == 0)
1222				error = errno;
1223		}
1224		(void)g_flush(fd);
1225	} while (--overwrites > 0);
1226	if (error != 0) {
1227		gctl_error(req, "Cannot trash metadata on provider %s: %s.",
1228		    prov, strerror(error));
1229		return (-1);
1230	}
1231	return (0);
1232}
1233
1234static void
1235eli_kill_detached(struct gctl_req *req, const char *prov)
1236{
1237	off_t offset;
1238	int fd;
1239
1240	/*
1241	 * NOTE: Maybe we should verify if this is geli provider first,
1242	 *       but 'kill' command is quite critical so better don't waste
1243	 *       the time.
1244	 */
1245#if 0
1246	error = g_metadata_read(prov, (unsigned char *)&md, sizeof(md),
1247	    G_ELI_MAGIC);
1248	if (error != 0) {
1249		gctl_error(req, "Cannot read metadata from %s: %s.", prov,
1250		    strerror(error));
1251		return;
1252	}
1253#endif
1254
1255	fd = g_open(prov, 1);
1256	if (fd == -1) {
1257		gctl_error(req, "Cannot open provider %s: %s.", prov,
1258		    strerror(errno));
1259		return;
1260	}
1261	offset = g_mediasize(fd) - g_sectorsize(fd);
1262	if (offset <= 0) {
1263		gctl_error(req,
1264		    "Cannot obtain media size or sector size for provider %s: %s.",
1265		    prov, strerror(errno));
1266		(void)g_close(fd);
1267		return;
1268	}
1269	(void)eli_trash_metadata(req, prov, fd, offset);
1270	(void)g_close(fd);
1271}
1272
1273static void
1274eli_kill(struct gctl_req *req)
1275{
1276	const char *prov;
1277	int i, nargs, all;
1278
1279	nargs = gctl_get_int(req, "nargs");
1280	all = gctl_get_int(req, "all");
1281	if (!all && nargs == 0) {
1282		gctl_error(req, "Too few arguments.");
1283		return;
1284	}
1285	/*
1286	 * How '-a' option combine with a list of providers:
1287	 * Delete Master Keys from all attached providers:
1288	 * geli kill -a
1289	 * Delete Master Keys from all attached providers and from
1290	 * detached da0 and da1:
1291	 * geli kill -a da0 da1
1292	 * Delete Master Keys from (attached or detached) da0 and da1:
1293	 * geli kill da0 da1
1294	 */
1295
1296	/* First detached providers. */
1297	for (i = 0; i < nargs; i++) {
1298		prov = gctl_get_ascii(req, "arg%d", i);
1299		if (!eli_is_attached(prov))
1300			eli_kill_detached(req, prov);
1301	}
1302	/* Now attached providers. */
1303	gctl_issue(req);
1304}
1305
1306static int
1307eli_backup_create(struct gctl_req *req, const char *prov, const char *file)
1308{
1309	unsigned char *sector;
1310	ssize_t secsize;
1311	int error, filefd, ret;
1312
1313	ret = -1;
1314	filefd = -1;
1315	sector = NULL;
1316	secsize = 0;
1317
1318	secsize = g_get_sectorsize(prov);
1319	if (secsize == 0) {
1320		gctl_error(req, "Cannot get informations about %s: %s.", prov,
1321		    strerror(errno));
1322		goto out;
1323	}
1324	sector = malloc(secsize);
1325	if (sector == NULL) {
1326		gctl_error(req, "Cannot allocate memory.");
1327		goto out;
1328	}
1329	/* Read metadata from the provider. */
1330	error = g_metadata_read(prov, sector, secsize, G_ELI_MAGIC);
1331	if (error != 0) {
1332		gctl_error(req, "Unable to read metadata from %s: %s.", prov,
1333		    strerror(error));
1334		goto out;
1335	}
1336
1337	filefd = open(file, O_WRONLY | O_TRUNC | O_CREAT, 0600);
1338	if (filefd == -1) {
1339		gctl_error(req, "Unable to open %s: %s.", file,
1340		    strerror(errno));
1341		goto out;
1342	}
1343	/* Write metadata to the destination file. */
1344	if (write(filefd, sector, secsize) != secsize) {
1345		gctl_error(req, "Unable to write to %s: %s.", file,
1346		    strerror(errno));
1347		(void)close(filefd);
1348		(void)unlink(file);
1349		goto out;
1350	}
1351	(void)fsync(filefd);
1352	(void)close(filefd);
1353	/* Success. */
1354	ret = 0;
1355out:
1356	if (sector != NULL) {
1357		bzero(sector, secsize);
1358		free(sector);
1359	}
1360	return (ret);
1361}
1362
1363static void
1364eli_backup(struct gctl_req *req)
1365{
1366	const char *file, *prov;
1367	int nargs;
1368
1369	nargs = gctl_get_int(req, "nargs");
1370	if (nargs != 2) {
1371		gctl_error(req, "Invalid number of arguments.");
1372		return;
1373	}
1374	prov = gctl_get_ascii(req, "arg0");
1375	file = gctl_get_ascii(req, "arg1");
1376
1377	eli_backup_create(req, prov, file);
1378}
1379
1380static void
1381eli_restore(struct gctl_req *req)
1382{
1383	struct g_eli_metadata md;
1384	const char *file, *prov;
1385	off_t mediasize;
1386	int nargs;
1387
1388	nargs = gctl_get_int(req, "nargs");
1389	if (nargs != 2) {
1390		gctl_error(req, "Invalid number of arguments.");
1391		return;
1392	}
1393	file = gctl_get_ascii(req, "arg0");
1394	prov = gctl_get_ascii(req, "arg1");
1395
1396	/* Read metadata from the backup file. */
1397	if (eli_metadata_read(req, file, &md) == -1)
1398		return;
1399	/* Obtain provider's mediasize. */
1400	mediasize = g_get_mediasize(prov);
1401	if (mediasize == 0) {
1402		gctl_error(req, "Cannot get informations about %s: %s.", prov,
1403		    strerror(errno));
1404		return;
1405	}
1406	/* Check if the provider size has changed since we did the backup. */
1407	if (md.md_provsize != (uint64_t)mediasize) {
1408		if (gctl_get_int(req, "force")) {
1409			md.md_provsize = mediasize;
1410		} else {
1411			gctl_error(req, "Provider size mismatch: "
1412			    "wrong backup file?");
1413			return;
1414		}
1415	}
1416	/* Write metadata to the provider. */
1417	(void)eli_metadata_store(req, prov, &md);
1418}
1419
1420static void
1421eli_resize(struct gctl_req *req)
1422{
1423	struct g_eli_metadata md;
1424	const char *prov;
1425	unsigned char *sector;
1426	ssize_t secsize;
1427	off_t mediasize, oldsize;
1428	int error, nargs, provfd;
1429
1430	nargs = gctl_get_int(req, "nargs");
1431	if (nargs != 1) {
1432		gctl_error(req, "Invalid number of arguments.");
1433		return;
1434	}
1435	prov = gctl_get_ascii(req, "arg0");
1436
1437	provfd = -1;
1438	sector = NULL;
1439	secsize = 0;
1440
1441	provfd = g_open(prov, 1);
1442	if (provfd == -1) {
1443		gctl_error(req, "Cannot open %s: %s.", prov, strerror(errno));
1444		goto out;
1445	}
1446
1447	mediasize = g_mediasize(provfd);
1448	secsize = g_sectorsize(provfd);
1449	if (mediasize == -1 || secsize == -1) {
1450		gctl_error(req, "Cannot get information about %s: %s.", prov,
1451		    strerror(errno));
1452		goto out;
1453	}
1454
1455	sector = malloc(secsize);
1456	if (sector == NULL) {
1457		gctl_error(req, "Cannot allocate memory.");
1458		goto out;
1459	}
1460
1461	oldsize = gctl_get_intmax(req, "oldsize");
1462	if (oldsize < 0 || oldsize > mediasize) {
1463		gctl_error(req, "Invalid oldsize: Out of range.");
1464		goto out;
1465	}
1466	if (oldsize == mediasize) {
1467		gctl_error(req, "Size hasn't changed.");
1468		goto out;
1469	}
1470
1471	/* Read metadata from the 'oldsize' offset. */
1472	if (pread(provfd, sector, secsize, oldsize - secsize) != secsize) {
1473		gctl_error(req, "Cannot read old metadata: %s.",
1474		    strerror(errno));
1475		goto out;
1476	}
1477
1478	/* Check if this sector contains geli metadata. */
1479	error = eli_metadata_decode(sector, &md);
1480	switch (error) {
1481	case 0:
1482		break;
1483	case EOPNOTSUPP:
1484		gctl_error(req,
1485		    "Provider's %s metadata version %u is too new.\n"
1486		    "geli: The highest supported version is %u.",
1487		    prov, (unsigned int)md.md_version, G_ELI_VERSION);
1488		goto out;
1489	case EINVAL:
1490		gctl_error(req, "Inconsistent provider's %s metadata.", prov);
1491		goto out;
1492	default:
1493		gctl_error(req,
1494		    "Unexpected error while decoding provider's %s metadata: %s.",
1495		    prov, strerror(error));
1496		goto out;
1497	}
1498
1499	/*
1500	 * If the old metadata doesn't have a correct provider size, refuse
1501	 * to resize.
1502	 */
1503	if (md.md_provsize != (uint64_t)oldsize) {
1504		gctl_error(req, "Provider size mismatch at oldsize.");
1505		goto out;
1506	}
1507
1508	/*
1509	 * Update the old metadata with the current provider size and write
1510	 * it back to the correct place on the provider.
1511	 */
1512	md.md_provsize = mediasize;
1513	/* Write metadata to the provider. */
1514	(void)eli_metadata_store(req, prov, &md);
1515	/* Now trash the old metadata. */
1516	(void)eli_trash_metadata(req, prov, provfd, oldsize - secsize);
1517out:
1518	if (provfd != -1)
1519		(void)g_close(provfd);
1520	if (sector != NULL) {
1521		bzero(sector, secsize);
1522		free(sector);
1523	}
1524}
1525
1526static void
1527eli_clear(struct gctl_req *req)
1528{
1529	const char *name;
1530	int error, i, nargs;
1531
1532	nargs = gctl_get_int(req, "nargs");
1533	if (nargs < 1) {
1534		gctl_error(req, "Too few arguments.");
1535		return;
1536	}
1537
1538	for (i = 0; i < nargs; i++) {
1539		name = gctl_get_ascii(req, "arg%d", i);
1540		error = g_metadata_clear(name, G_ELI_MAGIC);
1541		if (error != 0) {
1542			fprintf(stderr, "Cannot clear metadata on %s: %s.\n",
1543			    name, strerror(error));
1544			gctl_error(req, "Not fully done.");
1545			continue;
1546		}
1547		if (verbose)
1548			printf("Metadata cleared on %s.\n", name);
1549	}
1550}
1551
1552static void
1553eli_dump(struct gctl_req *req)
1554{
1555	struct g_eli_metadata md;
1556	const char *name;
1557	int i, nargs;
1558
1559	nargs = gctl_get_int(req, "nargs");
1560	if (nargs < 1) {
1561		gctl_error(req, "Too few arguments.");
1562		return;
1563	}
1564
1565	for (i = 0; i < nargs; i++) {
1566		name = gctl_get_ascii(req, "arg%d", i);
1567		if (eli_metadata_read(NULL, name, &md) == -1) {
1568			gctl_error(req, "Not fully done.");
1569			continue;
1570		}
1571		printf("Metadata on %s:\n", name);
1572		eli_metadata_dump(&md);
1573		printf("\n");
1574	}
1575}
1576