diff options
author | djm@openbsd.org <djm@openbsd.org> | 2020-04-11 10:16:11 +0000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2020-04-11 20:20:58 +1000 |
commit | 3779b50ee952078018a5d9e1df20977f4355df17 (patch) | |
tree | 4f21fc8fb3bdba5432e34064560f543479eee4ea /sshkey.c | |
parent | b6a4013647db67ec622c144a9e05dd768f1966b3 (diff) |
upstream: Refactor private key parsing. Eliminates a fair bit of
duplicated code and fixes oss-fuzz#20074 (NULL deref) caused by a missing key
type check in the ECDSA_CERT parsing path.
feedback and ok markus@
OpenBSD-Commit-ID: 4711981d88afb7196d228f7baad9be1d3b20f9c9
Diffstat (limited to 'sshkey.c')
-rw-r--r-- | sshkey.c | 187 |
1 files changed, 40 insertions, 147 deletions
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: sshkey.c,v 1.107 2020/04/08 00:08:46 djm Exp $ */ | 1 | /* $OpenBSD: sshkey.c,v 1.108 2020/04/11 10:16:11 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. | 3 | * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. |
4 | * Copyright (c) 2008 Alexander von Gernler. All rights reserved. | 4 | * Copyright (c) 2008 Alexander von Gernler. All rights reserved. |
@@ -3394,38 +3394,52 @@ sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **kp) | |||
3394 | if ((r = sshbuf_get_cstring(buf, &tname, NULL)) != 0) | 3394 | if ((r = sshbuf_get_cstring(buf, &tname, NULL)) != 0) |
3395 | goto out; | 3395 | goto out; |
3396 | type = sshkey_type_from_name(tname); | 3396 | type = sshkey_type_from_name(tname); |
3397 | switch (type) { | 3397 | if (sshkey_type_is_cert(type)) { |
3398 | #ifdef WITH_OPENSSL | 3398 | /* |
3399 | case KEY_DSA: | 3399 | * Certificate key private keys begin with the certificate |
3400 | * itself. Make sure this matches the type of the enclosing | ||
3401 | * private key. | ||
3402 | */ | ||
3403 | if ((r = sshkey_froms(buf, &k)) != 0) | ||
3404 | goto out; | ||
3405 | if (k->type != type) { | ||
3406 | r = SSH_ERR_KEY_CERT_MISMATCH; | ||
3407 | goto out; | ||
3408 | } | ||
3409 | /* For ECDSA keys, the group must match too */ | ||
3410 | if (k->type == KEY_ECDSA && | ||
3411 | k->ecdsa_nid != sshkey_ecdsa_nid_from_name(tname)) { | ||
3412 | r = SSH_ERR_KEY_CERT_MISMATCH; | ||
3413 | goto out; | ||
3414 | } | ||
3415 | } else { | ||
3400 | if ((k = sshkey_new(type)) == NULL) { | 3416 | if ((k = sshkey_new(type)) == NULL) { |
3401 | r = SSH_ERR_ALLOC_FAIL; | 3417 | r = SSH_ERR_ALLOC_FAIL; |
3402 | goto out; | 3418 | goto out; |
3403 | } | 3419 | } |
3420 | } | ||
3421 | switch (type) { | ||
3422 | #ifdef WITH_OPENSSL | ||
3423 | case KEY_DSA: | ||
3404 | if ((r = sshbuf_get_bignum2(buf, &dsa_p)) != 0 || | 3424 | if ((r = sshbuf_get_bignum2(buf, &dsa_p)) != 0 || |
3405 | (r = sshbuf_get_bignum2(buf, &dsa_q)) != 0 || | 3425 | (r = sshbuf_get_bignum2(buf, &dsa_q)) != 0 || |
3406 | (r = sshbuf_get_bignum2(buf, &dsa_g)) != 0 || | 3426 | (r = sshbuf_get_bignum2(buf, &dsa_g)) != 0 || |
3407 | (r = sshbuf_get_bignum2(buf, &dsa_pub_key)) != 0 || | 3427 | (r = sshbuf_get_bignum2(buf, &dsa_pub_key)) != 0) |
3408 | (r = sshbuf_get_bignum2(buf, &dsa_priv_key)) != 0) | ||
3409 | goto out; | 3428 | goto out; |
3410 | if (!DSA_set0_pqg(k->dsa, dsa_p, dsa_q, dsa_g)) { | 3429 | if (!DSA_set0_pqg(k->dsa, dsa_p, dsa_q, dsa_g)) { |
3411 | r = SSH_ERR_LIBCRYPTO_ERROR; | 3430 | r = SSH_ERR_LIBCRYPTO_ERROR; |
3412 | goto out; | 3431 | goto out; |
3413 | } | 3432 | } |
3414 | dsa_p = dsa_q = dsa_g = NULL; /* transferred */ | 3433 | dsa_p = dsa_q = dsa_g = NULL; /* transferred */ |
3415 | if (!DSA_set0_key(k->dsa, dsa_pub_key, dsa_priv_key)) { | 3434 | if (!DSA_set0_key(k->dsa, dsa_pub_key, NULL)) { |
3416 | r = SSH_ERR_LIBCRYPTO_ERROR; | 3435 | r = SSH_ERR_LIBCRYPTO_ERROR; |
3417 | goto out; | 3436 | goto out; |
3418 | } | 3437 | } |
3419 | dsa_pub_key = dsa_priv_key = NULL; /* transferred */ | 3438 | dsa_pub_key = NULL; /* transferred */ |
3420 | break; | 3439 | /* FALLTHROUGH */ |
3421 | case KEY_DSA_CERT: | 3440 | case KEY_DSA_CERT: |
3422 | if ((r = sshkey_froms(buf, &k)) != 0 || | 3441 | if ((r = sshbuf_get_bignum2(buf, &dsa_priv_key)) != 0) |
3423 | (r = sshbuf_get_bignum2(buf, &dsa_priv_key)) != 0) | ||
3424 | goto out; | 3442 | goto out; |
3425 | if (k->type != type) { | ||
3426 | r = SSH_ERR_INVALID_FORMAT; | ||
3427 | goto out; | ||
3428 | } | ||
3429 | if (!DSA_set0_key(k->dsa, NULL, dsa_priv_key)) { | 3443 | if (!DSA_set0_key(k->dsa, NULL, dsa_priv_key)) { |
3430 | r = SSH_ERR_LIBCRYPTO_ERROR; | 3444 | r = SSH_ERR_LIBCRYPTO_ERROR; |
3431 | goto out; | 3445 | goto out; |
@@ -3434,10 +3448,6 @@ sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **kp) | |||
3434 | break; | 3448 | break; |
3435 | # ifdef OPENSSL_HAS_ECC | 3449 | # ifdef OPENSSL_HAS_ECC |
3436 | case KEY_ECDSA: | 3450 | case KEY_ECDSA: |
3437 | if ((k = sshkey_new(type)) == NULL) { | ||
3438 | r = SSH_ERR_ALLOC_FAIL; | ||
3439 | goto out; | ||
3440 | } | ||
3441 | if ((k->ecdsa_nid = sshkey_ecdsa_nid_from_name(tname)) == -1) { | 3451 | if ((k->ecdsa_nid = sshkey_ecdsa_nid_from_name(tname)) == -1) { |
3442 | r = SSH_ERR_INVALID_ARGUMENT; | 3452 | r = SSH_ERR_INVALID_ARGUMENT; |
3443 | goto out; | 3453 | goto out; |
@@ -3453,27 +3463,12 @@ sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **kp) | |||
3453 | r = SSH_ERR_LIBCRYPTO_ERROR; | 3463 | r = SSH_ERR_LIBCRYPTO_ERROR; |
3454 | goto out; | 3464 | goto out; |
3455 | } | 3465 | } |
3456 | if ((r = sshbuf_get_eckey(buf, k->ecdsa)) != 0 || | 3466 | if ((r = sshbuf_get_eckey(buf, k->ecdsa)) != 0) |
3457 | (r = sshbuf_get_bignum2(buf, &exponent))) | ||
3458 | goto out; | ||
3459 | if (EC_KEY_set_private_key(k->ecdsa, exponent) != 1) { | ||
3460 | r = SSH_ERR_LIBCRYPTO_ERROR; | ||
3461 | goto out; | ||
3462 | } | ||
3463 | if ((r = sshkey_ec_validate_public(EC_KEY_get0_group(k->ecdsa), | ||
3464 | EC_KEY_get0_public_key(k->ecdsa))) != 0 || | ||
3465 | (r = sshkey_ec_validate_private(k->ecdsa)) != 0) | ||
3466 | goto out; | 3467 | goto out; |
3467 | break; | 3468 | /* FALLTHROUGH */ |
3468 | case KEY_ECDSA_CERT: | 3469 | case KEY_ECDSA_CERT: |
3469 | if ((r = sshkey_froms(buf, &k)) != 0 || | 3470 | if ((r = sshbuf_get_bignum2(buf, &exponent)) != 0) |
3470 | (r = sshbuf_get_bignum2(buf, &exponent)) != 0) | ||
3471 | goto out; | ||
3472 | if (k->type != type || | ||
3473 | k->ecdsa_nid != sshkey_ecdsa_nid_from_name(tname)) { | ||
3474 | r = SSH_ERR_INVALID_FORMAT; | ||
3475 | goto out; | 3471 | goto out; |
3476 | } | ||
3477 | if (EC_KEY_set_private_key(k->ecdsa, exponent) != 1) { | 3472 | if (EC_KEY_set_private_key(k->ecdsa, exponent) != 1) { |
3478 | r = SSH_ERR_LIBCRYPTO_ERROR; | 3473 | r = SSH_ERR_LIBCRYPTO_ERROR; |
3479 | goto out; | 3474 | goto out; |
@@ -3484,10 +3479,6 @@ sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **kp) | |||
3484 | goto out; | 3479 | goto out; |
3485 | break; | 3480 | break; |
3486 | case KEY_ECDSA_SK: | 3481 | case KEY_ECDSA_SK: |
3487 | if ((k = sshkey_new(type)) == NULL) { | ||
3488 | r = SSH_ERR_ALLOC_FAIL; | ||
3489 | goto out; | ||
3490 | } | ||
3491 | if ((k->ecdsa_nid = sshkey_ecdsa_nid_from_name(tname)) == -1) { | 3482 | if ((k->ecdsa_nid = sshkey_ecdsa_nid_from_name(tname)) == -1) { |
3492 | r = SSH_ERR_INVALID_ARGUMENT; | 3483 | r = SSH_ERR_INVALID_ARGUMENT; |
3493 | goto out; | 3484 | goto out; |
@@ -3520,8 +3511,6 @@ sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **kp) | |||
3520 | goto out; | 3511 | goto out; |
3521 | break; | 3512 | break; |
3522 | case KEY_ECDSA_SK_CERT: | 3513 | case KEY_ECDSA_SK_CERT: |
3523 | if ((r = sshkey_froms(buf, &k)) != 0) | ||
3524 | goto out; | ||
3525 | if ((k->sk_key_handle = sshbuf_new()) == NULL || | 3514 | if ((k->sk_key_handle = sshbuf_new()) == NULL || |
3526 | (k->sk_reserved = sshbuf_new()) == NULL) { | 3515 | (k->sk_reserved = sshbuf_new()) == NULL) { |
3527 | r = SSH_ERR_ALLOC_FAIL; | 3516 | r = SSH_ERR_ALLOC_FAIL; |
@@ -3539,43 +3528,21 @@ sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **kp) | |||
3539 | break; | 3528 | break; |
3540 | # endif /* OPENSSL_HAS_ECC */ | 3529 | # endif /* OPENSSL_HAS_ECC */ |
3541 | case KEY_RSA: | 3530 | case KEY_RSA: |
3542 | if ((k = sshkey_new(type)) == NULL) { | ||
3543 | r = SSH_ERR_ALLOC_FAIL; | ||
3544 | goto out; | ||
3545 | } | ||
3546 | if ((r = sshbuf_get_bignum2(buf, &rsa_n)) != 0 || | 3531 | if ((r = sshbuf_get_bignum2(buf, &rsa_n)) != 0 || |
3547 | (r = sshbuf_get_bignum2(buf, &rsa_e)) != 0 || | 3532 | (r = sshbuf_get_bignum2(buf, &rsa_e)) != 0) |
3548 | (r = sshbuf_get_bignum2(buf, &rsa_d)) != 0 || | ||
3549 | (r = sshbuf_get_bignum2(buf, &rsa_iqmp)) != 0 || | ||
3550 | (r = sshbuf_get_bignum2(buf, &rsa_p)) != 0 || | ||
3551 | (r = sshbuf_get_bignum2(buf, &rsa_q)) != 0) | ||
3552 | goto out; | ||
3553 | if (!RSA_set0_key(k->rsa, rsa_n, rsa_e, rsa_d)) { | ||
3554 | r = SSH_ERR_LIBCRYPTO_ERROR; | ||
3555 | goto out; | 3533 | goto out; |
3556 | } | 3534 | if (!RSA_set0_key(k->rsa, rsa_n, rsa_e, NULL)) { |
3557 | rsa_n = rsa_e = rsa_d = NULL; /* transferred */ | ||
3558 | if (!RSA_set0_factors(k->rsa, rsa_p, rsa_q)) { | ||
3559 | r = SSH_ERR_LIBCRYPTO_ERROR; | 3535 | r = SSH_ERR_LIBCRYPTO_ERROR; |
3560 | goto out; | 3536 | goto out; |
3561 | } | 3537 | } |
3562 | rsa_p = rsa_q = NULL; /* transferred */ | 3538 | rsa_n = rsa_e = NULL; /* transferred */ |
3563 | if ((r = check_rsa_length(k->rsa)) != 0) | 3539 | /* FALLTHROUGH */ |
3564 | goto out; | ||
3565 | if ((r = ssh_rsa_complete_crt_parameters(k, rsa_iqmp)) != 0) | ||
3566 | goto out; | ||
3567 | break; | ||
3568 | case KEY_RSA_CERT: | 3540 | case KEY_RSA_CERT: |
3569 | if ((r = sshkey_froms(buf, &k)) != 0 || | 3541 | if ((r = sshbuf_get_bignum2(buf, &rsa_d)) != 0 || |
3570 | (r = sshbuf_get_bignum2(buf, &rsa_d)) != 0 || | ||
3571 | (r = sshbuf_get_bignum2(buf, &rsa_iqmp)) != 0 || | 3542 | (r = sshbuf_get_bignum2(buf, &rsa_iqmp)) != 0 || |
3572 | (r = sshbuf_get_bignum2(buf, &rsa_p)) != 0 || | 3543 | (r = sshbuf_get_bignum2(buf, &rsa_p)) != 0 || |
3573 | (r = sshbuf_get_bignum2(buf, &rsa_q)) != 0) | 3544 | (r = sshbuf_get_bignum2(buf, &rsa_q)) != 0) |
3574 | goto out; | 3545 | goto out; |
3575 | if (k->type != type) { | ||
3576 | r = SSH_ERR_INVALID_FORMAT; | ||
3577 | goto out; | ||
3578 | } | ||
3579 | if (!RSA_set0_key(k->rsa, NULL, NULL, rsa_d)) { | 3546 | if (!RSA_set0_key(k->rsa, NULL, NULL, rsa_d)) { |
3580 | r = SSH_ERR_LIBCRYPTO_ERROR; | 3547 | r = SSH_ERR_LIBCRYPTO_ERROR; |
3581 | goto out; | 3548 | goto out; |
@@ -3593,30 +3560,10 @@ sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **kp) | |||
3593 | break; | 3560 | break; |
3594 | #endif /* WITH_OPENSSL */ | 3561 | #endif /* WITH_OPENSSL */ |
3595 | case KEY_ED25519: | 3562 | case KEY_ED25519: |
3596 | if ((k = sshkey_new(type)) == NULL) { | ||
3597 | r = SSH_ERR_ALLOC_FAIL; | ||
3598 | goto out; | ||
3599 | } | ||
3600 | if ((r = sshbuf_get_string(buf, &ed25519_pk, &pklen)) != 0 || | ||
3601 | (r = sshbuf_get_string(buf, &ed25519_sk, &sklen)) != 0) | ||
3602 | goto out; | ||
3603 | if (pklen != ED25519_PK_SZ || sklen != ED25519_SK_SZ) { | ||
3604 | r = SSH_ERR_INVALID_FORMAT; | ||
3605 | goto out; | ||
3606 | } | ||
3607 | k->ed25519_pk = ed25519_pk; | ||
3608 | k->ed25519_sk = ed25519_sk; | ||
3609 | ed25519_pk = ed25519_sk = NULL; | ||
3610 | break; | ||
3611 | case KEY_ED25519_CERT: | 3563 | case KEY_ED25519_CERT: |
3612 | if ((r = sshkey_froms(buf, &k)) != 0 || | 3564 | if ((r = sshbuf_get_string(buf, &ed25519_pk, &pklen)) != 0 || |
3613 | (r = sshbuf_get_string(buf, &ed25519_pk, &pklen)) != 0 || | ||
3614 | (r = sshbuf_get_string(buf, &ed25519_sk, &sklen)) != 0) | 3565 | (r = sshbuf_get_string(buf, &ed25519_sk, &sklen)) != 0) |
3615 | goto out; | 3566 | goto out; |
3616 | if (k->type != type) { | ||
3617 | r = SSH_ERR_INVALID_FORMAT; | ||
3618 | goto out; | ||
3619 | } | ||
3620 | if (pklen != ED25519_PK_SZ || sklen != ED25519_SK_SZ) { | 3567 | if (pklen != ED25519_PK_SZ || sklen != ED25519_SK_SZ) { |
3621 | r = SSH_ERR_INVALID_FORMAT; | 3568 | r = SSH_ERR_INVALID_FORMAT; |
3622 | goto out; | 3569 | goto out; |
@@ -3626,38 +3573,9 @@ sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **kp) | |||
3626 | ed25519_pk = ed25519_sk = NULL; /* transferred */ | 3573 | ed25519_pk = ed25519_sk = NULL; /* transferred */ |
3627 | break; | 3574 | break; |
3628 | case KEY_ED25519_SK: | 3575 | case KEY_ED25519_SK: |
3629 | if ((k = sshkey_new(type)) == NULL) { | ||
3630 | r = SSH_ERR_ALLOC_FAIL; | ||
3631 | goto out; | ||
3632 | } | ||
3633 | if ((r = sshbuf_get_string(buf, &ed25519_pk, &pklen)) != 0) | ||
3634 | goto out; | ||
3635 | if (pklen != ED25519_PK_SZ) { | ||
3636 | r = SSH_ERR_INVALID_FORMAT; | ||
3637 | goto out; | ||
3638 | } | ||
3639 | if ((k->sk_key_handle = sshbuf_new()) == NULL || | ||
3640 | (k->sk_reserved = sshbuf_new()) == NULL) { | ||
3641 | r = SSH_ERR_ALLOC_FAIL; | ||
3642 | goto out; | ||
3643 | } | ||
3644 | if ((r = sshbuf_get_cstring(buf, &k->sk_application, | ||
3645 | NULL)) != 0 || | ||
3646 | (r = sshbuf_get_u8(buf, &k->sk_flags)) != 0 || | ||
3647 | (r = sshbuf_get_stringb(buf, k->sk_key_handle)) != 0 || | ||
3648 | (r = sshbuf_get_stringb(buf, k->sk_reserved)) != 0) | ||
3649 | goto out; | ||
3650 | k->ed25519_pk = ed25519_pk; | ||
3651 | ed25519_pk = NULL; | ||
3652 | break; | ||
3653 | case KEY_ED25519_SK_CERT: | 3576 | case KEY_ED25519_SK_CERT: |
3654 | if ((r = sshkey_froms(buf, &k)) != 0 || | 3577 | if ((r = sshbuf_get_string(buf, &ed25519_pk, &pklen)) != 0) |
3655 | (r = sshbuf_get_string(buf, &ed25519_pk, &pklen)) != 0) | ||
3656 | goto out; | ||
3657 | if (k->type != type) { | ||
3658 | r = SSH_ERR_INVALID_FORMAT; | ||
3659 | goto out; | 3578 | goto out; |
3660 | } | ||
3661 | if (pklen != ED25519_PK_SZ) { | 3579 | if (pklen != ED25519_PK_SZ) { |
3662 | r = SSH_ERR_INVALID_FORMAT; | 3580 | r = SSH_ERR_INVALID_FORMAT; |
3663 | goto out; | 3581 | goto out; |
@@ -3678,10 +3596,7 @@ sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **kp) | |||
3678 | break; | 3596 | break; |
3679 | #ifdef WITH_XMSS | 3597 | #ifdef WITH_XMSS |
3680 | case KEY_XMSS: | 3598 | case KEY_XMSS: |
3681 | if ((k = sshkey_new(type)) == NULL) { | 3599 | case KEY_XMSS_CERT: |
3682 | r = SSH_ERR_ALLOC_FAIL; | ||
3683 | goto out; | ||
3684 | } | ||
3685 | if ((r = sshbuf_get_cstring(buf, &xmss_name, NULL)) != 0 || | 3600 | if ((r = sshbuf_get_cstring(buf, &xmss_name, NULL)) != 0 || |
3686 | (r = sshkey_xmss_init(k, xmss_name)) != 0 || | 3601 | (r = sshkey_xmss_init(k, xmss_name)) != 0 || |
3687 | (r = sshbuf_get_string(buf, &xmss_pk, &pklen)) != 0 || | 3602 | (r = sshbuf_get_string(buf, &xmss_pk, &pklen)) != 0 || |
@@ -3699,28 +3614,6 @@ sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **kp) | |||
3699 | if ((r = sshkey_xmss_deserialize_state_opt(k, buf)) != 0) | 3614 | if ((r = sshkey_xmss_deserialize_state_opt(k, buf)) != 0) |
3700 | goto out; | 3615 | goto out; |
3701 | break; | 3616 | break; |
3702 | case KEY_XMSS_CERT: | ||
3703 | if ((r = sshkey_froms(buf, &k)) != 0 || | ||
3704 | (r = sshbuf_get_cstring(buf, &xmss_name, NULL)) != 0 || | ||
3705 | (r = sshbuf_get_string(buf, &xmss_pk, &pklen)) != 0 || | ||
3706 | (r = sshbuf_get_string(buf, &xmss_sk, &sklen)) != 0) | ||
3707 | goto out; | ||
3708 | if (k->type != type || strcmp(xmss_name, k->xmss_name) != 0) { | ||
3709 | r = SSH_ERR_INVALID_FORMAT; | ||
3710 | goto out; | ||
3711 | } | ||
3712 | if (pklen != sshkey_xmss_pklen(k) || | ||
3713 | sklen != sshkey_xmss_sklen(k)) { | ||
3714 | r = SSH_ERR_INVALID_FORMAT; | ||
3715 | goto out; | ||
3716 | } | ||
3717 | k->xmss_pk = xmss_pk; | ||
3718 | k->xmss_sk = xmss_sk; | ||
3719 | xmss_pk = xmss_sk = NULL; | ||
3720 | /* optional internal state */ | ||
3721 | if ((r = sshkey_xmss_deserialize_state_opt(k, buf)) != 0) | ||
3722 | goto out; | ||
3723 | break; | ||
3724 | #endif /* WITH_XMSS */ | 3617 | #endif /* WITH_XMSS */ |
3725 | default: | 3618 | default: |
3726 | r = SSH_ERR_KEY_TYPE_UNKNOWN; | 3619 | r = SSH_ERR_KEY_TYPE_UNKNOWN; |