gelidev.c revision 344399
1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2018 Ian Lepore <ian@FreeBSD.org>
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * $FreeBSD: stable/11/stand/libsa/geli/gelidev.c 344399 2019-02-20 23:55:35Z kevans $
28 */
29
30#include <stand.h>
31#include <stdarg.h>
32#include <uuid.h>
33#include <sys/disk.h>
34#include "disk.h"
35#include "geliboot.h"
36#include "geliboot_internal.h"
37
38static int  geli_dev_init(void);
39static int  geli_dev_strategy(void *, int, daddr_t, size_t, char *, size_t *);
40static int  geli_dev_open(struct open_file *f, ...);
41static int  geli_dev_close(struct open_file *f);
42static int  geli_dev_ioctl(struct open_file *, u_long, void *);
43static int  geli_dev_print(int);
44static void geli_dev_cleanup(void);
45
46/*
47 * geli_devsw is static because it never appears in any arch's global devsw
48 * array.  Instead, when devopen() opens a DEVT_DISK device, it then calls
49 * geli_probe_and_attach(), and if we find that the disk_devdesc describes a
50 * geli-encrypted partition, we create a geli_devdesc which references this
51 * devsw and has a pointer to the original disk_devdesc of the underlying host
52 * disk. Then we manipulate the open_file struct to reference the new
53 * geli_devdesc, effectively routing all IO operations through our code.
54 */
55static struct devsw geli_devsw = {
56	.dv_name     = "gelidisk",
57	.dv_type     = DEVT_DISK,
58	.dv_init     = geli_dev_init,
59	.dv_strategy = geli_dev_strategy,
60	.dv_open     = geli_dev_open,
61	.dv_close    = geli_dev_close,
62	.dv_ioctl    = geli_dev_ioctl,
63	.dv_print    = geli_dev_print,
64	.dv_cleanup  = geli_dev_cleanup,
65};
66
67/*
68 * geli_devdesc instances replace the disk_devdesc in an open_file struct when
69 * the partition is encrypted.  We keep a reference to the original host
70 * disk_devdesc so that we can read the raw encrypted data using it.
71 */
72struct geli_devdesc {
73	struct disk_devdesc	ddd;	/* Must be first. */
74	struct disk_devdesc	*hdesc;	/* disk/slice/part hosting geli vol */
75	struct geli_dev		*gdev;	/* geli_dev entry */
76};
77
78
79/*
80 * A geli_readfunc that reads via a disk_devdesc passed in readpriv. This is
81 * used to read the underlying host disk data when probing/tasting to see if the
82 * host provider is geli-encrypted.
83 */
84static int
85diskdev_read(void *vdev, void *readpriv, off_t offbytes,
86    void *buf, size_t sizebytes)
87{
88	struct disk_devdesc *ddev;
89
90	ddev = (struct disk_devdesc *)readpriv;
91
92	return (ddev->dd.d_dev->dv_strategy(ddev, F_READ, offbytes / DEV_BSIZE,
93	    sizebytes, buf, NULL));
94}
95
96static int
97geli_dev_init(void)
98{
99
100	/*
101	 * Since geli_devsw never gets referenced in any arch's global devsw
102	 * table, this function should never get called.
103	 */
104	panic("%s: should never be called", __func__);
105	return (ENXIO);
106}
107
108static int
109geli_dev_strategy(void *devdata, int rw, daddr_t blk, size_t size, char *buf,
110    size_t *rsize)
111{
112	struct geli_devdesc *gdesc;
113	off_t alnend, alnstart, reqend, reqstart;
114	size_t alnsize;
115	char *iobuf;
116	int rc;
117
118	/* We only handle reading; no write support. */
119	if ((rw & F_MASK) != F_READ)
120		return (EOPNOTSUPP);
121
122	gdesc = (struct geli_devdesc *)devdata;
123
124	/*
125	 * We can only decrypt full geli blocks.  The blk arg is expressed in
126	 * units of DEV_BSIZE blocks, while size is in bytes.  Convert
127	 * everything to bytes, and calculate the geli-blocksize-aligned start
128	 * and end points.
129	 *
130	 * Note: md_sectorsize must be cast to a signed type for the round2
131	 * macros to work correctly (otherwise they get zero-extended to 64 bits
132	 * and mask off the high order 32 bits of the requested start/end).
133	 */
134
135	reqstart = blk * DEV_BSIZE;
136	reqend   = reqstart + size;
137	alnstart = rounddown2(reqstart, (int)gdesc->gdev->md.md_sectorsize);
138	alnend   = roundup2(reqend, (int)gdesc->gdev->md.md_sectorsize);
139	alnsize  = alnend - alnstart;
140
141	/*
142	 * If alignment requires us to read more than the size of the provided
143	 * buffer, allocate a temporary buffer.
144	 */
145	if (alnsize <= size)
146		iobuf = buf;
147	else if ((iobuf = malloc(alnsize)) == NULL)
148		return (ENOMEM);
149
150	/*
151	 * Read the encrypted data using the host provider, then decrypt it.
152	 */
153	rc = gdesc->hdesc->dd.d_dev->dv_strategy(gdesc->hdesc, rw,
154	    alnstart / DEV_BSIZE, alnsize, iobuf, NULL);
155	if (rc != 0)
156		goto out;
157	rc = geli_read(gdesc->gdev, alnstart, iobuf, alnsize);
158	if (rc != 0)
159		goto out;
160
161	/*
162	 * If we had to use a temporary buffer, copy the requested part of the
163	 * data to the caller's buffer.
164	 */
165	if (iobuf != buf)
166		memcpy(buf, iobuf + (reqstart - alnstart), size);
167
168	if (rsize != NULL)
169		*rsize = size;
170out:
171	if (iobuf != buf)
172		free(iobuf);
173
174	return (rc);
175}
176
177static int
178geli_dev_open(struct open_file *f, ...)
179{
180
181	/*
182	 * Since geli_devsw never gets referenced in any arch's global devsw
183	 * table, this function should never get called.
184	 */
185	panic("%s: should never be called", __func__);
186	return (ENXIO);
187}
188
189static int
190geli_dev_close(struct open_file *f)
191{
192	struct geli_devdesc *gdesc;
193
194	/*
195	 * Detach the geli_devdesc from the open_file and reattach the
196	 * underlying host provider's disk_devdesc; this undoes the work done at
197	 * the end of geli_probe_and_attach().  Call the host provider's
198	 * dv_close() (because that's what our caller thought it was doing).
199	 */
200	gdesc = (struct geli_devdesc *)f->f_devdata;
201	f->f_devdata = gdesc->hdesc;
202	f->f_dev = gdesc->hdesc->dd.d_dev;
203	free(gdesc);
204	f->f_dev->dv_close(f);
205	return (0);
206}
207
208static int
209geli_dev_ioctl(struct open_file *f, u_long cmd, void *data)
210{
211	struct geli_devdesc *gdesc;
212	struct g_eli_metadata *md;
213
214	gdesc = (struct geli_devdesc *)f->f_devdata;
215	md = &gdesc->gdev->md;
216
217	switch (cmd) {
218	case DIOCGSECTORSIZE:
219		*(u_int *)data = md->md_sectorsize;
220		break;
221	case DIOCGMEDIASIZE:
222		*(uint64_t *)data = md->md_sectorsize * md->md_provsize;
223		break;
224	default:
225		return (ENOTTY);
226	}
227
228	return (0);
229}
230
231static int
232geli_dev_print(int verbose)
233{
234
235	/*
236	 * Since geli_devsw never gets referenced in any arch's global devsw
237	 * table, this function should never get called.
238	 */
239	panic("%s: should never be called", __func__);
240	return (ENXIO);
241}
242
243static void
244geli_dev_cleanup(void)
245{
246
247	/*
248	 * Since geli_devsw never gets referenced in any arch's global devsw
249	 * table, this function should never get called.
250	 */
251	panic("%s: should never be called", __func__);
252}
253
254
255/*
256 * geli_probe_and_attach() is called from devopen() after it successfully calls
257 * the dv_open() method of a DEVT_DISK device.  We taste the partition described
258 * by the disk_devdesc, and if it's geli-encrypted and we can decrypt it, we
259 * create a geli_devdesc and store it into the open_file struct in place of the
260 * underlying provider's disk_devdesc, effectively attaching our code to all IO
261 * processing for the partition.  Not quite the elegant stacking provided by
262 * geom in the kernel, but it gets the job done.
263 */
264void
265geli_probe_and_attach(struct open_file *f)
266{
267	static char gelipw[GELI_PW_MAXLEN];
268	const char *envpw;
269	struct geli_dev *gdev;
270	struct geli_devdesc *gdesc;
271	struct disk_devdesc *hdesc;
272	uint64_t hmediasize;
273	daddr_t hlastblk;
274	int rc;
275
276	hdesc = (struct disk_devdesc *)(f->f_devdata);
277
278	/* Get the last block number for the host provider. */
279	hdesc->dd.d_dev->dv_ioctl(f, DIOCGMEDIASIZE, &hmediasize);
280	hlastblk = (hmediasize / DEV_BSIZE) - 1;
281
282	/* Taste the host provider.  If it's not geli-encrypted just return. */
283	gdev = geli_taste(diskdev_read, hdesc, hlastblk, disk_fmtdev(hdesc));
284	if (gdev == NULL)
285		return;
286
287	/*
288	 * It's geli, try to decrypt it with existing keys, or prompt for a
289	 * passphrase if we don't yet have a cached key for it.
290	 */
291	if ((rc = geli_havekey(gdev)) != 0) {
292		envpw = getenv("kern.geom.eli.passphrase");
293		if (envpw != NULL) {
294			/* Use the cached passphrase */
295			bcopy(envpw, &gelipw, GELI_PW_MAXLEN);
296		}
297		if ((rc = geli_passphrase(gdev, gelipw)) == 0) {
298			/* Passphrase is good, cache it. */
299			setenv("kern.geom.eli.passphrase", gelipw, 1);
300		}
301		explicit_bzero(gelipw, sizeof(gelipw));
302		if (rc != 0)
303			return;
304	}
305
306	/*
307	 * It's geli-encrypted and we can decrypt it.  Create a geli_devdesc,
308	 * store a reference to the underlying provider's disk_devdesc in it,
309	 * then attach it to the openfile struct in place of the host provider.
310	 */
311	if ((gdesc = malloc(sizeof(*gdesc))) == NULL)
312		return;
313	gdesc->ddd.dd.d_dev = &geli_devsw;
314	gdesc->ddd.dd.d_opendata = NULL;
315	gdesc->ddd.dd.d_unit = hdesc->dd.d_unit;
316	gdesc->ddd.d_offset = hdesc->d_offset;
317	gdesc->ddd.d_partition = hdesc->d_partition;
318	gdesc->ddd.d_slice = hdesc->d_slice;
319	gdesc->hdesc = hdesc;
320	gdesc->gdev = gdev;
321	f->f_dev = gdesc->ddd.dd.d_dev;
322	f->f_devdata = gdesc;
323}
324