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 = "disk", 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 .dv_fmtdev = disk_fmtdev, 66 }; 67 68 /* 69 * geli_devdesc instances replace the disk_devdesc in an open_file struct when 70 * the partition is encrypted. We keep a reference to the original host 71 * disk_devdesc so that we can read the raw encrypted data using it. 72 */ 73 struct geli_devdesc { 74 struct disk_devdesc ddd; /* Must be first. */ 75 struct disk_devdesc *hdesc; /* disk/slice/part hosting geli vol */ 76 struct geli_dev *gdev; /* geli_dev entry */ 77 }; 78 79 80 /* 81 * A geli_readfunc that reads via a disk_devdesc passed in readpriv. This is 82 * used to read the underlying host disk data when probing/tasting to see if the 83 * host provider is geli-encrypted. 84 */ 85 static int 86 diskdev_read(void *vdev, void *readpriv, off_t offbytes, 87 void *buf, size_t sizebytes) 88 { 89 struct disk_devdesc *ddev; 90 91 ddev = (struct disk_devdesc *)readpriv; 92 93 return (ddev->dd.d_dev->dv_strategy(ddev, F_READ, offbytes / DEV_BSIZE, 94 sizebytes, buf, NULL)); 95 } 96 97 static int 98 geli_dev_init(void) 99 { 100 101 /* 102 * Since geli_devsw never gets referenced in any arch's global devsw 103 * table, this function should never get called. 104 */ 105 panic("%s: should never be called", __func__); 106 return (ENXIO); 107 } 108 109 static int 110 geli_dev_strategy(void *devdata, int rw, daddr_t blk, size_t size, char *buf, 111 size_t *rsize) 112 { 113 struct geli_devdesc *gdesc; 114 off_t alnend, alnstart, reqend, reqstart; 115 size_t alnsize; 116 char *iobuf; 117 int rc; 118 119 gdesc = (struct geli_devdesc *)devdata; 120 121 /* 122 * We can only decrypt full geli blocks. The blk arg is expressed in 123 * units of DEV_BSIZE blocks, while size is in bytes. Convert 124 * everything to bytes, and calculate the geli-blocksize-aligned start 125 * and end points. 126 * 127 * Note: md_sectorsize must be cast to a signed type for the round2 128 * macros to work correctly (otherwise they get zero-extended to 64 bits 129 * and mask off the high order 32 bits of the requested start/end). 130 */ 131 132 reqstart = blk * DEV_BSIZE; 133 reqend = reqstart + size; 134 alnstart = rounddown2(reqstart, (int)gdesc->gdev->md.md_sectorsize); 135 alnend = roundup2(reqend, (int)gdesc->gdev->md.md_sectorsize); 136 alnsize = alnend - alnstart; 137 138 /* 139 * If alignment requires us to read/write more than the size of the 140 * provided buffer, allocate a temporary buffer. 141 * The writes will always get temporary buffer because of encryption. 142 */ 143 if (alnsize <= size && (rw & F_MASK) == F_READ) 144 iobuf = buf; 145 else if ((iobuf = malloc(alnsize)) == NULL) 146 return (ENOMEM); 147 148 switch (rw & F_MASK) { 149 case F_READ: 150 /* 151 * Read the encrypted data using the host provider, 152 * then decrypt it. 153 */ 154 rc = gdesc->hdesc->dd.d_dev->dv_strategy(gdesc->hdesc, rw, 155 alnstart / DEV_BSIZE, alnsize, iobuf, NULL); 156 if (rc != 0) 157 goto out; 158 rc = geli_io(gdesc->gdev, GELI_DECRYPT, alnstart, iobuf, 159 alnsize); 160 if (rc != 0) 161 goto out; 162 163 /* 164 * If we had to use a temporary buffer, copy the requested 165 * part of the data to the caller's buffer. 166 */ 167 if (iobuf != buf) 168 memcpy(buf, iobuf + (reqstart - alnstart), size); 169 170 if (rsize != NULL) 171 *rsize = size; 172 break; 173 case F_WRITE: 174 if (iobuf != buf) { 175 /* Read, decrypt, then modify. */ 176 rc = gdesc->hdesc->dd.d_dev->dv_strategy(gdesc->hdesc, 177 F_READ, alnstart / DEV_BSIZE, alnsize, iobuf, NULL); 178 if (rc != 0) 179 goto out; 180 rc = geli_io(gdesc->gdev, GELI_DECRYPT, alnstart, iobuf, 181 alnsize); 182 if (rc != 0) 183 goto out; 184 /* Copy data to iobuf */ 185 memcpy(iobuf + (reqstart - alnstart), buf, size); 186 } 187 188 /* Encrypt and write it. */ 189 rc = geli_io(gdesc->gdev, GELI_ENCRYPT, alnstart, iobuf, 190 alnsize); 191 if (rc != 0) 192 goto out; 193 rc = gdesc->hdesc->dd.d_dev->dv_strategy(gdesc->hdesc, 194 rw, alnstart / DEV_BSIZE, alnsize, iobuf, NULL); 195 } 196 out: 197 if (iobuf != buf) 198 free(iobuf); 199 200 return (rc); 201 } 202 203 static int 204 geli_dev_open(struct open_file *f, ...) 205 { 206 207 /* 208 * Since geli_devsw never gets referenced in any arch's global devsw 209 * table, this function should never get called. 210 */ 211 panic("%s: should never be called", __func__); 212 return (ENXIO); 213 } 214 215 static int 216 geli_dev_close(struct open_file *f) 217 { 218 struct geli_devdesc *gdesc; 219 220 /* 221 * Detach the geli_devdesc from the open_file and reattach the 222 * underlying host provider's disk_devdesc; this undoes the work done at 223 * the end of geli_probe_and_attach(). Call the host provider's 224 * dv_close() (because that's what our caller thought it was doing). 225 */ 226 gdesc = (struct geli_devdesc *)f->f_devdata; 227 f->f_devdata = gdesc->hdesc; 228 f->f_dev = gdesc->hdesc->dd.d_dev; 229 free(gdesc); 230 f->f_dev->dv_close(f); 231 return (0); 232 } 233 234 static int 235 geli_dev_ioctl(struct open_file *f, u_long cmd, void *data) 236 { 237 struct geli_devdesc *gdesc; 238 struct g_eli_metadata *md; 239 240 gdesc = (struct geli_devdesc *)f->f_devdata; 241 md = &gdesc->gdev->md; 242 243 switch (cmd) { 244 case DIOCGSECTORSIZE: 245 *(u_int *)data = md->md_sectorsize; 246 break; 247 case DIOCGMEDIASIZE: 248 *(uint64_t *)data = md->md_provsize; 249 break; 250 default: 251 return (ENOTTY); 252 } 253 254 return (0); 255 } 256 257 static int 258 geli_dev_print(int verbose) 259 { 260 261 /* 262 * Since geli_devsw never gets referenced in any arch's global devsw 263 * table, this function should never get called. 264 */ 265 panic("%s: should never be called", __func__); 266 return (ENXIO); 267 } 268 269 static void 270 geli_dev_cleanup(void) 271 { 272 273 /* 274 * Since geli_devsw never gets referenced in any arch's global devsw 275 * table, this function should never get called. 276 */ 277 panic("%s: should never be called", __func__); 278 } 279 280 281 /* 282 * geli_probe_and_attach() is called from devopen() after it successfully calls 283 * the dv_open() method of a DEVT_DISK device. We taste the partition described 284 * by the disk_devdesc, and if it's geli-encrypted and we can decrypt it, we 285 * create a geli_devdesc and store it into the open_file struct in place of the 286 * underlying provider's disk_devdesc, effectively attaching our code to all IO 287 * processing for the partition. Not quite the elegant stacking provided by 288 * geom in the kernel, but it gets the job done. 289 */ 290 void 291 geli_probe_and_attach(struct open_file *f) 292 { 293 static char gelipw[GELI_PW_MAXLEN]; 294 const char *envpw; 295 struct geli_dev *gdev; 296 struct geli_devdesc *gdesc; 297 struct disk_devdesc *hdesc; 298 uint64_t hmediasize; 299 daddr_t hlastblk; 300 int rc; 301 302 hdesc = (struct disk_devdesc *)(f->f_devdata); 303 304 /* Get the last block number for the host provider. */ 305 hdesc->dd.d_dev->dv_ioctl(f, DIOCGMEDIASIZE, &hmediasize); 306 hlastblk = (hmediasize / DEV_BSIZE) - 1; 307 308 /* Taste the host provider. If it's not geli-encrypted just return. */ 309 gdev = geli_taste(diskdev_read, hdesc, hlastblk, devformat(&hdesc->dd)); 310 if (gdev == NULL) 311 return; 312 313 /* 314 * It's geli, try to decrypt it with existing keys, or prompt for a 315 * passphrase if we don't yet have a cached key for it. 316 */ 317 if ((rc = geli_havekey(gdev)) != 0) { 318 envpw = getenv("kern.geom.eli.passphrase"); 319 if (envpw != NULL) { 320 /* Use the cached passphrase */ 321 bcopy(envpw, &gelipw, GELI_PW_MAXLEN); 322 } 323 if ((rc = geli_passphrase(gdev, gelipw)) == 0) { 324 /* Passphrase is good, cache it. */ 325 setenv("kern.geom.eli.passphrase", gelipw, 1); 326 } 327 explicit_bzero(gelipw, sizeof(gelipw)); 328 if (rc != 0) 329 return; 330 } 331 332 /* 333 * It's geli-encrypted and we can decrypt it. Create a geli_devdesc, 334 * store a reference to the underlying provider's disk_devdesc in it, 335 * then attach it to the openfile struct in place of the host provider. 336 */ 337 if ((gdesc = malloc(sizeof(*gdesc))) == NULL) 338 return; 339 gdesc->ddd.dd.d_dev = &geli_devsw; 340 gdesc->ddd.dd.d_opendata = NULL; 341 gdesc->ddd.dd.d_unit = hdesc->dd.d_unit; 342 gdesc->ddd.d_offset = hdesc->d_offset; 343 gdesc->ddd.d_partition = hdesc->d_partition; 344 gdesc->ddd.d_slice = hdesc->d_slice; 345 gdesc->hdesc = hdesc; 346 gdesc->gdev = gdev; 347 f->f_dev = gdesc->ddd.dd.d_dev; 348 f->f_devdata = gdesc; 349 } 350