diff --git a/SPECS/libssh/CVE-2023-48795.patch b/SPECS/libssh/CVE-2023-48795.patch new file mode 100644 index 00000000000..d10ef72ed4e --- /dev/null +++ b/SPECS/libssh/CVE-2023-48795.patch @@ -0,0 +1,719 @@ +From 4cef5e965a46e9271aed62631b152e4bd23c1e3c Mon Sep 17 00:00:00 2001 +From: Aris Adamantiadis +Date: Tue, 12 Dec 2023 23:09:57 +0100 +Subject: CVE-2023-48795: client side mitigation + +Signed-off-by: Aris Adamantiadis +Signed-off-by: Jakub Jelen +Reviewed-by: Andreas Schneider +--- + include/libssh/packet.h | 1 + + include/libssh/session.h | 6 +++++ + src/curve25519.c | 19 +++---------- + src/dh-gex.c | 7 +---- + src/dh.c | 17 +++--------- + src/ecdh.c | 8 +----- + src/ecdh_crypto.c | 12 +++------ + src/ecdh_gcrypt.c | 10 +++---- + src/ecdh_mbedcrypto.c | 11 +++----- + src/kex.c | 34 +++++++++++++++++++---- + src/packet.c | 58 ++++++++++++++++++++++++++++++++++++++++ + src/packet_cb.c | 12 +++++++++ + 12 files changed, 126 insertions(+), 69 deletions(-) + +diff --git a/include/libssh/packet.h b/include/libssh/packet.h +index 7f10a70..f0c8cb2 100644 +--- a/include/libssh/packet.h ++++ b/include/libssh/packet.h +@@ -67,6 +67,7 @@ SSH_PACKET_CALLBACK(ssh_packet_ext_info); + SSH_PACKET_CALLBACK(ssh_packet_kexdh_init); + #endif + ++int ssh_packet_send_newkeys(ssh_session session); + int ssh_packet_send_unimplemented(ssh_session session, uint32_t seqnum); + int ssh_packet_parse_type(ssh_session session); + //int packet_flush(ssh_session session, int enforce_blocking); +diff --git a/include/libssh/session.h b/include/libssh/session.h +index eb14e97..9793619 100644 +--- a/include/libssh/session.h ++++ b/include/libssh/session.h +@@ -81,6 +81,12 @@ enum ssh_pending_call_e { + * sending it twice during key exchange to simplify the state machine. */ + #define SSH_SESSION_FLAG_KEXINIT_SENT 4 + ++/* The current SSH2 session implements the "strict KEX" feature and should behave ++ * differently on SSH2_MSG_NEWKEYS. */ ++#define SSH_SESSION_FLAG_KEX_STRICT 0x0010 ++/* Unexpected packets have been sent while the session was still unencrypted */ ++#define SSH_SESSION_FLAG_KEX_TAINTED 0x0020 ++ + /* codes to use with ssh_handle_packets*() */ + /* Infinite timeout */ + #define SSH_TIMEOUT_INFINITE -1 +diff --git a/src/curve25519.c b/src/curve25519.c +index 66291b5..4aeb475 100644 +--- a/src/curve25519.c ++++ b/src/curve25519.c +@@ -335,16 +335,10 @@ static SSH_PACKET_CALLBACK(ssh_packet_client_curve25519_reply){ + } + + /* Send the MSG_NEWKEYS */ +- if (ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) { +- goto error; +- } +- +- rc=ssh_packet_send(session); ++ rc = ssh_packet_send_newkeys(session); + if (rc == SSH_ERROR) { + goto error; + } +- +- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); + session->dh_handshake_state = DH_STATE_NEWKEYS_SENT; + + return SSH_PACKET_USED; +@@ -502,18 +496,13 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init){ + return SSH_ERROR; + } + +- /* Send the MSG_NEWKEYS */ +- rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS); +- if (rc < 0) { +- goto error; +- } +- + session->dh_handshake_state = DH_STATE_NEWKEYS_SENT; +- rc = ssh_packet_send(session); ++ ++ /* Send the MSG_NEWKEYS */ ++ rc = ssh_packet_send_newkeys(session); + if (rc == SSH_ERROR) { + goto error; + } +- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); + + return SSH_PACKET_USED; + error: +diff --git a/src/dh-gex.c b/src/dh-gex.c +index 9161708..642a88a 100644 +--- a/src/dh-gex.c ++++ b/src/dh-gex.c +@@ -297,15 +297,10 @@ static SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_reply) + } + + /* Send the MSG_NEWKEYS */ +- if (ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) { +- goto error; +- } +- +- rc = ssh_packet_send(session); ++ rc = ssh_packet_send_newkeys(session); + if (rc == SSH_ERROR) { + goto error; + } +- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); + session->dh_handshake_state = DH_STATE_NEWKEYS_SENT; + + return SSH_PACKET_USED; +diff --git a/src/dh.c b/src/dh.c +index 011d97b..e19e43d 100644 +--- a/src/dh.c ++++ b/src/dh.c +@@ -398,16 +398,10 @@ SSH_PACKET_CALLBACK(ssh_packet_client_dh_reply){ + } + + /* Send the MSG_NEWKEYS */ +- if (ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) { +- goto error; +- } +- +- rc=ssh_packet_send(session); ++ rc = ssh_packet_send_newkeys(session); + if (rc == SSH_ERROR) { + goto error; + } +- +- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); + session->dh_handshake_state = DH_STATE_NEWKEYS_SENT; + return SSH_PACKET_USED; + error: +@@ -551,15 +545,12 @@ int ssh_server_dh_process_init(ssh_session session, ssh_buffer packet) + } + SSH_LOG(SSH_LOG_DEBUG, "Sent KEX_DH_[GEX]_REPLY"); + +- if (ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) { +- ssh_buffer_reinit(session->out_buffer); +- goto error; +- } + session->dh_handshake_state=DH_STATE_NEWKEYS_SENT; +- if (ssh_packet_send(session) == SSH_ERROR) { ++ /* Send the MSG_NEWKEYS */ ++ rc = ssh_packet_send_newkeys(session); ++ if (rc == SSH_ERROR) { + goto error; + } +- SSH_LOG(SSH_LOG_PACKET, "SSH_MSG_NEWKEYS sent"); + + return SSH_OK; + error: +diff --git a/src/ecdh.c b/src/ecdh.c +index e5b11ba..af80bee 100644 +--- a/src/ecdh.c ++++ b/src/ecdh.c +@@ -93,16 +93,10 @@ SSH_PACKET_CALLBACK(ssh_packet_client_ecdh_reply){ + } + + /* Send the MSG_NEWKEYS */ +- if (ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) { +- goto error; +- } +- +- rc=ssh_packet_send(session); ++ rc = ssh_packet_send_newkeys(session); + if (rc == SSH_ERROR) { + goto error; + } +- +- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); + session->dh_handshake_state = DH_STATE_NEWKEYS_SENT; + + return SSH_PACKET_USED; +diff --git a/src/ecdh_crypto.c b/src/ecdh_crypto.c +index 51084b7..069b137 100644 +--- a/src/ecdh_crypto.c ++++ b/src/ecdh_crypto.c +@@ -619,18 +619,12 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){ + goto error; + } + +- /* Send the MSG_NEWKEYS */ +- rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS); +- if (rc < 0) { +- goto error; +- } +- + session->dh_handshake_state = DH_STATE_NEWKEYS_SENT; +- rc = ssh_packet_send(session); +- if (rc == SSH_ERROR){ ++ /* Send the MSG_NEWKEYS */ ++ rc = ssh_packet_send_newkeys(session); ++ if (rc == SSH_ERROR) { + goto error; + } +- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); + + return SSH_PACKET_USED; + error: +diff --git a/src/ecdh_gcrypt.c b/src/ecdh_gcrypt.c +index 235f290..3d9d426 100644 +--- a/src/ecdh_gcrypt.c ++++ b/src/ecdh_gcrypt.c +@@ -372,17 +372,13 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){ + goto out; + } + +- ++ session->dh_handshake_state = DH_STATE_NEWKEYS_SENT; + /* Send the MSG_NEWKEYS */ +- rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS); +- if (rc != SSH_OK) { ++ rc = ssh_packet_send_newkeys(session); ++ if (rc == SSH_ERROR) { + goto out; + } + +- session->dh_handshake_state = DH_STATE_NEWKEYS_SENT; +- rc = ssh_packet_send(session); +- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); +- + out: + gcry_sexp_release(param); + gcry_sexp_release(key); +diff --git a/src/ecdh_mbedcrypto.c b/src/ecdh_mbedcrypto.c +index cfe017a..dda7392 100644 +--- a/src/ecdh_mbedcrypto.c ++++ b/src/ecdh_mbedcrypto.c +@@ -318,16 +318,13 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){ + goto out; + } + +- rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS); +- if (rc < 0) { +- rc = SSH_ERROR; ++ session->dh_handshake_state = DH_STATE_NEWKEYS_SENT; ++ /* Send the MSG_NEWKEYS */ ++ rc = ssh_packet_send_newkeys(session); ++ if (rc == SSH_ERROR) { + goto out; + } + +- session->dh_handshake_state = DH_STATE_NEWKEYS_SENT; +- rc = ssh_packet_send(session); +- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); +- + out: + mbedtls_ecp_group_free(&grp); + if (rc == SSH_ERROR) { +diff --git a/src/kex.c b/src/kex.c +index b9455d2..3818297 100644 +--- a/src/kex.c ++++ b/src/kex.c +@@ -188,6 +188,9 @@ + + /* RFC 8308 */ + #define KEX_EXTENSION_CLIENT "ext-info-c" ++/* Strict kex mitigation against CVE-2023-48795 */ ++#define KEX_STRICT_CLIENT "kex-strict-c-v00@openssh.com" ++#define KEX_STRICT_SERVER "kex-strict-s-v00@openssh.com" + + /* Allowed algorithms in FIPS mode */ + #define FIPS_ALLOWED_CIPHERS "aes256-gcm@openssh.com,"\ +@@ -516,6 +519,27 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit) + session->first_kex_follows_guess_wrong ? "wrong" : "right"); + } + ++ /* ++ * handle the "strict KEX" feature. If supported by peer, then set up the ++ * flag and verify packet sequence numbers. ++ */ ++ if (server_kex) { ++ ok = ssh_match_group(crypto->client_kex.methods[SSH_KEX], ++ KEX_STRICT_CLIENT); ++ if (ok) { ++ SSH_LOG(SSH_LOG_DEBUG, "Client supports strict kex, enabling."); ++ session->flags |= SSH_SESSION_FLAG_KEX_STRICT; ++ } ++ } else { ++ /* client kex */ ++ ok = ssh_match_group(crypto->server_kex.methods[SSH_KEX], ++ KEX_STRICT_SERVER); ++ if (ok) { ++ SSH_LOG(SSH_LOG_DEBUG, "Server supports strict kex, enabling."); ++ session->flags |= SSH_SESSION_FLAG_KEX_STRICT; ++ } ++ } ++ + if (server_kex) { + /* + * If client sent a ext-info-c message in the kex list, it supports +@@ -792,21 +816,21 @@ int ssh_set_client_kex(ssh_session session) + return SSH_OK; + } + +- /* Here we append ext-info-c to the list of kex algorithms */ ++ /* Here we append ext-info-c and kex-strict-c-v00@openssh.com to the list of kex algorithms */ + kex = client->methods[SSH_KEX]; + len = strlen(kex); +- if (len + strlen(KEX_EXTENSION_CLIENT) + 2 < len) { ++ /* Comma, comma, nul byte */ ++ kex_len = len + 1 + strlen(KEX_EXTENSION_CLIENT) + 1 + strlen(KEX_STRICT_CLIENT ) + 1; ++ if (kex_len >= MAX_PACKET_LEN) { + /* Overflow */ + return SSH_ERROR; + } +- kex_len = len + strlen(KEX_EXTENSION_CLIENT) + 2; /* comma, NULL */ + kex_tmp = realloc(kex, kex_len); + if (kex_tmp == NULL) { +- free(kex); + ssh_set_error_oom(session); + return SSH_ERROR; + } +- snprintf(kex_tmp + len, kex_len - len, ",%s", KEX_EXTENSION_CLIENT); ++ snprintf(kex_tmp + len, kex_len - len, ",%s,%s", KEX_EXTENSION_CLIENT, KEX_STRICT_CLIENT); + client->methods[SSH_KEX] = kex_tmp; + + return SSH_OK; +diff --git a/src/packet.c b/src/packet.c +index eb7eb42..ea73f9a 100644 +--- a/src/packet.c ++++ b/src/packet.c +@@ -1314,6 +1314,19 @@ ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user) + } + #endif /* WITH_ZLIB */ + payloadsize = ssh_buffer_get_len(session->in_buffer); ++ if (session->recv_seq == UINT32_MAX) { ++ /* Overflowing sequence numbers is always fishy */ ++ if (crypto == NULL) { ++ /* don't allow sequence number overflow when unencrypted */ ++ ssh_set_error(session, ++ SSH_FATAL, ++ "Incoming sequence number overflow"); ++ goto error; ++ } else { ++ SSH_LOG(SSH_LOG_WARNING, ++ "Incoming sequence number overflow"); ++ } ++ } + session->recv_seq++; + if (crypto != NULL) { + struct ssh_cipher_struct *cipher = NULL; +@@ -1338,7 +1351,19 @@ ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user) + "comp=%" PRIu32 ",payload=%" PRIu32 "]", + session->in_packet.type, packet_len, padding, compsize, + payloadsize); ++ if (crypto == NULL) { ++ /* In strict kex, only a few packets are allowed. Taint the session ++ * if we received packets that are normally allowed but to be ++ * refused if we are in strict kex when KEX is over. ++ */ ++ uint8_t type = session->in_packet.type; + ++ if (type != SSH2_MSG_KEXINIT && type != SSH2_MSG_NEWKEYS && ++ (type < SSH2_MSG_KEXDH_INIT || ++ type > SSH2_MSG_KEX_DH_GEX_REQUEST)) { ++ session->flags |= SSH_SESSION_FLAG_KEX_TAINTED; ++ } ++ } + /* Check if the packet is expected */ + filter_result = ssh_packet_incoming_filter(session); + +@@ -1354,6 +1379,9 @@ ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user) + session->in_packet.type); + goto error; + case SSH_PACKET_UNKNOWN: ++ if (crypto == NULL) { ++ session->flags |= SSH_SESSION_FLAG_KEX_TAINTED; ++ } + ssh_packet_send_unimplemented(session, session->recv_seq - 1); + break; + } +@@ -1529,7 +1557,33 @@ void ssh_packet_process(ssh_session session, uint8_t type) + SSH_LOG(SSH_LOG_RARE, "Failed to send unimplemented: %s", + ssh_get_error(session)); + } ++ if (session->current_crypto == NULL) { ++ session->flags |= SSH_SESSION_FLAG_KEX_TAINTED; ++ } ++ } ++} ++ ++/** @internal ++ * @brief sends a SSH_MSG_NEWKEYS when enabling the new negotiated ciphers ++ * @param session the SSH session ++ * @return SSH_ERROR on error, else SSH_OK ++ */ ++int ssh_packet_send_newkeys(ssh_session session) ++{ ++ int rc; ++ ++ /* Send the MSG_NEWKEYS */ ++ rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS); ++ if (rc < 0) { ++ return rc; + } ++ ++ rc = ssh_packet_send(session); ++ if (rc == SSH_ERROR) { ++ return rc; ++ } ++ SSH_LOG(SSH_LOG_DEBUG, "SSH_MSG_NEWKEYS sent"); ++ return rc; + } + + /** @internal +@@ -1842,6 +1896,10 @@ int ssh_packet_send(ssh_session session) + if (rc == SSH_OK && type == SSH2_MSG_NEWKEYS) { + struct ssh_iterator *it; + ++ if (session->flags & SSH_SESSION_FLAG_KEX_STRICT) { ++ /* reset packet sequence number when running in strict kex mode */ ++ session->send_seq = 0; ++ } + for (it = ssh_list_get_iterator(session->out_queue); + it != NULL; + it = ssh_list_get_iterator(session->out_queue)) { +diff --git a/src/packet_cb.c b/src/packet_cb.c +index 0ecf877..2f364c2 100644 +--- a/src/packet_cb.c ++++ b/src/packet_cb.c +@@ -115,6 +115,18 @@ SSH_PACKET_CALLBACK(ssh_packet_newkeys){ + goto error; + } + ++ if (session->flags & SSH_SESSION_FLAG_KEX_STRICT) { ++ /* reset packet sequence number when running in strict kex mode */ ++ session->recv_seq = 0; ++ /* Check that we aren't tainted */ ++ if (session->flags & SSH_SESSION_FLAG_KEX_TAINTED) { ++ ssh_set_error(session, ++ SSH_FATAL, ++ "Received unexpected packets in strict KEX mode."); ++ goto error; ++ } ++ } ++ + if(session->server){ + /* server things are done in server.c */ + session->dh_handshake_state=DH_STATE_FINISHED; +-- +2.25.1 + +From 0870c8db28be9eb457ee3d4f9a168959d9507efd Mon Sep 17 00:00:00 2001 +From: Aris Adamantiadis +Date: Tue, 12 Dec 2023 23:30:26 +0100 +Subject: CVE-2023-48795: Server side mitigations + +Signed-off-by: Aris Adamantiadis +Signed-off-by: Jakub Jelen +Reviewed-by: Andreas Schneider +--- + include/libssh/kex.h | 1 + + src/kex.c | 46 ++++++++++++++++++++++++++++++++++---------- + src/server.c | 8 +++++++- + 3 files changed, 44 insertions(+), 11 deletions(-) + +diff --git a/include/libssh/kex.h b/include/libssh/kex.h +index ede7fa8..ba98fde 100644 +--- a/include/libssh/kex.h ++++ b/include/libssh/kex.h +@@ -40,6 +40,7 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit); + 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_append_extensions(ssh_session session, struct ssh_kex_struct *pkex); + int ssh_kex_select_methods(ssh_session session); + int ssh_verify_existing_algo(enum ssh_kex_types_e algo, const char *name); + char *ssh_keep_known_algos(enum ssh_kex_types_e algo, const char *list); +diff --git a/src/kex.c b/src/kex.c +index 3818297..9ad671d 100644 +--- a/src/kex.c ++++ b/src/kex.c +@@ -763,11 +763,8 @@ int ssh_set_client_kex(ssh_session session) + { + struct ssh_kex_struct *client = &session->next_crypto->client_kex; + const char *wanted; +- char *kex = NULL; +- char *kex_tmp = NULL; + int ok; + 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. */ +@@ -816,11 +813,33 @@ int ssh_set_client_kex(ssh_session session) + return SSH_OK; + } + +- /* Here we append ext-info-c and kex-strict-c-v00@openssh.com to the list of kex algorithms */ +- kex = client->methods[SSH_KEX]; ++ ok = ssh_kex_append_extensions(session, client); ++ if (ok != SSH_OK){ ++ return ok; ++ } ++ ++ return SSH_OK; ++} ++ ++int ssh_kex_append_extensions(ssh_session session, struct ssh_kex_struct *pkex) ++{ ++ char *kex = NULL; ++ char *kex_tmp = NULL; ++ size_t kex_len, len; ++ ++ /* Here we append ext-info-c and kex-strict-c-v00@openssh.com for client ++ * and kex-strict-s-v00@openssh.com for server to the list of kex algorithms ++ */ ++ kex = pkex->methods[SSH_KEX]; + len = strlen(kex); +- /* Comma, comma, nul byte */ +- kex_len = len + 1 + strlen(KEX_EXTENSION_CLIENT) + 1 + strlen(KEX_STRICT_CLIENT ) + 1; ++ if (session->server) { ++ /* Comma, nul byte */ ++ kex_len = len + 1 + strlen(KEX_STRICT_SERVER) + 1; ++ } else { ++ /* Comma, comma, nul byte */ ++ kex_len = len + 1 + strlen(KEX_EXTENSION_CLIENT) + 1 + ++ strlen(KEX_STRICT_CLIENT) + 1; ++ } + if (kex_len >= MAX_PACKET_LEN) { + /* Overflow */ + return SSH_ERROR; +@@ -830,9 +849,16 @@ int ssh_set_client_kex(ssh_session session) + ssh_set_error_oom(session); + return SSH_ERROR; + } +- snprintf(kex_tmp + len, kex_len - len, ",%s,%s", KEX_EXTENSION_CLIENT, KEX_STRICT_CLIENT); +- client->methods[SSH_KEX] = kex_tmp; +- ++ if (session->server){ ++ snprintf(kex_tmp + len, kex_len - len, ",%s", KEX_STRICT_SERVER); ++ } else { ++ snprintf(kex_tmp + len, ++ kex_len - len, ++ ",%s,%s", ++ KEX_EXTENSION_CLIENT, ++ KEX_STRICT_CLIENT); ++ } ++ pkex->methods[SSH_KEX] = kex_tmp; + return SSH_OK; + } + +diff --git a/src/server.c b/src/server.c +index b978fc1..cf54d0c 100644 +--- a/src/server.c ++++ b/src/server.c +@@ -195,7 +195,13 @@ int server_set_kex(ssh_session session) + } + } + +- return 0; ++ /* Do not append the extensions during rekey */ ++ if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED) { ++ return SSH_OK; ++ } ++ ++ rc = ssh_kex_append_extensions(session, server); ++ return rc; + } + + int ssh_server_init_kex(ssh_session session) { +-- +2.25.1 + +From 5846e57538c750c5ce67df887d09fa99861c79c6 Mon Sep 17 00:00:00 2001 +From: Jakub Jelen +Date: Thu, 14 Dec 2023 12:22:01 +0100 +Subject: CVE-2023-48795: Strip extensions from both kex lists for matching + +Signed-off-by: Jakub Jelen +Reviewed-by: Andreas Schneider +--- + src/kex.c | 16 ++++++++++++---- + 1 file changed, 12 insertions(+), 4 deletions(-) + +diff --git a/src/kex.c b/src/kex.c +index 9ad671d..fbc70cf 100644 +--- a/src/kex.c ++++ b/src/kex.c +@@ -961,11 +961,19 @@ int ssh_kex_select_methods (ssh_session session) + enum ssh_key_exchange_e kex_type; + int i; + +- /* Here we should drop the ext-info-c from the list so we avoid matching. ++ /* Here we should drop the extensions from the list so we avoid matching. + * it. We added it to the end, so we can just truncate the string here */ +- ext_start = strstr(client->methods[SSH_KEX], ","KEX_EXTENSION_CLIENT); +- if (ext_start != NULL) { +- ext_start[0] = '\0'; ++ if (session->client) { ++ ext_start = strstr(client->methods[SSH_KEX], "," KEX_EXTENSION_CLIENT); ++ if (ext_start != NULL) { ++ ext_start[0] = '\0'; ++ } ++ } ++ if (session->server) { ++ ext_start = strstr(server->methods[SSH_KEX], "," KEX_STRICT_SERVER); ++ if (ext_start != NULL) { ++ ext_start[0] = '\0'; ++ } + } + + for (i = 0; i < SSH_KEX_METHODS; i++) { +-- +2.25.1 + +From 89df759200d31fc79fbbe213d8eda0d329eebf6d Mon Sep 17 00:00:00 2001 +From: Jakub Jelen +Date: Thu, 14 Dec 2023 12:47:48 +0100 +Subject: CVE-2023-48795: tests: Adjust calculation to strict kex + +Signed-off-by: Jakub Jelen +Reviewed-by: Andreas Schneider +--- + tests/client/torture_rekey.c | 55 ++++++++++++++++++++---------------- + 1 file changed, 31 insertions(+), 24 deletions(-) + +diff --git a/tests/client/torture_rekey.c b/tests/client/torture_rekey.c +index ccd5ae2..57e03e3 100644 +--- a/tests/client/torture_rekey.c ++++ b/tests/client/torture_rekey.c +@@ -148,6 +148,29 @@ static void torture_rekey_default(void **state) + ssh_disconnect(s->ssh.session); + } + ++static void sanity_check_session(void **state) ++{ ++ struct torture_state *s = *state; ++ struct ssh_crypto_struct *c = NULL; ++ ++ c = s->ssh.session->current_crypto; ++ assert_non_null(c); ++ assert_int_equal(c->in_cipher->max_blocks, ++ bytes / c->in_cipher->blocksize); ++ assert_int_equal(c->out_cipher->max_blocks, ++ bytes / c->out_cipher->blocksize); ++ /* when strict kex is used, the newkeys reset the sequence number */ ++ if ((s->ssh.session->flags & SSH_SESSION_FLAG_KEX_STRICT) != 0) { ++ assert_int_equal(c->out_cipher->packets, s->ssh.session->send_seq); ++ assert_int_equal(c->in_cipher->packets, s->ssh.session->recv_seq); ++ } else { ++ /* Otherwise we have less encrypted packets than transferred ++ * (first are not encrypted) */ ++ assert_true(c->out_cipher->packets < s->ssh.session->send_seq); ++ assert_true(c->in_cipher->packets < s->ssh.session->recv_seq); ++ } ++} ++ + /* We lower the rekey limits manually and check that the rekey + * really happens when sending data + */ +@@ -166,16 +189,10 @@ static void torture_rekey_send(void **state) + rc = ssh_connect(s->ssh.session); + assert_ssh_return_code(s->ssh.session, rc); + +- /* The blocks limit is set correctly */ +- c = s->ssh.session->current_crypto; +- assert_int_equal(c->in_cipher->max_blocks, +- bytes / c->in_cipher->blocksize); +- assert_int_equal(c->out_cipher->max_blocks, +- bytes / c->out_cipher->blocksize); +- /* We should have less encrypted packets than transferred (first are not encrypted) */ +- assert_true(c->out_cipher->packets < s->ssh.session->send_seq); +- assert_true(c->in_cipher->packets < s->ssh.session->recv_seq); ++ sanity_check_session(state); + /* Copy the initial secret hash = session_id so we know we changed keys later */ ++ c = s->ssh.session->current_crypto; ++ assert_non_null(c); + secret_hash = malloc(c->digest_len); + assert_non_null(secret_hash); + memcpy(secret_hash, c->secret_hash, c->digest_len); +@@ -273,15 +290,10 @@ static void torture_rekey_recv(void **state) + mode_t mask; + int rc; + +- /* The blocks limit is set correctly */ ++ sanity_check_session(state); ++ /* Copy the initial secret hash = session_id so we know we changed keys later */ + c = s->ssh.session->current_crypto; + assert_non_null(c); +- assert_int_equal(c->in_cipher->max_blocks, bytes / c->in_cipher->blocksize); +- assert_int_equal(c->out_cipher->max_blocks, bytes / c->out_cipher->blocksize); +- /* We should have less encrypted packets than transferred (first are not encrypted) */ +- assert_true(c->out_cipher->packets < s->ssh.session->send_seq); +- assert_true(c->in_cipher->packets < s->ssh.session->recv_seq); +- /* Copy the initial secret hash = session_id so we know we changed keys later */ + secret_hash = malloc(c->digest_len); + assert_non_null(secret_hash); + memcpy(secret_hash, c->secret_hash, c->digest_len); +@@ -468,15 +480,10 @@ static void torture_rekey_different_kex(void **state) + assert_ssh_return_code(s->ssh.session, rc); + + /* The blocks limit is set correctly */ +- c = s->ssh.session->current_crypto; +- assert_int_equal(c->in_cipher->max_blocks, +- bytes / c->in_cipher->blocksize); +- assert_int_equal(c->out_cipher->max_blocks, +- bytes / c->out_cipher->blocksize); +- /* We should have less encrypted packets than transferred (first are not encrypted) */ +- assert_true(c->out_cipher->packets < s->ssh.session->send_seq); +- assert_true(c->in_cipher->packets < s->ssh.session->recv_seq); ++ sanity_check_session(state); + /* Copy the initial secret hash = session_id so we know we changed keys later */ ++ c = s->ssh.session->current_crypto; ++ assert_non_null(c); + secret_hash = malloc(c->digest_len); + assert_non_null(secret_hash); + memcpy(secret_hash, c->secret_hash, c->digest_len); +-- +2.25.1 + diff --git a/SPECS/libssh/libssh.spec b/SPECS/libssh/libssh.spec index 6916226b62b..54b225de036 100644 --- a/SPECS/libssh/libssh.spec +++ b/SPECS/libssh/libssh.spec @@ -2,7 +2,7 @@ Vendor: Microsoft Corporation Distribution: Azure Linux Name: libssh Version: 0.10.5 -Release: 2%{?dist} +Release: 3%{?dist} Summary: A library implementing the SSH protocol License: LGPLv2+ URL: http://www.libssh.org @@ -12,6 +12,7 @@ Source1: https://www.libssh.org/files/0.10/%{name}-%{version}.tar.xz.asc Source2: https://cryptomilk.org/gpgkey-8DFF53E18F2ABC8D8F3C92237EE0FC4DCC014E3D.gpg#/%{name}.keyring Source3: libssh_client.config Source4: libssh_server.config +Patch0: CVE-2023-48795.patch BuildRequires: cmake BuildRequires: gcc-c++ @@ -144,6 +145,9 @@ popd %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/libssh/libssh_server.config %changelog +* Thu Nov 28 2024 Sumedh Sharma - 0.10.5-3 +- Add patch to resolve CVE-2023-48795 + * Fri May 26 2023 Vince Perri - 0.10.5-2 - License verified. - Switched to out-of-source build.