diff options
author | Marc-André Moreau <marcandre.moreau@gmail.com> | 2011-06-29 14:55:25 +0400 |
---|---|---|
committer | Marc-André Moreau <marcandre.moreau@gmail.com> | 2011-06-29 14:55:25 +0400 |
commit | 1a61308b2d1da843f8f3d7120f10c8891becff65 (patch) | |
tree | 1214d12614c04ee9151012ad655db0cebbd52dc4 | |
parent | 46c1d75a72521b548e8f0f677b86f3aab01027e8 (diff) | |
parent | cecf7eb4f9e8aa2ba21dc22c09ec2d3279180f4d (diff) |
Merge pull request #55 from awakecoding/master
core network refactoring
38 files changed, 1908 insertions, 1575 deletions
diff --git a/channels/drdynvc/drdynvc_main.c b/channels/drdynvc/drdynvc_main.c index 90ac6ac..1283b7d 100644 --- a/channels/drdynvc/drdynvc_main.c +++ b/channels/drdynvc/drdynvc_main.c @@ -28,6 +28,7 @@ #include <freerdp/utils/stream.h> #include <freerdp/utils/chan_plugin.h> #include <freerdp/utils/wait_obj.h> +#include <freerdp/utils/hexdump.h> #include "drdynvc_main.h" @@ -72,43 +73,9 @@ struct drdynvc_plugin }; #if LOG_LEVEL > 10 -void -hexdump(char* p, int len) -{ - unsigned char* line; - int i; - int thisline; - int offset; - - line = (unsigned char*)p; - offset = 0; - while (offset < len) - { - printf("%04x ", offset); - thisline = len - offset; - if (thisline > 16) - { - thisline = 16; - } - for (i = 0; i < thisline; i++) - { - printf("%02x ", line[i]); - } - for (; i < 16; i++) - { - printf(" "); - } - for (i = 0; i < thisline; i++) - { - printf("%c", (line[i] >= 0x20 && line[i] < 0x7f) ? line[i] : '.'); - } - printf("\n"); - offset += thisline; - line += thisline; - } -} +#define hexdump(data,length) freerdp_hexdump(data,length) #else -#define hexdump(p,len) +#define hexdump(data,length) do { } while (0) #endif static int diff --git a/include/freerdp/types/base.h b/include/freerdp/types/base.h index de200ca..327c5b3 100644 --- a/include/freerdp/types/base.h +++ b/include/freerdp/types/base.h @@ -23,6 +23,11 @@ #ifndef __TYPES_BASE_H #define __TYPES_BASE_H +#ifndef True +#define True (1) +#define False (0) +#endif + typedef unsigned char uint8; typedef signed char sint8; typedef unsigned short uint16; diff --git a/include/freerdp/types/ui.h b/include/freerdp/types/ui.h index 067363d..f96a32c 100644 --- a/include/freerdp/types/ui.h +++ b/include/freerdp/types/ui.h @@ -31,11 +31,6 @@ typedef void *RD_HGLYPH; typedef void *RD_HPALETTE; typedef void *RD_HCURSOR; -#ifndef True -#define True (1) -#define False (0) -#endif - typedef struct _RD_POINT { sint16 x, y; diff --git a/include/freerdp/utils/Makefile.am b/include/freerdp/utils/Makefile.am index 410babf..4788c93 100644 --- a/include/freerdp/utils/Makefile.am +++ b/include/freerdp/utils/Makefile.am @@ -11,4 +11,5 @@ include_HEADERS = \ stopwatch.h \ stream.h \ unicode.h \ - wait_obj.h + wait_obj.h \ + hexdump.h diff --git a/include/freerdp/utils/hexdump.h b/include/freerdp/utils/hexdump.h new file mode 100644 index 0000000..d57ba8d --- /dev/null +++ b/include/freerdp/utils/hexdump.h @@ -0,0 +1,27 @@ +/* + FreeRDP: A Remote Desktop Protocol client. + Hex Dump Utils + + Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com> + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef __UTILS_HEXDUMP_H +#define __UTILS_HEXDUMP_H + +#define FREERDP_HEXDUMP_LINE_LENGTH 16 + +void freerdp_hexdump(uint8* data, int length); + +#endif /* __UTILS_HEXDUMP_H */ diff --git a/libfreerdp-core/Makefile.am b/libfreerdp-core/Makefile.am index c443f53..140b39a 100644 --- a/libfreerdp-core/Makefile.am +++ b/libfreerdp-core/Makefile.am @@ -10,6 +10,7 @@ libfreerdp_core_la_SOURCES = \ bitmap.c bitmap.h \ cache.c cache.h \ capabilities.c capabilities.h \ + connect.c connect.h \ chan.c chan.h \ ext.c ext.h \ freerdp.c \ @@ -22,7 +23,8 @@ libfreerdp_core_la_SOURCES = \ pstcache.c pstcache.h \ rail.c rail.h \ rdp.c rdp.h \ - secure.c secure.h \ + security.c security.h \ + network.c network.h \ crypto.h \ tcp.c tcp.h \ nego.c nego.h \ diff --git a/libfreerdp-core/asn1.c b/libfreerdp-core/asn1.c index 58a953c..11fc19b 100644 --- a/libfreerdp-core/asn1.c +++ b/libfreerdp-core/asn1.c @@ -21,9 +21,9 @@ #include "iso.h" #include "mcs.h" #include "chan.h" -#include "secure.h" #include "rdp.h" #include "asn1.h" +#include "security.h" /* Parse an ASN.1 BER header */ RD_BOOL @@ -42,7 +42,7 @@ ber_parse_header(rdpMcs * mcs, STREAM s, int tagval, int *length) if (tag != tagval) { - ui_error(mcs->sec->rdp->inst, "expected tag %d, got %d\n", tagval, tag); + ui_error(mcs->net->rdp->inst, "expected tag %d, got %d\n", tagval, tag); return False; } diff --git a/libfreerdp-core/chan.c b/libfreerdp-core/chan.c index a0bbec4..267cfe7 100644 --- a/libfreerdp-core/chan.c +++ b/libfreerdp-core/chan.c @@ -20,8 +20,8 @@ #include "frdp.h" #include "chan.h" #include "mcs.h" -#include "secure.h" #include "rdp.h" +#include "security.h" #include <freerdp/rdpset.h> #include <freerdp/utils/memory.h> #include <freerdp/constants/vchan.h> @@ -38,11 +38,11 @@ vchan_send(rdpChannels * chan, int mcs_id, char * data, int total_length) rdpSet * settings; struct rdp_chan * channel; - settings = chan->mcs->sec->rdp->settings; + settings = chan->mcs->net->rdp->settings; chan_index = (mcs_id - MCS_GLOBAL_CHANNEL) - 1; if ((chan_index < 0) || (chan_index >= settings->num_channels)) { - ui_error(chan->mcs->sec->rdp->inst, "error\n"); + ui_error(chan->mcs->net->rdp->inst, "error\n"); return 0; } channel = &(settings->channels[chan_index]); @@ -61,12 +61,12 @@ vchan_send(rdpChannels * chan, int mcs_id, char * data, int total_length) { chan_flags |= CHANNEL_FLAG_SHOW_PROTOCOL; } - s = sec_init(chan->mcs->sec, sec_flags, length + 8); + s = sec_init(chan->mcs->net->sec, sec_flags, length + 8); out_uint32_le(s, total_length); out_uint32_le(s, chan_flags); out_uint8p(s, data + sent, length); s_mark_end(s); - sec_send_to_channel(chan->mcs->sec, s, sec_flags, mcs_id); + sec_send_to_channel(chan->mcs->net->sec, s, sec_flags, mcs_id); sent += length; chan_flags = 0; } @@ -86,7 +86,7 @@ vchan_process(rdpChannels * chan, STREAM s, int mcs_id) length = (int) (s->end - s->p); data = (char *) (s->p); s->p += length; - ui_channel_data(chan->mcs->sec->rdp->inst, mcs_id, data, length, flags, total_length); + ui_channel_data(chan->mcs->net->sec->rdp->inst, mcs_id, data, length, flags, total_length); } rdpChannels * diff --git a/libfreerdp-core/chan.h b/libfreerdp-core/chan.h index 867b7b3..e2391e2 100644 --- a/libfreerdp-core/chan.h +++ b/libfreerdp-core/chan.h @@ -21,6 +21,7 @@ #define __CHAN_H #include "mcs.h" +#include "network.h" struct rdp_channels { diff --git a/libfreerdp-core/connect.c b/libfreerdp-core/connect.c new file mode 100644 index 0000000..b84169a --- /dev/null +++ b/libfreerdp-core/connect.c @@ -0,0 +1,438 @@ +/* + FreeRDP: A Remote Desktop Protocol client. + Connection Sequence + + Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com> + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#include "rdp.h" +#include "nego.h" +#include "security.h" +#include <freerdp/rdpset.h> +#include <freerdp/freerdp.h> +#include <freerdp/utils/memory.h> +#include <freerdp/utils/unicode.h> + +#include "connect.h" + +static void +connect_output_client_core_data(rdpSec * sec, rdpSet * settings, STREAM s) +{ + char * p; + size_t len; + int con_type; + uint16 highColorDepth; + uint16 supportedColorDepths; + uint16 earlyCapabilityFlags; + + out_uint16_le(s, UDH_CS_CORE); /* User Data Header type */ + out_uint16_le(s, 216); /* total length */ + + out_uint32_le(s, settings->rdp_version >= 5 ? 0x00080004 : 0x00080001); /* client version */ + out_uint16_le(s, settings->width); /* desktopWidth */ + out_uint16_le(s, settings->height); /* desktopHeight */ + out_uint16_le(s, RNS_UD_COLOR_8BPP); /* colorDepth, ignored because of postBeta2ColorDepth */ + out_uint16_le(s, RNS_UD_SAS_DEL); /* SASSequence (Secure Access Sequence) */ + out_uint32_le(s, settings->keyboard_layout); /* keyboardLayout */ + out_uint32_le(s, 2600); /* clientBuild */ + + /* Unicode name of client, truncated to 15 characters */ + p = freerdp_uniconv_out(sec->rdp->uniconv, settings->hostname, &len); + if (len > 30) + { + len = 30; + p[len] = 0; + p[len + 1] = 0; + } + out_uint8a(s, p, len + 2); + out_uint8s(s, 32 - len - 2); + xfree(p); + + out_uint32_le(s, settings->keyboard_type); /* keyboardType */ + out_uint32_le(s, settings->keyboard_subtype); /* keyboardSubType */ + out_uint32_le(s, settings->keyboard_functionkeys); /* keyboardFunctionKey */ + + /* Input Method Editor (IME) file name associated with the input locale. + Up to 31 Unicode characters plus a NULL terminator */ + /* FIXME: populate this field with real data */ + out_uint8s(s, 64); /* imeFileName */ + + out_uint16_le(s, RNS_UD_COLOR_8BPP); /* postBeta2ColorDepth */ + out_uint16_le(s, 1); /* clientProductID */ + out_uint32_le(s, 0); /* serialNumber (should be initialized to 0) */ + + highColorDepth = MIN(settings->server_depth, 24); /* 32 must be reported as 24 and RNS_UD_CS_WANT_32BPP_SESSION */ + out_uint16_le(s, highColorDepth); /* (requested) highColorDepth */ + + supportedColorDepths = RNS_UD_32BPP_SUPPORT | RNS_UD_24BPP_SUPPORT | RNS_UD_16BPP_SUPPORT | RNS_UD_15BPP_SUPPORT; + out_uint16_le(s, supportedColorDepths); /* supportedColorDepths */ + + con_type = 0; + earlyCapabilityFlags = RNS_UD_CS_SUPPORT_ERRINFO_PDU; + if (sec->rdp->settings->performanceflags == PERF_FLAG_NONE) + { + earlyCapabilityFlags |= RNS_UD_CS_VALID_CONNECTION_TYPE; + con_type = CONNECTION_TYPE_LAN; + } + if (settings->server_depth == 32) + earlyCapabilityFlags |= RNS_UD_CS_WANT_32BPP_SESSION; + + out_uint16_le(s, earlyCapabilityFlags); /* earlyCapabilityFlags */ + out_uint8s(s, 64); /* clientDigProductId (64 bytes) */ + /* connectionType, only valid when RNS_UD_CS_VALID_CONNECTION_TYPE + is set in earlyCapabilityFlags */ + out_uint8(s, con_type); + out_uint8(s, 0); /* pad1octet */ + out_uint32_le(s, sec->net->nego->selected_protocol); /* serverSelectedProtocol */ +} + +static void +connect_output_client_security_data(rdpSec * sec, rdpSet * settings, STREAM s) +{ + uint16 encryptionMethods = 0; + + out_uint16_le(s, UDH_CS_SECURITY); /* User Data Header type */ + out_uint16_le(s, 12); /* total length */ + + if (settings->encryption || sec->net->tls_connected) + encryptionMethods = ENCRYPTION_40BIT_FLAG | ENCRYPTION_128BIT_FLAG; + + out_uint32_le(s, encryptionMethods); /* encryptionMethods */ + out_uint32_le(s, 0); /* extEncryptionMethods */ +} + +static void +connect_output_client_network_data(rdpSec * sec, rdpSet * settings, STREAM s) +{ + int i; + + DEBUG_SEC("num_channels is %d", settings->num_channels); + if (settings->num_channels > 0) + { + out_uint16_le(s, UDH_CS_NET); /* User Data Header type */ + out_uint16_le(s, settings->num_channels * 12 + 8); /* total length */ + + out_uint32_le(s, settings->num_channels); /* channelCount */ + for (i = 0; i < settings->num_channels; i++) + { + DEBUG_SEC("Requesting channel %s", settings->channels[i].name); + out_uint8a(s, settings->channels[i].name, 8); /* name (8 bytes) 7 characters with null terminator */ + out_uint32_le(s, settings->channels[i].flags); /* options (4 bytes) */ + } + } +} + +static void +connect_output_client_cluster_data(rdpSec * sec, rdpSet * settings, STREAM s) +{ + out_uint16_le(s, UDH_CS_CLUSTER); /* User Data Header type */ + out_uint16_le(s, 12); /* total length */ + + out_uint32_le(s, (settings->console_session || sec->rdp->redirect_session_id) ? + REDIRECTED_SESSIONID_FIELD_VALID | REDIRECTION_SUPPORTED | REDIRECTION_VERSION4 : + REDIRECTION_SUPPORTED | REDIRECTION_VERSION4); /* flags */ + + out_uint32_le(s, sec->rdp->redirect_session_id); /* RedirectedSessionID */ +} + +static void +connect_output_client_monitor_data(rdpSec * sec, rdpSet * settings, STREAM s) +{ + int length, n; + DEBUG_SEC("Setting monitor data... num_monitors: %d", settings->num_monitors); + if (settings->num_monitors <= 1) + return; + + DEBUG_SEC("Setting monitor data..."); + out_uint16_le(s, UDH_CS_MONITOR); /* User Data Header type */ + + length = 12 + (20 * settings->num_monitors); + out_uint16_le(s, length); + out_uint32_le(s, 0); /* flags (unused) */ + out_uint32_le(s, settings->num_monitors); /* monitorCount */ + for (n = 0; n < settings->num_monitors; n++) + { + out_uint32_le(s, settings->monitors[n].x); /* left */ + out_uint32_le(s, settings->monitors[n].y); /* top */ + out_uint32_le(s, settings->monitors[n].x + + settings->monitors[n].width-1); /* right */ + out_uint32_le(s, settings->monitors[n].y + + settings->monitors[n].height-1); /* bottom */ + out_uint32_le(s, settings->monitors[n].is_primary ? 1 : 0); /* isPrimary */ + } +} + +void +connect_output_gcc_conference_create_request(rdpSec * sec, STREAM s) +{ + int length; + rdpSet * settings = sec->rdp->settings; + + /* See ITU-T Rec. T.124 (02/98) Generic Conference Control */ + + /* the part before userData is of a fixed size, making things convenient */ + s->p = s->data + 23; + connect_output_client_core_data(sec, settings, s); + connect_output_client_cluster_data(sec, settings, s); + connect_output_client_security_data(sec, settings, s); + connect_output_client_network_data(sec, settings, s); + connect_output_client_monitor_data(sec, settings, s); + length = (s->p - s->data) - 23; + s->p = s->data; + + /* t124Identifier = 0.0.20.124.0.1 */ + out_uint16_be(s, 5); + out_uint16_be(s, 0x14); + out_uint8(s, 0x7c); + out_uint16_be(s, 1); + + /* connectPDU octet string */ + out_uint16_be(s, ((length + 14) | 0x8000)); /* connectPDU length in two bytes*/ + + /* connectPDU content is ConnectGCCPDU PER encoded: */ + out_uint16_be(s, 8); /* ConferenceCreateRequest ... */ + out_uint16_be(s, 16); + out_uint8(s, 0); + out_uint16_le(s, 0xC001); /* userData key is h221NonStandard */ + out_uint8(s, 0); /* 4 bytes: */ + out_uint32_le(s, 0x61637544); /* "Duca" */ + out_uint16_be(s, (length | 0x8000)); /* userData value length in two bytes */ + s->p = s->data + length + 23; /* userData (outputted earlier) */ + + s_mark_end(s); +} + +/* Parse Server Security Data */ +static RD_BOOL +connect_process_server_security_data(rdpSec * sec, STREAM s, uint32 * encryptionMethod, uint8 server_random[SEC_RANDOM_SIZE], uint8 * modulus, uint8 * exponent) +{ + uint32 encryptionLevel; + uint32 serverRandomLen; + uint32 serverCertLen; + uint32 certChainVersion; + uint32 dwVersion; + + /** + * encryptionMethod: + * + * ENCRYPTION_METHOD_NONE 0 + * ENCRYPTION_METHOD_40BIT 1 + * ENCRYPTION_METHOD_128BIT 2 + * ENCRYPTION_METHOD_56BIT 8 + * ENCRYPTION_METHOD_FIPS 16 + * + */ + in_uint32_le(s, *encryptionMethod); /* encryptionMethod, 0 for TLS/NLA */ + + /** + * encryptionLevel: + * + * ENCRYPTION_LEVEL_NONE 0 + * ENCRYPTION_LEVEL_LOW 1 + * ENCRYPTION_LEVEL_CLIENT_COMPATIBLE 2 + * ENCRYPTION_LEVEL_HIGH 3 + * ENCRYPTION_LEVEL_FIPS 4 + * + */ + in_uint32_le(s, encryptionLevel); /* encryptionLevel, 0 for TLS/NLA */ + + if (encryptionLevel == 0) /* no encryption */ + return False; + + in_uint32_le(s, serverRandomLen); /* serverRandomLen */ + in_uint32_le(s, serverCertLen); /* serverCertLen */ + + if (serverRandomLen != SEC_RANDOM_SIZE) + { + ui_error(sec->rdp->inst, "serverRandomLen %d, expected %d\n", serverRandomLen, SEC_RANDOM_SIZE); + return False; + } + + in_uint8a(s, server_random, SEC_RANDOM_SIZE); /* serverRandom */ + + /* Server Certificate: */ + in_uint32_le(s, dwVersion); /* bit 0x80000000 = temporary certificate */ + certChainVersion = dwVersion & 0x7FFFFFFF; + + if (certChainVersion == 1) /* Server Proprietary Certificate */ + { + sec_parse_cert_chain_v1(sec, s, modulus, exponent); + } + else if (certChainVersion == 2) /* X.509 */ + { + sec_parse_cert_chain_v2(sec, s, modulus, exponent); + } + else + { + ui_error(sec->rdp->inst, "invalid cert chain version: %d\n", certChainVersion); + return False; + } + + return s_check_end(s); +} + +/* Process Server Security Data */ +static void +connect_input_server_security_data(rdpSec * sec, STREAM s) +{ + uint32 rc4_key_size; + uint8 server_random[SEC_RANDOM_SIZE]; + uint8 client_random[SEC_RANDOM_SIZE]; + uint8 modulus[SEC_MAX_MODULUS_SIZE]; + uint8 exponent[SEC_EXPONENT_SIZE]; + uint8 client_random_rev[SEC_RANDOM_SIZE]; + uint8 crypted_random_rev[SEC_MAX_MODULUS_SIZE]; + + memset(modulus, 0, sizeof(modulus)); + memset(exponent, 0, sizeof(exponent)); + if (!connect_process_server_security_data(sec, s, &rc4_key_size, server_random, modulus, exponent)) + { + /* encryptionMethod (rc4_key_size) = 0 means TLS */ + if (rc4_key_size > 0) + { + DEBUG_SEC("Failed to parse crypt info"); + } + return; + } + + DEBUG_SEC("Generating client random"); + generate_random(client_random); + sec_reverse_copy(client_random_rev, client_random, SEC_RANDOM_SIZE); + crypto_rsa_encrypt(SEC_RANDOM_SIZE, client_random_rev, crypted_random_rev, + sec->server_public_key_len, modulus, exponent); + sec_reverse_copy(sec->sec_crypted_random, crypted_random_rev, sec->server_public_key_len); + sec_generate_keys(sec, client_random, server_random, rc4_key_size); +} + +/* Process Server Core Data */ +static void +connect_input_server_core_data(rdpSec * sec, STREAM s, uint16 length) +{ + uint32 server_rdp_version, clientRequestedProtocols; + in_uint32_le(s, server_rdp_version); + + if(server_rdp_version == 0x00080001) + { + sec->rdp->settings->rdp_version = 4; + sec->rdp->settings->server_depth = 8; + } + else if(server_rdp_version == 0x00080004) + { + sec->rdp->settings->rdp_version = 5; /* FIXME: We can't just upgrade the RDP version! */ + } + else + { + ui_error(sec->rdp->inst, "Invalid server rdp version %ul\n", server_rdp_version); + } + + DEBUG_SEC("Server RDP version is %d", sec->rdp->settings->rdp_version); + if (length >= 12) + { + in_uint32_le(s, clientRequestedProtocols); + } +} + +/* Process Server Network Data */ +static void +connect_input_server_network_data(rdpSec * sec, STREAM s) +{ + int i; + uint16 MCSChannelId; + uint16 channelCount; + + in_uint16_le(s, MCSChannelId); /* MCSChannelId */ + if (MCSChannelId != MCS_GLOBAL_CHANNEL) + ui_error(sec->rdp->inst, "expected IO channel 0x%x=%d but got 0x%x=%d\n", + MCS_GLOBAL_CHANNEL, MCS_GLOBAL_CHANNEL, MCSChannelId, MCSChannelId); + in_uint16_le(s, channelCount); /* channelCount */ + + /* TODO: Check that it matches rdp->settings->num_channels */ + if (channelCount != sec->rdp->settings->num_channels) + { + ui_error(sec->rdp->inst, "client requested %d channels, server replied with %d channels", + sec->rdp->settings->num_channels, channelCount); + } + + /* channelIdArray */ + for (i = 0; i < channelCount; i++) + { + uint16 channelId; + in_uint16_le(s, channelId); /* Channel ID allocated to requested channel number i */ + + /* TODO: Assign channel ids here instead of in freerdp.c l_rdp_connect */ + if (channelId != sec->rdp->settings->channels[i].chan_id) + { + ui_error(sec->rdp->inst, "channel %d is %d but should have been %d\n", + i, channelId, sec->rdp->settings->channels[i].chan_id); + } + } + + if (channelCount % 2 == 1) + in_uint8s(s, 2); /* Padding */ +} + +/* Process connect response data blob */ +void +connect_process_mcs_data(rdpSec * sec, STREAM s) +{ + uint8 byte; + uint16 type; + uint16 length; + uint16 totalLength; + uint8 *next_tag; + + in_uint8s(s, 21); /* TODO: T.124 ConferenceCreateResponse userData with key h221NonStandard McDn */ + + in_uint8(s, byte); + totalLength = (uint16) byte; + + if (byte & 0x80) + { + totalLength &= ~0x80; + totalLength <<= 8; + in_uint8(s, byte); + totalLength += (uint16) byte; + } + + while (s->p < s->end) + { + in_uint16_le(s, type); + in_uint16_le(s, length); + + if (length <= 4) + return; + + next_tag = s->p + length - 4; + + switch (type) + { + case UDH_SC_CORE: /* Server Core Data */ + connect_input_server_core_data(sec, s, length); + break; + + case UDH_SC_NET: /* Server Network Data */ + connect_input_server_network_data(sec, s); + break; + + case UDH_SC_SECURITY: /* Server Security Data */ + connect_input_server_security_data(sec, s); + break; + } + + s->p = next_tag; + } +} + + diff --git a/libfreerdp-core/connect.h b/libfreerdp-core/connect.h new file mode 100644 index 0000000..b8596b7 --- /dev/null +++ b/libfreerdp-core/connect.h @@ -0,0 +1,26 @@ +/* + FreeRDP: A Remote Desktop Protocol client. + Connection Sequence + + Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com> + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef __CONNECT_H +#define __CONNECT_H + +void +connect_output_gcc_conference_create_request(rdpSec * sec, STREAM s); + +#endif /* __CONNECT_H */ diff --git a/libfreerdp-core/credssp.c b/libfreerdp-core/credssp.c index 86e60d5..a80cd7b 100644 --- a/libfreerdp-core/credssp.c +++ b/libfreerdp-core/credssp.c @@ -21,7 +21,7 @@ #include "rdp.h" #include "tls.h" #include "asn1.h" -#include "secure.h" +#include "security.h" #include "stream.h" #include "tcp.h" #include "mcs.h" @@ -58,7 +58,7 @@ asn1_write(const void *buffer, size_t size, void *fd) void credssp_ntlmssp_init(rdpCredssp *credssp) { NTLMSSP *ntlmssp = credssp->ntlmssp; - rdpSet *settings = credssp->sec->rdp->settings; + rdpSet *settings = credssp->net->rdp->settings; ntlmssp_set_password(ntlmssp, settings->password); ntlmssp_set_username(ntlmssp, settings->username); @@ -92,7 +92,7 @@ int credssp_get_public_key(rdpCredssp *credssp) CryptoCert cert; int ret; - cert = tls_get_certificate(credssp->sec->tls); + cert = tls_get_certificate(credssp->net->tls); if (cert == NULL) { printf("credssp_get_public_key: tls_get_certificate failed to return the server certificate.\n"); @@ -396,7 +396,7 @@ void credssp_send(rdpCredssp *credssp, DATABLOB *negoToken, DATABLOB *pubKeyAuth if (enc_rval.encoded != -1) { - tls_write(credssp->sec->tls, buffer, size); + tls_write(credssp->net->tls, buffer, size); } asn_DEF_TSRequest.free_struct(&asn_DEF_TSRequest, ts_request, 0); @@ -422,7 +422,7 @@ int credssp_recv(rdpCredssp *credssp, DATABLOB *negoToken, DATABLOB *pubKeyAuth, TSRequest_t *ts_request = 0; recv_buffer = xmalloc(size); - bytes_read = tls_read(credssp->sec->tls, recv_buffer, size); + bytes_read = tls_read(credssp->net->tls, recv_buffer, size); if (bytes_read < 0) return -1; @@ -503,7 +503,7 @@ void credssp_current_time(uint8* timestamp) */ rdpCredssp * -credssp_new(struct rdp_sec * sec) +credssp_new(struct rdp_network * net) { rdpCredssp * self; @@ -511,7 +511,7 @@ credssp_new(struct rdp_sec * sec) if (self != NULL) { memset(self, 0, sizeof(rdpCredssp)); - self->sec = sec; + self->net = net; self->send_seq_num = 0; self->ntlmssp = ntlmssp_new(); } diff --git a/libfreerdp-core/credssp.h b/libfreerdp-core/credssp.h index 2ed888e..8d8b6e4 100644 --- a/libfreerdp-core/credssp.h +++ b/libfreerdp-core/credssp.h @@ -20,7 +20,7 @@ #ifndef __CREDSSP_H #define __CREDSSP_H -#include "secure.h" +#include "network.h" #include "ntlmssp.h" struct rdp_credssp @@ -33,7 +33,7 @@ struct rdp_credssp DATABLOB ts_credentials; CryptoRc4 rc4_seal_state; struct _NTLMSSP *ntlmssp; - struct rdp_sec * sec; + struct rdp_network * net; }; typedef struct rdp_credssp rdpCredssp; @@ -50,7 +50,7 @@ void credssp_encode_ts_credentials(rdpCredssp *credssp); void credssp_current_time(uint8* timestamp); void credssp_rc4k(uint8* key, int length, uint8* plaintext, uint8* ciphertext); -rdpCredssp* credssp_new(rdpSec *sec); +rdpCredssp* credssp_new(struct rdp_network * net); void credssp_free(rdpCredssp *credssp); -#endif // __CREDSSP_H +#endif /* __CREDSSP_H */ diff --git a/libfreerdp-core/crypto/openssl.c b/libfreerdp-core/crypto/openssl.c index 84417d7..507288c 100644 --- a/libfreerdp-core/crypto/openssl.c +++ b/libfreerdp-core/crypto/openssl.c @@ -19,6 +19,7 @@ #include "frdp.h" #include "crypto.h" +#include <freerdp/types/base.h> #include <freerdp/utils/memory.h> #include <freerdp/constants/constants.h> #include <time.h> diff --git a/libfreerdp-core/frdp.h b/libfreerdp-core/frdp.h index 68614b8..19f79f1 100644 --- a/libfreerdp-core/frdp.h +++ b/libfreerdp-core/frdp.h @@ -53,8 +53,6 @@ void ui_warning(rdpInst * inst, char * format, ...); void ui_unimpl(rdpInst * inst, char * format, ...); -void -hexdump(unsigned char * p, int len); int load_license(unsigned char ** data); RD_BOOL diff --git a/libfreerdp-core/freerdp.c b/libfreerdp-core/freerdp.c index 503203a..2637ce5 100644 --- a/libfreerdp-core/freerdp.c +++ b/libfreerdp-core/freerdp.c @@ -19,7 +19,7 @@ #include <stdarg.h> #include "frdp.h" #include "rdp.h" -#include "secure.h" +#include "security.h" #include "mcs.h" #include "iso.h" #include "tcp.h" @@ -27,6 +27,7 @@ #include "ext.h" #include <freerdp/freerdp.h> #include <freerdp/utils/memory.h> +#include <freerdp/utils/hexdump.h> #define RDP_FROM_INST(_inst) ((rdpRdp *) (_inst->rdp)) @@ -90,34 +91,6 @@ ui_unimpl(rdpInst * inst, char * format, ...) xfree(text2); } -void -hexdump(unsigned char * p, int len) -{ - unsigned char *line = p; - int i, thisline, offset = 0; - - while (offset < len) - { - printf("%04x ", offset); - thisline = len - offset; - if (thisline > 16) - thisline = 16; - - for (i = 0; i < thisline; i++) - printf("%02x ", line[i]); - - for (; i < 16; i++) - printf(" "); - - for (i = 0; i < thisline; i++) - printf("%c", (line[i] >= 0x20 && line[i] < 0x7f) ? line[i] : '.'); - - printf("\n"); - offset += thisline; - line += thisline; - } -} - int load_license(unsigned char ** data) { @@ -477,9 +450,9 @@ l_rdp_get_fds(rdpInst * inst, void ** read_fds, int * read_count, rdp = RDP_FROM_INST(inst); #ifdef _WIN32 - read_fds[*read_count] = (void *) (rdp->sec->mcs->iso->tcp->wsa_event); + read_fds[*read_count] = (void *) (rdp->net->tcp->wsa_event); #else - read_fds[*read_count] = (void *)(long) (rdp->sec->mcs->iso->tcp->sock); + read_fds[*read_count] = (void *)(long) (rdp->net->tcp->sockfd); #endif (*read_count)++; return 0; @@ -495,10 +468,10 @@ l_rdp_check_fds(rdpInst * inst) rdp = RDP_FROM_INST(inst); #ifdef _WIN32 - WSAResetEvent(rdp->sec->mcs->iso->tcp->wsa_event); + WSAResetEvent(rdp->net->tcp->wsa_event); #endif rv = 0; - if (tcp_can_recv(rdp->sec->mcs->iso->tcp->sock, 0)) + if (tcp_can_recv(rdp->net->tcp->sockfd, 0)) { if (!rdp_loop(rdp, &deactivated)) { @@ -559,7 +532,7 @@ l_rdp_channel_data(rdpInst * inst, int chan_id, char * data, int data_size) rdpChannels * chan; rdp = RDP_FROM_INST(inst); - chan = rdp->sec->mcs->chan; + chan = rdp->net->mcs->chan; return vchan_send(chan, chan_id, data, data_size); } diff --git a/libfreerdp-core/iso.c b/libfreerdp-core/iso.c index 238b80c..de7b40d 100644 --- a/libfreerdp-core/iso.c +++ b/libfreerdp-core/iso.c @@ -20,7 +20,7 @@ #include "tcp.h" #include "mcs.h" #include "nego.h" -#include "secure.h" +#include "security.h" #include "credssp.h" #include "rdp.h" #include <freerdp/rdpset.h> @@ -69,7 +69,7 @@ x224_send_dst_src_class(rdpIso * iso, uint8 code) { STREAM s; - s = tcp_init(iso->tcp, 11); + s = network_stream_init(iso->net, 11); tpkt_output_header(s, 11); @@ -80,7 +80,7 @@ x224_send_dst_src_class(rdpIso * iso, uint8 code) out_uint8(s, 0); /* class */ s_mark_end(s); - tcp_send(iso->tcp, s); + network_send(iso->net, s); } /* Output and send X.224 Connection Request TPDU with routing for username */ @@ -93,9 +93,9 @@ x224_send_connection_request(rdpIso * iso) cookie_length = strlen(iso->cookie); - if (iso->mcs->sec->rdp->redirect_routingtoken) + if (iso->net->rdp->redirect_routingtoken) /* routingToken */ - length += iso->mcs->sec->rdp->redirect_routingtoken_len; + length += iso->net->rdp->redirect_routingtoken_len; else /* cookie */ length += 19 + cookie_length; @@ -104,7 +104,7 @@ x224_send_connection_request(rdpIso * iso) length += 8; /* FIXME: Use x224_send_dst_src_class */ - s = tcp_init(iso->tcp, length); + s = network_stream_init(iso->net, length); tpkt_output_header(s, length); @@ -115,10 +115,10 @@ x224_send_connection_request(rdpIso * iso) out_uint16_le(s, 0); /* src_ref */ out_uint8(s, 0); /* class */ - if (iso->mcs->sec->rdp->redirect_routingtoken) + if (iso->net->rdp->redirect_routingtoken) { /* routingToken */ - out_uint8p(s, iso->mcs->sec->rdp->redirect_routingtoken, iso->mcs->sec->rdp->redirect_routingtoken_len); + out_uint8p(s, iso->net->rdp->redirect_routingtoken, iso->net->rdp->redirect_routingtoken_len); } else { @@ -138,7 +138,7 @@ x224_send_connection_request(rdpIso * iso) } s_mark_end(s); - tcp_send(iso->tcp, s); + network_send(iso->net, s); } /* Receive an X.224 TPDU */ @@ -149,7 +149,7 @@ x224_recv(rdpIso * iso, STREAM s, int length, uint8 * pcode) uint8 code; uint8 subcode; - s = tcp_recv(iso->tcp, s, length - 4); + s = network_recv(iso->net, s, length - 4); if (s == NULL) return NULL; @@ -217,7 +217,7 @@ tpkt_recv(rdpIso * iso, uint8 * pcode, isoRecvType * ptype) STREAM s; int length; - s = tcp_recv(iso->tcp, NULL, 4); + s = network_recv(iso->net, NULL, 4); if (s == NULL) return NULL; @@ -250,7 +250,7 @@ tpkt_recv(rdpIso * iso, uint8 * pcode, isoRecvType * ptype) next_be(s, length); } - s = tcp_recv(iso->tcp, s, length - 4); + s = network_recv(iso->net, s, length - 4); return s; } return NULL; /* Fast-Path not allowed */ @@ -268,7 +268,7 @@ STREAM iso_init(rdpIso * iso, int length) { STREAM s; - s = tcp_init(iso->tcp, length + 7); + s = network_stream_init(iso->net, length + 7); s_push_layer(s, iso_hdr, 7); return s; } @@ -278,7 +278,7 @@ STREAM iso_fp_init(rdpIso * iso, int length) { STREAM s; - s = tcp_init(iso->tcp, length + 3); + s = network_stream_init(iso->net, length + 3); s_push_layer(s, iso_hdr, 3); return s; } @@ -300,7 +300,7 @@ iso_send(rdpIso * iso, STREAM s) out_uint8(s, X224_TPDU_DATA); /* code */ out_uint8(s, 0x80); /* eot */ - tcp_send(iso->tcp, s); + network_send(iso->net, s); } /* Send an fast path data PDU */ @@ -335,7 +335,7 @@ iso_fp_send(rdpIso * iso, STREAM s, uint32 flags) out_uint8(s, len); } - tcp_send(iso->tcp, s); + network_send(iso->net, s); } /* Receive ISO transport data packet @@ -355,7 +355,7 @@ iso_recv(rdpIso * iso, isoRecvType * ptype) (*ptype == ISO_RECV_X224) && (code != X224_TPDU_DATA)) { - ui_error(iso->mcs->sec->rdp->inst, "expected X224_TPDU_DATA, got 0x%x\n", code); + ui_error(iso->net->rdp->inst, "expected X224_TPDU_DATA, got 0x%x\n", code); return NULL; } @@ -366,8 +366,14 @@ iso_recv(rdpIso * iso, isoRecvType * ptype) RD_BOOL iso_connect(rdpIso * iso, char *server, char *username, int port) { - if (strlen(iso->mcs->sec->rdp->settings->domain) > 0) - iso->cookie = iso->mcs->sec->rdp->settings->domain; + if (iso->net == NULL) + printf("iso->net\n"); + + if (iso->net->rdp == NULL) + printf("iso->net->rdp\n"); + + if (strlen(iso->net->rdp->settings->domain) > 0) + iso->cookie = iso->net->rdp->settings->domain; else iso->cookie = username; @@ -376,6 +382,7 @@ iso_connect(rdpIso * iso, char *server, char *username, int port) iso->nego->tcp_connected = 0; nego_init(iso->nego); + if (nego_connect(iso->nego) > 0) { return True; @@ -392,15 +399,11 @@ void iso_disconnect(rdpIso * iso) { x224_send_dst_src_class(iso, X224_TPDU_DISCONNECT_REQUEST); -#ifndef DISABLE_TLS - if (iso->mcs->sec->tls) - tls_disconnect(iso->mcs->sec->tls); -#endif - tcp_disconnect(iso->tcp); + network_disconnect(iso->net); } rdpIso * -iso_new(struct rdp_mcs *mcs) +iso_new(struct rdp_network * net) { rdpIso *self; @@ -409,9 +412,9 @@ iso_new(struct rdp_mcs *mcs) if (self != NULL) { memset(self, 0, sizeof(rdpIso)); - self->mcs = mcs; - self->tcp = tcp_new(self); - self->nego = nego_new(self); + self->net = net; + self->mcs = net->mcs; + self->nego = net->nego; } return self; @@ -422,9 +425,6 @@ iso_free(rdpIso * iso) { if (iso != NULL) { - tcp_free(iso->tcp); - if (iso->nego != NULL) - nego_free(iso->nego); xfree(iso); } } diff --git a/libfreerdp-core/iso.h b/libfreerdp-core/iso.h index 148b687..f9e3669 100644 --- a/libfreerdp-core/iso.h +++ b/libfreerdp-core/iso.h @@ -18,6 +18,8 @@ */ #include <freerdp/types/ui.h> + +#include "network.h" #include "stream.h" #include "nego.h" @@ -29,7 +31,7 @@ struct rdp_iso char* cookie; struct _NEGO * nego; struct rdp_mcs * mcs; - struct rdp_tcp * tcp; + struct rdp_network * net; }; typedef struct rdp_iso rdpIso; @@ -60,7 +62,7 @@ iso_connect(rdpIso * iso, char * server, char * username, int port); void iso_disconnect(rdpIso * iso); rdpIso * -iso_new(struct rdp_mcs * mcs); +iso_new(struct rdp_network * net); void iso_free(rdpIso * iso); diff --git a/libfreerdp-core/license.c b/libfreerdp-core/license.c index 2863757..b79e52e 100644 --- a/libfreerdp-core/license.c +++ b/libfreerdp-core/license.c @@ -19,7 +19,7 @@ #include "frdp.h" #include "crypto.h" -#include "secure.h" +#include "security.h" #include "rdp.h" #include <freerdp/rdpset.h> #include <freerdp/utils/memory.h> @@ -49,7 +49,7 @@ static void license_generate_hwid(rdpLicense * license, uint8 * hwid) { buf_out_uint32(hwid, 2); - strncpy((char *) (hwid + 4), license->sec->rdp->settings->hostname, LICENSE_HWID_SIZE - 4); + strncpy((char *) (hwid + 4), license->net->rdp->settings->hostname, LICENSE_HWID_SIZE - 4); } /* Send a Licensing packet with Client License Information */ @@ -63,7 +63,7 @@ license_present(rdpLicense * license, uint8 * client_random, uint8 * rsa_data, license_size + LICENSE_HWID_SIZE + LICENSE_SIGNATURE_SIZE; STREAM s; - s = sec_init(license->sec, sec_flags, length + 4); + s = sec_init(license->net->sec, sec_flags, length + 4); /* Licensing Preamble (LICENSE_PREAMBLE) */ out_uint8(s, LICENSE_INFO); /* bMsgType LICENSE_INFO */ @@ -96,7 +96,7 @@ license_present(rdpLicense * license, uint8 * client_random, uint8 * rsa_data, out_uint8p(s, signature, LICENSE_SIGNATURE_SIZE); /* MACData */ s_mark_end(s); - sec_send(license->sec, s, sec_flags); + sec_send(license->net->sec, s, sec_flags); } /* Send a Licensing packet with Client New License Request */ @@ -109,7 +109,7 @@ license_send_request(rdpLicense * license, uint8 * client_random, uint8 * rsa_da uint16 length = 128 + userlen + hostlen; STREAM s; - s = sec_init(license->sec, sec_flags, length + 2); + s = sec_init(license->net->sec, sec_flags, length + 2); /* Licensing Preamble (LICENSE_PREAMBLE) */ out_uint8(s, NEW_LICENSE_REQUEST); /* NEW_LICENSE_REQUEST */ @@ -139,7 +139,7 @@ license_send_request(rdpLicense * license, uint8 * client_random, uint8 * rsa_da out_uint8p(s, host, hostlen); s_mark_end(s); - sec_send(license->sec, s, sec_flags); + sec_send(license->net->sec, s, sec_flags); } /* Process a Server License Request packet */ @@ -208,8 +208,8 @@ license_process_request(rdpLicense * license, STREAM s) } license_send_request(license, null_data, null_data, - license->sec->rdp->settings->username, - license->sec->rdp->settings->hostname); + license->net->rdp->settings->username, + license->net->rdp->settings->hostname); } /* Send a Licensing packet with Platform Challenge Response */ @@ -220,7 +220,7 @@ license_send_authresp(rdpLicense * license, uint8 * token, uint8 * crypt_hwid, u uint16 length = 58; STREAM s; - s = sec_init(license->sec, sec_flags, length + 2); + s = sec_init(license->net->sec, sec_flags, length + 2); /* Licensing Preamble (LICENSE_PREAMBLE) */ out_uint8(s, PLATFORM_CHALLENGE_RESPONSE); /* PLATFORM_CHALLENGE_RESPONSE */ @@ -240,7 +240,7 @@ license_send_authresp(rdpLicense * license, uint8 * token, uint8 * crypt_hwid, u out_uint8p(s, signature, LICENSE_SIGNATURE_SIZE); /* MACData */ s_mark_end(s); - sec_send(license->sec, s, sec_flags); + sec_send(license->net->sec, s, sec_flags); } /* Parse a Server Platform Challenge packet */ @@ -256,7 +256,7 @@ license_parse_authreq(rdpLicense * license, STREAM s, uint8 ** token, uint8 ** s in_uint16_le(s, tokenlen); /* wBlobLen */ if (tokenlen != LICENSE_TOKEN_SIZE) { - ui_error(license->sec->rdp->inst, "token len %d\n", tokenlen); + ui_error(license->net->rdp->inst, "token len %d\n", tokenlen); return False; } in_uint8p(s, *token, tokenlen); /* RC4-encrypted challenge data */ @@ -396,14 +396,15 @@ license_process(rdpLicense * license, STREAM s) break; default: - ui_unimpl(license->sec->rdp->inst, "Unknown license tag 0x%x", tag); + ui_unimpl(license->net->rdp->inst, "Unknown license tag 0x%x", tag); + break; } s->p = license_start + wMsgSize; /* FIXME: Shouldn't be necessary if parsed properly */ ASSERT(s->p <= s->end); } rdpLicense * -license_new(struct rdp_sec *sec) +license_new(struct rdp_network * net) { rdpLicense *self; @@ -411,7 +412,7 @@ license_new(struct rdp_sec *sec) if (self != NULL) { memset(self, 0, sizeof(rdpLicense)); - self->sec = sec; + self->net = net; } return self; } diff --git a/libfreerdp-core/license.h b/libfreerdp-core/license.h index 3b2e525..35df3f0 100644 --- a/libfreerdp-core/license.h +++ b/libfreerdp-core/license.h @@ -21,12 +21,13 @@ #define __LICENSE_H #include "stream.h" +#include "network.h" #include <freerdp/types/ui.h> #include <freerdp/utils/debug.h> struct rdp_license { - struct rdp_sec * sec; + struct rdp_network * net; uint8 license_key[16]; uint8 license_sign_key[16]; RD_BOOL license_issued; @@ -36,7 +37,7 @@ typedef struct rdp_license rdpLicense; void license_process(rdpLicense * license, STREAM s); rdpLicense * -license_new(struct rdp_sec * secure); +license_new(struct rdp_network * net); void license_free(rdpLicense * license); diff --git a/libfreerdp-core/mcs.c b/libfreerdp-core/mcs.c index f1a2715..80c54d1 100644 --- a/libfreerdp-core/mcs.c +++ b/libfreerdp-core/mcs.c @@ -20,10 +20,11 @@ #include "frdp.h" #include "iso.h" #include "chan.h" -#include "secure.h" #include "rdp.h" #include "asn1.h" #include "tcp.h" +#include "connect.h" +#include "security.h" #include <freerdp/rdpset.h> #include <freerdp/utils/memory.h> @@ -69,7 +70,7 @@ mcs_send_connect_initial(rdpMcs * mcs) gccCCrq.p = gccCCrq.data = (uint8 *) xmalloc(gccCCrq.size); gccCCrq.end = gccCCrq.data + gccCCrq.size; - sec_out_gcc_conference_create_request(mcs->sec, &gccCCrq); + connect_output_gcc_conference_create_request(mcs->net->sec, &gccCCrq); gccCCrq_length = gccCCrq.end - gccCCrq.data; length = 9 + 3 * 34 + 4 + gccCCrq_length; @@ -115,7 +116,7 @@ mcs_recv_connect_response(rdpMcs * mcs) in_uint8(s, result); if (result != 0) { - ui_error(mcs->sec->rdp->inst, "MCS connect: %d\n", result); + ui_error(mcs->net->rdp->inst, "MCS connect: %d\n", result); return False; } @@ -125,7 +126,7 @@ mcs_recv_connect_response(rdpMcs * mcs) ber_parse_header(mcs, s, BER_TAG_OCTET_STRING, &length); - sec_process_mcs_data(mcs->sec, s); + connect_process_mcs_data(mcs->net->sec, s); return s_check_end(s); } @@ -173,14 +174,14 @@ mcs_recv_aucf(rdpMcs * mcs, uint16 * mcs_userid) in_uint8(s, opcode); if ((opcode >> 2) != T125_DOMAINMCSPDU_AttachUserConfirm) { - ui_error(mcs->sec->rdp->inst, "expected AUcf, got %d\n", opcode); + ui_error(mcs->net->rdp->inst, "expected AUcf, got %d\n", opcode); return False; } in_uint8(s, result); if (result != 0) { - ui_error(mcs->sec->rdp->inst, "AUrq: %d\n", result); + ui_error(mcs->net->rdp->inst, "AUrq: %d\n", result); return False; } @@ -222,14 +223,14 @@ mcs_recv_cjcf(rdpMcs * mcs) in_uint8(s, opcode); if ((opcode >> 2) != T125_DOMAINMCSPDU_ChannelJoinConfirm) { - ui_error(mcs->sec->rdp->inst, "expected CJcf, got %d\n", opcode); + ui_error(mcs->net->rdp->inst, "expected CJcf, got %d\n", opcode); return False; } in_uint8(s, result); if (result != 0) { - ui_error(mcs->sec->rdp->inst, "CJrq: %d\n", result); + ui_error(mcs->net->rdp->inst, "CJrq: %d\n", result); return False; } @@ -321,7 +322,7 @@ mcs_recv(rdpMcs * mcs, isoRecvType * ptype, uint16 * channel) { if (pduType != T125_DOMAINMCSPDU_DisconnectProviderUltimatum) { - ui_error(mcs->sec->rdp->inst, "expected data, got %d\n", pduType); + ui_error(mcs->net->rdp->inst, "expected data, got %d\n", pduType); } return NULL; } @@ -356,7 +357,7 @@ mcs_connect(rdpMcs * mcs) mcs_send_connect_initial(mcs); if (!mcs_recv_connect_response(mcs)) { - ui_error(mcs->sec->rdp->inst, "invalid mcs_recv_connect_response\n"); + ui_error(mcs->net->rdp->inst, "invalid mcs_recv_connect_response\n"); goto error; } @@ -365,7 +366,7 @@ mcs_connect(rdpMcs * mcs) mcs_send_aurq(mcs); if (!mcs_recv_aucf(mcs, &(mcs->mcs_userid))) { - ui_error(mcs->sec->rdp->inst, "invalid mcs_recv_aucf\n"); + ui_error(mcs->net->rdp->inst, "invalid mcs_recv_aucf\n"); goto error; } @@ -373,29 +374,29 @@ mcs_connect(rdpMcs * mcs) if (!mcs_recv_cjcf(mcs)) { - ui_error(mcs->sec->rdp->inst, "invalid mcs_recv_cjcf\n"); + ui_error(mcs->net->rdp->inst, "invalid mcs_recv_cjcf\n"); goto error; } mcs_send_cjrq(mcs, MCS_GLOBAL_CHANNEL); if (!mcs_recv_cjcf(mcs)) { - ui_error(mcs->sec->rdp->inst, "invalid mcs_recv_cjcf\n"); + ui_error(mcs->net->rdp->inst, "invalid mcs_recv_cjcf\n"); goto error; } - settings = mcs->sec->rdp->settings; + settings = mcs->net->rdp->settings; for (i = 0; i < settings->num_channels; i++) { mcs_id = settings->channels[i].chan_id; if (mcs_id >= mcs->mcs_userid + MCS_USERCHANNEL_BASE) { - ui_warning(mcs->sec->rdp->inst, "channel %d got id %d >= %d\n", i, mcs_id, mcs->mcs_userid + MCS_USERCHANNEL_BASE); + ui_warning(mcs->net->rdp->inst, "channel %d got id %d >= %d\n", i, mcs_id, mcs->mcs_userid + MCS_USERCHANNEL_BASE); } mcs_send_cjrq(mcs, mcs_id); if (!mcs_recv_cjcf(mcs)) { - ui_error(mcs->sec->rdp->inst, "channel %d id %d invalid mcs_recv_cjcf\n", i, mcs_id); + ui_error(mcs->net->rdp->inst, "channel %d id %d invalid mcs_recv_cjcf\n", i, mcs_id); goto error; } } @@ -414,18 +415,21 @@ mcs_disconnect(rdpMcs * mcs) } rdpMcs * -mcs_new(struct rdp_sec * sec) +mcs_new(struct rdp_network * net) { rdpMcs * self; self = (rdpMcs *) xmalloc(sizeof(rdpMcs)); + if (self != NULL) { memset(self, 0, sizeof(rdpMcs)); - self->sec = sec; - self->iso = iso_new(self); + self->net = net; + self->iso = iso_new(net); self->chan = vchan_new(self); + self->net->iso = self->iso; } + return self; } diff --git a/libfreerdp-core/mcs.h b/libfreerdp-core/mcs.h index 79dc6f5..8313035 100644 --- a/libfreerdp-core/mcs.h +++ b/libfreerdp-core/mcs.h @@ -21,13 +21,14 @@ #define __MCS_H #include "iso.h" +#include "network.h" #include <freerdp/utils/debug.h> struct rdp_mcs { - struct rdp_sec * sec; uint16 mcs_userid; struct rdp_iso * iso; + struct rdp_network * net; struct rdp_channels * chan; }; typedef struct rdp_mcs rdpMcs; @@ -49,7 +50,7 @@ mcs_connect(rdpMcs * mcs); void mcs_disconnect(rdpMcs * mcs); rdpMcs * -mcs_new(struct rdp_sec * secure); +mcs_new(struct rdp_network * net); void mcs_free(rdpMcs * mcs); diff --git a/libfreerdp-core/nego.c b/libfreerdp-core/nego.c index 31698f9..0d960bd 100644 --- a/libfreerdp-core/nego.c +++ b/libfreerdp-core/nego.c @@ -69,7 +69,7 @@ int nego_tcp_connect(NEGO *nego) { if (nego->tcp_connected == 0) { - if (tcp_connect(nego->iso->tcp, nego->hostname, nego->port) == False) + if (tcp_connect(nego->net->tcp, nego->hostname, nego->port) == False) { nego->tcp_connected = 0; return 0; @@ -93,7 +93,7 @@ int nego_tcp_connect(NEGO *nego) int nego_tcp_disconnect(NEGO *nego) { if (nego->tcp_connected) - tcp_disconnect(nego->iso->tcp); + tcp_disconnect(nego->net->tcp); nego->tcp_connected = 0; return 1; @@ -110,8 +110,8 @@ void nego_attempt_nla(NEGO *nego) nego->requested_protocols = PROTOCOL_NLA | PROTOCOL_TLS; nego_tcp_connect(nego); - x224_send_connection_request(nego->iso); - tpkt_recv(nego->iso, &code, NULL); + x224_send_connection_request(nego->net->iso); + tpkt_recv(nego->net->iso, &code, NULL); if (nego->state != NEGO_STATE_FINAL) { @@ -137,8 +137,8 @@ void nego_attempt_tls(NEGO *nego) nego->requested_protocols = PROTOCOL_TLS; nego_tcp_connect(nego); - x224_send_connection_request(nego->iso); - tpkt_recv(nego->iso, &code, NULL); + x224_send_connection_request(nego->net->iso); + tpkt_recv(nego->net->iso, &code, NULL); if (nego->state != NEGO_STATE_FINAL) { @@ -162,9 +162,9 @@ void nego_attempt_rdp(NEGO *nego) nego->requested_protocols = PROTOCOL_RDP; nego_tcp_connect(nego); - x224_send_connection_request(nego->iso); + x224_send_connection_request(nego->net->iso); - if (tpkt_recv(nego->iso, &code, NULL) == NULL) + if (tpkt_recv(nego->net->iso, &code, NULL) == NULL) nego->state = NEGO_STATE_FAIL; else nego->state = NEGO_STATE_FINAL; @@ -278,14 +278,14 @@ void nego_process_negotiation_failure(NEGO *nego, STREAM s) * @return */ -NEGO* nego_new(struct rdp_iso * iso) +NEGO* nego_new(struct rdp_network * net) { NEGO *nego = (NEGO*) xmalloc(sizeof(NEGO)); if (nego != NULL) { memset(nego, '\0', sizeof(NEGO)); - nego->iso = iso; + nego->net = net; nego_init(nego); } diff --git a/libfreerdp-core/nego.h b/libfreerdp-core/nego.h index e1a5bfc..a179e19 100644 --- a/libfreerdp-core/nego.h +++ b/libfreerdp-core/nego.h @@ -22,7 +22,7 @@ #include "frdp.h" #include "stream.h" -#include "iso.h" +#include "network.h" enum _NEGO_STATE { @@ -41,7 +41,7 @@ struct _NEGO char *hostname; NEGO_STATE state; int tcp_connected; - struct rdp_iso * iso; + struct rdp_network * net; uint32 selected_protocol; uint32 requested_protocols; uint8 enabled_protocols[3]; @@ -60,7 +60,7 @@ void nego_recv(NEGO *nego, STREAM s); void nego_process_negotiation_response(NEGO *nego, STREAM s); void nego_process_negotiation_failure(NEGO *nego, STREAM s); -NEGO* nego_new(rdpIso * iso); +NEGO* nego_new(struct rdp_network * net); void nego_init(NEGO *nego); void nego_free(NEGO *nego); diff --git a/libfreerdp-core/network.c b/libfreerdp-core/network.c new file mode 100644 index 0000000..5d9904d --- /dev/null +++ b/libfreerdp-core/network.c @@ -0,0 +1,346 @@ +/* + FreeRDP: A Remote Desktop Protocol client. + Network Transport Abstraction Layer + + Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com> + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#include <freerdp/types/base.h> +#include <freerdp/utils/memory.h> + +#include "network.h" + +/* Initialize and return STREAM. + * The stream will have room for at least min_size. + * The tcp layers out stream will be used. */ + +STREAM +network_stream_init(rdpNetwork * net, uint32 min_size) +{ + STREAM result = &(net->out); + + if (min_size > result->size) + { + result->data = (uint8 *) xrealloc(result->data, min_size); + result->size = min_size; + } + + result->p = result->data; + result->end = result->data + result->size; + + return result; +} + +#ifndef DISABLE_TLS + +/* verify SSL/TLS connection integrity. 2 checks are carried out. First make sure that the + * certificate is assigned to the server we're connected to, and second make sure that the + * certificate is signed by a trusted certification authority + */ + +RD_BOOL +network_verify_tls(rdpNetwork * net) +{ + char * issuer; + char * subject; + char * fingerprint; + CryptoCert cert; + RD_BOOL verified = False; + + cert = tls_get_certificate(net->tls); + + if (!cert) + { + goto exit; + } + + subject = crypto_cert_get_subject(cert); + issuer = crypto_cert_get_issuer(cert); + fingerprint = crypto_cert_get_fingerprint(cert); + + verified = tls_verify(net->tls, net->server); + + if (verified != False) + verified = crypto_cert_verify_peer_identity(cert, net->server); + + verified = ui_check_certificate(net->rdp->inst, fingerprint, subject, issuer, verified); + + xfree(fingerprint); + xfree(subject); + xfree(issuer); + +exit: + if (cert) + { + crypto_cert_free(cert); + cert = NULL; + } + + return verified; +} +#endif + +RD_BOOL +network_connect_rdp(rdpNetwork * net) +{ + RD_BOOL status = False; + + printf("Standard RDP encryption negotiated\n"); + + status = mcs_connect(net->mcs); + + if (status && net->rdp->settings->encryption) + sec_establish_key(net->sec); + + return status; +} + +RD_BOOL +network_connect_tls(rdpNetwork * net) +{ + RD_BOOL status = False; + net->tls = tls_new(); + + if (!tls_connect(net->tls, net->tcp->sockfd)) + return False; + + if (!network_verify_tls(net)) + return False; + + net->tls_connected = 1; + net->rdp->settings->encryption = 0; + + status = mcs_connect(net->mcs); + + return status; +} + +RD_BOOL +network_connect_nla(rdpNetwork * net) +{ + /* TLS with NLA was successfully negotiated */ + + RD_BOOL status = 1; + net->tls = tls_new(); + + if (!tls_connect(net->tls, net->tcp->sockfd)) + return False; + + if (!network_verify_tls(net)) + return False; + + net->tls_connected = 1; + net->rdp->settings->encryption = 0; + + if (!net->rdp->settings->autologin) + if (!ui_authenticate(net->rdp->inst)) + return False; + + net->credssp = credssp_new(net); + + if (credssp_authenticate(net->credssp) < 0) + { + printf("Authentication failure, check credentials.\n" + "If credentials are valid, the NTLMSSP implementation may be to blame.\n"); + credssp_free(net->credssp); + return 0; + } + + credssp_free(net->credssp); + status = mcs_connect(net->mcs); + + return status; +} + +RD_BOOL +network_connect(rdpNetwork * net, char* server, char* username, int port) +{ + NEGO *nego = net->iso->nego; + + net->port = port; + net->server = server; + net->username = username; + net->license->license_issued = 0; + + if (net->rdp->settings->nla_security) + nego->enabled_protocols[PROTOCOL_NLA] = 1; + if (net->rdp->settings->tls_security) + nego->enabled_protocols[PROTOCOL_TLS] = 1; + if (net->rdp->settings->rdp_security) + nego->enabled_protocols[PROTOCOL_RDP] = 1; + + if (!iso_connect(net->iso, net->server, net->username, net->port)) + return False; + +#ifndef DISABLE_TLS + if(nego->selected_protocol & PROTOCOL_NLA) + { + /* TLS with NLA was successfully negotiated */ + printf("TLS encryption with NLA negotiated\n"); + return network_connect_nla(net); + } + else if(nego->selected_protocol & PROTOCOL_TLS) + { + /* TLS without NLA was successfully negotiated */ + printf("TLS encryption negotiated\n"); + return network_connect_tls(net); + } + else +#endif + { + return network_connect_rdp(net); + } + + return False; +} + +void +network_disconnect(rdpNetwork * net) +{ +#ifndef DISABLE_TLS + if (net->tls) + tls_disconnect(net->tls); +#endif + tcp_disconnect(net->tcp); +} + +void +network_send(rdpNetwork * net, STREAM s) +{ +#ifndef DISABLE_TLS + if (net->tls_connected) + { + tls_write(net->tls, (char*) s->data, s->end - s->data); + } + else +#endif + { + tcp_write(net->tcp, (char*) s->data, s->end - s->data); + } +} + +STREAM +network_recv(rdpNetwork * net, STREAM s, uint32 length) +{ + int rcvd = 0; + uint32 p_offset; + uint32 new_length; + uint32 end_offset; + + if (s == NULL) + { + /* read into "new" stream */ + if (length > net->in.size) + { + net->in.data = (uint8 *) xrealloc(net->in.data, length); + net->in.size = length; + } + + net->in.end = net->in.p = net->in.data; + s = &(net->in); + } + else + { + /* append to existing stream */ + new_length = (s->end - s->data) + length; + if (new_length > s->size) + { + p_offset = s->p - s->data; + end_offset = s->end - s->data; + s->data = (uint8 *) xrealloc(s->data, new_length); + s->size = new_length; + s->p = s->data + p_offset; + s->end = s->data + end_offset; + } + } + + while (length > 0) + { +#ifndef DISABLE_TLS + if (net->tls_connected) + { + rcvd = tls_read(net->tls, (char*) s->end, length); + + if (rcvd < 0) + return NULL; + } + else +#endif + { + rcvd = tcp_read(net->tcp, (char*) s->end, length); + } + + s->end += rcvd; + length -= rcvd; + } + + return s; +} + +rdpNetwork* +network_new(rdpRdp * rdp) +{ + rdpNetwork * self; + + self = (rdpNetwork *) xmalloc(sizeof(rdpNetwork)); + + if (self != NULL) + { + memset(self, 0, sizeof(rdpNetwork)); + self->rdp = rdp; + self->sec = rdp->sec; + self->mcs = mcs_new(self); + self->tcp = tcp_new(self); + self->nego = nego_new(self); + self->iso = iso_new(self); + self->license = license_new(self); + self->sec->net = self; + + self->in.size = 4096; + self->in.data = (uint8 *) xmalloc(self->in.size); + + self->out.size = 4096; + self->out.data = (uint8 *) xmalloc(self->out.size); + } + + return self; +} + +void +network_free(rdpNetwork * net) +{ + if (net != NULL) + { + xfree(net->in.data); + xfree(net->out.data); + + if (net->tcp != NULL) + tcp_free(net->tcp); + + if (net->iso != NULL) + iso_free(net->iso); + + if (net->mcs != NULL) + mcs_free(net->mcs); + + if (net->nego != NULL) + nego_free(net->nego); + + if (net->license != NULL) + license_free(net->license); + + xfree(net); + } +} diff --git a/libfreerdp-core/network.h b/libfreerdp-core/network.h new file mode 100644 index 0000000..01a1796 --- /dev/null +++ b/libfreerdp-core/network.h @@ -0,0 +1,73 @@ +/* + FreeRDP: A Remote Desktop Protocol client. + Network Transport Abstraction Layer + + Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com> + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef __NETWORK_H +#define __NETWORK_H + +#include <freerdp/freerdp.h> +#include <freerdp/types/ui.h> + +#include "tcp.h" +#include "tls.h" +#include "iso.h" +#include "mcs.h" +#include "rdp.h" +#include "security.h" +#include "stream.h" +#include "credssp.h" +#include "license.h" + +struct rdp_network +{ + int port; + char* server; + char* username; + struct stream in; + struct stream out; + int tls_connected; + struct _NEGO * nego; + struct rdp_rdp * rdp; + struct rdp_tcp * tcp; + struct rdp_sec * sec; + struct rdp_tls * tls; + struct rdp_iso * iso; + struct rdp_mcs * mcs; + struct rdp_credssp * credssp; + struct rdp_license * license; +}; +typedef struct rdp_network rdpNetwork; + +STREAM +network_stream_init(rdpNetwork * net, uint32 min_size); +RD_BOOL +network_connect(rdpNetwork * net, char* server, char* username, int port); +void +network_disconnect(rdpNetwork * net); + +void +network_send(rdpNetwork * net, STREAM s); +STREAM +network_recv(rdpNetwork * net, STREAM s, uint32 length); + +rdpNetwork* +network_new(rdpRdp * rdp); +void +network_free(rdpNetwork * net); + +#endif /* __NETWORK_H */ diff --git a/libfreerdp-core/ntlmssp.h b/libfreerdp-core/ntlmssp.h index 2bc7ed1..4b5f3f0 100644 --- a/libfreerdp-core/ntlmssp.h +++ b/libfreerdp-core/ntlmssp.h @@ -20,7 +20,7 @@ #ifndef __NTLMSSP_H #define __NTLMSSP_H -#include "secure.h" +#include "security.h" #include "credssp.h" #include <freerdp/utils/debug.h> diff --git a/libfreerdp-core/rail.c b/libfreerdp-core/rail.c index 5f232b6..a7bf8d9 100644 --- a/libfreerdp-core/rail.c +++ b/libfreerdp-core/rail.c @@ -19,7 +19,7 @@ #include "frdp.h" #include "rdp.h" -#include "secure.h" +#include "security.h" #include <freerdp/rdpset.h> #include <freerdp/utils/memory.h> diff --git a/libfreerdp-core/rdp.c b/libfreerdp-core/rdp.c index 701fddf..d3fa364 100644 --- a/libfreerdp-core/rdp.c +++ b/libfreerdp-core/rdp.c @@ -26,7 +26,7 @@ #include "iso.h" #include "tcp.h" #include "mcs.h" -#include "secure.h" +#include "security.h" #include "rail.h" #include "capabilities.h" #include "orders.h" @@ -35,7 +35,9 @@ #include "bitmap.h" #include "ext.h" #include "surface.h" +#include "network.h" #include <freerdp/freerdp.h> +#include <freerdp/utils/hexdump.h> #include "rdp.h" @@ -152,7 +154,7 @@ rdp_init_data(rdpRdp * rdp, int maxlen) uint32 sec_flags; - if (rdp->sec->tls_connected) + if (rdp->net->tls_connected) sec_flags = 0; else sec_flags = rdp->settings->encryption ? SEC_ENCRYPT : 0; @@ -185,7 +187,7 @@ rdp_send_data(rdpRdp * rdp, STREAM s, uint8 data_pdu_type) /* Share Control Header */ out_uint16_le(s, length); /* totalLength */ out_uint16_le(s, (RDP_PDU_DATA | 0x10)); /* pduType */ - out_uint16_le(s, (rdp->sec->mcs->mcs_userid + 1001)); /* PDUSource */ + out_uint16_le(s, (rdp->net->mcs->mcs_userid + 1001)); /* PDUSource */ /* Share Data Header */ out_uint32_le(s, rdp->rdp_shareid); /* shareId */ @@ -351,7 +353,7 @@ rdp_send_client_info(rdpRdp * rdp, uint32 flags, char *domain_name, userName = freerdp_uniconv_out(rdp->uniconv, username, &cbUserName); alternateShell = freerdp_uniconv_out(rdp->uniconv, shell, &cbAlternateShell); workingDir = freerdp_uniconv_out(rdp->uniconv, dir, &cbWorkingDir); - clientAddress = freerdp_uniconv_out(rdp->uniconv, tcp_get_address(rdp->sec->mcs->iso->tcp), &cbClientAddress); + clientAddress = freerdp_uniconv_out(rdp->uniconv, tcp_get_address(rdp->net->tcp), &cbClientAddress); clientDir = freerdp_uniconv_out(rdp->uniconv, dll, &cbClientDir); /* client working directory OR binary name */ length = 8 + (5 * 4) + cbDomain + cbUserName + cbPassword + cbAlternateShell + cbWorkingDir; @@ -763,7 +765,7 @@ rdp_send_confirm_active(rdpRdp * rdp) s_mark_end(caps); caplen = (int) (caps->end - caps->data); - if (rdp->sec->tls_connected) + if (rdp->net->tls_connected) sec_flags = 0; else sec_flags = rdp->settings->encryption ? SEC_ENCRYPT : 0; @@ -774,7 +776,7 @@ rdp_send_confirm_active(rdpRdp * rdp) /* share control header */ out_uint16_le(s, length); out_uint16_le(s, (RDP_PDU_CONFIRM_ACTIVE | 0x10)); /* Version 1 */ - out_uint16_le(s, (rdp->sec->mcs->mcs_userid + 1001)); + out_uint16_le(s, (rdp->net->mcs->mcs_userid + 1001)); out_uint32_le(s, rdp->rdp_shareid); /* sharedId */ /* originatorId must be set to the server channel ID @@ -1041,6 +1043,7 @@ process_system_pointer_pdu(rdpRdp * rdp, STREAM s) default: ui_unimpl(rdp->inst, "Unknown System Pointer message 0x%x\n", system_pointer_type); + break; } } @@ -1090,6 +1093,7 @@ process_pointer_pdu(rdpRdp * rdp, STREAM s) default: ui_unimpl(rdp->inst, "Unknown Pointer message 0x%x\n", message_type); + break; } } @@ -1262,6 +1266,7 @@ process_update_pdu(rdpRdp * rdp, STREAM s) default: ui_unimpl(rdp->inst, "Unknown update pdu type 0x%x\n", update_type); + break; } ui_end_update(rdp->inst); } @@ -1359,6 +1364,7 @@ process_data_pdu(rdpRdp * rdp, STREAM s) default: ui_unimpl(rdp->inst, "Unknown data PDU type 0x%x\n", pduType2); + break; } return False; } @@ -1410,7 +1416,7 @@ process_redirect_pdu(rdpRdp * rdp, STREAM s) rdp->redirect_routingtoken = xmalloc_in_len32_data(rdp, s, &rdp->redirect_routingtoken_len); printf("redirect_cookie_len: %d\n", rdp->redirect_routingtoken_len); - hexdump((void*)rdp->redirect_routingtoken, rdp->redirect_routingtoken_len); + freerdp_hexdump((uint8*)rdp->redirect_routingtoken, rdp->redirect_routingtoken_len); } if (redirFlags & LB_USERNAME) { @@ -1444,7 +1450,7 @@ process_redirect_pdu(rdpRdp * rdp, STREAM s) rdp->redirect_target_net_addresses = xmalloc_in_len32_data(rdp, s, &rdp->redirect_target_net_addresses_len); printf("redirect_target_net_addresses_len: %d\n", rdp->redirect_target_net_addresses_len); - hexdump((void*)rdp->redirect_target_net_addresses, rdp->redirect_target_net_addresses_len); + freerdp_hexdump((uint8*)rdp->redirect_target_net_addresses, rdp->redirect_target_net_addresses_len); } if (redirFlags & LB_NOREDIRECT) { @@ -1641,6 +1647,7 @@ rdp_loop(rdpRdp * rdp, RD_BOOL * deactivated) break; default: ui_unimpl(rdp->inst, "Unknown PDU type 0x%x", type); + break; } if (disc) return False; @@ -1671,7 +1678,7 @@ rdp_connect(rdpRdp * rdp) connect_flags |= INFO_REMOTECONSOLEAUDIO; } - if (!sec_connect(rdp->sec, rdp->settings->server, rdp->settings->username, rdp->settings->tcp_port_rdp)) + if (!network_connect(rdp->net, rdp->settings->server, rdp->settings->username, rdp->settings->tcp_port_rdp)) return False; password_encoded = freerdp_uniconv_out(rdp->uniconv, rdp->settings->password, &password_encoded_len); @@ -1679,7 +1686,7 @@ rdp_connect(rdpRdp * rdp) xfree(password_encoded); /* by setting encryption to False here, we have an encrypted login packet but unencrypted transfer of other packets */ - if (rdp->sec->tls_connected) + if (rdp->net->tls_connected) rdp->settings->encryption = 0; return True; @@ -1701,7 +1708,8 @@ rdp_reconnect(rdpRdp * rdp) sec_disconnect(rdp->sec); sec_free(rdp->sec); rdp->sec = sec_new(rdp); - if (!sec_connect(rdp->sec, server, username, rdp->settings->tcp_port_rdp)) + + if (!network_connect(rdp->net, server, username, rdp->settings->tcp_port_rdp)) return False; domain = rdp->redirect_domain ? rdp->redirect_domain : rdp->settings->domain; @@ -1713,11 +1721,14 @@ rdp_reconnect(rdpRdp * rdp) } else password = freerdp_uniconv_out(rdp->uniconv, rdp->settings->password, &password_len); + rdp_send_client_info(rdp, INFO_NORMALLOGON | INFO_AUTOLOGON, domain, username, password, password_len, rdp->settings->shell, rdp->settings->directory); + if (!rdp->redirect_password) xfree(password); + return True; } @@ -1745,6 +1756,7 @@ rdp_new(struct rdp_set *settings, struct rdp_inst *inst) self->buffer = xmalloc(self->buffer_size); memset(self->buffer, 0, self->buffer_size); self->sec = sec_new(self); + self->net = network_new(self); self->orders = orders_new(self); self->pcache = pcache_new(self); self->cache = cache_new(self); diff --git a/libfreerdp-core/rdp.h b/libfreerdp-core/rdp.h index 6ce5cad..1596776 100644 --- a/libfreerdp-core/rdp.h +++ b/libfreerdp-core/rdp.h @@ -65,11 +65,12 @@ struct rdp_rdp UNICONV *uniconv; RDPCOMP mppc_dict; struct rdp_sec * sec; - struct rdp_set * settings; // RDP settings + struct rdp_network * net; + struct rdp_set * settings; struct rdp_orders * orders; struct rdp_pcache * pcache; struct rdp_cache * cache; - struct rdp_app * app; // RemoteApp + struct rdp_app * app; struct rdp_ext * ext; /* Session Directory redirection */ int redirect; diff --git a/libfreerdp-core/secure.c b/libfreerdp-core/secure.c deleted file mode 100644 index 7be5cdc..0000000 --- a/libfreerdp-core/secure.c +++ /dev/null @@ -1,1200 +0,0 @@ -/* - FreeRDP: A Remote Desktop Protocol client. - RDP encryption and licensing - - Copyright (C) Matthew Chapman 1999-2008 - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#include "frdp.h" -#include "mcs.h" -#include "chan.h" -#include "license.h" -#include "rdp.h" -#include "iso.h" -#include "tcp.h" -#include <freerdp/rdpset.h> -#include <freerdp/utils/memory.h> - -#ifndef DISABLE_TLS -#include "tls.h" -#include "credssp.h" -#endif - -#include "secure.h" - -static RD_BOOL sec_global_initialized = False; - -RD_BOOL -sec_global_init(void) -{ - if (!sec_global_initialized) - { - sec_global_initialized = crypto_global_init(); - } - return sec_global_initialized; -} - -void -sec_global_finish(void) -{ - crypto_global_finish(); - sec_global_initialized = False; -} - -/* these are read only */ -static uint8 pad_54[40] = { - 54, 54, 54, 54, 54, 54, 54, 54, - 54, 54, 54, 54, 54, 54, 54, 54, - 54, 54, 54, 54, 54, 54, 54, 54, - 54, 54, 54, 54, 54, 54, 54, 54, - 54, 54, 54, 54, 54, 54, 54, 54 -}; - -static uint8 pad_92[48] = { - 92, 92, 92, 92, 92, 92, 92, 92, - 92, 92, 92, 92, 92, 92, 92, 92, - 92, 92, 92, 92, 92, 92, 92, 92, - 92, 92, 92, 92, 92, 92, 92, 92, - 92, 92, 92, 92, 92, 92, 92, 92, - 92, 92, 92, 92, 92, 92, 92, 92 -}; - -/* - * 48-byte transformation used to generate master secret (6.1) and key material (6.2.2). - * Both SHA1 and MD5 algorithms are used. - */ -void -sec_hash_48(uint8 * out, uint8 * in, uint8 * salt1, uint8 * salt2, uint8 salt) -{ - int i; - uint8 pad[4]; - uint8 shasig[20]; - CryptoMd5 md5; - CryptoSha1 sha1; - - for (i = 0; i < 3; i++) - { - memset(pad, salt + i, i + 1); - - sha1 = crypto_sha1_init(); - crypto_sha1_update(sha1, pad, i + 1); - crypto_sha1_update(sha1, in, 48); - crypto_sha1_update(sha1, salt1, 32); - crypto_sha1_update(sha1, salt2, 32); - crypto_sha1_final(sha1, shasig); - - md5 = crypto_md5_init(); - crypto_md5_update(md5, in, 48); - crypto_md5_update(md5, shasig, 20); - crypto_md5_final(md5, &out[i * 16]); - } -} - -/* - * 16-byte transformation used to generate export keys (6.2.2). - */ -void -sec_hash_16(uint8 * out, uint8 * in, uint8 * salt1, uint8 * salt2) -{ - CryptoMd5 md5 = crypto_md5_init(); - crypto_md5_update(md5, in, 16); - crypto_md5_update(md5, salt1, 32); - crypto_md5_update(md5, salt2, 32); - crypto_md5_final(md5, out); -} - -/* Reduce key entropy from 64 to 40 bits */ -static void -sec_make_40bit(uint8 * key) -{ - key[0] = 0xd1; - key[1] = 0x26; - key[2] = 0x9e; -} - -/* Generate encryption keys given client and server randoms */ -static void -sec_generate_keys(rdpSec * sec, uint8 * client_random, uint8 * server_random, int rc4_key_size) -{ - uint8 pre_master_secret[48]; - uint8 master_secret[48]; - uint8 key_block[48]; - - /* Construct pre-master secret */ - memcpy(pre_master_secret, client_random, 24); - memcpy(pre_master_secret + 24, server_random, 24); - - /* Generate master secret and then key material */ - sec_hash_48(master_secret, pre_master_secret, client_random, server_random, 'A'); - sec_hash_48(key_block, master_secret, client_random, server_random, 'X'); - - /* First 16 bytes of key material is MAC secret */ - memcpy(sec->sec_sign_key, key_block, 16); - - /* Generate export keys from next two blocks of 16 bytes */ - sec_hash_16(sec->sec_decrypt_key, &key_block[16], client_random, server_random); - sec_hash_16(sec->sec_encrypt_key, &key_block[32], client_random, server_random); - - if (rc4_key_size == 1) - { - DEBUG_SEC("40-bit encryption enabled"); - sec_make_40bit(sec->sec_sign_key); - sec_make_40bit(sec->sec_decrypt_key); - sec_make_40bit(sec->sec_encrypt_key); - sec->rc4_key_len = 8; - } - else - { - DEBUG_SEC("rc_4_key_size == %d, 128-bit encryption enabled", rc4_key_size); - sec->rc4_key_len = 16; - } - - /* Save initial RC4 keys as update keys */ - memcpy(sec->sec_decrypt_update_key, sec->sec_decrypt_key, 16); - memcpy(sec->sec_encrypt_update_key, sec->sec_encrypt_key, 16); - - /* Initialize RC4 state arrays */ - sec->rc4_decrypt_key = crypto_rc4_init(sec->sec_decrypt_key, sec->rc4_key_len); - sec->rc4_encrypt_key = crypto_rc4_init(sec->sec_encrypt_key, sec->rc4_key_len); -} - -/* Output a uint32 into a buffer (little-endian) */ -void -buf_out_uint32(uint8 * buffer, uint32 value) -{ - buffer[0] = (value) & 0xff; - buffer[1] = (value >> 8) & 0xff; - buffer[2] = (value >> 16) & 0xff; - buffer[3] = (value >> 24) & 0xff; -} - -/* Generate a MAC hash (5.2.3.1), using a combination of SHA1 and MD5 */ -void -sec_sign(uint8 * signature, int siglen, uint8 * session_key, int keylen, uint8 * data, int datalen) -{ - uint8 shasig[20]; - uint8 md5sig[16]; - uint8 lenhdr[4]; - CryptoSha1 sha1; - CryptoMd5 md5; - - buf_out_uint32(lenhdr, datalen); - - sha1 = crypto_sha1_init(); - crypto_sha1_update(sha1, session_key, keylen); - crypto_sha1_update(sha1, pad_54, 40); - crypto_sha1_update(sha1, lenhdr, 4); - crypto_sha1_update(sha1, data, datalen); - crypto_sha1_final(sha1, shasig); - - md5 = crypto_md5_init(); - crypto_md5_update(md5, session_key, keylen); - crypto_md5_update(md5, pad_92, 48); - crypto_md5_update(md5, shasig, 20); - crypto_md5_final(md5, md5sig); - - memcpy(signature, md5sig, siglen); -} - -/* Update an encryption key */ -static void -sec_update(rdpSec * sec, uint8 * key, uint8 * update_key) -{ - uint8 shasig[20]; - CryptoSha1 sha1; - CryptoMd5 md5; - CryptoRc4 update; - - sha1 = crypto_sha1_init(); - crypto_sha1_update(sha1, update_key, sec->rc4_key_len); - crypto_sha1_update(sha1, pad_54, 40); - crypto_sha1_update(sha1, key, sec->rc4_key_len); - crypto_sha1_final(sha1, shasig); - - md5 = crypto_md5_init(); - crypto_md5_update(md5, update_key, sec->rc4_key_len); - crypto_md5_update(md5, pad_92, 48); - crypto_md5_update(md5, shasig, 20); - crypto_md5_final(md5, key); - - update = crypto_rc4_init(key, sec->rc4_key_len); - crypto_rc4(update, sec->rc4_key_len, key, key); - crypto_rc4_free(update); - - if (sec->rc4_key_len == 8) - sec_make_40bit(key); -} - -/* Encrypt data using RC4 */ -static void -sec_encrypt(rdpSec * sec, uint8 * data, int length) -{ - if (sec->sec_encrypt_use_count == 4096) - { - sec_update(sec, sec->sec_encrypt_key, sec->sec_encrypt_update_key); - crypto_rc4_free(sec->rc4_encrypt_key); - sec->rc4_encrypt_key = crypto_rc4_init(sec->sec_encrypt_key, sec->rc4_key_len); - sec->sec_encrypt_use_count = 0; - } - - crypto_rc4(sec->rc4_encrypt_key, length, data, data); - sec->sec_encrypt_use_count++; -} - -/* Decrypt data using RC4 */ -static void -sec_decrypt(rdpSec * sec, uint8 * data, int length) -{ -#ifndef DISABLE_TLS - if (sec->tls_connected) - return; -#endif - - if (sec->sec_decrypt_use_count == 4096) - { - sec_update(sec, sec->sec_decrypt_key, sec->sec_decrypt_update_key); - crypto_rc4_free(sec->rc4_decrypt_key); - sec->rc4_decrypt_key = crypto_rc4_init(sec->sec_decrypt_key, sec->rc4_key_len); - sec->sec_decrypt_use_count = 0; - } - - crypto_rc4(sec->rc4_decrypt_key, length, data, data); - sec->sec_decrypt_use_count++; -} - -/* Initialize secure transport packet */ -STREAM -sec_init(rdpSec * sec, uint32 flags, int maxlen) -{ - STREAM s; - int hdrlen; - - if (flags) - { - if (!(sec->license->license_issued)) - hdrlen = (flags & SEC_ENCRYPT) ? 12 : 4; - else - hdrlen = (flags & SEC_ENCRYPT) ? 12 : 0; - } - else - hdrlen = 0; - - s = mcs_init(sec->mcs, maxlen + hdrlen); - s_push_layer(s, sec_hdr, hdrlen); - - return s; -} - -/* Initialize fast path secure transport packet */ -STREAM -sec_fp_init(rdpSec * sec, uint32 flags, int maxlen) -{ - STREAM s; - int hdrlen; - - hdrlen = (flags & SEC_ENCRYPT) ? 8 : 0; - s = mcs_fp_init(sec->mcs, maxlen + hdrlen); - s_push_layer(s, sec_hdr, hdrlen); - - return s; -} - -/* Transmit secure transport packet over specified channel */ -void -sec_send_to_channel(rdpSec * sec, STREAM s, uint32 flags, uint16 channel) -{ - int datalen; - s_pop_layer(s, sec_hdr); - - if (flags) - { - /* Basic Security Header */ - if (!(sec->license->license_issued) || (flags & SEC_ENCRYPT)) - out_uint32_le(s, flags); /* flags */ - - if (flags & SEC_ENCRYPT) - { - flags &= ~SEC_ENCRYPT; - datalen = s->end - s->p - 8; - -#if WITH_DEBUG - DEBUG_SEC("Sending encrypted packet:"); - hexdump(s->p + 8, datalen); -#endif - - sec_sign(s->p, 8, sec->sec_sign_key, sec->rc4_key_len, s->p + 8, datalen); - sec_encrypt(sec, s->p + 8, datalen); - } - } - - mcs_send_to_channel(sec->mcs, s, channel); -} - -/* Transmit secure transport packet */ - -void -sec_send(rdpSec * sec, STREAM s, uint32 flags) -{ - sec_send_to_channel(sec, s, flags, MCS_GLOBAL_CHANNEL); -} - -/* Transmit secure fast path packet */ -void -sec_fp_send(rdpSec * sec, STREAM s, uint32 flags) -{ - int datalen; - s_pop_layer(s, sec_hdr); - if (flags & SEC_ENCRYPT) - { - datalen = ((int) (s->end - s->p)) - 8; - sec_sign(s->p, 8, sec->sec_sign_key, sec->rc4_key_len, s->p + 8, datalen); - sec_encrypt(sec, s->p + 8, datalen); - } - mcs_fp_send(sec->mcs, s, flags); -} - -/* Transfer the client random to the server */ -static void -sec_establish_key(rdpSec * sec) -{ - uint32 length = sec->server_public_key_len + SEC_PADDING_SIZE; - uint32 flags = SEC_EXCHANGE_PKT; - STREAM s; - - s = sec_init(sec, flags, length + 4); - - out_uint32_le(s, length); - out_uint8p(s, sec->sec_crypted_random, sec->server_public_key_len); - out_uint8s(s, SEC_PADDING_SIZE); - - s_mark_end(s); - sec_send(sec, s, flags); -} - -static void -sec_out_client_core_data(rdpSec * sec, rdpSet * settings, STREAM s) -{ - char * p; - size_t len; - uint16 highColorDepth; - uint16 supportedColorDepths; - uint16 earlyCapabilityFlags; - int con_type; - - out_uint16_le(s, UDH_CS_CORE); /* User Data Header type */ - out_uint16_le(s, 216); /* total length */ - - out_uint32_le(s, settings->rdp_version >= 5 ? 0x00080004 : 0x00080001); /* client version */ - out_uint16_le(s, settings->width); /* desktopWidth */ - out_uint16_le(s, settings->height); /* desktopHeight */ - out_uint16_le(s, RNS_UD_COLOR_8BPP); /* colorDepth, ignored because of postBeta2ColorDepth */ - out_uint16_le(s, RNS_UD_SAS_DEL); /* SASSequence (Secure Access Sequence) */ - out_uint32_le(s, settings->keyboard_layout); /* keyboardLayout */ - out_uint32_le(s, 2600); /* clientBuild */ - - /* Unicode name of client, truncated to 15 characters */ - p = freerdp_uniconv_out(sec->rdp->uniconv, settings->hostname, &len); - if (len > 30) - { - len = 30; - p[len] = 0; - p[len + 1] = 0; - } - out_uint8a(s, p, len + 2); - out_uint8s(s, 32 - len - 2); - xfree(p); - - out_uint32_le(s, settings->keyboard_type); /* keyboardType */ - out_uint32_le(s, settings->keyboard_subtype); /* keyboardSubType */ - out_uint32_le(s, settings->keyboard_functionkeys); /* keyboardFunctionKey */ - - /* Input Method Editor (IME) file name associated with the input locale. - Up to 31 Unicode characters plus a NULL terminator */ - /* FIXME: populate this field with real data */ - out_uint8s(s, 64); /* imeFileName */ - - out_uint16_le(s, RNS_UD_COLOR_8BPP); /* postBeta2ColorDepth */ - out_uint16_le(s, 1); /* clientProductID */ - out_uint32_le(s, 0); /* serialNumber (should be initialized to 0) */ - - highColorDepth = MIN(settings->server_depth, 24); /* 32 must be reported as 24 and RNS_UD_CS_WANT_32BPP_SESSION */ - out_uint16_le(s, highColorDepth); /* (requested) highColorDepth */ - - supportedColorDepths = RNS_UD_32BPP_SUPPORT | RNS_UD_24BPP_SUPPORT | RNS_UD_16BPP_SUPPORT | RNS_UD_15BPP_SUPPORT; - out_uint16_le(s, supportedColorDepths); /* supportedColorDepths */ - - con_type = 0; - earlyCapabilityFlags = RNS_UD_CS_SUPPORT_ERRINFO_PDU; - if (sec->rdp->settings->performanceflags == PERF_FLAG_NONE) - { - earlyCapabilityFlags |= RNS_UD_CS_VALID_CONNECTION_TYPE; - con_type = CONNECTION_TYPE_LAN; - } - if (settings->server_depth == 32) - earlyCapabilityFlags |= RNS_UD_CS_WANT_32BPP_SESSION; - - out_uint16_le(s, earlyCapabilityFlags); /* earlyCapabilityFlags */ - out_uint8s(s, 64); /* clientDigProductId (64 bytes) */ - /* connectionType, only valid when RNS_UD_CS_VALID_CONNECTION_TYPE - is set in earlyCapabilityFlags */ - out_uint8(s, con_type); - out_uint8(s, 0); /* pad1octet */ - out_uint32_le(s, sec->mcs->iso->nego->selected_protocol); /* serverSelectedProtocol */ -} - -static void -sec_out_client_security_data(rdpSec * sec, rdpSet * settings, STREAM s) -{ - uint16 encryptionMethods = 0; - - out_uint16_le(s, UDH_CS_SECURITY); /* User Data Header type */ - out_uint16_le(s, 12); /* total length */ - - if (settings->encryption || sec->tls_connected) - encryptionMethods = ENCRYPTION_40BIT_FLAG | ENCRYPTION_128BIT_FLAG; - - out_uint32_le(s, encryptionMethods); /* encryptionMethods */ - out_uint32_le(s, 0); /* extEncryptionMethods */ -} - -static void -sec_out_client_network_data(rdpSec * sec, rdpSet * settings, STREAM s) -{ - int i; - - DEBUG_SEC("num_channels is %d", settings->num_channels); - if (settings->num_channels > 0) - { - out_uint16_le(s, UDH_CS_NET); /* User Data Header type */ - out_uint16_le(s, settings->num_channels * 12 + 8); /* total length */ - - out_uint32_le(s, settings->num_channels); /* channelCount */ - for (i = 0; i < settings->num_channels; i++) - { - DEBUG_SEC("Requesting channel %s", settings->channels[i].name); - out_uint8a(s, settings->channels[i].name, 8); /* name (8 bytes) 7 characters with null terminator */ - out_uint32_le(s, settings->channels[i].flags); /* options (4 bytes) */ - } - } -} - -static void -sec_out_client_cluster_data(rdpSec * sec, rdpSet * settings, STREAM s) -{ - out_uint16_le(s, UDH_CS_CLUSTER); /* User Data Header type */ - out_uint16_le(s, 12); /* total length */ - - out_uint32_le(s, (settings->console_session || sec->rdp->redirect_session_id) ? - REDIRECTED_SESSIONID_FIELD_VALID | REDIRECTION_SUPPORTED | REDIRECTION_VERSION4 : - REDIRECTION_SUPPORTED | REDIRECTION_VERSION4); /* flags */ - - out_uint32_le(s, sec->rdp->redirect_session_id); /* RedirectedSessionID */ -} - -static void -sec_out_client_monitor_data(rdpSec * sec, rdpSet * settings, STREAM s) -{ - int length, n; - DEBUG_SEC("Setting monitor data... num_monitors: %d", settings->num_monitors); - if (settings->num_monitors <= 1) - return; - - DEBUG_SEC("Setting monitor data..."); - out_uint16_le(s, UDH_CS_MONITOR); /* User Data Header type */ - - length = 12 + (20 * settings->num_monitors); - out_uint16_le(s, length); - out_uint32_le(s, 0); /* flags (unused) */ - out_uint32_le(s, settings->num_monitors); /* monitorCount */ - for (n = 0; n < settings->num_monitors; n++) - { - out_uint32_le(s, settings->monitors[n].x); /* left */ - out_uint32_le(s, settings->monitors[n].y); /* top */ - out_uint32_le(s, settings->monitors[n].x + - settings->monitors[n].width-1); /* right */ - out_uint32_le(s, settings->monitors[n].y + - settings->monitors[n].height-1); /* bottom */ - out_uint32_le(s, settings->monitors[n].is_primary ? 1 : 0); /* isPrimary */ - } -} - -void -sec_out_gcc_conference_create_request(rdpSec * sec, STREAM s) -{ - int length; - rdpSet * settings = sec->rdp->settings; - - /* See ITU-T Rec. T.124 (02/98) Generic Conference Control */ - - /* the part before userData is of a fixed size, making things convenient */ - s->p = s->data + 23; - sec_out_client_core_data(sec, settings, s); - sec_out_client_cluster_data(sec, settings, s); - sec_out_client_security_data(sec, settings, s); - sec_out_client_network_data(sec, settings, s); - sec_out_client_monitor_data(sec, settings, s); - length = (s->p - s->data) - 23; - s->p = s->data; - - /* t124Identifier = 0.0.20.124.0.1 */ - out_uint16_be(s, 5); - out_uint16_be(s, 0x14); - out_uint8(s, 0x7c); - out_uint16_be(s, 1); - - /* connectPDU octet string */ - out_uint16_be(s, ((length + 14) | 0x8000)); /* connectPDU length in two bytes*/ - - /* connectPDU content is ConnectGCCPDU PER encoded: */ - out_uint16_be(s, 8); /* ConferenceCreateRequest ... */ - out_uint16_be(s, 16); - out_uint8(s, 0); - out_uint16_le(s, 0xC001); /* userData key is h221NonStandard */ - out_uint8(s, 0); /* 4 bytes: */ - out_uint32_le(s, 0x61637544); /* "Duca" */ - out_uint16_be(s, (length | 0x8000)); /* userData value length in two bytes */ - s->p = s->data + length + 23; /* userData (outputted earlier) */ - - s_mark_end(s); -} - -static void -revcpy(uint8 * out, uint8 * in, int len) -{ - int i; - in += len; - for (i = 0; i < len; i++) - { - *out++ = *--in; - } -} - -/* Parse a Server Proprietary Certificate RSA Public Key */ -static RD_BOOL -sec_parse_public_key(rdpSec * sec, STREAM s, uint32 len, uint8 * modulus, uint8 * exponent) -{ - uint32 magic; - uint32 modulus_len; - - in_uint32_le(s, magic); - if (magic != SEC_RSA_MAGIC) - { - ui_error(sec->rdp->inst, "RSA magic 0x%x\n", magic); - return False; - } - - in_uint32_le(s, modulus_len); - if (4 + 4 + 4 + 4 + SEC_EXPONENT_SIZE + modulus_len != len) - { - ui_error(sec->rdp->inst, "Inconsistent Server Proprietary Certificate public key size\n"); - return False; - } - modulus_len -= SEC_PADDING_SIZE; - if ((modulus_len < SEC_MODULUS_SIZE) || (modulus_len > SEC_MAX_MODULUS_SIZE)) - { - ui_error(sec->rdp->inst, "Bad Server Proprietary Certificate public key size (%u bits)\n", modulus_len * 8); - return False; - } - - in_uint8s(s, 4); /* modulus_bits - must match modulus_len */ - in_uint8s(s, 4); /* datalen - how much data can be encoded */ - revcpy(exponent, s->p, SEC_EXPONENT_SIZE); - in_uint8s(s, SEC_EXPONENT_SIZE); - revcpy(modulus, s->p, modulus_len); - in_uint8s(s, modulus_len); - in_uint8s(s, SEC_PADDING_SIZE); /* zero padding - included in modulus_len but not in modulus_bits */ - sec->server_public_key_len = modulus_len; - - return s_check(s); -} - -/* Parse a Proprietary Certificate signature */ -static RD_BOOL -sec_parse_public_sig(STREAM s, uint32 len) -{ - /* The Proprietary Certificate signature uses a static published private key. - * That is completely nonsense, so we won't bother checking it. */ - - in_uint8s(s, len); - return len == 72; -} - -/* Parse Server Security Data */ -static RD_BOOL -sec_parse_server_security_data(rdpSec * sec, STREAM s, uint32 * encryptionMethod, uint8 server_random[SEC_RANDOM_SIZE], uint8 * modulus, uint8 * exponent) -{ - uint32 encryptionLevel; - uint32 serverRandomLen; - uint32 serverCertLen; - uint32 certChainVersion; - uint32 dwVersion; - - in_uint32_le(s, *encryptionMethod); /* 1 = 40-bit, 2 = 128-bit, 0 for TLS/CredSSP */ - in_uint32_le(s, encryptionLevel); /* 1 = low, 2 = client compatible, 3 = high */ - if (encryptionLevel == 0) /* no encryption */ - return False; - in_uint32_le(s, serverRandomLen); - in_uint32_le(s, serverCertLen); - - if (serverRandomLen != SEC_RANDOM_SIZE) - { - ui_error(sec->rdp->inst, "random len %d, expected %d\n", serverRandomLen, SEC_RANDOM_SIZE); - return False; - } - - in_uint8a(s, server_random, SEC_RANDOM_SIZE); - - /* Server Certificate: */ - in_uint32_le(s, dwVersion); /* bit 0x80000000 = temporary certificate */ - certChainVersion = dwVersion & 0x7FFFFFFF; - - if (certChainVersion == 1) /* Server Proprietary Certificate */ - { - uint16 wPublicKeyBlobType, wPublicKeyBlobLen; - uint16 wSignatureBlobType, wSignatureBlobLen; - - DEBUG_SEC("We're going for a Server Proprietary Certificate (no TS license)"); - in_uint8s(s, 4); /* dwSigAlgId must be 1 (SIGNATURE_ALG_RSA) */ - in_uint8s(s, 4); /* dwKeyAlgId must be 1 (KEY_EXCHANGE_ALG_RSA ) */ - - in_uint16_le(s, wPublicKeyBlobType); - if (wPublicKeyBlobType != BB_RSA_KEY_BLOB) - return False; - - in_uint16_le(s, wPublicKeyBlobLen); - - if (!sec_parse_public_key(sec, s, wPublicKeyBlobLen, modulus, exponent)) - return False; - - in_uint16_le(s, wSignatureBlobType); - if (wSignatureBlobType != BB_RSA_SIGNATURE_BLOB) - return False; - - in_uint16_le(s, wSignatureBlobLen); - if (!sec_parse_public_sig(s, wSignatureBlobLen)) - return False; - } - else if (certChainVersion == 2) /* X.509 */ - { - uint32 cert_total_count, cert_counter; - uint32 license_cert_len, ts_cert_len; - CryptoCert license_cert, ts_cert; - - DEBUG_SEC("We're going for a X.509 Certificate (TS license)"); - in_uint32_le(s, cert_total_count); /* Number of certificates */ - DEBUG_SEC("Cert chain length: %d", cert_total_count); - if (cert_total_count < 2) - { - ui_error(sec->rdp->inst, "Server didn't send enough X509 certificates\n"); - return False; - } - /* X.509 Certificate Chain: */ - /* Only the 2 last certificates in chain are _really_ interesting */ - for (cert_counter=0; cert_counter < cert_total_count - 2; cert_counter++) - { - uint32 ignorelen; - CryptoCert ignorecert; - - DEBUG_SEC("Ignoring cert: %d", cert_counter); - in_uint32_le(s, ignorelen); - DEBUG_SEC("Ignored Certificate length is %d", ignorelen); - ignorecert = crypto_cert_read(s->p, ignorelen); - in_uint8s(s, ignorelen); - if (ignorecert == NULL) - { - ui_error(sec->rdp->inst, "Couldn't read certificate %d from server certificate chain\n", cert_counter); - return False; - } - -#ifdef WITH_DEBUG_SEC - DEBUG_SEC("cert #%d (ignored):", cert_counter); - crypto_cert_print_fp(stdout, ignorecert); -#endif - /* TODO: Verify the certificate chain all the way from CA root to prevent MITM attacks */ - crypto_cert_free(ignorecert); - } - /* The second to last certificate is the license server */ - in_uint32_le(s, license_cert_len); - DEBUG_SEC("License Server Certificate length is %d", license_cert_len); - license_cert = crypto_cert_read(s->p, license_cert_len); - in_uint8s(s, license_cert_len); - if (NULL == license_cert) - { - ui_error(sec->rdp->inst, "Couldn't load License Server Certificate from server\n"); - return False; - } -#ifdef WITH_DEBUG_SEC - crypto_cert_print_fp(stdout, license_cert); -#endif - /* The last certificate is the Terminal Server */ - in_uint32_le(s, ts_cert_len); - DEBUG_SEC("TS Certificate length is %d", ts_cert_len); - ts_cert = crypto_cert_read(s->p, ts_cert_len); - in_uint8s(s, ts_cert_len); - if (NULL == ts_cert) - { - crypto_cert_free(license_cert); - ui_error(sec->rdp->inst, "Couldn't load TS Certificate from server\n"); - return False; - } -#ifdef WITH_DEBUG_SEC - crypto_cert_print_fp(stdout, ts_cert); -#endif - if (!crypto_cert_verify(ts_cert, license_cert)) - { - crypto_cert_free(ts_cert); - crypto_cert_free(license_cert); - ui_error(sec->rdp->inst, "TS Certificate not signed with License Certificate\n"); - return False; - } - crypto_cert_free(license_cert); - - if (crypto_cert_get_pub_exp_mod(ts_cert, &(sec->server_public_key_len), - exponent, SEC_EXPONENT_SIZE, modulus, SEC_MAX_MODULUS_SIZE) != 0) - { - ui_error(sec->rdp->inst, "Problem extracting RSA key from TS Certificate\n"); - crypto_cert_free(ts_cert); - return False; - } - crypto_cert_free(ts_cert); - if ((sec->server_public_key_len < SEC_MODULUS_SIZE) || - (sec->server_public_key_len > SEC_MAX_MODULUS_SIZE)) - { - ui_error(sec->rdp->inst, "Bad TS Certificate public key size (%u bits)\n", - sec->server_public_key_len * 8); - return False; - } - in_uint8s(s, 8 + 4 * cert_total_count); /* Padding */ - } - else - { - ui_error(sec->rdp->inst, "Invalid cert chain version\n"); - return False; - } - - return s_check_end(s); -} - -/* Process Server Security Data */ -static void -sec_process_server_security_data(rdpSec * sec, STREAM s) -{ - uint32 rc4_key_size; - uint8 server_random[SEC_RANDOM_SIZE]; - uint8 client_random[SEC_RANDOM_SIZE]; - uint8 modulus[SEC_MAX_MODULUS_SIZE]; - uint8 exponent[SEC_EXPONENT_SIZE]; - uint8 client_random_rev[SEC_RANDOM_SIZE]; - uint8 crypted_random_rev[SEC_MAX_MODULUS_SIZE]; - - memset(modulus, 0, sizeof(modulus)); - memset(exponent, 0, sizeof(exponent)); - if (!sec_parse_server_security_data(sec, s, &rc4_key_size, server_random, modulus, exponent)) - { - /* encryptionMethod (rc4_key_size) = 0 means TLS */ - if (rc4_key_size > 0) - { - DEBUG_SEC("Failed to parse crypt info"); - } - return; - } - - DEBUG_SEC("Generating client random"); - generate_random(client_random); - revcpy(client_random_rev, client_random, SEC_RANDOM_SIZE); - crypto_rsa_encrypt(SEC_RANDOM_SIZE, client_random_rev, crypted_random_rev, - sec->server_public_key_len, modulus, exponent); - revcpy(sec->sec_crypted_random, crypted_random_rev, sec->server_public_key_len); - sec_generate_keys(sec, client_random, server_random, rc4_key_size); -} - -/* Process Server Core Data */ -static void -sec_process_server_core_data(rdpSec * sec, STREAM s, uint16 length) -{ - uint32 server_rdp_version, clientRequestedProtocols; - in_uint32_le(s, server_rdp_version); - - if(server_rdp_version == 0x00080001) - { - sec->rdp->settings->rdp_version = 4; - sec->rdp->settings->server_depth = 8; - } - else if(server_rdp_version == 0x00080004) - { - sec->rdp->settings->rdp_version = 5; /* FIXME: We can't just upgrade the RDP version! */ - } - else - { - ui_error(sec->rdp->inst, "Invalid server rdp version %ul\n", server_rdp_version); - } - - DEBUG_SEC("Server RDP version is %d", sec->rdp->settings->rdp_version); - if (length >= 12) - { - in_uint32_le(s, clientRequestedProtocols); - } -} - -/* Process Server Network Data */ -static void -sec_process_server_network_data(rdpSec * sec, STREAM s) -{ - int i; - uint16 MCSChannelId; - uint16 channelCount; - - in_uint16_le(s, MCSChannelId); /* MCSChannelId */ - if (MCSChannelId != MCS_GLOBAL_CHANNEL) - ui_error(sec->rdp->inst, "expected IO channel 0x%x=%d but got 0x%x=%d\n", - MCS_GLOBAL_CHANNEL, MCS_GLOBAL_CHANNEL, MCSChannelId, MCSChannelId); - in_uint16_le(s, channelCount); /* channelCount */ - - /* TODO: Check that it matches rdp->settings->num_channels */ - if (channelCount != sec->rdp->settings->num_channels) - { - ui_error(sec->rdp->inst, "client requested %d channels, server replied with %d channels", - sec->rdp->settings->num_channels, channelCount); - } - - /* channelIdArray */ - for (i = 0; i < channelCount; i++) - { - uint16 channelId; - in_uint16_le(s, channelId); /* Channel ID allocated to requested channel number i */ - - /* TODO: Assign channel ids here instead of in freerdp.c l_rdp_connect */ - if (channelId != sec->rdp->settings->channels[i].chan_id) - { - ui_error(sec->rdp->inst, "channel %d is %d but should have been %d\n", - i, channelId, sec->rdp->settings->channels[i].chan_id); - } - } - - if (channelCount % 2 == 1) - in_uint8s(s, 2); /* Padding */ -} - -/* Process connect response data blob */ -void -sec_process_mcs_data(rdpSec * sec, STREAM s) -{ - uint8 byte; - uint16 type; - uint16 length; - uint16 totalLength; - uint8 *next_tag; - - in_uint8s(s, 21); /* TODO: T.124 ConferenceCreateResponse userData with key h221NonStandard McDn */ - - in_uint8(s, byte); - totalLength = (uint16) byte; - - if (byte & 0x80) - { - totalLength &= ~0x80; - totalLength <<= 8; - in_uint8(s, byte); - totalLength += (uint16) byte; - } - - while (s->p < s->end) - { - in_uint16_le(s, type); - in_uint16_le(s, length); - - if (length <= 4) - return; - - next_tag = s->p + length - 4; - - switch (type) - { - case UDH_SC_CORE: /* Server Core Data */ - sec_process_server_core_data(sec, s, length); - break; - - case UDH_SC_NET: /* Server Network Data */ - sec_process_server_network_data(sec, s); - break; - - case UDH_SC_SECURITY: /* Server Security Data */ - sec_process_server_security_data(sec, s); - break; - } - - s->p = next_tag; - } -} - -/* Receive secure transport packet - * Some package types are processed internally. - * If s is returned a package of *type must be processed by the caller */ -STREAM -sec_recv(rdpSec * sec, secRecvType * type) -{ - STREAM s; - uint16 channel; - uint32 sec_flags; - isoRecvType iso_type; - - while ((s = mcs_recv(sec->mcs, &iso_type, &channel)) != NULL) - { - if ((iso_type == ISO_RECV_FAST_PATH) || - (iso_type == ISO_RECV_FAST_PATH_ENCRYPTED)) - { - *type = SEC_RECV_FAST_PATH; - if (iso_type == ISO_RECV_FAST_PATH_ENCRYPTED) - { - in_uint8s(s, 8); /* dataSignature */ /* TODO: Check signature! */ - sec_decrypt(sec, s->p, s->end - s->p); - } - return s; - } - if (iso_type != ISO_RECV_X224) - { - ui_error(sec->rdp->inst, "expected ISO_RECV_X224, got %d\n", iso_type); - return NULL; - } - if (sec->rdp->settings->encryption || !sec->license->license_issued) - { - /* basicSecurityHeader: */ - in_uint32_le(s, sec_flags); - - if ((sec_flags & SEC_ENCRYPT) || (sec_flags & SEC_REDIRECTION_PKT)) - { - in_uint8s(s, 8); /* dataSignature */ /* TODO: Check signature! */ - sec_decrypt(sec, s->p, s->end - s->p); - } - - if (sec_flags & SEC_LICENSE_PKT) - { - *type = SEC_RECV_LICENSE; - license_process(sec->license, s); - continue; - } - - if (sec_flags & SEC_REDIRECTION_PKT) - { - *type = SEC_RECV_REDIRECT; - return s; - } - } - - if (channel != MCS_GLOBAL_CHANNEL) - { - vchan_process(sec->mcs->chan, s, channel); - *type = SEC_RECV_IOCHANNEL; - return s; - } - *type = SEC_RECV_SHARE_CONTROL; - return s; - } - - return NULL; -} - -#ifndef DISABLE_TLS - -/* verify SSL/TLS connection integrity. 2 checks are carried out. First make sure that the - * certificate is assigned to the server we're connected to, and second make sure that the - * certificate is signed by a trusted certification authority - */ - -static RD_BOOL -sec_verify_tls(rdpSec * sec, const char * server) -{ - char * issuer; - char * subject; - char * fingerprint; - CryptoCert cert; - RD_BOOL verified = False; - -#ifdef _WIN32 - /* - * TODO: FIX ME! This is really bad, I know... - * There appears to be a buffer overflow only - * on Windows that affects this part of the code. - * Skipping it is a workaround, but it's obviously - * not a permanent "solution". - */ - return True; -#endif - - cert = tls_get_certificate(sec->tls); - - if (!cert) - { - goto exit; - } - - subject = crypto_cert_get_subject(cert); - issuer = crypto_cert_get_issuer(cert); - fingerprint = crypto_cert_get_fingerprint(cert); - - verified = tls_verify(sec->tls, server); - - if (verified != False) - verified = crypto_cert_verify_peer_identity(cert, server); - - verified = ui_check_certificate(sec->rdp->inst, fingerprint, subject, issuer, verified); - - xfree(fingerprint); - xfree(subject); - xfree(issuer); - -exit: - if (cert) - { - crypto_cert_free(cert); - cert = NULL; - } - - return verified; -} -#endif - -/* Establish a secure connection */ -RD_BOOL -sec_connect(rdpSec * sec, char *server, char *username, int port) -{ - NEGO *nego = sec->mcs->iso->nego; - - sec->license->license_issued = 0; - if (sec->rdp->settings->nla_security) - nego->enabled_protocols[PROTOCOL_NLA] = 1; - if (sec->rdp->settings->tls_security) - nego->enabled_protocols[PROTOCOL_TLS] = 1; - if (sec->rdp->settings->rdp_security) - nego->enabled_protocols[PROTOCOL_RDP] = 1; - - if (!iso_connect(sec->mcs->iso, server, username, port)) - return False; - -#ifndef DISABLE_TLS - if(nego->selected_protocol & PROTOCOL_NLA) - { - /* TLS with NLA was successfully negotiated */ - RD_BOOL status = 1; - printf("TLS encryption with NLA negotiated\n"); - sec->tls = tls_new(); - if (!tls_connect(sec->tls, sec->mcs->iso->tcp->sock)) - return False; - if (!sec_verify_tls(sec, server)) - return False; - sec->tls_connected = 1; - sec->rdp->settings->encryption = 0; - - if (!sec->rdp->settings->autologin) - if (!ui_authenticate(sec->rdp->inst)) - return False; - - sec->credssp = credssp_new(sec); - - if (credssp_authenticate(sec->credssp) < 0) - { - printf("Authentication failure, check credentials.\n" - "If credentials are valid, the NTLMSSP implementation may be to blame.\n"); - credssp_free(sec->credssp); - return 0; - } - - credssp_free(sec->credssp); - - status = mcs_connect(sec->mcs); - return status; - } - else if(nego->selected_protocol & PROTOCOL_TLS) - { - /* TLS without NLA was successfully negotiated */ - RD_BOOL success; - printf("TLS encryption negotiated\n"); - sec->tls = tls_new(); - if (!tls_connect(sec->tls, sec->mcs->iso->tcp->sock)) - return False; - if (!sec_verify_tls(sec, server)) - return False; - sec->tls_connected = 1; - sec->rdp->settings->encryption = 0; - success = mcs_connect(sec->mcs); - return success; - } - else -#endif - { - RD_BOOL success; - - printf("Standard RDP encryption negotiated\n"); - - success = mcs_connect(sec->mcs); - - if (success && sec->rdp->settings->encryption) - sec_establish_key(sec); - - return success; - } - - return 0; -} - -/* Disconnect a connection */ -void -sec_disconnect(rdpSec * sec) -{ - mcs_disconnect(sec->mcs); - -#ifndef DISABLE_TLS - if (sec->tls) - tls_free(sec->tls); - sec->tls = NULL; - sec->tls_connected = 0; -#endif - - if (sec->rc4_decrypt_key) - crypto_rc4_free(sec->rc4_decrypt_key); - sec->rc4_decrypt_key = NULL; - if (sec->rc4_encrypt_key) - crypto_rc4_free(sec->rc4_encrypt_key); - sec->rc4_encrypt_key = NULL; -} - -rdpSec * -sec_new(struct rdp_rdp * rdp) -{ - rdpSec * self; - - self = (rdpSec *) xmalloc(sizeof(rdpSec)); - if (self != NULL) - { - memset(self, 0, sizeof(rdpSec)); - self->rdp = rdp; - self->mcs = mcs_new(self); - self->license = license_new(self); - self->rc4_decrypt_key = NULL; - self->rc4_encrypt_key = NULL; - } - return self; -} - -void -sec_free(rdpSec * sec) -{ - if (sec != NULL) - { - license_free(sec->license); - mcs_free(sec->mcs); - xfree(sec); - } -} diff --git a/libfreerdp-core/security.c b/libfreerdp-core/security.c new file mode 100644 index 0000000..ded9472 --- /dev/null +++ b/libfreerdp-core/security.c @@ -0,0 +1,681 @@ +/* + FreeRDP: A Remote Desktop Protocol client. + Standard RDP Security + + Copyright (C) Matthew Chapman 1999-2008 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#include "frdp.h" +#include "mcs.h" +#include "chan.h" +#include "license.h" +#include "rdp.h" +#include "iso.h" +#include "tcp.h" +#include <freerdp/rdpset.h> +#include <freerdp/utils/memory.h> + +#ifndef DISABLE_TLS +#include "tls.h" +#include "credssp.h" +#endif + +#include "security.h" + +static RD_BOOL sec_global_initialized = False; + +RD_BOOL +sec_global_init(void) +{ + if (!sec_global_initialized) + { + sec_global_initialized = crypto_global_init(); + } + return sec_global_initialized; +} + +void +sec_global_finish(void) +{ + crypto_global_finish(); + sec_global_initialized = False; +} + +/* these are read only */ +static uint8 pad_54[40] = { + 54, 54, 54, 54, 54, 54, 54, 54, + 54, 54, 54, 54, 54, 54, 54, 54, + 54, 54, 54, 54, 54, 54, 54, 54, + 54, 54, 54, 54, 54, 54, 54, 54, + 54, 54, 54, 54, 54, 54, 54, 54 +}; + +static uint8 pad_92[48] = { + 92, 92, 92, 92, 92, 92, 92, 92, + 92, 92, 92, 92, 92, 92, 92, 92, + 92, 92, 92, 92, 92, 92, 92, 92, + 92, 92, 92, 92, 92, 92, 92, 92, + 92, 92, 92, 92, 92, 92, 92, 92, + 92, 92, 92, 92, 92, 92, 92, 92 +}; + +/* + * 48-byte transformation used to generate master secret (6.1) and key material (6.2.2). + * Both SHA1 and MD5 algorithms are used. + */ +void +sec_hash_48(uint8 * out, uint8 * in, uint8 * salt1, uint8 * salt2, uint8 salt) +{ + int i; + uint8 pad[4]; + uint8 shasig[20]; + CryptoMd5 md5; + CryptoSha1 sha1; + + for (i = 0; i < 3; i++) + { + memset(pad, salt + i, i + 1); + + sha1 = crypto_sha1_init(); + crypto_sha1_update(sha1, pad, i + 1); + crypto_sha1_update(sha1, in, 48); + crypto_sha1_update(sha1, salt1, 32); + crypto_sha1_update(sha1, salt2, 32); + crypto_sha1_final(sha1, shasig); + + md5 = crypto_md5_init(); + crypto_md5_update(md5, in, 48); + crypto_md5_update(md5, shasig, 20); + crypto_md5_final(md5, &out[i * 16]); + } +} + +/* + * 16-byte transformation used to generate export keys (6.2.2). + */ +void +sec_hash_16(uint8 * out, uint8 * in, uint8 * salt1, uint8 * salt2) +{ + CryptoMd5 md5 = crypto_md5_init(); + crypto_md5_update(md5, in, 16); + crypto_md5_update(md5, salt1, 32); + crypto_md5_update(md5, salt2, 32); + crypto_md5_final(md5, out); +} + +/* Reduce key entropy from 64 to 40 bits */ +static void +sec_make_40bit(uint8 * key) +{ + key[0] = 0xd1; + key[1] = 0x26; + key[2] = 0x9e; +} + +/* Generate encryption keys given client and server randoms */ +void +sec_generate_keys(rdpSec * sec, uint8 * client_random, uint8 * server_random, int rc4_key_size) +{ + uint8 pre_master_secret[48]; + uint8 master_secret[48]; + uint8 key_block[48]; + + /* Construct pre-master secret */ + memcpy(pre_master_secret, client_random, 24); + memcpy(pre_master_secret + 24, server_random, 24); + + /* Generate master secret and then key material */ + sec_hash_48(master_secret, pre_master_secret, client_random, server_random, 'A'); + sec_hash_48(key_block, master_secret, client_random, server_random, 'X'); + + /* First 16 bytes of key material is MAC secret */ + memcpy(sec->sec_sign_key, key_block, 16); + + /* Generate export keys from next two blocks of 16 bytes */ + sec_hash_16(sec->sec_decrypt_key, &key_block[16], client_random, server_random); + sec_hash_16(sec->sec_encrypt_key, &key_block[32], client_random, server_random); + + if (rc4_key_size == 1) + { + DEBUG_SEC("40-bit encryption enabled"); + sec_make_40bit(sec->sec_sign_key); + sec_make_40bit(sec->sec_decrypt_key); + sec_make_40bit(sec->sec_encrypt_key); + sec->rc4_key_len = 8; + } + else + { + DEBUG_SEC("rc_4_key_size == %d, 128-bit encryption enabled", rc4_key_size); + sec->rc4_key_len = 16; + } + + /* Save initial RC4 keys as update keys */ + memcpy(sec->sec_decrypt_update_key, sec->sec_decrypt_key, 16); + memcpy(sec->sec_encrypt_update_key, sec->sec_encrypt_key, 16); + + /* Initialize RC4 state arrays */ + sec->rc4_decrypt_key = crypto_rc4_init(sec->sec_decrypt_key, sec->rc4_key_len); + sec->rc4_encrypt_key = crypto_rc4_init(sec->sec_encrypt_key, sec->rc4_key_len); +} + +/* Output a uint32 into a buffer (little-endian) */ +void +buf_out_uint32(uint8 * buffer, uint32 value) +{ + buffer[0] = (value) & 0xff; + buffer[1] = (value >> 8) & 0xff; + buffer[2] = (value >> 16) & 0xff; + buffer[3] = (value >> 24) & 0xff; +} + +/* Generate a MAC hash (5.2.3.1), using a combination of SHA1 and MD5 */ +void +sec_sign(uint8 * signature, int siglen, uint8 * session_key, int keylen, uint8 * data, int datalen) +{ + uint8 shasig[20]; + uint8 md5sig[16]; + uint8 lenhdr[4]; + CryptoSha1 sha1; + CryptoMd5 md5; + + buf_out_uint32(lenhdr, datalen); + + sha1 = crypto_sha1_init(); + crypto_sha1_update(sha1, session_key, keylen); + crypto_sha1_update(sha1, pad_54, 40); + crypto_sha1_update(sha1, lenhdr, 4); + crypto_sha1_update(sha1, data, datalen); + crypto_sha1_final(sha1, shasig); + + md5 = crypto_md5_init(); + crypto_md5_update(md5, session_key, keylen); + crypto_md5_update(md5, pad_92, 48); + crypto_md5_update(md5, shasig, 20); + crypto_md5_final(md5, md5sig); + + memcpy(signature, md5sig, siglen); +} + +/* Update an encryption key */ +static void +sec_update(rdpSec * sec, uint8 * key, uint8 * update_key) +{ + uint8 shasig[20]; + CryptoSha1 sha1; + CryptoMd5 md5; + CryptoRc4 update; + + sha1 = crypto_sha1_init(); + crypto_sha1_update(sha1, update_key, sec->rc4_key_len); + crypto_sha1_update(sha1, pad_54, 40); + crypto_sha1_update(sha1, key, sec->rc4_key_len); + crypto_sha1_final(sha1, shasig); + + md5 = crypto_md5_init(); + crypto_md5_update(md5, update_key, sec->rc4_key_len); + crypto_md5_update(md5, pad_92, 48); + crypto_md5_update(md5, shasig, 20); + crypto_md5_final(md5, key); + + update = crypto_rc4_init(key, sec->rc4_key_len); + crypto_rc4(update, sec->rc4_key_len, key, key); + crypto_rc4_free(update); + + if (sec->rc4_key_len == 8) + sec_make_40bit(key); +} + +/* Encrypt data using RC4 */ +static void +sec_encrypt(rdpSec * sec, uint8 * data, int length) +{ + if (sec->sec_encrypt_use_count == 4096) + { + sec_update(sec, sec->sec_encrypt_key, sec->sec_encrypt_update_key); + crypto_rc4_free(sec->rc4_encrypt_key); + sec->rc4_encrypt_key = crypto_rc4_init(sec->sec_encrypt_key, sec->rc4_key_len); + sec->sec_encrypt_use_count = 0; + } + + crypto_rc4(sec->rc4_encrypt_key, length, data, data); + sec->sec_encrypt_use_count++; +} + +/* Decrypt data using RC4 */ +static void +sec_decrypt(rdpSec * sec, uint8 * data, int length) +{ + if (sec->sec_decrypt_use_count == 4096) + { + sec_update(sec, sec->sec_decrypt_key, sec->sec_decrypt_update_key); + crypto_rc4_free(sec->rc4_decrypt_key); + sec->rc4_decrypt_key = crypto_rc4_init(sec->sec_decrypt_key, sec->rc4_key_len); + sec->sec_decrypt_use_count = 0; + } + + crypto_rc4(sec->rc4_decrypt_key, length, data, data); + sec->sec_decrypt_use_count++; +} + +/* Initialize secure transport packet */ +STREAM +sec_init(rdpSec * sec, uint32 flags, int maxlen) +{ + STREAM s; + int hdrlen; + + if (flags) + { + if (!(sec->net->license->license_issued)) + hdrlen = (flags & SEC_ENCRYPT) ? 12 : 4; + else + hdrlen = (flags & SEC_ENCRYPT) ? 12 : 0; + } + else + hdrlen = 0; + + s = mcs_init(sec->net->mcs, maxlen + hdrlen); + s_push_layer(s, sec_hdr, hdrlen); + + return s; +} + +/* Initialize fast path secure transport packet */ +STREAM +sec_fp_init(rdpSec * sec, uint32 flags, int maxlen) +{ + STREAM s; + int hdrlen; + + hdrlen = (flags & SEC_ENCRYPT) ? 8 : 0; + s = mcs_fp_init(sec->net->mcs, maxlen + hdrlen); + s_push_layer(s, sec_hdr, hdrlen); + + return s; +} + +/* Transmit secure transport packet over specified channel */ +void +sec_send_to_channel(rdpSec * sec, STREAM s, uint32 flags, uint16 channel) +{ + int datalen; + s_pop_layer(s, sec_hdr); + + if (flags) + { + /* Basic Security Header */ + if (!(sec->net->license->license_issued) || (flags & SEC_ENCRYPT)) + out_uint32_le(s, flags); /* flags */ + + if (flags & SEC_ENCRYPT) + { + flags &= ~SEC_ENCRYPT; + datalen = s->end - s->p - 8; + +#if WITH_DEBUG + DEBUG_SEC("Sending encrypted packet:"); + hexdump(s->p + 8, datalen); +#endif + + sec_sign(s->p, 8, sec->sec_sign_key, sec->rc4_key_len, s->p + 8, datalen); + sec_encrypt(sec, s->p + 8, datalen); + } + } + + mcs_send_to_channel(sec->net->mcs, s, channel); +} + +/* Transmit secure transport packet */ + +void +sec_send(rdpSec * sec, STREAM s, uint32 flags) +{ + sec_send_to_channel(sec, s, flags, MCS_GLOBAL_CHANNEL); +} + +/* Transmit secure fast path packet */ +void +sec_fp_send(rdpSec * sec, STREAM s, uint32 flags) +{ + int datalen; + s_pop_layer(s, sec_hdr); + if (flags & SEC_ENCRYPT) + { + datalen = ((int) (s->end - s->p)) - 8; + sec_sign(s->p, 8, sec->sec_sign_key, sec->rc4_key_len, s->p + 8, datalen); + sec_encrypt(sec, s->p + 8, datalen); + } + mcs_fp_send(sec->net->mcs, s, flags); +} + +/* Transfer the client random to the server */ +void +sec_establish_key(rdpSec * sec) +{ + uint32 length = sec->server_public_key_len + SEC_PADDING_SIZE; + uint32 flags = SEC_EXCHANGE_PKT; + STREAM s; + + s = sec_init(sec, flags, length + 4); + + out_uint32_le(s, length); + out_uint8p(s, sec->sec_crypted_random, sec->server_public_key_len); + out_uint8s(s, SEC_PADDING_SIZE); + + s_mark_end(s); + sec_send(sec, s, flags); +} + +void +sec_reverse_copy(uint8 * out, uint8 * in, int len) +{ + int i; + in += len; + for (i = 0; i < len; i++) + { + *out++ = *--in; + } +} + +/* Parse a Server Proprietary Certificate RSA Public Key */ +RD_BOOL +sec_parse_public_key(rdpSec * sec, STREAM s, uint32 len, uint8 * modulus, uint8 * exponent) +{ + uint32 magic; + uint32 modulus_len; + + in_uint32_le(s, magic); + if (magic != SEC_RSA_MAGIC) + { + ui_error(sec->rdp->inst, "RSA magic 0x%x\n", magic); + return False; + } + + in_uint32_le(s, modulus_len); + if (4 + 4 + 4 + 4 + SEC_EXPONENT_SIZE + modulus_len != len) + { + ui_error(sec->rdp->inst, "Inconsistent Server Proprietary Certificate public key size\n"); + return False; + } + modulus_len -= SEC_PADDING_SIZE; + if ((modulus_len < SEC_MODULUS_SIZE) || (modulus_len > SEC_MAX_MODULUS_SIZE)) + { + ui_error(sec->rdp->inst, "Bad Server Proprietary Certificate public key size (%u bits)\n", modulus_len * 8); + return False; + } + + in_uint8s(s, 4); /* modulus_bits - must match modulus_len */ + in_uint8s(s, 4); /* datalen - how much data can be encoded */ + sec_reverse_copy(exponent, s->p, SEC_EXPONENT_SIZE); + in_uint8s(s, SEC_EXPONENT_SIZE); + sec_reverse_copy(modulus, s->p, modulus_len); + in_uint8s(s, modulus_len); + in_uint8s(s, SEC_PADDING_SIZE); /* zero padding - included in modulus_len but not in modulus_bits */ + sec->server_public_key_len = modulus_len; + + return s_check(s); +} + +/* Parse a Proprietary Certificate signature */ +RD_BOOL +sec_parse_public_sig(STREAM s, uint32 len) +{ + /* The Proprietary Certificate signature uses a static published private key. + * That is completely nonsense, so we won't bother checking it. */ + + in_uint8s(s, len); + return len == 72; +} + +RD_BOOL +sec_parse_cert_chain_v1(rdpSec * sec, STREAM s, uint8 * modulus, uint8 * exponent) +{ + uint16 wPublicKeyBlobType, wPublicKeyBlobLen; + uint16 wSignatureBlobType, wSignatureBlobLen; + + DEBUG_SEC("We're going for a Server Proprietary Certificate (no TS license)"); + in_uint8s(s, 4); /* dwSigAlgId must be 1 (SIGNATURE_ALG_RSA) */ + in_uint8s(s, 4); /* dwKeyAlgId must be 1 (KEY_EXCHANGE_ALG_RSA ) */ + + in_uint16_le(s, wPublicKeyBlobType); + if (wPublicKeyBlobType != BB_RSA_KEY_BLOB) + return False; + + in_uint16_le(s, wPublicKeyBlobLen); + + if (!sec_parse_public_key(sec, s, wPublicKeyBlobLen, modulus, exponent)) + return False; + + in_uint16_le(s, wSignatureBlobType); + if (wSignatureBlobType != BB_RSA_SIGNATURE_BLOB) + return False; + + in_uint16_le(s, wSignatureBlobLen); + if (!sec_parse_public_sig(s, wSignatureBlobLen)) + return False; + + return True; +} + +RD_BOOL +sec_parse_cert_chain_v2(rdpSec * sec, STREAM s, uint8 * modulus, uint8 * exponent) +{ + uint32 cert_total_count, cert_counter; + uint32 license_cert_len, ts_cert_len; + CryptoCert license_cert, ts_cert; + + DEBUG_SEC("We're going for a X.509 Certificate (TS license)"); + in_uint32_le(s, cert_total_count); /* Number of certificates */ + DEBUG_SEC("Cert chain length: %d", cert_total_count); + + if (cert_total_count < 2) + { + ui_error(sec->rdp->inst, "Server didn't send enough X509 certificates\n"); + return False; + } + + /* X.509 Certificate Chain: */ + /* Only the 2 last certificates in chain are _really_ interesting */ + for (cert_counter=0; cert_counter < cert_total_count - 2; cert_counter++) + { + uint32 ignorelen; + CryptoCert ignorecert; + + DEBUG_SEC("Ignoring cert: %d", cert_counter); + in_uint32_le(s, ignorelen); + DEBUG_SEC("Ignored Certificate length is %d", ignorelen); + ignorecert = crypto_cert_read(s->p, ignorelen); + in_uint8s(s, ignorelen); + if (ignorecert == NULL) + { + ui_error(sec->rdp->inst, "Couldn't read certificate %d from server certificate chain\n", cert_counter); + return False; + } + +#ifdef WITH_DEBUG_SEC + DEBUG_SEC("cert #%d (ignored):", cert_counter); + crypto_cert_print_fp(stdout, ignorecert); +#endif + /* TODO: Verify the certificate chain all the way from CA root to prevent MITM attacks */ + crypto_cert_free(ignorecert); + } + + /* The second to last certificate is the license server */ + in_uint32_le(s, license_cert_len); + DEBUG_SEC("License Server Certificate length is %d", license_cert_len); + license_cert = crypto_cert_read(s->p, license_cert_len); + in_uint8s(s, license_cert_len); + if (NULL == license_cert) + { + ui_error(sec->rdp->inst, "Couldn't load License Server Certificate from server\n"); + return False; + } +#ifdef WITH_DEBUG_SEC + crypto_cert_print_fp(stdout, license_cert); +#endif + + /* The last certificate is the Terminal Server */ + in_uint32_le(s, ts_cert_len); + DEBUG_SEC("TS Certificate length is %d", ts_cert_len); + ts_cert = crypto_cert_read(s->p, ts_cert_len); + in_uint8s(s, ts_cert_len); + if (NULL == ts_cert) + { + crypto_cert_free(license_cert); + ui_error(sec->rdp->inst, "Couldn't load TS Certificate from server\n"); + return False; + } + +#ifdef WITH_DEBUG_SEC + crypto_cert_print_fp(stdout, ts_cert); +#endif + if (!crypto_cert_verify(ts_cert, license_cert)) + { + crypto_cert_free(ts_cert); + crypto_cert_free(license_cert); + ui_error(sec->rdp->inst, "TS Certificate not signed with License Certificate\n"); + return False; + } + crypto_cert_free(license_cert); + + if (crypto_cert_get_pub_exp_mod(ts_cert, &(sec->server_public_key_len), + exponent, SEC_EXPONENT_SIZE, modulus, SEC_MAX_MODULUS_SIZE) != 0) + { + ui_error(sec->rdp->inst, "Problem extracting RSA key from TS Certificate\n"); + crypto_cert_free(ts_cert); + return False; + } + crypto_cert_free(ts_cert); + + if ((sec->server_public_key_len < SEC_MODULUS_SIZE) || + (sec->server_public_key_len > SEC_MAX_MODULUS_SIZE)) + { + ui_error(sec->rdp->inst, "Bad TS Certificate public key size (%u bits)\n", + sec->server_public_key_len * 8); + return False; + } + in_uint8s(s, 8 + 4 * cert_total_count); /* Padding */ + + return True; +} + +/* Receive secure transport packet + * Some package types are processed internally. + * If s is returned a package of *type must be processed by the caller */ +STREAM +sec_recv(rdpSec * sec, secRecvType * type) +{ + STREAM s; + uint16 channel; + uint32 sec_flags; + isoRecvType iso_type; + + while ((s = mcs_recv(sec->net->mcs, &iso_type, &channel)) != NULL) + { + if ((iso_type == ISO_RECV_FAST_PATH) || + (iso_type == ISO_RECV_FAST_PATH_ENCRYPTED)) + { + *type = SEC_RECV_FAST_PATH; + if (iso_type == ISO_RECV_FAST_PATH_ENCRYPTED) + { + in_uint8s(s, 8); /* dataSignature */ /* TODO: Check signature! */ + sec_decrypt(sec, s->p, s->end - s->p); + } + return s; + } + if (iso_type != ISO_RECV_X224) + { + ui_error(sec->rdp->inst, "expected ISO_RECV_X224, got %d\n", iso_type); + return NULL; + } + if (sec->rdp->settings->encryption || !sec->net->license->license_issued) + { + /* basicSecurityHeader: */ + in_uint32_le(s, sec_flags); + + if ((sec_flags & SEC_ENCRYPT) || (sec_flags & SEC_REDIRECTION_PKT)) + { + in_uint8s(s, 8); /* dataSignature */ /* TODO: Check signature! */ + sec_decrypt(sec, s->p, s->end - s->p); + } + + if (sec_flags & SEC_LICENSE_PKT) + { + *type = SEC_RECV_LICENSE; + license_process(sec->net->license, s); + continue; + } + + if (sec_flags & SEC_REDIRECTION_PKT) + { + *type = SEC_RECV_REDIRECT; + return s; + } + } + + if (channel != MCS_GLOBAL_CHANNEL) + { + vchan_process(sec->net->mcs->chan, s, channel); + *type = SEC_RECV_IOCHANNEL; + return s; + } + *type = SEC_RECV_SHARE_CONTROL; + return s; + } + + return NULL; +} + +/* Disconnect a connection */ +void +sec_disconnect(rdpSec * sec) +{ + mcs_disconnect(sec->net->mcs); + + if (sec->rc4_decrypt_key) + crypto_rc4_free(sec->rc4_decrypt_key); + sec->rc4_decrypt_key = NULL; + if (sec->rc4_encrypt_key) + crypto_rc4_free(sec->rc4_encrypt_key); + sec->rc4_encrypt_key = NULL; +} + +rdpSec * +sec_new(struct rdp_rdp * rdp) +{ + rdpSec * self; + + self = (rdpSec *) xmalloc(sizeof(rdpSec)); + if (self != NULL) + { + memset(self, 0, sizeof(rdpSec)); + self->rdp = rdp; + self->rc4_decrypt_key = NULL; + self->rc4_encrypt_key = NULL; + self->net = rdp->net; + } + return self; +} + +void +sec_free(rdpSec * sec) +{ + if (sec != NULL) + { + license_free(sec->net->license); + mcs_free(sec->net->mcs); + xfree(sec); + } +} diff --git a/libfreerdp-core/secure.h b/libfreerdp-core/security.h index d5973cf..4e3f05a 100644 --- a/libfreerdp-core/secure.h +++ b/libfreerdp-core/security.h @@ -1,6 +1,6 @@ /* FreeRDP: A Remote Desktop Protocol client. - Protocol services - RDP encryption and licensing + Standard RDP Security Copyright (C) Jay Sorg 2009-2011 @@ -17,8 +17,8 @@ limitations under the License. */ -#ifndef __SECURE_H -#define __SECURE_H +#ifndef __SECURITY_H +#define __SECURITY_H typedef struct rdp_sec rdpSec; @@ -37,8 +37,9 @@ sec_global_finish(void); struct rdp_sec { - struct rdp_rdp * rdp; int rc4_key_len; + struct rdp_rdp * rdp; + struct rdp_network * net; CryptoRc4 rc4_decrypt_key; CryptoRc4 rc4_encrypt_key; uint32 server_public_key_len; @@ -51,13 +52,6 @@ struct rdp_sec /* These values must be available to reset state - Session Directory */ int sec_encrypt_use_count; int sec_decrypt_use_count; - struct rdp_mcs * mcs; - struct rdp_license * license; - int tls_connected; -#ifndef DISABLE_TLS - struct rdp_tls * tls; - struct rdp_credssp * credssp; -#endif }; enum sec_recv_type @@ -79,6 +73,12 @@ buf_out_uint32(uint8 * buffer, uint32 value); void sec_sign(uint8 * signature, int siglen, uint8 * session_key, int keylen, uint8 * data, int datalen); +RD_BOOL +sec_parse_public_key(rdpSec * sec, STREAM s, uint32 len, uint8 * modulus, uint8 * exponent); +RD_BOOL +sec_parse_public_sig(STREAM s, uint32 len); +void +sec_generate_keys(rdpSec * sec, uint8 * client_random, uint8 * server_random, int rc4_key_size); STREAM sec_init(rdpSec * sec, uint32 flags, int maxlen); STREAM @@ -90,13 +90,17 @@ sec_send(rdpSec * sec, STREAM s, uint32 flags); void sec_fp_send(rdpSec * sec, STREAM s, uint32 flags); void -sec_process_mcs_data(rdpSec * sec, STREAM s); +sec_reverse_copy(uint8 * out, uint8 * in, int len); +RD_BOOL +sec_parse_cert_chain_v1(rdpSec * sec, STREAM s, uint8 * modulus, uint8 * exponent); +RD_BOOL +sec_parse_cert_chain_v2(rdpSec * sec, STREAM s, uint8 * modulus, uint8 * exponent); +void +connect_process_mcs_data(rdpSec * sec, STREAM s); STREAM sec_recv(rdpSec * sec, secRecvType * type); void -sec_out_gcc_conference_create_request(rdpSec * sec, STREAM s); -RD_BOOL -sec_connect(rdpSec * sec, char *server, char *username, int port); +sec_establish_key(rdpSec * sec); void sec_disconnect(rdpSec * sec); rdpSec * diff --git a/libfreerdp-core/surface.c b/libfreerdp-core/surface.c index c5c4b4c..df69f47 100644 --- a/libfreerdp-core/surface.c +++ b/libfreerdp-core/surface.c @@ -21,6 +21,7 @@ #include "rdp.h" #include "stream.h" #include <freerdp/freerdp.h> +#include <freerdp/utils/hexdump.h> #include "surface.h" @@ -83,7 +84,7 @@ surface_codec_cap(rdpRdp * rdp, uint8 * codec_guid, int codec_id, else { //printf("unknown guid\n"); - hexdump(codec_guid, 16); + freerdp_hexdump(codec_guid, 16); } return s; } diff --git a/libfreerdp-core/tcp.c b/libfreerdp-core/tcp.c index 035b2c4..5bfbb27 100644 --- a/libfreerdp-core/tcp.c +++ b/libfreerdp-core/tcp.c @@ -32,9 +32,9 @@ #include "frdp.h" #include "iso.h" #include "mcs.h" -#include "secure.h" #include "rdp.h" #include <freerdp/utils/memory.h> +#include <freerdp/utils/hexdump.h> #include "tcp.h" @@ -79,6 +79,7 @@ tcp_socket_ok(int sck) return True; } } + return False; } @@ -92,13 +93,16 @@ tcp_can_send(int sck, int millis) time.tv_sec = millis / 1000; time.tv_usec = (millis * 1000) % 1000000; + FD_ZERO(&wfds); FD_SET(sck, &wfds); sel_count = select(sck + 1, 0, &wfds, 0, &time); + if (sel_count > 0) { return tcp_socket_ok(sck); } + return False; } @@ -112,164 +116,85 @@ tcp_can_recv(int sck, int millis) time.tv_sec = millis / 1000; time.tv_usec = (millis * 1000) % 1000000; + FD_ZERO(&rfds); FD_SET(sck, &rfds); sel_count = select(sck + 1, &rfds, 0, 0, &time); + if (sel_count > 0) { return tcp_socket_ok(sck); } - return False; -} - -/* Initialize and return STREAM. - * The stream will have room for at least minsize. - * The tcp layers out stream will be used. */ -STREAM -tcp_init(rdpTcp * tcp, uint32 minsize) -{ - STREAM result = &(tcp->out); - - if (minsize > result->size) - { - result->data = (uint8 *) xrealloc(result->data, minsize); - result->size = minsize; - } - result->p = result->data; - result->end = result->data + result->size; - return result; + return False; } -/* Send data from stream to tcp socket. - * Will block until all data has been sent. */ void -tcp_send(rdpTcp * tcp, STREAM s) +tcp_write(rdpTcp * tcp, char* b, int length) { int sent = 0; int total = 0; - int length = s->end - s->data; -#ifndef DISABLE_TLS - if (tcp->iso->mcs->sec->tls_connected) - { - tls_write(tcp->iso->mcs->sec->tls, (char*) s->data, length); - } - else -#endif + while (total < length) { while (total < length) { - while (total < length) + sent = send(tcp->sockfd, b + total, length - total, MSG_NOSIGNAL); + if (sent <= 0) { - sent = send(tcp->sock, s->data + total, length - total, MSG_NOSIGNAL); - if (sent <= 0) + if (sent == -1 && TCP_BLOCKS) + { + tcp_can_send(tcp->sockfd, 100); + sent = 0; + } + else { - if (sent == -1 && TCP_BLOCKS) - { - tcp_can_send(tcp->sock, 100); - sent = 0; - } - else - { - ui_error(tcp->iso->mcs->sec->rdp->inst, "send: %s\n", TCP_STRERROR); - return; - } + ui_error(tcp->net->rdp->inst, "send: %s\n", TCP_STRERROR); + return; } - total += sent; } + total += sent; } } } -/* Read length bytes from tcp socket to stream and return it. - * Appends to stream s if specified, otherwise it uses stream from tcp layer. - * Will block until data available. - * Returns NULL on error. */ -STREAM -tcp_recv(rdpTcp * tcp, STREAM s, uint32 length) +int +tcp_read(rdpTcp * tcp, char* b, int length) { int rcvd = 0; - uint32 p_offset; - uint32 new_length; - uint32 end_offset; - if (s == NULL) - { - /* read into "new" stream */ - if (length > tcp->in.size) - { - tcp->in.data = (uint8 *) xrealloc(tcp->in.data, length); - tcp->in.size = length; - } + if (!ui_select(tcp->net->sec->rdp->inst, tcp->sockfd)) + return -1; /* user quit */ - tcp->in.end = tcp->in.p = tcp->in.data; - s = &(tcp->in); - } - else - { - /* append to existing stream */ - new_length = (s->end - s->data) + length; - if (new_length > s->size) - { - p_offset = s->p - s->data; - end_offset = s->end - s->data; - s->data = (uint8 *) xrealloc(s->data, new_length); - s->size = new_length; - s->p = s->data + p_offset; - s->end = s->data + end_offset; - } - } + rcvd = recv(tcp->sockfd, b, length, 0); - while (length > 0) + if (rcvd < 0) { -#ifndef DISABLE_TLS - if (tcp->iso->mcs->sec->tls_connected) + if (rcvd == -1 && TCP_BLOCKS) { - rcvd = tls_read(tcp->iso->mcs->sec->tls, (char*) s->end, length); - - if (rcvd < 0) - return NULL; + tcp_can_recv(tcp->sockfd, 1); + rcvd = 0; } else -#endif { - if (!ui_select(tcp->iso->mcs->sec->rdp->inst, tcp->sock)) - return NULL; /* user quit */ - - rcvd = recv(tcp->sock, s->end, length, 0); - if (rcvd < 0) - { - if (rcvd == -1 && TCP_BLOCKS) - { - tcp_can_recv(tcp->sock, 1); - rcvd = 0; - } - else - { - ui_error(tcp->iso->mcs->sec->rdp->inst, "recv: %s\n", TCP_STRERROR); - return NULL; - } - } - else if (rcvd == 0) - { - ui_error(tcp->iso->mcs->sec->rdp->inst, "Connection closed\n"); - return NULL; - } + ui_error(tcp->net->rdp->inst, "recv: %s\n", TCP_STRERROR); + return -1; } - - s->end += rcvd; - length -= rcvd; + } + else if (rcvd == 0) + { + ui_error(tcp->net->rdp->inst, "Connection closed\n"); + return -1; } - return s; + return rcvd; } /* Establish a connection on the TCP layer */ RD_BOOL tcp_connect(rdpTcp * tcp, char * server, int port) { - int sock; + int sockfd; uint32 option_value; socklen_t option_len; @@ -289,29 +214,29 @@ tcp_connect(rdpTcp * tcp, char * server, int port) if ((n = getaddrinfo(server, tcp_port_rdp_s, &hints, &res))) { - ui_error(tcp->iso->mcs->sec->rdp->inst, "getaddrinfo: %s\n", gai_strerror(n)); + ui_error(tcp->net->rdp->inst, "getaddrinfo: %s\n", gai_strerror(n)); return False; } ressave = res; - sock = -1; + sockfd = -1; while (res) { - sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); - if (!(sock < 0)) + sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (!(sockfd < 0)) { - if (connect(sock, res->ai_addr, res->ai_addrlen) == 0) + if (connect(sockfd, res->ai_addr, res->ai_addrlen) == 0) break; - TCP_CLOSE(sock); - sock = -1; + TCP_CLOSE(sockfd); + sockfd = -1; } res = res->ai_next; } freeaddrinfo(ressave); - if (sock == -1) + if (sockfd == -1) { - ui_error(tcp->iso->mcs->sec->rdp->inst, "%s: unable to connect\n", server); + ui_error(tcp->net->rdp->inst, "%s: unable to connect\n", server); return False; } @@ -328,13 +253,13 @@ tcp_connect(rdpTcp * tcp, char * server, int port) } else if ((servaddr.sin_addr.s_addr = inet_addr(server)) == INADDR_NONE) { - ui_error(tcp->iso->mcs->sec->rdp->inst, "%s: unable to resolve host\n", server); + ui_error(tcp->net->rdp->inst, "%s: unable to resolve host\n", server); return False; } if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - ui_error(tcp->iso->mcs->sec->rdp->inst, "socket: %s\n", TCP_STRERROR); + ui_error(tcp->net->rdp->inst, "socket: %s\n", TCP_STRERROR); return False; } @@ -343,40 +268,40 @@ tcp_connect(rdpTcp * tcp, char * server, int port) if (connect(sock, (struct sockaddr *) &servaddr, sizeof(struct sockaddr)) < 0) { - ui_error(tcp->iso->mcs->sec->rdp->inst, "connect: %s\n", TCP_STRERROR); + ui_error(tcp->net->rdp->inst, "connect: %s\n", TCP_STRERROR); TCP_CLOSE(sock); return False; } #endif /* IPv6 */ - tcp->sock = sock; + tcp->sockfd = sockfd; /* set socket as non blocking */ #ifdef _WIN32 { u_long arg = 1; - ioctlsocket(tcp->sock, FIONBIO, &arg); + ioctlsocket(tcp->sockfd, FIONBIO, &arg); tcp->wsa_event = WSACreateEvent(); - WSAEventSelect(tcp->sock, tcp->wsa_event, FD_READ); + WSAEventSelect(tcp->sockfd, tcp->wsa_event, FD_READ); } #else - option_value = fcntl(tcp->sock, F_GETFL); + option_value = fcntl(tcp->sockfd, F_GETFL); option_value = option_value | O_NONBLOCK; - fcntl(tcp->sock, F_SETFL, option_value); + fcntl(tcp->sockfd, F_SETFL, option_value); #endif option_value = 1; option_len = sizeof(option_value); - setsockopt(tcp->sock, IPPROTO_TCP, TCP_NODELAY, (void *) &option_value, option_len); + setsockopt(tcp->sockfd, IPPROTO_TCP, TCP_NODELAY, (void *) &option_value, option_len); /* receive buffer must be a least 16 K */ - if (getsockopt(tcp->sock, SOL_SOCKET, SO_RCVBUF, (void *) &option_value, &option_len) == 0) + if (getsockopt(tcp->sockfd, SOL_SOCKET, SO_RCVBUF, (void *) &option_value, &option_len) == 0) { if (option_value < (1024 * 16)) { option_value = 1024 * 16; option_len = sizeof(option_value); - setsockopt(tcp->sock, SOL_SOCKET, SO_RCVBUF, (void *) &option_value, + setsockopt(tcp->sockfd, SOL_SOCKET, SO_RCVBUF, (void *) &option_value, option_len); } } @@ -388,10 +313,10 @@ tcp_connect(rdpTcp * tcp, char * server, int port) void tcp_disconnect(rdpTcp * tcp) { - if (tcp->sock != -1) + if (tcp->sockfd != -1) { - TCP_CLOSE(tcp->sock); - tcp->sock = -1; + TCP_CLOSE(tcp->sockfd); + tcp->sockfd = -1; } #ifdef _WIN32 if (tcp->wsa_event) @@ -408,7 +333,7 @@ tcp_get_address(rdpTcp * tcp) { struct sockaddr_in sockaddr; socklen_t len = sizeof(sockaddr); - if (getsockname(tcp->sock, (struct sockaddr *) &sockaddr, &len) == 0) + if (getsockname(tcp->sockfd, (struct sockaddr *) &sockaddr, &len) == 0) { uint8 *ip = (uint8 *) & sockaddr.sin_addr; snprintf(tcp->ipaddr, sizeof(tcp->ipaddr), "%d.%d.%d.%d", ip[0], ip[1], ip[2], @@ -421,24 +346,19 @@ tcp_get_address(rdpTcp * tcp) } rdpTcp * -tcp_new(struct rdp_iso * iso) +tcp_new(struct rdp_network * net) { rdpTcp * self; self = (rdpTcp *) xmalloc(sizeof(rdpTcp)); + if (self != NULL) { memset(self, 0, sizeof(rdpTcp)); - self->iso = iso; - - self->in.size = 4096; - self->in.data = (uint8 *) xmalloc(self->in.size); - - self->out.size = 4096; - self->out.data = (uint8 *) xmalloc(self->out.size); - - self->sock = -1; + self->net = net; + self->sockfd = -1; } + return self; } @@ -447,8 +367,6 @@ tcp_free(rdpTcp * tcp) { if (tcp != NULL) { - xfree(tcp->in.data); - xfree(tcp->out.data); xfree(tcp); } } diff --git a/libfreerdp-core/tcp.h b/libfreerdp-core/tcp.h index e5f2d69..e71ded2 100644 --- a/libfreerdp-core/tcp.h +++ b/libfreerdp-core/tcp.h @@ -26,28 +26,25 @@ struct rdp_tcp { - int sock; - struct rdp_iso * iso; - struct stream in; - struct stream out; - int tcp_port_rdp; + int sockfd; char ipaddr[32]; + int tcp_port_rdp; + struct rdp_network * net; #ifdef _WIN32 WSAEVENT wsa_event; #endif }; typedef struct rdp_tcp rdpTcp; +void +tcp_write(rdpTcp * tcp, char* b, int length); +int +tcp_read(rdpTcp * tcp, char* b, int length); + RD_BOOL tcp_can_send(int sck, int millis); RD_BOOL tcp_can_recv(int sck, int millis); -STREAM -tcp_init(rdpTcp * tcp, uint32 minsize); -void -tcp_send(rdpTcp * tcp, STREAM s); -STREAM -tcp_recv(rdpTcp * tcp, STREAM s, uint32 length); RD_BOOL tcp_connect(rdpTcp * tcp, char * server, int port); void @@ -55,7 +52,7 @@ tcp_disconnect(rdpTcp * tcp); char * tcp_get_address(rdpTcp * tcp); rdpTcp * -tcp_new(struct rdp_iso * iso); +tcp_new(struct rdp_network * net); void tcp_free(rdpTcp * tcp); diff --git a/libfreerdp-utils/Makefile.am b/libfreerdp-utils/Makefile.am index d6912fd..eda8d01 100644 --- a/libfreerdp-utils/Makefile.am +++ b/libfreerdp-utils/Makefile.am @@ -13,7 +13,8 @@ libfreerdp_utils_la_SOURCES = \ wait_obj.c \ chan_plugin.c \ stopwatch.c \ - profiler.c + profiler.c \ + hexdump.c libfreerdp_utils_la_CFLAGS = \ -I$(top_srcdir) \ diff --git a/libfreerdp-utils/hexdump.c b/libfreerdp-utils/hexdump.c new file mode 100644 index 0000000..ce02eed --- /dev/null +++ b/libfreerdp-utils/hexdump.c @@ -0,0 +1,56 @@ +/* + FreeRDP: A Remote Desktop Protocol client. + Hex Dump Utils + + Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com> + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#include <stdio.h> +#include <string.h> + +#include <freerdp/types/base.h> + +#include <freerdp/utils/hexdump.h> + +void freerdp_hexdump(uint8* data, int length) +{ + uint8* p = data; + int i, line, offset = 0; + + while (offset < length) + { + printf("%04x ", offset); + + line = length - offset; + + if (line > FREERDP_HEXDUMP_LINE_LENGTH) + line = FREERDP_HEXDUMP_LINE_LENGTH; + + for (i = 0; i < line; i++) + printf("%02x ", p[i]); + + for (; i < FREERDP_HEXDUMP_LINE_LENGTH; i++) + printf(" "); + + for (i = 0; i < line; i++) + printf("%c", (p[i] >= 0x20 && p[i] < 0x7F) ? p[i] : '.'); + + printf("\n"); + + offset += line; + p += line; + } +} + |