diff options
Diffstat (limited to 'src/client.c')
-rw-r--r-- | src/client.c | 476 |
1 files changed, 287 insertions, 189 deletions
diff --git a/src/client.c b/src/client.c index 4610b787..b3d0fe62 100644 --- a/src/client.c +++ b/src/client.c @@ -60,7 +60,8 @@ * @param code one of SSH_SOCKET_CONNECTED_OK or SSH_SOCKET_CONNECTED_ERROR * @param user is a pointer to session */ -static void socket_callback_connected(int code, int errno_code, void *user){ +static void socket_callback_connected(int code, int errno_code, void *user) +{ ssh_session session=(ssh_session)user; if (session->session_state != SSH_SESSION_STATE_CONNECTING && @@ -72,12 +73,14 @@ static void socket_callback_connected(int code, int errno_code, void *user){ return; } - SSH_LOG(SSH_LOG_RARE,"Socket connection callback: %d (%d)",code, errno_code); + SSH_LOG(SSH_LOG_TRACE,"Socket connection callback: %d (%d)",code, errno_code); if(code == SSH_SOCKET_CONNECTED_OK) session->session_state=SSH_SESSION_STATE_SOCKET_CONNECTED; else { + char err_msg[SSH_ERRNO_MSG_MAX] = {0}; session->session_state=SSH_SESSION_STATE_ERROR; - ssh_set_error(session,SSH_FATAL,"%s",strerror(errno_code)); + ssh_set_error(session,SSH_FATAL,"%s", + ssh_strerror(errno_code, err_msg, SSH_ERRNO_MSG_MAX)); } session->ssh_connection_callback(session); } @@ -93,12 +96,12 @@ static void socket_callback_connected(int code, int errno_code, void *user){ * @param user is a pointer to session * @returns Number of bytes processed, or zero if the banner is not complete. */ -static int callback_receive_banner(const void *data, size_t len, void *user) +static size_t callback_receive_banner(const void *data, size_t len, void *user) { char *buffer = (char *)data; - ssh_session session=(ssh_session) user; + ssh_session session = (ssh_session) user; char *str = NULL; - size_t i; + uint32_t i; int ret=0; if (session->session_state != SSH_SESSION_STATE_SOCKET_CONNECTED) { @@ -106,7 +109,7 @@ static int callback_receive_banner(const void *data, size_t len, void *user) "Wrong state in callback_receive_banner : %d", session->session_state); - return SSH_ERROR; + return 0; } for (i = 0; i < len; ++i) { #ifdef WITH_PCAP @@ -243,10 +246,13 @@ end: * @warning this function returning is no proof that DH handshake is * completed */ -static int dh_handshake(ssh_session session) { - +int dh_handshake(ssh_session session) +{ int rc = SSH_AGAIN; + SSH_LOG(SSH_LOG_TRACE, "dh_handshake_state = %d, kex_type = %d", + session->dh_handshake_state, session->next_crypto->kex_type); + switch (session->dh_handshake_state) { case DH_STATE_INIT: switch(session->next_crypto->kex_type){ @@ -299,18 +305,25 @@ static int dh_handshake(ssh_session session) { return rc; } -static int ssh_service_request_termination(void *s){ - ssh_session session = (ssh_session)s; - if(session->session_state == SSH_SESSION_STATE_ERROR || - session->auth.service_state != SSH_AUTH_SERVICE_SENT) - return 1; - else - return 0; +static int ssh_service_request_termination(void *s) +{ + ssh_session session = (ssh_session)s; + + if (session->session_state == SSH_SESSION_STATE_ERROR || + session->auth.service_state != SSH_AUTH_SERVICE_SENT) + return 1; + else + return 0; } /** - * @internal + * @addtogroup libssh_session * + * @{ + */ + +/** + * @internal * @brief Request a service from the SSH server. * * Service requests are for example: ssh-userauth, ssh-connection, etc. @@ -323,8 +336,9 @@ static int ssh_service_request_termination(void *s){ * @return SSH_AGAIN No response received yet * @bug actually only works with ssh-userauth */ -int ssh_service_request(ssh_session session, const char *service) { - int rc=SSH_ERROR; +int ssh_service_request(ssh_session session, const char *service) +{ + int rc = SSH_ERROR; if(session->auth.service_state != SSH_AUTH_SERVICE_NONE) goto pending; @@ -371,12 +385,6 @@ pending: } /** - * @addtogroup libssh_session - * - * @{ - */ - -/** * @internal * * @brief A function to be called each time a step has been done in the @@ -386,111 +394,117 @@ static void ssh_client_connection_callback(ssh_session session) { int rc; - switch(session->session_state) { - case SSH_SESSION_STATE_NONE: - case SSH_SESSION_STATE_CONNECTING: - break; - case SSH_SESSION_STATE_SOCKET_CONNECTED: - ssh_set_fd_towrite(session); - ssh_send_banner(session, 0); + SSH_LOG(SSH_LOG_DEBUG, "session_state=%d", session->session_state); - break; - case SSH_SESSION_STATE_BANNER_RECEIVED: - if (session->serverbanner == NULL) { - goto error; - } - set_status(session, 0.4f); - SSH_LOG(SSH_LOG_PROTOCOL, - "SSH server banner: %s", session->serverbanner); + switch (session->session_state) { + case SSH_SESSION_STATE_NONE: + case SSH_SESSION_STATE_CONNECTING: + break; + case SSH_SESSION_STATE_SOCKET_CONNECTED: + ssh_set_fd_towrite(session); + ssh_send_banner(session, 0); - /* Here we analyze the different protocols the server allows. */ - rc = ssh_analyze_banner(session, 0); - if (rc < 0) { - ssh_set_error(session, SSH_FATAL, - "No version of SSH protocol usable (banner: %s)", - session->serverbanner); - goto error; - } + break; + case SSH_SESSION_STATE_BANNER_RECEIVED: + if (session->serverbanner == NULL) { + goto error; + } + set_status(session, 0.4f); + SSH_LOG(SSH_LOG_DEBUG, "SSH server banner: %s", session->serverbanner); - ssh_packet_register_socket_callback(session, session->socket); + /* Here we analyze the different protocols the server allows. */ + rc = ssh_analyze_banner(session, 0); + if (rc < 0) { + ssh_set_error(session, SSH_FATAL, + "No version of SSH protocol usable (banner: %s)", + session->serverbanner); + goto error; + } + + ssh_packet_register_socket_callback(session, session->socket); + + ssh_packet_set_default_callbacks(session); + session->session_state = SSH_SESSION_STATE_INITIAL_KEX; + rc = ssh_set_client_kex(session); + if (rc != SSH_OK) { + goto error; + } + rc = ssh_send_kex(session); + if (rc < 0) { + goto error; + } + set_status(session, 0.5f); - ssh_packet_set_default_callbacks(session); - session->session_state = SSH_SESSION_STATE_INITIAL_KEX; + break; + case SSH_SESSION_STATE_INITIAL_KEX: + /* TODO: This state should disappear in favor of get_key handle */ + break; + case SSH_SESSION_STATE_KEXINIT_RECEIVED: + set_status(session, 0.6f); + ssh_list_kex(&session->next_crypto->server_kex); + if ((session->flags & SSH_SESSION_FLAG_KEXINIT_SENT) == 0) { + /* in rekeying state if next_crypto client_kex might be empty */ rc = ssh_set_client_kex(session); if (rc != SSH_OK) { goto error; } - rc = ssh_send_kex(session, 0); + rc = ssh_send_kex(session); if (rc < 0) { goto error; } - set_status(session, 0.5f); - - break; - case SSH_SESSION_STATE_INITIAL_KEX: - /* TODO: This state should disappear in favor of get_key handle */ - break; - case SSH_SESSION_STATE_KEXINIT_RECEIVED: - set_status(session,0.6f); - ssh_list_kex(&session->next_crypto->server_kex); - if (session->next_crypto->client_kex.methods[0] == NULL) { - /* in rekeying state if next_crypto client_kex is empty */ - rc = ssh_set_client_kex(session); - if (rc != SSH_OK) { - goto error; - } - rc = ssh_send_kex(session, 0); - if (rc < 0) { - goto error; - } - } - if (ssh_kex_select_methods(session) == SSH_ERROR) - goto error; - set_status(session,0.8f); - session->session_state=SSH_SESSION_STATE_DH; - if (dh_handshake(session) == SSH_ERROR) { - goto error; - } - FALL_THROUGH; - case SSH_SESSION_STATE_DH: - if(session->dh_handshake_state==DH_STATE_FINISHED){ - set_status(session,1.0f); - session->connected = 1; - if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED) - session->session_state = SSH_SESSION_STATE_AUTHENTICATED; - else - session->session_state=SSH_SESSION_STATE_AUTHENTICATING; - } - break; - case SSH_SESSION_STATE_AUTHENTICATING: - break; - case SSH_SESSION_STATE_ERROR: + } + if (ssh_kex_select_methods(session) == SSH_ERROR) goto error; - default: - ssh_set_error(session,SSH_FATAL,"Invalid state %d",session->session_state); + set_status(session, 0.8f); + session->session_state = SSH_SESSION_STATE_DH; + + /* If the init packet was already sent in previous step, this will be no + * operation */ + if (dh_handshake(session) == SSH_ERROR) { + goto error; + } + FALL_THROUGH; + case SSH_SESSION_STATE_DH: + if (session->dh_handshake_state == DH_STATE_FINISHED) { + set_status(session, 1.0f); + session->connected = 1; + if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED) { + session->session_state = SSH_SESSION_STATE_AUTHENTICATED; + } else { + session->session_state = SSH_SESSION_STATE_AUTHENTICATING; + } + } + break; + case SSH_SESSION_STATE_AUTHENTICATING: + break; + case SSH_SESSION_STATE_ERROR: + goto error; + default: + ssh_set_error(session, SSH_FATAL, "Invalid state %d", + session->session_state); } return; error: - ssh_socket_close(session->socket); - session->alive = 0; - session->session_state=SSH_SESSION_STATE_ERROR; - + ssh_session_socket_close(session); + SSH_LOG(SSH_LOG_WARN, "%s", ssh_get_error(session)); } /** @internal * @brief describe under which conditions the ssh_connect function may stop */ -static int ssh_connect_termination(void *user){ - ssh_session session = (ssh_session)user; - switch(session->session_state){ +static int ssh_connect_termination(void *user) +{ + ssh_session session = (ssh_session)user; + + switch (session->session_state) { case SSH_SESSION_STATE_ERROR: case SSH_SESSION_STATE_AUTHENTICATING: case SSH_SESSION_STATE_DISCONNECTED: - return 1; + return 1; default: - return 0; - } + return 0; + } } /** @@ -558,7 +572,7 @@ int ssh_connect(ssh_session session) return SSH_ERROR; } - SSH_LOG(SSH_LOG_PROTOCOL, + SSH_LOG(SSH_LOG_DEBUG, "libssh %s, using threading %s", ssh_copyright(), ssh_threads_get_type()); @@ -593,7 +607,7 @@ int ssh_connect(ssh_session session) set_status(session, 0.2f); session->alive = 1; - SSH_LOG(SSH_LOG_PROTOCOL, + SSH_LOG(SSH_LOG_DEBUG, "Socket connecting, now waiting for the callbacks to work"); pending: @@ -649,12 +663,13 @@ pending: * * @return A newly allocated string with the banner, NULL on error. */ -char *ssh_get_issue_banner(ssh_session session) { - if (session == NULL || session->banner == NULL) { - return NULL; - } +char *ssh_get_issue_banner(ssh_session session) +{ + if (session == NULL || session->banner == NULL) { + return NULL; + } - return ssh_string_to_char(session->banner); + return ssh_string_to_char(session->banner); } /** @@ -675,102 +690,185 @@ char *ssh_get_issue_banner(ssh_session session) { * } * @endcode */ -int ssh_get_openssh_version(ssh_session session) { - if (session == NULL) { - return 0; - } +int ssh_get_openssh_version(ssh_session session) +{ + if (session == NULL) { + return 0; + } + + return session->openssh; +} + +/** + * @brief Most SSH connections will only ever request a single session, but an + * attacker may abuse a running ssh client to surreptitiously open + * additional sessions under their control. OpenSSH provides a global + * request "no-more-sessions@openssh.com" to mitigate this attack. + * + * @param[in] session The SSH session to use. + * + * @returns SSH_OK on success, SSH_ERROR on error. + * @returns SSH_AGAIN, if the session is in nonblocking mode, + * and call must be done again. + */ +int ssh_request_no_more_sessions(ssh_session session) +{ + if (session == NULL) { + return SSH_ERROR; + } - return session->openssh; + return ssh_global_request(session, "no-more-sessions@openssh.com", NULL, 1); } /** + * @brief Add disconnect message when ssh_session is disconnected + * To add a disconnect message to give peer a better hint. + * @param session The SSH session to use. + * @param message The message to send after the session is disconnected. + * If no message is passed then a default message i.e + * "Bye Bye" will be sent. + */ +int +ssh_session_set_disconnect_message(ssh_session session, const char *message) +{ + if (session == NULL) { + return SSH_ERROR; + } + + if (message == NULL || strlen(message) == 0) { + SAFE_FREE(session->disconnect_message); //To free any message set earlier. + session->disconnect_message = strdup("Bye Bye") ; + if (session->disconnect_message == NULL) { + ssh_set_error_oom(session); + return SSH_ERROR; + } + return SSH_OK; + } + SAFE_FREE(session->disconnect_message); //To free any message set earlier. + session->disconnect_message = strdup(message); + if (session->disconnect_message == NULL) { + ssh_set_error_oom(session); + return SSH_ERROR; + } + return SSH_OK; +} + + +/** * @brief Disconnect from a session (client or server). + * * The session can then be reused to open a new session. * + * @note Note that this function won't close the socket if it was set with + * ssh_options_set and SSH_OPTIONS_FD. You're responsible for closing the + * socket. This is new behavior in libssh 0.10. + * * @param[in] session The SSH session to use. */ -void ssh_disconnect(ssh_session session) { - struct ssh_iterator *it; - int rc; +void +ssh_disconnect(ssh_session session) +{ + struct ssh_iterator *it; + int rc; - if (session == NULL) { - return; - } + if (session == NULL) { + return; + } - if (session->socket != NULL && ssh_socket_is_open(session->socket)) { - rc = ssh_buffer_pack(session->out_buffer, - "bdss", - SSH2_MSG_DISCONNECT, - SSH2_DISCONNECT_BY_APPLICATION, - "Bye Bye", - ""); /* language tag */ - if (rc != SSH_OK){ - ssh_set_error_oom(session); - goto error; + if (session->disconnect_message == NULL) { + session->disconnect_message = strdup("Bye Bye") ; + if (session->disconnect_message == NULL) { + ssh_set_error_oom(session); + goto error; + } + } + + if (session->socket != NULL && ssh_socket_is_open(session->socket)) { + rc = ssh_buffer_pack(session->out_buffer, + "bdss", + SSH2_MSG_DISCONNECT, + SSH2_DISCONNECT_BY_APPLICATION, + session->disconnect_message, + ""); /* language tag */ + if (rc != SSH_OK) { + ssh_set_error_oom(session); + goto error; + } + + ssh_packet_send(session); + ssh_session_socket_close(session); } - ssh_packet_send(session); - ssh_socket_close(session->socket); - } error: - session->recv_seq = 0; - session->send_seq = 0; - session->alive = 0; - if (session->socket != NULL){ - ssh_socket_reset(session->socket); - } - session->opts.fd = SSH_INVALID_SOCKET; - session->session_state=SSH_SESSION_STATE_DISCONNECTED; + session->recv_seq = 0; + session->send_seq = 0; + session->alive = 0; + if (session->socket != NULL){ + ssh_socket_reset(session->socket); + } + session->opts.fd = SSH_INVALID_SOCKET; + session->session_state = SSH_SESSION_STATE_DISCONNECTED; + session->pending_call_state = SSH_PENDING_CALL_NONE; - while ((it=ssh_list_get_iterator(session->channels)) != NULL) { - ssh_channel_do_free(ssh_iterator_value(ssh_channel,it)); - ssh_list_remove(session->channels, it); - } - if(session->current_crypto){ - crypto_free(session->current_crypto); - session->current_crypto=NULL; - } - if (session->next_crypto) { - crypto_free(session->next_crypto); - session->next_crypto = crypto_new(); - if (session->next_crypto == NULL) { - ssh_set_error_oom(session); + while ((it = ssh_list_get_iterator(session->channels)) != NULL) { + ssh_channel_do_free(ssh_iterator_value(ssh_channel, it)); + ssh_list_remove(session->channels, it); } - } - if (session->in_buffer) { - ssh_buffer_reinit(session->in_buffer); - } - if (session->out_buffer) { - ssh_buffer_reinit(session->out_buffer); - } - if (session->in_hashbuf) { - ssh_buffer_reinit(session->in_hashbuf); - } - if (session->out_hashbuf) { - ssh_buffer_reinit(session->out_hashbuf); - } - session->auth.supported_methods = 0; - SAFE_FREE(session->serverbanner); - SAFE_FREE(session->clientbanner); - - if(session->ssh_message_list){ - ssh_message msg; - while((msg=ssh_list_pop_head(ssh_message ,session->ssh_message_list)) - != NULL){ - ssh_message_free(msg); - } - ssh_list_free(session->ssh_message_list); - session->ssh_message_list=NULL; - } + if (session->current_crypto) { + crypto_free(session->current_crypto); + session->current_crypto = NULL; + } + if (session->next_crypto) { + crypto_free(session->next_crypto); + session->next_crypto = crypto_new(); + if (session->next_crypto == NULL) { + ssh_set_error_oom(session); + } + } + if (session->in_buffer) { + ssh_buffer_reinit(session->in_buffer); + } + if (session->out_buffer) { + ssh_buffer_reinit(session->out_buffer); + } + if (session->in_hashbuf) { + ssh_buffer_reinit(session->in_hashbuf); + } + if (session->out_hashbuf) { + ssh_buffer_reinit(session->out_hashbuf); + } + session->auth.supported_methods = 0; + SAFE_FREE(session->serverbanner); + SAFE_FREE(session->clientbanner); + SAFE_FREE(session->disconnect_message); - if (session->packet_callbacks){ - ssh_list_free(session->packet_callbacks); - session->packet_callbacks=NULL; - } + if (session->ssh_message_list) { + ssh_message msg = NULL; + + while ((msg = ssh_list_pop_head(ssh_message, + session->ssh_message_list)) != NULL) { + ssh_message_free(msg); + } + ssh_list_free(session->ssh_message_list); + session->ssh_message_list = NULL; + } + + if (session->packet_callbacks) { + ssh_list_free(session->packet_callbacks); + session->packet_callbacks = NULL; + } } -const char *ssh_copyright(void) { - return SSH_STRINGIFY(LIBSSH_VERSION) " (c) 2003-2019 " +/** + * @brief Copyright information + * + * Returns copyright information + * + * @returns SSH_STRING copyright + */ +const char *ssh_copyright(void) +{ + return SSH_STRINGIFY(LIBSSH_VERSION) " (c) 2003-2024 " "Aris Adamantiadis, Andreas Schneider " "and libssh contributors. " "Distributed under the LGPL, please refer to COPYING " |