aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--.gitlab-ci.yml100
-rw-r--r--CMakeLists.txt4
-rw-r--r--CPackConfig.cmake2
-rw-r--r--ChangeLog12
-rw-r--r--examples/sshnetcat.c5
-rw-r--r--include/libssh/config_parser.h11
-rw-r--r--include/libssh/curve25519.h1
-rw-r--r--include/libssh/dh-gex.h1
-rw-r--r--include/libssh/dh.h2
-rw-r--r--include/libssh/ecdh.h1
-rw-r--r--include/libssh/kex.h3
-rw-r--r--include/libssh/libcrypto.h5
-rw-r--r--include/libssh/libgcrypt.h1
-rw-r--r--include/libssh/libmbedcrypto.h1
-rw-r--r--include/libssh/misc.h2
-rw-r--r--include/libssh/packet.h1
-rw-r--r--include/libssh/session.h40
-rw-r--r--include/libssh/wrapper.h39
-rw-r--r--src/ABI/current2
-rw-r--r--src/ABI/libssh-4.8.8.symbols421
-rw-r--r--src/ABI/libssh-4.8.9.symbols421
-rw-r--r--src/CMakeLists.txt17
-rw-r--r--src/channels.c12
-rw-r--r--src/client.c155
-rw-r--r--src/config.c4
-rw-r--r--src/config_parser.c28
-rw-r--r--src/connect.c2
-rw-r--r--src/curve25519.c26
-rw-r--r--src/dh-gex.c14
-rw-r--r--src/dh.c24
-rw-r--r--src/ecdh.c15
-rw-r--r--src/ecdh_crypto.c12
-rw-r--r--src/ecdh_gcrypt.c10
-rw-r--r--src/ecdh_mbedcrypto.c11
-rw-r--r--src/gssapi.c32
-rw-r--r--src/init.c2
-rw-r--r--src/kdf.c126
-rw-r--r--src/kex.c557
-rw-r--r--src/libcrypto.c215
-rw-r--r--src/libgcrypt.c186
-rw-r--r--src/libmbedcrypto.c236
-rw-r--r--src/misc.c131
-rw-r--r--src/options.c34
-rw-r--r--src/packet.c104
-rw-r--r--src/packet_cb.c15
-rw-r--r--src/pki_crypto.c40
-rw-r--r--src/server.c251
-rw-r--r--src/session.c76
-rw-r--r--src/token.c2
-rw-r--r--src/wrapper.c17
-rw-r--r--tests/client/torture_auth.c7
-rw-r--r--tests/client/torture_proxycommand.c53
-rw-r--r--tests/client/torture_rekey.c192
-rw-r--r--tests/client/torture_session.c35
-rw-r--r--tests/keys/certauth/id_rsa54
-rw-r--r--tests/keys/certauth/id_rsa-cert.pub2
-rw-r--r--tests/keys/certauth/id_rsa.pub2
-rw-r--r--tests/pkd/pkd_keyutil.c24
-rw-r--r--tests/server/torture_server_config.c10
-rw-r--r--tests/torture.c61
-rw-r--r--tests/unittests/torture_config.c93
-rw-r--r--tests/unittests/torture_misc.c119
-rw-r--r--tests/unittests/torture_options.c22
64 files changed, 3029 insertions, 1075 deletions
diff --git a/.gitignore b/.gitignore
index da6827ca..c8a7348a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,7 @@
*~$
cscope.*
compile_commands.json
+/.cache
/.clangd
tags
/build
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index e2b99e00..4ac98512 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -2,6 +2,8 @@ variables:
BUILD_IMAGES_PROJECT: libssh/build-images
FEDORA_BUILD: buildenv-fedora
CENTOS7_BUILD: buildenv-centos7
+ CENTOS8_BUILD: buildenv-c8s
+ CENTOS9_BUILD: buildenv-c9s
TUMBLEWEED_BUILD: buildenv-tumbleweed
MINGW_BUILD: buildenv-mingw
@@ -25,6 +27,47 @@ centos7/openssl_1.0.x/x86_64:
paths:
- obj/
+centos8/openssl_1.1.1/x86_64:
+ image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$CENTOS8_BUILD
+ script:
+ - mkdir -p obj && cd obj && cmake
+ -DCMAKE_BUILD_TYPE=RelWithDebInfo
+ -DPICKY_DEVELOPER=ON
+ -DWITH_BLOWFISH_CIPHER=ON
+ -DWITH_SFTP=ON -DWITH_SERVER=ON -DWITH_ZLIB=ON -DWITH_PCAP=ON
+ -DUNIT_TESTING=ON -DCLIENT_TESTING=ON -DSERVER_TESTING=ON .. &&
+ make -j$(nproc) && ctest --output-on-failure
+ tags:
+ - shared
+ except:
+ - tags
+ artifacts:
+ expire_in: 1 week
+ when: on_failure
+ paths:
+ - obj/
+
+centos9/openssl_3.0.x/x86_64:
+ image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$CENTOS9_BUILD
+ script:
+ - export OPENSSL_ENABLE_SHA1_SIGNATURES=1
+ - mkdir -p obj && cd obj && cmake
+ -DCMAKE_BUILD_TYPE=RelWithDebInfo
+ -DPICKY_DEVELOPER=ON
+ -DWITH_BLOWFISH_CIPHER=ON
+ -DWITH_SFTP=ON -DWITH_SERVER=ON -DWITH_ZLIB=ON -DWITH_PCAP=ON
+ -DUNIT_TESTING=ON -DCLIENT_TESTING=ON -DSERVER_TESTING=ON .. &&
+ make -j$(nproc) && ctest --output-on-failure
+ tags:
+ - shared
+ except:
+ - tags
+ artifacts:
+ expire_in: 1 week
+ when: on_failure
+ paths:
+ - obj/
+
fedora/openssl_1.1.x/x86_64:
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$FEDORA_BUILD
script:
@@ -47,8 +90,8 @@ fedora/openssl_1.1.x/x86_64:
paths:
- obj/
-fedora/openssl_1.1.x/x86_64/fips:
- image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$FEDORA_BUILD
+centos8/openssl_1.1.1/x86_64/fips:
+ image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$CENTOS8_BUILD
script:
- echo 1 > /etc/system-fips
- update-crypto-policies --set FIPS
@@ -469,47 +512,34 @@ tumbleweed/static-analysis:
variables:
ErrorActionPreference: STOP
script:
- - $env:VCPKG_DEFAULT_TRIPLET="x64-windows"
- - mkdir -p obj; if ($?) {cd obj}; if (! $?) {exit 1}
- - cmake
- -A x64
- -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_TOOLCHAIN_FILE"
- -DPICKY_DEVELOPER=ON
- -DWITH_SFTP=ON -DWITH_SERVER=ON -DWITH_ZLIB=ON -DWITH_PCAP=ON
- -DUNIT_TESTING=ON ..
- - cmake --build .
- - ctest --output-on-failure
+ - cmake --build .
+ - ctest --output-on-failure
tags:
- - windows
- - shared-windows
+ - windows
+ - shared-windows
except:
- - tags
+ - tags
artifacts:
expire_in: 1 week
when: on_failure
paths:
- obj/
before_script:
- - choco install --no-progress -y cmake
- - $env:Path += ';C:\Program Files\CMake\bin'
- - If (!(test-path .vcpkg\archives)) { mkdir -p .vcpkg\archives }
- - $env:VCPKG_DEFAULT_BINARY_CACHE="$PWD\.vcpkg\archives"
- - echo $env:VCPKG_DEFAULT_BINARY_CACHE
- - $env:VCPKG_DEFAULT_TRIPLET="$TRIPLET-windows"
- - vcpkg install cmocka
- - vcpkg install openssl
- - vcpkg install zlib
- - vcpkg integrate install
- - mkdir -p obj; if ($?) {cd obj}; if (! $?) {exit 1}
- - cmake
- -A $PLATFORM
- -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake
- -DPICKY_DEVELOPER=ON
- -DWITH_SFTP=ON -DWITH_SERVER=ON -DWITH_ZLIB=ON -DWITH_PCAP=ON
- -DUNIT_TESTING=ON ..
- # The Windows runners are broken for last month
- # https://gitlab.com/gitlab-org/ci-cd/shared-runners/images/gcp/windows-containers/-/issues/40
- allow_failure: true
+ - If (!(test-path .vcpkg\archives)) { mkdir -p .vcpkg\archives }
+ - $env:VCPKG_DEFAULT_BINARY_CACHE="$PWD\.vcpkg\archives"
+ - echo $env:VCPKG_DEFAULT_BINARY_CACHE
+ - $env:VCPKG_DEFAULT_TRIPLET="$TRIPLET-windows"
+ - vcpkg install cmocka
+ - vcpkg install openssl
+ - vcpkg install zlib
+ - vcpkg integrate install
+ - mkdir -p obj; if ($?) {cd obj}; if (! $?) {exit 1}
+ - cmake
+ -A $PLATFORM
+ -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake
+ -DPICKY_DEVELOPER=ON
+ -DWITH_SFTP=ON -DWITH_SERVER=ON -DWITH_ZLIB=ON -DWITH_PCAP=ON
+ -DUNIT_TESTING=ON ..
visualstudio/x86_64:
extends: .vs
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a993012e..3636ebb7 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -10,7 +10,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules")
include(DefineCMakeDefaults)
include(DefineCompilerFlags)
-project(libssh VERSION 0.9.6 LANGUAGES C)
+project(libssh VERSION 0.9.8 LANGUAGES C)
# global needed variable
set(APPLICATION_NAME ${PROJECT_NAME})
@@ -22,7 +22,7 @@ set(APPLICATION_NAME ${PROJECT_NAME})
# Increment AGE. Set REVISION to 0
# If the source code was changed, but there were no interface changes:
# Increment REVISION.
-set(LIBRARY_VERSION "4.8.7")
+set(LIBRARY_VERSION "4.8.9")
set(LIBRARY_SOVERSION "4")
# where to look first for cmake modules, before ${CMAKE_ROOT}/Modules/ is checked
diff --git a/CPackConfig.cmake b/CPackConfig.cmake
index c4a3598a..526bed32 100644
--- a/CPackConfig.cmake
+++ b/CPackConfig.cmake
@@ -10,7 +10,7 @@ set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION})
# SOURCE GENERATOR
set(CPACK_SOURCE_GENERATOR "TXZ")
-set(CPACK_SOURCE_IGNORE_FILES "~$;[.]swp$;/[.]git/;/[.]clangd/;.gitignore;/build*;/obj*;tags;cscope.*;compile_commands.json;.*\.patch")
+set(CPACK_SOURCE_IGNORE_FILES "~$;[.]swp$;/[.]git;/[.]clangd/;/[.]cache/;.gitignore;/build*;/obj*;tags;cscope.*;compile_commands.json;.*\.patch")
set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}")
### NSIS INSTALLER
diff --git a/ChangeLog b/ChangeLog
index 1f3aac98..4090ffcd 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,17 @@
ChangeLog
==========
+version 0.9.8 (released 2023-12-18)
+ * Fix CVE-2023-6004: Command injection using proxycommand
+ * Fix CVE-2023-48795: Potential downgrade attack using strict kex
+ * Fix CVE-2023-6918: Missing checks for return values of MD functions
+ * Allow @ in usernames when parsing from URI composes
+
+version 0.9.7 (released 2023-05-04)
+ * Fix CVE-2023-1667: a NULL dereference during rekeying with algorithm guessing
+ * Fix CVE-2023-2283: a possible authorization bypass in
+ pki_verify_data_signature under low-memory conditions.
+ * Fix several memory leaks in GSSAPI handling code
+ * Build and test related backports
version 0.9.6 (released 2021-08-26)
* CVE-2021-3634: Fix possible heap-buffer overflow when rekeying with
diff --git a/examples/sshnetcat.c b/examples/sshnetcat.c
index ccc72c86..01851d96 100644
--- a/examples/sshnetcat.c
+++ b/examples/sshnetcat.c
@@ -233,9 +233,10 @@ void set_pcap(ssh_session session){
}
void cleanup_pcap(void);
-void cleanup_pcap(){
+void cleanup_pcap(void)
+{
ssh_pcap_file_free(pcap);
- pcap=NULL;
+ pcap = NULL;
}
#endif
diff --git a/include/libssh/config_parser.h b/include/libssh/config_parser.h
index e974917c..ee647bfb 100644
--- a/include/libssh/config_parser.h
+++ b/include/libssh/config_parser.h
@@ -26,6 +26,8 @@
#ifndef CONFIG_PARSER_H_
#define CONFIG_PARSER_H_
+#include <stdbool.h>
+
char *ssh_config_get_cmd(char **str);
char *ssh_config_get_token(char **str);
@@ -45,13 +47,16 @@ int ssh_config_get_yesno(char **str, int notfound);
* be stored or NULL if we do not care about the result.
* @param[out] port Pointer to the location, where the new port will
* be stored or NULL if we do not care about the result.
+ * @param[in] ignore_port Set to true if the we should not attempt to parse
+ * port number.
*
* @returns SSH_OK if the provided string is in format of SSH URI,
* SSH_ERROR on failure
*/
int ssh_config_parse_uri(const char *tok,
- char **username,
- char **hostname,
- char **port);
+ char **username,
+ char **hostname,
+ char **port,
+ bool ignore_port);
#endif /* LIBSSH_CONFIG_H_ */
diff --git a/include/libssh/curve25519.h b/include/libssh/curve25519.h
index f0cc6348..77e6c313 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 4fc23d82..7a91d7d4 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 390b30da..704888cf 100644
--- a/include/libssh/dh.h
+++ b/include/libssh/dh.h
@@ -63,8 +63,10 @@ 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);
#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 17fe02e7..c1f03a9c 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/include/libssh/kex.h b/include/libssh/kex.h
index 3a1f4a6f..40da4ef2 100644
--- a/include/libssh/kex.h
+++ b/include/libssh/kex.h
@@ -33,9 +33,10 @@ 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_append_extensions(ssh_session session, struct ssh_kex_struct *pkex);
int ssh_kex_select_methods(ssh_session session);
int ssh_verify_existing_algo(enum ssh_kex_types_e algo, const char *name);
char *ssh_keep_known_algos(enum ssh_kex_types_e algo, const char *list);
diff --git a/include/libssh/libcrypto.h b/include/libssh/libcrypto.h
index 4117942c..35b277c5 100644
--- a/include/libssh/libcrypto.h
+++ b/include/libssh/libcrypto.h
@@ -39,11 +39,6 @@ typedef EVP_MD_CTX* SHA384CTX;
typedef EVP_MD_CTX* SHA512CTX;
typedef EVP_MD_CTX* MD5CTX;
typedef HMAC_CTX* HMACCTX;
-#ifdef HAVE_ECC
-typedef EVP_MD_CTX *EVPCTX;
-#else
-typedef void *EVPCTX;
-#endif
#define SHA_DIGEST_LEN SHA_DIGEST_LENGTH
#define SHA256_DIGEST_LEN SHA256_DIGEST_LENGTH
diff --git a/include/libssh/libgcrypt.h b/include/libssh/libgcrypt.h
index 347d851b..3a803fa4 100644
--- a/include/libssh/libgcrypt.h
+++ b/include/libssh/libgcrypt.h
@@ -32,7 +32,6 @@ typedef gcry_md_hd_t SHA384CTX;
typedef gcry_md_hd_t SHA512CTX;
typedef gcry_md_hd_t MD5CTX;
typedef gcry_md_hd_t HMACCTX;
-typedef gcry_md_hd_t EVPCTX;
#define SHA_DIGEST_LENGTH 20
#define SHA_DIGEST_LEN SHA_DIGEST_LENGTH
#define MD5_DIGEST_LEN 16
diff --git a/include/libssh/libmbedcrypto.h b/include/libssh/libmbedcrypto.h
index fe53019b..b6e3e2a3 100644
--- a/include/libssh/libmbedcrypto.h
+++ b/include/libssh/libmbedcrypto.h
@@ -41,7 +41,6 @@ typedef mbedtls_md_context_t *SHA384CTX;
typedef mbedtls_md_context_t *SHA512CTX;
typedef mbedtls_md_context_t *MD5CTX;
typedef mbedtls_md_context_t *HMACCTX;
-typedef mbedtls_md_context_t *EVPCTX;
#define SHA_DIGEST_LENGTH 20
#define SHA_DIGEST_LEN SHA_DIGEST_LENGTH
diff --git a/include/libssh/misc.h b/include/libssh/misc.h
index 3cc3b113..a5bee930 100644
--- a/include/libssh/misc.h
+++ b/include/libssh/misc.h
@@ -97,4 +97,6 @@ int ssh_mkdirs(const char *pathname, mode_t mode);
int ssh_quote_file_name(const char *file_name, char *buf, size_t buf_len);
int ssh_newline_vis(const char *string, char *buf, size_t buf_len);
+int ssh_check_hostname_syntax(const char *hostname);
+
#endif /* MISC_H_ */
diff --git a/include/libssh/packet.h b/include/libssh/packet.h
index 561bba8e..c6fbc3fc 100644
--- a/include/libssh/packet.h
+++ b/include/libssh/packet.h
@@ -63,6 +63,7 @@ SSH_PACKET_CALLBACK(ssh_packet_ext_info);
SSH_PACKET_CALLBACK(ssh_packet_kexdh_init);
#endif
+int ssh_packet_send_newkeys(ssh_session session);
int ssh_packet_send_unimplemented(ssh_session session, uint32_t seqnum);
int ssh_packet_parse_type(ssh_session session);
//int packet_flush(ssh_session session, int enforce_blocking);
diff --git a/include/libssh/session.h b/include/libssh/session.h
index 22256150..3cde0dd4 100644
--- a/include/libssh/session.h
+++ b/include/libssh/session.h
@@ -75,6 +75,17 @@ enum ssh_pending_call_e {
/* Client successfully authenticated */
#define SSH_SESSION_FLAG_AUTHENTICATED 2
+/* The KEXINIT message can be sent first by either of the parties so this flag
+ * indicates that the message was already sent to make sure it is sent and avoid
+ * sending it twice during key exchange to simplify the state machine. */
+#define SSH_SESSION_FLAG_KEXINIT_SENT 4
+
+/* The current SSH2 session implements the "strict KEX" feature and should behave
+ * differently on SSH2_MSG_NEWKEYS. */
+#define SSH_SESSION_FLAG_KEX_STRICT 0x0010
+/* Unexpected packets have been sent while the session was still unencrypted */
+#define SSH_SESSION_FLAG_KEX_TAINTED 0x0020
+
/* codes to use with ssh_handle_packets*() */
/* Infinite timeout */
#define SSH_TIMEOUT_INFINITE -1
@@ -131,10 +142,8 @@ struct ssh_session_struct {
/* Extensions negotiated using RFC 8308 */
uint32_t extensions;
- ssh_string banner; /* that's the issue banner from
- the server */
- char *discon_msg; /* disconnect message from
- the remote host */
+ ssh_string banner; /* that's the issue banner from the server */
+ char *discon_msg; /* disconnect message from the remote host */
ssh_buffer in_buffer;
PACKET in_packet;
ssh_buffer out_buffer;
@@ -158,25 +167,33 @@ 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;
struct ssh_crypto_struct *current_crypto;
- struct ssh_crypto_struct *next_crypto; /* next_crypto is going to be used after a SSH2_MSG_NEWKEYS */
+ /* next_crypto is going to be used after a SSH2_MSG_NEWKEYS */
+ struct ssh_crypto_struct *next_crypto;
struct ssh_list *channels; /* linked list of channels */
int maxchannel;
ssh_agent agent; /* ssh agent */
-/* keyb interactive data */
+ /* keyboard interactive data */
struct ssh_kbdint_struct *kbdint;
struct ssh_gssapi_struct *gssapi;
@@ -193,7 +210,8 @@ struct ssh_session_struct {
/* auths accepted by server */
struct ssh_list *ssh_message_list; /* list of delayed SSH messages */
- int (*ssh_message_callback)( struct ssh_session_struct *session, ssh_message msg, void *userdata);
+ int (*ssh_message_callback)(struct ssh_session_struct *session,
+ ssh_message msg, void *userdata);
void *ssh_message_callback_data;
ssh_server_callbacks server_callbacks;
void (*ssh_connection_callback)( struct ssh_session_struct *session);
@@ -219,7 +237,7 @@ struct ssh_session_struct {
char *custombanner;
unsigned long timeout; /* seconds */
unsigned long timeout_usec;
- unsigned int port;
+ uint16_t port;
socket_t fd;
int StrictHostKeyChecking;
char compressionlevel;
diff --git a/include/libssh/wrapper.h b/include/libssh/wrapper.h
index ba64939b..e6384b50 100644
--- a/include/libssh/wrapper.h
+++ b/include/libssh/wrapper.h
@@ -67,37 +67,38 @@ struct ssh_crypto_struct;
typedef struct ssh_mac_ctx_struct *ssh_mac_ctx;
MD5CTX md5_init(void);
-void md5_update(MD5CTX c, const void *data, unsigned long len);
-void md5_final(unsigned char *md,MD5CTX c);
+void md5_ctx_free(MD5CTX);
+int md5_update(MD5CTX c, const void *data, unsigned long len);
+int md5_final(unsigned char *md,MD5CTX c);
SHACTX sha1_init(void);
-void sha1_update(SHACTX c, const void *data, unsigned long len);
-void sha1_final(unsigned char *md,SHACTX c);
-void sha1(const unsigned char *digest,int len,unsigned char *hash);
+void sha1_ctx_free(SHACTX);
+int sha1_update(SHACTX c, const void *data, unsigned long len);
+int sha1_final(unsigned char *md,SHACTX c);
+int sha1(const unsigned char *digest,int len,unsigned char *hash);
SHA256CTX sha256_init(void);
-void sha256_update(SHA256CTX c, const void *data, unsigned long len);
-void sha256_final(unsigned char *md,SHA256CTX c);
-void sha256(const unsigned char *digest, int len, unsigned char *hash);
+void sha256_ctx_free(SHA256CTX);
+int sha256_update(SHA256CTX c, const void *data, unsigned long len);
+int sha256_final(unsigned char *md,SHA256CTX c);
+int sha256(const unsigned char *digest, int len, unsigned char *hash);
SHA384CTX sha384_init(void);
-void sha384_update(SHA384CTX c, const void *data, unsigned long len);
-void sha384_final(unsigned char *md,SHA384CTX c);
-void sha384(const unsigned char *digest, int len, unsigned char *hash);
+void sha384_ctx_free(SHA384CTX);
+int sha384_update(SHA384CTX c, const void *data, unsigned long len);
+int sha384_final(unsigned char *md,SHA384CTX c);
+int sha384(const unsigned char *digest, int len, unsigned char *hash);
SHA512CTX sha512_init(void);
-void sha512_update(SHA512CTX c, const void *data, unsigned long len);
-void sha512_final(unsigned char *md,SHA512CTX c);
-void sha512(const unsigned char *digest, int len, unsigned char *hash);
-
-void evp(int nid, unsigned char *digest, int len, unsigned char *hash, unsigned int *hlen);
-EVPCTX evp_init(int nid);
-void evp_update(EVPCTX ctx, const void *data, unsigned long len);
-void evp_final(EVPCTX ctx, unsigned char *md, unsigned int *mdlen);
+void sha512_ctx_free(SHA512CTX);
+int sha512_update(SHA512CTX c, const void *data, unsigned long len);
+int sha512_final(unsigned char *md,SHA512CTX c);
+int sha512(const unsigned char *digest, int len, unsigned char *hash);
HMACCTX hmac_init(const void *key,int len, enum ssh_hmac_e type);
void hmac_update(HMACCTX c, const void *data, unsigned long len);
void hmac_final(HMACCTX ctx,unsigned char *hashmacbuf,unsigned int *len);
+
size_t hmac_digest_len(enum ssh_hmac_e type);
int ssh_kdf(struct ssh_crypto_struct *crypto,
diff --git a/src/ABI/current b/src/ABI/current
index 4850fb30..84da2709 100644
--- a/src/ABI/current
+++ b/src/ABI/current
@@ -1 +1 @@
-4.8.7 \ No newline at end of file
+4.8.9 \ No newline at end of file
diff --git a/src/ABI/libssh-4.8.8.symbols b/src/ABI/libssh-4.8.8.symbols
new file mode 100644
index 00000000..dce4addd
--- /dev/null
+++ b/src/ABI/libssh-4.8.8.symbols
@@ -0,0 +1,421 @@
+_ssh_log
+buffer_free
+buffer_get
+buffer_get_len
+buffer_new
+channel_accept_x11
+channel_change_pty_size
+channel_close
+channel_forward_accept
+channel_forward_cancel
+channel_forward_listen
+channel_free
+channel_get_exit_status
+channel_get_session
+channel_is_closed
+channel_is_eof
+channel_is_open
+channel_new
+channel_open_forward
+channel_open_session
+channel_poll
+channel_read
+channel_read_buffer
+channel_read_nonblocking
+channel_request_env
+channel_request_exec
+channel_request_pty
+channel_request_pty_size
+channel_request_send_signal
+channel_request_sftp
+channel_request_shell
+channel_request_subsystem
+channel_request_x11
+channel_select
+channel_send_eof
+channel_set_blocking
+channel_write
+channel_write_stderr
+privatekey_free
+privatekey_from_file
+publickey_free
+publickey_from_file
+publickey_from_privatekey
+publickey_to_string
+sftp_async_read
+sftp_async_read_begin
+sftp_attributes_free
+sftp_canonicalize_path
+sftp_chmod
+sftp_chown
+sftp_client_message_free
+sftp_client_message_get_data
+sftp_client_message_get_filename
+sftp_client_message_get_flags
+sftp_client_message_get_submessage
+sftp_client_message_get_type
+sftp_client_message_set_filename
+sftp_close
+sftp_closedir
+sftp_dir_eof
+sftp_extension_supported
+sftp_extensions_get_count
+sftp_extensions_get_data
+sftp_extensions_get_name
+sftp_file_set_blocking
+sftp_file_set_nonblocking
+sftp_free
+sftp_fstat
+sftp_fstatvfs
+sftp_fsync
+sftp_get_client_message
+sftp_get_error
+sftp_handle
+sftp_handle_alloc
+sftp_handle_remove
+sftp_init
+sftp_lstat
+sftp_mkdir
+sftp_new
+sftp_new_channel
+sftp_open
+sftp_opendir
+sftp_read
+sftp_readdir
+sftp_readlink
+sftp_rename
+sftp_reply_attr
+sftp_reply_data
+sftp_reply_handle
+sftp_reply_name
+sftp_reply_names
+sftp_reply_names_add
+sftp_reply_status
+sftp_rewind
+sftp_rmdir
+sftp_seek
+sftp_seek64
+sftp_send_client_message
+sftp_server_free
+sftp_server_init
+sftp_server_new
+sftp_server_version
+sftp_setstat
+sftp_stat
+sftp_statvfs
+sftp_statvfs_free
+sftp_symlink
+sftp_tell
+sftp_tell64
+sftp_unlink
+sftp_utimes
+sftp_write
+ssh_accept
+ssh_add_channel_callbacks
+ssh_auth_list
+ssh_basename
+ssh_bind_accept
+ssh_bind_accept_fd
+ssh_bind_fd_toaccept
+ssh_bind_free
+ssh_bind_get_fd
+ssh_bind_listen
+ssh_bind_new
+ssh_bind_options_parse_config
+ssh_bind_options_set
+ssh_bind_set_blocking
+ssh_bind_set_callbacks
+ssh_bind_set_fd
+ssh_blocking_flush
+ssh_buffer_add_data
+ssh_buffer_free
+ssh_buffer_get
+ssh_buffer_get_data
+ssh_buffer_get_len
+ssh_buffer_new
+ssh_buffer_reinit
+ssh_channel_accept_forward
+ssh_channel_accept_x11
+ssh_channel_cancel_forward
+ssh_channel_change_pty_size
+ssh_channel_close
+ssh_channel_free
+ssh_channel_get_exit_status
+ssh_channel_get_session
+ssh_channel_is_closed
+ssh_channel_is_eof
+ssh_channel_is_open
+ssh_channel_listen_forward
+ssh_channel_new
+ssh_channel_open_auth_agent
+ssh_channel_open_forward
+ssh_channel_open_forward_unix
+ssh_channel_open_reverse_forward
+ssh_channel_open_session
+ssh_channel_open_x11
+ssh_channel_poll
+ssh_channel_poll_timeout
+ssh_channel_read
+ssh_channel_read_nonblocking
+ssh_channel_read_timeout
+ssh_channel_request_auth_agent
+ssh_channel_request_env
+ssh_channel_request_exec
+ssh_channel_request_pty
+ssh_channel_request_pty_size
+ssh_channel_request_send_break
+ssh_channel_request_send_exit_signal
+ssh_channel_request_send_exit_status
+ssh_channel_request_send_signal
+ssh_channel_request_sftp
+ssh_channel_request_shell
+ssh_channel_request_subsystem
+ssh_channel_request_x11
+ssh_channel_select
+ssh_channel_send_eof
+ssh_channel_set_blocking
+ssh_channel_set_counter
+ssh_channel_window_size
+ssh_channel_write
+ssh_channel_write_stderr
+ssh_clean_pubkey_hash
+ssh_connect
+ssh_connector_free
+ssh_connector_new
+ssh_connector_set_in_channel
+ssh_connector_set_in_fd
+ssh_connector_set_out_channel
+ssh_connector_set_out_fd
+ssh_copyright
+ssh_dirname
+ssh_disconnect
+ssh_dump_knownhost
+ssh_event_add_connector
+ssh_event_add_fd
+ssh_event_add_session
+ssh_event_dopoll
+ssh_event_free
+ssh_event_new
+ssh_event_remove_connector
+ssh_event_remove_fd
+ssh_event_remove_session
+ssh_execute_message_callbacks
+ssh_finalize
+ssh_forward_accept
+ssh_forward_cancel
+ssh_forward_listen
+ssh_free
+ssh_get_cipher_in
+ssh_get_cipher_out
+ssh_get_clientbanner
+ssh_get_disconnect_message
+ssh_get_error
+ssh_get_error_code
+ssh_get_fd
+ssh_get_fingerprint_hash
+ssh_get_hexa
+ssh_get_hmac_in
+ssh_get_hmac_out
+ssh_get_issue_banner
+ssh_get_kex_algo
+ssh_get_log_callback
+ssh_get_log_level
+ssh_get_log_userdata
+ssh_get_openssh_version
+ssh_get_poll_flags
+ssh_get_pubkey
+ssh_get_pubkey_hash
+ssh_get_publickey
+ssh_get_publickey_hash
+ssh_get_random
+ssh_get_server_publickey
+ssh_get_serverbanner
+ssh_get_status
+ssh_get_version
+ssh_getpass
+ssh_gssapi_get_creds
+ssh_gssapi_set_creds
+ssh_handle_key_exchange
+ssh_init
+ssh_is_blocking
+ssh_is_connected
+ssh_is_server_known
+ssh_key_cmp
+ssh_key_free
+ssh_key_is_private
+ssh_key_is_public
+ssh_key_new
+ssh_key_type
+ssh_key_type_from_name
+ssh_key_type_to_char
+ssh_known_hosts_parse_line
+ssh_knownhosts_entry_free
+ssh_log
+ssh_message_auth_interactive_request
+ssh_message_auth_kbdint_is_response
+ssh_message_auth_password
+ssh_message_auth_pubkey
+ssh_message_auth_publickey
+ssh_message_auth_publickey_state
+ssh_message_auth_reply_pk_ok
+ssh_message_auth_reply_pk_ok_simple
+ssh_message_auth_reply_success
+ssh_message_auth_set_methods
+ssh_message_auth_user
+ssh_message_channel_request_channel
+ssh_message_channel_request_command
+ssh_message_channel_request_env_name
+ssh_message_channel_request_env_value
+ssh_message_channel_request_open_destination
+ssh_message_channel_request_open_destination_port
+ssh_message_channel_request_open_originator
+ssh_message_channel_request_open_originator_port
+ssh_message_channel_request_open_reply_accept
+ssh_message_channel_request_open_reply_accept_channel
+ssh_message_channel_request_pty_height
+ssh_message_channel_request_pty_pxheight
+ssh_message_channel_request_pty_pxwidth
+ssh_message_channel_request_pty_term
+ssh_message_channel_request_pty_width
+ssh_message_channel_request_reply_success
+ssh_message_channel_request_subsystem
+ssh_message_channel_request_x11_auth_cookie
+ssh_message_channel_request_x11_auth_protocol
+ssh_message_channel_request_x11_screen_number
+ssh_message_channel_request_x11_single_connection
+ssh_message_free
+ssh_message_get
+ssh_message_global_request_address
+ssh_message_global_request_port
+ssh_message_global_request_reply_success
+ssh_message_reply_default
+ssh_message_retrieve
+ssh_message_service_reply_success
+ssh_message_service_service
+ssh_message_subtype
+ssh_message_type
+ssh_mkdir
+ssh_new
+ssh_options_copy
+ssh_options_get
+ssh_options_get_port
+ssh_options_getopt
+ssh_options_parse_config
+ssh_options_set
+ssh_pcap_file_close
+ssh_pcap_file_free
+ssh_pcap_file_new
+ssh_pcap_file_open
+ssh_pki_copy_cert_to_privkey
+ssh_pki_export_privkey_base64
+ssh_pki_export_privkey_file
+ssh_pki_export_privkey_to_pubkey
+ssh_pki_export_pubkey_base64
+ssh_pki_export_pubkey_file
+ssh_pki_generate
+ssh_pki_import_cert_base64
+ssh_pki_import_cert_file
+ssh_pki_import_privkey_base64
+ssh_pki_import_privkey_file
+ssh_pki_import_pubkey_base64
+ssh_pki_import_pubkey_file
+ssh_pki_key_ecdsa_name
+ssh_print_hash
+ssh_print_hexa
+ssh_privatekey_type
+ssh_publickey_to_file
+ssh_remove_channel_callbacks
+ssh_scp_accept_request
+ssh_scp_close
+ssh_scp_deny_request
+ssh_scp_free
+ssh_scp_init
+ssh_scp_leave_directory
+ssh_scp_new
+ssh_scp_pull_request
+ssh_scp_push_directory
+ssh_scp_push_file
+ssh_scp_push_file64
+ssh_scp_read
+ssh_scp_request_get_filename
+ssh_scp_request_get_permissions
+ssh_scp_request_get_size
+ssh_scp_request_get_size64
+ssh_scp_request_get_warning
+ssh_scp_write
+ssh_select
+ssh_send_debug
+ssh_send_ignore
+ssh_send_keepalive
+ssh_server_init_kex
+ssh_service_request
+ssh_session_export_known_hosts_entry
+ssh_session_get_known_hosts_entry
+ssh_session_has_known_hosts_entry
+ssh_session_is_known_server
+ssh_session_update_known_hosts
+ssh_set_agent_channel
+ssh_set_agent_socket
+ssh_set_auth_methods
+ssh_set_blocking
+ssh_set_callbacks
+ssh_set_channel_callbacks
+ssh_set_counters
+ssh_set_fd_except
+ssh_set_fd_toread
+ssh_set_fd_towrite
+ssh_set_log_callback
+ssh_set_log_level
+ssh_set_log_userdata
+ssh_set_message_callback
+ssh_set_pcap_file
+ssh_set_server_callbacks
+ssh_silent_disconnect
+ssh_string_burn
+ssh_string_copy
+ssh_string_data
+ssh_string_fill
+ssh_string_free
+ssh_string_free_char
+ssh_string_from_char
+ssh_string_get_char
+ssh_string_len
+ssh_string_new
+ssh_string_to_char
+ssh_threads_get_default
+ssh_threads_get_noop
+ssh_threads_get_pthread
+ssh_threads_set_callbacks
+ssh_try_publickey_from_file
+ssh_userauth_agent
+ssh_userauth_agent_pubkey
+ssh_userauth_autopubkey
+ssh_userauth_gssapi
+ssh_userauth_kbdint
+ssh_userauth_kbdint_getanswer
+ssh_userauth_kbdint_getinstruction
+ssh_userauth_kbdint_getname
+ssh_userauth_kbdint_getnanswers
+ssh_userauth_kbdint_getnprompts
+ssh_userauth_kbdint_getprompt
+ssh_userauth_kbdint_setanswer
+ssh_userauth_list
+ssh_userauth_none
+ssh_userauth_offer_pubkey
+ssh_userauth_password
+ssh_userauth_privatekey_file
+ssh_userauth_pubkey
+ssh_userauth_publickey
+ssh_userauth_publickey_auto
+ssh_userauth_try_publickey
+ssh_version
+ssh_write_knownhost
+string_burn
+string_copy
+string_data
+string_fill
+string_free
+string_from_char
+string_len
+string_new
+string_to_char \ No newline at end of file
diff --git a/src/ABI/libssh-4.8.9.symbols b/src/ABI/libssh-4.8.9.symbols
new file mode 100644
index 00000000..dce4addd
--- /dev/null
+++ b/src/ABI/libssh-4.8.9.symbols
@@ -0,0 +1,421 @@
+_ssh_log
+buffer_free
+buffer_get
+buffer_get_len
+buffer_new
+channel_accept_x11
+channel_change_pty_size
+channel_close
+channel_forward_accept
+channel_forward_cancel
+channel_forward_listen
+channel_free
+channel_get_exit_status
+channel_get_session
+channel_is_closed
+channel_is_eof
+channel_is_open
+channel_new
+channel_open_forward
+channel_open_session
+channel_poll
+channel_read
+channel_read_buffer
+channel_read_nonblocking
+channel_request_env
+channel_request_exec
+channel_request_pty
+channel_request_pty_size
+channel_request_send_signal
+channel_request_sftp
+channel_request_shell
+channel_request_subsystem
+channel_request_x11
+channel_select
+channel_send_eof
+channel_set_blocking
+channel_write
+channel_write_stderr
+privatekey_free
+privatekey_from_file
+publickey_free
+publickey_from_file
+publickey_from_privatekey
+publickey_to_string
+sftp_async_read
+sftp_async_read_begin
+sftp_attributes_free
+sftp_canonicalize_path
+sftp_chmod
+sftp_chown
+sftp_client_message_free
+sftp_client_message_get_data
+sftp_client_message_get_filename
+sftp_client_message_get_flags
+sftp_client_message_get_submessage
+sftp_client_message_get_type
+sftp_client_message_set_filename
+sftp_close
+sftp_closedir
+sftp_dir_eof
+sftp_extension_supported
+sftp_extensions_get_count
+sftp_extensions_get_data
+sftp_extensions_get_name
+sftp_file_set_blocking
+sftp_file_set_nonblocking
+sftp_free
+sftp_fstat
+sftp_fstatvfs
+sftp_fsync
+sftp_get_client_message
+sftp_get_error
+sftp_handle
+sftp_handle_alloc
+sftp_handle_remove
+sftp_init
+sftp_lstat
+sftp_mkdir
+sftp_new
+sftp_new_channel
+sftp_open
+sftp_opendir
+sftp_read
+sftp_readdir
+sftp_readlink
+sftp_rename
+sftp_reply_attr
+sftp_reply_data
+sftp_reply_handle
+sftp_reply_name
+sftp_reply_names
+sftp_reply_names_add
+sftp_reply_status
+sftp_rewind
+sftp_rmdir
+sftp_seek
+sftp_seek64
+sftp_send_client_message
+sftp_server_free
+sftp_server_init
+sftp_server_new
+sftp_server_version
+sftp_setstat
+sftp_stat
+sftp_statvfs
+sftp_statvfs_free
+sftp_symlink
+sftp_tell
+sftp_tell64
+sftp_unlink
+sftp_utimes
+sftp_write
+ssh_accept
+ssh_add_channel_callbacks
+ssh_auth_list
+ssh_basename
+ssh_bind_accept
+ssh_bind_accept_fd
+ssh_bind_fd_toaccept
+ssh_bind_free
+ssh_bind_get_fd
+ssh_bind_listen
+ssh_bind_new
+ssh_bind_options_parse_config
+ssh_bind_options_set
+ssh_bind_set_blocking
+ssh_bind_set_callbacks
+ssh_bind_set_fd
+ssh_blocking_flush
+ssh_buffer_add_data
+ssh_buffer_free
+ssh_buffer_get
+ssh_buffer_get_data
+ssh_buffer_get_len
+ssh_buffer_new
+ssh_buffer_reinit
+ssh_channel_accept_forward
+ssh_channel_accept_x11
+ssh_channel_cancel_forward
+ssh_channel_change_pty_size
+ssh_channel_close
+ssh_channel_free
+ssh_channel_get_exit_status
+ssh_channel_get_session
+ssh_channel_is_closed
+ssh_channel_is_eof
+ssh_channel_is_open
+ssh_channel_listen_forward
+ssh_channel_new
+ssh_channel_open_auth_agent
+ssh_channel_open_forward
+ssh_channel_open_forward_unix
+ssh_channel_open_reverse_forward
+ssh_channel_open_session
+ssh_channel_open_x11
+ssh_channel_poll
+ssh_channel_poll_timeout
+ssh_channel_read
+ssh_channel_read_nonblocking
+ssh_channel_read_timeout
+ssh_channel_request_auth_agent
+ssh_channel_request_env
+ssh_channel_request_exec
+ssh_channel_request_pty
+ssh_channel_request_pty_size
+ssh_channel_request_send_break
+ssh_channel_request_send_exit_signal
+ssh_channel_request_send_exit_status
+ssh_channel_request_send_signal
+ssh_channel_request_sftp
+ssh_channel_request_shell
+ssh_channel_request_subsystem
+ssh_channel_request_x11
+ssh_channel_select
+ssh_channel_send_eof
+ssh_channel_set_blocking
+ssh_channel_set_counter
+ssh_channel_window_size
+ssh_channel_write
+ssh_channel_write_stderr
+ssh_clean_pubkey_hash
+ssh_connect
+ssh_connector_free
+ssh_connector_new
+ssh_connector_set_in_channel
+ssh_connector_set_in_fd
+ssh_connector_set_out_channel
+ssh_connector_set_out_fd
+ssh_copyright
+ssh_dirname
+ssh_disconnect
+ssh_dump_knownhost
+ssh_event_add_connector
+ssh_event_add_fd
+ssh_event_add_session
+ssh_event_dopoll
+ssh_event_free
+ssh_event_new
+ssh_event_remove_connector
+ssh_event_remove_fd
+ssh_event_remove_session
+ssh_execute_message_callbacks
+ssh_finalize
+ssh_forward_accept
+ssh_forward_cancel
+ssh_forward_listen
+ssh_free
+ssh_get_cipher_in
+ssh_get_cipher_out
+ssh_get_clientbanner
+ssh_get_disconnect_message
+ssh_get_error
+ssh_get_error_code
+ssh_get_fd
+ssh_get_fingerprint_hash
+ssh_get_hexa
+ssh_get_hmac_in
+ssh_get_hmac_out
+ssh_get_issue_banner
+ssh_get_kex_algo
+ssh_get_log_callback
+ssh_get_log_level
+ssh_get_log_userdata
+ssh_get_openssh_version
+ssh_get_poll_flags
+ssh_get_pubkey
+ssh_get_pubkey_hash
+ssh_get_publickey
+ssh_get_publickey_hash
+ssh_get_random
+ssh_get_server_publickey
+ssh_get_serverbanner
+ssh_get_status
+ssh_get_version
+ssh_getpass
+ssh_gssapi_get_creds
+ssh_gssapi_set_creds
+ssh_handle_key_exchange
+ssh_init
+ssh_is_blocking
+ssh_is_connected
+ssh_is_server_known
+ssh_key_cmp
+ssh_key_free
+ssh_key_is_private
+ssh_key_is_public
+ssh_key_new
+ssh_key_type
+ssh_key_type_from_name
+ssh_key_type_to_char
+ssh_known_hosts_parse_line
+ssh_knownhosts_entry_free
+ssh_log
+ssh_message_auth_interactive_request
+ssh_message_auth_kbdint_is_response
+ssh_message_auth_password
+ssh_message_auth_pubkey
+ssh_message_auth_publickey
+ssh_message_auth_publickey_state
+ssh_message_auth_reply_pk_ok
+ssh_message_auth_reply_pk_ok_simple
+ssh_message_auth_reply_success
+ssh_message_auth_set_methods
+ssh_message_auth_user
+ssh_message_channel_request_channel
+ssh_message_channel_request_command
+ssh_message_channel_request_env_name
+ssh_message_channel_request_env_value
+ssh_message_channel_request_open_destination
+ssh_message_channel_request_open_destination_port
+ssh_message_channel_request_open_originator
+ssh_message_channel_request_open_originator_port
+ssh_message_channel_request_open_reply_accept
+ssh_message_channel_request_open_reply_accept_channel
+ssh_message_channel_request_pty_height
+ssh_message_channel_request_pty_pxheight
+ssh_message_channel_request_pty_pxwidth
+ssh_message_channel_request_pty_term
+ssh_message_channel_request_pty_width
+ssh_message_channel_request_reply_success
+ssh_message_channel_request_subsystem
+ssh_message_channel_request_x11_auth_cookie
+ssh_message_channel_request_x11_auth_protocol
+ssh_message_channel_request_x11_screen_number
+ssh_message_channel_request_x11_single_connection
+ssh_message_free
+ssh_message_get
+ssh_message_global_request_address
+ssh_message_global_request_port
+ssh_message_global_request_reply_success
+ssh_message_reply_default
+ssh_message_retrieve
+ssh_message_service_reply_success
+ssh_message_service_service
+ssh_message_subtype
+ssh_message_type
+ssh_mkdir
+ssh_new
+ssh_options_copy
+ssh_options_get
+ssh_options_get_port
+ssh_options_getopt
+ssh_options_parse_config
+ssh_options_set
+ssh_pcap_file_close
+ssh_pcap_file_free
+ssh_pcap_file_new
+ssh_pcap_file_open
+ssh_pki_copy_cert_to_privkey
+ssh_pki_export_privkey_base64
+ssh_pki_export_privkey_file
+ssh_pki_export_privkey_to_pubkey
+ssh_pki_export_pubkey_base64
+ssh_pki_export_pubkey_file
+ssh_pki_generate
+ssh_pki_import_cert_base64
+ssh_pki_import_cert_file
+ssh_pki_import_privkey_base64
+ssh_pki_import_privkey_file
+ssh_pki_import_pubkey_base64
+ssh_pki_import_pubkey_file
+ssh_pki_key_ecdsa_name
+ssh_print_hash
+ssh_print_hexa
+ssh_privatekey_type
+ssh_publickey_to_file
+ssh_remove_channel_callbacks
+ssh_scp_accept_request
+ssh_scp_close
+ssh_scp_deny_request
+ssh_scp_free
+ssh_scp_init
+ssh_scp_leave_directory
+ssh_scp_new
+ssh_scp_pull_request
+ssh_scp_push_directory
+ssh_scp_push_file
+ssh_scp_push_file64
+ssh_scp_read
+ssh_scp_request_get_filename
+ssh_scp_request_get_permissions
+ssh_scp_request_get_size
+ssh_scp_request_get_size64
+ssh_scp_request_get_warning
+ssh_scp_write
+ssh_select
+ssh_send_debug
+ssh_send_ignore
+ssh_send_keepalive
+ssh_server_init_kex
+ssh_service_request
+ssh_session_export_known_hosts_entry
+ssh_session_get_known_hosts_entry
+ssh_session_has_known_hosts_entry
+ssh_session_is_known_server
+ssh_session_update_known_hosts
+ssh_set_agent_channel
+ssh_set_agent_socket
+ssh_set_auth_methods
+ssh_set_blocking
+ssh_set_callbacks
+ssh_set_channel_callbacks
+ssh_set_counters
+ssh_set_fd_except
+ssh_set_fd_toread
+ssh_set_fd_towrite
+ssh_set_log_callback
+ssh_set_log_level
+ssh_set_log_userdata
+ssh_set_message_callback
+ssh_set_pcap_file
+ssh_set_server_callbacks
+ssh_silent_disconnect
+ssh_string_burn
+ssh_string_copy
+ssh_string_data
+ssh_string_fill
+ssh_string_free
+ssh_string_free_char
+ssh_string_from_char
+ssh_string_get_char
+ssh_string_len
+ssh_string_new
+ssh_string_to_char
+ssh_threads_get_default
+ssh_threads_get_noop
+ssh_threads_get_pthread
+ssh_threads_set_callbacks
+ssh_try_publickey_from_file
+ssh_userauth_agent
+ssh_userauth_agent_pubkey
+ssh_userauth_autopubkey
+ssh_userauth_gssapi
+ssh_userauth_kbdint
+ssh_userauth_kbdint_getanswer
+ssh_userauth_kbdint_getinstruction
+ssh_userauth_kbdint_getname
+ssh_userauth_kbdint_getnanswers
+ssh_userauth_kbdint_getnprompts
+ssh_userauth_kbdint_getprompt
+ssh_userauth_kbdint_setanswer
+ssh_userauth_list
+ssh_userauth_none
+ssh_userauth_offer_pubkey
+ssh_userauth_password
+ssh_userauth_privatekey_file
+ssh_userauth_pubkey
+ssh_userauth_publickey
+ssh_userauth_publickey_auto
+ssh_userauth_try_publickey
+ssh_version
+ssh_write_knownhost
+string_burn
+string_copy
+string_data
+string_fill
+string_free
+string_from_char
+string_len
+string_new
+string_to_char \ No newline at end of file
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index a576cf71..fc401793 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -9,13 +9,6 @@ set(LIBSSH_LINK_LIBRARIES
${LIBSSH_REQUIRED_LIBRARIES}
)
-if (WIN32)
- set(LIBSSH_LINK_LIBRARIES
- ${LIBSSH_LINK_LIBRARIES}
- ws2_32
- )
-endif (WIN32)
-
if (OPENSSL_CRYPTO_LIBRARIES)
set(LIBSSH_PRIVATE_INCLUDE_DIRS
${LIBSSH_PRIVATE_INCLUDE_DIRS}
@@ -93,6 +86,16 @@ if (MINGW AND Threads_FOUND)
)
endif()
+# This needs to be last for mingw to build
+# https://gitlab.com/libssh/libssh-mirror/-/issues/84
+if (WIN32)
+ set(LIBSSH_LINK_LIBRARIES
+ ${LIBSSH_LINK_LIBRARIES}
+ iphlpapi
+ ws2_32
+ )
+endif (WIN32)
+
if (BUILD_STATIC_LIB)
set(LIBSSH_STATIC_LIBRARY
ssh_static
diff --git a/src/channels.c b/src/channels.c
index 1041dbff..69dd25ca 100644
--- a/src/channels.c
+++ b/src/channels.c
@@ -3403,9 +3403,15 @@ int ssh_channel_select(ssh_channel *readchans, ssh_channel *writechans,
firstround=0;
} while(1);
- memcpy(readchans, rchans, (count_ptrs(rchans) + 1) * sizeof(ssh_channel ));
- memcpy(writechans, wchans, (count_ptrs(wchans) + 1) * sizeof(ssh_channel ));
- memcpy(exceptchans, echans, (count_ptrs(echans) + 1) * sizeof(ssh_channel ));
+ if (readchans != &dummy) {
+ memcpy(readchans, rchans, (count_ptrs(rchans) + 1) * sizeof(ssh_channel));
+ }
+ if (writechans != &dummy) {
+ memcpy(writechans, wchans, (count_ptrs(wchans) + 1) * sizeof(ssh_channel));
+ }
+ if (exceptchans != &dummy) {
+ memcpy(exceptchans, echans, (count_ptrs(echans) + 1) * sizeof(ssh_channel));
+ }
SAFE_FREE(rchans);
SAFE_FREE(wchans);
SAFE_FREE(echans);
diff --git a/src/client.c b/src/client.c
index a4a7317e..8aa59daf 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,95 +389,101 @@ 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);
+ SSH_LOG(SSH_LOG_DEBUG, "session_state=%d", session->session_state);
- break;
- case SSH_SESSION_STATE_BANNER_RECEIVED:
- if (session->serverbanner == NULL) {
- goto error;
- }
- set_status(session, 0.4f);
- SSH_LOG(SSH_LOG_PROTOCOL,
- "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_PROTOCOL,
+ "SSH server banner: %s", session->serverbanner);
+
+ /* 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_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);
+ 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->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) {
goto error;
}
- rc = ssh_send_kex(session, 0);
+ rc = ssh_send_kex(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 the init packet was already sent in previous step, this will be no
+ * operation */
+ 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;
}
diff --git a/src/config.c b/src/config.c
index 54ada276..a813568d 100644
--- a/src/config.c
+++ b/src/config.c
@@ -324,7 +324,7 @@ ssh_config_parse_proxy_jump(ssh_session session, const char *s, bool do_parsing)
}
if (parse_entry) {
/* We actually care only about the first item */
- rv = ssh_config_parse_uri(cp, &username, &hostname, &port);
+ rv = ssh_config_parse_uri(cp, &username, &hostname, &port, false);
/* The rest of the list needs to be passed on */
if (endp != NULL) {
next = strdup(endp + 1);
@@ -335,7 +335,7 @@ ssh_config_parse_proxy_jump(ssh_session session, const char *s, bool do_parsing)
}
} else {
/* The rest is just sanity-checked to avoid failures later */
- rv = ssh_config_parse_uri(cp, NULL, NULL, NULL);
+ rv = ssh_config_parse_uri(cp, NULL, NULL, NULL, false);
}
if (rv != SSH_OK) {
goto out;
diff --git a/src/config_parser.c b/src/config_parser.c
index ae2aa2c8..a2da0a62 100644
--- a/src/config_parser.c
+++ b/src/config_parser.c
@@ -30,6 +30,7 @@
#include "libssh/config_parser.h"
#include "libssh/priv.h"
+#include "libssh/misc.h"
char *ssh_config_get_cmd(char **str)
{
@@ -133,12 +134,14 @@ int ssh_config_get_yesno(char **str, int notfound)
}
int ssh_config_parse_uri(const char *tok,
- char **username,
- char **hostname,
- char **port)
+ char **username,
+ char **hostname,
+ char **port,
+ bool ignore_port)
{
char *endp = NULL;
long port_n;
+ int rc;
/* Sanitize inputs */
if (username != NULL) {
@@ -152,7 +155,7 @@ int ssh_config_parse_uri(const char *tok,
}
/* Username part (optional) */
- endp = strchr(tok, '@');
+ endp = strrchr(tok, '@');
if (endp != NULL) {
/* Zero-length username is not valid */
if (tok == endp) {
@@ -180,12 +183,17 @@ int ssh_config_parse_uri(const char *tok,
if (endp == NULL) {
goto error;
}
- } else {
- /* Hostnames or aliases expand to the last colon or to the end */
+ } else if (!ignore_port) {
+ /* Hostnames or aliases expand to the last colon (if port is requested)
+ * or to the end */
endp = strrchr(tok, ':');
if (endp == NULL) {
endp = strchr(tok, '\0');
}
+ } else {
+ /* If no port is requested, expand to the end of line
+ * (to accommodate the IPv6 addresses) */
+ endp = strchr(tok, '\0');
}
if (tok == endp) {
/* Zero-length hostnames are not valid */
@@ -196,6 +204,14 @@ int ssh_config_parse_uri(const char *tok,
if (*hostname == NULL) {
goto error;
}
+ /* if not an ip, check syntax */
+ rc = ssh_is_ipaddr(*hostname);
+ if (rc == 0) {
+ rc = ssh_check_hostname_syntax(*hostname);
+ if (rc != SSH_OK) {
+ goto error;
+ }
+ }
}
/* Skip also the closing bracket */
if (*endp == ']') {
diff --git a/src/connect.c b/src/connect.c
index ce4d58df..ca62dcf0 100644
--- a/src/connect.c
+++ b/src/connect.c
@@ -136,7 +136,7 @@ static int getai(const char *host, int port, struct addrinfo **ai)
#endif
}
- if (ssh_is_ipaddr(host)) {
+ if (ssh_is_ipaddr(host) == 1) {
/* this is an IP address */
SSH_LOG(SSH_LOG_PACKET, "host %s matches an IP address", host);
hints.ai_flags |= AI_NUMERICHOST;
diff --git a/src/curve25519.c b/src/curve25519.c
index d2517551..6b7b4238 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) {
@@ -330,16 +335,10 @@ static SSH_PACKET_CALLBACK(ssh_packet_client_curve25519_reply){
}
/* Send the MSG_NEWKEYS */
- if (ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) {
- goto error;
- }
-
- rc=ssh_packet_send(session);
+ rc = ssh_packet_send_newkeys(session);
if (rc == SSH_ERROR) {
goto error;
}
-
- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent");
session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
return SSH_PACKET_USED;
@@ -497,18 +496,13 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init){
return SSH_ERROR;
}
- /* Send the MSG_NEWKEYS */
- rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS);
- if (rc < 0) {
- goto error;
- }
-
session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
- rc = ssh_packet_send(session);
+
+ /* Send the MSG_NEWKEYS */
+ rc = ssh_packet_send_newkeys(session);
if (rc == SSH_ERROR) {
goto error;
}
- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent");
return SSH_PACKET_USED;
error:
diff --git a/src/dh-gex.c b/src/dh-gex.c
index 88a97140..f1880270 100644
--- a/src/dh-gex.c
+++ b/src/dh-gex.c
@@ -238,6 +238,11 @@ error:
return SSH_PACKET_USED;
}
+void ssh_client_dhgex_remove_callbacks(ssh_session session)
+{
+ ssh_packet_remove_callbacks(session, &ssh_dhgex_client_callbacks);
+}
+
static SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_reply)
{
struct ssh_crypto_struct *crypto=session->next_crypto;
@@ -248,7 +253,7 @@ static SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_reply)
(void)user;
SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_KEX_DH_GEX_REPLY received");
- ssh_packet_remove_callbacks(session, &ssh_dhgex_client_callbacks);
+ ssh_client_dhgex_remove_callbacks(session);
rc = ssh_buffer_unpack(packet,
"SBS",
&pubkey_blob, &server_pubkey,
@@ -282,15 +287,10 @@ static SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_reply)
}
/* Send the MSG_NEWKEYS */
- if (ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) {
- goto error;
- }
-
- rc = ssh_packet_send(session);
+ rc = ssh_packet_send_newkeys(session);
if (rc == SSH_ERROR) {
goto error;
}
- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent");
session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
return SSH_PACKET_USED;
diff --git a/src/dh.c b/src/dh.c
index 18b71734..1d519c63 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);
@@ -381,16 +386,10 @@ SSH_PACKET_CALLBACK(ssh_packet_client_dh_reply){
}
/* Send the MSG_NEWKEYS */
- if (ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) {
- goto error;
- }
-
- rc=ssh_packet_send(session);
+ rc = ssh_packet_send_newkeys(session);
if (rc == SSH_ERROR) {
goto error;
}
-
- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent");
session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
return SSH_PACKET_USED;
error:
@@ -527,15 +526,12 @@ int ssh_server_dh_process_init(ssh_session session, ssh_buffer packet)
}
SSH_LOG(SSH_LOG_DEBUG, "Sent KEX_DH_[GEX]_REPLY");
- if (ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) {
- ssh_buffer_reinit(session->out_buffer);
- goto error;
- }
session->dh_handshake_state=DH_STATE_NEWKEYS_SENT;
- if (ssh_packet_send(session) == SSH_ERROR) {
+ /* Send the MSG_NEWKEYS */
+ rc = ssh_packet_send_newkeys(session);
+ if (rc == SSH_ERROR) {
goto error;
}
- SSH_LOG(SSH_LOG_PACKET, "SSH_MSG_NEWKEYS sent");
return SSH_OK;
error:
diff --git a/src/ecdh.c b/src/ecdh.c
index a4c07ccb..af80beec 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");
@@ -88,16 +93,10 @@ SSH_PACKET_CALLBACK(ssh_packet_client_ecdh_reply){
}
/* Send the MSG_NEWKEYS */
- if (ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) {
- goto error;
- }
-
- rc=ssh_packet_send(session);
+ rc = ssh_packet_send_newkeys(session);
if (rc == SSH_ERROR) {
goto error;
}
-
- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent");
session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
return SSH_PACKET_USED;
diff --git a/src/ecdh_crypto.c b/src/ecdh_crypto.c
index a1de27fd..62578c1b 100644
--- a/src/ecdh_crypto.c
+++ b/src/ecdh_crypto.c
@@ -323,18 +323,12 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){
goto error;
}
- /* Send the MSG_NEWKEYS */
- rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS);
- if (rc < 0) {
- goto error;
- }
-
session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
- rc = ssh_packet_send(session);
- if (rc == SSH_ERROR){
+ /* Send the MSG_NEWKEYS */
+ rc = ssh_packet_send_newkeys(session);
+ if (rc == SSH_ERROR) {
goto error;
}
- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent");
return SSH_PACKET_USED;
error:
diff --git a/src/ecdh_gcrypt.c b/src/ecdh_gcrypt.c
index d9c41bf9..dd4332d7 100644
--- a/src/ecdh_gcrypt.c
+++ b/src/ecdh_gcrypt.c
@@ -372,17 +372,13 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){
goto out;
}
-
+ session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
/* Send the MSG_NEWKEYS */
- rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS);
- if (rc != SSH_OK) {
+ rc = ssh_packet_send_newkeys(session);
+ if (rc == SSH_ERROR) {
goto out;
}
- session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
- rc = ssh_packet_send(session);
- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent");
-
out:
gcry_sexp_release(param);
gcry_sexp_release(key);
diff --git a/src/ecdh_mbedcrypto.c b/src/ecdh_mbedcrypto.c
index 718f1522..45251a42 100644
--- a/src/ecdh_mbedcrypto.c
+++ b/src/ecdh_mbedcrypto.c
@@ -300,16 +300,13 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){
goto out;
}
- rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS);
- if (rc < 0) {
- rc = SSH_ERROR;
+ session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
+ /* Send the MSG_NEWKEYS */
+ rc = ssh_packet_send_newkeys(session);
+ if (rc == SSH_ERROR) {
goto out;
}
- session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
- rc = ssh_packet_send(session);
- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent");
-
out:
mbedtls_ecp_group_free(&grp);
if (rc == SSH_ERROR) {
diff --git a/src/gssapi.c b/src/gssapi.c
index 1d0fb6ae..ba42ba99 100644
--- a/src/gssapi.c
+++ b/src/gssapi.c
@@ -223,6 +223,7 @@ int ssh_gssapi_handle_userauth(ssh_session session, const char *user, uint32_t n
"indicate mechs",
maj_stat,
min_stat);
+ gss_release_oid_set(&min_stat, &both_supported);
return SSH_ERROR;
}
@@ -259,8 +260,10 @@ int ssh_gssapi_handle_userauth(ssh_session session, const char *user, uint32_t n
return SSH_OK;
}
/* from now we have room for context */
- if (ssh_gssapi_init(session) == SSH_ERROR)
+ if (ssh_gssapi_init(session) == SSH_ERROR) {
+ gss_release_oid_set(&min_stat, &both_supported);
return SSH_ERROR;
+ }
name_buf.value = service_name;
name_buf.length = strlen(name_buf.value) + 1;
@@ -272,6 +275,7 @@ int ssh_gssapi_handle_userauth(ssh_session session, const char *user, uint32_t n
"importing name",
maj_stat,
min_stat);
+ gss_release_oid_set(&min_stat, &both_supported);
return -1;
}
@@ -338,6 +342,7 @@ static char *ssh_gssapi_name_to_char(gss_name_t name){
min_stat);
ptr = malloc(buffer.length + 1);
if (ptr == NULL) {
+ gss_release_buffer(&min_stat, &buffer);
return NULL;
}
memcpy(ptr, buffer.value, buffer.length);
@@ -421,6 +426,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_server){
"Gssapi error",
maj_stat,
min_stat);
+ gss_release_buffer(&min_stat, &output_token);
ssh_auth_reply_default(session,0);
ssh_gssapi_free(session);
session->gssapi=NULL;
@@ -438,6 +444,9 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_server){
(size_t)output_token.length, output_token.value);
ssh_packet_send(session);
}
+
+ gss_release_buffer(&min_stat, &output_token);
+
if(maj_stat == GSS_S_COMPLETE){
session->gssapi->state = SSH_GSSAPI_STATE_RCV_MIC;
}
@@ -638,7 +647,7 @@ fail:
static int ssh_gssapi_match(ssh_session session, gss_OID_set *valid_oids)
{
OM_uint32 maj_stat, min_stat, lifetime;
- gss_OID_set actual_mechs;
+ gss_OID_set actual_mechs = GSS_C_NO_OID_SET;
gss_buffer_desc namebuf;
gss_name_t client_id = GSS_C_NO_NAME;
gss_OID oid;
@@ -700,6 +709,7 @@ static int ssh_gssapi_match(ssh_session session, gss_OID_set *valid_oids)
ret = SSH_OK;
end:
+ gss_release_oid_set(&min_stat, &actual_mechs);
gss_release_name(&min_stat, &client_id);
return ret;
}
@@ -713,7 +723,7 @@ end:
*/
int ssh_gssapi_auth_mic(ssh_session session){
size_t i;
- gss_OID_set selected; /* oid selected for authentication */
+ gss_OID_set selected = GSS_C_NO_OID_SET; /* oid selected for authentication */
ssh_string *oids = NULL;
int rc;
size_t n_oids = 0;
@@ -790,6 +800,8 @@ out:
SSH_STRING_FREE(oids[i]);
}
free(oids);
+ gss_release_oid_set(&min_stat, &selected);
+
if (rc != SSH_ERROR) {
return SSH_AUTH_AGAIN;
}
@@ -893,6 +905,8 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_response){
ssh_packet_send(session);
session->auth.state = SSH_AUTH_STATE_GSSAPI_TOKEN;
}
+
+ gss_release_buffer(&min_stat, &output_token);
return SSH_PACKET_USED;
error:
@@ -921,9 +935,11 @@ static int ssh_gssapi_send_mic(ssh_session session){
maj_stat = gss_get_mic(&min_stat,session->gssapi->ctx, GSS_C_QOP_DEFAULT,
&mic_buf, &mic_token_buf);
+
+ SSH_BUFFER_FREE(mic_buffer);
+
if (GSS_ERROR(maj_stat)){
- SSH_BUFFER_FREE(mic_buffer);
- ssh_gssapi_log_error(SSH_LOG_PROTOCOL,
+ ssh_gssapi_log_error(SSH_LOG_DEBUG,
"generating MIC",
maj_stat,
min_stat);
@@ -935,8 +951,10 @@ static int ssh_gssapi_send_mic(ssh_session session){
SSH2_MSG_USERAUTH_GSSAPI_MIC,
mic_token_buf.length,
(size_t)mic_token_buf.length, mic_token_buf.value);
+
+ gss_release_buffer(&min_stat, &mic_token_buf);
+
if (rc != SSH_OK) {
- SSH_BUFFER_FREE(mic_buffer);
ssh_set_error_oom(session);
return SSH_ERROR;
}
@@ -1005,6 +1023,8 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_client){
ssh_packet_send(session);
}
+ gss_release_buffer(&min_stat, &output_token);
+
if (maj_stat == GSS_S_COMPLETE) {
ssh_gssapi_send_mic(session);
session->auth.state = SSH_AUTH_STATE_GSSAPI_MIC_SENT;
diff --git a/src/init.c b/src/init.c
index 2ebcedf6..c25b213b 100644
--- a/src/init.c
+++ b/src/init.c
@@ -269,7 +269,7 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL,
*
* @see ssh_init()
*/
-bool is_ssh_initialized() {
+bool is_ssh_initialized(void) {
bool is_initialized = false;
diff --git a/src/kdf.c b/src/kdf.c
index 09644739..b08f0b2f 100644
--- a/src/kdf.c
+++ b/src/kdf.c
@@ -58,65 +58,102 @@ static ssh_mac_ctx ssh_mac_ctx_init(enum ssh_kdf_digest type)
}
ctx->digest_type = type;
- switch(type){
+ switch (type) {
case SSH_KDF_SHA1:
ctx->ctx.sha1_ctx = sha1_init();
+ if (ctx->ctx.sha1_ctx == NULL) {
+ goto err;
+ }
return ctx;
case SSH_KDF_SHA256:
ctx->ctx.sha256_ctx = sha256_init();
+ if (ctx->ctx.sha256_ctx == NULL) {
+ goto err;
+ }
return ctx;
case SSH_KDF_SHA384:
ctx->ctx.sha384_ctx = sha384_init();
+ if (ctx->ctx.sha384_ctx == NULL) {
+ goto err;
+ }
return ctx;
case SSH_KDF_SHA512:
ctx->ctx.sha512_ctx = sha512_init();
+ if (ctx->ctx.sha512_ctx == NULL) {
+ goto err;
+ }
return ctx;
- default:
- SAFE_FREE(ctx);
- return NULL;
}
+err:
+ SAFE_FREE(ctx);
+ return NULL;
}
-static void ssh_mac_update(ssh_mac_ctx ctx, const void *data, size_t len)
+static void ssh_mac_ctx_free(ssh_mac_ctx ctx)
{
- switch(ctx->digest_type){
+ if (ctx == NULL) {
+ return;
+ }
+
+ switch (ctx->digest_type) {
case SSH_KDF_SHA1:
- sha1_update(ctx->ctx.sha1_ctx, data, len);
+ sha1_ctx_free(ctx->ctx.sha1_ctx);
break;
case SSH_KDF_SHA256:
- sha256_update(ctx->ctx.sha256_ctx, data, len);
+ sha256_ctx_free(ctx->ctx.sha256_ctx);
break;
case SSH_KDF_SHA384:
- sha384_update(ctx->ctx.sha384_ctx, data, len);
+ sha384_ctx_free(ctx->ctx.sha384_ctx);
break;
case SSH_KDF_SHA512:
- sha512_update(ctx->ctx.sha512_ctx, data, len);
+ sha512_ctx_free(ctx->ctx.sha512_ctx);
break;
}
+ SAFE_FREE(ctx);
}
-static void ssh_mac_final(unsigned char *md, ssh_mac_ctx ctx)
+static int ssh_mac_update(ssh_mac_ctx ctx, const void *data, size_t len)
{
- switch(ctx->digest_type){
+ switch (ctx->digest_type) {
+ case SSH_KDF_SHA1:
+ return sha1_update(ctx->ctx.sha1_ctx, data, len);
+ case SSH_KDF_SHA256:
+ return sha256_update(ctx->ctx.sha256_ctx, data, len);
+ case SSH_KDF_SHA384:
+ return sha384_update(ctx->ctx.sha384_ctx, data, len);
+ case SSH_KDF_SHA512:
+ return sha512_update(ctx->ctx.sha512_ctx, data, len);
+ }
+ return SSH_ERROR;
+}
+
+static int ssh_mac_final(unsigned char *md, ssh_mac_ctx ctx)
+{
+ int rc = SSH_ERROR;
+
+ switch (ctx->digest_type) {
case SSH_KDF_SHA1:
- sha1_final(md,ctx->ctx.sha1_ctx);
+ rc = sha1_final(md, ctx->ctx.sha1_ctx);
break;
case SSH_KDF_SHA256:
- sha256_final(md,ctx->ctx.sha256_ctx);
+ rc = sha256_final(md, ctx->ctx.sha256_ctx);
break;
case SSH_KDF_SHA384:
- sha384_final(md,ctx->ctx.sha384_ctx);
+ rc = sha384_final(md, ctx->ctx.sha384_ctx);
break;
case SSH_KDF_SHA512:
- sha512_final(md,ctx->ctx.sha512_ctx);
+ rc = sha512_final(md, ctx->ctx.sha512_ctx);
break;
}
SAFE_FREE(ctx);
+ return rc;
}
int sshkdf_derive_key(struct ssh_crypto_struct *crypto,
- unsigned char *key, size_t key_len,
- int key_type, unsigned char *output,
+ unsigned char *key,
+ size_t key_len,
+ int key_type,
+ unsigned char *output,
size_t requested_len)
{
/* Can't use VLAs with Visual Studio, so allocate the biggest
@@ -125,6 +162,7 @@ int sshkdf_derive_key(struct ssh_crypto_struct *crypto,
size_t output_len = crypto->digest_len;
char letter = key_type;
ssh_mac_ctx ctx;
+ int rc;
if (DIGEST_MAX_LEN < crypto->digest_len) {
return -1;
@@ -135,11 +173,30 @@ int sshkdf_derive_key(struct ssh_crypto_struct *crypto,
return -1;
}
- ssh_mac_update(ctx, key, key_len);
- ssh_mac_update(ctx, crypto->secret_hash, crypto->digest_len);
- ssh_mac_update(ctx, &letter, 1);
- ssh_mac_update(ctx, crypto->session_id, crypto->session_id_len);
- ssh_mac_final(digest, ctx);
+ rc = ssh_mac_update(ctx, key, key_len);
+ if (rc != SSH_OK) {
+ ssh_mac_ctx_free(ctx);
+ return -1;
+ }
+ rc = ssh_mac_update(ctx, crypto->secret_hash, crypto->digest_len);
+ if (rc != SSH_OK) {
+ ssh_mac_ctx_free(ctx);
+ return -1;
+ }
+ rc = ssh_mac_update(ctx, &letter, 1);
+ if (rc != SSH_OK) {
+ ssh_mac_ctx_free(ctx);
+ return -1;
+ }
+ rc = ssh_mac_update(ctx, crypto->session_id, crypto->session_id_len);
+ if (rc != SSH_OK) {
+ ssh_mac_ctx_free(ctx);
+ return -1;
+ }
+ rc = ssh_mac_final(digest, ctx);
+ if (rc != SSH_OK) {
+ return -1;
+ }
if (requested_len < output_len) {
output_len = requested_len;
@@ -151,10 +208,25 @@ int sshkdf_derive_key(struct ssh_crypto_struct *crypto,
if (ctx == NULL) {
return -1;
}
- ssh_mac_update(ctx, key, key_len);
- ssh_mac_update(ctx, crypto->secret_hash, crypto->digest_len);
- ssh_mac_update(ctx, output, output_len);
- ssh_mac_final(digest, ctx);
+ rc = ssh_mac_update(ctx, key, key_len);
+ if (rc != SSH_OK) {
+ ssh_mac_ctx_free(ctx);
+ return -1;
+ }
+ rc = ssh_mac_update(ctx, crypto->secret_hash, crypto->digest_len);
+ if (rc != SSH_OK) {
+ ssh_mac_ctx_free(ctx);
+ return -1;
+ }
+ rc = ssh_mac_update(ctx, output, output_len);
+ if (rc != SSH_OK) {
+ ssh_mac_ctx_free(ctx);
+ return -1;
+ }
+ rc = ssh_mac_final(digest, ctx);
+ if (rc != SSH_OK) {
+ return -1;
+ }
if (requested_len < output_len + crypto->digest_len) {
memcpy(output + output_len, digest, requested_len - output_len);
} else {
diff --git a/src/kex.c b/src/kex.c
index 82071c74..eea3604b 100644
--- a/src/kex.c
+++ b/src/kex.c
@@ -28,6 +28,7 @@
#include <stdio.h>
#include <stdbool.h>
+#include "libssh/libssh.h"
#include "libssh/priv.h"
#include "libssh/buffer.h"
#include "libssh/dh.h"
@@ -162,6 +163,9 @@
/* RFC 8308 */
#define KEX_EXTENSION_CLIENT "ext-info-c"
+/* Strict kex mitigation against CVE-2023-48795 */
+#define KEX_STRICT_CLIENT "kex-strict-c-v00@openssh.com"
+#define KEX_STRICT_SERVER "kex-strict-s-v00@openssh.com"
/* Allowed algorithms in FIPS mode */
#define FIPS_ALLOWED_CIPHERS "aes256-gcm@openssh.com,"\
@@ -305,6 +309,10 @@ static int cmp_first_kex_algo(const char *client_str,
int is_wrong = 1;
+ if (client_str == NULL || server_str == NULL) {
+ return is_wrong;
+ }
+
colon = strchr(client_str, ',');
if (colon == NULL) {
client_kex_len = strlen(client_str);
@@ -331,6 +339,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};
@@ -344,35 +353,67 @@ 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) {
- SSH_LOG(SSH_LOG_INFO, "Initiating key re-exchange");
+ if (session->dh_handshake_state == DH_STATE_FINISHED) {
+ SSH_LOG(SSH_LOG_DEBUG, "Peer initiated key re-exchange");
+ /* Reset the sent flag if the re-kex was initiated by the peer */
+ session->flags &= ~SSH_SESSION_FLAG_KEXINIT_SENT;
+ } else if (session->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;
+ }
} 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);
+#ifdef WITH_SERVER
+ 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;
+ }
+
+ ok = server_set_kex(session);
+ if (ok == SSH_ERROR) {
goto error;
}
+#endif
} else {
- len = ssh_buffer_get_data(packet,session->next_crypto->server_kex.cookie, 16);
+ 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;
+ }
+
+ ok = ssh_set_client_kex(session);
+ if (ok == SSH_ERROR) {
goto error;
}
}
@@ -385,7 +426,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;
}
@@ -398,14 +440,14 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit)
str = NULL;
}
- /* copy the server kex info into an array of strings */
+ /* copy the peer 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];
}
}
@@ -419,30 +461,69 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit)
* that its value is included when computing the session ID (see
* 'make_sessionid').
*/
- if (server_kex) {
- rc = ssh_buffer_get_u8(packet, &first_kex_packet_follows);
- if (rc != 1) {
- goto error;
- }
+ rc = ssh_buffer_get_u8(packet, &first_kex_packet_follows);
+ if (rc != 1) {
+ goto error;
+ }
- rc = ssh_buffer_add_u8(session->in_hashbuf, first_kex_packet_follows);
- if (rc < 0) {
- goto error;
- }
+ rc = ssh_buffer_add_u8(session->in_hashbuf, first_kex_packet_follows);
+ if (rc < 0) {
+ goto error;
+ }
- rc = ssh_buffer_add_u32(session->in_hashbuf, kexinit_reserved);
- if (rc < 0) {
- goto error;
+ rc = ssh_buffer_add_u32(session->in_hashbuf, kexinit_reserved);
+ if (rc < 0) {
+ goto error;
+ }
+
+ /*
+ * Remember whether 'first_kex_packet_follows' was set and the client
+ * guess was wrong: in this case the next SSH_MSG_KEXDH_INIT message
+ * must be ignored on the server side.
+ * Client needs to start the Key exchange over with the correct method
+ */
+ 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 =
+ cmp_first_kex_algo(client_methods[SSH_KEX],
+ 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");
+ }
+
+ /*
+ * handle the "strict KEX" feature. If supported by peer, then set up the
+ * flag and verify packet sequence numbers.
+ */
+ if (server_kex) {
+ ok = ssh_match_group(crypto->client_kex.methods[SSH_KEX],
+ KEX_STRICT_CLIENT);
+ if (ok) {
+ SSH_LOG(SSH_LOG_DEBUG, "Client supports strict kex, enabling.");
+ session->flags |= SSH_SESSION_FLAG_KEX_STRICT;
+ }
+ } else {
+ /* client kex */
+ ok = ssh_match_group(crypto->server_kex.methods[SSH_KEX],
+ KEX_STRICT_SERVER);
+ if (ok) {
+ SSH_LOG(SSH_LOG_DEBUG, "Server supports strict kex, enabling.");
+ session->flags |= SSH_SESSION_FLAG_KEX_STRICT;
}
+ }
+ if (server_kex) {
/*
* If client sent a ext-info-c message in the kex list, it supports
* 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 +533,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 +550,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 +568,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) {
@@ -507,24 +587,16 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit)
session->extensions & SSH_EXT_SIG_RSA_SHA256 ? "SHA256" : "",
session->extensions & SSH_EXT_SIG_RSA_SHA512 ? " SHA512" : "");
}
-
- /*
- * Remember whether 'first_kex_packet_follows' was set and the client
- * guess was wrong: in this case the next SSH_MSG_KEXDH_INIT message
- * must be ignored.
- */
- if (first_kex_packet_follows) {
- session->first_kex_follows_guess_wrong =
- cmp_first_kex_algo(session->next_crypto->client_kex.methods[SSH_KEX],
- session->next_crypto->server_kex.methods[SSH_KEX]) ||
- cmp_first_kex_algo(session->next_crypto->client_kex.methods[SSH_HOSTKEYS],
- session->next_crypto->server_kex.methods[SSH_HOSTKEYS]);
- }
}
/* Note, that his overwrites authenticated state in case of rekeying */
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;
@@ -666,11 +738,14 @@ int ssh_set_client_kex(ssh_session session)
{
struct ssh_kex_struct *client = &session->next_crypto->client_kex;
const char *wanted;
- char *kex = NULL;
- char *kex_tmp = NULL;
int ok;
int i;
- size_t kex_len, len;
+
+ /* Skip if already set, for example for the rekey or when we do the guessing
+ * it could have been already used to make some protocol decisions. */
+ if (client->methods[0] != NULL) {
+ return SSH_OK;
+ }
ok = ssh_get_random(client->cookie, 16, 0);
if (!ok) {
@@ -678,8 +753,6 @@ int ssh_set_client_kex(ssh_session session)
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++) {
@@ -715,23 +788,52 @@ int ssh_set_client_kex(ssh_session session)
return SSH_OK;
}
- /* Here we append ext-info-c to the list of kex algorithms */
- kex = client->methods[SSH_KEX];
+ ok = ssh_kex_append_extensions(session, client);
+ if (ok != SSH_OK){
+ return ok;
+ }
+
+ return SSH_OK;
+}
+
+int ssh_kex_append_extensions(ssh_session session, struct ssh_kex_struct *pkex)
+{
+ char *kex = NULL;
+ char *kex_tmp = NULL;
+ size_t kex_len, len;
+
+ /* Here we append ext-info-c and kex-strict-c-v00@openssh.com for client
+ * and kex-strict-s-v00@openssh.com for server to the list of kex algorithms
+ */
+ kex = pkex->methods[SSH_KEX];
len = strlen(kex);
- if (len + strlen(KEX_EXTENSION_CLIENT) + 2 < len) {
+ if (session->server) {
+ /* Comma, nul byte */
+ kex_len = len + 1 + strlen(KEX_STRICT_SERVER) + 1;
+ } else {
+ /* Comma, comma, nul byte */
+ kex_len = len + 1 + strlen(KEX_EXTENSION_CLIENT) + 1 +
+ strlen(KEX_STRICT_CLIENT) + 1;
+ }
+ if (kex_len >= MAX_PACKET_LEN) {
/* Overflow */
return SSH_ERROR;
}
- kex_len = len + strlen(KEX_EXTENSION_CLIENT) + 2; /* comma, NULL */
kex_tmp = realloc(kex, kex_len);
if (kex_tmp == NULL) {
- free(kex);
ssh_set_error_oom(session);
return SSH_ERROR;
}
- snprintf(kex_tmp + len, kex_len - len, ",%s", KEX_EXTENSION_CLIENT);
- client->methods[SSH_KEX] = kex_tmp;
-
+ if (session->server){
+ snprintf(kex_tmp + len, kex_len - len, ",%s", KEX_STRICT_SERVER);
+ } else {
+ snprintf(kex_tmp + len,
+ kex_len - len,
+ ",%s,%s",
+ KEX_EXTENSION_CLIENT,
+ KEX_STRICT_CLIENT);
+ }
+ pkex->methods[SSH_KEX] = kex_tmp;
return SSH_OK;
}
@@ -749,70 +851,141 @@ 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;
+}
+
+
+/** @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.
*/
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;
+ 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;
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.
+ /* Here we should drop the extensions from the list so we avoid matching.
* it. We added it to the end, so we can just truncate the string here */
- ext_start = strstr(client->methods[SSH_KEX], ","KEX_EXTENSION_CLIENT);
- if (ext_start != NULL) {
- ext_start[0] = '\0';
+ if (session->client) {
+ ext_start = strstr(client->methods[SSH_KEX], "," KEX_EXTENSION_CLIENT);
+ if (ext_start != NULL) {
+ ext_start[0] = '\0';
+ }
+ }
+ if (session->server) {
+ ext_start = strstr(server->methods[SSH_KEX], "," KEX_STRICT_SERVER);
+ if (ext_start != NULL) {
+ ext_start[0] = '\0';
+ }
}
for (i = 0; i < SSH_KEX_METHODS; i++) {
- 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;
-#ifdef WITH_GEX
- } else if (strcmp(session->next_crypto->kex_methods[SSH_KEX], "diffie-hellman-group-exchange-sha1") == 0){
- session->next_crypto->kex_type=SSH_KEX_DH_GEX_SHA1;
- } else if (strcmp(session->next_crypto->kex_methods[SSH_KEX], "diffie-hellman-group-exchange-sha256") == 0){
- session->next_crypto->kex_type=SSH_KEX_DH_GEX_SHA256;
-#endif /* WITH_GEX */
- } else if (strcmp(session->next_crypto->kex_methods[SSH_KEX], "ecdh-sha2-nistp256") == 0){
- session->next_crypto->kex_type=SSH_KEX_ECDH_SHA2_NISTP256;
- } else if (strcmp(session->next_crypto->kex_methods[SSH_KEX], "ecdh-sha2-nistp384") == 0){
- session->next_crypto->kex_type=SSH_KEX_ECDH_SHA2_NISTP384;
- } else if (strcmp(session->next_crypto->kex_methods[SSH_KEX], "ecdh-sha2-nistp521") == 0){
- session->next_crypto->kex_type=SSH_KEX_ECDH_SHA2_NISTP521;
- } else if (strcmp(session->next_crypto->kex_methods[SSH_KEX], "curve25519-sha256@libssh.org") == 0){
- session->next_crypto->kex_type=SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG;
- } else if (strcmp(session->next_crypto->kex_methods[SSH_KEX], "curve25519-sha256") == 0){
- session->next_crypto->kex_type=SSH_KEX_CURVE25519_SHA256;
+
+ /* 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_INFO, "Negotiated %s,%s,%s,%s,%s,%s,%s,%s,%s,%s",
session->next_crypto->kex_methods[SSH_KEX],
session->next_crypto->kex_methods[SSH_HOSTKEYS],
@@ -830,63 +1003,116 @@ int ssh_kex_select_methods (ssh_session session)
/* this function only sends the predefined set of kex methods */
-int ssh_send_kex(ssh_session session, int server_kex)
+int ssh_send_kex(ssh_session session)
{
- struct ssh_kex_struct *kex = (server_kex ? &session->next_crypto->server_kex :
- &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;
- }
+ struct ssh_kex_struct *kex = (session->server ?
+ &session->next_crypto->server_kex :
+ &session->next_crypto->client_kex);
+ 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",
+ 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);
- 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;
+ }
- 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_hashbuf, str) < 0) {
- goto error;
+ rc = ssh_buffer_pack(session->out_buffer,
+ "bd",
+ first_kex_packet_follows,
+ 0);
+ if (rc != SSH_OK) {
+ goto error;
}
- if (ssh_buffer_add_ssh_string(session->out_buffer, str) < 0) {
- goto error;
+
+ /* Prepare also the first_kex_packet_follows and reserved to 0 */
+ rc = ssh_buffer_add_u8(session->out_hashbuf, first_kex_packet_follows);
+ if (rc < 0) {
+ goto error;
+ }
+ rc = ssh_buffer_add_u32(session->out_hashbuf, 0);
+ if (rc < 0) {
+ 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;
- }
+ session->flags |= SSH_SESSION_FLAG_KEXINIT_SENT;
+ SSH_LOG(SSH_LOG_PACKET, "SSH_MSG_KEXINIT sent");
+
+ /* If we indicated that we are sending the guessed key exchange packet,
+ * do it now. The packet is simple, but we need to do some preparations */
+ if (first_kex_packet_follows) {
+ char *list = kex->methods[SSH_KEX];
+ char *colon = strchr(list, ',');
+ size_t kex_name_len = colon ? (size_t)(colon - list) : strlen(list);
+ char *kex_name = calloc(kex_name_len + 1, 1);
+ if (kex_name == NULL) {
+ ssh_set_error_oom(session);
+ goto error;
+ }
+ snprintf(kex_name, kex_name_len + 1, "%.*s", (int)kex_name_len, list);
+ SSH_LOG(SSH_LOG_TRACE, "Sending the first kex packet for %s", kex_name);
+
+ session->next_crypto->kex_type = kex_select_kex_type(kex_name);
+ free(kex_name);
+
+ /* run the first step of the DH handshake */
+ session->dh_handshake_state = DH_STATE_INIT;
+ if (dh_handshake(session) == SSH_ERROR) {
+ goto error;
+ }
+ }
+ return 0;
- 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;
}
/*
@@ -929,7 +1155,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;
@@ -1006,33 +1232,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/libcrypto.c b/src/libcrypto.c
index 3db75df6..45f45a9e 100644
--- a/src/libcrypto.c
+++ b/src/libcrypto.c
@@ -126,81 +126,47 @@ SHACTX sha1_init(void)
return c;
}
-void sha1_update(SHACTX c, const void *data, unsigned long len)
+void sha1_ctx_free(SHACTX c)
{
- EVP_DigestUpdate(c, data, len);
-}
-
-void sha1_final(unsigned char *md, SHACTX c)
-{
- unsigned int mdlen = 0;
-
- EVP_DigestFinal(c, md, &mdlen);
EVP_MD_CTX_destroy(c);
}
-void sha1(const unsigned char *digest, int len, unsigned char *hash)
-{
- SHACTX c = sha1_init();
- if (c != NULL) {
- sha1_update(c, digest, len);
- sha1_final(hash, c);
- }
-}
-
-#ifdef HAVE_OPENSSL_ECC
-static const EVP_MD *nid_to_evpmd(int nid)
+int sha1_update(SHACTX c, const void *data, unsigned long len)
{
- switch (nid) {
- case NID_X9_62_prime256v1:
- return EVP_sha256();
- case NID_secp384r1:
- return EVP_sha384();
- case NID_secp521r1:
- return EVP_sha512();
- default:
- return NULL;
+ int rc = EVP_DigestUpdate(c, data, len);
+ if (rc != 1) {
+ return SSH_ERROR;
}
-
- return NULL;
-}
-
-void evp(int nid, unsigned char *digest, int len, unsigned char *hash, unsigned int *hlen)
-{
- const EVP_MD *evp_md = nid_to_evpmd(nid);
- EVP_MD_CTX *md = EVP_MD_CTX_new();
-
- EVP_DigestInit(md, evp_md);
- EVP_DigestUpdate(md, digest, len);
- EVP_DigestFinal(md, hash, hlen);
- EVP_MD_CTX_free(md);
+ return SSH_OK;
}
-EVPCTX evp_init(int nid)
+int sha1_final(unsigned char *md, SHACTX c)
{
- const EVP_MD *evp_md = nid_to_evpmd(nid);
+ unsigned int mdlen = 0;
+ int rc = EVP_DigestFinal(c, md, &mdlen);
- EVPCTX ctx = EVP_MD_CTX_new();
- if (ctx == NULL) {
- return NULL;
+ EVP_MD_CTX_destroy(c);
+ if (rc != 1) {
+ return SSH_ERROR;
}
-
- EVP_DigestInit(ctx, evp_md);
-
- return ctx;
+ return SSH_OK;
}
-void evp_update(EVPCTX ctx, const void *data, unsigned long len)
+int sha1(const unsigned char *digest, int len, unsigned char *hash)
{
- EVP_DigestUpdate(ctx, data, len);
-}
+ SHACTX c = sha1_init();
+ int rc;
-void evp_final(EVPCTX ctx, unsigned char *md, unsigned int *mdlen)
-{
- EVP_DigestFinal(ctx, md, mdlen);
- EVP_MD_CTX_free(ctx);
+ if (c == NULL) {
+ return SSH_ERROR;
+ }
+ rc = sha1_update(c, digest, len);
+ if (rc != SSH_OK) {
+ sha1_ctx_free(c);
+ return SSH_ERROR;
+ }
+ return sha1_final(hash, c);
}
-#endif
SHA256CTX sha256_init(void)
{
@@ -218,26 +184,46 @@ SHA256CTX sha256_init(void)
return c;
}
-void sha256_update(SHA256CTX c, const void *data, unsigned long len)
+void sha256_ctx_free(SHA256CTX c)
+{
+ EVP_MD_CTX_destroy(c);
+}
+
+int sha256_update(SHA256CTX c, const void *data, unsigned long len)
{
- EVP_DigestUpdate(c, data, len);
+ int rc = EVP_DigestUpdate(c, data, len);
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
}
-void sha256_final(unsigned char *md, SHA256CTX c)
+int sha256_final(unsigned char *md, SHA256CTX c)
{
unsigned int mdlen = 0;
+ int rc = EVP_DigestFinal(c, md, &mdlen);
- EVP_DigestFinal(c, md, &mdlen);
EVP_MD_CTX_destroy(c);
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
}
-void sha256(const unsigned char *digest, int len, unsigned char *hash)
+int sha256(const unsigned char *digest, int len, unsigned char *hash)
{
SHA256CTX c = sha256_init();
- if (c != NULL) {
- sha256_update(c, digest, len);
- sha256_final(hash, c);
+ int rc;
+
+ if (c == NULL) {
+ return SSH_ERROR;
+ }
+ rc = sha256_update(c, digest, len);
+ if (rc != SSH_OK) {
+ sha256_ctx_free(c);
+ return SSH_ERROR;
}
+ return sha256_final(hash, c);
}
SHA384CTX sha384_init(void)
@@ -256,26 +242,47 @@ SHA384CTX sha384_init(void)
return c;
}
-void sha384_update(SHA384CTX c, const void *data, unsigned long len)
+void
+sha384_ctx_free(SHA384CTX c)
{
- EVP_DigestUpdate(c, data, len);
+ EVP_MD_CTX_destroy(c);
}
-void sha384_final(unsigned char *md, SHA384CTX c)
+int sha384_update(SHA384CTX c, const void *data, unsigned long len)
+{
+ int rc = EVP_DigestUpdate(c, data, len);
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int sha384_final(unsigned char *md, SHA384CTX c)
{
unsigned int mdlen = 0;
+ int rc = EVP_DigestFinal(c, md, &mdlen);
- EVP_DigestFinal(c, md, &mdlen);
EVP_MD_CTX_destroy(c);
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
}
-void sha384(const unsigned char *digest, int len, unsigned char *hash)
+int sha384(const unsigned char *digest, int len, unsigned char *hash)
{
SHA384CTX c = sha384_init();
- if (c != NULL) {
- sha384_update(c, digest, len);
- sha384_final(hash, c);
+ int rc;
+
+ if (c == NULL) {
+ return SSH_ERROR;
}
+ rc = sha384_update(c, digest, len);
+ if (rc != SSH_OK) {
+ sha384_ctx_free(c);
+ return SSH_ERROR;
+ }
+ return sha384_final(hash, c);
}
SHA512CTX sha512_init(void)
@@ -294,26 +301,46 @@ SHA512CTX sha512_init(void)
return c;
}
-void sha512_update(SHA512CTX c, const void *data, unsigned long len)
+void sha512_ctx_free(SHA512CTX c)
+{
+ EVP_MD_CTX_destroy(c);
+}
+
+int sha512_update(SHA512CTX c, const void *data, unsigned long len)
{
- EVP_DigestUpdate(c, data, len);
+ int rc = EVP_DigestUpdate(c, data, len);
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
}
-void sha512_final(unsigned char *md, SHA512CTX c)
+int sha512_final(unsigned char *md, SHA512CTX c)
{
unsigned int mdlen = 0;
+ int rc = EVP_DigestFinal(c, md, &mdlen);
- EVP_DigestFinal(c, md, &mdlen);
EVP_MD_CTX_destroy(c);
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
}
-void sha512(const unsigned char *digest, int len, unsigned char *hash)
+int sha512(const unsigned char *digest, int len, unsigned char *hash)
{
SHA512CTX c = sha512_init();
- if (c != NULL) {
- sha512_update(c, digest, len);
- sha512_final(hash, c);
+ int rc;
+
+ if (c == NULL) {
+ return SSH_ERROR;
+ }
+ rc = sha512_update(c, digest, len);
+ if (rc != SSH_OK) {
+ sha512_ctx_free(c);
+ return SSH_ERROR;
}
+ return sha512_final(hash, c);
}
MD5CTX md5_init(void)
@@ -332,19 +359,33 @@ MD5CTX md5_init(void)
return c;
}
-void md5_update(MD5CTX c, const void *data, unsigned long len)
+void md5_ctx_free(MD5CTX c)
{
- EVP_DigestUpdate(c, data, len);
+ EVP_MD_CTX_destroy(c);
}
-void md5_final(unsigned char *md, MD5CTX c)
+int md5_update(MD5CTX c, const void *data, unsigned long len)
+{
+ int rc = EVP_DigestUpdate(c, data, len);
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int md5_final(unsigned char *md, MD5CTX c)
{
unsigned int mdlen = 0;
+ int rc = EVP_DigestFinal(c, md, &mdlen);
- EVP_DigestFinal(c, md, &mdlen);
EVP_MD_CTX_destroy(c);
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
}
+
#ifdef HAVE_OPENSSL_EVP_KDF_CTX_NEW_ID
static const EVP_MD *sshkdf_digest_to_md(enum ssh_kdf_digest digest_type)
{
diff --git a/src/libgcrypt.c b/src/libgcrypt.c
index 8fbf2157..d1332af3 100644
--- a/src/libgcrypt.c
+++ b/src/libgcrypt.c
@@ -68,72 +68,37 @@ SHACTX sha1_init(void) {
return ctx;
}
-void sha1_update(SHACTX c, const void *data, unsigned long len) {
- gcry_md_write(c, data, len);
-}
-
-void sha1_final(unsigned char *md, SHACTX c) {
- gcry_md_final(c);
- memcpy(md, gcry_md_read(c, 0), SHA_DIGEST_LEN);
- gcry_md_close(c);
-}
-
-void sha1(const unsigned char *digest, int len, unsigned char *hash) {
- gcry_md_hash_buffer(GCRY_MD_SHA1, hash, digest, len);
-}
-
-#ifdef HAVE_GCRYPT_ECC
-static int nid_to_md_algo(int nid)
+void
+sha1_ctx_free(SHACTX c)
{
- switch (nid) {
- case NID_gcrypt_nistp256:
- return GCRY_MD_SHA256;
- case NID_gcrypt_nistp384:
- return GCRY_MD_SHA384;
- case NID_gcrypt_nistp521:
- return GCRY_MD_SHA512;
- }
- return GCRY_MD_NONE;
+ gcry_md_close(c);
}
-void evp(int nid, unsigned char *digest, int len,
- unsigned char *hash, unsigned int *hlen)
-{
- int algo = nid_to_md_algo(nid);
-
- /* Note: What gcrypt calls 'hash' is called 'digest' here and
- vice-versa. */
- gcry_md_hash_buffer(algo, hash, digest, len);
- *hlen = gcry_md_get_algo_dlen(algo);
+int sha1_update(SHACTX c, const void *data, unsigned long len) {
+ gcry_md_write(c, data, len);
+ return SSH_OK;
}
-EVPCTX evp_init(int nid)
+int sha1_final(unsigned char *md, SHACTX c)
{
- gcry_error_t err;
- int algo = nid_to_md_algo(nid);
- EVPCTX ctx;
+ unsigned char *tmp = NULL;
- err = gcry_md_open(&ctx, algo, 0);
- if (err) {
- return NULL;
+ gcry_md_final(c);
+ tmp = gcry_md_read(c, 0);
+ if (tmp == NULL) {
+ gcry_md_close(c);
+ return SSH_ERROR;
}
-
- return ctx;
+ memcpy(md, tmp, SHA_DIGEST_LEN);
+ gcry_md_close(c);
+ return SSH_OK;
}
-void evp_update(EVPCTX ctx, const void *data, unsigned long len)
-{
- gcry_md_write(ctx, data, len);
+int sha1(const unsigned char *digest, int len, unsigned char *hash) {
+ gcry_md_hash_buffer(GCRY_MD_SHA1, hash, digest, len);
+ return SSH_OK;
}
-void evp_final(EVPCTX ctx, unsigned char *md, unsigned int *mdlen)
-{
- int algo = gcry_md_get_algo(ctx);
- *mdlen = gcry_md_get_algo_dlen(algo);
- memcpy(md, gcry_md_read(ctx, algo), *mdlen);
- gcry_md_close(ctx);
-}
-#endif
SHA256CTX sha256_init(void) {
SHA256CTX ctx = NULL;
@@ -142,18 +107,35 @@ SHA256CTX sha256_init(void) {
return ctx;
}
-void sha256_update(SHACTX c, const void *data, unsigned long len) {
+void
+sha256_ctx_free(SHA256CTX c)
+{
+ gcry_md_close(c);
+}
+
+int sha256_update(SHACTX c, const void *data, unsigned long len) {
gcry_md_write(c, data, len);
+ return SSH_OK;
}
-void sha256_final(unsigned char *md, SHACTX c) {
- gcry_md_final(c);
- memcpy(md, gcry_md_read(c, 0), SHA256_DIGEST_LEN);
- gcry_md_close(c);
+int sha256_final(unsigned char *md, SHACTX c)
+{
+ unsigned char *tmp = NULL;
+
+ gcry_md_final(c);
+ tmp = gcry_md_read(c, 0);
+ if (tmp == NULL) {
+ gcry_md_close(c);
+ return SSH_ERROR;
+ }
+ memcpy(md, tmp, SHA256_DIGEST_LEN);
+ gcry_md_close(c);
+ return SSH_OK;
}
-void sha256(const unsigned char *digest, int len, unsigned char *hash){
+int sha256(const unsigned char *digest, int len, unsigned char *hash){
gcry_md_hash_buffer(GCRY_MD_SHA256, hash, digest, len);
+ return SSH_OK;
}
SHA384CTX sha384_init(void) {
@@ -163,18 +145,35 @@ SHA384CTX sha384_init(void) {
return ctx;
}
-void sha384_update(SHACTX c, const void *data, unsigned long len) {
+void
+sha384_ctx_free(SHA384CTX c)
+{
+ gcry_md_close(c);
+}
+
+int sha384_update(SHACTX c, const void *data, unsigned long len) {
gcry_md_write(c, data, len);
+ return SSH_OK;
}
-void sha384_final(unsigned char *md, SHACTX c) {
- gcry_md_final(c);
- memcpy(md, gcry_md_read(c, 0), SHA384_DIGEST_LEN);
- gcry_md_close(c);
+int sha384_final(unsigned char *md, SHACTX c)
+{
+ unsigned char *tmp = NULL;
+
+ gcry_md_final(c);
+ tmp = gcry_md_read(c, 0);
+ if (tmp == NULL) {
+ gcry_md_close(c);
+ return SSH_ERROR;
+ }
+ memcpy(md, tmp, SHA384_DIGEST_LEN);
+ gcry_md_close(c);
+ return SSH_OK;
}
-void sha384(const unsigned char *digest, int len, unsigned char *hash) {
+int sha384(const unsigned char *digest, int len, unsigned char *hash) {
gcry_md_hash_buffer(GCRY_MD_SHA384, hash, digest, len);
+ return SSH_OK;
}
SHA512CTX sha512_init(void) {
@@ -184,18 +183,35 @@ SHA512CTX sha512_init(void) {
return ctx;
}
-void sha512_update(SHACTX c, const void *data, unsigned long len) {
+void
+sha512_ctx_free(SHA512CTX c)
+{
+ gcry_md_close(c);
+}
+
+int sha512_update(SHACTX c, const void *data, unsigned long len) {
gcry_md_write(c, data, len);
+ return SSH_OK;
}
-void sha512_final(unsigned char *md, SHACTX c) {
- gcry_md_final(c);
- memcpy(md, gcry_md_read(c, 0), SHA512_DIGEST_LEN);
- gcry_md_close(c);
+int sha512_final(unsigned char *md, SHACTX c)
+{
+ unsigned char *tmp = NULL;
+
+ gcry_md_final(c);
+ tmp = gcry_md_read(c, 0);
+ if (tmp == NULL) {
+ gcry_md_close(c);
+ return SSH_ERROR;
+ }
+ memcpy(md, tmp, SHA512_DIGEST_LEN);
+ gcry_md_close(c);
+ return SSH_OK;
}
-void sha512(const unsigned char *digest, int len, unsigned char *hash) {
+int sha512(const unsigned char *digest, int len, unsigned char *hash) {
gcry_md_hash_buffer(GCRY_MD_SHA512, hash, digest, len);
+ return SSH_OK;
}
MD5CTX md5_init(void) {
@@ -205,14 +221,30 @@ MD5CTX md5_init(void) {
return c;
}
-void md5_update(MD5CTX c, const void *data, unsigned long len) {
+void
+md5_ctx_free(MD5CTX c)
+{
+ gcry_md_close(c);
+}
+
+int md5_update(MD5CTX c, const void *data, unsigned long len) {
gcry_md_write(c,data,len);
+ return SSH_OK;
}
-void md5_final(unsigned char *md, MD5CTX c) {
- gcry_md_final(c);
- memcpy(md, gcry_md_read(c, 0), MD5_DIGEST_LEN);
- gcry_md_close(c);
+int md5_final(unsigned char *md, MD5CTX c)
+{
+ unsigned char *tmp = NULL;
+
+ gcry_md_final(c);
+ tmp = gcry_md_read(c, 0);
+ if (tmp == NULL) {
+ gcry_md_close(c);
+ return SSH_ERROR;
+ }
+ memcpy(md, tmp, MD5_DIGEST_LEN);
+ gcry_md_close(c);
+ return SSH_OK;
}
int ssh_kdf(struct ssh_crypto_struct *crypto,
diff --git a/src/libmbedcrypto.c b/src/libmbedcrypto.c
index a2e74d3b..e9a2b8e5 100644
--- a/src/libmbedcrypto.c
+++ b/src/libmbedcrypto.c
@@ -82,99 +82,46 @@ SHACTX sha1_init(void)
return ctx;
}
-void sha1_update(SHACTX c, const void *data, unsigned long len)
+void
+sha1_ctx_free(SHACTX c)
{
- mbedtls_md_update(c, data, len);
-}
-
-void sha1_final(unsigned char *md, SHACTX c)
-{
- mbedtls_md_finish(c, md);
mbedtls_md_free(c);
SAFE_FREE(c);
}
-void sha1(const unsigned char *digest, int len, unsigned char *hash)
+int sha1_update(SHACTX c, const void *data, unsigned long len)
{
- const mbedtls_md_info_t *md_info =
- mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
- if (md_info != NULL) {
- mbedtls_md(md_info, digest, len, hash);
+ int rc = mbedtls_md_update(c, data, len);
+ if (rc != 0) {
+ return SSH_ERROR;
}
+ return SSH_OK;
}
-static mbedtls_md_type_t nid_to_md_algo(int nid)
-{
- switch (nid) {
- case NID_mbedtls_nistp256:
- return MBEDTLS_MD_SHA256;
- case NID_mbedtls_nistp384:
- return MBEDTLS_MD_SHA384;
- case NID_mbedtls_nistp521:
- return MBEDTLS_MD_SHA512;
- }
- return MBEDTLS_MD_NONE;
-}
-
-void evp(int nid, unsigned char *digest, int len,
- unsigned char *hash, unsigned int *hlen)
+int sha1_final(unsigned char *md, SHACTX c)
{
- mbedtls_md_type_t algo = nid_to_md_algo(nid);
- const mbedtls_md_info_t *md_info =
- mbedtls_md_info_from_type(algo);
-
-
- if (md_info != NULL) {
- *hlen = mbedtls_md_get_size(md_info);
- mbedtls_md(md_info, digest, len, hash);
+ int rc = mbedtls_md_finish(c, md);
+ sha1_ctx_free(c);
+ if (rc != 0) {
+ return SSH_ERROR;
}
+ return SSH_OK;
}
-EVPCTX evp_init(int nid)
+int sha1(const unsigned char *digest, int len, unsigned char *hash)
{
- EVPCTX ctx = NULL;
- int rc;
- mbedtls_md_type_t algo = nid_to_md_algo(nid);
const mbedtls_md_info_t *md_info =
- mbedtls_md_info_from_type(algo);
+ mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
+ int rc;
if (md_info == NULL) {
- return NULL;
- }
-
- ctx = malloc(sizeof(mbedtls_md_context_t));
- if (ctx == NULL) {
- return NULL;
- }
-
- mbedtls_md_init(ctx);
-
- rc = mbedtls_md_setup(ctx, md_info, 0);
- if (rc != 0) {
- SAFE_FREE(ctx);
- return NULL;
+ return SSH_ERROR;
}
-
- rc = mbedtls_md_starts(ctx);
+ rc = mbedtls_md(md_info, digest, len, hash);
if (rc != 0) {
- SAFE_FREE(ctx);
- return NULL;
+ return SSH_ERROR;
}
-
- return ctx;
-}
-
-void evp_update(EVPCTX ctx, const void *data, unsigned long len)
-{
- mbedtls_md_update(ctx, data, len);
-}
-
-void evp_final(EVPCTX ctx, unsigned char *md, unsigned int *mdlen)
-{
- *mdlen = mbedtls_md_get_size(ctx->md_info);
- mbedtls_md_finish(ctx, md);
- mbedtls_md_free(ctx);
- SAFE_FREE(ctx);
+ return SSH_OK;
}
SHA256CTX sha256_init(void)
@@ -210,25 +157,46 @@ SHA256CTX sha256_init(void)
return ctx;
}
-void sha256_update(SHA256CTX c, const void *data, unsigned long len)
+void
+sha256_ctx_free(SHA256CTX c)
{
- mbedtls_md_update(c, data, len);
+ mbedtls_md_free(c);
+ SAFE_FREE(c);
}
-void sha256_final(unsigned char *md, SHA256CTX c)
+int sha256_update(SHA256CTX c, const void *data, unsigned long len)
{
- mbedtls_md_finish(c, md);
- mbedtls_md_free(c);
- SAFE_FREE(c);
+ int rc = mbedtls_md_update(c, data, len);
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
}
-void sha256(const unsigned char *digest, int len, unsigned char *hash)
+int sha256_final(unsigned char *md, SHA256CTX c)
+{
+ int rc = mbedtls_md_finish(c, md);
+ sha256_ctx_free(c);
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int sha256(const unsigned char *digest, int len, unsigned char *hash)
{
const mbedtls_md_info_t *md_info =
mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
- if (md_info != NULL) {
- mbedtls_md(md_info, digest, len, hash);
+ int rc;
+
+ if (md_info == NULL) {
+ return SSH_ERROR;
+ }
+ rc = mbedtls_md(md_info, digest, len, hash);
+ if (rc != 0) {
+ return SSH_ERROR;
}
+ return SSH_OK;
}
SHA384CTX sha384_init(void)
@@ -264,25 +232,46 @@ SHA384CTX sha384_init(void)
return ctx;
}
-void sha384_update(SHA384CTX c, const void *data, unsigned long len)
+void
+sha384_ctx_free(SHA384CTX c)
{
- mbedtls_md_update(c, data, len);
+ mbedtls_md_free(c);
+ SAFE_FREE(c);
}
-void sha384_final(unsigned char *md, SHA384CTX c)
+int sha384_update(SHA384CTX c, const void *data, unsigned long len)
{
- mbedtls_md_finish(c, md);
- mbedtls_md_free(c);
- SAFE_FREE(c);
+ int rc = mbedtls_md_update(c, data, len);
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int sha384_final(unsigned char *md, SHA384CTX c)
+{
+ int rc = mbedtls_md_finish(c, md);
+ sha384_ctx_free(c);
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
}
-void sha384(const unsigned char *digest, int len, unsigned char *hash)
+int sha384(const unsigned char *digest, int len, unsigned char *hash)
{
const mbedtls_md_info_t *md_info =
mbedtls_md_info_from_type(MBEDTLS_MD_SHA384);
- if (md_info != NULL) {
- mbedtls_md(md_info, digest, len, hash);
+ int rc;
+
+ if (md_info == NULL) {
+ return SSH_ERROR;
}
+ rc = mbedtls_md(md_info, digest, len, hash);
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
}
SHA512CTX sha512_init(void)
@@ -317,25 +306,46 @@ SHA512CTX sha512_init(void)
return ctx;
}
-void sha512_update(SHA512CTX c, const void *data, unsigned long len)
+void
+sha512_ctx_free(SHA512CTX c)
{
- mbedtls_md_update(c, data, len);
+ mbedtls_md_free(c);
+ SAFE_FREE(c);
}
-void sha512_final(unsigned char *md, SHA512CTX c)
+int sha512_update(SHA512CTX c, const void *data, unsigned long len)
{
- mbedtls_md_finish(c, md);
- mbedtls_md_free(c);
- SAFE_FREE(c);
+ int rc = mbedtls_md_update(c, data, len);
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
}
-void sha512(const unsigned char *digest, int len, unsigned char *hash)
+int sha512_final(unsigned char *md, SHA512CTX c)
+{
+ int rc = mbedtls_md_finish(c, md);
+ sha512_ctx_free(c);
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int sha512(const unsigned char *digest, int len, unsigned char *hash)
{
const mbedtls_md_info_t *md_info =
mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
- if (md_info != NULL) {
- mbedtls_md(md_info, digest, len, hash);
+ int rc;
+
+ if (md_info == NULL) {
+ return SSH_ERROR;
+ }
+ rc = mbedtls_md(md_info, digest, len, hash);
+ if (rc != 0) {
+ return SSH_ERROR;
}
+ return SSH_OK;
}
MD5CTX md5_init(void)
@@ -370,16 +380,30 @@ MD5CTX md5_init(void)
return ctx;
}
+void
+md5_ctx_free(MD5CTX c)
+{
+ mbedtls_md_free(c);
+ SAFE_FREE(c);
+}
-void md5_update(MD5CTX c, const void *data, unsigned long len) {
- mbedtls_md_update(c, data, len);
+int md5_update(MD5CTX c, const void *data, unsigned long len)
+{
+ int rc = mbedtls_md_update(c, data, len);
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
}
-void md5_final(unsigned char *md, MD5CTX c)
+int md5_final(unsigned char *md, MD5CTX c)
{
- mbedtls_md_finish(c, md);
- mbedtls_md_free(c);
- SAFE_FREE(c);
+ int rc = mbedtls_md_finish(c, md);
+ md5_ctx_free(c);
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
}
int ssh_kdf(struct ssh_crypto_struct *crypto,
diff --git a/src/misc.c b/src/misc.c
index 0f1a7d49..6f5d2d60 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -32,6 +32,7 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
+#include <net/if.h>
#endif /* _WIN32 */
@@ -59,6 +60,7 @@
#include <ws2tcpip.h>
#include <shlobj.h>
#include <direct.h>
+#include <netioapi.h>
#ifdef HAVE_IO_H
#include <io.h>
@@ -94,6 +96,8 @@
#define ZLIB_STRING ""
#endif
+#define ARPA_DOMAIN_MAX_LEN 63
+
/**
* @defgroup libssh_misc The SSH helper functions.
* @ingroup libssh
@@ -214,22 +218,37 @@ int ssh_is_ipaddr_v4(const char *str) {
int ssh_is_ipaddr(const char *str) {
int rc = SOCKET_ERROR;
+ char *s = strdup(str);
- if (strchr(str, ':')) {
+ if (s == NULL) {
+ return -1;
+ }
+ if (strchr(s, ':')) {
struct sockaddr_storage ss;
int sslen = sizeof(ss);
-
- /* TODO link-local (IP:v6:addr%ifname). */
- rc = WSAStringToAddressA((LPSTR) str,
+ char *network_interface = strchr(s, '%');
+
+ /* link-local (IP:v6:addr%ifname). */
+ if (network_interface != NULL) {
+ rc = if_nametoindex(network_interface + 1);
+ if (rc == 0) {
+ free(s);
+ return 0;
+ }
+ *network_interface = '\0';
+ }
+ rc = WSAStringToAddressA((LPSTR) s,
AF_INET6,
NULL,
(struct sockaddr*)&ss,
&sslen);
if (rc == 0) {
+ free(s);
return 1;
}
}
+ free(s);
return ssh_is_ipaddr_v4(str);
}
#else /* _WIN32 */
@@ -333,17 +352,32 @@ int ssh_is_ipaddr_v4(const char *str) {
int ssh_is_ipaddr(const char *str) {
int rc = -1;
+ char *s = strdup(str);
- if (strchr(str, ':')) {
+ if (s == NULL) {
+ return -1;
+ }
+ if (strchr(s, ':')) {
struct in6_addr dest6;
-
- /* TODO link-local (IP:v6:addr%ifname). */
- rc = inet_pton(AF_INET6, str, &dest6);
+ char *network_interface = strchr(s, '%');
+
+ /* link-local (IP:v6:addr%ifname). */
+ if (network_interface != NULL) {
+ rc = if_nametoindex(network_interface + 1);
+ if (rc == 0) {
+ free(s);
+ return 0;
+ }
+ *network_interface = '\0';
+ }
+ rc = inet_pton(AF_INET6, s, &dest6);
if (rc > 0) {
+ free(s);
return 1;
}
}
+ free(s);
return ssh_is_ipaddr_v4(str);
}
@@ -1164,14 +1198,13 @@ char *ssh_path_expand_escape(ssh_session session, const char *s) {
x = strdup(session->opts.username);
break;
case 'p':
- if (session->opts.port < 65536) {
- char tmp[6];
-
- snprintf(tmp,
- sizeof(tmp),
- "%u",
- session->opts.port > 0 ? session->opts.port : 22);
- x = strdup(tmp);
+ {
+ char tmp[6];
+
+ snprintf(tmp, sizeof(tmp), "%hu",
+ (uint16_t)(session->opts.port > 0 ? session->opts.port
+ : 22));
+ x = strdup(tmp);
}
break;
default:
@@ -1735,4 +1768,70 @@ int ssh_newline_vis(const char *string, char *buf, size_t buf_len)
return out - buf;
}
+/**
+ * @brief Checks syntax of a domain name
+ *
+ * The check is made based on the RFC1035 section 2.3.1
+ * Allowed characters are: hyphen, period, digits (0-9) and letters (a-zA-Z)
+ *
+ * The label should be no longer than 63 characters
+ * The label should start with a letter and end with a letter or number
+ * The label in this implementation can start with a number to allow virtual
+ * URLs to pass. Note that this will make IPv4 addresses to pass
+ * this check too.
+ *
+ * @param hostname The domain name to be checked, has to be null terminated
+ *
+ * @return SSH_OK if the hostname passes syntax check
+ * SSH_ERROR otherwise or if hostname is NULL or empty string
+ */
+int ssh_check_hostname_syntax(const char *hostname)
+{
+ char *it = NULL, *s = NULL, *buf = NULL;
+ size_t it_len;
+ char c;
+
+ if (hostname == NULL || strlen(hostname) == 0) {
+ return SSH_ERROR;
+ }
+
+ /* strtok_r writes into the string, keep the input clean */
+ s = strdup(hostname);
+ if (s == NULL) {
+ return SSH_ERROR;
+ }
+
+ it = strtok_r(s, ".", &buf);
+ /* if the token has 0 length */
+ if (it == NULL) {
+ free(s);
+ return SSH_ERROR;
+ }
+ do {
+ it_len = strlen(it);
+ if (it_len > ARPA_DOMAIN_MAX_LEN ||
+ /* the first char must be a letter, but some virtual urls start
+ * with a number */
+ isalnum(it[0]) == 0 ||
+ isalnum(it[it_len - 1]) == 0) {
+ free(s);
+ return SSH_ERROR;
+ }
+ while (*it != '\0') {
+ c = *it;
+ /* the "." is allowed too, but tokenization removes it from the
+ * string */
+ if (isalnum(c) == 0 && c != '-') {
+ free(s);
+ return SSH_ERROR;
+ }
+ it++;
+ }
+ } while ((it = strtok_r(NULL, ".", &buf)) != NULL);
+
+ free(s);
+
+ return SSH_OK;
+}
+
/** @} */
diff --git a/src/options.c b/src/options.c
index b5f951ac..0890ff2e 100644
--- a/src/options.c
+++ b/src/options.c
@@ -36,6 +36,7 @@
#include "libssh/session.h"
#include "libssh/misc.h"
#include "libssh/options.h"
+#include "libssh/config_parser.h"
#ifdef WITH_SERVER
#include "libssh/server.h"
#include "libssh/bind.h"
@@ -490,33 +491,18 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_invalid(session);
return -1;
} else {
- q = strdup(value);
- if (q == NULL) {
- ssh_set_error_oom(session);
+ char *username = NULL, *hostname = NULL;
+ rc = ssh_config_parse_uri(value, &username, &hostname, NULL, true);
+ if (rc != SSH_OK) {
return -1;
}
- p = strchr(q, '@');
-
- SAFE_FREE(session->opts.host);
-
- if (p) {
- *p = '\0';
- session->opts.host = strdup(p + 1);
- if (session->opts.host == NULL) {
- SAFE_FREE(q);
- ssh_set_error_oom(session);
- return -1;
- }
-
+ if (username != NULL) {
SAFE_FREE(session->opts.username);
- session->opts.username = strdup(q);
- SAFE_FREE(q);
- if (session->opts.username == NULL) {
- ssh_set_error_oom(session);
- return -1;
- }
- } else {
- session->opts.host = q;
+ session->opts.username = username;
+ }
+ if (hostname != NULL) {
+ SAFE_FREE(session->opts.host);
+ session->opts.host = hostname;
}
}
break;
diff --git a/src/packet.c b/src/packet.c
index ec4a7203..82965fb3 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;
@@ -1304,6 +1309,19 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
}
#endif /* WITH_ZLIB */
payloadsize = ssh_buffer_get_len(session->in_buffer);
+ if (session->recv_seq == UINT32_MAX) {
+ /* Overflowing sequence numbers is always fishy */
+ if (crypto == NULL) {
+ /* don't allow sequence number overflow when unencrypted */
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Incoming sequence number overflow");
+ goto error;
+ } else {
+ SSH_LOG(SSH_LOG_WARNING,
+ "Incoming sequence number overflow");
+ }
+ }
session->recv_seq++;
if (crypto != NULL) {
struct ssh_cipher_struct *cipher = NULL;
@@ -1326,7 +1344,19 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
SSH_LOG(SSH_LOG_PACKET,
"packet: read type %hhd [len=%d,padding=%hhd,comp=%d,payload=%d]",
session->in_packet.type, packet_len, padding, compsize, payloadsize);
+ if (crypto == NULL) {
+ /* In strict kex, only a few packets are allowed. Taint the session
+ * if we received packets that are normally allowed but to be
+ * refused if we are in strict kex when KEX is over.
+ */
+ uint8_t type = session->in_packet.type;
+ if (type != SSH2_MSG_KEXINIT && type != SSH2_MSG_NEWKEYS &&
+ (type < SSH2_MSG_KEXDH_INIT ||
+ type > SSH2_MSG_KEX_DH_GEX_REQUEST)) {
+ session->flags |= SSH_SESSION_FLAG_KEX_TAINTED;
+ }
+ }
/* Check if the packet is expected */
filter_result = ssh_packet_incoming_filter(session);
@@ -1342,6 +1372,9 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
session->in_packet.type);
goto error;
case SSH_PACKET_UNKNOWN:
+ if (crypto == NULL) {
+ session->flags |= SSH_SESSION_FLAG_KEX_TAINTED;
+ }
ssh_packet_send_unimplemented(session, session->recv_seq - 1);
break;
}
@@ -1410,18 +1443,23 @@ static void ssh_packet_socket_controlflow_callback(int code, void *userdata)
}
}
-void ssh_packet_register_socket_callback(ssh_session session, ssh_socket s){
- session->socket_callbacks.data=ssh_packet_socket_callback;
- session->socket_callbacks.connected=NULL;
- session->socket_callbacks.controlflow = ssh_packet_socket_controlflow_callback;
- session->socket_callbacks.userdata=session;
- ssh_socket_set_callbacks(s,&session->socket_callbacks);
+void ssh_packet_register_socket_callback(ssh_session session, ssh_socket s)
+{
+ struct ssh_socket_callbacks_struct *callbacks = &session->socket_callbacks;
+
+ callbacks->data = ssh_packet_socket_callback;
+ callbacks->connected = NULL;
+ callbacks->controlflow = ssh_packet_socket_controlflow_callback;
+ callbacks->userdata = session;
+ ssh_socket_set_callbacks(s, callbacks);
}
/** @internal
* @brief sets the callbacks for the packet layer
*/
-void ssh_packet_set_callbacks(ssh_session session, ssh_packet_callbacks callbacks){
+void
+ssh_packet_set_callbacks(ssh_session session, ssh_packet_callbacks callbacks)
+{
if (session->packet_callbacks == NULL) {
session->packet_callbacks = ssh_list_new();
if (session->packet_callbacks == NULL) {
@@ -1435,8 +1473,11 @@ void ssh_packet_set_callbacks(ssh_session session, ssh_packet_callbacks callback
/** @internal
* @brief remove the callbacks from the packet layer
*/
-void ssh_packet_remove_callbacks(ssh_session session, ssh_packet_callbacks callbacks){
+void
+ssh_packet_remove_callbacks(ssh_session session, ssh_packet_callbacks callbacks)
+{
struct ssh_iterator *it = NULL;
+
it = ssh_list_find(session->packet_callbacks, callbacks);
if (it != NULL) {
ssh_list_remove(session->packet_callbacks, it);
@@ -1446,12 +1487,15 @@ void ssh_packet_remove_callbacks(ssh_session session, ssh_packet_callbacks callb
/** @internal
* @brief sets the default packet handlers
*/
-void ssh_packet_set_default_callbacks(ssh_session session){
- session->default_packet_callbacks.start=1;
- session->default_packet_callbacks.n_callbacks=sizeof(default_packet_handlers)/sizeof(ssh_packet_callback);
- session->default_packet_callbacks.user=session;
- session->default_packet_callbacks.callbacks=default_packet_handlers;
- ssh_packet_set_callbacks(session, &session->default_packet_callbacks);
+void ssh_packet_set_default_callbacks(ssh_session session)
+{
+ struct ssh_packet_callbacks_struct *c = &session->default_packet_callbacks;
+
+ c->start = 1;
+ c->n_callbacks = sizeof(default_packet_handlers) / sizeof(ssh_packet_callback);
+ c->user = session;
+ c->callbacks = default_packet_handlers;
+ ssh_packet_set_callbacks(session, c);
}
/** @internal
@@ -1505,7 +1549,33 @@ void ssh_packet_process(ssh_session session, uint8_t type)
SSH_LOG(SSH_LOG_RARE, "Failed to send unimplemented: %s",
ssh_get_error(session));
}
+ if (session->current_crypto == NULL) {
+ session->flags |= SSH_SESSION_FLAG_KEX_TAINTED;
+ }
+ }
+}
+
+/** @internal
+ * @brief sends a SSH_MSG_NEWKEYS when enabling the new negotiated ciphers
+ * @param session the SSH session
+ * @return SSH_ERROR on error, else SSH_OK
+ */
+int ssh_packet_send_newkeys(ssh_session session)
+{
+ int rc;
+
+ /* Send the MSG_NEWKEYS */
+ rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS);
+ if (rc < 0) {
+ return rc;
}
+
+ rc = ssh_packet_send(session);
+ if (rc == SSH_ERROR) {
+ return rc;
+ }
+ SSH_LOG(SSH_LOG_DEBUG, "SSH_MSG_NEWKEYS sent");
+ return rc;
}
/** @internal
@@ -1813,6 +1883,10 @@ int ssh_packet_send(ssh_session session)
if (rc == SSH_OK && type == SSH2_MSG_NEWKEYS) {
struct ssh_iterator *it;
+ if (session->flags & SSH_SESSION_FLAG_KEX_STRICT) {
+ /* reset packet sequence number when running in strict kex mode */
+ session->send_seq = 0;
+ }
for (it = ssh_list_get_iterator(session->out_queue);
it != NULL;
it = ssh_list_get_iterator(session->out_queue)) {
@@ -1926,7 +2000,7 @@ ssh_packet_set_newkeys(ssh_session session,
memcpy(session->next_crypto->session_id,
session->current_crypto->session_id,
session_id_len);
- session->next_crypto->session_id_len = session_id_len;
+ session->next_crypto->session_id_len = session_id_len;
return SSH_OK;
}
diff --git a/src/packet_cb.c b/src/packet_cb.c
index 39575b17..a08f1d8a 100644
--- a/src/packet_cb.c
+++ b/src/packet_cb.c
@@ -110,6 +110,18 @@ SSH_PACKET_CALLBACK(ssh_packet_newkeys){
goto error;
}
+ if (session->flags & SSH_SESSION_FLAG_KEX_STRICT) {
+ /* reset packet sequence number when running in strict kex mode */
+ session->recv_seq = 0;
+ /* Check that we aren't tainted */
+ if (session->flags & SSH_SESSION_FLAG_KEX_TAINTED) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Received unexpected packets in strict KEX mode.");
+ goto error;
+ }
+ }
+
if(session->server){
/* server things are done in server.c */
session->dh_handshake_state=DH_STATE_FINISHED;
@@ -156,6 +168,9 @@ SSH_PACKET_CALLBACK(ssh_packet_newkeys){
session->next_crypto->digest_len);
SSH_SIGNATURE_FREE(sig);
if (rc == SSH_ERROR) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Failed to verify server hostkey signature");
goto error;
}
SSH_LOG(SSH_LOG_PROTOCOL,"Signature verified and valid");
diff --git a/src/pki_crypto.c b/src/pki_crypto.c
index ec9cfa4b..1edae351 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;
}
diff --git a/src/server.c b/src/server.c
index 841a1c42..35e84465 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) {
@@ -191,7 +195,13 @@ int server_set_kex(ssh_session session)
}
}
- return 0;
+ /* Do not append the extensions during rekey */
+ if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED) {
+ return SSH_OK;
+ }
+
+ rc = ssh_kex_append_extensions(session, server);
+ return rc;
}
int ssh_server_init_kex(ssh_session session) {
@@ -335,117 +345,121 @@ 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_PROTOCOL,
- "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;
- }
+ 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_PROTOCOL,
+ "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;
+ }
- /* 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;
+ if (ssh_send_kex(session) < 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->flags & SSH_SESSION_FLAG_KEXINIT_SENT) == 0) {
+ if (server_set_kex(session) == 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 */
+ if (ssh_send_kex(session) < 0)
goto error;
- }
- if (crypt_set_algorithms_server(session) == SSH_ERROR)
+ }
+ ssh_list_kex(&session->next_crypto->client_kex); // log client kex
+ if (ssh_kex_select_methods(session) < 0) {
+ goto error;
+ }
+ if (crypt_set_algorithms_server(session) == 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;
}
/**
@@ -459,16 +473,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 int 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 +492,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 +509,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;
}
@@ -525,10 +541,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 +559,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 */
diff --git a/src/session.c b/src/session.c
index 3199096a..349373aa 100644
--- a/src/session.c
+++ b/src/session.c
@@ -643,6 +643,10 @@ int ssh_handle_packets(ssh_session session, int timeout) {
}
spoll = ssh_socket_get_poll_handle(session->socket);
+ if (spoll == NULL) {
+ ssh_set_error_oom(session);
+ return SSH_ERROR;
+ }
ssh_poll_add_events(spoll, POLLIN);
ctx = ssh_poll_get_ctx(spoll);
@@ -997,7 +1001,18 @@ int ssh_get_pubkey_hash(ssh_session session, unsigned char **hash)
*hash = NULL;
if (session->current_crypto == NULL ||
session->current_crypto->server_pubkey == NULL) {
- ssh_set_error(session,SSH_FATAL,"No current cryptographic context");
+ ssh_set_error(session, SSH_FATAL, "No current cryptographic context");
+ return SSH_ERROR;
+ }
+
+ rc = ssh_get_server_publickey(session, &pubkey);
+ if (rc != SSH_OK) {
+ return SSH_ERROR;
+ }
+
+ rc = ssh_pki_export_pubkey_blob(pubkey, &pubkey_blob);
+ ssh_key_free(pubkey);
+ if (rc != SSH_OK) {
return SSH_ERROR;
}
@@ -1012,25 +1027,21 @@ int ssh_get_pubkey_hash(ssh_session session, unsigned char **hash)
return SSH_ERROR;
}
- rc = ssh_get_server_publickey(session, &pubkey);
+ rc = md5_update(ctx,
+ ssh_string_data(pubkey_blob),
+ ssh_string_len(pubkey_blob));
if (rc != SSH_OK) {
- md5_final(h, ctx);
+ md5_ctx_free(ctx);
SAFE_FREE(h);
- return SSH_ERROR;
+ return rc;
}
-
- rc = ssh_pki_export_pubkey_blob(pubkey, &pubkey_blob);
- ssh_key_free(pubkey);
+ SSH_STRING_FREE(pubkey_blob);
+ rc = md5_final(h, ctx);
if (rc != SSH_OK) {
- md5_final(h, ctx);
SAFE_FREE(h);
- return SSH_ERROR;
+ return rc;
}
- md5_update(ctx, ssh_string_data(pubkey_blob), ssh_string_len(pubkey_blob));
- SSH_STRING_FREE(pubkey_blob);
- md5_final(h, ctx);
-
*hash = h;
return MD5_DIGEST_LEN;
@@ -1149,8 +1160,17 @@ int ssh_get_publickey_hash(const ssh_key key,
goto out;
}
- sha1_update(ctx, ssh_string_data(blob), ssh_string_len(blob));
- sha1_final(h, ctx);
+ rc = sha1_update(ctx, ssh_string_data(blob), ssh_string_len(blob));
+ if (rc != SSH_OK) {
+ free(h);
+ sha1_ctx_free(ctx);
+ goto out;
+ }
+ rc = sha1_final(h, ctx);
+ if (rc != SSH_OK) {
+ free(h);
+ goto out;
+ }
*hlen = SHA_DIGEST_LEN;
}
@@ -1172,8 +1192,17 @@ int ssh_get_publickey_hash(const ssh_key key,
goto out;
}
- sha256_update(ctx, ssh_string_data(blob), ssh_string_len(blob));
- sha256_final(h, ctx);
+ rc = sha256_update(ctx, ssh_string_data(blob), ssh_string_len(blob));
+ if (rc != SSH_OK) {
+ free(h);
+ sha256_ctx_free(ctx);
+ goto out;
+ }
+ rc = sha256_final(h, ctx);
+ if (rc != SSH_OK) {
+ free(h);
+ goto out;
+ }
*hlen = SHA256_DIGEST_LEN;
}
@@ -1203,8 +1232,17 @@ int ssh_get_publickey_hash(const ssh_key key,
goto out;
}
- md5_update(ctx, ssh_string_data(blob), ssh_string_len(blob));
- md5_final(h, ctx);
+ rc = md5_update(ctx, ssh_string_data(blob), ssh_string_len(blob));
+ if (rc != SSH_OK) {
+ free(h);
+ md5_ctx_free(ctx);
+ goto out;
+ }
+ rc = md5_final(h, ctx);
+ if (rc != SSH_OK) {
+ free(h);
+ goto out;
+ }
*hlen = MD5_DIGEST_LEN;
}
diff --git a/src/token.c b/src/token.c
index 0924d3bd..58b2f3e2 100644
--- a/src/token.c
+++ b/src/token.c
@@ -87,7 +87,7 @@ struct ssh_tokens_st *ssh_tokenize(const char *chain, char separator)
return NULL;
}
- tokens->buffer= strdup(chain);
+ tokens->buffer = strdup(chain);
if (tokens->buffer == NULL) {
goto error;
}
diff --git a/src/wrapper.c b/src/wrapper.c
index 05c820da..bbc75744 100644
--- a/src/wrapper.c
+++ b/src/wrapper.c
@@ -147,15 +147,16 @@ static void cipher_free(struct ssh_cipher_struct *cipher) {
SAFE_FREE(cipher);
}
-struct ssh_crypto_struct *crypto_new(void) {
- struct ssh_crypto_struct *crypto;
+struct ssh_crypto_struct *crypto_new(void)
+{
+ struct ssh_crypto_struct *crypto;
- crypto = malloc(sizeof(struct ssh_crypto_struct));
- if (crypto == NULL) {
- return NULL;
- }
- ZERO_STRUCTP(crypto);
- return crypto;
+ crypto = malloc(sizeof(struct ssh_crypto_struct));
+ if (crypto == NULL) {
+ return NULL;
+ }
+ ZERO_STRUCTP(crypto);
+ return crypto;
}
void crypto_free(struct ssh_crypto_struct *crypto)
diff --git a/tests/client/torture_auth.c b/tests/client/torture_auth.c
index 4b97f5da..a2364a2a 100644
--- a/tests/client/torture_auth.c
+++ b/tests/client/torture_auth.c
@@ -200,7 +200,8 @@ static int agent_teardown(void **state)
assert_non_null(ssh_agent_pidfile);
/* kill agent pid */
- torture_terminate_process(ssh_agent_pidfile);
+ rc = torture_terminate_process(ssh_agent_pidfile);
+ assert_return_code(rc, errno);
unlink(ssh_agent_pidfile);
@@ -551,6 +552,7 @@ static void torture_auth_cert(void **state) {
static void torture_auth_agent_cert(void **state)
{
+#if OPENSSH_VERSION_MAJOR < 8 || (OPENSSH_VERSION_MAJOR == 8 && OPENSSH_VERSION_MINOR == 0)
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
int rc;
@@ -570,6 +572,7 @@ static void torture_auth_agent_cert(void **state)
"ssh-rsa-cert-v01@openssh.com");
assert_int_equal(rc, SSH_OK);
}
+#endif /* OPENSSH_VERSION_MAJOR < 8.1 */
/* Setup loads a different key, tests are exactly the same. */
torture_auth_agent(state);
@@ -577,6 +580,7 @@ static void torture_auth_agent_cert(void **state)
static void torture_auth_agent_cert_nonblocking(void **state)
{
+#if OPENSSH_VERSION_MAJOR < 8 || (OPENSSH_VERSION_MAJOR == 8 && OPENSSH_VERSION_MINOR == 0)
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
int rc;
@@ -596,6 +600,7 @@ static void torture_auth_agent_cert_nonblocking(void **state)
"ssh-rsa-cert-v01@openssh.com");
assert_int_equal(rc, SSH_OK);
}
+#endif /* OPENSSH_VERSION_MAJOR < 8.1 */
torture_auth_agent_nonblocking(state);
}
diff --git a/tests/client/torture_proxycommand.c b/tests/client/torture_proxycommand.c
index c04ff2ab..dc17f3d8 100644
--- a/tests/client/torture_proxycommand.c
+++ b/tests/client/torture_proxycommand.c
@@ -161,6 +161,56 @@ static void torture_options_set_proxycommand_ssh_stderr(void **state)
assert_int_equal(rc & O_RDWR, O_RDWR);
}
+static void torture_options_proxycommand_injection(void **state)
+{
+ struct torture_state *s = *state;
+ struct passwd *pwd = NULL;
+ const char *malicious_host = "`echo foo > mfile`";
+ const char *command = "nc %h %p";
+ char *current_dir = NULL;
+ char *malicious_file_path = NULL;
+ int mfp_len;
+ int verbosity = torture_libssh_verbosity();
+ struct stat sb;
+ int rc;
+
+ pwd = getpwnam("bob");
+ assert_non_null(pwd);
+
+ rc = setuid(pwd->pw_uid);
+ assert_return_code(rc, errno);
+
+ s->ssh.session = ssh_new();
+ assert_non_null(s->ssh.session);
+
+ ssh_options_set(s->ssh.session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
+ // if we would be checking the rc, this should fail
+ ssh_options_set(s->ssh.session, SSH_OPTIONS_HOST, malicious_host);
+
+ ssh_options_set(s->ssh.session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE);
+
+ rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_PROXYCOMMAND, command);
+ assert_int_equal(rc, 0);
+ rc = ssh_connect(s->ssh.session);
+ assert_ssh_return_code_equal(s->ssh.session, rc, SSH_ERROR);
+
+ current_dir = torture_get_current_working_dir();
+ assert_non_null(current_dir);
+ mfp_len = strlen(current_dir) + 6;
+ malicious_file_path = malloc(mfp_len);
+ assert_non_null(malicious_file_path);
+ rc = snprintf(malicious_file_path, mfp_len,
+ "%s/mfile", current_dir);
+ assert_int_equal(rc, mfp_len);
+ free(current_dir);
+ rc = stat(malicious_file_path, &sb);
+ assert_int_not_equal(rc, 0);
+
+ // cleanup
+ remove(malicious_file_path);
+ free(malicious_file_path);
+}
+
int torture_run_tests(void) {
int rc;
struct CMUnitTest tests[] = {
@@ -176,6 +226,9 @@ int torture_run_tests(void) {
cmocka_unit_test_setup_teardown(torture_options_set_proxycommand_ssh_stderr,
session_setup,
session_teardown),
+ cmocka_unit_test_setup_teardown(torture_options_proxycommand_injection,
+ NULL,
+ session_teardown),
};
diff --git a/tests/client/torture_rekey.c b/tests/client/torture_rekey.c
index 46a015db..bfb273af 100644
--- a/tests/client/torture_rekey.c
+++ b/tests/client/torture_rekey.c
@@ -38,6 +38,8 @@
#include <fcntl.h>
#include <pwd.h>
+#define KEX_RETRY 32
+
static uint64_t bytes = 2048; /* 2KB (more than the authentication phase) */
static int sshd_setup(void **state)
@@ -146,6 +148,29 @@ static void torture_rekey_default(void **state)
ssh_disconnect(s->ssh.session);
}
+static void sanity_check_session(void **state)
+{
+ struct torture_state *s = *state;
+ struct ssh_crypto_struct *c = NULL;
+
+ c = s->ssh.session->current_crypto;
+ assert_non_null(c);
+ assert_int_equal(c->in_cipher->max_blocks,
+ bytes / c->in_cipher->blocksize);
+ assert_int_equal(c->out_cipher->max_blocks,
+ bytes / c->out_cipher->blocksize);
+ /* when strict kex is used, the newkeys reset the sequence number */
+ if ((s->ssh.session->flags & SSH_SESSION_FLAG_KEX_STRICT) != 0) {
+ assert_int_equal(c->out_cipher->packets, s->ssh.session->send_seq);
+ assert_int_equal(c->in_cipher->packets, s->ssh.session->recv_seq);
+ } else {
+ /* Otherwise we have less encrypted packets than transferred
+ * (first are not encrypted) */
+ assert_true(c->out_cipher->packets < s->ssh.session->send_seq);
+ assert_true(c->in_cipher->packets < s->ssh.session->recv_seq);
+ }
+}
+
/* We lower the rekey limits manually and check that the rekey
* really happens when sending data
*/
@@ -164,16 +189,10 @@ static void torture_rekey_send(void **state)
rc = ssh_connect(s->ssh.session);
assert_ssh_return_code(s->ssh.session, rc);
- /* The blocks limit is set correctly */
- c = s->ssh.session->current_crypto;
- assert_int_equal(c->in_cipher->max_blocks,
- bytes / c->in_cipher->blocksize);
- assert_int_equal(c->out_cipher->max_blocks,
- bytes / c->out_cipher->blocksize);
- /* We should have less encrypted packets than transfered (first are not encrypted) */
- assert_true(c->out_cipher->packets < s->ssh.session->send_seq);
- assert_true(c->in_cipher->packets < s->ssh.session->recv_seq);
+ sanity_check_session(state);
/* Copy the initial secret hash = session_id so we know we changed keys later */
+ c = s->ssh.session->current_crypto;
+ assert_non_null(c);
secret_hash = malloc(c->digest_len);
assert_non_null(secret_hash);
memcpy(secret_hash, c->secret_hash, c->digest_len);
@@ -190,10 +209,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);
}
@@ -269,14 +289,10 @@ static void torture_rekey_recv(void **state)
sftp_file file;
mode_t mask;
- /* The blocks limit is set correctly */
- c = s->ssh.session->current_crypto;
- assert_int_equal(c->in_cipher->max_blocks, bytes / c->in_cipher->blocksize);
- assert_int_equal(c->out_cipher->max_blocks, bytes / c->out_cipher->blocksize);
- /* We should have less encrypted packets than transfered (first are not encrypted) */
- assert_true(c->out_cipher->packets < s->ssh.session->send_seq);
- assert_true(c->in_cipher->packets < s->ssh.session->recv_seq);
+ sanity_check_session(state);
/* Copy the initial secret hash = session_id so we know we changed keys later */
+ c = s->ssh.session->current_crypto;
+ assert_non_null(c);
secret_hash = malloc(c->digest_len);
assert_non_null(secret_hash);
memcpy(secret_hash, c->secret_hash, c->digest_len);
@@ -461,15 +477,10 @@ static void torture_rekey_different_kex(void **state)
assert_ssh_return_code(s->ssh.session, rc);
/* The blocks limit is set correctly */
- c = s->ssh.session->current_crypto;
- assert_int_equal(c->in_cipher->max_blocks,
- bytes / c->in_cipher->blocksize);
- assert_int_equal(c->out_cipher->max_blocks,
- bytes / c->out_cipher->blocksize);
- /* We should have less encrypted packets than transfered (first are not encrypted) */
- assert_true(c->out_cipher->packets < s->ssh.session->send_seq);
- assert_true(c->in_cipher->packets < s->ssh.session->recv_seq);
+ sanity_check_session(state);
/* Copy the initial secret hash = session_id so we know we changed keys later */
+ c = s->ssh.session->current_crypto;
+ assert_non_null(c);
secret_hash = malloc(c->digest_len);
assert_non_null(secret_hash);
memcpy(secret_hash, c->secret_hash, c->digest_len);
@@ -496,9 +507,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 */
@@ -568,9 +585,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 */
@@ -651,6 +674,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 +780,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();
diff --git a/tests/client/torture_session.c b/tests/client/torture_session.c
index b5ed7a62..abe458d0 100644
--- a/tests/client/torture_session.c
+++ b/tests/client/torture_session.c
@@ -118,12 +118,47 @@ static void torture_channel_read_error(void **state) {
ssh_channel_free(channel);
}
+static void torture_pubkey_hash(void **state)
+{
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ char *hash = NULL;
+ char *hexa = NULL;
+ int rc = 0;
+
+ /* bad arguments */
+ rc = ssh_get_pubkey_hash(session, NULL);
+ assert_int_equal(rc, SSH_ERROR);
+
+ rc = ssh_get_pubkey_hash(NULL, (unsigned char **)&hash);
+ assert_int_equal(rc, SSH_ERROR);
+
+ /* deprecated, but should be covered by tests! */
+ rc = ssh_get_pubkey_hash(session, (unsigned char **)&hash);
+ if (ssh_fips_mode()) {
+ /* When in FIPS mode, expect the call to fail */
+ assert_int_equal(rc, SSH_ERROR);
+ } else {
+ assert_int_equal(rc, MD5_DIGEST_LEN);
+
+ hexa = ssh_get_hexa((unsigned char *)hash, rc);
+ SSH_STRING_FREE_CHAR(hash);
+ assert_string_equal(hexa,
+ "ee:80:7f:61:f9:d5:be:f1:96:86:cc:96:7a:db:7a:7b");
+
+ SSH_STRING_FREE_CHAR(hexa);
+ }
+}
+
int torture_run_tests(void) {
int rc;
struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(torture_channel_read_error,
session_setup,
session_teardown),
+ cmocka_unit_test_setup_teardown(torture_pubkey_hash,
+ session_setup,
+ session_teardown),
};
ssh_init();
diff --git a/tests/keys/certauth/id_rsa b/tests/keys/certauth/id_rsa
index aa86ac2b..a0b679c2 100644
--- a/tests/keys/certauth/id_rsa
+++ b/tests/keys/certauth/id_rsa
@@ -1,27 +1,27 @@
------BEGIN RSA PRIVATE KEY-----
-MIIEiwIBAAKB/QMTSsCQqarOIauonYgjAt8E+lgSWBU/43ITyDDzLM4IS4wCcqXB
-1Fagz386FU1B2AcUqlPZ1+7RlaXkqgKr4nGHv00U/GG+YAUgUAw1G12kI4cvrnWr
-FIXwcq+VTJNej5pHxEqcRLw7ZBorpqm2UsY5KLr5R3uMNap7koj1Hbt9lKsvfDn6
-HjM4qY0ygx8hxf/4wCzIh5V4k9/UAMkqI2CM9c3yEE2aWh/4MDOnAFj+0T2sMAo8
-jyOZ6v+W7hmEtsUc9mEv+5B+hhVeYO/RwxketJAQRPYDSPSi1mjtv9fnzGk15q/l
-Hb2V/HP/pyIpao19A4daR0a4ia9Hk4UCAwEAAQKB/QKEaPxjrKzlWoQSWRdUaQY5
-Idyy7yw9hiMa9BK1COh/u66XVlY86Fwb9puR5Fu/WF67WIuX1PpizJXkLBBRtuDs
-lvY2BjrPQ/MONtc3JPYp4vbFXYxtAzh6zrTPhMVfcjV7Jr1XWZ+lEVOmhR2G4gvk
-P2WDozIKWub3jMLTt4afgHCGaKfKEUpKjFkiAalz8oLVv8qV1FVPPDT2PWeKMuE3
-XfoN7YUaP6+aPlNnjIv/3BDsrPsiKZ+AKXcERdPvVQa/LypzW08cqC6sIJKWVmQI
-3KgoYs9VvbDXfQ8jKfcsTApZkSDaLX6tf3Ei+76R0lbV4L1rpypa25qj9YECfwHP
-N+v/6yObJFL5/1rEuT7CFbfP8g5J8qUVufcPRKv//ChluLuWNxgLJmIv2ZffWwhe
-GKHlT98QPgFvsMSOyLeut4beZYKDSeVNvEt9eCBjOax2jOBGo3hv8j/Fs8yAfZOV
-Ardv2qUszubM+DVwjJzb3vaZyEesRucJISqkJeUCfwGzGdMp0LXrZ7aaQGgHj/P2
-DKGq0E2gnj/EBapatjxKm4hMRn/vkTWjCDCryTnvJqkW/00tr4GqWXoeilBFD270
-RcvbOe9LQmGlHIYzgwc5nfLDBQyeNnHRmkeD9LQRUfdTdHj4jf+35pHlsVUT0Rnl
-IMNoRA6V07bySFdI3SECfiF+1rbrxuhaCRIA0Ax3pL0eGuuTgksAm8VlbCMTgSiC
-kF1CrXXgSAHOZb02C9Bf4cwEFfjh/KxM/4eXDa+Rfg7JQJxmVLivqEAlxIOvIxBp
-xDnSWAljmrrllozyQnBsJDbbOm6BLf5+e5wIuryHvnP7vHNEU0J24g/78PxrrQJ/
-AVD4OzYzUfESzbUBFJBmyIZSmhJ0aOpwJOpniNvgLymI8zI/l22uhF/TQ/6HRbsV
-sfcBmoA7YKzRx2ZHsIsLvN6p/4u1fsJGkuERCk5yt/HDhfPLwU321IeEeMaVia+w
-T1/u4JF/SADhLTU69az3UJrHmQ7zRmh7I0DZDeB8gQJ+XqIqutPeerNtbqMjXGW8
-TdpqZAzAQAv6dPgaH0W0OzJe2hP9uy0D84H5f8Im/irJh/AXo/QL3obXqopyeLf0
-HfcUUnEZEBPlqsirZFtPClD+HL6Orf1je0oVV/aQssPkQl6/aXBNd+kS27U3NBML
-LmRhC4+Q+/M5MlRggLtn
------END RSA PRIVATE KEY-----
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn
+NhAAAAAwEAAQAAAQEA0DehrU/ohoimMKojFXdo1uEAcqx4fS87AjDUz8t4s436ppP+0+U3
++qrhOCE/mXZvXewjTHltmEtCHNSbsWhYTjwrEQUDRNOVahn2PEQTcX/9itvsv9PX79Imbv
+ZsLR0f1FsmorkWpnDQmuga7hYBFBj4sV+VML5ieoK2OraUEq46ILsxqRgUTetkErlzX7S2
+SPE7vNM0ahmA+HNBuKNUD+BOtCzkqN54flGA9TZ7kapC7xqiRHK+ZzahQ2PFR4BxbVP1uT
+DsanbjKOpBC4hISao3hi4iUnyj0gJ8itmkhQS+oI/2KWSGW01/k9W7jOUXDSt7LGUTSW6s
+ILYHzmefCwAAA9B/6IFvf+iBbwAAAAdzc2gtcnNhAAABAQDQN6GtT+iGiKYwqiMVd2jW4Q
+ByrHh9LzsCMNTPy3izjfqmk/7T5Tf6quE4IT+Zdm9d7CNMeW2YS0Ic1JuxaFhOPCsRBQNE
+05VqGfY8RBNxf/2K2+y/09fv0iZu9mwtHR/UWyaiuRamcNCa6BruFgEUGPixX5UwvmJ6gr
+Y6tpQSrjoguzGpGBRN62QSuXNftLZI8Tu80zRqGYD4c0G4o1QP4E60LOSo3nh+UYD1NnuR
+qkLvGqJEcr5nNqFDY8VHgHFtU/W5MOxqduMo6kELiEhJqjeGLiJSfKPSAnyK2aSFBL6gj/
+YpZIZbTX+T1buM5RcNK3ssZRNJbqwgtgfOZ58LAAAAAwEAAQAAAQEAxjzxFU0LGWtortSN
+apaxnkPCZWuHm8gn6kILm3shg/IdPhORfrSxw1qF6ybcooN8LHPyd5D0oxaj70cMpK+vw2
+zNo/qdzh2UF9x375Dw4hL1lgslMM3EvXPbW7IJ9DnSYCAYfLyzr+ug8JsjaKJSjIvp2xYh
+uLLKl9FzJhtGhzDaJr9FCbSmd5R7Telz4En0Lwo/VxYvyzCoRwzhVUVqJZpdtF7/1du4tT
+agfPzPYY9zM9muR7AawtzMc4UFvMzl1OtjOHYtqSMVBZx44fRpXT3/fy7A98+7erd0zTWj
+s6gaz6I8VmRPk4iTdBH4KBzC8dGZQNMrY9SQ/ZANet8eYQAAAIBI8hS6bX00NpNXwOaEqr
+jZKf/u1W71KHpYBAY1w3xanGqPVOX5PEsFH6NLjqSLF75Bk22pvJvZ8EAaoISyvLSqqO5t
+1vvjCjVKALSaVIFDcA20NpCXRugmVT1HeQNKHCTt3yOoraL8Sh9wlRbxLmlxgISYS6uH6e
+dEPa6qFshMVwAAAIEA7RFx7+mZJfrSJUu9pYiZJc6+Ns2WrSA2mgI+mIhWqreDK4Kw0a5g
+akqD0mb9oPHySnf3lCe+17yqNxH2fcX0G3B5LxiRnFVNm2wC4ZGvb+yMU2+0uSI/Sf8L1N
+sfWm+z4VC93Qhe0fIpuk8JAMNOwCvFcEFu5rr9sxHtPWjWtj0AAACBAODYXjs6jqsvW+3P
+e0efFp9kIezi8CejSxVXX0/zmWMCpTw1laiUmK41coKTKBSDZcNgrsF/ns1uCrPg6C2u/y
+evF8J+DeqU3vo1QhnRAJA2fLZk1Dr/GfAsp9mS9w6FdIQiQjQ0f/X9rYgAr9x4qMogYgrG
+zkb7k6FUoGgD7sbnAAAAE2xpYnNzaF90b3J0dXJlX2F1dGgBAgMEBQYH
+-----END OPENSSH PRIVATE KEY-----
diff --git a/tests/keys/certauth/id_rsa-cert.pub b/tests/keys/certauth/id_rsa-cert.pub
index 615d39f0..ceb44039 100644
--- a/tests/keys/certauth/id_rsa-cert.pub
+++ b/tests/keys/certauth/id_rsa-cert.pub
@@ -1 +1 @@
-ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgHZLan4ufbTFWr8Hl/8JvZTLYa0eNNm2qov9zPlK7qfwAAAADAQABAAAA/QMTSsCQqarOIauonYgjAt8E+lgSWBU/43ITyDDzLM4IS4wCcqXB1Fagz386FU1B2AcUqlPZ1+7RlaXkqgKr4nGHv00U/GG+YAUgUAw1G12kI4cvrnWrFIXwcq+VTJNej5pHxEqcRLw7ZBorpqm2UsY5KLr5R3uMNap7koj1Hbt9lKsvfDn6HjM4qY0ygx8hxf/4wCzIh5V4k9/UAMkqI2CM9c3yEE2aWh/4MDOnAFj+0T2sMAo8jyOZ6v+W7hmEtsUc9mEv+5B+hhVeYO/RwxketJAQRPYDSPSi1mjtv9fnzGk15q/lHb2V/HP/pyIpao19A4daR0a4ia9Hk4UAAAAAAAAAAAAAAAEAAAATdG9ydHVyZV9hdXRoX2NhcmxvcwAAAAkAAAAFYWxpY2UAAAAAAAAAAP//////////AAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAARcAAAAHc3NoLXJzYQAAAAMBAAEAAAEBAKcDafm8fNluz8a9GQaWgk1XUJcchLleeubTke6xQlJbI+rcjWIIwd1gDuh7Mdr0YIVhsh6dpg/L4bpRJBGNhDPxK8BmjTpIU14lKxrWQAirHN09P2QGtGtgrf09lA+xhV9E+pkF2Zz6PCt/P3sgUQnJcwjjsWhMaSASrt67fPanH+10hnfgjkevkMMHGJxmLiOW7JFQkd9I+gHHKEXs6Q9fhtiStzr3WN4hAPG5uXrnRZgseAV9p3TFPMEgUTpdRvnkOnkCBF169KiyjU97QgoXHExWk/rrgsJtgrTou/qRyi18WWm9S1HXLHyNOgZxKirmxLNPC9dIcJBD1kDWG8UAAAEPAAAAB3NzaC1yc2EAAAEAhNLOXT0jyz/Web0HUyrtPCvUZsLkDyBWCNoNTfsxGVoYsE4WCpNwqQO1A4NT5AtIE+R7rn9wfjvXM7sYh6hJyq3HVEWhts1SkQVU7sQBrImTIrj2cWKR3gmQ+ehsgNFGhcFZTK77ugw1fMfzZRvKVTkRWhe6v92wQOtkoINtf3f1fK6xY+vLwAA/E4VdaRJmhwAaNpy3PfMAJytkCLjcjUSWHYDha4hs98/EBPduGNNNiZdyG7lcpSvvq9HBDxzOiHBa/We9m38/Dk4TNVkZ/wrtBFQxH75if6SgGa/feGJrKQHBru7sPh8dO4R1AmZaoLmRzMnzZOtB0oEXmBqHmw== libssh_torture_auth
+ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgllK1Wz9hM1kUks5QXU/vXbDmzpQWMtFWObMvi9ymg38AAAADAQABAAABAQDQN6GtT+iGiKYwqiMVd2jW4QByrHh9LzsCMNTPy3izjfqmk/7T5Tf6quE4IT+Zdm9d7CNMeW2YS0Ic1JuxaFhOPCsRBQNE05VqGfY8RBNxf/2K2+y/09fv0iZu9mwtHR/UWyaiuRamcNCa6BruFgEUGPixX5UwvmJ6grY6tpQSrjoguzGpGBRN62QSuXNftLZI8Tu80zRqGYD4c0G4o1QP4E60LOSo3nh+UYD1NnuRqkLvGqJEcr5nNqFDY8VHgHFtU/W5MOxqduMo6kELiEhJqjeGLiJSfKPSAnyK2aSFBL6gj/YpZIZbTX+T1buM5RcNK3ssZRNJbqwgtgfOZ58LAAAAAAAAAAAAAAABAAAAE3RvcnR1cmVfYXV0aF9jYXJsb3MAAAAJAAAABWFsaWNlAAAAAAAAAAD//////////wAAAAAAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAEXAAAAB3NzaC1yc2EAAAADAQABAAABAQCnA2n5vHzZbs/GvRkGloJNV1CXHIS5Xnrm05HusUJSWyPq3I1iCMHdYA7oezHa9GCFYbIenaYPy+G6USQRjYQz8SvAZo06SFNeJSsa1kAIqxzdPT9kBrRrYK39PZQPsYVfRPqZBdmc+jwrfz97IFEJyXMI47FoTGkgEq7eu3z2px/tdIZ34I5Hr5DDBxicZi4jluyRUJHfSPoBxyhF7OkPX4bYkrc691jeIQDxubl650WYLHgFfad0xTzBIFE6XUb55Dp5AgRdevSoso1Pe0IKFxxMVpP664LCbYK06Lv6kcotfFlpvUtR1yx8jToGcSoq5sSzTwvXSHCQQ9ZA1hvFAAABFAAAAAxyc2Etc2hhMi01MTIAAAEAeI9eUAWyL5DVWBj0vU2kKdGxMXQx1Y8R68DXwXhnxfLwilJPa6IFg+g988lpF5aZzvAiX6TgDtJAhzfuBU+ZREGfdclUQIpz3xwDG76Gmg/DpQHdmqU76n2Na32s6+4SsSmWWKx6cPPdjbCRS0VMSrMohLuDyPGMoC7RjLfDDxqW5TIbMtqQdOiPl/0PpR73Q0FjB50Ec12buQDkExlEOi2Y+yB830vuTJN3ds7bx6NXM1Jjftg/8D0SzNRAIYDQFnpyXKO6kNrN66o48E3mrnVHXuFBTf+kpdYrK+1LKQVk/hVLBHr+NuVmQltL0zcjJfiXj7i5ZqcsqR1UAU5hKg== libssh_torture_auth
diff --git a/tests/keys/certauth/id_rsa.pub b/tests/keys/certauth/id_rsa.pub
index 4cbfc4ce..3f83c6fc 100644
--- a/tests/keys/certauth/id_rsa.pub
+++ b/tests/keys/certauth/id_rsa.pub
@@ -1 +1 @@
-ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAA/QMTSsCQqarOIauonYgjAt8E+lgSWBU/43ITyDDzLM4IS4wCcqXB1Fagz386FU1B2AcUqlPZ1+7RlaXkqgKr4nGHv00U/GG+YAUgUAw1G12kI4cvrnWrFIXwcq+VTJNej5pHxEqcRLw7ZBorpqm2UsY5KLr5R3uMNap7koj1Hbt9lKsvfDn6HjM4qY0ygx8hxf/4wCzIh5V4k9/UAMkqI2CM9c3yEE2aWh/4MDOnAFj+0T2sMAo8jyOZ6v+W7hmEtsUc9mEv+5B+hhVeYO/RwxketJAQRPYDSPSi1mjtv9fnzGk15q/lHb2V/HP/pyIpao19A4daR0a4ia9Hk4U= libssh_torture_auth
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDQN6GtT+iGiKYwqiMVd2jW4QByrHh9LzsCMNTPy3izjfqmk/7T5Tf6quE4IT+Zdm9d7CNMeW2YS0Ic1JuxaFhOPCsRBQNE05VqGfY8RBNxf/2K2+y/09fv0iZu9mwtHR/UWyaiuRamcNCa6BruFgEUGPixX5UwvmJ6grY6tpQSrjoguzGpGBRN62QSuXNftLZI8Tu80zRqGYD4c0G4o1QP4E60LOSo3nh+UYD1NnuRqkLvGqJEcr5nNqFDY8VHgHFtU/W5MOxqduMo6kELiEhJqjeGLiJSfKPSAnyK2aSFBL6gj/YpZIZbTX+T1buM5RcNK3ssZRNJbqwgtgfOZ58L libssh_torture_auth
diff --git a/tests/pkd/pkd_keyutil.c b/tests/pkd/pkd_keyutil.c
index 3991bcbb..533d2788 100644
--- a/tests/pkd/pkd_keyutil.c
+++ b/tests/pkd/pkd_keyutil.c
@@ -22,7 +22,7 @@
#include "pkd_keyutil.h"
#include "pkd_util.h"
-void setup_rsa_key() {
+void setup_rsa_key(void) {
int rc = 0;
if (access(LIBSSH_RSA_TESTKEY, F_OK) != 0) {
rc = system_checked(OPENSSH_KEYGEN " -t rsa -q -N \"\" -f "
@@ -31,7 +31,7 @@ void setup_rsa_key() {
assert_int_equal(rc, 0);
}
-void setup_ed25519_key() {
+void setup_ed25519_key(void) {
int rc = 0;
if (access(LIBSSH_ED25519_TESTKEY, F_OK) != 0) {
rc = system_checked(OPENSSH_KEYGEN " -t ed25519 -q -N \"\" -f "
@@ -41,7 +41,7 @@ void setup_ed25519_key() {
}
#ifdef HAVE_DSA
-void setup_dsa_key() {
+void setup_dsa_key(void) {
int rc = 0;
if (access(LIBSSH_DSA_TESTKEY, F_OK) != 0) {
rc = system_checked(OPENSSH_KEYGEN " -t dsa -q -N \"\" -f "
@@ -51,7 +51,7 @@ void setup_dsa_key() {
}
#endif
-void setup_ecdsa_keys() {
+void setup_ecdsa_keys(void) {
int rc = 0;
if (access(LIBSSH_ECDSA_256_TESTKEY, F_OK) != 0) {
@@ -71,27 +71,27 @@ void setup_ecdsa_keys() {
}
}
-void cleanup_rsa_key() {
+void cleanup_rsa_key(void) {
cleanup_key(LIBSSH_RSA_TESTKEY);
}
-void cleanup_ed25519_key() {
+void cleanup_ed25519_key(void) {
cleanup_key(LIBSSH_ED25519_TESTKEY);
}
#ifdef HAVE_DSA
-void cleanup_dsa_key() {
+void cleanup_dsa_key(void) {
cleanup_key(LIBSSH_DSA_TESTKEY);
}
#endif
-void cleanup_ecdsa_keys() {
+void cleanup_ecdsa_keys(void) {
cleanup_key(LIBSSH_ECDSA_256_TESTKEY);
cleanup_key(LIBSSH_ECDSA_384_TESTKEY);
cleanup_key(LIBSSH_ECDSA_521_TESTKEY);
}
-void setup_openssh_client_keys() {
+void setup_openssh_client_keys(void) {
int rc = 0;
if (access(OPENSSH_CA_TESTKEY, F_OK) != 0) {
@@ -184,7 +184,7 @@ void setup_openssh_client_keys() {
}
}
-void cleanup_openssh_client_keys() {
+void cleanup_openssh_client_keys(void) {
cleanup_key(OPENSSH_CA_TESTKEY);
cleanup_key(OPENSSH_RSA_TESTKEY);
cleanup_file(OPENSSH_RSA_TESTKEY "-sha256-cert.pub");
@@ -199,7 +199,7 @@ void cleanup_openssh_client_keys() {
}
}
-void setup_dropbear_client_rsa_key() {
+void setup_dropbear_client_rsa_key(void) {
int rc = 0;
if (access(DROPBEAR_RSA_TESTKEY, F_OK) != 0) {
rc = system_checked(DROPBEAR_KEYGEN " -t rsa -f "
@@ -208,6 +208,6 @@ void setup_dropbear_client_rsa_key() {
assert_int_equal(rc, 0);
}
-void cleanup_dropbear_client_rsa_key() {
+void cleanup_dropbear_client_rsa_key(void) {
unlink(DROPBEAR_RSA_TESTKEY);
}
diff --git a/tests/server/torture_server_config.c b/tests/server/torture_server_config.c
index d751dd74..b3d63eec 100644
--- a/tests/server/torture_server_config.c
+++ b/tests/server/torture_server_config.c
@@ -285,9 +285,7 @@ static int stop_server(void **state)
assert_non_null(s);
rc = torture_terminate_process(s->srv_pidfile);
- if (rc != 0) {
- fprintf(stderr, "XXXXXX Failed to terminate sshd\n");
- }
+ assert_return_code(rc, errno);
unlink(s->srv_pidfile);
@@ -513,6 +511,12 @@ static void torture_server_config_ciphers(void **state)
/* Try each algorithm individually */
j = 0;
while(tokens->tokens[j] != NULL) {
+ char *cmp = strstr(OPENSSH_CIPHERS, tokens->tokens[j]);
+ if (cmp == NULL) {
+ /* This cipher is not supported by the OpenSSH. Skip it */
+ j++;
+ continue;
+ }
snprintf(config_content,
sizeof(config_content),
"HostKey %s\nCiphers %s\n",
diff --git a/tests/torture.c b/tests/torture.c
index b3ea36b4..f485c070 100644
--- a/tests/torture.c
+++ b/tests/torture.c
@@ -250,8 +250,12 @@ int torture_terminate_process(const char *pidfile)
rc = kill(pid, 0);
if (rc != 0) {
- is_running = 0;
- break;
+ /* Process not found */
+ if (errno == ESRCH) {
+ is_running = 0;
+ rc = 0;
+ break;
+ }
}
}
@@ -260,7 +264,7 @@ int torture_terminate_process(const char *pidfile)
"WARNING: The process with pid %u is still running!\n", pid);
}
- return 0;
+ return rc;
}
ssh_session torture_ssh_session(struct torture_state *s,
@@ -856,21 +860,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);
@@ -882,7 +881,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);
}
@@ -922,29 +934,12 @@ 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);
- if (rc != 0) {
- fprintf(stderr,
- "ERROR: SSHD process %u died during reload!\n", pid);
- return SSH_ERROR;
- }
+ rc = torture_terminate_process(s->srv_pidfile);
+ assert_return_code(rc, errno);
- /* 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
@@ -980,9 +975,7 @@ void torture_teardown_sshd_server(void **state)
int rc;
rc = torture_terminate_process(s->srv_pidfile);
- if (rc != 0) {
- fprintf(stderr, "XXXXXX Failed to terminate sshd\n");
- }
+ assert_return_code(rc, errno);
torture_teardown_socket_dir(state);
}
diff --git a/tests/unittests/torture_config.c b/tests/unittests/torture_config.c
index f91112a9..d8097e79 100644
--- a/tests/unittests/torture_config.c
+++ b/tests/unittests/torture_config.c
@@ -2,6 +2,7 @@
#define LIBSSH_STATIC
+#include <errno.h>
#include "torture.h"
#include "libssh/options.h"
#include "libssh/session.h"
@@ -671,24 +672,40 @@ static void torture_config_proxyjump(void **state) {
assert_string_equal(session->opts.ProxyCommand,
"ssh -W [%h]:%p 2620:52:0::fed");
- /* Try to create some invalid configurations */
- /* Non-numeric port */
+ /* Multiple @ is allowed in second jump */
torture_write_file(LIBSSH_TESTCONFIG11,
- "Host bad-port\n"
- "\tProxyJump jumpbox:22bad22\n"
+ "Host allowed-hostname\n"
+ "\tProxyJump localhost,user@principal.com@jumpbox:22\n"
"");
torture_reset_config(session);
- ssh_options_set(session, SSH_OPTIONS_HOST, "bad-port");
+ ssh_options_set(session, SSH_OPTIONS_HOST, "allowed-hostname");
ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG11);
- assert_ssh_return_code_equal(session, ret, SSH_ERROR);
+ assert_ssh_return_code(session, ret);
+ assert_string_equal(session->opts.ProxyCommand,
+ "ssh -J user@principal.com@jumpbox:22 -W [%h]:%p localhost");
- /* Too many @ */
+ /* Multiple @ is allowed */
torture_write_file(LIBSSH_TESTCONFIG11,
- "Host bad-hostname\n"
+ "Host allowed-hostname\n"
"\tProxyJump user@principal.com@jumpbox:22\n"
"");
torture_reset_config(session);
- ssh_options_set(session, SSH_OPTIONS_HOST, "bad-hostname");
+ ssh_options_set(session, SSH_OPTIONS_HOST, "allowed-hostname");
+ ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG11);
+ assert_ssh_return_code(session, ret);
+ assert_string_equal(session->opts.ProxyCommand,
+ "ssh -l user@principal.com -p 22 -W [%h]:%p jumpbox");
+
+ /* In this part, we try various other config files and strings. */
+
+ /* Try to create some invalid configurations */
+ /* Non-numeric port */
+ torture_write_file(LIBSSH_TESTCONFIG11,
+ "Host bad-port\n"
+ "\tProxyJump jumpbox:22bad22\n"
+ "");
+ torture_reset_config(session);
+ ssh_options_set(session, SSH_OPTIONS_HOST, "bad-port");
ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG11);
assert_ssh_return_code_equal(session, ret, SSH_ERROR);
@@ -752,16 +769,6 @@ static void torture_config_proxyjump(void **state) {
ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG11);
assert_ssh_return_code_equal(session, ret, SSH_ERROR);
- /* Too many @ in second jump */
- torture_write_file(LIBSSH_TESTCONFIG11,
- "Host bad-hostname\n"
- "\tProxyJump localhost,user@principal.com@jumpbox:22\n"
- "");
- torture_reset_config(session);
- ssh_options_set(session, SSH_OPTIONS_HOST, "bad-hostname");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG11);
- assert_ssh_return_code_equal(session, ret, SSH_ERROR);
-
/* Braces mismatch in second jump */
torture_write_file(LIBSSH_TESTCONFIG11,
"Host mismatch\n"
@@ -991,6 +998,53 @@ static void torture_config_match_pattern(void **state)
}
+static void torture_config_parse_uri(void **state)
+{
+ char *username = NULL;
+ char *hostname = NULL;
+ char *port = NULL;
+ int rc;
+
+ (void)state; /* unused */
+
+ rc = ssh_config_parse_uri("localhost", &username, &hostname, &port, false);
+ assert_return_code(rc, errno);
+ assert_null(username);
+ assert_string_equal(hostname, "localhost");
+ SAFE_FREE(hostname);
+ assert_null(port);
+
+ rc = ssh_config_parse_uri("1.2.3.4", &username, &hostname, &port, false);
+ assert_return_code(rc, errno);
+ assert_null(username);
+ assert_string_equal(hostname, "1.2.3.4");
+ SAFE_FREE(hostname);
+ assert_null(port);
+
+ rc = ssh_config_parse_uri("1.2.3.4:2222", &username, &hostname, &port, false);
+ assert_return_code(rc, errno);
+ assert_null(username);
+ assert_string_equal(hostname, "1.2.3.4");
+ SAFE_FREE(hostname);
+ assert_string_equal(port, "2222");
+ SAFE_FREE(port);
+
+ rc = ssh_config_parse_uri("[1:2:3::4]:2222", &username, &hostname, &port, false);
+ assert_return_code(rc, errno);
+ assert_null(username);
+ assert_string_equal(hostname, "1:2:3::4");
+ SAFE_FREE(hostname);
+ assert_string_equal(port, "2222");
+ SAFE_FREE(port);
+
+ /* do not want port */
+ rc = ssh_config_parse_uri("1:2:3::4", &username, &hostname, NULL, true);
+ assert_return_code(rc, errno);
+ assert_null(username);
+ assert_string_equal(hostname, "1:2:3::4");
+ SAFE_FREE(hostname);
+}
+
int torture_run_tests(void) {
int rc;
@@ -1006,6 +1060,7 @@ int torture_run_tests(void) {
cmocka_unit_test(torture_config_rekey),
cmocka_unit_test(torture_config_pubkeyacceptedkeytypes),
cmocka_unit_test(torture_config_match_pattern),
+ cmocka_unit_test(torture_config_parse_uri),
};
diff --git a/tests/unittests/torture_misc.c b/tests/unittests/torture_misc.c
index 0a48abbe..f16b766e 100644
--- a/tests/unittests/torture_misc.c
+++ b/tests/unittests/torture_misc.c
@@ -17,7 +17,14 @@
#include "misc.c"
#include "error.c"
+#ifdef _WIN32
+#include <netioapi.h>
+#else
+#include <net/if.h>
+#endif
+
#define TORTURE_TEST_DIR "/usr/local/bin/truc/much/.."
+#define TORTURE_IPV6_LOCAL_LINK "fe80::98e1:82ff:fe8d:28b3%%%s"
const char template[] = "temp_dir_XXXXXX";
@@ -656,6 +663,116 @@ static void torture_ssh_newline_vis(UNUSED_PARAM(void **state))
assert_string_equal(buffer, "a\\nb\\n");
}
+static void torture_ssh_check_hostname_syntax(void **state)
+{
+ int rc;
+ (void)state;
+
+ rc = ssh_check_hostname_syntax("duckduckgo.com");
+ assert_int_equal(rc, SSH_OK);
+ rc = ssh_check_hostname_syntax("www.libssh.org");
+ assert_int_equal(rc, SSH_OK);
+ rc = ssh_check_hostname_syntax("Some-Thing.com");
+ assert_int_equal(rc, SSH_OK);
+ rc = ssh_check_hostname_syntax("amazon.a23456789012345678901234567890123456789012345678901234567890123");
+ assert_int_equal(rc, SSH_OK);
+ rc = ssh_check_hostname_syntax("amazon.a23456789012345678901234567890123456789012345678901234567890123.a23456789012345678901234567890123456789012345678901234567890123.ok");
+ assert_int_equal(rc, SSH_OK);
+ rc = ssh_check_hostname_syntax("amazon.a23456789012345678901234567890123456789012345678901234567890123.a23456789012345678901234567890123456789012345678901234567890123.a23456789012345678901234567890123456789012345678901234567890123");
+ assert_int_equal(rc, SSH_OK);
+ rc = ssh_check_hostname_syntax("lavabo-inter.innocentes-manus-meas");
+ assert_int_equal(rc, SSH_OK);
+ rc = ssh_check_hostname_syntax("localhost");
+ assert_int_equal(rc, SSH_OK);
+ rc = ssh_check_hostname_syntax("a");
+ assert_int_equal(rc, SSH_OK);
+ rc = ssh_check_hostname_syntax("a-0.b-b");
+ assert_int_equal(rc, SSH_OK);
+ rc = ssh_check_hostname_syntax("libssh.");
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_check_hostname_syntax(NULL);
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_hostname_syntax("");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_hostname_syntax("/");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_hostname_syntax("@");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_hostname_syntax("[");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_hostname_syntax("`");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_hostname_syntax("{");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_hostname_syntax("&");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_hostname_syntax("|");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_hostname_syntax("\"");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_hostname_syntax("`");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_hostname_syntax(" ");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_hostname_syntax("*the+giant&\"rooks\".c0m");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_hostname_syntax("!www.libssh.org");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_hostname_syntax("--.--");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_hostname_syntax("libssh.a234567890123456789012345678901234567890123456789012345678901234");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_hostname_syntax("libssh.a234567890123456789012345678901234567890123456789012345678901234.a234567890123456789012345678901234567890123456789012345678901234");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_hostname_syntax("libssh-");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_hostname_syntax("fe80::9656:d028:8652:66b6");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_hostname_syntax(".");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_hostname_syntax("..");
+ assert_int_equal(rc, SSH_ERROR);
+}
+
+static void torture_ssh_is_ipaddr(void **state) {
+ int rc;
+ char *interf = malloc(64);
+ char *test_interf = malloc(128);
+ (void)state;
+
+ assert_non_null(interf);
+ assert_non_null(test_interf);
+ rc = ssh_is_ipaddr("201.255.3.69");
+ assert_int_equal(rc, 1);
+ rc = ssh_is_ipaddr("::1");
+ assert_int_equal(rc, 1);
+ rc = ssh_is_ipaddr("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
+ assert_int_equal(rc, 1);
+ if_indextoname(1, interf);
+ assert_non_null(interf);
+ rc = sprintf(test_interf, TORTURE_IPV6_LOCAL_LINK, interf);
+ /* the "%%s" is not written */
+ assert_int_equal(rc, strlen(interf) + strlen(TORTURE_IPV6_LOCAL_LINK) - 3);
+ rc = ssh_is_ipaddr(test_interf);
+ assert_int_equal(rc, 1);
+ free(interf);
+ free(test_interf);
+
+ rc = ssh_is_ipaddr("..");
+ assert_int_equal(rc, 0);
+ rc = ssh_is_ipaddr(":::");
+ assert_int_equal(rc, 0);
+ rc = ssh_is_ipaddr("1.1.1.1.1");
+ assert_int_equal(rc, 0);
+ rc = ssh_is_ipaddr("1.1");
+ assert_int_equal(rc, 0);
+ rc = ssh_is_ipaddr("caesar");
+ assert_int_equal(rc, 0);
+ rc = ssh_is_ipaddr("::xa:1");
+ assert_int_equal(rc, 0);
+}
+
int torture_run_tests(void) {
int rc;
struct CMUnitTest tests[] = {
@@ -678,6 +795,8 @@ int torture_run_tests(void) {
cmocka_unit_test(torture_ssh_newline_vis),
cmocka_unit_test(torture_ssh_mkdirs),
cmocka_unit_test(torture_ssh_quote_file_name),
+ cmocka_unit_test(torture_ssh_check_hostname_syntax),
+ cmocka_unit_test(torture_ssh_is_ipaddr),
};
ssh_init();
diff --git a/tests/unittests/torture_options.c b/tests/unittests/torture_options.c
index d0fdaed1..576ca9cd 100644
--- a/tests/unittests/torture_options.c
+++ b/tests/unittests/torture_options.c
@@ -59,12 +59,34 @@ static void torture_options_set_host(void **state) {
assert_non_null(session->opts.host);
assert_string_equal(session->opts.host, "localhost");
+ /* IPv4 address */
+ rc = ssh_options_set(session, SSH_OPTIONS_HOST, "127.1.1.1");
+ assert_true(rc == 0);
+ assert_non_null(session->opts.host);
+ assert_string_equal(session->opts.host, "127.1.1.1");
+ assert_null(session->opts.username);
+
+ /* IPv6 address */
+ rc = ssh_options_set(session, SSH_OPTIONS_HOST, "::1");
+ assert_true(rc == 0);
+ assert_non_null(session->opts.host);
+ assert_string_equal(session->opts.host, "::1");
+ assert_null(session->opts.username);
+
rc = ssh_options_set(session, SSH_OPTIONS_HOST, "guru@meditation");
assert_true(rc == 0);
assert_non_null(session->opts.host);
assert_string_equal(session->opts.host, "meditation");
assert_non_null(session->opts.username);
assert_string_equal(session->opts.username, "guru");
+
+ /* more @ in uri is OK -- it should go to the username */
+ rc = ssh_options_set(session, SSH_OPTIONS_HOST, "at@login@hostname");
+ assert_true(rc == 0);
+ assert_non_null(session->opts.host);
+ assert_string_equal(session->opts.host, "hostname");
+ assert_non_null(session->opts.username);
+ assert_string_equal(session->opts.username, "at@login");
}
static void torture_options_set_ciphers(void **state) {