Index: conf/files
===================================================================
RCS file: /cvs/src/sys/conf/files,v
retrieving revision 1.592
diff -u -p -r1.592 files
--- conf/files 11 May 2015 06:46:21 -0000 1.592
+++ conf/files 24 May 2015 12:29:00 -0000
@@ -517,6 +517,8 @@ attach softraid at root
file dev/softraid.c softraid needs-flag
file dev/softraid_concat.c softraid
file dev/softraid_crypto.c softraid & crypto
+file dev/softraid_luks.c softraid & crypto
+file dev/luks.c softraid & crypto
file dev/softraid_raid0.c softraid
file dev/softraid_raid1.c softraid
file dev/softraid_raid5.c softraid
@@ -686,7 +688,7 @@ file kern/kern_synch.c
file kern/kern_tc.c
file kern/kern_time.c
file kern/kern_timeout.c
-file kern/kern_uuid.c gpt
+file kern/kern_uuid.c gpt | (softraid & crypto)
file kern/kern_watchdog.c !small_kernel
file kern/kern_task.c
file kern/kern_xxx.c
Index: crypto/xform.c
===================================================================
RCS file: /cvs/src/sys/crypto/xform.c,v
retrieving revision 1.46
diff -u -p -r1.46 xform.c
--- crypto/xform.c 14 Mar 2015 03:38:46 -0000 1.46
+++ crypto/xform.c 24 May 2015 12:02:30 -0000
@@ -18,6 +18,8 @@
*
* AES XTS implementation in 2008 by Damien Miller
*
+ * PBKDF2: Copyright (c) 2008 Damien Bergamini <damien.bergam...@free.fr>
+ *
* Copyright (C) 1995, 1996, 1997, 1998, 1999 by John Ioannidis,
* Angelos D. Keromytis and Niels Provos.
*
@@ -46,6 +48,8 @@
#include <sys/errno.h>
#include <sys/time.h>
#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/stdint.h>
#include <machine/cpu.h>
#include <crypto/md5.h>
@@ -58,6 +62,7 @@
#include <crypto/cryptodev.h>
#include <crypto/xform.h>
#include <crypto/gmac.h>
+#include <crypto/hmac.h>
extern void des_ecb3_encrypt(caddr_t, caddr_t, caddr_t, caddr_t, caddr_t, int);
extern void des_ecb_encrypt(caddr_t, caddr_t, caddr_t, int);
@@ -675,4 +680,64 @@ lzs_dummy(u_int8_t *data, u_int32_t size
{
*out = NULL;
return (0);
+}
+
+/*
+ * Password-Based Key Derivation Function 2 (PKCS #5 v2.0).
+ * Code based on IEEE Std 802.11-2007, Annex H.4.2.
+ */
+int
+pkcs5_pbkdf2(const char *pass, size_t pass_len, const char *salt,
+ size_t salt_len, u_int8_t *key, size_t key_len, u_int rounds)
+{
+ HMAC_SHA1_CTX ctx;
+ u_int8_t *asalt, obuf[SHA1_DIGEST_LENGTH];
+ u_int8_t d1[SHA1_DIGEST_LENGTH], d2[SHA1_DIGEST_LENGTH];
+ u_int i, j;
+ u_int count;
+ size_t r;
+
+ if (rounds < 1 || key_len == 0)
+ return -1;
+ if (salt_len == 0 || salt_len > SIZE_MAX - 1)
+ return -1;
+ asalt = malloc(salt_len + 4, M_TEMP, M_NOWAIT);
+ if (asalt == NULL)
+ return -1;
+
+ memcpy(asalt, salt, salt_len);
+
+ for (count = 1; key_len > 0; count++) {
+ asalt[salt_len + 0] = (count >> 24) & 0xff;
+ asalt[salt_len + 1] = (count >> 16) & 0xff;
+ asalt[salt_len + 2] = (count >> 8) & 0xff;
+ asalt[salt_len + 3] = count & 0xff;
+ HMAC_SHA1_Init(&ctx, pass, pass_len);
+ HMAC_SHA1_Update(&ctx, asalt, salt_len + 4);
+ HMAC_SHA1_Final(d1, &ctx);
+ memcpy(obuf, d1, sizeof(obuf));
+
+ for (i = 1; i < rounds; i++) {
+ HMAC_SHA1_Init(&ctx, pass, pass_len);
+ HMAC_SHA1_Update(&ctx, d1, sizeof d1);
+ HMAC_SHA1_Final(d2, &ctx);
+
+ memcpy(d1, d2, sizeof(d1));
+ for (j = 0; j < sizeof(obuf); j++)
+ obuf[j] ^= d1[j];
+ }
+
+ r = MIN(key_len, SHA1_DIGEST_LENGTH);
+ memcpy(key, obuf, r);
+ key += r;
+ key_len -= r;
+ };
+ explicit_bzero(asalt, salt_len + 4);
+ explicit_bzero(d1, sizeof(d1));
+ explicit_bzero(d2, sizeof(d2));
+ explicit_bzero(obuf, sizeof(obuf));
+ explicit_bzero(&ctx, sizeof ctx);
+ free(asalt, M_TEMP, salt_len + 4);
+
+ return 0;
}
Index: dev/luks.c
===================================================================
RCS file: dev/luks.c
diff -N dev/luks.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ dev/luks.c 24 May 2015 12:02:30 -0000
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 2015 Martin Pelikan <peli...@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+
+#include <lib/libsa/aes_xts.h>
+#include <lib/libsa/pbkdf2.h>
+#include <crypto/cryptodev.h>
+#include <crypto/sha1.h>
+
+#include <dev/softraidvar.h>
+#include <dev/softraid_luks.h>
+#include <dev/luks.h>
+
+#define LUKS_INFLATED_KEY_MAX (4 * 1024 * 1024)
+
+static const u_int8_t luks_magic[] = LUKS_MAGIC;
+struct luks_cipher luks_ciphers[] = {
+ { "aes", "xts-plain64", CRYPTO_AES_XTS, 128 * 2, 256 * 2 },
+ { "blowfish", "cbc-plain", CRYPTO_BLF_CBC, 32, 256 },
+};
+struct luks_hash luks_hashes[] = {
+ { "sha1", CRYPTO_SHA1 },
+};
+
+/*
+ * It's now obvious where that "implementation error" came from.
+ * H1 only reads i-th hunk to compute i-th hash and therefore
+ * can be done in place. H2 needs the whole stripe all the time.
+ * Because neither SHA1 nor XOR touch bits where they don't operate,
+ * the repeated-H1 process is parallelizable across the hunks while
+ * the repeated-H2 has to wait before it uses the next stripe.
+ */
+static void
+H1(u_int8_t *stripe, size_t len)
+{
+ u_int8_t digest[SHA1_DIGEST_LENGTH];
+ const size_t blocks = len / sizeof digest;
+ const size_t oddblk = len % sizeof digest;
+ u_int32_t i, i_be;
+ SHA1_CTX shactx;
+
+ for (i = 0; i < blocks; ++i) {
+ u_int8_t *hunk = stripe + (i * sizeof digest);
+
+ SHA1Init(&shactx);
+ i_be = htobe32(i);
+ SHA1Update(&shactx, (void *) &i_be, sizeof i_be);
+ SHA1Update(&shactx, hunk, sizeof digest);
+ SHA1Final(hunk, &shactx);
+ }
+
+ if (oddblk) {
+ u_int8_t *hunk = stripe + (blocks * sizeof digest);
+
+ SHA1Init(&shactx);
+ i_be = htobe32(i);
+ SHA1Update(&shactx, (void *) &i_be, sizeof i_be);
+ SHA1Update(&shactx, hunk, oddblk);
+ SHA1Final(digest, &shactx);
+ memcpy(hunk, digest, oddblk);
+ }
+}
+
+#ifdef notyet
+/*
+ * Obfuscate the master key before its encryption by making up (N - 1) stripes
+ * of random data, constructing a cryptographically safe dependency among them,
+ * XORing that last value with the key, then encrypt and store the whole thing.
+ */
+static void
+afsplit(u_int8_t *dst, const u_int8_t *src, size_t len, size_t stripes)
+{
+ /* Use the last stripe as temporary. */
+ u_int8_t *temp = dst + (len * (stripes - 1));
+ size_t i, n;
+
+ /* s_1 ... s_{n-1} randomly chosen */
+ arc4random_buf(dst, temp - dst);
+
+ /* d_0 = 0 */
+ memset(temp, 0, len);
+
+ /* s-indexes go from 1, but this loop and d-indexes go from 0 */
+ for (n = 0; n < stripes - 1; ++n) {
+ u_int8_t *current = dst + (len * n);
+
+ /* d_{k-1} ^ s_k */
+ for (i = 0; i < len; ++i)
+ temp[i] ^= current[i];
+
+ /* d_k = H(last_step) */
+ H1(temp, len);
+ }
+
+ /* s_n = d_{n-1} ^ D */
+ for (i = 0; i < len; ++i)
+ temp[i] ^= src[i];
+}
+#endif /* notyet */
+
+/*
+ * De-obfuscate the master key material and return the key itself.
+ * Note that the last de-obfuscation stripe is constructed in the
+ * same manner as above. Code duplicated and renamed for clarity.
+ */
+static void
+afmerge(u_int8_t * __restrict key, const u_int8_t * __restrict src, size_t len,
+ size_t stripes)
+{
+ size_t i, n;
+
+ /* d_0 = 0 */
+ memset(key, 0, len);
+
+ /* s-indexes go from 1, but this loop and d-indexes go from 0 */
+ for (n = 0; n < stripes - 1; ++n) {
+ const u_int8_t *current = src + (len * n);
+
+ /* d_{k-1} ^ s_k */
+ for (i = 0; i < len; ++i)
+ key[i] ^= current[i];
+
+ /* d_k = H(last_step) */
+ H1(key, len);
+ }
+
+ /* D = d_{n-1} ^ s_n */
+ src += len * (stripes - 1);
+ for (i = 0; i < len; ++i)
+ key[i] ^= src[i];
+}
+
+int
+luks_deobfuscate_key(struct luks_phdr *luks, void *masterkey, void *userkey,
+ void *userkdf, size_t bytes, size_t stripes)
+{
+ u_int8_t digest[LUKS_DIGEST_LEN];
+
+ afmerge(masterkey, userkey, bytes, stripes);
+
+ /* XXX will need to be replaced to support non-SHA1 hashes. */
+ if (pkcs5_pbkdf2(masterkey, bytes,
+ luks->master_key_pbkdf2_salt, LUKS_SALT_LEN,
+ digest, sizeof digest,
+ betoh32(luks->master_key_pbkdf2_iterations)))
+ return (EPERM);
+
+ return (memcmp(luks->master_key_pbkdf2, digest, sizeof digest) ?
+ EPERM : 0);
+}
+
+static void
+luks_choose_cipher_hash(struct sr_luks *sl, u_int32_t keylen)
+{
+ struct luks_phdr *h = sl->sl_phdr;
+ int i;
+
+ /* Figure out if we can use the cipher and hash specified. */
+ for (i = 0; i < sizeof luks_ciphers / sizeof *luks_ciphers; ++i) {
+ if (strncmp(luks_ciphers[i].lc_name,
+ h->cipher_name, sizeof h->cipher_name))
+ continue;
+
+ if (strncmp(luks_ciphers[i].lc_mode,
+ h->cipher_mode, sizeof h->cipher_mode))
+ continue;
+
+ if (keylen * CHAR_BIT < luks_ciphers[i].lc_klen_min ||
+ keylen * CHAR_BIT > luks_ciphers[i].lc_klen_max)
+ continue;
+
+ sl->sl_alg = luks_ciphers[i].lc_alg;
+ sl->sl_keylen = keylen * CHAR_BIT;
+ break;
+ }
+
+ for (i = 0; i < sizeof luks_hashes / sizeof *luks_hashes; ++i) {
+ if (strncmp(luks_hashes[i].lh_name,
+ h->hash_spec, sizeof h->hash_spec))
+ continue;
+
+ sl->sl_hash = luks_hashes[i].lh_alg;
+ break;
+ }
+}
+
+const char *
+luks_phdr_parse_and_validate(struct sr_luks *sl, int64_t last_sector)
+{
+ struct luks_phdr *h = sl->sl_phdr;
+ u_int32_t payload_at = betoh32(h->payload_offset);
+ u_int32_t keylen = betoh32(h->master_key_bytes);
+ int i;
+
+ if (last_sector < 2)
+ return "disk too small";
+
+ if (memcmp(h->magic, luks_magic, sizeof luks_magic))
+ return "bad magic constant";
+
+ if (h->version != htobe16(1))
+ return "bad version";
+
+ if (payload_at > last_sector)
+ return "payload beyond end of disk";
+
+ if (keylen == 0)
+ return "zero key length";
+
+ luks_choose_cipher_hash(sl, keylen);
+ if (sl->sl_alg == 0 || sl->sl_hash == 0)
+ return "cipher or hash not supported";
+
+ /* Check all the key positions for sanity. */
+ for (i = 0; i < LUKS_NUM_KEYS; ++i) {
+ struct luks_key *k = h->keyblock + i;
+ int64_t key_at = betoh32(k->key_material_offset);
+ int64_t inflated = keylen * betoh32(k->stripes);
+
+ switch (betoh32(k->active)) {
+ case LUKS_KEY_ACTIVE_MAGIC:
+ case LUKS_KEY_INACTIVE_MAGIC:
+ break;
+ default:
+ return "key slot invalid";
+ }
+
+ if (inflated > LUKS_INFLATED_KEY_MAX)
+ return "inflated key too big";
+ inflated /= DEV_BSIZE;
+ if (key_at + inflated > payload_at)
+ return "inflated key beyond end of disk";
+ }
+
+ return (NULL);
+}
Index: dev/luks.h
===================================================================
RCS file: dev/luks.h
diff -N dev/luks.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ dev/luks.h 24 May 2015 12:02:30 -0000
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2015 Martin Pelikan <peli...@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#pragma once
+
+/*
+ * Linux Unified Key Setup on-disk data structure format, from the spec at:
+ *
https://gitlab.com/cryptsetup/cryptsetup/wikis/LUKS-standard/on-disk-format.pdf
+ * Fields are big-endian.
+ */
+
+#define LUKS_MAGIC { 'L', 'U', 'K', 'S', 0xBA, 0xBE }
+#define LUKS_MAGIC_LEN 6
+#define LUKS_CIPHER_NAME_LEN 32
+#define LUKS_CIPHER_MODE_LEN 32
+#define LUKS_HASH_SPEC_LEN 32
+#define LUKS_DIGEST_LEN 20
+#define LUKS_SALT_LEN 32
+#define LUKS_UUID_STRING_LEN 40
+
+#define LUKS_NUM_KEYS 8
+#define LUKS_KEY_ACTIVE_MAGIC 0x00AC71F3
+#define LUKS_KEY_INACTIVE_MAGIC 0x0000DEAD
+
+struct luks_key {
+ u_int32_t active;
+ u_int32_t pbkdf2_iterations;
+ u_int8_t pbkdf2_salt[LUKS_SALT_LEN];
+ u_int32_t key_material_offset; /* in sectors */
+ u_int32_t stripes;
+};
+
+struct luks_phdr {
+ u_int8_t magic[LUKS_MAGIC_LEN];
+ u_int16_t version;
+ char cipher_name[LUKS_CIPHER_NAME_LEN];
+ char cipher_mode[LUKS_CIPHER_MODE_LEN];
+ char hash_spec[LUKS_HASH_SPEC_LEN];
+ u_int32_t payload_offset; /* in sectors */
+ u_int32_t master_key_bytes;
+ u_int8_t master_key_pbkdf2[LUKS_DIGEST_LEN];
+ u_int8_t master_key_pbkdf2_salt[LUKS_SALT_LEN];
+ u_int32_t master_key_pbkdf2_iterations;
+ char uuid[LUKS_UUID_STRING_LEN]; /* no comment. */
+
+ struct luks_key keyblock[LUKS_NUM_KEYS];
+};
Index: dev/softraid.c
===================================================================
RCS file: /cvs/src/sys/dev/softraid.c,v
retrieving revision 1.353
diff -u -p -r1.353 softraid.c
--- dev/softraid.c 20 May 2015 15:21:57 -0000 1.353
+++ dev/softraid.c 24 May 2015 12:02:30 -0000
@@ -48,6 +48,7 @@
#include <scsi/scsi_disk.h>
#include <dev/softraidvar.h>
+#include <dev/softraid_luks.h>
#ifdef HIBERNATE
#include <lib/libsa/aes_xts.h>
@@ -154,8 +155,6 @@ void sr_sensors_delete(struct
sr_disci
/* metadata */
int sr_meta_probe(struct sr_discipline *, dev_t *, int);
int sr_meta_attach(struct sr_discipline *, int, int);
-int sr_meta_rw(struct sr_discipline *, dev_t, void *,
- size_t, daddr_t, long);
int sr_meta_clear(struct sr_discipline *);
void sr_meta_init(struct sr_discipline *, int, int);
void sr_meta_init_complete(struct sr_discipline *);
@@ -201,8 +200,8 @@ void sr_meta_print(struct
sr_metadata
/* the metadata driver should remain stateless */
struct sr_meta_driver {
- daddr_t smd_offset; /* metadata location */
- u_int32_t smd_size; /* size of metadata */
+ daddr_t smd_offset; /* metadata location (bytes) */
+ u_int32_t smd_size; /* size of metadata (bytes) */
int (*smd_probe)(struct sr_softc *,
struct sr_chunk *);
@@ -218,7 +217,10 @@ struct sr_meta_driver {
{ SR_META_OFFSET, SR_META_SIZE * 512,
sr_meta_native_probe, sr_meta_native_attach, NULL,
sr_meta_native_read, sr_meta_native_write, NULL },
- { 0, 0, NULL, NULL, NULL, NULL }
+ { LUKS_META_OFFSET, LUKS_META_SIZE,
+ luks_meta_probe, luks_meta_attach, NULL,
+ luks_meta_read, luks_meta_write, luks_meta_validate },
+ { 0, 0, NULL, NULL, NULL, NULL, NULL, NULL }
};
int
@@ -770,13 +772,7 @@ sr_meta_read(struct sr_discipline *sd)
continue;
}
- if (sm->ssdi.ssd_magic != SR_MAGIC) {
- DNPRINTF(SR_D_META, "%s: sr_meta_read !SR_MAGIC\n",
- DEVNAME(sc));
- continue;
- }
-
- /* validate metadata */
+ /* validate metadata and convert to softraid native format */
if (sr_meta_validate(sd, ch_entry->src_dev_mm, sm, fm)) {
DNPRINTF(SR_D_META, "%s: invalid metadata\n",
DEVNAME(sc));
@@ -3403,8 +3399,8 @@ sr_ioctl_createraid(struct sr_softc *sc,
/* Ensure metadata level matches requested assembly level. */
if (sd->sd_meta->ssdi.ssd_level != bc->bc_level) {
- sr_error(sc, "volume level does not match metadata "
- "level");
+ sr_error(sc, "volume level %u does not match metadata "
+ "level %u", sd->sd_meta->ssdi.ssd_level,
bc->bc_level);
goto unwind;
}
@@ -3965,6 +3961,9 @@ sr_discipline_init(struct sr_discipline
#ifdef CRYPTO
case 'C':
sr_crypto_discipline_init(sd);
+ break;
+ case 'L':
+ sr_luks_discipline_init(sd);
break;
#endif
case 'c':
Index: dev/softraid_crypto.c
===================================================================
RCS file: /cvs/src/sys/dev/softraid_crypto.c,v
retrieving revision 1.117
diff -u -p -r1.117 softraid_crypto.c
--- dev/softraid_crypto.c 14 Mar 2015 03:38:46 -0000 1.117
+++ dev/softraid_crypto.c 24 May 2015 12:02:30 -0000
@@ -1127,7 +1127,7 @@ sr_crypto_write(struct cryptop *crp)
struct sr_workunit *wu = &crwu->cr_wu;
int s;
- DNPRINTF(SR_D_INTR, "%s: sr_crypto_write: wu %x xs: %x\n",
+ DNPRINTF(SR_D_INTR, "%s: sr_crypto_write: wu %p xs: %p\n",
DEVNAME(wu->swu_dis->sd_sc), wu, wu->swu_xs);
if (crp->crp_etype) {
@@ -1206,7 +1206,7 @@ sr_crypto_read(struct cryptop *crp)
struct sr_workunit *wu = &crwu->cr_wu;
int s;
- DNPRINTF(SR_D_INTR, "%s: sr_crypto_read: wu %x xs: %x\n",
+ DNPRINTF(SR_D_INTR, "%s: sr_crypto_read: wu %p xs: %p\n",
DEVNAME(wu->swu_dis->sd_sc), wu, wu->swu_xs);
if (crp->crp_etype)
Index: dev/softraid_luks.c
===================================================================
RCS file: dev/softraid_luks.c
diff -N dev/softraid_luks.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ dev/softraid_luks.c 24 May 2015 12:02:30 -0000
@@ -0,0 +1,776 @@
+/*
+ * Copyright (c) 2015 Martin Pelikan <peli...@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * This is a bridge between a LUKS implementation and the softraid(4)
framework.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/queue.h>
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/malloc.h>
+#include <sys/pool.h>
+#include <sys/atomic.h>
+#include <sys/stdint.h>
+#include <sys/disklabel.h>
+#include <sys/vnode.h>
+#include <sys/disk.h>
+#include <sys/dkio.h>
+#include <sys/uuid.h>
+
+#include <crypto/cryptodev.h>
+
+#include <dev/softraidvar.h>
+#include <dev/softraid_luks.h>
+#include <dev/luks.h>
+
+/* Decrypting and verifying eight keys can be done in parallel. */
+struct user_key_context {
+ struct task task;
+ struct sr_discipline *sd;
+ void *kdf;
+ void *masterkey;
+ volatile unsigned int *done;
+ u_int64_t sessid;
+ struct cryptoini cri;
+ struct uio uio;
+ struct iovec iov;
+ int keyidx;
+ int error;
+};
+
+extern struct taskq *crypto_taskq;
+
+static int sr_luks_crypto_init(struct sr_discipline *);
+static void sr_luks_hotplug(struct sr_discipline *, struct disk *, int);
+
+/* virtual methods of our sr_discipline, all return errno */
+int sr_luks_alloc_resources(struct sr_discipline *);
+int sr_luks_assemble(struct sr_discipline *,
+ struct bioc_createraid *, int, void *);
+int sr_luks_create(struct sr_discipline *,
+ struct bioc_createraid *, int, int64_t);
+void sr_luks_free_resources(struct sr_discipline *);
+int sr_luks_rw(struct sr_workunit *);
+void sr_luks_done(struct sr_workunit *);
+
+
+void
+sr_luks_discipline_init(struct sr_discipline *sd)
+{
+ strlcpy(sd->sd_name, "LUKS", sizeof sd->sd_name);
+ sd->sd_capabilities = SR_CAP_SYSTEM_DISK;
+ sd->sd_max_wu = SR_CRYPTO_NOWU;
+
+ sd->mds.mdd_luks.sl_sessionid = (u_int64_t)-1;
+
+ /* Virtual methods doing the actual work. */
+ sd->sd_alloc_resources = sr_luks_alloc_resources;
+ sd->sd_assemble = sr_luks_assemble;
+ sd->sd_create = sr_luks_create;
+ sd->sd_free_resources = sr_luks_free_resources;
+ sd->sd_scsi_rw = sr_luks_rw;
+ sd->sd_scsi_done = sr_luks_done;
+}
+
+int
+luks_meta_probe(struct sr_softc *sc, struct sr_chunk *ch_entry)
+{
+ struct disklabel label;
+ int error, part = DISKPART(ch_entry->src_dev_mm);
+ daddr_t size;
+#ifdef SR_DEBUG
+ char *devname = ch_entry->src_devname;
+#endif
+
+ error = VOP_IOCTL(ch_entry->src_vn, DIOCGDINFO, (caddr_t)&label, FREAD,
+ NOCRED, curproc);
+ if (error) {
+ DNPRINTF(SR_D_META, "%s: %s can't obtain disklabel\n",
+ DEVNAME(sc), devname);
+ goto fail;
+ }
+ memcpy(ch_entry->src_duid, label.d_uid, sizeof ch_entry->src_duid);
+
+ if (label.d_partitions[part].p_fstype != FS_LUKS) {
+ DNPRINTF(SR_D_META, "%s: %s partition not of type LUKS (%d)\n",
+ DEVNAME(sc), devname, label.d_partitions[part].p_fstype);
+ goto fail;
+ }
+
+ size = DL_SECTOBLK(&label, DL_GETPSIZE(&label.d_partitions[part]));
+ if (size <= LUKS_META_SIZE) {
+ DNPRINTF(SR_D_META, "%s: %s partition too small: %llu blocks\n",
+ DEVNAME(sc), devname, size);
+ goto fail;
+ }
+ ch_entry->src_size = size;
+
+ DNPRINTF(SR_D_META, "%s: probe found %s size %zu blocks\n", DEVNAME(sc),
+ devname, (size_t) size);
+
+ return (SR_META_F_LUKS);
+ fail:
+ DNPRINTF(SR_D_META, "%s: invalid device: %s\n", DEVNAME(sc),
+ devname ? devname : "no device");
+ return (SR_META_F_INVALID);
+}
+
+int
+luks_meta_attach(struct sr_discipline *sd, int force)
+{
+ struct sr_chunk_head *cl = &sd->sd_vol.sv_chunk_list;
+ struct sr_chunk *ch_entry;
+
+ DNPRINTF(SR_D_META, "%s: luks_meta_attach\n", DEVNAME(sd->sd_sc));
+
+ /* Store our chunk size; there should be only one chunk anyway. */
+ sd->sd_vol.sv_chunk_minsz = INT64_MAX;
+ sd->sd_vol.sv_chunk_maxsz = 0;
+
+ SLIST_FOREACH(ch_entry, cl, src_link) {
+ if (ch_entry->src_size < sd->sd_vol.sv_chunk_minsz)
+ sd->sd_vol.sv_chunk_minsz = ch_entry->src_size;
+ if (ch_entry->src_size > sd->sd_vol.sv_chunk_maxsz)
+ sd->sd_vol.sv_chunk_maxsz = ch_entry->src_size;
+
+ /* No integrity checking, everything is always online. */
+ ch_entry->src_meta.scm_status = BIOC_SDONLINE;
+ }
+
+ if (sd->sd_vol.sv_chunk_minsz != sd->sd_vol.sv_chunk_maxsz) {
+ sr_error(sd->sd_sc, "too many LUKS chunks\n");
+ return (1);
+ }
+
+ return (0);
+}
+
+int
+luks_meta_read(struct sr_discipline *sd, dev_t dev, struct sr_metadata *md,
+ void *fm)
+{
+ /*
+ * If the caller wants foreign metadata, we must leave @md intact,
+ * because native metadata can contain headers well past sizeof(md)
+ * boundary, which we would fill with our rubbish.
+ */
+ if (fm == NULL) {
+ DNPRINTF(SR_D_META, "didn't ask for foreign metadata!\n");
+ return sr_meta_rw(sd, dev, md,
+ LUKS_META_SIZE, LUKS_META_OFFSET, B_READ);
+ }
+ return sr_meta_rw(sd, dev, fm,
+ LUKS_META_SIZE, LUKS_META_OFFSET, B_READ);
+}
+
+int
+luks_meta_write(struct sr_discipline *sd, dev_t dev, struct sr_metadata *md,
+ void *fm)
+{
+ if ((sd->sd_meta->ssd_meta_flags & SR_META_DIRTY) == 0)
+ return (0);
+
+ return (EOPNOTSUPP);
+}
+
+/* Whatever we fake in @md will be relied upon and accessed as sd->sd_meta. */
+int
+luks_meta_validate(struct sr_discipline *sd, struct sr_metadata *md, void *fm)
+{
+ struct luks_phdr *luks;
+ size_t chunksz, start;
+ const char *errstr;
+ int error;
+
+ chunksz = sd->sd_vol.sv_chunk_minsz;
+
+ /* Preserve the real LUKS metadata for future reference. */
+ luks = malloc(sizeof *luks, M_DEVBUF, M_NOWAIT);
+ if (luks == NULL) {
+ sr_error(sd->sd_sc, "out of memory for LUKS phdr");
+ return (1);
+ }
+ memcpy(luks, fm, sizeof *luks);
+ sd->mds.mdd_luks.sl_phdr = luks;
+ errstr = luks_phdr_parse_and_validate(&sd->mds.mdd_luks, chunksz);
+ if (errstr != NULL) {
+ sr_error(sd->sd_sc, "invalid LUKS header: %s", errstr);
+ return (1);
+ }
+
+ /* Pretend this is a softraid(4) volume by faking its metadata. */
+ memset(md, 0, sizeof *md);
+ md->ssdi.ssd_magic = SR_MAGIC;
+ md->ssdi.ssd_version = SR_META_VERSION;
+
+ strlcpy(md->ssdi.ssd_vendor, "LUKS", sizeof md->ssdi.ssd_vendor);
+ strlcpy(md->ssdi.ssd_product, luks->cipher_name,
+ sizeof md->ssdi.ssd_product);
+ strlcat(md->ssdi.ssd_product, ",", sizeof md->ssdi.ssd_product);
+ strlcat(md->ssdi.ssd_product, luks->cipher_mode,
+ sizeof md->ssdi.ssd_product);
+
+ error = uuid_from_string(luks->uuid, &md->ssdi.ssd_uuid);
+ if (error) {
+ printf("uuid_from_string %s failed: %d\n", luks->uuid, error);
+ return (1);
+ }
+ md->ssdi.ssd_opt_no = 0;
+ md->ssdi.ssd_level = 'L';
+
+ /* The whole volume is encrypted using just one master key. */
+ sd->sd_max_ccb_per_wu = md->ssdi.ssd_chunk_no = 1;
+
+ /* Compute the size of the device minus physical metadata. */
+ start = betoh32(luks->payload_offset);
+ md->ssdi.ssd_size = chunksz - start;
+ md->ssd_data_offset = start * DEV_BSIZE;
+
+ DNPRINTF(SR_D_META, "%s: available %llu blocks (%zu - %zu)\n",
+ DEVNAME(sd->sd_sc), md->ssdi.ssd_size, start, chunksz);
+
+ /* Fake our own checksum as well. */
+ sr_checksum(sd->sd_sc, md,
+ &md->ssd_checksum, sizeof(struct sr_meta_invariant));
+
+ return (0);
+}
+
+int
+sr_luks_unlock_key_callback(struct cryptop *arg)
+{
+ /* nothing here, but crypto(9) needs something. */
+ return (0);
+}
+
+void
+sr_luks_unlock_key(void *arg)
+{
+ struct user_key_context *ctx = arg;
+ struct sr_discipline *sd = ctx->sd;
+ struct sr_luks *sl = &sd->mds.mdd_luks;
+ struct luks_phdr *luks = sl->sl_phdr;
+ struct cryptop *crp;
+ struct cryptodesc *crd;
+ size_t bytes, stripes, offset, len;
+ void *userkey;
+ u_int64_t blk;
+ int i = ctx->keyidx;
+ dev_t dev;
+
+ bytes = sl->sl_keylen / CHAR_BIT;
+
+ KASSERT(luks->keyblock[i].active == htobe32(LUKS_KEY_ACTIVE_MAGIC));
+
+ /* Load the key from disk. */
+ stripes = betoh32(luks->keyblock[i].stripes);
+ offset = betoh32(luks->keyblock[i].key_material_offset);
+ userkey = mallocarray(stripes, bytes, M_DEVBUF, M_WAITOK);
+ len = stripes * bytes;
+
+ DNPRINTF(SR_D_DIS, "%s: sr_luks_unlock: key %d at sector %zu: "
+ "%zu bytes x %zu stripes\n",
+ DEVNAME(sd->sd_sc), i, offset, bytes, stripes);
+
+ dev = sd->sd_vol.sv_chunks[0]->src_dev_mm;
+ ctx->error = sr_meta_rw(sd, dev, userkey, len, offset, B_READ);
+ if (ctx->error) {
+ printf("%s: read(userkey) %d: %d\n", __func__, i, ctx->error);
+ goto done;
+ }
+
+ ctx->cri.cri_alg = sl->sl_alg;
+ ctx->cri.cri_klen = sl->sl_keylen;
+ ctx->cri.cri_key = ctx->kdf;
+
+ ctx->error = crypto_newsession(&ctx->sessid, &ctx->cri, 0);
+ if (ctx->error) {
+ printf("%s: crypto_newsession for key %d: %d\n", __func__, i,
+ ctx->error);
+ goto done;
+ }
+
+ crp = crypto_getreq(len / DEV_BSIZE);
+ if (crp == NULL) {
+ printf("%s: crypto_getreq for key %d\n", __func__, i);
+ goto freesession;
+ }
+
+ ctx->iov.iov_base = userkey;
+ ctx->iov.iov_len = len;
+
+ ctx->uio.uio_iovcnt = 1;
+ ctx->uio.uio_iov = &ctx->iov;
+
+ crp->crp_buf = &ctx->uio;
+ crp->crp_sid = ctx->sessid;
+ crp->crp_flags = CRYPTO_F_NOQUEUE;
+ crp->crp_ilen = len;
+ crp->crp_alloctype = M_DEVBUF;
+ crp->crp_callback = sr_luks_unlock_key_callback;
+
+ /* Prepare descriptors with per-block IVs and KDF result as input. */
+ for (blk = 0, crd = crp->crp_desc; crd; ++blk, crd = crd->crd_next) {
+ KASSERT(crd != NULL);
+
+ crd->crd_skip = blk << DEV_BSHIFT;
+ crd->crd_len = DEV_BSIZE;
+ crd->crd_inject = 0;
+ crd->crd_flags = CRD_F_IV_PRESENT | CRD_F_IV_EXPLICIT;
+ crd->crd_alg = sl->sl_alg;
+ crd->crd_klen = sl->sl_keylen;
+ crd->crd_key = ctx->kdf;
+ memcpy(crd->crd_iv, &blk, sizeof blk);
+ }
+
+ /* Decrypt the obfuscated master key, */
+ ctx->error = crypto_invoke(crp);
+ if (ctx->error) {
+ printf("%s: crypto_invoke %d: %d\n", __func__, i, ctx->error);
+ goto freecrp;
+ }
+
+ ctx->masterkey = malloc(bytes, M_DEVBUF, M_WAITOK);
+
+ ctx->error = luks_deobfuscate_key(luks, ctx->masterkey, userkey,
+ ctx->kdf, bytes, stripes);
+ if (ctx->error) {
+ /* Bad passphrase, probably. */
+ explicit_bzero(ctx->masterkey, bytes);
+ free(ctx->masterkey, M_DEVBUF, bytes);
+ ctx->masterkey = NULL;
+ goto freecrp;
+ }
+
+ freecrp:
+ crypto_freereq(crp);
+ freesession:
+ crypto_freesession(ctx->sessid);
+ done:
+ explicit_bzero(userkey, len);
+ free(userkey, M_DEVBUF, len);
+
+ atomic_inc_int(ctx->done);
+ wakeup(sd);
+}
+
+int
+sr_luks_unlock(struct sr_discipline *sd, struct bioc_createraid *bc)
+{
+ struct sr_luks *sl = &sd->mds.mdd_luks;
+ struct luks_phdr *luks = sl->sl_phdr;
+ struct user_key_context keys[LUKS_NUM_KEYS];
+ volatile unsigned int done = 0;
+ size_t expected, bytes;
+ int i, need, error = EINVAL;
+ u_int8_t *response;
+
+ bytes = sl->sl_keylen / CHAR_BIT;
+ expected = bytes * LUKS_NUM_KEYS;
+
+ if (!(bc->bc_opaque_flags & BIOC_SOIN) ||
+ bc->bc_opaque_size != expected)
+ return (error);
+
+ response = malloc(expected, M_DEVBUF, M_WAITOK);
+ if ((error = copyin(bc->bc_opaque, response, expected))) {
+ free(response, M_DEVBUF, expected);
+ return (error);
+ }
+
+ memset(&keys, 0, sizeof keys);
+ for (need = 0, i = 0; i < LUKS_NUM_KEYS; ++i) {
+ if (luks->keyblock[i].active != htobe32(LUKS_KEY_ACTIVE_MAGIC))
+ continue;
+
+ keys[i].sd = sd;
+ keys[i].keyidx = i;
+ keys[i].done = &done;
+ keys[i].kdf = response + (i * bytes);
+
+ ++need;
+ task_set(&keys[i].task, sr_luks_unlock_key, &keys[i]);
+ task_add(crypto_taskq, &keys[i].task);
+ }
+
+ while (done != need) {
+ tsleep(sd, PWAIT, "lukskey", 0);
+ }
+
+ explicit_bzero(response, expected);
+ free(response, M_DEVBUF, expected);
+
+ /* Collect the first succesful master key (there should be only one). */
+ for (i = 0; i < LUKS_NUM_KEYS; ++i) {
+ if (keys[i].masterkey == NULL)
+ continue;
+
+ if (sl->sl_masterkey == NULL)
+ sl->sl_masterkey = keys[i].masterkey;
+ else {
+ explicit_bzero(keys[i].masterkey, bytes);
+ free(keys[i].masterkey, M_DEVBUF, bytes);
+ }
+ }
+
+ if (sl->sl_masterkey == NULL) {
+ sr_error(sd->sd_sc, "incorrect key or passphrase");
+ return (EPERM);
+ } else {
+ bc->bc_opaque_status = BIOC_SOINOUT_OK;
+ error = sr_luks_crypto_init(sd);
+ }
+
+ return (error);
+}
+
+int
+sr_luks_assemble(struct sr_discipline *sd, struct bioc_createraid *bc,
+ int no_chunk, void *data)
+{
+ struct luks_phdr *luks = sd->mds.mdd_luks.sl_phdr;
+ int error = EINVAL, k;
+
+ /*
+ * Provide userland with kdf hint, or check its response.
+ */
+ if (bc->bc_opaque_flags & BIOC_SOOUT) {
+ struct luks_user_pbkdf2 hint;
+
+ if (sizeof hint < bc->bc_opaque_size)
+ return (error);
+
+ hint.key_bytes = sd->mds.mdd_luks.sl_keylen / CHAR_BIT;
+ hint.have_keys = 0;
+
+ for (k = 0; k < LUKS_NUM_KEYS; ++k) {
+ if (luks->keyblock[k].active !=
+ htobe32(LUKS_KEY_ACTIVE_MAGIC))
+ continue;
+
+ hint.have_keys |= (1 << k);
+ memcpy(&hint.keys[k].salt,
+ luks->keyblock[k].pbkdf2_salt, LUKS_SALT_LEN);
+ hint.keys[k].iterations =
+ betoh32(luks->keyblock[k].pbkdf2_iterations);
+ }
+
+ if ((error = copyout(&hint, bc->bc_opaque, bc->bc_opaque_size)))
+ return (error);
+
+ /* We're done, the user has to take action. */
+ bc->bc_opaque_status = BIOC_SOINOUT_OK;
+ error = EAGAIN;
+ }
+ else if (bc->bc_opaque_flags & BIOC_SOIN) {
+ /* Use userland-provided KDF result to obtain the master key. */
+ error = sr_luks_unlock(sd, bc);
+ }
+ else {
+ sr_error(sd->sd_sc, "%s: invalid opaque flags %x\n", __func__,
+ bc->bc_opaque_flags);
+ }
+
+ return (error);
+}
+
+int
+sr_luks_create(struct sr_discipline *sd, struct bioc_createraid *bc,
+ int no_chunk, int64_t coerced_size)
+{
+ return (EOPNOTSUPP);
+}
+
+static int
+sr_luks_crypto_init(struct sr_discipline *sd)
+{
+ struct sr_luks *sl = &sd->mds.mdd_luks;
+ struct cryptoini cri;
+ int error;
+
+ memset(&cri, 0, sizeof cri);
+
+ cri.cri_alg = sl->sl_alg;
+ cri.cri_klen = sl->sl_keylen;
+ cri.cri_key = sl->sl_masterkey;
+
+ error = crypto_newsession(&sl->sl_sessionid, &cri, 0 /* no hardware */);
+ if (error) {
+ sr_error(sd->sd_sc, "LUKS crypto_newsession");
+ return (error);
+ }
+
+ return (0);
+}
+
+struct sr_luks_wu *
+sr_luks_crypto_prepare(struct sr_workunit *wu, int encrypt)
+{
+ struct scsi_xfer *xs = wu->swu_xs;
+ struct sr_discipline *sd = wu->swu_dis;
+ struct sr_luks_wu *lwu = (struct sr_luks_wu *)wu;
+ struct cryptodesc *crd;
+ int flags, i, n;
+ daddr_t blk;
+
+ lwu->lwu_uio.uio_iovcnt = 1;
+ lwu->lwu_uio.uio_iov[0].iov_len = xs->datalen;
+ if (xs->flags & SCSI_DATA_OUT) {
+ memcpy(lwu->lwu_dmabuf, xs->data, xs->datalen);
+ lwu->lwu_uio.uio_iov[0].iov_base = lwu->lwu_dmabuf;
+ } else
+ lwu->lwu_uio.uio_iov[0].iov_base = xs->data;
+
+ blk = wu->swu_blk_start;
+ n = xs->datalen >> DEV_BSHIFT;
+
+ crd = lwu->lwu_descs;
+ for (i = 0; i < ((MAXPHYS >> DEV_BSHIFT) - n); ++i) {
+ crd = crd->crd_next;
+ KASSERT(crd);
+ }
+ lwu->lwu_crp->crp_desc = crd;
+ flags = (encrypt ? CRD_F_ENCRYPT : 0) |
+ CRD_F_IV_PRESENT | CRD_F_IV_EXPLICIT;
+
+ lwu->lwu_crp->crp_sid = sd->mds.mdd_luks.sl_sessionid;
+ lwu->lwu_crp->crp_opaque = lwu;
+ lwu->lwu_crp->crp_ilen = xs->datalen;
+ lwu->lwu_crp->crp_alloctype = M_DEVBUF;
+ lwu->lwu_crp->crp_buf = &lwu->lwu_uio;
+
+ for (i = 0, crd = lwu->lwu_crp->crp_desc; crd;
+ ++i, ++blk, crd = crd->crd_next) {
+ crd->crd_skip = i << DEV_BSHIFT;
+ crd->crd_len = DEV_BSIZE;
+ crd->crd_inject = 0;
+ crd->crd_flags = flags;
+ crd->crd_alg = sd->mds.mdd_luks.sl_alg;
+ crd->crd_klen = sd->mds.mdd_luks.sl_keylen;
+ crd->crd_key = sd->mds.mdd_luks.sl_masterkey;
+ memcpy(crd->crd_iv, &blk, sizeof blk);
+ }
+
+ return (lwu);
+}
+
+int
+sr_luks_alloc_resources(struct sr_discipline *sd)
+{
+ struct sr_workunit *wu;
+ struct sr_luks_wu *lwu;
+
+ if (sr_wu_alloc(sd, sizeof(struct sr_luks_wu))) {
+ sr_error(sd->sd_sc, "unable to allocate work units");
+ return (ENOMEM);
+ }
+ if (sr_ccb_alloc(sd)) {
+ sr_error(sd->sd_sc, "unable to allocate CCBs");
+ return (ENOMEM);
+ }
+
+ /* Connect preallocated uio/iovec/crypto structures like sr_crypto. */
+ TAILQ_FOREACH(wu, &sd->sd_wu, swu_next) {
+ lwu = (struct sr_luks_wu *)wu;
+ lwu->lwu_uio.uio_iov = &lwu->lwu_iov;
+
+ lwu->lwu_dmabuf = dma_alloc(MAXPHYS, PR_WAITOK);
+ lwu->lwu_crp = crypto_getreq(MAXPHYS >> DEV_BSHIFT);
+ if (lwu->lwu_crp == NULL)
+ return (ENOMEM);
+ lwu->lwu_descs = lwu->lwu_crp->crp_desc;
+ }
+
+ sr_hotplug_register(sd, sr_luks_hotplug);
+
+ return (0);
+}
+
+void
+sr_luks_free_resources(struct sr_discipline *sd)
+{
+ struct sr_luks *sl = &sd->mds.mdd_luks;
+ struct luks_phdr *luks = sl->sl_phdr;
+ struct sr_workunit *wu;
+ struct sr_luks_wu *lwu;
+ size_t keylen;
+
+ DNPRINTF(SR_D_DIS, "%s: %s\n", DEVNAME(sd->sd_sc), __func__);
+
+ /*
+ * Make sure the master key is gone in all cases!
+ */
+
+ /* We might have never made it to luks_meta_validate. */
+ if (luks == NULL) {
+ KASSERT(sl->sl_masterkey == NULL);
+ return;
+ }
+
+ keylen = betoh32(luks->master_key_bytes);
+ if (sl->sl_masterkey != NULL) {
+ explicit_bzero(sl->sl_masterkey, keylen);
+ free(sl->sl_masterkey, M_DEVBUF, keylen);
+ sl->sl_masterkey = NULL;
+ }
+
+ free(sl->sl_phdr, M_DEVBUF, sizeof(struct luks_phdr));
+
+ sr_hotplug_unregister(sd, sr_luks_hotplug);
+
+ /* Free preallocated DMA buffers and crypto descriptors. */
+ TAILQ_FOREACH(wu, &sd->sd_wu, swu_next) {
+ lwu = (struct sr_luks_wu *)wu;
+ if (lwu->lwu_dmabuf)
+ dma_free(lwu->lwu_dmabuf, MAXPHYS);
+ if (lwu->lwu_crp) {
+ lwu->lwu_crp->crp_desc = lwu->lwu_descs;
+ crypto_freereq(lwu->lwu_crp);
+ }
+ }
+
+ crypto_freesession(sl->sl_sessionid);
+ sl->sl_sessionid = (u_int64_t)-1;
+
+ sr_wu_free(sd);
+ sr_ccb_free(sd);
+}
+
+int
+sr_luks_dev_rw(struct sr_workunit *wu)
+{
+ struct sr_luks_wu *lwu = (struct sr_luks_wu *)wu;
+ struct sr_discipline *sd = wu->swu_dis;
+ struct scsi_xfer *xs = wu->swu_xs;
+ struct sr_ccb *ccb;
+ struct uio *uio;
+ daddr_t blk;
+
+ blk = wu->swu_blk_start + sd->sd_meta->ssd_data_offset / DEV_BSIZE;
+
+ ccb = sr_ccb_rw(sd, 0, blk, xs->datalen, xs->data, xs->flags, 0);
+ if (ccb == NULL) {
+ printf("%s: %s: too many CCBs queued, blk %lld, len %d, "
+ "flags %x\n", DEVNAME(sd->sd_sc), __func__, blk,
+ xs->datalen, xs->flags);
+ lwu->lwu_crp->crp_etype = EINVAL;
+ return (1);
+ }
+ if (!ISSET(xs->flags, SCSI_DATA_IN)) {
+ uio = lwu->lwu_crp->crp_buf;
+ ccb->ccb_buf.b_data = uio->uio_iov[0].iov_base;
+ ccb->ccb_opaque = wu;
+ }
+ sr_wu_enqueue_ccb(wu, ccb);
+ sr_schedule_wu(wu);
+
+ return (0);
+}
+
+int
+sr_luks_write(struct cryptop *crp)
+{
+ struct sr_workunit *wu = crp->crp_opaque;
+ int s;
+
+ DNPRINTF(SR_D_INTR, "%s: sr_luks_write: wu: %p xs: %p\n",
+ DEVNAME(wu->swu_dis->sd_sc), wu, wu->swu_xs);
+
+ if (crp->crp_etype) {
+ wu->swu_xs->error = XS_DRIVER_STUFFUP;
+ s = splbio();
+ sr_scsi_done(wu->swu_dis, wu->swu_xs);
+ splx(s);
+ }
+
+ return sr_luks_dev_rw(wu);
+}
+
+int
+sr_luks_rw(struct sr_workunit *wu)
+{
+ struct sr_luks_wu *lwu = (struct sr_luks_wu *)wu;
+ daddr_t blk;
+
+ DNPRINTF(SR_D_INTR, "%s\n", __func__);
+
+ if (sr_validate_io(wu, &blk, "sr_luks_rw")) {
+ DNPRINTF(SR_D_INTR, "invalid I/O blk %lld", blk);
+ return (1);
+ }
+
+ if (wu->swu_xs->flags & SCSI_DATA_OUT) {
+ int error;
+
+ lwu = sr_luks_crypto_prepare(wu, 1);
+ lwu->lwu_crp->crp_callback = sr_luks_write;
+
+ if ((error = crypto_invoke(lwu->lwu_crp)) == 0)
+ error = lwu->lwu_crp->crp_etype;
+ return (error);
+ } else
+ return sr_luks_dev_rw(wu);
+}
+
+int
+sr_luks_read(struct cryptop *crp)
+{
+ struct sr_workunit *wu = crp->crp_opaque;
+ int s;
+
+ DNPRINTF(SR_D_INTR, "%s: sr_luks_read: wu: %p xs: %p\n",
+ DEVNAME(wu->swu_dis->sd_sc), wu, wu->swu_xs);
+
+ if (crp->crp_etype)
+ wu->swu_xs->error = XS_DRIVER_STUFFUP;
+
+ s = splbio();
+ sr_scsi_done(wu->swu_dis, wu->swu_xs);
+ splx(s);
+ return (0);
+}
+
+void
+sr_luks_done(struct sr_workunit *wu)
+{
+ struct sr_luks_wu *lwu;
+ struct scsi_xfer *xs = wu->swu_xs;
+ int s;
+
+ /* Decrypt data succesfully read from the disk. */
+ if (ISSET(xs->flags, SCSI_DATA_IN) && xs->error == XS_NOERROR) {
+ lwu = sr_luks_crypto_prepare(wu, 0 /* decrypt */);
+ lwu->lwu_crp->crp_callback = sr_luks_read;
+ DNPRINTF(SR_D_INTR, "%s: sr_luks_done: crypto_invoke %p\n",
+ DEVNAME(wu->swu_dis->sd_sc), lwu->lwu_crp);
+ crypto_invoke(lwu->lwu_crp);
+ return;
+ }
+
+ s = splbio();
+ sr_scsi_done(wu->swu_dis, xs);
+ splx(s);
+}
+
+static void
+sr_luks_hotplug(struct sr_discipline *sd, struct disk *diskp, int action)
+{
+ printf("%s: %s %s %d\n", __func__, DEVNAME(sd->sd_sc), diskp->dk_name,
+ action);
+}
Index: dev/softraid_luks.h
===================================================================
RCS file: dev/softraid_luks.h
diff -N dev/softraid_luks.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ dev/softraid_luks.h 24 May 2015 12:02:30 -0000
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2015 Martin Pelikan <peli...@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#pragma once
+#include <dev/luks.h>
+
+/*
+ * Correct userland response is a @key_bytes * LUKS_NUM_KEYS long byte array.
+ */
+struct luks_user_pbkdf2 {
+ u_int32_t key_bytes;
+ u_int32_t have_keys; /* bitmap */
+ struct {
+ u_int32_t iterations;
+ u_int8_t salt[LUKS_SALT_LEN];
+ } keys[LUKS_NUM_KEYS];
+};
+
+#if defined(_KERNEL) || defined(_STANDALONE)
+/* Like sr_crypto, embed uio + iovec to avoid risk of allocation failures. */
+struct sr_luks_wu {
+ struct sr_workunit lwu_workunit; /* parent class must be first */
+ struct uio lwu_uio;
+ struct iovec lwu_iov;
+ struct cryptop *lwu_crp;
+ struct cryptodesc *lwu_descs;
+ void *lwu_dmabuf;
+};
+
+/* the actual LUKS implementation */
+int luks_deobfuscate_key(struct luks_phdr *, void *, void *, void *,
+ size_t, size_t);
+const char * luks_phdr_parse_and_validate(struct sr_luks *, int64_t);
+
+/* softraid(4) metadata driver */
+int luks_meta_probe(struct sr_softc *, struct sr_chunk *);
+int luks_meta_attach(struct sr_discipline *, int);
+int luks_meta_detach(struct sr_discipline *);
+int luks_meta_read(struct sr_discipline *, dev_t, struct sr_metadata *,
+ void *);
+int luks_meta_write(struct sr_discipline *, dev_t, struct sr_metadata *,
+ void *);
+int luks_meta_validate(struct sr_discipline *, struct sr_metadata *,
+ void *);
+
+#define LUKS_META_OFFSET 0
+#define LUKS_META_SIZE 4096 /* variable, depends on header
within */
+
+/* softraid(4) ioctl(2) integration */
+void sr_luks_discipline_init(struct sr_discipline *);
+
+/* Mapping between on-disk strings and crypto(9) identifiers. */
+struct luks_cipher {
+ const char *lc_name;
+ const char *lc_mode;
+ int lc_alg;
+ int lc_klen_min; /* bits */
+ int lc_klen_max; /* bits */
+};
+struct luks_hash {
+ const char *lh_name;
+ int lh_alg;
+};
+#endif /* defined(_KERNEL) || defined(_STANDALONE) */
Index: dev/softraidvar.h
===================================================================
RCS file: /cvs/src/sys/dev/softraidvar.h,v
retrieving revision 1.159
diff -u -p -r1.159 softraidvar.h
--- dev/softraidvar.h 27 Jan 2015 03:17:36 -0000 1.159
+++ dev/softraidvar.h 24 May 2015 12:02:30 -0000
@@ -99,6 +99,7 @@ struct sr_crypto_kdfpair {
#define SR_META_V3_DATA_OFFSET (SR_META_V3_OFFSET + SR_META_V3_SIZE)
#define SR_META_F_NATIVE 0 /* Native metadata format. */
+#define SR_META_F_LUKS 1 /* Linux Unified Key Setup. */
#define SR_META_F_INVALID -1
#define SR_HEADER_SIZE (SR_META_SIZE + SR_BOOT_SIZE)
@@ -153,7 +154,7 @@ struct sr_metadata {
char ssd_devname[32];/* /dev/XXXXX */
u_int32_t ssd_meta_flags;
#define SR_META_DIRTY 0x1
- u_int32_t ssd_data_offset;
+ u_int32_t ssd_data_offset;/* in bytes */
u_int64_t ssd_ondisk; /* on disk version counter */
int64_t ssd_rebuild; /* last block of rebuild */
} __packed;
@@ -457,6 +458,16 @@ struct sr_crypto {
u_int64_t scr_sid[SR_CRYPTO_MAXKEYS];
};
+/* LUKS */
+struct sr_luks {
+ void *sl_phdr;
+ void *sl_masterkey;
+ u_int64_t sl_sessionid;
+ int sl_alg;
+ int sl_keylen; /* bits */
+ int sl_hash;
+};
+
#define SR_CONCAT_NOWU 16
struct sr_concat {
};
@@ -521,6 +532,7 @@ struct sr_discipline {
struct sr_concat mdd_concat;
#ifdef CRYPTO
struct sr_crypto mdd_crypto;
+ struct sr_luks mdd_luks;
#endif /* CRYPTO */
} sd_dis_specific;/* dis specific members */
#define mds sd_dis_specific
@@ -663,6 +675,8 @@ void sr_warn(struct sr_softc *,
const
void sr_error(struct sr_softc *, const char *, ...);
int32_t sr_validate_stripsize(u_int32_t);
int sr_meta_read(struct sr_discipline *);
+int sr_meta_rw(struct sr_discipline *, dev_t, void *,
+ size_t, daddr_t, long);
int sr_meta_native_read(struct sr_discipline *, dev_t,
struct sr_metadata *, void *);
int sr_meta_validate(struct sr_discipline *, dev_t,
Index: kern/kern_uuid.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_uuid.c,v
retrieving revision 1.2
diff -u -p -r1.2 kern_uuid.c
--- kern/kern_uuid.c 31 Aug 2014 20:15:54 -0000 1.2
+++ kern/kern_uuid.c 24 May 2015 12:02:31 -0000
@@ -251,3 +251,58 @@ uuid_dec_be(const void *buf, struct uuid
for (i = 0; i < _UUID_NODE_LEN; i++)
uuid->node[i] = p[10 + i];
}
+
+static inline int
+hex_byte_from_string(const char * __restrict s, u_int8_t * __restrict ret)
+{
+ int i = 0;
+
+ *ret = 0;
+ do {
+ *ret <<= 4;
+
+ if (s[i] >= '0' && s[i] <= '9')
+ *ret |= s[i] - '0';
+ else if (s[i] >= 'A' && s[i] <= 'F')
+ *ret |= (s[i] - 'A') + 10;
+ else if (s[i] >= 'a' && s[i] <= 'f')
+ *ret |= (s[i] - 'a') + 10;
+ else
+ return 1;
+ } while (++i < 2);
+ return 0;
+}
+
+int
+uuid_from_string(const char * __restrict s, void * __restrict dst)
+{
+ u_int8_t *p = dst;
+ int bytes, shorts;
+
+ /* 32-bit number - (3x 16-bit number -) 48-bit number */
+
+ for (bytes = 0; bytes < 4; ++bytes)
+ if (hex_byte_from_string(s + bytes, p + bytes))
+ return (1);
+
+ if (s[8] != '-')
+ return (2);
+ s += 9;
+ p += 4;
+
+ for (shorts = 0; shorts < 3; ++shorts) {
+ for (bytes = 0; bytes < 2; ++bytes)
+ if (hex_byte_from_string(s + bytes, p + bytes))
+ return (1);
+
+ if (s[4] != '-')
+ return (2);
+ s += 5;
+ p += 2;
+ }
+
+ for (bytes = 0; bytes < 6; ++bytes)
+ if (hex_byte_from_string(s + bytes, p + bytes))
+ return (1);
+ return (0);
+}
Index: kern/subr_disk.c
===================================================================
RCS file: /cvs/src/sys/kern/subr_disk.c,v
retrieving revision 1.183
diff -u -p -r1.183 subr_disk.c
--- kern/subr_disk.c 9 May 2015 17:11:26 -0000 1.183
+++ kern/subr_disk.c 24 May 2015 12:02:31 -0000
@@ -418,6 +418,10 @@ donot:
fstype = FS_EXT2FS;
break;
+ case DOSPTYP_LUKS:
+ fstype = FS_LUKS;
+ break;
+
case DOSPTYP_NTFS:
fstype = FS_NTFS;
break;
@@ -567,13 +571,14 @@ gpt_get_fstype(struct uuid *uuid_part)
{
static int init = 0;
static struct uuid uuid_openbsd, uuid_msdos, uuid_chromerootfs,
- uuid_chromekernelfs, uuid_linux, uuid_hfs, uuid_unused;
+ uuid_chromekernelfs, uuid_linux, uuid_hfs, uuid_luks, uuid_unused;
static const uint8_t gpt_uuid_openbsd[] = GPT_UUID_OPENBSD;
static const uint8_t gpt_uuid_msdos[] = GPT_UUID_MSDOS;
static const uint8_t gpt_uuid_chromerootfs[] = GPT_UUID_CHROMEROOTFS;
static const uint8_t gpt_uuid_chromekernelfs[] =
GPT_UUID_CHROMEKERNELFS;
static const uint8_t gpt_uuid_linux[] = GPT_UUID_LINUX;
static const uint8_t gpt_uuid_hfs[] = GPT_UUID_APPLE_HFS;
+ static const uint8_t gpt_uuid_luks[] = GPT_UUID_LUKS;
static const uint8_t gpt_uuid_unused[] = GPT_UUID_UNUSED;
if (init == 0) {
@@ -583,6 +588,7 @@ gpt_get_fstype(struct uuid *uuid_part)
uuid_dec_be(gpt_uuid_chromekernelfs, &uuid_chromekernelfs);
uuid_dec_be(gpt_uuid_linux, &uuid_linux);
uuid_dec_be(gpt_uuid_hfs, &uuid_hfs);
+ uuid_dec_be(gpt_uuid_luks, &uuid_luks);
uuid_dec_be(gpt_uuid_unused, &uuid_unused);
init = 1;
}
@@ -601,6 +607,8 @@ gpt_get_fstype(struct uuid *uuid_part)
return (FS_EXT2FS);
else if (!memcmp(uuid_part, &uuid_hfs, sizeof(struct uuid)))
return (FS_HFS);
+ else if (!memcmp(uuid_part, &uuid_luks, sizeof(struct uuid)))
+ return (FS_LUKS);
else {
DPRINTF("Unknown GUID: %02x%02x%02x%02x-%02x%02x-%02x%02x-"
"%02x%02x-%02x%02x%02x%02x%02x%02x\n",
Index: sys/disklabel.h
===================================================================
RCS file: /cvs/src/sys/sys/disklabel.h,v
retrieving revision 1.65
diff -u -p -r1.65 disklabel.h
--- sys/disklabel.h 9 May 2015 17:11:26 -0000 1.65
+++ sys/disklabel.h 24 May 2015 12:02:31 -0000
@@ -276,6 +276,7 @@ static char *dktypenames[] = {
#define FS_RAID 19 /* RAIDframe or softraid */
#define FS_NTFS 20 /* Windows/NT file system */
#define FS_UDF 21 /* UDF (DVD) filesystem */
+#define FS_LUKS 22 /* Linux Unified Key Setup */
#ifdef DKTYPENAMES
static char *fstypenames[] = {
@@ -301,6 +302,7 @@ static char *fstypenames[] = {
"RAID",
"NTFS",
"UDF",
+ "LUKS",
NULL
};
@@ -328,6 +330,7 @@ static char *fstypesnames[] = {
"", /* 19 */
"ntfs", /* 20 */
"udf", /* 21 */
+ "luks", /* 22 */
NULL
};
@@ -456,6 +459,9 @@ struct gpt_partition {
#define GPT_UUID_LINUX_SRV \
{ 0x3b, 0x8f, 0x84, 0x25, 0x20, 0xe0, 0x4f, 0x3b, \
0x90, 0x7f, 0x1a, 0x25, 0xa7, 0x6f, 0x98, 0xe8 }
+#define GPT_UUID_LUKS \
+ { 0xca, 0x7d, 0x7c, 0xcb, 0x63, 0xed, 0x4c, 0x53, \
+ 0x86, 0x1c, 0x17, 0x42, 0x53, 0x60, 0x59, 0xcc }
#define GPT_UUID_FBSD_DATA \
{ 0x51, 0x6e, 0x7c, 0xb4, 0x6e, 0xcf, 0x11, 0xd6, \
0x8f, 0xf8, 0x00, 0x02, 0x2d, 0x09, 0x71, 0x2b }
@@ -519,6 +525,7 @@ struct dos_partition {
#define DOSPTYP_FREEBSD 0xa5 /* FreeBSD partition type */
#define DOSPTYP_OPENBSD 0xa6 /* OpenBSD partition type */
#define DOSPTYP_NETBSD 0xa9 /* NetBSD partition type */
+#define DOSPTYP_LUKS 0xe8 /* Linux Unified Key Setup */
#define DOSPTYP_EFI 0xee /* EFI Protective Partition */
#define DOSPTYP_EFISYS 0xef /* EFI System Partition */
Index: sys/uuid.h
===================================================================
RCS file: /cvs/src/sys/sys/uuid.h,v
retrieving revision 1.3
diff -u -p -r1.3 uuid.h
--- sys/uuid.h 31 Aug 2014 09:36:39 -0000 1.3
+++ sys/uuid.h 24 May 2015 12:02:31 -0000
@@ -65,6 +65,7 @@ void uuid_dec_be(const void *, struct uu
void uuid_dec_le(const void *, struct uuid *);
void uuid_enc_be(void *, const struct uuid *);
void uuid_enc_le(void *, const struct uuid *);
+int uuid_from_string(const char * __restrict, void * __restrict);
#else /* _KERNEL */