From afc9ac89ae12c8469db0cf9ef8f7d28229054794 Mon Sep 17 00:00:00 2001 From: Jay Sorg Date: Mon, 5 Sep 2011 11:02:52 -0700 Subject: [PATCH] work on Proprietary Certificate --- libfreerdp-core/certificate.c | 88 ++++++++++++++++++++++++++++++++--- libfreerdp-core/certificate.h | 12 +++-- libfreerdp-core/connection.c | 31 +++++++++++- libfreerdp-core/gcc.c | 63 +++++++++++++++++++++---- libfreerdp-core/license.c | 3 +- libfreerdp-core/mcs.c | 3 ++ libfreerdp-core/rdp.c | 34 ++++++++++++++ libfreerdp-core/settings.c | 2 + libfreerdp-core/tcp.c | 8 ++++ libfreerdp-utils/args.c | 3 ++ 10 files changed, 226 insertions(+), 21 deletions(-) diff --git a/libfreerdp-core/certificate.c b/libfreerdp-core/certificate.c index 9f8e62906..2153cab98 100644 --- a/libfreerdp-core/certificate.c +++ b/libfreerdp-core/certificate.c @@ -227,15 +227,88 @@ 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) { + 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 +317,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 +353,7 @@ void certificate_read_server_x509_certificate_chain(rdpCertificate* certificate, DEBUG_CERTIFICATE("modulus length:%d", certificate->cert_info.modulus.length); } } + return True; } /** @@ -289,7 +363,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 +374,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 +393,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 +404,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 +412,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 620da4ec0..2155d9eeb 100644 --- a/libfreerdp-core/connection.c +++ b/libfreerdp-core/connection.c @@ -104,11 +104,38 @@ boolean rdp_client_connect(rdpRdp* rdp) return True; } +static boolean rdp_establish_keys(rdpRdp* rdp) +{ + uint8 client_random[32]; + uint8 crypt_client_random[256]; + uint32 key_len; + uint8* mod; + uint8* exp; + + printf("rdp_establish_keys:\n"); + if (rdp->settings->encryption == False) + { + /* no RDP encryption */ + return True; + } + + memset(client_random, 0x5e, 32); /* TODO: get real random */ + crypto_nonce(client_random, 32); + 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_encrypt(client_random, 32, key_len, mod, exp, crypt_client_random); + + 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)) @@ -188,6 +215,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/gcc.c b/libfreerdp-core/gcc.c index 289afbaa1..86f7b37f5 100644 --- a/libfreerdp-core/gcc.c +++ b/libfreerdp-core/gcc.c @@ -18,6 +18,7 @@ */ #include "gcc.h" +#include "certificate.h" /** * T.124 GCC is defined in: @@ -250,12 +251,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; } @@ -357,34 +362,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; @@ -729,7 +749,11 @@ void gcc_write_client_security_data(STREAM* s, rdpSettings *settings) { gcc_write_user_data_header(s, CS_SECURITY, 12); - if (settings->encryption > 0) + printf("settings->encryption %d\n", settings->encryption); + printf("settings->encryption_method %d\n", settings->encryption_method); + printf("settings->encryption_level %d\n", settings->encryption_level); + + if (settings->encryption) { stream_write_uint32(s, settings->encryption_method); /* encryptionMethods */ stream_write_uint32(s, 0); /* extEncryptionMethods */ @@ -744,23 +768,30 @@ 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) + printf("gcc_read_server_security_data: encryption_method %d encryption_level %d\n", settings->encryption_method, settings->encryption_level); + + 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; } stream_read_uint32(s, serverRandomLen); /* serverRandomLen */ stream_read_uint32(s, serverCertLen); /* serverCertLen */ + printf("gcc_read_server_security_data: serverRandomLen %d serverCertLen %d\n", serverRandomLen, serverCertLen); + if (serverRandomLen > 0) { /* serverRandom */ @@ -768,6 +799,10 @@ boolean gcc_read_server_security_data(STREAM* s, rdpSettings *settings) memcpy(settings->server_random.data, s->p, serverRandomLen); stream_seek(s, serverRandomLen); } + else + { + return False; + } if (serverCertLen > 0) { @@ -775,6 +810,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/license.c b/libfreerdp-core/license.c index 39d2fc097..9047f39da 100644 --- a/libfreerdp-core/license.c +++ b/libfreerdp-core/license.c @@ -864,7 +864,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 f884ba633..b55c21988 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/rdp.c b/libfreerdp-core/rdp.c index bfddc9cad..ab87b2031 100644 --- a/libfreerdp-core/rdp.c +++ b/libfreerdp-core/rdp.c @@ -397,6 +397,20 @@ void rdp_recv_data_pdu(rdpRdp* rdp, STREAM* s) } } +/** + * Decrypt an RDP packet.\n + * @param rdp RDP module + * @param s stream + * @param length int + */ + +static boolean rdp_decrypt(rdpRdp* rdp, STREAM* s, int length) +{ + printf("rdp_decrypt:\n"); + stream_seek(s, 8); /* signature */ + return True; +} + /** * Process an RDP packet.\n * @param rdp RDP module @@ -409,6 +423,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)) { @@ -416,6 +431,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)) + { + printf("rdp_decrypt failed\n"); + return False; + } + } + } + if (channelId != MCS_GLOBAL_CHANNEL_ID) { vchan_process(rdp->vchan, s, channelId); @@ -492,6 +525,7 @@ static int rdp_recv_callback(rdpTransport* transport, STREAM* s, void* extra) { rdpRdp* rdp = (rdpRdp*) extra; + printf("state %d\n", rdp->state); switch (rdp->state) { case CONNECTION_STATE_NEGO: diff --git a/libfreerdp-core/settings.c b/libfreerdp-core/settings.c index ca900b014..fc553ca2b 100644 --- a/libfreerdp-core/settings.c +++ b/libfreerdp-core/settings.c @@ -167,6 +167,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 88c849358..f32867c52 100644 --- a/libfreerdp-core/tcp.c +++ b/libfreerdp-core/tcp.c @@ -165,6 +165,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; } @@ -175,6 +180,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 32d392c0f..47fc6d26e 100644 --- a/libfreerdp-utils/args.c +++ b/libfreerdp-utils/args.c @@ -283,6 +283,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 */ {