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 } 324