/** \brief connect to the ssh server * \param session ssh session * \return 0 on success, SSH_ERROR on error * \see ssh_new() * \see ssh_disconnect() */ int ssh_connect(SSH_SESSION *session){ int fd; int ssh1, ssh2; SSH_OPTIONS *options=session->options; if(!session->options){ ssh_set_error(session,SSH_FATAL,"Must set options before connect"); return SSH_ERROR; } session->alive=0; session->client=1; ssh_crypto_init(); if(options->fd==-1 && !options->host){ ssh_set_error(session,SSH_FATAL,"Hostname required"); return SSH_ERROR; } if(options->fd != -1) fd=options->fd; else fd=ssh_connect_host(session,options->host,options->bindaddr,options->port, options->timeout,options->timeout_usec); if(fd<0) return -1; set_status(options,0.2); session->fd=fd; session->alive=1; if(!(session->serverbanner=ssh_get_banner(session))){ if(session->fd>=0) close(session->fd); session->fd=-1; session->alive=0; return -1; } set_status(options,0.4); ssh_say(2,"banner : %s\n",session->serverbanner); /* here we analyse the different protocols the server allows */ if(ssh_analyze_banner(session,&ssh1,&ssh2)){ if(session->fd>=0) close(session->fd); session->fd=-1; session->alive=0; return -1; } /* here we decide which version of the protocol to use */ if(ssh2 && options->ssh2allowed) session->version=2; else if(ssh1 && options->ssh1allowed) session->version=1; else { ssh_set_error(session,SSH_FATAL, "no version of SSH protocol usable (banner: %s)", session->serverbanner); close(session->fd); session->fd=-1; session->alive=0; return -1; } ssh_send_banner(session,0); set_status(options,0.5); switch(session->version){ case 2: if(ssh_get_kex(session,0)){ if(session->fd>=0) close(session->fd); session->fd=-1; session->alive=0; return -1; } set_status(options,0.6); ssh_list_kex(&session->server_kex); if(set_kex(session)){ if(session->fd>=0) close(session->fd); session->fd=-1; session->alive=0; return -1; } ssh_send_kex(session,0); set_status(options,0.8); if(dh_handshake(session)){ if(session->fd>=0) close(session->fd); session->fd=-1; session->alive=0; return -1; } set_status(options,1.0); session->connected=1; break; case 1: if(ssh_get_kex1(session)){ if(session->fd>=0) close(session->fd); session->fd=-1; session->alive=0; return -1; } set_status(options,0.6); session->connected=1; break; } return 0; }
/** * @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: case SSH_SESSION_STATE_SOCKET_CONNECTED: 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) session->socket_callbacks.data=ssh_packet_socket_callback; #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; 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 (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; }