This diff allows boot from crypto volumes that are using a keydisk. So far booting only works from passphrase-based crypto volumes.
Tested on i386 and amd64 using USB keydisks. Note that the BIOS needs to see the keydisk for this to work. SD cards or anything else that's usually not bootable won't work in many cases. If the keydisk is not present or cannot be found the boot loader will keep asking for a passphrase. The install media still fit with this. Index: i386/stand/boot/conf.c =================================================================== RCS file: /cvs/src/sys/arch/i386/stand/boot/conf.c,v retrieving revision 1.50 diff -u -p -r1.50 conf.c --- i386/stand/boot/conf.c 31 Oct 2012 13:57:59 -0000 1.50 +++ i386/stand/boot/conf.c 18 Sep 2013 14:14:53 -0000 @@ -43,7 +43,7 @@ #include <dev/cons.h> #include "debug.h" -const char version[] = "3.21"; +const char version[] = "3.22"; int debug = 1; Index: i386/stand/libsa/softraid.c =================================================================== RCS file: /cvs/src/sys/arch/i386/stand/libsa/softraid.c,v retrieving revision 1.4 diff -u -p -r1.4 softraid.c --- i386/stand/libsa/softraid.c 11 Jun 2013 16:42:09 -0000 1.4 +++ i386/stand/libsa/softraid.c 18 Sep 2013 14:16:07 -0000 @@ -37,6 +37,14 @@ /* List of softraid volumes. */ struct sr_boot_volume_head sr_volumes; +/* Metadata from keydisks. */ +struct sr_boot_meta_keydisk { + struct sr_metadata *md; + SLIST_ENTRY(sr_boot_meta_keydisk) skm_link; +}; +SLIST_HEAD(sr_boot_meta_keydisk_head, sr_boot_meta_keydisk); +struct sr_boot_meta_keydisk_head sr_keydisks; + void srprobe_meta_opt_load(struct sr_metadata *sm, struct sr_meta_opt_head *som) { @@ -97,6 +105,7 @@ srprobe(void) struct sr_boot_chunk *bc, *bc1, *bc2; struct sr_meta_chunk *mc; struct sr_metadata *md; + struct sr_boot_meta_keydisk *mk; struct diskinfo *dip; struct partition *pp; int i, error, volno; @@ -105,6 +114,7 @@ srprobe(void) /* Probe for softraid volumes. */ SLIST_INIT(&sr_volumes); + SLIST_INIT(&sr_keydisks); md = alloc(SR_META_SIZE * 512); @@ -139,6 +149,15 @@ srprobe(void) /* XXX - validate checksum. */ + /* Handle key disks separately... later. */ + if (md->ssdi.ssd_level == SR_KEYDISK_LEVEL) { + mk = alloc(sizeof(struct sr_boot_meta_keydisk)); + mk->md = alloc(SR_META_SIZE * 512); + bcopy(md, mk->md, SR_META_SIZE * 512); + SLIST_INSERT_HEAD(&sr_keydisks, mk, skm_link); + continue; + } + /* Locate chunk-specific metadata for this chunk. */ mc = (struct sr_meta_chunk *)(md + 1); mc += md->ssdi.ssd_chunk_id; @@ -157,10 +176,6 @@ srprobe(void) bc->sbc_ondisk = md->ssd_ondisk; bc->sbc_state = mc->scm_status; - /* Handle key disks separately... later. */ - if (md->ssdi.ssd_level == SR_KEYDISK_LEVEL) - continue; - SLIST_FOREACH(bv, &sr_volumes, sbv_link) { if (bcmp(&md->ssdi.ssd_uuid, &bv->sbv_uuid, sizeof(md->ssdi.ssd_uuid)) == 0) @@ -248,12 +263,28 @@ srprobe(void) bv->sbv_state = BIOC_SVOFFLINE; switch (bv->sbv_level) { case 0: - case 'C': case 'c': if (bv->sbv_chunk_no == bv->sbv_chunks_found) bv->sbv_state = BIOC_SVONLINE; break; + case 'C': + if (bv->sbv_chunk_no == bv->sbv_chunks_found) { + bv->sbv_state = BIOC_SVONLINE; + + /* Load keydisk metadata for this volume. */ + SLIST_FOREACH(mk, &sr_keydisks, skm_link) { + if (bcmp(&mk->md->ssdi.ssd_uuid, + &bv->sbv_uuid, + sizeof(mk->md->ssdi.ssd_uuid)) == 0) + break; + } + if (mk) + srprobe_meta_opt_load(mk->md, + &bv->sbv_meta_opt); + } + break; + case 1: if (bv->sbv_chunk_no == bv->sbv_chunks_found) bv->sbv_state = BIOC_SVONLINE; @@ -382,7 +413,7 @@ sr_getdisklabel(struct sr_boot_volume *b buf = alloca(DEV_BSIZE); sr_strategy(bv, F_READ, start, sizeof(struct disklabel), buf, NULL); -#if BIOS_DEBUG +#ifdef BIOS_DEBUG printf("sr_getdisklabel: magic %lx\n", ((struct disklabel *)buf)->d_magic); for (i = 0; i < MAXPARTITIONS; i++) @@ -424,6 +455,7 @@ void sr_clear_keys(void) { struct sr_boot_volume *bv; + struct sr_boot_meta_keydisk *mk; SLIST_FOREACH(bv, &sr_volumes, sbv_link) { if (bv->sbv_level != 'C') @@ -439,6 +471,11 @@ sr_clear_keys(void) bv->sbv_maskkey = NULL; } } + SLIST_FOREACH(mk, &sr_keydisks, skm_link) { + explicit_bzero(mk->md, SR_META_SIZE * 512); + free(mk->md, 0); + mk->md = NULL; + } } void @@ -467,6 +504,7 @@ int sr_crypto_decrypt_keys(struct sr_boot_volume *bv) { struct sr_meta_crypto *cm; + struct sr_meta_keydisk *skm; struct sr_meta_opt_item *omi; struct sr_crypto_kdf_pbkdf2 *kdfhint; struct sr_crypto_kdfinfo kdfinfo; @@ -499,26 +537,35 @@ sr_crypto_decrypt_keys(struct sr_boot_vo goto done; } - printf("Passphrase: "); - for (i = 0; i < PASSPHRASE_LENGTH - 1; i++) { - c = cngetc(); - if (c == '\r' || c == '\n') + SLIST_FOREACH(omi, &bv->sbv_meta_opt, omi_link) + if (omi->omi_som->som_type == SR_OPT_KEYDISK) break; - passphrase[i] = (c & 0xff); - } - passphrase[i] = 0; - printf("\n"); + if (omi) { + skm = (struct sr_meta_keydisk*)omi->omi_som; + bcopy(&skm->skm_maskkey, &kdfinfo.maskkey, + sizeof(kdfinfo.maskkey)); + } else { + printf("Passphrase: "); + for (i = 0; i < PASSPHRASE_LENGTH - 1; i++) { + c = cngetc(); + if (c == '\r' || c == '\n') + break; + passphrase[i] = (c & 0xff); + } + passphrase[i] = 0; + printf("\n"); #ifdef BIOS_DEBUG - printf("Got passphrase: %s with len %d\n", - passphrase, strlen(passphrase)); + printf("Got passphrase: %s with len %d\n", + passphrase, strlen(passphrase)); #endif - if (pkcs5_pbkdf2(passphrase, strlen(passphrase), kdfhint->salt, - sizeof(kdfhint->salt), kdfinfo.maskkey, sizeof(kdfinfo.maskkey), - kdfhint->rounds) != 0) { - printf("pbkdf2 failed\n"); - goto done; + if (pkcs5_pbkdf2(passphrase, strlen(passphrase), kdfhint->salt, + sizeof(kdfhint->salt), kdfinfo.maskkey, + sizeof(kdfinfo.maskkey), kdfhint->rounds) != 0) { + printf("pbkdf2 failed\n"); + goto done; + } } /* kdfinfo->maskkey now has key. */ @@ -540,11 +587,11 @@ sr_crypto_decrypt_keys(struct sr_boot_vo sizeof(kdfinfo.maskkey), keys, SR_CRYPTO_KEYBLOCK_BYTES, digest); if (bcmp(digest, cm->chk_hmac_sha1.sch_mac, sizeof(digest))) { - printf("incorrect passphrase\n"); + printf("incorrect passphrase or keydisk\n"); goto done; } - /* Keys will be cleared before boot and from _rtt. */ + /* Keys and keydisks will be cleared before boot and from _rtt. */ bv->sbv_keys = keys; bv->sbv_maskkey = alloc(sizeof(kdfinfo.maskkey)); bcopy(&kdfinfo.maskkey, bv->sbv_maskkey, sizeof(kdfinfo.maskkey)); Index: amd64/stand/boot/conf.c =================================================================== RCS file: /cvs/src/sys/arch/amd64/stand/boot/conf.c,v retrieving revision 1.26 diff -u -p -r1.26 conf.c --- amd64/stand/boot/conf.c 27 Oct 2012 15:43:42 -0000 1.26 +++ amd64/stand/boot/conf.c 18 Sep 2013 14:14:07 -0000 @@ -42,7 +42,7 @@ #include <biosdev.h> #include <dev/cons.h> -const char version[] = "3.23"; +const char version[] = "3.24"; int debug = 1; Index: amd64/stand/libsa/softraid.c =================================================================== RCS file: /cvs/src/sys/arch/amd64/stand/libsa/softraid.c,v retrieving revision 1.4 diff -u -p -r1.4 softraid.c --- amd64/stand/libsa/softraid.c 11 Jun 2013 16:42:07 -0000 1.4 +++ amd64/stand/libsa/softraid.c 18 Sep 2013 14:14:07 -0000 @@ -37,6 +37,14 @@ /* List of softraid volumes. */ struct sr_boot_volume_head sr_volumes; +/* Metadata from keydisks. */ +struct sr_boot_meta_keydisk { + struct sr_metadata *md; + SLIST_ENTRY(sr_boot_meta_keydisk) skm_link; +}; +SLIST_HEAD(sr_boot_meta_keydisk_head, sr_boot_meta_keydisk); +struct sr_boot_meta_keydisk_head sr_keydisks; + void srprobe_meta_opt_load(struct sr_metadata *sm, struct sr_meta_opt_head *som) { @@ -97,6 +105,7 @@ srprobe(void) struct sr_boot_chunk *bc, *bc1, *bc2; struct sr_meta_chunk *mc; struct sr_metadata *md; + struct sr_boot_meta_keydisk *mk; struct diskinfo *dip; struct partition *pp; int i, error, volno; @@ -105,6 +114,7 @@ srprobe(void) /* Probe for softraid volumes. */ SLIST_INIT(&sr_volumes); + SLIST_INIT(&sr_keydisks); md = alloc(SR_META_SIZE * 512); @@ -139,6 +149,15 @@ srprobe(void) /* XXX - validate checksum. */ + /* Handle key disks separately... later. */ + if (md->ssdi.ssd_level == SR_KEYDISK_LEVEL) { + mk = alloc(sizeof(struct sr_boot_meta_keydisk)); + mk->md = alloc(SR_META_SIZE * 512); + bcopy(md, mk->md, SR_META_SIZE * 512); + SLIST_INSERT_HEAD(&sr_keydisks, mk, skm_link); + continue; + } + /* Locate chunk-specific metadata for this chunk. */ mc = (struct sr_meta_chunk *)(md + 1); mc += md->ssdi.ssd_chunk_id; @@ -157,10 +176,6 @@ srprobe(void) bc->sbc_ondisk = md->ssd_ondisk; bc->sbc_state = mc->scm_status; - /* Handle key disks separately... later. */ - if (md->ssdi.ssd_level == SR_KEYDISK_LEVEL) - continue; - SLIST_FOREACH(bv, &sr_volumes, sbv_link) { if (bcmp(&md->ssdi.ssd_uuid, &bv->sbv_uuid, sizeof(md->ssdi.ssd_uuid)) == 0) @@ -248,12 +263,28 @@ srprobe(void) bv->sbv_state = BIOC_SVOFFLINE; switch (bv->sbv_level) { case 0: - case 'C': case 'c': if (bv->sbv_chunk_no == bv->sbv_chunks_found) bv->sbv_state = BIOC_SVONLINE; break; + case 'C': + if (bv->sbv_chunk_no == bv->sbv_chunks_found) { + bv->sbv_state = BIOC_SVONLINE; + + /* Load keydisk metadata for this volume. */ + SLIST_FOREACH(mk, &sr_keydisks, skm_link) { + if (bcmp(&mk->md->ssdi.ssd_uuid, + &bv->sbv_uuid, + sizeof(mk->md->ssdi.ssd_uuid)) == 0) + break; + } + if (mk) + srprobe_meta_opt_load(mk->md, + &bv->sbv_meta_opt); + } + break; + case 1: if (bv->sbv_chunk_no == bv->sbv_chunks_found) bv->sbv_state = BIOC_SVONLINE; @@ -382,7 +413,7 @@ sr_getdisklabel(struct sr_boot_volume *b buf = alloca(DEV_BSIZE); sr_strategy(bv, F_READ, start, sizeof(struct disklabel), buf, NULL); -#if BIOS_DEBUG +#ifdef BIOS_DEBUG printf("sr_getdisklabel: magic %lx\n", ((struct disklabel *)buf)->d_magic); for (i = 0; i < MAXPARTITIONS; i++) @@ -424,6 +455,7 @@ void sr_clear_keys(void) { struct sr_boot_volume *bv; + struct sr_boot_meta_keydisk *mk; SLIST_FOREACH(bv, &sr_volumes, sbv_link) { if (bv->sbv_level != 'C') @@ -439,6 +471,11 @@ sr_clear_keys(void) bv->sbv_maskkey = NULL; } } + SLIST_FOREACH(mk, &sr_keydisks, skm_link) { + explicit_bzero(mk->md, SR_META_SIZE * 512); + free(mk->md, 0); + mk->md = NULL; + } } void @@ -467,6 +504,7 @@ int sr_crypto_decrypt_keys(struct sr_boot_volume *bv) { struct sr_meta_crypto *cm; + struct sr_meta_keydisk *skm; struct sr_meta_opt_item *omi; struct sr_crypto_kdf_pbkdf2 *kdfhint; struct sr_crypto_kdfinfo kdfinfo; @@ -499,26 +537,35 @@ sr_crypto_decrypt_keys(struct sr_boot_vo goto done; } - printf("Passphrase: "); - for (i = 0; i < PASSPHRASE_LENGTH - 1; i++) { - c = cngetc(); - if (c == '\r' || c == '\n') + SLIST_FOREACH(omi, &bv->sbv_meta_opt, omi_link) + if (omi->omi_som->som_type == SR_OPT_KEYDISK) break; - passphrase[i] = (c & 0xff); - } - passphrase[i] = 0; - printf("\n"); + if (omi) { + skm = (struct sr_meta_keydisk*)omi->omi_som; + bcopy(&skm->skm_maskkey, &kdfinfo.maskkey, + sizeof(kdfinfo.maskkey)); + } else { + printf("Passphrase: "); + for (i = 0; i < PASSPHRASE_LENGTH - 1; i++) { + c = cngetc(); + if (c == '\r' || c == '\n') + break; + passphrase[i] = (c & 0xff); + } + passphrase[i] = 0; + printf("\n"); #ifdef BIOS_DEBUG - printf("Got passphrase: %s with len %d\n", - passphrase, strlen(passphrase)); + printf("Got passphrase: %s with len %d\n", + passphrase, strlen(passphrase)); #endif - if (pkcs5_pbkdf2(passphrase, strlen(passphrase), kdfhint->salt, - sizeof(kdfhint->salt), kdfinfo.maskkey, sizeof(kdfinfo.maskkey), - kdfhint->rounds) != 0) { - printf("pbkdf2 failed\n"); - goto done; + if (pkcs5_pbkdf2(passphrase, strlen(passphrase), kdfhint->salt, + sizeof(kdfhint->salt), kdfinfo.maskkey, + sizeof(kdfinfo.maskkey), kdfhint->rounds) != 0) { + printf("pbkdf2 failed\n"); + goto done; + } } /* kdfinfo->maskkey now has key. */ @@ -540,11 +587,11 @@ sr_crypto_decrypt_keys(struct sr_boot_vo sizeof(kdfinfo.maskkey), keys, SR_CRYPTO_KEYBLOCK_BYTES, digest); if (bcmp(digest, cm->chk_hmac_sha1.sch_mac, sizeof(digest))) { - printf("incorrect passphrase\n"); + printf("incorrect passphrase or keydisk\n"); goto done; } - /* Keys will be cleared before boot and from _rtt. */ + /* Keys and keydisks will be cleared before boot and from _rtt. */ bv->sbv_keys = keys; bv->sbv_maskkey = alloc(sizeof(kdfinfo.maskkey)); bcopy(&kdfinfo.maskkey, bv->sbv_maskkey, sizeof(kdfinfo.maskkey));