xref: /freebsd/stand/libsa/geli/gelidev.c (revision 9f23cbd6cae82fd77edfad7173432fa8dccd0a95)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
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$
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 
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);
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  */
55 static struct devsw geli_devsw = {
56 	.dv_name     = "disk",
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 	.dv_fmtdev   = disk_fmtdev,
66 	.dv_parsedev = disk_parsedev,
67 };
68 
69 /*
70  * geli_devdesc instances replace the disk_devdesc in an open_file struct when
71  * the partition is encrypted.  We keep a reference to the original host
72  * disk_devdesc so that we can read the raw encrypted data using it.
73  */
74 struct geli_devdesc {
75 	struct disk_devdesc	ddd;	/* Must be first. */
76 	struct disk_devdesc	*hdesc;	/* disk/slice/part hosting geli vol */
77 	struct geli_dev		*gdev;	/* geli_dev entry */
78 };
79 
80 
81 /*
82  * A geli_readfunc that reads via a disk_devdesc passed in readpriv. This is
83  * used to read the underlying host disk data when probing/tasting to see if the
84  * host provider is geli-encrypted.
85  */
86 static int
87 diskdev_read(void *vdev, void *readpriv, off_t offbytes,
88     void *buf, size_t sizebytes)
89 {
90 	struct disk_devdesc *ddev;
91 
92 	ddev = (struct disk_devdesc *)readpriv;
93 
94 	return (ddev->dd.d_dev->dv_strategy(ddev, F_READ, offbytes / DEV_BSIZE,
95 	    sizebytes, buf, NULL));
96 }
97 
98 static int
99 geli_dev_init(void)
100 {
101 
102 	/*
103 	 * Since geli_devsw never gets referenced in any arch's global devsw
104 	 * table, this function should never get called.
105 	 */
106 	panic("%s: should never be called", __func__);
107 	return (ENXIO);
108 }
109 
110 static int
111 geli_dev_strategy(void *devdata, int rw, daddr_t blk, size_t size, char *buf,
112     size_t *rsize)
113 {
114 	struct geli_devdesc *gdesc;
115 	off_t alnend, alnstart, reqend, reqstart;
116 	size_t alnsize;
117 	char *iobuf;
118 	int rc;
119 
120 	gdesc = (struct geli_devdesc *)devdata;
121 
122 	/*
123 	 * We can only decrypt full geli blocks.  The blk arg is expressed in
124 	 * units of DEV_BSIZE blocks, while size is in bytes.  Convert
125 	 * everything to bytes, and calculate the geli-blocksize-aligned start
126 	 * and end points.
127 	 *
128 	 * Note: md_sectorsize must be cast to a signed type for the round2
129 	 * macros to work correctly (otherwise they get zero-extended to 64 bits
130 	 * and mask off the high order 32 bits of the requested start/end).
131 	 */
132 
133 	reqstart = blk * DEV_BSIZE;
134 	reqend   = reqstart + size;
135 	alnstart = rounddown2(reqstart, (int)gdesc->gdev->md.md_sectorsize);
136 	alnend   = roundup2(reqend, (int)gdesc->gdev->md.md_sectorsize);
137 	alnsize  = alnend - alnstart;
138 
139 	/*
140 	 * If alignment requires us to read/write more than the size of the
141 	 * provided buffer, allocate a temporary buffer.
142 	 * The writes will always get temporary buffer because of encryption.
143 	 */
144 	if (alnsize <= size && (rw & F_MASK) == F_READ)
145 		iobuf = buf;
146 	else if ((iobuf = malloc(alnsize)) == NULL)
147 		return (ENOMEM);
148 
149 	switch (rw & F_MASK) {
150 	case F_READ:
151 		/*
152 		 * Read the encrypted data using the host provider,
153 		 * then decrypt it.
154 		 */
155 		rc = gdesc->hdesc->dd.d_dev->dv_strategy(gdesc->hdesc, rw,
156 		    alnstart / DEV_BSIZE, alnsize, iobuf, NULL);
157 		if (rc != 0)
158 			goto out;
159 		rc = geli_io(gdesc->gdev, GELI_DECRYPT, alnstart, iobuf,
160 		    alnsize);
161 		if (rc != 0)
162 			goto out;
163 
164 		/*
165 		 * If we had to use a temporary buffer, copy the requested
166 		 * part of the data to the caller's buffer.
167 		 */
168 		if (iobuf != buf)
169 			memcpy(buf, iobuf + (reqstart - alnstart), size);
170 
171 		if (rsize != NULL)
172 			*rsize = size;
173 		break;
174 	case F_WRITE:
175 		if (iobuf != buf) {
176 			/* Read, decrypt, then modify.  */
177 			rc = gdesc->hdesc->dd.d_dev->dv_strategy(gdesc->hdesc,
178 			    F_READ, alnstart / DEV_BSIZE, alnsize, iobuf, NULL);
179 			if (rc != 0)
180 				goto out;
181 			rc = geli_io(gdesc->gdev, GELI_DECRYPT, alnstart, iobuf,
182 			    alnsize);
183 			if (rc != 0)
184 				goto out;
185 			/* Copy data to iobuf */
186 			memcpy(iobuf + (reqstart - alnstart), buf, size);
187 		}
188 
189 		/* Encrypt and write it. */
190 		rc = geli_io(gdesc->gdev, GELI_ENCRYPT, alnstart, iobuf,
191 		    alnsize);
192 		if (rc != 0)
193 			goto out;
194 		rc = gdesc->hdesc->dd.d_dev->dv_strategy(gdesc->hdesc,
195 		    rw, alnstart / DEV_BSIZE, alnsize, iobuf, NULL);
196 	}
197 out:
198 	if (iobuf != buf)
199 		free(iobuf);
200 
201 	return (rc);
202 }
203 
204 static int
205 geli_dev_open(struct open_file *f, ...)
206 {
207 
208 	/*
209 	 * Since geli_devsw never gets referenced in any arch's global devsw
210 	 * table, this function should never get called.
211 	 */
212 	panic("%s: should never be called", __func__);
213 	return (ENXIO);
214 }
215 
216 static int
217 geli_dev_close(struct open_file *f)
218 {
219 	struct geli_devdesc *gdesc;
220 
221 	/*
222 	 * Detach the geli_devdesc from the open_file and reattach the
223 	 * underlying host provider's disk_devdesc; this undoes the work done at
224 	 * the end of geli_probe_and_attach().  Call the host provider's
225 	 * dv_close() (because that's what our caller thought it was doing).
226 	 */
227 	gdesc = (struct geli_devdesc *)f->f_devdata;
228 	f->f_devdata = gdesc->hdesc;
229 	f->f_dev = gdesc->hdesc->dd.d_dev;
230 	free(gdesc);
231 	f->f_dev->dv_close(f);
232 	return (0);
233 }
234 
235 static int
236 geli_dev_ioctl(struct open_file *f, u_long cmd, void *data)
237 {
238 	struct geli_devdesc *gdesc;
239 	struct g_eli_metadata *md;
240 
241 	gdesc = (struct geli_devdesc *)f->f_devdata;
242 	md = &gdesc->gdev->md;
243 
244 	switch (cmd) {
245 	case DIOCGSECTORSIZE:
246 		*(u_int *)data = md->md_sectorsize;
247 		break;
248 	case DIOCGMEDIASIZE:
249 		*(uint64_t *)data = md->md_provsize;
250 		break;
251 	default:
252 		return (ENOTTY);
253 	}
254 
255 	return (0);
256 }
257 
258 static int
259 geli_dev_print(int verbose)
260 {
261 
262 	/*
263 	 * Since geli_devsw never gets referenced in any arch's global devsw
264 	 * table, this function should never get called.
265 	 */
266 	panic("%s: should never be called", __func__);
267 	return (ENXIO);
268 }
269 
270 static void
271 geli_dev_cleanup(void)
272 {
273 
274 	/*
275 	 * Since geli_devsw never gets referenced in any arch's global devsw
276 	 * table, this function should never get called.
277 	 */
278 	panic("%s: should never be called", __func__);
279 }
280 
281 
282 /*
283  * geli_probe_and_attach() is called from devopen() after it successfully calls
284  * the dv_open() method of a DEVT_DISK device.  We taste the partition described
285  * by the disk_devdesc, and if it's geli-encrypted and we can decrypt it, we
286  * create a geli_devdesc and store it into the open_file struct in place of the
287  * underlying provider's disk_devdesc, effectively attaching our code to all IO
288  * processing for the partition.  Not quite the elegant stacking provided by
289  * geom in the kernel, but it gets the job done.
290  */
291 void
292 geli_probe_and_attach(struct open_file *f)
293 {
294 	static char gelipw[GELI_PW_MAXLEN];
295 	const char *envpw;
296 	struct geli_dev *gdev;
297 	struct geli_devdesc *gdesc;
298 	struct disk_devdesc *hdesc;
299 	uint64_t hmediasize;
300 	daddr_t hlastblk;
301 	int rc;
302 
303 	hdesc = (struct disk_devdesc *)(f->f_devdata);
304 
305 	/* We only work on DEVT_DISKs */
306 	if (hdesc->dd.d_dev->dv_type != DEVT_DISK)
307 		return;
308 	/* Get the last block number for the host provider. */
309 	if (hdesc->dd.d_dev->dv_ioctl(f, DIOCGMEDIASIZE, &hmediasize) != 0)
310 		return;
311 	hlastblk = (hmediasize / DEV_BSIZE) - 1;
312 
313 	/* Taste the host provider.  If it's not geli-encrypted just return. */
314 	gdev = geli_taste(diskdev_read, hdesc, hlastblk, devformat(&hdesc->dd));
315 	if (gdev == NULL)
316 		return;
317 
318 	/*
319 	 * It's geli, try to decrypt it with existing keys, or prompt for a
320 	 * passphrase if we don't yet have a cached key for it.
321 	 */
322 	if ((rc = geli_havekey(gdev)) != 0) {
323 		envpw = getenv("kern.geom.eli.passphrase");
324 		if (envpw != NULL) {
325 			/* Use the cached passphrase */
326 			bcopy(envpw, &gelipw, GELI_PW_MAXLEN);
327 		}
328 		if ((rc = geli_passphrase(gdev, gelipw)) == 0) {
329 			/* Passphrase is good, cache it. */
330 			setenv("kern.geom.eli.passphrase", gelipw, 1);
331 		}
332 		explicit_bzero(gelipw, sizeof(gelipw));
333 		if (rc != 0)
334 			return;
335 	}
336 
337 	/*
338 	 * It's geli-encrypted and we can decrypt it.  Create a geli_devdesc,
339 	 * store a reference to the underlying provider's disk_devdesc in it,
340 	 * then attach it to the openfile struct in place of the host provider.
341 	 */
342 	if ((gdesc = malloc(sizeof(*gdesc))) == NULL)
343 		return;
344 	gdesc->ddd.dd.d_dev = &geli_devsw;
345 	gdesc->ddd.dd.d_opendata = NULL;
346 	gdesc->ddd.dd.d_unit = hdesc->dd.d_unit;
347 	gdesc->ddd.d_offset = hdesc->d_offset;
348 	gdesc->ddd.d_partition = hdesc->d_partition;
349 	gdesc->ddd.d_slice = hdesc->d_slice;
350 	gdesc->hdesc = hdesc;
351 	gdesc->gdev = gdev;
352 	f->f_dev = gdesc->ddd.dd.d_dev;
353 	f->f_devdata = gdesc;
354 }
355