geom_eli.c revision 330449
1148456Spjd/*-
2330449Seadler * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3330449Seadler *
4213073Spjd * Copyright (c) 2004-2010 Pawel Jakub Dawidek <pjd@FreeBSD.org>
5148456Spjd * All rights reserved.
6148456Spjd *
7148456Spjd * Redistribution and use in source and binary forms, with or without
8148456Spjd * modification, are permitted provided that the following conditions
9148456Spjd * are met:
10148456Spjd * 1. Redistributions of source code must retain the above copyright
11148456Spjd *    notice, this list of conditions and the following disclaimer.
12148456Spjd * 2. Redistributions in binary form must reproduce the above copyright
13148456Spjd *    notice, this list of conditions and the following disclaimer in the
14148456Spjd *    documentation and/or other materials provided with the distribution.
15155175Spjd *
16148456Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
17148456Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18148456Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19148456Spjd * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
20148456Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21148456Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22148456Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23148456Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24148456Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25148456Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26148456Spjd * SUCH DAMAGE.
27148456Spjd */
28148456Spjd
29148456Spjd#include <sys/cdefs.h>
30148456Spjd__FBSDID("$FreeBSD: stable/11/sbin/geom/class/eli/geom_eli.c 330449 2018-03-05 07:26:05Z eadler $");
31148456Spjd
32226715Spjd#include <sys/param.h>
33226715Spjd#include <sys/mman.h>
34213060Spjd#include <sys/sysctl.h>
35226715Spjd#include <sys/resource.h>
36226715Spjd#include <opencrypto/cryptodev.h>
37213060Spjd
38226715Spjd#include <assert.h>
39226715Spjd#include <err.h>
40226715Spjd#include <errno.h>
41226715Spjd#include <fcntl.h>
42226715Spjd#include <libgeom.h>
43226715Spjd#include <paths.h>
44226715Spjd#include <readpassphrase.h>
45213172Spjd#include <stdbool.h>
46226715Spjd#include <stdint.h>
47148456Spjd#include <stdio.h>
48148456Spjd#include <stdlib.h>
49148456Spjd#include <string.h>
50148456Spjd#include <strings.h>
51226715Spjd#include <unistd.h>
52148456Spjd
53148456Spjd#include <geom/eli/g_eli.h>
54148456Spjd#include <geom/eli/pkcs5v2.h>
55148456Spjd
56148456Spjd#include "core/geom.h"
57148456Spjd#include "misc/subr.h"
58148456Spjd
59148456Spjd
60148456Spjduint32_t lib_version = G_LIB_VERSION;
61148456Spjduint32_t version = G_ELI_VERSION;
62148456Spjd
63182452Spjd#define	GELI_BACKUP_DIR	"/var/backups/"
64212547Spjd#define	GELI_ENC_ALGO	"aes"
65182452Spjd
66148456Spjdstatic void eli_main(struct gctl_req *req, unsigned flags);
67148456Spjdstatic void eli_init(struct gctl_req *req);
68148456Spjdstatic void eli_attach(struct gctl_req *req);
69162353Spjdstatic void eli_configure(struct gctl_req *req);
70148456Spjdstatic void eli_setkey(struct gctl_req *req);
71148456Spjdstatic void eli_delkey(struct gctl_req *req);
72214118Spjdstatic void eli_resume(struct gctl_req *req);
73148456Spjdstatic void eli_kill(struct gctl_req *req);
74148456Spjdstatic void eli_backup(struct gctl_req *req);
75148456Spjdstatic void eli_restore(struct gctl_req *req);
76212934Sbrianstatic void eli_resize(struct gctl_req *req);
77226723Spjdstatic void eli_version(struct gctl_req *req);
78148456Spjdstatic void eli_clear(struct gctl_req *req);
79148456Spjdstatic void eli_dump(struct gctl_req *req);
80148456Spjd
81182452Spjdstatic int eli_backup_create(struct gctl_req *req, const char *prov,
82182452Spjd    const char *file);
83182452Spjd
84148456Spjd/*
85148456Spjd * Available commands:
86148456Spjd *
87329114Skevans * init [-bdgPTv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] [-s sectorsize] [-V version] prov
88148456Spjd * label - alias for 'init'
89213172Spjd * attach [-dprv] [-j passfile] [-k keyfile] prov
90148456Spjd * detach [-fl] prov ...
91148456Spjd * stop - alias for 'detach'
92181639Spjd * onetime [-d] [-a aalgo] [-e ealgo] [-l keylen] prov
93297691Sallanjude * configure [-bBgGtT] prov ...
94213172Spjd * setkey [-pPv] [-n keyno] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov
95148456Spjd * delkey [-afv] [-n keyno] prov
96214118Spjd * suspend [-v] -a | prov ...
97214118Spjd * resume [-pv] [-j passfile] [-k keyfile] prov
98148456Spjd * kill [-av] [prov ...]
99148456Spjd * backup [-v] prov file
100212934Sbrian * restore [-fv] file prov
101212934Sbrian * resize [-v] -s oldsize prov
102226723Spjd * version [prov ...]
103148456Spjd * clear [-v] prov ...
104148456Spjd * dump [-v] prov ...
105148456Spjd */
106148456Spjdstruct g_command class_commands[] = {
107148456Spjd	{ "init", G_FLAG_VERBOSE, eli_main,
108148456Spjd	    {
109212547Spjd		{ 'a', "aalgo", "", G_TYPE_STRING },
110162868Spjd		{ 'b', "boot", NULL, G_TYPE_BOOL },
111212547Spjd		{ 'B', "backupfile", "", G_TYPE_STRING },
112329114Skevans		{ 'd', "displaypass", NULL, G_TYPE_BOOL },
113226733Spjd		{ 'e', "ealgo", "", G_TYPE_STRING },
114297691Sallanjude		{ 'g', "geliboot", NULL, G_TYPE_BOOL },
115212554Spjd		{ 'i', "iterations", "-1", G_TYPE_NUMBER },
116213172Spjd		{ 'J', "newpassfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
117213172Spjd		{ 'K', "newkeyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
118212554Spjd		{ 'l', "keylen", "0", G_TYPE_NUMBER },
119162868Spjd		{ 'P', "nonewpassphrase", NULL, G_TYPE_BOOL },
120212554Spjd		{ 's', "sectorsize", "0", G_TYPE_NUMBER },
121286444Spjd		{ 'T', "notrim", NULL, G_TYPE_BOOL },
122226733Spjd		{ 'V', "mdversion", "-1", G_TYPE_NUMBER },
123148456Spjd		G_OPT_SENTINEL
124148456Spjd	    },
125329114Skevans	    "[-bdgPTv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] [-s sectorsize] [-V version] prov"
126148456Spjd	},
127148456Spjd	{ "label", G_FLAG_VERBOSE, eli_main,
128148456Spjd	    {
129212547Spjd		{ 'a', "aalgo", "", G_TYPE_STRING },
130162868Spjd		{ 'b', "boot", NULL, G_TYPE_BOOL },
131212547Spjd		{ 'B', "backupfile", "", G_TYPE_STRING },
132329114Skevans		{ 'd', "displaypass", NULL, G_TYPE_BOOL },
133226733Spjd		{ 'e', "ealgo", "", G_TYPE_STRING },
134297691Sallanjude		{ 'g', "geliboot", NULL, G_TYPE_BOOL },
135212554Spjd		{ 'i', "iterations", "-1", G_TYPE_NUMBER },
136213172Spjd		{ 'J', "newpassfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
137213172Spjd		{ 'K', "newkeyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
138212554Spjd		{ 'l', "keylen", "0", G_TYPE_NUMBER },
139162868Spjd		{ 'P', "nonewpassphrase", NULL, G_TYPE_BOOL },
140212554Spjd		{ 's', "sectorsize", "0", G_TYPE_NUMBER },
141226733Spjd		{ 'V', "mdversion", "-1", G_TYPE_NUMBER },
142148456Spjd		G_OPT_SENTINEL
143148456Spjd	    },
144212554Spjd	    "- an alias for 'init'"
145148456Spjd	},
146148456Spjd	{ "attach", G_FLAG_VERBOSE | G_FLAG_LOADKLD, eli_main,
147148456Spjd	    {
148162868Spjd		{ 'd', "detach", NULL, G_TYPE_BOOL },
149213172Spjd		{ 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
150213172Spjd		{ 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
151162868Spjd		{ 'p', "nopassphrase", NULL, G_TYPE_BOOL },
152162868Spjd		{ 'r', "readonly", NULL, G_TYPE_BOOL },
153148456Spjd		G_OPT_SENTINEL
154148456Spjd	    },
155213172Spjd	    "[-dprv] [-j passfile] [-k keyfile] prov"
156148456Spjd	},
157148456Spjd	{ "detach", 0, NULL,
158148456Spjd	    {
159162868Spjd		{ 'f', "force", NULL, G_TYPE_BOOL },
160162868Spjd		{ 'l', "last", NULL, G_TYPE_BOOL },
161148456Spjd		G_OPT_SENTINEL
162148456Spjd	    },
163212554Spjd	    "[-fl] prov ..."
164148456Spjd	},
165148456Spjd	{ "stop", 0, NULL,
166148456Spjd	    {
167162868Spjd		{ 'f', "force", NULL, G_TYPE_BOOL },
168162868Spjd		{ 'l', "last", NULL, G_TYPE_BOOL },
169148456Spjd		G_OPT_SENTINEL
170148456Spjd	    },
171212554Spjd	    "- an alias for 'detach'"
172148456Spjd	},
173148456Spjd	{ "onetime", G_FLAG_VERBOSE | G_FLAG_LOADKLD, NULL,
174148456Spjd	    {
175212547Spjd		{ 'a', "aalgo", "", G_TYPE_STRING },
176162868Spjd		{ 'd', "detach", NULL, G_TYPE_BOOL },
177212547Spjd		{ 'e', "ealgo", GELI_ENC_ALGO, G_TYPE_STRING },
178212554Spjd		{ 'l', "keylen", "0", G_TYPE_NUMBER },
179212554Spjd		{ 's', "sectorsize", "0", G_TYPE_NUMBER },
180286444Spjd		{ 'T', "notrim", NULL, G_TYPE_BOOL },
181148456Spjd		G_OPT_SENTINEL
182148456Spjd	    },
183286444Spjd	    "[-dT] [-a aalgo] [-e ealgo] [-l keylen] [-s sectorsize] prov"
184148456Spjd	},
185162353Spjd	{ "configure", G_FLAG_VERBOSE, eli_main,
186162353Spjd	    {
187162868Spjd		{ 'b', "boot", NULL, G_TYPE_BOOL },
188162868Spjd		{ 'B', "noboot", NULL, G_TYPE_BOOL },
189329114Skevans		{ 'd', "displaypass", NULL, G_TYPE_BOOL },
190329114Skevans		{ 'D', "nodisplaypass", NULL, G_TYPE_BOOL },
191297691Sallanjude		{ 'g', "geliboot", NULL, G_TYPE_BOOL },
192297691Sallanjude		{ 'G', "nogeliboot", NULL, G_TYPE_BOOL },
193286444Spjd		{ 't', "trim", NULL, G_TYPE_BOOL },
194286444Spjd		{ 'T', "notrim", NULL, G_TYPE_BOOL },
195162353Spjd		G_OPT_SENTINEL
196162353Spjd	    },
197329114Skevans	    "[-bBdDgGtT] prov ..."
198162353Spjd	},
199148456Spjd	{ "setkey", G_FLAG_VERBOSE, eli_main,
200148456Spjd	    {
201212554Spjd		{ 'i', "iterations", "-1", G_TYPE_NUMBER },
202213172Spjd		{ 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
203213172Spjd		{ 'J', "newpassfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
204213172Spjd		{ 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
205213172Spjd		{ 'K', "newkeyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
206212554Spjd		{ 'n', "keyno", "-1", G_TYPE_NUMBER },
207162868Spjd		{ 'p', "nopassphrase", NULL, G_TYPE_BOOL },
208162868Spjd		{ 'P', "nonewpassphrase", NULL, G_TYPE_BOOL },
209148456Spjd		G_OPT_SENTINEL
210148456Spjd	    },
211213172Spjd	    "[-pPv] [-n keyno] [-i iterations] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov"
212148456Spjd	},
213148456Spjd	{ "delkey", G_FLAG_VERBOSE, eli_main,
214148456Spjd	    {
215162868Spjd		{ 'a', "all", NULL, G_TYPE_BOOL },
216162868Spjd		{ 'f', "force", NULL, G_TYPE_BOOL },
217212554Spjd		{ 'n', "keyno", "-1", G_TYPE_NUMBER },
218148456Spjd		G_OPT_SENTINEL
219148456Spjd	    },
220212554Spjd	    "[-afv] [-n keyno] prov"
221148456Spjd	},
222214118Spjd	{ "suspend", G_FLAG_VERBOSE, NULL,
223214118Spjd	    {
224214118Spjd		{ 'a', "all", NULL, G_TYPE_BOOL },
225214118Spjd		G_OPT_SENTINEL
226214118Spjd	    },
227214118Spjd	    "[-v] -a | prov ..."
228214118Spjd	},
229214118Spjd	{ "resume", G_FLAG_VERBOSE, eli_main,
230214118Spjd	    {
231214118Spjd		{ 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
232214118Spjd		{ 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
233214118Spjd		{ 'p', "nopassphrase", NULL, G_TYPE_BOOL },
234214118Spjd		G_OPT_SENTINEL
235214118Spjd	    },
236214118Spjd	    "[-pv] [-j passfile] [-k keyfile] prov"
237214118Spjd	},
238148456Spjd	{ "kill", G_FLAG_VERBOSE, eli_main,
239148456Spjd	    {
240162868Spjd		{ 'a', "all", NULL, G_TYPE_BOOL },
241148456Spjd		G_OPT_SENTINEL
242148456Spjd	    },
243212554Spjd	    "[-av] [prov ...]"
244148456Spjd	},
245212554Spjd	{ "backup", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS,
246148456Spjd	    "[-v] prov file"
247148456Spjd	},
248212934Sbrian	{ "restore", G_FLAG_VERBOSE, eli_main,
249212934Sbrian	    {
250212934Sbrian		{ 'f', "force", NULL, G_TYPE_BOOL },
251212934Sbrian		G_OPT_SENTINEL
252212934Sbrian	    },
253212934Sbrian	    "[-fv] file prov"
254148456Spjd	},
255212934Sbrian	{ "resize", G_FLAG_VERBOSE, eli_main,
256212934Sbrian	    {
257212934Sbrian		{ 's', "oldsize", NULL, G_TYPE_NUMBER },
258212934Sbrian		G_OPT_SENTINEL
259212934Sbrian	    },
260212934Sbrian	    "[-v] -s oldsize prov"
261212934Sbrian	},
262226723Spjd	{ "version", G_FLAG_LOADKLD, eli_main, G_NULL_OPTS,
263226723Spjd	    "[prov ...]"
264226723Spjd	},
265212554Spjd	{ "clear", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS,
266148456Spjd	    "[-v] prov ..."
267148456Spjd	},
268212554Spjd	{ "dump", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS,
269148456Spjd	    "[-v] prov ..."
270148456Spjd	},
271148456Spjd	G_CMD_SENTINEL
272148456Spjd};
273148456Spjd
274148456Spjdstatic int verbose = 0;
275148456Spjd
276248475Spjd#define	BUFSIZE	1024
277248475Spjd
278148456Spjdstatic int
279148456Spjdeli_protect(struct gctl_req *req)
280148456Spjd{
281148456Spjd	struct rlimit rl;
282148456Spjd
283148456Spjd	/* Disable core dumps. */
284148456Spjd	rl.rlim_cur = 0;
285148456Spjd	rl.rlim_max = 0;
286148456Spjd	if (setrlimit(RLIMIT_CORE, &rl) == -1) {
287148456Spjd		gctl_error(req, "Cannot disable core dumps: %s.",
288148456Spjd		    strerror(errno));
289148456Spjd		return (-1);
290148456Spjd	}
291148456Spjd	/* Disable swapping. */
292148456Spjd	if (mlockall(MCL_FUTURE) == -1) {
293148456Spjd		gctl_error(req, "Cannot lock memory: %s.", strerror(errno));
294148456Spjd		return (-1);
295148456Spjd	}
296148456Spjd	return (0);
297148456Spjd}
298148456Spjd
299148456Spjdstatic void
300213172Spjdeli_main(struct gctl_req *req, unsigned int flags)
301148456Spjd{
302148456Spjd	const char *name;
303148456Spjd
304148456Spjd	if (eli_protect(req) == -1)
305148456Spjd		return;
306148456Spjd
307148456Spjd	if ((flags & G_FLAG_VERBOSE) != 0)
308148456Spjd		verbose = 1;
309148456Spjd
310153190Spjd	name = gctl_get_ascii(req, "verb");
311148456Spjd	if (name == NULL) {
312148456Spjd		gctl_error(req, "No '%s' argument.", "verb");
313148456Spjd		return;
314148456Spjd	}
315148456Spjd	if (strcmp(name, "init") == 0 || strcmp(name, "label") == 0)
316148456Spjd		eli_init(req);
317148456Spjd	else if (strcmp(name, "attach") == 0)
318148456Spjd		eli_attach(req);
319162353Spjd	else if (strcmp(name, "configure") == 0)
320162353Spjd		eli_configure(req);
321148456Spjd	else if (strcmp(name, "setkey") == 0)
322148456Spjd		eli_setkey(req);
323148456Spjd	else if (strcmp(name, "delkey") == 0)
324148456Spjd		eli_delkey(req);
325214118Spjd	else if (strcmp(name, "resume") == 0)
326214118Spjd		eli_resume(req);
327148456Spjd	else if (strcmp(name, "kill") == 0)
328148456Spjd		eli_kill(req);
329148456Spjd	else if (strcmp(name, "backup") == 0)
330148456Spjd		eli_backup(req);
331148456Spjd	else if (strcmp(name, "restore") == 0)
332148456Spjd		eli_restore(req);
333212934Sbrian	else if (strcmp(name, "resize") == 0)
334212934Sbrian		eli_resize(req);
335226723Spjd	else if (strcmp(name, "version") == 0)
336226723Spjd		eli_version(req);
337148456Spjd	else if (strcmp(name, "dump") == 0)
338148456Spjd		eli_dump(req);
339148456Spjd	else if (strcmp(name, "clear") == 0)
340148456Spjd		eli_clear(req);
341148456Spjd	else
342148456Spjd		gctl_error(req, "Unknown command: %s.", name);
343148456Spjd}
344148456Spjd
345226717Spjdstatic bool
346148456Spjdeli_is_attached(const char *prov)
347148456Spjd{
348148456Spjd	char name[MAXPATHLEN];
349148456Spjd
350148456Spjd	/*
351148456Spjd	 * Not the best way to do it, but the easiest.
352148456Spjd	 * We try to open provider and check if it is a GEOM provider
353148456Spjd	 * by asking about its sectorsize.
354148456Spjd	 */
355148456Spjd	snprintf(name, sizeof(name), "%s%s", prov, G_ELI_SUFFIX);
356226717Spjd	return (g_get_sectorsize(name) > 0);
357148456Spjd}
358148456Spjd
359213172Spjdstatic int
360213172Spjdeli_genkey_files(struct gctl_req *req, bool new, const char *type,
361213172Spjd    struct hmac_ctx *ctxp, char *passbuf, size_t passbufsize)
362148456Spjd{
363248475Spjd	char *p, buf[BUFSIZE], argname[16];
364213172Spjd	const char *file;
365213172Spjd	int error, fd, i;
366213172Spjd	ssize_t done;
367148456Spjd
368213172Spjd	assert((strcmp(type, "keyfile") == 0 && ctxp != NULL &&
369213172Spjd	    passbuf == NULL && passbufsize == 0) ||
370213172Spjd	    (strcmp(type, "passfile") == 0 && ctxp == NULL &&
371213172Spjd	    passbuf != NULL && passbufsize > 0));
372213172Spjd	assert(strcmp(type, "keyfile") == 0 || passbuf[0] == '\0');
373148456Spjd
374213172Spjd	for (i = 0; ; i++) {
375213172Spjd		snprintf(argname, sizeof(argname), "%s%s%d",
376213172Spjd		    new ? "new" : "", type, i);
377148456Spjd
378213172Spjd		/* No more {key,pass}files? */
379213172Spjd		if (!gctl_has_param(req, argname))
380213172Spjd			return (i);
381148456Spjd
382215704Sbrucec		file = gctl_get_ascii(req, "%s", argname);
383213172Spjd		assert(file != NULL);
384213172Spjd
385213172Spjd		if (strcmp(file, "-") == 0)
386148456Spjd			fd = STDIN_FILENO;
387148456Spjd		else {
388213172Spjd			fd = open(file, O_RDONLY);
389148456Spjd			if (fd == -1) {
390213172Spjd				gctl_error(req, "Cannot open %s %s: %s.",
391213172Spjd				    type, file, strerror(errno));
392213172Spjd				return (-1);
393148456Spjd			}
394148456Spjd		}
395213172Spjd		if (strcmp(type, "keyfile") == 0) {
396213172Spjd			while ((done = read(fd, buf, sizeof(buf))) > 0)
397213172Spjd				g_eli_crypto_hmac_update(ctxp, buf, done);
398213172Spjd		} else /* if (strcmp(type, "passfile") == 0) */ {
399246621Spjd			assert(strcmp(type, "passfile") == 0);
400246621Spjd
401213172Spjd			while ((done = read(fd, buf, sizeof(buf) - 1)) > 0) {
402213172Spjd				buf[done] = '\0';
403213172Spjd				p = strchr(buf, '\n');
404213172Spjd				if (p != NULL) {
405213172Spjd					*p = '\0';
406213172Spjd					done = p - buf;
407213172Spjd				}
408213172Spjd				if (strlcat(passbuf, buf, passbufsize) >=
409213172Spjd				    passbufsize) {
410213172Spjd					gctl_error(req,
411213172Spjd					    "Passphrase in %s too long.", file);
412213172Spjd					bzero(buf, sizeof(buf));
413213172Spjd					return (-1);
414213172Spjd				}
415213172Spjd				if (p != NULL)
416213172Spjd					break;
417213172Spjd			}
418213172Spjd		}
419148456Spjd		error = errno;
420213172Spjd		if (strcmp(file, "-") != 0)
421148456Spjd			close(fd);
422148456Spjd		bzero(buf, sizeof(buf));
423148456Spjd		if (done == -1) {
424213172Spjd			gctl_error(req, "Cannot read %s %s: %s.",
425213172Spjd			    type, file, strerror(error));
426213172Spjd			return (-1);
427148456Spjd		}
428148456Spjd	}
429213172Spjd	/* NOTREACHED */
430213172Spjd}
431148456Spjd
432213172Spjdstatic int
433213172Spjdeli_genkey_passphrase_prompt(struct gctl_req *req, bool new, char *passbuf,
434213172Spjd    size_t passbufsize)
435213172Spjd{
436213172Spjd	char *p;
437148456Spjd
438213172Spjd	for (;;) {
439213172Spjd		p = readpassphrase(
440284250Sbrueffer		    new ? "Enter new passphrase: " : "Enter passphrase: ",
441213172Spjd		    passbuf, passbufsize, RPP_ECHO_OFF | RPP_REQUIRE_TTY);
442213172Spjd		if (p == NULL) {
443213172Spjd			bzero(passbuf, passbufsize);
444213172Spjd			gctl_error(req, "Cannot read passphrase: %s.",
445213172Spjd			    strerror(errno));
446213172Spjd			return (-1);
447149047Spjd		}
448213172Spjd
449213172Spjd		if (new) {
450248475Spjd			char tmpbuf[BUFSIZE];
451213172Spjd
452213172Spjd			p = readpassphrase("Reenter new passphrase: ",
453213172Spjd			    tmpbuf, sizeof(tmpbuf),
454213172Spjd			    RPP_ECHO_OFF | RPP_REQUIRE_TTY);
455148456Spjd			if (p == NULL) {
456213172Spjd				bzero(passbuf, passbufsize);
457213172Spjd				gctl_error(req,
458213172Spjd				    "Cannot read passphrase: %s.",
459148456Spjd				    strerror(errno));
460213172Spjd				return (-1);
461148456Spjd			}
462213172Spjd
463213172Spjd			if (strcmp(passbuf, tmpbuf) != 0) {
464213172Spjd				bzero(passbuf, passbufsize);
465213172Spjd				fprintf(stderr, "They didn't match.\n");
466213172Spjd				continue;
467148456Spjd			}
468213172Spjd			bzero(tmpbuf, sizeof(tmpbuf));
469148456Spjd		}
470213172Spjd		return (0);
471213172Spjd	}
472213172Spjd	/* NOTREACHED */
473213172Spjd}
474213172Spjd
475213172Spjdstatic int
476213172Spjdeli_genkey_passphrase(struct gctl_req *req, struct g_eli_metadata *md, bool new,
477213172Spjd    struct hmac_ctx *ctxp)
478213172Spjd{
479248475Spjd	char passbuf[BUFSIZE];
480213172Spjd	bool nopassphrase;
481213172Spjd	int nfiles;
482213172Spjd
483213172Spjd	nopassphrase =
484213172Spjd	    gctl_get_int(req, new ? "nonewpassphrase" : "nopassphrase");
485213172Spjd	if (nopassphrase) {
486213172Spjd		if (gctl_has_param(req, new ? "newpassfile0" : "passfile0")) {
487213172Spjd			gctl_error(req,
488213172Spjd			    "Options -%c and -%c are mutually exclusive.",
489213172Spjd			    new ? 'J' : 'j', new ? 'P' : 'p');
490213172Spjd			return (-1);
491148456Spjd		}
492213172Spjd		return (0);
493213172Spjd	}
494148456Spjd
495213172Spjd	if (!new && md->md_iterations == -1) {
496213172Spjd		gctl_error(req, "Missing -p flag.");
497213172Spjd		return (-1);
498213172Spjd	}
499213172Spjd	passbuf[0] = '\0';
500213172Spjd	nfiles = eli_genkey_files(req, new, "passfile", NULL, passbuf,
501213172Spjd	    sizeof(passbuf));
502213172Spjd	if (nfiles == -1)
503213172Spjd		return (-1);
504213172Spjd	else if (nfiles == 0) {
505213172Spjd		if (eli_genkey_passphrase_prompt(req, new, passbuf,
506213172Spjd		    sizeof(passbuf)) == -1) {
507213172Spjd			return (-1);
508148456Spjd		}
509148456Spjd	}
510213172Spjd	/*
511213172Spjd	 * Field md_iterations equal to -1 means "choose some sane
512213172Spjd	 * value for me".
513213172Spjd	 */
514213172Spjd	if (md->md_iterations == -1) {
515213172Spjd		assert(new);
516213172Spjd		if (verbose)
517213172Spjd			printf("Calculating number of iterations...\n");
518213172Spjd		md->md_iterations = pkcs5v2_calculate(2000000);
519213172Spjd		assert(md->md_iterations > 0);
520213172Spjd		if (verbose) {
521213172Spjd			printf("Done, using %d iterations.\n",
522213172Spjd			    md->md_iterations);
523213172Spjd		}
524213172Spjd	}
525213172Spjd	/*
526213172Spjd	 * If md_iterations is equal to 0, user doesn't want PKCS#5v2.
527213172Spjd	 */
528213172Spjd	if (md->md_iterations == 0) {
529213172Spjd		g_eli_crypto_hmac_update(ctxp, md->md_salt,
530213172Spjd		    sizeof(md->md_salt));
531213172Spjd		g_eli_crypto_hmac_update(ctxp, passbuf, strlen(passbuf));
532213172Spjd	} else /* if (md->md_iterations > 0) */ {
533213172Spjd		unsigned char dkey[G_ELI_USERKEYLEN];
534213172Spjd
535213172Spjd		pkcs5v2_genkey(dkey, sizeof(dkey), md->md_salt,
536213172Spjd		    sizeof(md->md_salt), passbuf, md->md_iterations);
537213172Spjd		g_eli_crypto_hmac_update(ctxp, dkey, sizeof(dkey));
538213172Spjd		bzero(dkey, sizeof(dkey));
539213172Spjd	}
540213172Spjd	bzero(passbuf, sizeof(passbuf));
541213172Spjd
542213172Spjd	return (0);
543213172Spjd}
544213172Spjd
545213172Spjdstatic unsigned char *
546213172Spjdeli_genkey(struct gctl_req *req, struct g_eli_metadata *md, unsigned char *key,
547213172Spjd    bool new)
548213172Spjd{
549213172Spjd	struct hmac_ctx ctx;
550213172Spjd	bool nopassphrase;
551213172Spjd	int nfiles;
552213172Spjd
553213172Spjd	nopassphrase =
554213172Spjd	    gctl_get_int(req, new ? "nonewpassphrase" : "nopassphrase");
555213172Spjd
556213172Spjd	g_eli_crypto_hmac_init(&ctx, NULL, 0);
557213172Spjd
558213172Spjd	nfiles = eli_genkey_files(req, new, "keyfile", &ctx, NULL, 0);
559213172Spjd	if (nfiles == -1)
560213172Spjd		return (NULL);
561213172Spjd	else if (nfiles == 0 && nopassphrase) {
562213172Spjd		gctl_error(req, "No key components given.");
563213172Spjd		return (NULL);
564213172Spjd	}
565213172Spjd
566213172Spjd	if (eli_genkey_passphrase(req, md, new, &ctx) == -1)
567213172Spjd		return (NULL);
568213172Spjd
569148456Spjd	g_eli_crypto_hmac_final(&ctx, key, 0);
570213172Spjd
571148456Spjd	return (key);
572148456Spjd}
573148456Spjd
574148456Spjdstatic int
575148456Spjdeli_metadata_read(struct gctl_req *req, const char *prov,
576148456Spjd    struct g_eli_metadata *md)
577148456Spjd{
578148456Spjd	unsigned char sector[sizeof(struct g_eli_metadata)];
579148456Spjd	int error;
580148456Spjd
581148456Spjd	if (g_get_sectorsize(prov) == 0) {
582148456Spjd		int fd;
583148456Spjd
584148456Spjd		/* This is a file probably. */
585148456Spjd		fd = open(prov, O_RDONLY);
586148456Spjd		if (fd == -1) {
587148456Spjd			gctl_error(req, "Cannot open %s: %s.", prov,
588148456Spjd			    strerror(errno));
589148456Spjd			return (-1);
590148456Spjd		}
591148456Spjd		if (read(fd, sector, sizeof(sector)) != sizeof(sector)) {
592148456Spjd			gctl_error(req, "Cannot read metadata from %s: %s.",
593148456Spjd			    prov, strerror(errno));
594148456Spjd			close(fd);
595148456Spjd			return (-1);
596148456Spjd		}
597148456Spjd		close(fd);
598148456Spjd	} else {
599148456Spjd		/* This is a GEOM provider. */
600148456Spjd		error = g_metadata_read(prov, sector, sizeof(sector),
601148456Spjd		    G_ELI_MAGIC);
602148456Spjd		if (error != 0) {
603148456Spjd			gctl_error(req, "Cannot read metadata from %s: %s.",
604148456Spjd			    prov, strerror(error));
605148456Spjd			return (-1);
606148456Spjd		}
607148456Spjd	}
608226722Spjd	error = eli_metadata_decode(sector, md);
609226722Spjd	switch (error) {
610226722Spjd	case 0:
611226722Spjd		break;
612226722Spjd	case EOPNOTSUPP:
613226722Spjd		gctl_error(req,
614226722Spjd		    "Provider's %s metadata version %u is too new.\n"
615226722Spjd		    "geli: The highest supported version is %u.",
616226722Spjd		    prov, (unsigned int)md->md_version, G_ELI_VERSION);
617148456Spjd		return (-1);
618226722Spjd	case EINVAL:
619226722Spjd		gctl_error(req, "Inconsistent provider's %s metadata.", prov);
620226722Spjd		return (-1);
621226722Spjd	default:
622226722Spjd		gctl_error(req,
623226722Spjd		    "Unexpected error while decoding provider's %s metadata: %s.",
624226722Spjd		    prov, strerror(error));
625226722Spjd		return (-1);
626148456Spjd	}
627148456Spjd	return (0);
628148456Spjd}
629148456Spjd
630148456Spjdstatic int
631148456Spjdeli_metadata_store(struct gctl_req *req, const char *prov,
632148456Spjd    struct g_eli_metadata *md)
633148456Spjd{
634148456Spjd	unsigned char sector[sizeof(struct g_eli_metadata)];
635148456Spjd	int error;
636148456Spjd
637148456Spjd	eli_metadata_encode(md, sector);
638148456Spjd	if (g_get_sectorsize(prov) == 0) {
639148456Spjd		int fd;
640148456Spjd
641148456Spjd		/* This is a file probably. */
642148456Spjd		fd = open(prov, O_WRONLY | O_TRUNC);
643148456Spjd		if (fd == -1) {
644148456Spjd			gctl_error(req, "Cannot open %s: %s.", prov,
645148456Spjd			    strerror(errno));
646148456Spjd			bzero(sector, sizeof(sector));
647148456Spjd			return (-1);
648148456Spjd		}
649148456Spjd		if (write(fd, sector, sizeof(sector)) != sizeof(sector)) {
650148456Spjd			gctl_error(req, "Cannot write metadata to %s: %s.",
651148456Spjd			    prov, strerror(errno));
652148456Spjd			bzero(sector, sizeof(sector));
653148456Spjd			close(fd);
654148456Spjd			return (-1);
655148456Spjd		}
656148456Spjd		close(fd);
657148456Spjd	} else {
658148456Spjd		/* This is a GEOM provider. */
659148456Spjd		error = g_metadata_store(prov, sector, sizeof(sector));
660148456Spjd		if (error != 0) {
661148456Spjd			gctl_error(req, "Cannot write metadata to %s: %s.",
662148456Spjd			    prov, strerror(errno));
663148456Spjd			bzero(sector, sizeof(sector));
664148456Spjd			return (-1);
665148456Spjd		}
666148456Spjd	}
667148456Spjd	bzero(sector, sizeof(sector));
668148456Spjd	return (0);
669148456Spjd}
670148456Spjd
671148456Spjdstatic void
672148456Spjdeli_init(struct gctl_req *req)
673148456Spjd{
674148456Spjd	struct g_eli_metadata md;
675148456Spjd	unsigned char sector[sizeof(struct g_eli_metadata)];
676148456Spjd	unsigned char key[G_ELI_USERKEYLEN];
677182452Spjd	char backfile[MAXPATHLEN];
678148456Spjd	const char *str, *prov;
679226733Spjd	unsigned int secsize, version;
680148456Spjd	off_t mediasize;
681153190Spjd	intmax_t val;
682155536Spjd	int error, nargs;
683148456Spjd
684153190Spjd	nargs = gctl_get_int(req, "nargs");
685153190Spjd	if (nargs != 1) {
686158214Spjd		gctl_error(req, "Invalid number of arguments.");
687148456Spjd		return;
688148456Spjd	}
689153190Spjd	prov = gctl_get_ascii(req, "arg0");
690148456Spjd	mediasize = g_get_mediasize(prov);
691148456Spjd	secsize = g_get_sectorsize(prov);
692148456Spjd	if (mediasize == 0 || secsize == 0) {
693148456Spjd		gctl_error(req, "Cannot get informations about %s: %s.", prov,
694148456Spjd		    strerror(errno));
695148456Spjd		return;
696148456Spjd	}
697148456Spjd
698148456Spjd	bzero(&md, sizeof(md));
699148456Spjd	strlcpy(md.md_magic, G_ELI_MAGIC, sizeof(md.md_magic));
700226733Spjd	val = gctl_get_intmax(req, "mdversion");
701226733Spjd	if (val == -1) {
702226733Spjd		version = G_ELI_VERSION;
703226733Spjd	} else if (val < 0 || val > G_ELI_VERSION) {
704226733Spjd		gctl_error(req,
705226733Spjd		    "Invalid version specified should be between %u and %u.",
706226733Spjd		    G_ELI_VERSION_00, G_ELI_VERSION);
707226733Spjd		return;
708226733Spjd	} else {
709226733Spjd		version = val;
710226733Spjd	}
711226733Spjd	md.md_version = version;
712148456Spjd	md.md_flags = 0;
713155536Spjd	if (gctl_get_int(req, "boot"))
714148456Spjd		md.md_flags |= G_ELI_FLAG_BOOT;
715297691Sallanjude	if (gctl_get_int(req, "geliboot"))
716297691Sallanjude		md.md_flags |= G_ELI_FLAG_GELIBOOT;
717329114Skevans	if (gctl_get_int(req, "displaypass"))
718329114Skevans		md.md_flags |= G_ELI_FLAG_GELIDISPLAYPASS;
719286444Spjd	if (gctl_get_int(req, "notrim"))
720286444Spjd		md.md_flags |= G_ELI_FLAG_NODELETE;
721159361Spjd	md.md_ealgo = CRYPTO_ALGORITHM_MIN - 1;
722159308Spjd	str = gctl_get_ascii(req, "aalgo");
723212547Spjd	if (*str != '\0') {
724226733Spjd		if (version < G_ELI_VERSION_01) {
725226733Spjd			gctl_error(req,
726226733Spjd			    "Data authentication is supported starting from version %u.",
727226733Spjd			    G_ELI_VERSION_01);
728226733Spjd			return;
729226733Spjd		}
730159308Spjd		md.md_aalgo = g_eli_str2aalgo(str);
731159361Spjd		if (md.md_aalgo >= CRYPTO_ALGORITHM_MIN &&
732159361Spjd		    md.md_aalgo <= CRYPTO_ALGORITHM_MAX) {
733159361Spjd			md.md_flags |= G_ELI_FLAG_AUTH;
734159361Spjd		} else {
735159361Spjd			/*
736159361Spjd			 * For backward compatibility, check if the -a option
737159361Spjd			 * was used to provide encryption algorithm.
738159361Spjd			 */
739159361Spjd			md.md_ealgo = g_eli_str2ealgo(str);
740159361Spjd			if (md.md_ealgo < CRYPTO_ALGORITHM_MIN ||
741159361Spjd			    md.md_ealgo > CRYPTO_ALGORITHM_MAX) {
742159361Spjd				gctl_error(req,
743159361Spjd				    "Invalid authentication algorithm.");
744159361Spjd				return;
745159361Spjd			} else {
746159361Spjd				fprintf(stderr, "warning: The -e option, not "
747159361Spjd				    "the -a option is now used to specify "
748159361Spjd				    "encryption algorithm to use.\n");
749159361Spjd			}
750159308Spjd		}
751159308Spjd	}
752159308Spjd	if (md.md_ealgo < CRYPTO_ALGORITHM_MIN ||
753159308Spjd	    md.md_ealgo > CRYPTO_ALGORITHM_MAX) {
754159361Spjd		str = gctl_get_ascii(req, "ealgo");
755226733Spjd		if (*str == '\0') {
756226733Spjd			if (version < G_ELI_VERSION_05)
757226733Spjd				str = "aes-cbc";
758226733Spjd			else
759226733Spjd				str = GELI_ENC_ALGO;
760226733Spjd		}
761159361Spjd		md.md_ealgo = g_eli_str2ealgo(str);
762159361Spjd		if (md.md_ealgo < CRYPTO_ALGORITHM_MIN ||
763159361Spjd		    md.md_ealgo > CRYPTO_ALGORITHM_MAX) {
764159361Spjd			gctl_error(req, "Invalid encryption algorithm.");
765159361Spjd			return;
766159361Spjd		}
767226733Spjd		if (md.md_ealgo == CRYPTO_CAMELLIA_CBC &&
768226733Spjd		    version < G_ELI_VERSION_04) {
769226733Spjd			gctl_error(req,
770226733Spjd			    "Camellia-CBC algorithm is supported starting from version %u.",
771226733Spjd			    G_ELI_VERSION_04);
772226733Spjd			return;
773226733Spjd		}
774226733Spjd		if (md.md_ealgo == CRYPTO_AES_XTS &&
775226733Spjd		    version < G_ELI_VERSION_05) {
776226733Spjd			gctl_error(req,
777226733Spjd			    "AES-XTS algorithm is supported starting from version %u.",
778226733Spjd			    G_ELI_VERSION_05);
779226733Spjd			return;
780226733Spjd		}
781148456Spjd	}
782153190Spjd	val = gctl_get_intmax(req, "keylen");
783153190Spjd	md.md_keylen = val;
784159308Spjd	md.md_keylen = g_eli_keylen(md.md_ealgo, md.md_keylen);
785148456Spjd	if (md.md_keylen == 0) {
786148456Spjd		gctl_error(req, "Invalid key length.");
787148456Spjd		return;
788148456Spjd	}
789148456Spjd	md.md_provsize = mediasize;
790148456Spjd
791153190Spjd	val = gctl_get_intmax(req, "iterations");
792155536Spjd	if (val != -1) {
793155536Spjd		int nonewpassphrase;
794155536Spjd
795155536Spjd		/*
796155536Spjd		 * Don't allow to set iterations when there will be no
797155536Spjd		 * passphrase.
798155536Spjd		 */
799155536Spjd		nonewpassphrase = gctl_get_int(req, "nonewpassphrase");
800155536Spjd		if (nonewpassphrase) {
801155536Spjd			gctl_error(req,
802155536Spjd			    "Options -i and -P are mutually exclusive.");
803155536Spjd			return;
804155536Spjd		}
805155536Spjd	}
806153190Spjd	md.md_iterations = val;
807148456Spjd
808153190Spjd	val = gctl_get_intmax(req, "sectorsize");
809153190Spjd	if (val == 0)
810148456Spjd		md.md_sectorsize = secsize;
811148456Spjd	else {
812260254Spjd		if (val < 0 || (val % secsize) != 0 || !powerof2(val)) {
813148456Spjd			gctl_error(req, "Invalid sector size.");
814148456Spjd			return;
815148456Spjd		}
816167229Spjd		if (val > sysconf(_SC_PAGE_SIZE)) {
817214404Spjd			fprintf(stderr,
818214404Spjd			    "warning: Using sectorsize bigger than the page size!\n");
819167229Spjd		}
820153190Spjd		md.md_sectorsize = val;
821148456Spjd	}
822148456Spjd
823148456Spjd	md.md_keys = 0x01;
824246620Spjd	arc4random_buf(md.md_salt, sizeof(md.md_salt));
825246620Spjd	arc4random_buf(md.md_mkeys, sizeof(md.md_mkeys));
826148456Spjd
827148456Spjd	/* Generate user key. */
828213172Spjd	if (eli_genkey(req, &md, key, true) == NULL) {
829148456Spjd		bzero(key, sizeof(key));
830148456Spjd		bzero(&md, sizeof(md));
831148456Spjd		return;
832148456Spjd	}
833148456Spjd
834148456Spjd	/* Encrypt the first and the only Master Key. */
835159308Spjd	error = g_eli_mkey_encrypt(md.md_ealgo, key, md.md_keylen, md.md_mkeys);
836148456Spjd	bzero(key, sizeof(key));
837148456Spjd	if (error != 0) {
838148456Spjd		bzero(&md, sizeof(md));
839148456Spjd		gctl_error(req, "Cannot encrypt Master Key: %s.",
840148456Spjd		    strerror(error));
841148456Spjd		return;
842148456Spjd	}
843148456Spjd
844148456Spjd	eli_metadata_encode(&md, sector);
845148456Spjd	bzero(&md, sizeof(md));
846148456Spjd	error = g_metadata_store(prov, sector, sizeof(sector));
847148456Spjd	bzero(sector, sizeof(sector));
848148456Spjd	if (error != 0) {
849148456Spjd		gctl_error(req, "Cannot store metadata on %s: %s.", prov,
850148456Spjd		    strerror(error));
851148456Spjd		return;
852148456Spjd	}
853148456Spjd	if (verbose)
854148456Spjd		printf("Metadata value stored on %s.\n", prov);
855182452Spjd	/* Backup metadata to a file. */
856182452Spjd	str = gctl_get_ascii(req, "backupfile");
857182452Spjd	if (str[0] != '\0') {
858182452Spjd		/* Backupfile given be the user, just copy it. */
859182452Spjd		strlcpy(backfile, str, sizeof(backfile));
860182452Spjd	} else {
861182452Spjd		/* Generate file name automatically. */
862182452Spjd		const char *p = prov;
863182452Spjd		unsigned int i;
864182452Spjd
865213662Sae		if (strncmp(p, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
866213662Sae			p += sizeof(_PATH_DEV) - 1;
867182452Spjd		snprintf(backfile, sizeof(backfile), "%s%s.eli",
868182452Spjd		    GELI_BACKUP_DIR, p);
869182452Spjd		/* Replace all / with _. */
870182452Spjd		for (i = strlen(GELI_BACKUP_DIR); backfile[i] != '\0'; i++) {
871182452Spjd			if (backfile[i] == '/')
872182452Spjd				backfile[i] = '_';
873182452Spjd		}
874182452Spjd	}
875182452Spjd	if (strcmp(backfile, "none") != 0 &&
876182452Spjd	    eli_backup_create(req, prov, backfile) == 0) {
877182452Spjd		printf("\nMetadata backup can be found in %s and\n", backfile);
878182452Spjd		printf("can be restored with the following command:\n");
879182452Spjd		printf("\n\t# geli restore %s %s\n\n", backfile, prov);
880182452Spjd	}
881148456Spjd}
882148456Spjd
883148456Spjdstatic void
884148456Spjdeli_attach(struct gctl_req *req)
885148456Spjd{
886148456Spjd	struct g_eli_metadata md;
887148456Spjd	unsigned char key[G_ELI_USERKEYLEN];
888148456Spjd	const char *prov;
889212934Sbrian	off_t mediasize;
890153190Spjd	int nargs;
891148456Spjd
892153190Spjd	nargs = gctl_get_int(req, "nargs");
893153190Spjd	if (nargs != 1) {
894158214Spjd		gctl_error(req, "Invalid number of arguments.");
895148456Spjd		return;
896148456Spjd	}
897153190Spjd	prov = gctl_get_ascii(req, "arg0");
898148456Spjd
899148456Spjd	if (eli_metadata_read(req, prov, &md) == -1)
900148456Spjd		return;
901148456Spjd
902212934Sbrian	mediasize = g_get_mediasize(prov);
903212934Sbrian	if (md.md_provsize != (uint64_t)mediasize) {
904212934Sbrian		gctl_error(req, "Provider size mismatch.");
905212934Sbrian		return;
906212934Sbrian	}
907212934Sbrian
908213172Spjd	if (eli_genkey(req, &md, key, false) == NULL) {
909148456Spjd		bzero(key, sizeof(key));
910148456Spjd		return;
911148456Spjd	}
912148456Spjd
913148456Spjd	gctl_ro_param(req, "key", sizeof(key), key);
914148456Spjd	if (gctl_issue(req) == NULL) {
915148456Spjd		if (verbose)
916166892Spjd			printf("Attached to %s.\n", prov);
917148456Spjd	}
918148456Spjd	bzero(key, sizeof(key));
919148456Spjd}
920148456Spjd
921148456Spjdstatic void
922286444Spjdeli_configure_detached(struct gctl_req *req, const char *prov, int boot,
923329114Skevans    int geliboot, int displaypass, int trim)
924162353Spjd{
925162353Spjd	struct g_eli_metadata md;
926286444Spjd	bool changed = 0;
927162353Spjd
928162353Spjd	if (eli_metadata_read(req, prov, &md) == -1)
929162353Spjd		return;
930162353Spjd
931286444Spjd	if (boot == 1 && (md.md_flags & G_ELI_FLAG_BOOT)) {
932162353Spjd		if (verbose)
933162353Spjd			printf("BOOT flag already configured for %s.\n", prov);
934286444Spjd	} else if (boot == 0 && !(md.md_flags & G_ELI_FLAG_BOOT)) {
935162353Spjd		if (verbose)
936162353Spjd			printf("BOOT flag not configured for %s.\n", prov);
937286444Spjd	} else if (boot >= 0) {
938162353Spjd		if (boot)
939162353Spjd			md.md_flags |= G_ELI_FLAG_BOOT;
940162353Spjd		else
941162353Spjd			md.md_flags &= ~G_ELI_FLAG_BOOT;
942286444Spjd		changed = 1;
943286444Spjd	}
944286444Spjd
945297691Sallanjude	if (geliboot == 1 && (md.md_flags & G_ELI_FLAG_GELIBOOT)) {
946297691Sallanjude		if (verbose)
947297691Sallanjude			printf("GELIBOOT flag already configured for %s.\n", prov);
948297691Sallanjude	} else if (geliboot == 0 && !(md.md_flags & G_ELI_FLAG_GELIBOOT)) {
949297691Sallanjude		if (verbose)
950297691Sallanjude			printf("GELIBOOT flag not configured for %s.\n", prov);
951297691Sallanjude	} else if (geliboot >= 0) {
952297691Sallanjude		if (geliboot)
953297691Sallanjude			md.md_flags |= G_ELI_FLAG_GELIBOOT;
954297691Sallanjude		else
955297691Sallanjude			md.md_flags &= ~G_ELI_FLAG_GELIBOOT;
956297691Sallanjude		changed = 1;
957297691Sallanjude	}
958297691Sallanjude
959329114Skevans	if (displaypass == 1 && (md.md_flags & G_ELI_FLAG_GELIDISPLAYPASS)) {
960329114Skevans		if (verbose)
961329114Skevans			printf("GELIDISPLAYPASS flag already configured for %s.\n", prov);
962329114Skevans	} else if (displaypass == 0 &&
963329114Skevans	    !(md.md_flags & G_ELI_FLAG_GELIDISPLAYPASS)) {
964329114Skevans		if (verbose)
965329114Skevans			printf("GELIDISPLAYPASS flag not configured for %s.\n", prov);
966329114Skevans	} else if (displaypass >= 0) {
967329114Skevans		if (displaypass)
968329114Skevans			md.md_flags |= G_ELI_FLAG_GELIDISPLAYPASS;
969329114Skevans		else
970329114Skevans			md.md_flags &= ~G_ELI_FLAG_GELIDISPLAYPASS;
971329114Skevans		changed = 1;
972329114Skevans	}
973329114Skevans
974286444Spjd	if (trim == 0 && (md.md_flags & G_ELI_FLAG_NODELETE)) {
975286444Spjd		if (verbose)
976286444Spjd			printf("TRIM disable flag already configured for %s.\n", prov);
977286444Spjd	} else if (trim == 1 && !(md.md_flags & G_ELI_FLAG_NODELETE)) {
978286444Spjd		if (verbose)
979286444Spjd			printf("TRIM disable flag not configured for %s.\n", prov);
980286444Spjd	} else if (trim >= 0) {
981286444Spjd		if (trim)
982286444Spjd			md.md_flags &= ~G_ELI_FLAG_NODELETE;
983286444Spjd		else
984286444Spjd			md.md_flags |= G_ELI_FLAG_NODELETE;
985286444Spjd		changed = 1;
986286444Spjd	}
987286444Spjd
988286444Spjd	if (changed)
989162353Spjd		eli_metadata_store(req, prov, &md);
990162353Spjd	bzero(&md, sizeof(md));
991162353Spjd}
992162353Spjd
993162353Spjdstatic void
994162353Spjdeli_configure(struct gctl_req *req)
995162353Spjd{
996162353Spjd	const char *prov;
997329114Skevans	bool boot, noboot, geliboot, nogeliboot, displaypass, nodisplaypass;
998329114Skevans	bool trim, notrim;
999329114Skevans	int doboot, dogeliboot, dodisplaypass, dotrim;
1000213172Spjd	int i, nargs;
1001162353Spjd
1002162353Spjd	nargs = gctl_get_int(req, "nargs");
1003162353Spjd	if (nargs == 0) {
1004162353Spjd		gctl_error(req, "Too few arguments.");
1005162353Spjd		return;
1006162353Spjd	}
1007162353Spjd
1008162353Spjd	boot = gctl_get_int(req, "boot");
1009162353Spjd	noboot = gctl_get_int(req, "noboot");
1010297691Sallanjude	geliboot = gctl_get_int(req, "geliboot");
1011297691Sallanjude	nogeliboot = gctl_get_int(req, "nogeliboot");
1012329114Skevans	displaypass = gctl_get_int(req, "displaypass");
1013329114Skevans	nodisplaypass = gctl_get_int(req, "nodisplaypass");
1014286444Spjd	trim = gctl_get_int(req, "trim");
1015286444Spjd	notrim = gctl_get_int(req, "notrim");
1016162353Spjd
1017286444Spjd	doboot = -1;
1018162353Spjd	if (boot && noboot) {
1019162353Spjd		gctl_error(req, "Options -b and -B are mutually exclusive.");
1020162353Spjd		return;
1021162353Spjd	}
1022286444Spjd	if (boot)
1023286444Spjd		doboot = 1;
1024286444Spjd	else if (noboot)
1025286444Spjd		doboot = 0;
1026286444Spjd
1027297691Sallanjude	dogeliboot = -1;
1028297691Sallanjude	if (geliboot && nogeliboot) {
1029297691Sallanjude		gctl_error(req, "Options -g and -G are mutually exclusive.");
1030297691Sallanjude		return;
1031297691Sallanjude	}
1032297691Sallanjude	if (geliboot)
1033297691Sallanjude		dogeliboot = 1;
1034297691Sallanjude	else if (nogeliboot)
1035297691Sallanjude		dogeliboot = 0;
1036297691Sallanjude
1037329114Skevans	dodisplaypass = -1;
1038329114Skevans	if (displaypass && nodisplaypass) {
1039329114Skevans		gctl_error(req, "Options -d and -D are mutually exclusive.");
1040329114Skevans		return;
1041329114Skevans	}
1042329114Skevans	if (displaypass)
1043329114Skevans		dodisplaypass = 1;
1044329114Skevans	else if (nodisplaypass)
1045329114Skevans		dodisplaypass = 0;
1046329114Skevans
1047286444Spjd	dotrim = -1;
1048286444Spjd	if (trim && notrim) {
1049286444Spjd		gctl_error(req, "Options -t and -T are mutually exclusive.");
1050286444Spjd		return;
1051286444Spjd	}
1052286444Spjd	if (trim)
1053286444Spjd		dotrim = 1;
1054286444Spjd	else if (notrim)
1055286444Spjd		dotrim = 0;
1056286444Spjd
1057329114Skevans	if (doboot == -1 && dogeliboot == -1 && dodisplaypass == -1 &&
1058329114Skevans	    dotrim == -1) {
1059162353Spjd		gctl_error(req, "No option given.");
1060162353Spjd		return;
1061162353Spjd	}
1062162353Spjd
1063162353Spjd	/* First attached providers. */
1064162353Spjd	gctl_issue(req);
1065162353Spjd	/* Now the rest. */
1066162353Spjd	for (i = 0; i < nargs; i++) {
1067162353Spjd		prov = gctl_get_ascii(req, "arg%d", i);
1068329114Skevans		if (!eli_is_attached(prov)) {
1069329114Skevans			eli_configure_detached(req, prov, doboot, dogeliboot,
1070329114Skevans			    dodisplaypass, dotrim);
1071329114Skevans		}
1072162353Spjd	}
1073162353Spjd}
1074162353Spjd
1075162353Spjdstatic void
1076155101Spjdeli_setkey_attached(struct gctl_req *req, struct g_eli_metadata *md)
1077148456Spjd{
1078148456Spjd	unsigned char key[G_ELI_USERKEYLEN];
1079166216Spjd	intmax_t val, old = 0;
1080166216Spjd	int error;
1081148456Spjd
1082153190Spjd	val = gctl_get_intmax(req, "iterations");
1083149304Spjd	/* Check if iterations number should be changed. */
1084153190Spjd	if (val != -1)
1085153190Spjd		md->md_iterations = val;
1086166216Spjd	else
1087166216Spjd		old = md->md_iterations;
1088148456Spjd
1089148456Spjd	/* Generate key for Master Key encryption. */
1090213172Spjd	if (eli_genkey(req, md, key, true) == NULL) {
1091148456Spjd		bzero(key, sizeof(key));
1092148456Spjd		return;
1093148456Spjd	}
1094166216Spjd	/*
1095166216Spjd	 * If number of iterations has changed, but wasn't given as a
1096166216Spjd	 * command-line argument, update the request.
1097166216Spjd	 */
1098166216Spjd	if (val == -1 && md->md_iterations != old) {
1099166216Spjd		error = gctl_change_param(req, "iterations", sizeof(intmax_t),
1100166216Spjd		    &md->md_iterations);
1101166216Spjd		assert(error == 0);
1102166216Spjd	}
1103148456Spjd
1104148456Spjd	gctl_ro_param(req, "key", sizeof(key), key);
1105148456Spjd	gctl_issue(req);
1106148456Spjd	bzero(key, sizeof(key));
1107148456Spjd}
1108148456Spjd
1109148456Spjdstatic void
1110149304Spjdeli_setkey_detached(struct gctl_req *req, const char *prov,
1111149304Spjd struct g_eli_metadata *md)
1112148456Spjd{
1113148456Spjd	unsigned char key[G_ELI_USERKEYLEN], mkey[G_ELI_DATAIVKEYLEN];
1114148456Spjd	unsigned char *mkeydst;
1115213172Spjd	unsigned int nkey;
1116153190Spjd	intmax_t val;
1117148456Spjd	int error;
1118148456Spjd
1119149928Spjd	if (md->md_keys == 0) {
1120149928Spjd		gctl_error(req, "No valid keys on %s.", prov);
1121149928Spjd		return;
1122149928Spjd	}
1123149928Spjd
1124148456Spjd	/* Generate key for Master Key decryption. */
1125213172Spjd	if (eli_genkey(req, md, key, false) == NULL) {
1126148456Spjd		bzero(key, sizeof(key));
1127148456Spjd		return;
1128148456Spjd	}
1129148456Spjd
1130148456Spjd	/* Decrypt Master Key. */
1131149304Spjd	error = g_eli_mkey_decrypt(md, key, mkey, &nkey);
1132148456Spjd	bzero(key, sizeof(key));
1133148456Spjd	if (error != 0) {
1134149304Spjd		bzero(md, sizeof(*md));
1135148456Spjd		if (error == -1)
1136148456Spjd			gctl_error(req, "Wrong key for %s.", prov);
1137148456Spjd		else /* if (error > 0) */ {
1138148456Spjd			gctl_error(req, "Cannot decrypt Master Key: %s.",
1139148456Spjd			    strerror(error));
1140148456Spjd		}
1141148456Spjd		return;
1142148456Spjd	}
1143148456Spjd	if (verbose)
1144148456Spjd		printf("Decrypted Master Key %u.\n", nkey);
1145148456Spjd
1146153190Spjd	val = gctl_get_intmax(req, "keyno");
1147153190Spjd	if (val != -1)
1148153190Spjd		nkey = val;
1149148456Spjd#if 0
1150148456Spjd	else
1151148456Spjd		; /* Use the key number which was found during decryption. */
1152148456Spjd#endif
1153148456Spjd	if (nkey >= G_ELI_MAXMKEYS) {
1154148456Spjd		gctl_error(req, "Invalid '%s' argument.", "keyno");
1155148456Spjd		return;
1156148456Spjd	}
1157148456Spjd
1158153190Spjd	val = gctl_get_intmax(req, "iterations");
1159149304Spjd	/* Check if iterations number should and can be changed. */
1160317858Smav	if (val != -1 && md->md_iterations == -1) {
1161317858Smav		md->md_iterations = val;
1162317858Smav	} else if (val != -1 && val != md->md_iterations) {
1163149304Spjd		if (bitcount32(md->md_keys) != 1) {
1164149304Spjd			gctl_error(req, "To be able to use '-i' option, only "
1165149304Spjd			    "one key can be defined.");
1166149304Spjd			return;
1167149304Spjd		}
1168149304Spjd		if (md->md_keys != (1 << nkey)) {
1169149304Spjd			gctl_error(req, "Only already defined key can be "
1170149304Spjd			    "changed when '-i' option is used.");
1171149304Spjd			return;
1172149304Spjd		}
1173153190Spjd		md->md_iterations = val;
1174149304Spjd	}
1175148456Spjd
1176149304Spjd	mkeydst = md->md_mkeys + nkey * G_ELI_MKEYLEN;
1177149304Spjd	md->md_keys |= (1 << nkey);
1178149304Spjd
1179148456Spjd	bcopy(mkey, mkeydst, sizeof(mkey));
1180148456Spjd	bzero(mkey, sizeof(mkey));
1181148456Spjd
1182148456Spjd	/* Generate key for Master Key encryption. */
1183213172Spjd	if (eli_genkey(req, md, key, true) == NULL) {
1184148456Spjd		bzero(key, sizeof(key));
1185149304Spjd		bzero(md, sizeof(*md));
1186148456Spjd		return;
1187148456Spjd	}
1188148456Spjd
1189148456Spjd	/* Encrypt the Master-Key with the new key. */
1190159308Spjd	error = g_eli_mkey_encrypt(md->md_ealgo, key, md->md_keylen, mkeydst);
1191148456Spjd	bzero(key, sizeof(key));
1192148456Spjd	if (error != 0) {
1193149304Spjd		bzero(md, sizeof(*md));
1194148456Spjd		gctl_error(req, "Cannot encrypt Master Key: %s.",
1195148456Spjd		    strerror(error));
1196148456Spjd		return;
1197148456Spjd	}
1198148456Spjd
1199148456Spjd	/* Store metadata with fresh key. */
1200149304Spjd	eli_metadata_store(req, prov, md);
1201149304Spjd	bzero(md, sizeof(*md));
1202148456Spjd}
1203148456Spjd
1204148456Spjdstatic void
1205148456Spjdeli_setkey(struct gctl_req *req)
1206148456Spjd{
1207149304Spjd	struct g_eli_metadata md;
1208148456Spjd	const char *prov;
1209153190Spjd	int nargs;
1210148456Spjd
1211153190Spjd	nargs = gctl_get_int(req, "nargs");
1212153190Spjd	if (nargs != 1) {
1213158214Spjd		gctl_error(req, "Invalid number of arguments.");
1214148456Spjd		return;
1215148456Spjd	}
1216153190Spjd	prov = gctl_get_ascii(req, "arg0");
1217148456Spjd
1218149304Spjd	if (eli_metadata_read(req, prov, &md) == -1)
1219149304Spjd		return;
1220149304Spjd
1221148456Spjd	if (eli_is_attached(prov))
1222155101Spjd		eli_setkey_attached(req, &md);
1223148456Spjd	else
1224149304Spjd		eli_setkey_detached(req, prov, &md);
1225182452Spjd
1226182452Spjd	if (req->error == NULL || req->error[0] == '\0') {
1227182452Spjd		printf("Note, that the master key encrypted with old keys "
1228182452Spjd		    "and/or passphrase may still exists in a metadata backup "
1229182452Spjd		    "file.\n");
1230182452Spjd	}
1231148456Spjd}
1232148456Spjd
1233148456Spjdstatic void
1234148456Spjdeli_delkey_attached(struct gctl_req *req, const char *prov __unused)
1235148456Spjd{
1236148456Spjd
1237148456Spjd	gctl_issue(req);
1238148456Spjd}
1239148456Spjd
1240148456Spjdstatic void
1241148456Spjdeli_delkey_detached(struct gctl_req *req, const char *prov)
1242148456Spjd{
1243148456Spjd	struct g_eli_metadata md;
1244148456Spjd	unsigned char *mkeydst;
1245213172Spjd	unsigned int nkey;
1246153190Spjd	intmax_t val;
1247213172Spjd	bool all, force;
1248148456Spjd
1249148456Spjd	if (eli_metadata_read(req, prov, &md) == -1)
1250148456Spjd		return;
1251148456Spjd
1252153190Spjd	all = gctl_get_int(req, "all");
1253153190Spjd	if (all)
1254246620Spjd		arc4random_buf(md.md_mkeys, sizeof(md.md_mkeys));
1255148456Spjd	else {
1256153190Spjd		force = gctl_get_int(req, "force");
1257153190Spjd		val = gctl_get_intmax(req, "keyno");
1258153190Spjd		if (val == -1) {
1259148456Spjd			gctl_error(req, "Key number has to be specified.");
1260148456Spjd			return;
1261148456Spjd		}
1262153190Spjd		nkey = val;
1263148456Spjd		if (nkey >= G_ELI_MAXMKEYS) {
1264148456Spjd			gctl_error(req, "Invalid '%s' argument.", "keyno");
1265148456Spjd			return;
1266148456Spjd		}
1267153190Spjd		if (!(md.md_keys & (1 << nkey)) && !force) {
1268148456Spjd			gctl_error(req, "Master Key %u is not set.", nkey);
1269148456Spjd			return;
1270148456Spjd		}
1271148456Spjd		md.md_keys &= ~(1 << nkey);
1272153190Spjd		if (md.md_keys == 0 && !force) {
1273148456Spjd			gctl_error(req, "This is the last Master Key. Use '-f' "
1274148456Spjd			    "option if you really want to remove it.");
1275148456Spjd			return;
1276148456Spjd		}
1277148456Spjd		mkeydst = md.md_mkeys + nkey * G_ELI_MKEYLEN;
1278246620Spjd		arc4random_buf(mkeydst, G_ELI_MKEYLEN);
1279148456Spjd	}
1280148456Spjd
1281148456Spjd	eli_metadata_store(req, prov, &md);
1282148456Spjd	bzero(&md, sizeof(md));
1283148456Spjd}
1284148456Spjd
1285148456Spjdstatic void
1286148456Spjdeli_delkey(struct gctl_req *req)
1287148456Spjd{
1288148456Spjd	const char *prov;
1289153190Spjd	int nargs;
1290148456Spjd
1291153190Spjd	nargs = gctl_get_int(req, "nargs");
1292153190Spjd	if (nargs != 1) {
1293158214Spjd		gctl_error(req, "Invalid number of arguments.");
1294148456Spjd		return;
1295148456Spjd	}
1296153190Spjd	prov = gctl_get_ascii(req, "arg0");
1297148456Spjd
1298148456Spjd	if (eli_is_attached(prov))
1299148456Spjd		eli_delkey_attached(req, prov);
1300148456Spjd	else
1301148456Spjd		eli_delkey_detached(req, prov);
1302148456Spjd}
1303148456Spjd
1304214118Spjdstatic void
1305214118Spjdeli_resume(struct gctl_req *req)
1306214118Spjd{
1307214118Spjd	struct g_eli_metadata md;
1308214118Spjd	unsigned char key[G_ELI_USERKEYLEN];
1309214118Spjd	const char *prov;
1310214118Spjd	off_t mediasize;
1311214118Spjd	int nargs;
1312214118Spjd
1313214118Spjd	nargs = gctl_get_int(req, "nargs");
1314214118Spjd	if (nargs != 1) {
1315214118Spjd		gctl_error(req, "Invalid number of arguments.");
1316214118Spjd		return;
1317214118Spjd	}
1318214118Spjd	prov = gctl_get_ascii(req, "arg0");
1319214118Spjd
1320214118Spjd	if (eli_metadata_read(req, prov, &md) == -1)
1321214118Spjd		return;
1322214118Spjd
1323214118Spjd	mediasize = g_get_mediasize(prov);
1324214118Spjd	if (md.md_provsize != (uint64_t)mediasize) {
1325214118Spjd		gctl_error(req, "Provider size mismatch.");
1326214118Spjd		return;
1327214118Spjd	}
1328214118Spjd
1329214118Spjd	if (eli_genkey(req, &md, key, false) == NULL) {
1330214118Spjd		bzero(key, sizeof(key));
1331214118Spjd		return;
1332214118Spjd	}
1333214118Spjd
1334214118Spjd	gctl_ro_param(req, "key", sizeof(key), key);
1335214118Spjd	if (gctl_issue(req) == NULL) {
1336214118Spjd		if (verbose)
1337214118Spjd			printf("Resumed %s.\n", prov);
1338214118Spjd	}
1339214118Spjd	bzero(key, sizeof(key));
1340214118Spjd}
1341214118Spjd
1342213060Spjdstatic int
1343213060Spjdeli_trash_metadata(struct gctl_req *req, const char *prov, int fd, off_t offset)
1344213060Spjd{
1345213060Spjd	unsigned int overwrites;
1346213060Spjd	unsigned char *sector;
1347213060Spjd	ssize_t size;
1348213060Spjd	int error;
1349213060Spjd
1350213060Spjd	size = sizeof(overwrites);
1351213060Spjd	if (sysctlbyname("kern.geom.eli.overwrites", &overwrites, &size,
1352213060Spjd	    NULL, 0) == -1 || overwrites == 0) {
1353213060Spjd		overwrites = G_ELI_OVERWRITES;
1354213060Spjd	}
1355213060Spjd
1356213060Spjd	size = g_sectorsize(fd);
1357213060Spjd	if (size <= 0) {
1358213060Spjd		gctl_error(req, "Cannot obtain provider sector size %s: %s.",
1359213060Spjd		    prov, strerror(errno));
1360213060Spjd		return (-1);
1361213060Spjd	}
1362213060Spjd	sector = malloc(size);
1363213060Spjd	if (sector == NULL) {
1364213060Spjd		gctl_error(req, "Cannot allocate %zd bytes of memory.", size);
1365213060Spjd		return (-1);
1366213060Spjd	}
1367213060Spjd
1368213060Spjd	error = 0;
1369213060Spjd	do {
1370246620Spjd		arc4random_buf(sector, size);
1371213060Spjd		if (pwrite(fd, sector, size, offset) != size) {
1372213060Spjd			if (error == 0)
1373213060Spjd				error = errno;
1374213060Spjd		}
1375213060Spjd		(void)g_flush(fd);
1376213060Spjd	} while (--overwrites > 0);
1377246622Spjd	free(sector);
1378213060Spjd	if (error != 0) {
1379213060Spjd		gctl_error(req, "Cannot trash metadata on provider %s: %s.",
1380213060Spjd		    prov, strerror(error));
1381213060Spjd		return (-1);
1382213060Spjd	}
1383213060Spjd	return (0);
1384213060Spjd}
1385213060Spjd
1386148456Spjdstatic void
1387148456Spjdeli_kill_detached(struct gctl_req *req, const char *prov)
1388148456Spjd{
1389213060Spjd	off_t offset;
1390213060Spjd	int fd;
1391148456Spjd
1392148456Spjd	/*
1393148456Spjd	 * NOTE: Maybe we should verify if this is geli provider first,
1394148456Spjd	 *       but 'kill' command is quite critical so better don't waste
1395148456Spjd	 *       the time.
1396148456Spjd	 */
1397148456Spjd#if 0
1398148456Spjd	error = g_metadata_read(prov, (unsigned char *)&md, sizeof(md),
1399148456Spjd	    G_ELI_MAGIC);
1400148456Spjd	if (error != 0) {
1401148456Spjd		gctl_error(req, "Cannot read metadata from %s: %s.", prov,
1402148456Spjd		    strerror(error));
1403148456Spjd		return;
1404148456Spjd	}
1405148456Spjd#endif
1406148456Spjd
1407213060Spjd	fd = g_open(prov, 1);
1408213060Spjd	if (fd == -1) {
1409213060Spjd		gctl_error(req, "Cannot open provider %s: %s.", prov,
1410213060Spjd		    strerror(errno));
1411213060Spjd		return;
1412148456Spjd	}
1413213060Spjd	offset = g_mediasize(fd) - g_sectorsize(fd);
1414213060Spjd	if (offset <= 0) {
1415213060Spjd		gctl_error(req,
1416213060Spjd		    "Cannot obtain media size or sector size for provider %s: %s.",
1417213060Spjd		    prov, strerror(errno));
1418213060Spjd		(void)g_close(fd);
1419213060Spjd		return;
1420213060Spjd	}
1421213060Spjd	(void)eli_trash_metadata(req, prov, fd, offset);
1422213060Spjd	(void)g_close(fd);
1423148456Spjd}
1424148456Spjd
1425148456Spjdstatic void
1426148456Spjdeli_kill(struct gctl_req *req)
1427148456Spjd{
1428148456Spjd	const char *prov;
1429153190Spjd	int i, nargs, all;
1430148456Spjd
1431153190Spjd	nargs = gctl_get_int(req, "nargs");
1432153190Spjd	all = gctl_get_int(req, "all");
1433153190Spjd	if (!all && nargs == 0) {
1434148456Spjd		gctl_error(req, "Too few arguments.");
1435148456Spjd		return;
1436148456Spjd	}
1437148456Spjd	/*
1438148456Spjd	 * How '-a' option combine with a list of providers:
1439148456Spjd	 * Delete Master Keys from all attached providers:
1440148456Spjd	 * geli kill -a
1441169312Spjd	 * Delete Master Keys from all attached providers and from
1442148456Spjd	 * detached da0 and da1:
1443148456Spjd	 * geli kill -a da0 da1
1444148456Spjd	 * Delete Master Keys from (attached or detached) da0 and da1:
1445148456Spjd	 * geli kill da0 da1
1446148456Spjd	 */
1447148456Spjd
1448169312Spjd	/* First detached providers. */
1449153190Spjd	for (i = 0; i < nargs; i++) {
1450153190Spjd		prov = gctl_get_ascii(req, "arg%d", i);
1451148456Spjd		if (!eli_is_attached(prov))
1452148456Spjd			eli_kill_detached(req, prov);
1453148456Spjd	}
1454162347Spjd	/* Now attached providers. */
1455162347Spjd	gctl_issue(req);
1456148456Spjd}
1457148456Spjd
1458182452Spjdstatic int
1459182452Spjdeli_backup_create(struct gctl_req *req, const char *prov, const char *file)
1460148456Spjd{
1461148456Spjd	unsigned char *sector;
1462213059Spjd	ssize_t secsize;
1463226716Spjd	int error, filefd, ret;
1464148456Spjd
1465182452Spjd	ret = -1;
1466226716Spjd	filefd = -1;
1467148456Spjd	sector = NULL;
1468148456Spjd	secsize = 0;
1469148456Spjd
1470226716Spjd	secsize = g_get_sectorsize(prov);
1471226716Spjd	if (secsize == 0) {
1472148456Spjd		gctl_error(req, "Cannot get informations about %s: %s.", prov,
1473148456Spjd		    strerror(errno));
1474169193Spjd		goto out;
1475148456Spjd	}
1476148456Spjd	sector = malloc(secsize);
1477148456Spjd	if (sector == NULL) {
1478148456Spjd		gctl_error(req, "Cannot allocate memory.");
1479169193Spjd		goto out;
1480148456Spjd	}
1481148456Spjd	/* Read metadata from the provider. */
1482226716Spjd	error = g_metadata_read(prov, sector, secsize, G_ELI_MAGIC);
1483226716Spjd	if (error != 0) {
1484226716Spjd		gctl_error(req, "Unable to read metadata from %s: %s.", prov,
1485226716Spjd		    strerror(error));
1486148456Spjd		goto out;
1487148456Spjd	}
1488226716Spjd
1489226716Spjd	filefd = open(file, O_WRONLY | O_TRUNC | O_CREAT, 0600);
1490226716Spjd	if (filefd == -1) {
1491226716Spjd		gctl_error(req, "Unable to open %s: %s.", file,
1492226716Spjd		    strerror(errno));
1493148456Spjd		goto out;
1494148456Spjd	}
1495148456Spjd	/* Write metadata to the destination file. */
1496213059Spjd	if (write(filefd, sector, secsize) != secsize) {
1497226716Spjd		gctl_error(req, "Unable to write to %s: %s.", file,
1498148456Spjd		    strerror(errno));
1499226716Spjd		(void)close(filefd);
1500226716Spjd		(void)unlink(file);
1501148456Spjd		goto out;
1502148456Spjd	}
1503213059Spjd	(void)fsync(filefd);
1504226716Spjd	(void)close(filefd);
1505182452Spjd	/* Success. */
1506182452Spjd	ret = 0;
1507148456Spjdout:
1508148456Spjd	if (sector != NULL) {
1509148456Spjd		bzero(sector, secsize);
1510148456Spjd		free(sector);
1511148456Spjd	}
1512182452Spjd	return (ret);
1513148456Spjd}
1514148456Spjd
1515148456Spjdstatic void
1516182452Spjdeli_backup(struct gctl_req *req)
1517182452Spjd{
1518182452Spjd	const char *file, *prov;
1519182452Spjd	int nargs;
1520182452Spjd
1521182452Spjd	nargs = gctl_get_int(req, "nargs");
1522182452Spjd	if (nargs != 2) {
1523182452Spjd		gctl_error(req, "Invalid number of arguments.");
1524182452Spjd		return;
1525182452Spjd	}
1526182452Spjd	prov = gctl_get_ascii(req, "arg0");
1527182452Spjd	file = gctl_get_ascii(req, "arg1");
1528182452Spjd
1529182452Spjd	eli_backup_create(req, prov, file);
1530182452Spjd}
1531182452Spjd
1532182452Spjdstatic void
1533148456Spjdeli_restore(struct gctl_req *req)
1534148456Spjd{
1535148456Spjd	struct g_eli_metadata md;
1536148456Spjd	const char *file, *prov;
1537148456Spjd	off_t mediasize;
1538226716Spjd	int nargs;
1539148456Spjd
1540153190Spjd	nargs = gctl_get_int(req, "nargs");
1541153190Spjd	if (nargs != 2) {
1542148456Spjd		gctl_error(req, "Invalid number of arguments.");
1543148456Spjd		return;
1544148456Spjd	}
1545153190Spjd	file = gctl_get_ascii(req, "arg0");
1546153190Spjd	prov = gctl_get_ascii(req, "arg1");
1547148456Spjd
1548226716Spjd	/* Read metadata from the backup file. */
1549226716Spjd	if (eli_metadata_read(req, file, &md) == -1)
1550226716Spjd		return;
1551226716Spjd	/* Obtain provider's mediasize. */
1552226716Spjd	mediasize = g_get_mediasize(prov);
1553226716Spjd	if (mediasize == 0) {
1554148456Spjd		gctl_error(req, "Cannot get informations about %s: %s.", prov,
1555148456Spjd		    strerror(errno));
1556226716Spjd		return;
1557148456Spjd	}
1558212934Sbrian	/* Check if the provider size has changed since we did the backup. */
1559212934Sbrian	if (md.md_provsize != (uint64_t)mediasize) {
1560212934Sbrian		if (gctl_get_int(req, "force")) {
1561212934Sbrian			md.md_provsize = mediasize;
1562212934Sbrian		} else {
1563212934Sbrian			gctl_error(req, "Provider size mismatch: "
1564212934Sbrian			    "wrong backup file?");
1565226716Spjd			return;
1566212934Sbrian		}
1567212934Sbrian	}
1568226716Spjd	/* Write metadata to the provider. */
1569226716Spjd	(void)eli_metadata_store(req, prov, &md);
1570148456Spjd}
1571148456Spjd
1572148456Spjdstatic void
1573212934Sbrianeli_resize(struct gctl_req *req)
1574212934Sbrian{
1575212934Sbrian	struct g_eli_metadata md;
1576212934Sbrian	const char *prov;
1577212934Sbrian	unsigned char *sector;
1578213056Spjd	ssize_t secsize;
1579212934Sbrian	off_t mediasize, oldsize;
1580226722Spjd	int error, nargs, provfd;
1581212934Sbrian
1582212934Sbrian	nargs = gctl_get_int(req, "nargs");
1583212934Sbrian	if (nargs != 1) {
1584212934Sbrian		gctl_error(req, "Invalid number of arguments.");
1585212934Sbrian		return;
1586212934Sbrian	}
1587212934Sbrian	prov = gctl_get_ascii(req, "arg0");
1588212934Sbrian
1589212934Sbrian	provfd = -1;
1590212934Sbrian	sector = NULL;
1591212934Sbrian	secsize = 0;
1592212934Sbrian
1593213056Spjd	provfd = g_open(prov, 1);
1594212934Sbrian	if (provfd == -1) {
1595212934Sbrian		gctl_error(req, "Cannot open %s: %s.", prov, strerror(errno));
1596212934Sbrian		goto out;
1597212934Sbrian	}
1598212934Sbrian
1599213056Spjd	mediasize = g_mediasize(provfd);
1600213056Spjd	secsize = g_sectorsize(provfd);
1601213056Spjd	if (mediasize == -1 || secsize == -1) {
1602212934Sbrian		gctl_error(req, "Cannot get information about %s: %s.", prov,
1603212934Sbrian		    strerror(errno));
1604212934Sbrian		goto out;
1605212934Sbrian	}
1606212934Sbrian
1607212934Sbrian	sector = malloc(secsize);
1608212934Sbrian	if (sector == NULL) {
1609212934Sbrian		gctl_error(req, "Cannot allocate memory.");
1610212934Sbrian		goto out;
1611212934Sbrian	}
1612212934Sbrian
1613212934Sbrian	oldsize = gctl_get_intmax(req, "oldsize");
1614212934Sbrian	if (oldsize < 0 || oldsize > mediasize) {
1615212934Sbrian		gctl_error(req, "Invalid oldsize: Out of range.");
1616212934Sbrian		goto out;
1617212934Sbrian	}
1618213058Spjd	if (oldsize == mediasize) {
1619213058Spjd		gctl_error(req, "Size hasn't changed.");
1620213058Spjd		goto out;
1621213058Spjd	}
1622212934Sbrian
1623212934Sbrian	/* Read metadata from the 'oldsize' offset. */
1624213056Spjd	if (pread(provfd, sector, secsize, oldsize - secsize) != secsize) {
1625212934Sbrian		gctl_error(req, "Cannot read old metadata: %s.",
1626212934Sbrian		    strerror(errno));
1627212934Sbrian		goto out;
1628212934Sbrian	}
1629212934Sbrian
1630212934Sbrian	/* Check if this sector contains geli metadata. */
1631226722Spjd	error = eli_metadata_decode(sector, &md);
1632226722Spjd	switch (error) {
1633226722Spjd	case 0:
1634226722Spjd		break;
1635226722Spjd	case EOPNOTSUPP:
1636226722Spjd		gctl_error(req,
1637226722Spjd		    "Provider's %s metadata version %u is too new.\n"
1638226722Spjd		    "geli: The highest supported version is %u.",
1639226722Spjd		    prov, (unsigned int)md.md_version, G_ELI_VERSION);
1640212934Sbrian		goto out;
1641226722Spjd	case EINVAL:
1642226722Spjd		gctl_error(req, "Inconsistent provider's %s metadata.", prov);
1643226722Spjd		goto out;
1644226722Spjd	default:
1645226722Spjd		gctl_error(req,
1646226722Spjd		    "Unexpected error while decoding provider's %s metadata: %s.",
1647226722Spjd		    prov, strerror(error));
1648226722Spjd		goto out;
1649212934Sbrian	}
1650212934Sbrian
1651212934Sbrian	/*
1652212934Sbrian	 * If the old metadata doesn't have a correct provider size, refuse
1653212934Sbrian	 * to resize.
1654212934Sbrian	 */
1655212934Sbrian	if (md.md_provsize != (uint64_t)oldsize) {
1656212934Sbrian		gctl_error(req, "Provider size mismatch at oldsize.");
1657212934Sbrian		goto out;
1658212934Sbrian	}
1659212934Sbrian
1660212934Sbrian	/*
1661212934Sbrian	 * Update the old metadata with the current provider size and write
1662212934Sbrian	 * it back to the correct place on the provider.
1663212934Sbrian	 */
1664212934Sbrian	md.md_provsize = mediasize;
1665226720Spjd	/* Write metadata to the provider. */
1666226720Spjd	(void)eli_metadata_store(req, prov, &md);
1667212934Sbrian	/* Now trash the old metadata. */
1668226720Spjd	(void)eli_trash_metadata(req, prov, provfd, oldsize - secsize);
1669212934Sbrianout:
1670226720Spjd	if (provfd != -1)
1671213056Spjd		(void)g_close(provfd);
1672212934Sbrian	if (sector != NULL) {
1673212934Sbrian		bzero(sector, secsize);
1674212934Sbrian		free(sector);
1675212934Sbrian	}
1676212934Sbrian}
1677212934Sbrian
1678212934Sbrianstatic void
1679226723Spjdeli_version(struct gctl_req *req)
1680226723Spjd{
1681226723Spjd	struct g_eli_metadata md;
1682226723Spjd	const char *name;
1683226723Spjd	unsigned int version;
1684226723Spjd	int error, i, nargs;
1685226723Spjd
1686226723Spjd	nargs = gctl_get_int(req, "nargs");
1687226723Spjd
1688226723Spjd	if (nargs == 0) {
1689226723Spjd		unsigned int kernver;
1690226723Spjd		ssize_t size;
1691226723Spjd
1692226723Spjd		size = sizeof(kernver);
1693226723Spjd		if (sysctlbyname("kern.geom.eli.version", &kernver, &size,
1694226723Spjd		    NULL, 0) == -1) {
1695226723Spjd			warn("Unable to obtain GELI kernel version");
1696226723Spjd		} else {
1697226723Spjd			printf("kernel: %u\n", kernver);
1698226723Spjd		}
1699226723Spjd		printf("userland: %u\n", G_ELI_VERSION);
1700226723Spjd		return;
1701226723Spjd	}
1702226723Spjd
1703226723Spjd	for (i = 0; i < nargs; i++) {
1704226723Spjd		name = gctl_get_ascii(req, "arg%d", i);
1705226723Spjd		error = g_metadata_read(name, (unsigned char *)&md,
1706226723Spjd		    sizeof(md), G_ELI_MAGIC);
1707226723Spjd		if (error != 0) {
1708226723Spjd			warn("%s: Unable to read metadata: %s.", name,
1709226723Spjd			    strerror(error));
1710226723Spjd			gctl_error(req, "Not fully done.");
1711226723Spjd			continue;
1712226723Spjd		}
1713226723Spjd		version = le32dec(&md.md_version);
1714226723Spjd		printf("%s: %u\n", name, version);
1715226723Spjd	}
1716226723Spjd}
1717226723Spjd
1718226723Spjdstatic void
1719148456Spjdeli_clear(struct gctl_req *req)
1720148456Spjd{
1721148456Spjd	const char *name;
1722153190Spjd	int error, i, nargs;
1723148456Spjd
1724153190Spjd	nargs = gctl_get_int(req, "nargs");
1725153190Spjd	if (nargs < 1) {
1726148456Spjd		gctl_error(req, "Too few arguments.");
1727148456Spjd		return;
1728148456Spjd	}
1729148456Spjd
1730153190Spjd	for (i = 0; i < nargs; i++) {
1731153190Spjd		name = gctl_get_ascii(req, "arg%d", i);
1732148456Spjd		error = g_metadata_clear(name, G_ELI_MAGIC);
1733148456Spjd		if (error != 0) {
1734148456Spjd			fprintf(stderr, "Cannot clear metadata on %s: %s.\n",
1735148456Spjd			    name, strerror(error));
1736148456Spjd			gctl_error(req, "Not fully done.");
1737148456Spjd			continue;
1738148456Spjd		}
1739148456Spjd		if (verbose)
1740155175Spjd			printf("Metadata cleared on %s.\n", name);
1741148456Spjd	}
1742148456Spjd}
1743148456Spjd
1744148456Spjdstatic void
1745148456Spjdeli_dump(struct gctl_req *req)
1746148456Spjd{
1747226719Spjd	struct g_eli_metadata md;
1748148456Spjd	const char *name;
1749226719Spjd	int i, nargs;
1750148456Spjd
1751153190Spjd	nargs = gctl_get_int(req, "nargs");
1752153190Spjd	if (nargs < 1) {
1753148456Spjd		gctl_error(req, "Too few arguments.");
1754148456Spjd		return;
1755148456Spjd	}
1756148456Spjd
1757153190Spjd	for (i = 0; i < nargs; i++) {
1758153190Spjd		name = gctl_get_ascii(req, "arg%d", i);
1759226719Spjd		if (eli_metadata_read(NULL, name, &md) == -1) {
1760148456Spjd			gctl_error(req, "Not fully done.");
1761148456Spjd			continue;
1762148456Spjd		}
1763148456Spjd		printf("Metadata on %s:\n", name);
1764148456Spjd		eli_metadata_dump(&md);
1765148456Spjd		printf("\n");
1766148456Spjd	}
1767148456Spjd}
1768