From 207cb9b818b6af7583574977127429cf2fd9a5e7 Mon Sep 17 00:00:00 2001 From: Pawel Jakub Dawidek Date: Mon, 23 Jan 2012 22:29:33 +0100 Subject: [PATCH 01/21] Fix typo: use & instead of | to check for flags. This fixes detection of requested protocols. --- libfreerdp-core/connection.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libfreerdp-core/connection.c b/libfreerdp-core/connection.c index 5403db3c3..229baf57b 100644 --- a/libfreerdp-core/connection.c +++ b/libfreerdp-core/connection.c @@ -441,7 +441,7 @@ boolean rdp_server_accept_nego(rdpRdp* rdp, STREAM* s) } printf("Requested protocols:"); - if ((rdp->nego->requested_protocols | PROTOCOL_TLS)) + if ((rdp->nego->requested_protocols & PROTOCOL_TLS)) { printf(" TLS"); if (rdp->settings->tls_security) @@ -452,7 +452,7 @@ boolean rdp_server_accept_nego(rdpRdp* rdp, STREAM* s) else printf("(n)"); } - if ((rdp->nego->requested_protocols | PROTOCOL_NLA)) + if ((rdp->nego->requested_protocols & PROTOCOL_NLA)) { printf(" NLA"); if (rdp->settings->nla_security) From b3a4be029822051bb1b00f2ef8af52735eb17d27 Mon Sep 17 00:00:00 2001 From: Pawel Jakub Dawidek Date: Tue, 24 Jan 2012 15:49:27 +0100 Subject: [PATCH 02/21] We calculate checksum on plain text, so we must have already decrypt it, which means decrypt_use_count is off by one. Account for this. --- libfreerdp-core/security.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libfreerdp-core/security.c b/libfreerdp-core/security.c index e003b213b..8df8a6548 100644 --- a/libfreerdp-core/security.c +++ b/libfreerdp-core/security.c @@ -264,7 +264,13 @@ void security_salted_mac_signature(rdpRdp *rdp, uint8* data, uint32 length, bool if (encryption) security_uint32_le(use_count_le, rdp->encrypt_use_count); else - security_uint32_le(use_count_le, rdp->decrypt_use_count); + { + /* + * We calculate checksum on plain text, so we must have already + * decrypt it, which means decrypt_use_count is off by one. + */ + security_uint32_le(use_count_le, rdp->decrypt_use_count - 1); + } /* SHA1_Digest = SHA1(MACKeyN + pad1 + length + data) */ sha1 = crypto_sha1_init(); From 4df52d7a42889cdcc9d8adf03645fdd2b64d4426 Mon Sep 17 00:00:00 2001 From: Pawel Jakub Dawidek Date: Tue, 24 Jan 2012 15:57:06 +0100 Subject: [PATCH 03/21] Use rdp_read_security_header() to read security header. --- libfreerdp-core/capabilities.c | 8 ++++---- libfreerdp-core/rdp.c | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/libfreerdp-core/capabilities.c b/libfreerdp-core/capabilities.c index 4611375a4..df13781cc 100644 --- a/libfreerdp-core/capabilities.c +++ b/libfreerdp-core/capabilities.c @@ -1793,20 +1793,20 @@ boolean rdp_recv_demand_active(rdpRdp* rdp, STREAM* s) uint16 numberCapabilities; uint16 lengthSourceDescriptor; uint16 lengthCombinedCapabilities; - uint32 securityHeader; + uint16 securityFlags; if (!rdp_read_header(rdp, s, &length, &channelId)) return false; if (rdp->settings->encryption) { - stream_read_uint32(s, securityHeader); - if (securityHeader & SEC_SECURE_CHECKSUM) + rdp_read_security_header(s, &securityFlags); + if (securityFlags & SEC_SECURE_CHECKSUM) { printf("Error: TODO\n"); return false; } - if (securityHeader & SEC_ENCRYPT) + if (securityFlags & SEC_ENCRYPT) { if (!rdp_decrypt(rdp, s, length - 4)) { diff --git a/libfreerdp-core/rdp.c b/libfreerdp-core/rdp.c index 83a87b539..0d54722da 100644 --- a/libfreerdp-core/rdp.c +++ b/libfreerdp-core/rdp.c @@ -635,7 +635,7 @@ static boolean rdp_recv_tpkt_pdu(rdpRdp* rdp, STREAM* s) uint16 pduLength; uint16 pduSource; uint16 channelId; - uint32 securityHeader; + uint16 securityFlags; if (!rdp_read_header(rdp, s, &length, &channelId)) { @@ -645,13 +645,13 @@ static boolean rdp_recv_tpkt_pdu(rdpRdp* rdp, STREAM* s) if (rdp->settings->encryption) { - stream_read_uint32(s, securityHeader); - if (securityHeader & SEC_SECURE_CHECKSUM) + rdp_read_security_header(s, &securityFlags); + if (securityFlags & SEC_SECURE_CHECKSUM) { printf("Error: TODO\n"); return false; } - if (securityHeader & (SEC_ENCRYPT|SEC_REDIRECTION_PKT)) + if (securityFlags & (SEC_ENCRYPT|SEC_REDIRECTION_PKT)) { if (!rdp_decrypt(rdp, s, length - 4)) { @@ -659,7 +659,7 @@ static boolean rdp_recv_tpkt_pdu(rdpRdp* rdp, STREAM* s) return false; } } - if (securityHeader & SEC_REDIRECTION_PKT) + if (securityFlags & SEC_REDIRECTION_PKT) { /* * [MS-RDPBCGR] 2.2.13.2.1 From e2be360ec419b22c659ba2a35df239bf0be2812e Mon Sep 17 00:00:00 2001 From: Pawel Jakub Dawidek Date: Tue, 24 Jan 2012 15:58:30 +0100 Subject: [PATCH 04/21] Add support for SEC_SECURE_CHECKSUM and FASTPATH_OUTPUT_SECURE_CHECKSUM flags. --- libfreerdp-core/capabilities.c | 7 +------ libfreerdp-core/peer.c | 2 +- libfreerdp-core/rdp.c | 32 ++++++++++++++++++++------------ libfreerdp-core/rdp.h | 2 +- 4 files changed, 23 insertions(+), 20 deletions(-) diff --git a/libfreerdp-core/capabilities.c b/libfreerdp-core/capabilities.c index df13781cc..0e23e9af1 100644 --- a/libfreerdp-core/capabilities.c +++ b/libfreerdp-core/capabilities.c @@ -1801,14 +1801,9 @@ boolean rdp_recv_demand_active(rdpRdp* rdp, STREAM* s) if (rdp->settings->encryption) { rdp_read_security_header(s, &securityFlags); - if (securityFlags & SEC_SECURE_CHECKSUM) - { - printf("Error: TODO\n"); - return false; - } if (securityFlags & SEC_ENCRYPT) { - if (!rdp_decrypt(rdp, s, length - 4)) + if (!rdp_decrypt(rdp, s, length - 4, securityFlags)) { printf("rdp_decrypt failed\n"); return false; diff --git a/libfreerdp-core/peer.c b/libfreerdp-core/peer.c index 3960e9d91..ef44d301f 100644 --- a/libfreerdp-core/peer.c +++ b/libfreerdp-core/peer.c @@ -169,7 +169,7 @@ static boolean peer_recv_fastpath_pdu(freerdp_peer* client, STREAM* s) if (fastpath->encryptionFlags & FASTPATH_OUTPUT_ENCRYPTED) { - rdp_decrypt(rdp, s, length); + rdp_decrypt(rdp, s, length, (fastpath->encryptionFlags & FASTPATH_OUTPUT_SECURE_CHECKSUM) ? SEC_SECURE_CHECKSUM : 0); } return fastpath_recv_inputs(fastpath, s); diff --git a/libfreerdp-core/rdp.c b/libfreerdp-core/rdp.c index 0d54722da..6437a8af5 100644 --- a/libfreerdp-core/rdp.c +++ b/libfreerdp-core/rdp.c @@ -312,7 +312,10 @@ static uint32 rdp_security_stream_out(rdpRdp* rdp, STREAM* s, int length) { data = s->p + 8; length = length - (data - s->data); - security_mac_signature(rdp, data, length, s->p); + if (sec_flags & SEC_SECURE_CHECKSUM) + security_salted_mac_signature(rdp, data, length, true, s->p); + else + security_mac_signature(rdp, data, length, s->p); stream_seek(s, 8); security_encrypt(s->p, length, rdp); } @@ -575,7 +578,7 @@ boolean rdp_recv_out_of_sequence_pdu(rdpRdp* rdp, STREAM* s) * @param length int */ -boolean rdp_decrypt(rdpRdp* rdp, STREAM* s, int length) +boolean rdp_decrypt(rdpRdp* rdp, STREAM* s, int length, uint16 securityFlags) { uint8 cmac[8], wmac[8]; @@ -614,10 +617,20 @@ boolean rdp_decrypt(rdpRdp* rdp, STREAM* s, int length) stream_read(s, wmac, sizeof(wmac)); length -= sizeof(wmac); security_decrypt(s->p, length, rdp); - security_mac_signature(rdp, s->p, length, cmac); + if (securityFlags & SEC_SECURE_CHECKSUM) + security_salted_mac_signature(rdp, s->p, length, false, cmac); + else + security_mac_signature(rdp, s->p, length, cmac); if (memcmp(wmac, cmac, sizeof(wmac)) != 0) { - printf("FATAL: invalid packet signature\n"); - return false; + printf("WARNING: invalid packet signature\n"); + /* + * Because Standard RDP Security is totally broken, + * and cannot protect against MITM, don't treat signature + * verification failure as critical. This at least enables + * us to work with broken RDP clients and servers that + * generate invalid signatures. + */ + //return false; } return true; } @@ -646,14 +659,9 @@ static boolean rdp_recv_tpkt_pdu(rdpRdp* rdp, STREAM* s) if (rdp->settings->encryption) { rdp_read_security_header(s, &securityFlags); - if (securityFlags & SEC_SECURE_CHECKSUM) - { - printf("Error: TODO\n"); - return false; - } if (securityFlags & (SEC_ENCRYPT|SEC_REDIRECTION_PKT)) { - if (!rdp_decrypt(rdp, s, length - 4)) + if (!rdp_decrypt(rdp, s, length - 4, securityFlags)) { printf("rdp_decrypt failed\n"); return false; @@ -721,7 +729,7 @@ static boolean rdp_recv_fastpath_pdu(rdpRdp* rdp, STREAM* s) if (fastpath->encryptionFlags & FASTPATH_OUTPUT_ENCRYPTED) { - rdp_decrypt(rdp, s, length); + rdp_decrypt(rdp, s, length, (fastpath->encryptionFlags & FASTPATH_OUTPUT_SECURE_CHECKSUM) ? SEC_SECURE_CHECKSUM : 0); } return fastpath_recv_updates(rdp->fastpath, s); diff --git a/libfreerdp-core/rdp.h b/libfreerdp-core/rdp.h index ac99c9bea..03cb2a4b8 100644 --- a/libfreerdp-core/rdp.h +++ b/libfreerdp-core/rdp.h @@ -198,6 +198,6 @@ void rdp_free(rdpRdp* rdp); #define DEBUG_RDP(fmt, ...) DEBUG_NULL(fmt, ## __VA_ARGS__) #endif -boolean rdp_decrypt(rdpRdp* rdp, STREAM* s, int length); +boolean rdp_decrypt(rdpRdp* rdp, STREAM* s, int length, uint16 securityFlags); #endif /* __RDP_H */ From af873601358843c8efba33562e8245aa551a84f6 Mon Sep 17 00:00:00 2001 From: Pawel Jakub Dawidek Date: Wed, 25 Jan 2012 11:12:37 +0100 Subject: [PATCH 05/21] MCS data header might be 7 or 8 bytes long, depending on the length. To reduce confusion a bit rename MCS_SEND_DATA_HEADER_LENGTH to MCS_SEND_DATA_HEADER_MAX_LENGTH and also rename other defines that use MCS_SEND_DATA_HEADER_MAX_LENGTH. --- libfreerdp-core/connection.c | 4 ++-- libfreerdp-core/license.c | 4 ++-- libfreerdp-core/license.h | 4 ++-- libfreerdp-core/mcs.h | 2 +- libfreerdp-core/rdp.c | 14 +++++++------- libfreerdp-core/rdp.h | 2 +- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/libfreerdp-core/connection.c b/libfreerdp-core/connection.c index 229baf57b..6a0d0c5b9 100644 --- a/libfreerdp-core/connection.c +++ b/libfreerdp-core/connection.c @@ -206,7 +206,7 @@ static boolean rdp_client_establish_keys(rdpRdp* rdp) crypto_rsa_public_encrypt(client_random, 32, key_len, mod, exp, crypt_client_random); /* send crypt client random to server */ - length = RDP_PACKET_HEADER_LENGTH + RDP_SECURITY_HEADER_LENGTH + 4 + key_len + 8; + length = RDP_PACKET_HEADER_MAX_LENGTH + RDP_SECURITY_HEADER_LENGTH + 4 + key_len + 8; s = transport_send_stream_init(rdp->mcs->transport, length); rdp_write_header(rdp, s, length, MCS_GLOBAL_CHANNEL_ID); rdp_write_security_header(s, SEC_EXCHANGE_PKT); @@ -371,7 +371,7 @@ boolean rdp_client_connect_demand_active(rdpRdp* rdp, STREAM* s) if (!rdp_recv_demand_active(rdp, s)) { stream_set_mark(s, mark); - stream_seek(s, RDP_PACKET_HEADER_LENGTH); + stream_seek(s, RDP_PACKET_HEADER_MAX_LENGTH); if (rdp_recv_out_of_sequence_pdu(rdp, s) != true) return false; diff --git a/libfreerdp-core/license.c b/libfreerdp-core/license.c index 016b0225f..830043cc3 100644 --- a/libfreerdp-core/license.c +++ b/libfreerdp-core/license.c @@ -111,7 +111,7 @@ STREAM* license_send_stream_init(rdpLicense* license) { STREAM* s; s = transport_send_stream_init(license->rdp->transport, 4096); - stream_seek(s, LICENSE_PACKET_HEADER_LENGTH); + stream_seek(s, LICENSE_PACKET_HEADER_MAX_LENGTH); return s; } @@ -135,7 +135,7 @@ boolean license_send(rdpLicense* license, STREAM* s, uint8 type) stream_set_pos(s, 0); sec_flags = SEC_LICENSE_PKT; - wMsgSize = length - LICENSE_PACKET_HEADER_LENGTH + 4; + wMsgSize = length - LICENSE_PACKET_HEADER_MAX_LENGTH + 4; /** * Using EXTENDED_ERROR_MSG_SUPPORTED here would cause mstsc to crash when * running in server mode! This flag seems to be incorrectly documented. diff --git a/libfreerdp-core/license.h b/libfreerdp-core/license.h index a6d1a5e93..0fe1de683 100644 --- a/libfreerdp-core/license.h +++ b/libfreerdp-core/license.h @@ -44,8 +44,8 @@ typedef struct rdp_license rdpLicense; #define LICENSE_PKT_SC_MASK (LICENSE_REQUEST | PLATFORM_CHALLENGE | NEW_LICENSE | UPGRADE_LICENSE | ERROR_ALERT) #define LICENSE_PKT_MASK (LICENSE_PKT_CS_MASK | LICENSE_PKT_SC_MASK) -#define LICENSE_PREAMBLE_LENGTH 4 -#define LICENSE_PACKET_HEADER_LENGTH (RDP_PACKET_HEADER_LENGTH + RDP_SECURITY_HEADER_LENGTH + LICENSE_PREAMBLE_LENGTH) +#define LICENSE_PREAMBLE_LENGTH 4 +#define LICENSE_PACKET_HEADER_MAX_LENGTH (RDP_PACKET_HEADER_MAX_LENGTH + RDP_SECURITY_HEADER_LENGTH + LICENSE_PREAMBLE_LENGTH) /* Cryptographic Lengths */ #define CLIENT_RANDOM_LENGTH 32 diff --git a/libfreerdp-core/mcs.h b/libfreerdp-core/mcs.h index 97dcf40f7..a92d9dc20 100644 --- a/libfreerdp-core/mcs.h +++ b/libfreerdp-core/mcs.h @@ -124,7 +124,7 @@ struct rdp_mcs }; typedef struct rdp_mcs rdpMcs; -#define MCS_SEND_DATA_HEADER_LENGTH 8 +#define MCS_SEND_DATA_HEADER_MAX_LENGTH 8 #define MCS_TYPE_CONNECT_INITIAL 0x65 #define MCS_TYPE_CONNECT_RESPONSE 0x66 diff --git a/libfreerdp-core/rdp.c b/libfreerdp-core/rdp.c index 6437a8af5..e2c6d5fee 100644 --- a/libfreerdp-core/rdp.c +++ b/libfreerdp-core/rdp.c @@ -105,7 +105,7 @@ boolean rdp_read_share_control_header(STREAM* s, uint16* length, uint16* type, u void rdp_write_share_control_header(STREAM* s, uint16 length, uint16 type, uint16 channel_id) { - length -= RDP_PACKET_HEADER_LENGTH; + length -= RDP_PACKET_HEADER_MAX_LENGTH; /* Share Control Header */ stream_write_uint16(s, length); /* totalLength */ @@ -143,7 +143,7 @@ boolean rdp_read_share_data_header(STREAM* s, uint16* length, uint8* type, uint3 void rdp_write_share_data_header(STREAM* s, uint16 length, uint8 type, uint32 share_id) { - length -= RDP_PACKET_HEADER_LENGTH; + length -= RDP_PACKET_HEADER_MAX_LENGTH; length -= RDP_SHARE_CONTROL_HEADER_LENGTH; length -= RDP_SHARE_DATA_HEADER_LENGTH; @@ -184,7 +184,7 @@ STREAM* rdp_send_stream_init(rdpRdp* rdp) STREAM* s; s = transport_send_stream_init(rdp->transport, 2048); - stream_seek(s, RDP_PACKET_HEADER_LENGTH); + stream_seek(s, RDP_PACKET_HEADER_MAX_LENGTH); rdp_security_stream_init(rdp, s); return s; @@ -194,7 +194,7 @@ STREAM* rdp_pdu_init(rdpRdp* rdp) { STREAM* s; s = transport_send_stream_init(rdp->transport, 2048); - stream_seek(s, RDP_PACKET_HEADER_LENGTH); + stream_seek(s, RDP_PACKET_HEADER_MAX_LENGTH); rdp_security_stream_init(rdp, s); stream_seek(s, RDP_SHARE_CONTROL_HEADER_LENGTH); return s; @@ -204,7 +204,7 @@ STREAM* rdp_data_pdu_init(rdpRdp* rdp) { STREAM* s; s = transport_send_stream_init(rdp->transport, 2048); - stream_seek(s, RDP_PACKET_HEADER_LENGTH); + stream_seek(s, RDP_PACKET_HEADER_MAX_LENGTH); rdp_security_stream_init(rdp, s); stream_seek(s, RDP_SHARE_CONTROL_HEADER_LENGTH); stream_seek(s, RDP_SHARE_DATA_HEADER_LENGTH); @@ -257,7 +257,7 @@ void rdp_write_header(rdpRdp* rdp, STREAM* s, uint16 length, uint16 channel_id) { int pad; - body_length = length - RDP_PACKET_HEADER_LENGTH - 16; + body_length = length - RDP_PACKET_HEADER_MAX_LENGTH - 16; pad = 8 - (body_length % 8); if (pad != 8) length += pad; @@ -268,7 +268,7 @@ void rdp_write_header(rdpRdp* rdp, STREAM* s, uint16 length, uint16 channel_id) per_write_integer16(s, channel_id, 0); /* channelId */ stream_write_uint8(s, 0x70); /* dataPriority + segmentation */ - length = (length - RDP_PACKET_HEADER_LENGTH) | 0x8000; + length = (length - RDP_PACKET_HEADER_MAX_LENGTH) | 0x8000; stream_write_uint16_be(s, length); /* userData (OCTET_STRING) */ } diff --git a/libfreerdp-core/rdp.h b/libfreerdp-core/rdp.h index 03cb2a4b8..10a2b62b0 100644 --- a/libfreerdp-core/rdp.h +++ b/libfreerdp-core/rdp.h @@ -63,7 +63,7 @@ #define RDP_SECURITY_HEADER_LENGTH 4 #define RDP_SHARE_CONTROL_HEADER_LENGTH 6 #define RDP_SHARE_DATA_HEADER_LENGTH 12 -#define RDP_PACKET_HEADER_LENGTH (TPDU_DATA_LENGTH + MCS_SEND_DATA_HEADER_LENGTH) +#define RDP_PACKET_HEADER_MAX_LENGTH (TPDU_DATA_LENGTH + MCS_SEND_DATA_HEADER_MAX_LENGTH) #define PDU_TYPE_DEMAND_ACTIVE 0x1 #define PDU_TYPE_CONFIRM_ACTIVE 0x3 From cea62dd9f641cfa6963a30b27f2ef6e6244581c1 Mon Sep 17 00:00:00 2001 From: Pawel Jakub Dawidek Date: Wed, 25 Jan 2012 16:00:16 +0100 Subject: [PATCH 06/21] Simplify code by using per_read_length() instead of reimplementing it. --- libfreerdp-core/fastpath.c | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/libfreerdp-core/fastpath.c b/libfreerdp-core/fastpath.c index 90dffb731..0366573fd 100644 --- a/libfreerdp-core/fastpath.c +++ b/libfreerdp-core/fastpath.c @@ -24,6 +24,7 @@ #include #include "orders.h" +#include "per.h" #include "update.h" #include "surface.h" @@ -99,10 +100,7 @@ uint16 fastpath_read_header_rdp(rdpFastPath* fastpath, STREAM* s) { uint8 header; uint16 length; - uint8 t; - uint16 hs; - hs = 2; stream_read_uint8(s, header); if (fastpath != NULL) @@ -111,18 +109,9 @@ uint16 fastpath_read_header_rdp(rdpFastPath* fastpath, STREAM* s) fastpath->numberEvents = (header & 0x3C) >> 2; } - stream_read_uint8(s, length); /* length1 */ - /* If most significant bit is not set, length2 is not presented. */ - if ((length & 0x80)) - { - hs++; - length &= 0x7F; - length <<= 8; - stream_read_uint8(s, t); - length += t; - } + per_read_length(s, &length); - return length - hs; + return length - stream_get_length(s); } static void fastpath_recv_orders(rdpFastPath* fastpath, STREAM* s) From 68b9f65e8721e3ca401050d4299afc3e22d2b238 Mon Sep 17 00:00:00 2001 From: Pawel Jakub Dawidek Date: Wed, 25 Jan 2012 16:06:31 +0100 Subject: [PATCH 07/21] Add --secure-checksum option that will enable usage of salted checksums with Standard RDP encryption. --- include/freerdp/settings.h | 3 ++- libfreerdp-core/connection.c | 2 ++ libfreerdp-core/rdp.c | 2 ++ libfreerdp-core/rdp.h | 1 + libfreerdp-core/settings.c | 1 + libfreerdp-utils/args.c | 5 +++++ 6 files changed, 13 insertions(+), 1 deletion(-) diff --git a/include/freerdp/settings.h b/include/freerdp/settings.h index c16b97c21..e83ab92f6 100644 --- a/include/freerdp/settings.h +++ b/include/freerdp/settings.h @@ -296,7 +296,8 @@ struct rdp_settings boolean nla_security; /* 146 */ boolean rdp_security; /* 147 */ uint32 ntlm_version; /* 148 */ - uint32 paddingF[160 - 149]; /* 149 */ + boolean secure_checksum; /* 149 */ + uint32 paddingF[160 - 150]; /* 150 */ /* Session */ boolean console_audio; /* 160 */ diff --git a/libfreerdp-core/connection.c b/libfreerdp-core/connection.c index 6a0d0c5b9..2a88d9770 100644 --- a/libfreerdp-core/connection.c +++ b/libfreerdp-core/connection.c @@ -225,6 +225,8 @@ static boolean rdp_client_establish_keys(rdpRdp* rdp) } rdp->do_crypt = true; + if (rdp->settings->secure_checksum) + rdp->do_secure_checksum = true; if (rdp->settings->encryption_method == ENCRYPTION_METHOD_FIPS) { diff --git a/libfreerdp-core/rdp.c b/libfreerdp-core/rdp.c index e2c6d5fee..2b48102fd 100644 --- a/libfreerdp-core/rdp.c +++ b/libfreerdp-core/rdp.c @@ -165,6 +165,8 @@ static int rdp_security_stream_init(rdpRdp* rdp, STREAM* s) if (rdp->settings->encryption_method == ENCRYPTION_METHOD_FIPS) stream_seek(s, 4); rdp->sec_flags |= SEC_ENCRYPT; + if (rdp->do_secure_checksum) + rdp->sec_flags |= SEC_SECURE_CHECKSUM; } else if (rdp->sec_flags != 0) { diff --git a/libfreerdp-core/rdp.h b/libfreerdp-core/rdp.h index 10a2b62b0..fb7c5c912 100644 --- a/libfreerdp-core/rdp.h +++ b/libfreerdp-core/rdp.h @@ -143,6 +143,7 @@ struct rdp_rdp struct crypto_hmac_struct* fips_hmac; uint32 sec_flags; boolean do_crypt; + boolean do_secure_checksum; uint8 sign_key[16]; uint8 decrypt_key[16]; uint8 encrypt_key[16]; diff --git a/libfreerdp-core/settings.c b/libfreerdp-core/settings.c index 69113dae3..715eceaad 100644 --- a/libfreerdp-core/settings.c +++ b/libfreerdp-core/settings.c @@ -56,6 +56,7 @@ rdpSettings* settings_new(void* instance) settings->kbd_fn_keys = 0; settings->kbd_layout = 0; settings->encryption = false; + settings->secure_checksum = false; settings->port = 3389; settings->desktop_resize = true; diff --git a/libfreerdp-utils/args.c b/libfreerdp-utils/args.c index 6d7ef652d..e50d44f34 100644 --- a/libfreerdp-utils/args.c +++ b/libfreerdp-utils/args.c @@ -101,6 +101,7 @@ int freerdp_parse_args(rdpSettings* settings, int argc, char** argv, " --ntlm: force NTLM authentication protocol version (1 or 2)\n" " --ignore-certificate: ignore verification of logon certificate\n" " --sec: force protocol security (rdp, tls or nla)\n" + " --secure-checksum: use salted checksums with Standard RDP encryption\n" " --version: print version information\n" "\n", argv[0]); return FREERDP_ARGS_PARSE_HELP; //TODO: What is the correct return @@ -600,6 +601,10 @@ int freerdp_parse_args(rdpSettings* settings, int argc, char** argv, } num_extensions++; } + else if (strcmp("--secure-checksum", argv[index]) == 0) + { + settings->secure_checksum = true; + } else if (strcmp("--version", argv[index]) == 0) { printf("This is FreeRDP version %s\n", FREERDP_VERSION_FULL); From 9c8a6bd8e919db42fdace93e750155cc0d598c10 Mon Sep 17 00:00:00 2001 From: Pawel Jakub Dawidek Date: Wed, 25 Jan 2012 16:07:15 +0100 Subject: [PATCH 08/21] Add some FASTPATH_INPUT_* enums. --- libfreerdp-core/fastpath.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/libfreerdp-core/fastpath.h b/libfreerdp-core/fastpath.h index 1997e1209..70e90bcac 100644 --- a/libfreerdp-core/fastpath.h +++ b/libfreerdp-core/fastpath.h @@ -25,12 +25,24 @@ typedef struct rdp_fastpath rdpFastPath; +enum FASTPATH_INPUT_ACTION_TYPE +{ + FASTPATH_INPUT_ACTION_FASTPATH = 0x0, + FASTPATH_INPUT_ACTION_X224 = 0x3 +}; + enum FASTPATH_OUTPUT_ACTION_TYPE { FASTPATH_OUTPUT_ACTION_FASTPATH = 0x0, FASTPATH_OUTPUT_ACTION_X224 = 0x3 }; +enum FASTPATH_INPUT_ENCRYPTION_FLAGS +{ + FASTPATH_INPUT_SECURE_CHECKSUM = 0x1, + FASTPATH_INPUT_ENCRYPTED = 0x2 +}; + enum FASTPATH_OUTPUT_ENCRYPTION_FLAGS { FASTPATH_OUTPUT_SECURE_CHECKSUM = 0x1, From 61aa1dfb797f370525a91cb841c01d8aa1c8be0b Mon Sep 17 00:00:00 2001 From: Pawel Jakub Dawidek Date: Wed, 25 Jan 2012 16:08:14 +0100 Subject: [PATCH 09/21] Add Standard RDP encryption suport for fastpath. Both input and output. --- libfreerdp-core/fastpath.c | 104 ++++++++++++++++++++++++++++++++----- 1 file changed, 91 insertions(+), 13 deletions(-) diff --git a/libfreerdp-core/fastpath.c b/libfreerdp-core/fastpath.c index 0366573fd..22cf16367 100644 --- a/libfreerdp-core/fastpath.c +++ b/libfreerdp-core/fastpath.c @@ -474,19 +474,48 @@ boolean fastpath_recv_inputs(rdpFastPath* fastpath, STREAM* s) return true; } +static uint32 fastpath_get_sec_bytes(rdpRdp* rdp) +{ + uint32 sec_bytes; + + if (rdp->do_crypt) + { + sec_bytes = 8; + if (rdp->settings->encryption_method == ENCRYPTION_METHOD_FIPS) + sec_bytes += 4; + } + else + sec_bytes = 0; + return sec_bytes; +} + STREAM* fastpath_input_pdu_init(rdpFastPath* fastpath, uint8 eventFlags, uint8 eventCode) { + rdpRdp *rdp; STREAM* s; - s = transport_send_stream_init(fastpath->rdp->transport, 127); - stream_seek(s, 2); /* fpInputHeader and length1 */ - /* length2 is not necessary since input PDU should not exceed 127 bytes */ + + rdp = fastpath->rdp; + + s = transport_send_stream_init(rdp->transport, 256); + stream_seek(s, 3); /* fpInputHeader, length1 and length2 */ + if (rdp->do_crypt) { + rdp->sec_flags |= SEC_ENCRYPT; + if (rdp->do_secure_checksum) + rdp->sec_flags |= SEC_SECURE_CHECKSUM; + } + stream_seek(s, fastpath_get_sec_bytes(rdp)); stream_write_uint8(s, eventFlags | (eventCode << 5)); /* eventHeader (1 byte) */ return s; } boolean fastpath_send_input_pdu(rdpFastPath* fastpath, STREAM* s) { + rdpRdp *rdp; uint16 length; + uint8 eventHeader; + int sec_bytes; + + rdp = fastpath->rdp; length = stream_get_length(s); if (length > 127) @@ -495,11 +524,39 @@ boolean fastpath_send_input_pdu(rdpFastPath* fastpath, STREAM* s) return false; } - stream_set_pos(s, 0); - stream_write_uint8(s, (1 << 2)); - stream_write_uint8(s, length); + eventHeader = FASTPATH_INPUT_ACTION_FASTPATH; + eventHeader |= (1 << 2); /* numberEvents */ + if (rdp->sec_flags & SEC_ENCRYPT) + eventHeader |= (FASTPATH_INPUT_ENCRYPTED << 6); + if (rdp->sec_flags & SEC_SECURE_CHECKSUM) + eventHeader |= (FASTPATH_INPUT_SECURE_CHECKSUM << 6); - stream_set_pos(s, length); + stream_set_pos(s, 0); + stream_write_uint8(s, eventHeader); + sec_bytes = fastpath_get_sec_bytes(fastpath->rdp); + /* + * We always encode length in two bytes, eventhough we could use + * only one byte if length <= 0x7F. It is just easier that way, + * because we can leave room for fixed-length header, store all + * the data first and then store the header. + */ + stream_write_uint16_be(s, 0x8000 | (length + sec_bytes)); + + if (sec_bytes > 0) + { + uint8* ptr; + + ptr = stream_get_tail(s) + sec_bytes; + if (rdp->sec_flags & SEC_SECURE_CHECKSUM) + security_salted_mac_signature(rdp, ptr, length - 3, true, stream_get_tail(s)); + else + security_mac_signature(rdp, ptr, length - 3, stream_get_tail(s)); + security_encrypt(ptr, length - 3, rdp); + } + + rdp->sec_flags = 0; + + stream_set_pos(s, length + sec_bytes); if (transport_write(fastpath->rdp->transport, s) < 0) return false; @@ -511,26 +568,33 @@ STREAM* fastpath_update_pdu_init(rdpFastPath* fastpath) STREAM* s; s = transport_send_stream_init(fastpath->rdp->transport, FASTPATH_MAX_PACKET_SIZE); stream_seek(s, 3); /* fpOutputHeader, length1 and length2 */ + stream_seek(s, fastpath_get_sec_bytes(fastpath->rdp)); stream_seek(s, 3); /* updateHeader, size */ return s; } boolean fastpath_send_update_pdu(rdpFastPath* fastpath, uint8 updateCode, STREAM* s) { + rdpRdp *rdp; uint8* bm; + uint8* ptr; int fragment; + int sec_bytes; uint16 length; boolean result; uint16 pduLength; uint16 maxLength; uint32 totalLength; uint8 fragmentation; + uint8 header; STREAM* update; result = true; - maxLength = FASTPATH_MAX_PACKET_SIZE - 6; - totalLength = stream_get_length(s) - 6; + rdp = fastpath->rdp; + sec_bytes = fastpath_get_sec_bytes(rdp); + maxLength = FASTPATH_MAX_PACKET_SIZE - 6 - sec_bytes; + totalLength = stream_get_length(s) - 6 - sec_bytes; stream_set_pos(s, 0); update = stream_new(0); @@ -538,7 +602,7 @@ boolean fastpath_send_update_pdu(rdpFastPath* fastpath, uint8 updateCode, STREAM { length = MIN(maxLength, totalLength); totalLength -= length; - pduLength = length + 6; + pduLength = length + 6 + sec_bytes; if (totalLength == 0) fragmentation = (fragment == 0) ? FASTPATH_FRAGMENT_SINGLE : FASTPATH_FRAGMENT_LAST; @@ -546,14 +610,28 @@ boolean fastpath_send_update_pdu(rdpFastPath* fastpath, uint8 updateCode, STREAM fragmentation = (fragment == 0) ? FASTPATH_FRAGMENT_FIRST : FASTPATH_FRAGMENT_NEXT; stream_get_mark(s, bm); - stream_write_uint8(s, 0); /* fpOutputHeader (1 byte) */ + header = 0; + if (sec_bytes > 0) + header |= (FASTPATH_OUTPUT_ENCRYPTED << 6); + stream_write_uint8(s, header); /* fpOutputHeader (1 byte) */ stream_write_uint8(s, 0x80 | (pduLength >> 8)); /* length1 */ stream_write_uint8(s, pduLength & 0xFF); /* length2 */ + if (sec_bytes > 0) + stream_seek(s, sec_bytes); fastpath_write_update_header(s, updateCode, fragmentation, 0); stream_write_uint16(s, length); stream_attach(update, bm, pduLength); stream_seek(update, pduLength); + if (sec_bytes > 0) + { + ptr = bm + 3 + sec_bytes; + if (rdp->sec_flags & SEC_SECURE_CHECKSUM) + security_salted_mac_signature(rdp, ptr, length + 3, true, bm + 3); + else + security_mac_signature(rdp, ptr, length + 3, bm + 3); + security_encrypt(ptr, length + 3, rdp); + } if (transport_write(fastpath->rdp->transport, update) < 0) { stream_detach(update); @@ -562,8 +640,8 @@ boolean fastpath_send_update_pdu(rdpFastPath* fastpath, uint8 updateCode, STREAM } stream_detach(update); - /* Reserve 6 bytes for the next fragment header, if any. */ - stream_seek(s, length - 6); + /* Reserve 6+sec_bytes bytes for the next fragment header, if any. */ + stream_seek(s, length - 6 - sec_bytes); } stream_free(update); From 31b6968263cde6c392015ec0810a2b7739bc662c Mon Sep 17 00:00:00 2001 From: Pawel Jakub Dawidek Date: Wed, 25 Jan 2012 16:25:22 +0100 Subject: [PATCH 10/21] Add a comment explaining why we always encode length in two bytes, eventhough we could use one byte sometimes. --- libfreerdp-core/rdp.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libfreerdp-core/rdp.c b/libfreerdp-core/rdp.c index 2b48102fd..7a9ddfd37 100644 --- a/libfreerdp-core/rdp.c +++ b/libfreerdp-core/rdp.c @@ -269,7 +269,12 @@ void rdp_write_header(rdpRdp* rdp, STREAM* s, uint16 length, uint16 channel_id) per_write_integer16(s, rdp->mcs->user_id, MCS_BASE_CHANNEL_ID); /* initiator */ per_write_integer16(s, channel_id, 0); /* channelId */ stream_write_uint8(s, 0x70); /* dataPriority + segmentation */ - + /* + * We always encode length in two bytes, eventhough we could use + * only one byte if length <= 0x7F. It is just easier that way, + * because we can leave room for fixed-length header, store all + * the data first and then store the header. + */ length = (length - RDP_PACKET_HEADER_MAX_LENGTH) | 0x8000; stream_write_uint16_be(s, length); /* userData (OCTET_STRING) */ } From ac87b066fb30a7b8de447f78c1cb4ccddffea671 Mon Sep 17 00:00:00 2001 From: Pawel Jakub Dawidek Date: Wed, 25 Jan 2012 16:26:32 +0100 Subject: [PATCH 11/21] Correct style. --- libfreerdp-core/rdp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libfreerdp-core/rdp.c b/libfreerdp-core/rdp.c index 7a9ddfd37..ab5bc993d 100644 --- a/libfreerdp-core/rdp.c +++ b/libfreerdp-core/rdp.c @@ -628,7 +628,8 @@ boolean rdp_decrypt(rdpRdp* rdp, STREAM* s, int length, uint16 securityFlags) security_salted_mac_signature(rdp, s->p, length, false, cmac); else security_mac_signature(rdp, s->p, length, cmac); - if (memcmp(wmac, cmac, sizeof(wmac)) != 0) { + if (memcmp(wmac, cmac, sizeof(wmac)) != 0) + { printf("WARNING: invalid packet signature\n"); /* * Because Standard RDP Security is totally broken, From d37cff01ed81e19753d25469ddb27fe0173375c2 Mon Sep 17 00:00:00 2001 From: Pawel Jakub Dawidek Date: Wed, 25 Jan 2012 16:27:00 +0100 Subject: [PATCH 12/21] White-space fixes. --- libfreerdp-core/rdp.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libfreerdp-core/rdp.c b/libfreerdp-core/rdp.c index ab5bc993d..c7bb01938 100644 --- a/libfreerdp-core/rdp.c +++ b/libfreerdp-core/rdp.c @@ -113,7 +113,7 @@ void rdp_write_share_control_header(STREAM* s, uint16 length, uint16 type, uint1 stream_write_uint16(s, channel_id); /* pduSource */ } -boolean rdp_read_share_data_header(STREAM* s, uint16* length, uint8* type, uint32* share_id, +boolean rdp_read_share_data_header(STREAM* s, uint16* length, uint8* type, uint32* share_id, uint8 *compressed_type, uint16 *compressed_len) { if (stream_get_left(s) < 12) @@ -126,7 +126,7 @@ boolean rdp_read_share_data_header(STREAM* s, uint16* length, uint8* type, uint3 stream_read_uint16(s, *length); /* uncompressedLength (2 bytes) */ stream_read_uint8(s, *type); /* pduType2, Data PDU Type (1 byte) */ - if (*type & 0x80) + if (*type & 0x80) { stream_read_uint8(s, *compressed_type); /* compressedType (1 byte) */ stream_read_uint16(s, *compressed_len); /* compressedLength (2 bytes) */ @@ -381,7 +381,7 @@ boolean rdp_send(rdpRdp* rdp, STREAM* s, uint16 channel_id) s->p = sec_hold; length += rdp_security_stream_out(rdp, s, length); - + stream_set_pos(s, length); if (transport_write(rdp->transport, s) < 0) return false; @@ -728,7 +728,7 @@ static boolean rdp_recv_fastpath_pdu(rdpRdp* rdp, STREAM* s) fastpath = rdp->fastpath; length = fastpath_read_header_rdp(fastpath, s); - + if (length == 0 || length > stream_get_left(s)) { printf("incorrect FastPath PDU header length %d\n", length); From 86910c8401247ffb007338f512fc1409d1c07e33 Mon Sep 17 00:00:00 2001 From: Pawel Jakub Dawidek Date: Wed, 25 Jan 2012 16:30:54 +0100 Subject: [PATCH 13/21] Add missing decryption. --- libfreerdp-core/capabilities.c | 15 +++++++++++++++ libfreerdp-core/info.c | 23 ++++++++++++++++++++--- libfreerdp-core/peer.c | 19 ++++++++++++++++++- 3 files changed, 53 insertions(+), 4 deletions(-) diff --git a/libfreerdp-core/capabilities.c b/libfreerdp-core/capabilities.c index 0e23e9af1..7c3891ce3 100644 --- a/libfreerdp-core/capabilities.c +++ b/libfreerdp-core/capabilities.c @@ -1922,9 +1922,24 @@ boolean rdp_recv_confirm_active(rdpRdp* rdp, STREAM* s) uint16 lengthSourceDescriptor; uint16 lengthCombinedCapabilities; uint16 numberCapabilities; + uint16 securityFlags; if (!rdp_read_header(rdp, s, &length, &channelId)) return false; + + if (rdp->settings->encryption) + { + rdp_read_security_header(s, &securityFlags); + if (securityFlags & SEC_ENCRYPT) + { + if (!rdp_decrypt(rdp, s, length - 4, securityFlags)) + { + printf("rdp_decrypt failed\n"); + return false; + } + } + } + if (channelId != MCS_GLOBAL_CHANNEL_ID) return false; diff --git a/libfreerdp-core/info.c b/libfreerdp-core/info.c index bd4afad78..1336d25b7 100644 --- a/libfreerdp-core/info.c +++ b/libfreerdp-core/info.c @@ -564,15 +564,32 @@ boolean rdp_recv_client_info(rdpRdp* rdp, STREAM* s) { uint16 length; uint16 channelId; - uint16 sec_flags; + uint16 securityFlags; if (!rdp_read_header(rdp, s, &length, &channelId)) return false; - rdp_read_security_header(s, &sec_flags); - if ((sec_flags & SEC_INFO_PKT) == 0) + rdp_read_security_header(s, &securityFlags); + if ((securityFlags & SEC_INFO_PKT) == 0) return false; + if (rdp->settings->encryption) + { + if (securityFlags & SEC_REDIRECTION_PKT) + { + printf("Error: SEC_REDIRECTION_PKT unsupported\n"); + return false; + } + if (securityFlags & SEC_ENCRYPT) + { + if (!rdp_decrypt(rdp, s, length - 4, securityFlags)) + { + printf("rdp_decrypt failed\n"); + return false; + } + } + } + return rdp_read_info_packet(s, rdp->settings); } diff --git a/libfreerdp-core/peer.c b/libfreerdp-core/peer.c index ef44d301f..3136d173b 100644 --- a/libfreerdp-core/peer.c +++ b/libfreerdp-core/peer.c @@ -112,18 +112,35 @@ static boolean peer_recv_data_pdu(freerdp_peer* client, STREAM* s) static boolean peer_recv_tpkt_pdu(freerdp_peer* client, STREAM* s) { + rdpRdp *rdp; uint16 length; uint16 pduType; uint16 pduLength; uint16 pduSource; uint16 channelId; + uint16 securityFlags; - if (!rdp_read_header(client->context->rdp, s, &length, &channelId)) + rdp = client->context->rdp; + + if (!rdp_read_header(rdp, s, &length, &channelId)) { printf("Incorrect RDP header.\n"); return false; } + if (rdp->settings->encryption) + { + rdp_read_security_header(s, &securityFlags); + if (securityFlags & SEC_ENCRYPT) + { + if (!rdp_decrypt(rdp, s, length - 4, securityFlags)) + { + printf("rdp_decrypt failed\n"); + return false; + } + } + } + if (channelId != MCS_GLOBAL_CHANNEL_ID) { freerdp_channel_peer_process(client, s, channelId); From f5033b1a7c9709584339b8802ee1a884e22a450d Mon Sep 17 00:00:00 2001 From: Pawel Jakub Dawidek Date: Wed, 25 Jan 2012 16:32:56 +0100 Subject: [PATCH 14/21] - Don't fill client_random with 0x5e, we are going to fill it up with random data in the next line. - Use less magic numbers. --- libfreerdp-core/connection.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/libfreerdp-core/connection.c b/libfreerdp-core/connection.c index 2a88d9770..224fd433e 100644 --- a/libfreerdp-core/connection.c +++ b/libfreerdp-core/connection.c @@ -182,7 +182,7 @@ boolean rdp_client_redirect(rdpRdp* rdp) static boolean rdp_client_establish_keys(rdpRdp* rdp) { - uint8 client_random[32]; + uint8 client_random[CLIENT_RANDOM_LENGTH]; uint8 crypt_client_random[256 + 8]; uint32 key_len; uint8* mod; @@ -198,12 +198,11 @@ static boolean rdp_client_establish_keys(rdpRdp* rdp) /* encrypt client random */ memset(crypt_client_random, 0, sizeof(crypt_client_random)); - memset(client_random, 0x5e, 32); - crypto_nonce(client_random, 32); + crypto_nonce(client_random, sizeof(client_random)); key_len = rdp->settings->server_cert->cert_info.modulus.length; mod = rdp->settings->server_cert->cert_info.modulus.data; exp = rdp->settings->server_cert->cert_info.exponent; - crypto_rsa_public_encrypt(client_random, 32, key_len, mod, exp, crypt_client_random); + crypto_rsa_public_encrypt(client_random, sizeof(client_random), key_len, mod, exp, crypt_client_random); /* send crypt client random to server */ length = RDP_PACKET_HEADER_MAX_LENGTH + RDP_SECURITY_HEADER_LENGTH + 4 + key_len + 8; From ee9739f490babb954a32551fe42a9cb6c618f87e Mon Sep 17 00:00:00 2001 From: Pawel Jakub Dawidek Date: Wed, 25 Jan 2012 16:45:21 +0100 Subject: [PATCH 15/21] Add API to load RSA key for Standard RDP Security in server mode. --- libfreerdp-core/certificate.c | 75 +++++++++++++++++++++++++++++++++++ libfreerdp-core/certificate.h | 10 +++++ libfreerdp-core/peer.c | 4 ++ libfreerdp-core/settings.c | 2 + 4 files changed, 91 insertions(+) diff --git a/libfreerdp-core/certificate.c b/libfreerdp-core/certificate.c index 0d6b3abb0..a255275be 100644 --- a/libfreerdp-core/certificate.c +++ b/libfreerdp-core/certificate.c @@ -17,6 +17,13 @@ * limitations under the License. */ +#include +#include +#include + +#include +#include + #include "certificate.h" /** @@ -482,3 +489,71 @@ void certificate_free(rdpCertificate* certificate) } } +rdpKey* key_new(const char *keyfile) +{ + rdpKey* key; + RSA *rsa; + FILE *fp; + + key = (rdpKey*) xzalloc(sizeof(rdpKey)); + if (key == NULL) + return NULL; + + fp = fopen(keyfile, "r"); + if (fp == NULL) { + printf("unable to load RSA key from %s: %s.", keyfile, + strerror(errno)); + return NULL; + } + rsa = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL); + if (rsa == NULL) { + ERR_print_errors_fp(stdout); + fclose(fp); + return NULL; + } + fclose(fp); + + switch (RSA_check_key(rsa)) { + case 0: + RSA_free(rsa); + printf("invalid RSA key in %s", keyfile); + return NULL; + case 1: + /* Valid key. */ + break; + default: + ERR_print_errors_fp(stdout); + RSA_free(rsa); + return NULL; + } + + if (BN_num_bytes(rsa->e) > 4) { + RSA_free(rsa); + printf("RSA public exponent too large in %s", keyfile); + return NULL; + } + + freerdp_blob_alloc(&key->modulus, BN_num_bytes(rsa->n)); + BN_bn2bin(rsa->n, key->modulus.data); + crypto_reverse(key->modulus.data, key->modulus.length); + freerdp_blob_alloc(&key->private_exponent, BN_num_bytes(rsa->d)); + BN_bn2bin(rsa->d, key->private_exponent.data); + crypto_reverse(key->private_exponent.data, key->private_exponent.length); + memset(key->exponent, 0, sizeof(key->exponent)); + BN_bn2bin(rsa->e, key->exponent + sizeof(key->exponent) - BN_num_bytes(rsa->e)); + crypto_reverse(key->exponent, sizeof(key->exponent)); + + RSA_free(rsa); + + return key; +} + +void key_free(rdpKey* key) +{ + if (key != NULL) + { + freerdp_blob_free(&key->modulus); + freerdp_blob_free(&key->private_exponent); + xfree(key); + } +} diff --git a/libfreerdp-core/certificate.h b/libfreerdp-core/certificate.h index 82962663e..a1e7ef006 100644 --- a/libfreerdp-core/certificate.h +++ b/libfreerdp-core/certificate.h @@ -42,6 +42,13 @@ #define BB_RSA_KEY_BLOB 6 #define BB_RSA_SIGNATURE_BLOB 8 +struct rdp_key +{ + rdpBlob modulus; + rdpBlob private_exponent; + uint8 exponent[4]; +}; + void certificate_read_x509_certificate(rdpCertBlob* cert, rdpCertInfo* info); rdpX509CertChain* certificate_new_x509_certificate_chain(uint32 count); @@ -54,6 +61,9 @@ boolean certificate_read_server_certificate(rdpCertificate* certificate, uint8* rdpCertificate* certificate_new(void); void certificate_free(rdpCertificate* certificate); +rdpKey* key_new(const char *keyfile); +void key_free(rdpKey* key); + #ifdef WITH_DEBUG_CERTIFICATE #define DEBUG_CERTIFICATE(fmt, ...) DEBUG_CLASS(CERTIFICATE, fmt, ## __VA_ARGS__) #else diff --git a/libfreerdp-core/peer.c b/libfreerdp-core/peer.c index 3136d173b..e5ca4990c 100644 --- a/libfreerdp-core/peer.c +++ b/libfreerdp-core/peer.c @@ -23,6 +23,10 @@ static boolean freerdp_peer_initialize(freerdp_peer* client) { client->context->rdp->settings->server_mode = true; client->context->rdp->state = CONNECTION_STATE_INITIAL; + if (client->context->rdp->settings->rdp_key_file != NULL) { + client->context->rdp->settings->server_key = + key_new(client->context->rdp->settings->rdp_key_file); + } return true; } diff --git a/libfreerdp-core/settings.c b/libfreerdp-core/settings.c index 715eceaad..04b1b39a1 100644 --- a/libfreerdp-core/settings.c +++ b/libfreerdp-core/settings.c @@ -198,6 +198,7 @@ void settings_free(rdpSettings* settings) freerdp_blob_free(settings->server_certificate); xfree(settings->server_random); xfree(settings->server_certificate); + xfree(settings->rdp_key_file); certificate_free(settings->server_cert); xfree(settings->client_auto_reconnect_cookie); xfree(settings->server_auto_reconnect_cookie); @@ -205,6 +206,7 @@ void settings_free(rdpSettings* settings) xfree(settings->bitmapCacheV2CellInfo); xfree(settings->glyphCache); xfree(settings->fragCache); + key_free(settings->server_key); xfree(settings); } } From f49ea9853e48db2221791171d384833ecbf5d1f2 Mon Sep 17 00:00:00 2001 From: Pawel Jakub Dawidek Date: Wed, 25 Jan 2012 16:52:32 +0100 Subject: [PATCH 16/21] Add fields to store RSA key for server-side Standard RDP Security. --- include/freerdp/settings.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/freerdp/settings.h b/include/freerdp/settings.h index e83ab92f6..3556f38a9 100644 --- a/include/freerdp/settings.h +++ b/include/freerdp/settings.h @@ -145,6 +145,7 @@ typedef struct /* Certificates */ typedef struct rdp_certificate rdpCertificate; +typedef struct rdp_key rdpKey; struct rdp_CertBlob { @@ -350,7 +351,9 @@ struct rdp_settings rdpBlob* server_certificate; /* 267 */ boolean ignore_certificate; /* 268 */ rdpCertificate* server_cert; /* 269 */ - uint32 paddingL[280 - 270]; /* 270 */ + char* rdp_key_file; /* 270 */ + rdpKey* server_key; /* 271 */ + uint32 paddingL[280 - 272]; /* 272 */ /* Codecs */ boolean rfx_codec; /* 280 */ From 76f36461ff22ea2759e22ee5a560a4ed5542ba4e Mon Sep 17 00:00:00 2001 From: Pawel Jakub Dawidek Date: Wed, 25 Jan 2012 16:55:03 +0100 Subject: [PATCH 17/21] Add a note why we are skipping 8 bytes. --- libfreerdp-core/certificate.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libfreerdp-core/certificate.c b/libfreerdp-core/certificate.c index a255275be..cbf9e0617 100644 --- a/libfreerdp-core/certificate.c +++ b/libfreerdp-core/certificate.c @@ -258,6 +258,7 @@ static boolean certificate_process_server_public_key(rdpCertificate* certificate modlen = keylen - 8; freerdp_blob_alloc(&(certificate->cert_info.modulus), modlen); stream_read(s, certificate->cert_info.modulus.data, modlen); + /* 8 bytes of zero padding */ stream_seek(s, 8); return true; From 0a97242b3cbeea3b7307ba56966481af12ce13fb Mon Sep 17 00:00:00 2001 From: Pawel Jakub Dawidek Date: Wed, 25 Jan 2012 16:57:23 +0100 Subject: [PATCH 18/21] Add rdp_server_establish_keys() function that is responsible for establishing encryption keys for server-side Standard RDP Security. --- libfreerdp-core/connection.c | 67 ++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/libfreerdp-core/connection.c b/libfreerdp-core/connection.c index 224fd433e..6f170c41e 100644 --- a/libfreerdp-core/connection.c +++ b/libfreerdp-core/connection.c @@ -243,6 +243,73 @@ static boolean rdp_client_establish_keys(rdpRdp* rdp) return true; } +static boolean rdp_server_establish_keys(rdpRdp* rdp, STREAM* s) +{ + uint8 client_random[64]; /* Should be only 32 after successfull decryption, but on failure might take up to 64 bytes. */ + uint8 crypt_client_random[256 + 8]; + uint32 rand_len, key_len; + uint16 channel_id, length, sec_flags; + uint8* mod; + uint8* priv_exp; + + if (rdp->settings->encryption == false) + { + /* No RDP Security. */ + return true; + } + + if (!rdp_read_header(rdp, s, &length, &channel_id)) + { + printf("rdp_server_establish_keys: invalid RDP header\n"); + return false; + } + rdp_read_security_header(s, &sec_flags); + if ((sec_flags & SEC_EXCHANGE_PKT) == 0) + { + printf("rdp_server_establish_keys: missing SEC_EXCHANGE_PKT in security header\n"); + return false; + } + stream_read_uint32(s, rand_len); + key_len = rdp->settings->server_key->modulus.length; + if (rand_len != key_len + 8) + { + printf("rdp_server_establish_keys: invalid encrypted client random length\n"); + return false; + } + memset(crypt_client_random, 0, sizeof(crypt_client_random)); + stream_read(s, crypt_client_random, rand_len); + /* 8 zero bytes of padding */ + stream_seek(s, 8); + mod = rdp->settings->server_key->modulus.data; + priv_exp = rdp->settings->server_key->private_exponent.data; + crypto_rsa_private_decrypt(crypt_client_random, rand_len - 8, key_len, mod, priv_exp, client_random); + + /* now calculate encrypt / decrypt and update keys */ + if (!security_establish_keys(client_random, rdp)) + { + return false; + } + + rdp->do_crypt = true; + if (rdp->settings->secure_checksum) + rdp->do_secure_checksum = true; + + if (rdp->settings->encryption_method == ENCRYPTION_METHOD_FIPS) + { + uint8 fips_ivec[8] = { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF }; + rdp->fips_encrypt = crypto_des3_encrypt_init(rdp->fips_encrypt_key, fips_ivec); + rdp->fips_decrypt = crypto_des3_decrypt_init(rdp->fips_decrypt_key, fips_ivec); + + rdp->fips_hmac = crypto_hmac_new(); + return true; + } + + rdp->rc4_decrypt_key = crypto_rc4_init(rdp->decrypt_key, rdp->rc4_key_len); + rdp->rc4_encrypt_key = crypto_rc4_init(rdp->encrypt_key, rdp->rc4_key_len); + + return true; +} + boolean rdp_client_connect_mcs_connect_response(rdpRdp* rdp, STREAM* s) { if (!mcs_recv_connect_response(rdp->mcs, s)) From daf565dbd3ae7fe165be9a8b9ec2d0e185f844be Mon Sep 17 00:00:00 2001 From: Pawel Jakub Dawidek Date: Wed, 25 Jan 2012 17:00:40 +0100 Subject: [PATCH 19/21] Add complete implementation of gcc_write_server_security_data() function that implements server-side Standard RDP Security. --- libfreerdp-core/gcc.c | 127 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 120 insertions(+), 7 deletions(-) diff --git a/libfreerdp-core/gcc.c b/libfreerdp-core/gcc.c index c87e87fd8..ef2892c46 100644 --- a/libfreerdp-core/gcc.c +++ b/libfreerdp-core/gcc.c @@ -825,16 +825,129 @@ boolean gcc_read_server_security_data(STREAM* s, rdpSettings *settings) return true; } +static const uint8 initial_signature[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01 +}; + void gcc_write_server_security_data(STREAM* s, rdpSettings *settings) { - gcc_write_user_data_header(s, SC_SECURITY, 12); + uint32 headerLen, serverRandomLen, serverCertLen, wPublicKeyBlobLen; + uint8 signature[sizeof(initial_signature)]; + uint8 encryptedSignature[TSSK_KEY_LENGTH]; + uint8* sigData; + int expLen, keyLen, sigDataLen; - stream_write_uint32(s, ENCRYPTION_METHOD_NONE); /* encryptionMethod */ - stream_write_uint32(s, ENCRYPTION_LEVEL_NONE); /* encryptionLevel */ -#if 0 - stream_write_uint32(s, 0); /* serverRandomLen */ - stream_write_uint32(s, 0); /* serverCertLen */ -#endif + if (!settings->encryption) { + settings->encryption_method = ENCRYPTION_METHOD_NONE; + settings->encryption_level = ENCRYPTION_LEVEL_NONE; + } + else if ((settings->encryption_method & ENCRYPTION_METHOD_FIPS) != 0) + { + settings->encryption_method = ENCRYPTION_METHOD_FIPS; + } + else if ((settings->encryption_method & ENCRYPTION_METHOD_128BIT) != 0) + { + settings->encryption_method = ENCRYPTION_METHOD_128BIT; + } + else if ((settings->encryption_method & ENCRYPTION_METHOD_40BIT) != 0) + { + settings->encryption_method = ENCRYPTION_METHOD_40BIT; + } + + if (settings->encryption_method != ENCRYPTION_METHOD_NONE) + settings->encryption_level = ENCRYPTION_LEVEL_CLIENT_COMPATIBLE; + + headerLen = 12; + keyLen = 0; + wPublicKeyBlobLen = 0; + serverRandomLen = 0; + serverCertLen = 0; + + if (settings->encryption_method != ENCRYPTION_METHOD_NONE || + settings->encryption_level != ENCRYPTION_LEVEL_NONE) { + serverRandomLen = 32; + + keyLen = settings->server_key->modulus.length; + expLen = sizeof(settings->server_key->exponent); + wPublicKeyBlobLen = 4; /* magic (RSA1) */ + wPublicKeyBlobLen += 4; /* keylen */ + wPublicKeyBlobLen += 4; /* bitlen */ + wPublicKeyBlobLen += 4; /* datalen */ + wPublicKeyBlobLen += expLen; + wPublicKeyBlobLen += keyLen; + wPublicKeyBlobLen += 8; /* 8 bytes of zero padding */ + + serverCertLen = 4; /* dwVersion */ + serverCertLen += 4; /* dwSigAlgId */ + serverCertLen += 4; /* dwKeyAlgId */ + serverCertLen += 2; /* wPublicKeyBlobType */ + serverCertLen += 2; /* wPublicKeyBlobLen */ + serverCertLen += wPublicKeyBlobLen; + serverCertLen += 2; /* wSignatureBlobType */ + serverCertLen += 2; /* wSignatureBlobLen */ + serverCertLen += sizeof(encryptedSignature); /* SignatureBlob */ + serverCertLen += 8; /* 8 bytes of zero padding */ + + headerLen += sizeof(serverRandomLen); + headerLen += sizeof(serverCertLen); + headerLen += serverRandomLen; + headerLen += serverCertLen; + } + + gcc_write_user_data_header(s, SC_SECURITY, headerLen); + + stream_write_uint32(s, settings->encryption_method); /* encryptionMethod */ + stream_write_uint32(s, settings->encryption_level); /* encryptionLevel */ + if (settings->encryption_method == ENCRYPTION_METHOD_NONE && + settings->encryption_level == ENCRYPTION_LEVEL_NONE) { + return; + } + + stream_write_uint32(s, serverRandomLen); /* serverRandomLen */ + stream_write_uint32(s, serverCertLen); /* serverCertLen */ + + freerdp_blob_alloc(settings->server_random, serverRandomLen); + crypto_nonce(settings->server_random->data, serverRandomLen); + stream_write(s, settings->server_random->data, serverRandomLen); + + sigData = stream_get_tail(s); + + stream_write_uint32(s, CERT_CHAIN_VERSION_1); /* dwVersion (4 bytes) */ + stream_write_uint32(s, SIGNATURE_ALG_RSA); /* dwSigAlgId */ + stream_write_uint32(s, KEY_EXCHANGE_ALG_RSA); /* dwKeyAlgId */ + stream_write_uint16(s, BB_RSA_KEY_BLOB); /* wPublicKeyBlobType */ + + stream_write_uint16(s, wPublicKeyBlobLen); /* wPublicKeyBlobLen */ + stream_write(s, "RSA1", 4); /* magic */ + stream_write_uint32(s, keyLen + 8); /* keylen */ + stream_write_uint32(s, keyLen * 8); /* bitlen */ + stream_write_uint32(s, keyLen - 1); /* datalen */ + + stream_write(s, settings->server_key->exponent, expLen); + stream_write(s, settings->server_key->modulus.data, keyLen); + stream_write_zero(s, 8); + + sigDataLen = stream_get_tail(s) - sigData; + + stream_write_uint16(s, BB_RSA_SIGNATURE_BLOB); /* wSignatureBlobType */ + stream_write_uint16(s, keyLen + 8); /* wSignatureBlobLen */ + + memcpy(signature, initial_signature, sizeof(initial_signature)); + CryptoMd5 md5Ctx; + md5Ctx = crypto_md5_init(); + crypto_md5_update(md5Ctx, sigData, sigDataLen); + crypto_md5_final(md5Ctx, signature); + + crypto_rsa_private_encrypt(signature, sizeof(signature), TSSK_KEY_LENGTH, tssk_modulus, tssk_privateExponent, encryptedSignature); + stream_write(s, encryptedSignature, sizeof(encryptedSignature)); + stream_write_zero(s, 8); } /** From 7207e945c3e1dffd45593db787880a970a5f44fb Mon Sep 17 00:00:00 2001 From: Pawel Jakub Dawidek Date: Wed, 25 Jan 2012 17:04:19 +0100 Subject: [PATCH 20/21] - Ignore received encryption_method and encryption_level if we don't support encryption. - Print a warning if we receive unregognized type. --- libfreerdp-core/gcc.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/libfreerdp-core/gcc.c b/libfreerdp-core/gcc.c index ef2892c46..590dfeb1c 100644 --- a/libfreerdp-core/gcc.c +++ b/libfreerdp-core/gcc.c @@ -407,6 +407,7 @@ boolean gcc_read_server_data_blocks(STREAM* s, rdpSettings* settings, int length break; default: + printf("gcc_read_server_data_blocks: ignoring type=%hu\n", type); break; } offset += blockLength; @@ -740,10 +741,16 @@ boolean gcc_read_client_security_data(STREAM* s, rdpSettings *settings, uint16 b if (blockLength < 8) return false; - stream_read_uint32(s, settings->encryption_method); /* encryptionMethods */ - if (settings->encryption_method == 0) - stream_read_uint32(s, settings->encryption_method); /* extEncryptionMethods */ - + if (settings->encryption) + { + stream_read_uint32(s, settings->encryption_method); /* encryptionMethods */ + if (settings->encryption_method == 0) + stream_read_uint32(s, settings->encryption_method); /* extEncryptionMethods */ + } + else + { + stream_seek(s, 8); + } return true; } From 31529071fd5fae6143c10d8a1eee5517b61a864e Mon Sep 17 00:00:00 2001 From: Pawel Jakub Dawidek Date: Wed, 25 Jan 2012 17:08:10 +0100 Subject: [PATCH 21/21] Add glue that enables server-side support for Standard RDP Security. --- libfreerdp-core/connection.c | 27 +++++++++++++---- libfreerdp-core/connection.h | 2 ++ libfreerdp-core/nego.c | 58 +++++++++++++++++++++++++++++++++--- libfreerdp-core/peer.c | 9 ++++++ 4 files changed, 87 insertions(+), 9 deletions(-) diff --git a/libfreerdp-core/connection.c b/libfreerdp-core/connection.c index 6f170c41e..d5184a5af 100644 --- a/libfreerdp-core/connection.c +++ b/libfreerdp-core/connection.c @@ -502,11 +502,8 @@ boolean rdp_server_accept_nego(rdpRdp* rdp, STREAM* s) if (!nego_read_request(rdp->nego, s)) return false; - if (rdp->nego->requested_protocols == PROTOCOL_RDP) - { - printf("Standard RDP encryption is not supported.\n"); - return false; - } + + rdp->nego->selected_protocol = 0; printf("Requested protocols:"); if ((rdp->nego->requested_protocols & PROTOCOL_TLS)) @@ -531,6 +528,14 @@ boolean rdp_server_accept_nego(rdpRdp* rdp, STREAM* s) else printf("(n)"); } + printf(" RDP"); + if (rdp->settings->rdp_security && rdp->nego->selected_protocol == 0) + { + printf("(Y)"); + rdp->nego->selected_protocol = PROTOCOL_RDP; + } + else + printf("(n)"); printf("\n"); if (!nego_send_negotiation_response(rdp->nego)) @@ -632,8 +637,20 @@ boolean rdp_server_accept_mcs_channel_join_request(rdpRdp* rdp, STREAM* s) return true; } +boolean rdp_server_accept_client_keys(rdpRdp* rdp, STREAM* s) +{ + + if (!rdp_server_establish_keys(rdp, s)) + return false; + + rdp->state = CONNECTION_STATE_ESTABLISH_KEYS; + + return true; +} + boolean rdp_server_accept_client_info(rdpRdp* rdp, STREAM* s) { + if (!rdp_recv_client_info(rdp, s)) return false; diff --git a/libfreerdp-core/connection.h b/libfreerdp-core/connection.h index ebcae1f7b..eb5c0460f 100644 --- a/libfreerdp-core/connection.h +++ b/libfreerdp-core/connection.h @@ -39,6 +39,7 @@ enum CONNECTION_STATE CONNECTION_STATE_MCS_ERECT_DOMAIN, CONNECTION_STATE_MCS_ATTACH_USER, CONNECTION_STATE_MCS_CHANNEL_JOIN, + CONNECTION_STATE_ESTABLISH_KEYS, CONNECTION_STATE_LICENSE, CONNECTION_STATE_CAPABILITY, CONNECTION_STATE_FINALIZATION, @@ -59,6 +60,7 @@ boolean rdp_server_accept_mcs_connect_initial(rdpRdp* rdp, STREAM* s); boolean rdp_server_accept_mcs_erect_domain_request(rdpRdp* rdp, STREAM* s); boolean rdp_server_accept_mcs_attach_user_request(rdpRdp* rdp, STREAM* s); boolean rdp_server_accept_mcs_channel_join_request(rdpRdp* rdp, STREAM* s); +boolean rdp_server_accept_client_keys(rdpRdp* rdp, STREAM* s); boolean rdp_server_accept_client_info(rdpRdp* rdp, STREAM* s); boolean rdp_server_accept_confirm_active(rdpRdp* rdp, STREAM* s); boolean rdp_server_reactivate(rdpRdp* rdp); diff --git a/libfreerdp-core/nego.c b/libfreerdp-core/nego.c index 43ee42742..7eb810bba 100644 --- a/libfreerdp-core/nego.c +++ b/libfreerdp-core/nego.c @@ -521,8 +521,13 @@ void nego_process_negotiation_failure(rdpNego* nego, STREAM* s) boolean nego_send_negotiation_response(rdpNego* nego) { STREAM* s; + rdpSettings* settings; int length; uint8 *bm, *em; + boolean ret; + + ret = true; + settings = nego->transport->settings; s = transport_send_stream_init(nego->transport, 256); length = TPDU_CONNECTION_CONFIRM_LENGTH; @@ -538,6 +543,20 @@ boolean nego_send_negotiation_response(rdpNego* nego) stream_write_uint32(s, nego->selected_protocol); /* selectedProtocol */ length += 8; } + else if (!settings->rdp_security) + { + stream_write_uint8(s, TYPE_RDP_NEG_FAILURE); + stream_write_uint8(s, 0); /* flags */ + stream_write_uint16(s, 8); /* RDP_NEG_DATA length (8) */ + /* + * TODO: Check for other possibilities, + * like SSL_NOT_ALLOWED_BY_SERVER. + */ + printf("nego_send_negotiation_response: client supports only Standard RDP Security\n"); + stream_write_uint32(s, SSL_REQUIRED_BY_SERVER); + length += 8; + ret = false; + } stream_get_mark(s, em); stream_set_mark(s, bm); @@ -548,11 +567,42 @@ boolean nego_send_negotiation_response(rdpNego* nego) if (transport_write(nego->transport, s) < 0) return false; - /* update settings with negotiated protocol security */ - nego->transport->settings->requested_protocols = nego->requested_protocols; - nego->transport->settings->selected_protocol = nego->selected_protocol; + if (ret) + { + /* update settings with negotiated protocol security */ + settings->requested_protocols = nego->requested_protocols; + settings->selected_protocol = nego->selected_protocol; - return true; + if (settings->selected_protocol == PROTOCOL_RDP) + { + settings->tls_security = false; + settings->nla_security = false; + settings->rdp_security = true; + settings->encryption = true; + settings->encryption_method = ENCRYPTION_METHOD_40BIT | ENCRYPTION_METHOD_128BIT | ENCRYPTION_METHOD_FIPS; + settings->encryption_level = ENCRYPTION_LEVEL_CLIENT_COMPATIBLE; + } + else if (settings->selected_protocol == PROTOCOL_TLS) + { + settings->tls_security = true; + settings->nla_security = false; + settings->rdp_security = false; + settings->encryption = false; + settings->encryption_method = ENCRYPTION_METHOD_NONE; + settings->encryption_level = ENCRYPTION_LEVEL_NONE; + } + else if (settings->selected_protocol == PROTOCOL_NLA) + { + settings->tls_security = true; + settings->nla_security = true; + settings->rdp_security = false; + settings->encryption = false; + settings->encryption_method = ENCRYPTION_METHOD_NONE; + settings->encryption_level = ENCRYPTION_LEVEL_NONE; + } + } + + return ret; } /** diff --git a/libfreerdp-core/peer.c b/libfreerdp-core/peer.c index e5ca4990c..a7db7c9bb 100644 --- a/libfreerdp-core/peer.c +++ b/libfreerdp-core/peer.c @@ -236,6 +236,15 @@ static boolean peer_recv_callback(rdpTransport* transport, STREAM* s, void* extr break; case CONNECTION_STATE_MCS_CHANNEL_JOIN: + if (client->context->rdp->settings->encryption) { + if (!rdp_server_accept_client_keys(client->context->rdp, s)) + return false; + break; + } + client->context->rdp->state = CONNECTION_STATE_ESTABLISH_KEYS; + /* FALLTHROUGH */ + + case CONNECTION_STATE_ESTABLISH_KEYS: if (!rdp_server_accept_client_info(client->context->rdp, s)) return false; IFCALL(client->Capabilities, client);