diff options
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) @@ -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} @@ -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; @@ -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; } @@ -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 * @@ -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); |