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 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 /* Get the last block number for the host provider. */ 304 hdesc->dd.d_dev->dv_ioctl(f, DIOCGMEDIASIZE, &hmediasize); 305 hlastblk = (hmediasize / DEV_BSIZE) - 1; 306 307 /* Taste the host provider. If it's not geli-encrypted just return. */ 308 gdev = geli_taste(diskdev_read, hdesc, hlastblk, disk_fmtdev(hdesc)); 309 if (gdev == NULL) 310 return; 311 312 /* 313 * It's geli, try to decrypt it with existing keys, or prompt for a 314 * passphrase if we don't yet have a cached key for it. 315 */ 316 if ((rc = geli_havekey(gdev)) != 0) { 317 envpw = getenv("kern.geom.eli.passphrase"); 318 if (envpw != NULL) { 319 /* Use the cached passphrase */ 320 bcopy(envpw, &gelipw, GELI_PW_MAXLEN); 321 } 322 if ((rc = geli_passphrase(gdev, gelipw)) == 0) { 323 /* Passphrase is good, cache it. */ 324 setenv("kern.geom.eli.passphrase", gelipw, 1); 325 } 326 explicit_bzero(gelipw, sizeof(gelipw)); 327 if (rc != 0) 328 return; 329 } 330 331 /* 332 * It's geli-encrypted and we can decrypt it. Create a geli_devdesc, 333 * store a reference to the underlying provider's disk_devdesc in it, 334 * then attach it to the openfile struct in place of the host provider. 335 */ 336 if ((gdesc = malloc(sizeof(*gdesc))) == NULL) 337 return; 338 gdesc->ddd.dd.d_dev = &geli_devsw; 339 gdesc->ddd.dd.d_opendata = NULL; 340 gdesc->ddd.dd.d_unit = hdesc->dd.d_unit; 341 gdesc->ddd.d_offset = hdesc->d_offset; 342 gdesc->ddd.d_partition = hdesc->d_partition; 343 gdesc->ddd.d_slice = hdesc->d_slice; 344 gdesc->hdesc = hdesc; 345 gdesc->gdev = gdev; 346 f->f_dev = gdesc->ddd.dd.d_dev; 347 f->f_devdata = gdesc; 348 } 349