From 0fcea2738bc2d58a930c930c6a7bab72a914c87e Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 10 Jul 2015 12:43:35 -0700 Subject: [PATCH] GUAC-1171: Migrate to common SSH within client. --- src/protocols/ssh/Makefile.am | 8 +- src/protocols/ssh/client.c | 1 - src/protocols/ssh/client.h | 11 +- src/protocols/ssh/guac_handlers.c | 15 +- src/protocols/ssh/ssh_buffer.c | 136 ----------- src/protocols/ssh/ssh_buffer.h | 82 ------- src/protocols/ssh/ssh_client.c | 382 +++++++++--------------------- src/protocols/ssh/ssh_key.c | 216 ----------------- src/protocols/ssh/ssh_key.h | 137 ----------- 9 files changed, 121 insertions(+), 867 deletions(-) delete mode 100644 src/protocols/ssh/ssh_buffer.c delete mode 100644 src/protocols/ssh/ssh_buffer.h delete mode 100644 src/protocols/ssh/ssh_key.c delete mode 100644 src/protocols/ssh/ssh_key.h diff --git a/src/protocols/ssh/Makefile.am b/src/protocols/ssh/Makefile.am index e13bc253..ad1a2f56 100644 --- a/src/protocols/ssh/Makefile.am +++ b/src/protocols/ssh/Makefile.am @@ -30,18 +30,14 @@ libguac_client_ssh_la_SOURCES = \ clipboard.c \ guac_handlers.c \ sftp.c \ - ssh_buffer.c \ - ssh_client.c \ - ssh_key.c + ssh_client.c noinst_HEADERS = \ client.h \ clipboard.h \ guac_handlers.h \ sftp.h \ - ssh_buffer.h \ - ssh_client.h \ - ssh_key.h + ssh_client.h # Add agent sources if enabled if ENABLE_SSH_AGENT diff --git a/src/protocols/ssh/client.c b/src/protocols/ssh/client.c index b3c5bf26..63c5f2c8 100644 --- a/src/protocols/ssh/client.c +++ b/src/protocols/ssh/client.c @@ -142,7 +142,6 @@ int guac_client_init(guac_client* client, int argc, char** argv) { strcpy(client_data->password, argv[IDX_PASSWORD]); /* Init public key auth information */ - client_data->key = NULL; strcpy(client_data->key_base64, argv[IDX_PRIVATE_KEY]); strcpy(client_data->key_passphrase, argv[IDX_PASSPHRASE]); diff --git a/src/protocols/ssh/client.h b/src/protocols/ssh/client.h index 39d8d544..40f97ab2 100644 --- a/src/protocols/ssh/client.h +++ b/src/protocols/ssh/client.h @@ -26,8 +26,8 @@ #include "config.h" +#include "guac_ssh.h" #include "sftp.h" -#include "ssh_key.h" #include "terminal.h" #include @@ -77,11 +77,6 @@ typedef struct ssh_guac_client_data { */ char key_passphrase[1024]; - /** - * The private key to use for authentication, if any. - */ - ssh_key* key; - /** * The name of the font to use for display rendering. */ @@ -117,12 +112,12 @@ typedef struct ssh_guac_client_data { /** * SSH session, used by the SSH client thread. */ - LIBSSH2_SESSION* session; + guac_common_ssh_session* session; /** * The distinct SSH session used for SFTP. */ - LIBSSH2_SESSION* sftp_ssh_session; + guac_common_ssh_session* sftp_ssh_session; /** * SFTP session, used for file transfers. diff --git a/src/protocols/ssh/guac_handlers.c b/src/protocols/ssh/guac_handlers.c index ea01e884..8293afe2 100644 --- a/src/protocols/ssh/guac_handlers.c +++ b/src/protocols/ssh/guac_handlers.c @@ -24,7 +24,7 @@ #include "client.h" #include "guac_handlers.h" -#include "ssh_key.h" +#include "guac_ssh.h" #include "terminal.h" #include @@ -106,10 +106,8 @@ int ssh_guac_client_free_handler(guac_client* client) { libssh2_sftp_shutdown(guac_client_data->sftp_session); /* Disconnect SSH session corresponding to the SFTP session */ - if (guac_client_data->sftp_ssh_session) { - libssh2_session_disconnect(guac_client_data->sftp_ssh_session, "Bye"); - libssh2_session_free(guac_client_data->sftp_ssh_session); - } + if (guac_client_data->sftp_ssh_session) + guac_common_ssh_destroy_session(guac_client_data->sftp_ssh_session); /* Clean up the SFTP filesystem object */ if (guac_client_data->sftp_filesystem) @@ -117,15 +115,12 @@ int ssh_guac_client_free_handler(guac_client* client) { /* Free session */ if (guac_client_data->session != NULL) - libssh2_session_free(guac_client_data->session); - - /* Free auth key */ - if (guac_client_data->key != NULL) - ssh_key_free(guac_client_data->key); + guac_common_ssh_destroy_session(guac_client_data->session); /* Free generic data struct */ free(client->data); + guac_common_ssh_uninit(); return 0; } diff --git a/src/protocols/ssh/ssh_buffer.c b/src/protocols/ssh/ssh_buffer.c deleted file mode 100644 index 4d4d852a..00000000 --- a/src/protocols/ssh/ssh_buffer.c +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2013 Glyptodon LLC - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "config.h" - -#include -#include - -#include -#include -#include - -void buffer_write_byte(char** buffer, uint8_t value) { - - uint8_t* data = (uint8_t*) *buffer; - *data = value; - - (*buffer)++; - -} - -void buffer_write_uint32(char** buffer, uint32_t value) { - - uint8_t* data = (uint8_t*) *buffer; - - data[0] = (value & 0xFF000000) >> 24; - data[1] = (value & 0x00FF0000) >> 16; - data[2] = (value & 0x0000FF00) >> 8; - data[3] = value & 0x000000FF; - - *buffer += 4; - -} - -void buffer_write_data(char** buffer, const char* data, int length) { - memcpy(*buffer, data, length); - *buffer += length; -} - -void buffer_write_bignum(char** buffer, BIGNUM* value) { - - unsigned char* bn_buffer; - int length; - - /* If zero, just write zero length */ - if (BN_is_zero(value)) { - buffer_write_uint32(buffer, 0); - return; - } - - /* Allocate output buffer, add padding byte */ - length = BN_num_bytes(value); - bn_buffer = malloc(length); - - /* Convert BIGNUM */ - BN_bn2bin(value, bn_buffer); - - /* If first byte has high bit set, write padding byte */ - if (bn_buffer[0] & 0x80) { - buffer_write_uint32(buffer, length+1); - buffer_write_byte(buffer, 0); - } - else - buffer_write_uint32(buffer, length); - - /* Write data */ - memcpy(*buffer, bn_buffer, length); - *buffer += length; - - free(bn_buffer); - -} - -void buffer_write_string(char** buffer, const char* string, int length) { - buffer_write_uint32(buffer, length); - buffer_write_data(buffer, string, length); -} - -uint8_t buffer_read_byte(char** buffer) { - - uint8_t* data = (uint8_t*) *buffer; - uint8_t value = *data; - - (*buffer)++; - - return value; - -} - -uint32_t buffer_read_uint32(char** buffer) { - - uint8_t* data = (uint8_t*) *buffer; - uint32_t value = - (data[0] << 24) - | (data[1] << 16) - | (data[2] << 8) - | data[3]; - - *buffer += 4; - - return value; - -} - -char* buffer_read_string(char** buffer, int* length) { - - char* value; - - *length = buffer_read_uint32(buffer); - value = *buffer; - - *buffer += *length; - - return value; - -} - diff --git a/src/protocols/ssh/ssh_buffer.h b/src/protocols/ssh/ssh_buffer.h deleted file mode 100644 index cb7a6c13..00000000 --- a/src/protocols/ssh/ssh_buffer.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2013 Glyptodon LLC - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - - -#ifndef _GUAC_SSH_BUFFER_H -#define _GUAC_SSH_BUFFER_H - -#include "config.h" - -#include -#include - -/** - * Writes the given byte to the given buffer, advancing the buffer pointer by - * one byte. - */ -void buffer_write_byte(char** buffer, uint8_t value); - -/** - * Writes the given integer to the given buffer, advancing the buffer pointer - * four bytes. - */ -void buffer_write_uint32(char** buffer, uint32_t value); - -/** - * Writes the given string and its length to the given buffer, advancing the - * buffer pointer by the size of the length (four bytes) and the size of the - * string. - */ -void buffer_write_string(char** buffer, const char* string, int length); - -/** - * Writes the given BIGNUM the given buffer, advancing the buffer pointer by - * the size of the length (four bytes) and the size of the BIGNUM. - */ -void buffer_write_bignum(char** buffer, BIGNUM* value); - -/** - * Writes the given data the given buffer, advancing the buffer pointer by the - * given length. - */ -void buffer_write_data(char** buffer, const char* data, int length); - -/** - * Reads a single byte from the given buffer, advancing the buffer by one byte. - */ -uint8_t buffer_read_byte(char** buffer); - -/** - * Reads an integer from the given buffer, advancing the buffer by four bytes. - */ -uint32_t buffer_read_uint32(char** buffer); - -/** - * Reads a string and its length from the given buffer, advancing the buffer - * by the size of the length (four bytes) and the size of the string, and - * returning a pointer to the buffer. The length of the string is stored in - * the given int. - */ -char* buffer_read_string(char** buffer, int* length); - -#endif - diff --git a/src/protocols/ssh/ssh_client.c b/src/protocols/ssh/ssh_client.c index 301f83e0..1ac672be 100644 --- a/src/protocols/ssh/ssh_client.c +++ b/src/protocols/ssh/ssh_client.c @@ -25,7 +25,6 @@ #include "client.h" #include "guac_ssh.h" #include "sftp.h" -#include "ssh_key.h" #include "terminal.h" #ifdef ENABLE_SSH_AGENT @@ -57,6 +56,96 @@ #include #include +/** + * Produces a new user object containing a username and password or private + * key, prompting the user as necessary to obtain that information. + * + * @param client + * The Guacamole client containing any existing user data, as well as the + * terminal to use when prompting the user. + * + * @return + * A new user object containing the user's username and other credentials. + */ +static guac_common_ssh_user* guac_ssh_get_user(guac_client* client) { + + ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; + + guac_common_ssh_user* user; + + /* Get username */ + if (client_data->username[0] == 0) + guac_terminal_prompt(client_data->term, "Login as: ", + client_data->username, sizeof(client_data->username), true); + + /* Create user object from username */ + user = guac_common_ssh_create_user(client_data->username); + + /* If key specified, import */ + if (client_data->key_base64[0] != 0) { + + guac_client_log(client, GUAC_LOG_DEBUG, + "Attempting private key import (WITHOUT passphrase)"); + + /* Attempt to read key without passphrase */ + if (guac_common_ssh_user_import_key(user, + client_data->key_base64, NULL)) { + + /* Log failure of initial attempt */ + guac_client_log(client, GUAC_LOG_DEBUG, + "Initial import failed: %s", + guac_common_ssh_key_error()); + + guac_client_log(client, GUAC_LOG_DEBUG, + "Re-attempting private key import (WITH passphrase)"); + + /* Prompt for passphrase if missing */ + if (client_data->key_passphrase[0] == 0) + guac_terminal_prompt(client_data->term, "Key passphrase: ", + client_data->key_passphrase, + sizeof(client_data->key_passphrase), false); + + /* Reattempt import with passphrase */ + if (guac_common_ssh_user_import_key(user, + client_data->key_base64, + client_data->key_passphrase)) { + + /* If still failing, give up */ + guac_client_abort(client, + GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED, + "Auth key import failed: %s", + guac_common_ssh_key_error()); + + guac_common_ssh_destroy_user(user); + return NULL; + + } + + } /* end decrypt key with passphrase */ + + /* Success */ + guac_client_log(client, GUAC_LOG_INFO, + "Auth key successfully imported."); + + } /* end if key given */ + + /* Otherwise, get password if not provided */ + else if (client_data->password[0] == 0) { + + guac_terminal_prompt(client_data->term, "Password: ", + client_data->password, sizeof(client_data->password), false); + + guac_common_ssh_user_set_password(user, client_data->password); + + } + + /* Clear screen of any prompts */ + guac_terminal_printf(client_data->term, "\x1B[H\x1B[J"); + + return user; + +} + void* ssh_input_thread(void* data) { guac_client* client = (guac_client*) data; @@ -76,213 +165,13 @@ void* ssh_input_thread(void* data) { } -static int __sign_callback(LIBSSH2_SESSION* session, - unsigned char** sig, size_t* sig_len, - const unsigned char* data, size_t data_len, void **abstract) { - - ssh_key* key = (ssh_key*) abstract; - int length; - - /* Allocate space for signature */ - *sig = malloc(4096); - - /* Sign with key */ - length = ssh_key_sign(key, (const char*) data, data_len, *sig); - if (length < 0) - return 1; - - *sig_len = length; - return 0; -} - -/** - * Callback for the keyboard-interactive authentication method. Currently - * suports just one prompt for the password. - */ -static void __kbd_callback(const char *name, int name_len, - const char *instruction, int instruction_len, - int num_prompts, - const LIBSSH2_USERAUTH_KBDINT_PROMPT *prompts, - LIBSSH2_USERAUTH_KBDINT_RESPONSE *responses, - void **abstract) { - - guac_client* client = (guac_client*) *abstract; - ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; - - if (num_prompts == 1) { - responses[0].text = strdup(client_data->password); - responses[0].length = strlen(client_data->password); - } - else - guac_client_log(client, GUAC_LOG_WARNING, - "Unsupported number of keyboard-interactive prompts: %i", - num_prompts); - -} - -static LIBSSH2_SESSION* __guac_ssh_create_session(guac_client* client, - int* socket_fd) { - - int retval; - - int fd; - struct addrinfo* addresses; - struct addrinfo* current_address; - - char connected_address[1024]; - char connected_port[64]; - char *user_authlist; - - ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; - - 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(client_data->hostname, client_data->port, - &hints, &addresses))) { - guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Error parsing given address or port: %s", - gai_strerror(retval)); - return NULL; - - } - - /* Attempt connection to each address until success */ - current_address = addresses; - while (current_address != NULL) { - - int retval; - - /* 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; - - } - - /* Otherwise log information regarding bind failure */ - else - guac_client_log(client, GUAC_LOG_DEBUG, "Unable to connect to " - "host %s, port %s: %s", - connected_address, connected_port, strerror(errno)); - - current_address = current_address->ai_next; - - } - - /* 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; - - /* Get list of suported authentication methods */ - user_authlist = libssh2_userauth_list(session, client_data->username, strlen(client_data->username)); - guac_client_log(client, GUAC_LOG_DEBUG, "Supported authentication methods: %s", user_authlist); - - /* Authenticate with key if available */ - if (client_data->key != NULL) { - - /* Check if public key auth is suported on the server */ - if (strstr(user_authlist, "publickey") == NULL) { - guac_client_abort(client, GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED, - "Public key authentication not suported"); - return NULL; - } - - if (!libssh2_userauth_publickey(session, client_data->username, - (unsigned char*) client_data->key->public_key, - client_data->key->public_key_length, - __sign_callback, (void**) client_data->key)) - return session; - else { - char* error_message; - libssh2_session_last_error(session, &error_message, NULL, 0); - guac_client_abort(client, GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED, - "Public key authentication failed: %s", error_message); - return NULL; - } - } - - /* Authenticate with password */ - if (strstr(user_authlist, "password") != NULL) { - guac_client_log(client, GUAC_LOG_DEBUG, "Using password authentication method"); - retval = libssh2_userauth_password(session, client_data->username, client_data->password); - } - else if (strstr(user_authlist, "keyboard-interactive") != NULL) { - guac_client_log(client, GUAC_LOG_DEBUG, "Using keyboard-interactive authentication method"); - retval = libssh2_userauth_keyboard_interactive(session, client_data->username, &__kbd_callback); - } - else { - guac_client_abort(client, GUAC_PROTOCOL_STATUS_CLIENT_BAD_TYPE, "No known authentication methods"); - return NULL; - } - - if (retval == 0) - return session; - - else { - char* error_message; - libssh2_session_last_error(session, &error_message, NULL, 0); - guac_client_abort(client, GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED, - "Password authentication failed: %s", error_message); - return NULL; - } - -} - void* ssh_client_thread(void* data) { guac_client* client = (guac_client*) data; ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; - char name[1024]; - guac_socket* socket = client->socket; char buffer[8192]; - int bytes_read = -1234; - - int socket_fd; pthread_t input_thread; @@ -290,81 +179,31 @@ void* ssh_client_thread(void* data) { if (guac_common_ssh_init(client)) return NULL; - /* Get username */ - if (client_data->username[0] == 0) - guac_terminal_prompt(client_data->term, "Login as: ", - client_data->username, sizeof(client_data->username), true); + /* Get user and credentials */ + guac_common_ssh_user* user = guac_ssh_get_user(client); /* Send new name */ - snprintf(name, sizeof(name)-1, "%s@%s", client_data->username, client_data->hostname); + char name[1024]; + snprintf(name, sizeof(name)-1, "%s@%s", + client_data->username, client_data->hostname); guac_protocol_send_name(socket, name); - /* If key specified, import */ - if (client_data->key_base64[0] != 0) { - - guac_client_log(client, GUAC_LOG_DEBUG, - "Attempting private key import (WITHOUT passphrase)"); - - /* Attempt to read key without passphrase */ - client_data->key = ssh_key_alloc(client_data->key_base64, - strlen(client_data->key_base64), ""); - - /* On failure, attempt with passphrase */ - if (client_data->key == NULL) { - - /* Log failure of initial attempt */ - guac_client_log(client, GUAC_LOG_DEBUG, - "Initial import failed: %s", ssh_key_error()); - - guac_client_log(client, GUAC_LOG_DEBUG, - "Re-attempting private key import (WITH passphrase)"); - - /* Prompt for passphrase if missing */ - if (client_data->key_passphrase[0] == 0) - guac_terminal_prompt(client_data->term, "Key passphrase: ", - client_data->key_passphrase, sizeof(client_data->key_passphrase), false); - - /* Import key with passphrase */ - client_data->key = ssh_key_alloc(client_data->key_base64, - strlen(client_data->key_base64), - client_data->key_passphrase); - - /* If still failing, give up */ - if (client_data->key == NULL) { - guac_client_abort(client, - GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED, - "Auth key import failed: %s", ssh_key_error()); - return NULL; - } - - } /* end decrypt key with passphrase */ - - /* Success */ - guac_client_log(client, GUAC_LOG_INFO, "Auth key successfully imported."); - - } /* end if key given */ - - /* Otherwise, get password if not provided */ - else if (client_data->password[0] == 0) - guac_terminal_prompt(client_data->term, "Password: ", - client_data->password, sizeof(client_data->password), false); - - /* Clear screen */ - guac_terminal_printf(client_data->term, "\x1B[H\x1B[J"); - /* Open SSH session */ - client_data->session = __guac_ssh_create_session(client, &socket_fd); + client_data->session = guac_common_ssh_create_session(client, + client_data->hostname, client_data->port, user); if (client_data->session == NULL) { - /* Already aborted within __guac_ssh_create_session() */ + /* Already aborted within guac_common_ssh_create_session() */ return NULL; } pthread_mutex_init(&client_data->term_channel_lock, NULL); /* Open channel for terminal */ - client_data->term_channel = libssh2_channel_open_session(client_data->session); + client_data->term_channel = + libssh2_channel_open_session(client_data->session->session); if (client_data->term_channel == NULL) { - guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, "Unable to open terminal channel."); + guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, + "Unable to open terminal channel."); return NULL; } @@ -393,14 +232,15 @@ void* ssh_client_thread(void* data) { /* Create SSH session specific for SFTP */ guac_client_log(client, GUAC_LOG_DEBUG, "Reconnecting for SFTP..."); - client_data->sftp_ssh_session = __guac_ssh_create_session(client, NULL); + client_data->sftp_ssh_session = guac_common_ssh_create_session(client, + client_data->hostname, client_data->port, user); if (client_data->sftp_ssh_session == NULL) { - /* Already aborted within __guac_ssh_create_session() */ + /* Already aborted within guac_common_ssh_create_session() */ return NULL; } /* Request SFTP */ - client_data->sftp_session = libssh2_sftp_init(client_data->sftp_ssh_session); + client_data->sftp_session = libssh2_sftp_init(client_data->sftp_ssh_session->session); if (client_data->sftp_session == NULL) { guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, "Unable to start SFTP session."); return NULL; @@ -439,10 +279,10 @@ void* ssh_client_thread(void* data) { } /* Set non-blocking */ - libssh2_session_set_blocking(client_data->session, 0); + libssh2_session_set_blocking(client_data->session->session, 0); /* While data available, write to terminal */ - bytes_read = 0; + int bytes_read = 0; for (;;) { /* Track total amount of data read */ @@ -491,13 +331,14 @@ void* ssh_client_thread(void* data) { struct timeval timeout; FD_ZERO(&fds); - FD_SET(socket_fd, &fds); + FD_SET(client_data->session->fd, &fds); /* Wait for one second */ timeout.tv_sec = 1; timeout.tv_usec = 0; - if (select(socket_fd+1, &fds, NULL, NULL, &timeout) < 0) + if (select(client_data->session->fd + 1, &fds, + NULL, NULL, &timeout) < 0) break; } @@ -508,7 +349,6 @@ void* ssh_client_thread(void* data) { pthread_join(input_thread, NULL); pthread_mutex_destroy(&client_data->term_channel_lock); - guac_common_ssh_uninit(); guac_client_log(client, GUAC_LOG_INFO, "SSH connection ended."); return NULL; diff --git a/src/protocols/ssh/ssh_key.c b/src/protocols/ssh/ssh_key.c deleted file mode 100644 index 4e532af8..00000000 --- a/src/protocols/ssh/ssh_key.c +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright (C) 2013 Glyptodon LLC - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "config.h" - -#include "ssh_buffer.h" -#include "ssh_key.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -ssh_key* ssh_key_alloc(char* data, int length, char* passphrase) { - - ssh_key* key; - BIO* key_bio; - - char* public_key; - char* pos; - - /* Create BIO for reading key from memory */ - key_bio = BIO_new_mem_buf(data, length); - - /* If RSA key, load RSA */ - if (length > sizeof(SSH_RSA_KEY_HEADER)-1 - && memcmp(SSH_RSA_KEY_HEADER, data, - sizeof(SSH_RSA_KEY_HEADER)-1) == 0) { - - RSA* rsa_key; - - /* Read key */ - rsa_key = PEM_read_bio_RSAPrivateKey(key_bio, NULL, NULL, passphrase); - if (rsa_key == NULL) - return NULL; - - /* Allocate key */ - key = malloc(sizeof(ssh_key)); - key->rsa = rsa_key; - - /* Set type */ - key->type = SSH_KEY_RSA; - - /* Allocate space for public key */ - public_key = malloc(4096); - pos = public_key; - - /* Derive public key */ - buffer_write_string(&pos, "ssh-rsa", sizeof("ssh-rsa")-1); - buffer_write_bignum(&pos, rsa_key->e); - buffer_write_bignum(&pos, rsa_key->n); - - /* Save public key to structure */ - key->public_key = public_key; - key->public_key_length = pos - public_key; - - } - - /* If DSA key, load DSA */ - else if (length > sizeof(SSH_DSA_KEY_HEADER)-1 - && memcmp(SSH_DSA_KEY_HEADER, data, - sizeof(SSH_DSA_KEY_HEADER)-1) == 0) { - - DSA* dsa_key; - - /* Read key */ - dsa_key = PEM_read_bio_DSAPrivateKey(key_bio, NULL, NULL, passphrase); - if (dsa_key == NULL) - return NULL; - - /* Allocate key */ - key = malloc(sizeof(ssh_key)); - key->dsa = dsa_key; - - /* Set type */ - key->type = SSH_KEY_DSA; - - /* Allocate space for public key */ - public_key = malloc(4096); - pos = public_key; - - /* Derive public key */ - buffer_write_string(&pos, "ssh-dss", sizeof("ssh-dss")-1); - buffer_write_bignum(&pos, dsa_key->p); - buffer_write_bignum(&pos, dsa_key->q); - buffer_write_bignum(&pos, dsa_key->g); - buffer_write_bignum(&pos, dsa_key->pub_key); - - /* Save public key to structure */ - key->public_key = public_key; - key->public_key_length = pos - public_key; - - } - - /* Otherwise, unsupported type */ - else { - BIO_free(key_bio); - return NULL; - } - - /* Copy private key to structure */ - key->private_key_length = length; - key->private_key = malloc(length); - memcpy(key->private_key, data, length); - - BIO_free(key_bio); - return key; - -} - -const char* ssh_key_error() { - - /* Return static error string */ - return ERR_reason_error_string(ERR_get_error()); - -} - -void ssh_key_free(ssh_key* key) { - - /* Free key-specific data */ - if (key->type == SSH_KEY_RSA) - RSA_free(key->rsa); - else if (key->type == SSH_KEY_DSA) - DSA_free(key->dsa); - - free(key->public_key); - free(key); -} - -int ssh_key_sign(ssh_key* key, const char* data, int length, unsigned char* sig) { - - const EVP_MD* md; - EVP_MD_CTX md_ctx; - - unsigned char digest[EVP_MAX_MD_SIZE]; - unsigned int dlen, len; - - /* Get SHA1 digest */ - if ((md = EVP_get_digestbynid(NID_sha1)) == NULL) - return -1; - - /* Digest data */ - EVP_DigestInit(&md_ctx, md); - EVP_DigestUpdate(&md_ctx, data, length); - EVP_DigestFinal(&md_ctx, digest, &dlen); - - /* Sign with key */ - switch (key->type) { - - case SSH_KEY_RSA: - if (RSA_sign(NID_sha1, digest, dlen, sig, &len, key->rsa) == 1) - return len; - - case SSH_KEY_DSA: { - - DSA_SIG* dsa_sig = DSA_do_sign(digest, dlen, key->dsa); - if (dsa_sig != NULL) { - - /* Compute size of each half of signature */ - int rlen = BN_num_bytes(dsa_sig->r); - int slen = BN_num_bytes(dsa_sig->s); - - /* Ensure each number is within the required size */ - if (rlen > DSA_SIG_NUMBER_SIZE || slen > DSA_SIG_NUMBER_SIZE) - return -1; - - /* Init to all zeroes */ - memset(sig, 0, DSA_SIG_SIZE); - - /* Add R at the end of the first block of the signature */ - BN_bn2bin(dsa_sig->r, sig + DSA_SIG_SIZE - - DSA_SIG_NUMBER_SIZE - rlen); - - /* Add S at the end of the second block of the signature */ - BN_bn2bin(dsa_sig->s, sig + DSA_SIG_SIZE - slen); - - /* Done */ - DSA_SIG_free(dsa_sig); - return DSA_SIG_SIZE; - - } - - } - - } - - return -1; - -} - diff --git a/src/protocols/ssh/ssh_key.h b/src/protocols/ssh/ssh_key.h deleted file mode 100644 index 1e7e7970..00000000 --- a/src/protocols/ssh/ssh_key.h +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2013 Glyptodon LLC - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - - -#ifndef _GUAC_SSH_KEY_H -#define _GUAC_SSH_KEY_H - -#include "config.h" - -#include - -/** - * The expected header of RSA private keys. - */ -#define SSH_RSA_KEY_HEADER "-----BEGIN RSA PRIVATE KEY-----" - -/** - * The expected header of DSA private keys. - */ -#define SSH_DSA_KEY_HEADER "-----BEGIN DSA PRIVATE KEY-----" - -/** - * The size of single number within a DSA signature, in bytes. - */ -#define DSA_SIG_NUMBER_SIZE 20 - -/** - * The size of a DSA signature, in bytes. - */ -#define DSA_SIG_SIZE DSA_SIG_NUMBER_SIZE*2 - -/** - * The type of an SSH key. - */ -typedef enum ssh_key_type { - - /** - * RSA key. - */ - SSH_KEY_RSA, - - /** - * DSA key. - */ - SSH_KEY_DSA - -} ssh_key_type; - -/** - * Abstraction of a key used for SSH authentication. - */ -typedef struct ssh_key { - - /** - * The type of this key. - */ - ssh_key_type type; - - /** - * Underlying RSA private key, if any. - */ - RSA* rsa; - - /** - * Underlying DSA private key, if any. - */ - DSA* dsa; - - /** - * The associated public key, encoded as necessary for SSH. - */ - char* public_key; - - /** - * The length of the public key, in bytes. - */ - int public_key_length; - - /** - * The private key, encoded as necessary for SSH. - */ - char* private_key; - - /** - * The length of the private key, in bytes. - */ - int private_key_length; - -} ssh_key; - -/** - * Allocates a new key containing the given private key data and specified - * passphrase. If unable to read the key, NULL is returned. - */ -ssh_key* ssh_key_alloc(char* data, int length, char* passphrase); - -/** - * Returns a statically-allocated string describing the most recent SSH key - * error. - * - * @return - * A statically-allocated string describing the most recent SSH key error. - */ -const char* ssh_key_error(); - -/** - * Frees all memory associated with the given key. - */ -void ssh_key_free(ssh_key* key); - -/** - * Signs the given data using the given key, returning the length of the - * signature in bytes, or a value less than zero on error. - */ -int ssh_key_sign(ssh_key* key, const char* data, int length, unsigned char* sig); - -#endif -