* limitations under the License.
*/
+#include <freerdp/utils/print.h>
+
#include "gcc.h"
+#include "certificate.h"
/**
* T.124 GCC is defined in:
uint8 h221_sc_key[4] = "McDn";
/**
+ * Read a GCC Conference Create Request.\n
+ * @msdn{cc240836}
+ * @param s stream
+ * @param settings rdp settings
+ */
+
+boolean gcc_read_conference_create_request(STREAM* s, rdpSettings* settings)
+{
+ uint16 length;
+ uint8 choice;
+ uint8 number;
+ uint8 selection;
+
+ /* ConnectData */
+ if (!per_read_choice(s, &choice))
+ return false;
+ if (!per_read_object_identifier(s, t124_02_98_oid))
+ return false;
+
+ /* ConnectData::connectPDU (OCTET_STRING) */
+ if (!per_read_length(s, &length))
+ return false;
+
+ /* ConnectGCCPDU */
+ if (!per_read_choice(s, &choice))
+ return false;
+ if (!per_read_selection(s, &selection))
+ return false;
+
+ /* ConferenceCreateRequest::conferenceName */
+ if (!per_read_numeric_string(s, 1)) /* ConferenceName::numeric */
+ return false;
+ if (!per_read_padding(s, 1)) /* padding */
+ return false;
+
+ /* UserData (SET OF SEQUENCE) */
+ if (!per_read_number_of_sets(s, &number) || number != 1) /* one set of UserData */
+ return false;
+ if (!per_read_choice(s, &choice) || choice != 0xC0) /* UserData::value present + select h221NonStandard (1) */
+ return false;
+
+ /* h221NonStandard */
+ if (!per_read_octet_string(s, h221_cs_key, 4, 4)) /* h221NonStandard, client-to-server H.221 key, "Duca" */
+ return false;
+
+ /* userData::value (OCTET_STRING) */
+ if (!per_read_length(s, &length))
+ return false;
+ if (stream_get_left(s) < length)
+ return false;
+ if (!gcc_read_client_data_blocks(s, settings, length))
+ return false;
+
+ return true;
+}
+
+/**
* Write a GCC Conference Create Request.\n
* @msdn{cc240836}
* @param s stream
per_write_octet_string(s, user_data->data, stream_get_length(user_data), 0); /* array of client data blocks */
}
-void gcc_read_conference_create_response(STREAM* s, rdpSettings* settings)
+boolean gcc_read_conference_create_response(STREAM* s, rdpSettings* settings)
{
uint16 length;
uint32 tag;
per_read_choice(s, &choice);
/* h221NonStandard */
- per_read_octet_string(s, h221_sc_key, 4, 4); /* h221NonStandard, server-to-client H.221 key, "McDn" */
+ if (!per_read_octet_string(s, h221_sc_key, 4, 4)) /* h221NonStandard, server-to-client H.221 key, "McDn" */
+ return false;
/* userData (OCTET_STRING) */
per_read_length(s, &length);
- gcc_read_server_data_blocks(s, settings, length);
+ if (!gcc_read_server_data_blocks(s, settings, length))
+ {
+ printf("gcc_read_conference_create_response: gcc_read_server_data_blocks failed\n");
+ return false;
+ }
+
+ return true;
}
-void gcc_write_client_data_blocks(STREAM* s, rdpSettings *settings)
+void gcc_write_conference_create_response(STREAM* s, STREAM* user_data)
+{
+ /* ConnectData */
+ per_write_choice(s, 0);
+ per_write_object_identifier(s, t124_02_98_oid);
+
+ /* ConnectData::connectPDU (OCTET_STRING) */
+ per_write_length(s, stream_get_length(user_data) + 2);
+
+ /* ConnectGCCPDU */
+ per_write_choice(s, 0x14);
+
+ /* ConferenceCreateResponse::nodeID (UserID) */
+ per_write_integer16(s, 0x79F3, 1001);
+
+ /* ConferenceCreateResponse::tag (INTEGER) */
+ per_write_integer(s, 1);
+
+ /* ConferenceCreateResponse::result (ENUMERATED) */
+ per_write_enumerated(s, 0, MCS_Result_enum_length);
+
+ /* number of UserData sets */
+ per_write_number_of_sets(s, 1);
+
+ /* UserData::value present + select h221NonStandard (1) */
+ per_write_choice(s, 0xC0);
+
+ /* h221NonStandard */
+ per_write_octet_string(s, h221_sc_key, 4, 4); /* h221NonStandard, server-to-client H.221 key, "McDn" */
+
+ /* userData (OCTET_STRING) */
+ per_write_octet_string(s, user_data->data, stream_get_length(user_data), 0); /* array of server data blocks */
+}
+
+boolean gcc_read_client_data_blocks(STREAM* s, rdpSettings *settings, int length)
+{
+ uint16 type;
+ uint16 blockLength;
+ int pos;
+
+ while (length > 0)
+ {
+ pos = stream_get_pos(s);
+ gcc_read_user_data_header(s, &type, &blockLength);
+
+ switch (type)
+ {
+ case CS_CORE:
+ if (!gcc_read_client_core_data(s, settings, blockLength - 4))
+ return false;
+ break;
+
+ case CS_SECURITY:
+ if (!gcc_read_client_security_data(s, settings, blockLength - 4))
+ return false;
+ break;
+
+ case CS_NET:
+ if (!gcc_read_client_network_data(s, settings, blockLength - 4))
+ return false;
+ break;
+
+ case CS_CLUSTER:
+ if (!gcc_read_client_cluster_data(s, settings, blockLength - 4))
+ return false;
+ break;
+
+ case CS_MONITOR:
+ if (!gcc_read_client_monitor_data(s, settings, blockLength - 4))
+ return false;
+ break;
+
+ default:
+ break;
+ }
+
+ length -= blockLength;
+ stream_set_pos(s, pos + blockLength);
+ }
+
+ return true;
+}
+
+void gcc_write_client_data_blocks(STREAM* s, rdpSettings* settings)
{
gcc_write_client_core_data(s, settings);
gcc_write_client_cluster_data(s, settings);
gcc_write_client_security_data(s, settings);
gcc_write_client_network_data(s, settings);
- gcc_write_client_monitor_data(s, settings);
+
+ /* extended client data supported */
+
+ if (settings->negotiationFlags)
+ gcc_write_client_monitor_data(s, settings);
}
-void gcc_read_server_data_blocks(STREAM* s, rdpSettings *settings, int length)
+boolean gcc_read_server_data_blocks(STREAM* s, rdpSettings* settings, int length)
{
uint16 type;
uint16 offset = 0;
uint16 blockLength;
+ uint8* holdp;
while (offset < length)
{
- gcc_read_user_data_header(s, &type, &blockLength);
+ holdp = s->p;
+
+ if (!gcc_read_user_data_header(s, &type, &blockLength))
+ {
+ printf("gcc_read_server_data_blocks: gcc_read_user_data_header failed\n");
+ return false;
+ }
switch (type)
{
case SC_CORE:
- gcc_read_server_core_data(s, settings);
+ if (!gcc_read_server_core_data(s, settings))
+ {
+ printf("gcc_read_server_data_blocks: gcc_read_server_core_data failed\n");
+ return false;
+ }
break;
case SC_SECURITY:
- gcc_read_server_security_data(s, settings);
+ if (!gcc_read_server_security_data(s, settings))
+ {
+ printf("gcc_read_server_data_blocks: gcc_read_server_security_data failed\n");
+ return false;
+ }
break;
case SC_NET:
- gcc_read_server_network_data(s, settings);
+ if (!gcc_read_server_network_data(s, settings))
+ {
+ printf("gcc_read_server_data_blocks: gcc_read_server_network_data failed\n");
+ return false;
+ }
break;
default:
break;
}
-
offset += blockLength;
+ s->p = holdp + blockLength;
}
+
+ return true;
+}
+
+void gcc_write_server_data_blocks(STREAM* s, rdpSettings *settings)
+{
+ gcc_write_server_core_data(s, settings);
+ gcc_write_server_network_data(s, settings);
+ gcc_write_server_security_data(s, settings);
}
-void gcc_read_user_data_header(STREAM* s, uint16* type, uint16* length)
+boolean gcc_read_user_data_header(STREAM* s, uint16* type, uint16* length)
{
stream_read_uint16(s, *type); /* type */
stream_read_uint16(s, *length); /* length */
+
+ if (*length < 4)
+ return false;
+
+ if (stream_get_left(s) < *length - 4)
+ return false;
+
+ return true;
}
/**
}
/**
+ * Read a client core data block (TS_UD_CS_CORE).\n
+ * @msdn{cc240510}
+ * @param s stream
+ * @param settings rdp settings
+ */
+
+boolean gcc_read_client_core_data(STREAM* s, rdpSettings *settings, uint16 blockLength)
+{
+ uint32 version;
+ uint16 colorDepth = 0;
+ uint16 postBeta2ColorDepth = 0;
+ uint16 highColorDepth = 0;
+ uint16 supportedColorDepths = 0;
+ uint16 earlyCapabilityFlags = 0;
+ uint32 serverSelectedProtocol = 0;
+ char* str;
+
+ /* Length of all required fields, until imeFileName */
+ if (blockLength < 128)
+ return false;
+
+ stream_read_uint32(s, version); /* version */
+ settings->rdp_version = (version == RDP_VERSION_4 ? 4 : 7);
+
+ stream_read_uint16(s, settings->width); /* desktopWidth */
+ stream_read_uint16(s, settings->height); /* desktopHeight */
+ stream_read_uint16(s, colorDepth); /* colorDepth */
+ stream_seek_uint16(s); /* SASSequence (Secure Access Sequence) */
+ stream_read_uint32(s, settings->kbd_layout); /* keyboardLayout */
+ stream_read_uint32(s, settings->client_build); /* clientBuild */
+
+ /* clientName (32 bytes, null-terminated unicode, truncated to 15 characters) */
+ str = freerdp_uniconv_in(settings->uniconv, stream_get_tail(s), 32);
+ stream_seek(s, 32);
+ snprintf(settings->client_hostname, sizeof(settings->client_hostname), "%s", str);
+ xfree(str);
+
+ stream_read_uint32(s, settings->kbd_type); /* keyboardType */
+ stream_read_uint32(s, settings->kbd_subtype); /* keyboardSubType */
+ stream_read_uint32(s, settings->kbd_fn_keys); /* keyboardFunctionKey */
+
+ stream_seek(s, 64); /* imeFileName */
+
+ blockLength -= 128;
+
+ /**
+ * The following fields are all optional. If one field is present, all of the preceding
+ * fields MUST also be present. If one field is not present, all of the subsequent fields
+ * MUST NOT be present.
+ * We must check the bytes left before reading each field.
+ */
+
+ do
+ {
+ if (blockLength < 2)
+ break;
+ stream_read_uint16(s, postBeta2ColorDepth); /* postBeta2ColorDepth */
+ blockLength -= 2;
+
+ if (blockLength < 2)
+ break;
+ stream_seek_uint16(s); /* clientProductID */
+ blockLength -= 2;
+
+ if (blockLength < 4)
+ break;
+ stream_seek_uint32(s); /* serialNumber */
+ blockLength -= 4;
+
+ if (blockLength < 2)
+ break;
+ stream_read_uint16(s, highColorDepth); /* highColorDepth */
+ blockLength -= 2;
+
+ if (blockLength < 2)
+ break;
+ stream_read_uint16(s, supportedColorDepths); /* supportedColorDepths */
+ blockLength -= 2;
+
+ if (blockLength < 2)
+ break;
+ stream_read_uint16(s, earlyCapabilityFlags); /* earlyCapabilityFlags */
+ blockLength -= 2;
+
+ if (blockLength < 64)
+ break;
+ str = freerdp_uniconv_in(settings->uniconv, stream_get_tail(s), 64);
+ stream_seek(s, 64);
+ snprintf(settings->client_product_id, sizeof(settings->client_product_id), "%s", str);
+ xfree(str);
+ blockLength -= 64;
+
+ if (blockLength < 1)
+ break;
+ stream_read_uint8(s, settings->performance_flags); /* connectionType */
+ blockLength -= 1;
+
+ if (blockLength < 1)
+ break;
+ stream_seek_uint8(s); /* pad1octet */
+ blockLength -= 1;
+
+ if (blockLength < 4)
+ break;
+ stream_read_uint32(s, serverSelectedProtocol); /* serverSelectedProtocol */
+ blockLength -= 4;
+
+ if (settings->selected_protocol != serverSelectedProtocol)
+ return false;
+ } while (0);
+
+ if (highColorDepth > 0)
+ settings->color_depth = highColorDepth;
+ else if (postBeta2ColorDepth > 0)
+ {
+ switch (postBeta2ColorDepth)
+ {
+ case RNS_UD_COLOR_4BPP:
+ settings->color_depth = 4;
+ break;
+ case RNS_UD_COLOR_8BPP:
+ settings->color_depth = 8;
+ break;
+ case RNS_UD_COLOR_16BPP_555:
+ settings->color_depth = 15;
+ break;
+ case RNS_UD_COLOR_16BPP_565:
+ settings->color_depth = 16;
+ break;
+ case RNS_UD_COLOR_24BPP:
+ settings->color_depth = 24;
+ break;
+ default:
+ return false;
+ }
+ }
+ else
+ {
+ switch (colorDepth)
+ {
+ case RNS_UD_COLOR_4BPP:
+ settings->color_depth = 4;
+ break;
+ case RNS_UD_COLOR_8BPP:
+ settings->color_depth = 8;
+ break;
+ default:
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/**
* Write a client core data block (TS_UD_CS_CORE).\n
* @msdn{cc240510}
* @param s stream
RNS_UD_16BPP_SUPPORT |
RNS_UD_15BPP_SUPPORT;
- connectionType = 0;
+ connectionType = settings->connection_type;
earlyCapabilityFlags = RNS_UD_CS_SUPPORT_ERRINFO_PDU;
- if (settings->performance_flags == PERF_FLAG_NONE)
- {
- earlyCapabilityFlags |= RNS_UD_CS_VALID_CONNECTION_TYPE;
+ if (settings->rfx_codec)
connectionType = CONNECTION_TYPE_LAN;
- }
+
+ if (connectionType != 0)
+ earlyCapabilityFlags |= RNS_UD_CS_VALID_CONNECTION_TYPE;
if (settings->color_depth == 32)
{
stream_write_uint32(s, settings->selected_protocol); /* serverSelectedProtocol */
}
-void gcc_read_server_core_data(STREAM* s, rdpSettings *settings)
+boolean gcc_read_server_core_data(STREAM* s, rdpSettings *settings)
{
uint32 version;
uint32 clientRequestedProtocols;
settings->rdp_version = 4;
else if (version == RDP_VERSION_5_PLUS && settings->rdp_version < 5)
settings->rdp_version = 7;
+
+ return true;
+}
+
+void gcc_write_server_core_data(STREAM* s, rdpSettings *settings)
+{
+ gcc_write_user_data_header(s, SC_CORE, 12);
+
+ stream_write_uint32(s, settings->rdp_version == 4 ? RDP_VERSION_4 : RDP_VERSION_5_PLUS);
+ stream_write_uint32(s, settings->requested_protocols); /* clientRequestedProtocols */
+}
+
+/**
+ * Read a client security data block (TS_UD_CS_SEC).\n
+ * @msdn{cc240511}
+ * @param s stream
+ * @param settings rdp settings
+ */
+
+boolean gcc_read_client_security_data(STREAM* s, rdpSettings *settings, uint16 blockLength)
+{
+ if (blockLength < 8)
+ return false;
+
+ stream_read_uint32(s, settings->encryption_method); /* encryptionMethods */
+ if (settings->encryption_method == 0)
+ stream_read_uint32(s, settings->encryption_method); /* extEncryptionMethods */
+
+ return true;
}
/**
{
gcc_write_user_data_header(s, CS_SECURITY, 12);
- if (settings->encryption > 0)
+ if (settings->encryption)
{
stream_write_uint32(s, settings->encryption_method); /* encryptionMethods */
stream_write_uint32(s, 0); /* extEncryptionMethods */
}
}
-void gcc_read_server_security_data(STREAM* s, rdpSettings *settings)
+boolean gcc_read_server_security_data(STREAM* s, rdpSettings *settings)
{
- uint32 encryptionMethod;
- uint32 encryptionLevel;
+ uint8* data;
+ uint32 length;
uint32 serverRandomLen;
uint32 serverCertLen;
- stream_read_uint32(s, encryptionMethod); /* encryptionMethod */
- stream_read_uint32(s, encryptionLevel); /* encryptionLevel */
- stream_read_uint32(s, serverRandomLen); /* serverRandomLen */
- stream_read_uint32(s, serverCertLen); /* serverCertLen */
+ stream_read_uint32(s, settings->encryption_method); /* encryptionMethod */
+ stream_read_uint32(s, settings->encryption_level); /* encryptionLevel */
- if (encryptionMethod == 0 && encryptionLevel == 0)
+ if (settings->encryption_method == 0 && settings->encryption_level == 0)
{
/* serverRandom and serverRandom must not be present */
- return;
+ settings->encryption = false;
+ settings->encryption_method = ENCRYPTION_METHOD_NONE;
+ settings->encryption_level = ENCRYPTION_LEVEL_NONE;
+ return true;
}
+ stream_read_uint32(s, serverRandomLen); /* serverRandomLen */
+ stream_read_uint32(s, serverCertLen); /* serverCertLen */
+
if (serverRandomLen > 0)
{
/* serverRandom */
- freerdp_blob_alloc(&settings->server_random, serverRandomLen);
- memcpy(settings->server_random.data, s->p, serverRandomLen);
- stream_seek(s, serverRandomLen);
+ freerdp_blob_alloc(settings->server_random, serverRandomLen);
+ stream_read(s, settings->server_random->data, serverRandomLen);
+ }
+ else
+ {
+ return false;
}
if (serverCertLen > 0)
{
/* serverCertificate */
- freerdp_blob_alloc(&settings->server_certificate, serverCertLen);
- memcpy(settings->server_certificate.data, s->p, serverCertLen);
- stream_seek(s, serverCertLen);
+ freerdp_blob_alloc(settings->server_certificate, serverCertLen);
+ stream_read(s, settings->server_certificate->data, serverCertLen);
+ certificate_free(settings->server_cert);
+ settings->server_cert = certificate_new();
+ data = settings->server_certificate->data;
+ length = settings->server_certificate->length;
+
+ if (!certificate_read_server_certificate(settings->server_cert, data, length))
+ return false;
+ }
+ else
+ {
+ return false;
}
+
+ return true;
+}
+
+void gcc_write_server_security_data(STREAM* s, rdpSettings *settings)
+{
+ gcc_write_user_data_header(s, SC_SECURITY, 12);
+
+ stream_write_uint32(s, ENCRYPTION_METHOD_NONE); /* encryptionMethod */
+ stream_write_uint32(s, ENCRYPTION_LEVEL_NONE); /* encryptionLevel */
+#if 0
+ stream_write_uint32(s, 0); /* serverRandomLen */
+ stream_write_uint32(s, 0); /* serverCertLen */
+#endif
+}
+
+/**
+ * Read a client network data block (TS_UD_CS_NET).\n
+ * @msdn{cc240512}
+ * @param s stream
+ * @param settings rdp settings
+ */
+
+boolean gcc_read_client_network_data(STREAM* s, rdpSettings *settings, uint16 blockLength)
+{
+ int i;
+
+ if (blockLength < 4)
+ return false;
+
+ stream_read_uint32(s, settings->num_channels); /* channelCount */
+ if (blockLength < 4 + settings->num_channels * 12)
+ return false;
+ if (settings->num_channels > 16)
+ return false;
+
+ /* channelDefArray */
+ for (i = 0; i < settings->num_channels; i++)
+ {
+ /* CHANNEL_DEF */
+ stream_read(s, settings->channels[i].name, 8); /* name (8 bytes) */
+ stream_read_uint32(s, settings->channels[i].options); /* options (4 bytes) */
+ settings->channels[i].channel_id = MCS_GLOBAL_CHANNEL_ID + 1 + i;
+ }
+
+ return true;
}
/**
}
}
-void gcc_read_server_network_data(STREAM* s, rdpSettings *settings)
+boolean gcc_read_server_network_data(STREAM* s, rdpSettings *settings)
{
int i;
uint16 MCSChannelId;
for (i = 0; i < channelCount; i++)
{
stream_read_uint16(s, channelId); /* channelId */
- settings->channels[i].chan_id = channelId;
+ settings->channels[i].channel_id = channelId;
}
if (channelCount % 2 == 1)
stream_seek(s, 2); /* padding */
+
+ return true;
+}
+
+void gcc_write_server_network_data(STREAM* s, rdpSettings *settings)
+{
+ int i;
+
+ gcc_write_user_data_header(s, SC_NET, 8 + settings->num_channels * 2 + (settings->num_channels % 2 == 1 ? 2 : 0));
+
+ stream_write_uint16(s, MCS_GLOBAL_CHANNEL_ID); /* MCSChannelId */
+ stream_write_uint16(s, settings->num_channels); /* channelCount */
+
+ for (i = 0; i < settings->num_channels; i++)
+ {
+ stream_write_uint16(s, settings->channels[i].channel_id);
+ }
+
+ if (settings->num_channels % 2 == 1)
+ stream_write_uint16(s, 0);
+}
+
+/**
+ * Read a client cluster data block (TS_UD_CS_CLUSTER).\n
+ * @msdn{cc240514}
+ * @param s stream
+ * @param settings rdp settings
+ */
+
+boolean gcc_read_client_cluster_data(STREAM* s, rdpSettings *settings, uint16 blockLength)
+{
+ uint32 flags;
+
+ if (blockLength < 8)
+ return false;
+
+ stream_read_uint32(s, flags); /* flags */
+
+ if ((flags | REDIRECTED_SESSIONID_FIELD_VALID))
+ stream_read_uint32(s, settings->redirected_session_id); /* redirectedSessionID */
+
+ return true;
}
/**
}
/**
+ * Read a client monitor data block (TS_UD_CS_MONITOR).\n
+ * @msdn{dd305336}
+ * @param s stream
+ * @param settings rdp settings
+ */
+
+boolean gcc_read_client_monitor_data(STREAM* s, rdpSettings *settings, uint16 blockLength)
+{
+ printf("CS_MONITOR\n");
+ return true;
+}
+
+/**
* Write a client monitor data block (TS_UD_CS_MONITOR).\n
* @msdn{dd305336}
* @param s stream
}
}
}
+