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