1336252Sian/*- 2336252Sian * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3336252Sian * 4336252Sian * Copyright (c) 2018 Ian Lepore <ian@FreeBSD.org> 5336252Sian * 6336252Sian * Redistribution and use in source and binary forms, with or without 7336252Sian * modification, are permitted provided that the following conditions 8336252Sian * are met: 9336252Sian * 1. Redistributions of source code must retain the above copyright 10336252Sian * notice, this list of conditions and the following disclaimer. 11336252Sian * 2. Redistributions in binary form must reproduce the above copyright 12336252Sian * notice, this list of conditions and the following disclaimer in the 13336252Sian * documentation and/or other materials provided with the distribution. 14336252Sian * 15336252Sian * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 16336252Sian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17336252Sian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18336252Sian * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 19336252Sian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20336252Sian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21336252Sian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22336252Sian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23336252Sian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24336252Sian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25336252Sian * SUCH DAMAGE. 26336252Sian * 27336252Sian * $FreeBSD: stable/11/stand/libsa/geli/gelidev.c 344399 2019-02-20 23:55:35Z kevans $ 28336252Sian */ 29336252Sian 30336252Sian#include <stand.h> 31336252Sian#include <stdarg.h> 32336252Sian#include <uuid.h> 33336252Sian#include <sys/disk.h> 34336252Sian#include "disk.h" 35336252Sian#include "geliboot.h" 36336252Sian#include "geliboot_internal.h" 37336252Sian 38336252Sianstatic int geli_dev_init(void); 39336252Sianstatic int geli_dev_strategy(void *, int, daddr_t, size_t, char *, size_t *); 40336252Sianstatic int geli_dev_open(struct open_file *f, ...); 41336252Sianstatic int geli_dev_close(struct open_file *f); 42336252Sianstatic int geli_dev_ioctl(struct open_file *, u_long, void *); 43336252Sianstatic int geli_dev_print(int); 44336252Sianstatic void geli_dev_cleanup(void); 45336252Sian 46336252Sian/* 47336252Sian * geli_devsw is static because it never appears in any arch's global devsw 48336252Sian * array. Instead, when devopen() opens a DEVT_DISK device, it then calls 49336252Sian * geli_probe_and_attach(), and if we find that the disk_devdesc describes a 50336252Sian * geli-encrypted partition, we create a geli_devdesc which references this 51336252Sian * devsw and has a pointer to the original disk_devdesc of the underlying host 52336252Sian * disk. Then we manipulate the open_file struct to reference the new 53336252Sian * geli_devdesc, effectively routing all IO operations through our code. 54336252Sian */ 55336252Sianstatic struct devsw geli_devsw = { 56336252Sian .dv_name = "gelidisk", 57336252Sian .dv_type = DEVT_DISK, 58336252Sian .dv_init = geli_dev_init, 59336252Sian .dv_strategy = geli_dev_strategy, 60336252Sian .dv_open = geli_dev_open, 61336252Sian .dv_close = geli_dev_close, 62336252Sian .dv_ioctl = geli_dev_ioctl, 63336252Sian .dv_print = geli_dev_print, 64336252Sian .dv_cleanup = geli_dev_cleanup, 65336252Sian}; 66336252Sian 67336252Sian/* 68336252Sian * geli_devdesc instances replace the disk_devdesc in an open_file struct when 69336252Sian * the partition is encrypted. We keep a reference to the original host 70336252Sian * disk_devdesc so that we can read the raw encrypted data using it. 71336252Sian */ 72336252Sianstruct geli_devdesc { 73336252Sian struct disk_devdesc ddd; /* Must be first. */ 74336252Sian struct disk_devdesc *hdesc; /* disk/slice/part hosting geli vol */ 75336252Sian struct geli_dev *gdev; /* geli_dev entry */ 76336252Sian}; 77336252Sian 78336252Sian 79336252Sian/* 80336252Sian * A geli_readfunc that reads via a disk_devdesc passed in readpriv. This is 81336252Sian * used to read the underlying host disk data when probing/tasting to see if the 82336252Sian * host provider is geli-encrypted. 83336252Sian */ 84336252Sianstatic int 85336252Siandiskdev_read(void *vdev, void *readpriv, off_t offbytes, 86336252Sian void *buf, size_t sizebytes) 87336252Sian{ 88336252Sian struct disk_devdesc *ddev; 89336252Sian 90336252Sian ddev = (struct disk_devdesc *)readpriv; 91336252Sian 92336252Sian return (ddev->dd.d_dev->dv_strategy(ddev, F_READ, offbytes / DEV_BSIZE, 93336252Sian sizebytes, buf, NULL)); 94336252Sian} 95336252Sian 96336252Sianstatic int 97336252Siangeli_dev_init(void) 98336252Sian{ 99336252Sian 100336252Sian /* 101336252Sian * Since geli_devsw never gets referenced in any arch's global devsw 102336252Sian * table, this function should never get called. 103336252Sian */ 104336252Sian panic("%s: should never be called", __func__); 105336252Sian return (ENXIO); 106336252Sian} 107336252Sian 108336252Sianstatic int 109336252Siangeli_dev_strategy(void *devdata, int rw, daddr_t blk, size_t size, char *buf, 110336252Sian size_t *rsize) 111336252Sian{ 112336252Sian struct geli_devdesc *gdesc; 113336252Sian off_t alnend, alnstart, reqend, reqstart; 114336252Sian size_t alnsize; 115336252Sian char *iobuf; 116336252Sian int rc; 117336252Sian 118336252Sian /* We only handle reading; no write support. */ 119336252Sian if ((rw & F_MASK) != F_READ) 120336252Sian return (EOPNOTSUPP); 121336252Sian 122336252Sian gdesc = (struct geli_devdesc *)devdata; 123336252Sian 124336252Sian /* 125336252Sian * We can only decrypt full geli blocks. The blk arg is expressed in 126336252Sian * units of DEV_BSIZE blocks, while size is in bytes. Convert 127336252Sian * everything to bytes, and calculate the geli-blocksize-aligned start 128336252Sian * and end points. 129336252Sian * 130336252Sian * Note: md_sectorsize must be cast to a signed type for the round2 131336252Sian * macros to work correctly (otherwise they get zero-extended to 64 bits 132336252Sian * and mask off the high order 32 bits of the requested start/end). 133336252Sian */ 134336252Sian 135336252Sian reqstart = blk * DEV_BSIZE; 136336252Sian reqend = reqstart + size; 137336252Sian alnstart = rounddown2(reqstart, (int)gdesc->gdev->md.md_sectorsize); 138336252Sian alnend = roundup2(reqend, (int)gdesc->gdev->md.md_sectorsize); 139336252Sian alnsize = alnend - alnstart; 140336252Sian 141336252Sian /* 142336252Sian * If alignment requires us to read more than the size of the provided 143336252Sian * buffer, allocate a temporary buffer. 144336252Sian */ 145336252Sian if (alnsize <= size) 146336252Sian iobuf = buf; 147336252Sian else if ((iobuf = malloc(alnsize)) == NULL) 148336252Sian return (ENOMEM); 149336252Sian 150336252Sian /* 151336252Sian * Read the encrypted data using the host provider, then decrypt it. 152336252Sian */ 153336252Sian rc = gdesc->hdesc->dd.d_dev->dv_strategy(gdesc->hdesc, rw, 154336252Sian alnstart / DEV_BSIZE, alnsize, iobuf, NULL); 155336252Sian if (rc != 0) 156336252Sian goto out; 157336252Sian rc = geli_read(gdesc->gdev, alnstart, iobuf, alnsize); 158336252Sian if (rc != 0) 159336252Sian goto out; 160336252Sian 161336252Sian /* 162336252Sian * If we had to use a temporary buffer, copy the requested part of the 163336252Sian * data to the caller's buffer. 164336252Sian */ 165336252Sian if (iobuf != buf) 166336252Sian memcpy(buf, iobuf + (reqstart - alnstart), size); 167336252Sian 168336252Sian if (rsize != NULL) 169336252Sian *rsize = size; 170336252Sianout: 171336252Sian if (iobuf != buf) 172336252Sian free(iobuf); 173336252Sian 174336252Sian return (rc); 175336252Sian} 176336252Sian 177336252Sianstatic int 178336252Siangeli_dev_open(struct open_file *f, ...) 179336252Sian{ 180336252Sian 181336252Sian /* 182336252Sian * Since geli_devsw never gets referenced in any arch's global devsw 183336252Sian * table, this function should never get called. 184336252Sian */ 185336252Sian panic("%s: should never be called", __func__); 186336252Sian return (ENXIO); 187336252Sian} 188336252Sian 189336252Sianstatic int 190336252Siangeli_dev_close(struct open_file *f) 191336252Sian{ 192336252Sian struct geli_devdesc *gdesc; 193336252Sian 194336252Sian /* 195336252Sian * Detach the geli_devdesc from the open_file and reattach the 196336252Sian * underlying host provider's disk_devdesc; this undoes the work done at 197336252Sian * the end of geli_probe_and_attach(). Call the host provider's 198336252Sian * dv_close() (because that's what our caller thought it was doing). 199336252Sian */ 200336252Sian gdesc = (struct geli_devdesc *)f->f_devdata; 201336252Sian f->f_devdata = gdesc->hdesc; 202336252Sian f->f_dev = gdesc->hdesc->dd.d_dev; 203336252Sian free(gdesc); 204336252Sian f->f_dev->dv_close(f); 205336252Sian return (0); 206336252Sian} 207336252Sian 208336252Sianstatic int 209336252Siangeli_dev_ioctl(struct open_file *f, u_long cmd, void *data) 210336252Sian{ 211336252Sian struct geli_devdesc *gdesc; 212336252Sian struct g_eli_metadata *md; 213336252Sian 214336252Sian gdesc = (struct geli_devdesc *)f->f_devdata; 215336252Sian md = &gdesc->gdev->md; 216336252Sian 217336252Sian switch (cmd) { 218336252Sian case DIOCGSECTORSIZE: 219336252Sian *(u_int *)data = md->md_sectorsize; 220336252Sian break; 221336252Sian case DIOCGMEDIASIZE: 222336252Sian *(uint64_t *)data = md->md_sectorsize * md->md_provsize; 223336252Sian break; 224336252Sian default: 225336252Sian return (ENOTTY); 226336252Sian } 227336252Sian 228336252Sian return (0); 229336252Sian} 230336252Sian 231336252Sianstatic int 232336252Siangeli_dev_print(int verbose) 233336252Sian{ 234336252Sian 235336252Sian /* 236336252Sian * Since geli_devsw never gets referenced in any arch's global devsw 237336252Sian * table, this function should never get called. 238336252Sian */ 239336252Sian panic("%s: should never be called", __func__); 240336252Sian return (ENXIO); 241336252Sian} 242336252Sian 243336252Sianstatic void 244336252Siangeli_dev_cleanup(void) 245336252Sian{ 246336252Sian 247336252Sian /* 248336252Sian * Since geli_devsw never gets referenced in any arch's global devsw 249336252Sian * table, this function should never get called. 250336252Sian */ 251336252Sian panic("%s: should never be called", __func__); 252336252Sian} 253336252Sian 254336252Sian 255336252Sian/* 256336252Sian * geli_probe_and_attach() is called from devopen() after it successfully calls 257336252Sian * the dv_open() method of a DEVT_DISK device. We taste the partition described 258336252Sian * by the disk_devdesc, and if it's geli-encrypted and we can decrypt it, we 259336252Sian * create a geli_devdesc and store it into the open_file struct in place of the 260336252Sian * underlying provider's disk_devdesc, effectively attaching our code to all IO 261336252Sian * processing for the partition. Not quite the elegant stacking provided by 262336252Sian * geom in the kernel, but it gets the job done. 263336252Sian */ 264336252Sianvoid 265336252Siangeli_probe_and_attach(struct open_file *f) 266336252Sian{ 267336252Sian static char gelipw[GELI_PW_MAXLEN]; 268336252Sian const char *envpw; 269336252Sian struct geli_dev *gdev; 270336252Sian struct geli_devdesc *gdesc; 271336252Sian struct disk_devdesc *hdesc; 272336252Sian uint64_t hmediasize; 273336252Sian daddr_t hlastblk; 274336252Sian int rc; 275336252Sian 276336252Sian hdesc = (struct disk_devdesc *)(f->f_devdata); 277336252Sian 278336252Sian /* Get the last block number for the host provider. */ 279336252Sian hdesc->dd.d_dev->dv_ioctl(f, DIOCGMEDIASIZE, &hmediasize); 280336252Sian hlastblk = (hmediasize / DEV_BSIZE) - 1; 281336252Sian 282336252Sian /* Taste the host provider. If it's not geli-encrypted just return. */ 283336252Sian gdev = geli_taste(diskdev_read, hdesc, hlastblk, disk_fmtdev(hdesc)); 284336252Sian if (gdev == NULL) 285336252Sian return; 286336252Sian 287336252Sian /* 288336252Sian * It's geli, try to decrypt it with existing keys, or prompt for a 289336252Sian * passphrase if we don't yet have a cached key for it. 290336252Sian */ 291336252Sian if ((rc = geli_havekey(gdev)) != 0) { 292336252Sian envpw = getenv("kern.geom.eli.passphrase"); 293336252Sian if (envpw != NULL) { 294336252Sian /* Use the cached passphrase */ 295336252Sian bcopy(envpw, &gelipw, GELI_PW_MAXLEN); 296336252Sian } 297336252Sian if ((rc = geli_passphrase(gdev, gelipw)) == 0) { 298336252Sian /* Passphrase is good, cache it. */ 299336252Sian setenv("kern.geom.eli.passphrase", gelipw, 1); 300336252Sian } 301336252Sian explicit_bzero(gelipw, sizeof(gelipw)); 302336252Sian if (rc != 0) 303336252Sian return; 304336252Sian } 305336252Sian 306336252Sian /* 307336252Sian * It's geli-encrypted and we can decrypt it. Create a geli_devdesc, 308336252Sian * store a reference to the underlying provider's disk_devdesc in it, 309336252Sian * then attach it to the openfile struct in place of the host provider. 310336252Sian */ 311336252Sian if ((gdesc = malloc(sizeof(*gdesc))) == NULL) 312336252Sian return; 313336252Sian gdesc->ddd.dd.d_dev = &geli_devsw; 314336252Sian gdesc->ddd.dd.d_opendata = NULL; 315336252Sian gdesc->ddd.dd.d_unit = hdesc->dd.d_unit; 316336252Sian gdesc->ddd.d_offset = hdesc->d_offset; 317336252Sian gdesc->ddd.d_partition = hdesc->d_partition; 318336252Sian gdesc->ddd.d_slice = hdesc->d_slice; 319336252Sian gdesc->hdesc = hdesc; 320336252Sian gdesc->gdev = gdev; 321336252Sian f->f_dev = gdesc->ddd.dd.d_dev; 322336252Sian f->f_devdata = gdesc; 323336252Sian} 324