diff --git a/doc/rdesktop.1 b/doc/rdesktop.1 index 60b5c11..d5d8fd7 100644 --- a/doc/rdesktop.1 +++ b/doc/rdesktop.1 @@ -115,6 +115,10 @@ Any subsequential call to the above command line example will make use of the se connection sharing feature which spawns another notepad in the current connection to the specified server and then exit. +.TP +.BR "-V " +Set the Transport Level Security (also known as SSL) Version used. +Should be one of the following values: 1.0, 1.1, 1.2. If the option is missing 1.0 is assumed. .TP .BR "-B" Use the BackingStore of the Xserver instead of the integrated one in diff --git a/iso.c b/iso.c index 278f32a..40f77b7 100644 --- a/iso.c +++ b/iso.c @@ -32,6 +32,7 @@ extern char *g_sc_csp_name; extern char *g_sc_reader_name; extern char *g_sc_card_name; extern char *g_sc_container_name; +extern char g_tls_version[]; /* Send a self-contained ISO PDU */ @@ -211,6 +212,20 @@ iso_recv(RD_BOOL *is_fastpath, uint8 *fastpath_hdr) return s; } + +/* try to setup a more helpful error message about TLS */ +char *get_credSSP_reason(uint32 neg_proto) +{ +static char msg[256]; + +strcat(msg, "CredSSP required by server"); +if ((neg_proto & PROTOCOL_SSL) && + ( (g_tls_version[0] == 0) || + (strcmp(g_tls_version, "1.2") < 0))) + strcat(msg, " (check if server has disabled old TLS versions, if yes use -V option)"); +return msg; +} + /* Establish a connection up to the ISO layer */ RD_BOOL iso_connect(char *server, char *username, char *domain, char *password, @@ -298,7 +313,7 @@ iso_connect(char *server, char *username, char *domain, char *password, reason = "SSL required by server"; break; case HYBRID_REQUIRED_BY_SERVER: - reason = "CredSSP required by server"; + reason = get_credSSP_reason(neg_proto); break; default: reason = "unknown reason"; diff --git a/rdesktop.c b/rdesktop.c index d625d2e..a06c1a3 100644 --- a/rdesktop.c +++ b/rdesktop.c @@ -113,6 +113,7 @@ RD_BOOL g_seamless_rdp = False; RD_BOOL g_use_password_as_pin = False; char g_seamless_shell[512]; char g_seamless_spawn_cmd[512]; +char g_tls_version[4]; RD_BOOL g_seamless_persistent_mode = True; RD_BOOL g_user_quit = False; uint32 g_embed_wnd; @@ -184,6 +185,7 @@ usage(char *program) fprintf(stderr, " -b: force bitmap updates\n"); fprintf(stderr, " -L: local codepage\n"); fprintf(stderr, " -A: path to SeamlessRDP shell, this enables SeamlessRDP mode\n"); + fprintf(stderr, " -V: tls version (1.0, 1.1, 1.2, defaults to 1.0)\n"); fprintf(stderr, " -B: use BackingStore of X-server (if available)\n"); fprintf(stderr, " -e: disable encryption (French TS)\n"); fprintf(stderr, " -E: disable encryption from client to server\n"); @@ -805,13 +807,13 @@ main(int argc, char *argv[]) flags = RDP_INFO_MOUSE | RDP_INFO_DISABLECTRLALTDEL | RDP_INFO_UNICODE | RDP_INFO_MAXIMIZESHELL | RDP_INFO_ENABLEWINDOWSKEY; - g_seamless_spawn_cmd[0] = domain[0] = g_password[0] = shell[0] = directory[0] = 0; + g_seamless_spawn_cmd[0] = g_tls_version[0] = domain[0] = g_password[0] = shell[0] = directory[0] = 0; g_embed_wnd = 0; g_num_devices = 0; while ((c = getopt(argc, argv, - "A:u:L:d:s:c:p:n:k:g:o:fbBeEitmMzCDKS:T:NX:a:x:Pr:045vh?")) != -1) + "A:V:u:L:d:s:c:p:n:k:g:o:fbBeEitmMzCDKS:T:NX:a:x:Pr:045vh?")) != -1) { switch (c) { @@ -820,6 +822,10 @@ main(int argc, char *argv[]) STRNCPY(g_seamless_shell, optarg, sizeof(g_seamless_shell)); break; + case 'V': + STRNCPY(g_tls_version, optarg, sizeof(g_tls_version)); + break; + case 'u': g_username = (char *) xmalloc(strlen(optarg) + 1); STRNCPY(g_username, optarg, strlen(optarg) + 1); diff --git a/tcp.c b/tcp.c index 1db56f2..7d7ffdb 100644 --- a/tcp.c +++ b/tcp.c @@ -77,6 +77,7 @@ int g_tcp_port_rdp = TCP_PORT_RDP; extern RD_BOOL g_exit_mainloop; extern RD_BOOL g_network_error; extern RD_BOOL g_reconnect_loop; +extern char g_tls_version[]; /* wait till socket is ready to write or timeout */ static RD_BOOL @@ -299,7 +300,7 @@ tcp_recv(STREAM s, uint32 length) return s; } -/* Establish a SSL/TLS 1.0 connection */ +/* Establish a SSL/TLS 1.0-1-2 connection */ RD_BOOL tcp_tls_connect(void) { @@ -316,11 +317,28 @@ tcp_tls_connect(void) /* create process context */ if (g_ssl_ctx == NULL) { - g_ssl_ctx = SSL_CTX_new(TLSv1_client_method()); + + const SSL_METHOD *(*tlsmeth) (void) = NULL; + if (g_tls_version[0] == 0) + tlsmeth = TLSv1_method; + else if (!strcmp(g_tls_version, "1.0")) + tlsmeth = TLSv1_method; + else if (!strcmp(g_tls_version, "1.1")) + tlsmeth = TLSv1_1_method; + else if (!strcmp(g_tls_version, "1.2")) + tlsmeth = TLSv1_2_method; + if (tlsmeth == NULL) + { + logger(Core, Error, + "tcp_tls_connect(), TLS method should be 1.0, 1.1, or 1.2\n"); + goto fail; + } + + g_ssl_ctx = SSL_CTX_new(tlsmeth()); if (g_ssl_ctx == NULL) { logger(Core, Error, - "tcp_tls_connect(), SSL_CTX_new() failed to create TLS v1.0 context\n"); + "tcp_tls_connect(), SSL_CTX_new() failed to create TLS v1.x context\n"); goto fail; } @@ -375,7 +393,7 @@ tcp_tls_connect(void) return False; } -/* Get public key from server of TLS 1.0 connection */ +/* Get public key from server of TLS 1.x connection */ RD_BOOL tcp_tls_get_server_pubkey(STREAM s) {