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

