remmina_ssh.c 47.1 KB
Newer Older
Antenore Gatta's avatar
Antenore Gatta committed
1 2 3 4
/*
 * Remmina - The GTK+ Remote Desktop Client
 * Copyright (C) 2009-2011 Vic Lee
 * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo
5
 * Copyright (C) 2016-2018 Antenore Gatta, Giovanni Panozzo
Antenore Gatta's avatar
Antenore Gatta committed
6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
Antenore Gatta's avatar
Antenore Gatta committed
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
 * Boston, MA  02110-1301, USA.
 *
 *  In addition, as a special exception, the copyright holders give
 *  permission to link the code of portions of this program with the
 *  OpenSSL library under certain conditions as described in each
 *  individual source file, and distribute linked combinations
 *  including the two.
 *  You must obey the GNU General Public License in all respects
 *  for all of the code used other than OpenSSL. *  If you modify
 *  file(s) with this exception, you may extend this exception to your
 *  version of the file(s), but you are not obligated to do so. *  If you
 *  do not wish to do so, delete this exception statement from your
 *  version. *  If you delete this exception statement from all source
 *  files in the program, then also delete it here.
 *
 */
36 37 38 39 40 41 42 43

#include "config.h"

#ifdef HAVE_LIBSSH

/* Define this before stdlib.h to have posix_openpt */
#define _XOPEN_SOURCE 600

Antenore Gatta's avatar
Antenore Gatta committed
44
#define LIBSSH_STATIC 1
45
#include <libssh/libssh.h>
Antenore Gatta's avatar
Antenore Gatta committed
46
#include <gdk/gdkx.h>
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
#include <gtk/gtk.h>
#include <glib/gi18n.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <sys/types.h>
#include <pthread.h>
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_TERMIOS_H
#include <termios.h>
#endif
75 76 77 78 79 80
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_PTY_H
#include <pty.h>
#endif
81
#include "remmina_public.h"
82
#include "remmina_file.h"
83
#include "remmina_log.h"
84
#include "remmina_pref.h"
85
#include "remmina_ssh.h"
86
#include "remmina/remmina_trace_calls.h"
87

88 89
#ifdef HAVE_NETINET_TCP_H
#include <netinet/tcp.h>
90 91 92

#if defined(__FreeBSD__) || defined(__OpenBSD__)
#ifndef SOL_TCP
Antenore Gatta's avatar
Antenore Gatta committed
93
#define SOL_TCP IPPROTO_TCP
94 95 96
#endif
#endif

97 98
#endif

99

100
/*-----------------------------------------------------------------------------*
Antenore Gatta's avatar
Antenore Gatta committed
101 102
*                           SSH Base                                          *
*-----------------------------------------------------------------------------*/
103

Antenore Gatta's avatar
Antenore Gatta committed
104 105
#define LOCK_SSH(ssh) pthread_mutex_lock(&REMMINA_SSH(ssh)->ssh_mutex);
#define UNLOCK_SSH(ssh) pthread_mutex_unlock(&REMMINA_SSH(ssh)->ssh_mutex);
106

107 108
static const gchar *common_identities[] =
{
109
	".ssh/id_ed25519",
110 111 112 113
	".ssh/id_rsa",
	".ssh/id_dsa",
	".ssh/identity",
	NULL
114 115
};

116
gchar*
Antenore Gatta's avatar
Antenore Gatta committed
117
remmina_ssh_identity_path(const gchar *id)
118
{
119
	TRACE_CALL(__func__);
120
	if (id == NULL) return NULL;
Antenore Gatta's avatar
Antenore Gatta committed
121 122
	if (id[0] == '/') return g_strdup(id);
	return g_strdup_printf("%s/%s", g_get_home_dir(), id);
123 124
}

125
gchar*
Antenore Gatta's avatar
Antenore Gatta committed
126
remmina_ssh_find_identity(void)
127
{
128
	TRACE_CALL(__func__);
129 130 131
	gchar *path;
	gint i;

Antenore Gatta's avatar
Antenore Gatta committed
132 133 134
	for (i = 0; common_identities[i]; i++) {
		path = remmina_ssh_identity_path(common_identities[i]);
		if (g_file_test(path, G_FILE_TEST_IS_REGULAR | G_FILE_TEST_EXISTS)) {
135 136
			return path;
		}
137
		g_free(path);
138 139
	}
	return NULL;
140 141
}

142
void
Antenore Gatta's avatar
Antenore Gatta committed
143
remmina_ssh_set_error(RemminaSSH *ssh, const gchar *fmt)
144
{
145
	TRACE_CALL(__func__);
146
	const gchar *err;
147

Antenore Gatta's avatar
Antenore Gatta committed
148
	err = ssh_get_error(ssh->session);
149
	ssh->error = g_strdup_printf(fmt, err);
150 151
}

152
void
Antenore Gatta's avatar
Antenore Gatta committed
153
remmina_ssh_set_application_error(RemminaSSH *ssh, const gchar *fmt, ...)
154
{
155
	TRACE_CALL(__func__);
156
	va_list args;
157

Antenore Gatta's avatar
Antenore Gatta committed
158 159 160
	va_start(args, fmt);
	ssh->error = g_strdup_vprintf(fmt, args);
	va_end(args);
161 162
}

163
static gint
164
remmina_ssh_auth_interactive(RemminaSSH *ssh)
165
{
166
	TRACE_CALL(__func__);
167 168 169 170
	gint ret;
	gint n;
	gint i;

171
	ret = SSH_AUTH_ERROR;
172 173 174
	if (ssh->authenticated) return 1;
	if (ssh->password == NULL) return -1;

175 176 177 178
	while ((ret = ssh_userauth_kbdint(ssh->session, NULL, NULL)) == SSH_AUTH_INFO) {
		n = ssh_userauth_kbdint_getnprompts(ssh->session);
		for (i = 0; i < n; i++) {
			ssh_userauth_kbdint_setanswer(ssh->session, i, ssh->password);
179 180
		}
	}
181 182 183 184

	if (ret != SSH_AUTH_SUCCESS) {
		/* We pass the control to remmina_ssh_auth_password */
		return 0;
185
	}
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201

	ssh->authenticated = TRUE;
	return 1;
}

static gint
remmina_ssh_auth_password(RemminaSSH *ssh)
{
	TRACE_CALL(__func__);
	gint ret;

	ret = SSH_AUTH_ERROR;
	if (ssh->authenticated) return 1;
	if (ssh->password == NULL) return -1;

	ret = ssh_userauth_password(ssh->session, NULL, ssh->password);
Antenore Gatta's avatar
Antenore Gatta committed
202 203
	if (ret != SSH_AUTH_SUCCESS) {
		remmina_ssh_set_error(ssh, _("SSH password authentication failed: %s"));
204 205 206 207 208
		return 0;
	}

	ssh->authenticated = TRUE;
	return 1;
209 210
}

211
static gint
Antenore Gatta's avatar
Antenore Gatta committed
212
remmina_ssh_auth_pubkey(RemminaSSH *ssh)
213
{
214
	TRACE_CALL(__func__);
215
	gint ret;
Giovanni Panozzo's avatar
Giovanni Panozzo committed
216
	ssh_key priv_key;
217 218 219

	if (ssh->authenticated) return 1;

Antenore Gatta's avatar
Antenore Gatta committed
220
	if (ssh->privkeyfile == NULL) {
221
		ssh->error = g_strdup_printf(_("SSH public key authentication failed: %s"),
Antenore Gatta's avatar
Antenore Gatta committed
222
			_("SSH Key file not yet set."));
223 224 225
		return 0;
	}

226
	if ( ssh_pki_import_privkey_file( ssh->privkeyfile, (ssh->passphrase ? ssh->passphrase : ""),
Antenore Gatta's avatar
Antenore Gatta committed
227
		     NULL, NULL, &priv_key ) != SSH_OK ) {
228
		if (ssh->passphrase == NULL || ssh->passphrase[0] == '\0') return -1;
229

Antenore Gatta's avatar
Antenore Gatta committed
230
		remmina_ssh_set_error(ssh, _("SSH public key authentication failed: %s"));
231 232 233
		return 0;
	}

Antenore Gatta's avatar
Antenore Gatta committed
234
	ret = ssh_userauth_publickey(ssh->session, NULL, priv_key);
Giovanni Panozzo's avatar
Giovanni Panozzo committed
235
	ssh_key_free(priv_key);
236

Antenore Gatta's avatar
Antenore Gatta committed
237 238
	if (ret != SSH_AUTH_SUCCESS) {
		remmina_ssh_set_error(ssh, _("SSH public key authentication failed: %s"));
239 240 241 242 243
		return 0;
	}

	ssh->authenticated = TRUE;
	return 1;
244 245
}

246
static gint
Antenore Gatta's avatar
Antenore Gatta committed
247
remmina_ssh_auth_auto_pubkey(RemminaSSH* ssh)
248
{
249
	TRACE_CALL(__func__);
250
	gint ret = ssh_userauth_publickey_auto(ssh->session, NULL, ssh->passphrase);
Antenore Gatta's avatar
Antenore Gatta committed
251

Antenore Gatta's avatar
Antenore Gatta committed
252 253
	if (ret != SSH_AUTH_SUCCESS) {
		remmina_ssh_set_error(ssh, _("SSH automatic public key authentication failed: %s"));
Robert Ayrapetyan's avatar
Robert Ayrapetyan committed
254
		return -1;
Antenore Gatta's avatar
Antenore Gatta committed
255
	}
256 257 258

	ssh->authenticated = TRUE;
	return 1;
259 260
}

261
static gint
Antenore Gatta's avatar
Antenore Gatta committed
262
remmina_ssh_auth_agent(RemminaSSH* ssh)
263
{
264
	TRACE_CALL(__func__);
265
	gint ret;
266
	ret = ssh_userauth_agent(ssh->session, NULL);
267

Antenore Gatta's avatar
Antenore Gatta committed
268 269
	if (ret != SSH_AUTH_SUCCESS) {
		remmina_ssh_set_error(ssh, _("SSH public key authentication with ssh agent failed: %s"));
270 271 272 273 274 275 276
		return 0;
	}

	ssh->authenticated = TRUE;
	return 1;
}

277
static gint
Antenore Gatta's avatar
Antenore Gatta committed
278
remmina_ssh_auth_gssapi(RemminaSSH *ssh)
279
{
280
	TRACE_CALL(__func__);
Antenore Gatta's avatar
Antenore Gatta committed
281
	gint ret;
282

Antenore Gatta's avatar
Antenore Gatta committed
283
	if (ssh->authenticated) return 1;
284

Antenore Gatta's avatar
Antenore Gatta committed
285
	ret = ssh_userauth_gssapi(ssh->session);
286

Antenore Gatta's avatar
Antenore Gatta committed
287 288 289 290
	if (ret != SSH_AUTH_SUCCESS) {
		remmina_ssh_set_error(ssh, _("SSH Kerberos/GSSAPI authentication failed: %s"));
		return 0;
	}
291

Antenore Gatta's avatar
Antenore Gatta committed
292 293
	ssh->authenticated = TRUE;
	return 1;
294 295
}

296
gint
Antenore Gatta's avatar
Antenore Gatta committed
297
remmina_ssh_auth(RemminaSSH *ssh, const gchar *password)
298
{
299
	TRACE_CALL(__func__);
300 301 302
	gint method;
	gint ret;

303
	/* Check known host again to ensure it's still the original server when user forks
Antenore Gatta's avatar
Antenore Gatta committed
304
	   a new session from existing one */
Antenore Gatta's avatar
Antenore Gatta committed
305 306
	if (ssh_is_server_known(ssh->session) != SSH_SERVER_KNOWN_OK) {
		remmina_ssh_set_application_error(ssh, "SSH public key has changed!");
307 308 309
		return 0;
	}

Antenore Gatta's avatar
Antenore Gatta committed
310
	if (password) {
311
		g_free(ssh->password);
312
		g_free(ssh->passphrase);
Antenore Gatta's avatar
Antenore Gatta committed
313 314
		ssh->password = g_strdup(password);
		ssh->passphrase = g_strdup(password);
315 316
	}

317
	/** @todo Here we should call
318 319 320 321 322
	 * gint method;
	 * method = ssh_userauth_list(ssh->session, NULL);
	 * And than test both the method and the option selected by the user
	 */
	method = ssh_userauth_list(ssh->session, NULL);
Antenore Gatta's avatar
Antenore Gatta committed
323
	switch (ssh->auth) {
324

Antenore Gatta's avatar
Antenore Gatta committed
325
	case SSH_AUTH_PASSWORD:
326
		ret = 0;
327 328 329 330 331 332
		if (method & SSH_AUTH_METHOD_INTERACTIVE || method & SSH_AUTH_METHOD_PASSWORD) {
			ret = remmina_ssh_auth_interactive(ssh);
			if (!ssh->authenticated)
				return remmina_ssh_auth_password(ssh);
		}
		return ret;
333

Antenore Gatta's avatar
Antenore Gatta committed
334
	case SSH_AUTH_PUBLICKEY:
335 336
		if (method & SSH_AUTH_METHOD_PUBLICKEY)
			return remmina_ssh_auth_pubkey(ssh);
337

Antenore Gatta's avatar
Antenore Gatta committed
338 339
	case SSH_AUTH_AGENT:
		return remmina_ssh_auth_agent(ssh);
340

Antenore Gatta's avatar
Antenore Gatta committed
341
	case SSH_AUTH_AUTO_PUBLICKEY:
342
		/* ssh_agent or none */
Antenore Gatta's avatar
Antenore Gatta committed
343
		return remmina_ssh_auth_auto_pubkey(ssh);
344

345 346 347 348 349 350 351 352
#if 0
	/* Not yet supported by libssh */
	case SSH_AUTH_HOSTBASED:
		if (method & SSH_AUTH_METHOD_HOSTBASED)
			//return remmina_ssh_auth_hostbased;
			return 0;
#endif

Antenore Gatta's avatar
Antenore Gatta committed
353
	case SSH_AUTH_GSSAPI:
354 355
		if (method & SSH_AUTH_METHOD_GSSAPI_MIC)
			return remmina_ssh_auth_gssapi(ssh);
356

Antenore Gatta's avatar
Antenore Gatta committed
357 358
	default:
		return 0;
359
	}
360 361
}

362
gint
Antenore Gatta's avatar
Antenore Gatta committed
363
remmina_ssh_auth_gui(RemminaSSH *ssh, RemminaInitDialog *dialog, RemminaFile *remminafile)
364
{
365
	TRACE_CALL(__func__);
366 367
	gchar *tips;
	gchar *keyname;
368
	gchar *pwdtype;
369
	gint ret;
Giovanni Panozzo's avatar
Giovanni Panozzo committed
370
	size_t len;
371
	guchar *pubkey;
Giovanni Panozzo's avatar
Giovanni Panozzo committed
372
	ssh_key server_pubkey;
373
	gboolean disablepasswordstoring;
374 375

	/* Check if the server's public key is known */
Antenore Gatta's avatar
Antenore Gatta committed
376 377
	ret = ssh_is_server_known(ssh->session);
	switch (ret) {
378 379 380 381 382 383 384 385
	case SSH_SERVER_KNOWN_OK:
		break;                          /* ok */

	case SSH_SERVER_FILE_NOT_FOUND:
	/*  fallback to SSH_SERVER_NOT_KNOWN behavior */
	case SSH_SERVER_NOT_KNOWN:
	case SSH_SERVER_KNOWN_CHANGED:
	case SSH_SERVER_FOUND_OTHER:
Antenore Gatta's avatar
Antenore Gatta committed
386
		if ( ssh_get_publickey(ssh->session, &server_pubkey) != SSH_OK ) {
387 388 389
			remmina_ssh_set_error(ssh, "ssh_get_publickey() has failed: %s");
			return 0;
		}
Antenore Gatta's avatar
Antenore Gatta committed
390
		if ( ssh_get_publickey_hash(server_pubkey, SSH_PUBLICKEY_HASH_MD5, &pubkey, &len) != 0 ) {
Giovanni Panozzo's avatar
Giovanni Panozzo committed
391
			ssh_key_free(server_pubkey);
392 393 394 395
			remmina_ssh_set_error(ssh, "ssh_get_publickey_hash() has failed: %s");
			return 0;
		}
		ssh_key_free(server_pubkey);
Antenore Gatta's avatar
Antenore Gatta committed
396
		keyname = ssh_get_hexa(pubkey, len);
397

398

Antenore Gatta's avatar
Antenore Gatta committed
399 400 401 402
		if (ret == SSH_SERVER_NOT_KNOWN || ret == SSH_SERVER_FILE_NOT_FOUND) {
			ret = remmina_init_dialog_serverkey_unknown(dialog, keyname);
		}else  {
			ret = remmina_init_dialog_serverkey_changed(dialog, keyname);
403
		}
404

405
		ssh_string_free_char(keyname);
Antenore Gatta's avatar
Antenore Gatta committed
406
		ssh_clean_pubkey_hash(&pubkey);
407
		if (ret != GTK_RESPONSE_OK) return -1;
Antenore Gatta's avatar
Antenore Gatta committed
408
		ssh_write_knownhost(ssh->session);
409 410 411
		break;
	case SSH_SERVER_ERROR:
	default:
Antenore Gatta's avatar
Antenore Gatta committed
412
		remmina_ssh_set_error(ssh, "SSH known host checking failed: %s");
413
		return 0;
414 415
	}

Antenore Gatta's avatar
Antenore Gatta committed
416
	switch (ssh->auth) {
417 418 419 420 421 422
	case SSH_AUTH_PASSWORD:
		tips = _("Authenticating %s's password to SSH server %s...");
		keyname = _("SSH password");
		pwdtype = "ssh_password";
		break;
	case SSH_AUTH_PUBLICKEY:
423
	case SSH_AUTH_AGENT:
424 425 426 427 428
	case SSH_AUTH_AUTO_PUBLICKEY:
		tips = _("Authenticating %s's identity to SSH server %s...");
		keyname = _("SSH private key passphrase");
		pwdtype = "ssh_passphrase";
		break;
429 430 431 432 433
	case SSH_AUTH_GSSAPI:
		tips = _("Authenticating %s's Kerberos to SSH server %s...");
		keyname = _("SSH Kerberos/GSSAPI");
		pwdtype = "kerberos_token";
		break;
434 435 436 437
	default:
		return FALSE;
	}
	/* Try empty password or existing password/passphrase first */
Antenore Gatta's avatar
Antenore Gatta committed
438
	ret = remmina_ssh_auth(ssh, remmina_file_get_string(remminafile, pwdtype));
439 440 441
	if (ret > 0) return 1;

	/* Requested for a non-empty password */
Antenore Gatta's avatar
Antenore Gatta committed
442
	if (ret < 0) {
443 444
		if (!dialog) return -1;

Antenore Gatta's avatar
Antenore Gatta committed
445
		remmina_init_dialog_set_status(dialog, tips, ssh->user, ssh->server);
446
		disablepasswordstoring = remmina_file_get_int(remminafile, "disablepasswordstoring", FALSE);
Antenore Gatta's avatar
Antenore Gatta committed
447
		ret = remmina_init_dialog_authpwd(dialog, keyname, !disablepasswordstoring);
448

Antenore Gatta's avatar
Antenore Gatta committed
449
		if (ret == GTK_RESPONSE_OK) {
450 451
			if (dialog->save_password)
				remmina_file_set_string(remminafile, pwdtype, dialog->password);
Antenore Gatta's avatar
Antenore Gatta committed
452
		}else  {
453
			return -1;
454
		}
Antenore Gatta's avatar
Antenore Gatta committed
455
		ret = remmina_ssh_auth(ssh, dialog->password);
456 457
	}

Antenore Gatta's avatar
Antenore Gatta committed
458
	if (ret <= 0) {
459 460 461 462
		return 0;
	}

	return 1;
463 464
}

465
void
466
remmina_ssh_log_callback(ssh_session session, int priority, const char *message, void *userdata)
467
{
468
	TRACE_CALL(__func__);
Antenore Gatta's avatar
Antenore Gatta committed
469
	remmina_log_printf("[SSH] %s\n", message);
470 471
}

472
gboolean
Antenore Gatta's avatar
Antenore Gatta committed
473
remmina_ssh_init_session(RemminaSSH *ssh)
474
{
475
	TRACE_CALL(__func__);
476
	gint verbosity;
477
	gint rc;
478 479 480 481
#ifdef HAVE_NETINET_TCP_H
	socket_t sshsock;
	gint optval;
#endif
482

Antenore Gatta's avatar
Antenore Gatta committed
483
	ssh->callback = g_new0(struct ssh_callbacks_struct, 1);
484 485

	/* Init & startup the SSH session */
Antenore Gatta's avatar
Antenore Gatta committed
486 487 488
	ssh->session = ssh_new();
	ssh_options_set(ssh->session, SSH_OPTIONS_HOST, ssh->server);
	ssh_options_set(ssh->session, SSH_OPTIONS_PORT, &ssh->port);
489 490
	/** @todo add an option to set the compression nad set it to no as the default option */
	//ssh_options_set(ssh->session, SSH_OPTIONS_COMPRESSION, "yes");
491 492 493
	/* When SSH_OPTIONS_USER is not set, the local user account is used */
	if (*ssh->user != 0)
		ssh_options_set(ssh->session, SSH_OPTIONS_USER, ssh->user);
Antenore Gatta's avatar
Antenore Gatta committed
494 495 496 497 498 499
	if (ssh->privkeyfile && *ssh->privkeyfile != 0) {
		rc = ssh_options_set(ssh->session, SSH_OPTIONS_IDENTITY, ssh->privkeyfile);
		if (rc == 0) {
			remmina_log_printf("[SSH] SSH_OPTIONS_IDENTITY has been set to: %s\n", ssh->privkeyfile);
		}else {
			remmina_log_printf("[SSH] SSH_OPTIONS_IDENTITY is not set, by default identity, id_dsa and id_rsa are checked.\n");
500 501 502
		}
	}

503
#ifdef SNAP_BUILD
Antenore Gatta's avatar
Antenore Gatta committed
504
	ssh_options_set(ssh->session, SSH_OPTIONS_SSH_DIR, g_strdup_printf("%s/.ssh", g_getenv("SNAP_USER_COMMON")));
505
#endif
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535
	rc = ssh_options_set(ssh->session, SSH_OPTIONS_KEY_EXCHANGE, ssh->kex_algorithms);
	if (rc == 0) {
		remmina_log_printf("[SSH] SSH_OPTIONS_KEY_EXCHANGE has been set to: %s\n", ssh->kex_algorithms);
	}else {
		remmina_log_printf("[SSH] SSH_OPTIONS_KEY_EXCHANGE does not have a valid value: %s\n", ssh->kex_algorithms);
	}
	rc = ssh_options_set(ssh->session, SSH_OPTIONS_CIPHERS_C_S, ssh->ciphers);
	if (rc == 0) {
		remmina_log_printf("[SSH] SSH_OPTIONS_CIPHERS_C_S has been set to: %s\n", ssh->ciphers);
	}else {
		remmina_log_printf("[SSH] SSH_OPTIONS_CIPHERS_C_S does not have a valid value: %s\n", ssh->ciphers);
	}
	rc = ssh_options_set(ssh->session, SSH_OPTIONS_HOSTKEYS, ssh->hostkeytypes);
	if (rc == 0) {
		remmina_log_printf("[SSH] SSH_OPTIONS_HOSTKEYS has been set to: %s\n", ssh->hostkeytypes);
	}else {
		remmina_log_printf("[SSH] SSH_OPTIONS_HOSTKEYS does not have a valid value: %s\n", ssh->hostkeytypes);
	}
	rc = ssh_options_set(ssh->session, SSH_OPTIONS_PROXYCOMMAND, ssh->proxycommand);
	if (rc == 0) {
		remmina_log_printf("[SSH] SSH_OPTIONS_PROXYCOMMAND has been set to: %s\n", ssh->proxycommand);
	}else {
		remmina_log_printf("[SSH] SSH_OPTIONS_PROXYCOMMAND does not have a valid value: %s\n", ssh->proxycommand);
	}
	rc = ssh_options_set(ssh->session, SSH_OPTIONS_STRICTHOSTKEYCHECK, &ssh->stricthostkeycheck);
	if (rc == 0) {
		remmina_log_printf("[SSH] SSH_OPTIONS_STRICTHOSTKEYCHECK has been set to: %d\n", ssh->stricthostkeycheck);
	}else {
		remmina_log_printf("[SSH] SSH_OPTIONS_STRICTHOSTKEYCHECK does not have a valid value: %d\n", ssh->stricthostkeycheck);
	}
536 537 538 539 540 541
	rc = ssh_options_set(ssh->session, SSH_OPTIONS_COMPRESSION, ssh->compression);
	if (rc == 0) {
		remmina_log_printf("[SSH] SSH_OPTIONS_COMPRESSION has been set to: %s\n", ssh->compression);
	}else {
		remmina_log_printf("[SSH] SSH_OPTIONS_COMPRESSION does not have a valid value: %s\n", ssh->compression);
	}
542 543

	ssh_callbacks_init(ssh->callback);
Antenore Gatta's avatar
Antenore Gatta committed
544
	if (remmina_log_running()) {
545
		verbosity = remmina_pref.ssh_loglevel;
Antenore Gatta's avatar
Antenore Gatta committed
546
		ssh_options_set(ssh->session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
547
		ssh->callback->log_function = remmina_ssh_log_callback;
548 549
		/* Reset libssh legacy userdata. This is a workaround for a libssh bug */
		ssh_set_log_userdata(ssh->session);
550
	}
551
	ssh->callback->userdata = ssh;
552 553
	ssh_set_callbacks(ssh->session, ssh->callback);

554
	/* As the latest parse the ~/.ssh/config file */
555 556 557 558
	if (remmina_pref.ssh_parseconfig) {
		ssh_options_parse_config(ssh->session, NULL);
	}

Antenore Gatta's avatar
Antenore Gatta committed
559 560
	if (ssh_connect(ssh->session)) {
		remmina_ssh_set_error(ssh, _("Failed to startup SSH session: %s"));
561 562 563
		return FALSE;
	}

564 565 566 567 568 569
#ifdef HAVE_NETINET_TCP_H
	/* Set keepalive on ssh socket, so we can keep firewalls awaken and detect
	 * when we loss the tunnel */
	sshsock = ssh_get_fd(ssh->session);
	if (sshsock >= 0) {
		optval = 1;
Antenore Gatta's avatar
Antenore Gatta committed
570 571
		if (setsockopt(sshsock, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval)) < 0) {
			remmina_log_printf("[SSH] TCP KeepAlive not set\n");
572 573
		}else {
			remmina_log_printf("[SSH] TCP KeepAlive enabled\n");
574
		}
575
#ifdef TCP_KEEPIDLE
576
		optval = remmina_pref.ssh_tcp_keepidle;
577
		if (setsockopt(sshsock, IPPROTO_TCP, TCP_KEEPIDLE, &optval, sizeof(optval)) < 0) {
Antenore Gatta's avatar
Antenore Gatta committed
578
			remmina_log_printf("[SSH] TCP_KEEPIDLE not set\n");
579 580
		}else {
			remmina_log_printf("[SSH] TCP_KEEPIDLE set to %i\n", optval);
581
		}
582 583
#endif
#ifdef TCP_KEEPCNT
584
		optval = remmina_pref.ssh_tcp_keepcnt;
585
		if (setsockopt(sshsock, IPPROTO_TCP, TCP_KEEPCNT, &optval, sizeof(optval)) < 0) {
Antenore Gatta's avatar
Antenore Gatta committed
586
			remmina_log_printf("[SSH] TCP_KEEPCNT not set\n");
587 588
		}else {
			remmina_log_printf("[SSH] TCP_KEEPCNT set to %i\n", optval);
589
		}
590 591
#endif
#ifdef TCP_KEEPINTVL
592
		optval = remmina_pref.ssh_tcp_keepintvl;
593
		if (setsockopt(sshsock, IPPROTO_TCP, TCP_KEEPINTVL, &optval, sizeof(optval)) < 0) {
Antenore Gatta's avatar
Antenore Gatta committed
594
			remmina_log_printf("[SSH] TCP_KEEPINTVL not set\n");
595 596
		}else {
			remmina_log_printf("[SSH] TCP_KEEPINTVL set to %i\n", optval);
597
		}
598 599
#endif
#ifdef TCP_USER_TIMEOUT
600
		optval = remmina_pref.ssh_tcp_usrtimeout;
601
		if (setsockopt(sshsock, IPPROTO_TCP, TCP_USER_TIMEOUT, &optval, sizeof(optval)) < 0) {
Antenore Gatta's avatar
Antenore Gatta committed
602
			remmina_log_printf("[SSH] TCP_USER_TIMEOUT not set\n");
603 604
		}else {
			remmina_log_printf("[SSH] TCP_USER_TIMEOUT set to %i\n", optval);
605
		}
606
#endif
607 608 609
	}
#endif

610
	/* Try the "none" authentication */
Antenore Gatta's avatar
Antenore Gatta committed
611
	if (ssh_userauth_none(ssh->session, NULL) == SSH_AUTH_SUCCESS) {
612 613 614
		ssh->authenticated = TRUE;
	}
	return TRUE;
615 616
}

617
gboolean
Antenore Gatta's avatar
Antenore Gatta committed
618
remmina_ssh_init_from_file(RemminaSSH *ssh, RemminaFile *remminafile)
619
{
620
	TRACE_CALL(__func__);
621 622 623 624 625 626 627 628 629 630
	const gchar *ssh_server;
	const gchar *ssh_username;
	const gchar *ssh_privatekey;
	const gchar *server;
	gchar *s;

	ssh->session = NULL;
	ssh->callback = NULL;
	ssh->authenticated = FALSE;
	ssh->error = NULL;
631
	ssh->passphrase = NULL;
Antenore Gatta's avatar
Antenore Gatta committed
632
	pthread_mutex_init(&ssh->ssh_mutex, NULL);
633 634

	/* Parse the address and port */
Antenore Gatta's avatar
Antenore Gatta committed
635 636 637 638 639 640 641
	ssh_server = remmina_file_get_string(remminafile, "ssh_server");
	ssh_username = remmina_file_get_string(remminafile, "ssh_username");
	ssh_privatekey = remmina_file_get_string(remminafile, "ssh_privatekey");
	server = remmina_file_get_string(remminafile, "server");
	if (ssh_server) {
		remmina_public_get_server_port(ssh_server, 22, &ssh->server, &ssh->port);
		if (ssh->server[0] == '\0') {
642
			g_free(ssh->server);
Antenore Gatta's avatar
Antenore Gatta committed
643
			remmina_public_get_server_port(server, 0, &ssh->server, NULL);
644
		}
Antenore Gatta's avatar
Antenore Gatta committed
645 646
	}else if (server == NULL) {
		ssh->server = g_strdup("localhost");
647
		ssh->port = 22;
Antenore Gatta's avatar
Antenore Gatta committed
648 649
	}else  {
		remmina_public_get_server_port(server, 0, &ssh->server, NULL);
650 651 652
		ssh->port = 22;
	}

Antenore Gatta's avatar
Antenore Gatta committed
653
	ssh->user = g_strdup(ssh_username ? ssh_username : g_get_user_name());
654
	ssh->password = NULL;
Antenore Gatta's avatar
Antenore Gatta committed
655 656
	ssh->auth = remmina_file_get_int(remminafile, "ssh_auth", 0);
	ssh->charset = g_strdup(remmina_file_get_string(remminafile, "ssh_charset"));
657 658 659 660 661
	ssh->kex_algorithms = g_strdup(remmina_file_get_string(remminafile, "ssh_kex_algorithms"));
	ssh->ciphers = g_strdup(remmina_file_get_string(remminafile, "ssh_ciphers"));
	ssh->hostkeytypes = g_strdup(remmina_file_get_string(remminafile, "ssh_hostkeytypes"));
	ssh->proxycommand = g_strdup(remmina_file_get_string(remminafile, "ssh_proxycommand"));
	ssh->stricthostkeycheck = remmina_file_get_int(remminafile, "ssh_stricthostkeycheck", 0);
662 663
	gint c = remmina_file_get_int(remminafile, "ssh_compression", 0);
	ssh->compression = (c == 1) ? "yes" : "no";
664 665

	/* Public/Private keys */
Antenore Gatta's avatar
Antenore Gatta committed
666 667 668
	s = (ssh_privatekey ? g_strdup(ssh_privatekey) : remmina_ssh_find_identity());
	if (s) {
		ssh->privkeyfile = remmina_ssh_identity_path(s);
669
		g_free(s);
Antenore Gatta's avatar
Antenore Gatta committed
670
	}else  {
671 672 673 674
		ssh->privkeyfile = NULL;
	}

	return TRUE;
675 676
}

677
static gboolean
Antenore Gatta's avatar
Antenore Gatta committed
678
remmina_ssh_init_from_ssh(RemminaSSH *ssh, const RemminaSSH *ssh_src)
679
{
680
	TRACE_CALL(__func__);
681 682 683
	ssh->session = NULL;
	ssh->authenticated = FALSE;
	ssh->error = NULL;
Antenore Gatta's avatar
Antenore Gatta committed
684
	pthread_mutex_init(&ssh->ssh_mutex, NULL);
685

Antenore Gatta's avatar
Antenore Gatta committed
686
	ssh->server = g_strdup(ssh_src->server);
687
	ssh->port = ssh_src->port;
Antenore Gatta's avatar
Antenore Gatta committed
688
	ssh->user = g_strdup(ssh_src->user);
689
	ssh->auth = ssh_src->auth;
Antenore Gatta's avatar
Antenore Gatta committed
690 691 692
	ssh->password = g_strdup(ssh_src->password);
	ssh->privkeyfile = g_strdup(ssh_src->privkeyfile);
	ssh->charset = g_strdup(ssh_src->charset);
693 694 695 696
	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);
697
	ssh->compression = ssh_src->compression;
698 699

	return TRUE;
700 701
}

702
gchar*
Antenore Gatta's avatar
Antenore Gatta committed
703
remmina_ssh_convert(RemminaSSH *ssh, const gchar *from)
704
{
705
	TRACE_CALL(__func__);
706 707
	gchar *to = NULL;

Antenore Gatta's avatar
Antenore Gatta committed
708 709
	if (ssh->charset && from) {
		to = g_convert(from, -1, "UTF-8", ssh->charset, NULL, NULL, NULL);
710
	}
Antenore Gatta's avatar
Antenore Gatta committed
711
	if (!to) to = g_strdup(from);
712
	return to;
713 714
}

715
gchar*
Antenore Gatta's avatar
Antenore Gatta committed
716
remmina_ssh_unconvert(RemminaSSH *ssh, const gchar *from)
717
{
718
	TRACE_CALL(__func__);
719 720
	gchar *to = NULL;

Antenore Gatta's avatar
Antenore Gatta committed
721 722
	if (ssh->charset && from) {
		to = g_convert(from, -1, ssh->charset, "UTF-8", NULL, NULL, NULL);
723
	}
Antenore Gatta's avatar
Antenore Gatta committed
724
	if (!to) to = g_strdup(from);
725
	return to;
726 727
}

728
void
Antenore Gatta's avatar
Antenore Gatta committed
729
remmina_ssh_free(RemminaSSH *ssh)
730
{
731
	TRACE_CALL(__func__);
Antenore Gatta's avatar
Antenore Gatta committed
732 733 734
	if (ssh->session) {
		ssh_disconnect(ssh->session);
		ssh_free(ssh->session);
735 736
		ssh->session = NULL;
	}
737 738 739 740 741 742 743
	g_free(ssh->callback);
	g_free(ssh->server);
	g_free(ssh->user);
	g_free(ssh->password);
	g_free(ssh->privkeyfile);
	g_free(ssh->charset);
	g_free(ssh->error);
Antenore Gatta's avatar
Antenore Gatta committed
744
	pthread_mutex_destroy(&ssh->ssh_mutex);
745
	g_free(ssh);
746 747
}

748
/*-----------------------------------------------------------------------------*
Antenore Gatta's avatar
Antenore Gatta committed
749 750 751
*                           SSH Tunnel                                        *
*-----------------------------------------------------------------------------*/
struct _RemminaSSHTunnelBuffer {
752 753 754
	gchar *data;
	gchar *ptr;
	ssize_t len;
755 756
};

757
static RemminaSSHTunnelBuffer*
Antenore Gatta's avatar
Antenore Gatta committed
758
remmina_ssh_tunnel_buffer_new(ssize_t len)
759
{
760
	TRACE_CALL(__func__);
761
	RemminaSSHTunnelBuffer *buffer;
762

Antenore Gatta's avatar
Antenore Gatta committed
763 764
	buffer = g_new(RemminaSSHTunnelBuffer, 1);
	buffer->data = (gchar*)g_malloc(len);
765 766 767
	buffer->ptr = buffer->data;
	buffer->len = len;
	return buffer;
768 769
}

770
static void
Antenore Gatta's avatar
Antenore Gatta committed
771
remmina_ssh_tunnel_buffer_free(RemminaSSHTunnelBuffer *buffer)
772
{
773
	TRACE_CALL(__func__);
Antenore Gatta's avatar
Antenore Gatta committed
774
	if (buffer) {
775 776
		g_free(buffer->data);
		g_free(buffer);
777
	}
778 779
}

780
RemminaSSHTunnel*
Antenore Gatta's avatar
Antenore Gatta committed
781
remmina_ssh_tunnel_new_from_file(RemminaFile *remminafile)
782
{
783
	TRACE_CALL(__func__);
784 785
	RemminaSSHTunnel *tunnel;

Antenore Gatta's avatar
Antenore Gatta committed
786
	tunnel = g_new(RemminaSSHTunnel, 1);
787

Antenore Gatta's avatar
Antenore Gatta committed
788
	remmina_ssh_init_from_file(REMMINA_SSH(tunnel), remminafile);
789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812

	tunnel->tunnel_type = -1;
	tunnel->channels = NULL;
	tunnel->sockets = NULL;
	tunnel->socketbuffers = NULL;
	tunnel->num_channels = 0;
	tunnel->max_channels = 0;
	tunnel->x11_channel = NULL;
	tunnel->thread = 0;
	tunnel->running = FALSE;
	tunnel->server_sock = -1;
	tunnel->dest = NULL;
	tunnel->port = 0;
	tunnel->buffer = NULL;
	tunnel->buffer_len = 0;
	tunnel->channels_out = NULL;
	tunnel->remotedisplay = 0;
	tunnel->localdisplay = NULL;
	tunnel->init_func = NULL;
	tunnel->connect_func = NULL;
	tunnel->disconnect_func = NULL;
	tunnel->callback_data = NULL;

	return tunnel;
813 814
}

815
static void
Antenore Gatta's avatar
Antenore Gatta committed
816
remmina_ssh_tunnel_close_all_channels(RemminaSSHTunnel *tunnel)
817
{
818
	TRACE_CALL(__func__);
819 820
	int i;

Antenore Gatta's avatar
Antenore Gatta committed
821 822 823 824 825 826
	for (i = 0; i < tunnel->num_channels; i++) {
		close(tunnel->sockets[i]);
		remmina_ssh_tunnel_buffer_free(tunnel->socketbuffers[i]);
		ssh_channel_close(tunnel->channels[i]);
		ssh_channel_send_eof(tunnel->channels[i]);
		ssh_channel_free(tunnel->channels[i]);
827 828
	}

829
	g_free(tunnel->channels);
830
	tunnel->channels = NULL;
831
	g_free(tunnel->sockets);
832
	tunnel->sockets = NULL;
833
	g_free(tunnel->socketbuffers);
834 835 836 837 838
	tunnel->socketbuffers = NULL;

	tunnel->num_channels = 0;
	tunnel->max_channels = 0;

Antenore Gatta's avatar
Antenore Gatta committed
839 840 841 842
	if (tunnel->x11_channel) {
		ssh_channel_close(tunnel->x11_channel);
		ssh_channel_send_eof(tunnel->x11_channel);
		ssh_channel_free(tunnel->x11_channel);
843 844
		tunnel->x11_channel = NULL;
	}
845 846
}

847
static void
Antenore Gatta's avatar
Antenore Gatta committed
848
remmina_ssh_tunnel_remove_channel(RemminaSSHTunnel *tunnel, gint n)
849
{
850
	TRACE_CALL(__func__);
Antenore Gatta's avatar
Antenore Gatta committed
851 852 853 854 855
	ssh_channel_close(tunnel->channels[n]);
	ssh_channel_send_eof(tunnel->channels[n]);
	ssh_channel_free(tunnel->channels[n]);
	close(tunnel->sockets[n]);
	remmina_ssh_tunnel_buffer_free(tunnel->socketbuffers[n]);
856 857 858 859 860
	tunnel->num_channels--;
	tunnel->channels[n] = tunnel->channels[tunnel->num_channels];
	tunnel->channels[tunnel->num_channels] = NULL;
	tunnel->sockets[n] = tunnel->sockets[tunnel->num_channels];
	tunnel->socketbuffers[n] = tunnel->socketbuffers[tunnel->num_channels];
861 862 863
}

/* Register the new channel/socket pair */
864
static void
Antenore Gatta's avatar
Antenore Gatta committed
865
remmina_ssh_tunnel_add_channel(RemminaSSHTunnel *tunnel, ssh_channel channel, gint sock)
866
{
867
	TRACE_CALL(__func__);
868 869 870 871
	gint flags;
	gint i;

	i = tunnel->num_channels++;
Antenore Gatta's avatar
Antenore Gatta committed
872
	if (tunnel->num_channels > tunnel->max_channels) {
873
		/* Allocate an extra NULL pointer in channels for ssh_select */
Antenore Gatta's avatar
Antenore Gatta committed
874 875 876 877 878 879
		tunnel->channels = (ssh_channel*)g_realloc(tunnel->channels,
			sizeof(ssh_channel) * (tunnel->num_channels + 1));
		tunnel->sockets = (gint*)g_realloc(tunnel->sockets,
			sizeof(gint) * tunnel->num_channels);
		tunnel->socketbuffers = (RemminaSSHTunnelBuffer**)g_realloc(tunnel->socketbuffers,
			sizeof(RemminaSSHTunnelBuffer*) * tunnel->num_channels);
880 881
		tunnel->max_channels = tunnel->num_channels;

Antenore Gatta's avatar
Antenore Gatta committed
882 883
		tunnel->channels_out = (ssh_channel*)g_realloc(tunnel->channels_out,
			sizeof(ssh_channel) * (tunnel->num_channels + 1));
884 885 886 887 888 889
	}
	tunnel->channels[i] = channel;
	tunnel->channels[i + 1] = NULL;
	tunnel->sockets[i] = sock;
	tunnel->socketbuffers[i] = NULL;

Antenore Gatta's avatar
Antenore Gatta committed
890 891
	flags = fcntl(sock, F_GETFL, 0);
	fcntl(sock, F_SETFL, flags | O_NONBLOCK);
892 893
}

894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938
static int
remmina_ssh_tunnel_accept_local_connection(RemminaSSHTunnel *tunnel, gboolean blocking)
{
	gint sock, sock_flags;

	sock_flags = fcntl(tunnel->server_sock, F_GETFL, 0);
	if (blocking) {
		sock_flags &= ~O_NONBLOCK;
	}else  {
		sock_flags |= O_NONBLOCK;
	}
	fcntl(tunnel->server_sock, F_SETFL, sock_flags);

	/* Accept a local connection */
	sock = accept(tunnel->server_sock, NULL, NULL);
	if (sock < 0) {
		REMMINA_SSH(tunnel)->error = g_strdup("Failed to accept local socket");
	}

	return sock;
}

static ssh_channel
remmina_ssh_tunnel_create_forward_channel(RemminaSSHTunnel *tunnel)
{
	ssh_channel channel = NULL;

	channel = ssh_channel_new(tunnel->ssh.session);
	if (!channel) {
		remmina_ssh_set_error(REMMINA_SSH(tunnel), "Failed to create channel : %s");
		return NULL;
	}

	/* Request the SSH server to connect to the destination */
	if (ssh_channel_open_forward(channel, tunnel->dest, tunnel->port, "127.0.0.1", 0) != SSH_OK) {
		ssh_channel_close(channel);
		ssh_channel_send_eof(channel);
		ssh_channel_free(channel);
		remmina_ssh_set_error(REMMINA_SSH(tunnel), _("Failed to connect to the SSH tunnel destination: %s"));
		return NULL;
	}

	return channel;
}

939
static gpointer
Antenore Gatta's avatar
Antenore Gatta committed
940
remmina_ssh_tunnel_main_thread_proc(gpointer data)
941
{
942
	TRACE_CALL(__func__);
Antenore Gatta's avatar
Antenore Gatta committed
943
	RemminaSSHTunnel *tunnel = (RemminaSSHTunnel*)data;
944 945 946 947 948 949 950 951 952 953 954 955 956 957 958
	gchar *ptr;
	ssize_t len = 0, lenw = 0;
	fd_set set;
	struct timeval timeout;
	GTimeVal t1, t2;
	glong diff;
	ssh_channel channel = NULL;
	gboolean first = TRUE;
	gboolean disconnected;
	gint sock;
	gint maxfd;
	gint i;
	gint ret;
	struct sockaddr_in sin;

Antenore Gatta's avatar
Antenore Gatta committed
959
	g_get_current_time(&t1);
960 961
	t2 = t1;

Antenore Gatta's avatar
Antenore Gatta committed
962
	switch (tunnel->tunnel_type) {
963
	case REMMINA_SSH_TUNNEL_OPEN:
964
		sock = remmina_ssh_tunnel_accept_local_connection(tunnel, TRUE);
Antenore Gatta's avatar
Antenore Gatta committed
965
		if (sock < 0) {
966 967
		    tunnel->thread = 0;
		    return NULL;
968
		}
969

970 971
		channel = remmina_ssh_tunnel_create_forward_channel(tunnel);
		if (!tunnel) {
Antenore Gatta's avatar
Antenore Gatta committed
972
			close(sock);
973 974 975
			tunnel->thread = 0;
			return NULL;
		}
976

Antenore Gatta's avatar
Antenore Gatta committed
977
		remmina_ssh_tunnel_add_channel(tunnel, channel, sock);
978 979 980
		break;

	case REMMINA_SSH_TUNNEL_X11:
Antenore Gatta's avatar
Antenore Gatta committed
981 982
		if ((tunnel->x11_channel = ssh_channel_new(tunnel->ssh.session)) == NULL) {
			remmina_ssh_set_error(REMMINA_SSH(tunnel), "Failed to create channel : %s");
983 984 985
			tunnel->thread = 0;
			return NULL;
		}
Antenore Gatta's avatar
Antenore Gatta committed
986 987
		if (!remmina_public_get_xauth_cookie(tunnel->localdisplay, &ptr)) {
			remmina_ssh_set_application_error(REMMINA_SSH(tunnel), "%s", ptr);
988
			g_free(ptr);
989 990 991
			tunnel->thread = 0;
			return NULL;
		}
Antenore Gatta's avatar
Antenore Gatta committed
992 993 994
		if (ssh_channel_open_session(tunnel->x11_channel) ||
		    ssh_channel_request_x11(tunnel->x11_channel, TRUE, NULL, ptr,
			    gdk_x11_screen_get_screen_number(gdk_screen_get_default()))) {
995
			g_free(ptr);
Antenore Gatta's avatar
Antenore Gatta committed
996
			remmina_ssh_set_error(REMMINA_SSH(tunnel), "Failed to open channel : %s");
997 998 999 1000
			tunnel->thread = 0;
			return NULL;
		}
		g_free(ptr);
Antenore Gatta's avatar
Antenore Gatta committed
1001
		if (ssh_channel_request_exec(tunnel->x11_channel, tunnel->dest)) {
1002
			ptr = g_strdup_printf(_("Failed to execute %s on SSH server : %%s"), tunnel->dest);
Antenore Gatta's avatar
Antenore Gatta committed
1003
			remmina_ssh_set_error(REMMINA_SSH(tunnel), ptr);
1004 1005 1006 1007
			g_free(ptr);
			tunnel->thread = 0;
			return NULL;
		}
1008

1009
		if (tunnel->init_func &&
Antenore Gatta's avatar
Antenore Gatta committed
1010 1011 1012
		    !(*tunnel->init_func)(tunnel, tunnel->callback_data)) {
			if (tunnel->disconnect_func) {
				(*tunnel->disconnect_func)(tunnel, tunnel->callback_data);
1013
			}
1014 1015 1016
			tunnel->thread = 0;
			return NULL;
		}
1017

1018
		break;
1019

1020 1021
	case REMMINA_SSH_TUNNEL_XPORT:
		/* Detect the next available port starting from 6010 on the server */
Antenore Gatta's avatar
Antenore Gatta committed
1022
		for (i = 10; i <= MAX_X_DISPLAY_NUMBER; i++) {
1023
			G_GNUC_BEGIN_IGNORE_DEPRECATIONS
Antenore Gatta's avatar
Antenore Gatta committed
1024
			if (ssh_forward_listen(REMMINA_SSH(tunnel)->session, (tunnel->bindlocalhost ? "localhost" : NULL), 6000 + i, NULL)) {
1025
				continue;
Antenore Gatta's avatar
Antenore Gatta committed
1026
			}else  {
1027 1028 1029
				tunnel->remotedisplay = i;
				break;
			}
1030
			G_GNUC_END_IGNORE_DEPRECATIONS
1031
		}
Antenore Gatta's avatar
Antenore Gatta committed
1032 1033 1034 1035
		if (tunnel->remotedisplay < 1) {
			remmina_ssh_set_error(REMMINA_SSH(tunnel), _("Failed to request port forwarding : %s"));
			if (tunnel->disconnect_func) {
				(*tunnel->disconnect_func)(tunnel, tunnel->callback_data);
1036
			}
1037 1038 1039
			tunnel->thread = 0;
			return NULL;
		}
1040

1041
		if (tunnel->init_func &&
Antenore Gatta's avatar
Antenore Gatta committed
1042 1043 1044
		    !(*tunnel->init_func)(tunnel, tunnel->callback_data)) {
			if (tunnel->disconnect_func) {
				(*tunnel->disconnect_func)(tunnel, tunnel->callback_data);
1045
			}
1046 1047 1048
			tunnel->thread = 0;
			return NULL;
		}
1049

1050
		break;
1051

1052
	case REMMINA_SSH_TUNNEL_REVERSE:
1053
		G_GNUC_BEGIN_IGNORE_DEPRECATIONS
Antenore Gatta's avatar
Antenore Gatta committed
1054 1055 1056 1057
		if (ssh_forward_listen(REMMINA_SSH(tunnel)->session, NULL, tunnel->port, NULL)) {
			remmina_ssh_set_error(REMMINA_SSH (tunnel), _("Failed to request port forwarding : %s"));
			if (tunnel->disconnect_func) {
				(*tunnel->disconnect_func)(tunnel, tunnel->callback_data);
1058
			}
1059 1060 1061
			tunnel->thread = 0;
			return NULL;
		}
1062
		G_GNUC_END_IGNORE_DEPRECATIONS
1063

1064
		if (tunnel->init_func &&
Antenore Gatta's avatar
Antenore Gatta committed
1065 1066 1067
		    !(*tunnel->init_func)(tunnel, tunnel->callback_data)) {
			if (tunnel->disconnect_func) {
				(*tunnel->disconnect_func)(tunnel, tunnel->callback_data);
1068
			}
1069 1070 1071
			tunnel->thread = 0;
			return NULL;
		}
1072

1073
		break;
1074 1075 1076
	}

	tunnel->buffer_len = 10240;
Antenore Gatta's avatar
Antenore Gatta committed
1077
	tunnel->buffer = g_malloc(tunnel->buffer_len);
1078 1079

	/* Start the tunnel data transmittion */
Antenore Gatta's avatar
Antenore Gatta committed
1080
	while (tunnel->running) {
1081
		if (tunnel->tunnel_type == REMMINA_SSH_TUNNEL_XPORT ||
Antenore Gatta's avatar
Antenore Gatta committed
1082 1083 1084
		    tunnel->tunnel_type == REMMINA_SSH_TUNNEL_X11 ||
		    tunnel->tunnel_type == REMMINA_SSH_TUNNEL_REVERSE) {
			if (first) {
1085 1086
				first = FALSE;
				/* Wait for a period of time for the first incoming connection */
Antenore Gatta's avatar
Antenore Gatta committed
1087 1088 1089 1090
				if (tunnel->tunnel_type == REMMINA_SSH_TUNNEL_X11) {
					channel = ssh_channel_accept_x11(tunnel->x11_channel, 15000);
				}else  {
					channel = ssh_channel_accept_forward(REMMINA_SSH(tunnel)->session, 15000, &tunnel->port);
1091
				}
Antenore Gatta's avatar
Antenore Gatta committed
1092 1093 1094 1095
				if (!channel) {
					remmina_ssh_set_application_error(REMMINA_SSH(tunnel), _("No response from the server."));
					if (tunnel->disconnect_func) {
						(*tunnel->disconnect_func)(tunnel, tunnel->callback_data);
1096 1097 1098 1099
					}
					tunnel->thread = 0;
					return NULL;
				}
Antenore Gatta's avatar
Antenore Gatta committed
1100 1101
				if (tunnel->connect_func) {
					(*tunnel->connect_func)(tunnel, tunnel->callback_data);
1102
				}
Antenore Gatta's avatar
Antenore Gatta committed
1103
				if (tunnel->tunnel_type == REMMINA_SSH_TUNNEL_REVERSE) {
1104
					/* For reverse tunnel, we only need one connection. */
1105
					G_GNUC_BEGIN_IGNORE_DEPRECATIONS
Antenore Gatta's avatar
Antenore Gatta committed
1106
					ssh_forward_cancel(REMMINA_SSH (tunnel)->session, NULL, tunnel->port);
1107
					G_GNUC_END_IGNORE_DEPRECATIONS
1108
				}
Antenore Gatta's avatar
Antenore Gatta committed
1109
			}else if (tunnel->tunnel_type != REMMINA_SSH_TUNNEL_REVERSE) {
1110 1111
				/* Poll once per some period of time if no incoming connections.
				 * Don't try to poll continuously as it will significantly slow down the loop */
Antenore Gatta's avatar
Antenore Gatta committed
1112
				g_get_current_time(&t1);
1113
				diff = (t1.tv_sec - t2.tv_sec) * 10 + (t1.tv_usec - t2.tv_usec) / 100000;
Antenore Gatta's avatar
Antenore Gatta committed
1114 1115 1116 1117 1118
				if (diff > 1) {
					if (tunnel->tunnel_type == REMMINA_SSH_TUNNEL_X11) {
						channel = ssh_channel_accept_x11(tunnel->x11_channel, 0);
					}else  {
						channel = ssh_channel_accept_forward(REMMINA_SSH(tunnel)->session, 0, &tunnel->port);
1119
					}
Antenore Gatta's avatar
Antenore Gatta committed
1120
					if (channel == NULL) {
1121 1122 1123 1124 1125
						t2 = t1;
					}
				}
			}

Antenore Gatta's avatar
Antenore Gatta committed
1126 1127
			if (channel) {
				if (tunnel->tunnel_type == REMMINA_SSH_TUNNEL_REVERSE) {
1128
					sin.sin_family = AF_INET;
Antenore Gatta's avatar
Antenore Gatta committed
1129 1130 1131 1132 1133 1134 1135
					sin.sin_port = htons(tunnel->localport);
					sin.sin_addr.s_addr = inet_addr("127.0.0.1");
					sock = socket(AF_INET, SOCK_STREAM, 0);
					if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
						remmina_ssh_set_application_error(REMMINA_SSH(tunnel),
							"Cannot connect to local port %i.", tunnel->localport);
						close(sock);
1136 1137
						sock = -1;
					}
Antenore Gatta's avatar
Antenore Gatta committed
1138 1139
				}else  {
					sock = remmina_public_open_xdisplay(tunnel->localdisplay);
1140
				}
Antenore Gatta's avatar
Antenore Gatta committed
1141 1142 1143
				if (sock >= 0) {
					remmina_ssh_tunnel_add_channel(tunnel, channel, sock);
				}else  {
1144
					/* Failed to create unix socket. Will this happen? */
Antenore Gatta's avatar
Antenore Gatta committed
1145 1146 1147
					ssh_channel_close(channel);
					ssh_channel_send_eof(channel);
					ssh_channel_free(channel);
1148 1149 1150 1151 1152
				}
				channel = NULL;
			}
		}

<