summaryrefslogtreecommitdiff
path: root/sys/dev/softraid_crypto.c
diff options
context:
space:
mode:
authorJoel Sing <jsing@cvs.openbsd.org>2009-12-31 14:00:46 +0000
committerJoel Sing <jsing@cvs.openbsd.org>2009-12-31 14:00:46 +0000
commit6c48a0797c3ce6da990d15cfd72f07f067df8e92 (patch)
tree9a9a5dbe741b0f8687bbcd65c0491e04aef8c4ee /sys/dev/softraid_crypto.c
parentde50ae56c1a30c7995a9b681464dbd29776af065 (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.c353
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: