aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml7
-rw-r--r--CMakeLists.txt12
-rw-r--r--ConfigureChecks.cmake48
-rw-r--r--INSTALL4
-rw-r--r--config.h.cmake18
-rw-r--r--include/libssh/crypto.h2
-rw-r--r--include/libssh/libcrypto.h6
-rw-r--r--include/libssh/libssh.h4
-rw-r--r--include/libssh/pki.h2
-rw-r--r--src/CMakeLists.txt19
-rw-r--r--src/auth.c49
-rw-r--r--src/chachapoly.c2
-rw-r--r--src/channels.c102
-rw-r--r--src/client.c2
-rw-r--r--src/crypto_common.c34
-rw-r--r--src/curve25519.c20
-rw-r--r--src/dh-gex.c3
-rw-r--r--src/dh.c1
-rw-r--r--src/kex.c15
-rw-r--r--src/knownhosts.c1
-rw-r--r--src/libcrypto-compat.c114
-rw-r--r--src/libcrypto-compat.h7
-rw-r--r--src/libcrypto.c249
-rw-r--r--src/libmbedcrypto.c10
-rw-r--r--src/libssh.map1
-rw-r--r--src/messages.c38
-rw-r--r--src/packet.c46
-rw-r--r--src/packet_cb.c9
-rw-r--r--src/packet_crypt.c11
-rw-r--r--src/pki.c17
-rw-r--r--src/pki_crypto.c39
-rw-r--r--src/pki_ed25519_common.c11
-rw-r--r--src/pki_gcrypt.c25
-rw-r--r--src/pki_mbedcrypto.c13
-rw-r--r--src/threads/libcrypto.c12
-rw-r--r--src/wrapper.c1
-rw-r--r--tests/CMakeLists.txt51
-rw-r--r--tests/client/torture_auth.c93
-rw-r--r--tests/client/torture_session.c43
-rw-r--r--tests/external_override/CMakeLists.txt133
-rw-r--r--tests/external_override/chacha20_override.c80
-rw-r--r--tests/external_override/chacha20_override.h51
-rw-r--r--tests/external_override/curve25519_override.c58
-rw-r--r--tests/external_override/curve25519_override.h31
-rw-r--r--tests/external_override/ed25519_override.c71
-rw-r--r--tests/external_override/ed25519_override.h39
-rw-r--r--tests/external_override/poly1305_override.c54
-rw-r--r--tests/external_override/poly1305_override.h35
-rw-r--r--tests/external_override/torture_override.c320
-rw-r--r--tests/fuzz/README.md64
-rw-r--r--tests/keys/id_rsa_protected28
-rw-r--r--tests/keys/id_rsa_protected.pub1
-rw-r--r--tests/torture.c7
-rw-r--r--tests/torture.h4
-rw-r--r--tests/unittests/torture_callbacks.c5
-rw-r--r--tests/unittests/torture_config.c1238
-rw-r--r--tests/unittests/torture_packet_filter.c96
-rw-r--r--tests/unittests/torture_pki_ed25519.c6
-rw-r--r--tests/unittests/torture_session_keys.c4
59 files changed, 2509 insertions, 957 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 9ddd1b73..1a887203 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -445,6 +445,11 @@ visualstudio/x86:
###############################################################################
# Coverity #
###############################################################################
+#
+# git push -o ci.variable="COVERITY_SCAN_TOKEN=XXXXXX" \
+# -o ci.variable="COVERITY_SCAN_PROJECT_NAME=XXXXXX" \
+# -o ci.variable="COVERITY_SCAN_EMAIL=XXXXXX" \
+# -f gitlab
coverity:
stage: analysis
@@ -477,4 +482,4 @@ coverity:
expire_in: 1 week
when: on_failure
paths:
- - cov-int/*.txt
+ - obj/cov-int/*.txt
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 39bc0471..b8b4b0bc 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -58,8 +58,14 @@ elseif(WITH_MBEDTLS)
message(FATAL_ERROR "Could not find mbedTLS")
endif (NOT MBEDTLS_FOUND)
else (WITH_GCRYPT)
- find_package(OpenSSL)
- if (NOT OPENSSL_FOUND)
+ find_package(OpenSSL 1.0.1)
+ if (OPENSSL_FOUND)
+ # On CMake < 3.16, OPENSSL_CRYPTO_LIBRARIES is usually a synonym for OPENSSL_CRYPTO_LIBRARY, but is not defined
+ # when building on Windows outside of Cygwin. We provide the synonym here, if FindOpenSSL didn't define it already.
+ if (NOT DEFINED OPENSSL_CRYPTO_LIBRARIES)
+ set(OPENSSL_CRYPTO_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY})
+ endif (NOT DEFINED OPENSSL_CRYPTO_LIBRARIES)
+ else (OPENSSL_FOUND)
find_package(GCrypt)
if (NOT GCRYPT_FOUND)
find_package(MbedTLS)
@@ -67,7 +73,7 @@ else (WITH_GCRYPT)
message(FATAL_ERROR "Could not find OpenSSL, GCrypt or mbedTLS")
endif (NOT MBEDTLS_FOUND)
endif (NOT GCRYPT_FOUND)
- endif (NOT OPENSSL_FOUND)
+ endif (OPENSSL_FOUND)
endif(WITH_GCRYPT)
if (UNIT_TESTING)
diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake
index c62f4b92..384e7856 100644
--- a/ConfigureChecks.cmake
+++ b/ConfigureChecks.cmake
@@ -101,61 +101,37 @@ if (OPENSSL_FOUND)
check_include_file(openssl/ecdsa.h HAVE_OPENSSL_ECDSA_H)
set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
- set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY})
- check_function_exists(EVP_aes_128_ctr HAVE_OPENSSL_EVP_AES_CTR)
-
- set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
- set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY})
- check_function_exists(EVP_aes_128_cbc HAVE_OPENSSL_EVP_AES_CBC)
-
- set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
- set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY})
- check_function_exists(EVP_aes_128_gcm HAVE_OPENSSL_EVP_AES_GCM)
-
- set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
- set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY})
- check_function_exists(CRYPTO_THREADID_set_callback HAVE_OPENSSL_CRYPTO_THREADID_SET_CALLBACK)
-
- set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
- set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY})
- check_function_exists(CRYPTO_ctr128_encrypt HAVE_OPENSSL_CRYPTO_CTR128_ENCRYPT)
-
- set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
- set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY})
- check_function_exists(EVP_CIPHER_CTX_new HAVE_OPENSSL_EVP_CIPHER_CTX_NEW)
-
- set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
- set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY})
+ set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARIES})
check_function_exists(EVP_KDF_CTX_new_id HAVE_OPENSSL_EVP_KDF_CTX_NEW_ID)
set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
- set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY})
+ set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARIES})
check_function_exists(FIPS_mode HAVE_OPENSSL_FIPS_MODE)
set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
- set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY})
+ set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARIES})
check_function_exists(RAND_priv_bytes HAVE_OPENSSL_RAND_PRIV_BYTES)
set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
- set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY})
+ set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARIES})
check_function_exists(EVP_DigestSign HAVE_OPENSSL_EVP_DIGESTSIGN)
set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
- set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY})
+ set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARIES})
check_function_exists(EVP_DigestVerify HAVE_OPENSSL_EVP_DIGESTVERIFY)
check_function_exists(OPENSSL_ia32cap_loc HAVE_OPENSSL_IA32CAP_LOC)
set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
- set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY})
+ set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARIES})
check_symbol_exists(EVP_PKEY_ED25519 "openssl/evp.h" FOUND_OPENSSL_ED25519)
set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
- set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY})
+ set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARIES})
check_function_exists(EVP_chacha20 HAVE_OPENSSL_EVP_CHACHA20)
set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
- set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY})
+ set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARIES})
check_symbol_exists(EVP_PKEY_POLY1305 "openssl/evp.h" HAVE_OPENSSL_EVP_POLY1305)
if (HAVE_OPENSSL_EVP_DIGESTSIGN AND HAVE_OPENSSL_EVP_DIGESTVERIFY AND
@@ -164,7 +140,7 @@ if (OPENSSL_FOUND)
endif()
set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
- set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY})
+ set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARIES})
check_symbol_exists(EVP_PKEY_X25519 "openssl/evp.h" HAVE_OPENSSL_X25519)
unset(CMAKE_REQUIRED_INCLUDES)
@@ -296,6 +272,12 @@ endif (GCRYPT_FOUND)
if (MBEDTLS_FOUND)
set(HAVE_LIBMBEDCRYPTO 1)
set(HAVE_ECC 1)
+
+ set(CMAKE_REQUIRED_INCLUDES "${MBEDTLS_INCLUDE_DIR}/mbedtls")
+ check_include_file(chacha20.h HAVE_MBEDTLS_CHACHA20_H)
+ check_include_file(poly1305.h HAVE_MBEDTLS_POLY1305_H)
+ unset(CMAKE_REQUIRED_INCLUDES)
+
endif (MBEDTLS_FOUND)
if (CMAKE_USE_PTHREADS_INIT)
diff --git a/INSTALL b/INSTALL
index 0f0cebfb..c2eae34b 100644
--- a/INSTALL
+++ b/INSTALL
@@ -7,8 +7,8 @@
In order to build libssh, you need to install several components:
- A C compiler
-- [CMake](https://www.cmake.org) >= 2.6.0.
-- [openssl](https://www.openssl.org) >= 0.9.8
+- [CMake](https://www.cmake.org) >= 3.3.0
+- [openssl](https://www.openssl.org) >= 1.0.1
or
- [gcrypt](https://www.gnu.org/directory/Security/libgcrypt.html) >= 1.4
- [libz](https://www.zlib.net) >= 1.2
diff --git a/config.h.cmake b/config.h.cmake
index e708dd0b..34690304 100644
--- a/config.h.cmake
+++ b/config.h.cmake
@@ -111,27 +111,9 @@
/*************************** FUNCTIONS ***************************/
-/* Define to 1 if you have the `EVP_aes128_ctr' function. */
-#cmakedefine HAVE_OPENSSL_EVP_AES_CTR 1
-
-/* Define to 1 if you have the `EVP_aes128_cbc' function. */
-#cmakedefine HAVE_OPENSSL_EVP_AES_CBC 1
-
-/* Define to 1 if you have the `EVP_aes128_gcm' function. */
-#cmakedefine HAVE_OPENSSL_EVP_AES_GCM 1
-
/* Define to 1 if you have the `EVP_chacha20' function. */
#cmakedefine HAVE_OPENSSL_EVP_CHACHA20 1
-/* Define to 1 if you have the `CRYPTO_THREADID_set_callback' function. */
-#cmakedefine HAVE_OPENSSL_CRYPTO_THREADID_SET_CALLBACK 1
-
-/* Define to 1 if you have the `CRYPTO_ctr128_encrypt' function. */
-#cmakedefine HAVE_OPENSSL_CRYPTO_CTR128_ENCRYPT 1
-
-/* Define to 1 if you have the `EVP_CIPHER_CTX_new' function. */
-#cmakedefine HAVE_OPENSSL_EVP_CIPHER_CTX_NEW 1
-
/* Define to 1 if you have the `EVP_KDF_CTX_new_id' function. */
#cmakedefine HAVE_OPENSSL_EVP_KDF_CTX_NEW_ID 1
diff --git a/include/libssh/crypto.h b/include/libssh/crypto.h
index ede71661..67d98392 100644
--- a/include/libssh/crypto.h
+++ b/include/libssh/crypto.h
@@ -213,4 +213,6 @@ int sshkdf_derive_key(struct ssh_crypto_struct *crypto,
int key_type, unsigned char *output,
size_t requested_len);
+int secure_memcmp(const void *s1, const void *s2, size_t n);
+
#endif /* _CRYPTO_H_ */
diff --git a/include/libssh/libcrypto.h b/include/libssh/libcrypto.h
index 4117942c..403c2d22 100644
--- a/include/libssh/libcrypto.h
+++ b/include/libssh/libcrypto.h
@@ -38,7 +38,7 @@ typedef EVP_MD_CTX* SHA256CTX;
typedef EVP_MD_CTX* SHA384CTX;
typedef EVP_MD_CTX* SHA512CTX;
typedef EVP_MD_CTX* MD5CTX;
-typedef HMAC_CTX* HMACCTX;
+typedef EVP_MD_CTX* HMACCTX;
#ifdef HAVE_ECC
typedef EVP_MD_CTX *EVPCTX;
#else
@@ -60,10 +60,6 @@ typedef void *EVPCTX;
#include <openssl/bn.h>
#include <openssl/opensslv.h>
-#define OPENSSL_0_9_7b 0x0090702fL
-#if (OPENSSL_VERSION_NUMBER <= OPENSSL_0_9_7b)
-#define BROKEN_AES_CTR
-#endif
typedef BIGNUM* bignum;
typedef const BIGNUM* const_bignum;
typedef BN_CTX* bignum_CTX;
diff --git a/include/libssh/libssh.h b/include/libssh/libssh.h
index 12ff322d..4f401056 100644
--- a/include/libssh/libssh.h
+++ b/include/libssh/libssh.h
@@ -1,7 +1,7 @@
/*
* This file is part of the SSH Library
*
- * Copyright (c) 2003-2009 by Aris Adamantiadis
+ * Copyright (c) 2003-2021 by Aris Adamantiadis and the libssh team
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -757,6 +757,8 @@ LIBSSH_API int ssh_userauth_publickey(ssh_session session,
LIBSSH_API int ssh_userauth_agent(ssh_session session,
const char *username);
#endif
+LIBSSH_API int ssh_userauth_publickey_auto_get_current_identity(ssh_session session,
+ char** value);
LIBSSH_API int ssh_userauth_publickey_auto(ssh_session session,
const char *username,
const char *passphrase);
diff --git a/include/libssh/pki.h b/include/libssh/pki.h
index 0f8cae47..6357b0b6 100644
--- a/include/libssh/pki.h
+++ b/include/libssh/pki.h
@@ -135,6 +135,8 @@ enum ssh_digest_e ssh_key_hash_from_name(const char *name);
/* SSH Signature Functions */
ssh_signature ssh_signature_new(void);
void ssh_signature_free(ssh_signature sign);
+#define SSH_SIGNATURE_FREE(x) \
+ do { ssh_signature_free(x); x = NULL; } while(0)
int ssh_pki_export_signature_blob(const ssh_signature sign,
ssh_string *sign_blob);
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 621f8b35..0a91b45b 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -16,7 +16,7 @@ if (WIN32)
)
endif (WIN32)
-if (OPENSSL_CRYPTO_LIBRARY)
+if (OPENSSL_CRYPTO_LIBRARIES)
set(LIBSSH_PRIVATE_INCLUDE_DIRS
${LIBSSH_PRIVATE_INCLUDE_DIRS}
${OPENSSL_INCLUDE_DIR}
@@ -24,9 +24,9 @@ if (OPENSSL_CRYPTO_LIBRARY)
set(LIBSSH_LINK_LIBRARIES
${LIBSSH_LINK_LIBRARIES}
- ${OPENSSL_CRYPTO_LIBRARY}
+ ${OPENSSL_CRYPTO_LIBRARIES}
)
-endif (OPENSSL_CRYPTO_LIBRARY)
+endif (OPENSSL_CRYPTO_LIBRARIES)
if (MBEDTLS_CRYPTO_LIBRARY)
set(LIBSSH_PRIVATE_INCLUDE_DIRS
@@ -112,6 +112,7 @@ set(libssh_SRCS
config.c
connect.c
connector.c
+ crypto_common.c
curve25519.c
dh.c
ecdh.c
@@ -210,10 +211,16 @@ elseif (WITH_MBEDTLS)
external/fe25519.c
external/ge25519.c
external/sc25519.c
- external/chacha.c
- external/poly1305.c
- chachapoly.c
)
+ if (NOT (HAVE_MBEDTLS_CHACHA20_H AND HAVE_MBEDTLS_POLY1305_H))
+ set(libssh_SRCS
+ ${libssh_SRCS}
+ external/chacha.c
+ external/poly1305.c
+ chachapoly.c
+ )
+ endif()
+
else (WITH_GCRYPT)
set(libssh_SRCS
${libssh_SRCS}
diff --git a/src/auth.c b/src/auth.c
index 89272290..fcf39b7a 100644
--- a/src/auth.c
+++ b/src/auth.c
@@ -976,6 +976,55 @@ struct ssh_auth_auto_state_struct {
};
/**
+ * @brief Get the identity that is currenly being processed by
+ * ssh_userauth_publickey_auto()
+ *
+ * This is meant to be used by a callback that happens as part of the
+ * execution of ssh_userauth_publickey_auto(). The auth_function
+ * callback might want to know which key a passphrase is needed for,
+ * for example.
+ *
+ * @param[in] session The SSH session.
+ *
+ * @param[out] value The value to get into. As a char**, space will be
+ * allocated by the function for the value, it is
+ * your responsibility to free the memory using
+ * ssh_string_free_char().
+ *
+ * @return SSH_OK on success, SSH_ERROR on error.
+ */
+int ssh_userauth_publickey_auto_get_current_identity(ssh_session session,
+ char** value)
+{
+ const char *id = NULL;
+
+ if (session == NULL) {
+ return SSH_ERROR;
+ }
+
+ if (value == NULL) {
+ ssh_set_error_invalid(session);
+ return SSH_ERROR;
+ }
+
+ if (session->auth.auto_state != NULL && session->auth.auto_state->it != NULL) {
+ id = session->auth.auto_state->it->data;
+ }
+
+ if (id == NULL) {
+ return SSH_ERROR;
+ }
+
+ *value = strdup(id);
+ if (*value == NULL) {
+ ssh_set_error_oom(session);
+ return SSH_ERROR;
+ }
+
+ return SSH_OK;
+}
+
+/**
* @brief Tries to automatically authenticate with public key and "none"
*
* It may fail, for instance it doesn't ask for a password and uses a default
diff --git a/src/chachapoly.c b/src/chachapoly.c
index c90a1e97..c4827fd8 100644
--- a/src/chachapoly.c
+++ b/src/chachapoly.c
@@ -164,7 +164,7 @@ static int chacha20_poly1305_aead_decrypt(struct ssh_cipher_struct *cipher,
ssh_log_hexdump("received tag", mac, POLY1305_TAGLEN);
#endif
- cmp = memcmp(tag, mac, POLY1305_TAGLEN);
+ cmp = secure_memcmp(tag, mac, POLY1305_TAGLEN);
if(cmp != 0) {
/* mac error */
SSH_LOG(SSH_LOG_PACKET,"poly1305 verify error");
diff --git a/src/channels.c b/src/channels.c
index 607bd568..11a9413a 100644
--- a/src/channels.c
+++ b/src/channels.c
@@ -117,6 +117,13 @@ ssh_channel ssh_channel_new(ssh_session session)
if (session->channels == NULL) {
session->channels = ssh_list_new();
+ if (session->channels == NULL) {
+ ssh_set_error_oom(session);
+ SSH_BUFFER_FREE(channel->stdout_buffer);
+ SSH_BUFFER_FREE(channel->stderr_buffer);
+ SAFE_FREE(channel);
+ return NULL;
+ }
}
ssh_list_prepend(session->channels, channel);
@@ -641,40 +648,55 @@ SSH_PACKET_CALLBACK(channel_rcv_eof) {
return SSH_PACKET_USED;
}
+static bool ssh_channel_has_unread_data(ssh_channel channel)
+{
+ if (channel == NULL) {
+ return false;
+ }
+
+ if ((channel->stdout_buffer &&
+ ssh_buffer_get_len(channel->stdout_buffer) > 0) ||
+ (channel->stderr_buffer &&
+ ssh_buffer_get_len(channel->stderr_buffer) > 0))
+ {
+ return true;
+ }
+
+ return false;
+}
+
SSH_PACKET_CALLBACK(channel_rcv_close) {
- ssh_channel channel;
- (void)user;
- (void)type;
+ ssh_channel channel;
+ (void)user;
+ (void)type;
- channel = channel_from_msg(session,packet);
- if (channel == NULL) {
- SSH_LOG(SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session));
+ channel = channel_from_msg(session,packet);
+ if (channel == NULL) {
+ SSH_LOG(SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session));
- return SSH_PACKET_USED;
- }
+ return SSH_PACKET_USED;
+ }
- SSH_LOG(SSH_LOG_PACKET,
- "Received close on channel (%d:%d)",
- channel->local_channel,
- channel->remote_channel);
-
- if ((channel->stdout_buffer &&
- ssh_buffer_get_len(channel->stdout_buffer) > 0) ||
- (channel->stderr_buffer &&
- ssh_buffer_get_len(channel->stderr_buffer) > 0)) {
- channel->delayed_close = 1;
- } else {
- channel->state = SSH_CHANNEL_STATE_CLOSED;
- }
- if (channel->remote_eof == 0) {
- SSH_LOG(SSH_LOG_PACKET,
- "Remote host not polite enough to send an eof before close");
- }
- channel->remote_eof = 1;
- /*
- * The remote eof doesn't break things if there was still data into read
- * buffer because the eof is ignored until the buffer is empty.
- */
+ SSH_LOG(SSH_LOG_PACKET,
+ "Received close on channel (%d:%d)",
+ channel->local_channel,
+ channel->remote_channel);
+
+ if (!ssh_channel_has_unread_data(channel)) {
+ channel->state = SSH_CHANNEL_STATE_CLOSED;
+ } else {
+ channel->delayed_close = 1;
+ }
+
+ if (channel->remote_eof == 0) {
+ SSH_LOG(SSH_LOG_PACKET,
+ "Remote host not polite enough to send an eof before close");
+ }
+ /*
+ * The remote eof doesn't break things if there was still data into read
+ * buffer because the eof is ignored until the buffer is empty.
+ */
+ channel->remote_eof = 1;
ssh_callbacks_execute_list(channel->callbacks,
ssh_channel_callbacks,
@@ -682,11 +704,11 @@ SSH_PACKET_CALLBACK(channel_rcv_close) {
channel->session,
channel);
- channel->flags |= SSH_CHANNEL_FLAG_CLOSED_REMOTE;
- if(channel->flags & SSH_CHANNEL_FLAG_FREED_LOCAL)
- ssh_channel_do_free(channel);
+ channel->flags |= SSH_CHANNEL_FLAG_CLOSED_REMOTE;
+ if(channel->flags & SSH_CHANNEL_FLAG_FREED_LOCAL)
+ ssh_channel_do_free(channel);
- return SSH_PACKET_USED;
+ return SSH_PACKET_USED;
}
SSH_PACKET_CALLBACK(channel_rcv_request) {
@@ -1597,11 +1619,8 @@ int ssh_channel_is_eof(ssh_channel channel) {
if(channel == NULL) {
return SSH_ERROR;
}
- if ((channel->stdout_buffer &&
- ssh_buffer_get_len(channel->stdout_buffer) > 0) ||
- (channel->stderr_buffer &&
- ssh_buffer_get_len(channel->stderr_buffer) > 0)) {
- return 0;
+ if (ssh_channel_has_unread_data(channel)) {
+ return 0;
}
return (channel->remote_eof != 0);
@@ -2805,7 +2824,6 @@ static int ssh_channel_read_termination(void *s){
return 0;
}
-/* TODO FIXME Fix the delayed close thing */
/* TODO FIXME Fix the blocking behaviours */
/**
@@ -2950,6 +2968,10 @@ int ssh_channel_read_timeout(ssh_channel channel,
if (channel->counter != NULL) {
channel->counter->in_bytes += len;
}
+ /* Try completing the delayed_close */
+ if (channel->delayed_close && !ssh_channel_has_unread_data(channel)) {
+ channel->state = SSH_CHANNEL_STATE_CLOSED;
+ }
/* Authorize some buffering while userapp is busy */
if (channel->local_window < WINDOWLIMIT) {
if (grow_window(session, channel, 0) < 0) {
diff --git a/src/client.c b/src/client.c
index 3984b3f4..292c919e 100644
--- a/src/client.c
+++ b/src/client.c
@@ -775,7 +775,7 @@ error:
}
const char *ssh_copyright(void) {
- return SSH_STRINGIFY(LIBSSH_VERSION) " (c) 2003-2019 "
+ return SSH_STRINGIFY(LIBSSH_VERSION) " (c) 2003-2021 "
"Aris Adamantiadis, Andreas Schneider "
"and libssh contributors. "
"Distributed under the LGPL, please refer to COPYING "
diff --git a/src/crypto_common.c b/src/crypto_common.c
new file mode 100644
index 00000000..8213ddb9
--- /dev/null
+++ b/src/crypto_common.c
@@ -0,0 +1,34 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2020 by Anderson Toshiyuki Sasaki - Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+#include "libssh/crypto.h"
+
+int secure_memcmp(const void *s1, const void *s2, size_t n)
+{
+ int rc = 0;
+ const unsigned char *p1 = s1;
+ const unsigned char *p2 = s2;
+ for (; n > 0; --n) {
+ rc |= *p1++ ^ *p2++;
+ }
+ return (rc != 0);
+}
+
diff --git a/src/curve25519.c b/src/curve25519.c
index c13b3604..d2517551 100644
--- a/src/curve25519.c
+++ b/src/curve25519.c
@@ -377,12 +377,12 @@ void ssh_server_curve25519_init(ssh_session session){
*/
static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init){
/* ECDH keys */
- ssh_string q_c_string;
- ssh_string q_s_string;
+ ssh_string q_c_string = NULL;
+ ssh_string q_s_string = NULL;
ssh_string server_pubkey_blob = NULL;
/* SSH host keys (rsa,dsa,ecdsa) */
- ssh_key privkey;
+ ssh_key privkey = NULL;
enum ssh_digest_e digest = SSH_DIGEST_AUTO;
ssh_string sig_blob = NULL;
int rc;
@@ -402,7 +402,6 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init){
SSH_FATAL,
"Incorrect size for server Curve25519 public key: %zu",
ssh_string_len(q_c_string));
- SSH_STRING_FREE(q_c_string);
goto error;
}
@@ -460,12 +459,17 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init){
/* add ecdh public key */
q_s_string = ssh_string_new(CURVE25519_PUBKEY_SIZE);
if (q_s_string == NULL) {
+ ssh_set_error_oom(session);
goto error;
}
- ssh_string_fill(q_s_string,
- session->next_crypto->curve25519_server_pubkey,
- CURVE25519_PUBKEY_SIZE);
+ rc = ssh_string_fill(q_s_string,
+ session->next_crypto->curve25519_server_pubkey,
+ CURVE25519_PUBKEY_SIZE);
+ if (rc < 0) {
+ ssh_set_error(session, SSH_FATAL, "Could not copy public key");
+ goto error;
+ }
rc = ssh_buffer_add_ssh_string(session->out_buffer, q_s_string);
SSH_STRING_FREE(q_s_string);
@@ -508,6 +512,8 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init){
return SSH_PACKET_USED;
error:
+ SSH_STRING_FREE(q_c_string);
+ SSH_STRING_FREE(q_s_string);
ssh_buffer_reinit(session->out_buffer);
session->session_state=SSH_SESSION_STATE_ERROR;
return SSH_PACKET_USED;
diff --git a/src/dh-gex.c b/src/dh-gex.c
index 9bf0546a..88a97140 100644
--- a/src/dh-gex.c
+++ b/src/dh-gex.c
@@ -263,6 +263,8 @@ static SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_reply)
bignum_safe_free(server_pubkey);
goto error;
}
+ /* The ownership was passed to the crypto structure */
+ server_pubkey = NULL;
rc = ssh_dh_import_next_pubkey_blob(session, pubkey_blob);
SSH_STRING_FREE(pubkey_blob);
@@ -293,6 +295,7 @@ static SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_reply)
return SSH_PACKET_USED;
error:
+ SSH_STRING_FREE(pubkey_blob);
ssh_dh_cleanup(session->next_crypto);
session->session_state = SSH_SESSION_STATE_ERROR;
diff --git a/src/dh.c b/src/dh.c
index 05903daf..18b71734 100644
--- a/src/dh.c
+++ b/src/dh.c
@@ -361,6 +361,7 @@ SSH_PACKET_CALLBACK(ssh_packet_client_dh_reply){
rc = ssh_dh_keypair_set_keys(crypto->dh_ctx, DH_SERVER_KEYPAIR,
NULL, server_pubkey);
if (rc != SSH_OK) {
+ SSH_STRING_FREE(pubkey_blob);
bignum_safe_free(server_pubkey);
goto error;
}
diff --git a/src/kex.c b/src/kex.c
index 95948136..d2ee93ba 100644
--- a/src/kex.c
+++ b/src/kex.c
@@ -75,18 +75,9 @@
#elif defined(HAVE_LIBCRYPTO)
# ifdef HAVE_OPENSSL_AES_H
-# ifdef HAVE_OPENSSL_EVP_AES_GCM
-# define GCM "aes256-gcm@openssh.com,aes128-gcm@openssh.com,"
-# else
-# define GCM ""
-# endif /* HAVE_OPENSSL_EVP_AES_GCM */
-# ifdef BROKEN_AES_CTR
-# define AES GCM
-# define AES_CBC "aes256-cbc,aes192-cbc,aes128-cbc,"
-# else /* BROKEN_AES_CTR */
-# define AES GCM "aes256-ctr,aes192-ctr,aes128-ctr,"
-# define AES_CBC "aes256-cbc,aes192-cbc,aes128-cbc,"
-# endif /* BROKEN_AES_CTR */
+# define GCM "aes256-gcm@openssh.com,aes128-gcm@openssh.com,"
+# define AES GCM "aes256-ctr,aes192-ctr,aes128-ctr,"
+# define AES_CBC "aes256-cbc,aes192-cbc,aes128-cbc,"
# else /* HAVE_OPENSSL_AES_H */
# define AES ""
# define AES_CBC ""
diff --git a/src/knownhosts.c b/src/knownhosts.c
index fed75f90..f2ef088c 100644
--- a/src/knownhosts.c
+++ b/src/knownhosts.c
@@ -372,6 +372,7 @@ struct ssh_list *ssh_known_hosts_get_algorithms(ssh_session session)
list = ssh_list_new();
if (list == NULL) {
+ ssh_set_error_oom(session);
SAFE_FREE(host_port);
return NULL;
}
diff --git a/src/libcrypto-compat.c b/src/libcrypto-compat.c
index 01ca70e7..33b8dffd 100644
--- a/src/libcrypto-compat.c
+++ b/src/libcrypto-compat.c
@@ -12,19 +12,6 @@
#include <string.h>
#include "libcrypto-compat.h"
-#ifndef OPENSSL_NO_ENGINE
-#include <openssl/engine.h>
-#endif
-
-static void *OPENSSL_zalloc(size_t num)
-{
- void *ret = OPENSSL_malloc(num);
-
- if (ret != NULL)
- memset(ret, 0, num);
- return ret;
-}
-
int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d)
{
/* If the fields n and e in r are NULL, the corresponding input
@@ -236,111 +223,18 @@ int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
EVP_MD_CTX *EVP_MD_CTX_new(void)
{
- return OPENSSL_zalloc(sizeof(EVP_MD_CTX));
-}
-
-static void OPENSSL_clear_free(void *str, size_t num)
-{
- if (str == NULL)
- return;
- if (num)
- OPENSSL_cleanse(str, num);
- OPENSSL_free(str);
-}
-
-/* This call frees resources associated with the context */
-int EVP_MD_CTX_reset(EVP_MD_CTX *ctx)
-{
- if (ctx == NULL)
- return 1;
-
- /*
- * Don't assume ctx->md_data was cleaned in EVP_Digest_Final, because
- * sometimes only copies of the context are ever finalised.
- */
- if (ctx->digest && ctx->digest->cleanup
- && !EVP_MD_CTX_test_flags(ctx, EVP_MD_CTX_FLAG_CLEANED))
- ctx->digest->cleanup(ctx);
- if (ctx->digest && ctx->digest->ctx_size && ctx->md_data
- && !EVP_MD_CTX_test_flags(ctx, EVP_MD_CTX_FLAG_REUSE)) {
- OPENSSL_clear_free(ctx->md_data, ctx->digest->ctx_size);
- }
- EVP_PKEY_CTX_free(ctx->pctx);
-#ifndef OPENSSL_NO_ENGINE
- ENGINE_finish(ctx->engine);
-#endif
- OPENSSL_cleanse(ctx, sizeof(*ctx));
-
- return 1;
-}
-
-void EVP_MD_CTX_free(EVP_MD_CTX *ctx)
-{
- EVP_MD_CTX_reset(ctx);
- OPENSSL_free(ctx);
-}
-
-int EVP_CIPHER_CTX_reset(EVP_CIPHER_CTX *ctx)
-{
- EVP_CIPHER_CTX_init(ctx);
- return 1;
-}
-
-HMAC_CTX *HMAC_CTX_new(void)
-{
- HMAC_CTX *ctx = OPENSSL_zalloc(sizeof(HMAC_CTX));
-
+ EVP_MD_CTX *ctx = OPENSSL_malloc(sizeof(EVP_MD_CTX));
if (ctx != NULL) {
- if (!HMAC_CTX_reset(ctx)) {
- HMAC_CTX_free(ctx);
- return NULL;
- }
+ EVP_MD_CTX_init(ctx);
}
return ctx;
}
-static void hmac_ctx_cleanup(HMAC_CTX *ctx)
-{
- EVP_MD_CTX_reset(&ctx->i_ctx);
- EVP_MD_CTX_reset(&ctx->o_ctx);
- EVP_MD_CTX_reset(&ctx->md_ctx);
- ctx->md = NULL;
- ctx->key_length = 0;
- OPENSSL_cleanse(ctx->key, sizeof(ctx->key));
-}
-
-void HMAC_CTX_free(HMAC_CTX *ctx)
-{
- if (ctx != NULL) {
- hmac_ctx_cleanup(ctx);
-#if OPENSSL_VERSION_NUMBER > 0x10100000L
- EVP_MD_CTX_free(&ctx->i_ctx);
- EVP_MD_CTX_free(&ctx->o_ctx);
- EVP_MD_CTX_free(&ctx->md_ctx);
-#endif
- OPENSSL_free(ctx);
- }
-}
-
-int HMAC_CTX_reset(HMAC_CTX *ctx)
-{
- HMAC_CTX_init(ctx);
- return 1;
-}
-
-#ifndef HAVE_OPENSSL_EVP_CIPHER_CTX_NEW
-EVP_CIPHER_CTX *EVP_CIPHER_CTX_new(void)
-{
- return OPENSSL_zalloc(sizeof(EVP_CIPHER_CTX));
-}
-
-void EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *ctx)
+void EVP_MD_CTX_free(EVP_MD_CTX *ctx)
{
- /* EVP_CIPHER_CTX_reset(ctx); alias */
- EVP_CIPHER_CTX_init(ctx);
+ EVP_MD_CTX_cleanup(ctx);
OPENSSL_free(ctx);
}
-#endif
void DH_get0_pqg(const DH *dh,
const BIGNUM **p, const BIGNUM **q, const BIGNUM **g)
diff --git a/src/libcrypto-compat.h b/src/libcrypto-compat.h
index 0082e207..437b0534 100644
--- a/src/libcrypto-compat.h
+++ b/src/libcrypto-compat.h
@@ -30,16 +30,9 @@ int DSA_SIG_set0(DSA_SIG *sig, BIGNUM *r, BIGNUM *s);
void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps);
int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s);
-int EVP_MD_CTX_reset(EVP_MD_CTX *ctx);
EVP_MD_CTX *EVP_MD_CTX_new(void);
void EVP_MD_CTX_free(EVP_MD_CTX *ctx);
-int EVP_CIPHER_CTX_reset(EVP_CIPHER_CTX *ctx);
-
-HMAC_CTX *HMAC_CTX_new(void);
-int HMAC_CTX_reset(HMAC_CTX *ctx);
-void HMAC_CTX_free(HMAC_CTX *ctx);
-
void DH_get0_pqg(const DH *dh,
const BIGNUM **p, const BIGNUM **q, const BIGNUM **g);
int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g);
diff --git a/src/libcrypto.c b/src/libcrypto.c
index 96abec14..c14eeeea 100644
--- a/src/libcrypto.c
+++ b/src/libcrypto.c
@@ -71,10 +71,6 @@
#include <openssl/kdf.h>
#endif
-#ifdef HAVE_OPENSSL_CRYPTO_CTR128_ENCRYPT
-#include <openssl/modes.h>
-#endif
-
#include "libssh/crypto.h"
static int libcrypto_initialized = 0;
@@ -118,14 +114,13 @@ int ssh_get_random(void *where, int len, int strong)
SHACTX sha1_init(void)
{
int rc;
- SHACTX c = EVP_MD_CTX_create();
+ SHACTX c = EVP_MD_CTX_new();
if (c == NULL) {
return NULL;
}
- EVP_MD_CTX_init(c);
rc = EVP_DigestInit_ex(c, EVP_sha1(), NULL);
if (rc == 0) {
- EVP_MD_CTX_destroy(c);
+ EVP_MD_CTX_free(c);
c = NULL;
}
return c;
@@ -141,7 +136,7 @@ void sha1_final(unsigned char *md, SHACTX c)
unsigned int mdlen = 0;
EVP_DigestFinal(c, md, &mdlen);
- EVP_MD_CTX_destroy(c);
+ EVP_MD_CTX_free(c);
}
void sha1(const unsigned char *digest, int len, unsigned char *hash)
@@ -210,14 +205,13 @@ void evp_final(EVPCTX ctx, unsigned char *md, unsigned int *mdlen)
SHA256CTX sha256_init(void)
{
int rc;
- SHA256CTX c = EVP_MD_CTX_create();
+ SHA256CTX c = EVP_MD_CTX_new();
if (c == NULL) {
return NULL;
}
- EVP_MD_CTX_init(c);
rc = EVP_DigestInit_ex(c, EVP_sha256(), NULL);
if (rc == 0) {
- EVP_MD_CTX_destroy(c);
+ EVP_MD_CTX_free(c);
c = NULL;
}
return c;
@@ -233,7 +227,7 @@ void sha256_final(unsigned char *md, SHA256CTX c)
unsigned int mdlen = 0;
EVP_DigestFinal(c, md, &mdlen);
- EVP_MD_CTX_destroy(c);
+ EVP_MD_CTX_free(c);
}
void sha256(const unsigned char *digest, int len, unsigned char *hash)
@@ -248,14 +242,13 @@ void sha256(const unsigned char *digest, int len, unsigned char *hash)
SHA384CTX sha384_init(void)
{
int rc;
- SHA384CTX c = EVP_MD_CTX_create();
+ SHA384CTX c = EVP_MD_CTX_new();
if (c == NULL) {
return NULL;
}
- EVP_MD_CTX_init(c);
rc = EVP_DigestInit_ex(c, EVP_sha384(), NULL);
if (rc == 0) {
- EVP_MD_CTX_destroy(c);
+ EVP_MD_CTX_free(c);
c = NULL;
}
return c;
@@ -271,7 +264,7 @@ void sha384_final(unsigned char *md, SHA384CTX c)
unsigned int mdlen = 0;
EVP_DigestFinal(c, md, &mdlen);
- EVP_MD_CTX_destroy(c);
+ EVP_MD_CTX_free(c);
}
void sha384(const unsigned char *digest, int len, unsigned char *hash)
@@ -286,14 +279,13 @@ void sha384(const unsigned char *digest, int len, unsigned char *hash)
SHA512CTX sha512_init(void)
{
int rc = 0;
- SHA512CTX c = EVP_MD_CTX_create();
+ SHA512CTX c = EVP_MD_CTX_new();
if (c == NULL) {
return NULL;
}
- EVP_MD_CTX_init(c);
rc = EVP_DigestInit_ex(c, EVP_sha512(), NULL);
if (rc == 0) {
- EVP_MD_CTX_destroy(c);
+ EVP_MD_CTX_free(c);
c = NULL;
}
return c;
@@ -309,7 +301,7 @@ void sha512_final(unsigned char *md, SHA512CTX c)
unsigned int mdlen = 0;
EVP_DigestFinal(c, md, &mdlen);
- EVP_MD_CTX_destroy(c);
+ EVP_MD_CTX_free(c);
}
void sha512(const unsigned char *digest, int len, unsigned char *hash)
@@ -324,14 +316,13 @@ void sha512(const unsigned char *digest, int len, unsigned char *hash)
MD5CTX md5_init(void)
{
int rc;
- MD5CTX c = EVP_MD_CTX_create();
+ MD5CTX c = EVP_MD_CTX_new();
if (c == NULL) {
return NULL;
}
- EVP_MD_CTX_init(c);
rc = EVP_DigestInit_ex(c, EVP_md5(), NULL);
if(rc == 0) {
- EVP_MD_CTX_destroy(c);
+ EVP_MD_CTX_free(c);
c = NULL;
}
return c;
@@ -347,7 +338,7 @@ void md5_final(unsigned char *md, MD5CTX c)
unsigned int mdlen = 0;
EVP_DigestFinal(c, md, &mdlen);
- EVP_MD_CTX_destroy(c);
+ EVP_MD_CTX_free(c);
}
#ifdef HAVE_OPENSSL_EVP_KDF_CTX_NEW_ID
@@ -425,56 +416,70 @@ int ssh_kdf(struct ssh_crypto_struct *crypto,
}
#endif
-HMACCTX hmac_init(const void *key, int len, enum ssh_hmac_e type) {
- HMACCTX ctx = NULL;
+HMACCTX hmac_init(const void *key, int len, enum ssh_hmac_e type)
+{
+ HMACCTX ctx = NULL;
+ EVP_PKEY *pkey = NULL;
+ int rc = -1;
- ctx = HMAC_CTX_new();
- if (ctx == NULL) {
- return NULL;
- }
+ ctx = EVP_MD_CTX_new();
+ if (ctx == NULL) {
+ return NULL;
+ }
+ pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, key, len);
+ if (pkey == NULL) {
+ goto error;
+ }
- switch(type) {
+ switch (type) {
case SSH_HMAC_SHA1:
- HMAC_Init_ex(ctx, key, len, EVP_sha1(), NULL);
- break;
+ rc = EVP_DigestSignInit(ctx, NULL, EVP_sha1(), NULL, pkey);
+ break;
case SSH_HMAC_SHA256:
- HMAC_Init_ex(ctx, key, len, EVP_sha256(), NULL);
- break;
+ rc = EVP_DigestSignInit(ctx, NULL, EVP_sha256(), NULL, pkey);
+ break;
case SSH_HMAC_SHA512:
- HMAC_Init_ex(ctx, key, len, EVP_sha512(), NULL);
- break;
+ rc = EVP_DigestSignInit(ctx, NULL, EVP_sha512(), NULL, pkey);
+ break;
case SSH_HMAC_MD5:
- HMAC_Init_ex(ctx, key, len, EVP_md5(), NULL);
- break;
+ rc = EVP_DigestSignInit(ctx, NULL, EVP_md5(), NULL, pkey);
+ break;
default:
- HMAC_CTX_free(ctx);
- ctx = NULL;
- }
+ rc = -1;
+ break;
+ }
- return ctx;
-}
+ EVP_PKEY_free(pkey);
+ if (rc != 1) {
+ goto error;
+ }
+ return ctx;
-void hmac_update(HMACCTX ctx, const void *data, unsigned long len) {
- HMAC_Update(ctx, data, len);
+error:
+ EVP_MD_CTX_free(ctx);
+ return NULL;
}
-void hmac_final(HMACCTX ctx, unsigned char *hashmacbuf, unsigned int *len) {
- HMAC_Final(ctx,hashmacbuf,len);
+void hmac_update(HMACCTX ctx, const void *data, unsigned long len)
+{
+ EVP_DigestSignUpdate(ctx, data, len);
+}
-#if OPENSSL_VERSION_NUMBER > 0x10100000L
- HMAC_CTX_free(ctx);
- ctx = NULL;
-#else
- HMAC_cleanup(ctx);
- SAFE_FREE(ctx);
- ctx = NULL;
-#endif
+void hmac_final(HMACCTX ctx, unsigned char *hashmacbuf, unsigned int *len)
+{
+ size_t res;
+ EVP_DigestSignFinal(ctx, hashmacbuf, &res);
+ EVP_MD_CTX_free(ctx);
+ *len = res;
}
-static void evp_cipher_init(struct ssh_cipher_struct *cipher) {
+static void evp_cipher_init(struct ssh_cipher_struct *cipher)
+{
if (cipher->ctx == NULL) {
cipher->ctx = EVP_CIPHER_CTX_new();
+ } else {
+ EVP_CIPHER_CTX_init(cipher->ctx);
}
switch(cipher->ciphertype){
@@ -487,7 +492,6 @@ static void evp_cipher_init(struct ssh_cipher_struct *cipher) {
case SSH_AES256_CBC:
cipher->cipher = EVP_aes_256_cbc();
break;
-#ifdef HAVE_OPENSSL_EVP_AES_CTR
case SSH_AES128_CTR:
cipher->cipher = EVP_aes_128_ctr();
break;
@@ -497,26 +501,12 @@ static void evp_cipher_init(struct ssh_cipher_struct *cipher) {
case SSH_AES256_CTR:
cipher->cipher = EVP_aes_256_ctr();
break;
-#else
- case SSH_AES128_CTR:
- case SSH_AES192_CTR:
- case SSH_AES256_CTR:
- SSH_LOG(SSH_LOG_WARNING, "This cipher is not available in evp_cipher_init");
- break;
-#endif
-#ifdef HAVE_OPENSSL_EVP_AES_GCM
case SSH_AEAD_AES128_GCM:
cipher->cipher = EVP_aes_128_gcm();
break;
case SSH_AEAD_AES256_GCM:
cipher->cipher = EVP_aes_256_gcm();
break;
-#else
- case SSH_AEAD_AES128_GCM:
- case SSH_AEAD_AES256_GCM:
- SSH_LOG(SSH_LOG_WARNING, "This cipher is not available in evp_cipher_init");
- break;
-#endif /* HAVE_OPENSSL_EVP_AES_GCM */
case SSH_3DES_CBC:
cipher->cipher = EVP_des_ede3_cbc();
break;
@@ -541,7 +531,6 @@ static int evp_cipher_set_encrypt_key(struct ssh_cipher_struct *cipher,
int rc;
evp_cipher_init(cipher);
- EVP_CIPHER_CTX_reset(cipher->ctx);
rc = EVP_EncryptInit_ex(cipher->ctx, cipher->cipher, NULL, key, IV);
if (rc != 1){
@@ -549,7 +538,6 @@ static int evp_cipher_set_encrypt_key(struct ssh_cipher_struct *cipher,
return SSH_ERROR;
}
-#ifdef HAVE_OPENSSL_EVP_AES_GCM
/* For AES-GCM we need to set IV in specific way */
if (cipher->ciphertype == SSH_AEAD_AES128_GCM ||
cipher->ciphertype == SSH_AEAD_AES256_GCM) {
@@ -562,7 +550,6 @@ static int evp_cipher_set_encrypt_key(struct ssh_cipher_struct *cipher,
return SSH_ERROR;
}
}
-#endif /* HAVE_OPENSSL_EVP_AES_GCM */
EVP_CIPHER_CTX_set_padding(cipher->ctx, 0);
@@ -574,7 +561,6 @@ static int evp_cipher_set_decrypt_key(struct ssh_cipher_struct *cipher,
int rc;
evp_cipher_init(cipher);
- EVP_CIPHER_CTX_reset(cipher->ctx);
rc = EVP_DecryptInit_ex(cipher->ctx, cipher->cipher, NULL, key, IV);
if (rc != 1){
@@ -582,7 +568,6 @@ static int evp_cipher_set_decrypt_key(struct ssh_cipher_struct *cipher,
return SSH_ERROR;
}
-#ifdef HAVE_OPENSSL_EVP_AES_GCM
/* For AES-GCM we need to set IV in specific way */
if (cipher->ciphertype == SSH_AEAD_AES128_GCM ||
cipher->ciphertype == SSH_AEAD_AES256_GCM) {
@@ -595,7 +580,6 @@ static int evp_cipher_set_decrypt_key(struct ssh_cipher_struct *cipher,
return SSH_ERROR;
}
}
-#endif /* HAVE_OPENSSL_EVP_AES_GCM */
EVP_CIPHER_CTX_set_padding(cipher->ctx, 0);
@@ -661,68 +645,6 @@ static void evp_cipher_cleanup(struct ssh_cipher_struct *cipher) {
}
}
-#ifndef HAVE_OPENSSL_EVP_AES_CTR
-/* Some OS (osx, OpenIndiana, ...) have no support for CTR ciphers in EVP_aes */
-
-struct ssh_aes_key_schedule {
- AES_KEY key;
- uint8_t IV[AES_BLOCK_SIZE];
-};
-
-static int aes_ctr_set_key(struct ssh_cipher_struct *cipher, void *key,
- void *IV) {
- int rc;
-
- if (cipher->aes_key == NULL) {
- cipher->aes_key = malloc(sizeof (struct ssh_aes_key_schedule));
- }
- if (cipher->aes_key == NULL) {
- return SSH_ERROR;
- }
- ZERO_STRUCTP(cipher->aes_key);
- /* CTR doesn't need a decryption key */
- rc = AES_set_encrypt_key(key, cipher->keysize, &cipher->aes_key->key);
- if (rc < 0) {
- SAFE_FREE(cipher->aes_key);
- return SSH_ERROR;
- }
- memcpy(cipher->aes_key->IV, IV, AES_BLOCK_SIZE);
- return SSH_OK;
-}
-
-static void
-aes_ctr_encrypt(struct ssh_cipher_struct *cipher,
- void *in,
- void *out,
- size_t len)
-{
- unsigned char tmp_buffer[AES_BLOCK_SIZE];
- unsigned int num=0;
- /* Some things are special with ctr128 :
- * In this case, tmp_buffer is not being used, because it is used to store temporary data
- * when an encryption is made on lengths that are not multiple of blocksize.
- * Same for num, which is being used to store the current offset in blocksize in CTR
- * function.
- */
-#ifdef HAVE_OPENSSL_CRYPTO_CTR128_ENCRYPT
- CRYPTO_ctr128_encrypt(in, out, len, &cipher->aes_key->key, cipher->aes_key->IV, tmp_buffer, &num, (block128_f)AES_encrypt);
-#else
- AES_ctr128_encrypt(in, out, len, &cipher->aes_key->key, cipher->aes_key->IV, tmp_buffer, &num);
-#endif /* HAVE_OPENSSL_CRYPTO_CTR128_ENCRYPT */
-}
-
-static void aes_ctr_cleanup(struct ssh_cipher_struct *cipher){
- if (cipher != NULL) {
- if (cipher->aes_key != NULL) {
- explicit_bzero(cipher->aes_key, sizeof(*cipher->aes_key));
- }
- SAFE_FREE(cipher->aes_key);
- }
-}
-
-#endif /* HAVE_OPENSSL_EVP_AES_CTR */
-
-#ifdef HAVE_OPENSSL_EVP_AES_GCM
static int
evp_cipher_aead_get_length(struct ssh_cipher_struct *cipher,
void *in,
@@ -893,8 +815,6 @@ evp_cipher_aead_decrypt(struct ssh_cipher_struct *cipher,
return SSH_OK;
}
-#endif /* HAVE_OPENSSL_EVP_AES_GCM */
-
#if defined(HAVE_OPENSSL_EVP_CHACHA20) && defined(HAVE_OPENSSL_EVP_POLY1305)
struct chacha20_poly1305_keysched {
@@ -1178,7 +1098,7 @@ chacha20_poly1305_aead_decrypt(struct ssh_cipher_struct *cipher,
#endif /* DEBUG_CRYPTO */
/* Verify the calculated MAC matches the attached MAC */
- cmp = memcmp(tag, mac, POLY1305_TAGLEN);
+ cmp = CRYPTO_memcmp(tag, mac, POLY1305_TAGLEN);
if (cmp != 0) {
/* mac error */
SSH_LOG(SSH_LOG_PACKET, "poly1305 verify error");
@@ -1304,11 +1224,6 @@ static struct ssh_cipher_struct ssh_ciphertab[] = {
},
#endif
#ifdef HAS_AES
-#ifndef BROKEN_AES_CTR
-/* OpenSSL until 0.9.7c has a broken AES_ctr128_encrypt implementation which
- * increments the counter from 2^64 instead of 1. It's better not to use it
- */
-#ifdef HAVE_OPENSSL_EVP_AES_CTR
{
.name = "aes128-ctr",
.blocksize = AES_BLOCK_SIZE,
@@ -1342,42 +1257,6 @@ static struct ssh_cipher_struct ssh_ciphertab[] = {
.decrypt = evp_cipher_decrypt,
.cleanup = evp_cipher_cleanup
},
-#else /* HAVE_OPENSSL_EVP_AES_CTR */
- {
- .name = "aes128-ctr",
- .blocksize = AES_BLOCK_SIZE,
- .ciphertype = SSH_AES128_CTR,
- .keysize = 128,
- .set_encrypt_key = aes_ctr_set_key,
- .set_decrypt_key = aes_ctr_set_key,
- .encrypt = aes_ctr_encrypt,
- .decrypt = aes_ctr_encrypt,
- .cleanup = aes_ctr_cleanup
- },
- {
- .name = "aes192-ctr",
- .blocksize = AES_BLOCK_SIZE,
- .ciphertype = SSH_AES192_CTR,
- .keysize = 192,
- .set_encrypt_key = aes_ctr_set_key,
- .set_decrypt_key = aes_ctr_set_key,
- .encrypt = aes_ctr_encrypt,
- .decrypt = aes_ctr_encrypt,
- .cleanup = aes_ctr_cleanup
- },
- {
- .name = "aes256-ctr",
- .blocksize = AES_BLOCK_SIZE,
- .ciphertype = SSH_AES256_CTR,
- .keysize = 256,
- .set_encrypt_key = aes_ctr_set_key,
- .set_decrypt_key = aes_ctr_set_key,
- .encrypt = aes_ctr_encrypt,
- .decrypt = aes_ctr_encrypt,
- .cleanup = aes_ctr_cleanup
- },
-#endif /* HAVE_OPENSSL_EVP_AES_CTR */
-#endif /* BROKEN_AES_CTR */
{
.name = "aes128-cbc",
.blocksize = AES_BLOCK_SIZE,
@@ -1411,7 +1290,6 @@ static struct ssh_cipher_struct ssh_ciphertab[] = {
.decrypt = evp_cipher_decrypt,
.cleanup = evp_cipher_cleanup
},
-#ifdef HAVE_OPENSSL_EVP_AES_GCM
{
.name = "aes128-gcm@openssh.com",
.blocksize = AES_BLOCK_SIZE,
@@ -1440,7 +1318,6 @@ static struct ssh_cipher_struct ssh_ciphertab[] = {
.aead_decrypt = evp_cipher_aead_decrypt,
.cleanup = evp_cipher_cleanup
},
-#endif /* HAVE_OPENSSL_EVP_AES_GCM */
#endif /* HAS_AES */
#ifdef HAS_DES
{
diff --git a/src/libmbedcrypto.c b/src/libmbedcrypto.c
index ee3fad79..79f8ecc2 100644
--- a/src/libmbedcrypto.c
+++ b/src/libmbedcrypto.c
@@ -994,9 +994,9 @@ chacha20_poly1305_set_iv(struct ssh_cipher_struct *cipher,
return SSH_ERROR;
}
- ret = mbedtls_chacha20_starts(&ctx->header_ctx, seqbuf, 0);
+ ret = mbedtls_chacha20_starts(&ctx->main_ctx, seqbuf, 0);
if (ret != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_chacha20_starts(header_ctx) failed");
+ SSH_LOG(SSH_LOG_WARNING, "mbedtls_chacha20_starts(main_ctx) failed");
return SSH_ERROR;
}
@@ -1126,7 +1126,7 @@ chacha20_poly1305_aead_decrypt(struct ssh_cipher_struct *cipher,
#endif /* DEBUG_CRYPTO */
/* Verify the calculated MAC matches the attached MAC */
- cmp = memcmp(tag, mac, POLY1305_TAGLEN);
+ cmp = secure_memcmp(tag, mac, POLY1305_TAGLEN);
if (cmp != 0) {
/* mac error */
SSH_LOG(SSH_LOG_PACKET, "poly1305 verify error");
@@ -1187,7 +1187,7 @@ chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher,
/* We already did encrypt one block so the counter should be in the correct position */
ret = mbedtls_chacha20_update(&ctx->main_ctx, len - sizeof(uint32_t),
in_packet->payload, out_packet->payload);
- if (ret != 1) {
+ if (ret != 0) {
SSH_LOG(SSH_LOG_WARNING, "mbedtls_chacha20_update failed");
return;
}
@@ -1411,7 +1411,7 @@ int ssh_crypto_init(void)
mbedtls_ctr_drbg_free(&ssh_mbedtls_ctr_drbg);
}
-#if defined(MBEDTLS_CHACHA20_C) && defined(MBEDTLS_POLY1305_C)
+#if !(defined(MBEDTLS_CHACHA20_C) && defined(MBEDTLS_POLY1305_C))
for (i = 0; ssh_ciphertab[i].name != NULL; i++) {
int cmp;
diff --git a/src/libssh.map b/src/libssh.map
index c9bedee0..e30c2449 100644
--- a/src/libssh.map
+++ b/src/libssh.map
@@ -402,6 +402,7 @@ LIBSSH_4_5_0 # Released
ssh_userauth_pubkey;
ssh_userauth_publickey;
ssh_userauth_publickey_auto;
+ ssh_userauth_publickey_auto_get_current_identity;
ssh_userauth_try_publickey;
ssh_version;
ssh_write_knownhost;
diff --git a/src/messages.c b/src/messages.c
index 25683b23..c7fcc887 100644
--- a/src/messages.c
+++ b/src/messages.c
@@ -513,24 +513,30 @@ static int ssh_message_termination(void *s){
* @warning This function blocks until a message has been received. Betterset up
* a callback if this behavior is unwanted.
*/
-ssh_message ssh_message_get(ssh_session session) {
- ssh_message msg = NULL;
- int rc;
+ssh_message ssh_message_get(ssh_session session)
+{
+ ssh_message msg = NULL;
+ int rc;
- msg=ssh_message_pop_head(session);
- if(msg) {
- return msg;
- }
- if(session->ssh_message_list == NULL) {
- session->ssh_message_list = ssh_list_new();
- }
- rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_USER,
- ssh_message_termination, session);
- if(rc || session->session_state == SSH_SESSION_STATE_ERROR)
- return NULL;
- msg=ssh_list_pop_head(ssh_message, session->ssh_message_list);
+ msg = ssh_message_pop_head(session);
+ if (msg != NULL) {
+ return msg;
+ }
+ if (session->ssh_message_list == NULL) {
+ session->ssh_message_list = ssh_list_new();
+ if (session->ssh_message_list == NULL) {
+ ssh_set_error_oom(session);
+ return NULL;
+ }
+ }
+ rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_USER,
+ ssh_message_termination, session);
+ if (rc || session->session_state == SSH_SESSION_STATE_ERROR) {
+ return NULL;
+ }
+ msg = ssh_list_pop_head(ssh_message, session->ssh_message_list);
- return msg;
+ return msg;
}
/**
diff --git a/src/packet.c b/src/packet.c
index f14731c9..93591565 100644
--- a/src/packet.c
+++ b/src/packet.c
@@ -688,10 +688,12 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
/*
* States required:
* - session_state == SSH_SESSION_STATE_AUTHENTICATED
- * - session->global_req_state == SSH_CHANNEL_REQ_STATE_PENDING
*
* Transitions:
- * - session->global_req_state == SSH_CHANNEL_REQ_STATE_ACCEPTED
+ * - From channel->request_state == SSH_CHANNEL_REQ_STATE_PENDING
+ * - To channel->request_state = SSH_CHANNEL_REQ_STATE_ACCEPTED
+ *
+ * If not in a pending state, message is ignored in the callback handler.
* */
if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {
@@ -699,21 +701,18 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
break;
}
- if (session->global_req_state != SSH_CHANNEL_REQ_STATE_PENDING) {
- rc = SSH_PACKET_DENIED;
- break;
- }
-
rc = SSH_PACKET_ALLOWED;
break;
case SSH2_MSG_REQUEST_FAILURE: // 82
/*
* States required:
* - session_state == SSH_SESSION_STATE_AUTHENTICATED
- * - session->global_req_state == SSH_CHANNEL_REQ_STATE_PENDING
*
* Transitions:
- * - session->global_req_state == SSH_CHANNEL_REQ_STATE_DENIED
+ * - From channel->request_state == SSH_CHANNEL_REQ_STATE_PENDING
+ * - To channel->request_state = SSH_CHANNEL_REQ_STATE_ACCEPTED
+ *
+ * If not in a pending state, message is ignored in the callback handler.
* */
if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {
@@ -721,11 +720,6 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
break;
}
- if (session->global_req_state != SSH_CHANNEL_REQ_STATE_PENDING) {
- rc = SSH_PACKET_DENIED;
- break;
- }
-
rc = SSH_PACKET_ALLOWED;
break;
case SSH2_MSG_CHANNEL_OPEN: // 90
@@ -878,10 +872,12 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
/*
* States required:
* - session_state == SSH_SESSION_STATE_AUTHENTICATED
- * - channel->request_state == SSH_CHANNEL_REQ_STATE_PENDING
*
* Transitions:
- * - channel->request_state = SSH_CHANNEL_REQ_STATE_ACCEPTED
+ * - From channel->request_state == SSH_CHANNEL_REQ_STATE_PENDING
+ * - To channel->request_state = SSH_CHANNEL_REQ_STATE_ACCEPTED
+ *
+ * If not in a pending state, message is ignored in the callback handler.
* */
if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {
@@ -895,10 +891,12 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
/*
* States required:
* - session_state == SSH_SESSION_STATE_AUTHENTICATED
- * - channel->request_state == SSH_CHANNEL_REQ_STATE_PENDING
*
* Transitions:
- * - channel->request_state = SSH_CHANNEL_REQ_STATE_DENIED
+ * - From channel->request_state == SSH_CHANNEL_REQ_STATE_PENDING
+ * - To channel->request_state = SSH_CHANNEL_REQ_STATE_ACCEPTED
+ *
+ * If not in a pending state, message is ignored in the callback handler.
* */
if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {
@@ -1424,12 +1422,14 @@ void ssh_packet_register_socket_callback(ssh_session session, ssh_socket s){
* @brief sets the callbacks for the packet layer
*/
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) {
+ if (session->packet_callbacks == NULL) {
+ session->packet_callbacks = ssh_list_new();
+ if (session->packet_callbacks == NULL) {
+ ssh_set_error_oom(session);
+ return;
+ }
+ }
ssh_list_append(session->packet_callbacks, callbacks);
- }
}
/** @internal
diff --git a/src/packet_cb.c b/src/packet_cb.c
index 4e692915..39575b17 100644
--- a/src/packet_cb.c
+++ b/src/packet_cb.c
@@ -129,6 +129,8 @@ SSH_PACKET_CALLBACK(ssh_packet_newkeys){
}
rc = ssh_pki_import_signature_blob(sig_blob, server_key, &sig);
+ ssh_string_burn(sig_blob);
+ SSH_STRING_FREE(sig_blob);
if (rc != SSH_OK) {
goto error;
}
@@ -152,9 +154,7 @@ SSH_PACKET_CALLBACK(ssh_packet_newkeys){
server_key,
session->next_crypto->secret_hash,
session->next_crypto->digest_len);
- ssh_string_burn(sig_blob);
- SSH_STRING_FREE(sig_blob);
- ssh_signature_free(sig);
+ SSH_SIGNATURE_FREE(sig);
if (rc == SSH_ERROR) {
goto error;
}
@@ -170,6 +170,9 @@ SSH_PACKET_CALLBACK(ssh_packet_newkeys){
session->ssh_connection_callback(session);
return SSH_PACKET_USED;
error:
+ SSH_SIGNATURE_FREE(sig);
+ ssh_string_burn(sig_blob);
+ SSH_STRING_FREE(sig_blob);
session->session_state = SSH_SESSION_STATE_ERROR;
return SSH_PACKET_USED;
}
diff --git a/src/packet_crypt.c b/src/packet_crypt.c
index c2f7ab02..734ccafc 100644
--- a/src/packet_crypt.c
+++ b/src/packet_crypt.c
@@ -216,17 +216,6 @@ unsigned char *ssh_packet_encrypt(ssh_session session, void *data, uint32_t len)
return crypto->hmacbuf;
}
-static int secure_memcmp(const void *s1, const void *s2, size_t n)
-{
- int rc = 0;
- const unsigned char *p1 = s1;
- const unsigned char *p2 = s2;
- for (; n > 0; --n) {
- rc |= *p1++ ^ *p2++;
- }
- return (rc != 0);
-}
-
/**
* @internal
*
diff --git a/src/pki.c b/src/pki.c
index 0d86fbcd..9248c9b3 100644
--- a/src/pki.c
+++ b/src/pki.c
@@ -2238,8 +2238,12 @@ int ssh_pki_export_signature_blob(const ssh_signature sig,
return SSH_ERROR;
}
- ssh_string_fill(str, ssh_buffer_get(buf), ssh_buffer_get_len(buf));
+ rc = ssh_string_fill(str, ssh_buffer_get(buf), ssh_buffer_get_len(buf));
SSH_BUFFER_FREE(buf);
+ if (rc < 0) {
+ SSH_STRING_FREE(str);
+ return SSH_ERROR;
+ }
*sig_blob = str;
@@ -2558,7 +2562,10 @@ ssh_string ssh_pki_do_sign(ssh_session session,
if (session_id == NULL) {
return NULL;
}
- ssh_string_fill(session_id, crypto->session_id, crypto->digest_len);
+ rc = ssh_string_fill(session_id, crypto->session_id, crypto->digest_len);
+ if (rc < 0) {
+ goto end;
+ }
/* Fill the input */
sign_input = ssh_buffer_new();
@@ -2619,7 +2626,11 @@ ssh_string ssh_pki_do_sign_agent(ssh_session session,
if (session_id == NULL) {
return NULL;
}
- ssh_string_fill(session_id, crypto->session_id, crypto->digest_len);
+ rc = ssh_string_fill(session_id, crypto->session_id, crypto->digest_len);
+ if (rc < 0) {
+ SSH_STRING_FREE(session_id);
+ return NULL;
+ }
sig_buf = ssh_buffer_new();
if (sig_buf == NULL) {
diff --git a/src/pki_crypto.c b/src/pki_crypto.c
index 08409209..57534d2e 100644
--- a/src/pki_crypto.c
+++ b/src/pki_crypto.c
@@ -543,7 +543,6 @@ int pki_key_generate_rsa(ssh_key key, int parameter){
int pki_key_generate_dss(ssh_key key, int parameter){
int rc;
-#if OPENSSL_VERSION_NUMBER > 0x00908000L
key->dsa = DSA_new();
if (key->dsa == NULL) {
return SSH_ERROR;
@@ -560,13 +559,6 @@ int pki_key_generate_dss(ssh_key key, int parameter){
key->dsa = NULL;
return SSH_ERROR;
}
-#else
- key->dsa = DSA_generate_parameters(parameter, NULL, 0, NULL, NULL,
- NULL, NULL);
- if(key->dsa == NULL){
- return SSH_ERROR;
- }
-#endif
rc = DSA_generate_key(key->dsa);
if (rc != 1){
DSA_free(key->dsa);
@@ -840,7 +832,11 @@ ssh_string pki_private_key_to_pem(const ssh_key key,
goto err;
}
- ssh_string_fill(blob, buf->data, buf->length);
+ rc = ssh_string_fill(blob, buf->data, buf->length);
+ if (rc < 0) {
+ goto err;
+ }
+
BIO_free(mem);
return blob;
@@ -1411,6 +1407,7 @@ static ssh_string pki_dsa_signature_to_blob(const ssh_signature sig)
const unsigned char *raw_sig_data = NULL;
size_t raw_sig_len;
+ int rc;
DSA_SIG *dsa_sig;
@@ -1467,7 +1464,11 @@ static ssh_string pki_dsa_signature_to_blob(const ssh_signature sig)
return NULL;
}
- ssh_string_fill(sig_blob, buffer, 40);
+ rc = ssh_string_fill(sig_blob, buffer, 40);
+ if (rc < 0) {
+ SSH_STRING_FREE(sig_blob);
+ return NULL;
+ }
return sig_blob;
@@ -1544,7 +1545,10 @@ static ssh_string pki_ecdsa_signature_to_blob(const ssh_signature sig)
goto error;
}
- ssh_string_fill(sig_blob, ssh_buffer_get(buf), ssh_buffer_get_len(buf));
+ rc = ssh_string_fill(sig_blob, ssh_buffer_get(buf), ssh_buffer_get_len(buf));
+ if (rc < 0) {
+ goto error;
+ }
SSH_STRING_FREE(r);
SSH_STRING_FREE(s);
@@ -1554,6 +1558,7 @@ static ssh_string pki_ecdsa_signature_to_blob(const ssh_signature sig)
return sig_blob;
error:
+ SSH_STRING_FREE(sig_blob);
SSH_STRING_FREE(r);
SSH_STRING_FREE(s);
ECDSA_SIG_free(ecdsa_sig);
@@ -1698,7 +1703,11 @@ static int pki_signature_from_dsa_blob(UNUSED_PARAM(const ssh_key pubkey),
if (r == NULL) {
goto error;
}
- ssh_string_fill(r, ssh_string_data(sig_blob), 20);
+ rc = ssh_string_fill(r, ssh_string_data(sig_blob), 20);
+ if (rc < 0) {
+ SSH_STRING_FREE(r);
+ goto error;
+ }
pr = ssh_make_string_bn(r);
ssh_string_burn(r);
@@ -1711,7 +1720,11 @@ static int pki_signature_from_dsa_blob(UNUSED_PARAM(const ssh_key pubkey),
if (s == NULL) {
goto error;
}
- ssh_string_fill(s, (char *)ssh_string_data(sig_blob) + 20, 20);
+ rc = ssh_string_fill(s, (char *)ssh_string_data(sig_blob) + 20, 20);
+ if (rc < 0) {
+ SSH_STRING_FREE(s);
+ goto error;
+ }
ps = ssh_make_string_bn(s);
ssh_string_burn(s);
diff --git a/src/pki_ed25519_common.c b/src/pki_ed25519_common.c
index 9db14dac..7aa05269 100644
--- a/src/pki_ed25519_common.c
+++ b/src/pki_ed25519_common.c
@@ -214,6 +214,7 @@ int pki_ed25519_public_key_to_blob(ssh_buffer buffer, ssh_key key)
ssh_string pki_ed25519_signature_to_blob(ssh_signature sig)
{
ssh_string sig_blob;
+ int rc;
#ifdef HAVE_OPENSSL_ED25519
/* When using the OpenSSL implementation, the signature is stored in raw_sig
@@ -235,11 +236,15 @@ ssh_string pki_ed25519_signature_to_blob(ssh_signature sig)
}
#ifdef HAVE_OPENSSL_ED25519
- ssh_string_fill(sig_blob, ssh_string_data(sig->raw_sig),
- ssh_string_len(sig->raw_sig));
+ rc = ssh_string_fill(sig_blob, ssh_string_data(sig->raw_sig),
+ ssh_string_len(sig->raw_sig));
#else
- ssh_string_fill(sig_blob, sig->ed25519_sig, ED25519_SIG_LEN);
+ rc = ssh_string_fill(sig_blob, sig->ed25519_sig, ED25519_SIG_LEN);
#endif
+ if (rc < 0) {
+ SSH_STRING_FREE(sig_blob);
+ return NULL;
+ }
return sig_blob;
}
diff --git a/src/pki_gcrypt.c b/src/pki_gcrypt.c
index 0373cdae..7f8b140e 100644
--- a/src/pki_gcrypt.c
+++ b/src/pki_gcrypt.c
@@ -1781,6 +1781,7 @@ ssh_string pki_signature_to_blob(const ssh_signature sig)
gcry_sexp_t sexp;
size_t size = 0;
ssh_string sig_blob = NULL;
+ int rc;
switch(sig->type) {
case SSH_KEYTYPE_DSS:
@@ -1828,7 +1829,11 @@ ssh_string pki_signature_to_blob(const ssh_signature sig)
return NULL;
}
- ssh_string_fill(sig_blob, buffer, 40);
+ rc = ssh_string_fill(sig_blob, buffer, 40);
+ if (rc < 0) {
+ SSH_STRING_FREE(sig_blob);
+ return NULL;
+ }
break;
case SSH_KEYTYPE_RSA:
sexp = gcry_sexp_find_token(sig->rsa_sig, "s", 0);
@@ -1845,13 +1850,16 @@ ssh_string pki_signature_to_blob(const ssh_signature sig)
if (sig_blob == NULL) {
return NULL;
}
- ssh_string_fill(sig_blob, discard_const_p(char, s), size);
-
+ rc = ssh_string_fill(sig_blob, discard_const_p(char, s), size);
gcry_sexp_release(sexp);
+ if (rc < 0) {
+ SSH_STRING_FREE(sig_blob);
+ return NULL;
+ }
break;
case SSH_KEYTYPE_ED25519:
- sig_blob = pki_ed25519_signature_to_blob(sig);
- break;
+ sig_blob = pki_ed25519_signature_to_blob(sig);
+ break;
case SSH_KEYTYPE_ECDSA_P256:
case SSH_KEYTYPE_ECDSA_P384:
case SSH_KEYTYPE_ECDSA_P521:
@@ -1860,7 +1868,6 @@ ssh_string pki_signature_to_blob(const ssh_signature sig)
ssh_string R;
ssh_string S;
ssh_buffer b;
- int rc;
b = ssh_buffer_new();
if (b == NULL) {
@@ -1901,9 +1908,13 @@ ssh_string pki_signature_to_blob(const ssh_signature sig)
return NULL;
}
- ssh_string_fill(sig_blob,
+ rc = ssh_string_fill(sig_blob,
ssh_buffer_get(b), ssh_buffer_get_len(b));
SSH_BUFFER_FREE(b);
+ if (rc < 0) {
+ SSH_STRING_FREE(sig_blob);
+ return NULL;
+ }
break;
}
#endif
diff --git a/src/pki_mbedcrypto.c b/src/pki_mbedcrypto.c
index cac357f8..720fe1de 100644
--- a/src/pki_mbedcrypto.c
+++ b/src/pki_mbedcrypto.c
@@ -845,8 +845,13 @@ ssh_string pki_signature_to_blob(const ssh_signature sig)
return NULL;
}
- ssh_string_fill(sig_blob, ssh_buffer_get(b), ssh_buffer_get_len(b));
+ rc = ssh_string_fill(sig_blob, ssh_buffer_get(b), ssh_buffer_get_len(b));
SSH_BUFFER_FREE(b);
+ if (rc < 0) {
+ SSH_STRING_FREE(sig_blob);
+ return NULL;
+ }
+
break;
}
case SSH_KEYTYPE_ED25519:
@@ -1089,9 +1094,13 @@ static ssh_string rsa_do_sign_hash(const unsigned char *digest,
return NULL;
}
- ssh_string_fill(sig_blob, sig, slen);
+ ok = ssh_string_fill(sig_blob, sig, slen);
explicit_bzero(sig, slen);
SAFE_FREE(sig);
+ if (ok < 0) {
+ SSH_STRING_FREE(sig_blob);
+ return NULL;
+ }
return sig_blob;
}
diff --git a/src/threads/libcrypto.c b/src/threads/libcrypto.c
index 5786948b..dac840d3 100644
--- a/src/threads/libcrypto.c
+++ b/src/threads/libcrypto.c
@@ -57,14 +57,12 @@ void libcrypto_lock_callback(int mode, int i, const char *file, int line)
}
}
-#ifdef HAVE_OPENSSL_CRYPTO_THREADID_SET_CALLBACK
static void libcrypto_THREADID_callback(CRYPTO_THREADID *id)
{
unsigned long thread_id = (*user_callbacks->thread_id)();
CRYPTO_THREADID_set_numeric(id, thread_id);
}
-#endif /* HAVE_OPENSSL_CRYPTO_THREADID_SET_CALLBACK */
int crypto_thread_init(struct ssh_threads_callbacks_struct *cb)
{
@@ -96,12 +94,7 @@ int crypto_thread_init(struct ssh_threads_callbacks_struct *cb)
user_callbacks->mutex_init(&libcrypto_mutexes[i]);
}
-#ifdef HAVE_OPENSSL_CRYPTO_THREADID_SET_CALLBACK
CRYPTO_THREADID_set_callback(libcrypto_THREADID_callback);
-#else
- CRYPTO_set_id_callback(user_callbacks->thread_id);
-#endif
-
CRYPTO_set_locking_callback(libcrypto_lock_callback);
return SSH_OK;
@@ -116,12 +109,7 @@ void crypto_thread_finalize(void)
return;
}
-#ifdef HAVE_OPENSSL_CRYPTO_THREADID_SET_CALLBACK
CRYPTO_THREADID_set_callback(NULL);
-#else
- CRYPTO_set_id_callback(NULL);
-#endif
-
CRYPTO_set_locking_callback(NULL);
for (i = 0; i < n; ++i) {
diff --git a/src/wrapper.c b/src/wrapper.c
index d53a61a3..bbd4e4b2 100644
--- a/src/wrapper.c
+++ b/src/wrapper.c
@@ -185,6 +185,7 @@ void crypto_free(struct ssh_crypto_struct *crypto)
crypto->ecdh_privkey = NULL;
}
#endif
+ SAFE_FREE(crypto->dh_server_signature);
if (crypto->session_id != NULL) {
explicit_bzero(crypto->session_id, crypto->digest_len);
SAFE_FREE(crypto->session_id);
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 33a187fb..44d4f201 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -12,6 +12,7 @@ include_directories(${OPENSSL_INCLUDE_DIR}
${libssh_BINARY_DIR}/include
${libssh_BINARY_DIR}
${libssh_SOURCE_DIR}/src
+ ${CMAKE_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_BINARY_DIR}/tests)
@@ -32,6 +33,50 @@ target_compile_options(${TORTURE_LIBRARY} PRIVATE
-DSSH_PING_EXECUTABLE="${CMAKE_CURRENT_BINARY_DIR}/ssh_ping"
)
+# The shared version of the library is only useful when client testing is
+# enabled
+if (CLIENT_TESTING)
+ # create shared test library
+ set(TORTURE_SHARED_LIBRARY torture_shared)
+
+ if (MINGW)
+ set(USE_ATTRIBUTE_WEAK "-DUSE_ATTRIBUTE_WEAK")
+ endif ()
+
+ # Create a list of symbols that should be wrapped for override test
+ set(WRAP_SYMBOLS "")
+ list(APPEND WRAP_SYMBOLS
+ "-Wl,--wrap=chacha_keysetup"
+ "-Wl,--wrap=chacha_ivsetup"
+ "-Wl,--wrap=chacha_encrypt_bytes")
+ list(APPEND WRAP_SYMBOLS "-Wl,--wrap=poly1305_auth")
+ list(APPEND WRAP_SYMBOLS
+ "-Wl,--wrap=crypto_sign_ed25519_keypair"
+ "-Wl,--wrap=crypto_sign_ed25519"
+ "-Wl,--wrap=crypto_sign_ed25519_open")
+ list(APPEND WRAP_SYMBOLS
+ "-Wl,--wrap=crypto_scalarmult_base"
+ "-Wl,--wrap=crypto_scalarmult")
+
+ add_library(${TORTURE_SHARED_LIBRARY}
+ SHARED
+ cmdline.c
+ torture.c
+ torture_key.c
+ torture_pki.c
+ torture_cmocka.c
+ )
+ target_link_libraries(${TORTURE_SHARED_LIBRARY}
+ ${CMOCKA_LIBRARY}
+ ssh::static
+ ${WRAP_SYMBOLS}
+ )
+ target_compile_options(${TORTURE_SHARED_LIBRARY} PRIVATE
+ -DSSH_PING_EXECUTABLE="${CMAKE_CURRENT_BINARY_DIR}/ssh_ping"
+ ${USE_ATTRIBUTE_WEAK}
+ )
+endif ()
+
if (ARGP_LIBRARY)
target_link_libraries(${TORTURE_LIBRARY}
${ARGP_LIBRARY}
@@ -208,6 +253,9 @@ if (CLIENT_TESTING OR SERVER_TESTING)
# Give bob some keys
file(COPY keys/id_rsa DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/home/bob/.ssh/ FILE_PERMISSIONS OWNER_READ OWNER_WRITE)
file(COPY keys/id_rsa.pub DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/home/bob/.ssh/ FILE_PERMISSIONS OWNER_READ OWNER_WRITE)
+ # Same as id_rsa, protected with passphrase "secret"
+ file(COPY keys/id_rsa_protected DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/home/bob/.ssh/ FILE_PERMISSIONS OWNER_READ OWNER_WRITE)
+ file(COPY keys/id_rsa_protected.pub DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/home/bob/.ssh/ FILE_PERMISSIONS OWNER_READ OWNER_WRITE)
file(COPY keys/id_ecdsa DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/home/bob/.ssh/ FILE_PERMISSIONS OWNER_READ OWNER_WRITE)
file(COPY keys/id_ecdsa.pub DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/home/bob/.ssh/ FILE_PERMISSIONS OWNER_READ OWNER_WRITE)
file(COPY keys/id_ed25519 DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/home/bob/.ssh/ FILE_PERMISSIONS OWNER_READ OWNER_WRITE)
@@ -256,6 +304,9 @@ endif ()
if (CLIENT_TESTING)
add_subdirectory(client)
+
+ # Only add override testing if testing the client
+ add_subdirectory(external_override)
endif ()
if (WITH_SERVER AND SERVER_TESTING)
diff --git a/tests/client/torture_auth.c b/tests/client/torture_auth.c
index 24ecc507..29f6f5a5 100644
--- a/tests/client/torture_auth.c
+++ b/tests/client/torture_auth.c
@@ -281,6 +281,96 @@ static void torture_auth_autopubkey(void **state) {
assert_int_equal(rc, SSH_AUTH_SUCCESS);
}
+struct torture_auth_autopubkey_protected_data {
+ ssh_session session;
+ int n_calls;
+};
+
+static int
+torture_auth_autopubkey_protected_auth_function (const char *prompt, char *buf, size_t len,
+ int echo, int verify, void *userdata)
+{
+ int rc;
+ char *id, *expected_id;
+ struct torture_auth_autopubkey_protected_data *data = userdata;
+
+ assert_true(prompt != NULL);
+ assert_int_equal(echo, 0);
+ assert_int_equal(verify, 0);
+
+ expected_id = ssh_path_expand_escape(data->session, "%d/id_rsa_protected");
+ assert_true(expected_id != NULL);
+
+ rc = ssh_userauth_publickey_auto_get_current_identity(data->session, &id);
+ assert_int_equal(rc, SSH_OK);
+
+ assert_string_equal(expected_id, id);
+
+ ssh_string_free_char(id);
+ ssh_string_free_char(expected_id);
+
+ data->n_calls += 1;
+ strncpy(buf, "secret", len);
+ return 0;
+}
+
+static void torture_auth_autopubkey_protected(void **state) {
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ char *id;
+ int rc;
+
+ struct torture_auth_autopubkey_protected_data data = {
+ .session = session,
+ .n_calls = 0
+ };
+
+ struct ssh_callbacks_struct callbacks = {
+ .userdata = &data,
+ .auth_function = torture_auth_autopubkey_protected_auth_function
+ };
+
+ /* no session pointer */
+ rc = ssh_userauth_publickey_auto_get_current_identity(NULL, &id);
+ assert_int_equal(rc, SSH_ERROR);
+
+ /* no result pointer */
+ rc = ssh_userauth_publickey_auto_get_current_identity(session, NULL);
+ assert_int_equal(rc, SSH_ERROR);
+
+ /* no auto auth going on */
+ rc = ssh_userauth_publickey_auto_get_current_identity(session, &id);
+ assert_int_equal(rc, SSH_ERROR);
+
+ ssh_callbacks_init(&callbacks);
+ ssh_set_callbacks(session, &callbacks);
+
+ /* Authenticate as alice with bob his pubkey */
+ rc = ssh_options_set(session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE);
+ assert_int_equal(rc, SSH_OK);
+
+ /* Try id_rsa_protected first.
+ */
+ rc = ssh_options_set(session, SSH_OPTIONS_IDENTITY, "%d/id_rsa_protected");
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_connect(session);
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_userauth_none(session,NULL);
+ /* This request should return a SSH_REQUEST_DENIED error */
+ if (rc == SSH_ERROR) {
+ assert_int_equal(ssh_get_error_code(session), SSH_REQUEST_DENIED);
+ }
+ rc = ssh_userauth_list(session, NULL);
+ assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY);
+
+ rc = ssh_userauth_publickey_auto(session, NULL, NULL);
+ assert_int_equal(rc, SSH_AUTH_SUCCESS);
+
+ assert_int_equal (data.n_calls, 1);
+}
+
static void torture_auth_autopubkey_nonblocking(void **state) {
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
@@ -918,6 +1008,9 @@ int torture_run_tests(void) {
cmocka_unit_test_setup_teardown(torture_auth_autopubkey,
pubkey_setup,
session_teardown),
+ cmocka_unit_test_setup_teardown(torture_auth_autopubkey_protected,
+ pubkey_setup,
+ session_teardown),
cmocka_unit_test_setup_teardown(torture_auth_autopubkey_nonblocking,
pubkey_setup,
session_teardown),
diff --git a/tests/client/torture_session.c b/tests/client/torture_session.c
index d83ec9b6..2fa2ae33 100644
--- a/tests/client/torture_session.c
+++ b/tests/client/torture_session.c
@@ -218,6 +218,46 @@ static void torture_max_sessions(void **state)
#undef MAX_CHANNELS
}
+static void torture_channel_delayed_close(void **state)
+{
+ struct torture_state *s = *state;
+ ssh_session session = s->ssh.session;
+ ssh_channel channel;
+
+ char request[256];
+ char buff[256] = {0};
+
+ int rc;
+ int fd;
+
+ snprintf(request, 256,
+ "dd if=/dev/urandom of=/tmp/file bs=64000 count=2; hexdump -C /tmp/file");
+
+ channel = ssh_channel_new(session);
+ assert_non_null(channel);
+
+ rc = ssh_channel_open_session(channel);
+ assert_ssh_return_code(session, rc);
+
+ fd = ssh_get_fd(session);
+ assert_true(fd > 2);
+
+ /* Make the request, read parts with close */
+ rc = ssh_channel_request_exec(channel, request);
+ assert_ssh_return_code(session, rc);
+
+ do {
+ rc = ssh_channel_read(channel, buff, 256, 0);
+ } while(rc > 0);
+ assert_ssh_return_code(session, rc);
+
+ rc = ssh_channel_poll_timeout(channel, 500, 0);
+ assert_int_equal(rc, SSH_EOF);
+
+ ssh_channel_free(channel);
+
+}
+
int torture_run_tests(void) {
int rc;
struct CMUnitTest tests[] = {
@@ -233,6 +273,9 @@ int torture_run_tests(void) {
cmocka_unit_test_setup_teardown(torture_max_sessions,
session_setup,
session_teardown),
+ cmocka_unit_test_setup_teardown(torture_channel_delayed_close,
+ session_setup,
+ session_teardown),
};
ssh_init();
diff --git a/tests/external_override/CMakeLists.txt b/tests/external_override/CMakeLists.txt
new file mode 100644
index 00000000..a0d584e3
--- /dev/null
+++ b/tests/external_override/CMakeLists.txt
@@ -0,0 +1,133 @@
+project(external-override C)
+
+include_directories(${CMAKE_SOURCE_DIR}/include)
+
+set(LIBSSH_OVERRIDE_TESTS
+ torture_override
+)
+
+# chacha20_override
+add_library(chacha20_override SHARED
+ chacha20_override.c
+ ${libssh_SOURCE_DIR}/src/external/chacha.c
+ )
+set(CHACHA20_OVERRIDE_LIBRARY
+ ${libssh_BINARY_DIR}/lib/${CMAKE_SHARED_LIBRARY_PREFIX}chacha20_override${CMAKE_SHARED_LIBRARY_SUFFIX})
+
+# poly1305_override
+add_library(poly1305_override SHARED
+ poly1305_override.c
+ ${libssh_SOURCE_DIR}/src/external/poly1305.c
+ )
+set(POLY1305_OVERRIDE_LIBRARY
+${libssh_BINARY_DIR}/lib/${CMAKE_SHARED_LIBRARY_PREFIX}poly1305_override${CMAKE_SHARED_LIBRARY_SUFFIX})
+
+# ed25519_override
+add_library(ed25519_override SHARED
+ ed25519_override.c
+ ${libssh_SOURCE_DIR}/src/external/fe25519.c
+ ${libssh_SOURCE_DIR}/src/external/ge25519.c
+ ${libssh_SOURCE_DIR}/src/external/sc25519.c
+ ${libssh_SOURCE_DIR}/src/external/ed25519.c
+ )
+set(ED25519_OVERRIDE_LIBRARY
+${libssh_BINARY_DIR}/lib/${CMAKE_SHARED_LIBRARY_PREFIX}ed25519_override${CMAKE_SHARED_LIBRARY_SUFFIX})
+
+# curve25519_override
+add_library(curve25519_override SHARED
+ curve25519_override.c
+ ${libssh_SOURCE_DIR}/src/external/curve25519_ref.c
+ ${libssh_SOURCE_DIR}/src/external/fe25519.c
+ ${libssh_SOURCE_DIR}/src/external/ge25519.c
+ ${libssh_SOURCE_DIR}/src/external/sc25519.c
+ ${libssh_SOURCE_DIR}/src/external/ed25519.c
+ )
+set(CURVE25519_OVERRIDE_LIBRARY
+${libssh_BINARY_DIR}/lib/${CMAKE_SHARED_LIBRARY_PREFIX}curve25519_override${CMAKE_SHARED_LIBRARY_SUFFIX})
+
+set(OVERRIDE_LIBRARIES
+ ${CHACHA20_OVERRIDE_LIBRARY}:${POLY1305_OVERRIDE_LIBRARY}:${ED25519_OVERRIDE_LIBRARY}:${CURVE25519_OVERRIDE_LIBRARY}
+)
+
+if (WITH_MBEDTLS)
+ if (HAVE_MBEDTLS_CHACHA20_H AND HAVE_MBEDTLS_POLY1305_H)
+ list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_CHACHAPOLY=0")
+ else ()
+ list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_CHACHAPOLY=1")
+ endif ()
+ list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_ED25519=1")
+ list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_CURVE25519=1")
+elseif (WITH_GCRYPT)
+ if (HAVE_GCRYPT_CHACHA_POLY)
+ list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_CHACHAPOLY=0")
+ else ()
+ list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_CHACHAPOLY=1")
+ endif ()
+ list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_ED25519=1")
+ list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_CURVE25519=1")
+else ()
+ if (HAVE_OPENSSL_EVP_CHACHA20 AND HAVE_OPENSSL_EVP_POLY1305)
+ list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_CHACHAPOLY=0")
+ else ()
+ list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_CHACHAPOLY=1")
+ endif ()
+
+ if (HAVE_OPENSSL_ED25519)
+ list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_ED25519=0")
+ else ()
+ list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_ED25519=1")
+ endif ()
+
+ if (HAVE_OPENSSL_X25519)
+ list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_CURVE25519=0")
+ else ()
+ list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_CURVE25519=1")
+ endif ()
+endif ()
+
+if (NOT OSX)
+ # Remove any preload string from the environment variables list
+ foreach(env_string ${TORTURE_ENVIRONMENT})
+ if (${env_string} MATCHES "^LD_PRELOAD=*")
+ list(REMOVE_ITEM TORTURE_ENVIRONMENT ${env_string})
+ set(PRELOAD_STRING "${env_string}:")
+ endif ()
+ endforeach ()
+
+ if ("${PRELOAD_STRING}" STREQUAL "")
+ set(PRELOAD_STRING "LD_PRELOAD=")
+ endif ()
+
+ list(APPEND TORTURE_ENVIRONMENT
+ "${PRELOAD_STRING}${OVERRIDE_LIBRARIES}")
+endif()
+
+foreach(_OVERRIDE_TEST ${LIBSSH_OVERRIDE_TESTS})
+ add_cmocka_test(${_OVERRIDE_TEST}
+ SOURCES ${_OVERRIDE_TEST}.c
+ COMPILE_OPTIONS ${DEFAULT_C_COMPILE_FLAGS}
+ ${OVERRIDE_RESULTS}
+ LINK_LIBRARIES
+ ${TORTURE_SHARED_LIBRARY}
+ chacha20_override
+ poly1305_override
+ ed25519_override
+ curve25519_override
+ )
+
+ if (OSX)
+ set_property(
+ TEST
+ ${_OVERRIDE_TEST}
+ PROPERTY
+ ENVIRONMENT DYLD_FORCE_FLAT_NAMESPACE=1;DYLD_INSERT_LIBRARIES=${OVERRIDE_LIBRARIES})
+
+ else ()
+ set_property(
+ TEST
+ ${_OVERRIDE_TEST}
+ PROPERTY
+ ENVIRONMENT ${TORTURE_ENVIRONMENT})
+
+ endif()
+endforeach()
diff --git a/tests/external_override/chacha20_override.c b/tests/external_override/chacha20_override.c
new file mode 100644
index 00000000..7e166e7c
--- /dev/null
+++ b/tests/external_override/chacha20_override.c
@@ -0,0 +1,80 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2021 by Anderson Toshiyuki Sasaki - Red Hat, Inc.
+ *
+ * The SSH Library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * The SSH Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the SSH Library; see the file COPYING. If not,
+ * see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <libssh/chacha.h>
+#include <libssh/priv.h>
+
+#include "chacha20_override.h"
+
+static bool internal_function_called = false;
+
+void __wrap_chacha_keysetup(struct chacha_ctx *x,
+ const uint8_t *k,
+ uint32_t kbits)
+#ifdef HAVE_GCC_BOUNDED_ATTRIBUTE
+ __attribute__((__bounded__(__minbytes__, 2, CHACHA_MINKEYLEN)))
+#endif
+{
+ fprintf(stderr, "%s: Internal implementation was called\n", __func__);
+ internal_function_called = true;
+ chacha_keysetup(x, k, kbits);
+}
+
+void __wrap_chacha_ivsetup(struct chacha_ctx *x,
+ const uint8_t *iv,
+ const uint8_t *ctr)
+#ifdef HAVE_GCC_BOUNDED_ATTRIBUTE
+ __attribute__((__bounded__(__minbytes__, 2, CHACHA_NONCELEN)))
+ __attribute__((__bounded__(__minbytes__, 3, CHACHA_CTRLEN)))
+#endif
+{
+ fprintf(stderr, "%s: Internal implementation was called\n", __func__);
+ internal_function_called = true;
+ chacha_ivsetup(x, iv, ctr);
+}
+
+void __wrap_chacha_encrypt_bytes(struct chacha_ctx *x,
+ const uint8_t *m,
+ uint8_t *c,
+ uint32_t bytes)
+#ifdef HAVE_GCC_BOUNDED_ATTRIBUTE
+ __attribute__((__bounded__(__buffer__, 2, 4)))
+ __attribute__((__bounded__(__buffer__, 3, 4)))
+#endif
+{
+ fprintf(stderr, "%s: Internal implementation was called\n", __func__);
+ internal_function_called = true;
+ chacha_encrypt_bytes(x, m, c, bytes);
+}
+
+bool internal_chacha20_function_called(void)
+{
+ return internal_function_called;
+}
+
+void reset_chacha20_function_called(void)
+{
+ internal_function_called = false;
+}
diff --git a/tests/external_override/chacha20_override.h b/tests/external_override/chacha20_override.h
new file mode 100644
index 00000000..58f8f211
--- /dev/null
+++ b/tests/external_override/chacha20_override.h
@@ -0,0 +1,51 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2021 by Anderson Toshiyuki Sasaki - Red Hat, Inc.
+ *
+ * The SSH Library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * The SSH Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the SSH Library; see the file COPYING. If not,
+ * see <https://www.gnu.org/licenses/>.
+ */
+
+#include "libssh/chacha.h"
+
+void __wrap_chacha_keysetup(struct chacha_ctx *x,
+ const uint8_t *k,
+ uint32_t kbits)
+#ifdef HAVE_GCC_BOUNDED_ATTRIBUTE
+ __attribute__((__bounded__(__minbytes__, 2, CHACHA_MINKEYLEN)))
+#endif
+;
+
+void __wrap_chacha_ivsetup(struct chacha_ctx *x,
+ const uint8_t *iv,
+ const uint8_t *ctr)
+#ifdef HAVE_GCC_BOUNDED_ATTRIBUTE
+ __attribute__((__bounded__(__minbytes__, 2, CHACHA_NONCELEN)))
+ __attribute__((__bounded__(__minbytes__, 3, CHACHA_CTRLEN)))
+#endif
+;
+
+void __wrap_chacha_encrypt_bytes(struct chacha_ctx *x,
+ const uint8_t *m,
+ uint8_t *c,
+ uint32_t bytes)
+#ifdef HAVE_GCC_BOUNDED_ATTRIBUTE
+ __attribute__((__bounded__(__buffer__, 2, 4)))
+ __attribute__((__bounded__(__buffer__, 3, 4)))
+#endif
+;
+
+bool internal_chacha20_function_called(void);
+void reset_chacha20_function_called(void);
diff --git a/tests/external_override/curve25519_override.c b/tests/external_override/curve25519_override.c
new file mode 100644
index 00000000..983d57cd
--- /dev/null
+++ b/tests/external_override/curve25519_override.c
@@ -0,0 +1,58 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2021 by Anderson Toshiyuki Sasaki - Red Hat, Inc.
+ *
+ * The SSH Library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * The SSH Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the SSH Library; see the file COPYING. If not,
+ * see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <libssh/curve25519.h>
+#include <libssh/priv.h>
+
+#include "curve25519_override.h"
+
+static bool internal_function_called = false;
+
+int __wrap_crypto_scalarmult_base(unsigned char *q,
+ const unsigned char *n)
+{
+ fprintf(stderr, "%s: Internal implementation was called\n", __func__);
+ internal_function_called = true;
+ return crypto_scalarmult_base(q, n);
+}
+
+int __wrap_crypto_scalarmult(unsigned char *q,
+ const unsigned char *n,
+ const unsigned char *p)
+{
+ fprintf(stderr, "%s: Internal implementation was called\n", __func__);
+ internal_function_called = true;
+ return crypto_scalarmult(q, n, p);
+}
+
+bool internal_curve25519_function_called(void)
+{
+ return internal_function_called;
+}
+
+void reset_curve25519_function_called(void)
+{
+ internal_function_called = false;
+}
diff --git a/tests/external_override/curve25519_override.h b/tests/external_override/curve25519_override.h
new file mode 100644
index 00000000..634c1c40
--- /dev/null
+++ b/tests/external_override/curve25519_override.h
@@ -0,0 +1,31 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2021 by Anderson Toshiyuki Sasaki - Red Hat, Inc.
+ *
+ * The SSH Library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * The SSH Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the SSH Library; see the file COPYING. If not,
+ * see <https://www.gnu.org/licenses/>.
+ */
+
+#include "libssh/curve25519.h"
+
+int __wrap_crypto_scalarmult_base(unsigned char *q,
+ const unsigned char *n);
+
+int __wrap_crypto_scalarmult(unsigned char *q,
+ const unsigned char *n,
+ const unsigned char *p);
+
+bool internal_curve25519_function_called(void);
+void reset_curve25519_function_called(void);
diff --git a/tests/external_override/ed25519_override.c b/tests/external_override/ed25519_override.c
new file mode 100644
index 00000000..439a5dab
--- /dev/null
+++ b/tests/external_override/ed25519_override.c
@@ -0,0 +1,71 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2021 by Anderson Toshiyuki Sasaki - Red Hat, Inc.
+ *
+ * The SSH Library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * The SSH Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the SSH Library; see the file COPYING. If not,
+ * see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <libssh/ed25519.h>
+#include <libssh/priv.h>
+
+#include "ed25519_override.h"
+
+static bool internal_function_called = false;
+
+int __wrap_crypto_sign_ed25519_keypair(ed25519_pubkey pk,
+ ed25519_privkey sk)
+{
+ fprintf(stderr, "%s: Internal implementation was called\n", __func__);
+ internal_function_called = true;
+ return crypto_sign_ed25519_keypair(pk, sk);
+}
+
+int __wrap_crypto_sign_ed25519(unsigned char *sm,
+ uint64_t *smlen,
+ const unsigned char *m,
+ uint64_t mlen,
+ const ed25519_privkey sk)
+{
+ fprintf(stderr, "%s: Internal implementation was called\n", __func__);
+ internal_function_called = true;
+ return crypto_sign_ed25519(sm, smlen, m, mlen, sk);
+}
+
+int __wrap_crypto_sign_ed25519_open(unsigned char *m,
+ uint64_t *mlen,
+ const unsigned char *sm,
+ uint64_t smlen,
+ const ed25519_pubkey pk)
+{
+ fprintf(stderr, "%s: Internal implementation was called\n", __func__);
+ internal_function_called = true;
+ return crypto_sign_ed25519_open(m, mlen, sm, smlen, pk);
+}
+
+bool internal_ed25519_function_called(void)
+{
+ return internal_function_called;
+}
+
+void reset_ed25519_function_called(void)
+{
+ internal_function_called = false;
+}
diff --git a/tests/external_override/ed25519_override.h b/tests/external_override/ed25519_override.h
new file mode 100644
index 00000000..0abe2e24
--- /dev/null
+++ b/tests/external_override/ed25519_override.h
@@ -0,0 +1,39 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2021 by Anderson Toshiyuki Sasaki - Red Hat, Inc.
+ *
+ * The SSH Library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * The SSH Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the SSH Library; see the file COPYING. If not,
+ * see <https://www.gnu.org/licenses/>.
+ */
+
+#include "libssh/ed25519.h"
+
+int __wrap_crypto_sign_ed25519_keypair(ed25519_pubkey pk,
+ ed25519_privkey sk);
+
+int __wrap_crypto_sign_ed25519(unsigned char *sm,
+ uint64_t *smlen,
+ const unsigned char *m,
+ uint64_t mlen,
+ const ed25519_privkey sk);
+
+int __wrap_crypto_sign_ed25519_open(unsigned char *m,
+ uint64_t *mlen,
+ const unsigned char *sm,
+ uint64_t smlen,
+ const ed25519_pubkey pk);
+
+bool internal_ed25519_function_called(void);
+void reset_ed25519_function_called(void);
diff --git a/tests/external_override/poly1305_override.c b/tests/external_override/poly1305_override.c
new file mode 100644
index 00000000..4d78272f
--- /dev/null
+++ b/tests/external_override/poly1305_override.c
@@ -0,0 +1,54 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2021 by Anderson Toshiyuki Sasaki - Red Hat, Inc.
+ *
+ * The SSH Library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * The SSH Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the SSH Library; see the file COPYING. If not,
+ * see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <libssh/poly1305.h>
+#include <libssh/priv.h>
+
+static bool internal_function_called = false;
+
+void __wrap_poly1305_auth(uint8_t out[POLY1305_TAGLEN],
+ const uint8_t *m,
+ size_t inlen,
+ const uint8_t key[POLY1305_KEYLEN])
+#ifdef HAVE_GCC_BOUNDED_ATTRIBUTE
+ __attribute__((__bounded__(__minbytes__, 1, POLY1305_TAGLEN)))
+ __attribute__((__bounded__(__buffer__, 2, 3)))
+ __attribute__((__bounded__(__minbytes__, 4, POLY1305_KEYLEN)))
+#endif
+{
+ fprintf(stderr, "%s: Internal implementation was called\n", __func__);
+ internal_function_called = true;
+ poly1305_auth(out, m, inlen, key);
+}
+
+bool internal_poly1305_function_called(void)
+{
+ return internal_function_called;
+}
+
+void reset_poly1305_function_called(void)
+{
+ internal_function_called = false;
+}
diff --git a/tests/external_override/poly1305_override.h b/tests/external_override/poly1305_override.h
new file mode 100644
index 00000000..87717800
--- /dev/null
+++ b/tests/external_override/poly1305_override.h
@@ -0,0 +1,35 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2021 by Anderson Toshiyuki Sasaki - Red Hat, Inc.
+ *
+ * The SSH Library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * The SSH Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the SSH Library; see the file COPYING. If not,
+ * see <https://www.gnu.org/licenses/>.
+ */
+
+#include "libssh/poly1305.h"
+
+void __wrap_poly1305_auth(uint8_t out[POLY1305_TAGLEN],
+ const uint8_t *m,
+ size_t inlen,
+ const uint8_t key[POLY1305_KEYLEN])
+#ifdef HAVE_GCC_BOUNDED_ATTRIBUTE
+ __attribute__((__bounded__(__minbytes__, 1, POLY1305_TAGLEN)))
+ __attribute__((__bounded__(__buffer__, 2, 3)))
+ __attribute__((__bounded__(__minbytes__, 4, POLY1305_KEYLEN)))
+#endif
+;
+
+bool internal_poly1305_function_called(void);
+void reset_poly1305_function_called(void);
diff --git a/tests/external_override/torture_override.c b/tests/external_override/torture_override.c
new file mode 100644
index 00000000..f351d52e
--- /dev/null
+++ b/tests/external_override/torture_override.c
@@ -0,0 +1,320 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2021 by Anderson Toshiyuki Sasaki - Red Hat, Inc.
+ *
+ * The SSH Library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * The SSH Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the SSH Library; see the file COPYING. If not,
+ * see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "torture.h"
+#include "libssh/libssh.h"
+#include "libssh/priv.h"
+#include "libssh/session.h"
+
+#include <errno.h>
+#include <sys/types.h>
+#include <pwd.h>
+
+#include "chacha20_override.h"
+#include "poly1305_override.h"
+#include "curve25519_override.h"
+#include "ed25519_override.h"
+
+const char template[] = "temp_dir_XXXXXX";
+
+struct test_st {
+ char *temp_dir;
+ char *orig_dir;
+};
+
+static int sshd_setup(void **state)
+{
+ struct torture_state *s;
+ struct test_st *test_state = NULL;
+ char *temp_dir;
+ int rc;
+
+ torture_setup_sshd_server(state, false);
+
+ test_state = malloc(sizeof(struct test_st));
+ assert_non_null(test_state);
+
+ s = *((struct torture_state **)state);
+ s->private_data = test_state;
+
+ test_state->orig_dir = strdup(torture_get_current_working_dir());
+ assert_non_null(test_state->orig_dir);
+
+ temp_dir = torture_make_temp_dir(template);
+ assert_non_null(temp_dir);
+
+ rc = torture_change_dir(temp_dir);
+ assert_int_equal(rc, 0);
+
+ test_state->temp_dir = temp_dir;
+
+ return 0;
+}
+
+static int sshd_teardown(void **state)
+{
+ struct torture_state *s = *state;
+ struct test_st *test_state = s->private_data;
+ int rc;
+
+ rc = torture_change_dir(test_state->orig_dir);
+ assert_int_equal(rc, 0);
+
+ rc = torture_rmdirs(test_state->temp_dir);
+ assert_int_equal(rc, 0);
+
+ SAFE_FREE(test_state->temp_dir);
+ SAFE_FREE(test_state->orig_dir);
+ SAFE_FREE(test_state);
+
+ torture_teardown_sshd_server(state);
+
+ return 0;
+}
+
+static int session_setup(void **state)
+{
+ struct torture_state *s = *state;
+ int verbosity = torture_libssh_verbosity();
+ struct passwd *pwd;
+ bool false_v = false;
+ 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);
+ ssh_options_set(s->ssh.session, SSH_OPTIONS_HOST, TORTURE_SSH_SERVER);
+ /* Prevent parsing configuration files that can introduce different
+ * algorithms then we want to test */
+ ssh_options_set(s->ssh.session, SSH_OPTIONS_PROCESS_CONFIG, &false_v);
+
+ reset_chacha20_function_called();
+ reset_poly1305_function_called();
+ reset_curve25519_function_called();
+ reset_ed25519_function_called();
+
+ return 0;
+}
+
+static int session_teardown(void **state)
+{
+ struct torture_state *s = *state;
+
+ ssh_disconnect(s->ssh.session);
+ ssh_free(s->ssh.session);
+
+ return 0;
+}
+
+static void test_algorithm(ssh_session session,
+ const char *kex,
+ const char *cipher,
+ const char *hostkey)
+{
+ char data[256];
+ int rc;
+
+ if (kex != NULL) {
+ rc = ssh_options_set(session, SSH_OPTIONS_KEY_EXCHANGE, kex);
+ assert_ssh_return_code(session, rc);
+ }
+
+ if (cipher != NULL) {
+ rc = ssh_options_set(session, SSH_OPTIONS_CIPHERS_C_S, cipher);
+ assert_ssh_return_code(session, rc);
+ rc = ssh_options_set(session, SSH_OPTIONS_CIPHERS_S_C, cipher);
+ assert_ssh_return_code(session, rc);
+ }
+
+ if (hostkey != NULL) {
+ rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, hostkey);
+ assert_ssh_return_code(session, rc);
+ }
+
+ rc = ssh_connect(session);
+ assert_ssh_return_code(session, rc);
+
+ /* send ignore packets of all sizes */
+ memset(data, 'A', sizeof(data));
+ ssh_send_ignore(session, data);
+ ssh_handle_packets(session, 50);
+
+ rc = ssh_userauth_none(session, NULL);
+ if (rc != SSH_OK) {
+ rc = ssh_get_error_code(session);
+ assert_int_equal(rc, SSH_REQUEST_DENIED);
+ }
+
+ ssh_disconnect(session);
+}
+
+#ifdef OPENSSH_CHACHA20_POLY1305_OPENSSH_COM
+static void torture_override_chacha20_poly1305(void **state)
+{
+ struct torture_state *s = *state;
+
+ bool internal_chacha20_called;
+ bool internal_poly1305_called;
+
+ if (ssh_fips_mode()) {
+ skip();
+ }
+
+ test_algorithm(s->ssh.session,
+ NULL, /* kex */
+ "chacha20-poly1305@openssh.com",
+ NULL /* hostkey */);
+
+ internal_chacha20_called = internal_chacha20_function_called();
+ internal_poly1305_called = internal_poly1305_function_called();
+
+#if SHOULD_CALL_INTERNAL_CHACHAPOLY
+ assert_true(internal_chacha20_called ||
+ internal_poly1305_called);
+#else
+ assert_false(internal_chacha20_called ||
+ internal_poly1305_called);
+#endif
+
+}
+#endif /* OPENSSH_CHACHA20_POLY1305_OPENSSH_COM */
+
+#ifdef OPENSSH_CURVE25519_SHA256
+static void torture_override_ecdh_curve25519_sha256(void **state)
+{
+ struct torture_state *s = *state;
+ bool internal_curve25519_called;
+
+ if (ssh_fips_mode()) {
+ skip();
+ }
+
+ test_algorithm(s->ssh.session,
+ "curve25519-sha256",
+ NULL, /* cipher */
+ NULL /* hostkey */);
+
+ internal_curve25519_called = internal_curve25519_function_called();
+
+#if SHOULD_CALL_INTERNAL_CURVE25519
+ assert_true(internal_curve25519_called);
+#else
+ assert_false(internal_curve25519_called);
+#endif
+}
+#endif /* OPENSSH_CURVE25519_SHA256 */
+
+#ifdef OPENSSH_CURVE25519_SHA256_LIBSSH_ORG
+static void torture_override_ecdh_curve25519_sha256_libssh_org(void **state)
+{
+ struct torture_state *s = *state;
+ bool internal_curve25519_called;
+
+ if (ssh_fips_mode()) {
+ skip();
+ }
+
+ test_algorithm(s->ssh.session,
+ "curve25519-sha256@libssh.org",
+ NULL, /* cipher */
+ NULL /* hostkey */);
+
+ internal_curve25519_called = internal_curve25519_function_called();
+
+#if SHOULD_CALL_INTERNAL_CURVE25519
+ assert_true(internal_curve25519_called);
+#else
+ assert_false(internal_curve25519_called);
+#endif
+}
+#endif /* OPENSSH_CURVE25519_SHA256_LIBSSH_ORG */
+
+#ifdef OPENSSH_SSH_ED25519
+static void torture_override_ed25519(void **state)
+{
+ struct torture_state *s = *state;
+ bool internal_ed25519_called;
+
+ if (ssh_fips_mode()) {
+ skip();
+ }
+
+ test_algorithm(s->ssh.session,
+ NULL, /* kex */
+ NULL, /* cipher */
+ "ssh-ed25519");
+
+ internal_ed25519_called = internal_ed25519_function_called();
+
+#if SHOULD_CALL_INTERNAL_ED25519
+ assert_true(internal_ed25519_called);
+#else
+ assert_false(internal_ed25519_called);
+#endif
+}
+#endif /* OPENSSH_SSH_ED25519 */
+
+int torture_run_tests(void)
+{
+ int rc;
+ struct CMUnitTest tests[] = {
+#ifdef OPENSSH_CHACHA20_POLY1305_OPENSSH_COM
+ cmocka_unit_test_setup_teardown(torture_override_chacha20_poly1305,
+ session_setup,
+ session_teardown),
+#endif /* OPENSSH_CHACHA20_POLY1305_OPENSSH_COM */
+#ifdef OPENSSH_CURVE25519_SHA256
+ cmocka_unit_test_setup_teardown(torture_override_ecdh_curve25519_sha256,
+ session_setup,
+ session_teardown),
+#endif /* OPENSSH_CURVE25519_SHA256 */
+#ifdef OPENSSH_CURVE25519_SHA256_LIBSSH_ORG
+ cmocka_unit_test_setup_teardown(torture_override_ecdh_curve25519_sha256_libssh_org,
+ session_setup,
+ session_teardown),
+#endif /* OPENSSH_CURVE25519_SHA256_LIBSSH_ORG */
+#ifdef OPENSSH_SSH_ED25519
+ cmocka_unit_test_setup_teardown(torture_override_ed25519,
+ session_setup,
+ session_teardown),
+#endif /* OPENSSH_SSH_ED25519 */
+ };
+
+ ssh_init();
+
+ torture_filter_tests(tests);
+ rc = cmocka_run_group_tests(tests, sshd_setup, sshd_teardown);
+ if (rc != 0) {
+ return rc;
+ }
+
+ ssh_finalize();
+
+ return rc;
+}
diff --git a/tests/fuzz/README.md b/tests/fuzz/README.md
index 12afbb51..c68ef3d1 100644
--- a/tests/fuzz/README.md
+++ b/tests/fuzz/README.md
@@ -67,3 +67,67 @@ to use none cipher for the key exchange to be plausible.
cp /tmp/ssh_client tests/fuzz/ssh_client_fuzzer_corpus/$(sha1sum /tmp/ssh_client | cut -d ' ' -f 1)
cp /tmp/ssh_server tests/fuzz/ssh_server_fuzzer_corpus/$(sha1sum /tmp/ssh_server | cut -d ' ' -f 1)
+
+## Debugging issues reported by oss-fuzz
+
+OSS Fuzz provides helper scripts to reproduce issues locally. Even though the
+fuzzing scripts can ran anywhere, the best bet for reproducing is to use
+their container infrastructure. There is a
+[complete documentation](https://google.github.io/oss-fuzz/advanced-topics/reproducing/)
+but I will try to focus here on the workflow I use and libssh specifics.
+
+### Environment
+
+The helper scripts are written in Python and use docker to run containers
+so these needs to be installed. I am using podman instead of docker for
+some time, but it has some quirks that needs to be addressed in advance
+and that I describe in the rejected [PR](https://github.com/google/oss-fuzz/pull/4774).
+You can either pick up my branch or workaround them locally:
+
+ * Package `podman-docker` installs symlink from `/bin/docker` to `/bin/podman`
+ * The directories mounted to the containers need to have `container_file_t`
+ SELinux labels -- this is needed for the `build` directory that is created
+ under the oss-fuzz repository, for testcases and for source files
+ * `podman` does not like combination of `--privileged` and
+ `--cap-add SYS_PTRACE` flags. Podman can work with non-privileged containers
+ so you can just remove the `--privileged` from the `infra/helper.py`
+
+### Reproduce locally
+
+Clone the above repository from https://github.com/google/oss-fuzz/, apply
+changes from previous secion if needed, setup local clone of libssh repository
+and build the fuzzers locally (where `~/devel/libssh` is path to local libssh
+checkout):
+
+ python infra/helper.py build_fuzzers libssh ~/devel/libssh/
+
+Now, download the testcase from oss-fuzz.com (the file under `~/Downloads`)
+and we are ready to reproduce the issue locally (replace the `ssh_client_fuzzer`
+with the fuzzer name if the issue happens in other fuzzer):
+
+ python infra/helper.py reproduce libssh ssh_client_fuzzer ~/Downloads/clusterfuzz-testcase-ssh_client_fuzzer-4637376441483264
+
+This should give you the same error/leak/crash as you see on the testcase
+detail in oss-fuzz.com.
+
+I find it very useful to run libssh in debug mode, to see what happened and
+what exit path was taken to get to the error. Fortunatelly, we can simply
+pass environment variables to the container:
+
+ python infra/helper.py reproduce -eLIBSSH_VERBOSITY=9 libssh ssh_client_fuzzer ~/Downloads/clusterfuzz-testcase-ssh_client_fuzzer-4637376441483264
+
+### Fix the issue and verify the fix
+
+Now, we can properly investigate the issue and once we have a fix, we can
+make changes in our local checkout and repeat the steps above (from building
+fuzzers) to verify the issue is no longer present.
+
+### Fuzzing locally
+
+We can use the oss-fuzz tools even further and run the fuzzing process
+locally, to verify there are no similar issues happening very close to
+existing code paths and which would cause more reports very soon after
+we would fix the current issue. The following command will run fuzzer
+until it finds an issue or until killed:
+
+ python infra/helper.py run_fuzzer libssh ssh_client_fuzzer
diff --git a/tests/keys/id_rsa_protected b/tests/keys/id_rsa_protected
new file mode 100644
index 00000000..cdf5c2b8
--- /dev/null
+++ b/tests/keys/id_rsa_protected
@@ -0,0 +1,28 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABBjmItEMS
+YKDxy/7xvsZY+uAAAAEAAAAAEAAAEXAAAAB3NzaC1yc2EAAAADAQABAAABAQCz98jP4bLz
+1eNSFd5s2rauzUrREkRlcNt9yh9vXcRIMn19Jt35GUJQzqL5+gRVXbfFZ1qd2zYGSfva0a
+Kclp0iA5ZT6SjGn6BGa0ksT842IAolCpErd44k0EfoC33o0yongbC/nobhbry4+APBRVDB
+UhzoRzpHKmLPsMT5L76BK8FAhVRC3teQ9xc7I3nO6PmoOFkziXpXs6D0taPj/YgXlpy8qN
+8gyl6qaen3PoFNhlC25BTpvVW4RiFfK8zouQzCd2xUaHjqQMoyZFCHIDwDqq8sCWIwyrzy
+TmBHgB4l5OeoNH9DXbQjo8ypg2XpMtOTz8qic448NH9dcZveIXrvAAADwCLre52Jer2DTQ
+TJi91b/xNm5NRuW9366ZdoOC5NdWtbQFk4YJmdImEDo8k1t3Re24rVNxLMQwHwZX4ZLISl
+/e49RtSd6TDP44FkQF4NgtCjLUdmEWRTQj0mtENGto+wdLpL25HkmmI5WGrQU9SufVhhvj
+TxKi6ediSXIXEA5bSrWNvUaw084TT3ZfP9g98/6wr9tAYL1jVfTFUabvZzCR6+wRVoJIVc
+/+uN1bubj+IdOzYSm9Dhj4kUlK+KvI4GtouCzjuEZosjvn0ino3du1vgyT7SPdjmDxtIds
+YI7YiB1Xy3QcWdWFk+SoXhDizf9pupo2r1+G50GoBuXg2ELdsKBLXtxQ9bh37JyAcLzagq
+iVMCJjk3XMZvNXhdELRqLeWyhQ7U1BCtUBatbem0VsH6hQZ/pHReX2We8/GAUQkh4ZN8U2
+lkta9v5cb7XaBm49JjzIa3WeOS+tFHIUAWqd7MQ4f2FCTMhBssLAM7EJDOUXyo6938pa75
++LvdLZRUycE8d/PWG9SuFWSe4CJJrRlBQqPEwx9OPtKNNKgsXIGVKAFLXe+nJ4z6RXTR3R
+IGe0uaf8v9Jra5j22rq/dbQG1fP1fZNcCnIZQQo6olLaoyQmGCboC8CiCz1PNTsC1+r4pB
+oaRiCx5/qLF6EXQ03mdEqL1L/R+KMDa2+Ncw2hCSRU3GBby4wXmSqFsboRy5uxJB5sK0Ut
+sI3FW48k9zijiqVpdysRkalVVSQj8ymTG9LbjjEEmE7qxRf2dZCEnS/iPFUIu7iO9ISiOm
+4ThpROBspNyHMXKFR6mKArJX1vIwjehlaLAXA3UMY9PEFRDrWQcbatGWj4f/L6e3Tq+n7a
+t0djAgKlh40IvVL+Xf+Bsv8vUr7HAbKnOxpX69nEShiJqR5YWlEPXba+JCOjryE2ycoRB6
+d2d0SgDlB1M04uUmv2sy2Kw/CcSNHPLKGiYqqv8DAZ4GiKH2rI4oWvH9z2uRuQni98/Gw1
+1D5/QwJOHpqrUnVat4JXPBeTqiHYYtbTtqJLeIPX+Dsa6tbdjEOVgx2FkH3104xMwJyUKb
+Ccip4AbWsTwfM4GVPnJE6WCBcXC5WR6AOzuEEDQjhyzLs5K7RVb7irfhHa4Vs1/2LvxnRT
+dmTzdv/mhUNqS9RIPmFttfsSveDqY0P6WOn+K6FcCHQjpFJ3pK08glD+Sx4cbFv3lUQLfw
+hsjL0P+p+M+gTqeJ1kb2z87fiS03mHMV15lmb7nzoqyeJLIukV1jidWdGxf0efnQfmUVfX
+Wa+ehGaw==
+-----END OPENSSH PRIVATE KEY-----
diff --git a/tests/keys/id_rsa_protected.pub b/tests/keys/id_rsa_protected.pub
new file mode 100644
index 00000000..15a35b3a
--- /dev/null
+++ b/tests/keys/id_rsa_protected.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCz98jP4bLz1eNSFd5s2rauzUrREkRlcNt9yh9vXcRIMn19Jt35GUJQzqL5+gRVXbfFZ1qd2zYGSfva0aKclp0iA5ZT6SjGn6BGa0ksT842IAolCpErd44k0EfoC33o0yongbC/nobhbry4+APBRVDBUhzoRzpHKmLPsMT5L76BK8FAhVRC3teQ9xc7I3nO6PmoOFkziXpXs6D0taPj/YgXlpy8qN8gyl6qaen3PoFNhlC25BTpvVW4RiFfK8zouQzCd2xUaHjqQMoyZFCHIDwDqq8sCWIwyrzyTmBHgB4l5OeoNH9DXbQjo8ypg2XpMtOTz8qic448NH9dcZveIXrv asn@krikkit.cryptomilk.site
diff --git a/tests/torture.c b/tests/torture.c
index 7d26b013..4ac1ec66 100644
--- a/tests/torture.c
+++ b/tests/torture.c
@@ -1606,6 +1606,13 @@ void torture_reset_config(ssh_session session)
memset(session->opts.options_seen, 0, sizeof(session->opts.options_seen));
}
+#if ((defined _WIN32) || (defined _WIN64)) && (defined USE_ATTRIBUTE_WEAK)
+__attribute__((weak)) int torture_run_tests(void)
+{
+ fail();
+}
+#endif
+
int main(int argc, char **argv) {
struct argument_s arguments;
char *env = getenv("LIBSSH_VERBOSITY");
diff --git a/tests/torture.h b/tests/torture.h
index dc211443..b9b87b6a 100644
--- a/tests/torture.h
+++ b/tests/torture.h
@@ -144,7 +144,11 @@ void torture_setup_libssh_server(void **state, const char *server_path);
/*
* This function must be defined in every unit test file.
*/
+#if ((defined _WIN32) || (defined _WIN64)) && (defined USE_ATTRIBUTE_WEAK)
+__attribute__((weak)) int torture_run_tests(void);
+#else
int torture_run_tests(void);
+#endif
char *torture_make_temp_dir(const char *template);
char *torture_create_temp_file(const char *template);
diff --git a/tests/unittests/torture_callbacks.c b/tests/unittests/torture_callbacks.c
index f5f7e4da..85f4d1f4 100644
--- a/tests/unittests/torture_callbacks.c
+++ b/tests/unittests/torture_callbacks.c
@@ -138,6 +138,9 @@ static void torture_callbacks_execute_list(void **state){
};
(void)state;
+
+ assert_non_null(list);
+
ssh_callbacks_init(&c1);
ssh_callbacks_init(&c2);
ssh_callbacks_init(&c3);
@@ -213,6 +216,8 @@ static void torture_callbacks_iterate(void **state){
(void)state; /* unused */
+ assert_non_null(list);
+
ssh_callbacks_init(&c1);
ssh_callbacks_init(&c2);
diff --git a/tests/unittests/torture_config.c b/tests/unittests/torture_config.c
index 671e88c2..7ae846a4 100644
--- a/tests/unittests/torture_config.c
+++ b/tests/unittests/torture_config.c
@@ -10,6 +10,18 @@
extern LIBSSH_THREAD int ssh_log_level;
+#define USERNAME "testuser"
+#define PROXYCMD "ssh -q -W %h:%p gateway.example.com"
+#define ID_FILE "/etc/xxx"
+#define KEXALGORITHMS "ecdh-sha2-nistp521,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group14-sha1"
+#define HOSTKEYALGORITHMS "ssh-ed25519,ecdsa-sha2-nistp521,ssh-rsa"
+#define PUBKEYACCEPTEDTYPES "rsa-sha2-512,ssh-rsa,ecdsa-sha2-nistp521"
+#define MACS "hmac-sha1,hmac-sha2-256,hmac-sha2-512,hmac-sha1-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com"
+#define USER_KNOWN_HOSTS "%d/my_known_hosts"
+#define GLOBAL_KNOWN_HOSTS "/etc/ssh/my_ssh_known_hosts"
+#define BIND_ADDRESS "::1"
+
+
#define LIBSSH_TESTCONFIG1 "libssh_testconfig1.tmp"
#define LIBSSH_TESTCONFIG2 "libssh_testconfig2.tmp"
#define LIBSSH_TESTCONFIG3 "libssh_testconfig3.tmp"
@@ -23,25 +35,177 @@ extern LIBSSH_THREAD int ssh_log_level;
#define LIBSSH_TESTCONFIG11 "libssh_testconfig11.tmp"
#define LIBSSH_TESTCONFIG12 "libssh_testconfig12.tmp"
#define LIBSSH_TESTCONFIGGLOB "libssh_testc*[36].tmp"
-#define LIBSSH_TEST_PUBKEYACCEPTEDKEYTYPES "libssh_test_PubkeyAcceptedKeyTypes.tmp"
+#define LIBSSH_TEST_PUBKEYTYPES "libssh_test_PubkeyAcceptedKeyTypes.tmp"
+#define LIBSSH_TEST_NONEWLINEEND "libssh_test_NoNewLineEnd.tmp"
+#define LIBSSH_TEST_NONEWLINEONELINE "libssh_test_NoNewLineOneline.tmp"
+
+#define LIBSSH_TESTCONFIG_STRING1 \
+ "User "USERNAME"\nInclude "LIBSSH_TESTCONFIG2"\n\n"
+
+#define LIBSSH_TESTCONFIG_STRING2 \
+ "Include "LIBSSH_TESTCONFIG3"\n" \
+ "ProxyCommand "PROXYCMD"\n\n"
+
+#define LIBSSH_TESTCONFIG_STRING3 \
+ "\n\nIdentityFile "ID_FILE"\n" \
+ "\n\nKexAlgorithms "KEXALGORITHMS"\n" \
+ "\n\nHostKeyAlgorithms "HOSTKEYALGORITHMS"\n" \
+ "\n\nPubkeyAcceptedTypes "PUBKEYACCEPTEDTYPES"\n" \
+ "\n\nMACs "MACS"\n"
+
+/* Multiple Port settings -> parsing returns early. */
+#define LIBSSH_TESTCONFIG_STRING4 \
+ "Port 123\nPort 456\n"
+
+/* Testing glob include */
+#define LIBSSH_TESTCONFIG_STRING5 \
+ "User "USERNAME"\nInclude "LIBSSH_TESTCONFIGGLOB"\n\n" \
+
+#define LIBSSH_TESTCONFIG_STRING6 \
+ "ProxyCommand "PROXYCMD"\n\n"
+
+/* new options */
+#define LIBSSH_TESTCONFIG_STRING7 \
+ "\tBindAddress "BIND_ADDRESS"\n" \
+ "\tConnectTimeout 30\n" \
+ "\tLogLevel DEBUG3\n" \
+ "\tGlobalKnownHostsFile "GLOBAL_KNOWN_HOSTS"\n" \
+ "\tCompression yes\n" \
+ "\tStrictHostkeyChecking no\n" \
+ "\tGSSAPIDelegateCredentials yes\n" \
+ "\tGSSAPIServerIdentity example.com\n" \
+ "\tGSSAPIClientIdentity home.sweet\n" \
+ "\tUserKnownHostsFile "USER_KNOWN_HOSTS"\n"
+
+/* authentication methods */
+#define LIBSSH_TESTCONFIG_STRING8 \
+ "Host gss\n" \
+ "\tGSSAPIAuthentication yes\n" \
+ "Host kbd\n" \
+ "\tKbdInteractiveAuthentication yes\n" \
+ "Host pass\n" \
+ "\tPasswordAuthentication yes\n" \
+ "Host pubkey\n" \
+ "\tPubkeyAuthentication yes\n" \
+ "Host nogss\n" \
+ "\tGSSAPIAuthentication no\n" \
+ "Host nokbd\n" \
+ "\tKbdInteractiveAuthentication no\n" \
+ "Host nopass\n" \
+ "\tPasswordAuthentication no\n" \
+ "Host nopubkey\n" \
+ "\tPubkeyAuthentication no\n"
+
+/* unsupported options and corner cases */
+#define LIBSSH_TESTCONFIG_STRING9 \
+ "\n" /* empty line */ \
+ "# comment line\n" \
+ " # comment line not starting with hash\n" \
+ "UnknownConfigurationOption yes\n" \
+ "GSSAPIKexAlgorithms yes\n" \
+ "ControlMaster auto\n" /* SOC_NA */ \
+ "VisualHostkey yes\n" /* SOC_UNSUPPORTED */ \
+ "HostName =equal.sign\n" /* valid */ \
+ "ProxyJump = many-spaces.com\n" /* valid */
+
+/* Match keyword */
+#define LIBSSH_TESTCONFIG_STRING10 \
+ "Match host example\n" \
+ "\tHostName example.com\n" \
+ "Match host example1,example2\n" \
+ "\tHostName exampleN\n" \
+ "Match user guest\n" \
+ "\tHostName guest.com\n" \
+ "Match user tester host testhost\n" \
+ "\tHostName testhost.com\n" \
+ "Match !user tester host testhost\n" \
+ "\tHostName nonuser-testhost.com\n" \
+ "Match all\n" \
+ "\tHostName all-matched.com\n" \
+ /* Unsupported options */ \
+ "Match originalhost example\n" \
+ "\tHostName original-example.com\n" \
+ "Match localuser guest\n" \
+ "\tHostName local-guest.com\n"
+
+/* ProxyJump */
+#define LIBSSH_TESTCONFIG_STRING11 \
+ "Host simple\n" \
+ "\tProxyJump jumpbox\n" \
+ "Host user\n" \
+ "\tProxyJump user@jumpbox\n" \
+ "Host port\n" \
+ "\tProxyJump jumpbox:2222\n" \
+ "Host two-step\n" \
+ "\tProxyJump u1@first:222,u2@second:33\n" \
+ "Host none\n" \
+ "\tProxyJump none\n" \
+ "Host only-command\n" \
+ "\tProxyCommand "PROXYCMD"\n" \
+ "\tProxyJump jumpbox\n" \
+ "Host only-jump\n" \
+ "\tProxyJump jumpbox\n" \
+ "\tProxyCommand "PROXYCMD"\n" \
+ "Host ipv6\n" \
+ "\tProxyJump [2620:52:0::fed]\n"
+
+/* RekeyLimit combinations */
+#define LIBSSH_TESTCONFIG_STRING12 \
+ "Host default\n" \
+ "\tRekeyLimit default none\n" \
+ "Host data1\n" \
+ "\tRekeyLimit 42G\n" \
+ "Host data2\n" \
+ "\tRekeyLimit 31M\n" \
+ "Host data3\n" \
+ "\tRekeyLimit 521K\n" \
+ "Host time1\n" \
+ "\tRekeyLimit default 3D\n" \
+ "Host time2\n" \
+ "\tRekeyLimit default 2h\n" \
+ "Host time3\n" \
+ "\tRekeyLimit default 160m\n" \
+ "Host time4\n" \
+ "\tRekeyLimit default 9600\n"
+
+#define LIBSSH_TEST_PUBKEYTYPES_STRING \
+ "PubkeyAcceptedKeyTypes "PUBKEYACCEPTEDTYPES"\n"
+
+#define LIBSSH_TEST_NONEWLINEEND_STRING \
+ "ConnectTimeout 30\n" \
+ "LogLevel DEBUG3"
+
+#define LIBSSH_TEST_NONEWLINEONELINE_STRING \
+ "ConnectTimeout 30"
-#define USERNAME "testuser"
-#define PROXYCMD "ssh -q -W %h:%p gateway.example.com"
-#define ID_FILE "/etc/xxx"
-#define KEXALGORITHMS "ecdh-sha2-nistp521,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group14-sha1"
-#define HOSTKEYALGORITHMS "ssh-ed25519,ecdsa-sha2-nistp521,ssh-rsa"
-#define PUBKEYACCEPTEDTYPES "rsa-sha2-512,ssh-rsa,ecdsa-sha2-nistp521"
-#define MACS "hmac-sha1,hmac-sha2-256,hmac-sha2-512,hmac-sha1-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com"
-#define USER_KNOWN_HOSTS "%d/my_known_hosts"
-#define GLOBAL_KNOWN_HOSTS "/etc/ssh/my_ssh_known_hosts"
-#define BIND_ADDRESS "::1"
+/**
+ * @brief helper function loading configuration from either file or string
+ */
+static void _parse_config(ssh_session session,
+ const char *file, const char *string, int expected)
+{
+ int ret = -1;
+ /* make sure either config file or config string is given,
+ * not both */
+ assert_int_not_equal(file == NULL, string == NULL);
+ if (file != NULL) {
+ ret = ssh_config_parse_file(session, file);
+ } else if (string != NULL) {
+ ret = ssh_config_parse_string(session, string);
+ } else {
+ /* should not happen */
+ fail();
+ }
+
+ /* make sure parsing went as expected */
+ assert_ssh_return_code_equal(session, ret, expected);
+}
static int setup_config_files(void **state)
{
- ssh_session session;
- int verbosity;
+ (void) state; /* unused */
unlink(LIBSSH_TESTCONFIG1);
unlink(LIBSSH_TESTCONFIG2);
@@ -55,154 +219,68 @@ static int setup_config_files(void **state)
unlink(LIBSSH_TESTCONFIG10);
unlink(LIBSSH_TESTCONFIG11);
unlink(LIBSSH_TESTCONFIG12);
- unlink(LIBSSH_TEST_PUBKEYACCEPTEDKEYTYPES);
+ unlink(LIBSSH_TEST_PUBKEYTYPES);
+ unlink(LIBSSH_TEST_NONEWLINEEND);
+ unlink(LIBSSH_TEST_NONEWLINEONELINE);
torture_write_file(LIBSSH_TESTCONFIG1,
- "User "USERNAME"\nInclude "LIBSSH_TESTCONFIG2"\n\n");
+ LIBSSH_TESTCONFIG_STRING1);
torture_write_file(LIBSSH_TESTCONFIG2,
- "Include "LIBSSH_TESTCONFIG3"\n"
- "ProxyCommand "PROXYCMD"\n\n");
+ LIBSSH_TESTCONFIG_STRING2);
torture_write_file(LIBSSH_TESTCONFIG3,
- "\n\nIdentityFile "ID_FILE"\n"
- "\n\nKexAlgorithms "KEXALGORITHMS"\n"
- "\n\nHostKeyAlgorithms "HOSTKEYALGORITHMS"\n"
- "\n\nPubkeyAcceptedTypes "PUBKEYACCEPTEDTYPES"\n"
- "\n\nMACs "MACS"\n");
+ LIBSSH_TESTCONFIG_STRING3);
/* Multiple Port settings -> parsing returns early. */
torture_write_file(LIBSSH_TESTCONFIG4,
- "Port 123\nPort 456\n");
+ LIBSSH_TESTCONFIG_STRING4);
/* Testing glob include */
torture_write_file(LIBSSH_TESTCONFIG5,
- "User "USERNAME"\nInclude "LIBSSH_TESTCONFIGGLOB"\n\n");
+ LIBSSH_TESTCONFIG_STRING5);
torture_write_file(LIBSSH_TESTCONFIG6,
- "ProxyCommand "PROXYCMD"\n\n");
+ LIBSSH_TESTCONFIG_STRING6);
/* new options */
torture_write_file(LIBSSH_TESTCONFIG7,
- "\tBindAddress "BIND_ADDRESS"\n"
- "\tConnectTimeout 30\n"
- "\tLogLevel DEBUG3\n"
- "\tGlobalKnownHostsFile "GLOBAL_KNOWN_HOSTS"\n"
- "\tCompression yes\n"
- "\tStrictHostkeyChecking no\n"
- "\tGSSAPIDelegateCredentials yes\n"
- "\tGSSAPIServerIdentity example.com\n"
- "\tGSSAPIClientIdentity home.sweet\n"
- "\tUserKnownHostsFile "USER_KNOWN_HOSTS"\n");
+ LIBSSH_TESTCONFIG_STRING7);
/* authentication methods */
torture_write_file(LIBSSH_TESTCONFIG8,
- "Host gss\n"
- "\tGSSAPIAuthentication yes\n"
- "Host kbd\n"
- "\tKbdInteractiveAuthentication yes\n"
- "Host pass\n"
- "\tPasswordAuthentication yes\n"
- "Host pubkey\n"
- "\tPubkeyAuthentication yes\n"
- "Host nogss\n"
- "\tGSSAPIAuthentication no\n"
- "Host nokbd\n"
- "\tKbdInteractiveAuthentication no\n"
- "Host nopass\n"
- "\tPasswordAuthentication no\n"
- "Host nopubkey\n"
- "\tPubkeyAuthentication no\n");
+ LIBSSH_TESTCONFIG_STRING8);
/* unsupported options and corner cases */
torture_write_file(LIBSSH_TESTCONFIG9,
- "\n" /* empty line */
- "# comment line\n"
- " # comment line not starting with hash\n"
- "UnknownConfigurationOption yes\n"
- "GSSAPIKexAlgorithms yes\n"
- "ControlMaster auto\n" /* SOC_NA */
- "VisualHostkey yes\n" /* SOC_UNSUPPORTED */
- "HostName =equal.sign\n" /* valid */
- "ProxyJump = many-spaces.com\n" /* valid */
- "");
+ LIBSSH_TESTCONFIG_STRING9);
/* Match keyword */
torture_write_file(LIBSSH_TESTCONFIG10,
- "Match host example\n"
- "\tHostName example.com\n"
- "Match host example1,example2\n"
- "\tHostName exampleN\n"
- "Match user guest\n"
- "\tHostName guest.com\n"
- "Match user tester host testhost\n"
- "\tHostName testhost.com\n"
- "Match !user tester host testhost\n"
- "\tHostName nonuser-testhost.com\n"
- "Match all\n"
- "\tHostName all-matched.com\n"
- /* Unsupported options */
- "Match originalhost example\n"
- "\tHostName original-example.com\n"
- "Match localuser guest\n"
- "\tHostName local-guest.com\n"
- "");
+ LIBSSH_TESTCONFIG_STRING10);
/* ProxyJump */
torture_write_file(LIBSSH_TESTCONFIG11,
- "Host simple\n"
- "\tProxyJump jumpbox\n"
- "Host user\n"
- "\tProxyJump user@jumpbox\n"
- "Host port\n"
- "\tProxyJump jumpbox:2222\n"
- "Host two-step\n"
- "\tProxyJump u1@first:222,u2@second:33\n"
- "Host none\n"
- "\tProxyJump none\n"
- "Host only-command\n"
- "\tProxyCommand "PROXYCMD"\n"
- "\tProxyJump jumpbox\n"
- "Host only-jump\n"
- "\tProxyJump jumpbox\n"
- "\tProxyCommand "PROXYCMD"\n"
- "Host ipv6\n"
- "\tProxyJump [2620:52:0::fed]\n"
- "");
+ LIBSSH_TESTCONFIG_STRING11);
/* RekeyLimit combinations */
torture_write_file(LIBSSH_TESTCONFIG12,
- "Host default\n"
- "\tRekeyLimit default none\n"
- "Host data1\n"
- "\tRekeyLimit 42G\n"
- "Host data2\n"
- "\tRekeyLimit 31M\n"
- "Host data3\n"
- "\tRekeyLimit 521K\n"
- "Host time1\n"
- "\tRekeyLimit default 3D\n"
- "Host time2\n"
- "\tRekeyLimit default 2h\n"
- "Host time3\n"
- "\tRekeyLimit default 160m\n"
- "Host time4\n"
- "\tRekeyLimit default 9600\n"
- "");
-
- torture_write_file(LIBSSH_TEST_PUBKEYACCEPTEDKEYTYPES,
- "PubkeyAcceptedKeyTypes "PUBKEYACCEPTEDTYPES"\n");
+ LIBSSH_TESTCONFIG_STRING12);
- session = ssh_new();
+ torture_write_file(LIBSSH_TEST_PUBKEYTYPES,
+ LIBSSH_TEST_PUBKEYTYPES_STRING);
- verbosity = torture_libssh_verbosity();
- ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
+ torture_write_file(LIBSSH_TEST_NONEWLINEEND,
+ LIBSSH_TEST_NONEWLINEEND_STRING);
- *state = session;
+ torture_write_file(LIBSSH_TEST_NONEWLINEONELINE,
+ LIBSSH_TEST_NONEWLINEONELINE_STRING);
return 0;
}
-static int teardown(void **state)
+static int teardown_config_files(void **state)
{
+ (void) state; /* unused */
+
unlink(LIBSSH_TESTCONFIG1);
unlink(LIBSSH_TESTCONFIG2);
unlink(LIBSSH_TESTCONFIG3);
@@ -215,27 +293,47 @@ static int teardown(void **state)
unlink(LIBSSH_TESTCONFIG10);
unlink(LIBSSH_TESTCONFIG11);
unlink(LIBSSH_TESTCONFIG12);
- unlink(LIBSSH_TEST_PUBKEYACCEPTEDKEYTYPES);
+ unlink(LIBSSH_TEST_PUBKEYTYPES);
+ return 0;
+}
+
+static int setup(void **state)
+{
+ ssh_session session = NULL;
+ int verbosity;
+
+ session = ssh_new();
+
+ verbosity = torture_libssh_verbosity();
+ ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
+
+ *state = session;
+
+ return 0;
+}
+
+static int teardown(void **state)
+{
ssh_free(*state);
return 0;
}
/**
- * @brief tests ssh_config_parse_file with Include directives
+ * @brief tests ssh config parsing with Include directives
*/
-static void torture_config_from_file(void **state) {
- ssh_session session = *state;
+static void torture_config_include(void **state,
+ const char *file, const char *string)
+{
int ret;
char *v = NULL;
char *fips_algos = NULL;
+ ssh_session session = *state;
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG1);
- assert_true(ret == 0);
+ _parse_config(session, file, string, SSH_OK);
/* Test the variable presence */
-
ret = ssh_options_get(session, SSH_OPTIONS_PROXYCOMMAND, &v);
assert_true(ret == 0);
assert_non_null(v);
@@ -264,7 +362,8 @@ static void torture_config_from_file(void **state) {
SAFE_FREE(fips_algos);
fips_algos = ssh_keep_fips_algos(SSH_HOSTKEYS, HOSTKEYALGORITHMS);
assert_non_null(fips_algos);
- assert_string_equal(session->opts.wanted_methods[SSH_HOSTKEYS], fips_algos);
+ assert_string_equal(session->opts.wanted_methods[SSH_HOSTKEYS],
+ fips_algos);
SAFE_FREE(fips_algos);
fips_algos = ssh_keep_fips_algos(SSH_HOSTKEYS, PUBKEYACCEPTEDTYPES);
assert_non_null(fips_algos);
@@ -272,19 +371,24 @@ static void torture_config_from_file(void **state) {
SAFE_FREE(fips_algos);
fips_algos = ssh_keep_fips_algos(SSH_MAC_C_S, MACS);
assert_non_null(fips_algos);
- assert_string_equal(session->opts.wanted_methods[SSH_MAC_C_S], fips_algos);
+ assert_string_equal(session->opts.wanted_methods[SSH_MAC_C_S],
+ fips_algos);
SAFE_FREE(fips_algos);
fips_algos = ssh_keep_fips_algos(SSH_MAC_S_C, MACS);
assert_non_null(fips_algos);
- assert_string_equal(session->opts.wanted_methods[SSH_MAC_S_C], fips_algos);
+ assert_string_equal(session->opts.wanted_methods[SSH_MAC_S_C],
+ fips_algos);
SAFE_FREE(fips_algos);
} else {
assert_non_null(session->opts.wanted_methods[SSH_KEX]);
- assert_string_equal(session->opts.wanted_methods[SSH_KEX], KEXALGORITHMS);
+ assert_string_equal(session->opts.wanted_methods[SSH_KEX],
+ KEXALGORITHMS);
assert_non_null(session->opts.wanted_methods[SSH_HOSTKEYS]);
- assert_string_equal(session->opts.wanted_methods[SSH_HOSTKEYS], HOSTKEYALGORITHMS);
+ assert_string_equal(session->opts.wanted_methods[SSH_HOSTKEYS],
+ HOSTKEYALGORITHMS);
assert_non_null(session->opts.pubkey_accepted_types);
- assert_string_equal(session->opts.pubkey_accepted_types, PUBKEYACCEPTEDTYPES);
+ assert_string_equal(session->opts.pubkey_accepted_types,
+ PUBKEYACCEPTEDTYPES);
assert_non_null(session->opts.wanted_methods[SSH_MAC_S_C]);
assert_string_equal(session->opts.wanted_methods[SSH_MAC_C_S], MACS);
assert_non_null(session->opts.wanted_methods[SSH_MAC_S_C]);
@@ -293,27 +397,49 @@ static void torture_config_from_file(void **state) {
}
/**
+ * @brief tests ssh_config_parse_file with Include directives from file
+ */
+static void torture_config_include_file(void **state)
+{
+ torture_config_include(state, LIBSSH_TESTCONFIG1, NULL);
+}
+
+/**
+ * @brief tests ssh_config_parse_string with Include directives from string
+ */
+static void torture_config_include_string(void **state)
+{
+ torture_config_include(state, NULL, LIBSSH_TESTCONFIG_STRING1);
+}
+
+/**
* @brief tests ssh_config_parse_file with multiple Port settings.
*/
-static void torture_config_double_ports(void **state) {
- ssh_session session = *state;
- int ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG4);
- assert_true(ret == 0);
+static void torture_config_double_ports_file(void **state)
+{
+ _parse_config(*state, LIBSSH_TESTCONFIG4, NULL, SSH_OK);
}
-static void torture_config_glob(void **state) {
- ssh_session session = *state;
- int ret;
+/**
+ * @brief tests ssh_config_parse_string with multiple Port settings.
+ */
+static void torture_config_double_ports_string(void **state)
+{
+ _parse_config(*state, NULL, LIBSSH_TESTCONFIG_STRING4, SSH_OK);
+}
+
+static void torture_config_glob(void **state,
+ const char *file, const char *string)
+{
#if defined(HAVE_GLOB) && defined(HAVE_GLOB_GL_FLAGS_MEMBER)
+ int ret;
char *v;
-#endif /* HAVE_GLOB && HAVE_GLOB_GL_FLAGS_MEMBER */
+ ssh_session session = *state;
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG5);
- assert_true(ret == 0); /* non-existing files should not error */
+ _parse_config(session, file, string, SSH_OK);
/* Test the variable presence */
-#if defined(HAVE_GLOB) && defined(HAVE_GLOB_GL_FLAGS_MEMBER)
ret = ssh_options_get(session, SSH_OPTIONS_PROXYCOMMAND, &v);
assert_true(ret == 0);
assert_non_null(v);
@@ -330,16 +456,25 @@ static void torture_config_glob(void **state) {
#endif /* HAVE_GLOB && HAVE_GLOB_GL_FLAGS_MEMBER */
}
+static void torture_config_glob_file(void **state)
+{
+ torture_config_glob(state, LIBSSH_TESTCONFIG5, NULL);
+}
+
+static void torture_config_glob_string(void **state)
+{
+ torture_config_glob(state, NULL, LIBSSH_TESTCONFIG_STRING5);
+}
+
/**
* @brief Verify the new options are passed from configuration
*/
-static void torture_config_new(void **state)
+static void torture_config_new(void ** state,
+ const char *file, const char *string)
{
ssh_session session = *state;
- int ret = 0;
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG7);
- assert_true(ret == 0);
+ _parse_config(session, file, string, SSH_OK);
assert_string_equal(session->opts.knownhosts, USER_KNOWN_HOSTS);
assert_string_equal(session->opts.global_knownhosts, GLOBAL_KNOWN_HOSTS);
@@ -363,33 +498,40 @@ static void torture_config_new(void **state)
assert_int_equal(session->common.log_verbosity, SSH_LOG_TRACE);
}
+static void torture_config_new_file(void **state)
+{
+ torture_config_new(state, LIBSSH_TESTCONFIG7, NULL);
+}
+
+static void torture_config_new_string(void **state)
+{
+ torture_config_new(state, NULL, LIBSSH_TESTCONFIG_STRING7);
+}
+
/**
* @brief Verify the authentication methods from configuration are effective
*/
-static void torture_config_auth_methods(void **state) {
+static void torture_config_auth_methods(void **state,
+ const char *file, const char *string)
+{
ssh_session session = *state;
- int ret = 0;
/* gradually disable all the methods based on different hosts */
ssh_options_set(session, SSH_OPTIONS_HOST, "nogss");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG8);
- assert_true(ret == 0);
+ _parse_config(session, file, string, SSH_OK);
assert_false(session->opts.flags & SSH_OPT_FLAG_GSSAPI_AUTH);
assert_true(session->opts.flags & SSH_OPT_FLAG_KBDINT_AUTH);
ssh_options_set(session, SSH_OPTIONS_HOST, "nokbd");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG8);
- assert_true(ret == 0);
+ _parse_config(session, file, string, SSH_OK);
assert_false(session->opts.flags & SSH_OPT_FLAG_KBDINT_AUTH);
ssh_options_set(session, SSH_OPTIONS_HOST, "nopass");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG8);
- assert_true(ret == 0);
+ _parse_config(session, file, string, SSH_OK);
assert_false(session->opts.flags & SSH_OPT_FLAG_PASSWORD_AUTH);
ssh_options_set(session, SSH_OPTIONS_HOST, "nopubkey");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG8);
- assert_true(ret == 0);
+ _parse_config(session, file, string, SSH_OK);
assert_false(session->opts.flags & SSH_OPT_FLAG_PUBKEY_AUTH);
/* no method should be left enabled */
@@ -398,39 +540,55 @@ static void torture_config_auth_methods(void **state) {
/* gradually enable them again */
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "gss");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG8);
- assert_true(ret == 0);
+ _parse_config(session, file, string, SSH_OK);
assert_true(session->opts.flags & SSH_OPT_FLAG_GSSAPI_AUTH);
assert_false(session->opts.flags & SSH_OPT_FLAG_KBDINT_AUTH);
ssh_options_set(session, SSH_OPTIONS_HOST, "kbd");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG8);
- assert_true(ret == 0);
+ _parse_config(session, file, string, SSH_OK);
assert_true(session->opts.flags & SSH_OPT_FLAG_KBDINT_AUTH);
ssh_options_set(session, SSH_OPTIONS_HOST, "pass");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG8);
- assert_true(ret == 0);
+ _parse_config(session, file, string, SSH_OK);
assert_true(session->opts.flags & SSH_OPT_FLAG_PASSWORD_AUTH);
ssh_options_set(session, SSH_OPTIONS_HOST, "pubkey");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG8);
- assert_true(ret == 0);
+ _parse_config(session, file, string, SSH_OK);
assert_true(session->opts.flags & SSH_OPT_FLAG_PUBKEY_AUTH);
}
/**
+ * @brief Verify the authentication methods from configuration file
+ * are effective
+ */
+static void torture_config_auth_methods_file(void **state)
+{
+ torture_config_auth_methods(state, LIBSSH_TESTCONFIG8, NULL);
+}
+
+/**
+ * @brief Verify the authentication methods from configuration string
+ * are effective
+ */
+static void torture_config_auth_methods_string(void **state)
+{
+ torture_config_auth_methods(state, NULL, LIBSSH_TESTCONFIG_STRING8);
+}
+
+/**
* @brief Verify the configuration parser does not choke on unknown
* or unsupported configuration options
*/
-static void torture_config_unknown(void **state) {
+static void torture_config_unknown(void **state,
+ const char *file, const char *string)
+{
ssh_session session = *state;
int ret = 0;
/* test corner cases */
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG9);
- assert_true(ret == 0);
- assert_string_equal(session->opts.ProxyCommand, "ssh -W [%h]:%p many-spaces.com");
+ _parse_config(session, file, string, SSH_OK);
+ assert_string_equal(session->opts.ProxyCommand,
+ "ssh -W [%h]:%p many-spaces.com");
assert_string_equal(session->opts.host, "equal.sign");
ret = ssh_config_parse_file(session, "/etc/ssh/ssh_config");
@@ -439,128 +597,155 @@ static void torture_config_unknown(void **state) {
assert_true(ret == 0);
}
+/**
+ * @brief Verify the configuration parser does not choke on unknown
+ * or unsupported configuration options in configuration file
+ */
+static void torture_config_unknown_file(void **state)
+{
+ torture_config_unknown(state, LIBSSH_TESTCONFIG9, NULL);
+}
+
+/**
+ * @brief Verify the configuration parser does not choke on unknown
+ * or unsupported configuration options in configuration string
+ */
+static void torture_config_unknown_string(void **state)
+{
+ torture_config_unknown(state, NULL, LIBSSH_TESTCONFIG_STRING9);
+}
/**
* @brief Verify the configuration parser accepts Match keyword with
* full OpenSSH syntax.
*/
-static void torture_config_match(void **state)
+static void torture_config_match(void **state,
+ const char *file, const char *string)
{
ssh_session session = *state;
char *localuser = NULL;
- char config[1024];
- int ret = 0;
+ const char *config;
+ char config_string[1024];
/* Without any settings we should get all-matched.com hostname */
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "unmatched");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG10);
- assert_ssh_return_code(session, ret);
+ _parse_config(session, file, string, SSH_OK);
assert_string_equal(session->opts.host, "all-matched.com");
/* Hostname example does simple hostname matching */
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "example");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG10);
- assert_ssh_return_code(session, ret);
+ _parse_config(session, file, string, SSH_OK);
assert_string_equal(session->opts.host, "example.com");
/* We can match also both hosts from a comma separated list */
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "example1");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG10);
- assert_ssh_return_code(session, ret);
+ _parse_config(session, file, string, SSH_OK);
assert_string_equal(session->opts.host, "exampleN");
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "example2");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG10);
- assert_ssh_return_code(session, ret);
+ _parse_config(session, file, string, SSH_OK);
assert_string_equal(session->opts.host, "exampleN");
/* We can match by user */
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_USER, "guest");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG10);
- assert_ssh_return_code(session, ret);
+ _parse_config(session, file, string, SSH_OK);
assert_string_equal(session->opts.host, "guest.com");
/* We can combine two options on a single line to match both of them */
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_USER, "tester");
ssh_options_set(session, SSH_OPTIONS_HOST, "testhost");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG10);
- assert_ssh_return_code(session, ret);
+ _parse_config(session, file, string, SSH_OK);
assert_string_equal(session->opts.host, "testhost.com");
/* We can also negate conditions */
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_USER, "not-tester");
ssh_options_set(session, SSH_OPTIONS_HOST, "testhost");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG10);
- assert_ssh_return_code(session, ret);
+ _parse_config(session, file, string, SSH_OK);
assert_string_equal(session->opts.host, "nonuser-testhost.com");
+ /* In this part, we try various other config files and strings. */
+
/* Match final is not completely supported, but should do quite much the
* same as "match all". The trailing "all" is not mandatory. */
- torture_write_file(LIBSSH_TESTCONFIG10,
- "Match final all\n"
- "\tHostName final-all.com\n"
- "");
+ config = "Match final all\n"
+ "\tHostName final-all.com\n";
+ if (file != NULL) {
+ torture_write_file(file, config);
+ } else {
+ string = config;
+ }
torture_reset_config(session);
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG10);
- assert_ssh_return_code(session, ret);
+ _parse_config(session, file, string, SSH_OK);
assert_string_equal(session->opts.host, "final-all.com");
- torture_write_file(LIBSSH_TESTCONFIG10,
- "Match final\n"
- "\tHostName final.com\n"
- "");
+ config = "Match final\n"
+ "\tHostName final.com\n";
+ if (file != NULL) {
+ torture_write_file(file, config);
+ } else {
+ string = config;
+ }
torture_reset_config(session);
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG10);
- assert_ssh_return_code(session, ret);
+ _parse_config(session, file, string, SSH_OK);
assert_string_equal(session->opts.host, "final.com");
- /* Match canonical is not completely supported, but should do quite much the
- * same as "match all". The trailing "all" is not mandatory. */
- torture_write_file(LIBSSH_TESTCONFIG10,
- "Match canonical all\n"
- "\tHostName canonical-all.com\n"
- "");
+ /* Match canonical is not completely supported, but should do quite
+ * much the same as "match all". The trailing "all" is not mandatory. */
+ config = "Match canonical all\n"
+ "\tHostName canonical-all.com\n";
+ if (file != NULL) {
+ torture_write_file(file, config);
+ } else {
+ string = config;
+ }
torture_reset_config(session);
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG10);
- assert_ssh_return_code(session, ret);
+ _parse_config(session, file, string, SSH_OK);
assert_string_equal(session->opts.host, "canonical-all.com");
- torture_write_file(LIBSSH_TESTCONFIG10,
- "Match canonical all\n"
- "\tHostName canonical.com\n"
- "");
+ config = "Match canonical all\n"
+ "\tHostName canonical.com\n";
+ if (file != NULL) {
+ torture_write_file(file, config);
+ } else {
+ string = config;
+ }
torture_reset_config(session);
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG10);
- assert_ssh_return_code(session, ret);
+ _parse_config(session, file, string, SSH_OK);
assert_string_equal(session->opts.host, "canonical.com");
localuser = ssh_get_local_username();
assert_non_null(localuser);
- snprintf(config, sizeof(config),
+ snprintf(config_string, sizeof(config_string),
"Match localuser %s\n"
- "\tHostName otherhost\n"
- "", localuser);
+ "\tHostName otherhost\n",
+ localuser);
+ config = config_string;
free(localuser);
- torture_write_file(LIBSSH_TESTCONFIG10, config);
+ if (file != NULL) {
+ torture_write_file(file, config);
+ } else {
+ string = config;
+ }
torture_reset_config(session);
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG10);
- assert_ssh_return_code(session, ret);
+ _parse_config(session, file, string, SSH_OK);
assert_string_equal(session->opts.host, "otherhost");
- torture_write_file(LIBSSH_TESTCONFIG10,
- "Match exec true\n"
- "\tHostName execed-true.com\n"
- "");
+ config = "Match exec true\n"
+ "\tHostName execed-true.com\n";
+ if (file != NULL) {
+ torture_write_file(file, config);
+ } else {
+ string = config;
+ }
torture_reset_config(session);
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG10);
- assert_ssh_return_code(session, ret);
+ _parse_config(session, file, string, SSH_OK);
#ifdef _WIN32
/* The match exec is not supported on windows at this moment */
assert_string_equal(session->opts.host, "otherhost");
@@ -568,13 +753,15 @@ static void torture_config_match(void **state)
assert_string_equal(session->opts.host, "execed-true.com");
#endif
- torture_write_file(LIBSSH_TESTCONFIG10,
- "Match !exec false\n"
- "\tHostName execed-false.com\n"
- "");
+ config = "Match !exec false\n"
+ "\tHostName execed-false.com\n";
+ if (file != NULL) {
+ torture_write_file(file, config);
+ } else {
+ string = config;
+ }
torture_reset_config(session);
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG10);
- assert_ssh_return_code(session, ret);
+ _parse_config(session, file, string, SSH_OK);
#ifdef _WIN32
/* The match exec is not supported on windows at this moment */
assert_string_equal(session->opts.host, "otherhost");
@@ -582,13 +769,15 @@ static void torture_config_match(void **state)
assert_string_equal(session->opts.host, "execed-false.com");
#endif
- torture_write_file(LIBSSH_TESTCONFIG10,
- "Match exec \"test 1 -eq 1\"\n"
- "\tHostName execed-arguments.com\n"
- "");
+ config = "Match exec \"test 1 -eq 1\"\n"
+ "\tHostName execed-arguments.com\n";
+ if (file != NULL) {
+ torture_write_file(file, config);
+ } else {
+ string = config;
+ }
torture_reset_config(session);
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG10);
- assert_ssh_return_code(session, ret);
+ _parse_config(session, file, string, SSH_OK);
#ifdef _WIN32
/* The match exec is not supported on windows at this moment */
assert_string_equal(session->opts.host, "otherhost");
@@ -598,355 +787,435 @@ static void torture_config_match(void **state)
/* Try to create some invalid configurations */
/* Missing argument to Match*/
- torture_write_file(LIBSSH_TESTCONFIG10,
- "Match\n"
- "\tHost missing.com\n"
- "");
+ config = "Match\n"
+ "\tHost missing.com\n";
+ if (file != NULL) {
+ torture_write_file(file, config);
+ } else {
+ string = config;
+ }
torture_reset_config(session);
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG10);
- assert_ssh_return_code_equal(session, ret, SSH_ERROR);
+ _parse_config(session, file, string, SSH_ERROR);
/* Missing argument to unsupported option originalhost */
- torture_write_file(LIBSSH_TESTCONFIG10,
- "Match originalhost\n"
- "\tHost originalhost.com\n"
- "");
+ config = "Match originalhost\n"
+ "\tHost originalhost.com\n";
+ if (file != NULL) {
+ torture_write_file(file, config);
+ } else {
+ string = config;
+ }
torture_reset_config(session);
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG10);
- assert_ssh_return_code_equal(session, ret, SSH_ERROR);
+ _parse_config(session, file, string, SSH_ERROR);
/* Missing argument to option localuser */
- torture_write_file(LIBSSH_TESTCONFIG10,
- "Match localuser\n"
- "\tUser localuser2\n"
- "");
+ config = "Match localuser\n"
+ "\tUser localuser2\n";
+ if (file != NULL) {
+ torture_write_file(file, config);
+ } else {
+ string = config;
+ }
torture_reset_config(session);
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG10);
- assert_ssh_return_code_equal(session, ret, SSH_ERROR);
+ _parse_config(session, file, string, SSH_ERROR);
/* Missing argument to option user */
- torture_write_file(LIBSSH_TESTCONFIG10,
- "Match user\n"
- "\tUser user2\n"
- "");
+ config = "Match user\n"
+ "\tUser user2\n";
+ if (file != NULL) {
+ torture_write_file(file, config);
+ } else {
+ string = config;
+ }
torture_reset_config(session);
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG10);
- assert_ssh_return_code_equal(session, ret, SSH_ERROR);
+ _parse_config(session, file, string, SSH_ERROR);
/* Missing argument to option host */
- torture_write_file(LIBSSH_TESTCONFIG10,
- "Match host\n"
- "\tUser host2\n"
- "");
+ config = "Match host\n"
+ "\tUser host2\n";
+ if (file != NULL) {
+ torture_write_file(file, config);
+ } else {
+ string = config;
+ }
torture_reset_config(session);
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG10);
- assert_ssh_return_code_equal(session, ret, SSH_ERROR);
+ _parse_config(session, file, string, SSH_ERROR);
/* Missing argument to option exec */
- torture_write_file(LIBSSH_TESTCONFIG10,
- "Match exec\n"
- "\tUser exec\n"
- "");
+ config = "Match exec\n"
+ "\tUser exec\n";
+ if (file != NULL) {
+ torture_write_file(file, config);
+ } else {
+ string = config;
+ }
torture_reset_config(session);
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG10);
- assert_ssh_return_code_equal(session, ret, SSH_ERROR);
+ _parse_config(session, file, string, SSH_ERROR);
+}
+
+/**
+ * @brief Verify the configuration parser accepts Match keyword with
+ * full OpenSSH syntax through configuration file.
+ */
+static void torture_config_match_file(void **state)
+{
+ torture_config_match(state, LIBSSH_TESTCONFIG10, NULL);
+}
+
+/**
+ * @brief Verify the configuration parser accepts Match keyword with
+ * full OpenSSH syntax through configuration string.
+ */
+static void torture_config_match_string(void **state)
+{
+ torture_config_match(state, NULL, LIBSSH_TESTCONFIG_STRING10);
}
/**
* @brief Verify we can parse ProxyJump configuration option
*/
-static void torture_config_proxyjump(void **state) {
+static void torture_config_proxyjump(void **state,
+ const char *file, const char *string)
+{
ssh_session session = *state;
- int ret = 0;
+ const char *config;
/* Simplest version with just a hostname */
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "simple");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG11);
- assert_ssh_return_code(session, ret);
+ _parse_config(session, file, string, SSH_OK);
assert_string_equal(session->opts.ProxyCommand, "ssh -W [%h]:%p jumpbox");
/* With username */
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "user");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG11);
- assert_ssh_return_code(session, ret);
+ _parse_config(session, file, string, SSH_OK);
assert_string_equal(session->opts.ProxyCommand,
"ssh -l user -W [%h]:%p jumpbox");
/* With port */
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "port");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG11);
- assert_ssh_return_code(session, ret);
+ _parse_config(session, file, string, SSH_OK);
assert_string_equal(session->opts.ProxyCommand,
"ssh -p 2222 -W [%h]:%p jumpbox");
/* Two step jump */
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "two-step");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG11);
- assert_ssh_return_code(session, ret);
+ _parse_config(session, file, string, SSH_OK);
assert_string_equal(session->opts.ProxyCommand,
"ssh -l u1 -p 222 -J u2@second:33 -W [%h]:%p first");
/* none */
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "none");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG11);
- assert_ssh_return_code(session, ret);
+ _parse_config(session, file, string, SSH_OK);
assert_true(session->opts.ProxyCommand == NULL);
/* If also ProxyCommand is specifed, the first is applied */
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "only-command");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG11);
- assert_ssh_return_code(session, ret);
+ _parse_config(session, file, string, SSH_OK);
assert_string_equal(session->opts.ProxyCommand, PROXYCMD);
/* If also ProxyCommand is specifed, the first is applied */
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "only-jump");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG11);
- assert_ssh_return_code(session, ret);
+ _parse_config(session, file, string, SSH_OK);
assert_string_equal(session->opts.ProxyCommand,
"ssh -W [%h]:%p jumpbox");
/* IPv6 address */
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "ipv6");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG11);
- assert_ssh_return_code(session, ret);
+ _parse_config(session, file, string, SSH_OK);
assert_string_equal(session->opts.ProxyCommand,
"ssh -W [%h]:%p 2620:52:0::fed");
+ /* 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"
- "");
+ config = "Host bad-port\n"
+ "\tProxyJump jumpbox:22bad22\n";
+ if (file != NULL) {
+ torture_write_file(file, config);
+ } else {
+ string = config;
+ }
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);
+ _parse_config(session, file, string, SSH_ERROR);
/* Too many @ */
- torture_write_file(LIBSSH_TESTCONFIG11,
- "Host bad-hostname\n"
- "\tProxyJump user@principal.com@jumpbox:22\n"
- "");
+ config = "Host bad-hostname\n"
+ "\tProxyJump user@principal.com@jumpbox:22\n";
+ if (file != NULL) {
+ torture_write_file(file, config);
+ } else {
+ string = config;
+ }
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);
+ _parse_config(session, file, string, SSH_ERROR);
/* Braces mismatch in hostname */
- torture_write_file(LIBSSH_TESTCONFIG11,
- "Host mismatch\n"
- "\tProxyJump [::1\n"
- "");
+ config = "Host mismatch\n"
+ "\tProxyJump [::1\n";
+ if (file != NULL) {
+ torture_write_file(file, config);
+ } else {
+ string = config;
+ }
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "mismatch");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG11);
- assert_ssh_return_code_equal(session, ret, SSH_ERROR);
+ _parse_config(session, file, string, SSH_ERROR);
/* Bad host-port separator */
- torture_write_file(LIBSSH_TESTCONFIG11,
- "Host beef\n"
- "\tProxyJump [dead::beef]::22\n"
- "");
+ config = "Host beef\n"
+ "\tProxyJump [dead::beef]::22\n";
+ if (file != NULL) {
+ torture_write_file(file, config);
+ } else {
+ string = config;
+ }
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "beef");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG11);
- assert_ssh_return_code_equal(session, ret, SSH_ERROR);
+ _parse_config(session, file, string, SSH_ERROR);
/* Missing hostname */
- torture_write_file(LIBSSH_TESTCONFIG11,
- "Host no-host\n"
- "\tProxyJump user@:22\n"
- "");
+ config = "Host no-host\n"
+ "\tProxyJump user@:22\n";
+ if (file != NULL) {
+ torture_write_file(file, config);
+ } else {
+ string = config;
+ }
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "no-host");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG11);
- assert_ssh_return_code_equal(session, ret, SSH_ERROR);
+ _parse_config(session, file, string, SSH_ERROR);
/* Missing user */
- torture_write_file(LIBSSH_TESTCONFIG11,
- "Host no-user\n"
- "\tProxyJump @host:22\n"
- "");
+ config = "Host no-user\n"
+ "\tProxyJump @host:22\n";
+ if (file != NULL) {
+ torture_write_file(file, config);
+ } else {
+ string = config;
+ }
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "no-user");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG11);
- assert_ssh_return_code_equal(session, ret, SSH_ERROR);
+ _parse_config(session, file, string, SSH_ERROR);
/* Missing port */
- torture_write_file(LIBSSH_TESTCONFIG11,
- "Host no-port\n"
- "\tProxyJump host:\n"
- "");
+ config = "Host no-port\n"
+ "\tProxyJump host:\n";
+ if (file != NULL) {
+ torture_write_file(file, config);
+ } else {
+ string = config;
+ }
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "no-port");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG11);
- assert_ssh_return_code_equal(session, ret, SSH_ERROR);
+ _parse_config(session, file, string, SSH_ERROR);
/* Non-numeric port in second jump */
- torture_write_file(LIBSSH_TESTCONFIG11,
- "Host bad-port-2\n"
- "\tProxyJump localhost,jumpbox:22bad22\n"
- "");
+ config = "Host bad-port-2\n"
+ "\tProxyJump localhost,jumpbox:22bad22\n";
+ if (file != NULL) {
+ torture_write_file(file, config);
+ } else {
+ string = config;
+ }
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "bad-port-2");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG11);
- assert_ssh_return_code_equal(session, ret, SSH_ERROR);
+ _parse_config(session, file, string, SSH_ERROR);
/* Too many @ in second jump */
- torture_write_file(LIBSSH_TESTCONFIG11,
- "Host bad-hostname\n"
- "\tProxyJump localhost,user@principal.com@jumpbox:22\n"
- "");
+ config = "Host bad-hostname\n"
+ "\tProxyJump localhost,user@principal.com@jumpbox:22\n";
+ if (file != NULL) {
+ torture_write_file(file, config);
+ } else {
+ string = config;
+ }
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);
+ _parse_config(session, file, string, SSH_ERROR);
/* Braces mismatch in second jump */
- torture_write_file(LIBSSH_TESTCONFIG11,
- "Host mismatch\n"
- "\tProxyJump localhost,[::1:20\n"
- "");
+ config = "Host mismatch\n"
+ "\tProxyJump localhost,[::1:20\n";
+ if (file != NULL) {
+ torture_write_file(file, config);
+ } else {
+ string = config;
+ }
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "mismatch");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG11);
- assert_ssh_return_code_equal(session, ret, SSH_ERROR);
+ _parse_config(session, file, string, SSH_ERROR);
/* Bad host-port separator in second jump */
- torture_write_file(LIBSSH_TESTCONFIG11,
- "Host beef\n"
- "\tProxyJump localhost,[dead::beef]::22\n"
- "");
+ config = "Host beef\n"
+ "\tProxyJump localhost,[dead::beef]::22\n";
+ if (file != NULL) {
+ torture_write_file(file, config);
+ } else {
+ string = config;
+ }
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "beef");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG11);
- assert_ssh_return_code_equal(session, ret, SSH_ERROR);
+ _parse_config(session, file, string, SSH_ERROR);
/* Missing hostname in second jump */
- torture_write_file(LIBSSH_TESTCONFIG11,
- "Host no-host\n"
- "\tProxyJump localhost,user@:22\n"
- "");
+ config = "Host no-host\n"
+ "\tProxyJump localhost,user@:22\n";
+ if (file != NULL) {
+ torture_write_file(file, config);
+ } else {
+ string = config;
+ }
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "no-host");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG11);
- assert_ssh_return_code_equal(session, ret, SSH_ERROR);
+ _parse_config(session, file, string, SSH_ERROR);
/* Missing user in second jump */
- torture_write_file(LIBSSH_TESTCONFIG11,
- "Host no-user\n"
- "\tProxyJump localhost,@host:22\n"
- "");
+ config = "Host no-user\n"
+ "\tProxyJump localhost,@host:22\n";
+ if (file != NULL) {
+ torture_write_file(file, config);
+ } else {
+ string = config;
+ }
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "no-user");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG11);
- assert_ssh_return_code_equal(session, ret, SSH_ERROR);
+ _parse_config(session, file, string, SSH_ERROR);
/* Missing port in second jump */
- torture_write_file(LIBSSH_TESTCONFIG11,
- "Host no-port\n"
- "\tProxyJump localhost,host:\n"
- "");
+ config = "Host no-port\n"
+ "\tProxyJump localhost,host:\n";
+ if (file != NULL) {
+ torture_write_file(file, config);
+ } else {
+ string = config;
+ }
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "no-port");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG11);
- assert_ssh_return_code_equal(session, ret, SSH_ERROR);
+ _parse_config(session, file, string, SSH_ERROR);
+}
+
+/**
+ * @brief Verify we can parse ProxyJump configuration option from file
+ */
+static void torture_config_proxyjump_file(void **state)
+{
+ torture_config_proxyjump(state, LIBSSH_TESTCONFIG11, NULL);
+}
+
+/**
+ * @brief Verify we can parse ProxyJump configuration option from string
+ */
+static void torture_config_proxyjump_string(void **state)
+{
+ torture_config_proxyjump(state, NULL, LIBSSH_TESTCONFIG_STRING11);
}
/**
* @brief Verify the configuration parser handles all the possible
* versions of RekeyLimit configuration option.
*/
-static void torture_config_rekey(void **state)
+static void torture_config_rekey(void **state,
+ const char *file, const char *string)
{
ssh_session session = *state;
- int ret = 0;
/* Default values */
ssh_options_set(session, SSH_OPTIONS_HOST, "default");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG12);
- assert_ssh_return_code(session, ret);
+ _parse_config(session, file, string, SSH_OK);
assert_int_equal(session->opts.rekey_data, 0);
assert_int_equal(session->opts.rekey_time, 0);
/* 42 GB */
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "data1");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG12);
- assert_ssh_return_code(session, ret);
- assert_int_equal(session->opts.rekey_data, (uint64_t) 42 * 1024 * 1024 * 1024);
+ _parse_config(session, file, string, SSH_OK);
+ assert_int_equal(session->opts.rekey_data,
+ (uint64_t) 42 * 1024 * 1024 * 1024);
assert_int_equal(session->opts.rekey_time, 0);
/* 41 MB */
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "data2");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG12);
- assert_ssh_return_code(session, ret);
+ _parse_config(session, file, string, SSH_OK);
assert_int_equal(session->opts.rekey_data, 31 * 1024 * 1024);
assert_int_equal(session->opts.rekey_time, 0);
/* 521 KB */
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "data3");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG12);
- assert_ssh_return_code(session, ret);
+ _parse_config(session, file, string, SSH_OK);
assert_int_equal(session->opts.rekey_data, 521 * 1024);
assert_int_equal(session->opts.rekey_time, 0);
/* default 3D */
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "time1");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG12);
- assert_ssh_return_code(session, ret);
+ _parse_config(session, file, string, SSH_OK);
assert_int_equal(session->opts.rekey_data, 0);
assert_int_equal(session->opts.rekey_time, 3 * 24 * 60 * 60 * 1000);
/* default 2h */
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "time2");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG12);
- assert_ssh_return_code(session, ret);
+ _parse_config(session, file, string, SSH_OK);
assert_int_equal(session->opts.rekey_data, 0);
assert_int_equal(session->opts.rekey_time, 2 * 60 * 60 * 1000);
/* default 160m */
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "time3");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG12);
- assert_ssh_return_code(session, ret);
+ _parse_config(session, file, string, SSH_OK);
assert_int_equal(session->opts.rekey_data, 0);
assert_int_equal(session->opts.rekey_time, 160 * 60 * 1000);
/* default 9600 [s] */
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "time4");
- ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG12);
- assert_ssh_return_code(session, ret);
+ _parse_config(session, file, string, SSH_OK);
assert_int_equal(session->opts.rekey_data, 0);
assert_int_equal(session->opts.rekey_time, 9600 * 1000);
}
/**
- * @brief test ssh_config_parse_file with PubkeyAcceptedKeyTypes
+ * @brief Verify the configuration parser handles all the possible
+ * versions of RekeyLimit configuration option in file
+ */
+static void torture_config_rekey_file(void **state)
+{
+ torture_config_rekey(state, LIBSSH_TESTCONFIG12, NULL);
+}
+
+/**
+ * @brief Verify the configuration parser handles all the possible
+ * versions of RekeyLimit configuration option in string
+ */
+static void torture_config_rekey_string(void **state)
+{
+ torture_config_rekey(state, NULL, LIBSSH_TESTCONFIG_STRING12);
+}
+
+/**
+ * @brief test PubkeyAcceptedKeyTypes helper function
*/
-static void torture_config_pubkeyacceptedkeytypes(void **state)
+static void torture_config_pubkeytypes(void **state,
+ const char *file, const char *string)
{
ssh_session session = *state;
- int rc;
char *fips_algos;
- rc = ssh_config_parse_file(session, LIBSSH_TEST_PUBKEYACCEPTEDKEYTYPES);
- assert_int_equal(rc, SSH_OK);
+ _parse_config(session, file, string, SSH_OK);
if (ssh_fips_mode()) {
fips_algos = ssh_keep_fips_algos(SSH_HOSTKEYS, PUBKEYACCEPTEDTYPES);
@@ -954,10 +1223,85 @@ static void torture_config_pubkeyacceptedkeytypes(void **state)
assert_string_equal(session->opts.pubkey_accepted_types, fips_algos);
SAFE_FREE(fips_algos);
} else {
- assert_string_equal(session->opts.pubkey_accepted_types, PUBKEYACCEPTEDTYPES);
+ assert_string_equal(session->opts.pubkey_accepted_types,
+ PUBKEYACCEPTEDTYPES);
}
}
+/**
+ * @brief test parsing PubkeyAcceptedKeyTypes from file
+ */
+static void torture_config_pubkeytypes_file(void **state)
+{
+ torture_config_pubkeytypes(state, LIBSSH_TEST_PUBKEYTYPES, NULL);
+}
+
+/**
+ * @brief test parsing PubkeyAcceptedKeyTypes from string
+ */
+static void torture_config_pubkeytypes_string(void **state)
+{
+ torture_config_pubkeytypes(state, NULL, LIBSSH_TEST_PUBKEYTYPES_STRING);
+}
+
+/**
+ * @brief Verify the configuration parser handles
+ * missing newline in the end
+ */
+static void torture_config_nonewlineend(void **state,
+ const char *file, const char *string)
+{
+ _parse_config(*state, file, string, SSH_OK);
+}
+
+/**
+ * @brief Verify the configuration parser handles
+ * missing newline in the end of file
+ */
+static void torture_config_nonewlineend_file(void **state)
+{
+ torture_config_nonewlineend(state, LIBSSH_TEST_NONEWLINEEND, NULL);
+}
+
+/**
+ * @brief Verify the configuration parser handles
+ * missing newline in the end of string
+ */
+static void torture_config_nonewlineend_string(void **state)
+{
+ torture_config_nonewlineend(state, NULL, LIBSSH_TEST_NONEWLINEEND_STRING);
+}
+
+/**
+ * @brief Verify the configuration parser handles
+ * missing newline in the end
+ */
+static void torture_config_nonewlineoneline(void **state,
+ const char *file,
+ const char *string)
+{
+ _parse_config(*state, file, string, SSH_OK);
+}
+
+/**
+ * @brief Verify the configuration parser handles
+ * missing newline in the end of file
+ */
+static void torture_config_nonewlineoneline_file(void **state)
+{
+ torture_config_nonewlineend(state, LIBSSH_TEST_NONEWLINEONELINE, NULL);
+}
+
+/**
+ * @brief Verify the configuration parser handles
+ * missing newline in the end of string
+ */
+static void torture_config_nonewlineoneline_string(void **state)
+{
+ torture_config_nonewlineoneline(state,
+ NULL, LIBSSH_TEST_NONEWLINEONELINE_STRING);
+}
+
/* ssh_config_get_cmd() does three things:
* * Strips leading whitespace
* * Terminate the characted on the end of next quotes-enclosed string
@@ -994,7 +1338,8 @@ static void torture_config_parser_get_cmd(void **state)
assert_string_equal(tok, "something");
assert_int_equal(*p, '\0');
- /* But it does not split tokens by whitespace if they are not quoted, which is weird */
+ /* But it does not split tokens by whitespace
+ * if they are not quoted, which is weird */
strncpy(data, "multi string something\n", sizeof(data));
p = data;
tok = ssh_config_get_cmd(&p);
@@ -1004,7 +1349,8 @@ static void torture_config_parser_get_cmd(void **state)
/* ssh_config_get_token() should behave as expected
* * Strip leading whitespace
- * * Return first token separated by whitespace or equal sign, respecting quotes!
+ * * Return first token separated by whitespace or equal sign,
+ * respecting quotes!
*/
static void torture_config_parser_get_token(void **state)
{
@@ -1206,7 +1552,8 @@ static void torture_config_match_pattern(void **state)
assert_int_equal(rv, 1);
rv = match_pattern("aa", "?", MAX_MATCH_RECURSION);
assert_int_equal(rv, 0);
- rv = match_pattern("?", "a", MAX_MATCH_RECURSION); /* Wildcard in search string */
+ /* Wildcard in search string */
+ rv = match_pattern("?", "a", MAX_MATCH_RECURSION);
assert_int_equal(rv, 0);
rv = match_pattern("?", "?", MAX_MATCH_RECURSION);
assert_int_equal(rv, 1);
@@ -1216,7 +1563,8 @@ static void torture_config_match_pattern(void **state)
assert_int_equal(rv, 1);
rv = match_pattern("aa", "*", MAX_MATCH_RECURSION);
assert_int_equal(rv, 1);
- rv = match_pattern("*", "a", MAX_MATCH_RECURSION); /* Wildcard in search string */
+ /* Wildcard in search string */
+ rv = match_pattern("*", "a", MAX_MATCH_RECURSION);
assert_int_equal(rv, 0);
rv = match_pattern("*", "*", MAX_MATCH_RECURSION);
assert_int_equal(rv, 1);
@@ -1256,34 +1604,78 @@ static void torture_config_match_pattern(void **state)
/* Limit the maximum recursion */
rv = match_pattern("hostname", "*p*a*t*t*e*r*n*", 5);
assert_int_equal(rv, 0);
- rv = match_pattern("pattern", "*p*a*t*t*e*r*n*", 5); /* Too much recursion */
+ /* Too much recursion */
+ rv = match_pattern("pattern", "*p*a*t*t*e*r*n*", 5);
assert_int_equal(rv, 0);
}
-int torture_run_tests(void) {
+int torture_run_tests(void)
+{
int rc;
struct CMUnitTest tests[] = {
- cmocka_unit_test(torture_config_from_file),
- cmocka_unit_test(torture_config_double_ports),
- cmocka_unit_test(torture_config_glob),
- cmocka_unit_test(torture_config_new),
- cmocka_unit_test(torture_config_auth_methods),
- cmocka_unit_test(torture_config_unknown),
- cmocka_unit_test(torture_config_match),
- cmocka_unit_test(torture_config_proxyjump),
- cmocka_unit_test(torture_config_rekey),
- cmocka_unit_test(torture_config_pubkeyacceptedkeytypes),
- cmocka_unit_test(torture_config_parser_get_cmd),
- cmocka_unit_test(torture_config_parser_get_token),
- cmocka_unit_test(torture_config_match_pattern),
+ cmocka_unit_test_setup_teardown(torture_config_include_file,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_include_string,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_double_ports_file,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_double_ports_string,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_glob_file,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_glob_string,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_new_file,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_new_string,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_auth_methods_file,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_auth_methods_string,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_unknown_file,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_unknown_string,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_match_file,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_match_string,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_proxyjump_file,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_proxyjump_string,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_rekey_file,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_rekey_string,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_pubkeytypes_file,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_pubkeytypes_string,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_nonewlineend_file,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_nonewlineend_string,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_nonewlineoneline_file,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_nonewlineoneline_string,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_parser_get_cmd,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_parser_get_token,
+ setup, teardown),
+ cmocka_unit_test_setup_teardown(torture_config_match_pattern,
+ setup, teardown),
};
ssh_init();
torture_filter_tests(tests);
- rc = cmocka_run_group_tests(tests, setup_config_files, teardown);
+ rc = cmocka_run_group_tests(tests,
+ setup_config_files, teardown_config_files);
ssh_finalize();
return rc;
}
diff --git a/tests/unittests/torture_packet_filter.c b/tests/unittests/torture_packet_filter.c
index 85fb5c1b..ffa49602 100644
--- a/tests/unittests/torture_packet_filter.c
+++ b/tests/unittests/torture_packet_filter.c
@@ -517,12 +517,108 @@ static void torture_packet_filter_check_channel_open(void **state)
assert_int_equal(rc, 0);
}
+static void torture_packet_filter_check_channel_success(void **state)
+{
+ int rc;
+
+ /* The only condition to accept a CHANNEL_SUCCESS is to be authenticated */
+ global_state accepted[] = {
+ {
+ .flags = COMPARE_SESSION_STATE,
+ .session = SSH_SESSION_STATE_AUTHENTICATED,
+ }
+ };
+
+ int accepted_count = 1;
+
+ /* Unused */
+ (void) state;
+
+ rc = check_message_in_all_states(accepted, accepted_count,
+ SSH2_MSG_CHANNEL_SUCCESS);
+
+ assert_int_equal(rc, 0);
+}
+
+static void torture_packet_filter_check_channel_failure(void **state)
+{
+ int rc;
+
+ /* The only condition to accept a CHANNEL_FAILURE is to be authenticated */
+ global_state accepted[] = {
+ {
+ .flags = COMPARE_SESSION_STATE,
+ .session = SSH_SESSION_STATE_AUTHENTICATED,
+ }
+ };
+
+ int accepted_count = 1;
+
+ /* Unused */
+ (void) state;
+
+ rc = check_message_in_all_states(accepted, accepted_count,
+ SSH2_MSG_CHANNEL_FAILURE);
+
+ assert_int_equal(rc, 0);
+}
+
+static void torture_packet_filter_check_request_success(void **state)
+{
+ int rc;
+
+ /* The only condition to accept a REQUEST_SUCCESS is to be authenticated */
+ global_state accepted[] = {
+ {
+ .flags = COMPARE_SESSION_STATE,
+ .session = SSH_SESSION_STATE_AUTHENTICATED,
+ }
+ };
+
+ int accepted_count = 1;
+
+ /* Unused */
+ (void) state;
+
+ rc = check_message_in_all_states(accepted, accepted_count,
+ SSH2_MSG_REQUEST_SUCCESS);
+
+ assert_int_equal(rc, 0);
+}
+
+static void torture_packet_filter_check_request_failure(void **state)
+{
+ int rc;
+
+ /* The only condition to accept a REQUEST_FAILURE is to be authenticated */
+ global_state accepted[] = {
+ {
+ .flags = COMPARE_SESSION_STATE,
+ .session = SSH_SESSION_STATE_AUTHENTICATED,
+ }
+ };
+
+ int accepted_count = 1;
+
+ /* Unused */
+ (void) state;
+
+ rc = check_message_in_all_states(accepted, accepted_count,
+ SSH2_MSG_REQUEST_FAILURE);
+
+ assert_int_equal(rc, 0);
+}
+
int torture_run_tests(void)
{
int rc;
struct CMUnitTest tests[] = {
cmocka_unit_test(torture_packet_filter_check_auth_success),
cmocka_unit_test(torture_packet_filter_check_channel_open),
+ cmocka_unit_test(torture_packet_filter_check_channel_success),
+ cmocka_unit_test(torture_packet_filter_check_channel_failure),
+ cmocka_unit_test(torture_packet_filter_check_request_success),
+ cmocka_unit_test(torture_packet_filter_check_request_failure),
cmocka_unit_test(torture_packet_filter_check_unfiltered),
cmocka_unit_test(torture_packet_filter_check_msg_ext_info)
};
diff --git a/tests/unittests/torture_pki_ed25519.c b/tests/unittests/torture_pki_ed25519.c
index 07ccfd67..ff59b190 100644
--- a/tests/unittests/torture_pki_ed25519.c
+++ b/tests/unittests/torture_pki_ed25519.c
@@ -796,7 +796,8 @@ static void torture_pki_ed25519_verify(void **state){
assert_true(rc == SSH_OK);
assert_non_null(pubkey);
- ssh_string_fill(blob, ref_signature, ED25519_SIG_LEN);
+ rc = ssh_string_fill(blob, ref_signature, ED25519_SIG_LEN);
+ assert_int_equal(rc, 0);
sig = pki_signature_from_blob(pubkey, blob, SSH_KEYTYPE_ED25519, SSH_DIGEST_AUTO);
assert_non_null(sig);
@@ -853,7 +854,8 @@ static void torture_pki_ed25519_verify_bad(void **state){
/* alter signature and expect false result */
for (i=0; i < ED25519_SIG_LEN; ++i){
- ssh_string_fill(blob, ref_signature, ED25519_SIG_LEN);
+ rc = ssh_string_fill(blob, ref_signature, ED25519_SIG_LEN);
+ assert_int_equal(rc, 0);
((uint8_t *)ssh_string_data(blob))[i] ^= 0xff;
sig = pki_signature_from_blob(pubkey, blob, SSH_KEYTYPE_ED25519, SSH_DIGEST_AUTO);
assert_non_null(sig);
diff --git a/tests/unittests/torture_session_keys.c b/tests/unittests/torture_session_keys.c
index f220e010..6ae58831 100644
--- a/tests/unittests/torture_session_keys.c
+++ b/tests/unittests/torture_session_keys.c
@@ -68,7 +68,9 @@ static void torture_session_keys(UNUSED_PARAM(void **state))
int rc;
k_string = ssh_string_new(32);
- ssh_string_fill(k_string, key, 32);
+ rc = ssh_string_fill(k_string, key, 32);
+ assert_int_equal(rc, 0);
+
test_crypto.shared_secret = ssh_make_string_bn(k_string);
rc = ssh_generate_session_keys(&session);