Merge pull request #123 from glyptodon/fake-merge-screen-sharing-008-ssh
GUAC-1389: Add screen sharing support to SSH.
This commit is contained in:
commit
3f51d3da02
@ -54,7 +54,7 @@ SUBDIRS += src/protocols/rdp
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
if ENABLE_SSH
|
if ENABLE_SSH
|
||||||
#SUBDIRS += src/protocols/ssh
|
SUBDIRS += src/protocols/ssh
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if ENABLE_TELNET
|
if ENABLE_TELNET
|
||||||
|
@ -344,19 +344,48 @@ static int guac_common_ssh_authenticate(guac_common_ssh_session* common_session)
|
|||||||
/* Authenticate with password, if provided */
|
/* Authenticate with password, if provided */
|
||||||
else if (password != NULL) {
|
else if (password != NULL) {
|
||||||
|
|
||||||
/* Authenticate with password */
|
/* Check if password auth is supported on the server */
|
||||||
if (strstr(user_authlist, "password") != NULL) {
|
if (strstr(user_authlist, "password") != NULL) {
|
||||||
guac_client_log(client, GUAC_LOG_DEBUG,
|
|
||||||
"Using password authentication method");
|
/* Attempt password authentication */
|
||||||
return libssh2_userauth_password(session, username, password);
|
if (libssh2_userauth_password(session, username, password)) {
|
||||||
|
|
||||||
|
/* Abort on failure */
|
||||||
|
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 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Authenticate with password via keyboard-interactive auth */
|
/* Password authentication succeeded */
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if keyboard-interactive auth is supported on the server */
|
||||||
if (strstr(user_authlist, "keyboard-interactive") != NULL) {
|
if (strstr(user_authlist, "keyboard-interactive") != NULL) {
|
||||||
guac_client_log(client, GUAC_LOG_DEBUG,
|
|
||||||
"Using keyboard-interactive authentication method");
|
/* Attempt keyboard-interactive auth using provided password */
|
||||||
return libssh2_userauth_keyboard_interactive(session, username,
|
if (libssh2_userauth_keyboard_interactive(session, username,
|
||||||
&guac_common_ssh_kbd_callback);
|
&guac_common_ssh_kbd_callback)) {
|
||||||
|
|
||||||
|
/* Abort on failure */
|
||||||
|
char* error_message;
|
||||||
|
libssh2_session_last_error(session, &error_message, NULL, 0);
|
||||||
|
guac_client_abort(client,
|
||||||
|
GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED,
|
||||||
|
"Keyboard-interactive authentication failed: %s",
|
||||||
|
error_message);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Keyboard-interactive authentication succeeded */
|
||||||
|
return 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* No known authentication types available */
|
/* No known authentication types available */
|
||||||
|
@ -28,16 +28,20 @@ lib_LTLIBRARIES = libguac-client-ssh.la
|
|||||||
libguac_client_ssh_la_SOURCES = \
|
libguac_client_ssh_la_SOURCES = \
|
||||||
client.c \
|
client.c \
|
||||||
clipboard.c \
|
clipboard.c \
|
||||||
guac_handlers.c \
|
input.c \
|
||||||
|
settings.c \
|
||||||
sftp.c \
|
sftp.c \
|
||||||
ssh_client.c
|
ssh.c \
|
||||||
|
user.c
|
||||||
|
|
||||||
noinst_HEADERS = \
|
noinst_HEADERS = \
|
||||||
client.h \
|
client.h \
|
||||||
clipboard.h \
|
clipboard.h \
|
||||||
guac_handlers.h \
|
input.h \
|
||||||
|
settings.h \
|
||||||
sftp.h \
|
sftp.h \
|
||||||
ssh_client.h
|
ssh.h \
|
||||||
|
user.h
|
||||||
|
|
||||||
# Add agent sources if enabled
|
# Add agent sources if enabled
|
||||||
if ENABLE_SSH_AGENT
|
if ENABLE_SSH_AGENT
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2013 Glyptodon LLC
|
* Copyright (C) 2016 Glyptodon LLC
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -23,246 +23,37 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
#include "clipboard.h"
|
#include "guac_sftp.h"
|
||||||
#include "guac_handlers.h"
|
#include "ssh.h"
|
||||||
#include "ssh_client.h"
|
|
||||||
#include "terminal.h"
|
#include "terminal.h"
|
||||||
|
#include "user.h"
|
||||||
|
|
||||||
#include <langinfo.h>
|
#include <langinfo.h>
|
||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
#include <pthread.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <guacamole/client.h>
|
#include <guacamole/client.h>
|
||||||
#include <guacamole/protocol.h>
|
|
||||||
#include <guacamole/socket.h>
|
|
||||||
|
|
||||||
#define GUAC_SSH_DEFAULT_FONT_NAME "monospace"
|
int guac_client_init(guac_client* client) {
|
||||||
#define GUAC_SSH_DEFAULT_FONT_SIZE 12
|
|
||||||
#define GUAC_SSH_DEFAULT_PORT "22"
|
|
||||||
|
|
||||||
/* Client plugin arguments */
|
/* Set client args */
|
||||||
const char* GUAC_CLIENT_ARGS[] = {
|
client->args = GUAC_SSH_CLIENT_ARGS;
|
||||||
"hostname",
|
|
||||||
"port",
|
|
||||||
"username",
|
|
||||||
"password",
|
|
||||||
"font-name",
|
|
||||||
"font-size",
|
|
||||||
"enable-sftp",
|
|
||||||
"private-key",
|
|
||||||
"passphrase",
|
|
||||||
#ifdef ENABLE_SSH_AGENT
|
|
||||||
"enable-agent",
|
|
||||||
#endif
|
|
||||||
"color-scheme",
|
|
||||||
"command",
|
|
||||||
"typescript-path",
|
|
||||||
"typescript-name",
|
|
||||||
"create-typescript-path",
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
enum __SSH_ARGS_IDX {
|
/* Allocate client instance data */
|
||||||
|
guac_ssh_client* ssh_client = calloc(1, sizeof(guac_ssh_client));
|
||||||
|
client->data = ssh_client;
|
||||||
|
|
||||||
/**
|
/* Set handlers */
|
||||||
* The hostname to connect to. Required.
|
client->join_handler = guac_ssh_user_join_handler;
|
||||||
*/
|
client->free_handler = guac_ssh_client_free_handler;
|
||||||
IDX_HOSTNAME,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The port to connect to. Optional.
|
|
||||||
*/
|
|
||||||
IDX_PORT,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The name of the user to login as. Optional.
|
|
||||||
*/
|
|
||||||
IDX_USERNAME,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The password to use when logging in. Optional.
|
|
||||||
*/
|
|
||||||
IDX_PASSWORD,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The name of the font to use within the terminal.
|
|
||||||
*/
|
|
||||||
IDX_FONT_NAME,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The size of the font to use within the terminal, in points.
|
|
||||||
*/
|
|
||||||
IDX_FONT_SIZE,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether SFTP should be enabled.
|
|
||||||
*/
|
|
||||||
IDX_ENABLE_SFTP,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The private key to use for authentication, if any.
|
|
||||||
*/
|
|
||||||
IDX_PRIVATE_KEY,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The passphrase required to decrypt the private key, if any.
|
|
||||||
*/
|
|
||||||
IDX_PASSPHRASE,
|
|
||||||
|
|
||||||
#ifdef ENABLE_SSH_AGENT
|
|
||||||
/**
|
|
||||||
* Whether SSH agent forwarding support should be enabled.
|
|
||||||
*/
|
|
||||||
IDX_ENABLE_AGENT,
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The name of the color scheme to use. Currently valid color schemes are:
|
|
||||||
* "black-white", "white-black", "gray-black", and "green-black", each
|
|
||||||
* following the "foreground-background" pattern. By default, this will be
|
|
||||||
* "gray-black".
|
|
||||||
*/
|
|
||||||
IDX_COLOR_SCHEME,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The command to run instead if the default shell. If omitted, a normal
|
|
||||||
* shell session will be created.
|
|
||||||
*/
|
|
||||||
IDX_COMMAND,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The full absolute path to the directory in which typescripts should be
|
|
||||||
* written.
|
|
||||||
*/
|
|
||||||
IDX_TYPESCRIPT_PATH,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The name that should be given to typescripts which are written in the
|
|
||||||
* given path. Each typescript will consist of two files: "NAME" and
|
|
||||||
* "NAME.timing".
|
|
||||||
*/
|
|
||||||
IDX_TYPESCRIPT_NAME,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether the specified typescript path should automatically be created
|
|
||||||
* if it does not yet exist.
|
|
||||||
*/
|
|
||||||
IDX_CREATE_TYPESCRIPT_PATH,
|
|
||||||
|
|
||||||
SSH_ARGS_COUNT
|
|
||||||
};
|
|
||||||
|
|
||||||
int guac_client_init(guac_client* client, int argc, char** argv) {
|
|
||||||
|
|
||||||
guac_socket* socket = client->socket;
|
|
||||||
|
|
||||||
ssh_guac_client_data* client_data = calloc(1, sizeof(ssh_guac_client_data));
|
|
||||||
|
|
||||||
/* Init client data */
|
|
||||||
client->data = client_data;
|
|
||||||
|
|
||||||
if (argc != SSH_ARGS_COUNT) {
|
|
||||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Wrong number of arguments");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set locale and warn if not UTF-8 */
|
/* Set locale and warn if not UTF-8 */
|
||||||
setlocale(LC_CTYPE, "");
|
setlocale(LC_CTYPE, "");
|
||||||
if (strcmp(nl_langinfo(CODESET), "UTF-8") != 0)
|
if (strcmp(nl_langinfo(CODESET), "UTF-8") != 0) {
|
||||||
guac_client_log(client, GUAC_LOG_INFO, "Current locale does not use UTF-8. Some characters may not render correctly.");
|
guac_client_log(client, GUAC_LOG_INFO,
|
||||||
|
"Current locale does not use UTF-8. Some characters may "
|
||||||
/* Read parameters */
|
"not render correctly.");
|
||||||
strcpy(client_data->hostname, argv[IDX_HOSTNAME]);
|
|
||||||
strcpy(client_data->username, argv[IDX_USERNAME]);
|
|
||||||
strcpy(client_data->password, argv[IDX_PASSWORD]);
|
|
||||||
|
|
||||||
/* Init public key auth information */
|
|
||||||
strcpy(client_data->key_base64, argv[IDX_PRIVATE_KEY]);
|
|
||||||
strcpy(client_data->key_passphrase, argv[IDX_PASSPHRASE]);
|
|
||||||
|
|
||||||
/* Read font name */
|
|
||||||
if (argv[IDX_FONT_NAME][0] != 0)
|
|
||||||
strcpy(client_data->font_name, argv[IDX_FONT_NAME]);
|
|
||||||
else
|
|
||||||
strcpy(client_data->font_name, GUAC_SSH_DEFAULT_FONT_NAME );
|
|
||||||
|
|
||||||
/* Read font size */
|
|
||||||
if (argv[IDX_FONT_SIZE][0] != 0)
|
|
||||||
client_data->font_size = atoi(argv[IDX_FONT_SIZE]);
|
|
||||||
else
|
|
||||||
client_data->font_size = GUAC_SSH_DEFAULT_FONT_SIZE;
|
|
||||||
|
|
||||||
/* Parse SFTP enable */
|
|
||||||
client_data->enable_sftp = strcmp(argv[IDX_ENABLE_SFTP], "true") == 0;
|
|
||||||
|
|
||||||
#ifdef ENABLE_SSH_AGENT
|
|
||||||
client_data->enable_agent = strcmp(argv[IDX_ENABLE_AGENT], "true") == 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Read port */
|
|
||||||
if (argv[IDX_PORT][0] != 0)
|
|
||||||
strcpy(client_data->port, argv[IDX_PORT]);
|
|
||||||
else
|
|
||||||
strcpy(client_data->port, GUAC_SSH_DEFAULT_PORT);
|
|
||||||
|
|
||||||
/* Read command, if any */
|
|
||||||
if (argv[IDX_COMMAND][0] != 0)
|
|
||||||
client_data->command = strdup(argv[IDX_COMMAND]);
|
|
||||||
|
|
||||||
/* Create terminal */
|
|
||||||
client_data->term = guac_terminal_create(client,
|
|
||||||
client_data->font_name, client_data->font_size,
|
|
||||||
client->info.optimal_resolution,
|
|
||||||
client->info.optimal_width, client->info.optimal_height,
|
|
||||||
argv[IDX_COLOR_SCHEME]);
|
|
||||||
|
|
||||||
/* Fail if terminal init failed */
|
|
||||||
if (client_data->term == NULL) {
|
|
||||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Terminal initialization failed");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set up typescript, if requested */
|
|
||||||
const char* typescript_path = argv[IDX_TYPESCRIPT_PATH];
|
|
||||||
if (typescript_path[0] != 0) {
|
|
||||||
|
|
||||||
/* Default to "typescript" if no name provided */
|
|
||||||
const char* typescript_name = argv[IDX_TYPESCRIPT_NAME];
|
|
||||||
if (typescript_name[0] == 0)
|
|
||||||
typescript_name = "typescript";
|
|
||||||
|
|
||||||
/* Parse path creation flag */
|
|
||||||
int create_path =
|
|
||||||
strcmp(argv[IDX_CREATE_TYPESCRIPT_PATH], "true") == 0;
|
|
||||||
|
|
||||||
/* Create typescript */
|
|
||||||
guac_terminal_create_typescript(client_data->term, typescript_path,
|
|
||||||
typescript_name, create_path);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ensure main socket is threadsafe */
|
|
||||||
guac_socket_require_threadsafe(socket);
|
|
||||||
|
|
||||||
/* Send initial name */
|
|
||||||
guac_protocol_send_name(socket, client_data->hostname);
|
|
||||||
|
|
||||||
guac_socket_flush(socket);
|
|
||||||
|
|
||||||
/* Set basic handlers */
|
|
||||||
client->handle_messages = ssh_guac_client_handle_messages;
|
|
||||||
client->key_handler = ssh_guac_client_key_handler;
|
|
||||||
client->mouse_handler = ssh_guac_client_mouse_handler;
|
|
||||||
client->size_handler = ssh_guac_client_size_handler;
|
|
||||||
client->free_handler = ssh_guac_client_free_handler;
|
|
||||||
client->clipboard_handler = guac_ssh_clipboard_handler;
|
|
||||||
|
|
||||||
/* Start client thread */
|
|
||||||
if (pthread_create(&(client_data->client_thread), NULL, ssh_client_thread, (void*) client)) {
|
|
||||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Unable to start SSH client thread");
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Success */
|
/* Success */
|
||||||
@ -270,3 +61,48 @@ int guac_client_init(guac_client* client, int argc, char** argv) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int guac_ssh_client_free_handler(guac_client* client) {
|
||||||
|
|
||||||
|
guac_ssh_client* ssh_client = (guac_ssh_client*) client->data;
|
||||||
|
|
||||||
|
/* Close SSH channel */
|
||||||
|
if (ssh_client->term_channel != NULL) {
|
||||||
|
libssh2_channel_send_eof(ssh_client->term_channel);
|
||||||
|
libssh2_channel_close(ssh_client->term_channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free terminal (which may still be using term_channel) */
|
||||||
|
if (ssh_client->term != NULL) {
|
||||||
|
guac_terminal_free(ssh_client->term);
|
||||||
|
pthread_join(ssh_client->client_thread, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free terminal channel now that the terminal is finished */
|
||||||
|
if (ssh_client->term_channel != NULL)
|
||||||
|
libssh2_channel_free(ssh_client->term_channel);
|
||||||
|
|
||||||
|
/* Clean up the SFTP filesystem object and session */
|
||||||
|
if (ssh_client->sftp_filesystem) {
|
||||||
|
guac_common_ssh_destroy_sftp_filesystem(ssh_client->sftp_filesystem);
|
||||||
|
guac_common_ssh_destroy_session(ssh_client->sftp_session);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free interactive SSH session */
|
||||||
|
if (ssh_client->session != NULL)
|
||||||
|
guac_common_ssh_destroy_session(ssh_client->session);
|
||||||
|
|
||||||
|
/* Free SSH client credentials */
|
||||||
|
if (ssh_client->user != NULL)
|
||||||
|
guac_common_ssh_destroy_user(ssh_client->user);
|
||||||
|
|
||||||
|
/* Free parsed settings */
|
||||||
|
if (ssh_client->settings != NULL)
|
||||||
|
guac_ssh_settings_free(ssh_client->settings);
|
||||||
|
|
||||||
|
/* Free client structure */
|
||||||
|
free(ssh_client);
|
||||||
|
|
||||||
|
guac_common_ssh_uninit();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2013 Glyptodon LLC
|
* Copyright (C) 2016 Glyptodon LLC
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -20,138 +20,17 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef GUAC_SSH_CLIENT_H
|
||||||
|
#define GUAC_SSH_CLIENT_H
|
||||||
|
|
||||||
#ifndef _SSH_GUAC_CLIENT_H
|
#include <guacamole/client.h>
|
||||||
#define _SSH_GUAC_CLIENT_H
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include "guac_ssh.h"
|
|
||||||
#include "guac_ssh_user.h"
|
|
||||||
#include "sftp.h"
|
|
||||||
#include "terminal.h"
|
|
||||||
|
|
||||||
#include <libssh2.h>
|
|
||||||
#include <libssh2_sftp.h>
|
|
||||||
|
|
||||||
#include <guacamole/object.h>
|
|
||||||
|
|
||||||
#ifdef ENABLE_SSH_AGENT
|
|
||||||
#include "ssh_agent.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SSH-specific client data.
|
* Handler which is invoked when the SSH client needs to be disconnected (if
|
||||||
|
* connected) and freed. This can happen if initialization fails, or all users
|
||||||
|
* have left the connection.
|
||||||
*/
|
*/
|
||||||
typedef struct ssh_guac_client_data {
|
guac_client_free_handler guac_ssh_client_free_handler;
|
||||||
|
|
||||||
/**
|
|
||||||
* The hostname of the SSH server to connect to.
|
|
||||||
*/
|
|
||||||
char hostname[1024];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The port of the SSH server to connect to.
|
|
||||||
*/
|
|
||||||
char port[64];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The name of the user to login as.
|
|
||||||
*/
|
|
||||||
char username[1024];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The password to give when authenticating.
|
|
||||||
*/
|
|
||||||
char password[1024];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The private key, encoded as base64.
|
|
||||||
*/
|
|
||||||
char key_base64[4096];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The password to use to decrypt the given private key.
|
|
||||||
*/
|
|
||||||
char key_passphrase[1024];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The command to run instead of the default shell. If a normal shell
|
|
||||||
* session is desired, this will be NULL.
|
|
||||||
*/
|
|
||||||
char* command;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The name of the font to use for display rendering.
|
|
||||||
*/
|
|
||||||
char font_name[1024];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The size of the font to use, in points.
|
|
||||||
*/
|
|
||||||
int font_size;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether SFTP is enabled.
|
|
||||||
*/
|
|
||||||
bool enable_sftp;
|
|
||||||
|
|
||||||
#ifdef ENABLE_SSH_AGENT
|
|
||||||
/**
|
|
||||||
* Whether the SSH agent is enabled.
|
|
||||||
*/
|
|
||||||
bool enable_agent;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The current agent, if any.
|
|
||||||
*/
|
|
||||||
ssh_auth_agent* auth_agent;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The SSH client thread.
|
|
||||||
*/
|
|
||||||
pthread_t client_thread;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The user and credentials to use for all SSH sessions.
|
|
||||||
*/
|
|
||||||
guac_common_ssh_user* user;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SSH session, used by the SSH client thread.
|
|
||||||
*/
|
|
||||||
guac_common_ssh_session* session;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SFTP session, used by the SFTP client/filesystem.
|
|
||||||
*/
|
|
||||||
guac_common_ssh_session* sftp_session;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The filesystem object exposed for the SFTP session.
|
|
||||||
*/
|
|
||||||
guac_object* sftp_filesystem;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SSH terminal channel, used by the SSH client thread.
|
|
||||||
*/
|
|
||||||
LIBSSH2_CHANNEL* term_channel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lock dictating access to the SSH terminal channel.
|
|
||||||
*/
|
|
||||||
pthread_mutex_t term_channel_lock;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The terminal which will render all output from the SSH client.
|
|
||||||
*/
|
|
||||||
guac_terminal* term;
|
|
||||||
|
|
||||||
} ssh_guac_client_data;
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -21,19 +21,21 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "client.h"
|
|
||||||
#include "clipboard.h"
|
#include "clipboard.h"
|
||||||
|
#include "ssh.h"
|
||||||
#include "terminal.h"
|
#include "terminal.h"
|
||||||
|
|
||||||
#include <guacamole/client.h>
|
#include <guacamole/client.h>
|
||||||
#include <guacamole/stream.h>
|
#include <guacamole/stream.h>
|
||||||
|
#include <guacamole/user.h>
|
||||||
|
|
||||||
int guac_ssh_clipboard_handler(guac_client* client, guac_stream* stream,
|
int guac_ssh_clipboard_handler(guac_user* user, guac_stream* stream,
|
||||||
char* mimetype) {
|
char* mimetype) {
|
||||||
|
|
||||||
/* Clear clipboard and prepare for new data */
|
/* Clear clipboard and prepare for new data */
|
||||||
ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
|
guac_client* client = user->client;
|
||||||
guac_terminal_clipboard_reset(client_data->term, mimetype);
|
guac_ssh_client* ssh_client = (guac_ssh_client*) client->data;
|
||||||
|
guac_terminal_clipboard_reset(ssh_client->term, mimetype);
|
||||||
|
|
||||||
/* Set handlers for clipboard stream */
|
/* Set handlers for clipboard stream */
|
||||||
stream->blob_handler = guac_ssh_clipboard_blob_handler;
|
stream->blob_handler = guac_ssh_clipboard_blob_handler;
|
||||||
@ -42,17 +44,18 @@ int guac_ssh_clipboard_handler(guac_client* client, guac_stream* stream,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int guac_ssh_clipboard_blob_handler(guac_client* client, guac_stream* stream,
|
int guac_ssh_clipboard_blob_handler(guac_user* user, guac_stream* stream,
|
||||||
void* data, int length) {
|
void* data, int length) {
|
||||||
|
|
||||||
/* Append new data */
|
/* Append new data */
|
||||||
ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
|
guac_client* client = user->client;
|
||||||
guac_terminal_clipboard_append(client_data->term, data, length);
|
guac_ssh_client* ssh_client = (guac_ssh_client*) client->data;
|
||||||
|
guac_terminal_clipboard_append(ssh_client->term, data, length);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int guac_ssh_clipboard_end_handler(guac_client* client, guac_stream* stream) {
|
int guac_ssh_clipboard_end_handler(guac_user* user, guac_stream* stream) {
|
||||||
|
|
||||||
/* Nothing to do - clipboard is implemented within client */
|
/* Nothing to do - clipboard is implemented within client */
|
||||||
|
|
||||||
|
@ -29,21 +29,19 @@
|
|||||||
#include <guacamole/stream.h>
|
#include <guacamole/stream.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler for inbound clipboard data.
|
* Handler for inbound clipboard streams.
|
||||||
*/
|
*/
|
||||||
int guac_ssh_clipboard_handler(guac_client* client, guac_stream* stream,
|
guac_user_clipboard_handler guac_ssh_clipboard_handler;
|
||||||
char* mimetype);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler for stream data related to clipboard.
|
* Handler for data received along clipboard streams.
|
||||||
*/
|
*/
|
||||||
int guac_ssh_clipboard_blob_handler(guac_client* client, guac_stream* stream,
|
guac_user_blob_handler guac_ssh_clipboard_blob_handler;
|
||||||
void* data, int length);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler for end-of-stream related to clipboard.
|
* Handler for end-of-stream related to clipboard.
|
||||||
*/
|
*/
|
||||||
int guac_ssh_clipboard_end_handler(guac_client* client, guac_stream* stream);
|
guac_user_end_handler guac_ssh_clipboard_end_handler;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -1,128 +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 "client.h"
|
|
||||||
#include "guac_handlers.h"
|
|
||||||
#include "guac_sftp.h"
|
|
||||||
#include "guac_ssh.h"
|
|
||||||
#include "terminal.h"
|
|
||||||
|
|
||||||
#include <cairo/cairo.h>
|
|
||||||
#include <guacamole/client.h>
|
|
||||||
#include <libssh2.h>
|
|
||||||
#include <libssh2_sftp.h>
|
|
||||||
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
int ssh_guac_client_handle_messages(guac_client* client) {
|
|
||||||
|
|
||||||
ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
|
|
||||||
return guac_terminal_render_frame(client_data->term);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int ssh_guac_client_mouse_handler(guac_client* client, int x, int y, int mask) {
|
|
||||||
|
|
||||||
ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
|
|
||||||
guac_terminal* term = client_data->term;
|
|
||||||
|
|
||||||
/* Send mouse event */
|
|
||||||
guac_terminal_send_mouse(term, x, y, mask);
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) {
|
|
||||||
|
|
||||||
ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
|
|
||||||
guac_terminal* term = client_data->term;
|
|
||||||
|
|
||||||
/* Send key */
|
|
||||||
guac_terminal_send_key(term, keysym, pressed);
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int ssh_guac_client_size_handler(guac_client* client, int width, int height) {
|
|
||||||
|
|
||||||
/* Get terminal */
|
|
||||||
ssh_guac_client_data* guac_client_data = (ssh_guac_client_data*) client->data;
|
|
||||||
guac_terminal* terminal = guac_client_data->term;
|
|
||||||
|
|
||||||
/* Resize terminal */
|
|
||||||
guac_terminal_resize(terminal, width, height);
|
|
||||||
|
|
||||||
/* Update SSH pty size if connected */
|
|
||||||
if (guac_client_data->term_channel != NULL) {
|
|
||||||
pthread_mutex_lock(&(guac_client_data->term_channel_lock));
|
|
||||||
libssh2_channel_request_pty_size(guac_client_data->term_channel,
|
|
||||||
terminal->term_width, terminal->term_height);
|
|
||||||
pthread_mutex_unlock(&(guac_client_data->term_channel_lock));
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ssh_guac_client_free_handler(guac_client* client) {
|
|
||||||
|
|
||||||
ssh_guac_client_data* guac_client_data = (ssh_guac_client_data*) client->data;
|
|
||||||
|
|
||||||
/* Close SSH channel */
|
|
||||||
if (guac_client_data->term_channel != NULL) {
|
|
||||||
libssh2_channel_send_eof(guac_client_data->term_channel);
|
|
||||||
libssh2_channel_close(guac_client_data->term_channel);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Free terminal */
|
|
||||||
guac_terminal_free(guac_client_data->term);
|
|
||||||
pthread_join(guac_client_data->client_thread, NULL);
|
|
||||||
|
|
||||||
/* Free channels */
|
|
||||||
libssh2_channel_free(guac_client_data->term_channel);
|
|
||||||
|
|
||||||
/* Clean up the SFTP filesystem object and session */
|
|
||||||
if (guac_client_data->sftp_filesystem) {
|
|
||||||
guac_common_ssh_destroy_sftp_filesystem(guac_client_data->sftp_filesystem);
|
|
||||||
guac_common_ssh_destroy_session(guac_client_data->sftp_session);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Free session */
|
|
||||||
if (guac_client_data->session != NULL)
|
|
||||||
guac_common_ssh_destroy_session(guac_client_data->session);
|
|
||||||
|
|
||||||
/* Free user */
|
|
||||||
if (guac_client_data->user != NULL)
|
|
||||||
guac_common_ssh_destroy_user(guac_client_data->user);
|
|
||||||
|
|
||||||
/* Free copied settings */
|
|
||||||
free(guac_client_data->command);
|
|
||||||
|
|
||||||
/* Free generic data struct */
|
|
||||||
free(client->data);
|
|
||||||
|
|
||||||
guac_common_ssh_uninit();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
90
src/protocols/ssh/input.c
Normal file
90
src/protocols/ssh/input.c
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 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 "guac_cursor.h"
|
||||||
|
#include "guac_display.h"
|
||||||
|
#include "ssh.h"
|
||||||
|
#include "terminal.h"
|
||||||
|
|
||||||
|
#include <guacamole/client.h>
|
||||||
|
#include <guacamole/user.h>
|
||||||
|
#include <libssh2.h>
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
int guac_ssh_user_mouse_handler(guac_user* user, int x, int y, int mask) {
|
||||||
|
|
||||||
|
guac_client* client = user->client;
|
||||||
|
guac_ssh_client* ssh_client = (guac_ssh_client*) client->data;
|
||||||
|
guac_terminal* term = ssh_client->term;
|
||||||
|
|
||||||
|
/* Skip if terminal not yet ready */
|
||||||
|
if (term == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Send mouse event */
|
||||||
|
guac_terminal_send_mouse(term, user, x, y, mask);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int guac_ssh_user_key_handler(guac_user* user, int keysym, int pressed) {
|
||||||
|
|
||||||
|
guac_client* client = user->client;
|
||||||
|
guac_ssh_client* ssh_client = (guac_ssh_client*) client->data;
|
||||||
|
guac_terminal* term = ssh_client->term;
|
||||||
|
|
||||||
|
/* Skip if terminal not yet ready */
|
||||||
|
if (term == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Send key */
|
||||||
|
guac_terminal_send_key(term, keysym, pressed);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int guac_ssh_user_size_handler(guac_user* user, int width, int height) {
|
||||||
|
|
||||||
|
/* Get terminal */
|
||||||
|
guac_client* client = user->client;
|
||||||
|
guac_ssh_client* ssh_client = (guac_ssh_client*) client->data;
|
||||||
|
guac_terminal* terminal = ssh_client->term;
|
||||||
|
|
||||||
|
/* Skip if terminal not yet ready */
|
||||||
|
if (terminal == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Resize terminal */
|
||||||
|
guac_terminal_resize(terminal, width, height);
|
||||||
|
|
||||||
|
/* Update SSH pty size if connected */
|
||||||
|
if (ssh_client->term_channel != NULL) {
|
||||||
|
pthread_mutex_lock(&(ssh_client->term_channel_lock));
|
||||||
|
libssh2_channel_request_pty_size(ssh_client->term_channel,
|
||||||
|
terminal->term_width, terminal->term_height);
|
||||||
|
pthread_mutex_unlock(&(ssh_client->term_channel_lock));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2013 Glyptodon LLC
|
* Copyright (C) 2016 Glyptodon LLC
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -20,19 +20,27 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef GUAC_SSH_INPUT_H
|
||||||
#ifndef _SSH_GUAC_HANDLERS_H
|
#define GUAC_SSH_INPUT_H
|
||||||
#define _SSH_GUAC_HANDLERS_H
|
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <guacamole/client.h>
|
#include <guacamole/user.h>
|
||||||
|
|
||||||
int ssh_guac_client_handle_messages(guac_client* client);
|
/**
|
||||||
int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed);
|
* Handler for Guacamole user mouse events.
|
||||||
int ssh_guac_client_mouse_handler(guac_client* client, int x, int y, int mask);
|
*/
|
||||||
int ssh_guac_client_size_handler(guac_client* client, int width, int height);
|
guac_user_mouse_handler guac_ssh_user_mouse_handler;
|
||||||
int ssh_guac_client_free_handler(guac_client* client);
|
|
||||||
|
/**
|
||||||
|
* Handler for Guacamole user key events.
|
||||||
|
*/
|
||||||
|
guac_user_key_handler guac_ssh_user_key_handler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for Guacamole user size events.
|
||||||
|
*/
|
||||||
|
guac_user_size_handler guac_ssh_user_size_handler;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
269
src/protocols/ssh/settings.c
Normal file
269
src/protocols/ssh/settings.c
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 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 "client.h"
|
||||||
|
#include "settings.h"
|
||||||
|
|
||||||
|
#include <guacamole/user.h>
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
/* Client plugin arguments */
|
||||||
|
const char* GUAC_SSH_CLIENT_ARGS[] = {
|
||||||
|
"hostname",
|
||||||
|
"port",
|
||||||
|
"username",
|
||||||
|
"password",
|
||||||
|
"font-name",
|
||||||
|
"font-size",
|
||||||
|
"enable-sftp",
|
||||||
|
"private-key",
|
||||||
|
"passphrase",
|
||||||
|
#ifdef ENABLE_SSH_AGENT
|
||||||
|
"enable-agent",
|
||||||
|
#endif
|
||||||
|
"color-scheme",
|
||||||
|
"command",
|
||||||
|
"typescript-path",
|
||||||
|
"typescript-name",
|
||||||
|
"create-typescript-path",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
enum SSH_ARGS_IDX {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The hostname to connect to. Required.
|
||||||
|
*/
|
||||||
|
IDX_HOSTNAME,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The port to connect to. Optional.
|
||||||
|
*/
|
||||||
|
IDX_PORT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the user to login as. Optional.
|
||||||
|
*/
|
||||||
|
IDX_USERNAME,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The password to use when logging in. Optional.
|
||||||
|
*/
|
||||||
|
IDX_PASSWORD,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the font to use within the terminal.
|
||||||
|
*/
|
||||||
|
IDX_FONT_NAME,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The size of the font to use within the terminal, in points.
|
||||||
|
*/
|
||||||
|
IDX_FONT_SIZE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether SFTP should be enabled.
|
||||||
|
*/
|
||||||
|
IDX_ENABLE_SFTP,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The private key to use for authentication, if any.
|
||||||
|
*/
|
||||||
|
IDX_PRIVATE_KEY,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The passphrase required to decrypt the private key, if any.
|
||||||
|
*/
|
||||||
|
IDX_PASSPHRASE,
|
||||||
|
|
||||||
|
#ifdef ENABLE_SSH_AGENT
|
||||||
|
/**
|
||||||
|
* Whether SSH agent forwarding support should be enabled.
|
||||||
|
*/
|
||||||
|
IDX_ENABLE_AGENT,
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the color scheme to use. Currently valid color schemes are:
|
||||||
|
* "black-white", "white-black", "gray-black", and "green-black", each
|
||||||
|
* following the "foreground-background" pattern. By default, this will be
|
||||||
|
* "gray-black".
|
||||||
|
*/
|
||||||
|
IDX_COLOR_SCHEME,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The command to run instead if the default shell. If omitted, a normal
|
||||||
|
* shell session will be created.
|
||||||
|
*/
|
||||||
|
IDX_COMMAND,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The full absolute path to the directory in which typescripts should be
|
||||||
|
* written.
|
||||||
|
*/
|
||||||
|
IDX_TYPESCRIPT_PATH,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name that should be given to typescripts which are written in the
|
||||||
|
* given path. Each typescript will consist of two files: "NAME" and
|
||||||
|
* "NAME.timing".
|
||||||
|
*/
|
||||||
|
IDX_TYPESCRIPT_NAME,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the specified typescript path should automatically be created
|
||||||
|
* if it does not yet exist.
|
||||||
|
*/
|
||||||
|
IDX_CREATE_TYPESCRIPT_PATH,
|
||||||
|
|
||||||
|
SSH_ARGS_COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
guac_ssh_settings* guac_ssh_parse_args(guac_user* user,
|
||||||
|
int argc, const char** argv) {
|
||||||
|
|
||||||
|
/* Validate arg count */
|
||||||
|
if (argc != SSH_ARGS_COUNT) {
|
||||||
|
guac_user_log(user, GUAC_LOG_WARNING, "Incorrect number of connection "
|
||||||
|
"parameters provided: expected %i, got %i.",
|
||||||
|
SSH_ARGS_COUNT, argc);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
guac_ssh_settings* settings = calloc(1, sizeof(guac_ssh_settings));
|
||||||
|
|
||||||
|
/* Read parameters */
|
||||||
|
settings->hostname =
|
||||||
|
guac_user_parse_args_string(user, GUAC_SSH_CLIENT_ARGS, argv,
|
||||||
|
IDX_HOSTNAME, "");
|
||||||
|
|
||||||
|
settings->username =
|
||||||
|
guac_user_parse_args_string(user, GUAC_SSH_CLIENT_ARGS, argv,
|
||||||
|
IDX_USERNAME, NULL);
|
||||||
|
|
||||||
|
settings->password =
|
||||||
|
guac_user_parse_args_string(user, GUAC_SSH_CLIENT_ARGS, argv,
|
||||||
|
IDX_PASSWORD, NULL);
|
||||||
|
|
||||||
|
/* Init public key auth information */
|
||||||
|
settings->key_base64 =
|
||||||
|
guac_user_parse_args_string(user, GUAC_SSH_CLIENT_ARGS, argv,
|
||||||
|
IDX_PRIVATE_KEY, NULL);
|
||||||
|
|
||||||
|
settings->key_passphrase =
|
||||||
|
guac_user_parse_args_string(user, GUAC_SSH_CLIENT_ARGS, argv,
|
||||||
|
IDX_PASSPHRASE, NULL);
|
||||||
|
|
||||||
|
/* Read font name */
|
||||||
|
settings->font_name =
|
||||||
|
guac_user_parse_args_string(user, GUAC_SSH_CLIENT_ARGS, argv,
|
||||||
|
IDX_FONT_NAME, GUAC_SSH_DEFAULT_FONT_NAME);
|
||||||
|
|
||||||
|
/* Read font size */
|
||||||
|
settings->font_size =
|
||||||
|
guac_user_parse_args_int(user, GUAC_SSH_CLIENT_ARGS, argv,
|
||||||
|
IDX_FONT_SIZE, GUAC_SSH_DEFAULT_FONT_SIZE);
|
||||||
|
|
||||||
|
/* Copy requested color scheme */
|
||||||
|
settings->color_scheme =
|
||||||
|
guac_user_parse_args_string(user, GUAC_SSH_CLIENT_ARGS, argv,
|
||||||
|
IDX_COLOR_SCHEME, "");
|
||||||
|
|
||||||
|
/* Pull width/height/resolution directly from user */
|
||||||
|
settings->width = user->info.optimal_width;
|
||||||
|
settings->height = user->info.optimal_height;
|
||||||
|
settings->resolution = user->info.optimal_resolution;
|
||||||
|
|
||||||
|
/* Parse SFTP enable */
|
||||||
|
settings->enable_sftp =
|
||||||
|
guac_user_parse_args_boolean(user, GUAC_SSH_CLIENT_ARGS, argv,
|
||||||
|
IDX_ENABLE_SFTP, false);
|
||||||
|
|
||||||
|
#ifdef ENABLE_SSH_AGENT
|
||||||
|
settings->enable_agent =
|
||||||
|
guac_user_parse_args_boolean(user, GUAC_SSH_CLIENT_ARGS, argv,
|
||||||
|
IDX_ENABLE_AGENT, false);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Read port */
|
||||||
|
settings->port =
|
||||||
|
guac_user_parse_args_string(user, GUAC_SSH_CLIENT_ARGS, argv,
|
||||||
|
IDX_PORT, GUAC_SSH_DEFAULT_PORT);
|
||||||
|
|
||||||
|
/* Read command, if any */
|
||||||
|
settings->command =
|
||||||
|
guac_user_parse_args_string(user, GUAC_SSH_CLIENT_ARGS, argv,
|
||||||
|
IDX_COMMAND, NULL);
|
||||||
|
|
||||||
|
/* Read typescript path */
|
||||||
|
settings->typescript_path =
|
||||||
|
guac_user_parse_args_string(user, GUAC_SSH_CLIENT_ARGS, argv,
|
||||||
|
IDX_TYPESCRIPT_PATH, NULL);
|
||||||
|
|
||||||
|
/* Read typescript name */
|
||||||
|
settings->typescript_name =
|
||||||
|
guac_user_parse_args_string(user, GUAC_SSH_CLIENT_ARGS, argv,
|
||||||
|
IDX_TYPESCRIPT_NAME, GUAC_SSH_DEFAULT_TYPESCRIPT_NAME);
|
||||||
|
|
||||||
|
/* Parse path creation flag */
|
||||||
|
settings->create_typescript_path =
|
||||||
|
guac_user_parse_args_boolean(user, GUAC_SSH_CLIENT_ARGS, argv,
|
||||||
|
IDX_CREATE_TYPESCRIPT_PATH, false);
|
||||||
|
|
||||||
|
/* Parsing was successful */
|
||||||
|
return settings;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void guac_ssh_settings_free(guac_ssh_settings* settings) {
|
||||||
|
|
||||||
|
/* Free network connection information */
|
||||||
|
free(settings->hostname);
|
||||||
|
free(settings->port);
|
||||||
|
|
||||||
|
/* Free credentials */
|
||||||
|
free(settings->username);
|
||||||
|
free(settings->password);
|
||||||
|
free(settings->key_base64);
|
||||||
|
free(settings->key_passphrase);
|
||||||
|
|
||||||
|
/* Free display preferences */
|
||||||
|
free(settings->font_name);
|
||||||
|
free(settings->color_scheme);
|
||||||
|
|
||||||
|
/* Free requested command */
|
||||||
|
free(settings->command);
|
||||||
|
|
||||||
|
/* Free typescript settings */
|
||||||
|
free(settings->typescript_name);
|
||||||
|
free(settings->typescript_path);
|
||||||
|
|
||||||
|
/* Free overall structure */
|
||||||
|
free(settings);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
199
src/protocols/ssh/settings.h
Normal file
199
src/protocols/ssh/settings.h
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 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_SETTINGS_H
|
||||||
|
#define GUAC_SSH_SETTINGS_H
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <guacamole/user.h>
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the font to use for the terminal if no name is specified.
|
||||||
|
*/
|
||||||
|
#define GUAC_SSH_DEFAULT_FONT_NAME "monospace"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The size of the font to use for the terminal if no font size is specified,
|
||||||
|
* in points.
|
||||||
|
*/
|
||||||
|
#define GUAC_SSH_DEFAULT_FONT_SIZE 12
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The port to connect to when initiating any SSH connection, if no other port
|
||||||
|
* is specified.
|
||||||
|
*/
|
||||||
|
#define GUAC_SSH_DEFAULT_PORT "22"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The filename to use for the typescript, if not specified.
|
||||||
|
*/
|
||||||
|
#define GUAC_SSH_DEFAULT_TYPESCRIPT_NAME "typescript"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Settings for the SSH connection. The values for this structure are parsed
|
||||||
|
* from the arguments given during the Guacamole protocol handshake using the
|
||||||
|
* guac_ssh_parse_args() function.
|
||||||
|
*/
|
||||||
|
typedef struct guac_ssh_settings {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The hostname of the SSH server to connect to.
|
||||||
|
*/
|
||||||
|
char* hostname;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The port of the SSH server to connect to.
|
||||||
|
*/
|
||||||
|
char* port;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the user to login as, if any. If no username is specified,
|
||||||
|
* this will be NULL.
|
||||||
|
*/
|
||||||
|
char* username;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The password to give when authenticating, if any. If no password is
|
||||||
|
* specified, this will be NULL.
|
||||||
|
*/
|
||||||
|
char* password;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The private key, encoded as base64, if any. If no private key is
|
||||||
|
* specified, this will be NULL.
|
||||||
|
*/
|
||||||
|
char* key_base64;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The passphrase to use to decrypt the given private key, if any. If no
|
||||||
|
* passphrase is specified, this will be NULL.
|
||||||
|
*/
|
||||||
|
char* key_passphrase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The command to run instead of the default shell. If a normal shell
|
||||||
|
* session is desired, this will be NULL.
|
||||||
|
*/
|
||||||
|
char* command;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the font to use for display rendering.
|
||||||
|
*/
|
||||||
|
char* font_name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The size of the font to use, in points.
|
||||||
|
*/
|
||||||
|
int font_size;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the color scheme to use.
|
||||||
|
*/
|
||||||
|
char* color_scheme;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The desired width of the terminal display, in pixels.
|
||||||
|
*/
|
||||||
|
int width;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The desired height of the terminal display, in pixels.
|
||||||
|
*/
|
||||||
|
int height;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The desired screen resolution, in DPI.
|
||||||
|
*/
|
||||||
|
int resolution;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether SFTP is enabled.
|
||||||
|
*/
|
||||||
|
bool enable_sftp;
|
||||||
|
|
||||||
|
#ifdef ENABLE_SSH_AGENT
|
||||||
|
/**
|
||||||
|
* Whether the SSH agent is enabled.
|
||||||
|
*/
|
||||||
|
bool enable_agent;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The path in which the typescript should be saved, if enabled. If no
|
||||||
|
* typescript should be saved, this will be NULL.
|
||||||
|
*/
|
||||||
|
char* typescript_path;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The filename to use for the typescript, if enabled.
|
||||||
|
*/
|
||||||
|
char* typescript_name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the typescript path should be automatically created if it does
|
||||||
|
* not already exist.
|
||||||
|
*/
|
||||||
|
bool create_typescript_path;
|
||||||
|
|
||||||
|
} guac_ssh_settings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses all given args, storing them in a newly-allocated settings object. If
|
||||||
|
* the args fail to parse, NULL is returned.
|
||||||
|
*
|
||||||
|
* @param user
|
||||||
|
* The user who submitted the given arguments while joining the
|
||||||
|
* connection.
|
||||||
|
*
|
||||||
|
* @param argc
|
||||||
|
* The number of arguments within the argv array.
|
||||||
|
*
|
||||||
|
* @param argv
|
||||||
|
* The values of all arguments provided by the user.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* A newly-allocated settings object which must be freed with
|
||||||
|
* guac_ssh_settings_free() when no longer needed. If the arguments fail
|
||||||
|
* to parse, NULL is returned.
|
||||||
|
*/
|
||||||
|
guac_ssh_settings* guac_ssh_parse_args(guac_user* user,
|
||||||
|
int argc, const char** argv);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees the given guac_ssh_settings object, having been previously allocated
|
||||||
|
* via guac_ssh_parse_args().
|
||||||
|
*
|
||||||
|
* @param settings
|
||||||
|
* The settings object to free.
|
||||||
|
*/
|
||||||
|
void guac_ssh_settings_free(guac_ssh_settings* settings);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NULL-terminated array of accepted client args.
|
||||||
|
*/
|
||||||
|
extern const char* GUAC_SSH_CLIENT_ARGS[];
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -22,40 +22,75 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "client.h"
|
|
||||||
#include "guac_sftp.h"
|
#include "guac_sftp.h"
|
||||||
#include "sftp.h"
|
#include "sftp.h"
|
||||||
|
#include "ssh.h"
|
||||||
|
|
||||||
#include <guacamole/client.h>
|
#include <guacamole/client.h>
|
||||||
#include <guacamole/stream.h>
|
#include <guacamole/stream.h>
|
||||||
|
#include <guacamole/user.h>
|
||||||
|
|
||||||
int guac_sftp_file_handler(guac_client* client, guac_stream* stream,
|
int guac_sftp_file_handler(guac_user* user, guac_stream* stream,
|
||||||
char* mimetype, char* filename) {
|
char* mimetype, char* filename) {
|
||||||
|
|
||||||
ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
|
guac_client* client = user->client;
|
||||||
guac_object* filesystem = client_data->sftp_filesystem;
|
guac_ssh_client* ssh_client = (guac_ssh_client*) client->data;
|
||||||
|
guac_common_ssh_sftp_filesystem* filesystem = ssh_client->sftp_filesystem;
|
||||||
|
|
||||||
/* Handle file upload */
|
/* Handle file upload */
|
||||||
return guac_common_ssh_sftp_handle_file_stream(filesystem, stream,
|
return guac_common_ssh_sftp_handle_file_stream(filesystem, user, stream,
|
||||||
mimetype, filename);
|
mimetype, filename);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
guac_stream* guac_sftp_download_file(guac_client* client,
|
/**
|
||||||
char* filename) {
|
* Callback invoked on the current connection owner (if any) when a file
|
||||||
|
* download is being initiated through the terminal.
|
||||||
|
*
|
||||||
|
* @param owner
|
||||||
|
* The guac_user that is the owner of the connection, or NULL if the
|
||||||
|
* connection owner has left.
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* The filename of the file that should be downloaded.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The stream allocated for the file download, or NULL if no stream could
|
||||||
|
* be allocated.
|
||||||
|
*/
|
||||||
|
static void* guac_sftp_download_to_owner(guac_user* owner, void* data) {
|
||||||
|
|
||||||
ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
|
/* Do not bother attempting the download if the owner has left */
|
||||||
guac_object* filesystem = client_data->sftp_filesystem;
|
if (owner == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
guac_client* client = owner->client;
|
||||||
|
guac_ssh_client* ssh_client = (guac_ssh_client*) client->data;
|
||||||
|
guac_common_ssh_sftp_filesystem* filesystem = ssh_client->sftp_filesystem;
|
||||||
|
|
||||||
|
/* Ignore download if filesystem has been unloaded */
|
||||||
|
if (filesystem == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
char* filename = (char*) data;
|
||||||
|
|
||||||
/* Initiate download of requested file */
|
/* Initiate download of requested file */
|
||||||
return guac_common_ssh_sftp_download_file(filesystem, filename);
|
return guac_common_ssh_sftp_download_file(filesystem, owner, filename);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
guac_stream* guac_sftp_download_file(guac_client* client, char* filename) {
|
||||||
|
|
||||||
|
/* Initiate download to the owner of the connection */
|
||||||
|
return (guac_stream*) guac_client_for_owner(client,
|
||||||
|
guac_sftp_download_to_owner, filename);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void guac_sftp_set_upload_path(guac_client* client, char* path) {
|
void guac_sftp_set_upload_path(guac_client* client, char* path) {
|
||||||
|
|
||||||
ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
|
guac_ssh_client* ssh_client = (guac_ssh_client*) client->data;
|
||||||
guac_object* filesystem = client_data->sftp_filesystem;
|
guac_common_ssh_sftp_filesystem* filesystem = ssh_client->sftp_filesystem;
|
||||||
|
|
||||||
/* Set upload path as specified */
|
/* Set upload path as specified */
|
||||||
guac_common_ssh_sftp_set_upload_path(filesystem, path);
|
guac_common_ssh_sftp_set_upload_path(filesystem, path);
|
||||||
|
@ -28,32 +28,14 @@
|
|||||||
|
|
||||||
#include <guacamole/client.h>
|
#include <guacamole/client.h>
|
||||||
#include <guacamole/stream.h>
|
#include <guacamole/stream.h>
|
||||||
|
#include <guacamole/user.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles an incoming stream from a Guacamole "file" instruction, saving the
|
* Handles an incoming stream from a Guacamole "file" instruction, saving the
|
||||||
* contents of that stream to the file having the given name within the
|
* contents of that stream to the file having the given name within the
|
||||||
* upload directory set by guac_sftp_set_upload_path().
|
* upload directory set by guac_sftp_set_upload_path().
|
||||||
*
|
|
||||||
* @param client
|
|
||||||
* The client receiving the uploaded file.
|
|
||||||
*
|
|
||||||
* @param stream
|
|
||||||
* The stream through which the uploaded file data will be received.
|
|
||||||
*
|
|
||||||
* @param mimetype
|
|
||||||
* The mimetype of the data being received.
|
|
||||||
*
|
|
||||||
* @param filename
|
|
||||||
* The filename of the file to write to. This filename will always be taken
|
|
||||||
* relative to the upload path set by
|
|
||||||
* guac_common_ssh_sftp_set_upload_path().
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* Zero if the incoming stream has been handled successfully, non-zero on
|
|
||||||
* failure.
|
|
||||||
*/
|
*/
|
||||||
int guac_sftp_file_handler(guac_client* client, guac_stream* stream,
|
guac_user_file_handler guac_sftp_file_handler;
|
||||||
char* mimetype, char* filename);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initiates an SFTP file download to the user via the Guacamole "file"
|
* Initiates an SFTP file download to the user via the Guacamole "file"
|
||||||
@ -62,7 +44,7 @@ int guac_sftp_file_handler(guac_client* client, guac_stream* stream,
|
|||||||
* the client.
|
* the client.
|
||||||
*
|
*
|
||||||
* @param client
|
* @param client
|
||||||
* The client receiving the file.
|
* The client associated with the terminal emulator receiving the file.
|
||||||
*
|
*
|
||||||
* @param filename
|
* @param filename
|
||||||
* The filename of the file to download, relative to the given filesystem.
|
* The filename of the file to download, relative to the given filesystem.
|
||||||
|
@ -22,10 +22,11 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "client.h"
|
|
||||||
#include "guac_sftp.h"
|
#include "guac_sftp.h"
|
||||||
#include "guac_ssh.h"
|
#include "guac_ssh.h"
|
||||||
|
#include "settings.h"
|
||||||
#include "sftp.h"
|
#include "sftp.h"
|
||||||
|
#include "ssh.h"
|
||||||
#include "terminal.h"
|
#include "terminal.h"
|
||||||
|
|
||||||
#ifdef ENABLE_SSH_AGENT
|
#ifdef ENABLE_SSH_AGENT
|
||||||
@ -35,8 +36,6 @@
|
|||||||
#include <libssh2.h>
|
#include <libssh2.h>
|
||||||
#include <libssh2_sftp.h>
|
#include <libssh2_sftp.h>
|
||||||
#include <guacamole/client.h>
|
#include <guacamole/client.h>
|
||||||
#include <guacamole/protocol.h>
|
|
||||||
#include <guacamole/socket.h>
|
|
||||||
#include <openssl/err.h>
|
#include <openssl/err.h>
|
||||||
#include <openssl/ssl.h>
|
#include <openssl/ssl.h>
|
||||||
|
|
||||||
@ -70,27 +69,28 @@
|
|||||||
*/
|
*/
|
||||||
static guac_common_ssh_user* guac_ssh_get_user(guac_client* client) {
|
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_ssh_client* ssh_client = (guac_ssh_client*) client->data;
|
||||||
|
guac_ssh_settings* settings = ssh_client->settings;
|
||||||
|
|
||||||
guac_common_ssh_user* user;
|
guac_common_ssh_user* user;
|
||||||
|
|
||||||
/* Get username */
|
/* Get username */
|
||||||
if (client_data->username[0] == 0)
|
if (settings->username == NULL)
|
||||||
guac_terminal_prompt(client_data->term, "Login as: ",
|
settings->username = guac_terminal_prompt(ssh_client->term,
|
||||||
client_data->username, sizeof(client_data->username), true);
|
"Login as: ", true);
|
||||||
|
|
||||||
/* Create user object from username */
|
/* Create user object from username */
|
||||||
user = guac_common_ssh_create_user(client_data->username);
|
user = guac_common_ssh_create_user(settings->username);
|
||||||
|
|
||||||
/* If key specified, import */
|
/* If key specified, import */
|
||||||
if (client_data->key_base64[0] != 0) {
|
if (settings->key_base64 != NULL) {
|
||||||
|
|
||||||
guac_client_log(client, GUAC_LOG_DEBUG,
|
guac_client_log(client, GUAC_LOG_DEBUG,
|
||||||
"Attempting private key import (WITHOUT passphrase)");
|
"Attempting private key import (WITHOUT passphrase)");
|
||||||
|
|
||||||
/* Attempt to read key without passphrase */
|
/* Attempt to read key without passphrase */
|
||||||
if (guac_common_ssh_user_import_key(user,
|
if (guac_common_ssh_user_import_key(user,
|
||||||
client_data->key_base64, NULL)) {
|
settings->key_base64, NULL)) {
|
||||||
|
|
||||||
/* Log failure of initial attempt */
|
/* Log failure of initial attempt */
|
||||||
guac_client_log(client, GUAC_LOG_DEBUG,
|
guac_client_log(client, GUAC_LOG_DEBUG,
|
||||||
@ -101,15 +101,15 @@ static guac_common_ssh_user* guac_ssh_get_user(guac_client* client) {
|
|||||||
"Re-attempting private key import (WITH passphrase)");
|
"Re-attempting private key import (WITH passphrase)");
|
||||||
|
|
||||||
/* Prompt for passphrase if missing */
|
/* Prompt for passphrase if missing */
|
||||||
if (client_data->key_passphrase[0] == 0)
|
if (settings->key_passphrase == NULL)
|
||||||
guac_terminal_prompt(client_data->term, "Key passphrase: ",
|
settings->key_passphrase =
|
||||||
client_data->key_passphrase,
|
guac_terminal_prompt(ssh_client->term,
|
||||||
sizeof(client_data->key_passphrase), false);
|
"Key passphrase: ", false);
|
||||||
|
|
||||||
/* Reattempt import with passphrase */
|
/* Reattempt import with passphrase */
|
||||||
if (guac_common_ssh_user_import_key(user,
|
if (guac_common_ssh_user_import_key(user,
|
||||||
client_data->key_base64,
|
settings->key_base64,
|
||||||
client_data->key_passphrase)) {
|
settings->key_passphrase)) {
|
||||||
|
|
||||||
/* If still failing, give up */
|
/* If still failing, give up */
|
||||||
guac_client_abort(client,
|
guac_client_abort(client,
|
||||||
@ -134,18 +134,17 @@ static guac_common_ssh_user* guac_ssh_get_user(guac_client* client) {
|
|||||||
else {
|
else {
|
||||||
|
|
||||||
/* Get password if not provided */
|
/* Get password if not provided */
|
||||||
if (client_data->password[0] == 0)
|
if (settings->password == NULL)
|
||||||
guac_terminal_prompt(client_data->term, "Password: ",
|
settings->password = guac_terminal_prompt(ssh_client->term,
|
||||||
client_data->password, sizeof(client_data->password),
|
"Password: ", false);
|
||||||
false);
|
|
||||||
|
|
||||||
/* Set provided password */
|
/* Set provided password */
|
||||||
guac_common_ssh_user_set_password(user, client_data->password);
|
guac_common_ssh_user_set_password(user, settings->password);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Clear screen of any prompts */
|
/* Clear screen of any prompts */
|
||||||
guac_terminal_printf(client_data->term, "\x1B[H\x1B[J");
|
guac_terminal_printf(ssh_client->term, "\x1B[H\x1B[J");
|
||||||
|
|
||||||
return user;
|
return user;
|
||||||
|
|
||||||
@ -154,16 +153,16 @@ static guac_common_ssh_user* guac_ssh_get_user(guac_client* client) {
|
|||||||
void* ssh_input_thread(void* data) {
|
void* ssh_input_thread(void* data) {
|
||||||
|
|
||||||
guac_client* client = (guac_client*) data;
|
guac_client* client = (guac_client*) data;
|
||||||
ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
|
guac_ssh_client* ssh_client = (guac_ssh_client*) client->data;
|
||||||
|
|
||||||
char buffer[8192];
|
char buffer[8192];
|
||||||
int bytes_read;
|
int bytes_read;
|
||||||
|
|
||||||
/* Write all data read */
|
/* Write all data read */
|
||||||
while ((bytes_read = guac_terminal_read_stdin(client_data->term, buffer, sizeof(buffer))) > 0) {
|
while ((bytes_read = guac_terminal_read_stdin(ssh_client->term, buffer, sizeof(buffer))) > 0) {
|
||||||
pthread_mutex_lock(&(client_data->term_channel_lock));
|
pthread_mutex_lock(&(ssh_client->term_channel_lock));
|
||||||
libssh2_channel_write(client_data->term_channel, buffer, bytes_read);
|
libssh2_channel_write(ssh_client->term_channel, buffer, bytes_read);
|
||||||
pthread_mutex_unlock(&(client_data->term_channel_lock));
|
pthread_mutex_unlock(&(ssh_client->term_channel_lock));
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -173,9 +172,9 @@ void* ssh_input_thread(void* data) {
|
|||||||
void* ssh_client_thread(void* data) {
|
void* ssh_client_thread(void* data) {
|
||||||
|
|
||||||
guac_client* client = (guac_client*) data;
|
guac_client* client = (guac_client*) data;
|
||||||
ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
|
guac_ssh_client* ssh_client = (guac_ssh_client*) client->data;
|
||||||
|
guac_ssh_settings* settings = ssh_client->settings;
|
||||||
|
|
||||||
guac_socket* socket = client->socket;
|
|
||||||
char buffer[8192];
|
char buffer[8192];
|
||||||
|
|
||||||
pthread_t input_thread;
|
pthread_t input_thread;
|
||||||
@ -184,29 +183,44 @@ void* ssh_client_thread(void* data) {
|
|||||||
if (guac_common_ssh_init(client))
|
if (guac_common_ssh_init(client))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/* Get user and credentials */
|
/* Create terminal */
|
||||||
client_data->user = guac_ssh_get_user(client);
|
ssh_client->term = guac_terminal_create(client,
|
||||||
|
settings->font_name, settings->font_size,
|
||||||
|
settings->resolution, settings->width, settings->height,
|
||||||
|
settings->color_scheme);
|
||||||
|
|
||||||
/* Send new name */
|
/* Fail if terminal init failed */
|
||||||
char name[1024];
|
if (ssh_client->term == NULL) {
|
||||||
snprintf(name, sizeof(name)-1, "%s@%s",
|
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
|
||||||
client_data->username, client_data->hostname);
|
"Terminal initialization failed");
|
||||||
guac_protocol_send_name(socket, name);
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set up typescript, if requested */
|
||||||
|
if (settings->typescript_path != NULL) {
|
||||||
|
guac_terminal_create_typescript(ssh_client->term,
|
||||||
|
settings->typescript_path,
|
||||||
|
settings->typescript_name,
|
||||||
|
settings->create_typescript_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get user and credentials */
|
||||||
|
ssh_client->user = guac_ssh_get_user(client);
|
||||||
|
|
||||||
/* Open SSH session */
|
/* Open SSH session */
|
||||||
client_data->session = guac_common_ssh_create_session(client,
|
ssh_client->session = guac_common_ssh_create_session(client,
|
||||||
client_data->hostname, client_data->port, client_data->user);
|
settings->hostname, settings->port, ssh_client->user);
|
||||||
if (client_data->session == NULL) {
|
if (ssh_client->session == NULL) {
|
||||||
/* Already aborted within guac_common_ssh_create_session() */
|
/* Already aborted within guac_common_ssh_create_session() */
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_init(&client_data->term_channel_lock, NULL);
|
pthread_mutex_init(&ssh_client->term_channel_lock, NULL);
|
||||||
|
|
||||||
/* Open channel for terminal */
|
/* Open channel for terminal */
|
||||||
client_data->term_channel =
|
ssh_client->term_channel =
|
||||||
libssh2_channel_open_session(client_data->session->session);
|
libssh2_channel_open_session(ssh_client->session->session);
|
||||||
if (client_data->term_channel == NULL) {
|
if (ssh_client->term_channel == NULL) {
|
||||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR,
|
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR,
|
||||||
"Unable to open terminal channel.");
|
"Unable to open terminal channel.");
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -214,59 +228,60 @@ void* ssh_client_thread(void* data) {
|
|||||||
|
|
||||||
#ifdef ENABLE_SSH_AGENT
|
#ifdef ENABLE_SSH_AGENT
|
||||||
/* Start SSH agent forwarding, if enabled */
|
/* Start SSH agent forwarding, if enabled */
|
||||||
if (client_data->enable_agent) {
|
if (ssh_client->enable_agent) {
|
||||||
libssh2_session_callback_set(client_data->session,
|
libssh2_session_callback_set(ssh_client->session,
|
||||||
LIBSSH2_CALLBACK_AUTH_AGENT, (void*) ssh_auth_agent_callback);
|
LIBSSH2_CALLBACK_AUTH_AGENT, (void*) ssh_auth_agent_callback);
|
||||||
|
|
||||||
/* Request agent forwarding */
|
/* Request agent forwarding */
|
||||||
if (libssh2_channel_request_auth_agent(client_data->term_channel))
|
if (libssh2_channel_request_auth_agent(ssh_client->term_channel))
|
||||||
guac_client_log(client, GUAC_LOG_ERROR, "Agent forwarding request failed");
|
guac_client_log(client, GUAC_LOG_ERROR, "Agent forwarding request failed");
|
||||||
else
|
else
|
||||||
guac_client_log(client, GUAC_LOG_INFO, "Agent forwarding enabled.");
|
guac_client_log(client, GUAC_LOG_INFO, "Agent forwarding enabled.");
|
||||||
}
|
}
|
||||||
|
|
||||||
client_data->auth_agent = NULL;
|
ssh_client->auth_agent = NULL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Start SFTP session as well, if enabled */
|
/* Start SFTP session as well, if enabled */
|
||||||
if (client_data->enable_sftp) {
|
if (settings->enable_sftp) {
|
||||||
|
|
||||||
/* Create SSH session specific for SFTP */
|
/* Create SSH session specific for SFTP */
|
||||||
guac_client_log(client, GUAC_LOG_DEBUG, "Reconnecting for SFTP...");
|
guac_client_log(client, GUAC_LOG_DEBUG, "Reconnecting for SFTP...");
|
||||||
client_data->sftp_session =
|
ssh_client->sftp_session =
|
||||||
guac_common_ssh_create_session(client, client_data->hostname,
|
guac_common_ssh_create_session(client, settings->hostname,
|
||||||
client_data->port, client_data->user);
|
settings->port, ssh_client->user);
|
||||||
if (client_data->sftp_session == NULL) {
|
if (ssh_client->sftp_session == NULL) {
|
||||||
/* Already aborted within guac_common_ssh_create_session() */
|
/* Already aborted within guac_common_ssh_create_session() */
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Request SFTP */
|
/* Request SFTP */
|
||||||
client_data->sftp_filesystem =
|
ssh_client->sftp_filesystem = guac_common_ssh_create_sftp_filesystem(
|
||||||
guac_common_ssh_create_sftp_filesystem(
|
ssh_client->sftp_session, "/");
|
||||||
client_data->sftp_session, "/");
|
|
||||||
|
|
||||||
/* Set generic (non-filesystem) file upload handler */
|
/* Expose filesystem to connection owner */
|
||||||
client->file_handler = guac_sftp_file_handler;
|
guac_client_for_owner(client,
|
||||||
|
guac_common_ssh_expose_sftp_filesystem,
|
||||||
|
ssh_client->sftp_filesystem);
|
||||||
|
|
||||||
/* Init handlers for Guacamole-specific console codes */
|
/* Init handlers for Guacamole-specific console codes */
|
||||||
client_data->term->upload_path_handler = guac_sftp_set_upload_path;
|
ssh_client->term->upload_path_handler = guac_sftp_set_upload_path;
|
||||||
client_data->term->file_download_handler = guac_sftp_download_file;
|
ssh_client->term->file_download_handler = guac_sftp_download_file;
|
||||||
|
|
||||||
guac_client_log(client, GUAC_LOG_DEBUG, "SFTP session initialized");
|
guac_client_log(client, GUAC_LOG_DEBUG, "SFTP session initialized");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Request PTY */
|
/* Request PTY */
|
||||||
if (libssh2_channel_request_pty_ex(client_data->term_channel, "linux", sizeof("linux")-1, NULL, 0,
|
if (libssh2_channel_request_pty_ex(ssh_client->term_channel, "linux", sizeof("linux")-1, NULL, 0,
|
||||||
client_data->term->term_width, client_data->term->term_height, 0, 0)) {
|
ssh_client->term->term_width, ssh_client->term->term_height, 0, 0)) {
|
||||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, "Unable to allocate PTY.");
|
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, "Unable to allocate PTY.");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If a command is specified, run that instead of a shell */
|
/* If a command is specified, run that instead of a shell */
|
||||||
if (client_data->command != NULL) {
|
if (settings->command != NULL) {
|
||||||
if (libssh2_channel_exec(client_data->term_channel, client_data->command)) {
|
if (libssh2_channel_exec(ssh_client->term_channel, settings->command)) {
|
||||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR,
|
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR,
|
||||||
"Unable to execute command.");
|
"Unable to execute command.");
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -274,7 +289,7 @@ void* ssh_client_thread(void* data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Otherwise, request a shell */
|
/* Otherwise, request a shell */
|
||||||
else if (libssh2_channel_shell(client_data->term_channel)) {
|
else if (libssh2_channel_shell(ssh_client->term_channel)) {
|
||||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR,
|
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR,
|
||||||
"Unable to associate shell with PTY.");
|
"Unable to associate shell with PTY.");
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -290,7 +305,7 @@ void* ssh_client_thread(void* data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Set non-blocking */
|
/* Set non-blocking */
|
||||||
libssh2_session_set_blocking(client_data->session->session, 0);
|
libssh2_session_set_blocking(ssh_client->session->session, 0);
|
||||||
|
|
||||||
/* While data available, write to terminal */
|
/* While data available, write to terminal */
|
||||||
int bytes_read = 0;
|
int bytes_read = 0;
|
||||||
@ -299,23 +314,23 @@ void* ssh_client_thread(void* data) {
|
|||||||
/* Track total amount of data read */
|
/* Track total amount of data read */
|
||||||
int total_read = 0;
|
int total_read = 0;
|
||||||
|
|
||||||
pthread_mutex_lock(&(client_data->term_channel_lock));
|
pthread_mutex_lock(&(ssh_client->term_channel_lock));
|
||||||
|
|
||||||
/* Stop reading at EOF */
|
/* Stop reading at EOF */
|
||||||
if (libssh2_channel_eof(client_data->term_channel)) {
|
if (libssh2_channel_eof(ssh_client->term_channel)) {
|
||||||
pthread_mutex_unlock(&(client_data->term_channel_lock));
|
pthread_mutex_unlock(&(ssh_client->term_channel_lock));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read terminal data */
|
/* Read terminal data */
|
||||||
bytes_read = libssh2_channel_read(client_data->term_channel,
|
bytes_read = libssh2_channel_read(ssh_client->term_channel,
|
||||||
buffer, sizeof(buffer));
|
buffer, sizeof(buffer));
|
||||||
|
|
||||||
pthread_mutex_unlock(&(client_data->term_channel_lock));
|
pthread_mutex_unlock(&(ssh_client->term_channel_lock));
|
||||||
|
|
||||||
/* Attempt to write data received. Exit on failure. */
|
/* Attempt to write data received. Exit on failure. */
|
||||||
if (bytes_read > 0) {
|
if (bytes_read > 0) {
|
||||||
int written = guac_terminal_write_stdout(client_data->term, buffer, bytes_read);
|
int written = guac_terminal_write_stdout(ssh_client->term, buffer, bytes_read);
|
||||||
if (written < 0)
|
if (written < 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -327,12 +342,12 @@ void* ssh_client_thread(void* data) {
|
|||||||
|
|
||||||
#ifdef ENABLE_SSH_AGENT
|
#ifdef ENABLE_SSH_AGENT
|
||||||
/* If agent open, handle any agent packets */
|
/* If agent open, handle any agent packets */
|
||||||
if (client_data->auth_agent != NULL) {
|
if (ssh_client->auth_agent != NULL) {
|
||||||
bytes_read = ssh_auth_agent_read(client_data->auth_agent);
|
bytes_read = ssh_auth_agent_read(ssh_client->auth_agent);
|
||||||
if (bytes_read > 0)
|
if (bytes_read > 0)
|
||||||
total_read += bytes_read;
|
total_read += bytes_read;
|
||||||
else if (bytes_read < 0 && bytes_read != LIBSSH2_ERROR_EAGAIN)
|
else if (bytes_read < 0 && bytes_read != LIBSSH2_ERROR_EAGAIN)
|
||||||
client_data->auth_agent = NULL;
|
ssh_client->auth_agent = NULL;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -342,13 +357,13 @@ void* ssh_client_thread(void* data) {
|
|||||||
struct timeval timeout;
|
struct timeval timeout;
|
||||||
|
|
||||||
FD_ZERO(&fds);
|
FD_ZERO(&fds);
|
||||||
FD_SET(client_data->session->fd, &fds);
|
FD_SET(ssh_client->session->fd, &fds);
|
||||||
|
|
||||||
/* Wait for one second */
|
/* Wait for one second */
|
||||||
timeout.tv_sec = 1;
|
timeout.tv_sec = 1;
|
||||||
timeout.tv_usec = 0;
|
timeout.tv_usec = 0;
|
||||||
|
|
||||||
if (select(client_data->session->fd + 1, &fds,
|
if (select(ssh_client->session->fd + 1, &fds,
|
||||||
NULL, NULL, &timeout) < 0)
|
NULL, NULL, &timeout) < 0)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -359,7 +374,7 @@ void* ssh_client_thread(void* data) {
|
|||||||
guac_client_stop(client);
|
guac_client_stop(client);
|
||||||
pthread_join(input_thread, NULL);
|
pthread_join(input_thread, NULL);
|
||||||
|
|
||||||
pthread_mutex_destroy(&client_data->term_channel_lock);
|
pthread_mutex_destroy(&ssh_client->term_channel_lock);
|
||||||
|
|
||||||
guac_client_log(client, GUAC_LOG_INFO, "SSH connection ended.");
|
guac_client_log(client, GUAC_LOG_INFO, "SSH connection ended.");
|
||||||
return NULL;
|
return NULL;
|
115
src/protocols/ssh/ssh.h
Normal file
115
src/protocols/ssh/ssh.h
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
/*
|
||||||
|
* 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_H
|
||||||
|
#define GUAC_SSH_H
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "guac_sftp.h"
|
||||||
|
#include "guac_ssh.h"
|
||||||
|
#include "guac_ssh_user.h"
|
||||||
|
#include "settings.h"
|
||||||
|
#include "terminal.h"
|
||||||
|
|
||||||
|
#ifdef ENABLE_SSH_AGENT
|
||||||
|
#include "ssh_agent.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <guacamole/client.h>
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSH-specific client data.
|
||||||
|
*/
|
||||||
|
typedef struct guac_ssh_client {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSH connection settings.
|
||||||
|
*/
|
||||||
|
guac_ssh_settings* settings;
|
||||||
|
|
||||||
|
#ifdef ENABLE_SSH_AGENT
|
||||||
|
/**
|
||||||
|
* The current agent, if any.
|
||||||
|
*/
|
||||||
|
ssh_auth_agent* auth_agent;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The SSH client thread.
|
||||||
|
*/
|
||||||
|
pthread_t client_thread;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The user and credentials to use for all SSH sessions.
|
||||||
|
*/
|
||||||
|
guac_common_ssh_user* user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSH session, used by the SSH client thread.
|
||||||
|
*/
|
||||||
|
guac_common_ssh_session* session;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SFTP session, used by the SFTP client/filesystem.
|
||||||
|
*/
|
||||||
|
guac_common_ssh_session* sftp_session;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The filesystem object exposed for the SFTP session.
|
||||||
|
*/
|
||||||
|
guac_common_ssh_sftp_filesystem* sftp_filesystem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSH terminal channel, used by the SSH client thread.
|
||||||
|
*/
|
||||||
|
LIBSSH2_CHANNEL* term_channel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lock dictating access to the SSH terminal channel.
|
||||||
|
*/
|
||||||
|
pthread_mutex_t term_channel_lock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The terminal which will render all output from the SSH client.
|
||||||
|
*/
|
||||||
|
guac_terminal* term;
|
||||||
|
|
||||||
|
} guac_ssh_client ;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main SSH client thread, handling transfer of SSH output to STDOUT.
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* The guac_client to associate with a new SSH session, once the SSH
|
||||||
|
* connection succeeds.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* NULL in all cases. The return value of this thread is expected to be
|
||||||
|
* ignored.
|
||||||
|
*/
|
||||||
|
void* ssh_client_thread(void* data);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
87
src/protocols/ssh/user.c
Normal file
87
src/protocols/ssh/user.c
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 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 "clipboard.h"
|
||||||
|
#include "input.h"
|
||||||
|
#include "guac_display.h"
|
||||||
|
#include "user.h"
|
||||||
|
#include "sftp.h"
|
||||||
|
#include "ssh.h"
|
||||||
|
#include "settings.h"
|
||||||
|
|
||||||
|
#include <guacamole/client.h>
|
||||||
|
#include <guacamole/socket.h>
|
||||||
|
#include <guacamole/user.h>
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
int guac_ssh_user_join_handler(guac_user* user, int argc, char** argv) {
|
||||||
|
|
||||||
|
guac_client* client = user->client;
|
||||||
|
guac_ssh_client* ssh_client = (guac_ssh_client*) client->data;
|
||||||
|
|
||||||
|
/* Connect via SSH if owner */
|
||||||
|
if (user->owner) {
|
||||||
|
|
||||||
|
/* Parse arguments into client */
|
||||||
|
guac_ssh_settings* settings = ssh_client->settings =
|
||||||
|
guac_ssh_parse_args(user, argc, (const char**) argv);
|
||||||
|
|
||||||
|
/* Fail if settings cannot be parsed */
|
||||||
|
if (settings == NULL) {
|
||||||
|
guac_user_log(user, GUAC_LOG_INFO,
|
||||||
|
"Badly formatted client arguments.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start client thread */
|
||||||
|
if (pthread_create(&(ssh_client->client_thread), NULL,
|
||||||
|
ssh_client_thread, (void*) client)) {
|
||||||
|
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
|
||||||
|
"Unable to start SSH client thread");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If not owner, synchronize with current display */
|
||||||
|
else {
|
||||||
|
guac_terminal_dup(ssh_client->term, user, user->socket);
|
||||||
|
guac_socket_flush(user->socket);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set per-user event handlers */
|
||||||
|
user->key_handler = guac_ssh_user_key_handler;
|
||||||
|
user->mouse_handler = guac_ssh_user_mouse_handler;
|
||||||
|
user->size_handler = guac_ssh_user_size_handler;
|
||||||
|
user->clipboard_handler = guac_ssh_clipboard_handler;
|
||||||
|
|
||||||
|
/* Set generic (non-filesystem) file upload handler */
|
||||||
|
user->file_handler = guac_sftp_file_handler;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2013 Glyptodon LLC
|
* Copyright (C) 2014 Glyptodon LLC
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -20,18 +20,17 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef GUAC_VNC_USER_H
|
||||||
#ifndef __SSH_CLIENT_H
|
#define GUAC_VNC_USER_H
|
||||||
#define __SSH_CLIENT_H
|
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <guacamole/client.h>
|
#include <guacamole/user.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main SSH client thread, handling transfer of SSH output to STDOUT.
|
* Handler for joining users.
|
||||||
*/
|
*/
|
||||||
void* ssh_client_thread(void* data);
|
guac_user_join_handler guac_ssh_user_join_handler;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue
Block a user