GUAC-1171: Implement SSH connection (without auth).

This commit is contained in:
Michael Jumper 2015-07-09 12:28:46 -07:00
parent ed70281766
commit 5b627ae5cc
2 changed files with 178 additions and 15 deletions

View File

@ -32,7 +32,14 @@
#include <openssl/err.h> #include <openssl/err.h>
#include <openssl/ssl.h> #include <openssl/ssl.h>
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
#include <pthread.h> #include <pthread.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#ifdef LIBSSH2_USES_GCRYPT #ifdef LIBSSH2_USES_GCRYPT
GCRY_THREAD_OPTION_PTHREAD_IMPL; GCRY_THREAD_OPTION_PTHREAD_IMPL;
@ -129,20 +136,156 @@ void guac_common_ssh_uninit() {
guac_common_ssh_openssl_free_locks(CRYPTO_num_locks()); guac_common_ssh_openssl_free_locks(CRYPTO_num_locks());
} }
LIBSSH2_SESSION* guac_common_ssh_connect_password(const char* hostname, /**
int port, const char* username, const char* password) { * Connects to the SSH server running at the given hostname and port, but does
* not perform any authentication. Authentication must be immediately performed
* after creation of the session for the session to become usable. If an error
* occurs while connecting, the Guacamole client will automatically and fatally
* abort.
*
* @param client
* The Guacamole client that will be using SSH.
*
* @param hostname
* The hostname of the SSH server to connect to.
*
* @param port
* The port to connect to on the given hostname.
*
* @param socket_fd
* A pointer to an integer in which the newly-allocated file descriptor for
* the SSH socket should be stored.
*
* @return
* A new SSH session if the connection succeeds, or NULL if the connection
* was not successful.
*/
static LIBSSH2_SESSION* guac_common_ssh_connect(guac_client* client,
const char* hostname, const char* port, int* socket_fd) {
/* STUB */ int retval;
int fd;
struct addrinfo* addresses;
struct addrinfo* current_address;
char connected_address[1024];
char connected_port[64];
struct addrinfo hints = {
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_STREAM,
.ai_protocol = IPPROTO_TCP
};
/* Get socket */
fd = socket(AF_INET, SOCK_STREAM, 0);
/* Get addresses connection */
if ((retval = getaddrinfo(hostname, port, &hints, &addresses))) {
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
"Error parsing given address or port: %s",
gai_strerror(retval));
return NULL; return NULL;
}
/* Attempt connection to each address until success */
current_address = addresses;
while (current_address != NULL) {
/* Resolve hostname */
if ((retval = getnameinfo(current_address->ai_addr,
current_address->ai_addrlen,
connected_address, sizeof(connected_address),
connected_port, sizeof(connected_port),
NI_NUMERICHOST | NI_NUMERICSERV)))
guac_client_log(client, GUAC_LOG_DEBUG,
"Unable to resolve host: %s", gai_strerror(retval));
/* Connect */
if (connect(fd, current_address->ai_addr,
current_address->ai_addrlen) == 0) {
guac_client_log(client, GUAC_LOG_DEBUG,
"Successfully connected to host %s, port %s",
connected_address, connected_port);
/* Done if successful connect */
break;
} }
LIBSSH2_SESSION* guac_common_ssh_connect_private_key(const char* hostname, /* Otherwise log information regarding bind failure */
int port, const char* username, const char* private_key, else
const char* passphrase) { guac_client_log(client, GUAC_LOG_DEBUG, "Unable to connect to "
"host %s, port %s: %s",
connected_address, connected_port, strerror(errno));
/* STUB */ current_address = current_address->ai_next;
return NULL;
}
/* If unable to connect to anything, fail */
if (current_address == NULL) {
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR,
"Unable to connect to any addresses.");
return NULL;
}
/* Free addrinfo */
freeaddrinfo(addresses);
/* Open SSH session */
LIBSSH2_SESSION* session = libssh2_session_init_ex(NULL, NULL,
NULL, client);
if (session == NULL) {
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
"Session allocation failed.");
return NULL;
}
/* Perform handshake */
if (libssh2_session_handshake(session, fd)) {
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR,
"SSH handshake failed.");
return NULL;
}
/* Save file descriptor */
if (socket_fd != NULL)
*socket_fd = fd;
/* Return created session */
return session;
}
LIBSSH2_SESSION* guac_common_ssh_connect_password(guac_client* client,
const char* hostname, const char* port,
const char* username, const char* password,
int* socket_fd) {
LIBSSH2_SESSION* session = guac_common_ssh_connect(client,
hostname, port, socket_fd);
/* STUB */
return session;
}
LIBSSH2_SESSION* guac_common_ssh_connect_private_key(guac_client* client,
const char* hostname, const char* port,
const char* username, const char* private_key,
const char* passphrase,
int* socket_fd) {
LIBSSH2_SESSION* session = guac_common_ssh_connect(client,
hostname, port, socket_fd);
/* STUB */
return session;
} }

View File

@ -48,7 +48,11 @@ void guac_common_ssh_uninit();
/** /**
* Connects to the SSH server running at the given hostname and port using the * Connects to the SSH server running at the given hostname and port using the
* given username and password for authentication. * given username and password for authentication. If an error occurs while
* connecting, the Guacamole client will automatically and fatally abort.
*
* @param client
* The Guacamole client that will be using SSH.
* *
* @param hostname * @param hostname
* The hostname of the SSH server to connect to. * The hostname of the SSH server to connect to.
@ -62,16 +66,26 @@ void guac_common_ssh_uninit();
* @param password * @param password
* The password to provide when authenticating as the given user. * The password to provide when authenticating as the given user.
* *
* @param socket_fd
* A pointer to an integer in which the newly-allocated file descriptor for
* the SSH socket should be stored.
*
* @return * @return
* A new SSH session if the connection and authentication succeed, or * A new SSH session if the connection and authentication succeed, or
* NULL if the connection or authentication were not successful. * NULL if the connection or authentication were not successful.
*/ */
LIBSSH2_SESSION* guac_common_ssh_connect_password(const char* hostname, LIBSSH2_SESSION* guac_common_ssh_connect_password(guac_client* client,
int port, const char* username, const char* password); const char* hostname, const char* port,
const char* username, const char* password,
int* socket_fd);
/** /**
* Connects to the SSH server running at the given hostname and port using the * Connects to the SSH server running at the given hostname and port using the
* given username and private key for authentication. * given username and private key for authentication. If an error occurs while
* connecting, the Guacamole client will automatically and fatally abort.
*
* @param client
* The Guacamole client that will be using SSH.
* *
* @param hostname * @param hostname
* The hostname of the SSH server to connect to. * The hostname of the SSH server to connect to.
@ -89,13 +103,19 @@ LIBSSH2_SESSION* guac_common_ssh_connect_password(const char* hostname,
* The passphrase to use when importing the private key, if any, or NULL * The passphrase to use when importing the private key, if any, or NULL
* if no passphrase should be used. * if no passphrase should be used.
* *
* @param socket_fd
* A pointer to an integer in which the newly-allocated file descriptor for
* the SSH socket should be stored.
*
* @return * @return
* A new SSH session if the connection and authentication succeed, or * A new SSH session if the connection and authentication succeed, or
* NULL if the connection or authentication were not successful. * NULL if the connection or authentication were not successful.
*/ */
LIBSSH2_SESSION* guac_common_ssh_connect_private_key(const char* hostname, LIBSSH2_SESSION* guac_common_ssh_connect_private_key(guac_client* client,
int port, const char* username, const char* private_key, const char* hostname, const char* port,
const char* passphrase); const char* username, const char* private_key,
const char* passphrase,
int* socket_fd);
#endif #endif