Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/FreeRDP/FreeRDP-old.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'libfreerdp-core/connect.c')
-rw-r--r--libfreerdp-core/connect.c525
1 files changed, 525 insertions, 0 deletions
diff --git a/libfreerdp-core/connect.c b/libfreerdp-core/connect.c
new file mode 100644
index 0000000..0345dc0
--- /dev/null
+++ b/libfreerdp-core/connect.c
@@ -0,0 +1,525 @@
+/*
+ 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;
+
+ 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
+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;
+ }
+}
+
+