diff --git a/1000-pki_crypto-fix-possible-authentication-bypass.patch b/1000-pki_crypto-fix-possible-authentication-bypass.patch new file mode 100644 index 0000000000000000000000000000000000000000..ee2ecdd49cc46f7ecf970e2298a58503e16c5020 --- /dev/null +++ b/1000-pki_crypto-fix-possible-authentication-bypass.patch @@ -0,0 +1,106 @@ +From aa00d16fb4d6ebfeee8e2a094e66272965f42bcb Mon Sep 17 00:00:00 2001 +From: Liwei Ge +Date: Wed, 28 Jun 2023 17:56:38 +0800 +Subject: [PATCH 1000/1007] pki_crypto: fix possible authentication bypass + +backport patch from +https://git.libssh.org/projects/libssh.git/commit/?id= +0bda152ad24d96d6bef07d1f96152b473298ddb1 +https://git.libssh.org/projects/libssh.git/commit/?id= +4b5ccd4995e096151ec7cdd181e20ee62366d64f + +Signed-off-by: Liwei Ge +--- + src/pki_crypto.c | 40 ++++++++++++++++++++-------------------- + 1 file changed, 20 insertions(+), 20 deletions(-) + +diff --git a/src/pki_crypto.c b/src/pki_crypto.c +index ec9cfa4..1edae35 100644 +--- a/src/pki_crypto.c ++++ b/src/pki_crypto.c +@@ -2291,8 +2291,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 +@@ -2307,8 +2311,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; + } + +@@ -2351,8 +2355,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)); +@@ -2360,35 +2364,31 @@ 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); +- } +- if (pkey != NULL) { +- EVP_PKEY_free(pkey); +- } ++ EVP_MD_CTX_free(ctx); ++ EVP_PKEY_free(pkey); + return rc; + } + +-- +2.27.0 + diff --git a/1001-kex-Reformat-ssh_kex_select_methods.patch b/1001-kex-Reformat-ssh_kex_select_methods.patch new file mode 100644 index 0000000000000000000000000000000000000000..6fd45bf9a453ce0f3dbb4c9146070c94a980ff42 --- /dev/null +++ b/1001-kex-Reformat-ssh_kex_select_methods.patch @@ -0,0 +1,122 @@ +From 6c7bc0fa21ad1c868027f86ff0f41ff42045e49a Mon Sep 17 00:00:00 2001 +From: Liwei Ge +Date: Wed, 28 Jun 2023 20:09:37 +0800 +Subject: [PATCH 1001/1007] kex: Reformat ssh_kex_select_methods + +backport patch from +https://git.libssh.org/projects/libssh.git/commit/src/kex.c?id=070f679767b1e7b6c7b79edca29fddea8631bf38 + +Signed-off-by: Liwei Ge +--- + src/kex.c | 77 +++++++++++++++++++++++++++++-------------------------- + 1 file changed, 41 insertions(+), 36 deletions(-) + +diff --git a/src/kex.c b/src/kex.c +index 82071c7..a542da8 100644 +--- a/src/kex.c ++++ b/src/kex.c +@@ -754,9 +754,10 @@ static const char *ssh_find_aead_hmac(const char *cipher) + */ + int ssh_kex_select_methods (ssh_session session) + { +- struct ssh_kex_struct *server = &session->next_crypto->server_kex; +- struct ssh_kex_struct *client = &session->next_crypto->client_kex; +- char *ext_start = NULL; ++ struct ssh_crypto_struct *crypto = session->next_crypto; ++ struct ssh_kex_struct *server = &crypto->server_kex; ++ struct ssh_kex_struct *client = &crypto->client_kex; ++ char *ext_start = NULL, *kex; + const char *aead_hmac = NULL; + int i; + +@@ -768,50 +769,54 @@ int ssh_kex_select_methods (ssh_session session) + } + + for (i = 0; i < SSH_KEX_METHODS; i++) { +- session->next_crypto->kex_methods[i]=ssh_find_matching(server->methods[i],client->methods[i]); ++ crypto->kex_methods[i] = ssh_find_matching(server->methods[i], ++ client->methods[i]); + + if (i == SSH_MAC_C_S || i == SSH_MAC_S_C) { +- aead_hmac = ssh_find_aead_hmac(session->next_crypto->kex_methods[i-2]); ++ aead_hmac = ssh_find_aead_hmac(crypto->kex_methods[i - 2]); + if (aead_hmac) { +- free(session->next_crypto->kex_methods[i]); +- session->next_crypto->kex_methods[i] = strdup(aead_hmac); ++ free(crypto->kex_methods[i]); ++ crypto->kex_methods[i] = strdup(aead_hmac); + } + } +- if (session->next_crypto->kex_methods[i] == NULL && i < SSH_LANG_C_S){ +- ssh_set_error(session,SSH_FATAL,"kex error : no match for method %s: server [%s], client [%s]", +- ssh_kex_descriptions[i],server->methods[i],client->methods[i]); ++ if (crypto->kex_methods[i] == NULL && i < SSH_LANG_C_S) { ++ ssh_set_error(session, SSH_FATAL, ++ "kex error : no match for method %s: server [%s], " ++ "client [%s]", ssh_kex_descriptions[i], ++ server->methods[i], client->methods[i]); + return SSH_ERROR; +- } else if ((i >= SSH_LANG_C_S) && (session->next_crypto->kex_methods[i] == NULL)) { ++ } else if ((i >= SSH_LANG_C_S) && (crypto->kex_methods[i] == NULL)) { + /* we can safely do that for languages */ +- session->next_crypto->kex_methods[i] = strdup(""); ++ 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; ++ kex = session->next_crypto->kex_methods[SSH_KEX]; ++ if (strcmp(kex, "diffie-hellman-group1-sha1") == 0) { ++ session->next_crypto->kex_type = SSH_KEX_DH_GROUP1_SHA1; ++ } else if (strcmp(kex, "diffie-hellman-group14-sha1") == 0) { ++ session->next_crypto->kex_type = SSH_KEX_DH_GROUP14_SHA1; ++ } else if (strcmp(kex, "diffie-hellman-group14-sha256") == 0) { ++ session->next_crypto->kex_type = SSH_KEX_DH_GROUP14_SHA256; ++ } else if (strcmp(kex, "diffie-hellman-group16-sha512") == 0) { ++ session->next_crypto->kex_type = SSH_KEX_DH_GROUP16_SHA512; ++ } else if (strcmp(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; ++ } else if (strcmp(kex, "diffie-hellman-group-exchange-sha1") == 0) { ++ session->next_crypto->kex_type = SSH_KEX_DH_GEX_SHA1; ++ } else if (strcmp(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; ++ } else if (strcmp(kex, "ecdh-sha2-nistp256") == 0) { ++ session->next_crypto->kex_type = SSH_KEX_ECDH_SHA2_NISTP256; ++ } else if (strcmp(kex, "ecdh-sha2-nistp384") == 0) { ++ session->next_crypto->kex_type = SSH_KEX_ECDH_SHA2_NISTP384; ++ } else if (strcmp(kex, "ecdh-sha2-nistp521") == 0) { ++ session->next_crypto->kex_type = SSH_KEX_ECDH_SHA2_NISTP521; ++ } else if (strcmp(kex, "curve25519-sha256@libssh.org") == 0) { ++ session->next_crypto->kex_type = SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG; ++ } else if (strcmp(kex, "curve25519-sha256") == 0) { ++ session->next_crypto->kex_type = SSH_KEX_CURVE25519_SHA256; + } + SSH_LOG(SSH_LOG_DEBUG, "Negotiated %s,%s,%s,%s,%s,%s,%s,%s,%s,%s", + session->next_crypto->kex_methods[SSH_KEX], +-- +2.27.0 + diff --git a/1002-Reformat-ssh_packet_kexinit.patch b/1002-Reformat-ssh_packet_kexinit.patch new file mode 100644 index 0000000000000000000000000000000000000000..5ba800c8911201d746ce5698deb8d6a6d1f98f3c --- /dev/null +++ b/1002-Reformat-ssh_packet_kexinit.patch @@ -0,0 +1,155 @@ +From 6ff017877edcc832543eeed4343a525e6b1bccdc Mon Sep 17 00:00:00 2001 +From: Liwei Ge +Date: Wed, 28 Jun 2023 20:21:25 +0800 +Subject: [PATCH 1002/1007] Reformat ssh_packet_kexinit() + +backport patch from +https://git.libssh.org/projects/libssh.git/commit/src/kex.c?id=1d6f2e4d9b7adac08f2240b745082d20aa1cc3de + +Signed-off-by: Liwei Ge +--- + src/kex.c | 54 ++++++++++++++++++++++++++++++------------------------ + 1 file changed, 30 insertions(+), 24 deletions(-) + +diff --git a/src/kex.c b/src/kex.c +index a542da8..9af4ffd 100644 +--- a/src/kex.c ++++ b/src/kex.c +@@ -331,6 +331,7 @@ static int cmp_first_kex_algo(const char *client_str, + SSH_PACKET_CALLBACK(ssh_packet_kexinit) + { + int i, ok; ++ struct ssh_crypto_struct *crypto = session->next_crypto; + int server_kex = session->server; + ssh_string str = NULL; + char *strings[SSH_KEX_METHODS] = {0}; +@@ -347,32 +348,37 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit) + if (session->session_state == SSH_SESSION_STATE_AUTHENTICATED) { + SSH_LOG(SSH_LOG_DEBUG, "Initiating key re-exchange"); + } else if (session->session_state != SSH_SESSION_STATE_INITIAL_KEX) { +- ssh_set_error(session,SSH_FATAL,"SSH_KEXINIT received in wrong state"); ++ ssh_set_error(session, SSH_FATAL, ++ "SSH_KEXINIT received in wrong state"); + goto error; + } + + if (server_kex) { +- len = ssh_buffer_get_data(packet,session->next_crypto->client_kex.cookie, 16); ++ len = ssh_buffer_get_data(packet, crypto->client_kex.cookie, 16); + if (len != 16) { +- ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: no cookie in packet"); ++ ssh_set_error(session, SSH_FATAL, ++ "ssh_packet_kexinit: no cookie in packet"); + goto error; + } + +- ok = ssh_hashbufin_add_cookie(session, session->next_crypto->client_kex.cookie); ++ ok = ssh_hashbufin_add_cookie(session, crypto->client_kex.cookie); + if (ok < 0) { +- ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: adding cookie failed"); ++ ssh_set_error(session, SSH_FATAL, ++ "ssh_packet_kexinit: adding cookie failed"); + goto error; + } + } else { +- len = ssh_buffer_get_data(packet,session->next_crypto->server_kex.cookie, 16); ++ len = ssh_buffer_get_data(packet, crypto->server_kex.cookie, 16); + if (len != 16) { +- ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: no cookie in packet"); ++ ssh_set_error(session, SSH_FATAL, ++ "ssh_packet_kexinit: no cookie in packet"); + goto error; + } + +- ok = ssh_hashbufin_add_cookie(session, session->next_crypto->server_kex.cookie); ++ ok = ssh_hashbufin_add_cookie(session, crypto->server_kex.cookie); + if (ok < 0) { +- ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: adding cookie failed"); ++ ssh_set_error(session, SSH_FATAL, ++ "ssh_packet_kexinit: adding cookie failed"); + goto error; + } + } +@@ -385,7 +391,8 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit) + + rc = ssh_buffer_add_ssh_string(session->in_hashbuf, str); + if (rc < 0) { +- ssh_set_error(session, SSH_FATAL, "Error adding string in hash buffer"); ++ ssh_set_error(session, SSH_FATAL, ++ "Error adding string in hash buffer"); + goto error; + } + +@@ -401,11 +408,11 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit) + /* copy the server kex info into an array of strings */ + if (server_kex) { + for (i = 0; i < SSH_KEX_METHODS; i++) { +- session->next_crypto->client_kex.methods[i] = strings[i]; ++ crypto->client_kex.methods[i] = strings[i]; + } + } else { /* client */ + for (i = 0; i < SSH_KEX_METHODS; i++) { +- session->next_crypto->server_kex.methods[i] = strings[i]; ++ crypto->server_kex.methods[i] = strings[i]; + } + } + +@@ -439,10 +446,10 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit) + * If client sent a ext-info-c message in the kex list, it supports + * RFC 8308 extension negotiation. + */ +- ok = ssh_match_group(session->next_crypto->client_kex.methods[SSH_KEX], ++ ok = ssh_match_group(crypto->client_kex.methods[SSH_KEX], + KEX_EXTENSION_CLIENT); + if (ok) { +- const char *hostkeys = NULL; ++ const char *hostkeys = NULL, *wanted_hostkeys = NULL; + + /* The client supports extension negotiation */ + session->extensions |= SSH_EXT_NEGOTIATION; +@@ -452,14 +459,14 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit) + * by the client and enable the respective extensions to provide + * correct signature in the next packet if RSA is negotiated + */ +- hostkeys = session->next_crypto->client_kex.methods[SSH_HOSTKEYS]; ++ hostkeys = crypto->client_kex.methods[SSH_HOSTKEYS]; ++ wanted_hostkeys = session->opts.wanted_methods[SSH_HOSTKEYS]; + ok = ssh_match_group(hostkeys, "rsa-sha2-512"); + if (ok) { + /* Check if rsa-sha2-512 is allowed by config */ +- if (session->opts.wanted_methods[SSH_HOSTKEYS] != NULL) { +- char *is_allowed = +- ssh_find_matching(session->opts.wanted_methods[SSH_HOSTKEYS], +- "rsa-sha2-512"); ++ if (wanted_hostkeys != NULL) { ++ char *is_allowed = ssh_find_matching(wanted_hostkeys, ++ "rsa-sha2-512"); + if (is_allowed != NULL) { + session->extensions |= SSH_EXT_SIG_RSA_SHA512; + } +@@ -469,10 +476,9 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit) + ok = ssh_match_group(hostkeys, "rsa-sha2-256"); + if (ok) { + /* Check if rsa-sha2-256 is allowed by config */ +- if (session->opts.wanted_methods[SSH_HOSTKEYS] != NULL) { +- char *is_allowed = +- ssh_find_matching(session->opts.wanted_methods[SSH_HOSTKEYS], +- "rsa-sha2-256"); ++ if (wanted_hostkeys != NULL) { ++ char *is_allowed = ssh_find_matching(wanted_hostkeys, ++ "rsa-sha2-256"); + if (is_allowed != NULL) { + session->extensions |= SSH_EXT_SIG_RSA_SHA256; + } +@@ -488,7 +494,7 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit) + (session->extensions & SSH_EXT_SIG_RSA_SHA512)) { + session->extensions &= ~(SSH_EXT_SIG_RSA_SHA256 | SSH_EXT_SIG_RSA_SHA512); + rsa_sig_ext = ssh_find_matching("rsa-sha2-512,rsa-sha2-256", +- session->next_crypto->client_kex.methods[SSH_HOSTKEYS]); ++ hostkeys); + if (rsa_sig_ext == NULL) { + goto error; /* should never happen */ + } else if (strcmp(rsa_sig_ext, "rsa-sha2-512") == 0) { +-- +2.27.0 + diff --git a/1003-kex-Reformat-ssh_send_kex.patch b/1003-kex-Reformat-ssh_send_kex.patch new file mode 100644 index 0000000000000000000000000000000000000000..b1b55488aa350f9f08eadb38dbda11519ce9dba0 --- /dev/null +++ b/1003-kex-Reformat-ssh_send_kex.patch @@ -0,0 +1,132 @@ +From 8e5ef2846867f607be63070ea17b716855b50990 Mon Sep 17 00:00:00 2001 +From: Liwei Ge +Date: Wed, 28 Jun 2023 20:22:24 +0800 +Subject: [PATCH 1003/1007] kex: Reformat ssh_send_kex + +backport patch from +https://git.libssh.org/projects/libssh.git/commit/src/kex.c?id=e6cc8dfef5c70931a7057396b2461eb584991079 + +Signed-off-by: Liwei Ge +--- + src/kex.c | 95 +++++++++++++++++++++++++++++-------------------------- + 1 file changed, 50 insertions(+), 45 deletions(-) + +diff --git a/src/kex.c b/src/kex.c +index 9af4ffd..b270ab2 100644 +--- a/src/kex.c ++++ b/src/kex.c +@@ -843,61 +843,66 @@ 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) + { +- struct ssh_kex_struct *kex = (server_kex ? &session->next_crypto->server_kex : +- &session->next_crypto->client_kex); +- ssh_string str = NULL; +- int i; +- int rc; +- +- rc = ssh_buffer_pack(session->out_buffer, +- "bP", +- SSH2_MSG_KEXINIT, +- 16, +- kex->cookie); /* cookie */ +- if (rc != SSH_OK) +- goto error; +- if (ssh_hashbufout_add_cookie(session) < 0) { +- goto error; +- } +- +- ssh_list_kex(kex); ++ struct ssh_kex_struct *kex = (server_kex ? ++ &session->next_crypto->server_kex : ++ &session->next_crypto->client_kex); ++ ssh_string str = NULL; ++ int i; ++ int rc; + +- for (i = 0; i < SSH_KEX_METHODS; i++) { +- str = ssh_string_from_char(kex->methods[i]); +- if (str == NULL) { +- goto error; ++ rc = ssh_buffer_pack(session->out_buffer, ++ "bP", ++ SSH2_MSG_KEXINIT, ++ 16, ++ kex->cookie); /* cookie */ ++ if (rc != SSH_OK) ++ goto error; ++ if (ssh_hashbufout_add_cookie(session) < 0) { ++ goto error; + } + +- if (ssh_buffer_add_ssh_string(session->out_hashbuf, str) < 0) { +- goto error; ++ ssh_list_kex(kex); ++ ++ for (i = 0; i < SSH_KEX_METHODS; i++) { ++ str = ssh_string_from_char(kex->methods[i]); ++ if (str == NULL) { ++ goto error; ++ } ++ ++ rc = ssh_buffer_add_ssh_string(session->out_hashbuf, str); ++ if (rc < 0) { ++ goto error; ++ } ++ rc = ssh_buffer_add_ssh_string(session->out_buffer, str); ++ if (rc < 0) { ++ goto error; ++ } ++ SSH_STRING_FREE(str); ++ str = NULL; + } +- if (ssh_buffer_add_ssh_string(session->out_buffer, str) < 0) { +- goto error; ++ ++ rc = ssh_buffer_pack(session->out_buffer, ++ "bd", ++ 0, ++ 0); ++ if (rc != SSH_OK) { ++ goto error; + } +- SSH_STRING_FREE(str); +- str = NULL; +- } + +- rc = ssh_buffer_pack(session->out_buffer, +- "bd", +- 0, +- 0); +- if (rc != SSH_OK) { +- goto error; +- } ++ rc = ssh_packet_send(session); ++ if (rc == SSH_ERROR) { ++ return -1; ++ } + +- if (ssh_packet_send(session) == SSH_ERROR) { +- return -1; +- } ++ SSH_LOG(SSH_LOG_PACKET, "SSH_MSG_KEXINIT sent"); ++ return 0; + +- SSH_LOG(SSH_LOG_PACKET, "SSH_MSG_KEXINIT sent"); +- return 0; + error: +- ssh_buffer_reinit(session->out_buffer); +- ssh_buffer_reinit(session->out_hashbuf); +- SSH_STRING_FREE(str); ++ ssh_buffer_reinit(session->out_buffer); ++ ssh_buffer_reinit(session->out_hashbuf); ++ SSH_STRING_FREE(str); + +- return -1; ++ return -1; + } + + /* +-- +2.27.0 + diff --git a/1004-client-Reformat-ssh_client_connection_callback.patch b/1004-client-Reformat-ssh_client_connection_callback.patch new file mode 100644 index 0000000000000000000000000000000000000000..ea8fe8a38844600fefebfa503b6cc1f6231645c1 --- /dev/null +++ b/1004-client-Reformat-ssh_client_connection_callback.patch @@ -0,0 +1,187 @@ +From cfc7548855f54843c0b09a1581701597664dce70 Mon Sep 17 00:00:00 2001 +From: Liwei Ge +Date: Wed, 28 Jun 2023 20:44:17 +0800 +Subject: [PATCH 1004/1007] client: Reformat ssh_client_connection_callback + +backport patch from +https://git.libssh.org/projects/libssh.git/commit/src/client.c?id=82850b6ed1d07620b8c15b3e3971e10ab5157097 + +Signed-off-by: Liwei Ge +--- + src/client.c | 143 ++++++++++++++++++++++++++------------------------- + 1 file changed, 72 insertions(+), 71 deletions(-) + +diff --git a/src/client.c b/src/client.c +index a4a7317..5cc8e58 100644 +--- a/src/client.c ++++ b/src/client.c +@@ -386,36 +386,54 @@ static void ssh_client_connection_callback(ssh_session session) + { + int rc; + +- switch(session->session_state) { +- case SSH_SESSION_STATE_NONE: +- case SSH_SESSION_STATE_CONNECTING: +- break; +- case SSH_SESSION_STATE_SOCKET_CONNECTED: +- ssh_set_fd_towrite(session); +- ssh_send_banner(session, 0); +- +- break; +- case SSH_SESSION_STATE_BANNER_RECEIVED: +- if (session->serverbanner == NULL) { +- goto error; +- } +- set_status(session, 0.4f); +- SSH_LOG(SSH_LOG_DEBUG, +- "SSH server banner: %s", session->serverbanner); ++ switch (session->session_state) { ++ case SSH_SESSION_STATE_NONE: ++ case SSH_SESSION_STATE_CONNECTING: ++ break; ++ case SSH_SESSION_STATE_SOCKET_CONNECTED: ++ ssh_set_fd_towrite(session); ++ ssh_send_banner(session, 0); + +- /* Here we analyze the different protocols the server allows. */ +- rc = ssh_analyze_banner(session, 0); +- if (rc < 0) { +- ssh_set_error(session, SSH_FATAL, +- "No version of SSH protocol usable (banner: %s)", +- session->serverbanner); +- goto error; +- } ++ break; ++ case SSH_SESSION_STATE_BANNER_RECEIVED: ++ if (session->serverbanner == NULL) { ++ goto error; ++ } ++ set_status(session, 0.4f); ++ SSH_LOG(SSH_LOG_DEBUG, "SSH server banner: %s", session->serverbanner); + +- ssh_packet_register_socket_callback(session, session->socket); ++ /* Here we analyze the different protocols the server allows. */ ++ rc = ssh_analyze_banner(session, 0); ++ if (rc < 0) { ++ ssh_set_error(session, SSH_FATAL, ++ "No version of SSH protocol usable (banner: %s)", ++ session->serverbanner); ++ goto error; ++ } ++ ++ ssh_packet_register_socket_callback(session, session->socket); ++ ++ ssh_packet_set_default_callbacks(session); ++ session->session_state = SSH_SESSION_STATE_INITIAL_KEX; ++ rc = ssh_set_client_kex(session); ++ if (rc != SSH_OK) { ++ goto error; ++ } ++ rc = ssh_send_kex(session, 0); ++ if (rc < 0) { ++ goto error; ++ } ++ set_status(session, 0.5f); + +- ssh_packet_set_default_callbacks(session); +- session->session_state = SSH_SESSION_STATE_INITIAL_KEX; ++ break; ++ case SSH_SESSION_STATE_INITIAL_KEX: ++ /* TODO: This state should disappear in favor of get_key handle */ ++ break; ++ 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) { ++ /* in rekeying state if next_crypto client_kex might be empty */ + rc = ssh_set_client_kex(session); + if (rc != SSH_OK) { + goto error; +@@ -424,57 +442,40 @@ static void ssh_client_connection_callback(ssh_session session) + if (rc < 0) { + goto error; + } +- set_status(session, 0.5f); +- +- break; +- case SSH_SESSION_STATE_INITIAL_KEX: +- /* TODO: This state should disappear in favor of get_key handle */ +- break; +- 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) { +- /* in rekeying state if next_crypto client_kex is empty */ +- rc = ssh_set_client_kex(session); +- if (rc != SSH_OK) { +- goto error; +- } +- rc = ssh_send_kex(session, 0); +- if (rc < 0) { +- goto error; +- } +- } +- if (ssh_kex_select_methods(session) == SSH_ERROR) +- goto error; +- set_status(session,0.8f); +- session->session_state=SSH_SESSION_STATE_DH; +- if (dh_handshake(session) == SSH_ERROR) { +- goto error; +- } +- FALL_THROUGH; +- case SSH_SESSION_STATE_DH: +- if(session->dh_handshake_state==DH_STATE_FINISHED){ +- set_status(session,1.0f); +- session->connected = 1; +- if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED) +- session->session_state = SSH_SESSION_STATE_AUTHENTICATED; +- else +- session->session_state=SSH_SESSION_STATE_AUTHENTICATING; +- } +- break; +- case SSH_SESSION_STATE_AUTHENTICATING: +- break; +- case SSH_SESSION_STATE_ERROR: ++ } ++ if (ssh_kex_select_methods(session) == SSH_ERROR) + goto error; +- default: +- ssh_set_error(session,SSH_FATAL,"Invalid state %d",session->session_state); ++ set_status(session, 0.8f); ++ session->session_state = SSH_SESSION_STATE_DH; ++ if (dh_handshake(session) == SSH_ERROR) { ++ goto error; ++ } ++ FALL_THROUGH; ++ case SSH_SESSION_STATE_DH: ++ if (session->dh_handshake_state == DH_STATE_FINISHED) { ++ set_status(session, 1.0f); ++ session->connected = 1; ++ if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED) { ++ session->session_state = SSH_SESSION_STATE_AUTHENTICATED; ++ } else { ++ session->session_state = SSH_SESSION_STATE_AUTHENTICATING; ++ } ++ } ++ break; ++ case SSH_SESSION_STATE_AUTHENTICATING: ++ break; ++ case SSH_SESSION_STATE_ERROR: ++ goto error; ++ default: ++ ssh_set_error(session, SSH_FATAL, "Invalid state %d", ++ session->session_state); + } + + return; + error: + ssh_socket_close(session->socket); + session->alive = 0; +- session->session_state=SSH_SESSION_STATE_ERROR; ++ session->session_state = SSH_SESSION_STATE_ERROR; + SSH_LOG(SSH_LOG_WARN, "%s", ssh_get_error(session)); + } + +-- +2.27.0 + diff --git a/1005-server-Reformat-ssh_handle_key_exchange.patch b/1005-server-Reformat-ssh_handle_key_exchange.patch new file mode 100644 index 0000000000000000000000000000000000000000..d4ba8cf96d8963b9e8b607fb420d3647687ebb5a --- /dev/null +++ b/1005-server-Reformat-ssh_handle_key_exchange.patch @@ -0,0 +1,77 @@ +From 44123ffa0bfa1173b0046502c55ba516e6ba7436 Mon Sep 17 00:00:00 2001 +From: Liwei Ge +Date: Wed, 28 Jun 2023 20:51:54 +0800 +Subject: [PATCH 1005/1007] server: Reformat ssh_handle_key_exchange + +backport patch from +https://git.libssh.org/projects/libssh.git/commit/src/server.c?id=b0ce6935fc488d872d61878ffb2fe9b24717e0b7 + +Signed-off-by: Liwei Ge +--- + src/server.c | 31 ++++++++++++++++++------------- + 1 file changed, 18 insertions(+), 13 deletions(-) + +diff --git a/src/server.c b/src/server.c +index 841a1c4..3edc7d0 100644 +--- a/src/server.c ++++ b/src/server.c +@@ -525,10 +525,14 @@ void ssh_set_auth_methods(ssh_session session, int auth_methods) + } + + /* Do the banner and key exchange */ +-int ssh_handle_key_exchange(ssh_session session) { ++int ssh_handle_key_exchange(ssh_session session) ++{ + int rc; +- if (session->session_state != SSH_SESSION_STATE_NONE) +- goto pending; ++ ++ if (session->session_state != SSH_SESSION_STATE_NONE) { ++ goto pending; ++ } ++ + rc = ssh_send_banner(session, 1); + if (rc < 0) { + return SSH_ERROR; +@@ -539,27 +543,28 @@ int ssh_handle_key_exchange(ssh_session session) { + session->ssh_connection_callback = ssh_server_connection_callback; + session->session_state = SSH_SESSION_STATE_SOCKET_CONNECTED; + ssh_socket_set_callbacks(session->socket,&session->socket_callbacks); +- session->socket_callbacks.data=callback_receive_banner; +- session->socket_callbacks.exception=ssh_socket_exception_callback; +- session->socket_callbacks.userdata=session; ++ session->socket_callbacks.data = callback_receive_banner; ++ session->socket_callbacks.exception = ssh_socket_exception_callback; ++ session->socket_callbacks.userdata = session; + + rc = server_set_kex(session); + if (rc < 0) { + return SSH_ERROR; + } +- pending: ++pending: + rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_USER, +- ssh_server_kex_termination,session); ++ ssh_server_kex_termination,session); + SSH_LOG(SSH_LOG_PACKET, "ssh_handle_key_exchange: current state : %d", +- session->session_state); +- if (rc != SSH_OK) +- return rc; ++ session->session_state); ++ if (rc != SSH_OK) { ++ return rc; ++ } + if (session->session_state == SSH_SESSION_STATE_ERROR || + session->session_state == SSH_SESSION_STATE_DISCONNECTED) { +- return SSH_ERROR; ++ return SSH_ERROR; + } + +- return SSH_OK; ++ return SSH_OK; + } + + /* messages */ +-- +2.27.0 + diff --git a/1006-server-Reformat-callback_receive_banner.patch b/1006-server-Reformat-callback_receive_banner.patch new file mode 100644 index 0000000000000000000000000000000000000000..8ad7381fe5b1b5d3dfb8d4d4c43cd7c7404059a7 --- /dev/null +++ b/1006-server-Reformat-callback_receive_banner.patch @@ -0,0 +1,70 @@ +From dfe9412834d366e884f9e67f45177ac83545a4a8 Mon Sep 17 00:00:00 2001 +From: Liwei Ge +Date: Wed, 28 Jun 2023 20:55:56 +0800 +Subject: [PATCH 1006/1007] server: Reformat callback_receive_banner + +backport patch from +https://git.libssh.org/projects/libssh.git/commit/src/server.c?id=e1be63d78d6836925e57caefa053d1446daa93ab + +Signed-off-by: Liwei Ge +--- + src/server.c | 18 ++++++++++-------- + 1 file changed, 10 insertions(+), 8 deletions(-) + +diff --git a/src/server.c b/src/server.c +index 3edc7d0..3e4599c 100644 +--- a/src/server.c ++++ b/src/server.c +@@ -459,16 +459,17 @@ error: + * @param user is a pointer to session + * @returns Number of bytes processed, or zero if the banner is not complete. + */ +-static int callback_receive_banner(const void *data, size_t len, void *user) { +- char *buffer = (char *) data; +- ssh_session session = (ssh_session) user; ++static size_t callback_receive_banner(const void *data, size_t len, void *user) ++{ ++ char *buffer = (char *)data; ++ ssh_session session = (ssh_session)user; + char *str = NULL; + size_t i; + int ret=0; + + for (i = 0; i < len; i++) { + #ifdef WITH_PCAP +- if(session->pcap_ctx && buffer[i] == '\n') { ++ if (session->pcap_ctx && buffer[i] == '\n') { + ssh_pcap_context_write(session->pcap_ctx, + SSH_PCAP_DIR_IN, + buffer, +@@ -477,11 +478,11 @@ static int callback_receive_banner(const void *data, size_t len, void *user) { + } + #endif + if (buffer[i] == '\r') { +- buffer[i]='\0'; ++ buffer[i] = '\0'; + } + + if (buffer[i] == '\n') { +- buffer[i]='\0'; ++ buffer[i] = '\0'; + + str = strdup(buffer); + /* number of bytes read */ +@@ -494,10 +495,11 @@ static int callback_receive_banner(const void *data, size_t len, void *user) { + return ret; + } + +- if(i > 127) { ++ if (i > 127) { + /* Too big banner */ + session->session_state = SSH_SESSION_STATE_ERROR; +- ssh_set_error(session, SSH_FATAL, "Receiving banner: too large banner"); ++ ssh_set_error(session, SSH_FATAL, ++ "Receiving banner: too large banner"); + + return 0; + } +-- +2.27.0 + diff --git a/1007-server-Reformat-ssh_server_connection_callback.patch b/1007-server-Reformat-ssh_server_connection_callback.patch new file mode 100644 index 0000000000000000000000000000000000000000..586133ffc4abf492de79e3230e4cba6153e0dd43 --- /dev/null +++ b/1007-server-Reformat-ssh_server_connection_callback.patch @@ -0,0 +1,238 @@ +From b1bb518ccf5789d904a31acbc4174da2dd5a0e94 Mon Sep 17 00:00:00 2001 +From: Liwei Ge +Date: Wed, 28 Jun 2023 21:10:39 +0800 +Subject: [PATCH 1007/1007] server: Reformat ssh_server_connection_callback + +backport patch from +https://git.libssh.org/projects/libssh.git/commit/src/server.c?id=c00a3369c2b350bd7e16d611bb2dad149f9ceaff + +Signed-off-by: Liwei Ge +--- + src/server.c | 189 +++++++++++++++++++++++++++------------------------ + 1 file changed, 100 insertions(+), 89 deletions(-) + +diff --git a/src/server.c b/src/server.c +index 3e4599c..f3064c0 100644 +--- a/src/server.c ++++ b/src/server.c +@@ -335,117 +335,128 @@ ssh_get_key_params(ssh_session session, + * @brief A function to be called each time a step has been done in the + * connection. + */ +-static void ssh_server_connection_callback(ssh_session session){ ++static void ssh_server_connection_callback(ssh_session session) ++{ + int rc; + +- switch(session->session_state){ +- case SSH_SESSION_STATE_NONE: +- case SSH_SESSION_STATE_CONNECTING: +- case SSH_SESSION_STATE_SOCKET_CONNECTED: +- break; +- case SSH_SESSION_STATE_BANNER_RECEIVED: +- if (session->clientbanner == NULL) { +- goto error; +- } +- set_status(session, 0.4f); +- SSH_LOG(SSH_LOG_DEBUG, +- "SSH client banner: %s", session->clientbanner); ++ switch (session->session_state) { ++ case SSH_SESSION_STATE_NONE: ++ case SSH_SESSION_STATE_CONNECTING: ++ case SSH_SESSION_STATE_SOCKET_CONNECTED: ++ break; ++ case SSH_SESSION_STATE_BANNER_RECEIVED: ++ if (session->clientbanner == NULL) { ++ goto error; ++ } ++ set_status(session, 0.4f); ++ SSH_LOG(SSH_LOG_DEBUG, ++ "SSH client banner: %s", session->clientbanner); + +- /* Here we analyze the different protocols the server allows. */ +- rc = ssh_analyze_banner(session, 1); +- if (rc < 0) { +- ssh_set_error(session, SSH_FATAL, +- "No version of SSH protocol usable (banner: %s)", +- session->clientbanner); +- goto error; +- } ++ /* Here we analyze the different protocols the server allows. */ ++ rc = ssh_analyze_banner(session, 1); ++ if (rc < 0) { ++ ssh_set_error(session, SSH_FATAL, ++ "No version of SSH protocol usable (banner: %s)", ++ session->clientbanner); ++ goto error; ++ } + +- /* from now, the packet layer is handling incoming packets */ +- session->socket_callbacks.data=ssh_packet_socket_callback; +- ssh_packet_register_socket_callback(session, session->socket); ++ /* from now, the packet layer is handling incoming packets */ ++ session->socket_callbacks.data=ssh_packet_socket_callback; ++ ssh_packet_register_socket_callback(session, session->socket); + +- 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) { ++ ssh_packet_set_default_callbacks(session); ++ set_status(session, 0.5f); ++ session->session_state = SSH_SESSION_STATE_INITIAL_KEX; ++ rc = ssh_send_kex(session, 1); ++ if (rc < 0) { ++ goto error; ++ } ++ break; ++ case SSH_SESSION_STATE_INITIAL_KEX: ++ /* TODO: This state should disappear in favor of get_key handle */ ++ break; ++ case SSH_SESSION_STATE_KEXINIT_RECEIVED: ++ set_status(session, 0.6f); ++ if (session->next_crypto->server_kex.methods[0] == NULL) { ++ rc = server_set_kex(session); ++ if (rc == SSH_ERROR) { + goto error; + } +- break; +- case SSH_SESSION_STATE_INITIAL_KEX: +- /* TODO: This state should disappear in favor of get_key handle */ +- break; +- case SSH_SESSION_STATE_KEXINIT_RECEIVED: +- set_status(session,0.6f); +- if(session->next_crypto->server_kex.methods[0]==NULL){ +- 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) +- goto error; +- } +- ssh_list_kex(&session->next_crypto->client_kex); // log client kex +- if (ssh_kex_select_methods(session) < 0) { ++ /* We are in a rekeying, so we need to send the server kex */ ++ rc = ssh_send_kex(session, 1); ++ if (rc < 0) { + goto error; + } +- if (crypt_set_algorithms_server(session) == SSH_ERROR) ++ } ++ ssh_list_kex(&session->next_crypto->client_kex); // log client kex ++ rc = ssh_kex_select_methods(session); ++ if (rc < 0) { ++ goto error; ++ } ++ rc = crypt_set_algorithms_server(session); ++ if (rc == SSH_ERROR) { ++ goto error; ++ } ++ set_status(session, 0.8f); ++ session->session_state = SSH_SESSION_STATE_DH; ++ break; ++ case SSH_SESSION_STATE_DH: ++ if (session->dh_handshake_state == DH_STATE_FINISHED) { ++ ++ rc = ssh_packet_set_newkeys(session, SSH_DIRECTION_IN); ++ if (rc != SSH_OK) { + goto error; +- set_status(session,0.8f); +- session->session_state=SSH_SESSION_STATE_DH; +- break; +- case SSH_SESSION_STATE_DH: +- if(session->dh_handshake_state==DH_STATE_FINISHED){ +- +- rc = ssh_packet_set_newkeys(session, SSH_DIRECTION_IN); +- if (rc != SSH_OK) { +- goto error; +- } ++ } + ++ /* ++ * If the client supports extension negotiation, we will send ++ * our supported extensions now. This is the first message after ++ * sending NEWKEYS message and after turning on crypto. ++ */ ++ if (session->extensions & SSH_EXT_NEGOTIATION && ++ session->session_state != SSH_SESSION_STATE_AUTHENTICATED) { + /* +- * If the client supports extension negotiation, we will send +- * our supported extensions now. This is the first message after +- * sending NEWKEYS message and after turning on crypto. ++ * Only send an SSH_MSG_EXT_INFO message the first time the ++ * client undergoes NEWKEYS. It is unexpected for this message ++ * to be sent upon rekey, and may cause clients to log error ++ * messages. ++ * ++ * The session_state can not be used for this purpose because it ++ * is re-set to SSH_SESSION_STATE_KEXINIT_RECEIVED during rekey. ++ * So, use the connected flag which transitions from non-zero ++ * below. ++ * ++ * See also: ++ * - https://bugzilla.mindrot.org/show_bug.cgi?id=2929 + */ +- if (session->extensions & SSH_EXT_NEGOTIATION && +- session->session_state != SSH_SESSION_STATE_AUTHENTICATED) { +- +- /* +- * Only send an SSH_MSG_EXT_INFO message the first time the client +- * undergoes NEWKEYS. It is unexpected for this message to be sent +- * upon rekey, and may cause clients to log error messages. +- * +- * The session_state can not be used for this purpose because it is +- * re-set to SSH_SESSION_STATE_KEXINIT_RECEIVED during rekey. So, +- * use the connected flag which transitions from non-zero below. +- * +- * See also: +- * - https://bugzilla.mindrot.org/show_bug.cgi?id=2929 +- */ +- if (session->connected == 0) { +- ssh_server_send_extensions(session); +- } ++ if (session->connected == 0) { ++ ssh_server_send_extensions(session); + } ++ } + +- set_status(session,1.0f); +- session->connected = 1; +- session->session_state=SSH_SESSION_STATE_AUTHENTICATING; +- if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED) +- session->session_state = SSH_SESSION_STATE_AUTHENTICATED; ++ set_status(session, 1.0f); ++ session->connected = 1; ++ session->session_state = SSH_SESSION_STATE_AUTHENTICATING; ++ if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED) ++ session->session_state = SSH_SESSION_STATE_AUTHENTICATED; + +- } +- break; +- case SSH_SESSION_STATE_AUTHENTICATING: +- break; +- case SSH_SESSION_STATE_ERROR: +- goto error; +- default: +- ssh_set_error(session,SSH_FATAL,"Invalid state %d",session->session_state); ++ } ++ break; ++ case SSH_SESSION_STATE_AUTHENTICATING: ++ break; ++ case SSH_SESSION_STATE_ERROR: ++ goto error; ++ default: ++ ssh_set_error(session, SSH_FATAL, "Invalid state %d", ++ session->session_state); + } + + return; + error: + ssh_socket_close(session->socket); + session->alive = 0; +- session->session_state=SSH_SESSION_STATE_ERROR; ++ session->session_state = SSH_SESSION_STATE_ERROR; + } + + /** +-- +2.27.0 + diff --git a/1008-packet-Do-not-allow-servers-to-initiate-handshake.patch b/1008-packet-Do-not-allow-servers-to-initiate-handshake.patch new file mode 100644 index 0000000000000000000000000000000000000000..8384a14403829a9380f7e7fdd5a517f35c953bad --- /dev/null +++ b/1008-packet-Do-not-allow-servers-to-initiate-handshake.patch @@ -0,0 +1,32 @@ +From cd80da1fc8f497144a44e2135e913fb4727cdea3 Mon Sep 17 00:00:00 2001 +From: Liwei Ge +Date: Wed, 28 Jun 2023 19:57:01 +0800 +Subject: [PATCH 1008/1017] packet: Do not allow servers to initiate handshake + +backport patch from +https://git.libssh.org/projects/libssh.git/commit/?id=4fb6bccf22ed9c1b74ba89ba53c281762acfa1ec + +Signed-off-by: Liwei Ge +--- + src/packet.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/src/packet.c b/src/packet.c +index ec4a720..0cd553f 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_handhsake_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.27.0 + diff --git a/1009-packet_cb-Log-more-verbose-error-if-signature-verifi.patch b/1009-packet_cb-Log-more-verbose-error-if-signature-verifi.patch new file mode 100644 index 0000000000000000000000000000000000000000..a5d68cf9be4a10e3b495bbd7537b0386274024cb --- /dev/null +++ b/1009-packet_cb-Log-more-verbose-error-if-signature-verifi.patch @@ -0,0 +1,30 @@ +From 17996d72c3f732987b8465e04f734f06e076f08a Mon Sep 17 00:00:00 2001 +From: Liwei Ge +Date: Wed, 28 Jun 2023 19:58:25 +0800 +Subject: [PATCH 1009/1017] packet_cb: Log more verbose error if signature + verification fails + +backport patch from https://git.libssh.org/projects/libssh.git/commit/?id=fa902a37aefbe2215654c3f902ee6add1ece0200 + +Signed-off-by: Liwei Ge +--- + 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_DEBUG,"Signature verified and valid"); +-- +2.27.0 + diff --git a/1010-kex-Factor-out-the-kex-mapping-to-internal-enum.patch b/1010-kex-Factor-out-the-kex-mapping-to-internal-enum.patch new file mode 100644 index 0000000000000000000000000000000000000000..332af04f1620d5540d84707d72d16b8398641d50 --- /dev/null +++ b/1010-kex-Factor-out-the-kex-mapping-to-internal-enum.patch @@ -0,0 +1,99 @@ +From 172662060a75c555901af7da80fea66f43f1f397 Mon Sep 17 00:00:00 2001 +From: Liwei Ge +Date: Wed, 28 Jun 2023 20:12:07 +0800 +Subject: [PATCH 1010/1017] kex: Factor out the kex mapping to internal enum + +backport patch from +https://git.libssh.org/projects/libssh.git/commit/?id=f455ffe8b84df145a28eedb53dd3d72f3171e490 + +Signed-off-by: Liwei Ge +--- + src/kex.c | 65 +++++++++++++++++++++++++++++++------------------------ + 1 file changed, 37 insertions(+), 28 deletions(-) + +diff --git a/src/kex.c b/src/kex.c +index b270ab2..c13cf6e 100644 +--- a/src/kex.c ++++ b/src/kex.c +@@ -755,6 +755,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. + */ +@@ -796,34 +830,9 @@ int ssh_kex_select_methods (ssh_session session) + crypto->kex_methods[i] = strdup(""); + } + } +- kex = session->next_crypto->kex_methods[SSH_KEX]; +- if (strcmp(kex, "diffie-hellman-group1-sha1") == 0) { +- session->next_crypto->kex_type = SSH_KEX_DH_GROUP1_SHA1; +- } else if (strcmp(kex, "diffie-hellman-group14-sha1") == 0) { +- session->next_crypto->kex_type = SSH_KEX_DH_GROUP14_SHA1; +- } else if (strcmp(kex, "diffie-hellman-group14-sha256") == 0) { +- session->next_crypto->kex_type = SSH_KEX_DH_GROUP14_SHA256; +- } else if (strcmp(kex, "diffie-hellman-group16-sha512") == 0) { +- session->next_crypto->kex_type = SSH_KEX_DH_GROUP16_SHA512; +- } else if (strcmp(kex, "diffie-hellman-group18-sha512") == 0) { +- session->next_crypto->kex_type = SSH_KEX_DH_GROUP18_SHA512; +-#ifdef WITH_GEX +- } else if (strcmp(kex, "diffie-hellman-group-exchange-sha1") == 0) { +- session->next_crypto->kex_type = SSH_KEX_DH_GEX_SHA1; +- } else if (strcmp(kex, "diffie-hellman-group-exchange-sha256") == 0) { +- session->next_crypto->kex_type = SSH_KEX_DH_GEX_SHA256; +-#endif /* WITH_GEX */ +- } else if (strcmp(kex, "ecdh-sha2-nistp256") == 0) { +- session->next_crypto->kex_type = SSH_KEX_ECDH_SHA2_NISTP256; +- } else if (strcmp(kex, "ecdh-sha2-nistp384") == 0) { +- session->next_crypto->kex_type = SSH_KEX_ECDH_SHA2_NISTP384; +- } else if (strcmp(kex, "ecdh-sha2-nistp521") == 0) { +- session->next_crypto->kex_type = SSH_KEX_ECDH_SHA2_NISTP521; +- } else if (strcmp(kex, "curve25519-sha256@libssh.org") == 0) { +- session->next_crypto->kex_type = SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG; +- } else if (strcmp(kex, "curve25519-sha256") == 0) { +- session->next_crypto->kex_type = SSH_KEX_CURVE25519_SHA256; +- } ++ kex = crypto->kex_methods[SSH_KEX]; ++ crypto->kex_type = kex_select_kex_type(kex); ++ + SSH_LOG(SSH_LOG_DEBUG, "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.27.0 + diff --git a/1011-dh-Expose-the-callback-cleanup-functions.patch b/1011-dh-Expose-the-callback-cleanup-functions.patch new file mode 100644 index 0000000000000000000000000000000000000000..aac73df0d304f6e765a43160b6635c5f7c6e9f92 --- /dev/null +++ b/1011-dh-Expose-the-callback-cleanup-functions.patch @@ -0,0 +1,221 @@ +From 3d5b6a675a041ee9fe343b7b560d732fba7cec61 Mon Sep 17 00:00:00 2001 +From: Liwei Ge +Date: Wed, 28 Jun 2023 20:18:05 +0800 +Subject: [PATCH 1011/1017] dh: Expose the callback cleanup functions + +backport patch from +https://git.libssh.org/projects/libssh.git/commit/?id=cd0aa0bd913a7f446b94ff14c5e72edcea53581f + +Signed-off-by: Liwei Ge +--- + 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_DEBUG, "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 c13cf6e..4853dad 100644 +--- a/src/kex.c ++++ b/src/kex.c +@@ -789,6 +789,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.27.0 + diff --git a/1012-kex-Properly-conditionalize-server-code.patch b/1012-kex-Properly-conditionalize-server-code.patch new file mode 100644 index 0000000000000000000000000000000000000000..3d29864f90d579f055bfd3a4260ee3631232ee32 --- /dev/null +++ b/1012-kex-Properly-conditionalize-server-code.patch @@ -0,0 +1,75 @@ +From aa759e47ed1b818d93c3512ea5a2cc8e048cebfd Mon Sep 17 00:00:00 2001 +From: Liwei Ge +Date: Wed, 28 Jun 2023 20:24:26 +0800 +Subject: [PATCH 1012/1017] kex: Properly conditionalize server code + +backport patch from +https://git.libssh.org/projects/libssh.git/commit/?id=3981aeede2e2c07bb947ccbe8d44edcb1498fc3d + +Signed-off-by: Liwei Ge +--- + src/kex.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/src/kex.c b/src/kex.c +index 4853dad..4ff15aa 100644 +--- a/src/kex.c ++++ b/src/kex.c +@@ -354,6 +354,7 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit) + } + + if (server_kex) { ++#ifdef WITH_SERVER + len = ssh_buffer_get_data(packet, crypto->client_kex.cookie, 16); + if (len != 16) { + ssh_set_error(session, SSH_FATAL, +@@ -367,6 +368,7 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit) + "ssh_packet_kexinit: adding cookie failed"); + goto error; + } ++#endif /* WITH_SERVER */ + } else { + len = ssh_buffer_get_data(packet, crypto->server_kex.cookie, 16); + if (len != 16) { +@@ -407,9 +409,11 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit) + + /* copy the server kex info into an array of strings */ + if (server_kex) { ++#ifdef WITH_SERVER + for (i = 0; i < SSH_KEX_METHODS; i++) { + crypto->client_kex.methods[i] = strings[i]; + } ++#endif /* WITH_SERVER */ + } else { /* client */ + for (i = 0; i < SSH_KEX_METHODS; i++) { + crypto->server_kex.methods[i] = strings[i]; +@@ -426,6 +430,8 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit) + * that its value is included when computing the session ID (see + * 'make_sessionid'). + */ ++ ++#ifdef WITH_SERVER + if (server_kex) { + rc = ssh_buffer_get_u8(packet, &first_kex_packet_follows); + if (rc != 1) { +@@ -527,6 +533,7 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit) + session->next_crypto->server_kex.methods[SSH_HOSTKEYS]); + } + } ++#endif /* WITH_SERVER */ + + /* Note, that his overwrites authenticated state in case of rekeying */ + session->session_state = SSH_SESSION_STATE_KEXINIT_RECEIVED; +@@ -538,7 +545,9 @@ error: + SSH_STRING_FREE(str); + for (i = 0; i < SSH_KEX_METHODS; i++) { + if (server_kex) { ++#ifdef WITH_SERVER + session->next_crypto->client_kex.methods[i] = NULL; ++#endif /* WITH_SERVER */ + } else { /* client */ + session->next_crypto->server_kex.methods[i] = NULL; + } +-- +2.27.0 + diff --git a/1013-kex-Correctly-handle-last-fields-of-KEXINIT-also-in-.patch b/1013-kex-Correctly-handle-last-fields-of-KEXINIT-also-in-.patch new file mode 100644 index 0000000000000000000000000000000000000000..1db74d9241fc506004f52e3d543dac97802a986b --- /dev/null +++ b/1013-kex-Correctly-handle-last-fields-of-KEXINIT-also-in-.patch @@ -0,0 +1,273 @@ +From 853b168a07694dd6e64d74985fb563e99db52d3d Mon Sep 17 00:00:00 2001 +From: Liwei Ge +Date: Wed, 28 Jun 2023 21:18:33 +0800 +Subject: [PATCH 1013/1017] kex: Correctly handle last fields of KEXINIT also + in the client side + +backport from +https://git.libssh.org/projects/libssh.git/commit/?id=8dbe055328ca8cd33d798d647ed423ae8cba0b90 + +Signed-off-by: Liwei Ge +--- + include/libssh/session.h | 5 ++ + src/client.c | 2 +- + src/kex.c | 124 +++++++++++++++++++++------------------ + src/server.c | 8 ++- + 4 files changed, 79 insertions(+), 60 deletions(-) + +diff --git a/include/libssh/session.h b/include/libssh/session.h +index 2225615..0d17ee3 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 0x0008 ++ + /* 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 5cc8e58..22062de 100644 +--- a/src/client.c ++++ b/src/client.c +@@ -432,7 +432,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 might be empty */ + rc = ssh_set_client_kex(session); + if (rc != SSH_OK) { +diff --git a/src/kex.c b/src/kex.c +index 4ff15aa..672eada 100644 +--- a/src/kex.c ++++ b/src/kex.c +@@ -346,7 +346,17 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit) + (void)user; + + if (session->session_state == SSH_SESSION_STATE_AUTHENTICATED) { +- SSH_LOG(SSH_LOG_DEBUG, "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"); +@@ -368,6 +378,11 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit) + "ssh_packet_kexinit: adding cookie failed"); + goto error; + } ++ ++ ok = server_set_kex(session); ++ if (ok == SSH_ERROR) { ++ goto error; ++ } + #endif /* WITH_SERVER */ + } else { + len = ssh_buffer_get_data(packet, crypto->server_kex.cookie, 16); +@@ -383,6 +398,11 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit) + "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++) { +@@ -431,23 +451,38 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit) + * 'make_sessionid'). + */ + +-#ifdef WITH_SERVER +- 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 = crypto->client_kex.methods; ++ char **server_methods = 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]); ++ } + ++#ifdef WITH_SERVER ++ if (server_kex) { + /* + * If client sent a ext-info-c message in the kex list, it supports + * RFC 8308 extension negotiation. +@@ -519,19 +554,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]); +- } + } + #endif /* WITH_SERVER */ + +@@ -687,14 +709,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++) { +@@ -945,11 +971,22 @@ int ssh_send_kex(ssh_session session, int server_kex) + 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; ++ } ++ + rc = ssh_packet_send(session); + if (rc == SSH_ERROR) { + return -1; + } + ++ session->flags |= SSH_SESSION_FLAG_KEXINIT_SENT; + SSH_LOG(SSH_LOG_PACKET, "SSH_MSG_KEXINIT sent"); + return 0; + +@@ -1078,33 +1115,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 f3064c0..d8c072c 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) { +@@ -378,7 +382,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) { + rc = server_set_kex(session); + if (rc == SSH_ERROR) { + goto error; +-- +2.27.0 + diff --git a/1014-kex-Remove-needless-function-argument.patch b/1014-kex-Remove-needless-function-argument.patch new file mode 100644 index 0000000000000000000000000000000000000000..bd6938ed899e661322ec8ff053f990f6f4ebcded --- /dev/null +++ b/1014-kex-Remove-needless-function-argument.patch @@ -0,0 +1,101 @@ +From 991d4c322470dbca36667856ed319dd6999dfe3a Mon Sep 17 00:00:00 2001 +From: Liwei Ge +Date: Wed, 28 Jun 2023 21:19:55 +0800 +Subject: [PATCH 1014/1017] kex: Remove needless function argument + +backport patch from +https://git.libssh.org/projects/libssh.git/commit/?id=1c85acb6e6340588d298f2eba4df983a04dc44c5 + +Signed-off-by: Liwei Ge +--- + include/libssh/kex.h | 2 +- + src/client.c | 4 ++-- + src/kex.c | 6 +++--- + src/server.c | 4 ++-- + 4 files changed, 8 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 22062de..e1c8432 100644 +--- a/src/client.c ++++ b/src/client.c +@@ -419,7 +419,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; + } +@@ -438,7 +438,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 672eada..01b3c3c 100644 +--- a/src/kex.c ++++ b/src/kex.c +@@ -923,9 +923,9 @@ 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 ? ++ struct ssh_kex_struct *kex = (session->server ? + &session->next_crypto->server_kex : + &session->next_crypto->client_kex); + ssh_string str = NULL; +@@ -1038,7 +1038,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 d8c072c..2816ebb 100644 +--- a/src/server.c ++++ b/src/server.c +@@ -372,7 +372,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; +- rc = ssh_send_kex(session, 1); ++ rc = ssh_send_kex(session); + if (rc < 0) { + goto error; + } +@@ -388,7 +388,7 @@ static void ssh_server_connection_callback(ssh_session session) + goto error; + } + /* We are in a rekeying, so we need to send the server kex */ +- rc = ssh_send_kex(session, 1); ++ rc = ssh_send_kex(session); + if (rc < 0) { + goto error; + } +-- +2.27.0 + diff --git a/1015-kex-Add-support-for-sending-first_kex_packet_follows.patch b/1015-kex-Add-support-for-sending-first_kex_packet_follows.patch new file mode 100644 index 0000000000000000000000000000000000000000..3ec3eebee6f2186268e33b4fccce1dc2a57da68b --- /dev/null +++ b/1015-kex-Add-support-for-sending-first_kex_packet_follows.patch @@ -0,0 +1,270 @@ +From 0819071896577f1b4469d831fd9ec0e2768b2261 Mon Sep 17 00:00:00 2001 +From: Liwei Ge +Date: Wed, 28 Jun 2023 21:24:12 +0800 +Subject: [PATCH 1015/1017] kex: Add support for sending + first_kex_packet_follows flag + +backport patch from +https://git.libssh.org/projects/libssh.git/commit/?id=08386d4787f8f532ae289b2a49211486a6af48a9 + +Signed-off-by: Liwei Ge +--- + include/libssh/dh.h | 1 + + include/libssh/session.h | 13 +++++-- + src/client.c | 10 ++++- + src/kex.c | 84 ++++++++++++++++++++++++++++++++++------ + 4 files changed, 93 insertions(+), 15 deletions(-) + +diff --git a/include/libssh/dh.h b/include/libssh/dh.h +index 57f37cd..704888c 100644 +--- a/include/libssh/dh.h ++++ b/include/libssh/dh.h +@@ -63,6 +63,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 0d17ee3..1eb0709 100644 +--- a/include/libssh/session.h ++++ b/include/libssh/session.h +@@ -163,14 +163,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 e1c8432..27853c6 100644 +--- a/src/client.c ++++ b/src/client.c +@@ -243,10 +243,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){ +@@ -386,6 +389,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: +@@ -447,6 +452,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 01b3c3c..b5e3ab6 100644 +--- a/src/kex.c ++++ b/src/kex.c +@@ -28,6 +28,7 @@ + #include + #include + ++#include "libssh/libssh.h" + #include "libssh/priv.h" + #include "libssh/buffer.h" + #include "libssh/dh.h" +@@ -345,14 +346,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; +@@ -469,9 +475,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 = crypto->client_kex.methods; + char **server_methods = crypto->server_kex.methods; + session->first_kex_follows_guess_wrong = +@@ -479,6 +486,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"); + } + + #ifdef WITH_SERVER +@@ -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; + +@@ -870,8 +884,9 @@ int ssh_kex_select_methods (ssh_session session) + struct ssh_crypto_struct *crypto = session->next_crypto; + struct ssh_kex_struct *server = &crypto->server_kex; + struct ssh_kex_struct *client = &crypto->client_kex; +- char *ext_start = NULL, *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. +@@ -903,8 +918,18 @@ int ssh_kex_select_methods (ssh_session session) + crypto->kex_methods[i] = strdup(""); + } + } +- kex = crypto->kex_methods[SSH_KEX]; +- 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(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; ++ } ++ crypto->kex_type = kex_type; + + SSH_LOG(SSH_LOG_DEBUG, "Negotiated %s,%s,%s,%s,%s,%s,%s,%s,%s,%s", + session->next_crypto->kex_methods[SSH_KEX], +@@ -931,6 +956,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", +@@ -965,14 +1003,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; + } +@@ -988,6 +1026,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 == 1) { ++ 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: +-- +2.27.0 + diff --git a/1016-tests-Client-coverage-for-key-exchange-with-kex-gues.patch b/1016-tests-Client-coverage-for-key-exchange-with-kex-gues.patch new file mode 100644 index 0000000000000000000000000000000000000000..5f8242278e1a0d16446e56906231b03e4d9732e6 --- /dev/null +++ b/1016-tests-Client-coverage-for-key-exchange-with-kex-gues.patch @@ -0,0 +1,153 @@ +From 4ac5d6f112ace316cdda7826f13d1e7b88a9b9fc Mon Sep 17 00:00:00 2001 +From: Liwei Ge +Date: Wed, 28 Jun 2023 20:36:33 +0800 +Subject: [PATCH 1016/1017] tests: Client coverage for key exchange with kex + guessing + +backport patch from +https://git.libssh.org/projects/libssh.git/commit/?id=8bb17c46a80aabe040758d0b80d830aa6f7f6f82 + +Signed-off-by: Liwei Ge +--- + tests/client/torture_rekey.c | 109 +++++++++++++++++++++++++++++++++-- + 1 file changed, 105 insertions(+), 4 deletions(-) + +diff --git a/tests/client/torture_rekey.c b/tests/client/torture_rekey.c +index 46a015d..a2a739b 100644 +--- a/tests/client/torture_rekey.c ++++ b/tests/client/torture_rekey.c +@@ -651,6 +651,92 @@ 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; + struct CMUnitTest tests[] = { +@@ -671,19 +757,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.27.0 + diff --git a/1017-tests-Fix-rekey-test-so-it-passes-on-build-systems.patch b/1017-tests-Fix-rekey-test-so-it-passes-on-build-systems.patch new file mode 100644 index 0000000000000000000000000000000000000000..37ceb0c71daef1fa5e8599bc5d0479ad313e19d3 --- /dev/null +++ b/1017-tests-Fix-rekey-test-so-it-passes-on-build-systems.patch @@ -0,0 +1,69 @@ +From b3b3fbfa1dc297bb333aeff7130ece358b247c4c Mon Sep 17 00:00:00 2001 +From: Andreas Schneider +Date: Thu, 1 Sep 2022 16:56:39 +0200 +Subject: tests: Fix rekey test so it passes on build systems + +The test failed on Fedora Koji and openSUSE Build Service on i686 only. Probably +the rekey on the server needs longer here to collect enough entropy. So we need +to try harder before we stop :-) + +Signed-off-by: Andreas Schneider +Reviewed-by: Jakub Jelen +--- + tests/client/torture_rekey.c | 22 ++++++++++++++++++---- + 1 file changed, 18 insertions(+), 4 deletions(-) + +(limited to 'tests/client/torture_rekey.c') + +diff --git a/tests/client/torture_rekey.c b/tests/client/torture_rekey.c +index e7813e1e..ce31325e 100644 +--- a/tests/client/torture_rekey.c ++++ b/tests/client/torture_rekey.c +@@ -38,6 +38,8 @@ + #include + #include + ++#define KEX_RETRY 32 ++ + static uint64_t bytes = 2048; /* 2KB (more than the authentication phase) */ + + static int sshd_setup(void **state) +@@ -499,9 +501,15 @@ static void torture_rekey_different_kex(void **state) + * to make sure the rekey it completes with all different ciphers (paddings */ + memset(data, 0, sizeof(data)); + memset(data, 'A', 128); +- for (i = 0; i < 20; i++) { ++ for (i = 0; i < KEX_RETRY; i++) { + ssh_send_ignore(s->ssh.session, data); +- ssh_handle_packets(s->ssh.session, 50); ++ ssh_handle_packets(s->ssh.session, 100); ++ ++ c = s->ssh.session->current_crypto; ++ /* SHA256 len */ ++ if (c->digest_len != 32) { ++ break; ++ } + } + + /* The rekey limit was restored in the new crypto to the same value */ +@@ -571,9 +579,15 @@ static void torture_rekey_server_different_kex(void **state) + * to make sure the rekey it completes with all different ciphers (paddings */ + memset(data, 0, sizeof(data)); + memset(data, 'A', 128); +- for (i = 0; i < 25; i++) { ++ for (i = 0; i < KEX_RETRY; i++) { + ssh_send_ignore(s->ssh.session, data); +- ssh_handle_packets(s->ssh.session, 50); ++ ssh_handle_packets(s->ssh.session, 100); ++ ++ c = s->ssh.session->current_crypto; ++ /* SHA256 len */ ++ if (c->digest_len != 32) { ++ break; ++ } + } + + /* Check that the secret hash is different than initially */ +-- +cgit v1.2.3 + diff --git a/1018-tests-Send-a-bit-more-to-make-sure-rekey-is-complete.patch b/1018-tests-Send-a-bit-more-to-make-sure-rekey-is-complete.patch new file mode 100644 index 0000000000000000000000000000000000000000..2b378e6a77bb6947bc222b10b16f20c3ab586677 --- /dev/null +++ b/1018-tests-Send-a-bit-more-to-make-sure-rekey-is-complete.patch @@ -0,0 +1,35 @@ +From 6c139d50173e6118e452cf659e3158b664dadffa Mon Sep 17 00:00:00 2001 +From: Liwei Ge +Date: Wed, 28 Jun 2023 19:55:13 +0800 +Subject: [PATCH 1017/1017] tests: Send a bit more to make sure rekey is + completed + +backport patch from +https://git.libssh.org/projects/libssh.git/commit/?id=4e8db9d44b73b2b2bd77172125f1bdb0b7b172f3 + +Signed-off-by: Liwei Ge +--- + 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 a2a739b..20e4a0e 100644 +--- a/tests/client/torture_rekey.c ++++ b/tests/client/torture_rekey.c +@@ -190,10 +190,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.27.0 + diff --git a/1019-tests-Use-a-common-function-to-start-sshd.patch b/1019-tests-Use-a-common-function-to-start-sshd.patch new file mode 100644 index 0000000000000000000000000000000000000000..096af67c9511a1f5218e79cdeb1f11e214617c8e --- /dev/null +++ b/1019-tests-Use-a-common-function-to-start-sshd.patch @@ -0,0 +1,105 @@ +From 35224092ebd45679e233252b8e36ffcf94fb285d Mon Sep 17 00:00:00 2001 +From: Anderson Toshiyuki Sasaki +Date: Thu, 30 Jan 2020 15:53:26 +0100 +Subject: tests: Use a common function to start sshd + +In torture_reload_sshd_server(), instead of trying to use SIGHUP to +reload the configuration file, kill the original process and create a +new one with the new configuration. With this change, both +torture_setup_sshd_server() and torture_reload_sshd_server() need to +start sshd, with the only difference in the configuration setup. The +shared code to start the sshd server was moved to a new introduced +internal function torture_start_sshd_server(). + +Signed-off-by: Anderson Toshiyuki Sasaki +Reviewed-by: Jakub Jelen +--- + tests/torture.c | 45 +++++++++++++++++++-------------------------- + 1 file changed, 19 insertions(+), 26 deletions(-) + +diff --git a/tests/torture.c b/tests/torture.c +index 62cbfaf1..05d9a991 100644 +--- a/tests/torture.c ++++ b/tests/torture.c +@@ -825,21 +825,16 @@ static int torture_wait_for_daemon(unsigned int seconds) + return 1; + } + +-void torture_setup_sshd_server(void **state, bool pam) ++static int torture_start_sshd_server(void **state) + { +- struct torture_state *s; ++ struct torture_state *s = *state; + char sshd_start_cmd[1024]; + int rc; + +- torture_setup_socket_dir(state); +- torture_setup_create_sshd_config(state, pam); +- + /* Set the default interface for the server */ + setenv("SOCKET_WRAPPER_DEFAULT_IFACE", "10", 1); + setenv("PAM_WRAPPER", "1", 1); + +- s = *state; +- + snprintf(sshd_start_cmd, sizeof(sshd_start_cmd), + SSHD_EXECUTABLE " -r -f %s -E %s/sshd/daemon.log 2> %s/sshd/cwrap.log", + s->srv_config, s->socket_dir, s->socket_dir); +@@ -851,7 +846,20 @@ void torture_setup_sshd_server(void **state, bool pam) + unsetenv("PAM_WRAPPER"); + + /* Wait until the sshd is ready to accept connections */ +- rc = torture_wait_for_daemon(5); ++ rc = torture_wait_for_daemon(15); ++ assert_int_equal(rc, 0); ++ ++ return SSH_OK; ++} ++ ++void torture_setup_sshd_server(void **state, bool pam) ++{ ++ int rc; ++ ++ torture_setup_socket_dir(state); ++ torture_setup_create_sshd_config(state, pam); ++ ++ rc = torture_start_sshd_server(state); + assert_int_equal(rc, 0); + } + +@@ -908,29 +916,14 @@ static int + torture_reload_sshd_server(void **state) + { + struct torture_state *s = *state; +- pid_t pid; + int rc; + +- /* read the pidfile */ +- pid = torture_read_pidfile(s->srv_pidfile); +- assert_int_not_equal(pid, -1); +- +- kill(pid, SIGHUP); +- +- /* 10 ms */ +- usleep(10 * 1000); +- +- rc = kill(pid, 0); ++ rc = torture_terminate_process(s->srv_pidfile); + if (rc != 0) { +- fprintf(stderr, +- "ERROR: SSHD process %u died during reload!\n", pid); +- return SSH_ERROR; ++ fprintf(stderr, "XXXXXX Failed to terminate sshd\n"); + } + +- /* Wait until the sshd is ready to accept connections */ +- rc = torture_wait_for_daemon(5); +- assert_int_equal(rc, 0); +- return SSH_OK; ++ return torture_start_sshd_server(state); + } + + /* @brief: Updates SSHD server configuration with more options and +-- +cgit v1.2.3 + diff --git a/libssh.spec b/libssh.spec index cf9b42d66af1fe651ceb3e2b0e3734943a7bacfb..f4cca02773694812feb18de66aad50217f64e65b 100644 --- a/libssh.spec +++ b/libssh.spec @@ -1,7 +1,7 @@ -%define anolis_release .0.1 +%define anolis_release 7 Name: libssh Version: 0.9.6 -Release: 6%{anolis_release}%{?dist} +Release: %{anolis_release}%{?dist} Summary: A library implementing the SSH protocol License: LGPLv2+ URL: http://www.libssh.org @@ -15,6 +15,27 @@ Source4: libssh_server.config Patch0: loglevel.patch Patch1: s390x_fix.patch +Patch1000: 1000-pki_crypto-fix-possible-authentication-bypass.patch +Patch1001: 1001-kex-Reformat-ssh_kex_select_methods.patch +Patch1002: 1002-Reformat-ssh_packet_kexinit.patch +Patch1003: 1003-kex-Reformat-ssh_send_kex.patch +Patch1004: 1004-client-Reformat-ssh_client_connection_callback.patch +Patch1005: 1005-server-Reformat-ssh_handle_key_exchange.patch +Patch1006: 1006-server-Reformat-callback_receive_banner.patch +Patch1007: 1007-server-Reformat-ssh_server_connection_callback.patch +Patch1008: 1008-packet-Do-not-allow-servers-to-initiate-handshake.patch +Patch1009: 1009-packet_cb-Log-more-verbose-error-if-signature-verifi.patch +Patch1010: 1010-kex-Factor-out-the-kex-mapping-to-internal-enum.patch +Patch1011: 1011-dh-Expose-the-callback-cleanup-functions.patch +Patch1012: 1012-kex-Properly-conditionalize-server-code.patch +Patch1013: 1013-kex-Correctly-handle-last-fields-of-KEXINIT-also-in-.patch +Patch1014: 1014-kex-Remove-needless-function-argument.patch +Patch1015: 1015-kex-Add-support-for-sending-first_kex_packet_follows.patch +Patch1016: 1016-tests-Client-coverage-for-key-exchange-with-kex-gues.patch +Patch1017: 1017-tests-Fix-rekey-test-so-it-passes-on-build-systems.patch +Patch1018: 1018-tests-Send-a-bit-more-to-make-sure-rekey-is-complete.patch +Patch1019: 1019-tests-Use-a-common-function-to-start-sshd.patch + BuildRequires: cmake BuildRequires: doxygen BuildRequires: gcc-c++ @@ -154,6 +175,10 @@ popd %doc AUTHORS BSD ChangeLog README %changelog +* Wed Jun 28 2023 Liwei Ge - 0.9.6-7 +- NULL pointer dereference during rekeying with algorithm guessing (CVE-2023-1667) +- authorization bypass in pki_verify_data_signature (CVE-2023-2283) + * Thu May 25 2023 Weisson - 0.9.6-6.0.1 - Add doc sub package