]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/libsa/geli/gelidev.c
MFC GELI Loader Improvements: r336252, r336254, r336256, r336354,
[FreeBSD/FreeBSD.git] / stand / libsa / geli / gelidev.c
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$
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     = "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  */
72 struct 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  */
84 static int
85 diskdev_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
96 static int
97 geli_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
108 static int
109 geli_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;
170 out:
171         if (iobuf != buf)
172                 free(iobuf);
173
174         return (rc);
175 }
176
177 static int
178 geli_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
189 static int
190 geli_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
208 static int
209 geli_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
231 static int
232 geli_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
243 static void
244 geli_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  */
264 void
265 geli_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 }