2013-12-29 04:53:12 +00:00
|
|
|
/*
|
2016-03-25 19:59:40 +00:00
|
|
|
* Licensed to the Apache Software Foundation (ASF) under one
|
|
|
|
* or more contributor license agreements. See the NOTICE file
|
|
|
|
* distributed with this work for additional information
|
|
|
|
* regarding copyright ownership. The ASF licenses this file
|
|
|
|
* to you under the Apache License, Version 2.0 (the
|
|
|
|
* "License"); you may not use this file except in compliance
|
|
|
|
* with the License. You may obtain a copy of the License at
|
2013-05-18 03:47:05 +00:00
|
|
|
*
|
2016-03-25 19:59:40 +00:00
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
2013-05-18 03:47:05 +00:00
|
|
|
*
|
2016-03-25 19:59:40 +00:00
|
|
|
* Unless required by applicable law or agreed to in writing,
|
|
|
|
* software distributed under the License is distributed on an
|
|
|
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
|
|
* KIND, either express or implied. See the License for the
|
|
|
|
* specific language governing permissions and limitations
|
|
|
|
* under the License.
|
2013-12-29 04:53:12 +00:00
|
|
|
*/
|
|
|
|
|
2014-01-01 22:44:28 +00:00
|
|
|
#include "config.h"
|
2013-05-18 03:47:05 +00:00
|
|
|
|
2016-09-12 00:28:50 +00:00
|
|
|
#include "common/recording.h"
|
2017-02-27 22:28:23 +00:00
|
|
|
#include "common-ssh/sftp.h"
|
|
|
|
#include "common-ssh/ssh.h"
|
2016-03-01 05:51:42 +00:00
|
|
|
#include "settings.h"
|
2014-01-01 22:44:28 +00:00
|
|
|
#include "sftp.h"
|
2016-03-01 05:51:42 +00:00
|
|
|
#include "ssh.h"
|
2017-02-27 22:34:46 +00:00
|
|
|
#include "terminal/terminal.h"
|
2014-06-11 17:06:18 +00:00
|
|
|
|
|
|
|
#ifdef ENABLE_SSH_AGENT
|
|
|
|
#include "ssh_agent.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <libssh2.h>
|
|
|
|
#include <libssh2_sftp.h>
|
|
|
|
#include <guacamole/client.h>
|
2015-01-05 03:56:01 +00:00
|
|
|
#include <openssl/err.h>
|
2014-06-11 17:06:18 +00:00
|
|
|
#include <openssl/ssl.h>
|
2014-01-01 22:44:28 +00:00
|
|
|
|
2014-07-21 17:27:39 +00:00
|
|
|
#ifdef LIBSSH2_USES_GCRYPT
|
|
|
|
#include <gcrypt.h>
|
|
|
|
#endif
|
|
|
|
|
2014-01-01 22:44:28 +00:00
|
|
|
#include <errno.h>
|
|
|
|
#include <netdb.h>
|
|
|
|
#include <netinet/in.h>
|
2016-11-11 21:16:57 +00:00
|
|
|
#include <poll.h>
|
2014-01-01 22:44:28 +00:00
|
|
|
#include <pthread.h>
|
2014-06-11 17:06:18 +00:00
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stddef.h>
|
2013-05-18 03:47:05 +00:00
|
|
|
#include <stdio.h>
|
2014-06-11 17:06:18 +00:00
|
|
|
#include <stdlib.h>
|
2013-05-18 05:53:13 +00:00
|
|
|
#include <string.h>
|
2013-12-02 00:09:48 +00:00
|
|
|
#include <sys/socket.h>
|
2014-06-11 17:06:18 +00:00
|
|
|
#include <sys/time.h>
|
2013-12-02 10:00:37 +00:00
|
|
|
|
2014-06-13 16:12:51 +00:00
|
|
|
/**
|
2015-07-10 19:43:35 +00:00
|
|
|
* Produces a new user object containing a username and password or private
|
|
|
|
* key, prompting the user as necessary to obtain that information.
|
|
|
|
*
|
|
|
|
* @param client
|
|
|
|
* The Guacamole client containing any existing user data, as well as the
|
|
|
|
* terminal to use when prompting the user.
|
|
|
|
*
|
|
|
|
* @return
|
2017-09-29 17:44:24 +00:00
|
|
|
* A new user object containing the user's username and other credentials,
|
|
|
|
* or NULL if fails to import key.
|
2014-06-13 16:12:51 +00:00
|
|
|
*/
|
2015-07-10 19:43:35 +00:00
|
|
|
static guac_common_ssh_user* guac_ssh_get_user(guac_client* client) {
|
2013-12-02 00:09:48 +00:00
|
|
|
|
2016-03-01 05:51:42 +00:00
|
|
|
guac_ssh_client* ssh_client = (guac_ssh_client*) client->data;
|
|
|
|
guac_ssh_settings* settings = ssh_client->settings;
|
2013-12-02 00:26:41 +00:00
|
|
|
|
2015-07-10 19:43:35 +00:00
|
|
|
guac_common_ssh_user* user;
|
2013-10-19 05:05:03 +00:00
|
|
|
|
2015-07-10 19:43:35 +00:00
|
|
|
/* Get username */
|
2016-03-01 05:51:42 +00:00
|
|
|
if (settings->username == NULL)
|
|
|
|
settings->username = guac_terminal_prompt(ssh_client->term,
|
|
|
|
"Login as: ", true);
|
2013-10-19 05:05:03 +00:00
|
|
|
|
2015-07-10 19:43:35 +00:00
|
|
|
/* Create user object from username */
|
2016-03-01 05:51:42 +00:00
|
|
|
user = guac_common_ssh_create_user(settings->username);
|
2013-12-02 00:26:41 +00:00
|
|
|
|
2015-07-10 19:43:35 +00:00
|
|
|
/* If key specified, import */
|
2016-03-01 05:51:42 +00:00
|
|
|
if (settings->key_base64 != NULL) {
|
2013-12-02 00:09:48 +00:00
|
|
|
|
2015-07-10 19:43:35 +00:00
|
|
|
guac_client_log(client, GUAC_LOG_DEBUG,
|
|
|
|
"Attempting private key import (WITHOUT passphrase)");
|
2013-12-02 00:26:41 +00:00
|
|
|
|
2015-07-10 19:43:35 +00:00
|
|
|
/* Attempt to read key without passphrase */
|
|
|
|
if (guac_common_ssh_user_import_key(user,
|
2016-03-01 05:51:42 +00:00
|
|
|
settings->key_base64, NULL)) {
|
2013-12-02 00:26:41 +00:00
|
|
|
|
2015-07-10 19:43:35 +00:00
|
|
|
/* Log failure of initial attempt */
|
|
|
|
guac_client_log(client, GUAC_LOG_DEBUG,
|
|
|
|
"Initial import failed: %s",
|
|
|
|
guac_common_ssh_key_error());
|
|
|
|
|
|
|
|
guac_client_log(client, GUAC_LOG_DEBUG,
|
|
|
|
"Re-attempting private key import (WITH passphrase)");
|
2013-12-02 00:26:41 +00:00
|
|
|
|
2015-07-10 19:43:35 +00:00
|
|
|
/* Prompt for passphrase if missing */
|
2016-03-01 05:51:42 +00:00
|
|
|
if (settings->key_passphrase == NULL)
|
|
|
|
settings->key_passphrase =
|
|
|
|
guac_terminal_prompt(ssh_client->term,
|
|
|
|
"Key passphrase: ", false);
|
2013-12-02 00:26:41 +00:00
|
|
|
|
2015-07-10 19:43:35 +00:00
|
|
|
/* Reattempt import with passphrase */
|
|
|
|
if (guac_common_ssh_user_import_key(user,
|
2016-03-01 05:51:42 +00:00
|
|
|
settings->key_base64,
|
|
|
|
settings->key_passphrase)) {
|
2013-12-02 00:26:41 +00:00
|
|
|
|
2015-07-10 19:43:35 +00:00
|
|
|
/* If still failing, give up */
|
|
|
|
guac_client_abort(client,
|
|
|
|
GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED,
|
|
|
|
"Auth key import failed: %s",
|
|
|
|
guac_common_ssh_key_error());
|
2013-12-02 00:26:41 +00:00
|
|
|
|
2015-07-10 19:43:35 +00:00
|
|
|
guac_common_ssh_destroy_user(user);
|
|
|
|
return NULL;
|
2013-12-02 00:26:41 +00:00
|
|
|
|
2015-07-10 19:43:35 +00:00
|
|
|
}
|
2013-12-02 00:26:41 +00:00
|
|
|
|
2015-07-10 19:43:35 +00:00
|
|
|
} /* end decrypt key with passphrase */
|
2013-12-02 00:26:41 +00:00
|
|
|
|
2015-07-10 19:43:35 +00:00
|
|
|
/* Success */
|
|
|
|
guac_client_log(client, GUAC_LOG_INFO,
|
|
|
|
"Auth key successfully imported.");
|
2013-12-02 00:26:41 +00:00
|
|
|
|
2015-07-10 19:43:35 +00:00
|
|
|
} /* end if key given */
|
2013-12-02 00:26:41 +00:00
|
|
|
|
2015-07-24 20:20:36 +00:00
|
|
|
/* Otherwise, use password */
|
|
|
|
else {
|
2013-12-02 00:09:48 +00:00
|
|
|
|
2015-07-24 20:20:36 +00:00
|
|
|
/* Get password if not provided */
|
2016-03-01 05:51:42 +00:00
|
|
|
if (settings->password == NULL)
|
|
|
|
settings->password = guac_terminal_prompt(ssh_client->term,
|
|
|
|
"Password: ", false);
|
2013-12-02 00:09:48 +00:00
|
|
|
|
2015-07-24 20:20:36 +00:00
|
|
|
/* Set provided password */
|
2016-03-01 05:51:42 +00:00
|
|
|
guac_common_ssh_user_set_password(user, settings->password);
|
2013-12-02 17:23:31 +00:00
|
|
|
|
2013-12-02 00:09:48 +00:00
|
|
|
}
|
|
|
|
|
2015-07-10 19:43:35 +00:00
|
|
|
/* Clear screen of any prompts */
|
2016-03-01 05:51:42 +00:00
|
|
|
guac_terminal_printf(ssh_client->term, "\x1B[H\x1B[J");
|
2013-10-19 05:05:03 +00:00
|
|
|
|
2015-07-10 19:43:35 +00:00
|
|
|
return user;
|
2013-12-03 10:22:46 +00:00
|
|
|
|
2015-07-10 19:43:35 +00:00
|
|
|
}
|
2014-06-13 16:12:51 +00:00
|
|
|
|
2015-07-10 19:43:35 +00:00
|
|
|
void* ssh_input_thread(void* data) {
|
2014-07-17 18:49:24 +00:00
|
|
|
|
2015-07-10 19:43:35 +00:00
|
|
|
guac_client* client = (guac_client*) data;
|
2016-03-01 05:51:42 +00:00
|
|
|
guac_ssh_client* ssh_client = (guac_ssh_client*) client->data;
|
2014-06-13 16:12:51 +00:00
|
|
|
|
2015-07-10 19:43:35 +00:00
|
|
|
char buffer[8192];
|
|
|
|
int bytes_read;
|
2013-10-31 06:19:11 +00:00
|
|
|
|
2015-07-10 19:43:35 +00:00
|
|
|
/* Write all data read */
|
2016-03-01 05:51:42 +00:00
|
|
|
while ((bytes_read = guac_terminal_read_stdin(ssh_client->term, buffer, sizeof(buffer))) > 0) {
|
|
|
|
pthread_mutex_lock(&(ssh_client->term_channel_lock));
|
|
|
|
libssh2_channel_write(ssh_client->term_channel, buffer, bytes_read);
|
|
|
|
pthread_mutex_unlock(&(ssh_client->term_channel_lock));
|
2017-10-18 19:08:32 +00:00
|
|
|
|
|
|
|
/* Make sure ssh_input_thread can be terminated anyway */
|
|
|
|
if (client->state == GUAC_CLIENT_STOPPING)
|
|
|
|
break;
|
2014-07-17 18:49:24 +00:00
|
|
|
}
|
|
|
|
|
2017-10-18 19:08:32 +00:00
|
|
|
/* Stop the client so that ssh_client_thread can be terminated */
|
|
|
|
guac_client_stop(client);
|
2015-07-10 19:43:35 +00:00
|
|
|
return NULL;
|
2013-10-19 05:05:03 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-05-18 05:53:13 +00:00
|
|
|
void* ssh_client_thread(void* data) {
|
|
|
|
|
2013-05-20 07:33:17 +00:00
|
|
|
guac_client* client = (guac_client*) data;
|
2016-03-01 05:51:42 +00:00
|
|
|
guac_ssh_client* ssh_client = (guac_ssh_client*) client->data;
|
|
|
|
guac_ssh_settings* settings = ssh_client->settings;
|
2013-05-20 07:33:17 +00:00
|
|
|
|
2013-05-20 08:23:21 +00:00
|
|
|
char buffer[8192];
|
|
|
|
|
|
|
|
pthread_t input_thread;
|
2013-05-18 05:53:13 +00:00
|
|
|
|
2015-07-09 19:02:31 +00:00
|
|
|
/* Init SSH base libraries */
|
2016-04-19 01:15:32 +00:00
|
|
|
if (guac_common_ssh_init(client)) {
|
|
|
|
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
|
|
|
|
"SSH library initialization failed");
|
2014-07-21 17:27:39 +00:00
|
|
|
return NULL;
|
2016-04-19 01:15:32 +00:00
|
|
|
}
|
2013-12-02 00:56:36 +00:00
|
|
|
|
2016-02-29 07:51:46 +00:00
|
|
|
/* Set up screen recording, if requested */
|
|
|
|
if (settings->recording_path != NULL) {
|
2017-11-27 17:38:20 +00:00
|
|
|
ssh_client->recording = guac_common_recording_create(client,
|
2016-02-29 07:51:46 +00:00
|
|
|
settings->recording_path,
|
|
|
|
settings->recording_name,
|
|
|
|
settings->create_recording_path);
|
|
|
|
}
|
|
|
|
|
2016-03-01 05:51:42 +00:00
|
|
|
/* Create terminal */
|
|
|
|
ssh_client->term = guac_terminal_create(client,
|
|
|
|
settings->font_name, settings->font_size,
|
|
|
|
settings->resolution, settings->width, settings->height,
|
|
|
|
settings->color_scheme);
|
|
|
|
|
|
|
|
/* Fail if terminal init failed */
|
|
|
|
if (ssh_client->term == NULL) {
|
|
|
|
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
|
|
|
|
"Terminal initialization failed");
|
|
|
|
return NULL;
|
|
|
|
}
|
2013-05-18 05:53:13 +00:00
|
|
|
|
2016-03-01 05:51:42 +00:00
|
|
|
/* 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);
|
2017-09-29 17:44:24 +00:00
|
|
|
if (ssh_client->user == NULL) {
|
|
|
|
/* Already aborted within guac_ssh_get_user() */
|
|
|
|
return NULL;
|
|
|
|
}
|
2013-05-26 06:15:55 +00:00
|
|
|
|
2013-05-20 08:23:21 +00:00
|
|
|
/* Open SSH session */
|
2016-03-01 05:51:42 +00:00
|
|
|
ssh_client->session = guac_common_ssh_create_session(client,
|
2017-06-01 00:17:00 +00:00
|
|
|
settings->hostname, settings->port, ssh_client->user, settings->server_alive_interval);
|
2016-03-01 05:51:42 +00:00
|
|
|
if (ssh_client->session == NULL) {
|
2015-07-10 19:43:35 +00:00
|
|
|
/* Already aborted within guac_common_ssh_create_session() */
|
2013-05-20 08:23:21 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2016-03-01 05:51:42 +00:00
|
|
|
pthread_mutex_init(&ssh_client->term_channel_lock, NULL);
|
2014-07-21 18:23:36 +00:00
|
|
|
|
2013-05-20 08:23:21 +00:00
|
|
|
/* Open channel for terminal */
|
2016-03-01 05:51:42 +00:00
|
|
|
ssh_client->term_channel =
|
|
|
|
libssh2_channel_open_session(ssh_client->session->session);
|
|
|
|
if (ssh_client->term_channel == NULL) {
|
2015-07-10 19:43:35 +00:00
|
|
|
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR,
|
|
|
|
"Unable to open terminal channel.");
|
2013-05-18 05:53:13 +00:00
|
|
|
return NULL;
|
2013-05-20 08:23:21 +00:00
|
|
|
}
|
2013-05-18 05:53:13 +00:00
|
|
|
|
2013-12-02 10:00:37 +00:00
|
|
|
#ifdef ENABLE_SSH_AGENT
|
2013-12-02 10:07:17 +00:00
|
|
|
/* Start SSH agent forwarding, if enabled */
|
2016-03-01 05:51:42 +00:00
|
|
|
if (ssh_client->enable_agent) {
|
|
|
|
libssh2_session_callback_set(ssh_client->session,
|
2013-12-02 10:07:17 +00:00
|
|
|
LIBSSH2_CALLBACK_AUTH_AGENT, (void*) ssh_auth_agent_callback);
|
|
|
|
|
|
|
|
/* Request agent forwarding */
|
2016-03-01 05:51:42 +00:00
|
|
|
if (libssh2_channel_request_auth_agent(ssh_client->term_channel))
|
2014-11-08 00:32:19 +00:00
|
|
|
guac_client_log(client, GUAC_LOG_ERROR, "Agent forwarding request failed");
|
2013-12-02 10:07:17 +00:00
|
|
|
else
|
2014-11-08 00:32:19 +00:00
|
|
|
guac_client_log(client, GUAC_LOG_INFO, "Agent forwarding enabled.");
|
2013-12-02 10:07:17 +00:00
|
|
|
}
|
2013-12-03 10:22:46 +00:00
|
|
|
|
2016-03-01 05:51:42 +00:00
|
|
|
ssh_client->auth_agent = NULL;
|
2013-12-02 10:00:37 +00:00
|
|
|
#endif
|
|
|
|
|
2013-10-18 22:37:16 +00:00
|
|
|
/* Start SFTP session as well, if enabled */
|
2016-03-01 05:51:42 +00:00
|
|
|
if (settings->enable_sftp) {
|
2013-10-18 22:37:16 +00:00
|
|
|
|
2013-10-19 05:05:03 +00:00
|
|
|
/* Create SSH session specific for SFTP */
|
2014-11-29 01:20:02 +00:00
|
|
|
guac_client_log(client, GUAC_LOG_DEBUG, "Reconnecting for SFTP...");
|
2016-03-01 05:51:42 +00:00
|
|
|
ssh_client->sftp_session =
|
|
|
|
guac_common_ssh_create_session(client, settings->hostname,
|
2017-06-01 00:17:00 +00:00
|
|
|
settings->port, ssh_client->user, settings->server_alive_interval);
|
2016-03-01 05:51:42 +00:00
|
|
|
if (ssh_client->sftp_session == NULL) {
|
2015-07-10 19:43:35 +00:00
|
|
|
/* Already aborted within guac_common_ssh_create_session() */
|
2014-03-22 02:47:42 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2013-10-19 05:05:03 +00:00
|
|
|
|
2013-10-18 22:37:16 +00:00
|
|
|
/* Request SFTP */
|
2016-03-01 05:51:42 +00:00
|
|
|
ssh_client->sftp_filesystem = guac_common_ssh_create_sftp_filesystem(
|
2017-06-29 22:48:23 +00:00
|
|
|
ssh_client->sftp_session, settings->sftp_root_directory,
|
|
|
|
NULL);
|
2013-10-18 22:37:16 +00:00
|
|
|
|
2016-03-01 05:51:42 +00:00
|
|
|
/* Expose filesystem to connection owner */
|
|
|
|
guac_client_for_owner(client,
|
|
|
|
guac_common_ssh_expose_sftp_filesystem,
|
|
|
|
ssh_client->sftp_filesystem);
|
2013-10-19 01:20:46 +00:00
|
|
|
|
2015-07-11 00:21:58 +00:00
|
|
|
/* Init handlers for Guacamole-specific console codes */
|
2016-03-01 05:51:42 +00:00
|
|
|
ssh_client->term->upload_path_handler = guac_sftp_set_upload_path;
|
|
|
|
ssh_client->term->file_download_handler = guac_sftp_download_file;
|
2015-06-21 05:17:27 +00:00
|
|
|
|
2014-11-29 01:20:02 +00:00
|
|
|
guac_client_log(client, GUAC_LOG_DEBUG, "SFTP session initialized");
|
2013-10-18 22:37:16 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-05-20 08:23:21 +00:00
|
|
|
/* Request PTY */
|
2016-03-01 05:51:42 +00:00
|
|
|
if (libssh2_channel_request_pty_ex(ssh_client->term_channel, "linux", sizeof("linux")-1, NULL, 0,
|
|
|
|
ssh_client->term->term_width, ssh_client->term->term_height, 0, 0)) {
|
2014-03-22 02:47:42 +00:00
|
|
|
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, "Unable to allocate PTY.");
|
2013-05-20 08:23:21 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-10-19 22:58:44 +00:00
|
|
|
/* If a command is specified, run that instead of a shell */
|
2016-03-01 05:51:42 +00:00
|
|
|
if (settings->command != NULL) {
|
|
|
|
if (libssh2_channel_exec(ssh_client->term_channel, settings->command)) {
|
2015-10-19 22:58:44 +00:00
|
|
|
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR,
|
|
|
|
"Unable to execute command.");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Otherwise, request a shell */
|
2016-03-01 05:51:42 +00:00
|
|
|
else if (libssh2_channel_shell(ssh_client->term_channel)) {
|
2015-10-19 22:58:44 +00:00
|
|
|
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR,
|
|
|
|
"Unable to associate shell with PTY.");
|
2013-05-20 08:23:21 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Logged in */
|
2014-11-08 00:32:19 +00:00
|
|
|
guac_client_log(client, GUAC_LOG_INFO, "SSH connection successful.");
|
2013-05-20 08:23:21 +00:00
|
|
|
|
|
|
|
/* Start input thread */
|
|
|
|
if (pthread_create(&(input_thread), NULL, ssh_input_thread, (void*) client)) {
|
2014-03-22 02:47:42 +00:00
|
|
|
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Unable to start input thread");
|
2013-05-20 08:23:21 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-12-03 10:22:46 +00:00
|
|
|
/* Set non-blocking */
|
2016-03-01 05:51:42 +00:00
|
|
|
libssh2_session_set_blocking(ssh_client->session->session, 0);
|
2013-12-03 10:22:46 +00:00
|
|
|
|
2013-05-20 08:23:21 +00:00
|
|
|
/* While data available, write to terminal */
|
2015-07-10 19:43:35 +00:00
|
|
|
int bytes_read = 0;
|
2014-07-21 18:15:55 +00:00
|
|
|
for (;;) {
|
2013-05-20 17:44:43 +00:00
|
|
|
|
2013-12-03 10:22:46 +00:00
|
|
|
/* Track total amount of data read */
|
|
|
|
int total_read = 0;
|
|
|
|
|
2017-06-25 09:13:22 +00:00
|
|
|
/* Timeout for polling socket activity */
|
|
|
|
int timeout;
|
2017-05-31 12:00:09 +00:00
|
|
|
|
2016-03-01 05:51:42 +00:00
|
|
|
pthread_mutex_lock(&(ssh_client->term_channel_lock));
|
2014-07-21 18:15:55 +00:00
|
|
|
|
|
|
|
/* Stop reading at EOF */
|
2016-03-01 05:51:42 +00:00
|
|
|
if (libssh2_channel_eof(ssh_client->term_channel)) {
|
|
|
|
pthread_mutex_unlock(&(ssh_client->term_channel_lock));
|
2014-07-21 18:15:55 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-10-18 19:08:32 +00:00
|
|
|
/* Client is stopping, break the loop */
|
|
|
|
if (client->state == GUAC_CLIENT_STOPPING) {
|
|
|
|
pthread_mutex_unlock(&(ssh_client->term_channel_lock));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-05-31 12:00:09 +00:00
|
|
|
/* Send keepalive at configured interval */
|
2017-05-31 23:47:09 +00:00
|
|
|
if (settings->server_alive_interval > 0) {
|
2017-06-25 09:13:22 +00:00
|
|
|
timeout = 0;
|
2017-06-14 12:27:09 +00:00
|
|
|
if (libssh2_keepalive_send(ssh_client->session->session, &timeout) > 0)
|
2017-05-31 12:00:09 +00:00
|
|
|
break;
|
2017-06-25 09:13:22 +00:00
|
|
|
timeout *= 1000;
|
2017-05-31 12:00:09 +00:00
|
|
|
}
|
2017-05-31 12:30:56 +00:00
|
|
|
/* If keepalive is not configured, sleep for the default of 1 second */
|
2017-05-31 12:00:09 +00:00
|
|
|
else
|
2017-06-25 09:13:22 +00:00
|
|
|
timeout = GUAC_SSH_DEFAULT_POLL_TIMEOUT;
|
2017-05-31 12:00:09 +00:00
|
|
|
|
2013-12-03 10:22:46 +00:00
|
|
|
/* Read terminal data */
|
2016-03-01 05:51:42 +00:00
|
|
|
bytes_read = libssh2_channel_read(ssh_client->term_channel,
|
2013-12-03 10:22:46 +00:00
|
|
|
buffer, sizeof(buffer));
|
2013-05-20 08:23:21 +00:00
|
|
|
|
2016-03-01 05:51:42 +00:00
|
|
|
pthread_mutex_unlock(&(ssh_client->term_channel_lock));
|
2014-07-21 18:15:55 +00:00
|
|
|
|
2013-05-26 06:05:58 +00:00
|
|
|
/* Attempt to write data received. Exit on failure. */
|
|
|
|
if (bytes_read > 0) {
|
2017-01-15 23:31:33 +00:00
|
|
|
int written = guac_terminal_write(ssh_client->term, buffer, bytes_read);
|
2013-05-26 06:05:58 +00:00
|
|
|
if (written < 0)
|
|
|
|
break;
|
2013-12-03 10:22:46 +00:00
|
|
|
|
|
|
|
total_read += bytes_read;
|
|
|
|
}
|
|
|
|
|
2014-01-02 04:18:43 +00:00
|
|
|
else if (bytes_read < 0 && bytes_read != LIBSSH2_ERROR_EAGAIN)
|
|
|
|
break;
|
|
|
|
|
2013-12-03 17:21:47 +00:00
|
|
|
#ifdef ENABLE_SSH_AGENT
|
2013-12-03 10:22:46 +00:00
|
|
|
/* If agent open, handle any agent packets */
|
2016-03-01 05:51:42 +00:00
|
|
|
if (ssh_client->auth_agent != NULL) {
|
|
|
|
bytes_read = ssh_auth_agent_read(ssh_client->auth_agent);
|
2013-12-03 10:22:46 +00:00
|
|
|
if (bytes_read > 0)
|
|
|
|
total_read += bytes_read;
|
|
|
|
else if (bytes_read < 0 && bytes_read != LIBSSH2_ERROR_EAGAIN)
|
2016-03-01 05:51:42 +00:00
|
|
|
ssh_client->auth_agent = NULL;
|
2013-12-03 10:22:46 +00:00
|
|
|
}
|
2013-12-03 17:21:47 +00:00
|
|
|
#endif
|
2013-12-03 10:22:46 +00:00
|
|
|
|
|
|
|
/* Wait for more data if reads turn up empty */
|
|
|
|
if (total_read == 0) {
|
|
|
|
|
2016-11-11 21:16:57 +00:00
|
|
|
/* Wait on the SSH session file descriptor only */
|
|
|
|
struct pollfd fds[] = {{
|
|
|
|
.fd = ssh_client->session->fd,
|
|
|
|
.events = POLLIN,
|
|
|
|
.revents = 0,
|
|
|
|
}};
|
2013-12-03 10:22:46 +00:00
|
|
|
|
2017-06-25 09:13:22 +00:00
|
|
|
/* Wait up to computed timeout */
|
|
|
|
if (poll(fds, 1, timeout) < 0)
|
2013-12-03 10:22:46 +00:00
|
|
|
break;
|
2016-11-11 21:16:57 +00:00
|
|
|
|
2013-05-26 06:05:58 +00:00
|
|
|
}
|
2013-05-20 08:23:21 +00:00
|
|
|
|
|
|
|
}
|
2013-05-18 05:53:13 +00:00
|
|
|
|
2014-01-02 04:18:43 +00:00
|
|
|
/* Kill client and Wait for input thread to die */
|
|
|
|
guac_client_stop(client);
|
2013-05-20 17:52:47 +00:00
|
|
|
pthread_join(input_thread, NULL);
|
|
|
|
|
2016-03-01 05:51:42 +00:00
|
|
|
pthread_mutex_destroy(&ssh_client->term_channel_lock);
|
2014-07-21 18:15:55 +00:00
|
|
|
|
2014-11-08 00:32:19 +00:00
|
|
|
guac_client_log(client, GUAC_LOG_INFO, "SSH connection ended.");
|
2013-05-18 03:47:05 +00:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
}
|
|
|
|
|