1
1

server: Fixed the server implementation.

Signed-off-by: Andreas Schneider <asn@cynapses.org>
Этот коммит содержится в:
milo 2010-08-09 18:43:15 +02:00 коммит произвёл Andreas Schneider
родитель 51a21823f9
Коммит 8ef0fbc294

Просмотреть файл

@ -49,6 +49,9 @@
session->callbacks->connect_status_function(session->callbacks->userdata, status); \
} while (0)
static int dh_handshake_server(ssh_session session);
/**
* @addtogroup libssh_server
*
@ -379,6 +382,7 @@ SSH_PACKET_CALLBACK(ssh_packet_kexdh_init){
session->session_state=SSH_SESSION_STATE_ERROR;
} else {
session->dh_handshake_state=DH_STATE_INIT_SENT;
dh_handshake_server(session);
}
ssh_string_free(e);
@ -393,11 +397,6 @@ static int dh_handshake_server(ssh_session session) {
ssh_string sign;
ssh_public_key pub;
ssh_private_key prv;
/* waiting for SSH_MSG_KEXDH_INIT */
while(session->dh_handshake_state != DH_STATE_INIT_SENT){
ssh_handle_packets(session,-1);
}
/* received SSH_MSG_KEXDH_INIT */
if (dh_generate_y(session) < 0) {
ssh_set_error(session, SSH_FATAL, "Could not create y number");
@ -497,74 +496,304 @@ static int dh_handshake_server(ssh_session session) {
ssh_log(session, SSH_LOG_PACKET, "SSH_MSG_NEWKEYS sent");
session->dh_handshake_state=DH_STATE_NEWKEYS_SENT;
while(session->dh_handshake_state != DH_STATE_FINISHED)
ssh_handle_packets(session,-1);
return 0;
}
if (generate_session_keys(session) < 0) {
/**
* @internal
*
* @brief Analyze the SSH banner to find out if we have a SSHv1 or SSHv2
* server.
*
* @param session The session to analyze the banner from.
* @param ssh1 The variable which is set if it is a SSHv1 server.
* @param ssh2 The variable which is set if it is a SSHv2 server.
*
* @return 0 on success, < 0 on error.
*
* @see ssh_get_banner()
*/
static int ssh_analyze_banner(ssh_session session, int *ssh1, int *ssh2) {
const char *banner = session->clientbanner;
const char *openssh;
ssh_log(session, SSH_LOG_RARE, "Analyzing banner: %s", banner);
if (strncmp(banner, "SSH-", 4) != 0) {
ssh_set_error(session, SSH_FATAL, "Protocol mismatch: %s", banner);
return -1;
}
/*
* Once we got SSH2_MSG_NEWKEYS we can switch next_crypto and
* current_crypto
* Typical banners e.g. are:
* SSH-1.5-blah
* SSH-1.99-blah
* SSH-2.0-blah
*/
if (session->current_crypto) {
crypto_free(session->current_crypto);
switch(banner[4]) {
case '1':
*ssh1 = 1;
if (banner[6] == '9') {
*ssh2 = 1;
} else {
*ssh2 = 0;
}
break;
case '2':
*ssh1 = 0;
*ssh2 = 1;
break;
default:
ssh_set_error(session, SSH_FATAL, "Protocol mismatch: %s", banner);
return -1;
}
/* FIXME TODO later, include a function to change keys */
session->current_crypto = session->next_crypto;
session->next_crypto = crypto_new();
if (session->next_crypto == NULL) {
return -1;
openssh = strstr(banner, "OpenSSH");
if (openssh != NULL) {
int major, minor;
major = strtol(openssh + 8, (char **) NULL, 10);
minor = strtol(openssh + 10, (char **) NULL, 10);
session->openssh = SSH_VERSION_INT(major, minor, 0);
ssh_log(session, SSH_LOG_RARE,
"We are talking to an OpenSSH client version: %d.%d (%x)",
major, minor, session->openssh);
}
return 0;
}
/* FIXME TODO BUG
* Makes linker happy until I work on server code
/**
* @internal
*
* @brief A function to be called each time a step has been done in the
* connection.
*/
static char *ssh_get_banner(ssh_session session){
(void)session;
return strdup("BUG");
static void ssh_server_connection_callback(ssh_session session){
int ssh1,ssh2;
enter_function();
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->clientbanner == NULL) {
goto error;
}
set_status(session, 0.4f);
ssh_log(session, SSH_LOG_RARE,
"SSH client banner: %s", session->clientbanner);
/* Here we analyze the different protocols the server allows. */
if (ssh_analyze_banner(session, &ssh1, &ssh2) < 0) {
goto error;
}
/* Here we decide which version of the protocol to use. */
if (ssh2 && session->ssh2) {
session->version = 2;
} else if(ssh1 && session->ssh1) {
session->version = 1;
} else if(ssh1 && !session->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->clientbanner);
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);
set_status(session, 0.5f);
session->session_state=SSH_SESSION_STATE_INITIAL_KEX;
if (ssh_send_kex(session, 1) < 0) {
goto error;
}
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, &session->client_kex); // log client kex
crypt_set_algorithms_server(session);
if (set_kex(session) < 0) {
goto error;
}
set_status(session,0.8f);
session->session_state=SSH_SESSION_STATE_DH;
break;
case SSH_SESSION_STATE_DH:
if(session->dh_handshake_state==DH_STATE_FINISHED){
if (generate_session_keys(session) < 0) {
goto error;
}
/*
* Once we got SSH2_MSG_NEWKEYS we can switch next_crypto and
* current_crypto
*/
if (session->current_crypto) {
crypto_free(session->current_crypto);
}
/* FIXME TODO later, include a function to change keys */
session->current_crypto = session->next_crypto;
session->next_crypto = crypto_new();
if (session->next_crypto == NULL) {
goto error;
}
set_status(session,1.0f);
session->connected = 1;
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);
}
leave_function();
return;
error:
ssh_socket_close(session->socket);
session->alive = 0;
session->session_state=SSH_SESSION_STATE_ERROR;
leave_function();
}
/**
* @internal
*
* @brief Gets the banner from socket and saves it in session.
* Updates the session state
*
* @param data pointer to the beginning of header
* @param len size of the banner
* @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) {
char *buffer = (char *) data;
ssh_session session = (ssh_session) user;
char *str = NULL;
size_t i;
int ret=0;
enter_function();
for (i = 0; i < len; i++) {
#ifdef WITH_PCAP
if(session->pcap_ctx && buffer[i] == '\n') {
ssh_pcap_context_write(session->pcap_ctx,
SSH_PCAP_DIR_IN,
buffer,
i + 1,
i + 1);
}
#endif
if (buffer[i] == '\r') {
buffer[i]='\0';
}
if (buffer[i] == '\n') {
buffer[i]='\0';
str = strdup(buffer);
/* number of bytes read */
ret = i + 1;
session->clientbanner = str;
session->session_state = SSH_SESSION_STATE_BANNER_RECEIVED;
ssh_log(session, SSH_LOG_PACKET, "Received banner: %s", str);
session->ssh_connection_callback(session);
leave_function();
return ret;
}
if(i > 127) {
/* Too big banner */
session->session_state = SSH_SESSION_STATE_ERROR;
ssh_set_error(session, SSH_FATAL, "Receiving banner: too large banner");
leave_function();
return 0;
}
}
leave_function();
return ret;
}
/* Do the banner and key exchange */
int ssh_accept(ssh_session session) {
if (ssh_send_banner(session, 1) < 0) {
return -1;
}
int rc;
session->alive = 1;
rc = ssh_send_banner(session, 1);
if (rc < 0) {
return -1;
}
session->clientbanner = ssh_get_banner(session);
if (session->clientbanner == NULL) {
return -1;
}
session->alive = 1;
if (server_set_kex(session) < 0) {
return -1;
}
session->ssh_connection_callback = ssh_server_connection_callback;
session->session_state = SSH_SESSION_STATE_SOCKET_CONNECTED;
ssh_socket_set_callbacks(session->socket,&session->socket_callbacks);
session->socket_callbacks.data=callback_receive_banner;
#if 0
session->socket_callbacks.exception=socket_callback_exception;
#endif
session->socket_callbacks.userdata=session;
if (ssh_send_kex(session, 1) < 0) {
return -1;
}
rc = server_set_kex(session);
if (rc < 0) {
return -1;
}
/* TODO here things won't work anymore */
// if (ssh_get_kex(session,1) < 0) {
// return -1;
// }
while (session->session_state != SSH_SESSION_STATE_ERROR &&
session->session_state != SSH_SESSION_STATE_AUTHENTICATING &&
session->session_state != SSH_SESSION_STATE_DISCONNECTED) {
/*
* loop until SSH_SESSION_STATE_BANNER_RECEIVED or
* SSH_SESSION_STATE_ERROR
*/
ssh_handle_packets(session,-1);
ssh_log(session,SSH_LOG_PACKET, "ssh_accept: Actual state : %d",
session->session_state);
}
ssh_list_kex(session, &session->client_kex);
crypt_set_algorithms_server(session);
leave_function();
if (session->session_state == SSH_SESSION_STATE_ERROR ||
session->session_state == SSH_SESSION_STATE_DISCONNECTED) {
return SSH_ERROR;
}
if (dh_handshake_server(session) < 0) {
return -1;
}
session->connected = 1;
return 0;
return SSH_OK;
}
/**