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 348588 2019-06-03 21:04:23Z jhb $");
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'
89344397Skevans * attach [-Cdprv] [-n keyno] [-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	    {
148344397Skevans		{ 'C', "dryrun", NULL, G_TYPE_BOOL },
149162868Spjd		{ 'd', "detach", NULL, G_TYPE_BOOL },
150213172Spjd		{ 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
151213172Spjd		{ 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
152344397Skevans		{ 'n', "keyno", "-1", G_TYPE_NUMBER },
153162868Spjd		{ 'p', "nopassphrase", NULL, G_TYPE_BOOL },
154162868Spjd		{ 'r', "readonly", NULL, G_TYPE_BOOL },
155148456Spjd		G_OPT_SENTINEL
156148456Spjd	    },
157344397Skevans	    "[-Cdprv] [-n keyno] [-j passfile] [-k keyfile] prov"
158148456Spjd	},
159148456Spjd	{ "detach", 0, NULL,
160148456Spjd	    {
161162868Spjd		{ 'f', "force", NULL, G_TYPE_BOOL },
162162868Spjd		{ 'l', "last", NULL, G_TYPE_BOOL },
163148456Spjd		G_OPT_SENTINEL
164148456Spjd	    },
165212554Spjd	    "[-fl] prov ..."
166148456Spjd	},
167148456Spjd	{ "stop", 0, NULL,
168148456Spjd	    {
169162868Spjd		{ 'f', "force", NULL, G_TYPE_BOOL },
170162868Spjd		{ 'l', "last", NULL, G_TYPE_BOOL },
171148456Spjd		G_OPT_SENTINEL
172148456Spjd	    },
173212554Spjd	    "- an alias for 'detach'"
174148456Spjd	},
175148456Spjd	{ "onetime", G_FLAG_VERBOSE | G_FLAG_LOADKLD, NULL,
176148456Spjd	    {
177212547Spjd		{ 'a', "aalgo", "", G_TYPE_STRING },
178162868Spjd		{ 'd', "detach", NULL, G_TYPE_BOOL },
179212547Spjd		{ 'e', "ealgo", GELI_ENC_ALGO, G_TYPE_STRING },
180212554Spjd		{ 'l', "keylen", "0", G_TYPE_NUMBER },
181212554Spjd		{ 's', "sectorsize", "0", G_TYPE_NUMBER },
182286444Spjd		{ 'T', "notrim", NULL, G_TYPE_BOOL },
183148456Spjd		G_OPT_SENTINEL
184148456Spjd	    },
185286444Spjd	    "[-dT] [-a aalgo] [-e ealgo] [-l keylen] [-s sectorsize] prov"
186148456Spjd	},
187162353Spjd	{ "configure", G_FLAG_VERBOSE, eli_main,
188162353Spjd	    {
189162868Spjd		{ 'b', "boot", NULL, G_TYPE_BOOL },
190162868Spjd		{ 'B', "noboot", NULL, G_TYPE_BOOL },
191329114Skevans		{ 'd', "displaypass", NULL, G_TYPE_BOOL },
192329114Skevans		{ 'D', "nodisplaypass", NULL, G_TYPE_BOOL },
193297691Sallanjude		{ 'g', "geliboot", NULL, G_TYPE_BOOL },
194297691Sallanjude		{ 'G', "nogeliboot", NULL, G_TYPE_BOOL },
195286444Spjd		{ 't', "trim", NULL, G_TYPE_BOOL },
196286444Spjd		{ 'T', "notrim", NULL, G_TYPE_BOOL },
197162353Spjd		G_OPT_SENTINEL
198162353Spjd	    },
199329114Skevans	    "[-bBdDgGtT] prov ..."
200162353Spjd	},
201148456Spjd	{ "setkey", G_FLAG_VERBOSE, eli_main,
202148456Spjd	    {
203212554Spjd		{ 'i', "iterations", "-1", G_TYPE_NUMBER },
204213172Spjd		{ 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
205213172Spjd		{ 'J', "newpassfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
206213172Spjd		{ 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
207213172Spjd		{ 'K', "newkeyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
208212554Spjd		{ 'n', "keyno", "-1", G_TYPE_NUMBER },
209162868Spjd		{ 'p', "nopassphrase", NULL, G_TYPE_BOOL },
210162868Spjd		{ 'P', "nonewpassphrase", NULL, G_TYPE_BOOL },
211148456Spjd		G_OPT_SENTINEL
212148456Spjd	    },
213213172Spjd	    "[-pPv] [-n keyno] [-i iterations] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov"
214148456Spjd	},
215148456Spjd	{ "delkey", G_FLAG_VERBOSE, eli_main,
216148456Spjd	    {
217162868Spjd		{ 'a', "all", NULL, G_TYPE_BOOL },
218162868Spjd		{ 'f', "force", NULL, G_TYPE_BOOL },
219212554Spjd		{ 'n', "keyno", "-1", G_TYPE_NUMBER },
220148456Spjd		G_OPT_SENTINEL
221148456Spjd	    },
222212554Spjd	    "[-afv] [-n keyno] prov"
223148456Spjd	},
224214118Spjd	{ "suspend", G_FLAG_VERBOSE, NULL,
225214118Spjd	    {
226214118Spjd		{ 'a', "all", NULL, G_TYPE_BOOL },
227214118Spjd		G_OPT_SENTINEL
228214118Spjd	    },
229214118Spjd	    "[-v] -a | prov ..."
230214118Spjd	},
231214118Spjd	{ "resume", G_FLAG_VERBOSE, eli_main,
232214118Spjd	    {
233214118Spjd		{ 'j', "passfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
234214118Spjd		{ 'k', "keyfile", G_VAL_OPTIONAL, G_TYPE_STRING | G_TYPE_MULTI },
235214118Spjd		{ 'p', "nopassphrase", NULL, G_TYPE_BOOL },
236214118Spjd		G_OPT_SENTINEL
237214118Spjd	    },
238214118Spjd	    "[-pv] [-j passfile] [-k keyfile] prov"
239214118Spjd	},
240148456Spjd	{ "kill", G_FLAG_VERBOSE, eli_main,
241148456Spjd	    {
242162868Spjd		{ 'a', "all", NULL, G_TYPE_BOOL },
243148456Spjd		G_OPT_SENTINEL
244148456Spjd	    },
245212554Spjd	    "[-av] [prov ...]"
246148456Spjd	},
247212554Spjd	{ "backup", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS,
248148456Spjd	    "[-v] prov file"
249148456Spjd	},
250212934Sbrian	{ "restore", G_FLAG_VERBOSE, eli_main,
251212934Sbrian	    {
252212934Sbrian		{ 'f', "force", NULL, G_TYPE_BOOL },
253212934Sbrian		G_OPT_SENTINEL
254212934Sbrian	    },
255212934Sbrian	    "[-fv] file prov"
256148456Spjd	},
257212934Sbrian	{ "resize", G_FLAG_VERBOSE, eli_main,
258212934Sbrian	    {
259212934Sbrian		{ 's', "oldsize", NULL, G_TYPE_NUMBER },
260212934Sbrian		G_OPT_SENTINEL
261212934Sbrian	    },
262212934Sbrian	    "[-v] -s oldsize prov"
263212934Sbrian	},
264226723Spjd	{ "version", G_FLAG_LOADKLD, eli_main, G_NULL_OPTS,
265226723Spjd	    "[prov ...]"
266226723Spjd	},
267212554Spjd	{ "clear", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS,
268148456Spjd	    "[-v] prov ..."
269148456Spjd	},
270212554Spjd	{ "dump", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS,
271148456Spjd	    "[-v] prov ..."
272148456Spjd	},
273148456Spjd	G_CMD_SENTINEL
274148456Spjd};
275148456Spjd
276148456Spjdstatic int verbose = 0;
277148456Spjd
278248475Spjd#define	BUFSIZE	1024
279248475Spjd
280148456Spjdstatic int
281148456Spjdeli_protect(struct gctl_req *req)
282148456Spjd{
283148456Spjd	struct rlimit rl;
284148456Spjd
285148456Spjd	/* Disable core dumps. */
286148456Spjd	rl.rlim_cur = 0;
287148456Spjd	rl.rlim_max = 0;
288148456Spjd	if (setrlimit(RLIMIT_CORE, &rl) == -1) {
289148456Spjd		gctl_error(req, "Cannot disable core dumps: %s.",
290148456Spjd		    strerror(errno));
291148456Spjd		return (-1);
292148456Spjd	}
293148456Spjd	/* Disable swapping. */
294148456Spjd	if (mlockall(MCL_FUTURE) == -1) {
295148456Spjd		gctl_error(req, "Cannot lock memory: %s.", strerror(errno));
296148456Spjd		return (-1);
297148456Spjd	}
298148456Spjd	return (0);
299148456Spjd}
300148456Spjd
301148456Spjdstatic void
302213172Spjdeli_main(struct gctl_req *req, unsigned int flags)
303148456Spjd{
304148456Spjd	const char *name;
305148456Spjd
306148456Spjd	if (eli_protect(req) == -1)
307148456Spjd		return;
308148456Spjd
309148456Spjd	if ((flags & G_FLAG_VERBOSE) != 0)
310148456Spjd		verbose = 1;
311148456Spjd
312153190Spjd	name = gctl_get_ascii(req, "verb");
313148456Spjd	if (name == NULL) {
314148456Spjd		gctl_error(req, "No '%s' argument.", "verb");
315148456Spjd		return;
316148456Spjd	}
317148456Spjd	if (strcmp(name, "init") == 0 || strcmp(name, "label") == 0)
318148456Spjd		eli_init(req);
319148456Spjd	else if (strcmp(name, "attach") == 0)
320148456Spjd		eli_attach(req);
321162353Spjd	else if (strcmp(name, "configure") == 0)
322162353Spjd		eli_configure(req);
323148456Spjd	else if (strcmp(name, "setkey") == 0)
324148456Spjd		eli_setkey(req);
325148456Spjd	else if (strcmp(name, "delkey") == 0)
326148456Spjd		eli_delkey(req);
327214118Spjd	else if (strcmp(name, "resume") == 0)
328214118Spjd		eli_resume(req);
329148456Spjd	else if (strcmp(name, "kill") == 0)
330148456Spjd		eli_kill(req);
331148456Spjd	else if (strcmp(name, "backup") == 0)
332148456Spjd		eli_backup(req);
333148456Spjd	else if (strcmp(name, "restore") == 0)
334148456Spjd		eli_restore(req);
335212934Sbrian	else if (strcmp(name, "resize") == 0)
336212934Sbrian		eli_resize(req);
337226723Spjd	else if (strcmp(name, "version") == 0)
338226723Spjd		eli_version(req);
339148456Spjd	else if (strcmp(name, "dump") == 0)
340148456Spjd		eli_dump(req);
341148456Spjd	else if (strcmp(name, "clear") == 0)
342148456Spjd		eli_clear(req);
343148456Spjd	else
344148456Spjd		gctl_error(req, "Unknown command: %s.", name);
345148456Spjd}
346148456Spjd
347226717Spjdstatic bool
348148456Spjdeli_is_attached(const char *prov)
349148456Spjd{
350148456Spjd	char name[MAXPATHLEN];
351148456Spjd
352148456Spjd	/*
353148456Spjd	 * Not the best way to do it, but the easiest.
354148456Spjd	 * We try to open provider and check if it is a GEOM provider
355148456Spjd	 * by asking about its sectorsize.
356148456Spjd	 */
357148456Spjd	snprintf(name, sizeof(name), "%s%s", prov, G_ELI_SUFFIX);
358226717Spjd	return (g_get_sectorsize(name) > 0);
359148456Spjd}
360148456Spjd
361213172Spjdstatic int
362213172Spjdeli_genkey_files(struct gctl_req *req, bool new, const char *type,
363213172Spjd    struct hmac_ctx *ctxp, char *passbuf, size_t passbufsize)
364148456Spjd{
365248475Spjd	char *p, buf[BUFSIZE], argname[16];
366213172Spjd	const char *file;
367213172Spjd	int error, fd, i;
368213172Spjd	ssize_t done;
369148456Spjd
370213172Spjd	assert((strcmp(type, "keyfile") == 0 && ctxp != NULL &&
371213172Spjd	    passbuf == NULL && passbufsize == 0) ||
372213172Spjd	    (strcmp(type, "passfile") == 0 && ctxp == NULL &&
373213172Spjd	    passbuf != NULL && passbufsize > 0));
374213172Spjd	assert(strcmp(type, "keyfile") == 0 || passbuf[0] == '\0');
375148456Spjd
376213172Spjd	for (i = 0; ; i++) {
377213172Spjd		snprintf(argname, sizeof(argname), "%s%s%d",
378213172Spjd		    new ? "new" : "", type, i);
379148456Spjd
380213172Spjd		/* No more {key,pass}files? */
381213172Spjd		if (!gctl_has_param(req, argname))
382213172Spjd			return (i);
383148456Spjd
384215704Sbrucec		file = gctl_get_ascii(req, "%s", argname);
385213172Spjd		assert(file != NULL);
386213172Spjd
387213172Spjd		if (strcmp(file, "-") == 0)
388148456Spjd			fd = STDIN_FILENO;
389148456Spjd		else {
390213172Spjd			fd = open(file, O_RDONLY);
391148456Spjd			if (fd == -1) {
392213172Spjd				gctl_error(req, "Cannot open %s %s: %s.",
393213172Spjd				    type, file, strerror(errno));
394213172Spjd				return (-1);
395148456Spjd			}
396148456Spjd		}
397213172Spjd		if (strcmp(type, "keyfile") == 0) {
398213172Spjd			while ((done = read(fd, buf, sizeof(buf))) > 0)
399213172Spjd				g_eli_crypto_hmac_update(ctxp, buf, done);
400213172Spjd		} else /* if (strcmp(type, "passfile") == 0) */ {
401246621Spjd			assert(strcmp(type, "passfile") == 0);
402246621Spjd
403213172Spjd			while ((done = read(fd, buf, sizeof(buf) - 1)) > 0) {
404213172Spjd				buf[done] = '\0';
405213172Spjd				p = strchr(buf, '\n');
406213172Spjd				if (p != NULL) {
407213172Spjd					*p = '\0';
408213172Spjd					done = p - buf;
409213172Spjd				}
410213172Spjd				if (strlcat(passbuf, buf, passbufsize) >=
411213172Spjd				    passbufsize) {
412213172Spjd					gctl_error(req,
413213172Spjd					    "Passphrase in %s too long.", file);
414213172Spjd					bzero(buf, sizeof(buf));
415213172Spjd					return (-1);
416213172Spjd				}
417213172Spjd				if (p != NULL)
418213172Spjd					break;
419213172Spjd			}
420213172Spjd		}
421148456Spjd		error = errno;
422213172Spjd		if (strcmp(file, "-") != 0)
423148456Spjd			close(fd);
424148456Spjd		bzero(buf, sizeof(buf));
425148456Spjd		if (done == -1) {
426213172Spjd			gctl_error(req, "Cannot read %s %s: %s.",
427213172Spjd			    type, file, strerror(error));
428213172Spjd			return (-1);
429148456Spjd		}
430148456Spjd	}
431213172Spjd	/* NOTREACHED */
432213172Spjd}
433148456Spjd
434213172Spjdstatic int
435213172Spjdeli_genkey_passphrase_prompt(struct gctl_req *req, bool new, char *passbuf,
436213172Spjd    size_t passbufsize)
437213172Spjd{
438213172Spjd	char *p;
439148456Spjd
440213172Spjd	for (;;) {
441213172Spjd		p = readpassphrase(
442284250Sbrueffer		    new ? "Enter new passphrase: " : "Enter passphrase: ",
443213172Spjd		    passbuf, passbufsize, RPP_ECHO_OFF | RPP_REQUIRE_TTY);
444213172Spjd		if (p == NULL) {
445213172Spjd			bzero(passbuf, passbufsize);
446213172Spjd			gctl_error(req, "Cannot read passphrase: %s.",
447213172Spjd			    strerror(errno));
448213172Spjd			return (-1);
449149047Spjd		}
450213172Spjd
451213172Spjd		if (new) {
452248475Spjd			char tmpbuf[BUFSIZE];
453213172Spjd
454213172Spjd			p = readpassphrase("Reenter new passphrase: ",
455213172Spjd			    tmpbuf, sizeof(tmpbuf),
456213172Spjd			    RPP_ECHO_OFF | RPP_REQUIRE_TTY);
457148456Spjd			if (p == NULL) {
458213172Spjd				bzero(passbuf, passbufsize);
459213172Spjd				gctl_error(req,
460213172Spjd				    "Cannot read passphrase: %s.",
461148456Spjd				    strerror(errno));
462213172Spjd				return (-1);
463148456Spjd			}
464213172Spjd
465213172Spjd			if (strcmp(passbuf, tmpbuf) != 0) {
466213172Spjd				bzero(passbuf, passbufsize);
467213172Spjd				fprintf(stderr, "They didn't match.\n");
468213172Spjd				continue;
469148456Spjd			}
470213172Spjd			bzero(tmpbuf, sizeof(tmpbuf));
471148456Spjd		}
472213172Spjd		return (0);
473213172Spjd	}
474213172Spjd	/* NOTREACHED */
475213172Spjd}
476213172Spjd
477213172Spjdstatic int
478213172Spjdeli_genkey_passphrase(struct gctl_req *req, struct g_eli_metadata *md, bool new,
479213172Spjd    struct hmac_ctx *ctxp)
480213172Spjd{
481248475Spjd	char passbuf[BUFSIZE];
482213172Spjd	bool nopassphrase;
483213172Spjd	int nfiles;
484213172Spjd
485213172Spjd	nopassphrase =
486213172Spjd	    gctl_get_int(req, new ? "nonewpassphrase" : "nopassphrase");
487213172Spjd	if (nopassphrase) {
488213172Spjd		if (gctl_has_param(req, new ? "newpassfile0" : "passfile0")) {
489213172Spjd			gctl_error(req,
490213172Spjd			    "Options -%c and -%c are mutually exclusive.",
491213172Spjd			    new ? 'J' : 'j', new ? 'P' : 'p');
492213172Spjd			return (-1);
493148456Spjd		}
494213172Spjd		return (0);
495213172Spjd	}
496148456Spjd
497213172Spjd	if (!new && md->md_iterations == -1) {
498213172Spjd		gctl_error(req, "Missing -p flag.");
499213172Spjd		return (-1);
500213172Spjd	}
501213172Spjd	passbuf[0] = '\0';
502213172Spjd	nfiles = eli_genkey_files(req, new, "passfile", NULL, passbuf,
503213172Spjd	    sizeof(passbuf));
504213172Spjd	if (nfiles == -1)
505213172Spjd		return (-1);
506213172Spjd	else if (nfiles == 0) {
507213172Spjd		if (eli_genkey_passphrase_prompt(req, new, passbuf,
508213172Spjd		    sizeof(passbuf)) == -1) {
509213172Spjd			return (-1);
510148456Spjd		}
511148456Spjd	}
512213172Spjd	/*
513213172Spjd	 * Field md_iterations equal to -1 means "choose some sane
514213172Spjd	 * value for me".
515213172Spjd	 */
516213172Spjd	if (md->md_iterations == -1) {
517213172Spjd		assert(new);
518213172Spjd		if (verbose)
519213172Spjd			printf("Calculating number of iterations...\n");
520213172Spjd		md->md_iterations = pkcs5v2_calculate(2000000);
521213172Spjd		assert(md->md_iterations > 0);
522213172Spjd		if (verbose) {
523213172Spjd			printf("Done, using %d iterations.\n",
524213172Spjd			    md->md_iterations);
525213172Spjd		}
526213172Spjd	}
527213172Spjd	/*
528213172Spjd	 * If md_iterations is equal to 0, user doesn't want PKCS#5v2.
529213172Spjd	 */
530213172Spjd	if (md->md_iterations == 0) {
531213172Spjd		g_eli_crypto_hmac_update(ctxp, md->md_salt,
532213172Spjd		    sizeof(md->md_salt));
533213172Spjd		g_eli_crypto_hmac_update(ctxp, passbuf, strlen(passbuf));
534213172Spjd	} else /* if (md->md_iterations > 0) */ {
535213172Spjd		unsigned char dkey[G_ELI_USERKEYLEN];
536213172Spjd
537213172Spjd		pkcs5v2_genkey(dkey, sizeof(dkey), md->md_salt,
538213172Spjd		    sizeof(md->md_salt), passbuf, md->md_iterations);
539213172Spjd		g_eli_crypto_hmac_update(ctxp, dkey, sizeof(dkey));
540213172Spjd		bzero(dkey, sizeof(dkey));
541213172Spjd	}
542213172Spjd	bzero(passbuf, sizeof(passbuf));
543213172Spjd
544213172Spjd	return (0);
545213172Spjd}
546213172Spjd
547213172Spjdstatic unsigned char *
548213172Spjdeli_genkey(struct gctl_req *req, struct g_eli_metadata *md, unsigned char *key,
549213172Spjd    bool new)
550213172Spjd{
551213172Spjd	struct hmac_ctx ctx;
552213172Spjd	bool nopassphrase;
553213172Spjd	int nfiles;
554213172Spjd
555213172Spjd	nopassphrase =
556213172Spjd	    gctl_get_int(req, new ? "nonewpassphrase" : "nopassphrase");
557213172Spjd
558213172Spjd	g_eli_crypto_hmac_init(&ctx, NULL, 0);
559213172Spjd
560213172Spjd	nfiles = eli_genkey_files(req, new, "keyfile", &ctx, NULL, 0);
561213172Spjd	if (nfiles == -1)
562213172Spjd		return (NULL);
563213172Spjd	else if (nfiles == 0 && nopassphrase) {
564213172Spjd		gctl_error(req, "No key components given.");
565213172Spjd		return (NULL);
566213172Spjd	}
567213172Spjd
568213172Spjd	if (eli_genkey_passphrase(req, md, new, &ctx) == -1)
569213172Spjd		return (NULL);
570213172Spjd
571148456Spjd	g_eli_crypto_hmac_final(&ctx, key, 0);
572213172Spjd
573148456Spjd	return (key);
574148456Spjd}
575148456Spjd
576148456Spjdstatic int
577148456Spjdeli_metadata_read(struct gctl_req *req, const char *prov,
578148456Spjd    struct g_eli_metadata *md)
579148456Spjd{
580148456Spjd	unsigned char sector[sizeof(struct g_eli_metadata)];
581148456Spjd	int error;
582148456Spjd
583148456Spjd	if (g_get_sectorsize(prov) == 0) {
584148456Spjd		int fd;
585148456Spjd
586148456Spjd		/* This is a file probably. */
587148456Spjd		fd = open(prov, O_RDONLY);
588148456Spjd		if (fd == -1) {
589148456Spjd			gctl_error(req, "Cannot open %s: %s.", prov,
590148456Spjd			    strerror(errno));
591148456Spjd			return (-1);
592148456Spjd		}
593148456Spjd		if (read(fd, sector, sizeof(sector)) != sizeof(sector)) {
594148456Spjd			gctl_error(req, "Cannot read metadata from %s: %s.",
595148456Spjd			    prov, strerror(errno));
596148456Spjd			close(fd);
597148456Spjd			return (-1);
598148456Spjd		}
599148456Spjd		close(fd);
600148456Spjd	} else {
601148456Spjd		/* This is a GEOM provider. */
602148456Spjd		error = g_metadata_read(prov, sector, sizeof(sector),
603148456Spjd		    G_ELI_MAGIC);
604148456Spjd		if (error != 0) {
605148456Spjd			gctl_error(req, "Cannot read metadata from %s: %s.",
606148456Spjd			    prov, strerror(error));
607148456Spjd			return (-1);
608148456Spjd		}
609148456Spjd	}
610226722Spjd	error = eli_metadata_decode(sector, md);
611226722Spjd	switch (error) {
612226722Spjd	case 0:
613226722Spjd		break;
614226722Spjd	case EOPNOTSUPP:
615226722Spjd		gctl_error(req,
616226722Spjd		    "Provider's %s metadata version %u is too new.\n"
617226722Spjd		    "geli: The highest supported version is %u.",
618226722Spjd		    prov, (unsigned int)md->md_version, G_ELI_VERSION);
619148456Spjd		return (-1);
620226722Spjd	case EINVAL:
621226722Spjd		gctl_error(req, "Inconsistent provider's %s metadata.", prov);
622226722Spjd		return (-1);
623226722Spjd	default:
624226722Spjd		gctl_error(req,
625226722Spjd		    "Unexpected error while decoding provider's %s metadata: %s.",
626226722Spjd		    prov, strerror(error));
627226722Spjd		return (-1);
628148456Spjd	}
629148456Spjd	return (0);
630148456Spjd}
631148456Spjd
632148456Spjdstatic int
633148456Spjdeli_metadata_store(struct gctl_req *req, const char *prov,
634148456Spjd    struct g_eli_metadata *md)
635148456Spjd{
636148456Spjd	unsigned char sector[sizeof(struct g_eli_metadata)];
637148456Spjd	int error;
638148456Spjd
639148456Spjd	eli_metadata_encode(md, sector);
640148456Spjd	if (g_get_sectorsize(prov) == 0) {
641148456Spjd		int fd;
642148456Spjd
643148456Spjd		/* This is a file probably. */
644148456Spjd		fd = open(prov, O_WRONLY | O_TRUNC);
645148456Spjd		if (fd == -1) {
646148456Spjd			gctl_error(req, "Cannot open %s: %s.", prov,
647148456Spjd			    strerror(errno));
648148456Spjd			bzero(sector, sizeof(sector));
649148456Spjd			return (-1);
650148456Spjd		}
651148456Spjd		if (write(fd, sector, sizeof(sector)) != sizeof(sector)) {
652148456Spjd			gctl_error(req, "Cannot write metadata to %s: %s.",
653148456Spjd			    prov, strerror(errno));
654148456Spjd			bzero(sector, sizeof(sector));
655148456Spjd			close(fd);
656148456Spjd			return (-1);
657148456Spjd		}
658148456Spjd		close(fd);
659148456Spjd	} else {
660148456Spjd		/* This is a GEOM provider. */
661148456Spjd		error = g_metadata_store(prov, sector, sizeof(sector));
662148456Spjd		if (error != 0) {
663148456Spjd			gctl_error(req, "Cannot write metadata to %s: %s.",
664148456Spjd			    prov, strerror(errno));
665148456Spjd			bzero(sector, sizeof(sector));
666148456Spjd			return (-1);
667148456Spjd		}
668148456Spjd	}
669148456Spjd	bzero(sector, sizeof(sector));
670148456Spjd	return (0);
671148456Spjd}
672148456Spjd
673148456Spjdstatic void
674148456Spjdeli_init(struct gctl_req *req)
675148456Spjd{
676148456Spjd	struct g_eli_metadata md;
677332522Skevans	unsigned char sector[sizeof(struct g_eli_metadata)] __aligned(4);
678148456Spjd	unsigned char key[G_ELI_USERKEYLEN];
679182452Spjd	char backfile[MAXPATHLEN];
680148456Spjd	const char *str, *prov;
681226733Spjd	unsigned int secsize, version;
682148456Spjd	off_t mediasize;
683153190Spjd	intmax_t val;
684155536Spjd	int error, nargs;
685148456Spjd
686153190Spjd	nargs = gctl_get_int(req, "nargs");
687153190Spjd	if (nargs != 1) {
688158214Spjd		gctl_error(req, "Invalid number of arguments.");
689148456Spjd		return;
690148456Spjd	}
691153190Spjd	prov = gctl_get_ascii(req, "arg0");
692148456Spjd	mediasize = g_get_mediasize(prov);
693148456Spjd	secsize = g_get_sectorsize(prov);
694148456Spjd	if (mediasize == 0 || secsize == 0) {
695148456Spjd		gctl_error(req, "Cannot get informations about %s: %s.", prov,
696148456Spjd		    strerror(errno));
697148456Spjd		return;
698148456Spjd	}
699148456Spjd
700148456Spjd	bzero(&md, sizeof(md));
701148456Spjd	strlcpy(md.md_magic, G_ELI_MAGIC, sizeof(md.md_magic));
702226733Spjd	val = gctl_get_intmax(req, "mdversion");
703226733Spjd	if (val == -1) {
704226733Spjd		version = G_ELI_VERSION;
705226733Spjd	} else if (val < 0 || val > G_ELI_VERSION) {
706226733Spjd		gctl_error(req,
707226733Spjd		    "Invalid version specified should be between %u and %u.",
708226733Spjd		    G_ELI_VERSION_00, G_ELI_VERSION);
709226733Spjd		return;
710226733Spjd	} else {
711226733Spjd		version = val;
712226733Spjd	}
713226733Spjd	md.md_version = version;
714148456Spjd	md.md_flags = 0;
715155536Spjd	if (gctl_get_int(req, "boot"))
716148456Spjd		md.md_flags |= G_ELI_FLAG_BOOT;
717297691Sallanjude	if (gctl_get_int(req, "geliboot"))
718297691Sallanjude		md.md_flags |= G_ELI_FLAG_GELIBOOT;
719329114Skevans	if (gctl_get_int(req, "displaypass"))
720329114Skevans		md.md_flags |= G_ELI_FLAG_GELIDISPLAYPASS;
721286444Spjd	if (gctl_get_int(req, "notrim"))
722286444Spjd		md.md_flags |= G_ELI_FLAG_NODELETE;
723159361Spjd	md.md_ealgo = CRYPTO_ALGORITHM_MIN - 1;
724159308Spjd	str = gctl_get_ascii(req, "aalgo");
725212547Spjd	if (*str != '\0') {
726226733Spjd		if (version < G_ELI_VERSION_01) {
727226733Spjd			gctl_error(req,
728226733Spjd			    "Data authentication is supported starting from version %u.",
729226733Spjd			    G_ELI_VERSION_01);
730226733Spjd			return;
731226733Spjd		}
732159308Spjd		md.md_aalgo = g_eli_str2aalgo(str);
733159361Spjd		if (md.md_aalgo >= CRYPTO_ALGORITHM_MIN &&
734159361Spjd		    md.md_aalgo <= CRYPTO_ALGORITHM_MAX) {
735159361Spjd			md.md_flags |= G_ELI_FLAG_AUTH;
736159361Spjd		} else {
737159361Spjd			/*
738159361Spjd			 * For backward compatibility, check if the -a option
739159361Spjd			 * was used to provide encryption algorithm.
740159361Spjd			 */
741159361Spjd			md.md_ealgo = g_eli_str2ealgo(str);
742159361Spjd			if (md.md_ealgo < CRYPTO_ALGORITHM_MIN ||
743159361Spjd			    md.md_ealgo > CRYPTO_ALGORITHM_MAX) {
744159361Spjd				gctl_error(req,
745159361Spjd				    "Invalid authentication algorithm.");
746159361Spjd				return;
747159361Spjd			} else {
748159361Spjd				fprintf(stderr, "warning: The -e option, not "
749159361Spjd				    "the -a option is now used to specify "
750159361Spjd				    "encryption algorithm to use.\n");
751159361Spjd			}
752159308Spjd		}
753159308Spjd	}
754159308Spjd	if (md.md_ealgo < CRYPTO_ALGORITHM_MIN ||
755159308Spjd	    md.md_ealgo > CRYPTO_ALGORITHM_MAX) {
756159361Spjd		str = gctl_get_ascii(req, "ealgo");
757226733Spjd		if (*str == '\0') {
758226733Spjd			if (version < G_ELI_VERSION_05)
759226733Spjd				str = "aes-cbc";
760226733Spjd			else
761226733Spjd				str = GELI_ENC_ALGO;
762226733Spjd		}
763159361Spjd		md.md_ealgo = g_eli_str2ealgo(str);
764159361Spjd		if (md.md_ealgo < CRYPTO_ALGORITHM_MIN ||
765159361Spjd		    md.md_ealgo > CRYPTO_ALGORITHM_MAX) {
766159361Spjd			gctl_error(req, "Invalid encryption algorithm.");
767159361Spjd			return;
768159361Spjd		}
769226733Spjd		if (md.md_ealgo == CRYPTO_CAMELLIA_CBC &&
770226733Spjd		    version < G_ELI_VERSION_04) {
771226733Spjd			gctl_error(req,
772226733Spjd			    "Camellia-CBC algorithm is supported starting from version %u.",
773226733Spjd			    G_ELI_VERSION_04);
774226733Spjd			return;
775226733Spjd		}
776226733Spjd		if (md.md_ealgo == CRYPTO_AES_XTS &&
777226733Spjd		    version < G_ELI_VERSION_05) {
778226733Spjd			gctl_error(req,
779226733Spjd			    "AES-XTS algorithm is supported starting from version %u.",
780226733Spjd			    G_ELI_VERSION_05);
781226733Spjd			return;
782226733Spjd		}
783148456Spjd	}
784348588Sjhb	if (md.md_flags & G_ELI_FLAG_AUTH) {
785348588Sjhb		switch (md.md_aalgo) {
786348588Sjhb		case CRYPTO_MD5_HMAC:
787348588Sjhb			gctl_error(req,
788348588Sjhb			    "The %s authentication algorithm is deprecated.",
789348588Sjhb			    g_eli_algo2str(md.md_aalgo));
790348588Sjhb			return;
791348588Sjhb		}
792348588Sjhb	}
793348588Sjhb	switch (md.md_ealgo) {
794348588Sjhb	case CRYPTO_3DES_CBC:
795348588Sjhb	case CRYPTO_BLF_CBC:
796348588Sjhb		gctl_error(req, "The %s encryption algorithm is deprecated.",
797348588Sjhb		    g_eli_algo2str(md.md_ealgo));
798348588Sjhb		return;
799348588Sjhb	}
800153190Spjd	val = gctl_get_intmax(req, "keylen");
801153190Spjd	md.md_keylen = val;
802159308Spjd	md.md_keylen = g_eli_keylen(md.md_ealgo, md.md_keylen);
803148456Spjd	if (md.md_keylen == 0) {
804148456Spjd		gctl_error(req, "Invalid key length.");
805148456Spjd		return;
806148456Spjd	}
807148456Spjd	md.md_provsize = mediasize;
808148456Spjd
809153190Spjd	val = gctl_get_intmax(req, "iterations");
810155536Spjd	if (val != -1) {
811155536Spjd		int nonewpassphrase;
812155536Spjd
813155536Spjd		/*
814155536Spjd		 * Don't allow to set iterations when there will be no
815155536Spjd		 * passphrase.
816155536Spjd		 */
817155536Spjd		nonewpassphrase = gctl_get_int(req, "nonewpassphrase");
818155536Spjd		if (nonewpassphrase) {
819155536Spjd			gctl_error(req,
820155536Spjd			    "Options -i and -P are mutually exclusive.");
821155536Spjd			return;
822155536Spjd		}
823155536Spjd	}
824153190Spjd	md.md_iterations = val;
825148456Spjd
826153190Spjd	val = gctl_get_intmax(req, "sectorsize");
827153190Spjd	if (val == 0)
828148456Spjd		md.md_sectorsize = secsize;
829148456Spjd	else {
830260254Spjd		if (val < 0 || (val % secsize) != 0 || !powerof2(val)) {
831148456Spjd			gctl_error(req, "Invalid sector size.");
832148456Spjd			return;
833148456Spjd		}
834167229Spjd		if (val > sysconf(_SC_PAGE_SIZE)) {
835214404Spjd			fprintf(stderr,
836214404Spjd			    "warning: Using sectorsize bigger than the page size!\n");
837167229Spjd		}
838153190Spjd		md.md_sectorsize = val;
839148456Spjd	}
840148456Spjd
841148456Spjd	md.md_keys = 0x01;
842246620Spjd	arc4random_buf(md.md_salt, sizeof(md.md_salt));
843246620Spjd	arc4random_buf(md.md_mkeys, sizeof(md.md_mkeys));
844148456Spjd
845148456Spjd	/* Generate user key. */
846213172Spjd	if (eli_genkey(req, &md, key, true) == NULL) {
847148456Spjd		bzero(key, sizeof(key));
848148456Spjd		bzero(&md, sizeof(md));
849148456Spjd		return;
850148456Spjd	}
851148456Spjd
852148456Spjd	/* Encrypt the first and the only Master Key. */
853159308Spjd	error = g_eli_mkey_encrypt(md.md_ealgo, key, md.md_keylen, md.md_mkeys);
854148456Spjd	bzero(key, sizeof(key));
855148456Spjd	if (error != 0) {
856148456Spjd		bzero(&md, sizeof(md));
857148456Spjd		gctl_error(req, "Cannot encrypt Master Key: %s.",
858148456Spjd		    strerror(error));
859148456Spjd		return;
860148456Spjd	}
861148456Spjd
862148456Spjd	eli_metadata_encode(&md, sector);
863148456Spjd	bzero(&md, sizeof(md));
864148456Spjd	error = g_metadata_store(prov, sector, sizeof(sector));
865148456Spjd	bzero(sector, sizeof(sector));
866148456Spjd	if (error != 0) {
867148456Spjd		gctl_error(req, "Cannot store metadata on %s: %s.", prov,
868148456Spjd		    strerror(error));
869148456Spjd		return;
870148456Spjd	}
871148456Spjd	if (verbose)
872148456Spjd		printf("Metadata value stored on %s.\n", prov);
873182452Spjd	/* Backup metadata to a file. */
874182452Spjd	str = gctl_get_ascii(req, "backupfile");
875182452Spjd	if (str[0] != '\0') {
876182452Spjd		/* Backupfile given be the user, just copy it. */
877182452Spjd		strlcpy(backfile, str, sizeof(backfile));
878182452Spjd	} else {
879182452Spjd		/* Generate file name automatically. */
880182452Spjd		const char *p = prov;
881182452Spjd		unsigned int i;
882182452Spjd
883213662Sae		if (strncmp(p, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
884213662Sae			p += sizeof(_PATH_DEV) - 1;
885182452Spjd		snprintf(backfile, sizeof(backfile), "%s%s.eli",
886182452Spjd		    GELI_BACKUP_DIR, p);
887182452Spjd		/* Replace all / with _. */
888182452Spjd		for (i = strlen(GELI_BACKUP_DIR); backfile[i] != '\0'; i++) {
889182452Spjd			if (backfile[i] == '/')
890182452Spjd				backfile[i] = '_';
891182452Spjd		}
892182452Spjd	}
893182452Spjd	if (strcmp(backfile, "none") != 0 &&
894182452Spjd	    eli_backup_create(req, prov, backfile) == 0) {
895182452Spjd		printf("\nMetadata backup can be found in %s and\n", backfile);
896182452Spjd		printf("can be restored with the following command:\n");
897182452Spjd		printf("\n\t# geli restore %s %s\n\n", backfile, prov);
898182452Spjd	}
899148456Spjd}
900148456Spjd
901148456Spjdstatic void
902148456Spjdeli_attach(struct gctl_req *req)
903148456Spjd{
904148456Spjd	struct g_eli_metadata md;
905148456Spjd	unsigned char key[G_ELI_USERKEYLEN];
906148456Spjd	const char *prov;
907212934Sbrian	off_t mediasize;
908153190Spjd	int nargs;
909148456Spjd
910153190Spjd	nargs = gctl_get_int(req, "nargs");
911153190Spjd	if (nargs != 1) {
912158214Spjd		gctl_error(req, "Invalid number of arguments.");
913148456Spjd		return;
914148456Spjd	}
915153190Spjd	prov = gctl_get_ascii(req, "arg0");
916148456Spjd
917148456Spjd	if (eli_metadata_read(req, prov, &md) == -1)
918148456Spjd		return;
919148456Spjd
920212934Sbrian	mediasize = g_get_mediasize(prov);
921212934Sbrian	if (md.md_provsize != (uint64_t)mediasize) {
922212934Sbrian		gctl_error(req, "Provider size mismatch.");
923212934Sbrian		return;
924212934Sbrian	}
925212934Sbrian
926213172Spjd	if (eli_genkey(req, &md, key, false) == NULL) {
927148456Spjd		bzero(key, sizeof(key));
928148456Spjd		return;
929148456Spjd	}
930148456Spjd
931148456Spjd	gctl_ro_param(req, "key", sizeof(key), key);
932148456Spjd	if (gctl_issue(req) == NULL) {
933148456Spjd		if (verbose)
934166892Spjd			printf("Attached to %s.\n", prov);
935148456Spjd	}
936148456Spjd	bzero(key, sizeof(key));
937148456Spjd}
938148456Spjd
939148456Spjdstatic void
940286444Spjdeli_configure_detached(struct gctl_req *req, const char *prov, int boot,
941329114Skevans    int geliboot, int displaypass, int trim)
942162353Spjd{
943162353Spjd	struct g_eli_metadata md;
944286444Spjd	bool changed = 0;
945162353Spjd
946162353Spjd	if (eli_metadata_read(req, prov, &md) == -1)
947162353Spjd		return;
948162353Spjd
949286444Spjd	if (boot == 1 && (md.md_flags & G_ELI_FLAG_BOOT)) {
950162353Spjd		if (verbose)
951162353Spjd			printf("BOOT flag already configured for %s.\n", prov);
952286444Spjd	} else if (boot == 0 && !(md.md_flags & G_ELI_FLAG_BOOT)) {
953162353Spjd		if (verbose)
954162353Spjd			printf("BOOT flag not configured for %s.\n", prov);
955286444Spjd	} else if (boot >= 0) {
956162353Spjd		if (boot)
957162353Spjd			md.md_flags |= G_ELI_FLAG_BOOT;
958162353Spjd		else
959162353Spjd			md.md_flags &= ~G_ELI_FLAG_BOOT;
960286444Spjd		changed = 1;
961286444Spjd	}
962286444Spjd
963297691Sallanjude	if (geliboot == 1 && (md.md_flags & G_ELI_FLAG_GELIBOOT)) {
964297691Sallanjude		if (verbose)
965297691Sallanjude			printf("GELIBOOT flag already configured for %s.\n", prov);
966297691Sallanjude	} else if (geliboot == 0 && !(md.md_flags & G_ELI_FLAG_GELIBOOT)) {
967297691Sallanjude		if (verbose)
968297691Sallanjude			printf("GELIBOOT flag not configured for %s.\n", prov);
969297691Sallanjude	} else if (geliboot >= 0) {
970297691Sallanjude		if (geliboot)
971297691Sallanjude			md.md_flags |= G_ELI_FLAG_GELIBOOT;
972297691Sallanjude		else
973297691Sallanjude			md.md_flags &= ~G_ELI_FLAG_GELIBOOT;
974297691Sallanjude		changed = 1;
975297691Sallanjude	}
976297691Sallanjude
977329114Skevans	if (displaypass == 1 && (md.md_flags & G_ELI_FLAG_GELIDISPLAYPASS)) {
978329114Skevans		if (verbose)
979329114Skevans			printf("GELIDISPLAYPASS flag already configured for %s.\n", prov);
980329114Skevans	} else if (displaypass == 0 &&
981329114Skevans	    !(md.md_flags & G_ELI_FLAG_GELIDISPLAYPASS)) {
982329114Skevans		if (verbose)
983329114Skevans			printf("GELIDISPLAYPASS flag not configured for %s.\n", prov);
984329114Skevans	} else if (displaypass >= 0) {
985329114Skevans		if (displaypass)
986329114Skevans			md.md_flags |= G_ELI_FLAG_GELIDISPLAYPASS;
987329114Skevans		else
988329114Skevans			md.md_flags &= ~G_ELI_FLAG_GELIDISPLAYPASS;
989329114Skevans		changed = 1;
990329114Skevans	}
991329114Skevans
992286444Spjd	if (trim == 0 && (md.md_flags & G_ELI_FLAG_NODELETE)) {
993286444Spjd		if (verbose)
994286444Spjd			printf("TRIM disable flag already configured for %s.\n", prov);
995286444Spjd	} else if (trim == 1 && !(md.md_flags & G_ELI_FLAG_NODELETE)) {
996286444Spjd		if (verbose)
997286444Spjd			printf("TRIM disable flag not configured for %s.\n", prov);
998286444Spjd	} else if (trim >= 0) {
999286444Spjd		if (trim)
1000286444Spjd			md.md_flags &= ~G_ELI_FLAG_NODELETE;
1001286444Spjd		else
1002286444Spjd			md.md_flags |= G_ELI_FLAG_NODELETE;
1003286444Spjd		changed = 1;
1004286444Spjd	}
1005286444Spjd
1006286444Spjd	if (changed)
1007162353Spjd		eli_metadata_store(req, prov, &md);
1008162353Spjd	bzero(&md, sizeof(md));
1009162353Spjd}
1010162353Spjd
1011162353Spjdstatic void
1012162353Spjdeli_configure(struct gctl_req *req)
1013162353Spjd{
1014162353Spjd	const char *prov;
1015329114Skevans	bool boot, noboot, geliboot, nogeliboot, displaypass, nodisplaypass;
1016329114Skevans	bool trim, notrim;
1017329114Skevans	int doboot, dogeliboot, dodisplaypass, dotrim;
1018213172Spjd	int i, nargs;
1019162353Spjd
1020162353Spjd	nargs = gctl_get_int(req, "nargs");
1021162353Spjd	if (nargs == 0) {
1022162353Spjd		gctl_error(req, "Too few arguments.");
1023162353Spjd		return;
1024162353Spjd	}
1025162353Spjd
1026162353Spjd	boot = gctl_get_int(req, "boot");
1027162353Spjd	noboot = gctl_get_int(req, "noboot");
1028297691Sallanjude	geliboot = gctl_get_int(req, "geliboot");
1029297691Sallanjude	nogeliboot = gctl_get_int(req, "nogeliboot");
1030329114Skevans	displaypass = gctl_get_int(req, "displaypass");
1031329114Skevans	nodisplaypass = gctl_get_int(req, "nodisplaypass");
1032286444Spjd	trim = gctl_get_int(req, "trim");
1033286444Spjd	notrim = gctl_get_int(req, "notrim");
1034162353Spjd
1035286444Spjd	doboot = -1;
1036162353Spjd	if (boot && noboot) {
1037162353Spjd		gctl_error(req, "Options -b and -B are mutually exclusive.");
1038162353Spjd		return;
1039162353Spjd	}
1040286444Spjd	if (boot)
1041286444Spjd		doboot = 1;
1042286444Spjd	else if (noboot)
1043286444Spjd		doboot = 0;
1044286444Spjd
1045297691Sallanjude	dogeliboot = -1;
1046297691Sallanjude	if (geliboot && nogeliboot) {
1047297691Sallanjude		gctl_error(req, "Options -g and -G are mutually exclusive.");
1048297691Sallanjude		return;
1049297691Sallanjude	}
1050297691Sallanjude	if (geliboot)
1051297691Sallanjude		dogeliboot = 1;
1052297691Sallanjude	else if (nogeliboot)
1053297691Sallanjude		dogeliboot = 0;
1054297691Sallanjude
1055329114Skevans	dodisplaypass = -1;
1056329114Skevans	if (displaypass && nodisplaypass) {
1057329114Skevans		gctl_error(req, "Options -d and -D are mutually exclusive.");
1058329114Skevans		return;
1059329114Skevans	}
1060329114Skevans	if (displaypass)
1061329114Skevans		dodisplaypass = 1;
1062329114Skevans	else if (nodisplaypass)
1063329114Skevans		dodisplaypass = 0;
1064329114Skevans
1065286444Spjd	dotrim = -1;
1066286444Spjd	if (trim && notrim) {
1067286444Spjd		gctl_error(req, "Options -t and -T are mutually exclusive.");
1068286444Spjd		return;
1069286444Spjd	}
1070286444Spjd	if (trim)
1071286444Spjd		dotrim = 1;
1072286444Spjd	else if (notrim)
1073286444Spjd		dotrim = 0;
1074286444Spjd
1075329114Skevans	if (doboot == -1 && dogeliboot == -1 && dodisplaypass == -1 &&
1076329114Skevans	    dotrim == -1) {
1077162353Spjd		gctl_error(req, "No option given.");
1078162353Spjd		return;
1079162353Spjd	}
1080162353Spjd
1081162353Spjd	/* First attached providers. */
1082162353Spjd	gctl_issue(req);
1083162353Spjd	/* Now the rest. */
1084162353Spjd	for (i = 0; i < nargs; i++) {
1085162353Spjd		prov = gctl_get_ascii(req, "arg%d", i);
1086329114Skevans		if (!eli_is_attached(prov)) {
1087329114Skevans			eli_configure_detached(req, prov, doboot, dogeliboot,
1088329114Skevans			    dodisplaypass, dotrim);
1089329114Skevans		}
1090162353Spjd	}
1091162353Spjd}
1092162353Spjd
1093162353Spjdstatic void
1094155101Spjdeli_setkey_attached(struct gctl_req *req, struct g_eli_metadata *md)
1095148456Spjd{
1096148456Spjd	unsigned char key[G_ELI_USERKEYLEN];
1097166216Spjd	intmax_t val, old = 0;
1098166216Spjd	int error;
1099148456Spjd
1100153190Spjd	val = gctl_get_intmax(req, "iterations");
1101149304Spjd	/* Check if iterations number should be changed. */
1102153190Spjd	if (val != -1)
1103153190Spjd		md->md_iterations = val;
1104166216Spjd	else
1105166216Spjd		old = md->md_iterations;
1106148456Spjd
1107148456Spjd	/* Generate key for Master Key encryption. */
1108213172Spjd	if (eli_genkey(req, md, key, true) == NULL) {
1109148456Spjd		bzero(key, sizeof(key));
1110148456Spjd		return;
1111148456Spjd	}
1112166216Spjd	/*
1113166216Spjd	 * If number of iterations has changed, but wasn't given as a
1114166216Spjd	 * command-line argument, update the request.
1115166216Spjd	 */
1116166216Spjd	if (val == -1 && md->md_iterations != old) {
1117166216Spjd		error = gctl_change_param(req, "iterations", sizeof(intmax_t),
1118166216Spjd		    &md->md_iterations);
1119166216Spjd		assert(error == 0);
1120166216Spjd	}
1121148456Spjd
1122148456Spjd	gctl_ro_param(req, "key", sizeof(key), key);
1123148456Spjd	gctl_issue(req);
1124148456Spjd	bzero(key, sizeof(key));
1125148456Spjd}
1126148456Spjd
1127148456Spjdstatic void
1128149304Spjdeli_setkey_detached(struct gctl_req *req, const char *prov,
1129149304Spjd struct g_eli_metadata *md)
1130148456Spjd{
1131148456Spjd	unsigned char key[G_ELI_USERKEYLEN], mkey[G_ELI_DATAIVKEYLEN];
1132148456Spjd	unsigned char *mkeydst;
1133213172Spjd	unsigned int nkey;
1134153190Spjd	intmax_t val;
1135148456Spjd	int error;
1136148456Spjd
1137149928Spjd	if (md->md_keys == 0) {
1138149928Spjd		gctl_error(req, "No valid keys on %s.", prov);
1139149928Spjd		return;
1140149928Spjd	}
1141149928Spjd
1142148456Spjd	/* Generate key for Master Key decryption. */
1143213172Spjd	if (eli_genkey(req, md, key, false) == NULL) {
1144148456Spjd		bzero(key, sizeof(key));
1145148456Spjd		return;
1146148456Spjd	}
1147148456Spjd
1148148456Spjd	/* Decrypt Master Key. */
1149344397Skevans	error = g_eli_mkey_decrypt_any(md, key, mkey, &nkey);
1150148456Spjd	bzero(key, sizeof(key));
1151148456Spjd	if (error != 0) {
1152149304Spjd		bzero(md, sizeof(*md));
1153148456Spjd		if (error == -1)
1154148456Spjd			gctl_error(req, "Wrong key for %s.", prov);
1155148456Spjd		else /* if (error > 0) */ {
1156148456Spjd			gctl_error(req, "Cannot decrypt Master Key: %s.",
1157148456Spjd			    strerror(error));
1158148456Spjd		}
1159148456Spjd		return;
1160148456Spjd	}
1161148456Spjd	if (verbose)
1162148456Spjd		printf("Decrypted Master Key %u.\n", nkey);
1163148456Spjd
1164153190Spjd	val = gctl_get_intmax(req, "keyno");
1165153190Spjd	if (val != -1)
1166153190Spjd		nkey = val;
1167148456Spjd#if 0
1168148456Spjd	else
1169148456Spjd		; /* Use the key number which was found during decryption. */
1170148456Spjd#endif
1171148456Spjd	if (nkey >= G_ELI_MAXMKEYS) {
1172148456Spjd		gctl_error(req, "Invalid '%s' argument.", "keyno");
1173148456Spjd		return;
1174148456Spjd	}
1175148456Spjd
1176153190Spjd	val = gctl_get_intmax(req, "iterations");
1177149304Spjd	/* Check if iterations number should and can be changed. */
1178317858Smav	if (val != -1 && md->md_iterations == -1) {
1179317858Smav		md->md_iterations = val;
1180317858Smav	} else if (val != -1 && val != md->md_iterations) {
1181149304Spjd		if (bitcount32(md->md_keys) != 1) {
1182149304Spjd			gctl_error(req, "To be able to use '-i' option, only "
1183149304Spjd			    "one key can be defined.");
1184149304Spjd			return;
1185149304Spjd		}
1186149304Spjd		if (md->md_keys != (1 << nkey)) {
1187149304Spjd			gctl_error(req, "Only already defined key can be "
1188149304Spjd			    "changed when '-i' option is used.");
1189149304Spjd			return;
1190149304Spjd		}
1191153190Spjd		md->md_iterations = val;
1192149304Spjd	}
1193148456Spjd
1194149304Spjd	mkeydst = md->md_mkeys + nkey * G_ELI_MKEYLEN;
1195149304Spjd	md->md_keys |= (1 << nkey);
1196149304Spjd
1197148456Spjd	bcopy(mkey, mkeydst, sizeof(mkey));
1198148456Spjd	bzero(mkey, sizeof(mkey));
1199148456Spjd
1200148456Spjd	/* Generate key for Master Key encryption. */
1201213172Spjd	if (eli_genkey(req, md, key, true) == NULL) {
1202148456Spjd		bzero(key, sizeof(key));
1203149304Spjd		bzero(md, sizeof(*md));
1204148456Spjd		return;
1205148456Spjd	}
1206148456Spjd
1207148456Spjd	/* Encrypt the Master-Key with the new key. */
1208159308Spjd	error = g_eli_mkey_encrypt(md->md_ealgo, key, md->md_keylen, mkeydst);
1209148456Spjd	bzero(key, sizeof(key));
1210148456Spjd	if (error != 0) {
1211149304Spjd		bzero(md, sizeof(*md));
1212148456Spjd		gctl_error(req, "Cannot encrypt Master Key: %s.",
1213148456Spjd		    strerror(error));
1214148456Spjd		return;
1215148456Spjd	}
1216148456Spjd
1217148456Spjd	/* Store metadata with fresh key. */
1218149304Spjd	eli_metadata_store(req, prov, md);
1219149304Spjd	bzero(md, sizeof(*md));
1220148456Spjd}
1221148456Spjd
1222148456Spjdstatic void
1223148456Spjdeli_setkey(struct gctl_req *req)
1224148456Spjd{
1225149304Spjd	struct g_eli_metadata md;
1226148456Spjd	const char *prov;
1227153190Spjd	int nargs;
1228148456Spjd
1229153190Spjd	nargs = gctl_get_int(req, "nargs");
1230153190Spjd	if (nargs != 1) {
1231158214Spjd		gctl_error(req, "Invalid number of arguments.");
1232148456Spjd		return;
1233148456Spjd	}
1234153190Spjd	prov = gctl_get_ascii(req, "arg0");
1235148456Spjd
1236149304Spjd	if (eli_metadata_read(req, prov, &md) == -1)
1237149304Spjd		return;
1238149304Spjd
1239148456Spjd	if (eli_is_attached(prov))
1240155101Spjd		eli_setkey_attached(req, &md);
1241148456Spjd	else
1242149304Spjd		eli_setkey_detached(req, prov, &md);
1243182452Spjd
1244182452Spjd	if (req->error == NULL || req->error[0] == '\0') {
1245182452Spjd		printf("Note, that the master key encrypted with old keys "
1246182452Spjd		    "and/or passphrase may still exists in a metadata backup "
1247182452Spjd		    "file.\n");
1248182452Spjd	}
1249148456Spjd}
1250148456Spjd
1251148456Spjdstatic void
1252148456Spjdeli_delkey_attached(struct gctl_req *req, const char *prov __unused)
1253148456Spjd{
1254148456Spjd
1255148456Spjd	gctl_issue(req);
1256148456Spjd}
1257148456Spjd
1258148456Spjdstatic void
1259148456Spjdeli_delkey_detached(struct gctl_req *req, const char *prov)
1260148456Spjd{
1261148456Spjd	struct g_eli_metadata md;
1262148456Spjd	unsigned char *mkeydst;
1263213172Spjd	unsigned int nkey;
1264153190Spjd	intmax_t val;
1265213172Spjd	bool all, force;
1266148456Spjd
1267148456Spjd	if (eli_metadata_read(req, prov, &md) == -1)
1268148456Spjd		return;
1269148456Spjd
1270153190Spjd	all = gctl_get_int(req, "all");
1271153190Spjd	if (all)
1272246620Spjd		arc4random_buf(md.md_mkeys, sizeof(md.md_mkeys));
1273148456Spjd	else {
1274153190Spjd		force = gctl_get_int(req, "force");
1275153190Spjd		val = gctl_get_intmax(req, "keyno");
1276153190Spjd		if (val == -1) {
1277148456Spjd			gctl_error(req, "Key number has to be specified.");
1278148456Spjd			return;
1279148456Spjd		}
1280153190Spjd		nkey = val;
1281148456Spjd		if (nkey >= G_ELI_MAXMKEYS) {
1282148456Spjd			gctl_error(req, "Invalid '%s' argument.", "keyno");
1283148456Spjd			return;
1284148456Spjd		}
1285153190Spjd		if (!(md.md_keys & (1 << nkey)) && !force) {
1286148456Spjd			gctl_error(req, "Master Key %u is not set.", nkey);
1287148456Spjd			return;
1288148456Spjd		}
1289148456Spjd		md.md_keys &= ~(1 << nkey);
1290153190Spjd		if (md.md_keys == 0 && !force) {
1291148456Spjd			gctl_error(req, "This is the last Master Key. Use '-f' "
1292148456Spjd			    "option if you really want to remove it.");
1293148456Spjd			return;
1294148456Spjd		}
1295148456Spjd		mkeydst = md.md_mkeys + nkey * G_ELI_MKEYLEN;
1296246620Spjd		arc4random_buf(mkeydst, G_ELI_MKEYLEN);
1297148456Spjd	}
1298148456Spjd
1299148456Spjd	eli_metadata_store(req, prov, &md);
1300148456Spjd	bzero(&md, sizeof(md));
1301148456Spjd}
1302148456Spjd
1303148456Spjdstatic void
1304148456Spjdeli_delkey(struct gctl_req *req)
1305148456Spjd{
1306148456Spjd	const char *prov;
1307153190Spjd	int nargs;
1308148456Spjd
1309153190Spjd	nargs = gctl_get_int(req, "nargs");
1310153190Spjd	if (nargs != 1) {
1311158214Spjd		gctl_error(req, "Invalid number of arguments.");
1312148456Spjd		return;
1313148456Spjd	}
1314153190Spjd	prov = gctl_get_ascii(req, "arg0");
1315148456Spjd
1316148456Spjd	if (eli_is_attached(prov))
1317148456Spjd		eli_delkey_attached(req, prov);
1318148456Spjd	else
1319148456Spjd		eli_delkey_detached(req, prov);
1320148456Spjd}
1321148456Spjd
1322214118Spjdstatic void
1323214118Spjdeli_resume(struct gctl_req *req)
1324214118Spjd{
1325214118Spjd	struct g_eli_metadata md;
1326214118Spjd	unsigned char key[G_ELI_USERKEYLEN];
1327214118Spjd	const char *prov;
1328214118Spjd	off_t mediasize;
1329214118Spjd	int nargs;
1330214118Spjd
1331214118Spjd	nargs = gctl_get_int(req, "nargs");
1332214118Spjd	if (nargs != 1) {
1333214118Spjd		gctl_error(req, "Invalid number of arguments.");
1334214118Spjd		return;
1335214118Spjd	}
1336214118Spjd	prov = gctl_get_ascii(req, "arg0");
1337214118Spjd
1338214118Spjd	if (eli_metadata_read(req, prov, &md) == -1)
1339214118Spjd		return;
1340214118Spjd
1341214118Spjd	mediasize = g_get_mediasize(prov);
1342214118Spjd	if (md.md_provsize != (uint64_t)mediasize) {
1343214118Spjd		gctl_error(req, "Provider size mismatch.");
1344214118Spjd		return;
1345214118Spjd	}
1346214118Spjd
1347214118Spjd	if (eli_genkey(req, &md, key, false) == NULL) {
1348214118Spjd		bzero(key, sizeof(key));
1349214118Spjd		return;
1350214118Spjd	}
1351214118Spjd
1352214118Spjd	gctl_ro_param(req, "key", sizeof(key), key);
1353214118Spjd	if (gctl_issue(req) == NULL) {
1354214118Spjd		if (verbose)
1355214118Spjd			printf("Resumed %s.\n", prov);
1356214118Spjd	}
1357214118Spjd	bzero(key, sizeof(key));
1358214118Spjd}
1359214118Spjd
1360213060Spjdstatic int
1361213060Spjdeli_trash_metadata(struct gctl_req *req, const char *prov, int fd, off_t offset)
1362213060Spjd{
1363213060Spjd	unsigned int overwrites;
1364213060Spjd	unsigned char *sector;
1365213060Spjd	ssize_t size;
1366213060Spjd	int error;
1367213060Spjd
1368213060Spjd	size = sizeof(overwrites);
1369213060Spjd	if (sysctlbyname("kern.geom.eli.overwrites", &overwrites, &size,
1370213060Spjd	    NULL, 0) == -1 || overwrites == 0) {
1371213060Spjd		overwrites = G_ELI_OVERWRITES;
1372213060Spjd	}
1373213060Spjd
1374213060Spjd	size = g_sectorsize(fd);
1375213060Spjd	if (size <= 0) {
1376213060Spjd		gctl_error(req, "Cannot obtain provider sector size %s: %s.",
1377213060Spjd		    prov, strerror(errno));
1378213060Spjd		return (-1);
1379213060Spjd	}
1380213060Spjd	sector = malloc(size);
1381213060Spjd	if (sector == NULL) {
1382213060Spjd		gctl_error(req, "Cannot allocate %zd bytes of memory.", size);
1383213060Spjd		return (-1);
1384213060Spjd	}
1385213060Spjd
1386213060Spjd	error = 0;
1387213060Spjd	do {
1388246620Spjd		arc4random_buf(sector, size);
1389213060Spjd		if (pwrite(fd, sector, size, offset) != size) {
1390213060Spjd			if (error == 0)
1391213060Spjd				error = errno;
1392213060Spjd		}
1393213060Spjd		(void)g_flush(fd);
1394213060Spjd	} while (--overwrites > 0);
1395246622Spjd	free(sector);
1396213060Spjd	if (error != 0) {
1397213060Spjd		gctl_error(req, "Cannot trash metadata on provider %s: %s.",
1398213060Spjd		    prov, strerror(error));
1399213060Spjd		return (-1);
1400213060Spjd	}
1401213060Spjd	return (0);
1402213060Spjd}
1403213060Spjd
1404148456Spjdstatic void
1405148456Spjdeli_kill_detached(struct gctl_req *req, const char *prov)
1406148456Spjd{
1407213060Spjd	off_t offset;
1408213060Spjd	int fd;
1409148456Spjd
1410148456Spjd	/*
1411148456Spjd	 * NOTE: Maybe we should verify if this is geli provider first,
1412148456Spjd	 *       but 'kill' command is quite critical so better don't waste
1413148456Spjd	 *       the time.
1414148456Spjd	 */
1415148456Spjd#if 0
1416148456Spjd	error = g_metadata_read(prov, (unsigned char *)&md, sizeof(md),
1417148456Spjd	    G_ELI_MAGIC);
1418148456Spjd	if (error != 0) {
1419148456Spjd		gctl_error(req, "Cannot read metadata from %s: %s.", prov,
1420148456Spjd		    strerror(error));
1421148456Spjd		return;
1422148456Spjd	}
1423148456Spjd#endif
1424148456Spjd
1425213060Spjd	fd = g_open(prov, 1);
1426213060Spjd	if (fd == -1) {
1427213060Spjd		gctl_error(req, "Cannot open provider %s: %s.", prov,
1428213060Spjd		    strerror(errno));
1429213060Spjd		return;
1430148456Spjd	}
1431213060Spjd	offset = g_mediasize(fd) - g_sectorsize(fd);
1432213060Spjd	if (offset <= 0) {
1433213060Spjd		gctl_error(req,
1434213060Spjd		    "Cannot obtain media size or sector size for provider %s: %s.",
1435213060Spjd		    prov, strerror(errno));
1436213060Spjd		(void)g_close(fd);
1437213060Spjd		return;
1438213060Spjd	}
1439213060Spjd	(void)eli_trash_metadata(req, prov, fd, offset);
1440213060Spjd	(void)g_close(fd);
1441148456Spjd}
1442148456Spjd
1443148456Spjdstatic void
1444148456Spjdeli_kill(struct gctl_req *req)
1445148456Spjd{
1446148456Spjd	const char *prov;
1447153190Spjd	int i, nargs, all;
1448148456Spjd
1449153190Spjd	nargs = gctl_get_int(req, "nargs");
1450153190Spjd	all = gctl_get_int(req, "all");
1451153190Spjd	if (!all && nargs == 0) {
1452148456Spjd		gctl_error(req, "Too few arguments.");
1453148456Spjd		return;
1454148456Spjd	}
1455148456Spjd	/*
1456148456Spjd	 * How '-a' option combine with a list of providers:
1457148456Spjd	 * Delete Master Keys from all attached providers:
1458148456Spjd	 * geli kill -a
1459169312Spjd	 * Delete Master Keys from all attached providers and from
1460148456Spjd	 * detached da0 and da1:
1461148456Spjd	 * geli kill -a da0 da1
1462148456Spjd	 * Delete Master Keys from (attached or detached) da0 and da1:
1463148456Spjd	 * geli kill da0 da1
1464148456Spjd	 */
1465148456Spjd
1466169312Spjd	/* First detached providers. */
1467153190Spjd	for (i = 0; i < nargs; i++) {
1468153190Spjd		prov = gctl_get_ascii(req, "arg%d", i);
1469148456Spjd		if (!eli_is_attached(prov))
1470148456Spjd			eli_kill_detached(req, prov);
1471148456Spjd	}
1472162347Spjd	/* Now attached providers. */
1473162347Spjd	gctl_issue(req);
1474148456Spjd}
1475148456Spjd
1476182452Spjdstatic int
1477182452Spjdeli_backup_create(struct gctl_req *req, const char *prov, const char *file)
1478148456Spjd{
1479148456Spjd	unsigned char *sector;
1480213059Spjd	ssize_t secsize;
1481226716Spjd	int error, filefd, ret;
1482148456Spjd
1483182452Spjd	ret = -1;
1484226716Spjd	filefd = -1;
1485148456Spjd	sector = NULL;
1486148456Spjd	secsize = 0;
1487148456Spjd
1488226716Spjd	secsize = g_get_sectorsize(prov);
1489226716Spjd	if (secsize == 0) {
1490148456Spjd		gctl_error(req, "Cannot get informations about %s: %s.", prov,
1491148456Spjd		    strerror(errno));
1492169193Spjd		goto out;
1493148456Spjd	}
1494148456Spjd	sector = malloc(secsize);
1495148456Spjd	if (sector == NULL) {
1496148456Spjd		gctl_error(req, "Cannot allocate memory.");
1497169193Spjd		goto out;
1498148456Spjd	}
1499148456Spjd	/* Read metadata from the provider. */
1500226716Spjd	error = g_metadata_read(prov, sector, secsize, G_ELI_MAGIC);
1501226716Spjd	if (error != 0) {
1502226716Spjd		gctl_error(req, "Unable to read metadata from %s: %s.", prov,
1503226716Spjd		    strerror(error));
1504148456Spjd		goto out;
1505148456Spjd	}
1506226716Spjd
1507226716Spjd	filefd = open(file, O_WRONLY | O_TRUNC | O_CREAT, 0600);
1508226716Spjd	if (filefd == -1) {
1509226716Spjd		gctl_error(req, "Unable to open %s: %s.", file,
1510226716Spjd		    strerror(errno));
1511148456Spjd		goto out;
1512148456Spjd	}
1513148456Spjd	/* Write metadata to the destination file. */
1514213059Spjd	if (write(filefd, sector, secsize) != secsize) {
1515226716Spjd		gctl_error(req, "Unable to write to %s: %s.", file,
1516148456Spjd		    strerror(errno));
1517226716Spjd		(void)close(filefd);
1518226716Spjd		(void)unlink(file);
1519148456Spjd		goto out;
1520148456Spjd	}
1521213059Spjd	(void)fsync(filefd);
1522226716Spjd	(void)close(filefd);
1523182452Spjd	/* Success. */
1524182452Spjd	ret = 0;
1525148456Spjdout:
1526148456Spjd	if (sector != NULL) {
1527148456Spjd		bzero(sector, secsize);
1528148456Spjd		free(sector);
1529148456Spjd	}
1530182452Spjd	return (ret);
1531148456Spjd}
1532148456Spjd
1533148456Spjdstatic void
1534182452Spjdeli_backup(struct gctl_req *req)
1535182452Spjd{
1536182452Spjd	const char *file, *prov;
1537182452Spjd	int nargs;
1538182452Spjd
1539182452Spjd	nargs = gctl_get_int(req, "nargs");
1540182452Spjd	if (nargs != 2) {
1541182452Spjd		gctl_error(req, "Invalid number of arguments.");
1542182452Spjd		return;
1543182452Spjd	}
1544182452Spjd	prov = gctl_get_ascii(req, "arg0");
1545182452Spjd	file = gctl_get_ascii(req, "arg1");
1546182452Spjd
1547182452Spjd	eli_backup_create(req, prov, file);
1548182452Spjd}
1549182452Spjd
1550182452Spjdstatic void
1551148456Spjdeli_restore(struct gctl_req *req)
1552148456Spjd{
1553148456Spjd	struct g_eli_metadata md;
1554148456Spjd	const char *file, *prov;
1555148456Spjd	off_t mediasize;
1556226716Spjd	int nargs;
1557148456Spjd
1558153190Spjd	nargs = gctl_get_int(req, "nargs");
1559153190Spjd	if (nargs != 2) {
1560148456Spjd		gctl_error(req, "Invalid number of arguments.");
1561148456Spjd		return;
1562148456Spjd	}
1563153190Spjd	file = gctl_get_ascii(req, "arg0");
1564153190Spjd	prov = gctl_get_ascii(req, "arg1");
1565148456Spjd
1566226716Spjd	/* Read metadata from the backup file. */
1567226716Spjd	if (eli_metadata_read(req, file, &md) == -1)
1568226716Spjd		return;
1569226716Spjd	/* Obtain provider's mediasize. */
1570226716Spjd	mediasize = g_get_mediasize(prov);
1571226716Spjd	if (mediasize == 0) {
1572148456Spjd		gctl_error(req, "Cannot get informations about %s: %s.", prov,
1573148456Spjd		    strerror(errno));
1574226716Spjd		return;
1575148456Spjd	}
1576212934Sbrian	/* Check if the provider size has changed since we did the backup. */
1577212934Sbrian	if (md.md_provsize != (uint64_t)mediasize) {
1578212934Sbrian		if (gctl_get_int(req, "force")) {
1579212934Sbrian			md.md_provsize = mediasize;
1580212934Sbrian		} else {
1581212934Sbrian			gctl_error(req, "Provider size mismatch: "
1582212934Sbrian			    "wrong backup file?");
1583226716Spjd			return;
1584212934Sbrian		}
1585212934Sbrian	}
1586226716Spjd	/* Write metadata to the provider. */
1587226716Spjd	(void)eli_metadata_store(req, prov, &md);
1588148456Spjd}
1589148456Spjd
1590148456Spjdstatic void
1591212934Sbrianeli_resize(struct gctl_req *req)
1592212934Sbrian{
1593212934Sbrian	struct g_eli_metadata md;
1594212934Sbrian	const char *prov;
1595212934Sbrian	unsigned char *sector;
1596213056Spjd	ssize_t secsize;
1597212934Sbrian	off_t mediasize, oldsize;
1598226722Spjd	int error, nargs, provfd;
1599212934Sbrian
1600212934Sbrian	nargs = gctl_get_int(req, "nargs");
1601212934Sbrian	if (nargs != 1) {
1602212934Sbrian		gctl_error(req, "Invalid number of arguments.");
1603212934Sbrian		return;
1604212934Sbrian	}
1605212934Sbrian	prov = gctl_get_ascii(req, "arg0");
1606212934Sbrian
1607212934Sbrian	provfd = -1;
1608212934Sbrian	sector = NULL;
1609212934Sbrian	secsize = 0;
1610212934Sbrian
1611213056Spjd	provfd = g_open(prov, 1);
1612212934Sbrian	if (provfd == -1) {
1613212934Sbrian		gctl_error(req, "Cannot open %s: %s.", prov, strerror(errno));
1614212934Sbrian		goto out;
1615212934Sbrian	}
1616212934Sbrian
1617213056Spjd	mediasize = g_mediasize(provfd);
1618213056Spjd	secsize = g_sectorsize(provfd);
1619213056Spjd	if (mediasize == -1 || secsize == -1) {
1620212934Sbrian		gctl_error(req, "Cannot get information about %s: %s.", prov,
1621212934Sbrian		    strerror(errno));
1622212934Sbrian		goto out;
1623212934Sbrian	}
1624212934Sbrian
1625212934Sbrian	sector = malloc(secsize);
1626212934Sbrian	if (sector == NULL) {
1627212934Sbrian		gctl_error(req, "Cannot allocate memory.");
1628212934Sbrian		goto out;
1629212934Sbrian	}
1630212934Sbrian
1631212934Sbrian	oldsize = gctl_get_intmax(req, "oldsize");
1632212934Sbrian	if (oldsize < 0 || oldsize > mediasize) {
1633212934Sbrian		gctl_error(req, "Invalid oldsize: Out of range.");
1634212934Sbrian		goto out;
1635212934Sbrian	}
1636213058Spjd	if (oldsize == mediasize) {
1637213058Spjd		gctl_error(req, "Size hasn't changed.");
1638213058Spjd		goto out;
1639213058Spjd	}
1640212934Sbrian
1641212934Sbrian	/* Read metadata from the 'oldsize' offset. */
1642213056Spjd	if (pread(provfd, sector, secsize, oldsize - secsize) != secsize) {
1643212934Sbrian		gctl_error(req, "Cannot read old metadata: %s.",
1644212934Sbrian		    strerror(errno));
1645212934Sbrian		goto out;
1646212934Sbrian	}
1647212934Sbrian
1648212934Sbrian	/* Check if this sector contains geli metadata. */
1649226722Spjd	error = eli_metadata_decode(sector, &md);
1650226722Spjd	switch (error) {
1651226722Spjd	case 0:
1652226722Spjd		break;
1653226722Spjd	case EOPNOTSUPP:
1654226722Spjd		gctl_error(req,
1655226722Spjd		    "Provider's %s metadata version %u is too new.\n"
1656226722Spjd		    "geli: The highest supported version is %u.",
1657226722Spjd		    prov, (unsigned int)md.md_version, G_ELI_VERSION);
1658212934Sbrian		goto out;
1659226722Spjd	case EINVAL:
1660226722Spjd		gctl_error(req, "Inconsistent provider's %s metadata.", prov);
1661226722Spjd		goto out;
1662226722Spjd	default:
1663226722Spjd		gctl_error(req,
1664226722Spjd		    "Unexpected error while decoding provider's %s metadata: %s.",
1665226722Spjd		    prov, strerror(error));
1666226722Spjd		goto out;
1667212934Sbrian	}
1668212934Sbrian
1669212934Sbrian	/*
1670212934Sbrian	 * If the old metadata doesn't have a correct provider size, refuse
1671212934Sbrian	 * to resize.
1672212934Sbrian	 */
1673212934Sbrian	if (md.md_provsize != (uint64_t)oldsize) {
1674212934Sbrian		gctl_error(req, "Provider size mismatch at oldsize.");
1675212934Sbrian		goto out;
1676212934Sbrian	}
1677212934Sbrian
1678212934Sbrian	/*
1679212934Sbrian	 * Update the old metadata with the current provider size and write
1680212934Sbrian	 * it back to the correct place on the provider.
1681212934Sbrian	 */
1682212934Sbrian	md.md_provsize = mediasize;
1683226720Spjd	/* Write metadata to the provider. */
1684226720Spjd	(void)eli_metadata_store(req, prov, &md);
1685212934Sbrian	/* Now trash the old metadata. */
1686226720Spjd	(void)eli_trash_metadata(req, prov, provfd, oldsize - secsize);
1687212934Sbrianout:
1688226720Spjd	if (provfd != -1)
1689213056Spjd		(void)g_close(provfd);
1690212934Sbrian	if (sector != NULL) {
1691212934Sbrian		bzero(sector, secsize);
1692212934Sbrian		free(sector);
1693212934Sbrian	}
1694212934Sbrian}
1695212934Sbrian
1696212934Sbrianstatic void
1697226723Spjdeli_version(struct gctl_req *req)
1698226723Spjd{
1699226723Spjd	struct g_eli_metadata md;
1700226723Spjd	const char *name;
1701226723Spjd	unsigned int version;
1702226723Spjd	int error, i, nargs;
1703226723Spjd
1704226723Spjd	nargs = gctl_get_int(req, "nargs");
1705226723Spjd
1706226723Spjd	if (nargs == 0) {
1707226723Spjd		unsigned int kernver;
1708226723Spjd		ssize_t size;
1709226723Spjd
1710226723Spjd		size = sizeof(kernver);
1711226723Spjd		if (sysctlbyname("kern.geom.eli.version", &kernver, &size,
1712226723Spjd		    NULL, 0) == -1) {
1713226723Spjd			warn("Unable to obtain GELI kernel version");
1714226723Spjd		} else {
1715226723Spjd			printf("kernel: %u\n", kernver);
1716226723Spjd		}
1717226723Spjd		printf("userland: %u\n", G_ELI_VERSION);
1718226723Spjd		return;
1719226723Spjd	}
1720226723Spjd
1721226723Spjd	for (i = 0; i < nargs; i++) {
1722226723Spjd		name = gctl_get_ascii(req, "arg%d", i);
1723226723Spjd		error = g_metadata_read(name, (unsigned char *)&md,
1724226723Spjd		    sizeof(md), G_ELI_MAGIC);
1725226723Spjd		if (error != 0) {
1726226723Spjd			warn("%s: Unable to read metadata: %s.", name,
1727226723Spjd			    strerror(error));
1728226723Spjd			gctl_error(req, "Not fully done.");
1729226723Spjd			continue;
1730226723Spjd		}
1731226723Spjd		version = le32dec(&md.md_version);
1732226723Spjd		printf("%s: %u\n", name, version);
1733226723Spjd	}
1734226723Spjd}
1735226723Spjd
1736226723Spjdstatic void
1737148456Spjdeli_clear(struct gctl_req *req)
1738148456Spjd{
1739148456Spjd	const char *name;
1740153190Spjd	int error, i, nargs;
1741148456Spjd
1742153190Spjd	nargs = gctl_get_int(req, "nargs");
1743153190Spjd	if (nargs < 1) {
1744148456Spjd		gctl_error(req, "Too few arguments.");
1745148456Spjd		return;
1746148456Spjd	}
1747148456Spjd
1748153190Spjd	for (i = 0; i < nargs; i++) {
1749153190Spjd		name = gctl_get_ascii(req, "arg%d", i);
1750148456Spjd		error = g_metadata_clear(name, G_ELI_MAGIC);
1751148456Spjd		if (error != 0) {
1752148456Spjd			fprintf(stderr, "Cannot clear metadata on %s: %s.\n",
1753148456Spjd			    name, strerror(error));
1754148456Spjd			gctl_error(req, "Not fully done.");
1755148456Spjd			continue;
1756148456Spjd		}
1757148456Spjd		if (verbose)
1758155175Spjd			printf("Metadata cleared on %s.\n", name);
1759148456Spjd	}
1760148456Spjd}
1761148456Spjd
1762148456Spjdstatic void
1763148456Spjdeli_dump(struct gctl_req *req)
1764148456Spjd{
1765226719Spjd	struct g_eli_metadata md;
1766148456Spjd	const char *name;
1767226719Spjd	int i, nargs;
1768148456Spjd
1769153190Spjd	nargs = gctl_get_int(req, "nargs");
1770153190Spjd	if (nargs < 1) {
1771148456Spjd		gctl_error(req, "Too few arguments.");
1772148456Spjd		return;
1773148456Spjd	}
1774148456Spjd
1775153190Spjd	for (i = 0; i < nargs; i++) {
1776153190Spjd		name = gctl_get_ascii(req, "arg%d", i);
1777226719Spjd		if (eli_metadata_read(NULL, name, &md) == -1) {
1778148456Spjd			gctl_error(req, "Not fully done.");
1779148456Spjd			continue;
1780148456Spjd		}
1781148456Spjd		printf("Metadata on %s:\n", name);
1782148456Spjd		eli_metadata_dump(&md);
1783148456Spjd		printf("\n");
1784148456Spjd	}
1785148456Spjd}
1786