client.c | client.c | |||
---|---|---|---|---|
skipping to change at line 34 | skipping to change at line 34 | |||
#include <stdio.h> | #include <stdio.h> | |||
#include <stdlib.h> | #include <stdlib.h> | |||
#include <string.h> | #include <string.h> | |||
#ifndef _WIN32 | #ifndef _WIN32 | |||
#include <arpa/inet.h> | #include <arpa/inet.h> | |||
#endif | #endif | |||
#include "libssh/priv.h" | #include "libssh/priv.h" | |||
#include "libssh/ssh2.h" | #include "libssh/ssh2.h" | |||
#include "libssh/buffer.h" | ||||
#define set_status(opt,status) do {\ | #include "libssh/packet.h" | |||
if (opt->connect_status_function) \ | #include "libssh/socket.h" | |||
opt->connect_status_function(opt->connect_status_arg, status); | #include "libssh/session.h" | |||
\ | #include "libssh/dh.h" | |||
#define set_status(session, status) do {\ | ||||
if (session->callbacks && session->callbacks->connect_status_functi | ||||
on) \ | ||||
session->callbacks->connect_status_function(session->callbacks- | ||||
>userdata, status); \ | ||||
} while (0) | } while (0) | |||
/** | /** | |||
* @internal | * @internal | |||
* | * | |||
* @brief Get a banner from a socket. | * @brief Get a banner from a socket. | |||
* | * | |||
* The caller has to free memroy. | * The caller has to free memroy. | |||
* | * | |||
* @param session The session to get the banner from. | * @param session The session to get the banner from. | |||
* | * | |||
* @return A newly allocated string with the banner or NULL on error. | * @return A newly allocated string with the banner or NULL on error. | |||
*/ | */ | |||
char *ssh_get_banner(SSH_SESSION *session) { | char *ssh_get_banner(ssh_session session) { | |||
char buffer[128] = {0}; | char buffer[128] = {0}; | |||
char *str = NULL; | char *str = NULL; | |||
int i; | int i; | |||
enter_function(); | enter_function(); | |||
for (i = 0; i < 127; i++) { | for (i = 0; i < 127; i++) { | |||
if (ssh_socket_read(session->socket, &buffer[i], 1) != SSH_OK) { | if (ssh_socket_read(session->socket, &buffer[i], 1) != SSH_OK) { | |||
ssh_set_error(session, SSH_FATAL, "Remote host closed connection"); | ssh_set_error(session, SSH_FATAL, "Remote host closed connection"); | |||
leave_function(); | leave_function(); | |||
return NULL; | return NULL; | |||
} | } | |||
#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') { | if (buffer[i] == '\r') { | |||
buffer[i] = '\0'; | buffer[i] = '\0'; | |||
} | } | |||
if (buffer[i] == '\n') { | if (buffer[i] == '\n') { | |||
buffer[i] = '\0'; | buffer[i] = '\0'; | |||
str = strdup(buffer); | str = strdup(buffer); | |||
if (str == NULL) { | if (str == NULL) { | |||
leave_function(); | leave_function(); | |||
return NULL; | return NULL; | |||
} | } | |||
skipping to change at line 100 | skipping to change at line 109 | |||
* server. | * server. | |||
* | * | |||
* @param session The session to analyze the banner from. | * @param session The session to analyze the banner from. | |||
* @param ssh1 The variable which is set if it is a SSHv1 server. | * @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. | * @param ssh2 The variable which is set if it is a SSHv2 server. | |||
* | * | |||
* @return 0 on success, < 0 on error. | * @return 0 on success, < 0 on error. | |||
* | * | |||
* @see ssh_get_banner() | * @see ssh_get_banner() | |||
*/ | */ | |||
static int ssh_analyze_banner(SSH_SESSION *session, int *ssh1, int *ssh2) { | static int ssh_analyze_banner(ssh_session session, int *ssh1, int *ssh2) { | |||
const char *banner = session->serverbanner; | const char *banner = session->serverbanner; | |||
const char *openssh; | const char *openssh; | |||
ssh_log(session, SSH_LOG_RARE, "Analyzing banner: %s", banner); | ssh_log(session, SSH_LOG_RARE, "Analyzing banner: %s", banner); | |||
if (strncmp(banner, "SSH-", 4) != 0) { | if (strncmp(banner, "SSH-", 4) != 0) { | |||
ssh_set_error(session, SSH_FATAL, "Protocol mismatch: %s", banner); | ssh_set_error(session, SSH_FATAL, "Protocol mismatch: %s", banner); | |||
return -1; | return -1; | |||
} | } | |||
skipping to change at line 158 | skipping to change at line 167 | |||
/** @internal | /** @internal | |||
* @brief Sends a SSH banner to the server. | * @brief Sends a SSH banner to the server. | |||
* | * | |||
* @param session The SSH session to use. | * @param session The SSH session to use. | |||
* | * | |||
* @param server Send client or server banner. | * @param server Send client or server banner. | |||
* | * | |||
* @return 0 on success, < 0 on error. | * @return 0 on success, < 0 on error. | |||
*/ | */ | |||
int ssh_send_banner(SSH_SESSION *session, int server) { | int ssh_send_banner(ssh_session session, int server) { | |||
const char *banner = NULL; | const char *banner = NULL; | |||
char buffer[128] = {0}; | char buffer[128] = {0}; | |||
enter_function(); | enter_function(); | |||
banner = session->version == 1 ? CLIENTBANNER1 : CLIENTBANNER2; | banner = session->version == 1 ? CLIENTBANNER1 : CLIENTBANNER2; | |||
if (session->options->banner) { | if (session->xbanner) { | |||
banner = session->options->banner; | banner = session->xbanner; | |||
} | } | |||
if (server) { | if (server) { | |||
session->serverbanner = strdup(banner); | session->serverbanner = strdup(banner); | |||
if (session->serverbanner == NULL) { | if (session->serverbanner == NULL) { | |||
leave_function(); | leave_function(); | |||
return -1; | return -1; | |||
} | } | |||
} else { | } else { | |||
session->clientbanner = strdup(banner); | session->clientbanner = strdup(banner); | |||
skipping to change at line 195 | skipping to change at line 204 | |||
if (ssh_socket_write(session->socket, buffer, strlen(buffer)) == SSH_ERRO R) { | if (ssh_socket_write(session->socket, buffer, strlen(buffer)) == SSH_ERRO R) { | |||
leave_function(); | leave_function(); | |||
return -1; | return -1; | |||
} | } | |||
if (ssh_socket_blocking_flush(session->socket) != SSH_OK) { | if (ssh_socket_blocking_flush(session->socket) != SSH_OK) { | |||
leave_function(); | leave_function(); | |||
return -1; | return -1; | |||
} | } | |||
#ifdef WITH_PCAP | ||||
if(session->pcap_ctx) | ||||
ssh_pcap_context_write(session->pcap_ctx,SSH_PCAP_DIR_OUT,buffer,str | ||||
len(buffer),strlen(buffer)); | ||||
#endif | ||||
leave_function(); | leave_function(); | |||
return 0; | return 0; | |||
} | } | |||
#define DH_STATE_INIT 0 | #define DH_STATE_INIT 0 | |||
#define DH_STATE_INIT_TO_SEND 1 | #define DH_STATE_INIT_TO_SEND 1 | |||
#define DH_STATE_INIT_SENT 2 | #define DH_STATE_INIT_SENT 2 | |||
#define DH_STATE_NEWKEYS_TO_SEND 3 | #define DH_STATE_NEWKEYS_TO_SEND 3 | |||
#define DH_STATE_NEWKEYS_SENT 4 | #define DH_STATE_NEWKEYS_SENT 4 | |||
#define DH_STATE_FINISHED 5 | #define DH_STATE_FINISHED 5 | |||
static int dh_handshake(SSH_SESSION *session) { | static int dh_handshake(ssh_session session) { | |||
STRING *e = NULL; | ssh_string e = NULL; | |||
STRING *f = NULL; | ssh_string f = NULL; | |||
STRING *pubkey = NULL; | ssh_string pubkey = NULL; | |||
STRING *signature = NULL; | ssh_string signature = NULL; | |||
int rc = SSH_ERROR; | int rc = SSH_ERROR; | |||
enter_function(); | enter_function(); | |||
switch (session->dh_handshake_state) { | switch (session->dh_handshake_state) { | |||
case DH_STATE_INIT: | case DH_STATE_INIT: | |||
if (buffer_add_u8(session->out_buffer, SSH2_MSG_KEXDH_INIT) < 0) { | if (buffer_add_u8(session->out_buffer, SSH2_MSG_KEXDH_INIT) < 0) { | |||
goto error; | goto error; | |||
} | } | |||
skipping to change at line 416 | skipping to change at line 428 | |||
* | * | |||
* @brief Request a service from the SSH server. | * @brief Request a service from the SSH server. | |||
* | * | |||
* Service requests are for example: ssh-userauth, ssh-connection, etc. | * Service requests are for example: ssh-userauth, ssh-connection, etc. | |||
* | * | |||
* @param session The session to use to ask for a service request. | * @param session The session to use to ask for a service request. | |||
* @param service The service request. | * @param service The service request. | |||
* | * | |||
* @return 0 on success, < 0 on error. | * @return 0 on success, < 0 on error. | |||
*/ | */ | |||
int ssh_service_request(SSH_SESSION *session, const char *service) { | int ssh_service_request(ssh_session session, const char *service) { | |||
STRING *service_s = NULL; | ssh_string service_s = NULL; | |||
enter_function(); | enter_function(); | |||
if (buffer_add_u8(session->out_buffer, SSH2_MSG_SERVICE_REQUEST) < 0) { | if (buffer_add_u8(session->out_buffer, SSH2_MSG_SERVICE_REQUEST) < 0) { | |||
leave_function(); | leave_function(); | |||
return -1; | return -1; | |||
} | } | |||
service_s = string_from_char(service); | service_s = string_from_char(service); | |||
if (service_s == NULL) { | if (service_s == NULL) { | |||
skipping to change at line 472 | skipping to change at line 484 | |||
/** \addtogroup ssh_session | /** \addtogroup ssh_session | |||
* @{ | * @{ | |||
*/ | */ | |||
/** \brief connect to the ssh server | /** \brief connect to the ssh server | |||
* \param session ssh session | * \param session ssh session | |||
* \return SSH_OK on success, SSH_ERROR on error | * \return SSH_OK on success, SSH_ERROR on error | |||
* \see ssh_new() | * \see ssh_new() | |||
* \see ssh_disconnect() | * \see ssh_disconnect() | |||
*/ | */ | |||
int ssh_connect(SSH_SESSION *session) { | int ssh_connect(ssh_session session) { | |||
SSH_OPTIONS *options = session->options; | ||||
int ssh1 = 0; | int ssh1 = 0; | |||
int ssh2 = 0; | int ssh2 = 0; | |||
int fd = -1; | int fd = -1; | |||
if (session == NULL) { | if (session == NULL) { | |||
ssh_set_error(session, SSH_FATAL, "Invalid session pointer"); | ssh_set_error(session, SSH_FATAL, "Invalid session pointer"); | |||
return SSH_ERROR; | return SSH_ERROR; | |||
} | } | |||
if (session->options == NULL) { | ||||
ssh_set_error(session, SSH_FATAL, "No options set"); | ||||
return SSH_ERROR; | ||||
} | ||||
options = session->options; | ||||
enter_function(); | enter_function(); | |||
session->alive = 0; | session->alive = 0; | |||
session->client = 1; | session->client = 1; | |||
if (ssh_init() < 0) { | if (ssh_init() < 0) { | |||
leave_function(); | leave_function(); | |||
return SSH_ERROR; | return SSH_ERROR; | |||
} | } | |||
if (options->fd == -1 && options->host == NULL) { | if (session->fd == -1 && session->host == NULL) { | |||
ssh_set_error(session, SSH_FATAL, "Hostname required"); | ssh_set_error(session, SSH_FATAL, "Hostname required"); | |||
leave_function(); | leave_function(); | |||
return SSH_ERROR; | return SSH_ERROR; | |||
} | } | |||
if (options->fd != -1) { | if (session->fd != -1) { | |||
fd = options->fd; | fd = session->fd; | |||
} else { | } else { | |||
fd = ssh_connect_host(session, options->host, options->bindaddr, | fd = ssh_connect_host(session, session->host, session->bindaddr, | |||
options->port, options->timeout, options->timeout_usec); | session->port, session->timeout, session->timeout_usec); | |||
} | } | |||
if (fd < 0) { | if (fd < 0) { | |||
leave_function(); | leave_function(); | |||
return SSH_ERROR; | return SSH_ERROR; | |||
} | } | |||
set_status(options, 0.2); | set_status(session, 0.2); | |||
ssh_socket_set_fd(session->socket, fd); | ssh_socket_set_fd(session->socket, fd); | |||
session->alive = 1; | session->alive = 1; | |||
session->serverbanner = ssh_get_banner(session); | session->serverbanner = ssh_get_banner(session); | |||
if (session->serverbanner == NULL) { | if (session->serverbanner == NULL) { | |||
ssh_socket_close(session->socket); | ssh_socket_close(session->socket); | |||
session->alive = 0; | session->alive = 0; | |||
leave_function(); | leave_function(); | |||
return SSH_ERROR; | return SSH_ERROR; | |||
} | } | |||
set_status(options, 0.4); | set_status(session, 0.4); | |||
ssh_log(session, SSH_LOG_RARE, | ssh_log(session, SSH_LOG_RARE, | |||
"SSH server banner: %s", session->serverbanner); | "SSH server banner: %s", session->serverbanner); | |||
/* Here we analyse the different protocols the server allows. */ | /* Here we analyse the different protocols the server allows. */ | |||
if (ssh_analyze_banner(session, &ssh1, &ssh2) < 0) { | if (ssh_analyze_banner(session, &ssh1, &ssh2) < 0) { | |||
ssh_socket_close(session->socket); | ssh_socket_close(session->socket); | |||
session->alive = 0; | session->alive = 0; | |||
leave_function(); | leave_function(); | |||
return SSH_ERROR; | return SSH_ERROR; | |||
} | } | |||
/* Here we decide which version of the protocol to use. */ | /* Here we decide which version of the protocol to use. */ | |||
if (ssh2 && options->ssh2allowed) { | if (ssh2 && session->ssh2) { | |||
session->version = 2; | session->version = 2; | |||
} else if(ssh1 && options->ssh1allowed) { | } else if(ssh1 && session->ssh1) { | |||
session->version = 1; | session->version = 1; | |||
} else { | } else { | |||
ssh_set_error(session, SSH_FATAL, | ssh_set_error(session, SSH_FATAL, | |||
"No version of SSH protocol usable (banner: %s)", | "No version of SSH protocol usable (banner: %s)", | |||
session->serverbanner); | session->serverbanner); | |||
ssh_socket_close(session->socket); | ssh_socket_close(session->socket); | |||
session->alive = 0; | session->alive = 0; | |||
leave_function(); | leave_function(); | |||
return SSH_ERROR; | return SSH_ERROR; | |||
} | } | |||
if (ssh_send_banner(session, 0) < 0) { | if (ssh_send_banner(session, 0) < 0) { | |||
ssh_set_error(session, SSH_FATAL, "Sending the banner failed"); | ssh_set_error(session, SSH_FATAL, "Sending the banner failed"); | |||
ssh_socket_close(session->socket); | ssh_socket_close(session->socket); | |||
session->alive = 0; | session->alive = 0; | |||
leave_function(); | leave_function(); | |||
return SSH_ERROR; | return SSH_ERROR; | |||
} | } | |||
set_status(options, 0.5); | set_status(session, 0.5); | |||
switch (session->version) { | switch (session->version) { | |||
case 2: | case 2: | |||
if (ssh_get_kex(session,0) < 0) { | if (ssh_get_kex(session,0) < 0) { | |||
ssh_socket_close(session->socket); | ssh_socket_close(session->socket); | |||
session->alive = 0; | session->alive = 0; | |||
leave_function(); | leave_function(); | |||
return SSH_ERROR; | return SSH_ERROR; | |||
} | } | |||
set_status(options,0.6); | set_status(session,0.6); | |||
ssh_list_kex(session, &session->server_kex); | ssh_list_kex(session, &session->server_kex); | |||
if (set_kex(session) < 0) { | if (set_kex(session) < 0) { | |||
ssh_socket_close(session->socket); | ssh_socket_close(session->socket); | |||
session->alive = 0; | session->alive = 0; | |||
leave_function(); | leave_function(); | |||
return SSH_ERROR; | return SSH_ERROR; | |||
} | } | |||
if (ssh_send_kex(session, 0) < 0) { | if (ssh_send_kex(session, 0) < 0) { | |||
ssh_socket_close(session->socket); | ssh_socket_close(session->socket); | |||
session->alive = 0; | session->alive = 0; | |||
leave_function(); | leave_function(); | |||
return SSH_ERROR; | return SSH_ERROR; | |||
} | } | |||
set_status(options,0.8); | set_status(session,0.8); | |||
if (dh_handshake(session) < 0) { | if (dh_handshake(session) < 0) { | |||
ssh_socket_close(session->socket); | ssh_socket_close(session->socket); | |||
session->alive = 0; | session->alive = 0; | |||
leave_function(); | leave_function(); | |||
return SSH_ERROR; | return SSH_ERROR; | |||
} | } | |||
set_status(options,1.0); | set_status(session,1.0); | |||
session->connected = 1; | session->connected = 1; | |||
break; | break; | |||
case 1: | case 1: | |||
if (ssh_get_kex1(session) < 0) { | if (ssh_get_kex1(session) < 0) { | |||
ssh_socket_close(session->socket); | ssh_socket_close(session->socket); | |||
session->alive = 0; | session->alive = 0; | |||
leave_function(); | leave_function(); | |||
return SSH_ERROR; | return SSH_ERROR; | |||
} | } | |||
set_status(options,0.6); | set_status(session,0.6); | |||
session->connected = 1; | session->connected = 1; | |||
break; | break; | |||
} | } | |||
leave_function(); | leave_function(); | |||
return 0; | return 0; | |||
} | } | |||
/** | /** | |||
* @brief Get the issue banner from the server. | * @brief Get the issue banner from the server. | |||
* | * | |||
* This is the banner showing a disclaimer to users who log in, | * This is the banner showing a disclaimer to users who log in, | |||
* typically their right or the fact that they will be monitored. | * typically their right or the fact that they will be monitored. | |||
* | * | |||
* @param session The SSH session to use. | * @param session The SSH session to use. | |||
* | * | |||
* @return A newly allocated string with the banner, NULL on error. | * @return A newly allocated string with the banner, NULL on error. | |||
*/ | */ | |||
char *ssh_get_issue_banner(SSH_SESSION *session) { | char *ssh_get_issue_banner(ssh_session session) { | |||
if (session == NULL || session->banner == NULL) { | if (session == NULL || session->banner == NULL) { | |||
return NULL; | return NULL; | |||
} | } | |||
return string_to_char(session->banner); | return string_to_char(session->banner); | |||
} | } | |||
/** | /** | |||
* @brief Get the version of the OpenSSH server, if it is not an OpenSSH se rver | * @brief Get the version of the OpenSSH server, if it is not an OpenSSH se rver | |||
* then 0 will be returned. | * then 0 will be returned. | |||
* | * | |||
* You can use the SSH_VERSION_INT macro to compare version numbers. | * You can use the SSH_VERSION_INT macro to compare version numbers. | |||
* | * | |||
* @param session The SSH session to use. | * @param session The SSH session to use. | |||
* | * | |||
* @return The version number if available, 0 otherwise. | * @return The version number if available, 0 otherwise. | |||
*/ | */ | |||
int ssh_get_openssh_version(SSH_SESSION *session) { | int ssh_get_openssh_version(ssh_session session) { | |||
if (session == NULL) { | if (session == NULL) { | |||
return 0; | return 0; | |||
} | } | |||
return session->openssh; | return session->openssh; | |||
} | } | |||
/** | /** | |||
* @brief Disconnect from a session (client or server). | * @brief Disconnect from a session (client or server). | |||
* The session can then be reused to open a new session. | ||||
* | * | |||
* @param session The SSH session to disconnect. | * @param session The SSH session to disconnect. | |||
*/ | */ | |||
void ssh_disconnect(SSH_SESSION *session) { | void ssh_disconnect(ssh_session session) { | |||
STRING *str = NULL; | ssh_string str = NULL; | |||
if (session == NULL) { | if (session == NULL) { | |||
return; | return; | |||
} | } | |||
enter_function(); | enter_function(); | |||
if (ssh_socket_is_open(session->socket)) { | if (ssh_socket_is_open(session->socket)) { | |||
if (buffer_add_u8(session->out_buffer, SSH2_MSG_DISCONNECT) < 0) { | if (buffer_add_u8(session->out_buffer, SSH2_MSG_DISCONNECT) < 0) { | |||
goto error; | goto error; | |||
skipping to change at line 691 | skipping to change at line 697 | |||
} | } | |||
string_free(str); | string_free(str); | |||
packet_send(session); | packet_send(session); | |||
ssh_socket_close(session->socket); | ssh_socket_close(session->socket); | |||
} | } | |||
session->alive = 0; | session->alive = 0; | |||
error: | error: | |||
leave_function(); | leave_function(); | |||
ssh_cleanup(session); | ||||
} | } | |||
const char *ssh_copyright(void) { | const char *ssh_copyright(void) { | |||
return SSH_STRINGIFY(LIBSSH_VERSION) " (c) 2003-2008 Aris Adamantiadis " | return SSH_STRINGIFY(LIBSSH_VERSION) " (c) 2003-2008 Aris Adamantiadis " | |||
"(aris@0xbadc0de.be) Distributed under the LGPL, please refer to COPYIN G" | "(aris@0xbadc0de.be) Distributed under the LGPL, please refer to COPYIN G" | |||
"file for informations about your rights"; | "file for informations about your rights"; | |||
} | } | |||
/** @} */ | /** @} */ | |||
/* vim: set ts=2 sw=2 et cindent: */ | /* vim: set ts=2 sw=2 et cindent: */ | |||
End of changes. 28 change blocks. | ||||
46 lines changed or deleted | 54 lines changed or added | |||
This html diff was produced by rfcdiff 1.41. The latest version is available from http://tools.ietf.org/tools/rfcdiff/ |