Partial migration, including SFTP.

This commit is contained in:
Michael Jumper 2013-12-01 15:39:29 -08:00
parent 602571400d
commit d32018cf87
9 changed files with 283 additions and 112 deletions

View File

@ -52,6 +52,7 @@ libguac_client_ssh_la_SOURCES = \
ibar.c \
sftp.c \
ssh_client.c \
ssh_key.c \
terminal.c \
terminal_handlers.c
@ -65,9 +66,9 @@ noinst_HEADERS = \
display.h \
guac_handlers.h \
ibar.h \
libssh_compat.h \
sftp.h \
ssh_client.h \
ssh_key.h \
terminal.h \
terminal_handlers.h \
types.h

View File

@ -66,10 +66,8 @@ const char* GUAC_CLIENT_ARGS[] = {
"font-name",
"font-size",
"enable-sftp",
#ifdef ENABLE_SSH_PUBLIC_KEY
"private-key",
"passphrase",
#endif
NULL
};
@ -110,7 +108,6 @@ enum __SSH_ARGS_IDX {
*/
IDX_ENABLE_SFTP,
#ifdef ENABLE_SSH_PUBLIC_KEY
/**
* The private key to use for authentication, if any.
*/
@ -120,7 +117,6 @@ enum __SSH_ARGS_IDX {
* The passphrase required to decrypt the private key, if any.
*/
IDX_PASSPHRASE,
#endif
SSH_ARGS_COUNT
};
@ -149,12 +145,10 @@ int guac_client_init(guac_client* client, int argc, char** argv) {
strcpy(client_data->username, argv[IDX_USERNAME]);
strcpy(client_data->password, argv[IDX_PASSWORD]);
#ifdef ENABLE_SSH_PUBLIC_KEY
/* 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]);
#endif
/* Read font name */
if (argv[IDX_FONT_NAME][0] != 0)

View File

@ -40,12 +40,13 @@
#define _SSH_GUAC_CLIENT_H
#include <pthread.h>
#include <libssh/libssh.h>
#include <libssh/sftp.h>
#include <libssh2.h>
#include <libssh2_sftp.h>
#include "terminal.h"
#include "cursor.h"
#include "sftp.h"
#include "ssh_key.h"
/**
* SSH-specific client data.
@ -72,7 +73,6 @@ typedef struct ssh_guac_client_data {
*/
char password[1024];
#ifdef ENABLE_SSH_PUBLIC_KEY
/**
* The private key, encoded as base64.
*/
@ -86,8 +86,7 @@ typedef struct ssh_guac_client_data {
/**
* The private key to use for authentication, if any.
*/
ssh_key key;
#endif
ssh_key* key;
/**
* The name of the font to use for display rendering.
@ -112,17 +111,17 @@ typedef struct ssh_guac_client_data {
/**
* SSH session, used by the SSH client thread.
*/
ssh_session session;
LIBSSH2_SESSION* session;
/**
* The distinct SSH session used for SFTP.
*/
ssh_session sftp_ssh_session;
LIBSSH2_SESSION* sftp_ssh_session;
/**
* SFTP session, used for file transfers.
*/
sftp_session sftp_session;
LIBSSH2_SFTP* sftp_session;
/**
* The path files will be sent to.
@ -132,7 +131,7 @@ typedef struct ssh_guac_client_data {
/**
* SSH terminal channel, used by the SSH client thread.
*/
ssh_channel term_channel;
LIBSSH2_CHANNEL* term_channel;
/**
* The terminal which will render all output from the SSH client.

View File

@ -51,9 +51,7 @@
#include <guacamole/client.h>
#include <guacamole/error.h>
#include <libssh/libssh.h>
#include <libssh/sftp.h>
#include "libssh_compat.h"
#include <libssh2.h>
#include "guac_handlers.h"
#include "client.h"
@ -399,7 +397,7 @@ int ssh_guac_client_size_handler(guac_client* client, int width, int height) {
/* Update SSH pty size if connected */
if (guac_client_data->term_channel != NULL)
channel_change_pty_size(guac_client_data->term_channel,
libssh2_channel_request_pty_size(guac_client_data->term_channel,
terminal->term_width, terminal->term_height);
/* Reset scroll region */
@ -422,8 +420,8 @@ int ssh_guac_client_free_handler(guac_client* client) {
/* Close SSH channel */
if (guac_client_data->term_channel != NULL) {
ssh_channel_close(guac_client_data->term_channel);
ssh_channel_send_eof(guac_client_data->term_channel);
libssh2_channel_send_eof(guac_client_data->term_channel);
libssh2_channel_close(guac_client_data->term_channel);
}
/* Free terminal */
@ -431,23 +429,23 @@ int ssh_guac_client_free_handler(guac_client* client) {
pthread_join(guac_client_data->client_thread, NULL);
/* Free channels */
ssh_channel_free(guac_client_data->term_channel);
libssh2_channel_free(guac_client_data->term_channel);
/* Clean up SFTP */
if (guac_client_data->sftp_session)
sftp_free(guac_client_data->sftp_session);
libssh2_sftp_shutdown(guac_client_data->sftp_session);
if (guac_client_data->sftp_ssh_session)
ssh_free(guac_client_data->sftp_ssh_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);
}
/* Free session */
ssh_free(guac_client_data->session);
libssh2_session_free(guac_client_data->session);
#ifdef ENABLE_SSH_PUBLIC_KEY
/* Free auth key */
if (guac_client_data->key != NULL)
ssh_key_free(guac_client_data->key);
#endif
/* Free clipboard data */
free(guac_client_data->clipboard_data);

View File

@ -1,57 +0,0 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is libguac-client-ssh.
*
* The Initial Developer of the Original Code is
* Michael Jumper.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef _SSH_GUAC_LIBSSH_COMPAT_H
#define _SSH_GUAC_LIBSSH_COMPAT_H
/* Define ssh_channel_close() if undefined */
#ifndef HAVE_SSH_CHANNEL_CLOSE
#define ssh_channel_close channel_close
#endif
/* Define ssh_channel_send_eof() if undefined */
#ifndef HAVE_SSH_CHANNEL_SEND_EOF
#define ssh_channel_send_eof channel_send_eof
#endif
/* Define ssh_channel_free() if undefined */
#ifndef HAVE_SSH_CHANNEL_FREE
#define ssh_channel_free channel_free
#endif
#endif

View File

@ -43,8 +43,7 @@
#include <string.h>
#include <fcntl.h>
#include <libssh/libssh.h>
#include <libssh/sftp.h>
#include <libssh2.h>
#include <guacamole/client.h>
#include <guacamole/protocol.h>
@ -85,7 +84,7 @@ int guac_sftp_file_handler(guac_client* client, guac_stream* stream,
ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
char fullpath[GUAC_SFTP_MAX_PATH];
sftp_file file;
LIBSSH2_SFTP_HANDLE* file;
int i;
/* Ensure filename is a valid filename and not a path */
@ -129,8 +128,9 @@ int guac_sftp_file_handler(guac_client* client, guac_stream* stream,
fullpath[i] = '\0';
/* Open file via SFTP */
file = sftp_open(client_data->sftp_session, fullpath,
O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
file = libssh2_sftp_open(client_data->sftp_session, fullpath,
LIBSSH2_FXF_WRITE | LIBSSH2_FXF_CREAT | LIBSSH2_FXF_TRUNC,
S_IRUSR | S_IWUSR);
/* Inform of status */
if (file != NULL) {
@ -140,7 +140,7 @@ int guac_sftp_file_handler(guac_client* client, guac_stream* stream,
}
else {
guac_client_log_error(client, "Unable to open file \"%s\": %s",
fullpath, ssh_get_error(client_data->sftp_ssh_session));
fullpath, libssh2_sftp_last_error(client_data->sftp_session));
guac_protocol_send_ack(client->socket, stream, "SFTP: Open failed",
GUAC_PROTOCOL_STATUS_INTERNAL_ERROR);
guac_socket_flush(client->socket);
@ -157,10 +157,10 @@ int guac_sftp_blob_handler(guac_client* client, guac_stream* stream,
/* Pull file from stream */
ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
sftp_file file = (sftp_file) stream->data;
LIBSSH2_SFTP_HANDLE* file = (LIBSSH2_SFTP_HANDLE*) stream->data;
/* Attempt write */
if (sftp_write(file, data, length) == length) {
if (libssh2_sftp_write(file, data, length) == length) {
guac_protocol_send_ack(client->socket, stream, "SFTP: OK",
GUAC_PROTOCOL_STATUS_SUCCESS);
guac_socket_flush(client->socket);
@ -169,7 +169,7 @@ int guac_sftp_blob_handler(guac_client* client, guac_stream* stream,
/* Inform of any errors */
else {
guac_client_log_error(client, "Unable to write to file: %s",
ssh_get_error(client_data->sftp_ssh_session));
libssh2_sftp_last_error(client_data->sftp_session));
guac_protocol_send_ack(client->socket, stream, "SFTP: Write failed",
GUAC_PROTOCOL_STATUS_INTERNAL_ERROR);
guac_socket_flush(client->socket);
@ -182,10 +182,10 @@ int guac_sftp_blob_handler(guac_client* client, guac_stream* stream,
int guac_sftp_end_handler(guac_client* client, guac_stream* stream) {
/* Pull file from stream */
sftp_file file = (sftp_file) stream->data;
LIBSSH2_SFTP_HANDLE* file = (LIBSSH2_SFTP_HANDLE*) stream->data;
/* Attempt to close file */
if (sftp_close(file) == SSH_OK) {
if (libssh2_sftp_close(file) == 0) {
guac_protocol_send_ack(client->socket, stream, "SFTP: OK",
GUAC_PROTOCOL_STATUS_SUCCESS);
guac_socket_flush(client->socket);
@ -205,14 +205,14 @@ int guac_sftp_ack_handler(guac_client* client, guac_stream* stream,
char* message, guac_protocol_status status) {
ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
sftp_file file = (sftp_file) stream->data;
LIBSSH2_SFTP_HANDLE* file = (LIBSSH2_SFTP_HANDLE*) stream->data;
/* If successful, read data */
if (status == GUAC_PROTOCOL_STATUS_SUCCESS) {
/* Attempt read into buffer */
char buffer[4096];
int bytes_read = sftp_read(file, buffer, sizeof(buffer));
int bytes_read = libssh2_sftp_read(file, buffer, sizeof(buffer));
/* If bytes read, send as blob */
if (bytes_read > 0)
@ -228,7 +228,7 @@ int guac_sftp_ack_handler(guac_client* client, guac_stream* stream,
/* Otherwise, fail stream */
else {
guac_client_log_error(client, "Error reading file: %s",
ssh_get_error(client_data->sftp_ssh_session));
libssh2_sftp_last_error(client_data->sftp_session));
guac_protocol_send_end(client->socket, stream);
guac_client_free_stream(client, stream);
}
@ -249,13 +249,15 @@ guac_stream* guac_sftp_download_file(guac_client* client,
ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
guac_stream* stream;
sftp_file file;
LIBSSH2_SFTP_HANDLE* file;
/* Attempt to open file for reading */
file = sftp_open(client_data->sftp_session, filename, O_RDONLY, 0);
file = libssh2_sftp_open(client_data->sftp_session, filename,
LIBSSH2_FXF_READ, 0);
if (file == NULL) {
guac_client_log_error(client, "Unable to read file \"%s\": %s",
filename, ssh_get_error(client_data->sftp_ssh_session));
filename,
libssh2_sftp_last_error(client_data->sftp_session));
return NULL;
}

View File

@ -44,8 +44,7 @@
#include <guacamole/protocol.h>
#include <guacamole/socket.h>
#include <libssh/libssh.h>
#include <libssh/sftp.h>
#include <libssh2.h>
#include "client.h"
#include "common.h"
@ -153,7 +152,6 @@ static ssh_session __guac_ssh_create_session(guac_client* client) {
return NULL;
}
#ifdef ENABLE_SSH_PUBLIC_KEY
/* Authenticate with key if available */
if (client_data->key != NULL) {
if (ssh_userauth_publickey(session, NULL, client_data->key)
@ -166,7 +164,6 @@ static ssh_session __guac_ssh_create_session(guac_client* client) {
return NULL;
}
}
#endif
/* Authenticate with password */
if (ssh_userauth_password(session, NULL, client_data->password)
@ -205,7 +202,6 @@ void* ssh_client_thread(void* data) {
snprintf(name, sizeof(name)-1, "%s@%s", client_data->username, client_data->hostname);
guac_protocol_send_name(socket, name);
#ifdef ENABLE_SSH_PUBLIC_KEY
/* If key specified, import */
if (client_data->key_base64[0] != 0) {
@ -239,10 +235,6 @@ void* ssh_client_thread(void* data) {
/* Otherwise, get password if not provided */
else if (client_data->password[0] == 0) {
#else
/* Get password if not provided */
if (client_data->password[0] == 0) {
#endif
if (prompt(client, "Password: ", client_data->password,
sizeof(client_data->password), false) == NULL)
return NULL;

144
src/protocols/ssh/ssh_key.c Normal file
View File

@ -0,0 +1,144 @@
#include <string.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include "ssh_buffer.h"
#include "ssh_key.h"
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-dsa", sizeof("ssh-dsa")-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
return NULL;
/* Copy private key to structure */
key->private_key_length = length;
key->private_key = malloc(length);
memcpy(key->private_key, data, length);
return key;
}
void ssh_key_free(ssh_key* key) {
free(key->public_key);
free(key);
}
int ssh_key_sign(ssh_key* key, const char* data, int length, u_char* sig) {
const EVP_MD* md;
EVP_MD_CTX md_ctx;
u_char digest[EVP_MAX_MD_SIZE];
u_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:
if (DSA_sign(NID_sha1, digest, dlen, sig, &len, key->dsa) == 1)
return len;
}
return -1;
}

View File

@ -0,0 +1,98 @@
#ifndef _GUAC_SSH_KEY_H
#define _GUAC_SSH_KEY_H
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/dsa.h>
#include <openssl/rsa.h>
/**
* 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 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);
/**
* 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, u_char* sig);
#endif