diff options
author | Joel Sing <jsing@cvs.openbsd.org> | 2009-12-31 14:00:46 +0000 |
---|---|---|
committer | Joel Sing <jsing@cvs.openbsd.org> | 2009-12-31 14:00:46 +0000 |
commit | 6c48a0797c3ce6da990d15cfd72f07f067df8e92 (patch) | |
tree | 9a9a5dbe741b0f8687bbcd65c0491e04aef8c4ee /sys/dev/softraid_crypto.c | |
parent | de50ae56c1a30c7995a9b681464dbd29776af065 (diff) |
Add support for key disks. This allows a crypto volume to be constructed
without using a passphrase - instead the encryption mask key is stored on
the specified key disk partition (ideally being one on a removable device).
This also enables automatic assembly of crypto volumes at boot time.
ok marco@
Diffstat (limited to 'sys/dev/softraid_crypto.c')
-rw-r--r-- | sys/dev/softraid_crypto.c | 353 |
1 files changed, 337 insertions, 16 deletions
diff --git a/sys/dev/softraid_crypto.c b/sys/dev/softraid_crypto.c index 4b7fba4778a..3eb80233bb3 100644 --- a/sys/dev/softraid_crypto.c +++ b/sys/dev/softraid_crypto.c @@ -1,4 +1,4 @@ -/* $OpenBSD: softraid_crypto.c,v 1.44 2009/12/15 13:19:37 jsing Exp $ */ +/* $OpenBSD: softraid_crypto.c,v 1.45 2009/12/31 14:00:45 jsing Exp $ */ /* * Copyright (c) 2007 Marco Peereboom <marco@peereboom.us> * Copyright (c) 2008 Hans-Joerg Hoexer <hshoexer@openbsd.org> @@ -128,19 +128,33 @@ sr_crypto_create(struct sr_discipline *sd, struct bioc_createraid *bc, if (no_chunk != 1) goto done; - /* no hint available yet */ - if (bc->bc_opaque_flags & BIOC_SOOUT) { + sd->mds.mdd_crypto.key_disk = NULL; + + if (bc->bc_key_disk != NODEV) { + + /* Create a key disk. */ + if (sr_crypto_get_kdf(bc, sd)) + goto done; + sd->mds.mdd_crypto.key_disk = + sr_crypto_create_key_disk(sd, bc->bc_key_disk); + if (sd->mds.mdd_crypto.key_disk == NULL) + goto done; + sd->sd_capabilities |= SR_CAP_AUTO_ASSEMBLE; + + } else if (bc->bc_opaque_flags & BIOC_SOOUT) { + + /* No hint available yet. */ bc->bc_opaque_status = BIOC_SOINOUT_FAILED; rv = EAGAIN; goto done; - } - if (!(bc->bc_flags & BIOC_SCNOAUTOASSEMBLE)) + } else if (sr_crypto_get_kdf(bc, sd)) goto done; - - if (sr_crypto_get_kdf(bc, sd)) + + /* Passphrase volumes cannot be automatically assembled. */ + if (!(bc->bc_flags & BIOC_SCNOAUTOASSEMBLE) && bc->bc_key_disk == NODEV) goto done; - + strlcpy(sd->sd_name, "CRYPTO", sizeof(sd->sd_name)); sd->sd_meta->ssdi.ssd_size = coerced_size; @@ -159,8 +173,19 @@ sr_crypto_assemble(struct sr_discipline *sd, struct bioc_createraid *bc, { int rv = EINVAL; - /* provide userland with kdf hint */ - if (bc->bc_opaque_flags & BIOC_SOOUT) { + sd->mds.mdd_crypto.key_disk = NULL; + + if (bc->bc_key_disk != NODEV) { + + /* Read the mask key from the key disk. */ + sd->mds.mdd_crypto.key_disk = + sr_crypto_read_key_disk(sd, bc->bc_key_disk); + if (sd->mds.mdd_crypto.key_disk == NULL) + goto done; + + } else if (bc->bc_opaque_flags & BIOC_SOOUT) { + + /* provide userland with kdf hint */ if (bc->bc_opaque == NULL) goto done; @@ -176,13 +201,15 @@ sr_crypto_assemble(struct sr_discipline *sd, struct bioc_createraid *bc, bc->bc_opaque_status = BIOC_SOINOUT_OK; rv = EAGAIN; goto done; - } - /* get kdf with maskkey from userland */ - if (bc->bc_opaque_flags & BIOC_SOIN) + } else if (bc->bc_opaque_flags & BIOC_SOIN) { + + /* get kdf with maskkey from userland */ if (sr_crypto_get_kdf(bc, sd)) goto done; + } + sd->sd_max_ccb_per_wu = sd->sd_meta->ssdi.ssd_chunk_no; rv = 0; @@ -374,7 +401,7 @@ sr_crypto_encrypt(u_char *p, u_char *c, u_char *key, size_t size, int alg) break; default: DNPRINTF(SR_D_DIS, "%s: unsupported encryption algorithm %u\n", - DEVNAME(sd->sd_sc), alg); + "softraid", alg); rv = -1; goto out; } @@ -400,7 +427,7 @@ sr_crypto_decrypt(u_char *c, u_char *p, u_char *key, size_t size, int alg) break; default: DNPRINTF(SR_D_DIS, "%s: unsupported encryption algorithm %u\n", - DEVNAME(sd->sd_sc), alg); + "softraid", alg); rv = -1; goto out; } @@ -595,6 +622,296 @@ out: return (rv); } +struct sr_chunk * +sr_crypto_create_key_disk(struct sr_discipline *sd, dev_t dev) +{ + struct sr_softc *sc = sd->sd_sc; + struct sr_discipline *fakesd = NULL; + struct sr_metadata *sm = NULL; + struct sr_meta_chunk *km; + struct sr_meta_opt *om; + struct sr_chunk *key_disk = NULL; + struct disklabel label; + struct vnode *vn; + char devname[32]; + int c, part, open = 0; + + /* + * Create a metadata structure on the key disk and store + * keying material in the optional metadata. + */ + + sr_meta_getdevname(sc, dev, devname, sizeof(devname)); + + /* Make sure chunk is not already in use. */ + c = sr_chunk_in_use(sc, dev); + if (c != BIOC_SDINVALID && c != BIOC_SDOFFLINE) { + printf("%s: %s is already in use\n", DEVNAME(sc), devname); + goto done; + } + + /* Open device. */ + if (bdevvp(dev, &vn)) { + printf("%s:, sr_create_key_disk: can't allocate vnode\n", + DEVNAME(sc)); + goto done; + } + if (VOP_OPEN(vn, FREAD | FWRITE, NOCRED, 0)) { + DNPRINTF(SR_D_META,"%s: sr_create_key_disk cannot open %s\n", + DEVNAME(sc), devname); + vput(vn); + goto fail; + } + open = 1; /* close dev on error */ + + /* Get partition details. */ + part = DISKPART(dev); + if (VOP_IOCTL(vn, DIOCGDINFO, (caddr_t)&label, FREAD, NOCRED, 0)) { + DNPRINTF(SR_D_META, "%s: sr_create_key_disk ioctl failed\n", + DEVNAME(sc)); + VOP_CLOSE(vn, FREAD | FWRITE, NOCRED, 0); + vput(vn); + goto fail; + } + if (label.d_partitions[part].p_fstype != FS_RAID) { + printf("%s: %s partition not of type RAID (%d)\n", + DEVNAME(sc), devname, + label.d_partitions[part].p_fstype); + goto fail; + } + + /* + * Create and populate chunk metadata. + */ + + key_disk = malloc(sizeof(struct sr_chunk), M_DEVBUF, M_WAITOK | M_ZERO); + km = &key_disk->src_meta; + + key_disk->src_dev_mm = dev; + key_disk->src_vn = vn; + strlcpy(key_disk->src_devname, devname, sizeof(km->scmi.scm_devname)); + key_disk->src_size = 0; + + km->scmi.scm_volid = sd->sd_meta->ssdi.ssd_level; + km->scmi.scm_chunk_id = 0; + km->scmi.scm_size = 0; + km->scmi.scm_coerced_size = 0; + strlcpy(km->scmi.scm_devname, devname, sizeof(km->scmi.scm_devname)); + bcopy(&sd->sd_meta->ssdi.ssd_uuid, &km->scmi.scm_uuid, + sizeof(struct sr_uuid)); + + sr_checksum(sc, km, &km->scm_checksum, + sizeof(struct sr_meta_chunk_invariant)); + + km->scm_status = BIOC_SDONLINE; + + /* + * Create and populate our own discipline and metadata. + */ + + sm = malloc(sizeof(struct sr_metadata), M_DEVBUF, M_WAITOK | M_ZERO); + sm->ssdi.ssd_magic = SR_MAGIC; + sm->ssdi.ssd_version = SR_META_VERSION; + sm->ssd_ondisk = 0; + sm->ssdi.ssd_flags = 0; + bcopy(&sd->sd_meta->ssdi.ssd_uuid, &sm->ssdi.ssd_uuid, + sizeof(struct sr_uuid)); + sm->ssdi.ssd_chunk_no = 1; + sm->ssdi.ssd_volid = SR_KEYDISK_VOLID; + sm->ssdi.ssd_level = SR_KEYDISK_LEVEL; + sm->ssdi.ssd_size = 0; + strlcpy(sm->ssdi.ssd_vendor, "OPENBSD", sizeof(sm->ssdi.ssd_vendor)); + snprintf(sm->ssdi.ssd_product, sizeof(sm->ssdi.ssd_product), + "SR %s", "KEYDISK"); + snprintf(sm->ssdi.ssd_revision, sizeof(sm->ssdi.ssd_revision), + "%03d", SR_META_VERSION); + + fakesd = malloc(sizeof(struct sr_discipline), M_DEVBUF, + M_WAITOK | M_ZERO); + fakesd->sd_sc = sd->sd_sc; + fakesd->sd_meta = sm; + fakesd->sd_meta_type = SR_META_F_NATIVE; + fakesd->sd_vol_status = BIOC_SVONLINE; + strlcpy(fakesd->sd_name, "KEYDISK", sizeof(fakesd->sd_name)); + + /* Add chunk to volume. */ + fakesd->sd_vol.sv_chunks = malloc(sizeof(struct sr_chunk *), M_DEVBUF, + M_WAITOK | M_ZERO); + fakesd->sd_vol.sv_chunks[0] = key_disk; + SLIST_INIT(&fakesd->sd_vol.sv_chunk_list); + SLIST_INSERT_HEAD(&fakesd->sd_vol.sv_chunk_list, key_disk, src_link); + + /* Generate mask key. */ + arc4random_buf(sd->mds.mdd_crypto.scr_maskkey, + sizeof(sd->mds.mdd_crypto.scr_maskkey)); + + /* Copy mask key to optional metadata area. */ + sm->ssdi.ssd_opt_no = 1; + om = &key_disk->src_opt; + om->somi.som_type = SR_OPT_CRYPTO; + bcopy(sd->mds.mdd_crypto.scr_maskkey, &om->somi.som_meta.smm_crypto, + sizeof(om->somi.som_meta.smm_crypto)); + sr_checksum(sc, om, om->som_checksum, + sizeof(struct sr_meta_opt_invariant)); + + /* Save metadata. */ + if (sr_meta_save(fakesd, SR_META_DIRTY)) { + printf("%s: could not save metadata to %s\n", + DEVNAME(sc), devname); + goto fail; + } + + goto done; + +fail: + if (key_disk) + free(key_disk, M_DEVBUF); + key_disk = NULL; + +done: + if (fakesd && fakesd->sd_vol.sv_chunks) + free(fakesd->sd_vol.sv_chunks, M_DEVBUF); + if (fakesd) + free(fakesd, M_DEVBUF); + if (sm) + free(sm, M_DEVBUF); + if (open) { + VOP_CLOSE(vn, FREAD | FWRITE, NOCRED, 0); + vput(vn); + } + + return key_disk; +} + +struct sr_chunk * +sr_crypto_read_key_disk(struct sr_discipline *sd, dev_t dev) +{ + struct sr_softc *sc = sd->sd_sc; + struct sr_metadata *sm = NULL; + struct sr_meta_opt *om; + struct sr_chunk *key_disk = NULL; + struct disklabel label; + struct vnode *vn; + char devname[32]; + int c, part, open = 0; + + /* + * Load a key disk and load keying material into memory. + */ + + sr_meta_getdevname(sc, dev, devname, sizeof(devname)); + + /* Make sure chunk is not already in use. */ + c = sr_chunk_in_use(sc, dev); + if (c != BIOC_SDINVALID && c != BIOC_SDOFFLINE) { + printf("%s: %s is already in use\n", DEVNAME(sc), devname); + goto done; + } + + /* Open device. */ + if (bdevvp(dev, &vn)) { + printf("%s:, sr_create_key_disk: can't allocate vnode\n", + DEVNAME(sc)); + goto done; + } + if (VOP_OPEN(vn, FREAD | FWRITE, NOCRED, 0)) { + DNPRINTF(SR_D_META,"%s: sr_create_key_disk cannot open %s\n", + DEVNAME(sc), devname); + vput(vn); + goto done; + } + open = 1; /* close dev on error */ + + /* Get partition details. */ + part = DISKPART(dev); + if (VOP_IOCTL(vn, DIOCGDINFO, (caddr_t)&label, FREAD, NOCRED, 0)) { + DNPRINTF(SR_D_META, "%s: sr_create_key_disk ioctl failed\n", + DEVNAME(sc)); + VOP_CLOSE(vn, FREAD | FWRITE, NOCRED, 0); + vput(vn); + goto done; + } + if (label.d_partitions[part].p_fstype != FS_RAID) { + printf("%s: %s partition not of type RAID (%d)\n", + DEVNAME(sc), devname, + label.d_partitions[part].p_fstype); + goto done; + } + + /* + * Read and validate key disk metadata. + */ + sm = malloc(SR_META_SIZE * 512, M_DEVBUF, M_ZERO); + if (sm == NULL) { + printf("%s: not enough memory for metadata buffer\n", + DEVNAME(sc)); + goto done; + } + + if (sr_meta_native_read(sd, dev, sm, NULL)) { + printf("%s: native bootprobe could not read native " + "metadata\n", DEVNAME(sc)); + goto done; + } + + if (sr_meta_validate(sd, dev, sm, NULL)) { + DNPRINTF(SR_D_META, "%s: invalid metadata\n", + DEVNAME(sc)); + goto done; + } + + /* Make sure this is a key disk. */ + if (sm->ssdi.ssd_level != SR_KEYDISK_LEVEL) { + printf("%s: %s is not a key disk\n", DEVNAME(sc), devname); + goto done; + } + + /* Construct key disk chunk. */ + key_disk = malloc(sizeof(struct sr_chunk), M_DEVBUF, M_ZERO); + if (key_disk == NULL) { + printf("%s: not enough memory for chunk\n", + DEVNAME(sc)); + goto done; + } + + key_disk->src_dev_mm = dev; + key_disk->src_vn = vn; + key_disk->src_size = 0; + + bcopy((struct sr_meta_chunk *)(sm + 1), &key_disk->src_meta, + sizeof(key_disk->src_meta)); + + /* Read mask key from optional metadata. */ + + if (sm->ssdi.ssd_opt_no > 1) + panic("not yet read > 1 optional metadata members"); + + if (sm->ssdi.ssd_opt_no) { + om = (struct sr_meta_opt *)((u_int8_t *)(sm + 1) + + sizeof(struct sr_meta_chunk) * sm->ssdi.ssd_chunk_no); + bcopy(om, &key_disk->src_opt, sizeof(key_disk->src_opt)); + + if (om->somi.som_type == SR_OPT_CRYPTO) { + bcopy(&om->somi.som_meta.smm_crypto, + sd->mds.mdd_crypto.scr_maskkey, + sizeof(sd->mds.mdd_crypto.scr_maskkey)); + } + } + + open = 0; + +done: + if (sm) + free(sm, M_DEVBUF); + + if (vn && open) { + VOP_CLOSE(vn, FREAD, NOCRED, 0); + vput(vn); + } + + return key_disk; +} + int sr_crypto_alloc_resources(struct sr_discipline *sd) { @@ -671,6 +988,9 @@ sr_crypto_free_resources(struct sr_discipline *sd) DNPRINTF(SR_D_DIS, "%s: sr_crypto_free_resources\n", DEVNAME(sd->sd_sc)); + if (sd->mds.mdd_crypto.key_disk != NULL) + free(sd->mds.mdd_crypto.key_disk, M_DEVBUF); + sr_hotplug_unregister(sd, sr_crypto_hotplug); for (i = 0; sd->mds.mdd_crypto.scr_sid[i] != (u_int64_t)-1; i++) { @@ -698,7 +1018,8 @@ sr_crypto_ioctl(struct sr_discipline *sd, struct bioc_discipline *bd) struct sr_meta_opt *im_so; int size, rv = 1; - DNPRINTF(SR_D_IOCTL, "%s: sr_crypto_ioctl %u\n", DEVNAME(sc), cmd); + DNPRINTF(SR_D_IOCTL, "%s: sr_crypto_ioctl %u\n", + DEVNAME(sd->sd_sc), bd->bd_cmd); switch (bd->bd_cmd) { case SR_IOCTL_GET_KDFHINT: |