2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2018 Ian Lepore <ian@FreeBSD.org>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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
36 #include "geliboot_internal.h"
38 static int geli_dev_init(void);
39 static int geli_dev_strategy(void *, int, daddr_t, size_t, char *, size_t *);
40 static int geli_dev_open(struct open_file *f, ...);
41 static int geli_dev_close(struct open_file *f);
42 static int geli_dev_ioctl(struct open_file *, u_long, void *);
43 static int geli_dev_print(int);
44 static void geli_dev_cleanup(void);
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.
55 static struct devsw geli_devsw = {
56 .dv_name = "gelidisk",
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,
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.
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 */
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.
85 diskdev_read(void *vdev, void *readpriv, off_t offbytes,
86 void *buf, size_t sizebytes)
88 struct disk_devdesc *ddev;
90 ddev = (struct disk_devdesc *)readpriv;
92 return (ddev->dd.d_dev->dv_strategy(ddev, F_READ, offbytes / DEV_BSIZE,
93 sizebytes, buf, NULL));
101 * Since geli_devsw never gets referenced in any arch's global devsw
102 * table, this function should never get called.
104 panic("%s: should never be called", __func__);
109 geli_dev_strategy(void *devdata, int rw, daddr_t blk, size_t size, char *buf,
112 struct geli_devdesc *gdesc;
113 off_t alnend, alnstart, reqend, reqstart;
118 /* We only handle reading; no write support. */
119 if ((rw & F_MASK) != F_READ)
122 gdesc = (struct geli_devdesc *)devdata;
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
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).
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;
142 * If alignment requires us to read more than the size of the provided
143 * buffer, allocate a temporary buffer.
147 else if ((iobuf = malloc(alnsize)) == NULL)
151 * Read the encrypted data using the host provider, then decrypt it.
153 rc = gdesc->hdesc->dd.d_dev->dv_strategy(gdesc->hdesc, rw,
154 alnstart / DEV_BSIZE, alnsize, iobuf, NULL);
157 rc = geli_read(gdesc->gdev, alnstart, iobuf, alnsize);
162 * If we had to use a temporary buffer, copy the requested part of the
163 * data to the caller's buffer.
166 memcpy(buf, iobuf + (reqstart - alnstart), size);
178 geli_dev_open(struct open_file *f, ...)
182 * Since geli_devsw never gets referenced in any arch's global devsw
183 * table, this function should never get called.
185 panic("%s: should never be called", __func__);
190 geli_dev_close(struct open_file *f)
192 struct geli_devdesc *gdesc;
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).
200 gdesc = (struct geli_devdesc *)f->f_devdata;
201 f->f_devdata = gdesc->hdesc;
202 f->f_dev = gdesc->hdesc->dd.d_dev;
204 f->f_dev->dv_close(f);
209 geli_dev_ioctl(struct open_file *f, u_long cmd, void *data)
211 struct geli_devdesc *gdesc;
212 struct g_eli_metadata *md;
214 gdesc = (struct geli_devdesc *)f->f_devdata;
215 md = &gdesc->gdev->md;
218 case DIOCGSECTORSIZE:
219 *(u_int *)data = md->md_sectorsize;
222 *(uint64_t *)data = md->md_sectorsize * md->md_provsize;
232 geli_dev_print(int verbose)
236 * Since geli_devsw never gets referenced in any arch's global devsw
237 * table, this function should never get called.
239 panic("%s: should never be called", __func__);
244 geli_dev_cleanup(void)
248 * Since geli_devsw never gets referenced in any arch's global devsw
249 * table, this function should never get called.
251 panic("%s: should never be called", __func__);
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.
265 geli_probe_and_attach(struct open_file *f)
267 static char gelipw[GELI_PW_MAXLEN];
269 struct geli_dev *gdev;
270 struct geli_devdesc *gdesc;
271 struct disk_devdesc *hdesc;
276 hdesc = (struct disk_devdesc *)(f->f_devdata);
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;
282 /* Taste the host provider. If it's not geli-encrypted just return. */
283 gdev = geli_taste(diskdev_read, hdesc, hlastblk, disk_fmtdev(hdesc));
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.
291 if ((rc = geli_havekey(gdev)) != 0) {
292 envpw = getenv("kern.geom.eli.passphrase");
294 /* Use the cached passphrase */
295 bcopy(envpw, &gelipw, GELI_PW_MAXLEN);
297 if ((rc = geli_passphrase(gdev, gelipw)) == 0) {
298 /* Passphrase is good, cache it. */
299 setenv("kern.geom.eli.passphrase", gelipw, 1);
301 explicit_bzero(gelipw, sizeof(gelipw));
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.
311 if ((gdesc = malloc(sizeof(*gdesc))) == NULL)
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;
321 f->f_dev = gdesc->ddd.dd.d_dev;
322 f->f_devdata = gdesc;