fix CVE-2023-1667 and CVE-2023-2283

This commit is contained in:
renmingshuai 2023-05-24 12:13:09 +08:00
parent 31dbe772fa
commit df87af5d4e
12 changed files with 1452 additions and 1 deletions

View File

@ -0,0 +1,35 @@
From a30339d7b16da7784413e4a4667feb3604ed0458 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@redhat.com>
Date: Fri, 10 Mar 2023 16:14:08 +0100
Subject: [PATCH] CVE-2023-1667:packet_cb: Log more verbose error if signature
verification fails
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Norbert Pocs <npocs@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
---
Conflict:NA
Reference:https://gitlab.com/libssh/libssh-mirror/commit/a30339d7b16da7784413e4a4667feb3604ed0458
---
src/packet_cb.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/packet_cb.c b/src/packet_cb.c
index 39575b1..3e4d5f6 100644
--- a/src/packet_cb.c
+++ b/src/packet_cb.c
@@ -156,6 +156,9 @@ SSH_PACKET_CALLBACK(ssh_packet_newkeys){
session->next_crypto->digest_len);
SSH_SIGNATURE_FREE(sig);
if (rc == SSH_ERROR) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Failed to verify server hostkey signature");
goto error;
}
SSH_LOG(SSH_LOG_PROTOCOL,"Signature verified and valid");
--
2.33.0

View File

@ -0,0 +1,103 @@
From e8dfbb85a28514e1f869dac3000c6cec6cb8d08d Mon Sep 17 00:00:00 2001
From: Norbert Pocs <npocs@redhat.com>
Date: Mon, 24 Apr 2023 11:51:36 +0200
Subject: [PATCH] CVE-2023-2283:pki_crypto: Fix possible authentication bypass
The return value is changed by the call to pki_key_check_hash_compatible
causing the possibility of returning SSH_OK if memory allocation error
happens later in the function.
The assignment of SSH_ERROR if the verification fails is no longer needed,
because the value of the variable is already SSH_ERROR.
Signed-off-by: Norbert Pocs <npocs@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
Conflict:NA
Reference:https://gitlab.com/libssh/libssh-mirror/commit/e8dfbb85a28514e1f869dac3000c6cec6cb8d08d
---
src/pki_crypto.c | 32 ++++++++++++++++++--------------
1 file changed, 18 insertions(+), 14 deletions(-)
diff --git a/src/pki_crypto.c b/src/pki_crypto.c
index 013f569e..635b82cb 100644
--- a/src/pki_crypto.c
+++ b/src/pki_crypto.c
@@ -3175,8 +3175,12 @@ int pki_verify_data_signature(ssh_signature signature,
unsigned char *raw_sig_data = NULL;
unsigned int raw_sig_len;
+ /* Function return code
+ * Do not change this variable throughout the function until the signature
+ * is successfully verified!
+ */
int rc = SSH_ERROR;
- int evp_rc;
+ int ok;
if (pubkey == NULL || ssh_key_is_private(pubkey) || input == NULL ||
signature == NULL || (signature->raw_sig == NULL
@@ -3191,8 +3195,8 @@ int pki_verify_data_signature(ssh_signature signature,
}
/* Check if public key and hash type are compatible */
- rc = pki_key_check_hash_compatible(pubkey, signature->hash_type);
- if (rc != SSH_OK) {
+ ok = pki_key_check_hash_compatible(pubkey, signature->hash_type);
+ if (ok != SSH_OK) {
return SSH_ERROR;
}
@@ -3237,8 +3241,8 @@ int pki_verify_data_signature(ssh_signature signature,
}
/* Verify the signature */
- evp_rc = EVP_DigestVerifyInit(ctx, NULL, md, NULL, pkey);
- if (evp_rc != 1){
+ ok = EVP_DigestVerifyInit(ctx, NULL, md, NULL, pkey);
+ if (ok != 1){
SSH_LOG(SSH_LOG_TRACE,
"EVP_DigestVerifyInit() failed: %s",
ERR_error_string(ERR_get_error(), NULL));
@@ -3246,28 +3250,28 @@ int pki_verify_data_signature(ssh_signature signature,
}
#ifdef HAVE_OPENSSL_EVP_DIGESTVERIFY
- evp_rc = EVP_DigestVerify(ctx, raw_sig_data, raw_sig_len, input, input_len);
+ ok = EVP_DigestVerify(ctx, raw_sig_data, raw_sig_len, input, input_len);
#else
- evp_rc = EVP_DigestVerifyUpdate(ctx, input, input_len);
- if (evp_rc != 1) {
+ ok = EVP_DigestVerifyUpdate(ctx, input, input_len);
+ if (ok != 1) {
SSH_LOG(SSH_LOG_TRACE,
"EVP_DigestVerifyUpdate() failed: %s",
ERR_error_string(ERR_get_error(), NULL));
goto out;
}
- evp_rc = EVP_DigestVerifyFinal(ctx, raw_sig_data, raw_sig_len);
+ ok = EVP_DigestVerifyFinal(ctx, raw_sig_data, raw_sig_len);
#endif
- if (evp_rc == 1) {
- SSH_LOG(SSH_LOG_TRACE, "Signature valid");
- rc = SSH_OK;
- } else {
+ if (ok != 1) {
SSH_LOG(SSH_LOG_TRACE,
"Signature invalid: %s",
ERR_error_string(ERR_get_error(), NULL));
- rc = SSH_ERROR;
+ goto out;
}
+ SSH_LOG(SSH_LOG_TRACE, "Signature valid");
+ rc = SSH_OK;
+
out:
if (ctx != NULL) {
EVP_MD_CTX_free(ctx);
--
2.33.0

View File

@ -0,0 +1,35 @@
From 247a4a761cfa745ed1090290c5107de6321143c9 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@redhat.com>
Date: Tue, 14 Mar 2023 11:35:43 +0100
Subject: [PATCH] CVE-2023-1667:packet: Do not allow servers to initiate
handshake
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Norbert Pocs <npocs@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
Conflict:NA
Reference:https://gitlab.com/libssh/libssh-mirror/commit/247a4a761cfa745ed1090290c5107de6321143c9
---
src/packet.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/packet.c b/src/packet.c
index 60fc7fa3..eb7eb42a 100644
--- a/src/packet.c
+++ b/src/packet.c
@@ -366,6 +366,11 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
* - session->dh_handshake_state = DH_STATE_NEWKEYS_SENT
* */
+ if (!session->server) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
if (session->session_state != SSH_SESSION_STATE_DH) {
rc = SSH_PACKET_DENIED;
break;
--
2.23.0

View File

@ -0,0 +1,33 @@
From c68a58575b6d0520e342cb3d3796a8fecd66405d Mon Sep 17 00:00:00 2001
From: Norbert Pocs <npocs@redhat.com>
Date: Mon, 24 Apr 2023 11:55:59 +0200
Subject: [PATCH] CVE-2023-2283:pki_crypto: Remove unnecessary NULL check
Signed-off-by: Norbert Pocs <npocs@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
Conflict:NA
Reference:https://gitlab.com/libssh/libssh-mirror/commit/c68a58575b6d0520e342cb3d3796a8fecd66405d
---
src/pki_crypto.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/src/pki_crypto.c b/src/pki_crypto.c
index d17ae6a..4b85af6 100644
--- a/src/pki_crypto.c
+++ b/src/pki_crypto.c
@@ -3264,9 +3264,7 @@ int pki_verify_data_signature(ssh_signature signature,
rc = SSH_OK;
out:
- if (ctx != NULL) {
- EVP_MD_CTX_free(ctx);
- }
+ EVP_MD_CTX_free(ctx);
EVP_PKEY_free(pkey);
return rc;
}
--
2.23.0

View File

@ -0,0 +1,107 @@
From 99760776d4552d8e63edd68ba4a7448766517b8c Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@redhat.com>
Date: Mon, 13 Mar 2023 15:11:25 +0100
Subject: [PATCH] CVE-2023-1667:kex: Remove needless function argument
The information if the session is client or server session is already part of
the session structure so this argument only duplicated information.
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Norbert Pocs <npocs@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
Conflict:NA
Reference:https://gitlab.com/libssh/libssh-mirror/commit/99760776d4552d8e63edd68ba4a7448766517b8c
---
include/libssh/kex.h | 2 +-
src/client.c | 4 ++--
src/kex.c | 7 ++++---
src/server.c | 4 ++--
4 files changed, 9 insertions(+), 8 deletions(-)
diff --git a/include/libssh/kex.h b/include/libssh/kex.h
index 3a1f4a6..2ace69b 100644
--- a/include/libssh/kex.h
+++ b/include/libssh/kex.h
@@ -33,7 +33,7 @@ struct ssh_kex_struct {
SSH_PACKET_CALLBACK(ssh_packet_kexinit);
-int ssh_send_kex(ssh_session session, int server_kex);
+int ssh_send_kex(ssh_session session);
void ssh_list_kex(struct ssh_kex_struct *kex);
int ssh_set_client_kex(ssh_session session);
int ssh_kex_select_methods(ssh_session session);
diff --git a/src/client.c b/src/client.c
index 4eb798c..954ed39 100644
--- a/src/client.c
+++ b/src/client.c
@@ -420,7 +420,7 @@ static void ssh_client_connection_callback(ssh_session session)
if (rc != SSH_OK) {
goto error;
}
- rc = ssh_send_kex(session, 0);
+ rc = ssh_send_kex(session);
if (rc < 0) {
goto error;
}
@@ -439,7 +439,7 @@ static void ssh_client_connection_callback(ssh_session session)
if (rc != SSH_OK) {
goto error;
}
- rc = ssh_send_kex(session, 0);
+ rc = ssh_send_kex(session);
if (rc < 0) {
goto error;
}
diff --git a/src/kex.c b/src/kex.c
index 82071c7..4080a6b 100644
--- a/src/kex.c
+++ b/src/kex.c
@@ -830,9 +830,10 @@ int ssh_kex_select_methods (ssh_session session)
/* this function only sends the predefined set of kex methods */
-int ssh_send_kex(ssh_session session, int server_kex)
+int ssh_send_kex(ssh_session session)
{
- struct ssh_kex_struct *kex = (server_kex ? &session->next_crypto->server_kex :
+ struct ssh_kex_struct *kex = (session->server ?
+ &session->next_crypto->server_kex :
&session->next_crypto->client_kex);
ssh_string str = NULL;
int i;
@@ -929,7 +930,7 @@ int ssh_send_rekex(ssh_session session)
}
session->dh_handshake_state = DH_STATE_INIT;
- rc = ssh_send_kex(session, session->server);
+ rc = ssh_send_kex(session);
if (rc < 0) {
SSH_LOG(SSH_LOG_PACKET, "Failed to send kex");
return rc;
diff --git a/src/server.c b/src/server.c
index 080203f..2728d9b 100644
--- a/src/server.c
+++ b/src/server.c
@@ -366,7 +366,7 @@ static void ssh_server_connection_callback(ssh_session session){
ssh_packet_set_default_callbacks(session);
set_status(session, 0.5f);
session->session_state=SSH_SESSION_STATE_INITIAL_KEX;
- if (ssh_send_kex(session, 1) < 0) {
+ if (ssh_send_kex(session) < 0) {
goto error;
}
break;
@@ -379,7 +379,7 @@ static void ssh_server_connection_callback(ssh_session session){
if(server_set_kex(session) == SSH_ERROR)
goto error;
/* We are in a rekeying, so we need to send the server kex */
- if(ssh_send_kex(session, 1) < 0)
+ if (ssh_send_kex(session) < 0)
goto error;
}
ssh_list_kex(&session->next_crypto->client_kex); // log client kex
--
2.33.0

View File

@ -0,0 +1,101 @@
From 6df2daea040c47daff0a861a30761092886fe748 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@redhat.com>
Date: Thu, 16 Mar 2023 14:16:11 +0100
Subject: [PATCH] CVE-2023-1667:kex: Factor out the kex mapping to internal
enum
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Norbert Pocs <npocs@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
Conflict:NA
Reference:https://gitlab.com/libssh/libssh-mirror/commit/6df2daea040c47daff0a861a30761092886fe748
---
src/kex.c | 64 ++++++++++++++++++++++++++++++++-----------------------
1 file changed, 37 insertions(+), 27 deletions(-)
diff --git a/src/kex.c b/src/kex.c
index 4080a6b..94ccccf 100644
--- a/src/kex.c
+++ b/src/kex.c
@@ -749,6 +749,40 @@ static const char *ssh_find_aead_hmac(const char *cipher)
return NULL;
}
+static enum ssh_key_exchange_e
+kex_select_kex_type(const char *kex)
+{
+ if (strcmp(kex, "diffie-hellman-group1-sha1") == 0) {
+ return SSH_KEX_DH_GROUP1_SHA1;
+ } else if (strcmp(kex, "diffie-hellman-group14-sha1") == 0) {
+ return SSH_KEX_DH_GROUP14_SHA1;
+ } else if (strcmp(kex, "diffie-hellman-group14-sha256") == 0) {
+ return SSH_KEX_DH_GROUP14_SHA256;
+ } else if (strcmp(kex, "diffie-hellman-group16-sha512") == 0) {
+ return SSH_KEX_DH_GROUP16_SHA512;
+ } else if (strcmp(kex, "diffie-hellman-group18-sha512") == 0) {
+ return SSH_KEX_DH_GROUP18_SHA512;
+#ifdef WITH_GEX
+ } else if (strcmp(kex, "diffie-hellman-group-exchange-sha1") == 0) {
+ return SSH_KEX_DH_GEX_SHA1;
+ } else if (strcmp(kex, "diffie-hellman-group-exchange-sha256") == 0) {
+ return SSH_KEX_DH_GEX_SHA256;
+#endif /* WITH_GEX */
+ } else if (strcmp(kex, "ecdh-sha2-nistp256") == 0) {
+ return SSH_KEX_ECDH_SHA2_NISTP256;
+ } else if (strcmp(kex, "ecdh-sha2-nistp384") == 0) {
+ return SSH_KEX_ECDH_SHA2_NISTP384;
+ } else if (strcmp(kex, "ecdh-sha2-nistp521") == 0) {
+ return SSH_KEX_ECDH_SHA2_NISTP521;
+ } else if (strcmp(kex, "curve25519-sha256@libssh.org") == 0) {
+ return SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG;
+ } else if (strcmp(kex, "curve25519-sha256") == 0) {
+ return SSH_KEX_CURVE25519_SHA256;
+ }
+ /* should not happen. We should be getting only valid names at this stage */
+ return 0;
+}
+
/** @brief Select the different methods on basis of client's and
* server's kex messages, and watches out if a match is possible.
*/
@@ -786,33 +820,9 @@ int ssh_kex_select_methods (ssh_session session)
session->next_crypto->kex_methods[i] = strdup("");
}
}
- if (strcmp(session->next_crypto->kex_methods[SSH_KEX], "diffie-hellman-group1-sha1") == 0){
- session->next_crypto->kex_type=SSH_KEX_DH_GROUP1_SHA1;
- } else if (strcmp(session->next_crypto->kex_methods[SSH_KEX], "diffie-hellman-group14-sha1") == 0){
- session->next_crypto->kex_type=SSH_KEX_DH_GROUP14_SHA1;
- } else if (strcmp(session->next_crypto->kex_methods[SSH_KEX], "diffie-hellman-group14-sha256") == 0){
- session->next_crypto->kex_type=SSH_KEX_DH_GROUP14_SHA256;
- } else if (strcmp(session->next_crypto->kex_methods[SSH_KEX], "diffie-hellman-group16-sha512") == 0){
- session->next_crypto->kex_type=SSH_KEX_DH_GROUP16_SHA512;
- } else if (strcmp(session->next_crypto->kex_methods[SSH_KEX], "diffie-hellman-group18-sha512") == 0){
- session->next_crypto->kex_type=SSH_KEX_DH_GROUP18_SHA512;
-#ifdef WITH_GEX
- } else if (strcmp(session->next_crypto->kex_methods[SSH_KEX], "diffie-hellman-group-exchange-sha1") == 0){
- session->next_crypto->kex_type=SSH_KEX_DH_GEX_SHA1;
- } else if (strcmp(session->next_crypto->kex_methods[SSH_KEX], "diffie-hellman-group-exchange-sha256") == 0){
- session->next_crypto->kex_type=SSH_KEX_DH_GEX_SHA256;
-#endif /* WITH_GEX */
- } else if (strcmp(session->next_crypto->kex_methods[SSH_KEX], "ecdh-sha2-nistp256") == 0){
- session->next_crypto->kex_type=SSH_KEX_ECDH_SHA2_NISTP256;
- } else if (strcmp(session->next_crypto->kex_methods[SSH_KEX], "ecdh-sha2-nistp384") == 0){
- session->next_crypto->kex_type=SSH_KEX_ECDH_SHA2_NISTP384;
- } else if (strcmp(session->next_crypto->kex_methods[SSH_KEX], "ecdh-sha2-nistp521") == 0){
- session->next_crypto->kex_type=SSH_KEX_ECDH_SHA2_NISTP521;
- } else if (strcmp(session->next_crypto->kex_methods[SSH_KEX], "curve25519-sha256@libssh.org") == 0){
- session->next_crypto->kex_type=SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG;
- } else if (strcmp(session->next_crypto->kex_methods[SSH_KEX], "curve25519-sha256") == 0){
- session->next_crypto->kex_type=SSH_KEX_CURVE25519_SHA256;
- }
+ kex = session->next_crypto->kex_methods[SSH_KEX];
+ session->next_crypto->kex_type = kex_select_kex_type(kex);
+
SSH_LOG(SSH_LOG_INFO, "Negotiated %s,%s,%s,%s,%s,%s,%s,%s,%s,%s",
session->next_crypto->kex_methods[SSH_KEX],
session->next_crypto->kex_methods[SSH_HOSTKEYS],
--
2.33.0

View File

@ -0,0 +1,227 @@
From b759ae557d611ba347392c051504de474a8d9b60 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@redhat.com>
Date: Fri, 17 Mar 2023 14:05:01 +0100
Subject: [PATCH] CVE-2023-1667:dh: Expose the callback cleanup functions
These will be helpful when we already sent the first key exchange packet, but we
found out that our guess was wrong and we need to initiate different key
exchange method with different callbacks.
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Norbert Pocs <npocs@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
Conflict:NA
Reference:https://gitlab.com/libssh/libssh-mirror/commit/b759ae557d611ba347392c051504de474a8d9b60
---
include/libssh/curve25519.h | 1 +
include/libssh/dh-gex.h | 1 +
include/libssh/dh.h | 1 +
include/libssh/ecdh.h | 1 +
src/curve25519.c | 7 ++++++-
src/dh-gex.c | 7 ++++++-
src/dh.c | 7 ++++++-
src/ecdh.c | 7 ++++++-
src/kex.c | 38 +++++++++++++++++++++++++++++++++++++
9 files changed, 66 insertions(+), 4 deletions(-)
diff --git a/include/libssh/curve25519.h b/include/libssh/curve25519.h
index f0cc634..77e6c31 100644
--- a/include/libssh/curve25519.h
+++ b/include/libssh/curve25519.h
@@ -48,6 +48,7 @@ typedef unsigned char ssh_curve25519_privkey[CURVE25519_PRIVKEY_SIZE];
int ssh_client_curve25519_init(ssh_session session);
+void ssh_client_curve25519_remove_callbacks(ssh_session session);
#ifdef WITH_SERVER
void ssh_server_curve25519_init(ssh_session session);
diff --git a/include/libssh/dh-gex.h b/include/libssh/dh-gex.h
index 4fc23d8..7a91d7d 100644
--- a/include/libssh/dh-gex.h
+++ b/include/libssh/dh-gex.h
@@ -24,6 +24,7 @@
#define SRC_DH_GEX_H_
int ssh_client_dhgex_init(ssh_session session);
+void ssh_client_dhgex_remove_callbacks(ssh_session session);
#ifdef WITH_SERVER
void ssh_server_dhgex_init(ssh_session session);
diff --git a/include/libssh/dh.h b/include/libssh/dh.h
index 390b30d..57f37cd 100644
--- a/include/libssh/dh.h
+++ b/include/libssh/dh.h
@@ -65,6 +65,7 @@ int ssh_dh_get_next_server_publickey_blob(ssh_session session,
ssh_string *pubkey_blob);
int ssh_client_dh_init(ssh_session session);
+void ssh_client_dh_remove_callbacks(ssh_session session);
#ifdef WITH_SERVER
void ssh_server_dh_init(ssh_session session);
#endif /* WITH_SERVER */
diff --git a/include/libssh/ecdh.h b/include/libssh/ecdh.h
index 17fe02e..c1f03a9 100644
--- a/include/libssh/ecdh.h
+++ b/include/libssh/ecdh.h
@@ -45,6 +45,7 @@
extern struct ssh_packet_callbacks_struct ssh_ecdh_client_callbacks;
/* Backend-specific functions. */
int ssh_client_ecdh_init(ssh_session session);
+void ssh_client_ecdh_remove_callbacks(ssh_session session);
int ecdh_build_k(ssh_session session);
#ifdef WITH_SERVER
diff --git a/src/curve25519.c b/src/curve25519.c
index d251755..3765443 100644
--- a/src/curve25519.c
+++ b/src/curve25519.c
@@ -172,6 +172,11 @@ int ssh_client_curve25519_init(ssh_session session)
return rc;
}
+void ssh_client_curve25519_remove_callbacks(ssh_session session)
+{
+ ssh_packet_remove_callbacks(session, &ssh_curve25519_client_callbacks);
+}
+
static int ssh_curve25519_build_k(ssh_session session)
{
ssh_curve25519_pubkey k;
@@ -285,7 +290,7 @@ static SSH_PACKET_CALLBACK(ssh_packet_client_curve25519_reply){
(void)type;
(void)user;
- ssh_packet_remove_callbacks(session, &ssh_curve25519_client_callbacks);
+ ssh_client_curve25519_remove_callbacks(session);
pubkey_blob = ssh_buffer_get_ssh_string(packet);
if (pubkey_blob == NULL) {
diff --git a/src/dh-gex.c b/src/dh-gex.c
index 88a9714..4a29854 100644
--- a/src/dh-gex.c
+++ b/src/dh-gex.c
@@ -238,6 +238,11 @@ error:
return SSH_PACKET_USED;
}
+void ssh_client_dhgex_remove_callbacks(ssh_session session)
+{
+ ssh_packet_remove_callbacks(session, &ssh_dhgex_client_callbacks);
+}
+
static SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_reply)
{
struct ssh_crypto_struct *crypto=session->next_crypto;
@@ -248,7 +253,7 @@ static SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_reply)
(void)user;
SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_KEX_DH_GEX_REPLY received");
- ssh_packet_remove_callbacks(session, &ssh_dhgex_client_callbacks);
+ ssh_client_dhgex_remove_callbacks(session);
rc = ssh_buffer_unpack(packet,
"SBS",
&pubkey_blob, &server_pubkey,
diff --git a/src/dh.c b/src/dh.c
index 18b7173..c265efc 100644
--- a/src/dh.c
+++ b/src/dh.c
@@ -342,6 +342,11 @@ error:
return SSH_ERROR;
}
+void ssh_client_dh_remove_callbacks(ssh_session session)
+{
+ ssh_packet_remove_callbacks(session, &ssh_dh_client_callbacks);
+}
+
SSH_PACKET_CALLBACK(ssh_packet_client_dh_reply){
struct ssh_crypto_struct *crypto=session->next_crypto;
ssh_string pubkey_blob = NULL;
@@ -351,7 +356,7 @@ SSH_PACKET_CALLBACK(ssh_packet_client_dh_reply){
(void)type;
(void)user;
- ssh_packet_remove_callbacks(session, &ssh_dh_client_callbacks);
+ ssh_client_dh_remove_callbacks(session);
rc = ssh_buffer_unpack(packet, "SBS", &pubkey_blob, &server_pubkey,
&crypto->dh_server_signature);
diff --git a/src/ecdh.c b/src/ecdh.c
index a4c07cc..e5b11ba 100644
--- a/src/ecdh.c
+++ b/src/ecdh.c
@@ -43,6 +43,11 @@ struct ssh_packet_callbacks_struct ssh_ecdh_client_callbacks = {
.user = NULL
};
+void ssh_client_ecdh_remove_callbacks(ssh_session session)
+{
+ ssh_packet_remove_callbacks(session, &ssh_ecdh_client_callbacks);
+}
+
/** @internal
* @brief parses a SSH_MSG_KEX_ECDH_REPLY packet and sends back
* a SSH_MSG_NEWKEYS
@@ -55,7 +60,7 @@ SSH_PACKET_CALLBACK(ssh_packet_client_ecdh_reply){
(void)type;
(void)user;
- ssh_packet_remove_callbacks(session, &ssh_ecdh_client_callbacks);
+ ssh_client_ecdh_remove_callbacks(session);
pubkey_blob = ssh_buffer_get_ssh_string(packet);
if (pubkey_blob == NULL) {
ssh_set_error(session,SSH_FATAL, "No public key in packet");
diff --git a/src/kex.c b/src/kex.c
index 94ccccf..f1dab08 100644
--- a/src/kex.c
+++ b/src/kex.c
@@ -783,6 +783,44 @@ kex_select_kex_type(const char *kex)
return 0;
}
+
+/** @internal
+ * @brief Reverts guessed callbacks set during the dh_handshake()
+ * @param session session handle
+ * @returns void
+ */
+static void revert_kex_callbacks(ssh_session session)
+{
+ switch (session->next_crypto->kex_type) {
+ case SSH_KEX_DH_GROUP1_SHA1:
+ case SSH_KEX_DH_GROUP14_SHA1:
+ case SSH_KEX_DH_GROUP14_SHA256:
+ case SSH_KEX_DH_GROUP16_SHA512:
+ case SSH_KEX_DH_GROUP18_SHA512:
+ ssh_client_dh_remove_callbacks(session);
+ break;
+#ifdef WITH_GEX
+ case SSH_KEX_DH_GEX_SHA1:
+ case SSH_KEX_DH_GEX_SHA256:
+ ssh_client_dhgex_remove_callbacks(session);
+ break;
+#endif /* WITH_GEX */
+#ifdef HAVE_ECDH
+ case SSH_KEX_ECDH_SHA2_NISTP256:
+ case SSH_KEX_ECDH_SHA2_NISTP384:
+ case SSH_KEX_ECDH_SHA2_NISTP521:
+ ssh_client_ecdh_remove_callbacks(session);
+ break;
+#endif
+#ifdef HAVE_CURVE25519
+ case SSH_KEX_CURVE25519_SHA256:
+ case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG:
+ ssh_client_curve25519_remove_callbacks(session);
+ break;
+#endif
+ }
+}
+
/** @brief Select the different methods on basis of client's and
* server's kex messages, and watches out if a match is possible.
*/
--
2.33.0

View File

@ -0,0 +1,303 @@
From fc1a8bb4555624f85ba1370721ad2086a4feff8c Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@redhat.com>
Date: Fri, 10 Mar 2023 12:59:48 +0100
Subject: [PATCH] CVE-2023-1667:kex: Correctly handle last fields of KEXINIT
also in the client side
Previously, the last two fields of KEXINIT were considered as always zero for
the key exchange. This was true for the sending side, but might have not been
true for the received KEXINIT from the peer.
This moves the construction of these two fields closer to their reading or
writing, instead of hardcoding them on the last possible moment before they go
as input to the hashing function.
This also allows accepting the first_kex_packet_follows on the client side, even
though there is no kex algorithm now that would allow this.
It also avoid memory leaks in case the server_set_kex() or ssh_set_client_kex()
gets called multiple times, ensuring the algorithms will not change under our
hands.
It also makes use of a new flag to track if we sent KEXINIT.
Previously, this was tracked only implicitly by the content of the
session->next_crypto->{server,client}_kex (local kex). If it was not set, we
considered it was not send. But given that we need to check the local kex even
before sending it when we receive first_kex_packet_follows flag in the KEXINIT,
this can no longer be used.
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Norbert Pocs <npocs@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
Conflict:NA
Reference:https://gitlab.com/libssh/libssh-mirror/commit/fc1a8bb4555624f85ba1370721ad2086a4feff8c
---
include/libssh/session.h | 5 ++
src/client.c | 2 +-
src/kex.c | 124 +++++++++++++++++++++------------------
src/server.c | 8 ++-
4 files changed, 80 insertions(+), 59 deletions(-)
diff --git a/include/libssh/session.h b/include/libssh/session.h
index 03c2bb6..1c33a02 100644
--- a/include/libssh/session.h
+++ b/include/libssh/session.h
@@ -75,6 +75,11 @@ enum ssh_pending_call_e {
/* Client successfully authenticated */
#define SSH_SESSION_FLAG_AUTHENTICATED 2
+/* The KEXINIT message can be sent first by either of the parties so this flag
+ * indicates that the message was already sent to make sure it is sent and avoid
+ * sending it twice during key exchange to simplify the state machine. */
+#define SSH_SESSION_FLAG_KEXINIT_SENT 4
+
/* codes to use with ssh_handle_packets*() */
/* Infinite timeout */
#define SSH_TIMEOUT_INFINITE -1
diff --git a/src/client.c b/src/client.c
index 954ed39..20fa33f 100644
--- a/src/client.c
+++ b/src/client.c
@@ -433,7 +433,7 @@ static void ssh_client_connection_callback(ssh_session session)
case SSH_SESSION_STATE_KEXINIT_RECEIVED:
set_status(session,0.6f);
ssh_list_kex(&session->next_crypto->server_kex);
- if (session->next_crypto->client_kex.methods[0] == NULL) {
+ if ((session->flags & SSH_SESSION_FLAG_KEXINIT_SENT) == 0) {
/* in rekeying state if next_crypto client_kex is empty */
rc = ssh_set_client_kex(session);
if (rc != SSH_OK) {
diff --git a/src/kex.c b/src/kex.c
index f1dab08..49aec45 100644
--- a/src/kex.c
+++ b/src/kex.c
@@ -345,13 +345,24 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit)
(void)user;
if (session->session_state == SSH_SESSION_STATE_AUTHENTICATED) {
- SSH_LOG(SSH_LOG_INFO, "Initiating key re-exchange");
+ if (session->dh_handshake_state == DH_STATE_FINISHED) {
+ SSH_LOG(SSH_LOG_DEBUG, "Peer initiated key re-exchange");
+ /* Reset the sent flag if the re-kex was initiated by the peer */
+ session->flags &= ~SSH_SESSION_FLAG_KEXINIT_SENT;
+ } else if (session->dh_handshake_state == DH_STATE_INIT_SENT) {
+ SSH_LOG(SSH_LOG_DEBUG, "Receeved peer kexinit answer");
+ } else {
+ ssh_set_error(session, SSH_FATAL,
+ "SSH_KEXINIT received in wrong state");
+ goto error;
+ }
} else if (session->session_state != SSH_SESSION_STATE_INITIAL_KEX) {
ssh_set_error(session,SSH_FATAL,"SSH_KEXINIT received in wrong state");
goto error;
}
if (server_kex) {
+#ifdef WITH_SERVER
len = ssh_buffer_get_data(packet,session->next_crypto->client_kex.cookie, 16);
if (len != 16) {
ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: no cookie in packet");
@@ -363,6 +374,12 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit)
ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: adding cookie failed");
goto error;
}
+
+ ok = server_set_kex(session);
+ if (ok == SSH_ERROR) {
+ goto error;
+ }
+#endif
} else {
len = ssh_buffer_get_data(packet,session->next_crypto->server_kex.cookie, 16);
if (len != 16) {
@@ -375,6 +392,11 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit)
ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: adding cookie failed");
goto error;
}
+
+ ok = ssh_set_client_kex(session);
+ if (ok == SSH_ERROR) {
+ goto error;
+ }
}
for (i = 0; i < SSH_KEX_METHODS; i++) {
@@ -419,22 +441,37 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit)
* that its value is included when computing the session ID (see
* 'make_sessionid').
*/
- if (server_kex) {
- rc = ssh_buffer_get_u8(packet, &first_kex_packet_follows);
- if (rc != 1) {
- goto error;
- }
+ rc = ssh_buffer_get_u8(packet, &first_kex_packet_follows);
+ if (rc != 1) {
+ goto error;
+ }
- rc = ssh_buffer_add_u8(session->in_hashbuf, first_kex_packet_follows);
- if (rc < 0) {
- goto error;
- }
+ rc = ssh_buffer_add_u8(session->in_hashbuf, first_kex_packet_follows);
+ if (rc < 0) {
+ goto error;
+ }
- rc = ssh_buffer_add_u32(session->in_hashbuf, kexinit_reserved);
- if (rc < 0) {
- goto error;
- }
+ rc = ssh_buffer_add_u32(session->in_hashbuf, kexinit_reserved);
+ if (rc < 0) {
+ goto error;
+ }
+
+ /*
+ * Remember whether 'first_kex_packet_follows' was set and the client
+ * guess was wrong: in this case the next SSH_MSG_KEXDH_INIT message
+ * must be ignored.
+ */
+ if (first_kex_packet_follows) {
+ char **client_methods = session->next_crypto->client_kex.methods;
+ char **server_methods = session->next_crypto->server_kex.methods;
+ session->first_kex_follows_guess_wrong =
+ cmp_first_kex_algo(client_methods[SSH_KEX],
+ server_methods[SSH_KEX]) ||
+ cmp_first_kex_algo(client_methods[SSH_HOSTKEYS],
+ server_methods[SSH_HOSTKEYS]);
+ }
+ if (server_kex) {
/*
* If client sent a ext-info-c message in the kex list, it supports
* RFC 8308 extension negotiation.
@@ -507,19 +544,6 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit)
session->extensions & SSH_EXT_SIG_RSA_SHA256 ? "SHA256" : "",
session->extensions & SSH_EXT_SIG_RSA_SHA512 ? " SHA512" : "");
}
-
- /*
- * Remember whether 'first_kex_packet_follows' was set and the client
- * guess was wrong: in this case the next SSH_MSG_KEXDH_INIT message
- * must be ignored.
- */
- if (first_kex_packet_follows) {
- session->first_kex_follows_guess_wrong =
- cmp_first_kex_algo(session->next_crypto->client_kex.methods[SSH_KEX],
- session->next_crypto->server_kex.methods[SSH_KEX]) ||
- cmp_first_kex_algo(session->next_crypto->client_kex.methods[SSH_HOSTKEYS],
- session->next_crypto->server_kex.methods[SSH_HOSTKEYS]);
- }
}
/* Note, that his overwrites authenticated state in case of rekeying */
@@ -672,14 +696,18 @@ int ssh_set_client_kex(ssh_session session)
int i;
size_t kex_len, len;
+ /* Skip if already set, for example for the rekey or when we do the guessing
+ * it could have been already used to make some protocol decisions. */
+ if (client->methods[0] != NULL) {
+ return SSH_OK;
+ }
+
ok = ssh_get_random(client->cookie, 16, 0);
if (!ok) {
ssh_set_error(session, SSH_FATAL, "PRNG error");
return SSH_ERROR;
}
- memset(client->methods, 0, SSH_KEX_METHODS * sizeof(char **));
-
/* Set the list of allowed algorithms in order of preference, if it hadn't
* been set yet. */
for (i = 0; i < SSH_KEX_METHODS; i++) {
@@ -924,10 +952,21 @@ int ssh_send_kex(ssh_session session)
goto error;
}
+ /* Prepare also the first_kex_packet_follows and reserved to 0 */
+ rc = ssh_buffer_add_u8(session->out_hashbuf, 0);
+ if (rc < 0) {
+ goto error;
+ }
+ rc = ssh_buffer_add_u32(session->out_hashbuf, 0);
+ if (rc < 0) {
+ goto error;
+ }
+
if (ssh_packet_send(session) == SSH_ERROR) {
return -1;
}
+ session->flags |= SSH_SESSION_FLAG_KEXINIT_SENT;
SSH_LOG(SSH_LOG_PACKET, "SSH_MSG_KEXINIT sent");
return 0;
error:
@@ -1055,33 +1094,6 @@ int ssh_make_sessionid(ssh_session session)
client_hash = session->in_hashbuf;
}
- /*
- * Handle the two final fields for the KEXINIT message (RFC 4253 7.1):
- *
- * boolean first_kex_packet_follows
- * uint32 0 (reserved for future extension)
- */
- rc = ssh_buffer_add_u8(server_hash, 0);
- if (rc < 0) {
- goto error;
- }
- rc = ssh_buffer_add_u32(server_hash, 0);
- if (rc < 0) {
- goto error;
- }
-
- /* These fields are handled for the server case in ssh_packet_kexinit. */
- if (session->client) {
- rc = ssh_buffer_add_u8(client_hash, 0);
- if (rc < 0) {
- goto error;
- }
- rc = ssh_buffer_add_u32(client_hash, 0);
- if (rc < 0) {
- goto error;
- }
- }
-
rc = ssh_dh_get_next_server_publickey_blob(session, &server_pubkey_blob);
if (rc != SSH_OK) {
goto error;
diff --git a/src/server.c b/src/server.c
index 2728d9b..fac2e72 100644
--- a/src/server.c
+++ b/src/server.c
@@ -92,7 +92,11 @@ int server_set_kex(ssh_session session)
size_t len;
int ok;
- ZERO_STRUCTP(server);
+ /* Skip if already set, for example for the rekey or when we do the guessing
+ * it could have been already used to make some protocol decisions. */
+ if (server->methods[0] != NULL) {
+ return SSH_OK;
+ }
ok = ssh_get_random(server->cookie, 16, 0);
if (!ok) {
@@ -375,7 +379,7 @@ static void ssh_server_connection_callback(ssh_session session){
break;
case SSH_SESSION_STATE_KEXINIT_RECEIVED:
set_status(session,0.6f);
- if(session->next_crypto->server_kex.methods[0]==NULL){
+ if ((session->flags & SSH_SESSION_FLAG_KEXINIT_SENT) == 0) {
if(server_set_kex(session) == SSH_ERROR)
goto error;
/* We are in a rekeying, so we need to send the server kex */
--
2.33.0

View File

@ -0,0 +1,296 @@
From 70565ac43867053871f47378c53e5d90ba9007d8 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@redhat.com>
Date: Thu, 16 Mar 2023 11:55:12 +0100
Subject: [PATCH] CVE-2023-1667:kex: Add support for sending
first_kex_packet_follows flag
This is not completely straightforward as it requires us to do some
state
shuffling.
We introduce internal flag that can turn this on in client side, so far
for
testing only as we do not want to universally enable this. We also
repurpose the
server flag indicating the guess was wrong also for the client to make
desired
decisions.
If we found out our guess was wrong, we need to hope the server was able
to
figure out this much, we need to revert the DH FSM state, drop the
callbacks
from the "wrong" key exchange method and initiate the right one.
The server side is already tested by the pkd_hello_i1, which is
executing tests
against dropbrear clients, which is using this flag by default out of
the box.
Tested manually also with the pkd_hello --rekey to make sure the server
is able
to handle the rekeying with all key exchange methods.
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Norbert Pocs <npocs@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
Conflict:NA
Reference:https://gitlab.com/libssh/libssh-mirror/commit/70565ac43867053871f47378c53e5d90ba9007d8
---
include/libssh/dh.h | 1 +
include/libssh/session.h | 13 +++++--
src/client.c | 10 ++++-
src/kex.c | 82 +++++++++++++++++++++++++++++++++++-----
4 files changed, 92 insertions(+), 14 deletions(-)
diff --git a/include/libssh/dh.h b/include/libssh/dh.h
index 4a78738..08fe5d4 100644
--- a/include/libssh/dh.h
+++ b/include/libssh/dh.h
@@ -73,6 +73,7 @@ int ssh_dh_get_current_server_publickey_blob(ssh_session session,
ssh_key ssh_dh_get_next_server_publickey(ssh_session session);
int ssh_dh_get_next_server_publickey_blob(ssh_session session,
ssh_string *pubkey_blob);
+int dh_handshake(ssh_session session);
int ssh_client_dh_init(ssh_session session);
void ssh_client_dh_remove_callbacks(ssh_session session);
diff --git a/include/libssh/session.h b/include/libssh/session.h
index f1c8792..8bfca1f 100644
--- a/include/libssh/session.h
+++ b/include/libssh/session.h
@@ -165,14 +165,21 @@ struct ssh_session_struct {
uint32_t current_method;
} auth;
+ /* Sending this flag before key exchange to save one round trip during the
+ * key exchange. This might make sense on high-latency connections.
+ * So far internal only for testing. Usable only on the client side --
+ * there is no key exchange method that would start with server message */
+ bool send_first_kex_follows;
/*
* RFC 4253, 7.1: if the first_kex_packet_follows flag was set in
* the received SSH_MSG_KEXINIT, but the guess was wrong, this
* field will be set such that the following guessed packet will
- * be ignored. Once that packet has been received and ignored,
- * this field is cleared.
+ * be ignored on the receiving side. Once that packet has been received and
+ * ignored, this field is cleared.
+ * On the sending side, this is set after we got peer KEXINIT message and we
+ * need to resend the initial message of the negotiated KEX algorithm.
*/
- int first_kex_follows_guess_wrong;
+ bool first_kex_follows_guess_wrong;
ssh_buffer in_hashbuf;
ssh_buffer out_hashbuf;
diff --git a/src/client.c b/src/client.c
index 855fee8..f4b8f32 100644
--- a/src/client.c
+++ b/src/client.c
@@ -246,10 +246,13 @@ end:
* @warning this function returning is no proof that DH handshake is
* completed
*/
-static int dh_handshake(ssh_session session)
+int dh_handshake(ssh_session session)
{
int rc = SSH_AGAIN;
+ SSH_LOG(SSH_LOG_TRACE, "dh_handshake_state = %d, kex_type = %d",
+ session->dh_handshake_state, session->next_crypto->kex_type);
+
switch (session->dh_handshake_state) {
case DH_STATE_INIT:
switch(session->next_crypto->kex_type){
@@ -392,6 +395,8 @@ static void ssh_client_connection_callback(ssh_session session)
{
int rc;
+ SSH_LOG(SSH_LOG_DEBUG, "session_state=%d", session->session_state);
+
switch(session->session_state) {
case SSH_SESSION_STATE_NONE:
case SSH_SESSION_STATE_CONNECTING:
@@ -454,6 +459,9 @@ static void ssh_client_connection_callback(ssh_session session)
goto error;
set_status(session,0.8f);
session->session_state=SSH_SESSION_STATE_DH;
+
+ /* If the init packet was already sent in previous step, this will be no
+ * operation */
if (dh_handshake(session) == SSH_ERROR) {
goto error;
}
diff --git a/src/kex.c b/src/kex.c
index 5ace6d3..b4c9d8f 100644
--- a/src/kex.c
+++ b/src/kex.c
@@ -28,6 +28,7 @@
#include <stdio.h>
#include <stdbool.h>
+#include "libssh/libssh.h"
#include "libssh/priv.h"
#include "libssh/buffer.h"
#include "libssh/dh.h"
@@ -355,14 +356,19 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit)
(void)type;
(void)user;
+ SSH_LOG(SSH_LOG_TRACE, "KEXINIT received");
+
if (session->session_state == SSH_SESSION_STATE_AUTHENTICATED) {
if (session->dh_handshake_state == DH_STATE_FINISHED) {
SSH_LOG(SSH_LOG_DEBUG, "Peer initiated key re-exchange");
/* Reset the sent flag if the re-kex was initiated by the peer */
session->flags &= ~SSH_SESSION_FLAG_KEXINIT_SENT;
- } else if (session->dh_handshake_state == DH_STATE_INIT_SENT) {
- SSH_LOG(SSH_LOG_DEBUG, "Receeved peer kexinit answer");
- } else {
+ } else if (session->flags & SSH_SESSION_FLAG_KEXINIT_SENT &&
+ session->dh_handshake_state == DH_STATE_INIT_SENT) {
+ /* This happens only when we are sending our-guessed first kex
+ * packet right after our KEXINIT packet. */
+ SSH_LOG(SSH_LOG_DEBUG, "Received peer kexinit answer.");
+ } else if (session->session_state != SSH_SESSION_STATE_INITIAL_KEX) {
ssh_set_error(session, SSH_FATAL,
"SSH_KEXINIT received in wrong state");
goto error;
@@ -470,9 +476,10 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit)
/*
* Remember whether 'first_kex_packet_follows' was set and the client
* guess was wrong: in this case the next SSH_MSG_KEXDH_INIT message
- * must be ignored.
+ * must be ignored on the server side.
+ * Client needs to start the Key exchange over with the correct method
*/
- if (first_kex_packet_follows) {
+ if (first_kex_packet_follows || session->send_first_kex_follows) {
char **client_methods = session->next_crypto->client_kex.methods;
char **server_methods = session->next_crypto->server_kex.methods;
session->first_kex_follows_guess_wrong =
@@ -480,6 +487,8 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit)
server_methods[SSH_KEX]) ||
cmp_first_kex_algo(client_methods[SSH_HOSTKEYS],
server_methods[SSH_HOSTKEYS]);
+ SSH_LOG(SSH_LOG_DEBUG, "The initial guess was %s.",
+ session->first_kex_follows_guess_wrong ? "wrong" : "right");
}
if (server_kex) {
@@ -559,7 +568,12 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit)
/* Note, that his overwrites authenticated state in case of rekeying */
session->session_state = SSH_SESSION_STATE_KEXINIT_RECEIVED;
- session->dh_handshake_state = DH_STATE_INIT;
+ /* if we already sent our initial key exchange packet, do not reset the
+ * DH state. We will know if we were right with our guess only in
+ * dh_handshake_state() */
+ if (session->send_first_kex_follows == false) {
+ session->dh_handshake_state = DH_STATE_INIT;
+ }
session->ssh_connection_callback(session);
return SSH_PACKET_USED;
@@ -869,6 +883,7 @@ int ssh_kex_select_methods (ssh_session session)
struct ssh_kex_struct *client = &session->next_crypto->client_kex;
char *ext_start = NULL;
const char *aead_hmac = NULL;
+ enum ssh_key_exchange_e kex_type;
int i;
/* Here we should drop the ext-info-c from the list so we avoid matching.
@@ -897,8 +912,18 @@ int ssh_kex_select_methods (ssh_session session)
session->next_crypto->kex_methods[i] = strdup("");
}
}
- kex = session->next_crypto->kex_methods[SSH_KEX];
- session->next_crypto->kex_type = kex_select_kex_type(kex);
+
+ /* We can not set this value directly as the old value is needed to revert
+ * callbacks if we are client */
+ kex_type = kex_select_kex_type(session->next_crypto->kex_methods[SSH_KEX]);
+ if (session->client && session->first_kex_follows_guess_wrong) {
+ SSH_LOG(SSH_LOG_DEBUG, "Our guess was wrong. Restarting the KEX");
+ /* We need to remove the wrong callbacks and start kex again */
+ revert_kex_callbacks(session);
+ session->dh_handshake_state = DH_STATE_INIT;
+ session->first_kex_follows_guess_wrong = false;
+ }
+ session->next_crypto->kex_type = kex_type;
SSH_LOG(SSH_LOG_INFO, "Negotiated %s,%s,%s,%s,%s,%s,%s,%s,%s,%s",
session->next_crypto->kex_methods[SSH_KEX],
@@ -925,6 +950,19 @@ int ssh_send_kex(ssh_session session)
ssh_string str = NULL;
int i;
int rc;
+ int first_kex_packet_follows = 0;
+
+ /* Only client can initiate the handshake methods we implement. If we
+ * already received the peer mechanisms, there is no point in guessing */
+ if (session->client &&
+ session->session_state != SSH_SESSION_STATE_KEXINIT_RECEIVED &&
+ session->send_first_kex_follows) {
+ first_kex_packet_follows = 1;
+ }
+
+ SSH_LOG(SSH_LOG_TRACE,
+ "Sending KEXINIT packet, first_kex_packet_follows = %d",
+ first_kex_packet_follows);
rc = ssh_buffer_pack(session->out_buffer,
"bP",
@@ -957,14 +995,14 @@ int ssh_send_kex(ssh_session session)
rc = ssh_buffer_pack(session->out_buffer,
"bd",
- 0,
+ first_kex_packet_follows,
0);
if (rc != SSH_OK) {
goto error;
}
/* Prepare also the first_kex_packet_follows and reserved to 0 */
- rc = ssh_buffer_add_u8(session->out_hashbuf, 0);
+ rc = ssh_buffer_add_u8(session->out_hashbuf, first_kex_packet_follows);
if (rc < 0) {
goto error;
}
@@ -979,6 +1017,30 @@ int ssh_send_kex(ssh_session session)
session->flags |= SSH_SESSION_FLAG_KEXINIT_SENT;
SSH_LOG(SSH_LOG_PACKET, "SSH_MSG_KEXINIT sent");
+
+ /* If we indicated that we are sending the guessed key exchange packet,
+ * do it now. The packet is simple, but we need to do some preparations */
+ if (first_kex_packet_follows) {
+ char *list = kex->methods[SSH_KEX];
+ char *colon = strchr(list, ',');
+ size_t kex_name_len = colon ? (size_t)(colon - list) : strlen(list);
+ char *kex_name = calloc(kex_name_len + 1, 1);
+ if (kex_name == NULL) {
+ ssh_set_error_oom(session);
+ goto error;
+ }
+ snprintf(kex_name, kex_name_len + 1, "%.*s", (int)kex_name_len, list);
+ SSH_LOG(SSH_LOG_TRACE, "Sending the first kex packet for %s", kex_name);
+
+ session->next_crypto->kex_type = kex_select_kex_type(kex_name);
+ free(kex_name);
+
+ /* run the first step of the DH handshake */
+ session->dh_handshake_state = DH_STATE_INIT;
+ if (dh_handshake(session) == SSH_ERROR) {
+ goto error;
+ }
+ }
return 0;
error:
ssh_buffer_reinit(session->out_buffer);
--
2.23.0

View File

@ -0,0 +1,154 @@
From d08f1b2377fead6489aa1d6a102bf65895ecf858 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@redhat.com>
Date: Fri, 17 Mar 2023 14:09:14 +0100
Subject: [PATCH] CVE-2023-1667:tests: Client coverage for key exchange with
kex guessing
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Norbert Pocs <npocs@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
Conflict:NA
Reference:https://gitlab.com/libssh/libssh-mirror/commit/d08f1b2377fead6489aa1d6a102bf65895ecf858
---
tests/client/torture_rekey.c | 108 +++++++++++++++++++++++++++++++++--
1 file changed, 104 insertions(+), 4 deletions(-)
diff --git a/tests/client/torture_rekey.c b/tests/client/torture_rekey.c
index 46a015d..eab4a8b 100644
--- a/tests/client/torture_rekey.c
+++ b/tests/client/torture_rekey.c
@@ -650,6 +650,91 @@ static void torture_rekey_server_recv(void **state)
}
#endif /* WITH_SFTP */
+static void setup_server_for_good_guess(void *state)
+{
+ const char *default_sshd_config = "KexAlgorithms curve25519-sha256";
+ const char *fips_sshd_config = "KexAlgorithms ecdh-sha2-nistp256";
+ const char *sshd_config = default_sshd_config;
+
+ if (ssh_fips_mode()) {
+ sshd_config = fips_sshd_config;
+ }
+ /* This sets an only supported kex algorithm that we do not have as a first
+ * option */
+ torture_update_sshd_config(state, sshd_config);
+}
+
+static void torture_rekey_guess_send(void **state)
+{
+ struct torture_state *s = *state;
+
+ setup_server_for_good_guess(state);
+
+ /* Make the client send the first_kex_packet_follows flag during key
+ * exchange as well as during the rekey */
+ s->ssh.session->send_first_kex_follows = true;
+
+ torture_rekey_send(state);
+}
+
+static void torture_rekey_guess_wrong_send(void **state)
+{
+ struct torture_state *s = *state;
+ const char *sshd_config = "KexAlgorithms diffie-hellman-group14-sha256";
+
+ /* This sets an only supported kex algorithm that we do not have as a first
+ * option */
+ torture_update_sshd_config(state, sshd_config);
+
+ /* Make the client send the first_kex_packet_follows flag during key
+ * exchange as well as during the rekey */
+ s->ssh.session->send_first_kex_follows = true;
+
+ torture_rekey_send(state);
+}
+
+#ifdef WITH_SFTP
+static void torture_rekey_guess_recv(void **state)
+{
+ struct torture_state *s = *state;
+ int rc;
+
+ setup_server_for_good_guess(state);
+
+ /* Make the client send the first_kex_packet_follows flag during key
+ * exchange as well as during the rekey */
+ s->ssh.session->send_first_kex_follows = true;
+
+ rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_REKEY_DATA, &bytes);
+ assert_ssh_return_code(s->ssh.session, rc);
+
+ session_setup_sftp(state);
+
+ torture_rekey_recv(state);
+}
+
+static void torture_rekey_guess_wrong_recv(void **state)
+{
+ struct torture_state *s = *state;
+ const char *sshd_config = "KexAlgorithms diffie-hellman-group14-sha256";
+ int rc;
+
+ /* This sets an only supported kex algorithm that we do not have as a first
+ * option */
+ torture_update_sshd_config(state, sshd_config);
+
+ /* Make the client send the first_kex_packet_follows flag during key
+ * exchange as well as during the rekey */
+ s->ssh.session->send_first_kex_follows = true;
+
+ rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_REKEY_DATA, &bytes);
+ assert_ssh_return_code(s->ssh.session, rc);
+
+ session_setup_sftp(state);
+
+ torture_rekey_recv(state);
+}
+#endif /* WITH_SFTP */
int torture_run_tests(void) {
int rc;
@@ -671,19 +756,34 @@ int torture_run_tests(void) {
cmocka_unit_test_setup_teardown(torture_rekey_different_kex,
session_setup,
session_teardown),
- /* Note, that this modifies the sshd_config */
+ /* TODO verify the two rekey are possible and the states are not broken after rekey */
+
+ cmocka_unit_test_setup_teardown(torture_rekey_server_different_kex,
+ session_setup,
+ session_teardown),
+ /* Note, that these tests modify the sshd_config so follow-up tests
+ * might get unexpected behavior if they do not update the server with
+ * torture_update_sshd_config() too */
cmocka_unit_test_setup_teardown(torture_rekey_server_send,
session_setup,
session_teardown),
+ cmocka_unit_test_setup_teardown(torture_rekey_guess_send,
+ session_setup,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_rekey_guess_wrong_send,
+ session_setup,
+ session_teardown),
#ifdef WITH_SFTP
cmocka_unit_test_setup_teardown(torture_rekey_server_recv,
session_setup_sftp_server,
session_teardown),
-#endif /* WITH_SFTP */
- cmocka_unit_test_setup_teardown(torture_rekey_server_different_kex,
+ cmocka_unit_test_setup_teardown(torture_rekey_guess_recv,
session_setup,
session_teardown),
- /* TODO verify the two rekey are possible and the states are not broken after rekey */
+ cmocka_unit_test_setup_teardown(torture_rekey_guess_wrong_recv,
+ session_setup,
+ session_teardown),
+#endif /* WITH_SFTP */
};
ssh_init();
--
2.33.0

View File

@ -0,0 +1,40 @@
From dc1254d53e4fc6cbeb4797fc6ca1c9ed2c21f15c Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@redhat.com>
Date: Mon, 17 Apr 2023 16:53:35 +0200
Subject: [PATCH] CVE-2023-1667:tests: Send a bit more to make sure rekey is
completed
This was for some reason failing on CentOS 7 in 0.10 branch so bringing this to
the master too.
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Norbert Pocs <npocs@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
Conflict:NA
Reference:https://gitlab.com/libssh/libssh-mirror/commit/dc1254d53e4fc6cbeb4797fc6ca1c9ed2c21f15c
---
tests/client/torture_rekey.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/tests/client/torture_rekey.c b/tests/client/torture_rekey.c
index d9667267..ccd5ae2c 100644
--- a/tests/client/torture_rekey.c
+++ b/tests/client/torture_rekey.c
@@ -192,10 +192,11 @@ static void torture_rekey_send(void **state)
rc = ssh_userauth_publickey_auto(s->ssh.session, NULL, NULL);
assert_int_equal(rc, SSH_AUTH_SUCCESS);
- /* send ignore packets of up to 1KB to trigger rekey */
+ /* send ignore packets of up to 1KB to trigger rekey. Send little bit more
+ * to make sure it completes with all different ciphers */
memset(data, 0, sizeof(data));
memset(data, 'A', 128);
- for (i = 0; i < 16; i++) {
+ for (i = 0; i < KEX_RETRY; i++) {
ssh_send_ignore(s->ssh.session, data);
ssh_handle_packets(s->ssh.session, 50);
}
--
2.33.0

View File

@ -1,6 +1,6 @@
Name: libssh
Version: 0.10.4
Release: 3
Release: 4
Summary: A library implementing the SSH protocol
License: LGPLv2+
URL: http://www.libssh.org
@ -12,6 +12,17 @@ Source2: https://cryptomilk.org/gpgkey-8DFF53E18F2ABC8D8F3C92237EE0FC4DCC
Patch0: backport-config-Escape-brackets-in-ProxyCommand-build-from.patch
Patch1: backport-packet-do-not-enqueue-outgoing-packets-after-sending.patch
Patch2: backport-examples-Fix-build-issue-with-new-clang-15.patch
Patch3: backport-0001-CVE-2023-1667-packet_cb-Log-more-verbose-error-if-si.patch
Patch4: backport-0002-CVE-2023-1667-packet-Do-not-allow-servers-to-initiat.patch
Patch5: backport-0003-CVE-2023-1667-kex-Remove-needless-function-argument.patch
Patch6: backport-0004-CVE-2023-1667-kex-Factor-out-the-kex-mapping-to-inte.patch
Patch7: backport-0005-CVE-2023-1667-dh-Expose-the-callback-cleanup-functio.patch
Patch8: backport-0006-CVE-2023-1667-kex-Correctly-handle-last-fields-of-KE.patch
Patch9: backport-0007-CVE-2023-1667-kex-Add-support-for-sending-first_kex_.patch
Patch10: backport-0008-CVE-2023-1667-tests-Client-coverage-for-key-exchange.patch
Patch11: backport-0009-CVE-2023-1667-tests-Send-a-bit-more-to-make-sure-rek.patch
Patch12: backport-0001-CVE-2023-2283-pki_crypto-Fix-possible-authentication.patch
Patch13: backport-0002-CVE-2023-2283-pki_crypto-Remove-unnecessary-NUL.patch
BuildRequires: cmake gcc-c++ gnupg2 openssl-devel pkgconfig zlib-devel
BuildRequires: krb5-devel libcmocka-devel openssh-clients openssh-server
@ -97,6 +108,12 @@ popd
%doc CHANGELOG README
%changelog
* Wed May 24 2023 renmingshuai <renmingshuai@huawei.com> - 0.10.4-4
- Type:CVE
- Id:CVE-2023-1667,CVE-2023-2283
- SUG:NA
- DESC:fix CVE-2023-1667 and CVE-2023-2283
* Mon Apr 3 2023 Chenxi Mao <chenxi.mao@suse.com> - 0.10.4-3
- Type:bugfix
- Id:NA