From 2ac4a5fa96a975ec87848708d620cd5a80ca6537 Mon Sep 17 00:00:00 2001 From: Jay Sorg Date: Mon, 12 Sep 2011 23:40:27 -0700 Subject: [PATCH] work on rdp encryption --- CMakeLists.txt | 3 +- include/freerdp/settings.h | 7 ++ libfreerdp-core/connection.c | 48 +++++++++++++- libfreerdp-core/fastpath.c | 1 + libfreerdp-core/info.c | 5 +- libfreerdp-core/license.c | 2 +- libfreerdp-core/rdp.c | 91 ++++++++++++++++++++++++++ libfreerdp-core/rdp.h | 6 ++ libfreerdp-core/security.c | 123 ++++++++++++++++++++++++++++++++--- libfreerdp-core/security.h | 4 ++ libfreerdp-core/transport.c | 2 + 11 files changed, 274 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 23068d831..374825464 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,7 +53,8 @@ endif() # Compiler-specific flags if(CMAKE_COMPILER_IS_GNUCC) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-unused-but-set-variable") + #set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-unused-but-set-variable") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -lncurses") if(CMAKE_BUILD_TYPE STREQUAL "Release") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2") endif() diff --git a/include/freerdp/settings.h b/include/freerdp/settings.h index f9bc0c09a..8c224371f 100644 --- a/include/freerdp/settings.h +++ b/include/freerdp/settings.h @@ -198,6 +198,13 @@ struct rdp_settings rdpBlob server_certificate; struct rdp_certificate* server_cert; + uint8 sign_key[16]; + uint8 decrypt_key[16]; + uint8 encrypt_key[16]; + uint8 decrypt_update_key[16]; + uint8 encrypt_update_key[16]; + int rc4_key_len; + boolean console_audio; boolean console_session; uint32 redirected_session_id; diff --git a/libfreerdp-core/connection.c b/libfreerdp-core/connection.c index 2155d9eeb..a57149022 100644 --- a/libfreerdp-core/connection.c +++ b/libfreerdp-core/connection.c @@ -19,6 +19,7 @@ #include "connection.h" #include "info.h" +#include "per.h" /** * Connection Sequence @@ -107,10 +108,12 @@ boolean rdp_client_connect(rdpRdp* rdp) static boolean rdp_establish_keys(rdpRdp* rdp) { uint8 client_random[32]; - uint8 crypt_client_random[256]; + uint8 crypt_client_random[256 + 8]; uint32 key_len; uint8* mod; uint8* exp; + uint32 length; + STREAM* s; printf("rdp_establish_keys:\n"); if (rdp->settings->encryption == False) @@ -119,12 +122,51 @@ static boolean rdp_establish_keys(rdpRdp* rdp) return True; } - memset(client_random, 0x5e, 32); /* TODO: get real random */ - crypto_nonce(client_random, 32); + /* encrypt client random */ + memset(crypt_client_random, 0, sizeof(crypt_client_random)); + memset(client_random, 0x5e, 32); + //crypto_nonce(client_random, 32); + printf("client random\n"); + freerdp_hexdump(client_random, 32); key_len = rdp->settings->server_cert->cert_info.modulus.length; + printf("key_len %d %d %d\n", key_len, rdp->mcs->user_id, MCS_BASE_CHANNEL_ID); mod = rdp->settings->server_cert->cert_info.modulus.data; exp = rdp->settings->server_cert->cert_info.exponent; crypto_rsa_encrypt(client_random, 32, key_len, mod, exp, crypt_client_random); + printf("client crypt random\n"); + freerdp_hexdump(crypt_client_random, key_len); + + /* send crypt client random to server */ + length = 7 + 8 + 4 + 4 + key_len + 8; + s = transport_send_stream_init(rdp->mcs->transport, length); + tpkt_write_header(s, length); + tpdu_write_header(s, 2, 0xf0); + per_write_choice(s, DomainMCSPDU_SendDataRequest << 2); + per_write_integer16(s, rdp->mcs->user_id, MCS_BASE_CHANNEL_ID); + per_write_integer16(s, MCS_GLOBAL_CHANNEL_ID, 0); + stream_write_uint8(s, 0x70); + length = (4 + 4 + key_len + 8) | 0x8000; + stream_write_uint16_be(s, length); + stream_write_uint32(s, 1); /* SEC_CLIENT_RANDOM */ + length = key_len + 8; + stream_write_uint32(s, length); + memcpy(s->p, crypt_client_random, length); + stream_seek(s, length); + if (transport_write(rdp->mcs->transport, s) < 0) + { + return False; + } + + /* now calculate encrypt / decrypt and upate keys */ + if (!security_establish_keys(client_random, rdp->settings)) + { + return False; + } + + rdp->rc4_decrypt_key = crypto_rc4_init(rdp->settings->decrypt_key, rdp->settings->rc4_key_len); + rdp->rc4_encrypt_key = crypto_rc4_init(rdp->settings->encrypt_key, rdp->settings->rc4_key_len); + + rdp->do_crypt = True; return True; } diff --git a/libfreerdp-core/fastpath.c b/libfreerdp-core/fastpath.c index 155a036bf..ce4f8436b 100644 --- a/libfreerdp-core/fastpath.c +++ b/libfreerdp-core/fastpath.c @@ -57,6 +57,7 @@ uint16 fastpath_read_header(rdpFastPath* fastpath, STREAM* s) if (fastpath != NULL) { fastpath->encryptionFlags = (header & 0xC0) >> 6; + printf("fastpath_read_header: fastpath->encryptionFlags %d\n", fastpath->encryptionFlags); fastpath->numberEvents = (header & 0x3C) >> 2; } diff --git a/libfreerdp-core/info.c b/libfreerdp-core/info.c index f03981e17..3b7c74866 100644 --- a/libfreerdp-core/info.c +++ b/libfreerdp-core/info.c @@ -584,11 +584,10 @@ boolean rdp_send_client_info(rdpRdp* rdp) { STREAM* s; + //rdp->settings->crypt_flags |= SEC_INFO_PKT; + rdp->sec_flags |= SEC_INFO_PKT; s = rdp_send_stream_init(rdp); - - rdp_write_security_header(s, SEC_INFO_PKT); rdp_write_info_packet(s, rdp->settings); - return rdp_send(rdp, s, MCS_GLOBAL_CHANNEL_ID); } diff --git a/libfreerdp-core/license.c b/libfreerdp-core/license.c index 9047f39da..4ed766b00 100644 --- a/libfreerdp-core/license.c +++ b/libfreerdp-core/license.c @@ -179,7 +179,7 @@ boolean license_recv(rdpLicense* license, STREAM* s) rdp_read_security_header(s, &sec_flags); if (!(sec_flags & SEC_LICENSE_PKT)) { - printf("Unexpected license packet.\n"); + printf("Unexpected license packet. got 0x%4.4x need bit 0x%4.4x set\n", sec_flags, SEC_LICENSE_PKT); return False; } diff --git a/libfreerdp-core/rdp.c b/libfreerdp-core/rdp.c index ab87b2031..379c34a92 100644 --- a/libfreerdp-core/rdp.c +++ b/libfreerdp-core/rdp.c @@ -146,6 +146,21 @@ void rdp_write_share_data_header(STREAM* s, uint16 length, uint8 type, uint32 sh stream_write_uint16(s, 0); /* compressedLength (2 bytes) */ } +static int rdp_security_stream_init(rdpRdp* rdp, STREAM* s) +{ + printf("rdp_security_stream_init:\n"); + if (rdp->do_crypt) + { + stream_seek(s, 12); + rdp->sec_flags |= SEC_ENCRYPT; + } + else if (rdp->sec_flags != 0) + { + stream_seek(s, 4); + } + return 0; +} + /** * Initialize an RDP packet stream.\n * @param rdp rdp module @@ -155,8 +170,10 @@ void rdp_write_share_data_header(STREAM* s, uint16 length, uint8 type, uint32 sh STREAM* rdp_send_stream_init(rdpRdp* rdp) { STREAM* s; + s = transport_send_stream_init(rdp->transport, 2048); stream_seek(s, RDP_PACKET_HEADER_LENGTH); + rdp_security_stream_init(rdp, s); return s; } @@ -165,6 +182,7 @@ STREAM* rdp_pdu_init(rdpRdp* rdp) STREAM* s; s = transport_send_stream_init(rdp->transport, 2048); stream_seek(s, RDP_PACKET_HEADER_LENGTH); + rdp_security_stream_init(rdp, s); stream_seek(s, RDP_SHARE_CONTROL_HEADER_LENGTH); return s; } @@ -174,6 +192,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); + rdp_security_stream_init(rdp, s); stream_seek(s, RDP_SHARE_CONTROL_HEADER_LENGTH); stream_seek(s, RDP_SHARE_DATA_HEADER_LENGTH); return s; @@ -229,6 +248,46 @@ void rdp_write_header(rdpRdp* rdp, STREAM* s, uint16 length, uint16 channel_id) stream_write_uint16_be(s, length); /* userData (OCTET_STRING) */ } +static uint32 rdp_security_stream_out(rdpRdp* rdp, STREAM* s, int length) +{ + uint32 sec_flags; + uint32 ml; + uint8* mk; + uint8* data; + + printf("rdp_security_stream_out:\n"); + sec_flags = rdp->sec_flags; + if (sec_flags != 0) + { + rdp_write_security_header(s, sec_flags); + if (sec_flags & SEC_ENCRYPT) + { + data = s->p + 8; + length = length - (data - s->data); + mk = rdp->settings->sign_key; + ml = rdp->settings->rc4_key_len; + security_mac_signature(mk, ml, data, length, s->p); + stream_seek(s, 8); + security_encrypt(s->p, length, rdp); + } + rdp->sec_flags = 0; + } + return 0; +} + +static uint32 rdp_get_sec_bytes(uint32 sec_flags) +{ + uint32 sec_bytes; + + if (sec_flags & SEC_ENCRYPT) + sec_bytes = 12; + else if (sec_flags != 0) + sec_bytes = 4; + else + sec_bytes = 0; + return sec_bytes; +} + /** * Send an RDP packet.\n * @param rdp RDP module @@ -239,12 +298,22 @@ void rdp_write_header(rdpRdp* rdp, STREAM* s, uint16 length, uint16 channel_id) boolean rdp_send(rdpRdp* rdp, STREAM* s, uint16 channel_id) { uint16 length; + uint32 sec_bytes; + uint8* sec_hold; + printf("rdp_send:\n"); length = stream_get_length(s); stream_set_pos(s, 0); rdp_write_header(rdp, s, length, channel_id); + sec_bytes = rdp_get_sec_bytes(rdp->sec_flags); + sec_hold = s->p; + stream_seek(s, sec_bytes); + + s->p = sec_hold; + rdp_security_stream_out(rdp, s, length); + stream_set_pos(s, length); if (transport_write(rdp->transport, s) < 0) return False; @@ -255,13 +324,24 @@ boolean rdp_send(rdpRdp* rdp, STREAM* s, uint16 channel_id) boolean rdp_send_pdu(rdpRdp* rdp, STREAM* s, uint16 type, uint16 channel_id) { uint16 length; + uint32 sec_bytes; + uint8* sec_hold; + printf("rdp_send_pdu:\n"); length = stream_get_length(s); stream_set_pos(s, 0); rdp_write_header(rdp, s, length, MCS_GLOBAL_CHANNEL_ID); + + sec_bytes = rdp_get_sec_bytes(rdp->sec_flags); + sec_hold = s->p; + stream_seek(s, sec_bytes); + rdp_write_share_control_header(s, length, type, channel_id); + s->p = sec_hold; + rdp_security_stream_out(rdp, s, length); + stream_set_pos(s, length); if (transport_write(rdp->transport, s) < 0) return False; @@ -272,16 +352,27 @@ boolean rdp_send_pdu(rdpRdp* rdp, STREAM* s, uint16 type, uint16 channel_id) boolean rdp_send_data_pdu(rdpRdp* rdp, STREAM* s, uint8 type, uint16 channel_id) { uint16 length; + uint32 sec_bytes; + uint8* sec_hold; + printf("rdp_send_data_pdu:\n"); length = stream_get_length(s); stream_set_pos(s, 0); rdp_write_header(rdp, s, length, MCS_GLOBAL_CHANNEL_ID); + + sec_bytes = rdp_get_sec_bytes(rdp->sec_flags); + sec_hold = s->p; + stream_seek(s, sec_bytes); + rdp_write_share_control_header(s, length, PDU_TYPE_DATA, channel_id); rdp_write_share_data_header(s, length, type, rdp->settings->share_id); //printf("send %s Data PDU (0x%02X), length:%d\n", DATA_PDU_TYPE_STRINGS[type], type, length); + s->p = sec_hold; + rdp_security_stream_out(rdp, s, length); + stream_set_pos(s, length); if (transport_write(rdp->transport, s) < 0) return False; diff --git a/libfreerdp-core/rdp.h b/libfreerdp-core/rdp.h index e36236719..aabd18c9d 100644 --- a/libfreerdp-core/rdp.h +++ b/libfreerdp-core/rdp.h @@ -123,6 +123,12 @@ struct rdp_rdp struct rdp_settings* settings; struct rdp_transport* transport; struct rdp_vchan* vchan; + struct crypto_rc4_struct* rc4_decrypt_key; + int decrypt_use_count; + struct crypto_rc4_struct* rc4_encrypt_key; + int encrypt_use_count; + uint32 sec_flags; + boolean do_crypt; }; void rdp_read_security_header(STREAM* s, uint16* flags); diff --git a/libfreerdp-core/security.c b/libfreerdp-core/security.c index 81c494630..cc370fc0e 100644 --- a/libfreerdp-core/security.c +++ b/libfreerdp-core/security.c @@ -85,10 +85,10 @@ static void security_master_hash(char* input, int length, uint8* master_secret, void security_session_key_blob(uint8* master_secret, uint8* client_random, uint8* server_random, uint8* output) { - /* MasterHash = MasterHash('A') + MasterHash('BB') + MasterHash('CCC') */ - security_master_hash("A", 1, master_secret, client_random, server_random, &output[0]); - security_master_hash("BB", 2, master_secret, client_random, server_random, &output[16]); - security_master_hash("CCC", 3, master_secret, client_random, server_random, &output[32]); + /* MasterHash = MasterHash('X') + MasterHash('YY') + MasterHash('ZZZ') */ + security_master_hash("X", 1, master_secret, client_random, server_random, &output[0]); + security_master_hash("YY", 2, master_secret, client_random, server_random, &output[16]); + security_master_hash("ZZZ", 3, master_secret, client_random, server_random, &output[32]); } void security_mac_salt_key(uint8* session_key_blob, uint8* client_random, uint8* server_random, uint8* output) @@ -97,19 +97,23 @@ void security_mac_salt_key(uint8* session_key_blob, uint8* client_random, uint8* memcpy(output, session_key_blob, 16); } -void security_licensing_encryption_key(uint8* session_key_blob, uint8* client_random, uint8* server_random, uint8* output) +void security_md5_16_32_32(uint8* in0, uint8* in1, uint8* in2, uint8* output) { CryptoMd5 md5; - /* LicensingEncryptionKey = MD5(Second128Bits(SessionKeyBlob) + ClientRandom + ServerRandom)) */ - md5 = crypto_md5_init(); - crypto_md5_update(md5, &session_key_blob[16], 16); /* Second128Bits(SessionKeyBlob) */ - crypto_md5_update(md5, client_random, 32); /* ClientRandom */ - crypto_md5_update(md5, server_random, 32); /* ServerRandom */ + crypto_md5_update(md5, in0, 16); + crypto_md5_update(md5, in1, 32); + crypto_md5_update(md5, in2, 32); crypto_md5_final(md5, output); } +void security_licensing_encryption_key(uint8* session_key_blob, uint8* client_random, uint8* server_random, uint8* output) +{ + /* LicensingEncryptionKey = MD5(Second128Bits(SessionKeyBlob) + ClientRandom + ServerRandom)) */ + security_md5_16_32_32(&session_key_blob[16], client_random, server_random, output); +} + void security_uint32_le(uint8* output, uint32 value) { output[0] = (value) & 0xFF; @@ -172,3 +176,102 @@ void security_mac_signature(uint8* mac_key, int mac_key_length, uint8* data, uin memcpy(output, md5_digest, 8); } + +boolean security_establish_keys(uint8* client_random, rdpSettings* settings) +{ + uint8 pre_master_secret[48]; + uint8 master_secret[48]; + uint8 session_key_blob[48]; + uint8* server_random; + uint8 salt40[] = { 0xD1, 0x26, 0x9E }; + + printf("security_establish_keys:\n"); + + server_random = settings->server_random.data; + + memcpy(pre_master_secret, client_random, 24); + memcpy(pre_master_secret + 24, server_random, 24); + + security_master_secret(pre_master_secret, client_random, server_random, master_secret); + security_session_key_blob(master_secret, client_random, server_random, session_key_blob); + + memcpy(settings->sign_key, session_key_blob, 16); + + security_md5_16_32_32(&session_key_blob[16], client_random, server_random, settings->decrypt_key); + security_md5_16_32_32(&session_key_blob[32], client_random, server_random, settings->encrypt_key); + + if (settings->encryption_method == 1) /* 40 and 56 bit */ + { + memcpy(settings->sign_key, salt40, 3); /* TODO 56 bit */ + memcpy(settings->decrypt_key, salt40, 3); /* TODO 56 bit */ + memcpy(settings->encrypt_key, salt40, 3); /* TODO 56 bit */ + settings->rc4_key_len = 8; + } + else /* 128 bit */ + { + settings->rc4_key_len = 16; + } + + memcpy(settings->decrypt_update_key, settings->decrypt_key, 16); + memcpy(settings->encrypt_update_key, settings->encrypt_key, 16); + + return True; +} + +boolean security_key_update(uint8* key, uint8* update_key, int key_len) +{ + uint8 sha1h[20]; + CryptoMd5 md5; + CryptoSha1 sha1; + CryptoRc4 rc4; + uint8 salt40[] = { 0xD1, 0x26, 0x9E }; + + sha1 = crypto_sha1_init(); + crypto_sha1_update(sha1, update_key, key_len); + crypto_sha1_update(sha1, pad1, sizeof(pad1)); + crypto_sha1_update(sha1, key, key_len); + crypto_sha1_final(sha1, sha1h); + + md5 = crypto_md5_init(); + crypto_md5_update(md5, update_key, key_len); + crypto_md5_update(md5, pad2, sizeof(pad2)); + crypto_md5_update(md5, sha1h, 20); + crypto_md5_final(md5, key); + + rc4 = crypto_rc4_init(key, key_len); + crypto_rc4(rc4, key_len, key, key); + crypto_rc4_free(rc4); + + if (key_len == 8) + memcpy(key, salt40, 3); /* TODO 56 bit */ + + return True; +} + +boolean security_encrypt(uint8* data, int length, rdpRdp* rdp) +{ + if (rdp->encrypt_use_count >= 4096) + { + security_key_update(rdp->settings->encrypt_key, rdp->settings->encrypt_update_key, rdp->settings->rc4_key_len); + crypto_rc4_free(rdp->rc4_encrypt_key); + rdp->rc4_encrypt_key = crypto_rc4_init(rdp->settings->encrypt_key, rdp->settings->rc4_key_len); + rdp->encrypt_use_count = 0; + } + crypto_rc4(rdp->rc4_encrypt_key, length, data, data); + rdp->encrypt_use_count += 1; + return True; +} + +boolean security_decrypt(uint8* data, int length, rdpRdp* rdp) +{ + if (rdp->decrypt_use_count >= 4096) + { + security_key_update(rdp->settings->decrypt_key, rdp->settings->decrypt_update_key, rdp->settings->rc4_key_len); + crypto_rc4_free(rdp->rc4_decrypt_key); + rdp->rc4_decrypt_key = crypto_rc4_init(rdp->settings->decrypt_key, rdp->settings->rc4_key_len); + rdp->decrypt_use_count = 0; + } + crypto_rc4(rdp->rc4_decrypt_key, length, data, data); + rdp->decrypt_use_count += 1; + return True; +} diff --git a/libfreerdp-core/security.h b/libfreerdp-core/security.h index 82add4eb1..58f9498b1 100644 --- a/libfreerdp-core/security.h +++ b/libfreerdp-core/security.h @@ -33,5 +33,9 @@ void security_licensing_encryption_key(uint8* session_key_blob, uint8* client_ra void security_mac_data(uint8* mac_salt_key, uint8* data, uint32 length, uint8* output); void security_mac_signature(uint8* mac_key, int mac_key_length, uint8* data, uint32 length, uint8* output); +boolean security_establish_keys(uint8* client_random, rdpSettings* settings); + +boolean security_encrypt(uint8* data, int length, rdpRdp* rdp); +boolean security_decrypt(uint8* data, int length, rdpRdp* rdp); #endif /* __SECURITY_H */ diff --git a/libfreerdp-core/transport.c b/libfreerdp-core/transport.c index bbb62b30a..29b629864 100644 --- a/libfreerdp-core/transport.c +++ b/libfreerdp-core/transport.c @@ -226,6 +226,8 @@ int transport_write(rdpTransport* transport, STREAM* s) length = stream_get_length(s); stream_set_pos(s, 0); + printf("transport_write:\n"); + #ifdef WITH_DEBUG_TRANSPORT if (length > 0) {