1336252Sian/*-
2336252Sian * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3336252Sian *
4336252Sian * Copyright (c) 2018 Ian Lepore <ian@FreeBSD.org>
5336252Sian *
6336252Sian * Redistribution and use in source and binary forms, with or without
7336252Sian * modification, are permitted provided that the following conditions
8336252Sian * are met:
9336252Sian * 1. Redistributions of source code must retain the above copyright
10336252Sian *    notice, this list of conditions and the following disclaimer.
11336252Sian * 2. Redistributions in binary form must reproduce the above copyright
12336252Sian *    notice, this list of conditions and the following disclaimer in the
13336252Sian *    documentation and/or other materials provided with the distribution.
14336252Sian *
15336252Sian * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
16336252Sian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17336252Sian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18336252Sian * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
19336252Sian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20336252Sian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21336252Sian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22336252Sian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23336252Sian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24336252Sian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25336252Sian * SUCH DAMAGE.
26336252Sian *
27336252Sian * $FreeBSD: stable/11/stand/libsa/geli/gelidev.c 344399 2019-02-20 23:55:35Z kevans $
28336252Sian */
29336252Sian
30336252Sian#include <stand.h>
31336252Sian#include <stdarg.h>
32336252Sian#include <uuid.h>
33336252Sian#include <sys/disk.h>
34336252Sian#include "disk.h"
35336252Sian#include "geliboot.h"
36336252Sian#include "geliboot_internal.h"
37336252Sian
38336252Sianstatic int  geli_dev_init(void);
39336252Sianstatic int  geli_dev_strategy(void *, int, daddr_t, size_t, char *, size_t *);
40336252Sianstatic int  geli_dev_open(struct open_file *f, ...);
41336252Sianstatic int  geli_dev_close(struct open_file *f);
42336252Sianstatic int  geli_dev_ioctl(struct open_file *, u_long, void *);
43336252Sianstatic int  geli_dev_print(int);
44336252Sianstatic void geli_dev_cleanup(void);
45336252Sian
46336252Sian/*
47336252Sian * geli_devsw is static because it never appears in any arch's global devsw
48336252Sian * array.  Instead, when devopen() opens a DEVT_DISK device, it then calls
49336252Sian * geli_probe_and_attach(), and if we find that the disk_devdesc describes a
50336252Sian * geli-encrypted partition, we create a geli_devdesc which references this
51336252Sian * devsw and has a pointer to the original disk_devdesc of the underlying host
52336252Sian * disk. Then we manipulate the open_file struct to reference the new
53336252Sian * geli_devdesc, effectively routing all IO operations through our code.
54336252Sian */
55336252Sianstatic struct devsw geli_devsw = {
56336252Sian	.dv_name     = "gelidisk",
57336252Sian	.dv_type     = DEVT_DISK,
58336252Sian	.dv_init     = geli_dev_init,
59336252Sian	.dv_strategy = geli_dev_strategy,
60336252Sian	.dv_open     = geli_dev_open,
61336252Sian	.dv_close    = geli_dev_close,
62336252Sian	.dv_ioctl    = geli_dev_ioctl,
63336252Sian	.dv_print    = geli_dev_print,
64336252Sian	.dv_cleanup  = geli_dev_cleanup,
65336252Sian};
66336252Sian
67336252Sian/*
68336252Sian * geli_devdesc instances replace the disk_devdesc in an open_file struct when
69336252Sian * the partition is encrypted.  We keep a reference to the original host
70336252Sian * disk_devdesc so that we can read the raw encrypted data using it.
71336252Sian */
72336252Sianstruct geli_devdesc {
73336252Sian	struct disk_devdesc	ddd;	/* Must be first. */
74336252Sian	struct disk_devdesc	*hdesc;	/* disk/slice/part hosting geli vol */
75336252Sian	struct geli_dev		*gdev;	/* geli_dev entry */
76336252Sian};
77336252Sian
78336252Sian
79336252Sian/*
80336252Sian * A geli_readfunc that reads via a disk_devdesc passed in readpriv. This is
81336252Sian * used to read the underlying host disk data when probing/tasting to see if the
82336252Sian * host provider is geli-encrypted.
83336252Sian */
84336252Sianstatic int
85336252Siandiskdev_read(void *vdev, void *readpriv, off_t offbytes,
86336252Sian    void *buf, size_t sizebytes)
87336252Sian{
88336252Sian	struct disk_devdesc *ddev;
89336252Sian
90336252Sian	ddev = (struct disk_devdesc *)readpriv;
91336252Sian
92336252Sian	return (ddev->dd.d_dev->dv_strategy(ddev, F_READ, offbytes / DEV_BSIZE,
93336252Sian	    sizebytes, buf, NULL));
94336252Sian}
95336252Sian
96336252Sianstatic int
97336252Siangeli_dev_init(void)
98336252Sian{
99336252Sian
100336252Sian	/*
101336252Sian	 * Since geli_devsw never gets referenced in any arch's global devsw
102336252Sian	 * table, this function should never get called.
103336252Sian	 */
104336252Sian	panic("%s: should never be called", __func__);
105336252Sian	return (ENXIO);
106336252Sian}
107336252Sian
108336252Sianstatic int
109336252Siangeli_dev_strategy(void *devdata, int rw, daddr_t blk, size_t size, char *buf,
110336252Sian    size_t *rsize)
111336252Sian{
112336252Sian	struct geli_devdesc *gdesc;
113336252Sian	off_t alnend, alnstart, reqend, reqstart;
114336252Sian	size_t alnsize;
115336252Sian	char *iobuf;
116336252Sian	int rc;
117336252Sian
118336252Sian	/* We only handle reading; no write support. */
119336252Sian	if ((rw & F_MASK) != F_READ)
120336252Sian		return (EOPNOTSUPP);
121336252Sian
122336252Sian	gdesc = (struct geli_devdesc *)devdata;
123336252Sian
124336252Sian	/*
125336252Sian	 * We can only decrypt full geli blocks.  The blk arg is expressed in
126336252Sian	 * units of DEV_BSIZE blocks, while size is in bytes.  Convert
127336252Sian	 * everything to bytes, and calculate the geli-blocksize-aligned start
128336252Sian	 * and end points.
129336252Sian	 *
130336252Sian	 * Note: md_sectorsize must be cast to a signed type for the round2
131336252Sian	 * macros to work correctly (otherwise they get zero-extended to 64 bits
132336252Sian	 * and mask off the high order 32 bits of the requested start/end).
133336252Sian	 */
134336252Sian
135336252Sian	reqstart = blk * DEV_BSIZE;
136336252Sian	reqend   = reqstart + size;
137336252Sian	alnstart = rounddown2(reqstart, (int)gdesc->gdev->md.md_sectorsize);
138336252Sian	alnend   = roundup2(reqend, (int)gdesc->gdev->md.md_sectorsize);
139336252Sian	alnsize  = alnend - alnstart;
140336252Sian
141336252Sian	/*
142336252Sian	 * If alignment requires us to read more than the size of the provided
143336252Sian	 * buffer, allocate a temporary buffer.
144336252Sian	 */
145336252Sian	if (alnsize <= size)
146336252Sian		iobuf = buf;
147336252Sian	else if ((iobuf = malloc(alnsize)) == NULL)
148336252Sian		return (ENOMEM);
149336252Sian
150336252Sian	/*
151336252Sian	 * Read the encrypted data using the host provider, then decrypt it.
152336252Sian	 */
153336252Sian	rc = gdesc->hdesc->dd.d_dev->dv_strategy(gdesc->hdesc, rw,
154336252Sian	    alnstart / DEV_BSIZE, alnsize, iobuf, NULL);
155336252Sian	if (rc != 0)
156336252Sian		goto out;
157336252Sian	rc = geli_read(gdesc->gdev, alnstart, iobuf, alnsize);
158336252Sian	if (rc != 0)
159336252Sian		goto out;
160336252Sian
161336252Sian	/*
162336252Sian	 * If we had to use a temporary buffer, copy the requested part of the
163336252Sian	 * data to the caller's buffer.
164336252Sian	 */
165336252Sian	if (iobuf != buf)
166336252Sian		memcpy(buf, iobuf + (reqstart - alnstart), size);
167336252Sian
168336252Sian	if (rsize != NULL)
169336252Sian		*rsize = size;
170336252Sianout:
171336252Sian	if (iobuf != buf)
172336252Sian		free(iobuf);
173336252Sian
174336252Sian	return (rc);
175336252Sian}
176336252Sian
177336252Sianstatic int
178336252Siangeli_dev_open(struct open_file *f, ...)
179336252Sian{
180336252Sian
181336252Sian	/*
182336252Sian	 * Since geli_devsw never gets referenced in any arch's global devsw
183336252Sian	 * table, this function should never get called.
184336252Sian	 */
185336252Sian	panic("%s: should never be called", __func__);
186336252Sian	return (ENXIO);
187336252Sian}
188336252Sian
189336252Sianstatic int
190336252Siangeli_dev_close(struct open_file *f)
191336252Sian{
192336252Sian	struct geli_devdesc *gdesc;
193336252Sian
194336252Sian	/*
195336252Sian	 * Detach the geli_devdesc from the open_file and reattach the
196336252Sian	 * underlying host provider's disk_devdesc; this undoes the work done at
197336252Sian	 * the end of geli_probe_and_attach().  Call the host provider's
198336252Sian	 * dv_close() (because that's what our caller thought it was doing).
199336252Sian	 */
200336252Sian	gdesc = (struct geli_devdesc *)f->f_devdata;
201336252Sian	f->f_devdata = gdesc->hdesc;
202336252Sian	f->f_dev = gdesc->hdesc->dd.d_dev;
203336252Sian	free(gdesc);
204336252Sian	f->f_dev->dv_close(f);
205336252Sian	return (0);
206336252Sian}
207336252Sian
208336252Sianstatic int
209336252Siangeli_dev_ioctl(struct open_file *f, u_long cmd, void *data)
210336252Sian{
211336252Sian	struct geli_devdesc *gdesc;
212336252Sian	struct g_eli_metadata *md;
213336252Sian
214336252Sian	gdesc = (struct geli_devdesc *)f->f_devdata;
215336252Sian	md = &gdesc->gdev->md;
216336252Sian
217336252Sian	switch (cmd) {
218336252Sian	case DIOCGSECTORSIZE:
219336252Sian		*(u_int *)data = md->md_sectorsize;
220336252Sian		break;
221336252Sian	case DIOCGMEDIASIZE:
222336252Sian		*(uint64_t *)data = md->md_sectorsize * md->md_provsize;
223336252Sian		break;
224336252Sian	default:
225336252Sian		return (ENOTTY);
226336252Sian	}
227336252Sian
228336252Sian	return (0);
229336252Sian}
230336252Sian
231336252Sianstatic int
232336252Siangeli_dev_print(int verbose)
233336252Sian{
234336252Sian
235336252Sian	/*
236336252Sian	 * Since geli_devsw never gets referenced in any arch's global devsw
237336252Sian	 * table, this function should never get called.
238336252Sian	 */
239336252Sian	panic("%s: should never be called", __func__);
240336252Sian	return (ENXIO);
241336252Sian}
242336252Sian
243336252Sianstatic void
244336252Siangeli_dev_cleanup(void)
245336252Sian{
246336252Sian
247336252Sian	/*
248336252Sian	 * Since geli_devsw never gets referenced in any arch's global devsw
249336252Sian	 * table, this function should never get called.
250336252Sian	 */
251336252Sian	panic("%s: should never be called", __func__);
252336252Sian}
253336252Sian
254336252Sian
255336252Sian/*
256336252Sian * geli_probe_and_attach() is called from devopen() after it successfully calls
257336252Sian * the dv_open() method of a DEVT_DISK device.  We taste the partition described
258336252Sian * by the disk_devdesc, and if it's geli-encrypted and we can decrypt it, we
259336252Sian * create a geli_devdesc and store it into the open_file struct in place of the
260336252Sian * underlying provider's disk_devdesc, effectively attaching our code to all IO
261336252Sian * processing for the partition.  Not quite the elegant stacking provided by
262336252Sian * geom in the kernel, but it gets the job done.
263336252Sian */
264336252Sianvoid
265336252Siangeli_probe_and_attach(struct open_file *f)
266336252Sian{
267336252Sian	static char gelipw[GELI_PW_MAXLEN];
268336252Sian	const char *envpw;
269336252Sian	struct geli_dev *gdev;
270336252Sian	struct geli_devdesc *gdesc;
271336252Sian	struct disk_devdesc *hdesc;
272336252Sian	uint64_t hmediasize;
273336252Sian	daddr_t hlastblk;
274336252Sian	int rc;
275336252Sian
276336252Sian	hdesc = (struct disk_devdesc *)(f->f_devdata);
277336252Sian
278336252Sian	/* Get the last block number for the host provider. */
279336252Sian	hdesc->dd.d_dev->dv_ioctl(f, DIOCGMEDIASIZE, &hmediasize);
280336252Sian	hlastblk = (hmediasize / DEV_BSIZE) - 1;
281336252Sian
282336252Sian	/* Taste the host provider.  If it's not geli-encrypted just return. */
283336252Sian	gdev = geli_taste(diskdev_read, hdesc, hlastblk, disk_fmtdev(hdesc));
284336252Sian	if (gdev == NULL)
285336252Sian		return;
286336252Sian
287336252Sian	/*
288336252Sian	 * It's geli, try to decrypt it with existing keys, or prompt for a
289336252Sian	 * passphrase if we don't yet have a cached key for it.
290336252Sian	 */
291336252Sian	if ((rc = geli_havekey(gdev)) != 0) {
292336252Sian		envpw = getenv("kern.geom.eli.passphrase");
293336252Sian		if (envpw != NULL) {
294336252Sian			/* Use the cached passphrase */
295336252Sian			bcopy(envpw, &gelipw, GELI_PW_MAXLEN);
296336252Sian		}
297336252Sian		if ((rc = geli_passphrase(gdev, gelipw)) == 0) {
298336252Sian			/* Passphrase is good, cache it. */
299336252Sian			setenv("kern.geom.eli.passphrase", gelipw, 1);
300336252Sian		}
301336252Sian		explicit_bzero(gelipw, sizeof(gelipw));
302336252Sian		if (rc != 0)
303336252Sian			return;
304336252Sian	}
305336252Sian
306336252Sian	/*
307336252Sian	 * It's geli-encrypted and we can decrypt it.  Create a geli_devdesc,
308336252Sian	 * store a reference to the underlying provider's disk_devdesc in it,
309336252Sian	 * then attach it to the openfile struct in place of the host provider.
310336252Sian	 */
311336252Sian	if ((gdesc = malloc(sizeof(*gdesc))) == NULL)
312336252Sian		return;
313336252Sian	gdesc->ddd.dd.d_dev = &geli_devsw;
314336252Sian	gdesc->ddd.dd.d_opendata = NULL;
315336252Sian	gdesc->ddd.dd.d_unit = hdesc->dd.d_unit;
316336252Sian	gdesc->ddd.d_offset = hdesc->d_offset;
317336252Sian	gdesc->ddd.d_partition = hdesc->d_partition;
318336252Sian	gdesc->ddd.d_slice = hdesc->d_slice;
319336252Sian	gdesc->hdesc = hdesc;
320336252Sian	gdesc->gdev = gdev;
321336252Sian	f->f_dev = gdesc->ddd.dd.d_dev;
322336252Sian	f->f_devdata = gdesc;
323336252Sian}
324