/* * Key re-exchange (rekey) is triggered by this function. * It can not be called again after the rekey is initialized! */ int ssh_send_rekex(ssh_session session) { int rc; if (session->dh_handshake_state != DH_STATE_FINISHED) { /* Rekey/Key exchange is already in progress */ SSH_LOG(SSH_LOG_PACKET, "Attempting rekey in bad state"); return SSH_ERROR; } if (session->current_crypto == NULL) { /* No current crypto used -- can not exchange it */ SSH_LOG(SSH_LOG_PACKET, "No crypto to rekey"); return SSH_ERROR; } if (session->client) { rc = ssh_set_client_kex(session); if (rc != SSH_OK) { SSH_LOG(SSH_LOG_PACKET, "Failed to set client kex"); return rc; } } else { #ifdef WITH_SERVER rc = server_set_kex(session); if (rc == SSH_ERROR) { SSH_LOG(SSH_LOG_PACKET, "Failed to set server kex"); return rc; } #else SSH_LOG(SSH_LOG_PACKET, "Invalid session state."); return SSH_ERROR; #endif /* WITH_SERVER */ } session->dh_handshake_state = DH_STATE_INIT; rc = ssh_send_kex(session, session->server); if (rc < 0) { SSH_LOG(SSH_LOG_PACKET, "Failed to send kex"); return rc; } /* Reset the handshake state */ session->dh_handshake_state = DH_STATE_INIT_SENT; return SSH_OK; }
/** * @internal * * @brief A function to be called each time a step has been done in the * connection. */ static void ssh_client_connection_callback(ssh_session session){ int ssh1,ssh2; switch(session->session_state){ case SSH_SESSION_STATE_NONE: case SSH_SESSION_STATE_CONNECTING: break; case SSH_SESSION_STATE_SOCKET_CONNECTED: /* If SSHv1 is disabled, we can send the banner immedietly */ if (session->opts.ssh1 == 0) { ssh_set_fd_towrite(session); ssh_send_banner(session, 0); } break; case SSH_SESSION_STATE_BANNER_RECEIVED: if (session->serverbanner == NULL) { goto error; } set_status(session, 0.4f); SSH_LOG(SSH_LOG_RARE, "SSH server banner: %s", session->serverbanner); /* Here we analyze the different protocols the server allows. */ if (ssh_analyze_banner(session, 0, &ssh1, &ssh2) < 0) { goto error; } /* Here we decide which version of the protocol to use. */ if (ssh2 && session->opts.ssh2) { session->version = 2; #ifdef WITH_SSH1 } else if(ssh1 && session->opts.ssh1) { session->version = 1; #endif } else if(ssh1 && !session->opts.ssh1){ #ifdef WITH_SSH1 ssh_set_error(session, SSH_FATAL, "SSH-1 protocol not available (configure session to allow SSH-1)"); goto error; #else ssh_set_error(session, SSH_FATAL, "SSH-1 protocol not available (libssh compiled without SSH-1 support)"); goto error; #endif } else { ssh_set_error(session, SSH_FATAL, "No version of SSH protocol usable (banner: %s)", session->serverbanner); goto error; } /* from now, the packet layer is handling incoming packets */ if (session->version == 2) { ssh_packet_register_socket_callback(session, session->socket); } #ifdef WITH_SSH1 else session->socket_callbacks.data=ssh_packet_socket_callback1; #endif ssh_packet_set_default_callbacks(session); session->session_state=SSH_SESSION_STATE_INITIAL_KEX; if (session->opts.ssh1 == 1) { ssh_send_banner(session, 0); } set_status(session, 0.5f); break; case SSH_SESSION_STATE_INITIAL_KEX: /* TODO: This state should disappear in favor of get_key handle */ #ifdef WITH_SSH1 if(session->version==1){ if (ssh_get_kex1(session) < 0) goto error; set_status(session,0.6f); session->connected = 1; break; } #endif break; case SSH_SESSION_STATE_KEXINIT_RECEIVED: set_status(session,0.6f); ssh_list_kex(&session->next_crypto->server_kex); if (ssh_set_client_kex(session) < 0) { goto error; } if (ssh_kex_select_methods(session) == SSH_ERROR) goto error; if (ssh_send_kex(session, 0) < 0) { 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: 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; }
/** * @internal * * @brief A function to be called each time a step has been done in the * connection. */ 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); break; case SSH_SESSION_STATE_BANNER_RECEIVED: if (session->serverbanner == NULL) { goto error; } set_status(session, 0.4f); SSH_LOG(SSH_LOG_RARE, "SSH server banner: %s", session->serverbanner); /* Here we analyze the different protocols the server allows. */ rc = ssh_analyze_banner(session, 0); if (rc < 0) { ssh_set_error(session, SSH_FATAL, "No version of SSH protocol usable (banner: %s)", session->serverbanner); goto error; } ssh_packet_register_socket_callback(session, session->socket); ssh_packet_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, 0); 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: 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; }