From 755c677304ad3a561dc8a3d9107c15c80d7a4916 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 1 Dec 2013 17:05:55 -0800 Subject: [PATCH] Add ssh_agent source from test. --- configure.ac | 28 +++++ src/protocols/ssh/Makefile.am | 6 + src/protocols/ssh/ssh_agent.c | 206 +++++++++++++++++++++++++++++++++ src/protocols/ssh/ssh_agent.h | 78 +++++++++++++ src/protocols/ssh/ssh_client.c | 3 +- 5 files changed, 320 insertions(+), 1 deletion(-) create mode 100644 src/protocols/ssh/ssh_agent.c create mode 100644 src/protocols/ssh/ssh_agent.h diff --git a/configure.ac b/configure.ac index 66feaa79..0d855055 100644 --- a/configure.ac +++ b/configure.ac @@ -558,6 +558,34 @@ AM_CONDITIONAL([ENABLE_SSH], [test "x${have_libssh2}" = "xyes" \ AC_SUBST(SSH_LIBS) +# +# Agent forwarding support within libssh2 +# + +if test "x${have_libssh2}" = "xyes" +then + + have_ssh_agent=yes + AC_CHECK_DECL([libssh2_channel_request_auth_agent], + [], [have_ssh_agent=no], + [[#include ]]) + + AM_CONDITIONAL([ENABLE_SSH_AGENT], [test "x${have_ssh_agent}" = "xyes"]) + + if test "x${have_ssh_agent}" = "xno" + then + AC_MSG_WARN([ + -------------------------------------------- + No agent forwarding found in libssh2. + Support for agent forwarding will not be built. + --------------------------------------------]) + else + AC_DEFINE([ENABLE_SSH_AGENT]) + fi + +fi + + AC_CONFIG_FILES([Makefile tests/Makefile src/libguac/Makefile diff --git a/src/protocols/ssh/Makefile.am b/src/protocols/ssh/Makefile.am index eefa4c55..952c109d 100644 --- a/src/protocols/ssh/Makefile.am +++ b/src/protocols/ssh/Makefile.am @@ -75,6 +75,12 @@ noinst_HEADERS = \ terminal_handlers.h \ types.h +# Add agent sources if enabled +if ENABLE_SSH_AGENT +libguac_client_ssh_la_SOURCES += ssh_agent.c +noinst_HEADERS += ssh_agent.h +endif + libguac_client_ssh_la_CFLAGS = -Werror -Wall -pedantic -Iinclude @PANGO_CFLAGS@ @PANGOCAIRO_CFLAGS@ @LIBGUAC_INCLUDE@ libguac_client_ssh_la_LIBADD = @LIBGUAC_LTLIB@ libguac_client_ssh_la_LDFLAGS = -version-info 0:0:0 @SSH_LIBS@ @SSL_LIBS@ @PTHREAD_LIBS@ @PANGO_LIBS@ @PANGOCAIRO_LIBS@ @CAIRO_LIBS@ diff --git a/src/protocols/ssh/ssh_agent.c b/src/protocols/ssh/ssh_agent.c new file mode 100644 index 00000000..075d8c00 --- /dev/null +++ b/src/protocols/ssh/ssh_agent.c @@ -0,0 +1,206 @@ + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include "client.h" +#include "ssh_agent.h" +#include "ssh_buffer.h" + +void ssh_auth_agent_sign(ssh_auth_agent* agent, char* data, int data_length) { + + LIBSSH2_CHANNEL* channel = agent->channel; + ssh_key* key = agent->identity; + + char sig[4096]; + int sig_len; + + char buffer[4096]; + char* pos = buffer; + + /* Sign with key */ + sig_len = ssh_key_sign(key, data, data_length, (u_char*) sig); + if (sig_len < 0) + return; + + buffer_write_uint32(&pos, 1+4 + 4+7 + 4+sig_len); + + buffer_write_byte(&pos, SSH2_AGENT_SIGN_RESPONSE); + buffer_write_uint32(&pos, 4+7 + 4+sig_len); + + /* Write key type */ + if (key->type == SSH_KEY_RSA) + buffer_write_string(&pos, "ssh-rsa", 7); + else if (key->type == SSH_KEY_DSA) + buffer_write_string(&pos, "ssh-dsa", 7); + else + return; + + /* Write signature */ + buffer_write_string(&pos, sig, sig_len); + + libssh2_channel_write(channel, buffer, pos-buffer); + libssh2_channel_flush(channel); + + usleep(10000); + +} + +void ssh_auth_agent_list_identities(ssh_auth_agent* auth_agent) { + + LIBSSH2_CHANNEL* channel = auth_agent->channel; + ssh_key* key = auth_agent->identity; + + char buffer[4096]; + char* pos = buffer; + + buffer_write_uint32(&pos, 1+4 + + key->public_key_length+4 + + sizeof("comment")+3); + + buffer_write_byte(&pos, SSH2_AGENT_IDENTITIES_ANSWER); + buffer_write_uint32(&pos, 1); + + buffer_write_string(&pos, key->public_key, key->public_key_length); + buffer_write_string(&pos, "comment", sizeof("comment")-1); + + libssh2_channel_write(channel, buffer, pos-buffer); + libssh2_channel_flush(channel); + + usleep(10000); + +} + +void ssh_auth_agent_handle_packet(ssh_auth_agent* auth_agent, uint8_t type, + char* data, int data_length) { + + switch (type) { + + /* List identities */ + case SSH2_AGENT_REQUEST_IDENTITIES: + ssh_auth_agent_list_identities(auth_agent); + break; + + /* Sign request */ + case SSH2_AGENT_SIGN_REQUEST: { + + char* pos = data; + + int key_blob_length; + int sign_data_length; + char* sign_data; + + /* Skip past key, read data, ignore flags */ + buffer_read_string(&pos, &key_blob_length); + sign_data = buffer_read_string(&pos, &sign_data_length); + + /* Sign given data */ + ssh_auth_agent_sign(auth_agent, sign_data, sign_data_length); + break; + } + + /* Otherwise, return failure */ + default: + libssh2_channel_write(auth_agent->channel, + UNSUPPORTED, sizeof(UNSUPPORTED)-1); + + } + +} + +void* ssh_auth_agent_read_thread(void* arg) { + + ssh_auth_agent* auth_agent = (ssh_auth_agent*) arg; + LIBSSH2_CHANNEL* channel = auth_agent->channel; + + int bytes_read; + char buffer[4096]; + int buffer_length = 0; + + /* Wait for channel to settle */ + usleep(10000); + + do { + + /* Read data into buffer */ + while ((bytes_read = libssh2_channel_read(channel, buffer+buffer_length, + sizeof(buffer)-buffer_length)) >= 0 + || bytes_read == LIBSSH2_ERROR_EAGAIN) { + + /* If re-read required, wait a bit and retry */ + if (bytes_read == LIBSSH2_ERROR_EAGAIN) { + usleep(10000); + continue; + } + + /* Update buffer length */ + buffer_length += bytes_read; + + /* Read length and type if given */ + if (buffer_length >= 5) { + + /* Read length */ + uint32_t length = + (((unsigned char*) buffer)[0] << 24) + | (((unsigned char*) buffer)[1] << 16) + | (((unsigned char*) buffer)[2] << 8) + | ((unsigned char*) buffer)[3]; + + /* Read type */ + uint8_t type = ((unsigned char*) buffer)[4]; + + /* If enough data read, call handler, shift data */ + if (buffer_length >= length+4) { + + ssh_auth_agent_handle_packet(auth_agent, type, + buffer+5, length-1); + + buffer_length -= length+4; + memmove(buffer, buffer+length+4, buffer_length); + + } + + } /* end if have length and type */ + + /* If EOF, stop now */ + if (libssh2_channel_eof(channel)) + break; + + } /* end packet fill */ + + } while (bytes_read >= 0 && !libssh2_channel_eof(channel)); + + /* Done */ + return NULL; + +} + +void ssh_auth_agent_callback(LIBSSH2_SESSION *session, + LIBSSH2_CHANNEL *channel, void **abstract) { + + /* Get client data */ + guac_client* client = (guac_client*) *abstract; + ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; + + /* Get base auth agent thread */ + pthread_t read_thread; + ssh_auth_agent* auth_agent = malloc(sizeof(ssh_auth_agent)); + + auth_agent->channel = channel; + auth_agent->identity = client_data->key; + + /* Create thread */ + pthread_create(&read_thread, NULL, ssh_auth_agent_read_thread, auth_agent); + +} + diff --git a/src/protocols/ssh/ssh_agent.h b/src/protocols/ssh/ssh_agent.h new file mode 100644 index 00000000..e6c0e4e9 --- /dev/null +++ b/src/protocols/ssh/ssh_agent.h @@ -0,0 +1,78 @@ + +#ifndef _GUAC_SSH_AGENT_H +#define _GUAC_SSH_AGENT_H + +#include "ssh_key.h" + +/** + * Packet type of an agent identity request. + */ +#define SSH2_AGENT_REQUEST_IDENTITIES 0x0B + +/** + * Packet type of an agent identity response. + */ +#define SSH2_AGENT_IDENTITIES_ANSWER 0x0C + +/** + * Packet type of an agent sign request. + */ +#define SSH2_AGENT_SIGN_REQUEST 0x0D + +/** + * Packet type of an agent sign response. + */ +#define SSH2_AGENT_SIGN_RESPONSE 0x0E + +/** + * The packet sent by the SSH agent when an operation is not supported. + */ +#define UNSUPPORTED "\x00\x00\x00\x0C\x05Unsupported" + +/** + * Data representing an SSH auth agent. + */ +typedef struct ssh_auth_agent { + + /** + * The SSH channel being used for SSH agent protocol. + */ + LIBSSH2_CHANNEL* channel; + + /** + * The single private key to use for authentication. + */ + ssh_key* identity; + +} ssh_auth_agent; + +/** + * Handler for an agent sign request. + */ +void ssh_auth_agent_sign(ssh_auth_agent* auth_agent, + char* data, int data_length); + +/** + * Handler for an agent identity request. + */ +void ssh_auth_agent_list_identities(ssh_auth_agent* auth_agent); + +/** + * Generic handler for all packets received over the auth agent channel. + */ +void ssh_auth_agent_handle_packet(ssh_auth_agent* auth_agent, + uint8_t type, char* data, int data_length); + +/** + * Auth agent channel thread. + */ +void* auth_agent_read_thread(void* arg); + +/** + * Libssh2 callback, invoked when the auth agent channel is opened. + */ +void ssh_auth_agent_callback(LIBSSH2_SESSION *session, + LIBSSH2_CHANNEL *channel, void **abstract); + +#endif + diff --git a/src/protocols/ssh/ssh_client.c b/src/protocols/ssh/ssh_client.c index 525d7ca9..6dd0d62d 100644 --- a/src/protocols/ssh/ssh_client.c +++ b/src/protocols/ssh/ssh_client.c @@ -232,7 +232,8 @@ static LIBSSH2_SESSION* __guac_ssh_create_session(guac_client* client) { } /* Open SSH session */ - LIBSSH2_SESSION* session = libssh2_session_init(); + LIBSSH2_SESSION* session = libssh2_session_init_ex(NULL, NULL, + NULL, client); if (session == NULL) { guac_client_log_error(client, "Session allocation failed"); return NULL;