Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/stand/libsa/geli/gelidev.c
34860 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2018 Ian Lepore <[email protected]>
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
28
#include <stand.h>
29
#include <stdarg.h>
30
#include <uuid.h>
31
#include <sys/disk.h>
32
#include "disk.h"
33
#include "geliboot.h"
34
#include "geliboot_internal.h"
35
36
static int geli_dev_init(void);
37
static int geli_dev_strategy(void *, int, daddr_t, size_t, char *, size_t *);
38
static int geli_dev_open(struct open_file *f, ...);
39
static int geli_dev_close(struct open_file *f);
40
static int geli_dev_ioctl(struct open_file *, u_long, void *);
41
static int geli_dev_print(int);
42
static void geli_dev_cleanup(void);
43
44
/*
45
* geli_devsw is static because it never appears in any arch's global devsw
46
* array. Instead, when devopen() opens a DEVT_DISK device, it then calls
47
* geli_probe_and_attach(), and if we find that the disk_devdesc describes a
48
* geli-encrypted partition, we create a geli_devdesc which references this
49
* devsw and has a pointer to the original disk_devdesc of the underlying host
50
* disk. Then we manipulate the open_file struct to reference the new
51
* geli_devdesc, effectively routing all IO operations through our code.
52
*/
53
static struct devsw geli_devsw = {
54
.dv_name = "disk",
55
.dv_type = DEVT_DISK,
56
.dv_init = geli_dev_init,
57
.dv_strategy = geli_dev_strategy,
58
.dv_open = geli_dev_open,
59
.dv_close = geli_dev_close,
60
.dv_ioctl = geli_dev_ioctl,
61
.dv_print = geli_dev_print,
62
.dv_cleanup = geli_dev_cleanup,
63
.dv_fmtdev = disk_fmtdev,
64
.dv_parsedev = disk_parsedev,
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
gdesc = (struct geli_devdesc *)devdata;
119
120
/*
121
* We can only decrypt full geli blocks. The blk arg is expressed in
122
* units of DEV_BSIZE blocks, while size is in bytes. Convert
123
* everything to bytes, and calculate the geli-blocksize-aligned start
124
* and end points.
125
*
126
* Note: md_sectorsize must be cast to a signed type for the round2
127
* macros to work correctly (otherwise they get zero-extended to 64 bits
128
* and mask off the high order 32 bits of the requested start/end).
129
*/
130
131
reqstart = blk * DEV_BSIZE;
132
reqend = reqstart + size;
133
alnstart = rounddown2(reqstart, (int)gdesc->gdev->md.md_sectorsize);
134
alnend = roundup2(reqend, (int)gdesc->gdev->md.md_sectorsize);
135
alnsize = alnend - alnstart;
136
137
/*
138
* If alignment requires us to read/write more than the size of the
139
* provided buffer, allocate a temporary buffer.
140
* The writes will always get temporary buffer because of encryption.
141
*/
142
if (alnsize <= size && (rw & F_MASK) == F_READ)
143
iobuf = buf;
144
else if ((iobuf = malloc(alnsize)) == NULL)
145
return (ENOMEM);
146
147
switch (rw & F_MASK) {
148
case F_READ:
149
/*
150
* Read the encrypted data using the host provider,
151
* 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_io(gdesc->gdev, GELI_DECRYPT, alnstart, iobuf,
158
alnsize);
159
if (rc != 0)
160
goto out;
161
162
/*
163
* If we had to use a temporary buffer, copy the requested
164
* part of the data to the caller's buffer.
165
*/
166
if (iobuf != buf)
167
memcpy(buf, iobuf + (reqstart - alnstart), size);
168
169
if (rsize != NULL)
170
*rsize = size;
171
break;
172
case F_WRITE:
173
if (iobuf != buf) {
174
/* Read, decrypt, then modify. */
175
rc = gdesc->hdesc->dd.d_dev->dv_strategy(gdesc->hdesc,
176
F_READ, alnstart / DEV_BSIZE, alnsize, iobuf, NULL);
177
if (rc != 0)
178
goto out;
179
rc = geli_io(gdesc->gdev, GELI_DECRYPT, alnstart, iobuf,
180
alnsize);
181
if (rc != 0)
182
goto out;
183
/* Copy data to iobuf */
184
memcpy(iobuf + (reqstart - alnstart), buf, size);
185
}
186
187
/* Encrypt and write it. */
188
rc = geli_io(gdesc->gdev, GELI_ENCRYPT, alnstart, iobuf,
189
alnsize);
190
if (rc != 0)
191
goto out;
192
rc = gdesc->hdesc->dd.d_dev->dv_strategy(gdesc->hdesc,
193
rw, alnstart / DEV_BSIZE, alnsize, iobuf, NULL);
194
}
195
out:
196
if (iobuf != buf)
197
free(iobuf);
198
199
return (rc);
200
}
201
202
static int
203
geli_dev_open(struct open_file *f, ...)
204
{
205
206
/*
207
* Since geli_devsw never gets referenced in any arch's global devsw
208
* table, this function should never get called.
209
*/
210
panic("%s: should never be called", __func__);
211
return (ENXIO);
212
}
213
214
static int
215
geli_dev_close(struct open_file *f)
216
{
217
struct geli_devdesc *gdesc;
218
219
/*
220
* Detach the geli_devdesc from the open_file and reattach the
221
* underlying host provider's disk_devdesc; this undoes the work done at
222
* the end of geli_probe_and_attach(). Call the host provider's
223
* dv_close() (because that's what our caller thought it was doing).
224
*/
225
gdesc = (struct geli_devdesc *)f->f_devdata;
226
f->f_devdata = gdesc->hdesc;
227
f->f_dev = gdesc->hdesc->dd.d_dev;
228
free(gdesc);
229
f->f_dev->dv_close(f);
230
return (0);
231
}
232
233
static int
234
geli_dev_ioctl(struct open_file *f, u_long cmd, void *data)
235
{
236
struct geli_devdesc *gdesc;
237
struct g_eli_metadata *md;
238
239
gdesc = (struct geli_devdesc *)f->f_devdata;
240
md = &gdesc->gdev->md;
241
242
switch (cmd) {
243
case DIOCGSECTORSIZE:
244
*(u_int *)data = md->md_sectorsize;
245
break;
246
case DIOCGMEDIASIZE:
247
*(uint64_t *)data = md->md_provsize;
248
break;
249
default:
250
return (ENOTTY);
251
}
252
253
return (0);
254
}
255
256
static int
257
geli_dev_print(int verbose)
258
{
259
260
/*
261
* Since geli_devsw never gets referenced in any arch's global devsw
262
* table, this function should never get called.
263
*/
264
panic("%s: should never be called", __func__);
265
return (ENXIO);
266
}
267
268
static void
269
geli_dev_cleanup(void)
270
{
271
272
/*
273
* Since geli_devsw never gets referenced in any arch's global devsw
274
* table, this function should never get called.
275
*/
276
panic("%s: should never be called", __func__);
277
}
278
279
280
/*
281
* geli_probe_and_attach() is called from devopen() after it successfully calls
282
* the dv_open() method of a DEVT_DISK device. We taste the partition described
283
* by the disk_devdesc, and if it's geli-encrypted and we can decrypt it, we
284
* create a geli_devdesc and store it into the open_file struct in place of the
285
* underlying provider's disk_devdesc, effectively attaching our code to all IO
286
* processing for the partition. Not quite the elegant stacking provided by
287
* geom in the kernel, but it gets the job done.
288
*/
289
void
290
geli_probe_and_attach(struct open_file *f)
291
{
292
static char gelipw[GELI_PW_MAXLEN];
293
const char *envpw;
294
struct geli_dev *gdev;
295
struct geli_devdesc *gdesc;
296
struct disk_devdesc *hdesc;
297
uint64_t hmediasize;
298
daddr_t hlastblk;
299
int rc;
300
301
hdesc = (struct disk_devdesc *)(f->f_devdata);
302
303
/* We only work on DEVT_DISKs */
304
if (hdesc->dd.d_dev->dv_type != DEVT_DISK)
305
return;
306
/* Get the last block number for the host provider. */
307
if (hdesc->dd.d_dev->dv_ioctl(f, DIOCGMEDIASIZE, &hmediasize) != 0)
308
return;
309
hlastblk = (hmediasize / DEV_BSIZE) - 1;
310
311
/* Taste the host provider. If it's not geli-encrypted just return. */
312
gdev = geli_taste(diskdev_read, hdesc, hlastblk, devformat(&hdesc->dd));
313
if (gdev == NULL)
314
return;
315
316
/*
317
* It's geli, try to decrypt it with existing keys, or prompt for a
318
* passphrase if we don't yet have a cached key for it.
319
*/
320
if ((rc = geli_havekey(gdev)) != 0) {
321
envpw = getenv("kern.geom.eli.passphrase");
322
if (envpw != NULL) {
323
/* Use the cached passphrase */
324
bcopy(envpw, &gelipw, GELI_PW_MAXLEN);
325
}
326
if ((rc = geli_passphrase(gdev, gelipw)) == 0) {
327
/* Passphrase is good, cache it. */
328
setenv("kern.geom.eli.passphrase", gelipw, 1);
329
}
330
explicit_bzero(gelipw, sizeof(gelipw));
331
if (rc != 0)
332
return;
333
}
334
335
/*
336
* It's geli-encrypted and we can decrypt it. Create a geli_devdesc,
337
* store a reference to the underlying provider's disk_devdesc in it,
338
* then attach it to the openfile struct in place of the host provider.
339
*/
340
if ((gdesc = malloc(sizeof(*gdesc))) == NULL)
341
return;
342
gdesc->ddd.dd.d_dev = &geli_devsw;
343
gdesc->ddd.dd.d_opendata = NULL;
344
gdesc->ddd.dd.d_unit = hdesc->dd.d_unit;
345
gdesc->ddd.d_offset = hdesc->d_offset;
346
gdesc->ddd.d_partition = hdesc->d_partition;
347
gdesc->ddd.d_slice = hdesc->d_slice;
348
gdesc->hdesc = hdesc;
349
gdesc->gdev = gdev;
350
f->f_dev = gdesc->ddd.dd.d_dev;
351
f->f_devdata = gdesc;
352
}
353
354