geom_eli.c revision 348588
1254219Scy/*-
2254219Scy * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3254219Scy *
4254219Scy * Copyright (c) 2004-2010 Pawel Jakub Dawidek <pjd@FreeBSD.org>
5254219Scy * All rights reserved.
6254219Scy *
7254219Scy * Redistribution and use in source and binary forms, with or without
8254219Scy * modification, are permitted provided that the following conditions
9254219Scy * are met:
10254219Scy * 1. Redistributions of source code must retain the above copyright
11254219Scy *    notice, this list of conditions and the following disclaimer.
12254219Scy * 2. Redistributions in binary form must reproduce the above copyright
13254219Scy *    notice, this list of conditions and the following disclaimer in the
14254219Scy *    documentation and/or other materials provided with the distribution.
15254219Scy *
16254219Scy * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
17254219Scy * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18254219Scy * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19254219Scy * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
20254219Scy * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21254219Scy * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22254219Scy * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23254219Scy * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24254219Scy * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25254219Scy * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26254219Scy * SUCH DAMAGE.
27254219Scy */
28254219Scy
29254219Scy#include <sys/cdefs.h>
30254219Scy__FBSDID("$FreeBSD: stable/11/sbin/geom/class/eli/geom_eli.c 348588 2019-06-03 21:04:23Z jhb $");
31254219Scy
32254219Scy#include <sys/param.h>
33254219Scy#include <sys/mman.h>
34254219Scy#include <sys/sysctl.h>
35254219Scy#include <sys/resource.h>
36254219Scy#include <opencrypto/cryptodev.h>
37254219Scy
38254219Scy#include <assert.h>
39254219Scy#include <err.h>
40254219Scy#include <errno.h>
41254219Scy#include <fcntl.h>
42254219Scy#include <libgeom.h>
43254219Scy#include <paths.h>
44254219Scy#include <readpassphrase.h>
45254219Scy#include <stdbool.h>
46254219Scy#include <stdint.h>
47254219Scy#include <stdio.h>
48254219Scy#include <stdlib.h>
49254219Scy#include <string.h>
50254219Scy#include <strings.h>
51254219Scy#include <unistd.h>
52254219Scy
53254219Scy#include <geom/eli/g_eli.h>
54254219Scy#include <geom/eli/pkcs5v2.h>
55254219Scy
56254219Scy#include "core/geom.h"
57254219Scy#include "misc/subr.h"
58254219Scy
59254219Scy
60254219Scyuint32_t lib_version = G_LIB_VERSION;
61254219Scyuint32_t version = G_ELI_VERSION;
62254219Scy
63254219Scy#define	GELI_BACKUP_DIR	"/var/backups/"
64254219Scy#define	GELI_ENC_ALGO	"aes"
65254219Scy
66254219Scystatic void eli_main(struct gctl_req *req, unsigned flags);
67254219Scystatic void eli_init(struct gctl_req *req);
68254219Scystatic void eli_attach(struct gctl_req *req);
69254219Scystatic void eli_configure(struct gctl_req *req);
70254219Scystatic void eli_setkey(struct gctl_req *req);
71254219Scystatic void eli_delkey(struct gctl_req *req);
72254219Scystatic void eli_resume(struct gctl_req *req);
73254219Scystatic void eli_kill(struct gctl_req *req);
74254219Scystatic void eli_backup(struct gctl_req *req);
75254219Scystatic void eli_restore(struct gctl_req *req);
76254219Scystatic void eli_resize(struct gctl_req *req);
77254219Scystatic void eli_version(struct gctl_req *req);
78254219Scystatic void eli_clear(struct gctl_req *req);
79254219Scystatic void eli_dump(struct gctl_req *req);
80254219Scy
81254219Scystatic int eli_backup_create(struct gctl_req *req, const char *prov,
82254219Scy    const char *file);
83254219Scy
84254219Scy/*
85254219Scy * Available commands:
86254219Scy *
87254219Scy * init [-bdgPTv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] [-s sectorsize] [-V version] prov
88254219Scy * label - alias for 'init'
89254219Scy * attach [-Cdprv] [-n keyno] [-j passfile] [-k keyfile] prov
90254219Scy * detach [-fl] prov ...
91254219Scy * stop - alias for 'detach'
92254219Scy * onetime [-d] [-a aalgo] [-e ealgo] [-l keylen] prov
93254219Scy * configure [-bBgGtT] prov ...
94254219Scy * setkey [-pPv] [-n keyno] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov
95254219Scy * delkey [-afv] [-n keyno] prov
96254219Scy * suspend [-v] -a | prov ...
97254219Scy * resume [-pv] [-j passfile] [-k keyfile] prov
98254219Scy * kill [-av] [prov ...]
99254219Scy * backup [-v] prov file
100254219Scy * restore [-fv] file prov
101254219Scy * resize [-v] -s oldsize prov
102254219Scy * version [prov ...]
103254219Scy * clear [-v] prov ...
104254219Scy * dump [-v] prov ...
105254219Scy */
106254219Scystruct g_command class_commands[] = {
107254219Scy	{ "init", G_FLAG_VERBOSE, eli_main,
108254219Scy	    {
109254219Scy		{ 'a', "aalgo", "", G_TYPE_STRING },
110254219Scy		{ 'b', "boot", NULL, G_TYPE_BOOL },
111254219Scy		{ 'B', "backupfile", "", G_TYPE_STRING },
112254219Scy		{ 'd', "displaypass", NULL, G_TYPE_BOOL },
113254219Scy		{ 'e', "ealgo", "", G_TYPE_STRING },
114254219Scy		{ 'g', "geliboot", NULL, G_TYPE_BOOL },
115254219Scy		{ 'i', "iterations", "-1", G_TYPE_NUMBER },
116254219Scy		{ 'J', "newpassfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
117254219Scy		{ 'K', "newkeyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
118254219Scy		{ 'l', "keylen", "0", G_TYPE_NUMBER },
119254219Scy		{ 'P', "nonewpassphrase", NULL, G_TYPE_BOOL },
120254219Scy		{ 's', "sectorsize", "0", G_TYPE_NUMBER },
121254219Scy		{ 'T', "notrim", NULL, G_TYPE_BOOL },
122254219Scy		{ 'V', "mdversion", "-1", G_TYPE_NUMBER },
123254219Scy		G_OPT_SENTINEL
124254219Scy	    },
125254219Scy	    "[-bdgPTv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] [-s sectorsize] [-V version] prov"
126254219Scy	},
127254219Scy	{ "label", G_FLAG_VERBOSE, eli_main,
128254219Scy	    {
129254219Scy		{ 'a', "aalgo", "", G_TYPE_STRING },
130254219Scy		{ 'b', "boot", NULL, G_TYPE_BOOL },
131254219Scy		{ 'B', "backupfile", "", G_TYPE_STRING },
132254219Scy		{ 'd', "displaypass", NULL, G_TYPE_BOOL },
133254219Scy		{ 'e', "ealgo", "", G_TYPE_STRING },
134254219Scy		{ 'g', "geliboot", NULL, G_TYPE_BOOL },
135254219Scy		{ 'i', "iterations", "-1", G_TYPE_NUMBER },
136254219Scy		{ 'J', "newpassfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
137254219Scy		{ 'K', "newkeyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
138254219Scy		{ 'l', "keylen", "0", G_TYPE_NUMBER },
139254219Scy		{ 'P', "nonewpassphrase", NULL, G_TYPE_BOOL },
140254219Scy		{ 's', "sectorsize", "0", G_TYPE_NUMBER },
141254219Scy		{ 'V', "mdversion", "-1", G_TYPE_NUMBER },
142254219Scy		G_OPT_SENTINEL
143254219Scy	    },
144254219Scy	    "- an alias for 'init'"
145254219Scy	},
146254219Scy	{ "attach", G_FLAG_VERBOSE | G_FLAG_LOADKLD, eli_main,
147254219Scy	    {
148254219Scy		{ 'C', "dryrun", NULL, G_TYPE_BOOL },
149254219Scy		{ 'd', "detach", NULL, G_TYPE_BOOL },
150254219Scy		{ 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
151254219Scy		{ 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
152254219Scy		{ 'n', "keyno", "-1", G_TYPE_NUMBER },
153254219Scy		{ 'p', "nopassphrase", NULL, G_TYPE_BOOL },
154254219Scy		{ 'r', "readonly", NULL, G_TYPE_BOOL },
155254219Scy		G_OPT_SENTINEL
156254219Scy	    },
157254219Scy	    "[-Cdprv] [-n keyno] [-j passfile] [-k keyfile] prov"
158254219Scy	},
159254219Scy	{ "detach", 0, NULL,
160254219Scy	    {
161254219Scy		{ 'f', "force", NULL, G_TYPE_BOOL },
162254219Scy		{ 'l', "last", NULL, G_TYPE_BOOL },
163254219Scy		G_OPT_SENTINEL
164254219Scy	    },
165254219Scy	    "[-fl] prov ..."
166254219Scy	},
167254219Scy	{ "stop", 0, NULL,
168254219Scy	    {
169254219Scy		{ 'f', "force", NULL, G_TYPE_BOOL },
170254219Scy		{ 'l', "last", NULL, G_TYPE_BOOL },
171254219Scy		G_OPT_SENTINEL
172254219Scy	    },
173254219Scy	    "- an alias for 'detach'"
174254219Scy	},
175254219Scy	{ "onetime", G_FLAG_VERBOSE | G_FLAG_LOADKLD, NULL,
176254219Scy	    {
177254219Scy		{ 'a', "aalgo", "", G_TYPE_STRING },
178254219Scy		{ 'd', "detach", NULL, G_TYPE_BOOL },
179254219Scy		{ 'e', "ealgo", GELI_ENC_ALGO, G_TYPE_STRING },
180254219Scy		{ 'l', "keylen", "0", G_TYPE_NUMBER },
181254219Scy		{ 's', "sectorsize", "0", G_TYPE_NUMBER },
182254219Scy		{ 'T', "notrim", NULL, G_TYPE_BOOL },
183254219Scy		G_OPT_SENTINEL
184254219Scy	    },
185254219Scy	    "[-dT] [-a aalgo] [-e ealgo] [-l keylen] [-s sectorsize] prov"
186254219Scy	},
187254219Scy	{ "configure", G_FLAG_VERBOSE, eli_main,
188254219Scy	    {
189254219Scy		{ 'b', "boot", NULL, G_TYPE_BOOL },
190254219Scy		{ 'B', "noboot", NULL, G_TYPE_BOOL },
191254219Scy		{ 'd', "displaypass", NULL, G_TYPE_BOOL },
192254219Scy		{ 'D', "nodisplaypass", NULL, G_TYPE_BOOL },
193254219Scy		{ 'g', "geliboot", NULL, G_TYPE_BOOL },
194254219Scy		{ 'G', "nogeliboot", NULL, G_TYPE_BOOL },
195254219Scy		{ 't', "trim", NULL, G_TYPE_BOOL },
196254219Scy		{ 'T', "notrim", NULL, G_TYPE_BOOL },
197254219Scy		G_OPT_SENTINEL
198254219Scy	    },
199254219Scy	    "[-bBdDgGtT] prov ..."
200254219Scy	},
201254219Scy	{ "setkey", G_FLAG_VERBOSE, eli_main,
202254219Scy	    {
203254219Scy		{ 'i', "iterations", "-1", G_TYPE_NUMBER },
204254219Scy		{ 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
205254219Scy		{ 'J', "newpassfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
206254219Scy		{ 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
207254219Scy		{ 'K', "newkeyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
208254219Scy		{ 'n', "keyno", "-1", G_TYPE_NUMBER },
209254219Scy		{ 'p', "nopassphrase", NULL, G_TYPE_BOOL },
210254219Scy		{ 'P', "nonewpassphrase", NULL, G_TYPE_BOOL },
211254219Scy		G_OPT_SENTINEL
212254219Scy	    },
213254219Scy	    "[-pPv] [-n keyno] [-i iterations] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov"
214254219Scy	},
215254219Scy	{ "delkey", G_FLAG_VERBOSE, eli_main,
216254219Scy	    {
217254219Scy		{ 'a', "all", NULL, G_TYPE_BOOL },
218254219Scy		{ 'f', "force", NULL, G_TYPE_BOOL },
219254219Scy		{ 'n', "keyno", "-1", G_TYPE_NUMBER },
220254219Scy		G_OPT_SENTINEL
221254219Scy	    },
222254219Scy	    "[-afv] [-n keyno] prov"
223254219Scy	},
224254219Scy	{ "suspend", G_FLAG_VERBOSE, NULL,
225254219Scy	    {
226254219Scy		{ 'a', "all", NULL, G_TYPE_BOOL },
227254219Scy		G_OPT_SENTINEL
228254219Scy	    },
229254219Scy	    "[-v] -a | prov ..."
230254219Scy	},
231254219Scy	{ "resume", G_FLAG_VERBOSE, eli_main,
232254219Scy	    {
233254219Scy		{ 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
234254219Scy		{ 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
235254219Scy		{ 'p', "nopassphrase", NULL, G_TYPE_BOOL },
236254219Scy		G_OPT_SENTINEL
237254219Scy	    },
238254219Scy	    "[-pv] [-j passfile] [-k keyfile] prov"
239254219Scy	},
240254219Scy	{ "kill", G_FLAG_VERBOSE, eli_main,
241254219Scy	    {
242254219Scy		{ 'a', "all", NULL, G_TYPE_BOOL },
243254219Scy		G_OPT_SENTINEL
244254219Scy	    },
245254219Scy	    "[-av] [prov ...]"
246254219Scy	},
247254219Scy	{ "backup", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS,
248254219Scy	    "[-v] prov file"
249254219Scy	},
250254219Scy	{ "restore", G_FLAG_VERBOSE, eli_main,
251254219Scy	    {
252254219Scy		{ 'f', "force", NULL, G_TYPE_BOOL },
253254219Scy		G_OPT_SENTINEL
254254219Scy	    },
255254219Scy	    "[-fv] file prov"
256254219Scy	},
257254219Scy	{ "resize", G_FLAG_VERBOSE, eli_main,
258254219Scy	    {
259254219Scy		{ 's', "oldsize", NULL, G_TYPE_NUMBER },
260254219Scy		G_OPT_SENTINEL
261254219Scy	    },
262254219Scy	    "[-v] -s oldsize prov"
263254219Scy	},
264254219Scy	{ "version", G_FLAG_LOADKLD, eli_main, G_NULL_OPTS,
265254219Scy	    "[prov ...]"
266254219Scy	},
267254219Scy	{ "clear", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS,
268254219Scy	    "[-v] prov ..."
269254219Scy	},
270254219Scy	{ "dump", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS,
271254219Scy	    "[-v] prov ..."
272254219Scy	},
273254219Scy	G_CMD_SENTINEL
274254219Scy};
275254219Scy
276254219Scystatic int verbose = 0;
277254219Scy
278254219Scy#define	BUFSIZE	1024
279254219Scy
280254219Scystatic int
281254219Scyeli_protect(struct gctl_req *req)
282254219Scy{
283254219Scy	struct rlimit rl;
284254219Scy
285254219Scy	/* Disable core dumps. */
286254219Scy	rl.rlim_cur = 0;
287254219Scy	rl.rlim_max = 0;
288254219Scy	if (setrlimit(RLIMIT_CORE, &rl) == -1) {
289254219Scy		gctl_error(req, "Cannot disable core dumps: %s.",
290254219Scy		    strerror(errno));
291254219Scy		return (-1);
292254219Scy	}
293254219Scy	/* Disable swapping. */
294254219Scy	if (mlockall(MCL_FUTURE) == -1) {
295254219Scy		gctl_error(req, "Cannot lock memory: %s.", strerror(errno));
296254219Scy		return (-1);
297254219Scy	}
298254219Scy	return (0);
299254219Scy}
300254219Scy
301254219Scystatic void
302254219Scyeli_main(struct gctl_req *req, unsigned int flags)
303254219Scy{
304254219Scy	const char *name;
305254219Scy
306254219Scy	if (eli_protect(req) == -1)
307254219Scy		return;
308254219Scy
309254219Scy	if ((flags & G_FLAG_VERBOSE) != 0)
310254219Scy		verbose = 1;
311254219Scy
312254219Scy	name = gctl_get_ascii(req, "verb");
313254219Scy	if (name == NULL) {
314254219Scy		gctl_error(req, "No '%s' argument.", "verb");
315254219Scy		return;
316254219Scy	}
317254219Scy	if (strcmp(name, "init") == 0 || strcmp(name, "label") == 0)
318254219Scy		eli_init(req);
319254219Scy	else if (strcmp(name, "attach") == 0)
320254219Scy		eli_attach(req);
321254219Scy	else if (strcmp(name, "configure") == 0)
322254219Scy		eli_configure(req);
323254219Scy	else if (strcmp(name, "setkey") == 0)
324254219Scy		eli_setkey(req);
325254219Scy	else if (strcmp(name, "delkey") == 0)
326254219Scy		eli_delkey(req);
327254219Scy	else if (strcmp(name, "resume") == 0)
328254219Scy		eli_resume(req);
329254219Scy	else if (strcmp(name, "kill") == 0)
330254219Scy		eli_kill(req);
331254219Scy	else if (strcmp(name, "backup") == 0)
332254219Scy		eli_backup(req);
333254219Scy	else if (strcmp(name, "restore") == 0)
334254219Scy		eli_restore(req);
335254219Scy	else if (strcmp(name, "resize") == 0)
336254219Scy		eli_resize(req);
337254219Scy	else if (strcmp(name, "version") == 0)
338254219Scy		eli_version(req);
339254219Scy	else if (strcmp(name, "dump") == 0)
340254219Scy		eli_dump(req);
341254219Scy	else if (strcmp(name, "clear") == 0)
342254219Scy		eli_clear(req);
343254219Scy	else
344254219Scy		gctl_error(req, "Unknown command: %s.", name);
345254219Scy}
346254219Scy
347254219Scystatic bool
348254219Scyeli_is_attached(const char *prov)
349254219Scy{
350254219Scy	char name[MAXPATHLEN];
351254219Scy
352254219Scy	/*
353254219Scy	 * Not the best way to do it, but the easiest.
354254219Scy	 * We try to open provider and check if it is a GEOM provider
355254219Scy	 * by asking about its sectorsize.
356254219Scy	 */
357254219Scy	snprintf(name, sizeof(name), "%s%s", prov, G_ELI_SUFFIX);
358254219Scy	return (g_get_sectorsize(name) > 0);
359254219Scy}
360254219Scy
361254219Scystatic int
362254219Scyeli_genkey_files(struct gctl_req *req, bool new, const char *type,
363254219Scy    struct hmac_ctx *ctxp, char *passbuf, size_t passbufsize)
364254219Scy{
365254219Scy	char *p, buf[BUFSIZE], argname[16];
366254219Scy	const char *file;
367254219Scy	int error, fd, i;
368254219Scy	ssize_t done;
369254219Scy
370254219Scy	assert((strcmp(type, "keyfile") == 0 && ctxp != NULL &&
371254219Scy	    passbuf == NULL && passbufsize == 0) ||
372254219Scy	    (strcmp(type, "passfile") == 0 && ctxp == NULL &&
373254219Scy	    passbuf != NULL && passbufsize > 0));
374254219Scy	assert(strcmp(type, "keyfile") == 0 || passbuf[0] == '\0');
375254219Scy
376254219Scy	for (i = 0; ; i++) {
377254219Scy		snprintf(argname, sizeof(argname), "%s%s%d",
378254219Scy		    new ? "new" : "", type, i);
379254219Scy
380254219Scy		/* No more {key,pass}files? */
381254219Scy		if (!gctl_has_param(req, argname))
382254219Scy			return (i);
383254219Scy
384254219Scy		file = gctl_get_ascii(req, "%s", argname);
385254219Scy		assert(file != NULL);
386254219Scy
387254219Scy		if (strcmp(file, "-") == 0)
388254219Scy			fd = STDIN_FILENO;
389254219Scy		else {
390254219Scy			fd = open(file, O_RDONLY);
391254219Scy			if (fd == -1) {
392254219Scy				gctl_error(req, "Cannot open %s %s: %s.",
393254219Scy				    type, file, strerror(errno));
394254219Scy				return (-1);
395254219Scy			}
396254219Scy		}
397254219Scy		if (strcmp(type, "keyfile") == 0) {
398254219Scy			while ((done = read(fd, buf, sizeof(buf))) > 0)
399254219Scy				g_eli_crypto_hmac_update(ctxp, buf, done);
400254219Scy		} else /* if (strcmp(type, "passfile") == 0) */ {
401254219Scy			assert(strcmp(type, "passfile") == 0);
402254219Scy
403254219Scy			while ((done = read(fd, buf, sizeof(buf) - 1)) > 0) {
404254219Scy				buf[done] = '\0';
405254219Scy				p = strchr(buf, '\n');
406254219Scy				if (p != NULL) {
407254219Scy					*p = '\0';
408254219Scy					done = p - buf;
409254219Scy				}
410254219Scy				if (strlcat(passbuf, buf, passbufsize) >=
411254219Scy				    passbufsize) {
412254219Scy					gctl_error(req,
413254219Scy					    "Passphrase in %s too long.", file);
414254219Scy					bzero(buf, sizeof(buf));
415254219Scy					return (-1);
416254219Scy				}
417254219Scy				if (p != NULL)
418254219Scy					break;
419254219Scy			}
420254219Scy		}
421254219Scy		error = errno;
422254219Scy		if (strcmp(file, "-") != 0)
423254219Scy			close(fd);
424254219Scy		bzero(buf, sizeof(buf));
425254219Scy		if (done == -1) {
426254219Scy			gctl_error(req, "Cannot read %s %s: %s.",
427254219Scy			    type, file, strerror(error));
428254219Scy			return (-1);
429254219Scy		}
430254219Scy	}
431254219Scy	/* NOTREACHED */
432254219Scy}
433254219Scy
434254219Scystatic int
435254219Scyeli_genkey_passphrase_prompt(struct gctl_req *req, bool new, char *passbuf,
436254219Scy    size_t passbufsize)
437254219Scy{
438254219Scy	char *p;
439254219Scy
440254219Scy	for (;;) {
441254219Scy		p = readpassphrase(
442254219Scy		    new ? "Enter new passphrase: " : "Enter passphrase: ",
443254219Scy		    passbuf, passbufsize, RPP_ECHO_OFF | RPP_REQUIRE_TTY);
444254219Scy		if (p == NULL) {
445254219Scy			bzero(passbuf, passbufsize);
446254219Scy			gctl_error(req, "Cannot read passphrase: %s.",
447254219Scy			    strerror(errno));
448254219Scy			return (-1);
449254219Scy		}
450254219Scy
451254219Scy		if (new) {
452254219Scy			char tmpbuf[BUFSIZE];
453254219Scy
454254219Scy			p = readpassphrase("Reenter new passphrase: ",
455254219Scy			    tmpbuf, sizeof(tmpbuf),
456254219Scy			    RPP_ECHO_OFF | RPP_REQUIRE_TTY);
457254219Scy			if (p == NULL) {
458254219Scy				bzero(passbuf, passbufsize);
459254219Scy				gctl_error(req,
460254219Scy				    "Cannot read passphrase: %s.",
461254219Scy				    strerror(errno));
462254219Scy				return (-1);
463254219Scy			}
464254219Scy
465254219Scy			if (strcmp(passbuf, tmpbuf) != 0) {
466254219Scy				bzero(passbuf, passbufsize);
467254219Scy				fprintf(stderr, "They didn't match.\n");
468254219Scy				continue;
469254219Scy			}
470254219Scy			bzero(tmpbuf, sizeof(tmpbuf));
471254219Scy		}
472254219Scy		return (0);
473254219Scy	}
474254219Scy	/* NOTREACHED */
475254219Scy}
476254219Scy
477254219Scystatic int
478254219Scyeli_genkey_passphrase(struct gctl_req *req, struct g_eli_metadata *md, bool new,
479254219Scy    struct hmac_ctx *ctxp)
480254219Scy{
481254219Scy	char passbuf[BUFSIZE];
482254219Scy	bool nopassphrase;
483254219Scy	int nfiles;
484254219Scy
485254219Scy	nopassphrase =
486254219Scy	    gctl_get_int(req, new ? "nonewpassphrase" : "nopassphrase");
487254219Scy	if (nopassphrase) {
488254219Scy		if (gctl_has_param(req, new ? "newpassfile0" : "passfile0")) {
489254219Scy			gctl_error(req,
490254219Scy			    "Options -%c and -%c are mutually exclusive.",
491254219Scy			    new ? 'J' : 'j', new ? 'P' : 'p');
492254219Scy			return (-1);
493254219Scy		}
494254219Scy		return (0);
495254219Scy	}
496254219Scy
497254219Scy	if (!new && md->md_iterations == -1) {
498254219Scy		gctl_error(req, "Missing -p flag.");
499254219Scy		return (-1);
500254219Scy	}
501254219Scy	passbuf[0] = '\0';
502254219Scy	nfiles = eli_genkey_files(req, new, "passfile", NULL, passbuf,
503254219Scy	    sizeof(passbuf));
504254219Scy	if (nfiles == -1)
505254219Scy		return (-1);
506254219Scy	else if (nfiles == 0) {
507254219Scy		if (eli_genkey_passphrase_prompt(req, new, passbuf,
508254219Scy		    sizeof(passbuf)) == -1) {
509254219Scy			return (-1);
510254219Scy		}
511254219Scy	}
512254219Scy	/*
513254219Scy	 * Field md_iterations equal to -1 means "choose some sane
514254219Scy	 * value for me".
515254219Scy	 */
516254219Scy	if (md->md_iterations == -1) {
517254219Scy		assert(new);
518254219Scy		if (verbose)
519254219Scy			printf("Calculating number of iterations...\n");
520254219Scy		md->md_iterations = pkcs5v2_calculate(2000000);
521254219Scy		assert(md->md_iterations > 0);
522254219Scy		if (verbose) {
523254219Scy			printf("Done, using %d iterations.\n",
524254219Scy			    md->md_iterations);
525254219Scy		}
526254219Scy	}
527254219Scy	/*
528254219Scy	 * If md_iterations is equal to 0, user doesn't want PKCS#5v2.
529254219Scy	 */
530254219Scy	if (md->md_iterations == 0) {
531254219Scy		g_eli_crypto_hmac_update(ctxp, md->md_salt,
532254219Scy		    sizeof(md->md_salt));
533254219Scy		g_eli_crypto_hmac_update(ctxp, passbuf, strlen(passbuf));
534254219Scy	} else /* if (md->md_iterations > 0) */ {
535254219Scy		unsigned char dkey[G_ELI_USERKEYLEN];
536254219Scy
537254219Scy		pkcs5v2_genkey(dkey, sizeof(dkey), md->md_salt,
538254219Scy		    sizeof(md->md_salt), passbuf, md->md_iterations);
539254219Scy		g_eli_crypto_hmac_update(ctxp, dkey, sizeof(dkey));
540254219Scy		bzero(dkey, sizeof(dkey));
541254219Scy	}
542254219Scy	bzero(passbuf, sizeof(passbuf));
543254219Scy
544254219Scy	return (0);
545254219Scy}
546254219Scy
547254219Scystatic unsigned char *
548254219Scyeli_genkey(struct gctl_req *req, struct g_eli_metadata *md, unsigned char *key,
549254219Scy    bool new)
550254219Scy{
551254219Scy	struct hmac_ctx ctx;
552254219Scy	bool nopassphrase;
553254219Scy	int nfiles;
554254219Scy
555254219Scy	nopassphrase =
556254219Scy	    gctl_get_int(req, new ? "nonewpassphrase" : "nopassphrase");
557254219Scy
558254219Scy	g_eli_crypto_hmac_init(&ctx, NULL, 0);
559254219Scy
560254219Scy	nfiles = eli_genkey_files(req, new, "keyfile", &ctx, NULL, 0);
561254219Scy	if (nfiles == -1)
562254219Scy		return (NULL);
563254219Scy	else if (nfiles == 0 && nopassphrase) {
564254219Scy		gctl_error(req, "No key components given.");
565254219Scy		return (NULL);
566254219Scy	}
567254219Scy
568254219Scy	if (eli_genkey_passphrase(req, md, new, &ctx) == -1)
569254219Scy		return (NULL);
570254219Scy
571254219Scy	g_eli_crypto_hmac_final(&ctx, key, 0);
572254219Scy
573254219Scy	return (key);
574254219Scy}
575254219Scy
576254219Scystatic int
577254219Scyeli_metadata_read(struct gctl_req *req, const char *prov,
578254219Scy    struct g_eli_metadata *md)
579254219Scy{
580254219Scy	unsigned char sector[sizeof(struct g_eli_metadata)];
581254219Scy	int error;
582254219Scy
583254219Scy	if (g_get_sectorsize(prov) == 0) {
584254219Scy		int fd;
585254219Scy
586254219Scy		/* This is a file probably. */
587254219Scy		fd = open(prov, O_RDONLY);
588254219Scy		if (fd == -1) {
589254219Scy			gctl_error(req, "Cannot open %s: %s.", prov,
590254219Scy			    strerror(errno));
591254219Scy			return (-1);
592254219Scy		}
593254219Scy		if (read(fd, sector, sizeof(sector)) != sizeof(sector)) {
594254219Scy			gctl_error(req, "Cannot read metadata from %s: %s.",
595254219Scy			    prov, strerror(errno));
596254219Scy			close(fd);
597254219Scy			return (-1);
598254219Scy		}
599254219Scy		close(fd);
600254219Scy	} else {
601254219Scy		/* This is a GEOM provider. */
602254219Scy		error = g_metadata_read(prov, sector, sizeof(sector),
603254219Scy		    G_ELI_MAGIC);
604254219Scy		if (error != 0) {
605254219Scy			gctl_error(req, "Cannot read metadata from %s: %s.",
606254219Scy			    prov, strerror(error));
607254219Scy			return (-1);
608254219Scy		}
609254219Scy	}
610254219Scy	error = eli_metadata_decode(sector, md);
611254219Scy	switch (error) {
612254219Scy	case 0:
613254219Scy		break;
614254219Scy	case EOPNOTSUPP:
615254219Scy		gctl_error(req,
616254219Scy		    "Provider's %s metadata version %u is too new.\n"
617254219Scy		    "geli: The highest supported version is %u.",
618254219Scy		    prov, (unsigned int)md->md_version, G_ELI_VERSION);
619254219Scy		return (-1);
620254219Scy	case EINVAL:
621254219Scy		gctl_error(req, "Inconsistent provider's %s metadata.", prov);
622254219Scy		return (-1);
623254219Scy	default:
624254219Scy		gctl_error(req,
625254219Scy		    "Unexpected error while decoding provider's %s metadata: %s.",
626254219Scy		    prov, strerror(error));
627254219Scy		return (-1);
628254219Scy	}
629254219Scy	return (0);
630254219Scy}
631254219Scy
632254219Scystatic int
633254219Scyeli_metadata_store(struct gctl_req *req, const char *prov,
634254219Scy    struct g_eli_metadata *md)
635254219Scy{
636254219Scy	unsigned char sector[sizeof(struct g_eli_metadata)];
637254219Scy	int error;
638254219Scy
639254219Scy	eli_metadata_encode(md, sector);
640254219Scy	if (g_get_sectorsize(prov) == 0) {
641254219Scy		int fd;
642254219Scy
643254219Scy		/* This is a file probably. */
644254219Scy		fd = open(prov, O_WRONLY | O_TRUNC);
645254219Scy		if (fd == -1) {
646254219Scy			gctl_error(req, "Cannot open %s: %s.", prov,
647254219Scy			    strerror(errno));
648254219Scy			bzero(sector, sizeof(sector));
649254219Scy			return (-1);
650254219Scy		}
651254219Scy		if (write(fd, sector, sizeof(sector)) != sizeof(sector)) {
652254219Scy			gctl_error(req, "Cannot write metadata to %s: %s.",
653254219Scy			    prov, strerror(errno));
654254219Scy			bzero(sector, sizeof(sector));
655254219Scy			close(fd);
656254219Scy			return (-1);
657254219Scy		}
658254219Scy		close(fd);
659254219Scy	} else {
660254219Scy		/* This is a GEOM provider. */
661254219Scy		error = g_metadata_store(prov, sector, sizeof(sector));
662254219Scy		if (error != 0) {
663254219Scy			gctl_error(req, "Cannot write metadata to %s: %s.",
664254219Scy			    prov, strerror(errno));
665254219Scy			bzero(sector, sizeof(sector));
666254219Scy			return (-1);
667254219Scy		}
668254219Scy	}
669254219Scy	bzero(sector, sizeof(sector));
670254219Scy	return (0);
671254219Scy}
672254219Scy
673254219Scystatic void
674254219Scyeli_init(struct gctl_req *req)
675254219Scy{
676254219Scy	struct g_eli_metadata md;
677254219Scy	unsigned char sector[sizeof(struct g_eli_metadata)] __aligned(4);
678254219Scy	unsigned char key[G_ELI_USERKEYLEN];
679254219Scy	char backfile[MAXPATHLEN];
680254219Scy	const char *str, *prov;
681254219Scy	unsigned int secsize, version;
682254219Scy	off_t mediasize;
683254219Scy	intmax_t val;
684254219Scy	int error, nargs;
685254219Scy
686254219Scy	nargs = gctl_get_int(req, "nargs");
687254219Scy	if (nargs != 1) {
688254219Scy		gctl_error(req, "Invalid number of arguments.");
689254219Scy		return;
690254219Scy	}
691254219Scy	prov = gctl_get_ascii(req, "arg0");
692254219Scy	mediasize = g_get_mediasize(prov);
693254219Scy	secsize = g_get_sectorsize(prov);
694254219Scy	if (mediasize == 0 || secsize == 0) {
695254219Scy		gctl_error(req, "Cannot get informations about %s: %s.", prov,
696254219Scy		    strerror(errno));
697254219Scy		return;
698254219Scy	}
699254219Scy
700254219Scy	bzero(&md, sizeof(md));
701254219Scy	strlcpy(md.md_magic, G_ELI_MAGIC, sizeof(md.md_magic));
702254219Scy	val = gctl_get_intmax(req, "mdversion");
703254219Scy	if (val == -1) {
704254219Scy		version = G_ELI_VERSION;
705254219Scy	} else if (val < 0 || val > G_ELI_VERSION) {
706254219Scy		gctl_error(req,
707254219Scy		    "Invalid version specified should be between %u and %u.",
708254219Scy		    G_ELI_VERSION_00, G_ELI_VERSION);
709254219Scy		return;
710254219Scy	} else {
711254219Scy		version = val;
712254219Scy	}
713254219Scy	md.md_version = version;
714254219Scy	md.md_flags = 0;
715254219Scy	if (gctl_get_int(req, "boot"))
716254219Scy		md.md_flags |= G_ELI_FLAG_BOOT;
717254219Scy	if (gctl_get_int(req, "geliboot"))
718254219Scy		md.md_flags |= G_ELI_FLAG_GELIBOOT;
719254219Scy	if (gctl_get_int(req, "displaypass"))
720254219Scy		md.md_flags |= G_ELI_FLAG_GELIDISPLAYPASS;
721254219Scy	if (gctl_get_int(req, "notrim"))
722254219Scy		md.md_flags |= G_ELI_FLAG_NODELETE;
723254219Scy	md.md_ealgo = CRYPTO_ALGORITHM_MIN - 1;
724254219Scy	str = gctl_get_ascii(req, "aalgo");
725254219Scy	if (*str != '\0') {
726254219Scy		if (version < G_ELI_VERSION_01) {
727254219Scy			gctl_error(req,
728254219Scy			    "Data authentication is supported starting from version %u.",
729254219Scy			    G_ELI_VERSION_01);
730254219Scy			return;
731254219Scy		}
732254219Scy		md.md_aalgo = g_eli_str2aalgo(str);
733254219Scy		if (md.md_aalgo >= CRYPTO_ALGORITHM_MIN &&
734254219Scy		    md.md_aalgo <= CRYPTO_ALGORITHM_MAX) {
735254219Scy			md.md_flags |= G_ELI_FLAG_AUTH;
736254219Scy		} else {
737254219Scy			/*
738254219Scy			 * For backward compatibility, check if the -a option
739254219Scy			 * was used to provide encryption algorithm.
740254219Scy			 */
741254219Scy			md.md_ealgo = g_eli_str2ealgo(str);
742254219Scy			if (md.md_ealgo < CRYPTO_ALGORITHM_MIN ||
743254219Scy			    md.md_ealgo > CRYPTO_ALGORITHM_MAX) {
744254219Scy				gctl_error(req,
745254219Scy				    "Invalid authentication algorithm.");
746254219Scy				return;
747254219Scy			} else {
748254219Scy				fprintf(stderr, "warning: The -e option, not "
749254219Scy				    "the -a option is now used to specify "
750254219Scy				    "encryption algorithm to use.\n");
751254219Scy			}
752254219Scy		}
753254219Scy	}
754254219Scy	if (md.md_ealgo < CRYPTO_ALGORITHM_MIN ||
755254219Scy	    md.md_ealgo > CRYPTO_ALGORITHM_MAX) {
756254219Scy		str = gctl_get_ascii(req, "ealgo");
757254219Scy		if (*str == '\0') {
758254219Scy			if (version < G_ELI_VERSION_05)
759254219Scy				str = "aes-cbc";
760254219Scy			else
761254219Scy				str = GELI_ENC_ALGO;
762254219Scy		}
763254219Scy		md.md_ealgo = g_eli_str2ealgo(str);
764254219Scy		if (md.md_ealgo < CRYPTO_ALGORITHM_MIN ||
765254219Scy		    md.md_ealgo > CRYPTO_ALGORITHM_MAX) {
766254219Scy			gctl_error(req, "Invalid encryption algorithm.");
767254219Scy			return;
768254219Scy		}
769254219Scy		if (md.md_ealgo == CRYPTO_CAMELLIA_CBC &&
770254219Scy		    version < G_ELI_VERSION_04) {
771254219Scy			gctl_error(req,
772254219Scy			    "Camellia-CBC algorithm is supported starting from version %u.",
773254219Scy			    G_ELI_VERSION_04);
774254219Scy			return;
775254219Scy		}
776254219Scy		if (md.md_ealgo == CRYPTO_AES_XTS &&
777254219Scy		    version < G_ELI_VERSION_05) {
778254219Scy			gctl_error(req,
779254219Scy			    "AES-XTS algorithm is supported starting from version %u.",
780254219Scy			    G_ELI_VERSION_05);
781254219Scy			return;
782254219Scy		}
783254219Scy	}
784254219Scy	if (md.md_flags & G_ELI_FLAG_AUTH) {
785254219Scy		switch (md.md_aalgo) {
786254219Scy		case CRYPTO_MD5_HMAC:
787254219Scy			gctl_error(req,
788254219Scy			    "The %s authentication algorithm is deprecated.",
789254219Scy			    g_eli_algo2str(md.md_aalgo));
790254219Scy			return;
791254219Scy		}
792254219Scy	}
793254219Scy	switch (md.md_ealgo) {
794254219Scy	case CRYPTO_3DES_CBC:
795254219Scy	case CRYPTO_BLF_CBC:
796254219Scy		gctl_error(req, "The %s encryption algorithm is deprecated.",
797254219Scy		    g_eli_algo2str(md.md_ealgo));
798254219Scy		return;
799254219Scy	}
800254219Scy	val = gctl_get_intmax(req, "keylen");
801254219Scy	md.md_keylen = val;
802254219Scy	md.md_keylen = g_eli_keylen(md.md_ealgo, md.md_keylen);
803254219Scy	if (md.md_keylen == 0) {
804254219Scy		gctl_error(req, "Invalid key length.");
805254219Scy		return;
806254219Scy	}
807254219Scy	md.md_provsize = mediasize;
808254219Scy
809254219Scy	val = gctl_get_intmax(req, "iterations");
810254219Scy	if (val != -1) {
811254219Scy		int nonewpassphrase;
812254219Scy
813254219Scy		/*
814254219Scy		 * Don't allow to set iterations when there will be no
815254219Scy		 * passphrase.
816254219Scy		 */
817254219Scy		nonewpassphrase = gctl_get_int(req, "nonewpassphrase");
818254219Scy		if (nonewpassphrase) {
819254219Scy			gctl_error(req,
820254219Scy			    "Options -i and -P are mutually exclusive.");
821254219Scy			return;
822254219Scy		}
823254219Scy	}
824254219Scy	md.md_iterations = val;
825254219Scy
826254219Scy	val = gctl_get_intmax(req, "sectorsize");
827254219Scy	if (val == 0)
828254219Scy		md.md_sectorsize = secsize;
829254219Scy	else {
830254219Scy		if (val < 0 || (val % secsize) != 0 || !powerof2(val)) {
831254219Scy			gctl_error(req, "Invalid sector size.");
832254219Scy			return;
833254219Scy		}
834254219Scy		if (val > sysconf(_SC_PAGE_SIZE)) {
835254219Scy			fprintf(stderr,
836254219Scy			    "warning: Using sectorsize bigger than the page size!\n");
837254219Scy		}
838254219Scy		md.md_sectorsize = val;
839254219Scy	}
840254219Scy
841254219Scy	md.md_keys = 0x01;
842254219Scy	arc4random_buf(md.md_salt, sizeof(md.md_salt));
843254219Scy	arc4random_buf(md.md_mkeys, sizeof(md.md_mkeys));
844254219Scy
845254219Scy	/* Generate user key. */
846254219Scy	if (eli_genkey(req, &md, key, true) == NULL) {
847254219Scy		bzero(key, sizeof(key));
848254219Scy		bzero(&md, sizeof(md));
849254219Scy		return;
850254219Scy	}
851254219Scy
852254219Scy	/* Encrypt the first and the only Master Key. */
853254219Scy	error = g_eli_mkey_encrypt(md.md_ealgo, key, md.md_keylen, md.md_mkeys);
854254219Scy	bzero(key, sizeof(key));
855254219Scy	if (error != 0) {
856254219Scy		bzero(&md, sizeof(md));
857254219Scy		gctl_error(req, "Cannot encrypt Master Key: %s.",
858254219Scy		    strerror(error));
859254219Scy		return;
860254219Scy	}
861254219Scy
862254219Scy	eli_metadata_encode(&md, sector);
863254219Scy	bzero(&md, sizeof(md));
864254219Scy	error = g_metadata_store(prov, sector, sizeof(sector));
865254219Scy	bzero(sector, sizeof(sector));
866254219Scy	if (error != 0) {
867254219Scy		gctl_error(req, "Cannot store metadata on %s: %s.", prov,
868254219Scy		    strerror(error));
869254219Scy		return;
870254219Scy	}
871254219Scy	if (verbose)
872254219Scy		printf("Metadata value stored on %s.\n", prov);
873254219Scy	/* Backup metadata to a file. */
874254219Scy	str = gctl_get_ascii(req, "backupfile");
875254219Scy	if (str[0] != '\0') {
876254219Scy		/* Backupfile given be the user, just copy it. */
877254219Scy		strlcpy(backfile, str, sizeof(backfile));
878254219Scy	} else {
879254219Scy		/* Generate file name automatically. */
880254219Scy		const char *p = prov;
881254219Scy		unsigned int i;
882254219Scy
883254219Scy		if (strncmp(p, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
884254219Scy			p += sizeof(_PATH_DEV) - 1;
885254219Scy		snprintf(backfile, sizeof(backfile), "%s%s.eli",
886254219Scy		    GELI_BACKUP_DIR, p);
887254219Scy		/* Replace all / with _. */
888254219Scy		for (i = strlen(GELI_BACKUP_DIR); backfile[i] != '\0'; i++) {
889254219Scy			if (backfile[i] == '/')
890254219Scy				backfile[i] = '_';
891254219Scy		}
892254219Scy	}
893254219Scy	if (strcmp(backfile, "none") != 0 &&
894254219Scy	    eli_backup_create(req, prov, backfile) == 0) {
895254219Scy		printf("\nMetadata backup can be found in %s and\n", backfile);
896254219Scy		printf("can be restored with the following command:\n");
897254219Scy		printf("\n\t# geli restore %s %s\n\n", backfile, prov);
898254219Scy	}
899254219Scy}
900254219Scy
901254219Scystatic void
902254219Scyeli_attach(struct gctl_req *req)
903254219Scy{
904254219Scy	struct g_eli_metadata md;
905254219Scy	unsigned char key[G_ELI_USERKEYLEN];
906254219Scy	const char *prov;
907254219Scy	off_t mediasize;
908254219Scy	int nargs;
909254219Scy
910254219Scy	nargs = gctl_get_int(req, "nargs");
911254219Scy	if (nargs != 1) {
912254219Scy		gctl_error(req, "Invalid number of arguments.");
913254219Scy		return;
914254219Scy	}
915254219Scy	prov = gctl_get_ascii(req, "arg0");
916254219Scy
917254219Scy	if (eli_metadata_read(req, prov, &md) == -1)
918254219Scy		return;
919254219Scy
920254219Scy	mediasize = g_get_mediasize(prov);
921254219Scy	if (md.md_provsize != (uint64_t)mediasize) {
922254219Scy		gctl_error(req, "Provider size mismatch.");
923254219Scy		return;
924254219Scy	}
925254219Scy
926254219Scy	if (eli_genkey(req, &md, key, false) == NULL) {
927254219Scy		bzero(key, sizeof(key));
928254219Scy		return;
929254219Scy	}
930254219Scy
931254219Scy	gctl_ro_param(req, "key", sizeof(key), key);
932254219Scy	if (gctl_issue(req) == NULL) {
933254219Scy		if (verbose)
934254219Scy			printf("Attached to %s.\n", prov);
935254219Scy	}
936254219Scy	bzero(key, sizeof(key));
937254219Scy}
938254219Scy
939254219Scystatic void
940254219Scyeli_configure_detached(struct gctl_req *req, const char *prov, int boot,
941254219Scy    int geliboot, int displaypass, int trim)
942254219Scy{
943254219Scy	struct g_eli_metadata md;
944254219Scy	bool changed = 0;
945254219Scy
946254219Scy	if (eli_metadata_read(req, prov, &md) == -1)
947254219Scy		return;
948254219Scy
949254219Scy	if (boot == 1 && (md.md_flags & G_ELI_FLAG_BOOT)) {
950254219Scy		if (verbose)
951254219Scy			printf("BOOT flag already configured for %s.\n", prov);
952254219Scy	} else if (boot == 0 && !(md.md_flags & G_ELI_FLAG_BOOT)) {
953254219Scy		if (verbose)
954254219Scy			printf("BOOT flag not configured for %s.\n", prov);
955254219Scy	} else if (boot >= 0) {
956254219Scy		if (boot)
957254219Scy			md.md_flags |= G_ELI_FLAG_BOOT;
958254219Scy		else
959254219Scy			md.md_flags &= ~G_ELI_FLAG_BOOT;
960254219Scy		changed = 1;
961254219Scy	}
962254219Scy
963254219Scy	if (geliboot == 1 && (md.md_flags & G_ELI_FLAG_GELIBOOT)) {
964254219Scy		if (verbose)
965254219Scy			printf("GELIBOOT flag already configured for %s.\n", prov);
966254219Scy	} else if (geliboot == 0 && !(md.md_flags & G_ELI_FLAG_GELIBOOT)) {
967254219Scy		if (verbose)
968254219Scy			printf("GELIBOOT flag not configured for %s.\n", prov);
969254219Scy	} else if (geliboot >= 0) {
970254219Scy		if (geliboot)
971254219Scy			md.md_flags |= G_ELI_FLAG_GELIBOOT;
972254219Scy		else
973254219Scy			md.md_flags &= ~G_ELI_FLAG_GELIBOOT;
974254219Scy		changed = 1;
975254219Scy	}
976254219Scy
977254219Scy	if (displaypass == 1 && (md.md_flags & G_ELI_FLAG_GELIDISPLAYPASS)) {
978254219Scy		if (verbose)
979254219Scy			printf("GELIDISPLAYPASS flag already configured for %s.\n", prov);
980254219Scy	} else if (displaypass == 0 &&
981254219Scy	    !(md.md_flags & G_ELI_FLAG_GELIDISPLAYPASS)) {
982254219Scy		if (verbose)
983254219Scy			printf("GELIDISPLAYPASS flag not configured for %s.\n", prov);
984254219Scy	} else if (displaypass >= 0) {
985254219Scy		if (displaypass)
986254219Scy			md.md_flags |= G_ELI_FLAG_GELIDISPLAYPASS;
987254219Scy		else
988254219Scy			md.md_flags &= ~G_ELI_FLAG_GELIDISPLAYPASS;
989254219Scy		changed = 1;
990254219Scy	}
991254219Scy
992254219Scy	if (trim == 0 && (md.md_flags & G_ELI_FLAG_NODELETE)) {
993254219Scy		if (verbose)
994254219Scy			printf("TRIM disable flag already configured for %s.\n", prov);
995254219Scy	} else if (trim == 1 && !(md.md_flags & G_ELI_FLAG_NODELETE)) {
996254219Scy		if (verbose)
997254219Scy			printf("TRIM disable flag not configured for %s.\n", prov);
998254219Scy	} else if (trim >= 0) {
999254219Scy		if (trim)
1000254219Scy			md.md_flags &= ~G_ELI_FLAG_NODELETE;
1001254219Scy		else
1002254219Scy			md.md_flags |= G_ELI_FLAG_NODELETE;
1003254219Scy		changed = 1;
1004254219Scy	}
1005254219Scy
1006254219Scy	if (changed)
1007254219Scy		eli_metadata_store(req, prov, &md);
1008254219Scy	bzero(&md, sizeof(md));
1009254219Scy}
1010254219Scy
1011254219Scystatic void
1012254219Scyeli_configure(struct gctl_req *req)
1013254219Scy{
1014254219Scy	const char *prov;
1015254219Scy	bool boot, noboot, geliboot, nogeliboot, displaypass, nodisplaypass;
1016254219Scy	bool trim, notrim;
1017254219Scy	int doboot, dogeliboot, dodisplaypass, dotrim;
1018254219Scy	int i, nargs;
1019254219Scy
1020254219Scy	nargs = gctl_get_int(req, "nargs");
1021254219Scy	if (nargs == 0) {
1022254219Scy		gctl_error(req, "Too few arguments.");
1023254219Scy		return;
1024254219Scy	}
1025254219Scy
1026254219Scy	boot = gctl_get_int(req, "boot");
1027254219Scy	noboot = gctl_get_int(req, "noboot");
1028254219Scy	geliboot = gctl_get_int(req, "geliboot");
1029254219Scy	nogeliboot = gctl_get_int(req, "nogeliboot");
1030254219Scy	displaypass = gctl_get_int(req, "displaypass");
1031254219Scy	nodisplaypass = gctl_get_int(req, "nodisplaypass");
1032254219Scy	trim = gctl_get_int(req, "trim");
1033254219Scy	notrim = gctl_get_int(req, "notrim");
1034254219Scy
1035254219Scy	doboot = -1;
1036254219Scy	if (boot && noboot) {
1037254219Scy		gctl_error(req, "Options -b and -B are mutually exclusive.");
1038254219Scy		return;
1039254219Scy	}
1040254219Scy	if (boot)
1041254219Scy		doboot = 1;
1042254219Scy	else if (noboot)
1043254219Scy		doboot = 0;
1044254219Scy
1045254219Scy	dogeliboot = -1;
1046254219Scy	if (geliboot && nogeliboot) {
1047254219Scy		gctl_error(req, "Options -g and -G are mutually exclusive.");
1048254219Scy		return;
1049254219Scy	}
1050254219Scy	if (geliboot)
1051254219Scy		dogeliboot = 1;
1052254219Scy	else if (nogeliboot)
1053254219Scy		dogeliboot = 0;
1054254219Scy
1055254219Scy	dodisplaypass = -1;
1056254219Scy	if (displaypass && nodisplaypass) {
1057254219Scy		gctl_error(req, "Options -d and -D are mutually exclusive.");
1058254219Scy		return;
1059254219Scy	}
1060254219Scy	if (displaypass)
1061254219Scy		dodisplaypass = 1;
1062254219Scy	else if (nodisplaypass)
1063254219Scy		dodisplaypass = 0;
1064254219Scy
1065254219Scy	dotrim = -1;
1066254219Scy	if (trim && notrim) {
1067254219Scy		gctl_error(req, "Options -t and -T are mutually exclusive.");
1068254219Scy		return;
1069254219Scy	}
1070254219Scy	if (trim)
1071254219Scy		dotrim = 1;
1072254219Scy	else if (notrim)
1073254219Scy		dotrim = 0;
1074254219Scy
1075254219Scy	if (doboot == -1 && dogeliboot == -1 && dodisplaypass == -1 &&
1076254219Scy	    dotrim == -1) {
1077254219Scy		gctl_error(req, "No option given.");
1078254219Scy		return;
1079254219Scy	}
1080254219Scy
1081254219Scy	/* First attached providers. */
1082254219Scy	gctl_issue(req);
1083254219Scy	/* Now the rest. */
1084254219Scy	for (i = 0; i < nargs; i++) {
1085254219Scy		prov = gctl_get_ascii(req, "arg%d", i);
1086254219Scy		if (!eli_is_attached(prov)) {
1087254219Scy			eli_configure_detached(req, prov, doboot, dogeliboot,
1088254219Scy			    dodisplaypass, dotrim);
1089254219Scy		}
1090254219Scy	}
1091254219Scy}
1092254219Scy
1093254219Scystatic void
1094254219Scyeli_setkey_attached(struct gctl_req *req, struct g_eli_metadata *md)
1095254219Scy{
1096254219Scy	unsigned char key[G_ELI_USERKEYLEN];
1097254219Scy	intmax_t val, old = 0;
1098254219Scy	int error;
1099254219Scy
1100254219Scy	val = gctl_get_intmax(req, "iterations");
1101254219Scy	/* Check if iterations number should be changed. */
1102254219Scy	if (val != -1)
1103254219Scy		md->md_iterations = val;
1104254219Scy	else
1105254219Scy		old = md->md_iterations;
1106254219Scy
1107254219Scy	/* Generate key for Master Key encryption. */
1108254219Scy	if (eli_genkey(req, md, key, true) == NULL) {
1109254219Scy		bzero(key, sizeof(key));
1110254219Scy		return;
1111254219Scy	}
1112254219Scy	/*
1113254219Scy	 * If number of iterations has changed, but wasn't given as a
1114254219Scy	 * command-line argument, update the request.
1115254219Scy	 */
1116254219Scy	if (val == -1 && md->md_iterations != old) {
1117254219Scy		error = gctl_change_param(req, "iterations", sizeof(intmax_t),
1118254219Scy		    &md->md_iterations);
1119254219Scy		assert(error == 0);
1120254219Scy	}
1121254219Scy
1122254219Scy	gctl_ro_param(req, "key", sizeof(key), key);
1123254219Scy	gctl_issue(req);
1124254219Scy	bzero(key, sizeof(key));
1125254219Scy}
1126254219Scy
1127254219Scystatic void
1128254219Scyeli_setkey_detached(struct gctl_req *req, const char *prov,
1129254219Scy struct g_eli_metadata *md)
1130254219Scy{
1131254219Scy	unsigned char key[G_ELI_USERKEYLEN], mkey[G_ELI_DATAIVKEYLEN];
1132254219Scy	unsigned char *mkeydst;
1133254219Scy	unsigned int nkey;
1134254219Scy	intmax_t val;
1135254219Scy	int error;
1136254219Scy
1137254219Scy	if (md->md_keys == 0) {
1138254219Scy		gctl_error(req, "No valid keys on %s.", prov);
1139254219Scy		return;
1140254219Scy	}
1141254219Scy
1142254219Scy	/* Generate key for Master Key decryption. */
1143254219Scy	if (eli_genkey(req, md, key, false) == NULL) {
1144254219Scy		bzero(key, sizeof(key));
1145254219Scy		return;
1146254219Scy	}
1147254219Scy
1148254219Scy	/* Decrypt Master Key. */
1149254219Scy	error = g_eli_mkey_decrypt_any(md, key, mkey, &nkey);
1150254219Scy	bzero(key, sizeof(key));
1151254219Scy	if (error != 0) {
1152254219Scy		bzero(md, sizeof(*md));
1153254219Scy		if (error == -1)
1154254219Scy			gctl_error(req, "Wrong key for %s.", prov);
1155254219Scy		else /* if (error > 0) */ {
1156254219Scy			gctl_error(req, "Cannot decrypt Master Key: %s.",
1157254219Scy			    strerror(error));
1158254219Scy		}
1159254219Scy		return;
1160254219Scy	}
1161254219Scy	if (verbose)
1162254219Scy		printf("Decrypted Master Key %u.\n", nkey);
1163254219Scy
1164254219Scy	val = gctl_get_intmax(req, "keyno");
1165254219Scy	if (val != -1)
1166254219Scy		nkey = val;
1167254219Scy#if 0
1168254219Scy	else
1169254219Scy		; /* Use the key number which was found during decryption. */
1170254219Scy#endif
1171254219Scy	if (nkey >= G_ELI_MAXMKEYS) {
1172254219Scy		gctl_error(req, "Invalid '%s' argument.", "keyno");
1173254219Scy		return;
1174254219Scy	}
1175254219Scy
1176254219Scy	val = gctl_get_intmax(req, "iterations");
1177254219Scy	/* Check if iterations number should and can be changed. */
1178254219Scy	if (val != -1 && md->md_iterations == -1) {
1179254219Scy		md->md_iterations = val;
1180254219Scy	} else if (val != -1 && val != md->md_iterations) {
1181254219Scy		if (bitcount32(md->md_keys) != 1) {
1182254219Scy			gctl_error(req, "To be able to use '-i' option, only "
1183254219Scy			    "one key can be defined.");
1184254219Scy			return;
1185254219Scy		}
1186254219Scy		if (md->md_keys != (1 << nkey)) {
1187254219Scy			gctl_error(req, "Only already defined key can be "
1188254219Scy			    "changed when '-i' option is used.");
1189254219Scy			return;
1190254219Scy		}
1191254219Scy		md->md_iterations = val;
1192254219Scy	}
1193254219Scy
1194254219Scy	mkeydst = md->md_mkeys + nkey * G_ELI_MKEYLEN;
1195254219Scy	md->md_keys |= (1 << nkey);
1196254219Scy
1197254219Scy	bcopy(mkey, mkeydst, sizeof(mkey));
1198254219Scy	bzero(mkey, sizeof(mkey));
1199254219Scy
1200254219Scy	/* Generate key for Master Key encryption. */
1201254219Scy	if (eli_genkey(req, md, key, true) == NULL) {
1202254219Scy		bzero(key, sizeof(key));
1203254219Scy		bzero(md, sizeof(*md));
1204254219Scy		return;
1205254219Scy	}
1206254219Scy
1207254219Scy	/* Encrypt the Master-Key with the new key. */
1208254219Scy	error = g_eli_mkey_encrypt(md->md_ealgo, key, md->md_keylen, mkeydst);
1209254219Scy	bzero(key, sizeof(key));
1210254219Scy	if (error != 0) {
1211254219Scy		bzero(md, sizeof(*md));
1212254219Scy		gctl_error(req, "Cannot encrypt Master Key: %s.",
1213254219Scy		    strerror(error));
1214254219Scy		return;
1215254219Scy	}
1216254219Scy
1217254219Scy	/* Store metadata with fresh key. */
1218254219Scy	eli_metadata_store(req, prov, md);
1219254219Scy	bzero(md, sizeof(*md));
1220254219Scy}
1221254219Scy
1222254219Scystatic void
1223254219Scyeli_setkey(struct gctl_req *req)
1224254219Scy{
1225254219Scy	struct g_eli_metadata md;
1226254219Scy	const char *prov;
1227254219Scy	int nargs;
1228254219Scy
1229254219Scy	nargs = gctl_get_int(req, "nargs");
1230254219Scy	if (nargs != 1) {
1231254219Scy		gctl_error(req, "Invalid number of arguments.");
1232254219Scy		return;
1233254219Scy	}
1234254219Scy	prov = gctl_get_ascii(req, "arg0");
1235254219Scy
1236254219Scy	if (eli_metadata_read(req, prov, &md) == -1)
1237254219Scy		return;
1238254219Scy
1239254219Scy	if (eli_is_attached(prov))
1240254219Scy		eli_setkey_attached(req, &md);
1241254219Scy	else
1242254219Scy		eli_setkey_detached(req, prov, &md);
1243254219Scy
1244254219Scy	if (req->error == NULL || req->error[0] == '\0') {
1245254219Scy		printf("Note, that the master key encrypted with old keys "
1246254219Scy		    "and/or passphrase may still exists in a metadata backup "
1247254219Scy		    "file.\n");
1248254219Scy	}
1249254219Scy}
1250254219Scy
1251254219Scystatic void
1252254219Scyeli_delkey_attached(struct gctl_req *req, const char *prov __unused)
1253254219Scy{
1254254219Scy
1255254219Scy	gctl_issue(req);
1256254219Scy}
1257254219Scy
1258254219Scystatic void
1259254219Scyeli_delkey_detached(struct gctl_req *req, const char *prov)
1260254219Scy{
1261254219Scy	struct g_eli_metadata md;
1262254219Scy	unsigned char *mkeydst;
1263254219Scy	unsigned int nkey;
1264254219Scy	intmax_t val;
1265254219Scy	bool all, force;
1266254219Scy
1267254219Scy	if (eli_metadata_read(req, prov, &md) == -1)
1268254219Scy		return;
1269254219Scy
1270254219Scy	all = gctl_get_int(req, "all");
1271254219Scy	if (all)
1272254219Scy		arc4random_buf(md.md_mkeys, sizeof(md.md_mkeys));
1273254219Scy	else {
1274254219Scy		force = gctl_get_int(req, "force");
1275254219Scy		val = gctl_get_intmax(req, "keyno");
1276254219Scy		if (val == -1) {
1277254219Scy			gctl_error(req, "Key number has to be specified.");
1278254219Scy			return;
1279254219Scy		}
1280254219Scy		nkey = val;
1281254219Scy		if (nkey >= G_ELI_MAXMKEYS) {
1282254219Scy			gctl_error(req, "Invalid '%s' argument.", "keyno");
1283254219Scy			return;
1284254219Scy		}
1285254219Scy		if (!(md.md_keys & (1 << nkey)) && !force) {
1286254219Scy			gctl_error(req, "Master Key %u is not set.", nkey);
1287254219Scy			return;
1288254219Scy		}
1289254219Scy		md.md_keys &= ~(1 << nkey);
1290254219Scy		if (md.md_keys == 0 && !force) {
1291254219Scy			gctl_error(req, "This is the last Master Key. Use '-f' "
1292254219Scy			    "option if you really want to remove it.");
1293254219Scy			return;
1294254219Scy		}
1295254219Scy		mkeydst = md.md_mkeys + nkey * G_ELI_MKEYLEN;
1296254219Scy		arc4random_buf(mkeydst, G_ELI_MKEYLEN);
1297254219Scy	}
1298254219Scy
1299254219Scy	eli_metadata_store(req, prov, &md);
1300254219Scy	bzero(&md, sizeof(md));
1301254219Scy}
1302254219Scy
1303254219Scystatic void
1304254219Scyeli_delkey(struct gctl_req *req)
1305254219Scy{
1306254219Scy	const char *prov;
1307254219Scy	int nargs;
1308254219Scy
1309254219Scy	nargs = gctl_get_int(req, "nargs");
1310254219Scy	if (nargs != 1) {
1311254219Scy		gctl_error(req, "Invalid number of arguments.");
1312254219Scy		return;
1313254219Scy	}
1314254219Scy	prov = gctl_get_ascii(req, "arg0");
1315254219Scy
1316254219Scy	if (eli_is_attached(prov))
1317254219Scy		eli_delkey_attached(req, prov);
1318254219Scy	else
1319254219Scy		eli_delkey_detached(req, prov);
1320254219Scy}
1321254219Scy
1322254219Scystatic void
1323254219Scyeli_resume(struct gctl_req *req)
1324254219Scy{
1325254219Scy	struct g_eli_metadata md;
1326254219Scy	unsigned char key[G_ELI_USERKEYLEN];
1327254219Scy	const char *prov;
1328254219Scy	off_t mediasize;
1329254219Scy	int nargs;
1330254219Scy
1331254219Scy	nargs = gctl_get_int(req, "nargs");
1332254219Scy	if (nargs != 1) {
1333254219Scy		gctl_error(req, "Invalid number of arguments.");
1334254219Scy		return;
1335254219Scy	}
1336254219Scy	prov = gctl_get_ascii(req, "arg0");
1337254219Scy
1338254219Scy	if (eli_metadata_read(req, prov, &md) == -1)
1339254219Scy		return;
1340254219Scy
1341254219Scy	mediasize = g_get_mediasize(prov);
1342254219Scy	if (md.md_provsize != (uint64_t)mediasize) {
1343254219Scy		gctl_error(req, "Provider size mismatch.");
1344254219Scy		return;
1345254219Scy	}
1346254219Scy
1347254219Scy	if (eli_genkey(req, &md, key, false) == NULL) {
1348254219Scy		bzero(key, sizeof(key));
1349254219Scy		return;
1350254219Scy	}
1351254219Scy
1352254219Scy	gctl_ro_param(req, "key", sizeof(key), key);
1353254219Scy	if (gctl_issue(req) == NULL) {
1354254219Scy		if (verbose)
1355254219Scy			printf("Resumed %s.\n", prov);
1356254219Scy	}
1357254219Scy	bzero(key, sizeof(key));
1358254219Scy}
1359254219Scy
1360254219Scystatic int
1361254219Scyeli_trash_metadata(struct gctl_req *req, const char *prov, int fd, off_t offset)
1362254219Scy{
1363254219Scy	unsigned int overwrites;
1364254219Scy	unsigned char *sector;
1365254219Scy	ssize_t size;
1366254219Scy	int error;
1367254219Scy
1368254219Scy	size = sizeof(overwrites);
1369254219Scy	if (sysctlbyname("kern.geom.eli.overwrites", &overwrites, &size,
1370254219Scy	    NULL, 0) == -1 || overwrites == 0) {
1371254219Scy		overwrites = G_ELI_OVERWRITES;
1372254219Scy	}
1373254219Scy
1374254219Scy	size = g_sectorsize(fd);
1375254219Scy	if (size <= 0) {
1376254219Scy		gctl_error(req, "Cannot obtain provider sector size %s: %s.",
1377254219Scy		    prov, strerror(errno));
1378254219Scy		return (-1);
1379254219Scy	}
1380254219Scy	sector = malloc(size);
1381254219Scy	if (sector == NULL) {
1382254219Scy		gctl_error(req, "Cannot allocate %zd bytes of memory.", size);
1383254219Scy		return (-1);
1384254219Scy	}
1385254219Scy
1386254219Scy	error = 0;
1387254219Scy	do {
1388254219Scy		arc4random_buf(sector, size);
1389254219Scy		if (pwrite(fd, sector, size, offset) != size) {
1390254219Scy			if (error == 0)
1391254219Scy				error = errno;
1392254219Scy		}
1393254219Scy		(void)g_flush(fd);
1394254219Scy	} while (--overwrites > 0);
1395254219Scy	free(sector);
1396254219Scy	if (error != 0) {
1397254219Scy		gctl_error(req, "Cannot trash metadata on provider %s: %s.",
1398254219Scy		    prov, strerror(error));
1399254219Scy		return (-1);
1400254219Scy	}
1401254219Scy	return (0);
1402254219Scy}
1403254219Scy
1404254219Scystatic void
1405254219Scyeli_kill_detached(struct gctl_req *req, const char *prov)
1406254219Scy{
1407254219Scy	off_t offset;
1408254219Scy	int fd;
1409254219Scy
1410254219Scy	/*
1411254219Scy	 * NOTE: Maybe we should verify if this is geli provider first,
1412254219Scy	 *       but 'kill' command is quite critical so better don't waste
1413254219Scy	 *       the time.
1414254219Scy	 */
1415254219Scy#if 0
1416254219Scy	error = g_metadata_read(prov, (unsigned char *)&md, sizeof(md),
1417254219Scy	    G_ELI_MAGIC);
1418254219Scy	if (error != 0) {
1419254219Scy		gctl_error(req, "Cannot read metadata from %s: %s.", prov,
1420254219Scy		    strerror(error));
1421254219Scy		return;
1422254219Scy	}
1423254219Scy#endif
1424254219Scy
1425254219Scy	fd = g_open(prov, 1);
1426254219Scy	if (fd == -1) {
1427254219Scy		gctl_error(req, "Cannot open provider %s: %s.", prov,
1428254219Scy		    strerror(errno));
1429254219Scy		return;
1430254219Scy	}
1431254219Scy	offset = g_mediasize(fd) - g_sectorsize(fd);
1432254219Scy	if (offset <= 0) {
1433254219Scy		gctl_error(req,
1434254219Scy		    "Cannot obtain media size or sector size for provider %s: %s.",
1435254219Scy		    prov, strerror(errno));
1436254219Scy		(void)g_close(fd);
1437254219Scy		return;
1438254219Scy	}
1439254219Scy	(void)eli_trash_metadata(req, prov, fd, offset);
1440254219Scy	(void)g_close(fd);
1441254219Scy}
1442254219Scy
1443254219Scystatic void
1444254219Scyeli_kill(struct gctl_req *req)
1445254219Scy{
1446254219Scy	const char *prov;
1447254219Scy	int i, nargs, all;
1448254219Scy
1449254219Scy	nargs = gctl_get_int(req, "nargs");
1450254219Scy	all = gctl_get_int(req, "all");
1451254219Scy	if (!all && nargs == 0) {
1452254219Scy		gctl_error(req, "Too few arguments.");
1453254219Scy		return;
1454254219Scy	}
1455254219Scy	/*
1456254219Scy	 * How '-a' option combine with a list of providers:
1457254219Scy	 * Delete Master Keys from all attached providers:
1458254219Scy	 * geli kill -a
1459254219Scy	 * Delete Master Keys from all attached providers and from
1460254219Scy	 * detached da0 and da1:
1461254219Scy	 * geli kill -a da0 da1
1462254219Scy	 * Delete Master Keys from (attached or detached) da0 and da1:
1463254219Scy	 * geli kill da0 da1
1464254219Scy	 */
1465254219Scy
1466254219Scy	/* First detached providers. */
1467254219Scy	for (i = 0; i < nargs; i++) {
1468254219Scy		prov = gctl_get_ascii(req, "arg%d", i);
1469254219Scy		if (!eli_is_attached(prov))
1470254219Scy			eli_kill_detached(req, prov);
1471254219Scy	}
1472254219Scy	/* Now attached providers. */
1473254219Scy	gctl_issue(req);
1474254219Scy}
1475254219Scy
1476254219Scystatic int
1477254219Scyeli_backup_create(struct gctl_req *req, const char *prov, const char *file)
1478254219Scy{
1479254219Scy	unsigned char *sector;
1480254219Scy	ssize_t secsize;
1481254219Scy	int error, filefd, ret;
1482254219Scy
1483254219Scy	ret = -1;
1484254219Scy	filefd = -1;
1485254219Scy	sector = NULL;
1486254219Scy	secsize = 0;
1487254219Scy
1488254219Scy	secsize = g_get_sectorsize(prov);
1489254219Scy	if (secsize == 0) {
1490254219Scy		gctl_error(req, "Cannot get informations about %s: %s.", prov,
1491254219Scy		    strerror(errno));
1492254219Scy		goto out;
1493254219Scy	}
1494254219Scy	sector = malloc(secsize);
1495254219Scy	if (sector == NULL) {
1496254219Scy		gctl_error(req, "Cannot allocate memory.");
1497254219Scy		goto out;
1498254219Scy	}
1499254219Scy	/* Read metadata from the provider. */
1500254219Scy	error = g_metadata_read(prov, sector, secsize, G_ELI_MAGIC);
1501254219Scy	if (error != 0) {
1502254219Scy		gctl_error(req, "Unable to read metadata from %s: %s.", prov,
1503254219Scy		    strerror(error));
1504254219Scy		goto out;
1505254219Scy	}
1506254219Scy
1507254219Scy	filefd = open(file, O_WRONLY | O_TRUNC | O_CREAT, 0600);
1508254219Scy	if (filefd == -1) {
1509254219Scy		gctl_error(req, "Unable to open %s: %s.", file,
1510254219Scy		    strerror(errno));
1511254219Scy		goto out;
1512254219Scy	}
1513254219Scy	/* Write metadata to the destination file. */
1514254219Scy	if (write(filefd, sector, secsize) != secsize) {
1515254219Scy		gctl_error(req, "Unable to write to %s: %s.", file,
1516254219Scy		    strerror(errno));
1517254219Scy		(void)close(filefd);
1518254219Scy		(void)unlink(file);
1519254219Scy		goto out;
1520254219Scy	}
1521254219Scy	(void)fsync(filefd);
1522254219Scy	(void)close(filefd);
1523254219Scy	/* Success. */
1524254219Scy	ret = 0;
1525254219Scyout:
1526254219Scy	if (sector != NULL) {
1527254219Scy		bzero(sector, secsize);
1528254219Scy		free(sector);
1529	}
1530	return (ret);
1531}
1532
1533static void
1534eli_backup(struct gctl_req *req)
1535{
1536	const char *file, *prov;
1537	int nargs;
1538
1539	nargs = gctl_get_int(req, "nargs");
1540	if (nargs != 2) {
1541		gctl_error(req, "Invalid number of arguments.");
1542		return;
1543	}
1544	prov = gctl_get_ascii(req, "arg0");
1545	file = gctl_get_ascii(req, "arg1");
1546
1547	eli_backup_create(req, prov, file);
1548}
1549
1550static void
1551eli_restore(struct gctl_req *req)
1552{
1553	struct g_eli_metadata md;
1554	const char *file, *prov;
1555	off_t mediasize;
1556	int nargs;
1557
1558	nargs = gctl_get_int(req, "nargs");
1559	if (nargs != 2) {
1560		gctl_error(req, "Invalid number of arguments.");
1561		return;
1562	}
1563	file = gctl_get_ascii(req, "arg0");
1564	prov = gctl_get_ascii(req, "arg1");
1565
1566	/* Read metadata from the backup file. */
1567	if (eli_metadata_read(req, file, &md) == -1)
1568		return;
1569	/* Obtain provider's mediasize. */
1570	mediasize = g_get_mediasize(prov);
1571	if (mediasize == 0) {
1572		gctl_error(req, "Cannot get informations about %s: %s.", prov,
1573		    strerror(errno));
1574		return;
1575	}
1576	/* Check if the provider size has changed since we did the backup. */
1577	if (md.md_provsize != (uint64_t)mediasize) {
1578		if (gctl_get_int(req, "force")) {
1579			md.md_provsize = mediasize;
1580		} else {
1581			gctl_error(req, "Provider size mismatch: "
1582			    "wrong backup file?");
1583			return;
1584		}
1585	}
1586	/* Write metadata to the provider. */
1587	(void)eli_metadata_store(req, prov, &md);
1588}
1589
1590static void
1591eli_resize(struct gctl_req *req)
1592{
1593	struct g_eli_metadata md;
1594	const char *prov;
1595	unsigned char *sector;
1596	ssize_t secsize;
1597	off_t mediasize, oldsize;
1598	int error, nargs, provfd;
1599
1600	nargs = gctl_get_int(req, "nargs");
1601	if (nargs != 1) {
1602		gctl_error(req, "Invalid number of arguments.");
1603		return;
1604	}
1605	prov = gctl_get_ascii(req, "arg0");
1606
1607	provfd = -1;
1608	sector = NULL;
1609	secsize = 0;
1610
1611	provfd = g_open(prov, 1);
1612	if (provfd == -1) {
1613		gctl_error(req, "Cannot open %s: %s.", prov, strerror(errno));
1614		goto out;
1615	}
1616
1617	mediasize = g_mediasize(provfd);
1618	secsize = g_sectorsize(provfd);
1619	if (mediasize == -1 || secsize == -1) {
1620		gctl_error(req, "Cannot get information about %s: %s.", prov,
1621		    strerror(errno));
1622		goto out;
1623	}
1624
1625	sector = malloc(secsize);
1626	if (sector == NULL) {
1627		gctl_error(req, "Cannot allocate memory.");
1628		goto out;
1629	}
1630
1631	oldsize = gctl_get_intmax(req, "oldsize");
1632	if (oldsize < 0 || oldsize > mediasize) {
1633		gctl_error(req, "Invalid oldsize: Out of range.");
1634		goto out;
1635	}
1636	if (oldsize == mediasize) {
1637		gctl_error(req, "Size hasn't changed.");
1638		goto out;
1639	}
1640
1641	/* Read metadata from the 'oldsize' offset. */
1642	if (pread(provfd, sector, secsize, oldsize - secsize) != secsize) {
1643		gctl_error(req, "Cannot read old metadata: %s.",
1644		    strerror(errno));
1645		goto out;
1646	}
1647
1648	/* Check if this sector contains geli metadata. */
1649	error = eli_metadata_decode(sector, &md);
1650	switch (error) {
1651	case 0:
1652		break;
1653	case EOPNOTSUPP:
1654		gctl_error(req,
1655		    "Provider's %s metadata version %u is too new.\n"
1656		    "geli: The highest supported version is %u.",
1657		    prov, (unsigned int)md.md_version, G_ELI_VERSION);
1658		goto out;
1659	case EINVAL:
1660		gctl_error(req, "Inconsistent provider's %s metadata.", prov);
1661		goto out;
1662	default:
1663		gctl_error(req,
1664		    "Unexpected error while decoding provider's %s metadata: %s.",
1665		    prov, strerror(error));
1666		goto out;
1667	}
1668
1669	/*
1670	 * If the old metadata doesn't have a correct provider size, refuse
1671	 * to resize.
1672	 */
1673	if (md.md_provsize != (uint64_t)oldsize) {
1674		gctl_error(req, "Provider size mismatch at oldsize.");
1675		goto out;
1676	}
1677
1678	/*
1679	 * Update the old metadata with the current provider size and write
1680	 * it back to the correct place on the provider.
1681	 */
1682	md.md_provsize = mediasize;
1683	/* Write metadata to the provider. */
1684	(void)eli_metadata_store(req, prov, &md);
1685	/* Now trash the old metadata. */
1686	(void)eli_trash_metadata(req, prov, provfd, oldsize - secsize);
1687out:
1688	if (provfd != -1)
1689		(void)g_close(provfd);
1690	if (sector != NULL) {
1691		bzero(sector, secsize);
1692		free(sector);
1693	}
1694}
1695
1696static void
1697eli_version(struct gctl_req *req)
1698{
1699	struct g_eli_metadata md;
1700	const char *name;
1701	unsigned int version;
1702	int error, i, nargs;
1703
1704	nargs = gctl_get_int(req, "nargs");
1705
1706	if (nargs == 0) {
1707		unsigned int kernver;
1708		ssize_t size;
1709
1710		size = sizeof(kernver);
1711		if (sysctlbyname("kern.geom.eli.version", &kernver, &size,
1712		    NULL, 0) == -1) {
1713			warn("Unable to obtain GELI kernel version");
1714		} else {
1715			printf("kernel: %u\n", kernver);
1716		}
1717		printf("userland: %u\n", G_ELI_VERSION);
1718		return;
1719	}
1720
1721	for (i = 0; i < nargs; i++) {
1722		name = gctl_get_ascii(req, "arg%d", i);
1723		error = g_metadata_read(name, (unsigned char *)&md,
1724		    sizeof(md), G_ELI_MAGIC);
1725		if (error != 0) {
1726			warn("%s: Unable to read metadata: %s.", name,
1727			    strerror(error));
1728			gctl_error(req, "Not fully done.");
1729			continue;
1730		}
1731		version = le32dec(&md.md_version);
1732		printf("%s: %u\n", name, version);
1733	}
1734}
1735
1736static void
1737eli_clear(struct gctl_req *req)
1738{
1739	const char *name;
1740	int error, i, nargs;
1741
1742	nargs = gctl_get_int(req, "nargs");
1743	if (nargs < 1) {
1744		gctl_error(req, "Too few arguments.");
1745		return;
1746	}
1747
1748	for (i = 0; i < nargs; i++) {
1749		name = gctl_get_ascii(req, "arg%d", i);
1750		error = g_metadata_clear(name, G_ELI_MAGIC);
1751		if (error != 0) {
1752			fprintf(stderr, "Cannot clear metadata on %s: %s.\n",
1753			    name, strerror(error));
1754			gctl_error(req, "Not fully done.");
1755			continue;
1756		}
1757		if (verbose)
1758			printf("Metadata cleared on %s.\n", name);
1759	}
1760}
1761
1762static void
1763eli_dump(struct gctl_req *req)
1764{
1765	struct g_eli_metadata md;
1766	const char *name;
1767	int i, nargs;
1768
1769	nargs = gctl_get_int(req, "nargs");
1770	if (nargs < 1) {
1771		gctl_error(req, "Too few arguments.");
1772		return;
1773	}
1774
1775	for (i = 0; i < nargs; i++) {
1776		name = gctl_get_ascii(req, "arg%d", i);
1777		if (eli_metadata_read(NULL, name, &md) == -1) {
1778			gctl_error(req, "Not fully done.");
1779			continue;
1780		}
1781		printf("Metadata on %s:\n", name);
1782		eli_metadata_dump(&md);
1783		printf("\n");
1784	}
1785}
1786