1148456Spjd/*-
2213073Spjd * Copyright (c) 2004-2010 Pawel Jakub Dawidek <pjd@FreeBSD.org>
3148456Spjd * All rights reserved.
4148456Spjd *
5148456Spjd * Redistribution and use in source and binary forms, with or without
6148456Spjd * modification, are permitted provided that the following conditions
7148456Spjd * are met:
8148456Spjd * 1. Redistributions of source code must retain the above copyright
9148456Spjd *    notice, this list of conditions and the following disclaimer.
10148456Spjd * 2. Redistributions in binary form must reproduce the above copyright
11148456Spjd *    notice, this list of conditions and the following disclaimer in the
12148456Spjd *    documentation and/or other materials provided with the distribution.
13155175Spjd *
14148456Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
15148456Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16148456Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17148456Spjd * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
18148456Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19148456Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20148456Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21148456Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22148456Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23148456Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24148456Spjd * SUCH DAMAGE.
25148456Spjd */
26148456Spjd
27148456Spjd#include <sys/cdefs.h>
28148456Spjd__FBSDID("$FreeBSD$");
29148456Spjd
30226715Spjd#include <sys/param.h>
31226715Spjd#include <sys/mman.h>
32213060Spjd#include <sys/sysctl.h>
33226715Spjd#include <sys/resource.h>
34226715Spjd#include <opencrypto/cryptodev.h>
35213060Spjd
36226715Spjd#include <assert.h>
37226715Spjd#include <err.h>
38226715Spjd#include <errno.h>
39226715Spjd#include <fcntl.h>
40226715Spjd#include <libgeom.h>
41226715Spjd#include <paths.h>
42226715Spjd#include <readpassphrase.h>
43213172Spjd#include <stdbool.h>
44226715Spjd#include <stdint.h>
45148456Spjd#include <stdio.h>
46148456Spjd#include <stdlib.h>
47148456Spjd#include <string.h>
48148456Spjd#include <strings.h>
49226715Spjd#include <unistd.h>
50148456Spjd
51148456Spjd#include <geom/eli/g_eli.h>
52148456Spjd#include <geom/eli/pkcs5v2.h>
53148456Spjd
54148456Spjd#include "core/geom.h"
55148456Spjd#include "misc/subr.h"
56148456Spjd
57148456Spjd
58148456Spjduint32_t lib_version = G_LIB_VERSION;
59148456Spjduint32_t version = G_ELI_VERSION;
60148456Spjd
61182452Spjd#define	GELI_BACKUP_DIR	"/var/backups/"
62212547Spjd#define	GELI_ENC_ALGO	"aes"
63182452Spjd
64148456Spjdstatic void eli_main(struct gctl_req *req, unsigned flags);
65148456Spjdstatic void eli_init(struct gctl_req *req);
66148456Spjdstatic void eli_attach(struct gctl_req *req);
67162353Spjdstatic void eli_configure(struct gctl_req *req);
68148456Spjdstatic void eli_setkey(struct gctl_req *req);
69148456Spjdstatic void eli_delkey(struct gctl_req *req);
70214118Spjdstatic void eli_resume(struct gctl_req *req);
71148456Spjdstatic void eli_kill(struct gctl_req *req);
72148456Spjdstatic void eli_backup(struct gctl_req *req);
73148456Spjdstatic void eli_restore(struct gctl_req *req);
74212934Sbrianstatic void eli_resize(struct gctl_req *req);
75226723Spjdstatic void eli_version(struct gctl_req *req);
76148456Spjdstatic void eli_clear(struct gctl_req *req);
77148456Spjdstatic void eli_dump(struct gctl_req *req);
78148456Spjd
79182452Spjdstatic int eli_backup_create(struct gctl_req *req, const char *prov,
80182452Spjd    const char *file);
81182452Spjd
82148456Spjd/*
83148456Spjd * Available commands:
84148456Spjd *
85226733Spjd * init [-bhPv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] [-V version] prov
86148456Spjd * label - alias for 'init'
87213172Spjd * attach [-dprv] [-j passfile] [-k keyfile] prov
88148456Spjd * detach [-fl] prov ...
89148456Spjd * stop - alias for 'detach'
90181639Spjd * onetime [-d] [-a aalgo] [-e ealgo] [-l keylen] prov
91162353Spjd * configure [-bB] prov ...
92213172Spjd * setkey [-pPv] [-n keyno] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov
93148456Spjd * delkey [-afv] [-n keyno] prov
94214118Spjd * suspend [-v] -a | prov ...
95214118Spjd * resume [-pv] [-j passfile] [-k keyfile] prov
96148456Spjd * kill [-av] [prov ...]
97148456Spjd * backup [-v] prov file
98212934Sbrian * restore [-fv] file prov
99212934Sbrian * resize [-v] -s oldsize prov
100226723Spjd * version [prov ...]
101148456Spjd * clear [-v] prov ...
102148456Spjd * dump [-v] prov ...
103148456Spjd */
104148456Spjdstruct g_command class_commands[] = {
105148456Spjd	{ "init", G_FLAG_VERBOSE, eli_main,
106148456Spjd	    {
107212547Spjd		{ 'a', "aalgo", "", G_TYPE_STRING },
108162868Spjd		{ 'b', "boot", NULL, G_TYPE_BOOL },
109212547Spjd		{ 'B', "backupfile", "", G_TYPE_STRING },
110226733Spjd		{ 'e', "ealgo", "", G_TYPE_STRING },
111212554Spjd		{ 'i', "iterations", "-1", G_TYPE_NUMBER },
112213172Spjd		{ 'J', "newpassfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
113213172Spjd		{ 'K', "newkeyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
114212554Spjd		{ 'l', "keylen", "0", G_TYPE_NUMBER },
115162868Spjd		{ 'P', "nonewpassphrase", NULL, G_TYPE_BOOL },
116212554Spjd		{ 's', "sectorsize", "0", G_TYPE_NUMBER },
117226733Spjd		{ 'V', "mdversion", "-1", G_TYPE_NUMBER },
118148456Spjd		G_OPT_SENTINEL
119148456Spjd	    },
120226733Spjd	    "[-bPv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] [-s sectorsize] [-V version] prov"
121148456Spjd	},
122148456Spjd	{ "label", G_FLAG_VERBOSE, eli_main,
123148456Spjd	    {
124212547Spjd		{ 'a', "aalgo", "", G_TYPE_STRING },
125162868Spjd		{ 'b', "boot", NULL, G_TYPE_BOOL },
126212547Spjd		{ 'B', "backupfile", "", G_TYPE_STRING },
127226733Spjd		{ 'e', "ealgo", "", G_TYPE_STRING },
128212554Spjd		{ 'i', "iterations", "-1", G_TYPE_NUMBER },
129213172Spjd		{ 'J', "newpassfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
130213172Spjd		{ 'K', "newkeyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
131212554Spjd		{ 'l', "keylen", "0", G_TYPE_NUMBER },
132162868Spjd		{ 'P', "nonewpassphrase", NULL, G_TYPE_BOOL },
133212554Spjd		{ 's', "sectorsize", "0", G_TYPE_NUMBER },
134226733Spjd		{ 'V', "mdversion", "-1", G_TYPE_NUMBER },
135148456Spjd		G_OPT_SENTINEL
136148456Spjd	    },
137212554Spjd	    "- an alias for 'init'"
138148456Spjd	},
139148456Spjd	{ "attach", G_FLAG_VERBOSE | G_FLAG_LOADKLD, eli_main,
140148456Spjd	    {
141162868Spjd		{ 'd', "detach", NULL, G_TYPE_BOOL },
142213172Spjd		{ 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
143213172Spjd		{ 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
144162868Spjd		{ 'p', "nopassphrase", NULL, G_TYPE_BOOL },
145162868Spjd		{ 'r', "readonly", NULL, G_TYPE_BOOL },
146148456Spjd		G_OPT_SENTINEL
147148456Spjd	    },
148213172Spjd	    "[-dprv] [-j passfile] [-k keyfile] prov"
149148456Spjd	},
150148456Spjd	{ "detach", 0, NULL,
151148456Spjd	    {
152162868Spjd		{ 'f', "force", NULL, G_TYPE_BOOL },
153162868Spjd		{ 'l', "last", NULL, G_TYPE_BOOL },
154148456Spjd		G_OPT_SENTINEL
155148456Spjd	    },
156212554Spjd	    "[-fl] prov ..."
157148456Spjd	},
158148456Spjd	{ "stop", 0, NULL,
159148456Spjd	    {
160162868Spjd		{ 'f', "force", NULL, G_TYPE_BOOL },
161162868Spjd		{ 'l', "last", NULL, G_TYPE_BOOL },
162148456Spjd		G_OPT_SENTINEL
163148456Spjd	    },
164212554Spjd	    "- an alias for 'detach'"
165148456Spjd	},
166148456Spjd	{ "onetime", G_FLAG_VERBOSE | G_FLAG_LOADKLD, NULL,
167148456Spjd	    {
168212547Spjd		{ 'a', "aalgo", "", G_TYPE_STRING },
169162868Spjd		{ 'd', "detach", NULL, G_TYPE_BOOL },
170212547Spjd		{ 'e', "ealgo", GELI_ENC_ALGO, G_TYPE_STRING },
171212554Spjd		{ 'l', "keylen", "0", G_TYPE_NUMBER },
172212554Spjd		{ 's', "sectorsize", "0", G_TYPE_NUMBER },
173148456Spjd		G_OPT_SENTINEL
174148456Spjd	    },
175212554Spjd	    "[-d] [-a aalgo] [-e ealgo] [-l keylen] [-s sectorsize] prov"
176148456Spjd	},
177162353Spjd	{ "configure", G_FLAG_VERBOSE, eli_main,
178162353Spjd	    {
179162868Spjd		{ 'b', "boot", NULL, G_TYPE_BOOL },
180162868Spjd		{ 'B', "noboot", NULL, G_TYPE_BOOL },
181162353Spjd		G_OPT_SENTINEL
182162353Spjd	    },
183212554Spjd	    "[-bB] prov ..."
184162353Spjd	},
185148456Spjd	{ "setkey", G_FLAG_VERBOSE, eli_main,
186148456Spjd	    {
187212554Spjd		{ 'i', "iterations", "-1", G_TYPE_NUMBER },
188213172Spjd		{ 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
189213172Spjd		{ 'J', "newpassfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
190213172Spjd		{ 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
191213172Spjd		{ 'K', "newkeyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
192212554Spjd		{ 'n', "keyno", "-1", G_TYPE_NUMBER },
193162868Spjd		{ 'p', "nopassphrase", NULL, G_TYPE_BOOL },
194162868Spjd		{ 'P', "nonewpassphrase", NULL, G_TYPE_BOOL },
195148456Spjd		G_OPT_SENTINEL
196148456Spjd	    },
197213172Spjd	    "[-pPv] [-n keyno] [-i iterations] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov"
198148456Spjd	},
199148456Spjd	{ "delkey", G_FLAG_VERBOSE, eli_main,
200148456Spjd	    {
201162868Spjd		{ 'a', "all", NULL, G_TYPE_BOOL },
202162868Spjd		{ 'f', "force", NULL, G_TYPE_BOOL },
203212554Spjd		{ 'n', "keyno", "-1", G_TYPE_NUMBER },
204148456Spjd		G_OPT_SENTINEL
205148456Spjd	    },
206212554Spjd	    "[-afv] [-n keyno] prov"
207148456Spjd	},
208214118Spjd	{ "suspend", G_FLAG_VERBOSE, NULL,
209214118Spjd	    {
210214118Spjd		{ 'a', "all", NULL, G_TYPE_BOOL },
211214118Spjd		G_OPT_SENTINEL
212214118Spjd	    },
213214118Spjd	    "[-v] -a | prov ..."
214214118Spjd	},
215214118Spjd	{ "resume", G_FLAG_VERBOSE, eli_main,
216214118Spjd	    {
217214118Spjd		{ 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
218214118Spjd		{ 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
219214118Spjd		{ 'p', "nopassphrase", NULL, G_TYPE_BOOL },
220214118Spjd		G_OPT_SENTINEL
221214118Spjd	    },
222214118Spjd	    "[-pv] [-j passfile] [-k keyfile] prov"
223214118Spjd	},
224148456Spjd	{ "kill", G_FLAG_VERBOSE, eli_main,
225148456Spjd	    {
226162868Spjd		{ 'a', "all", NULL, G_TYPE_BOOL },
227148456Spjd		G_OPT_SENTINEL
228148456Spjd	    },
229212554Spjd	    "[-av] [prov ...]"
230148456Spjd	},
231212554Spjd	{ "backup", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS,
232148456Spjd	    "[-v] prov file"
233148456Spjd	},
234212934Sbrian	{ "restore", G_FLAG_VERBOSE, eli_main,
235212934Sbrian	    {
236212934Sbrian		{ 'f', "force", NULL, G_TYPE_BOOL },
237212934Sbrian		G_OPT_SENTINEL
238212934Sbrian	    },
239212934Sbrian	    "[-fv] file prov"
240148456Spjd	},
241212934Sbrian	{ "resize", G_FLAG_VERBOSE, eli_main,
242212934Sbrian	    {
243212934Sbrian		{ 's', "oldsize", NULL, G_TYPE_NUMBER },
244212934Sbrian		G_OPT_SENTINEL
245212934Sbrian	    },
246212934Sbrian	    "[-v] -s oldsize prov"
247212934Sbrian	},
248226723Spjd	{ "version", G_FLAG_LOADKLD, eli_main, G_NULL_OPTS,
249226723Spjd	    "[prov ...]"
250226723Spjd	},
251212554Spjd	{ "clear", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS,
252148456Spjd	    "[-v] prov ..."
253148456Spjd	},
254212554Spjd	{ "dump", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS,
255148456Spjd	    "[-v] prov ..."
256148456Spjd	},
257148456Spjd	G_CMD_SENTINEL
258148456Spjd};
259148456Spjd
260148456Spjdstatic int verbose = 0;
261148456Spjd
262248475Spjd#define	BUFSIZE	1024
263248475Spjd
264148456Spjdstatic int
265148456Spjdeli_protect(struct gctl_req *req)
266148456Spjd{
267148456Spjd	struct rlimit rl;
268148456Spjd
269148456Spjd	/* Disable core dumps. */
270148456Spjd	rl.rlim_cur = 0;
271148456Spjd	rl.rlim_max = 0;
272148456Spjd	if (setrlimit(RLIMIT_CORE, &rl) == -1) {
273148456Spjd		gctl_error(req, "Cannot disable core dumps: %s.",
274148456Spjd		    strerror(errno));
275148456Spjd		return (-1);
276148456Spjd	}
277148456Spjd	/* Disable swapping. */
278148456Spjd	if (mlockall(MCL_FUTURE) == -1) {
279148456Spjd		gctl_error(req, "Cannot lock memory: %s.", strerror(errno));
280148456Spjd		return (-1);
281148456Spjd	}
282148456Spjd	return (0);
283148456Spjd}
284148456Spjd
285148456Spjdstatic void
286213172Spjdeli_main(struct gctl_req *req, unsigned int flags)
287148456Spjd{
288148456Spjd	const char *name;
289148456Spjd
290148456Spjd	if (eli_protect(req) == -1)
291148456Spjd		return;
292148456Spjd
293148456Spjd	if ((flags & G_FLAG_VERBOSE) != 0)
294148456Spjd		verbose = 1;
295148456Spjd
296153190Spjd	name = gctl_get_ascii(req, "verb");
297148456Spjd	if (name == NULL) {
298148456Spjd		gctl_error(req, "No '%s' argument.", "verb");
299148456Spjd		return;
300148456Spjd	}
301148456Spjd	if (strcmp(name, "init") == 0 || strcmp(name, "label") == 0)
302148456Spjd		eli_init(req);
303148456Spjd	else if (strcmp(name, "attach") == 0)
304148456Spjd		eli_attach(req);
305162353Spjd	else if (strcmp(name, "configure") == 0)
306162353Spjd		eli_configure(req);
307148456Spjd	else if (strcmp(name, "setkey") == 0)
308148456Spjd		eli_setkey(req);
309148456Spjd	else if (strcmp(name, "delkey") == 0)
310148456Spjd		eli_delkey(req);
311214118Spjd	else if (strcmp(name, "resume") == 0)
312214118Spjd		eli_resume(req);
313148456Spjd	else if (strcmp(name, "kill") == 0)
314148456Spjd		eli_kill(req);
315148456Spjd	else if (strcmp(name, "backup") == 0)
316148456Spjd		eli_backup(req);
317148456Spjd	else if (strcmp(name, "restore") == 0)
318148456Spjd		eli_restore(req);
319212934Sbrian	else if (strcmp(name, "resize") == 0)
320212934Sbrian		eli_resize(req);
321226723Spjd	else if (strcmp(name, "version") == 0)
322226723Spjd		eli_version(req);
323148456Spjd	else if (strcmp(name, "dump") == 0)
324148456Spjd		eli_dump(req);
325148456Spjd	else if (strcmp(name, "clear") == 0)
326148456Spjd		eli_clear(req);
327148456Spjd	else
328148456Spjd		gctl_error(req, "Unknown command: %s.", name);
329148456Spjd}
330148456Spjd
331226717Spjdstatic bool
332148456Spjdeli_is_attached(const char *prov)
333148456Spjd{
334148456Spjd	char name[MAXPATHLEN];
335148456Spjd
336148456Spjd	/*
337148456Spjd	 * Not the best way to do it, but the easiest.
338148456Spjd	 * We try to open provider and check if it is a GEOM provider
339148456Spjd	 * by asking about its sectorsize.
340148456Spjd	 */
341148456Spjd	snprintf(name, sizeof(name), "%s%s", prov, G_ELI_SUFFIX);
342226717Spjd	return (g_get_sectorsize(name) > 0);
343148456Spjd}
344148456Spjd
345213172Spjdstatic int
346213172Spjdeli_genkey_files(struct gctl_req *req, bool new, const char *type,
347213172Spjd    struct hmac_ctx *ctxp, char *passbuf, size_t passbufsize)
348148456Spjd{
349248475Spjd	char *p, buf[BUFSIZE], argname[16];
350213172Spjd	const char *file;
351213172Spjd	int error, fd, i;
352213172Spjd	ssize_t done;
353148456Spjd
354213172Spjd	assert((strcmp(type, "keyfile") == 0 && ctxp != NULL &&
355213172Spjd	    passbuf == NULL && passbufsize == 0) ||
356213172Spjd	    (strcmp(type, "passfile") == 0 && ctxp == NULL &&
357213172Spjd	    passbuf != NULL && passbufsize > 0));
358213172Spjd	assert(strcmp(type, "keyfile") == 0 || passbuf[0] == '\0');
359148456Spjd
360213172Spjd	for (i = 0; ; i++) {
361213172Spjd		snprintf(argname, sizeof(argname), "%s%s%d",
362213172Spjd		    new ? "new" : "", type, i);
363148456Spjd
364213172Spjd		/* No more {key,pass}files? */
365213172Spjd		if (!gctl_has_param(req, argname))
366213172Spjd			return (i);
367148456Spjd
368215704Sbrucec		file = gctl_get_ascii(req, "%s", argname);
369213172Spjd		assert(file != NULL);
370213172Spjd
371213172Spjd		if (strcmp(file, "-") == 0)
372148456Spjd			fd = STDIN_FILENO;
373148456Spjd		else {
374213172Spjd			fd = open(file, O_RDONLY);
375148456Spjd			if (fd == -1) {
376213172Spjd				gctl_error(req, "Cannot open %s %s: %s.",
377213172Spjd				    type, file, strerror(errno));
378213172Spjd				return (-1);
379148456Spjd			}
380148456Spjd		}
381213172Spjd		if (strcmp(type, "keyfile") == 0) {
382213172Spjd			while ((done = read(fd, buf, sizeof(buf))) > 0)
383213172Spjd				g_eli_crypto_hmac_update(ctxp, buf, done);
384213172Spjd		} else /* if (strcmp(type, "passfile") == 0) */ {
385246621Spjd			assert(strcmp(type, "passfile") == 0);
386246621Spjd
387213172Spjd			while ((done = read(fd, buf, sizeof(buf) - 1)) > 0) {
388213172Spjd				buf[done] = '\0';
389213172Spjd				p = strchr(buf, '\n');
390213172Spjd				if (p != NULL) {
391213172Spjd					*p = '\0';
392213172Spjd					done = p - buf;
393213172Spjd				}
394213172Spjd				if (strlcat(passbuf, buf, passbufsize) >=
395213172Spjd				    passbufsize) {
396213172Spjd					gctl_error(req,
397213172Spjd					    "Passphrase in %s too long.", file);
398213172Spjd					bzero(buf, sizeof(buf));
399213172Spjd					return (-1);
400213172Spjd				}
401213172Spjd				if (p != NULL)
402213172Spjd					break;
403213172Spjd			}
404213172Spjd		}
405148456Spjd		error = errno;
406213172Spjd		if (strcmp(file, "-") != 0)
407148456Spjd			close(fd);
408148456Spjd		bzero(buf, sizeof(buf));
409148456Spjd		if (done == -1) {
410213172Spjd			gctl_error(req, "Cannot read %s %s: %s.",
411213172Spjd			    type, file, strerror(error));
412213172Spjd			return (-1);
413148456Spjd		}
414148456Spjd	}
415213172Spjd	/* NOTREACHED */
416213172Spjd}
417148456Spjd
418213172Spjdstatic int
419213172Spjdeli_genkey_passphrase_prompt(struct gctl_req *req, bool new, char *passbuf,
420213172Spjd    size_t passbufsize)
421213172Spjd{
422213172Spjd	char *p;
423148456Spjd
424213172Spjd	for (;;) {
425213172Spjd		p = readpassphrase(
426213172Spjd		    new ? "Enter new passphrase:" : "Enter passphrase:",
427213172Spjd		    passbuf, passbufsize, RPP_ECHO_OFF | RPP_REQUIRE_TTY);
428213172Spjd		if (p == NULL) {
429213172Spjd			bzero(passbuf, passbufsize);
430213172Spjd			gctl_error(req, "Cannot read passphrase: %s.",
431213172Spjd			    strerror(errno));
432213172Spjd			return (-1);
433149047Spjd		}
434213172Spjd
435213172Spjd		if (new) {
436248475Spjd			char tmpbuf[BUFSIZE];
437213172Spjd
438213172Spjd			p = readpassphrase("Reenter new passphrase: ",
439213172Spjd			    tmpbuf, sizeof(tmpbuf),
440213172Spjd			    RPP_ECHO_OFF | RPP_REQUIRE_TTY);
441148456Spjd			if (p == NULL) {
442213172Spjd				bzero(passbuf, passbufsize);
443213172Spjd				gctl_error(req,
444213172Spjd				    "Cannot read passphrase: %s.",
445148456Spjd				    strerror(errno));
446213172Spjd				return (-1);
447148456Spjd			}
448213172Spjd
449213172Spjd			if (strcmp(passbuf, tmpbuf) != 0) {
450213172Spjd				bzero(passbuf, passbufsize);
451213172Spjd				fprintf(stderr, "They didn't match.\n");
452213172Spjd				continue;
453148456Spjd			}
454213172Spjd			bzero(tmpbuf, sizeof(tmpbuf));
455148456Spjd		}
456213172Spjd		return (0);
457213172Spjd	}
458213172Spjd	/* NOTREACHED */
459213172Spjd}
460213172Spjd
461213172Spjdstatic int
462213172Spjdeli_genkey_passphrase(struct gctl_req *req, struct g_eli_metadata *md, bool new,
463213172Spjd    struct hmac_ctx *ctxp)
464213172Spjd{
465248475Spjd	char passbuf[BUFSIZE];
466213172Spjd	bool nopassphrase;
467213172Spjd	int nfiles;
468213172Spjd
469213172Spjd	nopassphrase =
470213172Spjd	    gctl_get_int(req, new ? "nonewpassphrase" : "nopassphrase");
471213172Spjd	if (nopassphrase) {
472213172Spjd		if (gctl_has_param(req, new ? "newpassfile0" : "passfile0")) {
473213172Spjd			gctl_error(req,
474213172Spjd			    "Options -%c and -%c are mutually exclusive.",
475213172Spjd			    new ? 'J' : 'j', new ? 'P' : 'p');
476213172Spjd			return (-1);
477148456Spjd		}
478213172Spjd		return (0);
479213172Spjd	}
480148456Spjd
481213172Spjd	if (!new && md->md_iterations == -1) {
482213172Spjd		gctl_error(req, "Missing -p flag.");
483213172Spjd		return (-1);
484213172Spjd	}
485213172Spjd	passbuf[0] = '\0';
486213172Spjd	nfiles = eli_genkey_files(req, new, "passfile", NULL, passbuf,
487213172Spjd	    sizeof(passbuf));
488213172Spjd	if (nfiles == -1)
489213172Spjd		return (-1);
490213172Spjd	else if (nfiles == 0) {
491213172Spjd		if (eli_genkey_passphrase_prompt(req, new, passbuf,
492213172Spjd		    sizeof(passbuf)) == -1) {
493213172Spjd			return (-1);
494148456Spjd		}
495148456Spjd	}
496213172Spjd	/*
497213172Spjd	 * Field md_iterations equal to -1 means "choose some sane
498213172Spjd	 * value for me".
499213172Spjd	 */
500213172Spjd	if (md->md_iterations == -1) {
501213172Spjd		assert(new);
502213172Spjd		if (verbose)
503213172Spjd			printf("Calculating number of iterations...\n");
504213172Spjd		md->md_iterations = pkcs5v2_calculate(2000000);
505213172Spjd		assert(md->md_iterations > 0);
506213172Spjd		if (verbose) {
507213172Spjd			printf("Done, using %d iterations.\n",
508213172Spjd			    md->md_iterations);
509213172Spjd		}
510213172Spjd	}
511213172Spjd	/*
512213172Spjd	 * If md_iterations is equal to 0, user doesn't want PKCS#5v2.
513213172Spjd	 */
514213172Spjd	if (md->md_iterations == 0) {
515213172Spjd		g_eli_crypto_hmac_update(ctxp, md->md_salt,
516213172Spjd		    sizeof(md->md_salt));
517213172Spjd		g_eli_crypto_hmac_update(ctxp, passbuf, strlen(passbuf));
518213172Spjd	} else /* if (md->md_iterations > 0) */ {
519213172Spjd		unsigned char dkey[G_ELI_USERKEYLEN];
520213172Spjd
521213172Spjd		pkcs5v2_genkey(dkey, sizeof(dkey), md->md_salt,
522213172Spjd		    sizeof(md->md_salt), passbuf, md->md_iterations);
523213172Spjd		g_eli_crypto_hmac_update(ctxp, dkey, sizeof(dkey));
524213172Spjd		bzero(dkey, sizeof(dkey));
525213172Spjd	}
526213172Spjd	bzero(passbuf, sizeof(passbuf));
527213172Spjd
528213172Spjd	return (0);
529213172Spjd}
530213172Spjd
531213172Spjdstatic unsigned char *
532213172Spjdeli_genkey(struct gctl_req *req, struct g_eli_metadata *md, unsigned char *key,
533213172Spjd    bool new)
534213172Spjd{
535213172Spjd	struct hmac_ctx ctx;
536213172Spjd	bool nopassphrase;
537213172Spjd	int nfiles;
538213172Spjd
539213172Spjd	nopassphrase =
540213172Spjd	    gctl_get_int(req, new ? "nonewpassphrase" : "nopassphrase");
541213172Spjd
542213172Spjd	g_eli_crypto_hmac_init(&ctx, NULL, 0);
543213172Spjd
544213172Spjd	nfiles = eli_genkey_files(req, new, "keyfile", &ctx, NULL, 0);
545213172Spjd	if (nfiles == -1)
546213172Spjd		return (NULL);
547213172Spjd	else if (nfiles == 0 && nopassphrase) {
548213172Spjd		gctl_error(req, "No key components given.");
549213172Spjd		return (NULL);
550213172Spjd	}
551213172Spjd
552213172Spjd	if (eli_genkey_passphrase(req, md, new, &ctx) == -1)
553213172Spjd		return (NULL);
554213172Spjd
555148456Spjd	g_eli_crypto_hmac_final(&ctx, key, 0);
556213172Spjd
557148456Spjd	return (key);
558148456Spjd}
559148456Spjd
560148456Spjdstatic int
561148456Spjdeli_metadata_read(struct gctl_req *req, const char *prov,
562148456Spjd    struct g_eli_metadata *md)
563148456Spjd{
564148456Spjd	unsigned char sector[sizeof(struct g_eli_metadata)];
565148456Spjd	int error;
566148456Spjd
567148456Spjd	if (g_get_sectorsize(prov) == 0) {
568148456Spjd		int fd;
569148456Spjd
570148456Spjd		/* This is a file probably. */
571148456Spjd		fd = open(prov, O_RDONLY);
572148456Spjd		if (fd == -1) {
573148456Spjd			gctl_error(req, "Cannot open %s: %s.", prov,
574148456Spjd			    strerror(errno));
575148456Spjd			return (-1);
576148456Spjd		}
577148456Spjd		if (read(fd, sector, sizeof(sector)) != sizeof(sector)) {
578148456Spjd			gctl_error(req, "Cannot read metadata from %s: %s.",
579148456Spjd			    prov, strerror(errno));
580148456Spjd			close(fd);
581148456Spjd			return (-1);
582148456Spjd		}
583148456Spjd		close(fd);
584148456Spjd	} else {
585148456Spjd		/* This is a GEOM provider. */
586148456Spjd		error = g_metadata_read(prov, sector, sizeof(sector),
587148456Spjd		    G_ELI_MAGIC);
588148456Spjd		if (error != 0) {
589148456Spjd			gctl_error(req, "Cannot read metadata from %s: %s.",
590148456Spjd			    prov, strerror(error));
591148456Spjd			return (-1);
592148456Spjd		}
593148456Spjd	}
594226722Spjd	error = eli_metadata_decode(sector, md);
595226722Spjd	switch (error) {
596226722Spjd	case 0:
597226722Spjd		break;
598226722Spjd	case EOPNOTSUPP:
599226722Spjd		gctl_error(req,
600226722Spjd		    "Provider's %s metadata version %u is too new.\n"
601226722Spjd		    "geli: The highest supported version is %u.",
602226722Spjd		    prov, (unsigned int)md->md_version, G_ELI_VERSION);
603148456Spjd		return (-1);
604226722Spjd	case EINVAL:
605226722Spjd		gctl_error(req, "Inconsistent provider's %s metadata.", prov);
606226722Spjd		return (-1);
607226722Spjd	default:
608226722Spjd		gctl_error(req,
609226722Spjd		    "Unexpected error while decoding provider's %s metadata: %s.",
610226722Spjd		    prov, strerror(error));
611226722Spjd		return (-1);
612148456Spjd	}
613148456Spjd	return (0);
614148456Spjd}
615148456Spjd
616148456Spjdstatic int
617148456Spjdeli_metadata_store(struct gctl_req *req, const char *prov,
618148456Spjd    struct g_eli_metadata *md)
619148456Spjd{
620148456Spjd	unsigned char sector[sizeof(struct g_eli_metadata)];
621148456Spjd	int error;
622148456Spjd
623148456Spjd	eli_metadata_encode(md, sector);
624148456Spjd	if (g_get_sectorsize(prov) == 0) {
625148456Spjd		int fd;
626148456Spjd
627148456Spjd		/* This is a file probably. */
628148456Spjd		fd = open(prov, O_WRONLY | O_TRUNC);
629148456Spjd		if (fd == -1) {
630148456Spjd			gctl_error(req, "Cannot open %s: %s.", prov,
631148456Spjd			    strerror(errno));
632148456Spjd			bzero(sector, sizeof(sector));
633148456Spjd			return (-1);
634148456Spjd		}
635148456Spjd		if (write(fd, sector, sizeof(sector)) != sizeof(sector)) {
636148456Spjd			gctl_error(req, "Cannot write metadata to %s: %s.",
637148456Spjd			    prov, strerror(errno));
638148456Spjd			bzero(sector, sizeof(sector));
639148456Spjd			close(fd);
640148456Spjd			return (-1);
641148456Spjd		}
642148456Spjd		close(fd);
643148456Spjd	} else {
644148456Spjd		/* This is a GEOM provider. */
645148456Spjd		error = g_metadata_store(prov, sector, sizeof(sector));
646148456Spjd		if (error != 0) {
647148456Spjd			gctl_error(req, "Cannot write metadata to %s: %s.",
648148456Spjd			    prov, strerror(errno));
649148456Spjd			bzero(sector, sizeof(sector));
650148456Spjd			return (-1);
651148456Spjd		}
652148456Spjd	}
653148456Spjd	bzero(sector, sizeof(sector));
654148456Spjd	return (0);
655148456Spjd}
656148456Spjd
657148456Spjdstatic void
658148456Spjdeli_init(struct gctl_req *req)
659148456Spjd{
660148456Spjd	struct g_eli_metadata md;
661148456Spjd	unsigned char sector[sizeof(struct g_eli_metadata)];
662148456Spjd	unsigned char key[G_ELI_USERKEYLEN];
663182452Spjd	char backfile[MAXPATHLEN];
664148456Spjd	const char *str, *prov;
665226733Spjd	unsigned int secsize, version;
666148456Spjd	off_t mediasize;
667153190Spjd	intmax_t val;
668155536Spjd	int error, nargs;
669148456Spjd
670153190Spjd	nargs = gctl_get_int(req, "nargs");
671153190Spjd	if (nargs != 1) {
672158214Spjd		gctl_error(req, "Invalid number of arguments.");
673148456Spjd		return;
674148456Spjd	}
675153190Spjd	prov = gctl_get_ascii(req, "arg0");
676148456Spjd	mediasize = g_get_mediasize(prov);
677148456Spjd	secsize = g_get_sectorsize(prov);
678148456Spjd	if (mediasize == 0 || secsize == 0) {
679148456Spjd		gctl_error(req, "Cannot get informations about %s: %s.", prov,
680148456Spjd		    strerror(errno));
681148456Spjd		return;
682148456Spjd	}
683148456Spjd
684148456Spjd	bzero(&md, sizeof(md));
685148456Spjd	strlcpy(md.md_magic, G_ELI_MAGIC, sizeof(md.md_magic));
686226733Spjd	val = gctl_get_intmax(req, "mdversion");
687226733Spjd	if (val == -1) {
688226733Spjd		version = G_ELI_VERSION;
689226733Spjd	} else if (val < 0 || val > G_ELI_VERSION) {
690226733Spjd		gctl_error(req,
691226733Spjd		    "Invalid version specified should be between %u and %u.",
692226733Spjd		    G_ELI_VERSION_00, G_ELI_VERSION);
693226733Spjd		return;
694226733Spjd	} else {
695226733Spjd		version = val;
696226733Spjd	}
697226733Spjd	md.md_version = version;
698148456Spjd	md.md_flags = 0;
699155536Spjd	if (gctl_get_int(req, "boot"))
700148456Spjd		md.md_flags |= G_ELI_FLAG_BOOT;
701159361Spjd	md.md_ealgo = CRYPTO_ALGORITHM_MIN - 1;
702159308Spjd	str = gctl_get_ascii(req, "aalgo");
703212547Spjd	if (*str != '\0') {
704226733Spjd		if (version < G_ELI_VERSION_01) {
705226733Spjd			gctl_error(req,
706226733Spjd			    "Data authentication is supported starting from version %u.",
707226733Spjd			    G_ELI_VERSION_01);
708226733Spjd			return;
709226733Spjd		}
710159308Spjd		md.md_aalgo = g_eli_str2aalgo(str);
711159361Spjd		if (md.md_aalgo >= CRYPTO_ALGORITHM_MIN &&
712159361Spjd		    md.md_aalgo <= CRYPTO_ALGORITHM_MAX) {
713159361Spjd			md.md_flags |= G_ELI_FLAG_AUTH;
714159361Spjd		} else {
715159361Spjd			/*
716159361Spjd			 * For backward compatibility, check if the -a option
717159361Spjd			 * was used to provide encryption algorithm.
718159361Spjd			 */
719159361Spjd			md.md_ealgo = g_eli_str2ealgo(str);
720159361Spjd			if (md.md_ealgo < CRYPTO_ALGORITHM_MIN ||
721159361Spjd			    md.md_ealgo > CRYPTO_ALGORITHM_MAX) {
722159361Spjd				gctl_error(req,
723159361Spjd				    "Invalid authentication algorithm.");
724159361Spjd				return;
725159361Spjd			} else {
726159361Spjd				fprintf(stderr, "warning: The -e option, not "
727159361Spjd				    "the -a option is now used to specify "
728159361Spjd				    "encryption algorithm to use.\n");
729159361Spjd			}
730159308Spjd		}
731159308Spjd	}
732159308Spjd	if (md.md_ealgo < CRYPTO_ALGORITHM_MIN ||
733159308Spjd	    md.md_ealgo > CRYPTO_ALGORITHM_MAX) {
734159361Spjd		str = gctl_get_ascii(req, "ealgo");
735226733Spjd		if (*str == '\0') {
736226733Spjd			if (version < G_ELI_VERSION_05)
737226733Spjd				str = "aes-cbc";
738226733Spjd			else
739226733Spjd				str = GELI_ENC_ALGO;
740226733Spjd		}
741159361Spjd		md.md_ealgo = g_eli_str2ealgo(str);
742159361Spjd		if (md.md_ealgo < CRYPTO_ALGORITHM_MIN ||
743159361Spjd		    md.md_ealgo > CRYPTO_ALGORITHM_MAX) {
744159361Spjd			gctl_error(req, "Invalid encryption algorithm.");
745159361Spjd			return;
746159361Spjd		}
747226733Spjd		if (md.md_ealgo == CRYPTO_CAMELLIA_CBC &&
748226733Spjd		    version < G_ELI_VERSION_04) {
749226733Spjd			gctl_error(req,
750226733Spjd			    "Camellia-CBC algorithm is supported starting from version %u.",
751226733Spjd			    G_ELI_VERSION_04);
752226733Spjd			return;
753226733Spjd		}
754226733Spjd		if (md.md_ealgo == CRYPTO_AES_XTS &&
755226733Spjd		    version < G_ELI_VERSION_05) {
756226733Spjd			gctl_error(req,
757226733Spjd			    "AES-XTS algorithm is supported starting from version %u.",
758226733Spjd			    G_ELI_VERSION_05);
759226733Spjd			return;
760226733Spjd		}
761148456Spjd	}
762153190Spjd	val = gctl_get_intmax(req, "keylen");
763153190Spjd	md.md_keylen = val;
764159308Spjd	md.md_keylen = g_eli_keylen(md.md_ealgo, md.md_keylen);
765148456Spjd	if (md.md_keylen == 0) {
766148456Spjd		gctl_error(req, "Invalid key length.");
767148456Spjd		return;
768148456Spjd	}
769148456Spjd	md.md_provsize = mediasize;
770148456Spjd
771153190Spjd	val = gctl_get_intmax(req, "iterations");
772155536Spjd	if (val != -1) {
773155536Spjd		int nonewpassphrase;
774155536Spjd
775155536Spjd		/*
776155536Spjd		 * Don't allow to set iterations when there will be no
777155536Spjd		 * passphrase.
778155536Spjd		 */
779155536Spjd		nonewpassphrase = gctl_get_int(req, "nonewpassphrase");
780155536Spjd		if (nonewpassphrase) {
781155536Spjd			gctl_error(req,
782155536Spjd			    "Options -i and -P are mutually exclusive.");
783155536Spjd			return;
784155536Spjd		}
785155536Spjd	}
786153190Spjd	md.md_iterations = val;
787148456Spjd
788153190Spjd	val = gctl_get_intmax(req, "sectorsize");
789153190Spjd	if (val == 0)
790148456Spjd		md.md_sectorsize = secsize;
791148456Spjd	else {
792153190Spjd		if (val < 0 || (val % secsize) != 0) {
793148456Spjd			gctl_error(req, "Invalid sector size.");
794148456Spjd			return;
795148456Spjd		}
796167229Spjd		if (val > sysconf(_SC_PAGE_SIZE)) {
797214404Spjd			fprintf(stderr,
798214404Spjd			    "warning: Using sectorsize bigger than the page size!\n");
799167229Spjd		}
800153190Spjd		md.md_sectorsize = val;
801148456Spjd	}
802148456Spjd
803148456Spjd	md.md_keys = 0x01;
804246620Spjd	arc4random_buf(md.md_salt, sizeof(md.md_salt));
805246620Spjd	arc4random_buf(md.md_mkeys, sizeof(md.md_mkeys));
806148456Spjd
807148456Spjd	/* Generate user key. */
808213172Spjd	if (eli_genkey(req, &md, key, true) == NULL) {
809148456Spjd		bzero(key, sizeof(key));
810148456Spjd		bzero(&md, sizeof(md));
811148456Spjd		return;
812148456Spjd	}
813148456Spjd
814148456Spjd	/* Encrypt the first and the only Master Key. */
815159308Spjd	error = g_eli_mkey_encrypt(md.md_ealgo, key, md.md_keylen, md.md_mkeys);
816148456Spjd	bzero(key, sizeof(key));
817148456Spjd	if (error != 0) {
818148456Spjd		bzero(&md, sizeof(md));
819148456Spjd		gctl_error(req, "Cannot encrypt Master Key: %s.",
820148456Spjd		    strerror(error));
821148456Spjd		return;
822148456Spjd	}
823148456Spjd
824148456Spjd	eli_metadata_encode(&md, sector);
825148456Spjd	bzero(&md, sizeof(md));
826148456Spjd	error = g_metadata_store(prov, sector, sizeof(sector));
827148456Spjd	bzero(sector, sizeof(sector));
828148456Spjd	if (error != 0) {
829148456Spjd		gctl_error(req, "Cannot store metadata on %s: %s.", prov,
830148456Spjd		    strerror(error));
831148456Spjd		return;
832148456Spjd	}
833148456Spjd	if (verbose)
834148456Spjd		printf("Metadata value stored on %s.\n", prov);
835182452Spjd	/* Backup metadata to a file. */
836182452Spjd	str = gctl_get_ascii(req, "backupfile");
837182452Spjd	if (str[0] != '\0') {
838182452Spjd		/* Backupfile given be the user, just copy it. */
839182452Spjd		strlcpy(backfile, str, sizeof(backfile));
840182452Spjd	} else {
841182452Spjd		/* Generate file name automatically. */
842182452Spjd		const char *p = prov;
843182452Spjd		unsigned int i;
844182452Spjd
845213662Sae		if (strncmp(p, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
846213662Sae			p += sizeof(_PATH_DEV) - 1;
847182452Spjd		snprintf(backfile, sizeof(backfile), "%s%s.eli",
848182452Spjd		    GELI_BACKUP_DIR, p);
849182452Spjd		/* Replace all / with _. */
850182452Spjd		for (i = strlen(GELI_BACKUP_DIR); backfile[i] != '\0'; i++) {
851182452Spjd			if (backfile[i] == '/')
852182452Spjd				backfile[i] = '_';
853182452Spjd		}
854182452Spjd	}
855182452Spjd	if (strcmp(backfile, "none") != 0 &&
856182452Spjd	    eli_backup_create(req, prov, backfile) == 0) {
857182452Spjd		printf("\nMetadata backup can be found in %s and\n", backfile);
858182452Spjd		printf("can be restored with the following command:\n");
859182452Spjd		printf("\n\t# geli restore %s %s\n\n", backfile, prov);
860182452Spjd	}
861148456Spjd}
862148456Spjd
863148456Spjdstatic void
864148456Spjdeli_attach(struct gctl_req *req)
865148456Spjd{
866148456Spjd	struct g_eli_metadata md;
867148456Spjd	unsigned char key[G_ELI_USERKEYLEN];
868148456Spjd	const char *prov;
869212934Sbrian	off_t mediasize;
870153190Spjd	int nargs;
871148456Spjd
872153190Spjd	nargs = gctl_get_int(req, "nargs");
873153190Spjd	if (nargs != 1) {
874158214Spjd		gctl_error(req, "Invalid number of arguments.");
875148456Spjd		return;
876148456Spjd	}
877153190Spjd	prov = gctl_get_ascii(req, "arg0");
878148456Spjd
879148456Spjd	if (eli_metadata_read(req, prov, &md) == -1)
880148456Spjd		return;
881148456Spjd
882212934Sbrian	mediasize = g_get_mediasize(prov);
883212934Sbrian	if (md.md_provsize != (uint64_t)mediasize) {
884212934Sbrian		gctl_error(req, "Provider size mismatch.");
885212934Sbrian		return;
886212934Sbrian	}
887212934Sbrian
888213172Spjd	if (eli_genkey(req, &md, key, false) == NULL) {
889148456Spjd		bzero(key, sizeof(key));
890148456Spjd		return;
891148456Spjd	}
892148456Spjd
893148456Spjd	gctl_ro_param(req, "key", sizeof(key), key);
894148456Spjd	if (gctl_issue(req) == NULL) {
895148456Spjd		if (verbose)
896166892Spjd			printf("Attached to %s.\n", prov);
897148456Spjd	}
898148456Spjd	bzero(key, sizeof(key));
899148456Spjd}
900148456Spjd
901148456Spjdstatic void
902213172Spjdeli_configure_detached(struct gctl_req *req, const char *prov, bool boot)
903162353Spjd{
904162353Spjd	struct g_eli_metadata md;
905162353Spjd
906162353Spjd	if (eli_metadata_read(req, prov, &md) == -1)
907162353Spjd		return;
908162353Spjd
909162353Spjd	if (boot && (md.md_flags & G_ELI_FLAG_BOOT)) {
910162353Spjd		if (verbose)
911162353Spjd			printf("BOOT flag already configured for %s.\n", prov);
912162353Spjd	} else if (!boot && !(md.md_flags & G_ELI_FLAG_BOOT)) {
913162353Spjd		if (verbose)
914162353Spjd			printf("BOOT flag not configured for %s.\n", prov);
915162353Spjd	} else {
916162353Spjd		if (boot)
917162353Spjd			md.md_flags |= G_ELI_FLAG_BOOT;
918162353Spjd		else
919162353Spjd			md.md_flags &= ~G_ELI_FLAG_BOOT;
920162353Spjd		eli_metadata_store(req, prov, &md);
921162353Spjd	}
922162353Spjd	bzero(&md, sizeof(md));
923162353Spjd}
924162353Spjd
925162353Spjdstatic void
926162353Spjdeli_configure(struct gctl_req *req)
927162353Spjd{
928162353Spjd	const char *prov;
929213172Spjd	bool boot, noboot;
930213172Spjd	int i, nargs;
931162353Spjd
932162353Spjd	nargs = gctl_get_int(req, "nargs");
933162353Spjd	if (nargs == 0) {
934162353Spjd		gctl_error(req, "Too few arguments.");
935162353Spjd		return;
936162353Spjd	}
937162353Spjd
938162353Spjd	boot = gctl_get_int(req, "boot");
939162353Spjd	noboot = gctl_get_int(req, "noboot");
940162353Spjd
941162353Spjd	if (boot && noboot) {
942162353Spjd		gctl_error(req, "Options -b and -B are mutually exclusive.");
943162353Spjd		return;
944162353Spjd	}
945162353Spjd	if (!boot && !noboot) {
946162353Spjd		gctl_error(req, "No option given.");
947162353Spjd		return;
948162353Spjd	}
949162353Spjd
950162353Spjd	/* First attached providers. */
951162353Spjd	gctl_issue(req);
952162353Spjd	/* Now the rest. */
953162353Spjd	for (i = 0; i < nargs; i++) {
954162353Spjd		prov = gctl_get_ascii(req, "arg%d", i);
955162353Spjd		if (!eli_is_attached(prov))
956162353Spjd			eli_configure_detached(req, prov, boot);
957162353Spjd	}
958162353Spjd}
959162353Spjd
960162353Spjdstatic void
961155101Spjdeli_setkey_attached(struct gctl_req *req, struct g_eli_metadata *md)
962148456Spjd{
963148456Spjd	unsigned char key[G_ELI_USERKEYLEN];
964166216Spjd	intmax_t val, old = 0;
965166216Spjd	int error;
966148456Spjd
967153190Spjd	val = gctl_get_intmax(req, "iterations");
968149304Spjd	/* Check if iterations number should be changed. */
969153190Spjd	if (val != -1)
970153190Spjd		md->md_iterations = val;
971166216Spjd	else
972166216Spjd		old = md->md_iterations;
973148456Spjd
974148456Spjd	/* Generate key for Master Key encryption. */
975213172Spjd	if (eli_genkey(req, md, key, true) == NULL) {
976148456Spjd		bzero(key, sizeof(key));
977148456Spjd		return;
978148456Spjd	}
979166216Spjd	/*
980166216Spjd	 * If number of iterations has changed, but wasn't given as a
981166216Spjd	 * command-line argument, update the request.
982166216Spjd	 */
983166216Spjd	if (val == -1 && md->md_iterations != old) {
984166216Spjd		error = gctl_change_param(req, "iterations", sizeof(intmax_t),
985166216Spjd		    &md->md_iterations);
986166216Spjd		assert(error == 0);
987166216Spjd	}
988148456Spjd
989148456Spjd	gctl_ro_param(req, "key", sizeof(key), key);
990148456Spjd	gctl_issue(req);
991148456Spjd	bzero(key, sizeof(key));
992148456Spjd}
993148456Spjd
994148456Spjdstatic void
995149304Spjdeli_setkey_detached(struct gctl_req *req, const char *prov,
996149304Spjd struct g_eli_metadata *md)
997148456Spjd{
998148456Spjd	unsigned char key[G_ELI_USERKEYLEN], mkey[G_ELI_DATAIVKEYLEN];
999148456Spjd	unsigned char *mkeydst;
1000213172Spjd	unsigned int nkey;
1001153190Spjd	intmax_t val;
1002148456Spjd	int error;
1003148456Spjd
1004149928Spjd	if (md->md_keys == 0) {
1005149928Spjd		gctl_error(req, "No valid keys on %s.", prov);
1006149928Spjd		return;
1007149928Spjd	}
1008149928Spjd
1009148456Spjd	/* Generate key for Master Key decryption. */
1010213172Spjd	if (eli_genkey(req, md, key, false) == NULL) {
1011148456Spjd		bzero(key, sizeof(key));
1012148456Spjd		return;
1013148456Spjd	}
1014148456Spjd
1015148456Spjd	/* Decrypt Master Key. */
1016149304Spjd	error = g_eli_mkey_decrypt(md, key, mkey, &nkey);
1017148456Spjd	bzero(key, sizeof(key));
1018148456Spjd	if (error != 0) {
1019149304Spjd		bzero(md, sizeof(*md));
1020148456Spjd		if (error == -1)
1021148456Spjd			gctl_error(req, "Wrong key for %s.", prov);
1022148456Spjd		else /* if (error > 0) */ {
1023148456Spjd			gctl_error(req, "Cannot decrypt Master Key: %s.",
1024148456Spjd			    strerror(error));
1025148456Spjd		}
1026148456Spjd		return;
1027148456Spjd	}
1028148456Spjd	if (verbose)
1029148456Spjd		printf("Decrypted Master Key %u.\n", nkey);
1030148456Spjd
1031153190Spjd	val = gctl_get_intmax(req, "keyno");
1032153190Spjd	if (val != -1)
1033153190Spjd		nkey = val;
1034148456Spjd#if 0
1035148456Spjd	else
1036148456Spjd		; /* Use the key number which was found during decryption. */
1037148456Spjd#endif
1038148456Spjd	if (nkey >= G_ELI_MAXMKEYS) {
1039148456Spjd		gctl_error(req, "Invalid '%s' argument.", "keyno");
1040148456Spjd		return;
1041148456Spjd	}
1042148456Spjd
1043153190Spjd	val = gctl_get_intmax(req, "iterations");
1044149304Spjd	/* Check if iterations number should and can be changed. */
1045153190Spjd	if (val != -1) {
1046149304Spjd		if (bitcount32(md->md_keys) != 1) {
1047149304Spjd			gctl_error(req, "To be able to use '-i' option, only "
1048149304Spjd			    "one key can be defined.");
1049149304Spjd			return;
1050149304Spjd		}
1051149304Spjd		if (md->md_keys != (1 << nkey)) {
1052149304Spjd			gctl_error(req, "Only already defined key can be "
1053149304Spjd			    "changed when '-i' option is used.");
1054149304Spjd			return;
1055149304Spjd		}
1056153190Spjd		md->md_iterations = val;
1057149304Spjd	}
1058148456Spjd
1059149304Spjd	mkeydst = md->md_mkeys + nkey * G_ELI_MKEYLEN;
1060149304Spjd	md->md_keys |= (1 << nkey);
1061149304Spjd
1062148456Spjd	bcopy(mkey, mkeydst, sizeof(mkey));
1063148456Spjd	bzero(mkey, sizeof(mkey));
1064148456Spjd
1065148456Spjd	/* Generate key for Master Key encryption. */
1066213172Spjd	if (eli_genkey(req, md, key, true) == NULL) {
1067148456Spjd		bzero(key, sizeof(key));
1068149304Spjd		bzero(md, sizeof(*md));
1069148456Spjd		return;
1070148456Spjd	}
1071148456Spjd
1072148456Spjd	/* Encrypt the Master-Key with the new key. */
1073159308Spjd	error = g_eli_mkey_encrypt(md->md_ealgo, key, md->md_keylen, mkeydst);
1074148456Spjd	bzero(key, sizeof(key));
1075148456Spjd	if (error != 0) {
1076149304Spjd		bzero(md, sizeof(*md));
1077148456Spjd		gctl_error(req, "Cannot encrypt Master Key: %s.",
1078148456Spjd		    strerror(error));
1079148456Spjd		return;
1080148456Spjd	}
1081148456Spjd
1082148456Spjd	/* Store metadata with fresh key. */
1083149304Spjd	eli_metadata_store(req, prov, md);
1084149304Spjd	bzero(md, sizeof(*md));
1085148456Spjd}
1086148456Spjd
1087148456Spjdstatic void
1088148456Spjdeli_setkey(struct gctl_req *req)
1089148456Spjd{
1090149304Spjd	struct g_eli_metadata md;
1091148456Spjd	const char *prov;
1092153190Spjd	int nargs;
1093148456Spjd
1094153190Spjd	nargs = gctl_get_int(req, "nargs");
1095153190Spjd	if (nargs != 1) {
1096158214Spjd		gctl_error(req, "Invalid number of arguments.");
1097148456Spjd		return;
1098148456Spjd	}
1099153190Spjd	prov = gctl_get_ascii(req, "arg0");
1100148456Spjd
1101149304Spjd	if (eli_metadata_read(req, prov, &md) == -1)
1102149304Spjd		return;
1103149304Spjd
1104148456Spjd	if (eli_is_attached(prov))
1105155101Spjd		eli_setkey_attached(req, &md);
1106148456Spjd	else
1107149304Spjd		eli_setkey_detached(req, prov, &md);
1108182452Spjd
1109182452Spjd	if (req->error == NULL || req->error[0] == '\0') {
1110182452Spjd		printf("Note, that the master key encrypted with old keys "
1111182452Spjd		    "and/or passphrase may still exists in a metadata backup "
1112182452Spjd		    "file.\n");
1113182452Spjd	}
1114148456Spjd}
1115148456Spjd
1116148456Spjdstatic void
1117148456Spjdeli_delkey_attached(struct gctl_req *req, const char *prov __unused)
1118148456Spjd{
1119148456Spjd
1120148456Spjd	gctl_issue(req);
1121148456Spjd}
1122148456Spjd
1123148456Spjdstatic void
1124148456Spjdeli_delkey_detached(struct gctl_req *req, const char *prov)
1125148456Spjd{
1126148456Spjd	struct g_eli_metadata md;
1127148456Spjd	unsigned char *mkeydst;
1128213172Spjd	unsigned int nkey;
1129153190Spjd	intmax_t val;
1130213172Spjd	bool all, force;
1131148456Spjd
1132148456Spjd	if (eli_metadata_read(req, prov, &md) == -1)
1133148456Spjd		return;
1134148456Spjd
1135153190Spjd	all = gctl_get_int(req, "all");
1136153190Spjd	if (all)
1137246620Spjd		arc4random_buf(md.md_mkeys, sizeof(md.md_mkeys));
1138148456Spjd	else {
1139153190Spjd		force = gctl_get_int(req, "force");
1140153190Spjd		val = gctl_get_intmax(req, "keyno");
1141153190Spjd		if (val == -1) {
1142148456Spjd			gctl_error(req, "Key number has to be specified.");
1143148456Spjd			return;
1144148456Spjd		}
1145153190Spjd		nkey = val;
1146148456Spjd		if (nkey >= G_ELI_MAXMKEYS) {
1147148456Spjd			gctl_error(req, "Invalid '%s' argument.", "keyno");
1148148456Spjd			return;
1149148456Spjd		}
1150153190Spjd		if (!(md.md_keys & (1 << nkey)) && !force) {
1151148456Spjd			gctl_error(req, "Master Key %u is not set.", nkey);
1152148456Spjd			return;
1153148456Spjd		}
1154148456Spjd		md.md_keys &= ~(1 << nkey);
1155153190Spjd		if (md.md_keys == 0 && !force) {
1156148456Spjd			gctl_error(req, "This is the last Master Key. Use '-f' "
1157148456Spjd			    "option if you really want to remove it.");
1158148456Spjd			return;
1159148456Spjd		}
1160148456Spjd		mkeydst = md.md_mkeys + nkey * G_ELI_MKEYLEN;
1161246620Spjd		arc4random_buf(mkeydst, G_ELI_MKEYLEN);
1162148456Spjd	}
1163148456Spjd
1164148456Spjd	eli_metadata_store(req, prov, &md);
1165148456Spjd	bzero(&md, sizeof(md));
1166148456Spjd}
1167148456Spjd
1168148456Spjdstatic void
1169148456Spjdeli_delkey(struct gctl_req *req)
1170148456Spjd{
1171148456Spjd	const char *prov;
1172153190Spjd	int nargs;
1173148456Spjd
1174153190Spjd	nargs = gctl_get_int(req, "nargs");
1175153190Spjd	if (nargs != 1) {
1176158214Spjd		gctl_error(req, "Invalid number of arguments.");
1177148456Spjd		return;
1178148456Spjd	}
1179153190Spjd	prov = gctl_get_ascii(req, "arg0");
1180148456Spjd
1181148456Spjd	if (eli_is_attached(prov))
1182148456Spjd		eli_delkey_attached(req, prov);
1183148456Spjd	else
1184148456Spjd		eli_delkey_detached(req, prov);
1185148456Spjd}
1186148456Spjd
1187214118Spjdstatic void
1188214118Spjdeli_resume(struct gctl_req *req)
1189214118Spjd{
1190214118Spjd	struct g_eli_metadata md;
1191214118Spjd	unsigned char key[G_ELI_USERKEYLEN];
1192214118Spjd	const char *prov;
1193214118Spjd	off_t mediasize;
1194214118Spjd	int nargs;
1195214118Spjd
1196214118Spjd	nargs = gctl_get_int(req, "nargs");
1197214118Spjd	if (nargs != 1) {
1198214118Spjd		gctl_error(req, "Invalid number of arguments.");
1199214118Spjd		return;
1200214118Spjd	}
1201214118Spjd	prov = gctl_get_ascii(req, "arg0");
1202214118Spjd
1203214118Spjd	if (eli_metadata_read(req, prov, &md) == -1)
1204214118Spjd		return;
1205214118Spjd
1206214118Spjd	mediasize = g_get_mediasize(prov);
1207214118Spjd	if (md.md_provsize != (uint64_t)mediasize) {
1208214118Spjd		gctl_error(req, "Provider size mismatch.");
1209214118Spjd		return;
1210214118Spjd	}
1211214118Spjd
1212214118Spjd	if (eli_genkey(req, &md, key, false) == NULL) {
1213214118Spjd		bzero(key, sizeof(key));
1214214118Spjd		return;
1215214118Spjd	}
1216214118Spjd
1217214118Spjd	gctl_ro_param(req, "key", sizeof(key), key);
1218214118Spjd	if (gctl_issue(req) == NULL) {
1219214118Spjd		if (verbose)
1220214118Spjd			printf("Resumed %s.\n", prov);
1221214118Spjd	}
1222214118Spjd	bzero(key, sizeof(key));
1223214118Spjd}
1224214118Spjd
1225213060Spjdstatic int
1226213060Spjdeli_trash_metadata(struct gctl_req *req, const char *prov, int fd, off_t offset)
1227213060Spjd{
1228213060Spjd	unsigned int overwrites;
1229213060Spjd	unsigned char *sector;
1230213060Spjd	ssize_t size;
1231213060Spjd	int error;
1232213060Spjd
1233213060Spjd	size = sizeof(overwrites);
1234213060Spjd	if (sysctlbyname("kern.geom.eli.overwrites", &overwrites, &size,
1235213060Spjd	    NULL, 0) == -1 || overwrites == 0) {
1236213060Spjd		overwrites = G_ELI_OVERWRITES;
1237213060Spjd	}
1238213060Spjd
1239213060Spjd	size = g_sectorsize(fd);
1240213060Spjd	if (size <= 0) {
1241213060Spjd		gctl_error(req, "Cannot obtain provider sector size %s: %s.",
1242213060Spjd		    prov, strerror(errno));
1243213060Spjd		return (-1);
1244213060Spjd	}
1245213060Spjd	sector = malloc(size);
1246213060Spjd	if (sector == NULL) {
1247213060Spjd		gctl_error(req, "Cannot allocate %zd bytes of memory.", size);
1248213060Spjd		return (-1);
1249213060Spjd	}
1250213060Spjd
1251213060Spjd	error = 0;
1252213060Spjd	do {
1253246620Spjd		arc4random_buf(sector, size);
1254213060Spjd		if (pwrite(fd, sector, size, offset) != size) {
1255213060Spjd			if (error == 0)
1256213060Spjd				error = errno;
1257213060Spjd		}
1258213060Spjd		(void)g_flush(fd);
1259213060Spjd	} while (--overwrites > 0);
1260246622Spjd	free(sector);
1261213060Spjd	if (error != 0) {
1262213060Spjd		gctl_error(req, "Cannot trash metadata on provider %s: %s.",
1263213060Spjd		    prov, strerror(error));
1264213060Spjd		return (-1);
1265213060Spjd	}
1266213060Spjd	return (0);
1267213060Spjd}
1268213060Spjd
1269148456Spjdstatic void
1270148456Spjdeli_kill_detached(struct gctl_req *req, const char *prov)
1271148456Spjd{
1272213060Spjd	off_t offset;
1273213060Spjd	int fd;
1274148456Spjd
1275148456Spjd	/*
1276148456Spjd	 * NOTE: Maybe we should verify if this is geli provider first,
1277148456Spjd	 *       but 'kill' command is quite critical so better don't waste
1278148456Spjd	 *       the time.
1279148456Spjd	 */
1280148456Spjd#if 0
1281148456Spjd	error = g_metadata_read(prov, (unsigned char *)&md, sizeof(md),
1282148456Spjd	    G_ELI_MAGIC);
1283148456Spjd	if (error != 0) {
1284148456Spjd		gctl_error(req, "Cannot read metadata from %s: %s.", prov,
1285148456Spjd		    strerror(error));
1286148456Spjd		return;
1287148456Spjd	}
1288148456Spjd#endif
1289148456Spjd
1290213060Spjd	fd = g_open(prov, 1);
1291213060Spjd	if (fd == -1) {
1292213060Spjd		gctl_error(req, "Cannot open provider %s: %s.", prov,
1293213060Spjd		    strerror(errno));
1294213060Spjd		return;
1295148456Spjd	}
1296213060Spjd	offset = g_mediasize(fd) - g_sectorsize(fd);
1297213060Spjd	if (offset <= 0) {
1298213060Spjd		gctl_error(req,
1299213060Spjd		    "Cannot obtain media size or sector size for provider %s: %s.",
1300213060Spjd		    prov, strerror(errno));
1301213060Spjd		(void)g_close(fd);
1302213060Spjd		return;
1303213060Spjd	}
1304213060Spjd	(void)eli_trash_metadata(req, prov, fd, offset);
1305213060Spjd	(void)g_close(fd);
1306148456Spjd}
1307148456Spjd
1308148456Spjdstatic void
1309148456Spjdeli_kill(struct gctl_req *req)
1310148456Spjd{
1311148456Spjd	const char *prov;
1312153190Spjd	int i, nargs, all;
1313148456Spjd
1314153190Spjd	nargs = gctl_get_int(req, "nargs");
1315153190Spjd	all = gctl_get_int(req, "all");
1316153190Spjd	if (!all && nargs == 0) {
1317148456Spjd		gctl_error(req, "Too few arguments.");
1318148456Spjd		return;
1319148456Spjd	}
1320148456Spjd	/*
1321148456Spjd	 * How '-a' option combine with a list of providers:
1322148456Spjd	 * Delete Master Keys from all attached providers:
1323148456Spjd	 * geli kill -a
1324169312Spjd	 * Delete Master Keys from all attached providers and from
1325148456Spjd	 * detached da0 and da1:
1326148456Spjd	 * geli kill -a da0 da1
1327148456Spjd	 * Delete Master Keys from (attached or detached) da0 and da1:
1328148456Spjd	 * geli kill da0 da1
1329148456Spjd	 */
1330148456Spjd
1331169312Spjd	/* First detached providers. */
1332153190Spjd	for (i = 0; i < nargs; i++) {
1333153190Spjd		prov = gctl_get_ascii(req, "arg%d", i);
1334148456Spjd		if (!eli_is_attached(prov))
1335148456Spjd			eli_kill_detached(req, prov);
1336148456Spjd	}
1337162347Spjd	/* Now attached providers. */
1338162347Spjd	gctl_issue(req);
1339148456Spjd}
1340148456Spjd
1341182452Spjdstatic int
1342182452Spjdeli_backup_create(struct gctl_req *req, const char *prov, const char *file)
1343148456Spjd{
1344148456Spjd	unsigned char *sector;
1345213059Spjd	ssize_t secsize;
1346226716Spjd	int error, filefd, ret;
1347148456Spjd
1348182452Spjd	ret = -1;
1349226716Spjd	filefd = -1;
1350148456Spjd	sector = NULL;
1351148456Spjd	secsize = 0;
1352148456Spjd
1353226716Spjd	secsize = g_get_sectorsize(prov);
1354226716Spjd	if (secsize == 0) {
1355148456Spjd		gctl_error(req, "Cannot get informations about %s: %s.", prov,
1356148456Spjd		    strerror(errno));
1357169193Spjd		goto out;
1358148456Spjd	}
1359148456Spjd	sector = malloc(secsize);
1360148456Spjd	if (sector == NULL) {
1361148456Spjd		gctl_error(req, "Cannot allocate memory.");
1362169193Spjd		goto out;
1363148456Spjd	}
1364148456Spjd	/* Read metadata from the provider. */
1365226716Spjd	error = g_metadata_read(prov, sector, secsize, G_ELI_MAGIC);
1366226716Spjd	if (error != 0) {
1367226716Spjd		gctl_error(req, "Unable to read metadata from %s: %s.", prov,
1368226716Spjd		    strerror(error));
1369148456Spjd		goto out;
1370148456Spjd	}
1371226716Spjd
1372226716Spjd	filefd = open(file, O_WRONLY | O_TRUNC | O_CREAT, 0600);
1373226716Spjd	if (filefd == -1) {
1374226716Spjd		gctl_error(req, "Unable to open %s: %s.", file,
1375226716Spjd		    strerror(errno));
1376148456Spjd		goto out;
1377148456Spjd	}
1378148456Spjd	/* Write metadata to the destination file. */
1379213059Spjd	if (write(filefd, sector, secsize) != secsize) {
1380226716Spjd		gctl_error(req, "Unable to write to %s: %s.", file,
1381148456Spjd		    strerror(errno));
1382226716Spjd		(void)close(filefd);
1383226716Spjd		(void)unlink(file);
1384148456Spjd		goto out;
1385148456Spjd	}
1386213059Spjd	(void)fsync(filefd);
1387226716Spjd	(void)close(filefd);
1388182452Spjd	/* Success. */
1389182452Spjd	ret = 0;
1390148456Spjdout:
1391148456Spjd	if (sector != NULL) {
1392148456Spjd		bzero(sector, secsize);
1393148456Spjd		free(sector);
1394148456Spjd	}
1395182452Spjd	return (ret);
1396148456Spjd}
1397148456Spjd
1398148456Spjdstatic void
1399182452Spjdeli_backup(struct gctl_req *req)
1400182452Spjd{
1401182452Spjd	const char *file, *prov;
1402182452Spjd	int nargs;
1403182452Spjd
1404182452Spjd	nargs = gctl_get_int(req, "nargs");
1405182452Spjd	if (nargs != 2) {
1406182452Spjd		gctl_error(req, "Invalid number of arguments.");
1407182452Spjd		return;
1408182452Spjd	}
1409182452Spjd	prov = gctl_get_ascii(req, "arg0");
1410182452Spjd	file = gctl_get_ascii(req, "arg1");
1411182452Spjd
1412182452Spjd	eli_backup_create(req, prov, file);
1413182452Spjd}
1414182452Spjd
1415182452Spjdstatic void
1416148456Spjdeli_restore(struct gctl_req *req)
1417148456Spjd{
1418148456Spjd	struct g_eli_metadata md;
1419148456Spjd	const char *file, *prov;
1420148456Spjd	off_t mediasize;
1421226716Spjd	int nargs;
1422148456Spjd
1423153190Spjd	nargs = gctl_get_int(req, "nargs");
1424153190Spjd	if (nargs != 2) {
1425148456Spjd		gctl_error(req, "Invalid number of arguments.");
1426148456Spjd		return;
1427148456Spjd	}
1428153190Spjd	file = gctl_get_ascii(req, "arg0");
1429153190Spjd	prov = gctl_get_ascii(req, "arg1");
1430148456Spjd
1431226716Spjd	/* Read metadata from the backup file. */
1432226716Spjd	if (eli_metadata_read(req, file, &md) == -1)
1433226716Spjd		return;
1434226716Spjd	/* Obtain provider's mediasize. */
1435226716Spjd	mediasize = g_get_mediasize(prov);
1436226716Spjd	if (mediasize == 0) {
1437148456Spjd		gctl_error(req, "Cannot get informations about %s: %s.", prov,
1438148456Spjd		    strerror(errno));
1439226716Spjd		return;
1440148456Spjd	}
1441212934Sbrian	/* Check if the provider size has changed since we did the backup. */
1442212934Sbrian	if (md.md_provsize != (uint64_t)mediasize) {
1443212934Sbrian		if (gctl_get_int(req, "force")) {
1444212934Sbrian			md.md_provsize = mediasize;
1445212934Sbrian		} else {
1446212934Sbrian			gctl_error(req, "Provider size mismatch: "
1447212934Sbrian			    "wrong backup file?");
1448226716Spjd			return;
1449212934Sbrian		}
1450212934Sbrian	}
1451226716Spjd	/* Write metadata to the provider. */
1452226716Spjd	(void)eli_metadata_store(req, prov, &md);
1453148456Spjd}
1454148456Spjd
1455148456Spjdstatic void
1456212934Sbrianeli_resize(struct gctl_req *req)
1457212934Sbrian{
1458212934Sbrian	struct g_eli_metadata md;
1459212934Sbrian	const char *prov;
1460212934Sbrian	unsigned char *sector;
1461213056Spjd	ssize_t secsize;
1462212934Sbrian	off_t mediasize, oldsize;
1463226722Spjd	int error, nargs, provfd;
1464212934Sbrian
1465212934Sbrian	nargs = gctl_get_int(req, "nargs");
1466212934Sbrian	if (nargs != 1) {
1467212934Sbrian		gctl_error(req, "Invalid number of arguments.");
1468212934Sbrian		return;
1469212934Sbrian	}
1470212934Sbrian	prov = gctl_get_ascii(req, "arg0");
1471212934Sbrian
1472212934Sbrian	provfd = -1;
1473212934Sbrian	sector = NULL;
1474212934Sbrian	secsize = 0;
1475212934Sbrian
1476213056Spjd	provfd = g_open(prov, 1);
1477212934Sbrian	if (provfd == -1) {
1478212934Sbrian		gctl_error(req, "Cannot open %s: %s.", prov, strerror(errno));
1479212934Sbrian		goto out;
1480212934Sbrian	}
1481212934Sbrian
1482213056Spjd	mediasize = g_mediasize(provfd);
1483213056Spjd	secsize = g_sectorsize(provfd);
1484213056Spjd	if (mediasize == -1 || secsize == -1) {
1485212934Sbrian		gctl_error(req, "Cannot get information about %s: %s.", prov,
1486212934Sbrian		    strerror(errno));
1487212934Sbrian		goto out;
1488212934Sbrian	}
1489212934Sbrian
1490212934Sbrian	sector = malloc(secsize);
1491212934Sbrian	if (sector == NULL) {
1492212934Sbrian		gctl_error(req, "Cannot allocate memory.");
1493212934Sbrian		goto out;
1494212934Sbrian	}
1495212934Sbrian
1496212934Sbrian	oldsize = gctl_get_intmax(req, "oldsize");
1497212934Sbrian	if (oldsize < 0 || oldsize > mediasize) {
1498212934Sbrian		gctl_error(req, "Invalid oldsize: Out of range.");
1499212934Sbrian		goto out;
1500212934Sbrian	}
1501213058Spjd	if (oldsize == mediasize) {
1502213058Spjd		gctl_error(req, "Size hasn't changed.");
1503213058Spjd		goto out;
1504213058Spjd	}
1505212934Sbrian
1506212934Sbrian	/* Read metadata from the 'oldsize' offset. */
1507213056Spjd	if (pread(provfd, sector, secsize, oldsize - secsize) != secsize) {
1508212934Sbrian		gctl_error(req, "Cannot read old metadata: %s.",
1509212934Sbrian		    strerror(errno));
1510212934Sbrian		goto out;
1511212934Sbrian	}
1512212934Sbrian
1513212934Sbrian	/* Check if this sector contains geli metadata. */
1514226722Spjd	error = eli_metadata_decode(sector, &md);
1515226722Spjd	switch (error) {
1516226722Spjd	case 0:
1517226722Spjd		break;
1518226722Spjd	case EOPNOTSUPP:
1519226722Spjd		gctl_error(req,
1520226722Spjd		    "Provider's %s metadata version %u is too new.\n"
1521226722Spjd		    "geli: The highest supported version is %u.",
1522226722Spjd		    prov, (unsigned int)md.md_version, G_ELI_VERSION);
1523212934Sbrian		goto out;
1524226722Spjd	case EINVAL:
1525226722Spjd		gctl_error(req, "Inconsistent provider's %s metadata.", prov);
1526226722Spjd		goto out;
1527226722Spjd	default:
1528226722Spjd		gctl_error(req,
1529226722Spjd		    "Unexpected error while decoding provider's %s metadata: %s.",
1530226722Spjd		    prov, strerror(error));
1531226722Spjd		goto out;
1532212934Sbrian	}
1533212934Sbrian
1534212934Sbrian	/*
1535212934Sbrian	 * If the old metadata doesn't have a correct provider size, refuse
1536212934Sbrian	 * to resize.
1537212934Sbrian	 */
1538212934Sbrian	if (md.md_provsize != (uint64_t)oldsize) {
1539212934Sbrian		gctl_error(req, "Provider size mismatch at oldsize.");
1540212934Sbrian		goto out;
1541212934Sbrian	}
1542212934Sbrian
1543212934Sbrian	/*
1544212934Sbrian	 * Update the old metadata with the current provider size and write
1545212934Sbrian	 * it back to the correct place on the provider.
1546212934Sbrian	 */
1547212934Sbrian	md.md_provsize = mediasize;
1548226720Spjd	/* Write metadata to the provider. */
1549226720Spjd	(void)eli_metadata_store(req, prov, &md);
1550212934Sbrian	/* Now trash the old metadata. */
1551226720Spjd	(void)eli_trash_metadata(req, prov, provfd, oldsize - secsize);
1552212934Sbrianout:
1553226720Spjd	if (provfd != -1)
1554213056Spjd		(void)g_close(provfd);
1555212934Sbrian	if (sector != NULL) {
1556212934Sbrian		bzero(sector, secsize);
1557212934Sbrian		free(sector);
1558212934Sbrian	}
1559212934Sbrian}
1560212934Sbrian
1561212934Sbrianstatic void
1562226723Spjdeli_version(struct gctl_req *req)
1563226723Spjd{
1564226723Spjd	struct g_eli_metadata md;
1565226723Spjd	const char *name;
1566226723Spjd	unsigned int version;
1567226723Spjd	int error, i, nargs;
1568226723Spjd
1569226723Spjd	nargs = gctl_get_int(req, "nargs");
1570226723Spjd
1571226723Spjd	if (nargs == 0) {
1572226723Spjd		unsigned int kernver;
1573226723Spjd		ssize_t size;
1574226723Spjd
1575226723Spjd		size = sizeof(kernver);
1576226723Spjd		if (sysctlbyname("kern.geom.eli.version", &kernver, &size,
1577226723Spjd		    NULL, 0) == -1) {
1578226723Spjd			warn("Unable to obtain GELI kernel version");
1579226723Spjd		} else {
1580226723Spjd			printf("kernel: %u\n", kernver);
1581226723Spjd		}
1582226723Spjd		printf("userland: %u\n", G_ELI_VERSION);
1583226723Spjd		return;
1584226723Spjd	}
1585226723Spjd
1586226723Spjd	for (i = 0; i < nargs; i++) {
1587226723Spjd		name = gctl_get_ascii(req, "arg%d", i);
1588226723Spjd		error = g_metadata_read(name, (unsigned char *)&md,
1589226723Spjd		    sizeof(md), G_ELI_MAGIC);
1590226723Spjd		if (error != 0) {
1591226723Spjd			warn("%s: Unable to read metadata: %s.", name,
1592226723Spjd			    strerror(error));
1593226723Spjd			gctl_error(req, "Not fully done.");
1594226723Spjd			continue;
1595226723Spjd		}
1596226723Spjd		version = le32dec(&md.md_version);
1597226723Spjd		printf("%s: %u\n", name, version);
1598226723Spjd	}
1599226723Spjd}
1600226723Spjd
1601226723Spjdstatic void
1602148456Spjdeli_clear(struct gctl_req *req)
1603148456Spjd{
1604148456Spjd	const char *name;
1605153190Spjd	int error, i, nargs;
1606148456Spjd
1607153190Spjd	nargs = gctl_get_int(req, "nargs");
1608153190Spjd	if (nargs < 1) {
1609148456Spjd		gctl_error(req, "Too few arguments.");
1610148456Spjd		return;
1611148456Spjd	}
1612148456Spjd
1613153190Spjd	for (i = 0; i < nargs; i++) {
1614153190Spjd		name = gctl_get_ascii(req, "arg%d", i);
1615148456Spjd		error = g_metadata_clear(name, G_ELI_MAGIC);
1616148456Spjd		if (error != 0) {
1617148456Spjd			fprintf(stderr, "Cannot clear metadata on %s: %s.\n",
1618148456Spjd			    name, strerror(error));
1619148456Spjd			gctl_error(req, "Not fully done.");
1620148456Spjd			continue;
1621148456Spjd		}
1622148456Spjd		if (verbose)
1623155175Spjd			printf("Metadata cleared on %s.\n", name);
1624148456Spjd	}
1625148456Spjd}
1626148456Spjd
1627148456Spjdstatic void
1628148456Spjdeli_dump(struct gctl_req *req)
1629148456Spjd{
1630226719Spjd	struct g_eli_metadata md;
1631148456Spjd	const char *name;
1632226719Spjd	int i, nargs;
1633148456Spjd
1634153190Spjd	nargs = gctl_get_int(req, "nargs");
1635153190Spjd	if (nargs < 1) {
1636148456Spjd		gctl_error(req, "Too few arguments.");
1637148456Spjd		return;
1638148456Spjd	}
1639148456Spjd
1640153190Spjd	for (i = 0; i < nargs; i++) {
1641153190Spjd		name = gctl_get_ascii(req, "arg%d", i);
1642226719Spjd		if (eli_metadata_read(NULL, name, &md) == -1) {
1643148456Spjd			gctl_error(req, "Not fully done.");
1644148456Spjd			continue;
1645148456Spjd		}
1646148456Spjd		printf("Metadata on %s:\n", name);
1647148456Spjd		eli_metadata_dump(&md);
1648148456Spjd		printf("\n");
1649148456Spjd	}
1650148456Spjd}
1651