Retry on incorrect passphrase for softraid crypto boot.
authorjsing <jsing@openbsd.org>
Fri, 10 Aug 2018 16:41:35 +0000 (16:41 +0000)
committerjsing <jsing@openbsd.org>
Fri, 10 Aug 2018 16:41:35 +0000 (16:41 +0000)
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@

sys/arch/amd64/stand/efiboot/efidev.c
sys/arch/amd64/stand/libsa/biosdev.c
sys/arch/i386/stand/libsa/biosdev.c
sys/arch/sparc64/stand/ofwboot/boot.c
sys/lib/libsa/softraid.c
sys/lib/libsa/softraid.h

index 2ec2f01..47f62b9 100644 (file)
@@ -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) {
index b9018b6..4ca8436 100644 (file)
@@ -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) {
index 4c06a87..2126d70 100644 (file)
@@ -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) {
index 09a2a47..2321dea 100644 (file)
@@ -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) {
index 8482403..41e964f 100644 (file)
@@ -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 <jsing@openbsd.org>
@@ -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);
index fc02f18..f4f7efe 100644 (file)
@@ -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 <jsing@openbsd.org>
@@ -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 */