From: jsing Date: Fri, 10 Aug 2018 16:41:35 +0000 (+0000) Subject: Retry on incorrect passphrase for softraid crypto boot. X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=27bea9a3c7fdba43cfa7c5fb6f268efe98f7720b;p=openbsd Retry on incorrect passphrase for softraid crypto boot. Historically, the softraid crypto support in the boot loaders has only given one attempt to provide the correct passphrase. There were a few reasons for this, including the fact that pkcs5_pbkdf2() allows an empty passphrase and that returning EPERM allowed for another attempt. With the event of KARL and the need for bsd.booted with hibernate resumption, this becomes much more of an issue - if you get the passphrase wrong you fail to resume. There are also other situations like using /etc/boot.conf to switch serial console, but an incorrect passphrase results in the config not being read. Also, bcrypt_pbkdf() does not permit empty passphrases. This reworks the softraid crypto support in the boot loaders so that it loops requesting a valid passphrase until one is provided, or an empty passphrase is entered (at which point it will abort). ok mortimer@ tb@ --- diff --git a/sys/arch/amd64/stand/efiboot/efidev.c b/sys/arch/amd64/stand/efiboot/efidev.c index 2ec2f014723..47f62b92b96 100644 --- a/sys/arch/amd64/stand/efiboot/efidev.c +++ b/sys/arch/amd64/stand/efiboot/efidev.c @@ -1,4 +1,4 @@ -/* $OpenBSD: efidev.c,v 1.29 2018/06/18 15:37:47 krw Exp $ */ +/* $OpenBSD: efidev.c,v 1.30 2018/08/10 16:41:35 jsing Exp $ */ /* * Copyright (c) 1996 Michael Shalayeff @@ -600,7 +600,7 @@ efiopen(struct open_file *f, ...) } if (bv->sbv_level == 'C' && bv->sbv_keys == NULL) - if (sr_crypto_decrypt_keys(bv) != 0) + if (sr_crypto_unlock_volume(bv) != 0) return EPERM; if (bv->sbv_diskinfo == NULL) { diff --git a/sys/arch/amd64/stand/libsa/biosdev.c b/sys/arch/amd64/stand/libsa/biosdev.c index b9018b623b1..4ca8436f32c 100644 --- a/sys/arch/amd64/stand/libsa/biosdev.c +++ b/sys/arch/amd64/stand/libsa/biosdev.c @@ -1,4 +1,4 @@ -/* $OpenBSD: biosdev.c,v 1.31 2017/07/21 01:21:42 yasuoka Exp $ */ +/* $OpenBSD: biosdev.c,v 1.32 2018/08/10 16:41:35 jsing Exp $ */ /* * Copyright (c) 1996 Michael Shalayeff @@ -529,7 +529,7 @@ biosopen(struct open_file *f, ...) } if (bv->sbv_level == 'C' && bv->sbv_keys == NULL) - if (sr_crypto_decrypt_keys(bv) != 0) + if (sr_crypto_unlock_volume(bv) != 0) return EPERM; if (bv->sbv_diskinfo == NULL) { diff --git a/sys/arch/i386/stand/libsa/biosdev.c b/sys/arch/i386/stand/libsa/biosdev.c index 4c06a87e9ff..2126d709af6 100644 --- a/sys/arch/i386/stand/libsa/biosdev.c +++ b/sys/arch/i386/stand/libsa/biosdev.c @@ -1,4 +1,4 @@ -/* $OpenBSD: biosdev.c,v 1.96 2017/07/21 01:21:42 yasuoka Exp $ */ +/* $OpenBSD: biosdev.c,v 1.97 2018/08/10 16:41:35 jsing Exp $ */ /* * Copyright (c) 1996 Michael Shalayeff @@ -530,7 +530,7 @@ biosopen(struct open_file *f, ...) } if (bv->sbv_level == 'C' && bv->sbv_keys == NULL) - if (sr_crypto_decrypt_keys(bv) != 0) + if (sr_crypto_unlock_volume(bv) != 0) return EPERM; if (bv->sbv_diskinfo == NULL) { diff --git a/sys/arch/sparc64/stand/ofwboot/boot.c b/sys/arch/sparc64/stand/ofwboot/boot.c index 09a2a4714d2..2321dea65ed 100644 --- a/sys/arch/sparc64/stand/ofwboot/boot.c +++ b/sys/arch/sparc64/stand/ofwboot/boot.c @@ -1,4 +1,4 @@ -/* $OpenBSD: boot.c,v 1.28 2018/03/29 08:12:58 stsp Exp $ */ +/* $OpenBSD: boot.c,v 1.29 2018/08/10 16:41:35 jsing Exp $ */ /* $NetBSD: boot.c,v 1.3 2001/05/31 08:55:19 mrg Exp $ */ /* * Copyright (c) 1997, 1999 Eduardo E. Horvath. All rights reserved. @@ -317,7 +317,7 @@ srbootdev(const char *bootline) } if (bv->sbv_level == 'C' && bv->sbv_keys == NULL) - if (sr_crypto_decrypt_keys(bv) != 0) + if (sr_crypto_unlock_volume(bv) != 0) return EPERM; if (bv->sbv_diskinfo == NULL) { diff --git a/sys/lib/libsa/softraid.c b/sys/lib/libsa/softraid.c index 84824038de7..41e964f879e 100644 --- a/sys/lib/libsa/softraid.c +++ b/sys/lib/libsa/softraid.c @@ -1,4 +1,4 @@ -/* $OpenBSD: softraid.c,v 1.3 2017/11/10 16:50:59 sunil Exp $ */ +/* $OpenBSD: softraid.c,v 1.4 2018/08/10 16:41:35 jsing Exp $ */ /* * Copyright (c) 2012 Joel Sing @@ -107,66 +107,54 @@ sr_crypto_calculate_check_hmac_sha1(u_int8_t *maskkey, int maskkey_size, explicit_bzero(&shactx, sizeof(shactx)); } -int -sr_crypto_decrypt_keys(struct sr_boot_volume *bv) +static int +sr_crypto_decrypt_keys(struct sr_meta_crypto *cm, + struct sr_crypto_kdfinfo *kdfinfo, u_int8_t *kp) { - struct sr_meta_crypto *cm; - struct sr_boot_keydisk *kd; - struct sr_meta_opt_item *omi; - struct sr_crypto_pbkdf *kdfhint; - struct sr_crypto_kdfinfo kdfinfo; - char passphrase[PASSPHRASE_LENGTH]; u_int8_t digest[SHA1_DIGEST_LENGTH]; - u_int8_t *keys = NULL; - u_int8_t *kp, *cp; rijndael_ctx ctx; - u_int32_t type; + u_int8_t *cp; int rv = -1; - int c, i; + int i; - SLIST_FOREACH(omi, &bv->sbv_meta_opt, omi_link) - if (omi->omi_som->som_type == SR_OPT_CRYPTO) - break; - - if (omi == NULL) { - printf("Crypto metadata not found!\n"); + if (rijndael_set_key(&ctx, kdfinfo->maskkey, 256) != 0) goto done; - } - cm = (struct sr_meta_crypto *)omi->omi_som; - kdfhint = (struct sr_crypto_pbkdf *)&cm->scm_kdfhint; + cp = (u_int8_t *)cm->scm_key; + for (i = 0; i < SR_CRYPTO_KEYBLOCK_BYTES; i += RIJNDAEL128_BLOCK_LEN) + rijndael_decrypt(&ctx, (u_char *)(cp + i), (u_char *)(kp + i)); - switch (cm->scm_mask_alg) { - case SR_CRYPTOM_AES_ECB_256: - break; - default: - printf("unsupported encryption algorithm %u\n", - cm->scm_mask_alg); - goto done; - } + /* Check that the key decrypted properly. */ + sr_crypto_calculate_check_hmac_sha1(kdfinfo->maskkey, + sizeof(kdfinfo->maskkey), kp, SR_CRYPTO_KEYBLOCK_BYTES, digest); - SLIST_FOREACH(kd, &sr_keydisks, kd_link) { - if (bcmp(&kd->kd_uuid, &bv->sbv_uuid, sizeof(kd->kd_uuid)) == 0) - break; - } - if (kd) { - bcopy(&kd->kd_key, &kdfinfo.maskkey, sizeof(kdfinfo.maskkey)); - } else if (kdfhint->generic.type == SR_CRYPTOKDFT_KEYDISK) { - printf("keydisk not found\n"); - goto done; - } else { - if (kdfhint->generic.type != SR_CRYPTOKDFT_PKCS5_PBKDF2 && - kdfhint->generic.type != SR_CRYPTOKDFT_BCRYPT_PBKDF) { - printf("unknown KDF type %u\n", kdfhint->generic.type); - goto done; - } + if (bcmp(digest, cm->chk_hmac_sha1.sch_mac, sizeof(digest)) == 0) + rv = 0; + + done: + explicit_bzero(digest, sizeof(digest)); + + return rv; +} + +static int +sr_crypto_passphrase_decrypt(struct sr_meta_crypto *cm, + struct sr_crypto_kdfinfo *kdfinfo, u_int8_t *kp) +{ + char passphrase[PASSPHRASE_LENGTH]; + struct sr_crypto_pbkdf *kdfhint; + int rv = -1; + int c, i; + kdfhint = (struct sr_crypto_pbkdf *)&cm->scm_kdfhint; + + for (;;) { printf("Passphrase: "); for (i = 0; i < PASSPHRASE_LENGTH - 1; i++) { c = cngetc(); - if (c == '\r' || c == '\n') + if (c == '\r' || c == '\n') { break; - else if (c == '\b') { + } else if (c == '\b') { i = i > 0 ? i - 2 : -1; continue; } @@ -175,54 +163,118 @@ sr_crypto_decrypt_keys(struct sr_boot_volume *bv) passphrase[i] = 0; printf("\n"); + /* Abort on an empty passphrase. */ + if (i == 0) { + printf("aborting...\n"); + goto done; + } + #ifdef DEBUG printf("Got passphrase: %s with len %d\n", passphrase, strlen(passphrase)); #endif - type = kdfhint->generic.type; - if (type == SR_CRYPTOKDFT_PKCS5_PBKDF2) { + switch (kdfhint->generic.type) { + case SR_CRYPTOKDFT_PKCS5_PBKDF2: if (pkcs5_pbkdf2(passphrase, strlen(passphrase), kdfhint->salt, sizeof(kdfhint->salt), - kdfinfo.maskkey, sizeof(kdfinfo.maskkey), + kdfinfo->maskkey, sizeof(kdfinfo->maskkey), kdfhint->rounds) != 0) { printf("pkcs5_pbkdf2 failed\n"); goto done; } - } else if (type == SR_CRYPTOKDFT_BCRYPT_PBKDF) { + break; + + case SR_CRYPTOKDFT_BCRYPT_PBKDF: if (bcrypt_pbkdf(passphrase, strlen(passphrase), kdfhint->salt, sizeof(kdfhint->salt), - kdfinfo.maskkey, sizeof(kdfinfo.maskkey), + kdfinfo->maskkey, sizeof(kdfinfo->maskkey), kdfhint->rounds) != 0) { printf("bcrypt_pbkdf failed\n"); goto done; } - } else { + break; + + default: printf("unknown KDF type %u\n", kdfhint->generic.type); goto done; } + + if (sr_crypto_decrypt_keys(cm, kdfinfo, kp) == 0) { + rv = 0; + goto done; + } + + printf("incorrect passphrase\n"); } - /* kdfinfo->maskkey now has key. */ + done: + explicit_bzero(passphrase, PASSPHRASE_LENGTH); - /* Decrypt disk keys. */ - keys = alloc(SR_CRYPTO_KEYBLOCK_BYTES); - bzero(keys, SR_CRYPTO_KEYBLOCK_BYTES); + return rv; +} + +int +sr_crypto_unlock_volume(struct sr_boot_volume *bv) +{ + struct sr_meta_crypto *cm; + struct sr_boot_keydisk *kd; + struct sr_meta_opt_item *omi; + struct sr_crypto_pbkdf *kdfhint; + struct sr_crypto_kdfinfo kdfinfo; + u_int8_t *keys = NULL; + int rv = -1; + + SLIST_FOREACH(omi, &bv->sbv_meta_opt, omi_link) + if (omi->omi_som->som_type == SR_OPT_CRYPTO) + break; - if (rijndael_set_key(&ctx, kdfinfo.maskkey, 256) != 0) + if (omi == NULL) { + printf("crypto metadata not found!\n"); goto done; + } - cp = (u_int8_t *)cm->scm_key; - kp = keys; - for (i = 0; i < SR_CRYPTO_KEYBLOCK_BYTES; i += RIJNDAEL128_BLOCK_LEN) - rijndael_decrypt(&ctx, (u_char *)(cp + i), (u_char *)(kp + i)); + cm = (struct sr_meta_crypto *)omi->omi_som; + kdfhint = (struct sr_crypto_pbkdf *)&cm->scm_kdfhint; - /* Check that the key decrypted properly. */ - sr_crypto_calculate_check_hmac_sha1(kdfinfo.maskkey, - sizeof(kdfinfo.maskkey), keys, SR_CRYPTO_KEYBLOCK_BYTES, digest); + switch (cm->scm_mask_alg) { + case SR_CRYPTOM_AES_ECB_256: + break; + default: + printf("unsupported encryption algorithm %u\n", + cm->scm_mask_alg); + goto done; + } - if (bcmp(digest, cm->chk_hmac_sha1.sch_mac, sizeof(digest))) { - printf("incorrect passphrase or keydisk\n"); + keys = alloc(SR_CRYPTO_KEYBLOCK_BYTES); + bzero(keys, SR_CRYPTO_KEYBLOCK_BYTES); + + switch (kdfhint->generic.type) { + case SR_CRYPTOKDFT_KEYDISK: + SLIST_FOREACH(kd, &sr_keydisks, kd_link) { + if (bcmp(&kd->kd_uuid, &bv->sbv_uuid, + sizeof(kd->kd_uuid)) == 0) + break; + } + if (kd == NULL) { + printf("keydisk not found\n"); + goto done; + } + bcopy(&kd->kd_key, &kdfinfo.maskkey, sizeof(kdfinfo.maskkey)); + if (sr_crypto_decrypt_keys(cm, &kdfinfo, keys) != 0) { + printf("incorrect keydisk\n"); + goto done; + } + break; + + case SR_CRYPTOKDFT_BCRYPT_PBKDF: + case SR_CRYPTOKDFT_PKCS5_PBKDF2: + if (sr_crypto_passphrase_decrypt(cm, &kdfinfo, keys) != 0) + goto done; + break; + + default: + printf("unknown KDF type %u\n", kdfhint->generic.type); goto done; } @@ -233,10 +285,8 @@ sr_crypto_decrypt_keys(struct sr_boot_volume *bv) rv = 0; -done: - explicit_bzero(passphrase, PASSPHRASE_LENGTH); + done: explicit_bzero(&kdfinfo, sizeof(kdfinfo)); - explicit_bzero(digest, sizeof(digest)); if (keys != NULL && rv != 0) { explicit_bzero(keys, SR_CRYPTO_KEYBLOCK_BYTES); diff --git a/sys/lib/libsa/softraid.h b/sys/lib/libsa/softraid.h index fc02f18b04d..f4f7efe99fc 100644 --- a/sys/lib/libsa/softraid.h +++ b/sys/lib/libsa/softraid.h @@ -1,4 +1,4 @@ -/* $OpenBSD: softraid.h,v 1.1 2016/09/11 17:49:36 jsing Exp $ */ +/* $OpenBSD: softraid.h,v 1.2 2018/08/10 16:41:35 jsing Exp $ */ /* * Copyright (c) 2012 Joel Sing @@ -34,6 +34,6 @@ extern struct sr_boot_volume_head sr_volumes; extern struct sr_boot_keydisk_head sr_keydisks; void sr_clear_keys(void); -int sr_crypto_decrypt_keys(struct sr_boot_volume *); +int sr_crypto_unlock_volume(struct sr_boot_volume *); #endif /* _SOFTRAID_H */