diff --git a/CMakeLists.txt b/CMakeLists.txt index 668fc3b0d..215139a26 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,7 +58,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/client/X11/CMakeLists.txt b/client/X11/CMakeLists.txt index 6e5e99fbd..7fb7bb0a8 100644 --- a/client/X11/CMakeLists.txt +++ b/client/X11/CMakeLists.txt @@ -78,7 +78,7 @@ target_link_libraries(xfreerdp freerdp-kbd) target_link_libraries(xfreerdp freerdp-rail) target_link_libraries(xfreerdp freerdp-chanman) target_link_libraries(xfreerdp freerdp-utils) -target_link_libraries(xfreerdp ${X11_LIBRARIES}) +target_link_libraries(xfreerdp ${X11_LIBRARIES} dl) install(TARGETS xfreerdp DESTINATION bin) diff --git a/client/test/CMakeLists.txt b/client/test/CMakeLists.txt index 09235b4e1..58d0783ff 100644 --- a/client/test/CMakeLists.txt +++ b/client/test/CMakeLists.txt @@ -23,4 +23,4 @@ add_executable(freerdp-test target_link_libraries(freerdp-test freerdp-core) target_link_libraries(freerdp-test freerdp-gdi) target_link_libraries(freerdp-test freerdp-utils) -target_link_libraries(freerdp-test freerdp-chanman) +target_link_libraries(freerdp-test freerdp-chanman dl) diff --git a/include/freerdp/settings.h b/include/freerdp/settings.h index 978f9b98c..a980e6743 100644 --- a/include/freerdp/settings.h +++ b/include/freerdp/settings.h @@ -205,6 +205,14 @@ struct rdp_settings rdpBlob server_random; 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; diff --git a/libfreerdp-core/capabilities.c b/libfreerdp-core/capabilities.c index 57dc91e3b..468e4486c 100644 --- a/libfreerdp-core/capabilities.c +++ b/libfreerdp-core/capabilities.c @@ -1522,13 +1522,17 @@ boolean rdp_read_capability_sets(STREAM* s, rdpSettings* settings, uint16 number { stream_get_mark(s, bm); + freerdp_hexdump(s->p, 4); rdp_read_capability_set_header(s, &length, &type); //printf("%s Capability Set (0x%02X), length:%d\n", CAPSET_TYPE_STRINGS[type], type, length); settings->received_caps[type] = True; em = bm + length; if (stream_get_left(s) < length - 4) + { + printf("stream problem\n"); return False; + } switch (type) { @@ -1669,18 +1673,46 @@ boolean rdp_recv_demand_active(rdpRdp* rdp, STREAM* s) uint16 numberCapabilities; uint16 lengthSourceDescriptor; uint16 lengthCombinedCapabilities; + uint32 securityHeader; if (!rdp_read_header(rdp, s, &length, &channelId)) return False; + if (rdp->settings->encryption) + { + stream_read_uint32(s, securityHeader); + if (securityHeader & SEC_SECURE_CHECKSUM) + { + printf("Error: TODO\n"); + return False; + } + if (securityHeader & SEC_ENCRYPT) + { + if (!rdp_decrypt(rdp, s, length - 4)) + { + printf("rdp_decrypt failed\n"); + return False; + } + } + } + if (channelId != MCS_GLOBAL_CHANNEL_ID) + { + printf("channelId bad\n"); return False; + } if (!rdp_read_share_control_header(s, &pduLength, &pduType, &rdp->settings->pdu_source)) + { + printf("rdp_read_share_control_header failed\n"); return False; + } if (pduType != PDU_TYPE_DEMAND_ACTIVE) + { + printf("pduType bad\n"); return False; + } stream_read_uint32(s, rdp->settings->share_id); /* shareId (4 bytes) */ stream_read_uint16(s, lengthSourceDescriptor); /* lengthSourceDescriptor (2 bytes) */ @@ -1691,7 +1723,10 @@ boolean rdp_recv_demand_active(rdpRdp* rdp, STREAM* s) /* capabilitySets */ if (!rdp_read_capability_sets(s, rdp->settings, numberCapabilities)) + { + printf("rdp_read_capability_sets failes\n"); return False; + } rdp->update->glyph_v2 = (rdp->settings->glyphSupportLevel > GLYPH_SUPPORT_FULL) ? True : False; diff --git a/libfreerdp-core/certificate.c b/libfreerdp-core/certificate.c index c3b4996e2..49fcdf789 100644 --- a/libfreerdp-core/certificate.c +++ b/libfreerdp-core/certificate.c @@ -227,15 +227,92 @@ void certificate_free_x509_certificate_chain(rdpX509CertChain* x509_cert_chain) xfree(x509_cert_chain); } +static boolean certificate_process_server_public_key(rdpCertificate* certificate, STREAM* s, uint32 length) +{ + uint8 magic[4]; + uint32 keylen; + uint32 bitlen; + uint32 datalen; + uint32 modlen; + + memcpy(magic, s->p, 4); + + if (memcmp(magic, "RSA1", 4) != 0) + { + printf("gcc_process_server_public_key: magic error\n"); + return False; + } + + stream_seek(s, 4); + stream_read_uint32(s, keylen); + stream_read_uint32(s, bitlen); + stream_read_uint32(s, datalen); + memcpy(certificate->cert_info.exponent, s->p, 4); + stream_seek(s, 4); + modlen = keylen - 8; + freerdp_blob_alloc(&(certificate->cert_info.modulus), modlen); + memcpy(certificate->cert_info.modulus.data, s->p, modlen); + stream_seek(s, keylen); + + return True; +} + +static boolean certificate_process_server_public_signature(rdpCertificate* certificate, STREAM* s, uint32 length) +{ + stream_seek(s, length); + return True; +} + /** * Read a Server Proprietary Certificate.\n * @param certificate certificate module * @param s stream */ -void certificate_read_server_proprietary_certificate(rdpCertificate* certificate, STREAM* s) +boolean certificate_read_server_proprietary_certificate(rdpCertificate* certificate, STREAM* s) { - DEBUG_CERTIFICATE("Server Proprietary Certificate"); + uint32 dwSigAlgId; + uint32 dwKeyAlgId; + uint32 wPublicKeyBlobType; + uint32 wPublicKeyBlobLen; + uint32 wSignatureBlobType; + uint32 wSignatureBlobLen; + + printf("Server Proprietary Certificate\n"); + + stream_read_uint32(s, dwSigAlgId); + stream_read_uint32(s, dwKeyAlgId); + if (!(dwSigAlgId == 1 && dwKeyAlgId == 1)) + { + printf("certificate_read_server_proprietary_certificate: parse error 1\n"); + return False; + } + stream_read_uint16(s, wPublicKeyBlobType); + if (wPublicKeyBlobType != BB_RSA_KEY_BLOB) + { + printf("certificate_read_server_proprietary_certificate: parse error 2\n"); + return False; + } + stream_read_uint16(s, wPublicKeyBlobLen); + if (!certificate_process_server_public_key(certificate, s, wPublicKeyBlobLen)) + { + printf("certificate_read_server_proprietary_certificate: parse error 3\n"); + return False; + } + stream_read_uint16(s, wSignatureBlobType); + if (wSignatureBlobType != BB_RSA_SIGNATURE_BLOB) + { + printf("certificate_read_server_proprietary_certificate: parse error 4\n"); + return False; + } + stream_read_uint16(s, wSignatureBlobLen); + if (!certificate_process_server_public_signature(certificate, s, wSignatureBlobLen)) + { + printf("certificate_read_server_proprietary_certificate: parse error 5\n"); + return False; + } + + return True; } /** @@ -244,7 +321,7 @@ void certificate_read_server_proprietary_certificate(rdpCertificate* certificate * @param s stream */ -void certificate_read_server_x509_certificate_chain(rdpCertificate* certificate, STREAM* s) +boolean certificate_read_server_x509_certificate_chain(rdpCertificate* certificate, STREAM* s) { int i; uint32 certLength; @@ -280,6 +357,8 @@ void certificate_read_server_x509_certificate_chain(rdpCertificate* certificate, DEBUG_CERTIFICATE("modulus length:%d", certificate->cert_info.modulus.length); } } + + return True; } /** @@ -289,7 +368,7 @@ void certificate_read_server_x509_certificate_chain(rdpCertificate* certificate, * @param length certificate length */ -void certificate_read_server_certificate(rdpCertificate* certificate, uint8* server_cert, int length) +boolean certificate_read_server_certificate(rdpCertificate* certificate, uint8* server_cert, int length) { STREAM* s; uint32 dwVersion; @@ -300,7 +379,7 @@ void certificate_read_server_certificate(rdpCertificate* certificate, uint8* ser if (length < 1) { printf("null server certificate\n"); - return; + return False; } stream_read_uint32(s, dwVersion); /* dwVersion (4 bytes) */ @@ -319,6 +398,9 @@ void certificate_read_server_certificate(rdpCertificate* certificate, uint8* ser printf("invalid certificate chain version:%d\n", dwVersion & CERT_CHAIN_VERSION_MASK); break; } + + xfree(s); + return True; } /** @@ -327,7 +409,7 @@ void certificate_read_server_certificate(rdpCertificate* certificate, uint8* ser * @return new certificate module */ -rdpCertificate* certificate_new(rdpRdp* rdp) +rdpCertificate* certificate_new(void) { rdpCertificate* certificate; @@ -335,7 +417,6 @@ rdpCertificate* certificate_new(rdpRdp* rdp) if (certificate != NULL) { - certificate->rdp = rdp; certificate->x509_cert_chain = NULL; } diff --git a/libfreerdp-core/certificate.h b/libfreerdp-core/certificate.h index dd60d9ede..e999bd8b8 100644 --- a/libfreerdp-core/certificate.h +++ b/libfreerdp-core/certificate.h @@ -37,6 +37,9 @@ typedef struct rdp_certificate rdpCertificate; #define CERT_PERMANENTLY_ISSUED 0x00000000 #define CERT_TEMPORARILY_ISSUED 0x80000000 +#define BB_RSA_KEY_BLOB 6 +#define BB_RSA_SIGNATURE_BLOB 8 + struct rdp_CertBlob { uint32 length; @@ -60,7 +63,6 @@ typedef struct rdp_CertInfo rdpCertInfo; struct rdp_certificate { - struct rdp_rdp* rdp; rdpCertInfo cert_info; rdpX509CertChain* x509_cert_chain; }; @@ -70,11 +72,11 @@ void certificate_read_x509_certificate(rdpCertBlob* cert, rdpCertInfo* info); rdpX509CertChain* certificate_new_x509_certificate_chain(uint32 count); void certificate_free_x509_certificate_chain(rdpX509CertChain* x509_cert_chain); -void certificate_read_server_proprietary_certificate(rdpCertificate* certificate, STREAM* s); -void certificate_read_server_x509_certificate_chain(rdpCertificate* certificate, STREAM* s); -void certificate_read_server_certificate(rdpCertificate* certificate, uint8* server_cert, int length); +boolean certificate_read_server_proprietary_certificate(rdpCertificate* certificate, STREAM* s); +boolean certificate_read_server_x509_certificate_chain(rdpCertificate* certificate, STREAM* s); +boolean certificate_read_server_certificate(rdpCertificate* certificate, uint8* server_cert, int length); -rdpCertificate* certificate_new(rdpRdp* rdp); +rdpCertificate* certificate_new(void); void certificate_free(rdpCertificate* certificate); #ifdef WITH_DEBUG_CERTIFICATE diff --git a/libfreerdp-core/connection.c b/libfreerdp-core/connection.c index 9c5b42674..ab6fa72b8 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 @@ -80,7 +81,7 @@ boolean rdp_client_connect(rdpRdp* rdp) ret = transport_connect_nla(rdp->transport); else if (rdp->nego->selected_protocol & PROTOCOL_TLS) ret = transport_connect_tls(rdp->transport); - else if (rdp->nego->selected_protocol & PROTOCOL_RDP) + else if (rdp->nego->selected_protocol == PROTOCOL_RDP) /* 0 */ ret = transport_connect_rdp(rdp->transport); if (!ret) @@ -145,11 +146,85 @@ boolean rdp_client_redirect(rdpRdp* rdp) return rdp_client_connect(rdp); } +static boolean rdp_establish_keys(rdpRdp* rdp) +{ + uint8 client_random[32]; + 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) + { + /* no RDP encryption */ + return True; + } + + /* 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); + + printf("key_len %d\n", rdp->settings->rc4_key_len); + printf("decrypt_key\n"); + freerdp_hexdump(rdp->settings->decrypt_key, rdp->settings->rc4_key_len); + printf("encrypt_key\n"); + freerdp_hexdump(rdp->settings->encrypt_key, rdp->settings->rc4_key_len); + + rdp->do_crypt = True; + + return True; +} + boolean rdp_client_connect_mcs_connect_response(rdpRdp* rdp, STREAM* s) { if (!mcs_recv_connect_response(rdp->mcs, s)) + { + printf("rdp_client_connect_mcs_connect_response: mcs_recv_connect_response failed\n"); return False; - + } if (!mcs_send_erect_domain_request(rdp->mcs)) return False; if (!mcs_send_attach_user_request(rdp->mcs)) @@ -229,6 +304,8 @@ boolean rdp_client_connect_mcs_channel_join_confirm(rdpRdp* rdp, STREAM* s) if (rdp->mcs->user_channel_joined && rdp->mcs->global_channel_joined && all_joined) { + if (!rdp_establish_keys(rdp)) + return False; if (!rdp_send_client_info(rdp)) return False; rdp->state = CONNECTION_STATE_LICENSE; diff --git a/libfreerdp-core/crypto.h b/libfreerdp-core/crypto.h index b63ff16c4..c09773303 100644 --- a/libfreerdp-core/crypto.h +++ b/libfreerdp-core/crypto.h @@ -40,7 +40,7 @@ #endif #define EXPONENT_MAX_SIZE 4 -#define MODULUS_MAX_SIZE 64 +#define MODULUS_MAX_SIZE 256 #include #include diff --git a/libfreerdp-core/fastpath.c b/libfreerdp-core/fastpath.c index 2335637a0..d656b94a8 100644 --- a/libfreerdp-core/fastpath.c +++ b/libfreerdp-core/fastpath.c @@ -55,6 +55,7 @@ uint16 fastpath_read_header(rdpFastPath* fastpath, STREAM* s) uint8 t; stream_read_uint8(s, header); + if (fastpath != NULL) { fastpath->encryptionFlags = (header & 0xC0) >> 6; @@ -94,6 +95,36 @@ INLINE void fastpath_write_update_header(STREAM* s, uint8 updateCode, uint8 frag stream_write_uint8(s, updateHeader); } +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) + { + fastpath->encryptionFlags = (header & 0xC0) >> 6; + 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; + } + + return length - hs; +} + boolean fastpath_read_security_header(rdpFastPath* fastpath, STREAM* s) { /* TODO: fipsInformation */ diff --git a/libfreerdp-core/fastpath.h b/libfreerdp-core/fastpath.h index 25899b3ba..0512ff771 100644 --- a/libfreerdp-core/fastpath.h +++ b/libfreerdp-core/fastpath.h @@ -91,6 +91,7 @@ struct rdp_fastpath }; uint16 fastpath_read_header(rdpFastPath* fastpath, STREAM* s); +uint16 fastpath_read_header_rdp(rdpFastPath* fastpath, STREAM* s); boolean fastpath_read_security_header(rdpFastPath* fastpath, STREAM* s); boolean fastpath_recv_updates(rdpFastPath* fastpath, STREAM* s); boolean fastpath_recv_inputs(rdpFastPath* fastpath, STREAM* s); diff --git a/libfreerdp-core/gcc.c b/libfreerdp-core/gcc.c index 301672f58..441ee8f69 100644 --- a/libfreerdp-core/gcc.c +++ b/libfreerdp-core/gcc.c @@ -20,6 +20,7 @@ #include #include "gcc.h" +#include "certificate.h" /** * T.124 GCC is defined in: @@ -252,12 +253,16 @@ boolean gcc_read_conference_create_response(STREAM* s, rdpSettings* settings) per_read_choice(s, &choice); /* h221NonStandard */ - per_read_octet_string(s, h221_sc_key, 4, 4); /* h221NonStandard, server-to-client H.221 key, "McDn" */ + if (!per_read_octet_string(s, h221_sc_key, 4, 4)) /* h221NonStandard, server-to-client H.221 key, "McDn" */ + return False; /* userData (OCTET_STRING) */ per_read_length(s, &length); if (!gcc_read_server_data_blocks(s, settings, length)) + { + printf("gcc_read_conference_create_response: gcc_read_server_data_blocks failed\n"); return False; + } return True; } @@ -359,34 +364,49 @@ boolean gcc_read_server_data_blocks(STREAM* s, rdpSettings *settings, int length uint16 type; uint16 offset = 0; uint16 blockLength; + uint8* holdp; while (offset < length) { + holdp = s->p; + if (!gcc_read_user_data_header(s, &type, &blockLength)) + { + printf("gcc_read_server_data_blocks: gcc_read_user_data_header failed\n"); return False; + } switch (type) { case SC_CORE: if (!gcc_read_server_core_data(s, settings)) + { + printf("gcc_read_server_data_blocks: gcc_read_server_core_data failed\n"); return False; + } break; case SC_SECURITY: if (!gcc_read_server_security_data(s, settings)) + { + printf("gcc_read_server_data_blocks: gcc_read_server_security_data failed\n"); return False; + } break; case SC_NET: if (!gcc_read_server_network_data(s, settings)) + { + printf("gcc_read_server_data_blocks: gcc_read_server_network_data failed\n"); return False; + } break; default: break; } - offset += blockLength; + s->p = holdp + blockLength; } return True; @@ -731,7 +751,7 @@ void gcc_write_client_security_data(STREAM* s, rdpSettings *settings) { gcc_write_user_data_header(s, CS_SECURITY, 12); - if (settings->encryption > 0) + if (settings->encryption) { stream_write_uint32(s, settings->encryption_method); /* encryptionMethods */ stream_write_uint32(s, 0); /* extEncryptionMethods */ @@ -746,17 +766,20 @@ void gcc_write_client_security_data(STREAM* s, rdpSettings *settings) boolean gcc_read_server_security_data(STREAM* s, rdpSettings *settings) { - uint32 encryptionMethod; - uint32 encryptionLevel; uint32 serverRandomLen; uint32 serverCertLen; + uint8* data; + uint32 len; - stream_read_uint32(s, encryptionMethod); /* encryptionMethod */ - stream_read_uint32(s, encryptionLevel); /* encryptionLevel */ + stream_read_uint32(s, settings->encryption_method); /* encryptionMethod */ + stream_read_uint32(s, settings->encryption_level); /* encryptionLevel */ - if (encryptionMethod == 0 && encryptionLevel == 0) + if (settings->encryption_method == 0 && settings->encryption_level == 0) { /* serverRandom and serverRandom must not be present */ + settings->encryption = False; + settings->encryption_method = ENCRYPTION_METHOD_NONE; + settings->encryption_level = ENCRYPTION_LEVEL_NONE; return True; } @@ -769,6 +792,11 @@ boolean gcc_read_server_security_data(STREAM* s, rdpSettings *settings) freerdp_blob_alloc(&settings->server_random, serverRandomLen); memcpy(settings->server_random.data, s->p, serverRandomLen); stream_seek(s, serverRandomLen); + freerdp_hexdump(settings->server_random.data, settings->server_random.length); + } + else + { + return False; } if (serverCertLen > 0) @@ -777,6 +805,18 @@ boolean gcc_read_server_security_data(STREAM* s, rdpSettings *settings) freerdp_blob_alloc(&settings->server_certificate, serverCertLen); memcpy(settings->server_certificate.data, s->p, serverCertLen); stream_seek(s, serverCertLen); + certificate_free(settings->server_cert); + settings->server_cert = certificate_new(); + data = settings->server_certificate.data; + len = settings->server_certificate.length; + if (!certificate_read_server_certificate(settings->server_cert, data, len)) + { + return False; + } + } + else + { + return False; } return True; diff --git a/libfreerdp-core/info.c b/libfreerdp-core/info.c index a0c2c76ac..9af478024 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 9a23ab3b3..56c4aa701 100644 --- a/libfreerdp-core/license.c +++ b/libfreerdp-core/license.c @@ -870,7 +870,8 @@ rdpLicense* license_new(rdpRdp* rdp) { license->rdp = rdp; license->state = LICENSE_STATE_AWAIT; - license->certificate = certificate_new(rdp); + //license->certificate = certificate_new(rdp); + license->certificate = certificate_new(); license->product_info = license_new_product_info(); license->error_info = license_new_binary_blob(BB_ERROR_BLOB); license->key_exchange_list = license_new_binary_blob(BB_KEY_EXCHG_ALG_BLOB); diff --git a/libfreerdp-core/mcs.c b/libfreerdp-core/mcs.c index 01c75e69a..151e01f6d 100644 --- a/libfreerdp-core/mcs.c +++ b/libfreerdp-core/mcs.c @@ -517,7 +517,10 @@ boolean mcs_recv_connect_response(rdpMcs* mcs, STREAM* s) ber_read_octet_string(s, &length); if (!gcc_read_conference_create_response(s, mcs->transport->settings)) + { + printf("mcs_recv_connect_response: gcc_read_conference_create_response failed\n"); return False; + } return True; } diff --git a/libfreerdp-core/peer.c b/libfreerdp-core/peer.c index 160664936..22917ec8c 100644 --- a/libfreerdp-core/peer.c +++ b/libfreerdp-core/peer.c @@ -157,15 +157,20 @@ static boolean peer_recv_fastpath_pdu(rdpPeer* peer, STREAM* s) { uint16 length; - length = fastpath_read_header(peer->rdp->fastpath, s); - if (length == 0 || length > stream_get_size(s)) + length = fastpath_read_header_rdp(peer->rdp->fastpath, s); + if (length == 0 || length > stream_get_left(s)) { printf("incorrect FastPath PDU header length %d\n", length); return False; } - if (!fastpath_read_security_header(peer->rdp->fastpath, s)) - return False; + if (peer->rdp->fastpath->encryptionFlags & FASTPATH_OUTPUT_ENCRYPTED) + { + rdp_decrypt(peer->rdp, s, length); + } + + //if (!fastpath_read_security_header(peer->rdp->fastpath, s)) + // return False; return fastpath_recv_inputs(peer->rdp->fastpath, s); } diff --git a/libfreerdp-core/rdp.c b/libfreerdp-core/rdp.c index b3387832f..e5a3a1b3a 100644 --- a/libfreerdp-core/rdp.c +++ b/libfreerdp-core/rdp.c @@ -156,6 +156,20 @@ 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) +{ + 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 @@ -165,8 +179,11 @@ 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; } @@ -175,6 +192,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; } @@ -184,6 +202,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; @@ -239,6 +258,45 @@ 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 ml; + uint8* mk; + uint8* data; + uint32 sec_flags; + + 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 @@ -249,12 +307,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; @@ -265,13 +333,23 @@ 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; 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; @@ -282,16 +360,26 @@ 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; 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; @@ -437,6 +525,26 @@ boolean rdp_recv_out_of_sequence_pdu(rdpRdp* rdp, STREAM* s) } } +/** + * Decrypt an RDP packet.\n + * @param rdp RDP module + * @param s stream + * @param length int + */ + +boolean rdp_decrypt(rdpRdp* rdp, STREAM* s, int length) +{ + int cryptlen; + + //printf("rdp_decrypt:\n"); + stream_seek(s, 8); /* signature */ + cryptlen = length - 8; + //printf("length %d cryptlen %d\n", length, cryptlen); + security_decrypt(s->p, cryptlen, rdp); + //freerdp_hexdump(s->p, cryptlen); + return True; +} + /** * Process an RDP packet.\n * @param rdp RDP module @@ -449,6 +557,7 @@ static boolean rdp_recv_tpkt_pdu(rdpRdp* rdp, STREAM* s) uint16 pduType; uint16 pduLength; uint16 channelId; + uint32 securityHeader; if (!rdp_read_header(rdp, s, &length, &channelId)) { @@ -456,6 +565,24 @@ static boolean rdp_recv_tpkt_pdu(rdpRdp* rdp, STREAM* s) return False; } + if (rdp->settings->encryption) + { + stream_read_uint32(s, securityHeader); + if (securityHeader & SEC_SECURE_CHECKSUM) + { + printf("Error: TODO\n"); + return False; + } + if (securityHeader & SEC_ENCRYPT) + { + if (!rdp_decrypt(rdp, s, length - 4)) + { + printf("rdp_decrypt failed\n"); + return False; + } + } + } + if (channelId != MCS_GLOBAL_CHANNEL_ID) { vchan_process(rdp->vchan, s, channelId); @@ -492,15 +619,18 @@ static boolean rdp_recv_fastpath_pdu(rdpRdp* rdp, STREAM* s) { uint16 length; - length = fastpath_read_header(rdp->fastpath, s); - if (length == 0 || length > stream_get_size(s)) + length = fastpath_read_header_rdp(rdp->fastpath, s); + + if (length == 0 || length > stream_get_left(s)) { printf("incorrect FastPath PDU header length %d\n", length); return False; } - if (!fastpath_read_security_header(rdp->fastpath, s)) - return False; + if (rdp->fastpath->encryptionFlags & FASTPATH_OUTPUT_ENCRYPTED) + { + rdp_decrypt(rdp, s, length); + } return fastpath_recv_updates(rdp->fastpath, s); } @@ -556,7 +686,10 @@ static int rdp_recv_callback(rdpTransport* transport, STREAM* s, void* extra) case CONNECTION_STATE_CAPABILITY: if (!rdp_client_connect_demand_active(rdp, s)) + { + printf("rdp_client_connect_demand_active failed\n"); return -1; + } break; case CONNECTION_STATE_ACTIVE: diff --git a/libfreerdp-core/rdp.h b/libfreerdp-core/rdp.h index fe269350c..32f5a408a 100644 --- a/libfreerdp-core/rdp.h +++ b/libfreerdp-core/rdp.h @@ -128,6 +128,12 @@ struct rdp_rdp struct rdp_transport* transport; struct rdp_vchan* vchan; struct rdp_mppc* mppc; + 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); @@ -171,4 +177,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); + #endif /* __RDP_H */ diff --git a/libfreerdp-core/security.c b/libfreerdp-core/security.c index 81c494630..803067993 100644 --- a/libfreerdp-core/security.c +++ b/libfreerdp-core/security.c @@ -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,119 @@ void security_mac_signature(uint8* mac_key, int mac_key_length, uint8* data, uin memcpy(output, md5_digest, 8); } + +static void security_A(uint8* master_secret, uint8* client_random, uint8* server_random, uint8* output) +{ + security_premaster_hash("A", 1, master_secret, client_random, server_random, &output[0]); + security_premaster_hash("BB", 2, master_secret, client_random, server_random, &output[16]); + security_premaster_hash("CCC", 3, master_secret, client_random, server_random, &output[32]); +} + +static void security_X(uint8* master_secret, uint8* client_random, uint8* server_random, uint8* output) +{ + security_premaster_hash("X", 1, master_secret, client_random, server_random, &output[0]); + security_premaster_hash("YY", 2, master_secret, client_random, server_random, &output[16]); + security_premaster_hash("ZZZ", 3, master_secret, client_random, server_random, &output[32]); +} + +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; + + freerdp_hexdump(client_random, 32); + freerdp_hexdump(server_random, 32); + + memcpy(pre_master_secret, client_random, 24); + memcpy(pre_master_secret + 24, server_random, 24); + + security_A(pre_master_secret, client_random, server_random, master_secret); + security_X(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/settings.c b/libfreerdp-core/settings.c index 4d5debe7d..a64bfca0f 100644 --- a/libfreerdp-core/settings.c +++ b/libfreerdp-core/settings.c @@ -169,6 +169,8 @@ void settings_free(rdpSettings* settings) xfree(settings->client_dir); xfree(settings->cert_file); xfree(settings->privatekey_file); + freerdp_blob_free(&(settings->server_certificate)); + certificate_free(settings->server_cert); xfree(settings); } } diff --git a/libfreerdp-core/tcp.c b/libfreerdp-core/tcp.c index faeb85d13..542206b5d 100644 --- a/libfreerdp-core/tcp.c +++ b/libfreerdp-core/tcp.c @@ -166,6 +166,11 @@ int tcp_read(rdpTcp* tcp, uint8* data, int length) perror("recv"); return -1; } + else + { + //printf("tcp_read: length %d\n", status); + //freerdp_hexdump(data, status); + } return status; } @@ -176,6 +181,9 @@ int tcp_write(rdpTcp* tcp, uint8* data, int length) status = send(tcp->sockfd, data, length, MSG_NOSIGNAL); + //printf("tcp_write: length %d\n", status); + //freerdp_hexdump(data, status); + if (status < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) diff --git a/libfreerdp-utils/args.c b/libfreerdp-utils/args.c index c374110d6..9590b28f4 100644 --- a/libfreerdp-utils/args.c +++ b/libfreerdp-utils/args.c @@ -367,6 +367,9 @@ int freerdp_parse_args(rdpSettings* settings, int argc, char** argv, settings->rdp_security = True; settings->tls_security = False; settings->nla_security = False; + settings->encryption = True; + settings->encryption_method = ENCRYPTION_METHOD_40BIT | ENCRYPTION_METHOD_128BIT; + settings->encryption_level = ENCRYPTION_LEVEL_HIGH; } else if (strncmp("tls", argv[index], 1) == 0) /* TLS */ {