diff options
author | Giovanni Panozzo <giovanni@panozzo.it> | 2020-03-24 23:30:45 +0300 |
---|---|---|
committer | Giovanni Panozzo <giovanni@panozzo.it> | 2020-03-24 23:30:45 +0300 |
commit | 3ac0453ef8a2c27b599efc8e5004ebda1c6727d5 (patch) | |
tree | 2fd5ed8c4ef68cceb2d663f2f497de15f791263f | |
parent | bb35b9fed7dd4e38d9d8deb98c52320bf43e50f7 (diff) | |
parent | f5eac78f19997d13349c6ee29b2091a48ae1f570 (diff) |
Merge branch 'issue_2102' into 'master'
SFTP with tunnel fixes
See merge request Remmina/Remmina!2023
-rw-r--r-- | src/remmina_protocol_widget.c | 184 | ||||
-rw-r--r-- | src/remmina_sftp_client.c | 43 | ||||
-rw-r--r-- | src/remmina_sftp_client.h | 4 | ||||
-rw-r--r-- | src/remmina_sftp_plugin.c | 74 | ||||
-rw-r--r-- | src/remmina_sftp_plugin.h | 1 | ||||
-rw-r--r-- | src/remmina_ssh.c | 73 | ||||
-rw-r--r-- | src/remmina_ssh.h | 11 | ||||
-rw-r--r-- | src/remmina_ssh_plugin.c | 46 |
8 files changed, 288 insertions, 148 deletions
diff --git a/src/remmina_protocol_widget.c b/src/remmina_protocol_widget.c index 1ebedf7d2..2bbc11a50 100644 --- a/src/remmina_protocol_widget.c +++ b/src/remmina_protocol_widget.c @@ -70,7 +70,9 @@ struct _RemminaProtocolWidgetPriv { gboolean has_error; gchar * error_message; - RemminaSSHTunnel * ssh_tunnel; + /* ssh_tunnels is an array of RemminaSSHTunnel* + * the 1st one is the "main" tunnel, other tunnels are used for example in sftp commands */ + GPtrArray * ssh_tunnels; RemminaTunnelInitFunc init_func; GtkWidget * chat_window; @@ -143,6 +145,25 @@ static void remmina_protocol_widget_class_init(RemminaProtocolWidgetClass *klass g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } + +static void remmina_protocol_widget_close_all_tunnels(RemminaProtocolWidget *gp) +{ + TRACE_CALL(__func__); + int i; + + if (gp->priv->ssh_tunnels) { + for(i = 0;i < gp->priv->ssh_tunnels->len; i++) { +#ifdef HAVE_LIBSSH + remmina_ssh_tunnel_free((RemminaSSHTunnel *)gp->priv->ssh_tunnels->pdata[i]); +#else + g_debug ("LibSSH support turned off, no need to free SSH tunnel data"); +#endif + } + } + g_ptr_array_set_size(gp->priv->ssh_tunnels, 0); +} + + static void remmina_protocol_widget_destroy(RemminaProtocolWidget *gp, gpointer data) { TRACE_CALL(__func__); @@ -179,9 +200,13 @@ static void remmina_protocol_widget_destroy(RemminaProtocolWidget *gp, gpointer g_free(gp->priv); gp->priv = NULL; -} + remmina_protocol_widget_close_all_tunnels(gp); + g_ptr_array_free(gp->priv->ssh_tunnels, TRUE); + + gp->priv->ssh_tunnels = NULL; +} void remmina_protocol_widget_grab_focus(RemminaProtocolWidget *gp) { @@ -204,6 +229,7 @@ static void remmina_protocol_widget_init(RemminaProtocolWidget *gp) priv = g_new0(RemminaProtocolWidgetPriv, 1); gp->priv = priv; gp->priv->closed = TRUE; + gp->priv->ssh_tunnels = g_ptr_array_new(); g_signal_connect(G_OBJECT(gp), "destroy", G_CALLBACK(remmina_protocol_widget_destroy), NULL); } @@ -299,15 +325,12 @@ void remmina_protocol_widget_open_connection(RemminaProtocolWidget *gp) static gboolean conn_closed(gpointer data) { - /* Close ssh tunnel */ TRACE_CALL(__func__); RemminaProtocolWidget *gp = (RemminaProtocolWidget *)data; #ifdef HAVE_LIBSSH - if (gp->priv->ssh_tunnel) { - remmina_ssh_tunnel_free(gp->priv->ssh_tunnel); - gp->priv->ssh_tunnel = NULL; - } + /* This will close all tunnels */ + remmina_protocol_widget_close_all_tunnels(gp); #endif /* Exec postcommand */ remmina_ext_exec_new(gp->priv->remmina_file, "postcommand"); @@ -329,11 +352,14 @@ static gboolean conn_opened(gpointer data) { TRACE_CALL(__func__); RemminaProtocolWidget *gp = (RemminaProtocolWidget *)data; + guint i; #ifdef HAVE_LIBSSH - if (gp->priv->ssh_tunnel) - remmina_ssh_tunnel_cancel_accept(gp->priv->ssh_tunnel); - + if (gp->priv->ssh_tunnels) { + for(i = 0;i < gp->priv->ssh_tunnels->len; i++) { + remmina_ssh_tunnel_cancel_accept((RemminaSSHTunnel *)gp->priv->ssh_tunnels->pdata[i]); + } + } #endif if (gp->priv->listen_message_panel) { rco_destroy_message_panel(gp->cnnobj, gp->priv->listen_message_panel); @@ -604,17 +630,19 @@ void remmina_protocol_widget_call_feature_by_ref(RemminaProtocolWidget *gp, cons switch (feature->id) { #ifdef HAVE_LIBSSH case REMMINA_PROTOCOL_FEATURE_TOOL_SSH: - if (gp->priv->ssh_tunnel) { + if (gp->priv->ssh_tunnels && gp->priv->ssh_tunnels->len > 0) { rcw_open_from_file_full( - remmina_file_dup_temp_protocol(gp->priv->remmina_file, "SSH"), NULL, gp->priv->ssh_tunnel, NULL); + remmina_file_dup_temp_protocol(gp->priv->remmina_file, "SSH"), NULL, + (RemminaSSHTunnel *)gp->priv->ssh_tunnels->pdata[0], NULL); return; } break; case REMMINA_PROTOCOL_FEATURE_TOOL_SFTP: - if (gp->priv->ssh_tunnel) { + if (gp->priv->ssh_tunnels && gp->priv->ssh_tunnels->len > 0) { rcw_open_from_file_full( - remmina_file_dup_temp_protocol(gp->priv->remmina_file, "SFTP"), NULL, gp->priv->ssh_tunnel, NULL); + remmina_file_dup_temp_protocol(gp->priv->remmina_file, "SFTP"), NULL, + gp->priv->ssh_tunnels->pdata[0], NULL); return; } break; @@ -701,7 +729,8 @@ static void cancel_init_tunnel_cb(void *cbdata, int btn) { printf("Remmina: Cancelling an opening tunnel is not implemented\n"); } -static gboolean remmina_protocol_widget_init_tunnel(RemminaProtocolWidget *gp) + +static RemminaSSHTunnel* remmina_protocol_widget_init_tunnel(RemminaProtocolWidget *gp) { TRACE_CALL(__func__); RemminaSSHTunnel *tunnel; @@ -709,38 +738,33 @@ static gboolean remmina_protocol_widget_init_tunnel(RemminaProtocolWidget *gp) gchar *msg; RemminaMessagePanel *mp; - /* Reuse existing SSH connection if it’s reconnecting to destination */ - if (gp->priv->ssh_tunnel == NULL) { - tunnel = remmina_ssh_tunnel_new_from_file(gp->priv->remmina_file); + tunnel = remmina_ssh_tunnel_new_from_file(gp->priv->remmina_file); - g_debug ("Connecting to \"%s\" via SSH…", REMMINA_SSH(tunnel)->server); - msg = g_strdup_printf(_("Connecting to \"%s\" via SSH…"), REMMINA_SSH(tunnel)->server); + g_debug ("[RPW] %s creating SSH tunnel to \"%s\" via SSH…", __func__, REMMINA_SSH(tunnel)->server); + msg = g_strdup_printf(_("Connecting to \"%s\" via SSH…"), REMMINA_SSH(tunnel)->server); - mp = remmina_protocol_widget_mpprogress(gp->cnnobj, msg, cancel_init_tunnel_cb, NULL); - g_free(msg); - - if (!remmina_ssh_init_session(REMMINA_SSH(tunnel), TRUE)) { - g_debug ("[SSH] %s Cannot init SSH session with tunnel struct", __func__); - remmina_protocol_widget_set_error(gp, REMMINA_SSH(tunnel)->error); - remmina_ssh_tunnel_free(tunnel); - return FALSE; - } - - ret = remmina_ssh_auth_gui(REMMINA_SSH(tunnel), gp, gp->priv->remmina_file); - g_debug ("[SSH] tunnel auth returned %d", ret); - if (ret != REMMINA_SSH_AUTH_SUCCESS) { - if (ret != REMMINA_SSH_AUTH_USERCANCEL) - remmina_protocol_widget_set_error(gp, REMMINA_SSH(tunnel)->error); - remmina_ssh_tunnel_free(tunnel); - return FALSE; - } + mp = remmina_protocol_widget_mpprogress(gp->cnnobj, msg, cancel_init_tunnel_cb, NULL); + g_free(msg); - remmina_protocol_widget_mpdestroy(gp->cnnobj, mp); + if (!remmina_ssh_init_session(REMMINA_SSH(tunnel))) { + g_debug ("[SSH] %s Cannot init SSH session with tunnel struct", __func__); + remmina_protocol_widget_set_error(gp, REMMINA_SSH(tunnel)->error); + remmina_ssh_tunnel_free(tunnel); + return NULL; + } - gp->priv->ssh_tunnel = tunnel; + ret = remmina_ssh_auth_gui(REMMINA_SSH(tunnel), gp, gp->priv->remmina_file); + g_debug ("[SSH] tunnel auth returned %d", ret); + if (ret != REMMINA_SSH_AUTH_SUCCESS) { + if (ret != REMMINA_SSH_AUTH_USERCANCEL) + remmina_protocol_widget_set_error(gp, REMMINA_SSH(tunnel)->error); + remmina_ssh_tunnel_free(tunnel); + return NULL; } - return TRUE; + remmina_protocol_widget_mpdestroy(gp->cnnobj, mp); + + return tunnel; } #endif @@ -752,6 +776,21 @@ static void cancel_start_direct_tunnel_cb(void *cbdata, int btn) } #endif +static gboolean remmina_protocol_widget_tunnel_destroy(RemminaSSHTunnel *tunnel, gpointer data) +{ + TRACE_CALL(__func__); + RemminaProtocolWidget *gp = REMMINA_PROTOCOL_WIDGET(data); + guint idx; + + if (g_ptr_array_find(gp->priv->ssh_tunnels, tunnel, &idx)) { +#ifdef HAVE_LIBSSH + g_debug("[RPW] tunnel with idx %u has been disconnected", idx); + remmina_ssh_tunnel_free(tunnel); +#endif + g_ptr_array_remove(gp->priv->ssh_tunnels, tunnel); + } + return TRUE; +} /** * Start an SSH tunnel if possible and return the host:port string. @@ -786,6 +825,7 @@ gchar *remmina_protocol_widget_start_direct_tunnel(RemminaProtocolWidget *gp, gi #ifdef HAVE_LIBSSH gchar *msg; RemminaMessagePanel *mp; + RemminaSSHTunnel *tunnel; if (!remmina_file_get_int(gp->priv->remmina_file, "ssh_tunnel_enabled", FALSE)) { dest = g_strdup_printf("[%s]:%i", srv_host, srv_port); @@ -794,13 +834,8 @@ gchar *remmina_protocol_widget_start_direct_tunnel(RemminaProtocolWidget *gp, gi return dest; } - /* If we have a previous SSH tunnel, destroy it */ - if (gp->priv->ssh_tunnel) { - remmina_ssh_tunnel_free(gp->priv->ssh_tunnel); - gp->priv->ssh_tunnel = NULL; - } - - if (!remmina_protocol_widget_init_tunnel(gp)) { + tunnel = remmina_protocol_widget_init_tunnel(gp); + if (!tunnel) { g_free(srv_host); g_free(ssh_tunnel_host); g_debug ("[SSH] %s remmina_protocol_widget_init_tunnel failed with error is %s\n", @@ -820,10 +855,11 @@ gchar *remmina_protocol_widget_start_direct_tunnel(RemminaProtocolWidget *gp, gi } g_debug ("%s: starting tunnel to: %s, port: %d", __func__, ssh_tunnel_host, ssh_tunnel_port); - if (!remmina_ssh_tunnel_open(gp->priv->ssh_tunnel, srv_host, srv_port, remmina_pref.sshtunnel_port)) { + if (!remmina_ssh_tunnel_open(tunnel, srv_host, srv_port, remmina_pref.sshtunnel_port)) { g_free(srv_host); g_free(ssh_tunnel_host); - remmina_protocol_widget_set_error(gp, REMMINA_SSH(gp->priv->ssh_tunnel)->error); + remmina_protocol_widget_set_error(gp, REMMINA_SSH(tunnel)->error); + remmina_ssh_tunnel_free(tunnel); return NULL; } g_free(srv_host); @@ -831,6 +867,11 @@ gchar *remmina_protocol_widget_start_direct_tunnel(RemminaProtocolWidget *gp, gi remmina_protocol_widget_mpdestroy(gp->cnnobj, mp); + tunnel->destroy_func = remmina_protocol_widget_tunnel_destroy; + tunnel->destroy_func_callback_data = (gpointer)gp; + + g_ptr_array_add(gp->priv->ssh_tunnels, tunnel); + return g_strdup_printf("127.0.0.1:%i", remmina_pref.sshtunnel_port); #else @@ -857,22 +898,25 @@ gboolean remmina_protocol_widget_start_reverse_tunnel(RemminaProtocolWidget *gp, #ifdef HAVE_LIBSSH gchar *msg; RemminaMessagePanel *mp; + RemminaSSHTunnel *tunnel; if (!remmina_file_get_int(gp->priv->remmina_file, "ssh_tunnel_enabled", FALSE)) return TRUE; - if (!remmina_protocol_widget_init_tunnel(gp)) + if (!(tunnel = remmina_protocol_widget_init_tunnel(gp))) return FALSE; msg = g_strdup_printf(_("Awaiting incoming SSH connection at port %i…"), remmina_file_get_int(gp->priv->remmina_file, "listenport", 0)); mp = remmina_protocol_widget_mpprogress(gp->cnnobj, msg, cancel_start_reverse_tunnel_cb, NULL); g_free(msg); - if (!remmina_ssh_tunnel_reverse(gp->priv->ssh_tunnel, remmina_file_get_int(gp->priv->remmina_file, "listenport", 0), local_port)) { - remmina_protocol_widget_set_error(gp, REMMINA_SSH(gp->priv->ssh_tunnel)->error); + if (!remmina_ssh_tunnel_reverse(tunnel, remmina_file_get_int(gp->priv->remmina_file, "listenport", 0), local_port)) { + remmina_ssh_tunnel_free(tunnel); + remmina_protocol_widget_set_error(gp, REMMINA_SSH(tunnel)->error); return FALSE; } remmina_protocol_widget_mpdestroy(gp->cnnobj, mp); + g_ptr_array_add(gp->priv->ssh_tunnels, tunnel); #endif return TRUE; @@ -882,13 +926,18 @@ gboolean remmina_protocol_widget_ssh_exec(RemminaProtocolWidget *gp, gboolean wa { TRACE_CALL(__func__); #ifdef HAVE_LIBSSH - RemminaSSHTunnel *tunnel = gp->priv->ssh_tunnel; + RemminaSSHTunnel *tunnel; ssh_channel channel; gint status; gboolean ret = FALSE; gchar *cmd, *ptr; va_list args; + if (gp->priv->ssh_tunnels->len < 1) + return FALSE; + + tunnel = (RemminaSSHTunnel*)gp->priv->ssh_tunnels->pdata[0]; + if ((channel = ssh_channel_new(REMMINA_SSH(tunnel)->session)) == NULL) return FALSE; @@ -985,30 +1034,33 @@ gboolean remmina_protocol_widget_start_xport_tunnel(RemminaProtocolWidget *gp, R gchar *server; gchar *msg; RemminaMessagePanel *mp; + RemminaSSHTunnel* tunnel; - if (!remmina_protocol_widget_init_tunnel(gp)) return FALSE; + if (!(tunnel = remmina_protocol_widget_init_tunnel(gp))) return FALSE; msg = g_strdup_printf(_("Connecting to %s via SSH…"), remmina_file_get_string(gp->priv->remmina_file, "server")); mp = remmina_protocol_widget_mpprogress(gp->cnnobj, msg, cancel_connect_xport_cb, NULL); g_free(msg); gp->priv->init_func = init_func; - gp->priv->ssh_tunnel->init_func = remmina_protocol_widget_xport_tunnel_init_callback; - gp->priv->ssh_tunnel->connect_func = remmina_protocol_widget_xport_tunnel_connect_callback; - gp->priv->ssh_tunnel->disconnect_func = remmina_protocol_widget_xport_tunnel_disconnect_callback; - gp->priv->ssh_tunnel->callback_data = gp; + tunnel->init_func = remmina_protocol_widget_xport_tunnel_init_callback; + tunnel->connect_func = remmina_protocol_widget_xport_tunnel_connect_callback; + tunnel->disconnect_func = remmina_protocol_widget_xport_tunnel_disconnect_callback; + tunnel->callback_data = gp; remmina_public_get_server_port(remmina_file_get_string(gp->priv->remmina_file, "server"), 0, &server, NULL); - bindlocalhost = (g_strcmp0(REMMINA_SSH(gp->priv->ssh_tunnel)->server, server) == 0); + bindlocalhost = (g_strcmp0(REMMINA_SSH(tunnel)->server, server) == 0); g_free(server); - if (!remmina_ssh_tunnel_xport(gp->priv->ssh_tunnel, bindlocalhost)) { + if (!remmina_ssh_tunnel_xport(tunnel, bindlocalhost)) { remmina_protocol_widget_set_error(gp, "Could not open channel, %s", - ssh_get_error(REMMINA_SSH(gp->priv->ssh_tunnel)->session)); + ssh_get_error(REMMINA_SSH(tunnel)->session)); + remmina_ssh_tunnel_free(tunnel); return FALSE; } remmina_protocol_widget_mpdestroy(gp->cnnobj, mp); + g_ptr_array_add(gp->priv->ssh_tunnels, tunnel); return TRUE; @@ -1021,8 +1073,12 @@ void remmina_protocol_widget_set_display(RemminaProtocolWidget *gp, gint display { TRACE_CALL(__func__); #ifdef HAVE_LIBSSH - if (gp->priv->ssh_tunnel->localdisplay) g_free(gp->priv->ssh_tunnel->localdisplay); - gp->priv->ssh_tunnel->localdisplay = g_strdup_printf("unix:%i", display); + RemminaSSHTunnel* tunnel; + if (gp->priv->ssh_tunnels->len < 1) + return; + tunnel = (RemminaSSHTunnel *)gp->priv->ssh_tunnels->pdata[0]; + if (tunnel->localdisplay) g_free(tunnel->localdisplay); + tunnel->localdisplay = g_strdup_printf("unix:%i", display); #endif } diff --git a/src/remmina_sftp_client.c b/src/remmina_sftp_client.c index b4bc49887..1d4677d7e 100644 --- a/src/remmina_sftp_client.c +++ b/src/remmina_sftp_client.c @@ -50,6 +50,7 @@ #include "remmina_pref.h" #include "remmina_ssh.h" #include "remmina_sftp_client.h" +#include "remmina_sftp_plugin.h" #include "remmina_masterthread_exec.h" #include "remmina/remmina_trace_calls.h" @@ -503,15 +504,41 @@ remmina_sftp_client_thread_main(gpointer data) gchar *refreshdir = NULL; gchar *tmp; gboolean refresh = FALSE; + gchar *host; + int port; task = remmina_sftp_client_thread_get_task(client); while (task) { size = 0; if (!sftp) { sftp = remmina_sftp_new_from_ssh(REMMINA_SSH(client->sftp)); - if (!remmina_ssh_init_session(REMMINA_SSH(sftp), FALSE) || - remmina_ssh_auth(REMMINA_SSH(sftp), NULL, NULL, NULL) <= 0 || - !remmina_sftp_open(sftp)) { + + /* we may need to open a new tunnel too */ + host = NULL; + port = 0; + if (!remmina_plugin_sftp_start_direct_tunnel(client->gp, &host, &port)) + return NULL; + (REMMINA_SSH(sftp))->tunnel_entrance_host = host; + (REMMINA_SSH(sftp))->tunnel_entrance_port = port; + + /* Open a new connection for this subcommand */ + g_debug("[SFTPCLI] %s opening ssh session to %s:%d", __func__, host, port); + if (!remmina_ssh_init_session(REMMINA_SSH(sftp))) { + g_debug("[SFTPCLI] remmina_ssh_init_session returned error %s\n", (REMMINA_SSH(sftp))->error); + remmina_sftp_client_thread_set_error(client, task, (REMMINA_SSH(sftp))->error); + remmina_ftp_task_free(task); + break; + } + + if (remmina_ssh_auth(REMMINA_SSH(sftp), REMMINA_SSH(sftp)->password, client->gp, NULL) != REMMINA_SSH_AUTH_SUCCESS) { + g_debug("[SFTPCLI] remmina_ssh_auth returned error %s\n", (REMMINA_SSH(sftp))->error); + remmina_sftp_client_thread_set_error(client, task, (REMMINA_SSH(sftp))->error); + remmina_ftp_task_free(task); + break; + } + + if (!remmina_sftp_open(sftp)) { + g_debug("[SFTPCLI] remmina_sftp_open returned error %s\n", (REMMINA_SSH(sftp))->error); remmina_sftp_client_thread_set_error(client, task, (REMMINA_SSH(sftp))->error); remmina_ftp_task_free(task); break; @@ -946,11 +973,11 @@ remmina_sftp_client_confirm_resume(RemminaSFTPClient *client, const gchar *path) return response; } -GtkWidget * +RemminaSFTPClient * remmina_sftp_client_new(void) { TRACE_CALL(__func__); - return GTK_WIDGET(g_object_new(REMMINA_TYPE_SFTP_CLIENT, NULL)); + return REMMINA_SFTP_CLIENT(g_object_new(REMMINA_TYPE_SFTP_CLIENT, NULL)); } void @@ -962,6 +989,7 @@ remmina_sftp_client_open(RemminaSFTPClient *client, RemminaSFTP *sftp) g_idle_add((GSourceFunc)remmina_sftp_client_refresh, client); } +/* GtkWidget * remmina_sftp_client_new_init(RemminaSFTP *sftp) { @@ -977,8 +1005,8 @@ remmina_sftp_client_new_init(RemminaSFTP *sftp) SET_CURSOR(gdk_cursor_new_for_display(display, GDK_WATCH)); gdk_display_flush(display); - if (!remmina_ssh_init_session(REMMINA_SSH(sftp), FALSE) || - remmina_ssh_auth(REMMINA_SSH(sftp), NULL, NULL, NULL) <= 0 || + if (!remmina_ssh_init_session(REMMINA_SSH(sftp)) || + remmina_ssh_auth(REMMINA_SSH(sftp), NULL, NULL, NULL) != REMMINA_SSH_AUTH_SUCCESS || !remmina_sftp_open(sftp)) { dialog = gtk_message_dialog_new(GTK_WINDOW(gtk_widget_get_toplevel(client)), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, @@ -994,5 +1022,6 @@ remmina_sftp_client_new_init(RemminaSFTP *sftp) g_idle_add((GSourceFunc)remmina_sftp_client_refresh, client); return client; } +*/ #endif diff --git a/src/remmina_sftp_client.h b/src/remmina_sftp_client.h index 362c692e3..4805311e9 100644 --- a/src/remmina_sftp_client.h +++ b/src/remmina_sftp_client.h @@ -61,6 +61,8 @@ typedef struct _RemminaSFTPClient { pthread_t thread; gint taskid; gboolean thread_abort; + RemminaProtocolWidget *gp; + } RemminaSFTPClient; typedef struct _RemminaSFTPClientClass { @@ -69,7 +71,7 @@ typedef struct _RemminaSFTPClientClass { GType remmina_sftp_client_get_type(void) G_GNUC_CONST; -GtkWidget *remmina_sftp_client_new(void); +RemminaSFTPClient *remmina_sftp_client_new(); void remmina_sftp_client_open(RemminaSFTPClient *client, RemminaSFTP *sftp); gint remmina_sftp_client_confirm_resume(RemminaSFTPClient *client, const gchar *path); diff --git a/src/remmina_sftp_plugin.c b/src/remmina_sftp_plugin.c index a6600823e..b247acbeb 100644 --- a/src/remmina_sftp_plugin.c +++ b/src/remmina_sftp_plugin.c @@ -56,13 +56,28 @@ #define GET_PLUGIN_DATA(gp) (RemminaPluginSftpData *)g_object_get_data(G_OBJECT(gp), "plugin-data"); typedef struct _RemminaPluginSftpData { - GtkWidget * client; + RemminaSFTPClient * client; pthread_t thread; RemminaSFTP * sftp; } RemminaPluginSftpData; static RemminaPluginService *remmina_plugin_service = NULL; +gboolean remmina_plugin_sftp_start_direct_tunnel(RemminaProtocolWidget *gp, char **phost, int *pport) +{ + gchar *hostport; + + hostport = remmina_plugin_service->protocol_plugin_start_direct_tunnel(gp, 22, FALSE); + if (hostport == NULL) { + remmina_plugin_service->protocol_plugin_signal_connection_closed(gp); + return FALSE; + } + + remmina_plugin_service->get_server_port(hostport, 22, phost, pport); + + return TRUE; +} + static gpointer remmina_plugin_sftp_main_thread(gpointer data) { @@ -75,62 +90,52 @@ remmina_plugin_sftp_main_thread(gpointer data) gboolean cont = FALSE; gint ret; const gchar *cs; - gchar *hostport; - gchar *tunnel_host; - int tunnel_port; + gchar *host; + int port; pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); CANCEL_ASYNC - gpdata = GET_PLUGIN_DATA(gp); - - /* remmina_plugin_service->protocol_plugin_start_direct_tunnel start the - * SSH Tunnel and return the server + port string - * Therefore we set the SSH Tunnel username here, before the tunnel - * is established and than set it back to the destination SSH user. - * - * */ + gpdata = GET_PLUGIN_DATA(gp); remminafile = remmina_plugin_service->protocol_plugin_get_file(gp); - // Optionally start the SSH tunnel - hostport = remmina_plugin_service->protocol_plugin_start_direct_tunnel(gp, 22, FALSE); - if (hostport == NULL) { - remmina_plugin_service->protocol_plugin_signal_connection_closed(gp); + /* we may need to open a new tunnel too */ + host = NULL; + port = 0; + if (!remmina_plugin_sftp_start_direct_tunnel(gp, &host, &port)) return NULL; - } - - remmina_plugin_service->get_server_port(hostport, 22, &tunnel_host, &tunnel_port); - g_free(hostport); - ssh = g_object_get_data(G_OBJECT(gp), "user-data"); if (ssh) { /* Create SFTP connection based on existing SSH session */ sftp = remmina_sftp_new_from_ssh(ssh); - if (remmina_ssh_init_session(REMMINA_SSH(sftp), FALSE) && - remmina_ssh_auth(REMMINA_SSH(sftp), NULL, gp, remminafile) > 0 && + ssh->tunnel_entrance_host = host; + ssh->tunnel_entrance_port = port; + if (remmina_ssh_init_session(REMMINA_SSH(sftp)) && + remmina_ssh_auth(REMMINA_SSH(sftp), NULL, gp, remminafile) == REMMINA_SSH_AUTH_SUCCESS && remmina_sftp_open(sftp)) cont = TRUE; } else { /* New SFTP connection */ sftp = remmina_sftp_new_from_file(remminafile); - sftp->ssh.tunnel_host = tunnel_host; - sftp->ssh.tunnel_port = tunnel_port; + ssh = REMMINA_SSH(sftp); + ssh->tunnel_entrance_host = host; + ssh->tunnel_entrance_port = port; while (1) { - if (!remmina_ssh_init_session(REMMINA_SSH(sftp), FALSE)) { - remmina_plugin_service->protocol_plugin_set_error(gp, "%s", REMMINA_SSH(sftp)->error); + if (!remmina_ssh_init_session(ssh)) { + remmina_plugin_service->protocol_plugin_set_error(gp, "%s", ssh->error); break; } - ret = remmina_ssh_auth_gui(REMMINA_SSH(sftp), gp, remminafile); + ret = remmina_ssh_auth_gui(ssh, gp, remminafile); if (ret != REMMINA_SSH_AUTH_SUCCESS) { if (ret != REMMINA_SSH_AUTH_USERCANCEL) - remmina_plugin_service->protocol_plugin_set_error(gp, "%s", REMMINA_SSH(sftp)->error); + remmina_plugin_service->protocol_plugin_set_error(gp, "%s", ssh->error); break; } if (!remmina_sftp_open(sftp)) { - remmina_plugin_service->protocol_plugin_set_error(gp, "%s", REMMINA_SSH(sftp)->error); + remmina_plugin_service->protocol_plugin_set_error(gp, "%s", ssh->error); break; } @@ -142,6 +147,7 @@ remmina_plugin_sftp_main_thread(gpointer data) break; } } + if (!cont) { if (sftp) remmina_sftp_free(sftp); remmina_plugin_service->protocol_plugin_signal_connection_closed(gp); @@ -181,8 +187,9 @@ remmina_plugin_sftp_init(RemminaProtocolWidget *gp) remminafile = remmina_plugin_service->protocol_plugin_get_file(gp); gpdata->client = remmina_sftp_client_new(); - gtk_widget_show(gpdata->client); - gtk_container_add(GTK_CONTAINER(gp), gpdata->client); + gpdata->client->gp = gp; + gtk_widget_show(GTK_WIDGET(gpdata->client)); + gtk_container_add(GTK_CONTAINER(gp), GTK_WIDGET(gpdata->client)); remmina_ftp_client_set_show_hidden(REMMINA_FTP_CLIENT(gpdata->client), remmina_plugin_service->file_get_int(remminafile, "showhidden", FALSE)); @@ -191,7 +198,7 @@ remmina_plugin_sftp_init(RemminaProtocolWidget *gp) remmina_plugin_service->file_get_int(remminafile, REMMINA_PLUGIN_SFTP_FEATURE_PREF_OVERWRITE_ALL_KEY, FALSE)); - remmina_plugin_service->protocol_plugin_register_hostkey(gp, gpdata->client); + remmina_plugin_service->protocol_plugin_register_hostkey(gp, GTK_WIDGET(gpdata->client)); g_signal_connect(G_OBJECT(gpdata->client), "realize", G_CALLBACK(remmina_plugin_sftp_client_on_realize), gp); @@ -318,6 +325,7 @@ static const RemminaProtocolSetting remmina_sftp_basic_settings[] = { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "ssh_auth", N_("Authentication type"), FALSE, ssh_auth, NULL }, { REMMINA_PROTOCOL_SETTING_TYPE_FILE, "ssh_privatekey", N_("Identity file"), FALSE, NULL, NULL }, { REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD, "ssh_passphrase", N_("Password to unlock private key"), FALSE, NULL, NULL }, + { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "ssh_proxycommand", N_("SSH Proxy Command"), FALSE, NULL, NULL }, { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL } }; diff --git a/src/remmina_sftp_plugin.h b/src/remmina_sftp_plugin.h index 250cddae3..bd4fe5715 100644 --- a/src/remmina_sftp_plugin.h +++ b/src/remmina_sftp_plugin.h @@ -39,5 +39,6 @@ G_BEGIN_DECLS void remmina_sftp_plugin_register(void); +gboolean remmina_plugin_sftp_start_direct_tunnel(RemminaProtocolWidget *gp, char **phost, int *pport); G_END_DECLS diff --git a/src/remmina_ssh.c b/src/remmina_ssh.c index 7960303c8..c4da45ff7 100644 --- a/src/remmina_ssh.c +++ b/src/remmina_ssh.c @@ -359,8 +359,8 @@ remmina_ssh_auth(RemminaSSH *ssh, const gchar *password, RemminaProtocolWidget * } if (password) { - g_free(ssh->password); - g_free(ssh->passphrase); + if (password != ssh->password) g_free(ssh->password); + if (password != ssh->passphrase) g_free(ssh->passphrase); ssh->password = g_strdup(password); ssh->passphrase = g_strdup(password); } @@ -685,7 +685,7 @@ remmina_ssh_log_callback(ssh_session session, int priority, const char *message, } gboolean -remmina_ssh_init_session(RemminaSSH *ssh, gboolean is_tunnel) +remmina_ssh_init_session(RemminaSSH *ssh) { TRACE_CALL(__func__); gint verbosity; @@ -698,16 +698,31 @@ remmina_ssh_init_session(RemminaSSH *ssh, gboolean is_tunnel) ssh->callback = g_new0(struct ssh_callbacks_struct, 1); /* Init & startup the SSH session */ - g_debug("[SSH] %s server=%s port=%d is_tunnel=%s tunnel_host=%s tunnel_port=%d\n", __func__, - ssh->server, ssh->port, ssh->is_tunnel ? "Yes" : "No", ssh->tunnel_host, ssh->tunnel_port); + g_debug("[SSH] %s server=%s port=%d is_tunnel=%s tunnel_entrance_host=%s tunnel_entrance_port=%d\n", __func__, + ssh->server, ssh->port, ssh->is_tunnel ? "Yes" : "No", ssh->tunnel_entrance_host, ssh->tunnel_entrance_port); + ssh->session = ssh_new(); - if (is_tunnel) { + /* Tunnel sanity checks */ + if (ssh->is_tunnel && ssh->tunnel_entrance_host != NULL) { + ssh->error = g_strdup_printf("Internal error in %s: is_tunnel and tunnel_entrance != NULL", __func__); + g_debug("[SSH] %s", ssh->error); + return FALSE; + } + if (!ssh->is_tunnel && ssh->tunnel_entrance_host == NULL) { + ssh->error = g_strdup_printf("Internal error in %s: is_tunnel == false and tunnel_entrance == NULL", __func__); + g_debug("[SSH] %s", ssh->error); + return FALSE; + } + + /* Set connection host/port */ + if (ssh->is_tunnel) { ssh_options_set(ssh->session, SSH_OPTIONS_HOST, ssh->server); ssh_options_set(ssh->session, SSH_OPTIONS_PORT, &ssh->port); } else { - ssh_options_set(ssh->session, SSH_OPTIONS_HOST, ssh->tunnel_host); - ssh_options_set(ssh->session, SSH_OPTIONS_PORT, &ssh->tunnel_port); + ssh_options_set(ssh->session, SSH_OPTIONS_HOST, ssh->tunnel_entrance_host); + ssh_options_set(ssh->session, SSH_OPTIONS_PORT, &ssh->tunnel_entrance_port); + g_debug("[SSH] setting SSH_OPTIONS_HOST to %s and SSH_OPTIONS_PORT to %d", ssh->tunnel_entrance_host, ssh->tunnel_entrance_port); } if (*ssh->user != 0) @@ -842,10 +857,11 @@ remmina_ssh_init_from_file(RemminaSSH *ssh, RemminaFile *remminafile, gboolean i ssh->error = NULL; ssh->passphrase = NULL; ssh->is_tunnel = is_tunnel; - ssh->tunnel_host = NULL; - ssh->tunnel_port = 0; pthread_mutex_init(&ssh->ssh_mutex, NULL); + ssh->tunnel_entrance_host = NULL; + ssh->tunnel_entrance_port = 0; + username = remmina_file_get_string(remminafile, is_tunnel ? "ssh_tunnel_username" : "username"); privatekey = remmina_file_get_string(remminafile, is_tunnel ? "ssh_tunnel_privatekey" : "ssh_privatekey"); @@ -916,14 +932,17 @@ remmina_ssh_init_from_ssh(RemminaSSH *ssh, const RemminaSSH *ssh_src) ssh->user = g_strdup(ssh_src->user); ssh->auth = ssh_src->auth; ssh->password = g_strdup(ssh_src->password); + ssh->passphrase = g_strdup(ssh_src->passphrase); ssh->privkeyfile = g_strdup(ssh_src->privkeyfile); ssh->charset = g_strdup(ssh_src->charset); ssh->proxycommand = g_strdup(ssh_src->proxycommand); ssh->kex_algorithms = g_strdup(ssh_src->kex_algorithms); ssh->ciphers = g_strdup(ssh_src->ciphers); ssh->hostkeytypes = g_strdup(ssh_src->hostkeytypes); + ssh->stricthostkeycheck = ssh_src->stricthostkeycheck; ssh->compression = ssh_src->compression; - ssh->tunnel_host = NULL; + ssh->tunnel_entrance_host = g_strdup(ssh_src->tunnel_entrance_host); + ssh->tunnel_entrance_port = ssh_src->tunnel_entrance_port; return TRUE; } @@ -968,7 +987,6 @@ remmina_ssh_free(RemminaSSH *ssh) g_free(ssh->privkeyfile); g_free(ssh->charset); g_free(ssh->error); - g_free(ssh->tunnel_host); pthread_mutex_destroy(&ssh->ssh_mutex); g_free(ssh); } @@ -1070,6 +1088,8 @@ remmina_ssh_tunnel_close_all_channels(RemminaSSHTunnel *tunnel) ssh_channel_free(tunnel->x11_channel); tunnel->x11_channel = NULL; } + + } static void @@ -1517,9 +1537,27 @@ remmina_ssh_tunnel_main_thread_proc(gpointer data) remmina_ssh_tunnel_close_all_channels(tunnel); + tunnel->running = FALSE; + + /* Notify tunnel owner of disconnection */ + if (tunnel->disconnect_func) + (*tunnel->disconnect_func)(tunnel, tunnel->callback_data); + return NULL; } +static gboolean remmina_ssh_notify_tunnel_main_thread_end(gpointer data) +{ + TRACE_CALL(__func__); + RemminaSSHTunnel *tunnel = (RemminaSSHTunnel *)data; + + /* Ask tunnel owner to destroy tunnel object */ + if (tunnel->destroy_func) + (*tunnel->destroy_func)(tunnel, tunnel->destroy_func_callback_data); + + return FALSE; +} + static gpointer remmina_ssh_tunnel_main_thread(gpointer data) { @@ -1533,9 +1571,14 @@ remmina_ssh_tunnel_main_thread(gpointer data) if (tunnel->server_sock < 0 || tunnel->thread == 0 || !tunnel->running) break; } tunnel->thread = 0; + + /* Do after tunnel thread cleanup */ + IDLE_ADD((GSourceFunc)remmina_ssh_notify_tunnel_main_thread_end, (gpointer)tunnel); + return NULL; } + void remmina_ssh_tunnel_cancel_accept(RemminaSSHTunnel *tunnel) { @@ -1663,6 +1706,8 @@ remmina_ssh_tunnel_free(RemminaSSHTunnel *tunnel) TRACE_CALL(__func__); pthread_t thread; + g_debug("[SSH] %s tunnel->thread = %lX\n", __func__, tunnel->thread); + thread = tunnel->thread; if (thread != 0) { tunnel->running = FALSE; @@ -1682,6 +1727,7 @@ remmina_ssh_tunnel_free(RemminaSSHTunnel *tunnel) close(tunnel->server_sock); tunnel->server_sock = -1; } + remmina_ssh_tunnel_close_all_channels(tunnel); g_free(tunnel->buffer); @@ -1689,7 +1735,8 @@ remmina_ssh_tunnel_free(RemminaSSHTunnel *tunnel) g_free(tunnel->dest); g_free(tunnel->localdisplay); - remmina_ssh_free(REMMINA_SSH(tunnel)); + remmina_ssh_free((RemminaSSH *)tunnel); + } /*-----------------------------------------------------------------------------* diff --git a/src/remmina_ssh.h b/src/remmina_ssh.h index 463d347b8..024cef411 100644 --- a/src/remmina_ssh.h +++ b/src/remmina_ssh.h @@ -83,8 +83,9 @@ typedef struct _RemminaSSH { gchar * passphrase; gboolean is_tunnel; - gchar * tunnel_host; - gint tunnel_port; + gchar * tunnel_entrance_host; + gint tunnel_entrance_port; + } RemminaSSH; gchar *remmina_ssh_identity_path(const gchar *id); @@ -96,7 +97,7 @@ gchar *remmina_ssh_find_identity(void); gboolean remmina_ssh_init_from_file(RemminaSSH *ssh, RemminaFile *remminafile, gboolean is_tunnel); /* Initialize the SSH session */ -gboolean remmina_ssh_init_session(RemminaSSH *ssh, gboolean is_tunnel); +gboolean remmina_ssh_init_session(RemminaSSH *ssh); /* Authenticate SSH session */ @@ -172,6 +173,10 @@ struct _RemminaSSHTunnel { RemminaSSHTunnelCallback connect_func; RemminaSSHTunnelCallback disconnect_func; gpointer callback_data; + + RemminaSSHTunnelCallback destroy_func; + gpointer destroy_func_callback_data; + }; /* Create a new SSH Tunnel session and connects to the SSH server */ diff --git a/src/remmina_ssh_plugin.c b/src/remmina_ssh_plugin.c index dcdaba240..1a1d9a7ec 100644 --- a/src/remmina_ssh_plugin.c +++ b/src/remmina_ssh_plugin.c @@ -227,68 +227,57 @@ remmina_plugin_ssh_main_thread(gpointer data) RemminaSSHShell *shell = NULL; gboolean cont = FALSE; gchar *hostport; - gchar *tunnel_host; - int tunnel_port; gint ret; pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); CANCEL_ASYNC - gpdata = GET_PLUGIN_DATA(gp); - - /** - * remmina_plugin_service->protocol_plugin_start_direct_tunnel start the - * SSH Tunnel and return the server + port string - * - **/ + gpdata = GET_PLUGIN_DATA(gp); remminafile = remmina_plugin_service->protocol_plugin_get_file(gp); - // Optionally start the SSH tunnel + /* we may need to open a new tunnel */ hostport = remmina_plugin_service->protocol_plugin_start_direct_tunnel(gp, 22, FALSE); if (hostport == NULL) { remmina_plugin_service->protocol_plugin_signal_connection_closed(gp); return NULL; } - remmina_plugin_service->get_server_port(hostport, 22, &tunnel_host, &tunnel_port); - g_free(hostport); - ssh = g_object_get_data(G_OBJECT(gp), "user-data"); if (ssh) { /* Create SSH shell connection based on existing SSH session */ shell = remmina_ssh_shell_new_from_ssh(ssh); - if (shell->ssh.tunnel_host) - g_free(shell->ssh.tunnel_host); - shell->ssh.tunnel_host = tunnel_host; - shell->ssh.tunnel_port = tunnel_port; - if (remmina_ssh_init_session(REMMINA_SSH(shell), FALSE) && - remmina_ssh_auth(REMMINA_SSH(shell), NULL, gp, remminafile) > 0 && + remmina_plugin_service->get_server_port(hostport, 22, &ssh->tunnel_entrance_host, &ssh->tunnel_entrance_port); + + if (remmina_ssh_init_session(REMMINA_SSH(shell)) && + remmina_ssh_auth(REMMINA_SSH(shell), NULL, gp, remminafile) == REMMINA_SSH_AUTH_SUCCESS && remmina_ssh_shell_open(shell, (RemminaSSHExitFunc) remmina_plugin_service->protocol_plugin_signal_connection_closed, gp)) cont = TRUE; } else { + /* New SSH Shell connection */ shell = remmina_ssh_shell_new_from_file(remminafile); - shell->ssh.tunnel_host = tunnel_host; - shell->ssh.tunnel_port = tunnel_port; + ssh = REMMINA_SSH(shell); + remmina_plugin_service->get_server_port(hostport, 22, &ssh->tunnel_entrance_host, &ssh->tunnel_entrance_port); + while (1) { - if (!remmina_ssh_init_session(REMMINA_SSH(shell), FALSE)) { - g_debug("[SSH] init session error: %s\n", REMMINA_SSH(shell)->error); - remmina_plugin_service->protocol_plugin_set_error(gp, "%s", REMMINA_SSH(shell)->error); + if (!remmina_ssh_init_session(ssh)) { + g_debug("[SSH] init session error: %s\n", ssh->error); + remmina_plugin_service->protocol_plugin_set_error(gp, "%s", ssh->error); break; } - ret = remmina_ssh_auth_gui(REMMINA_SSH(shell), gp, remminafile); + ret = remmina_ssh_auth_gui(ssh, gp, remminafile); if (ret != REMMINA_SSH_AUTH_SUCCESS) { if (ret != REMMINA_SSH_AUTH_USERCANCEL) - remmina_plugin_service->protocol_plugin_set_error(gp, "%s", REMMINA_SSH(shell)->error); + remmina_plugin_service->protocol_plugin_set_error(gp, "%s", ssh->error); break; } if (!remmina_ssh_shell_open(shell, (RemminaSSHExitFunc) remmina_plugin_service->protocol_plugin_signal_connection_closed, gp)) { - remmina_plugin_service->protocol_plugin_set_error(gp, "%s", REMMINA_SSH(shell)->error); + remmina_plugin_service->protocol_plugin_set_error(gp, "%s", ssh->error); break; } @@ -296,6 +285,9 @@ remmina_plugin_ssh_main_thread(gpointer data) break; } } + + g_free(hostport); + if (!cont) { if (shell) remmina_ssh_shell_free(shell); remmina_plugin_service->protocol_plugin_signal_connection_closed(gp); |