diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 5b78ede3..6c2d7de3 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -1,31 +1,29 @@ -libssh2 1.8.0 - -This release includes the following changes: - - o added a basic dockerised test suite - o crypto: add support for the mbedTLS backend +libssh2 1.8.1 This release includes the following bugfixes: - - o libgcrypt: fixed a NULL pointer dereference on OOM - o VMS: can't use %zd for off_t format - o VMS: update vms/libssh2_config.h - o windows: link with crypt32.lib - o libssh2_channel_open: speeling error fixed in channel error message - o msvc: fixed 14 compilation warnings - o tests: HAVE_NETINET_IN_H was not defined correctly - o openssl: add OpenSSL 1.1.0 compatibility - o cmake: Add CLEAR_MEMORY option, analogously to that for autoconf - o configure: make the --with-* options override the OpenSSL default - o libssh2_wait_socket: set err_msg on errors - o libssh2_wait_socket: Fix comparison with api_timeout to use milliseconds + o fixed possible integer overflow when reading a specially crafted packet + (https://www.libssh2.org/CVE-2019-3855.html) + o fixed possible integer overflow in userauth_keyboard_interactive with a + number of extremely long prompt strings + (https://www.libssh2.org/CVE-2019-3863.html) + o fixed possible integer overflow if the server sent an extremely large number + of keyboard prompts (https://www.libssh2.org/CVE-2019-3856.html) + o fixed possible out of bounds read when processing a specially crafted packet + (https://www.libssh2.org/CVE-2019-3861.html) + o fixed possible integer overflow when receiving a specially crafted exit + signal message channel packet (https://www.libssh2.org/CVE-2019-3857.html) + o fixed possible out of bounds read when receiving a specially crafted exit + status message channel packet (https://www.libssh2.org/CVE-2019-3862.html) + o fixed possible zero byte allocation when reading a specially crafted SFTP + packet (https://www.libssh2.org/CVE-2019-3858.html) + o fixed possible out of bounds reads when processing specially crafted SFTP + packets (https://www.libssh2.org/CVE-2019-3860.html) + o fixed possible out of bounds reads in _libssh2_packet_require(v) + (https://www.libssh2.org/CVE-2019-3859.html) + This release would not have looked like this without help, code, reports and advice from friends like these: - Alexander Lamaison, Antenore Gatta, Brad Harder, Charles Collicutt, - Craig A. Berry, Dan Fandrich, Daniel Stenberg, Kamil Dudka, Keno Fischer, - Taylor Holberton, Viktor Szakats, Will Cosgrove, Zenju - (12 contributors) - - Thanks! (and sorry if I forgot to mention someone) + Chris Coulson, Michael Buckley, Will Cosgrove, Daniel Stenberg + (4 contributors) diff --git a/include/libssh2.h b/include/libssh2.h index 9ef9ff11..f4bae24b 100644 --- a/include/libssh2.h +++ b/include/libssh2.h @@ -145,6 +145,18 @@ typedef int libssh2_socket_t; #define LIBSSH2_INVALID_SOCKET -1 #endif /* WIN32 */ +#ifndef SIZE_MAX +#if _WIN64 +#define SIZE_MAX 0xFFFFFFFFFFFFFFFF +#else +#define SIZE_MAX 0xFFFFFFFF +#endif +#endif + +#ifndef UINT_MAX +#define UINT_MAX 0xFFFFFFFF +#endif + /* * Determine whether there is small or large file support on windows. */ diff --git a/src/channel.c b/src/channel.c index 538a0ab0..39ff05bf 100644 --- a/src/channel.c +++ b/src/channel.c @@ -238,7 +238,20 @@ _libssh2_channel_open(LIBSSH2_SESSION * session, const char *channel_type, goto channel_error; } + if(session->open_data_len < 1) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "Unexpected packet size"); + goto channel_error; + } + if (session->open_data[0] == SSH_MSG_CHANNEL_OPEN_CONFIRMATION) { + + if(session->open_data_len < 17) { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "Unexpected packet size"); + goto channel_error; + } + session->open_channel->remote.id = _libssh2_ntohu32(session->open_data + 5); session->open_channel->local.window_size = @@ -518,7 +531,7 @@ channel_forward_listen(LIBSSH2_SESSION * session, const char *host, if (rc == LIBSSH2_ERROR_EAGAIN) { _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block"); return NULL; - } else if (rc) { + } else if (rc || data_len < 1) { _libssh2_error(session, LIBSSH2_ERROR_PROTO, "Unknown"); session->fwdLstn_state = libssh2_NB_state_idle; return NULL; @@ -855,6 +868,11 @@ static int channel_setenv(LIBSSH2_CHANNEL *channel, channel->setenv_state = libssh2_NB_state_idle; return rc; } + else if(data_len < 1) { + channel->setenv_state = libssh2_NB_state_idle; + return _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "Unexpected packet size"); + } if (data[0] == SSH_MSG_CHANNEL_SUCCESS) { LIBSSH2_FREE(session, data); @@ -971,7 +989,7 @@ static int channel_request_pty(LIBSSH2_CHANNEL *channel, &channel->reqPTY_packet_requirev_state); if (rc == LIBSSH2_ERROR_EAGAIN) { return rc; - } else if (rc) { + } else if (rc || data_len < 1) { channel->reqPTY_state = libssh2_NB_state_idle; return _libssh2_error(session, LIBSSH2_ERROR_PROTO, "Failed to require the PTY package"); @@ -1197,7 +1215,7 @@ channel_x11_req(LIBSSH2_CHANNEL *channel, int single_connection, &channel->reqX11_packet_requirev_state); if (rc == LIBSSH2_ERROR_EAGAIN) { return rc; - } else if (rc) { + } else if (rc || data_len < 1) { channel->reqX11_state = libssh2_NB_state_idle; return _libssh2_error(session, rc, "waiting for x11-req response packet"); @@ -1324,7 +1342,7 @@ _libssh2_channel_process_startup(LIBSSH2_CHANNEL *channel, &channel->process_packet_requirev_state); if (rc == LIBSSH2_ERROR_EAGAIN) { return rc; - } else if (rc) { + } else if (rc || data_len < 1) { channel->process_state = libssh2_NB_state_end; return _libssh2_error(session, rc, "Failed waiting for channel success"); diff --git a/src/kex.c b/src/kex.c index 65b722f4..3634cb5a 100644 --- a/src/kex.c +++ b/src/kex.c @@ -228,11 +228,23 @@ static int diffie_hellman_sha1(LIBSSH2_SESSION *session, } /* Parse KEXDH_REPLY */ + if(exchange_state->s_packet_len < 5) { + ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "Unexpected packet length"); + goto clean_exit; + } + exchange_state->s = exchange_state->s_packet + 1; session->server_hostkey_len = _libssh2_ntohu32(exchange_state->s); exchange_state->s += 4; + if(session->server_hostkey_len > exchange_state->s_packet_len - 5) { + ret = _libssh2_error(session, LIBSSH2_ERROR_OUT_OF_BOUNDARY, + "Host key length out of bounds"); + goto clean_exit; + } + if (session->server_hostkey) LIBSSH2_FREE(session, session->server_hostkey); @@ -848,11 +860,23 @@ static int diffie_hellman_sha256(LIBSSH2_SESSION *session, } /* Parse KEXDH_REPLY */ + if(exchange_state->s_packet_len < 5) { + ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "Unexpected packet length"); + goto clean_exit; + } + exchange_state->s = exchange_state->s_packet + 1; session->server_hostkey_len = _libssh2_ntohu32(exchange_state->s); exchange_state->s += 4; + if(session->server_hostkey_len > exchange_state->s_packet_len - 5) { + ret = _libssh2_error(session, LIBSSH2_ERROR_OUT_OF_BOUNDARY, + "Host key length out of bounds"); + goto clean_exit; + } + if (session->server_hostkey) LIBSSH2_FREE(session, session->server_hostkey); diff --git a/src/packet.c b/src/packet.c index 5f1feb8c..aa10633e 100644 --- a/src/packet.c +++ b/src/packet.c @@ -815,8 +815,15 @@ _libssh2_packet_add(LIBSSH2_SESSION * session, unsigned char *data, /* set signal name (without SIG prefix) */ uint32_t namelen = _libssh2_ntohu32(data + 9 + sizeof("exit-signal")); - channelp->exit_signal = - LIBSSH2_ALLOC(session, namelen + 1); + + if(namelen <= UINT_MAX - 1) { + channelp->exit_signal = + LIBSSH2_ALLOC(session, namelen + 1); + } + else { + channelp->exit_signal = NULL; + } + if (!channelp->exit_signal) rc = _libssh2_error(session, LIBSSH2_ERROR_ALLOC, "memory for signal name"); diff --git a/src/session.c b/src/session.c index 6352d12e..b5a83ddd 100644 --- a/src/session.c +++ b/src/session.c @@ -765,6 +765,11 @@ session_startup(LIBSSH2_SESSION *session, libssh2_socket_t sock) if (rc) return rc; + if(session->startup_data_len < 5) { + return _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "Unexpected packet length"); + } + session->startup_service_length = _libssh2_ntohu32(session->startup_data + 1); diff --git a/src/transport.c b/src/transport.c index 8725da09..1c5a13ca 100644 --- a/src/transport.c +++ b/src/transport.c @@ -438,6 +438,12 @@ int _libssh2_transport_read(LIBSSH2_SESSION * session) return LIBSSH2_ERROR_DECRYPT; p->padding_length = block[4]; + if(p->packet_length < 1) { + return LIBSSH2_ERROR_DECRYPT; + } + else if(p->packet_length > LIBSSH2_PACKET_MAXPAYLOAD) { + return LIBSSH2_ERROR_OUT_OF_BOUNDARY; + } /* total_num is the number of bytes following the initial (5 bytes) packet length and padding length fields */ diff --git a/src/userauth.c b/src/userauth.c index cdfa25e6..ed804629 100644 --- a/src/userauth.c +++ b/src/userauth.c @@ -107,7 +107,7 @@ static char *userauth_list(LIBSSH2_SESSION *session, const char *username, LIBSSH2_FREE(session, session->userauth_list_data); session->userauth_list_data = NULL; - if (rc) { + if (rc || (session->userauth_list_data_len < 1)) { _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-none request"); session->userauth_list_state = libssh2_NB_state_idle; @@ -143,8 +143,20 @@ static char *userauth_list(LIBSSH2_SESSION *session, const char *username, return NULL; } - methods_len = _libssh2_ntohu32(session->userauth_list_data + 1); + if(session->userauth_list_data_len < 5) { + LIBSSH2_FREE(session, session->userauth_list_data); + session->userauth_list_data = NULL; + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "Unexpected packet size"); + return NULL; + } + methods_len = _libssh2_ntohu32(session->userauth_list_data + 1); + if(methods_len >= session->userauth_list_data_len - 5) { + _libssh2_error(session, LIBSSH2_ERROR_OUT_OF_BOUNDARY, + "Unexpected userauth list size"); + return NULL; + } /* Do note that the memory areas overlap! */ memmove(session->userauth_list_data, session->userauth_list_data + 5, methods_len); @@ -285,6 +297,11 @@ userauth_password(LIBSSH2_SESSION *session, return _libssh2_error(session, rc, "Waiting for password response"); } + else if(session->userauth_pswd_data_len < 1) { + session->userauth_pswd_state = libssh2_NB_state_idle; + return _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "Unexpected packet size"); + } if (session->userauth_pswd_data[0] == SSH_MSG_USERAUTH_SUCCESS) { _libssh2_debug(session, LIBSSH2_TRACE_AUTH, @@ -312,6 +329,12 @@ userauth_password(LIBSSH2_SESSION *session, session->userauth_pswd_state = libssh2_NB_state_sent1; } + if(session->userauth_pswd_data_len < 1) { + session->userauth_pswd_state = libssh2_NB_state_idle; + return _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "Unexpected packet size"); + } + if ((session->userauth_pswd_data[0] == SSH_MSG_USERAUTH_PASSWD_CHANGEREQ) || (session->userauth_pswd_data0 == @@ -976,7 +999,7 @@ userauth_hostbased_fromfile(LIBSSH2_SESSION *session, } session->userauth_host_state = libssh2_NB_state_idle; - if (rc) { + if (rc || data_len < 1) { return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED, "Auth failed"); } @@ -1149,7 +1172,7 @@ _libssh2_userauth_publickey(LIBSSH2_SESSION *session, NULL, 0); if (rc == LIBSSH2_ERROR_EAGAIN) return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block"); - else if (rc) { + else if (rc || (session->userauth_pblc_data_len < 1)) { LIBSSH2_FREE(session, session->userauth_pblc_packet); session->userauth_pblc_packet = NULL; LIBSSH2_FREE(session, session->userauth_pblc_method); @@ -1332,7 +1355,7 @@ _libssh2_userauth_publickey(LIBSSH2_SESSION *session, if (rc == LIBSSH2_ERROR_EAGAIN) { return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block requesting userauth list"); - } else if (rc) { + } else if (rc || session->userauth_pblc_data_len < 1) { session->userauth_pblc_state = libssh2_NB_state_idle; return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED, "Waiting for publickey USERAUTH response"); @@ -1654,7 +1677,7 @@ userauth_keyboard_interactive(LIBSSH2_SESSION * session, if (rc == LIBSSH2_ERROR_EAGAIN) { return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block"); - } else if (rc) { + } else if (rc || session->userauth_kybd_data_len < 1) { session->userauth_kybd_state = libssh2_NB_state_idle; return _libssh2_error(session, LIBSSH2_ERROR_AUTHENTICATION_FAILED, @@ -1734,6 +1757,13 @@ userauth_keyboard_interactive(LIBSSH2_SESSION * session, /* int num-prompts */ session->userauth_kybd_num_prompts = _libssh2_ntohu32(s); s += 4; + if(session->userauth_kybd_num_prompts && + session->userauth_kybd_num_prompts > 100) { + _libssh2_error(session, LIBSSH2_ERROR_OUT_OF_BOUNDARY, + "Too many replies for " + "keyboard-interactive prompts"); + goto cleanup; + } if(session->userauth_kybd_num_prompts) { session->userauth_kybd_prompts = @@ -1801,8 +1831,17 @@ userauth_keyboard_interactive(LIBSSH2_SESSION * session, for(i = 0; i < session->userauth_kybd_num_prompts; i++) { /* string response[1] (ISO-10646 UTF-8) */ - session->userauth_kybd_packet_len += - 4 + session->userauth_kybd_responses[i].length; + if(session->userauth_kybd_responses[i].length <= + (SIZE_MAX - 4 - session->userauth_kybd_packet_len) ) { + session->userauth_kybd_packet_len += + 4 + session->userauth_kybd_responses[i].length; + } + else { + _libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for keyboard-" + "interactive response packet"); + goto cleanup; + } } /* A new userauth_kybd_data area is to be allocated, free the