Added support for protocol negotiation, this is a part of

adding Enhanced RDP Security support to rdesktop and brings
support for TLSv1 tunnel functionality.

git-svn-id: svn://svn.code.sf.net/p/rdesktop/code/rdesktop/trunk@1659 423420c4-83ab-492f-b58f-81f9feb106b5
This commit is contained in:
Henrik Andersson 2012-06-15 05:16:20 +00:00
parent cdce9aae75
commit c44025aa18
14 changed files with 455 additions and 109 deletions

View File

@ -26,7 +26,7 @@
#define CHANNEL_FLAG_LAST 0x02
#define CHANNEL_FLAG_SHOW_PROTOCOL 0x10
extern RD_BOOL g_use_rdp5;
extern RDP_VERSION g_rdp_version;
extern RD_BOOL g_encryption;
VCHANNEL g_channels[MAX_CHANNELS];
@ -47,7 +47,7 @@ channel_register(char *name, uint32 flags, void (*callback) (STREAM))
{
VCHANNEL *channel;
if (!g_use_rdp5)
if (g_rdp_version < RDP_V5)
return NULL;
if (g_num_channels >= MAX_CHANNELS)

View File

@ -103,9 +103,9 @@ AC_ARG_ENABLE(static-openssl,
if test x"$static_openssl" = "xyes"; then
# OpenSSL generally relies on libz
AC_SEARCH_LIBS(deflate, z)
LIBS="-L$ssldir/lib -L$ssldir/lib64 -Wl,-Bstatic -lcrypto -Wl,-Bdynamic $LIBS"
LIBS="-L$ssldir/lib -L$ssldir/lib64 -Wl,-Bstatic -lcrypto -lssl -Wl,-Bdynamic $LIBS"
else
LIBS="-L$ssldir/lib -L$ssldir/lib64 -lcrypto $LIBS"
LIBS="-L$ssldir/lib -L$ssldir/lib64 -lcrypto -lssl $LIBS"
#
# target-specific stuff

View File

@ -33,6 +33,31 @@ enum ISO_PDU_CODE
ISO_PDU_ER = 0x70 /* Error */
};
/* RDP protocol negotiating constants */
enum RDP_NEG_TYPE_CODE
{
RDP_NEG_REQ = 1,
RDP_NEG_RSP = 2,
RDP_NEG_FAILURE = 3
};
enum RDP_NEG_REQ_CODE
{
PROTOCOL_RDP = 0,
PROTOCOL_SSL = 1,
PROTOCOL_HYBRID = 2
};
enum RDP_NEG_FAILURE_CODE
{
SSL_REQUIRED_BY_SERVER = 1,
SSL_NOT_ALLOWED_BY_SERVER = 2,
SSL_CERT_NOT_ON_SERVER = 3,
INCONSISTENT_FLAGS = 4,
HYBRID_REQUIRED_BY_SERVER = 5,
SSL_WITH_USER_AUTH_REQUIRED_BY_SERVER = 6
};
/* MCS PDU codes */
enum MCS_PDU_TYPE
{

116
iso.c
View File

@ -20,6 +20,11 @@
#include "rdesktop.h"
extern RD_BOOL g_encryption;
extern RDP_VERSION g_rdp_version;
static RD_BOOL g_negotiate_rdp_protocol = True;
/* Send a self-contained ISO PDU */
static void
iso_send_msg(uint8 code)
@ -48,6 +53,9 @@ iso_send_connection_request(char *username)
STREAM s;
int length = 30 + strlen(username);
if (g_rdp_version >= RDP_V5 && g_negotiate_rdp_protocol)
length += 8;
s = tcp_init(length);
out_uint8(s, 3); /* version */
@ -63,8 +71,17 @@ iso_send_connection_request(char *username)
out_uint8p(s, "Cookie: mstshash=", strlen("Cookie: mstshash="));
out_uint8p(s, username, strlen(username));
out_uint8(s, 0x0d); /* Unknown */
out_uint8(s, 0x0a); /* Unknown */
out_uint8(s, 0x0d); /* cookie termination string: CR+LF */
out_uint8(s, 0x0a);
if (g_rdp_version >= RDP_V5 && g_negotiate_rdp_protocol)
{
/* optional rdp protocol negotiation request for RDPv5 */
out_uint8(s, RDP_NEG_REQ);
out_uint8(s, 0);
out_uint16(s, 8);
out_uint32(s, PROTOCOL_SSL);
}
s_mark_end(s);
tcp_send(s);
@ -174,9 +191,16 @@ iso_recv(uint8 * rdpver)
/* Establish a connection up to the ISO layer */
RD_BOOL
iso_connect(char *server, char *username, RD_BOOL reconnect)
iso_connect(char *server, char *username, RD_BOOL reconnect, uint32 * selected_protocol)
{
uint8 code = 0;
STREAM s;
uint8 code;
g_negotiate_rdp_protocol = True;
retry:
*selected_protocol = PROTOCOL_RDP;
code = 0;
if (!tcp_connect(server))
return False;
@ -190,7 +214,8 @@ iso_connect(char *server, char *username, RD_BOOL reconnect)
iso_send_connection_request(username);
}
if (iso_recv_msg(&code, NULL) == NULL)
s = iso_recv_msg(&code, NULL);
if (s == NULL)
return False;
if (code != ISO_PDU_CC)
@ -200,6 +225,87 @@ iso_connect(char *server, char *username, RD_BOOL reconnect)
return False;
}
if (g_rdp_version >= RDP_V5 && s_check_rem(s, 8))
{
/* handle RDP_NEG_REQ response */
const char *reason = NULL;
uint8 type = 0, flags = 0;
uint16 length = 0;
uint32 data = 0;
in_uint8(s, type);
in_uint8(s, flags);
in_uint16(s, length);
in_uint32(s, data);
if (type == RDP_NEG_FAILURE)
{
switch (data)
{
case SSL_REQUIRED_BY_SERVER:
reason = "SSL required by server";
break;
case SSL_WITH_USER_AUTH_REQUIRED_BY_SERVER:
reason = "SSL with user authentication required by server";
break;
case SSL_NOT_ALLOWED_BY_SERVER:
reason = "SSL not allowed by server";
break;
case SSL_CERT_NOT_ON_SERVER:
reason = "SSL certificated not on server";
break;
case INCONSISTENT_FLAGS:
reason = "inconsistent flags";
break;
case HYBRID_REQUIRED_BY_SERVER:
reason = "hybrid authentication (CredSSP) required by server";
break;
default:
reason = "unknown reason";
}
tcp_disconnect();
warning("RDP protocol negotiation failed with reason: %s (error 0x%x),\n",
reason, data);
warning("retrying without negotiation using plain RDP protocol.\n");
g_negotiate_rdp_protocol = False;
goto retry;
}
if (type != RDP_NEG_RSP)
{
tcp_disconnect();
error("expected RDP_NEG_RSP, got type = 0x%x\n", type);
warning("retrying without negotiation using plain RDP protocol.\n");
g_negotiate_rdp_protocol = False;
goto retry;
}
/* handle negotiation response */
if (data == PROTOCOL_SSL)
{
if (!tcp_tls_connect())
{
tcp_disconnect();
return False;
}
/* do not use encryption when using TLS */
g_encryption = False;
}
else if (data != PROTOCOL_RDP)
{
tcp_disconnect();
error("unexpected protocol in neqotiation response, got data = 0x%x.\n",
data);
return False;
}
*selected_protocol = data;
}
return True;
}

View File

@ -23,12 +23,13 @@
extern char *g_username;
extern char g_hostname[16];
extern RD_BOOL g_use_rdp5;
extern RDP_VERSION g_rdp_version;
static uint8 g_licence_key[16];
static uint8 g_licence_sign_key[16];
RD_BOOL g_licence_issued = False;
RD_BOOL g_licence_error_result = False;
/* Generate a session key and RC4 keys, given client and server randoms */
static void
@ -69,7 +70,7 @@ licence_present(uint8 * client_random, uint8 * rsa_data,
s = sec_init(sec_flags, length + 2);
out_uint8(s, LICENCE_TAG_PRESENT);
out_uint8(s, (g_use_rdp5 ? 3 : 2)); /* version */
out_uint8(s, ((g_rdp_version >= RDP_V5) ? 3 : 2)); /* version */
out_uint16_le(s, length);
out_uint32_le(s, 1);
@ -110,7 +111,7 @@ licence_send_request(uint8 * client_random, uint8 * rsa_data, char *user, char *
s = sec_init(sec_flags, length + 2);
out_uint8(s, LICENCE_TAG_REQUEST);
out_uint8(s, (g_use_rdp5 ? 3 : 2)); /* version */
out_uint8(s, ((g_rdp_version >= RDP_V5) ? 3 : 2)); /* version */
out_uint16_le(s, length);
out_uint32_le(s, 1);
@ -192,7 +193,7 @@ licence_send_authresp(uint8 * token, uint8 * crypt_hwid, uint8 * signature)
s = sec_init(sec_flags, length + 2);
out_uint8(s, LICENCE_TAG_AUTHRESP);
out_uint8(s, (g_use_rdp5 ? 3 : 2)); /* version */
out_uint8(s, ((g_rdp_version >= RDP_V5) ? 3 : 2)); /* version */
out_uint16_le(s, length);
out_uint16_le(s, 1);
@ -333,6 +334,7 @@ licence_process(STREAM s)
break;
case LICENCE_TAG_RESULT:
g_licence_error_result = True;
break;
default:

11
mcs.c
View File

@ -373,13 +373,16 @@ mcs_recv(uint16 * channel, uint8 * rdpver)
}
RD_BOOL
mcs_connect(char *server, STREAM mcs_data, char *username, RD_BOOL reconnect)
mcs_connect_start(char *server, char *username, RD_BOOL reconnect, uint32 * selected_protocol)
{
return iso_connect(server, username, reconnect, selected_protocol);
}
RD_BOOL
mcs_connect_finalize(STREAM mcs_data)
{
unsigned int i;
if (!iso_connect(server, username, reconnect))
return False;
mcs_send_connect_initial(mcs_data);
if (!mcs_recv_connect_response(mcs_data))
goto error;

View File

@ -22,7 +22,7 @@
extern uint8 *g_next_packet;
static RDP_ORDER_STATE g_order_state;
extern RD_BOOL g_use_rdp5;
extern RDP_VERSION g_rdp_version;
/* Read field indicating which parameters are present */
static void
@ -968,7 +968,7 @@ process_bmpcache(STREAM s)
in_uint16_le(s, bufsize); /* bufsize */
in_uint16_le(s, cache_idx);
if (g_use_rdp5)
if (g_rdp_version >= RDP_V5)
{
size = bufsize;
}

View File

@ -74,7 +74,7 @@ void ewmh_init(void);
STREAM iso_init(int length);
void iso_send(STREAM s);
STREAM iso_recv(uint8 * rdpver);
RD_BOOL iso_connect(char *server, char *username, RD_BOOL reconnect);
RD_BOOL iso_connect(char *server, char *username, RD_BOOL reconnect, uint32 * selected_protocol);
void iso_disconnect(void);
void iso_reset_state(void);
/* licence.c */
@ -84,7 +84,9 @@ STREAM mcs_init(int length);
void mcs_send_to_channel(STREAM s, uint16 channel);
void mcs_send(STREAM s);
STREAM mcs_recv(uint16 * channel, uint8 * rdpver);
RD_BOOL mcs_connect(char *server, STREAM mcs_data, char *username, RD_BOOL reconnect);
RD_BOOL mcs_connect_start(char *server, char *username, RD_BOOL reconnect,
uint32 * selected_protocol);
RD_BOOL mcs_connect_finalize(STREAM s);
void mcs_disconnect(void);
void mcs_reset_state(void);
/* orders.c */

View File

@ -92,7 +92,7 @@ RD_BOOL g_polygon_ellipse_orders = True; /* polygon / ellipse orders */
RD_BOOL g_fullscreen = False;
RD_BOOL g_grab_keyboard = True;
RD_BOOL g_hide_decorations = False;
RD_BOOL g_use_rdp5 = True;
RDP_VERSION g_rdp_version = RDP_V5; /* Default to version 5 */
RD_BOOL g_rdpclip = True;
RD_BOOL g_console_session = False;
RD_BOOL g_numlock_sync = False;
@ -838,11 +838,11 @@ main(int argc, char *argv[])
break;
case '4':
g_use_rdp5 = False;
g_rdp_version = RDP_V4;
break;
case '5':
g_use_rdp5 = True;
g_rdp_version = RDP_V5;
break;
case 'h':
@ -890,7 +890,7 @@ main(int argc, char *argv[])
error("You cannot use -X and -A at the same time\n");
return EX_USAGE;
}
if (!g_use_rdp5)
if (g_rdp_version < RDP_V5)
{
error("You cannot use -4 and -A at the same time\n");
return EX_USAGE;

24
rdp.c
View File

@ -44,7 +44,7 @@ extern RD_BOOL g_orders;
extern RD_BOOL g_encryption;
extern RD_BOOL g_desktop_save;
extern RD_BOOL g_polygon_ellipse_orders;
extern RD_BOOL g_use_rdp5;
extern RDP_VERSION g_rdp_version;
extern uint16 g_server_rdp_version;
extern uint32 g_rdp5_performanceflags;
extern int g_server_depth;
@ -341,7 +341,7 @@ rdp_send_logon_info(uint32 flags, char *domain, char *user,
time_t tzone;
uint8 security_verifier[16];
if (!g_use_rdp5 || 1 == g_server_rdp_version)
if (g_rdp_version == RDP_V4 || 1 == g_server_rdp_version)
{
DEBUG_RDP5(("Sending RDP4-style Logon packet\n"));
@ -644,7 +644,7 @@ rdp_out_general_caps(STREAM s)
out_uint16_le(s, 0x200); /* Protocol version */
out_uint16(s, 0); /* Pad */
out_uint16(s, 0); /* Compression types */
out_uint16_le(s, g_use_rdp5 ? 0x40d : 0);
out_uint16_le(s, (g_rdp_version >= RDP_V5) ? 0x40d : 0);
/* Pad, according to T.128. 0x40d seems to
trigger
the server to start sending RDP5 packets.
@ -896,7 +896,7 @@ rdp_send_confirm_active(void)
RDP_CAPLEN_BRUSHCACHE + 0x58 + 0x08 + 0x08 + 0x34 /* unknown caps */ +
4 /* w2k fix, sessionid */ ;
if (g_use_rdp5)
if (g_rdp_version >= RDP_V5)
{
caplen += RDP_CAPLEN_BMPCACHE2;
caplen += RDP_CAPLEN_NEWPOINTER;
@ -925,7 +925,7 @@ rdp_send_confirm_active(void)
rdp_out_general_caps(s);
rdp_out_bitmap_caps(s);
rdp_out_order_caps(s);
if (g_use_rdp5)
if (g_rdp_version >= RDP_V5)
{
rdp_out_bmpcache2_caps(s);
rdp_out_newpointer_caps(s);
@ -960,7 +960,7 @@ rdp_process_general_caps(STREAM s)
in_uint16_le(s, pad2octetsB);
if (!pad2octetsB)
g_use_rdp5 = False;
g_rdp_version = RDP_V4;
}
/* Process a bitmap capability set */
@ -1060,7 +1060,7 @@ process_demand_active(STREAM s)
rdp_send_input(0, RDP_INPUT_SYNCHRONIZE, 0,
g_numlock_sync ? ui_get_numlock_state(read_keyboard_state()) : 0, 0);
if (g_use_rdp5)
if (g_rdp_version >= RDP_V5)
{
rdp_enum_bmpcache2();
rdp_send_fonts(3);
@ -1633,10 +1633,20 @@ RD_BOOL
rdp_connect(char *server, uint32 flags, char *domain, char *password,
char *command, char *directory, RD_BOOL reconnect)
{
RD_BOOL deactivated = False;
uint32 ext_disc_reason = 0;
if (!sec_connect(server, g_username, reconnect))
return False;
rdp_send_logon_info(flags, domain, g_username, password, command, directory);
/* run RDP loop until first licence demand active PDU */
while (!g_rdp_shareid)
{
if (!rdp_loop(&deactivated, &ext_disc_reason))
return False;
}
return True;
}

133
secure.c
View File

@ -30,7 +30,8 @@ extern int g_keyboard_subtype;
extern int g_keyboard_functionkeys;
extern RD_BOOL g_encryption;
extern RD_BOOL g_licence_issued;
extern RD_BOOL g_use_rdp5;
extern RD_BOOL g_licence_error_result;
extern RDP_VERSION g_rdp_version;
extern RD_BOOL g_console_session;
extern int g_server_depth;
extern VCHANNEL g_channels[];
@ -316,7 +317,7 @@ sec_init(uint32 flags, int maxlen)
int hdrlen;
STREAM s;
if (!g_licence_issued)
if (!g_licence_issued && !g_licence_error_result)
hdrlen = (flags & SEC_ENCRYPT) ? 12 : 4;
else
hdrlen = (flags & SEC_ENCRYPT) ? 12 : 0;
@ -337,7 +338,7 @@ sec_send_to_channel(STREAM s, uint32 flags, uint16 channel)
#endif
s_pop_layer(s, sec_hdr);
if (!g_licence_issued || (flags & SEC_ENCRYPT))
if ((!g_licence_issued && !g_licence_error_result) || (flags & SEC_ENCRYPT))
out_uint32_le(s, flags);
if (flags & SEC_ENCRYPT)
@ -390,10 +391,10 @@ sec_establish_key(void)
/* Output connect initial data blob */
static void
sec_out_mcs_data(STREAM s)
sec_out_mcs_data(STREAM s, uint32 selected_protocol)
{
int hostlen = 2 * strlen(g_hostname);
int length = 158 + 76 + 12 + 4;
int length = 162 + 76 + 12 + 4;
unsigned int i;
if (g_num_channels > 0)
@ -421,8 +422,8 @@ sec_out_mcs_data(STREAM s)
/* Client information */
out_uint16_le(s, SEC_TAG_CLI_INFO);
out_uint16_le(s, 212); /* length */
out_uint16_le(s, g_use_rdp5 ? 4 : 1); /* RDP version. 1 == RDP4, 4 == RDP5. */
out_uint16_le(s, 216); /* length */
out_uint16_le(s, (g_rdp_version >= RDP_V5) ? 4 : 1); /* RDP version. 1 == RDP4, 4 >= RDP5 to RDP8 */
out_uint16_le(s, 8);
out_uint16_le(s, g_width);
out_uint16_le(s, g_height);
@ -449,7 +450,8 @@ sec_out_mcs_data(STREAM s)
out_uint16_le(s, 0x0700);
out_uint8(s, 0);
out_uint32_le(s, 1);
out_uint8s(s, 64); /* End of client info */
out_uint8s(s, 64);
out_uint32_le(s, selected_protocol); /* End of client info */
out_uint16_le(s, SEC_TAG_CLI_4);
out_uint16_le(s, 12);
@ -541,8 +543,12 @@ sec_parse_crypt_info(STREAM s, uint32 * rc4_key_size,
in_uint32_le(s, *rc4_key_size); /* 1 = 40-bit, 2 = 128-bit */
in_uint32_le(s, crypt_level); /* 1 = low, 2 = medium, 3 = high */
if (crypt_level == 0) /* no encryption */
if (crypt_level == 0)
{
/* no encryption */
return False;
}
in_uint32_le(s, random_len);
in_uint32_le(s, rsa_info_len);
@ -721,7 +727,7 @@ sec_process_srv_info(STREAM s)
DEBUG_RDP5(("Server RDP version is %d\n", g_server_rdp_version));
if (1 == g_server_rdp_version)
{
g_use_rdp5 = 0;
g_rdp_version = RDP_V4;
g_server_depth = 8;
}
}
@ -796,56 +802,67 @@ sec_recv(uint8 * rdpver)
return s;
}
}
if (g_encryption || !g_licence_issued)
if (g_encryption || (!g_licence_issued && !g_licence_error_result))
{
in_uint32_le(s, sec_flags);
if (sec_flags & SEC_ENCRYPT)
if (g_encryption)
{
in_uint8s(s, 8); /* signature */
sec_decrypt(s->p, s->end - s->p);
}
if (sec_flags & SEC_LICENCE_NEG)
{
licence_process(s);
continue;
}
if (sec_flags & 0x0400) /* SEC_REDIRECT_ENCRYPT */
{
uint8 swapbyte;
in_uint8s(s, 8); /* signature */
sec_decrypt(s->p, s->end - s->p);
/* Check for a redirect packet, starts with 00 04 */
if (s->p[0] == 0 && s->p[1] == 4)
if (sec_flags & SEC_ENCRYPT)
{
/* for some reason the PDU and the length seem to be swapped.
This isn't good, but we're going to do a byte for byte
swap. So the first foure value appear as: 00 04 XX YY,
where XX YY is the little endian length. We're going to
use 04 00 as the PDU type, so after our swap this will look
like: XX YY 04 00 */
swapbyte = s->p[0];
s->p[0] = s->p[2];
s->p[2] = swapbyte;
swapbyte = s->p[1];
s->p[1] = s->p[3];
s->p[3] = swapbyte;
swapbyte = s->p[2];
s->p[2] = s->p[3];
s->p[3] = swapbyte;
in_uint8s(s, 8); /* signature */
sec_decrypt(s->p, s->end - s->p);
}
#ifdef WITH_DEBUG
/* warning! this debug statement will show passwords in the clear! */
hexdump(s->p, s->end - s->p);
#endif
}
if (sec_flags & SEC_LICENCE_NEG)
{
licence_process(s);
continue;
}
if (sec_flags & 0x0400) /* SEC_REDIRECT_ENCRYPT */
{
uint8 swapbyte;
in_uint8s(s, 8); /* signature */
sec_decrypt(s->p, s->end - s->p);
/* Check for a redirect packet, starts with 00 04 */
if (s->p[0] == 0 && s->p[1] == 4)
{
/* for some reason the PDU and the length seem to be swapped.
This isn't good, but we're going to do a byte for byte
swap. So the first foure value appear as: 00 04 XX YY,
where XX YY is the little endian length. We're going to
use 04 00 as the PDU type, so after our swap this will look
like: XX YY 04 00 */
swapbyte = s->p[0];
s->p[0] = s->p[2];
s->p[2] = swapbyte;
swapbyte = s->p[1];
s->p[1] = s->p[3];
s->p[3] = swapbyte;
swapbyte = s->p[2];
s->p[2] = s->p[3];
s->p[3] = swapbyte;
}
#ifdef WITH_DEBUG
/* warning! this debug statement will show passwords in the clear! */
hexdump(s->p, s->end - s->p);
#endif
}
}
else
{
if ((sec_flags & 0xffff) == SEC_LICENCE_NEG)
{
licence_process(s);
continue;
}
s->p -= 4;
}
}
if (channel != MCS_GLOBAL_CHANNEL)
@ -866,14 +883,20 @@ sec_recv(uint8 * rdpver)
RD_BOOL
sec_connect(char *server, char *username, RD_BOOL reconnect)
{
uint32 selected_proto;
struct stream mcs_data;
/* Start a MCS connect sequence */
if (!mcs_connect_start(server, username, reconnect, &selected_proto))
return False;
/* We exchange some RDP data during the MCS-Connect */
mcs_data.size = 512;
mcs_data.p = mcs_data.data = (uint8 *) xmalloc(mcs_data.size);
sec_out_mcs_data(&mcs_data);
sec_out_mcs_data(&mcs_data, selected_proto);
if (!mcs_connect(server, &mcs_data, username, reconnect))
/* finialize the MCS connect sequence */
if (!mcs_connect_finalize(&mcs_data))
return False;
/* sec_process_mcs_data(&mcs_data); */

202
tcp.c
View File

@ -29,6 +29,10 @@
#include <errno.h> /* errno */
#endif
#include <openssl/ssl.h>
#include <openssl/x509.h>
#include <openssl/err.h>
#include "rdesktop.h"
#ifdef _WIN32
@ -52,6 +56,8 @@
#define STREAM_COUNT 1
#endif
static SSL *g_ssl = NULL;
static SSL_CTX *g_ssl_ctx = NULL;
static int g_sock;
static struct stream g_in;
static struct stream g_out[STREAM_COUNT];
@ -109,6 +115,7 @@ tcp_init(uint32 maxlen)
void
tcp_send(STREAM s)
{
int ssl_err;
int length = s->end - s->data;
int sent, total = 0;
@ -117,18 +124,40 @@ tcp_send(STREAM s)
#endif
while (total < length)
{
sent = send(g_sock, s->data + total, length - total, 0);
if (sent <= 0)
if (g_ssl)
{
if (sent == -1 && TCP_BLOCKS)
sent = SSL_write(g_ssl, s->data + total, length - total);
if (sent <= 0)
{
tcp_can_send(g_sock, 100);
sent = 0;
ssl_err = SSL_get_error(g_ssl, sent);
if (sent < 0 && (ssl_err == SSL_ERROR_WANT_READ ||
ssl_err == SSL_ERROR_WANT_WRITE))
{
tcp_can_send(g_sock, 100);
sent = 0;
}
else
{
error("SSL_write: %d (%s)\n", ssl_err, TCP_STRERROR);
return;
}
}
else
}
else
{
sent = send(g_sock, s->data + total, length - total, 0);
if (sent <= 0)
{
error("send: %s\n", TCP_STRERROR);
return;
if (sent == -1 && TCP_BLOCKS)
{
tcp_can_send(g_sock, 100);
sent = 0;
}
else
{
error("send: %s\n", TCP_STRERROR);
return;
}
}
}
total += sent;
@ -143,7 +172,7 @@ STREAM
tcp_recv(STREAM s, uint32 length)
{
uint32 new_length, end_offset, p_offset;
int rcvd = 0;
int rcvd = 0, ssl_err;
if (s == NULL)
{
@ -173,30 +202,55 @@ tcp_recv(STREAM s, uint32 length)
while (length > 0)
{
if (!ui_select(g_sock))
if ((!g_ssl || SSL_pending(g_ssl) <= 0) && !ui_select(g_sock))
{
/* User quit */
g_user_quit = True;
return NULL;
}
rcvd = recv(g_sock, s->end, length, 0);
if (rcvd < 0)
if (g_ssl)
{
if (rcvd == -1 && TCP_BLOCKS)
rcvd = SSL_read(g_ssl, s->end, length);
ssl_err = SSL_get_error(g_ssl, rcvd);
if (ssl_err == SSL_ERROR_SSL)
{
ERR_print_errors_fp(stdout);
return NULL;
}
if (ssl_err == SSL_ERROR_WANT_READ || ssl_err == SSL_ERROR_WANT_WRITE)
{
rcvd = 0;
}
else
else if (ssl_err != SSL_ERROR_NONE)
{
error("recv: %s\n", TCP_STRERROR);
error("SSL_read: %d (%s)\n", ssl_err, TCP_STRERROR);
return NULL;
}
}
else if (rcvd == 0)
else
{
error("Connection closed\n");
return NULL;
rcvd = recv(g_sock, s->end, length, 0);
if (rcvd < 0)
{
if (rcvd == -1 && TCP_BLOCKS)
{
rcvd = 0;
}
else
{
error("recv: %s\n", TCP_STRERROR);
return NULL;
}
}
else if (rcvd == 0)
{
error("Connection closed\n");
return NULL;
}
}
s->end += rcvd;
@ -206,6 +260,108 @@ tcp_recv(STREAM s, uint32 length)
return s;
}
/* Establish a SSL/TLS 1.0 connection */
RD_BOOL
tcp_tls_connect(void)
{
int err;
SSL_load_error_strings();
SSL_library_init();
g_ssl_ctx = SSL_CTX_new(TLSv1_client_method());
if (g_ssl_ctx == NULL)
{
error("tcp_tls_connect: SSL_CTX_new() failed to create TLS v1.0 context\n");
goto fail;
}
SSL_CTX_set_options(g_ssl_ctx, SSL_OP_ALL);
g_ssl = SSL_new(g_ssl_ctx);
if (g_ssl == NULL)
{
error("tcp_tls_connect: SSL_new() failed\n");
goto fail;
}
if (SSL_set_fd(g_ssl, g_sock) < 1)
{
error("tcp_tls_connect: SSL_set_fd() failed\n");
goto fail;
}
do
{
err = SSL_connect(g_ssl);
}
while (SSL_get_error(g_ssl, err) == SSL_ERROR_WANT_READ);
if (err < 0)
{
ERR_print_errors_fp(stdout);
goto fail;
}
return True;
fail:
if (g_ssl)
SSL_free(g_ssl);
if (g_ssl_ctx)
SSL_CTX_free(g_ssl_ctx);
g_ssl = NULL;
g_ssl_ctx = NULL;
return False;
}
/* Get public key from server of TLS 1.0 connection */
RD_BOOL
tcp_tls_get_server_pubkey(STREAM s)
{
X509 *cert = NULL;
EVP_PKEY *pkey = NULL;
s->data = s->p = NULL;
s->size = 0;
if (g_ssl == NULL)
goto out;
cert = SSL_get_peer_certificate(g_ssl);
if (cert == NULL)
{
error("tcp_tls_get_server_pubkey: SSL_get_peer_certificate() failed\n");
goto out;
}
pkey = X509_get_pubkey(cert);
if (pkey == NULL)
{
error("tcp_tls_get_server_pubkey: X509_get_pubkey() failed\n");
goto out;
}
s->size = i2d_PublicKey(pkey, NULL);
if (s->size < 1)
{
error("tcp_tls_get_server_pubkey: i2d_PublicKey() failed\n");
goto out;
}
s->data = s->p = xmalloc(s->size + 1);
i2d_PublicKey(pkey, &s->p);
s->end = s->p;
out:
if (cert)
X509_free(cert);
if (pkey)
EVP_PKEY_free(pkey);
return (s->size != 0);
}
/* Establish a connection on the TCP layer */
RD_BOOL
tcp_connect(char *server)
@ -318,6 +474,16 @@ tcp_connect(char *server)
void
tcp_disconnect(void)
{
int err;
if (g_ssl)
{
err = SSL_shutdown(g_ssl);
SSL_free(g_ssl);
g_ssl = NULL;
SSL_CTX_free(g_ssl_ctx);
g_ssl_ctx = NULL;
}
TCP_CLOSE(g_sock);
}

View File

@ -36,6 +36,15 @@ typedef void *RD_HGLYPH;
typedef void *RD_HCOLOURMAP;
typedef void *RD_HCURSOR;
typedef enum _RDP_VERSION
{
RDP_V4 = 4,
RDP_V5 = 5,
RDP_V6 = 6
} RDP_VERSION;
typedef struct _RD_POINT
{
sint16 x, y;

View File

@ -47,7 +47,7 @@ extern int g_keyboard_subtype;
extern int g_keyboard_functionkeys;
extern int g_win_button_size;
extern RD_BOOL g_enable_compose;
extern RD_BOOL g_use_rdp5;
extern RDP_VERSION g_rdp_version;
extern RD_BOOL g_numlock_sync;
static RD_BOOL keymap_loaded;
@ -467,7 +467,7 @@ send_winkey(uint32 ev_time, RD_BOOL pressed, RD_BOOL leftkey)
if (pressed)
{
if (g_use_rdp5)
if (g_rdp_version >= RDP_V5)
{
rdp_send_scancode(ev_time, RDP_KEYPRESS, winkey);
}
@ -481,7 +481,7 @@ send_winkey(uint32 ev_time, RD_BOOL pressed, RD_BOOL leftkey)
else
{
/* key released */
if (g_use_rdp5)
if (g_rdp_version >= RDP_V5)
{
rdp_send_scancode(ev_time, RDP_KEYRELEASE, winkey);
}
@ -496,7 +496,7 @@ send_winkey(uint32 ev_time, RD_BOOL pressed, RD_BOOL leftkey)
static void
reset_winkey(uint32 ev_time)
{
if (g_use_rdp5)
if (g_rdp_version >= RDP_V5)
{
/* For some reason, it seems to suffice to release
*either* the left or right winkey. */