diff options
author | Joel Sing <jsing@cvs.openbsd.org> | 2009-11-24 02:19:36 +0000 |
---|---|---|
committer | Joel Sing <jsing@cvs.openbsd.org> | 2009-11-24 02:19:36 +0000 |
commit | b8dcc6872637a4450b8760cbe6dbf7994a5b7959 (patch) | |
tree | e19613e7e1c26839d14183a14cc1f8a46a52c97c /sys/dev/softraid_crypto.c | |
parent | 1e3336cfd7e7b01e181841b629a4a762695afdc0 (diff) |
Allow the passphrase to be changed on softraid crypto volumes. Ensure that
you backup your data and lock up your pets prior to using this.
Tested by todd@
ok marco@
Diffstat (limited to 'sys/dev/softraid_crypto.c')
-rw-r--r-- | sys/dev/softraid_crypto.c | 148 |
1 files changed, 147 insertions, 1 deletions
diff --git a/sys/dev/softraid_crypto.c b/sys/dev/softraid_crypto.c index ceac71829c1..c209f726b7f 100644 --- a/sys/dev/softraid_crypto.c +++ b/sys/dev/softraid_crypto.c @@ -1,4 +1,4 @@ -/* $OpenBSD: softraid_crypto.c,v 1.41 2009/11/24 01:03:54 jsing Exp $ */ +/* $OpenBSD: softraid_crypto.c,v 1.42 2009/11/24 02:19:35 jsing Exp $ */ /* * Copyright (c) 2007 Marco Peereboom <marco@peereboom.us> * Copyright (c) 2008 Hans-Joerg Hoexer <hshoexer@openbsd.org> @@ -62,8 +62,12 @@ int sr_crypto_get_kdf(struct bioc_createraid *, int sr_crypto_decrypt(u_char *, u_char *, u_char *, size_t, int); int sr_crypto_encrypt(u_char *, u_char *, u_char *, size_t, int); int sr_crypto_decrypt_key(struct sr_discipline *); +int sr_crypto_change_maskkey(struct sr_discipline *, + struct sr_crypto_kdfinfo *, struct sr_crypto_kdfinfo *); int sr_crypto_alloc_resources(struct sr_discipline *); int sr_crypto_free_resources(struct sr_discipline *); +int sr_crypto_ioctl(struct sr_discipline *, + struct bioc_discipline *); int sr_crypto_write(struct cryptop *); int sr_crypto_rw(struct sr_workunit *); int sr_crypto_rw2(struct sr_workunit *, struct cryptop *); @@ -91,6 +95,7 @@ sr_crypto_discipline_init(struct sr_discipline *sd) sd->sd_alloc_resources = sr_crypto_alloc_resources; sd->sd_free_resources = sr_crypto_free_resources; sd->sd_start_discipline = NULL; + sd->sd_ioctl_handler = sr_crypto_ioctl; sd->sd_scsi_inquiry = sr_raid_inquiry; sd->sd_scsi_read_cap = sr_raid_read_cap; sd->sd_scsi_tur = sr_raid_tur; @@ -441,6 +446,74 @@ sr_crypto_create_keys(struct sr_discipline *sd) } int +sr_crypto_change_maskkey(struct sr_discipline *sd, + struct sr_crypto_kdfinfo *kdfinfo1, struct sr_crypto_kdfinfo *kdfinfo2) +{ + u_char check_digest[SHA1_DIGEST_LENGTH]; + u_char *p, *c; + size_t ksz; + int rv = 1; + + DNPRINTF(SR_D_DIS, "%s: sr_crypto_change_maskkey\n", + DEVNAME(sd->sd_sc)); + + if (sd->mds.mdd_crypto.scr_meta.scm_check_alg != SR_CRYPTOC_HMAC_SHA1) + goto out; + + c = (u_char *)sd->mds.mdd_crypto.scr_meta.scm_key; + ksz = sizeof(sd->mds.mdd_crypto.scr_key); + p = malloc(ksz, M_DEVBUF, M_WAITOK | M_ZERO); + if (p == NULL) + goto out; + + if (sr_crypto_decrypt(c, p, kdfinfo1->maskkey, ksz, + sd->mds.mdd_crypto.scr_meta.scm_mask_alg) == -1) + goto out; + +#ifdef SR_DEBUG0 + sr_crypto_dumpkeys(sd); +#endif + + sr_crypto_calculate_check_hmac_sha1(kdfinfo1->maskkey, + sizeof(kdfinfo1->maskkey), p, ksz, check_digest); + if (memcmp(sd->mds.mdd_crypto.scr_meta.chk_hmac_sha1.sch_mac, + check_digest, sizeof(check_digest)) != 0) { + rv = EPERM; + goto out; + } + + /* Mask the disk keys. */ + c = (u_char *)sd->mds.mdd_crypto.scr_meta.scm_key; + if (sr_crypto_encrypt(p, c, kdfinfo2->maskkey, ksz, + sd->mds.mdd_crypto.scr_meta.scm_mask_alg) == -1) + goto out; + + /* Prepare key decryption check code. */ + sd->mds.mdd_crypto.scr_meta.scm_check_alg = SR_CRYPTOC_HMAC_SHA1; + sr_crypto_calculate_check_hmac_sha1(kdfinfo2->maskkey, + sizeof(kdfinfo2->maskkey), (u_int8_t *)sd->mds.mdd_crypto.scr_key, + sizeof(sd->mds.mdd_crypto.scr_key), check_digest); + + /* Copy new encrypted key and HMAC to metadata. */ + bcopy(check_digest, sd->mds.mdd_crypto.scr_meta.chk_hmac_sha1.sch_mac, + sizeof(sd->mds.mdd_crypto.scr_meta.chk_hmac_sha1.sch_mac)); + + rv = 0; /* Success */ + +out: + if (p) { + bzero(p, ksz); + free(p, M_DEVBUF); + } + + bzero(check_digest, sizeof(check_digest)); + bzero(&kdfinfo1->maskkey, sizeof(kdfinfo1->maskkey)); + bzero(&kdfinfo2->maskkey, sizeof(kdfinfo2->maskkey)); + + return (rv); +} + +int sr_crypto_alloc_resources(struct sr_discipline *sd) { struct cryptoini cri; @@ -535,6 +608,79 @@ sr_crypto_free_resources(struct sr_discipline *sd) } int +sr_crypto_ioctl(struct sr_discipline *sd, struct bioc_discipline *bd) +{ + struct sr_crypto_kdfpair kdfpair; + struct sr_crypto_kdfinfo kdfinfo1, kdfinfo2; + struct sr_meta_opt *im_so; + int size, rv = 1; + + DNPRINTF(SR_D_IOCTL, "%s: sr_crypto_ioctl %u\n", DEVNAME(sc), cmd); + + switch (bd->bd_cmd) { + case SR_IOCTL_GET_KDFHINT: + + /* Get KDF hint for userland. */ + size = sizeof(sd->mds.mdd_crypto.scr_meta.scm_kdfhint); + if (bd->bd_data == NULL || bd->bd_size > size) + goto bad; + if (copyout(sd->mds.mdd_crypto.scr_meta.scm_kdfhint, + bd->bd_data, bd->bd_size)) + goto bad; + + rv = 0; + + break; + + case SR_IOCTL_CHANGE_PASSPHRASE: + + /* Attempt to change passphrase. */ + + size = sizeof(kdfpair); + if (bd->bd_data == NULL || bd->bd_size > size) + goto bad; + if (copyin(bd->bd_data, &kdfpair, size)) + goto bad; + + size = sizeof(kdfinfo1); + if (kdfpair.kdfinfo1 == NULL || kdfpair.kdfsize1 > size) + goto bad; + if (copyin(kdfpair.kdfinfo1, &kdfinfo1, size)) + goto bad; + + size = sizeof(kdfinfo2); + if (kdfpair.kdfinfo2 == NULL || kdfpair.kdfsize2 > size) + goto bad; + if (copyin(kdfpair.kdfinfo2, &kdfinfo2, size)) + goto bad; + + if (sr_crypto_change_maskkey(sd, &kdfinfo1, &kdfinfo2)) + goto bad; + + /* + * Copy encrypted key/passphrase into metadata. + */ + + /* Only one chunk in crypto volumes... */ + im_so = &sd->sd_vol.sv_chunks[0]->src_opt; + bcopy(&sd->mds.mdd_crypto.scr_meta, + &im_so->somi.som_meta.smm_crypto, + sizeof(im_so->somi.som_meta.smm_crypto)); + + sr_checksum(sd->sd_sc, im_so, im_so->som_checksum, + sizeof(struct sr_meta_opt_invariant)); + + /* Save metadata to disk. */ + rv = sr_meta_save(sd, SR_META_DIRTY); + + break; + } + +bad: + return (rv); +} + +int sr_crypto_rw(struct sr_workunit *wu) { struct cryptop *crp; |