939f1c639a
[MS-RDPBCGR] Section 5.3 describes the encryption level and method values for standard RDP security. Looking at the current usage of these values in the FreeRDP code gives me reason to believe that there is a certain lack of understanding of how these values should be handled. The encryption level is only configured on the server side in the "Encryption Level" setting found in the Remote Desktop Session Host Configuration RDP-Tcp properties dialog and this value is never transferred from the client to the server over the wire. The possible options are "None", "Low", "Client Compatible", "High" and "FIPS Compliant". The client receices this value in the Server Security Data block (TS_UD_SC_SEC1), probably only for informational purposes and maybe to give the client the possibility to verify if the server's decision for the encryption method confirms to the server's encryption level. The possible encryption methods are "NONE", "40BIT", "56BIT", "128BIT" and "FIPS" and the RDP client advertises the ones it supports to the server in the Client Security Data block (TS_UD_CS_SEC). The server's configured encryption level value restricts the possible final encryption method. Something that I was not able to find in the documentation is the priority level of the individual encryption methods based on which the server makes its final method decision if there are several options. My analysis with Windows Servers reveiled that the order is 128, 56, 40, FIPS. The server only chooses FIPS if the level is "FIPS Comliant" or if it is the only method advertised by the client. Bottom line: * FreeRDP's client side does not need to set settings->EncryptionLevel (which was done quite frequently). * FreeRDP's server side does not have to set the supported encryption methods list in settings->EncryptionMethods Changes in this commit: Removed unnecessary/confusing changes of EncryptionLevel/Methods settings Refactor settings->DisableEncryption * This value actually means "Advanced RDP Encryption (NLA/TLS) is NOT used" * The old name caused lots of confusion among developers * Renamed it to "UseRdpSecurityLayer" (the compare logic stays untouched) Any client's setting of settings->EncryptionMethods were annihilated * All clients "want" to set all supported methods * Some clients forgot 56bit because 56bit was not supported at the time the code was written * settings->EncryptionMethods was overwritten anyways in nego_connect() * Removed all client side settings of settings->EncryptionMethods The default is "None" (0) * Changed nego_connect() to advertise all supported methods if settings->EncryptionMethods is 0 (None) * Added a commandline option /encryption-methods:comma separated list of the values "40", "56", "128", "FIPS". E.g. /encryption-methods:56,128 * Print warning if server chooses non-advertised method Verify received level and method in client's gcc_read_server_security_data * Only accept valid/known encryption methods * Verify encryption level/method combinations according to MS-RDPBCGR 5.3.2 Server implementations can now set settings->EncryptionLevel * The default for settings->EncryptionLevel is 0 (None) * nego_send_negotiation_response() changes it to ClientCompatible in that case * default to ClientCompatible if the server implementation set an invalid level Fix server's gcc_write_server_security_data * Verify server encryption level value set by server implementations * Choose rdp encryption method based on level and supported client methods * Moved FIPS to the lowest priority (only used if other methods are possible) Updated sample server * Support RDP Security (RdpKeyFile was not set) * Added commented sample code for setting the security level
1803 lines
50 KiB
C
1803 lines
50 KiB
C
/**
|
|
* FreeRDP: A Remote Desktop Protocol Implementation
|
|
* T.124 Generic Conference Control (GCC)
|
|
*
|
|
* Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
|
* Copyright 2014 Norbert Federa <norbert.federa@thincast.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.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <winpr/crt.h>
|
|
|
|
#include <freerdp/log.h>
|
|
|
|
#include "gcc.h"
|
|
#include "certificate.h"
|
|
|
|
#define TAG FREERDP_TAG("core.gcc")
|
|
|
|
/**
|
|
* T.124 GCC is defined in:
|
|
*
|
|
* http://www.itu.int/rec/T-REC-T.124-199802-S/en
|
|
* ITU-T T.124 (02/98): Generic Conference Control
|
|
*/
|
|
|
|
/**
|
|
* ConnectData ::= SEQUENCE
|
|
* {
|
|
* t124Identifier Key,
|
|
* connectPDU OCTET_STRING
|
|
* }
|
|
*
|
|
* Key ::= CHOICE
|
|
* {
|
|
* object OBJECT_IDENTIFIER,
|
|
* h221NonStandard H221NonStandardIdentifier
|
|
* }
|
|
*
|
|
* ConnectGCCPDU ::= CHOICE
|
|
* {
|
|
* conferenceCreateRequest ConferenceCreateRequest,
|
|
* conferenceCreateResponse ConferenceCreateResponse,
|
|
* conferenceQueryRequest ConferenceQueryRequest,
|
|
* conferenceQueryResponse ConferenceQueryResponse,
|
|
* conferenceJoinRequest ConferenceJoinRequest,
|
|
* conferenceJoinResponse ConferenceJoinResponse,
|
|
* conferenceInviteRequest ConferenceInviteRequest,
|
|
* conferenceInviteResponse ConferenceInviteResponse,
|
|
* ...
|
|
* }
|
|
*
|
|
* ConferenceCreateRequest ::= SEQUENCE
|
|
* {
|
|
* conferenceName ConferenceName,
|
|
* convenerPassword Password OPTIONAL,
|
|
* password Password OPTIONAL,
|
|
* lockedConference BOOLEAN,
|
|
* listedConference BOOLEAN,
|
|
* conductibleConference BOOLEAN,
|
|
* terminationMethod TerminationMethod,
|
|
* conductorPrivileges SET OF Privilege OPTIONAL,
|
|
* conductedPrivileges SET OF Privilege OPTIONAL,
|
|
* nonConductedPrivileges SET OF Privilege OPTIONAL,
|
|
* conferenceDescription TextString OPTIONAL,
|
|
* callerIdentifier TextString OPTIONAL,
|
|
* userData UserData OPTIONAL,
|
|
* ...,
|
|
* conferencePriority ConferencePriority OPTIONAL,
|
|
* conferenceMode ConferenceMode OPTIONAL
|
|
* }
|
|
*
|
|
* ConferenceCreateResponse ::= SEQUENCE
|
|
* {
|
|
* nodeID UserID,
|
|
* tag INTEGER,
|
|
* result ENUMERATED
|
|
* {
|
|
* success (0),
|
|
* userRejected (1),
|
|
* resourcesNotAvailable (2),
|
|
* rejectedForSymmetryBreaking (3),
|
|
* lockedConferenceNotSupported (4)
|
|
* },
|
|
* userData UserData OPTIONAL,
|
|
* ...
|
|
* }
|
|
*
|
|
* ConferenceName ::= SEQUENCE
|
|
* {
|
|
* numeric SimpleNumericString
|
|
* text SimpleTextString OPTIONAL,
|
|
* ...
|
|
* }
|
|
*
|
|
* SimpleNumericString ::= NumericString (SIZE (1..255)) (FROM ("0123456789"))
|
|
*
|
|
* UserData ::= SET OF SEQUENCE
|
|
* {
|
|
* key Key,
|
|
* value OCTET_STRING OPTIONAL
|
|
* }
|
|
*
|
|
* H221NonStandardIdentifier ::= OCTET STRING (SIZE (4..255))
|
|
*
|
|
* UserID ::= DynamicChannelID
|
|
*
|
|
* ChannelID ::= INTEGER (1..65535)
|
|
* StaticChannelID ::= INTEGER (1..1000)
|
|
* DynamicChannelID ::= INTEGER (1001..65535)
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* OID = 0.0.20.124.0.1
|
|
* { itu-t(0) recommendation(0) t(20) t124(124) version(0) 1 }
|
|
* v.1 of ITU-T Recommendation T.124 (Feb 1998): "Generic Conference Control"
|
|
*/
|
|
BYTE t124_02_98_oid[6] = { 0, 0, 20, 124, 0, 1 };
|
|
|
|
BYTE h221_cs_key[4] = "Duca";
|
|
BYTE h221_sc_key[4] = "McDn";
|
|
|
|
/**
|
|
* Read a GCC Conference Create Request.\n
|
|
* @msdn{cc240836}
|
|
* @param s stream
|
|
* @param settings rdp settings
|
|
*/
|
|
|
|
BOOL gcc_read_conference_create_request(wStream* s, rdpMcs* mcs)
|
|
{
|
|
UINT16 length;
|
|
BYTE choice;
|
|
BYTE number;
|
|
BYTE 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_GetRemainingLength(s) < length)
|
|
return FALSE;
|
|
if (!gcc_read_client_data_blocks(s, mcs, length))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Write a GCC Conference Create Request.\n
|
|
* @msdn{cc240836}
|
|
* @param s stream
|
|
* @param user_data client data blocks
|
|
*/
|
|
|
|
void gcc_write_conference_create_request(wStream* s, wStream* userData)
|
|
{
|
|
/* ConnectData */
|
|
per_write_choice(s, 0); /* From Key select object (0) of type OBJECT_IDENTIFIER */
|
|
per_write_object_identifier(s, t124_02_98_oid); /* ITU-T T.124 (02/98) OBJECT_IDENTIFIER */
|
|
|
|
/* ConnectData::connectPDU (OCTET_STRING) */
|
|
per_write_length(s, Stream_GetPosition(userData) + 14); /* connectPDU length */
|
|
|
|
/* ConnectGCCPDU */
|
|
per_write_choice(s, 0); /* From ConnectGCCPDU select conferenceCreateRequest (0) of type ConferenceCreateRequest */
|
|
per_write_selection(s, 0x08); /* select optional userData from ConferenceCreateRequest */
|
|
|
|
/* ConferenceCreateRequest::conferenceName */
|
|
per_write_numeric_string(s, (BYTE*)"1", 1, 1); /* ConferenceName::numeric */
|
|
per_write_padding(s, 1); /* padding */
|
|
|
|
/* UserData (SET OF SEQUENCE) */
|
|
per_write_number_of_sets(s, 1); /* one set of UserData */
|
|
per_write_choice(s, 0xC0); /* UserData::value present + select h221NonStandard (1) */
|
|
|
|
/* h221NonStandard */
|
|
per_write_octet_string(s, h221_cs_key, 4, 4); /* h221NonStandard, client-to-server H.221 key, "Duca" */
|
|
|
|
/* userData::value (OCTET_STRING) */
|
|
per_write_octet_string(s, userData->buffer, Stream_GetPosition(userData), 0); /* array of client data blocks */
|
|
}
|
|
|
|
BOOL gcc_read_conference_create_response(wStream* s, rdpMcs* mcs)
|
|
{
|
|
UINT16 length;
|
|
UINT32 tag;
|
|
UINT16 nodeID;
|
|
BYTE result;
|
|
BYTE choice;
|
|
BYTE number;
|
|
|
|
/* ConnectData */
|
|
if (!per_read_choice(s, &choice) ||
|
|
!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;
|
|
|
|
/* ConferenceCreateResponse::nodeID (UserID) */
|
|
if (!per_read_integer16(s, &nodeID, 1001))
|
|
return FALSE;
|
|
|
|
/* ConferenceCreateResponse::tag (INTEGER) */
|
|
if (!per_read_integer(s, &tag))
|
|
return FALSE;
|
|
|
|
/* ConferenceCreateResponse::result (ENUMERATED) */
|
|
if (!per_read_enumerated(s, &result, MCS_Result_enum_length))
|
|
return FALSE;
|
|
|
|
/* number of UserData sets */
|
|
if (!per_read_number_of_sets(s, &number))
|
|
return FALSE;
|
|
|
|
/* UserData::value present + select h221NonStandard (1) */
|
|
if (!per_read_choice(s, &choice))
|
|
return FALSE;
|
|
|
|
/* h221NonStandard */
|
|
if (!per_read_octet_string(s, h221_sc_key, 4, 4)) /* h221NonStandard, server-to-client H.221 key, "McDn" */
|
|
return FALSE;
|
|
|
|
/* userData (OCTET_STRING) */
|
|
if (!per_read_length(s, &length))
|
|
return FALSE;
|
|
|
|
if (!gcc_read_server_data_blocks(s, mcs, length))
|
|
{
|
|
WLog_ERR(TAG, "gcc_read_conference_create_response: gcc_read_server_data_blocks failed");
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void gcc_write_conference_create_response(wStream* s, wStream* userData)
|
|
{
|
|
/* ConnectData */
|
|
per_write_choice(s, 0);
|
|
per_write_object_identifier(s, t124_02_98_oid);
|
|
|
|
/* ConnectData::connectPDU (OCTET_STRING) */
|
|
/* This length MUST be ignored by the client according to [MS-RDPBCGR] */
|
|
per_write_length(s, 0x2A);
|
|
|
|
/* 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, userData->buffer, Stream_GetPosition(userData), 0); /* array of server data blocks */
|
|
}
|
|
|
|
BOOL gcc_read_client_data_blocks(wStream* s, rdpMcs* mcs, int length)
|
|
{
|
|
UINT16 type;
|
|
UINT16 blockLength;
|
|
int begPos, endPos;
|
|
|
|
while (length > 0)
|
|
{
|
|
begPos = Stream_GetPosition(s);
|
|
|
|
if (!gcc_read_user_data_header(s, &type, &blockLength))
|
|
return FALSE;
|
|
|
|
if (Stream_GetRemainingLength(s) < (size_t) (blockLength - 4))
|
|
return FALSE;
|
|
|
|
switch (type)
|
|
{
|
|
case CS_CORE:
|
|
if (!gcc_read_client_core_data(s, mcs, blockLength - 4))
|
|
return FALSE;
|
|
break;
|
|
|
|
case CS_SECURITY:
|
|
if (!gcc_read_client_security_data(s, mcs, blockLength - 4))
|
|
return FALSE;
|
|
break;
|
|
|
|
case CS_NET:
|
|
if (!gcc_read_client_network_data(s, mcs, blockLength - 4))
|
|
return FALSE;
|
|
break;
|
|
|
|
case CS_CLUSTER:
|
|
if (!gcc_read_client_cluster_data(s, mcs, blockLength - 4))
|
|
return FALSE;
|
|
break;
|
|
|
|
case CS_MONITOR:
|
|
if (!gcc_read_client_monitor_data(s, mcs, blockLength - 4))
|
|
return FALSE;
|
|
break;
|
|
|
|
case CS_MCS_MSGCHANNEL:
|
|
if (!gcc_read_client_message_channel_data(s, mcs, blockLength - 4))
|
|
return FALSE;
|
|
break;
|
|
|
|
case CS_MONITOR_EX:
|
|
if (!gcc_read_client_monitor_extended_data(s, mcs, blockLength - 4))
|
|
return FALSE;
|
|
break;
|
|
|
|
case 0xC009:
|
|
case CS_MULTITRANSPORT:
|
|
if (!gcc_read_client_multitransport_channel_data(s, mcs, blockLength - 4))
|
|
return FALSE;
|
|
break;
|
|
|
|
default:
|
|
WLog_ERR(TAG, "Unknown GCC client data block: 0x%04X", type);
|
|
Stream_Seek(s, blockLength - 4);
|
|
break;
|
|
}
|
|
|
|
endPos = Stream_GetPosition(s);
|
|
|
|
if (endPos != (begPos + blockLength))
|
|
{
|
|
WLog_ERR(TAG, "Error parsing GCC client data block 0x%04X: Actual Offset: %d Expected Offset: %d",
|
|
type, endPos, begPos + blockLength);
|
|
}
|
|
|
|
length -= blockLength;
|
|
Stream_SetPosition(s, begPos + blockLength);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void gcc_write_client_data_blocks(wStream* s, rdpMcs* mcs)
|
|
{
|
|
rdpSettings* settings = mcs->settings;
|
|
|
|
gcc_write_client_core_data(s, mcs);
|
|
gcc_write_client_cluster_data(s, mcs);
|
|
gcc_write_client_security_data(s, mcs);
|
|
gcc_write_client_network_data(s, mcs);
|
|
|
|
/* extended client data supported */
|
|
|
|
if (settings->NegotiationFlags & EXTENDED_CLIENT_DATA_SUPPORTED)
|
|
{
|
|
if (settings->UseMultimon && !settings->SpanMonitors)
|
|
{
|
|
gcc_write_client_monitor_data(s, mcs);
|
|
}
|
|
|
|
gcc_write_client_message_channel_data(s, mcs);
|
|
gcc_write_client_multitransport_channel_data(s, mcs);
|
|
}
|
|
else
|
|
{
|
|
if (settings->UseMultimon && !settings->SpanMonitors)
|
|
{
|
|
WLog_ERR(TAG, "WARNING: true multi monitor support was not advertised by server!");
|
|
|
|
if (settings->ForceMultimon)
|
|
{
|
|
WLog_ERR(TAG, "Sending multi monitor information anyway (may break connectivity!)");
|
|
gcc_write_client_monitor_data(s, mcs);
|
|
}
|
|
else
|
|
{
|
|
WLog_ERR(TAG, "Use /multimon:force to force sending multi monitor information");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL gcc_read_server_data_blocks(wStream* s, rdpMcs* mcs, int length)
|
|
{
|
|
UINT16 type;
|
|
UINT16 offset = 0;
|
|
UINT16 blockLength;
|
|
BYTE* holdp;
|
|
|
|
while (offset < length)
|
|
{
|
|
holdp = Stream_Pointer(s);
|
|
|
|
if (!gcc_read_user_data_header(s, &type, &blockLength))
|
|
{
|
|
WLog_ERR(TAG, "gcc_read_server_data_blocks: gcc_read_user_data_header failed");
|
|
return FALSE;
|
|
}
|
|
|
|
switch (type)
|
|
{
|
|
case SC_CORE:
|
|
if (!gcc_read_server_core_data(s, mcs))
|
|
{
|
|
WLog_ERR(TAG, "gcc_read_server_data_blocks: gcc_read_server_core_data failed");
|
|
return FALSE;
|
|
}
|
|
break;
|
|
|
|
case SC_SECURITY:
|
|
if (!gcc_read_server_security_data(s, mcs))
|
|
{
|
|
WLog_ERR(TAG, "gcc_read_server_data_blocks: gcc_read_server_security_data failed");
|
|
return FALSE;
|
|
}
|
|
break;
|
|
|
|
case SC_NET:
|
|
if (!gcc_read_server_network_data(s, mcs))
|
|
{
|
|
WLog_ERR(TAG, "gcc_read_server_data_blocks: gcc_read_server_network_data failed");
|
|
return FALSE;
|
|
}
|
|
break;
|
|
|
|
case SC_MCS_MSGCHANNEL:
|
|
if (!gcc_read_server_message_channel_data(s, mcs))
|
|
{
|
|
WLog_ERR(TAG, "gcc_read_server_data_blocks: gcc_read_server_message_channel_data failed");
|
|
return FALSE;
|
|
}
|
|
break;
|
|
|
|
case SC_MULTITRANSPORT:
|
|
if (!gcc_read_server_multitransport_channel_data(s, mcs))
|
|
{
|
|
WLog_ERR(TAG, "gcc_read_server_data_blocks: gcc_read_server_multitransport_channel_data failed");
|
|
return FALSE;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
WLog_ERR(TAG, "gcc_read_server_data_blocks: ignoring type=%hu", type);
|
|
break;
|
|
}
|
|
offset += blockLength;
|
|
Stream_Pointer(s) = holdp + blockLength;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void gcc_write_server_data_blocks(wStream* s, rdpMcs* mcs)
|
|
{
|
|
gcc_write_server_core_data(s, mcs); /* serverCoreData */
|
|
gcc_write_server_network_data(s, mcs); /* serverNetworkData */
|
|
gcc_write_server_security_data(s, mcs); /* serverSecurityData */
|
|
gcc_write_server_message_channel_data(s, mcs); /* serverMessageChannelData */
|
|
|
|
/* TODO: Send these GCC data blocks only when the client sent them */
|
|
//gcc_write_server_multitransport_channel_data(s, settings); /* serverMultitransportChannelData */
|
|
}
|
|
|
|
BOOL gcc_read_user_data_header(wStream* s, UINT16* type, UINT16* length)
|
|
{
|
|
if (Stream_GetRemainingLength(s) < 4)
|
|
return FALSE;
|
|
|
|
Stream_Read_UINT16(s, *type); /* type */
|
|
Stream_Read_UINT16(s, *length); /* length */
|
|
|
|
if (Stream_GetRemainingLength(s) < (size_t) (*length - 4))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Write a user data header (TS_UD_HEADER).\n
|
|
* @msdn{cc240509}
|
|
* @param s stream
|
|
* @param type data block type
|
|
* @param length data block length
|
|
*/
|
|
|
|
void gcc_write_user_data_header(wStream* s, UINT16 type, UINT16 length)
|
|
{
|
|
Stream_Write_UINT16(s, type); /* type */
|
|
Stream_Write_UINT16(s, length); /* length */
|
|
}
|
|
|
|
/**
|
|
* Read a client core data block (TS_UD_CS_CORE).\n
|
|
* @msdn{cc240510}
|
|
* @param s stream
|
|
* @param settings rdp settings
|
|
*/
|
|
|
|
BOOL gcc_read_client_core_data(wStream* s, rdpMcs* mcs, UINT16 blockLength)
|
|
{
|
|
char* str = NULL;
|
|
UINT32 version;
|
|
BYTE connectionType = 0;
|
|
UINT32 clientColorDepth;
|
|
UINT16 colorDepth = 0;
|
|
UINT16 postBeta2ColorDepth = 0;
|
|
UINT16 highColorDepth = 0;
|
|
UINT16 supportedColorDepths = 0;
|
|
UINT32 serverSelectedProtocol = 0;
|
|
UINT32 desktopPhysicalWidth = 0;
|
|
UINT32 desktopPhysicalHeight = 0;
|
|
UINT16 desktopOrientation = 0;
|
|
UINT32 desktopScaleFactor = 0;
|
|
UINT32 deviceScaleFactor = 0;
|
|
UINT16 earlyCapabilityFlags = 0;
|
|
rdpSettings* settings = mcs->settings;
|
|
|
|
/* Length of all required fields, until imeFileName */
|
|
if (blockLength < 128)
|
|
return FALSE;
|
|
|
|
Stream_Read_UINT32(s, version); /* version (4 bytes) */
|
|
settings->RdpVersion = (version == RDP_VERSION_4 ? 4 : 7);
|
|
|
|
Stream_Read_UINT16(s, settings->DesktopWidth); /* DesktopWidth (2 bytes) */
|
|
Stream_Read_UINT16(s, settings->DesktopHeight); /* DesktopHeight (2 bytes) */
|
|
Stream_Read_UINT16(s, colorDepth); /* ColorDepth (2 bytes) */
|
|
Stream_Seek_UINT16(s); /* SASSequence (Secure Access Sequence) (2 bytes) */
|
|
Stream_Read_UINT32(s, settings->KeyboardLayout); /* KeyboardLayout (4 bytes) */
|
|
Stream_Read_UINT32(s, settings->ClientBuild); /* ClientBuild (4 bytes) */
|
|
|
|
/* clientName (32 bytes, null-terminated unicode, truncated to 15 characters) */
|
|
ConvertFromUnicode(CP_UTF8, 0, (WCHAR*) Stream_Pointer(s), 32 / 2, &str, 0, NULL, NULL);
|
|
Stream_Seek(s, 32);
|
|
sprintf_s(settings->ClientHostname, 31, "%s", str);
|
|
settings->ClientHostname[31] = 0;
|
|
free(str);
|
|
str = NULL;
|
|
|
|
Stream_Read_UINT32(s, settings->KeyboardType); /* KeyboardType (4 bytes) */
|
|
Stream_Read_UINT32(s, settings->KeyboardSubType); /* KeyboardSubType (4 bytes) */
|
|
Stream_Read_UINT32(s, settings->KeyboardFunctionKey); /* KeyboardFunctionKey (4 bytes) */
|
|
|
|
Stream_Seek(s, 64); /* imeFileName (64 bytes) */
|
|
|
|
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 (2 bytes) */
|
|
blockLength -= 2;
|
|
|
|
if (blockLength < 2)
|
|
break;
|
|
Stream_Seek_UINT16(s); /* clientProductID (2 bytes) */
|
|
blockLength -= 2;
|
|
|
|
if (blockLength < 4)
|
|
break;
|
|
Stream_Seek_UINT32(s); /* serialNumber (4 bytes) */
|
|
blockLength -= 4;
|
|
|
|
if (blockLength < 2)
|
|
break;
|
|
Stream_Read_UINT16(s, highColorDepth); /* highColorDepth (2 bytes) */
|
|
blockLength -= 2;
|
|
|
|
if (blockLength < 2)
|
|
break;
|
|
Stream_Read_UINT16(s, supportedColorDepths); /* supportedColorDepths (2 bytes) */
|
|
blockLength -= 2;
|
|
|
|
if (blockLength < 2)
|
|
break;
|
|
Stream_Read_UINT16(s, earlyCapabilityFlags); /* earlyCapabilityFlags (2 bytes) */
|
|
settings->EarlyCapabilityFlags = (UINT32) earlyCapabilityFlags;
|
|
blockLength -= 2;
|
|
|
|
if (blockLength < 64)
|
|
break;
|
|
|
|
ConvertFromUnicode(CP_UTF8, 0, (WCHAR*) Stream_Pointer(s), 64 / 2, &str, 0, NULL, NULL);
|
|
Stream_Seek(s, 64); /* clientDigProductId (64 bytes) */
|
|
sprintf_s(settings->ClientProductId, 32, "%s", str);
|
|
free(str);
|
|
blockLength -= 64;
|
|
|
|
if (blockLength < 1)
|
|
break;
|
|
Stream_Read_UINT8(s, connectionType); /* connectionType (1 byte) */
|
|
blockLength -= 1;
|
|
|
|
if (blockLength < 1)
|
|
break;
|
|
Stream_Seek_UINT8(s); /* pad1octet (1 byte) */
|
|
blockLength -= 1;
|
|
|
|
if (blockLength < 4)
|
|
break;
|
|
Stream_Read_UINT32(s, serverSelectedProtocol); /* serverSelectedProtocol (4 bytes) */
|
|
blockLength -= 4;
|
|
|
|
if (blockLength < 4)
|
|
break;
|
|
Stream_Read_UINT32(s, desktopPhysicalWidth); /* desktopPhysicalWidth (4 bytes) */
|
|
blockLength -= 4;
|
|
|
|
if (blockLength < 4)
|
|
break;
|
|
Stream_Read_UINT32(s, desktopPhysicalHeight); /* desktopPhysicalHeight (4 bytes) */
|
|
blockLength -= 4;
|
|
|
|
if (blockLength < 2)
|
|
break;
|
|
Stream_Read_UINT16(s, desktopOrientation); /* desktopOrientation (2 bytes) */
|
|
blockLength -= 2;
|
|
|
|
if (blockLength < 4)
|
|
break;
|
|
Stream_Read_UINT32(s, desktopScaleFactor); /* desktopScaleFactor (4 bytes) */
|
|
blockLength -= 4;
|
|
|
|
if (blockLength < 4)
|
|
break;
|
|
Stream_Read_UINT32(s, deviceScaleFactor); /* deviceScaleFactor (4 bytes) */
|
|
blockLength -= 4;
|
|
|
|
if (settings->SelectedProtocol != serverSelectedProtocol)
|
|
return FALSE;
|
|
} while (0);
|
|
|
|
if (highColorDepth > 0)
|
|
{
|
|
if (earlyCapabilityFlags & RNS_UD_CS_WANT_32BPP_SESSION)
|
|
clientColorDepth = 32;
|
|
else
|
|
clientColorDepth = highColorDepth;
|
|
}
|
|
else if (postBeta2ColorDepth > 0)
|
|
{
|
|
switch (postBeta2ColorDepth)
|
|
{
|
|
case RNS_UD_COLOR_4BPP:
|
|
clientColorDepth = 4;
|
|
break;
|
|
case RNS_UD_COLOR_8BPP:
|
|
clientColorDepth = 8;
|
|
break;
|
|
case RNS_UD_COLOR_16BPP_555:
|
|
clientColorDepth = 15;
|
|
break;
|
|
case RNS_UD_COLOR_16BPP_565:
|
|
clientColorDepth = 16;
|
|
break;
|
|
case RNS_UD_COLOR_24BPP:
|
|
clientColorDepth = 24;
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (colorDepth)
|
|
{
|
|
case RNS_UD_COLOR_4BPP:
|
|
clientColorDepth = 4;
|
|
break;
|
|
case RNS_UD_COLOR_8BPP:
|
|
clientColorDepth = 8;
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If we are in server mode, accept client's color depth only if
|
|
* it is smaller than ours. This is what Windows server does.
|
|
*/
|
|
if ((clientColorDepth < settings->ColorDepth) || !settings->ServerMode)
|
|
settings->ColorDepth = clientColorDepth;
|
|
|
|
if (settings->NetworkAutoDetect)
|
|
settings->NetworkAutoDetect = (earlyCapabilityFlags & RNS_UD_CS_SUPPORT_NETWORK_AUTODETECT) ? TRUE : FALSE;
|
|
|
|
if (settings->SupportHeartbeatPdu)
|
|
settings->SupportHeartbeatPdu = (earlyCapabilityFlags & RNS_UD_CS_SUPPORT_HEARTBEAT_PDU) ? TRUE : FALSE;
|
|
|
|
if (settings->SupportGraphicsPipeline)
|
|
settings->SupportGraphicsPipeline = (earlyCapabilityFlags & RNS_UD_CS_SUPPORT_DYNVC_GFX_PROTOCOL) ? TRUE : FALSE;
|
|
|
|
if (settings->SupportDynamicTimeZone)
|
|
settings->SupportDynamicTimeZone = (earlyCapabilityFlags & RNS_UD_CS_SUPPORT_DYNAMIC_TIME_ZONE) ? TRUE : FALSE;
|
|
|
|
if (!(earlyCapabilityFlags & RNS_UD_CS_VALID_CONNECTION_TYPE))
|
|
connectionType = 0;
|
|
|
|
settings->ConnectionType = connectionType;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Write a client core data block (TS_UD_CS_CORE).\n
|
|
* @msdn{cc240510}
|
|
* @param s stream
|
|
* @param settings rdp settings
|
|
*/
|
|
|
|
void gcc_write_client_core_data(wStream* s, rdpMcs* mcs)
|
|
{
|
|
UINT32 version;
|
|
WCHAR* clientName = NULL;
|
|
int clientNameLength;
|
|
BYTE connectionType;
|
|
UINT16 highColorDepth;
|
|
UINT16 supportedColorDepths;
|
|
UINT16 earlyCapabilityFlags;
|
|
WCHAR* clientDigProductId = NULL;
|
|
int clientDigProductIdLength;
|
|
rdpSettings* settings = mcs->settings;
|
|
|
|
gcc_write_user_data_header(s, CS_CORE, 216);
|
|
|
|
version = settings->RdpVersion >= 5 ? RDP_VERSION_5_PLUS : RDP_VERSION_4;
|
|
|
|
clientNameLength = ConvertToUnicode(CP_UTF8, 0, settings->ClientHostname, -1, &clientName, 0);
|
|
clientDigProductIdLength = ConvertToUnicode(CP_UTF8, 0, settings->ClientProductId, -1, &clientDigProductId, 0);
|
|
|
|
Stream_Write_UINT32(s, version); /* Version */
|
|
Stream_Write_UINT16(s, settings->DesktopWidth); /* DesktopWidth */
|
|
Stream_Write_UINT16(s, settings->DesktopHeight); /* DesktopHeight */
|
|
Stream_Write_UINT16(s, RNS_UD_COLOR_8BPP); /* ColorDepth, ignored because of postBeta2ColorDepth */
|
|
Stream_Write_UINT16(s, RNS_UD_SAS_DEL); /* SASSequence (Secure Access Sequence) */
|
|
Stream_Write_UINT32(s, settings->KeyboardLayout); /* KeyboardLayout */
|
|
Stream_Write_UINT32(s, settings->ClientBuild); /* ClientBuild */
|
|
|
|
/* clientName (32 bytes, null-terminated unicode, truncated to 15 characters) */
|
|
|
|
if (clientNameLength >= 16)
|
|
{
|
|
clientNameLength = 16;
|
|
clientName[clientNameLength - 1] = 0;
|
|
}
|
|
|
|
Stream_Write(s, clientName, (clientNameLength * 2));
|
|
Stream_Zero(s, 32 - (clientNameLength * 2));
|
|
free(clientName);
|
|
|
|
Stream_Write_UINT32(s, settings->KeyboardType); /* KeyboardType */
|
|
Stream_Write_UINT32(s, settings->KeyboardSubType); /* KeyboardSubType */
|
|
Stream_Write_UINT32(s, settings->KeyboardFunctionKey); /* KeyboardFunctionKey */
|
|
|
|
Stream_Zero(s, 64); /* imeFileName */
|
|
|
|
Stream_Write_UINT16(s, RNS_UD_COLOR_8BPP); /* postBeta2ColorDepth */
|
|
Stream_Write_UINT16(s, 1); /* clientProductID */
|
|
Stream_Write_UINT32(s, 0); /* serialNumber (should be initialized to 0) */
|
|
|
|
highColorDepth = MIN(settings->ColorDepth, 24);
|
|
|
|
supportedColorDepths =
|
|
RNS_UD_24BPP_SUPPORT |
|
|
RNS_UD_16BPP_SUPPORT |
|
|
RNS_UD_15BPP_SUPPORT;
|
|
|
|
earlyCapabilityFlags = RNS_UD_CS_SUPPORT_ERRINFO_PDU;
|
|
|
|
if (settings->NetworkAutoDetect)
|
|
settings->ConnectionType = CONNECTION_TYPE_AUTODETECT;
|
|
|
|
if (settings->RemoteFxCodec && !settings->NetworkAutoDetect)
|
|
settings->ConnectionType = CONNECTION_TYPE_LAN;
|
|
|
|
connectionType = settings->ConnectionType;
|
|
|
|
if (connectionType)
|
|
earlyCapabilityFlags |= RNS_UD_CS_VALID_CONNECTION_TYPE;
|
|
|
|
if (settings->ColorDepth == 32)
|
|
{
|
|
supportedColorDepths |= RNS_UD_32BPP_SUPPORT;
|
|
earlyCapabilityFlags |= RNS_UD_CS_WANT_32BPP_SESSION;
|
|
}
|
|
|
|
if (settings->NetworkAutoDetect)
|
|
earlyCapabilityFlags |= RNS_UD_CS_SUPPORT_NETWORK_AUTODETECT;
|
|
|
|
if (settings->SupportHeartbeatPdu)
|
|
earlyCapabilityFlags |= RNS_UD_CS_SUPPORT_HEARTBEAT_PDU;
|
|
|
|
if (settings->SupportGraphicsPipeline)
|
|
earlyCapabilityFlags |= RNS_UD_CS_SUPPORT_DYNVC_GFX_PROTOCOL;
|
|
|
|
if (settings->SupportDynamicTimeZone)
|
|
earlyCapabilityFlags |= RNS_UD_CS_SUPPORT_DYNAMIC_TIME_ZONE;
|
|
|
|
Stream_Write_UINT16(s, highColorDepth); /* highColorDepth */
|
|
Stream_Write_UINT16(s, supportedColorDepths); /* supportedColorDepths */
|
|
|
|
Stream_Write_UINT16(s, earlyCapabilityFlags); /* earlyCapabilityFlags */
|
|
|
|
/* clientDigProductId (64 bytes, null-terminated unicode, truncated to 31 characters) */
|
|
if (clientDigProductIdLength >= 32)
|
|
{
|
|
clientDigProductIdLength = 32;
|
|
clientDigProductId[clientDigProductIdLength - 1] = 0;
|
|
}
|
|
Stream_Write(s, clientDigProductId, (clientDigProductIdLength * 2) );
|
|
Stream_Zero(s, 64 - (clientDigProductIdLength * 2) );
|
|
free(clientDigProductId);
|
|
|
|
Stream_Write_UINT8(s, connectionType); /* connectionType */
|
|
Stream_Write_UINT8(s, 0); /* pad1octet */
|
|
|
|
Stream_Write_UINT32(s, settings->SelectedProtocol); /* serverSelectedProtocol */
|
|
}
|
|
|
|
BOOL gcc_read_server_core_data(wStream* s, rdpMcs* mcs)
|
|
{
|
|
UINT32 version;
|
|
UINT32 clientRequestedProtocols;
|
|
UINT32 earlyCapabilityFlags;
|
|
rdpSettings* settings = mcs->settings;
|
|
|
|
if (Stream_GetRemainingLength(s) < 4)
|
|
return FALSE;
|
|
|
|
Stream_Read_UINT32(s, version); /* version */
|
|
|
|
if (version == RDP_VERSION_4 && settings->RdpVersion > 4)
|
|
settings->RdpVersion = 4;
|
|
else if (version == RDP_VERSION_5_PLUS && settings->RdpVersion < 5)
|
|
settings->RdpVersion = 7;
|
|
|
|
if (Stream_GetRemainingLength(s) >= 4)
|
|
{
|
|
Stream_Read_UINT32(s, clientRequestedProtocols); /* clientRequestedProtocols */
|
|
}
|
|
|
|
if (Stream_GetRemainingLength(s) >= 4)
|
|
{
|
|
Stream_Read_UINT32(s, earlyCapabilityFlags); /* earlyCapabilityFlags */
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void gcc_write_server_core_data(wStream* s, rdpMcs* mcs)
|
|
{
|
|
UINT32 version;
|
|
UINT32 earlyCapabilityFlags = 0;
|
|
rdpSettings* settings = mcs->settings;
|
|
|
|
gcc_write_user_data_header(s, SC_CORE, 16);
|
|
|
|
version = settings->RdpVersion == 4 ? RDP_VERSION_4 : RDP_VERSION_5_PLUS;
|
|
|
|
if (settings->SupportDynamicTimeZone)
|
|
earlyCapabilityFlags |= RNS_UD_SC_DYNAMIC_DST_SUPPORTED;
|
|
|
|
Stream_Write_UINT32(s, version); /* version (4 bytes) */
|
|
Stream_Write_UINT32(s, settings->RequestedProtocols); /* clientRequestedProtocols (4 bytes) */
|
|
Stream_Write_UINT32(s, earlyCapabilityFlags); /* earlyCapabilityFlags (4 bytes) */
|
|
}
|
|
|
|
/**
|
|
* Read a client security data block (TS_UD_CS_SEC).\n
|
|
* @msdn{cc240511}
|
|
* @param s stream
|
|
* @param settings rdp settings
|
|
*/
|
|
|
|
BOOL gcc_read_client_security_data(wStream* s, rdpMcs* mcs, UINT16 blockLength)
|
|
{
|
|
rdpSettings* settings = mcs->settings;
|
|
|
|
if (blockLength < 8)
|
|
return FALSE;
|
|
|
|
if (settings->UseRdpSecurityLayer)
|
|
{
|
|
Stream_Read_UINT32(s, settings->EncryptionMethods); /* encryptionMethods */
|
|
if (settings->EncryptionMethods == 0)
|
|
Stream_Read_UINT32(s, settings->EncryptionMethods); /* extEncryptionMethods */
|
|
else
|
|
Stream_Seek(s, 4);
|
|
}
|
|
else
|
|
{
|
|
Stream_Seek(s, 8);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Write a client security data block (TS_UD_CS_SEC).\n
|
|
* @msdn{cc240511}
|
|
* @param s stream
|
|
* @param settings rdp settings
|
|
*/
|
|
|
|
void gcc_write_client_security_data(wStream* s, rdpMcs* mcs)
|
|
{
|
|
rdpSettings* settings = mcs->settings;
|
|
|
|
gcc_write_user_data_header(s, CS_SECURITY, 12);
|
|
|
|
if (settings->UseRdpSecurityLayer)
|
|
{
|
|
Stream_Write_UINT32(s, settings->EncryptionMethods); /* encryptionMethods */
|
|
Stream_Write_UINT32(s, 0); /* extEncryptionMethods */
|
|
}
|
|
else
|
|
{
|
|
/* French locale, disable encryption */
|
|
Stream_Write_UINT32(s, 0); /* encryptionMethods */
|
|
Stream_Write_UINT32(s, settings->EncryptionMethods); /* extEncryptionMethods */
|
|
}
|
|
}
|
|
|
|
BOOL gcc_read_server_security_data(wStream* s, rdpMcs* mcs)
|
|
{
|
|
BYTE* data;
|
|
UINT32 length;
|
|
rdpSettings* settings = mcs->settings;
|
|
BOOL validCryptoConfig = FALSE;
|
|
UINT32 serverEncryptionMethod;
|
|
|
|
if (Stream_GetRemainingLength(s) < 8)
|
|
return FALSE;
|
|
|
|
Stream_Read_UINT32(s, serverEncryptionMethod); /* encryptionMethod */
|
|
Stream_Read_UINT32(s, settings->EncryptionLevel); /* encryptionLevel */
|
|
|
|
/* Only accept valid/known encryption methods */
|
|
switch (serverEncryptionMethod)
|
|
{
|
|
case ENCRYPTION_METHOD_NONE:
|
|
WLog_DBG(TAG, "Server rdp encryption method: NONE");
|
|
break;
|
|
case ENCRYPTION_METHOD_40BIT:
|
|
WLog_DBG(TAG, "Server rdp encryption method: 40BIT");
|
|
break;
|
|
case ENCRYPTION_METHOD_56BIT:
|
|
WLog_DBG(TAG, "Server rdp encryption method: 56BIT");
|
|
break;
|
|
case ENCRYPTION_METHOD_128BIT:
|
|
WLog_DBG(TAG, "Server rdp encryption method: 128BIT");
|
|
break;
|
|
case ENCRYPTION_METHOD_FIPS:
|
|
WLog_DBG(TAG, "Server rdp encryption method: FIPS");
|
|
break;
|
|
default:
|
|
WLog_ERR(TAG, "Received unknown encryption method %08X", serverEncryptionMethod);
|
|
return FALSE;
|
|
}
|
|
|
|
if (settings->UseRdpSecurityLayer && !(settings->EncryptionMethods & serverEncryptionMethod))
|
|
{
|
|
WLog_WARN(TAG, "Server uses non-advertised encryption method 0x%08X", serverEncryptionMethod);
|
|
/* FIXME: Should we return FALSE; in this case ?? */
|
|
}
|
|
|
|
settings->EncryptionMethods = serverEncryptionMethod;
|
|
|
|
/* Verify encryption level/method combinations according to MS-RDPBCGR Section 5.3.2 */
|
|
switch (settings->EncryptionLevel)
|
|
{
|
|
case ENCRYPTION_LEVEL_NONE:
|
|
if (settings->EncryptionMethods == ENCRYPTION_METHOD_NONE)
|
|
{
|
|
validCryptoConfig = TRUE;
|
|
}
|
|
break;
|
|
case ENCRYPTION_LEVEL_FIPS:
|
|
if (settings->EncryptionMethods == ENCRYPTION_METHOD_FIPS)
|
|
{
|
|
validCryptoConfig = TRUE;
|
|
}
|
|
break;
|
|
case ENCRYPTION_LEVEL_LOW:
|
|
case ENCRYPTION_LEVEL_HIGH:
|
|
case ENCRYPTION_LEVEL_CLIENT_COMPATIBLE:
|
|
if (settings->EncryptionMethods == ENCRYPTION_METHOD_40BIT ||
|
|
settings->EncryptionMethods == ENCRYPTION_METHOD_56BIT ||
|
|
settings->EncryptionMethods == ENCRYPTION_METHOD_128BIT ||
|
|
settings->EncryptionMethods == ENCRYPTION_METHOD_FIPS)
|
|
{
|
|
validCryptoConfig = TRUE;
|
|
}
|
|
break;
|
|
default:
|
|
WLog_ERR(TAG, "Received unknown encryption level %08X", settings->EncryptionLevel);
|
|
}
|
|
|
|
if (!validCryptoConfig)
|
|
{
|
|
WLog_ERR(TAG, "Received invalid cryptographic configuration (level=0x%08X method=0x%08X)",
|
|
settings->EncryptionLevel, settings->EncryptionMethods);
|
|
return FALSE;
|
|
}
|
|
|
|
if (settings->EncryptionLevel == ENCRYPTION_LEVEL_NONE)
|
|
{
|
|
/* serverRandomLen and serverCertLen must not be present */
|
|
settings->UseRdpSecurityLayer = FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
if (Stream_GetRemainingLength(s) < 8)
|
|
return FALSE;
|
|
|
|
Stream_Read_UINT32(s, settings->ServerRandomLength); /* serverRandomLen */
|
|
Stream_Read_UINT32(s, settings->ServerCertificateLength); /* serverCertLen */
|
|
|
|
if (Stream_GetRemainingLength(s) < settings->ServerRandomLength + settings->ServerCertificateLength)
|
|
return FALSE;
|
|
|
|
if ((settings->ServerRandomLength <= 0) || (settings->ServerCertificateLength <= 0))
|
|
return FALSE;
|
|
|
|
/* serverRandom */
|
|
settings->ServerRandom = (BYTE*) malloc(settings->ServerRandomLength);
|
|
if (!settings->ServerRandom)
|
|
return FALSE;
|
|
Stream_Read(s, settings->ServerRandom, settings->ServerRandomLength);
|
|
|
|
|
|
/* serverCertificate */
|
|
settings->ServerCertificate = (BYTE*) malloc(settings->ServerCertificateLength);
|
|
if (!settings->ServerCertificate)
|
|
return FALSE;
|
|
Stream_Read(s, settings->ServerCertificate, settings->ServerCertificateLength);
|
|
|
|
certificate_free(settings->RdpServerCertificate);
|
|
settings->RdpServerCertificate = certificate_new();
|
|
if (!settings->RdpServerCertificate)
|
|
return FALSE;
|
|
|
|
data = settings->ServerCertificate;
|
|
length = settings->ServerCertificateLength;
|
|
|
|
return certificate_read_server_certificate(settings->RdpServerCertificate, data, length);
|
|
}
|
|
|
|
static const BYTE initial_signature[] =
|
|
{
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01
|
|
};
|
|
|
|
/*
|
|
* Terminal Services Signing Keys.
|
|
* Yes, Terminal Services Private Key is publicly available.
|
|
*/
|
|
|
|
const BYTE tssk_modulus[] =
|
|
{
|
|
0x3d, 0x3a, 0x5e, 0xbd, 0x72, 0x43, 0x3e, 0xc9,
|
|
0x4d, 0xbb, 0xc1, 0x1e, 0x4a, 0xba, 0x5f, 0xcb,
|
|
0x3e, 0x88, 0x20, 0x87, 0xef, 0xf5, 0xc1, 0xe2,
|
|
0xd7, 0xb7, 0x6b, 0x9a, 0xf2, 0x52, 0x45, 0x95,
|
|
0xce, 0x63, 0x65, 0x6b, 0x58, 0x3a, 0xfe, 0xef,
|
|
0x7c, 0xe7, 0xbf, 0xfe, 0x3d, 0xf6, 0x5c, 0x7d,
|
|
0x6c, 0x5e, 0x06, 0x09, 0x1a, 0xf5, 0x61, 0xbb,
|
|
0x20, 0x93, 0x09, 0x5f, 0x05, 0x6d, 0xea, 0x87
|
|
};
|
|
|
|
const BYTE tssk_privateExponent[] =
|
|
{
|
|
0x87, 0xa7, 0x19, 0x32, 0xda, 0x11, 0x87, 0x55,
|
|
0x58, 0x00, 0x16, 0x16, 0x25, 0x65, 0x68, 0xf8,
|
|
0x24, 0x3e, 0xe6, 0xfa, 0xe9, 0x67, 0x49, 0x94,
|
|
0xcf, 0x92, 0xcc, 0x33, 0x99, 0xe8, 0x08, 0x60,
|
|
0x17, 0x9a, 0x12, 0x9f, 0x24, 0xdd, 0xb1, 0x24,
|
|
0x99, 0xc7, 0x3a, 0xb8, 0x0a, 0x7b, 0x0d, 0xdd,
|
|
0x35, 0x07, 0x79, 0x17, 0x0b, 0x51, 0x9b, 0xb3,
|
|
0xc7, 0x10, 0x01, 0x13, 0xe7, 0x3f, 0xf3, 0x5f
|
|
};
|
|
|
|
const BYTE tssk_exponent[] =
|
|
{
|
|
0x5b, 0x7b, 0x88, 0xc0
|
|
};
|
|
|
|
void gcc_write_server_security_data(wStream* s, rdpMcs* mcs)
|
|
{
|
|
CryptoMd5 md5;
|
|
BYTE* sigData;
|
|
int expLen, keyLen, sigDataLen;
|
|
BYTE encryptedSignature[TSSK_KEY_LENGTH];
|
|
BYTE signature[sizeof(initial_signature)];
|
|
UINT32 headerLen, serverRandomLen, serverCertLen, wPublicKeyBlobLen;
|
|
rdpSettings* settings = mcs->settings;
|
|
|
|
/**
|
|
* Re: settings->EncryptionLevel:
|
|
* This is configured/set by the server implementation and serves the same
|
|
* purpose as the "Encryption Level" setting in the RDP-Tcp configuration
|
|
* dialog of Microsoft's Remote Desktop Session Host Configuration.
|
|
* Re: settings->EncryptionMethods:
|
|
* at this point this setting contains the client's supported encryption
|
|
* methods we've received in gcc_read_client_security_data()
|
|
*/
|
|
|
|
if (!settings->UseRdpSecurityLayer)
|
|
{
|
|
/* TLS/NLA is used: disable rdp style encryption */
|
|
settings->EncryptionLevel = ENCRYPTION_LEVEL_NONE;
|
|
}
|
|
|
|
/* verify server encryption level value */
|
|
switch (settings->EncryptionLevel)
|
|
{
|
|
case ENCRYPTION_LEVEL_NONE:
|
|
WLog_INFO(TAG, "Active rdp encryption level: NONE");
|
|
break;
|
|
case ENCRYPTION_LEVEL_FIPS:
|
|
WLog_INFO(TAG, "Active rdp encryption level: FIPS Compliant");
|
|
break;
|
|
case ENCRYPTION_LEVEL_HIGH:
|
|
WLog_INFO(TAG, "Active rdp encryption level: HIGH");
|
|
break;
|
|
case ENCRYPTION_LEVEL_LOW:
|
|
WLog_INFO(TAG, "Active rdp encryption level: LOW");
|
|
break;
|
|
case ENCRYPTION_LEVEL_CLIENT_COMPATIBLE:
|
|
WLog_INFO(TAG, "Active rdp encryption level: CLIENT-COMPATIBLE");
|
|
break;
|
|
default:
|
|
WLog_ERR(TAG, "Invalid server encryption level 0x%08X", settings->EncryptionLevel);
|
|
WLog_ERR(TAG, "Switching to encryption level CLIENT-COMPATIBLE");
|
|
settings->EncryptionLevel = ENCRYPTION_LEVEL_CLIENT_COMPATIBLE;
|
|
}
|
|
|
|
/* choose rdp encryption method based on server level and client methods */
|
|
switch (settings->EncryptionLevel)
|
|
{
|
|
case ENCRYPTION_LEVEL_NONE:
|
|
/* The only valid method is NONE in this case */
|
|
settings->EncryptionMethods = ENCRYPTION_METHOD_NONE;
|
|
break;
|
|
case ENCRYPTION_LEVEL_FIPS:
|
|
/* The only valid method is FIPS in this case */
|
|
if (!(settings->EncryptionMethods & ENCRYPTION_METHOD_FIPS))
|
|
{
|
|
WLog_WARN(TAG, "client does not support FIPS as required by server configuration");
|
|
}
|
|
settings->EncryptionMethods = ENCRYPTION_METHOD_FIPS;
|
|
break;
|
|
case ENCRYPTION_LEVEL_HIGH:
|
|
/* Maximum key strength supported by the server must be used (128 bit)*/
|
|
if (!(settings->EncryptionMethods & ENCRYPTION_METHOD_128BIT))
|
|
{
|
|
WLog_WARN(TAG, "client does not support 128 bit encryption method as required by server configuration");
|
|
}
|
|
settings->EncryptionMethods = ENCRYPTION_METHOD_128BIT;
|
|
break;
|
|
case ENCRYPTION_LEVEL_LOW:
|
|
case ENCRYPTION_LEVEL_CLIENT_COMPATIBLE:
|
|
/* Maximum key strength supported by the client must be used */
|
|
if (settings->EncryptionMethods & ENCRYPTION_METHOD_128BIT)
|
|
settings->EncryptionMethods = ENCRYPTION_METHOD_128BIT;
|
|
else if (settings->EncryptionMethods & ENCRYPTION_METHOD_56BIT)
|
|
settings->EncryptionMethods = ENCRYPTION_METHOD_56BIT;
|
|
else if (settings->EncryptionMethods & ENCRYPTION_METHOD_40BIT)
|
|
settings->EncryptionMethods = ENCRYPTION_METHOD_40BIT;
|
|
else if (settings->EncryptionMethods & ENCRYPTION_METHOD_FIPS)
|
|
settings->EncryptionMethods = ENCRYPTION_METHOD_FIPS;
|
|
else
|
|
{
|
|
WLog_WARN(TAG, "client has not announced any supported encryption methods");
|
|
settings->EncryptionMethods = ENCRYPTION_METHOD_128BIT;
|
|
}
|
|
break;
|
|
default:
|
|
WLog_ERR(TAG, "internal error: unknown encryption level");
|
|
}
|
|
|
|
/* log selected encryption method */
|
|
switch (settings->EncryptionMethods)
|
|
{
|
|
case ENCRYPTION_METHOD_NONE:
|
|
WLog_INFO(TAG, "Selected rdp encryption method: NONE");
|
|
break;
|
|
case ENCRYPTION_METHOD_40BIT:
|
|
WLog_INFO(TAG, "Selected rdp encryption method: 40BIT");
|
|
break;
|
|
case ENCRYPTION_METHOD_56BIT:
|
|
WLog_INFO(TAG, "Selected rdp encryption method: 56BIT");
|
|
break;
|
|
case ENCRYPTION_METHOD_128BIT:
|
|
WLog_INFO(TAG, "Selected rdp encryption method: 128BIT");
|
|
break;
|
|
case ENCRYPTION_METHOD_FIPS:
|
|
WLog_INFO(TAG, "Selected rdp encryption method: FIPS");
|
|
break;
|
|
default:
|
|
WLog_ERR(TAG, "internal error: unknown encryption method");
|
|
}
|
|
|
|
headerLen = 12;
|
|
keyLen = 0;
|
|
wPublicKeyBlobLen = 0;
|
|
serverRandomLen = 0;
|
|
serverCertLen = 0;
|
|
|
|
if (settings->EncryptionMethods != ENCRYPTION_METHOD_NONE)
|
|
{
|
|
serverRandomLen = 32;
|
|
|
|
keyLen = settings->RdpServerRsaKey->ModulusLength;
|
|
expLen = sizeof(settings->RdpServerRsaKey->exponent);
|
|
wPublicKeyBlobLen = 4; /* magic (RSA1) */
|
|
wPublicKeyBlobLen += 4; /* keylen */
|
|
wPublicKeyBlobLen += 4; /* bitlen */
|
|
wPublicKeyBlobLen += 4; /* datalen */
|
|
wPublicKeyBlobLen += expLen;
|
|
wPublicKeyBlobLen += keyLen;
|
|
wPublicKeyBlobLen += 8; /* 8 bytes of zero padding */
|
|
|
|
serverCertLen = 4; /* dwVersion */
|
|
serverCertLen += 4; /* dwSigAlgId */
|
|
serverCertLen += 4; /* dwKeyAlgId */
|
|
serverCertLen += 2; /* wPublicKeyBlobType */
|
|
serverCertLen += 2; /* wPublicKeyBlobLen */
|
|
serverCertLen += wPublicKeyBlobLen;
|
|
serverCertLen += 2; /* wSignatureBlobType */
|
|
serverCertLen += 2; /* wSignatureBlobLen */
|
|
serverCertLen += sizeof(encryptedSignature); /* SignatureBlob */
|
|
serverCertLen += 8; /* 8 bytes of zero padding */
|
|
|
|
headerLen += sizeof(serverRandomLen);
|
|
headerLen += sizeof(serverCertLen);
|
|
headerLen += serverRandomLen;
|
|
headerLen += serverCertLen;
|
|
}
|
|
|
|
gcc_write_user_data_header(s, SC_SECURITY, headerLen);
|
|
|
|
Stream_Write_UINT32(s, settings->EncryptionMethods); /* encryptionMethod */
|
|
Stream_Write_UINT32(s, settings->EncryptionLevel); /* encryptionLevel */
|
|
|
|
if (settings->EncryptionMethods == ENCRYPTION_METHOD_NONE)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Stream_Write_UINT32(s, serverRandomLen); /* serverRandomLen */
|
|
Stream_Write_UINT32(s, serverCertLen); /* serverCertLen */
|
|
|
|
settings->ServerRandomLength = serverRandomLen;
|
|
settings->ServerRandom = (BYTE*) malloc(serverRandomLen);
|
|
crypto_nonce(settings->ServerRandom, serverRandomLen);
|
|
Stream_Write(s, settings->ServerRandom, serverRandomLen);
|
|
|
|
sigData = Stream_Pointer(s);
|
|
|
|
Stream_Write_UINT32(s, CERT_CHAIN_VERSION_1); /* dwVersion (4 bytes) */
|
|
Stream_Write_UINT32(s, SIGNATURE_ALG_RSA); /* dwSigAlgId */
|
|
Stream_Write_UINT32(s, KEY_EXCHANGE_ALG_RSA); /* dwKeyAlgId */
|
|
Stream_Write_UINT16(s, BB_RSA_KEY_BLOB); /* wPublicKeyBlobType */
|
|
|
|
Stream_Write_UINT16(s, wPublicKeyBlobLen); /* wPublicKeyBlobLen */
|
|
Stream_Write(s, "RSA1", 4); /* magic */
|
|
Stream_Write_UINT32(s, keyLen + 8); /* keylen */
|
|
Stream_Write_UINT32(s, keyLen * 8); /* bitlen */
|
|
Stream_Write_UINT32(s, keyLen - 1); /* datalen */
|
|
|
|
Stream_Write(s, settings->RdpServerRsaKey->exponent, expLen);
|
|
Stream_Write(s, settings->RdpServerRsaKey->Modulus, keyLen);
|
|
Stream_Zero(s, 8);
|
|
|
|
sigDataLen = Stream_Pointer(s) - sigData;
|
|
|
|
Stream_Write_UINT16(s, BB_RSA_SIGNATURE_BLOB); /* wSignatureBlobType */
|
|
Stream_Write_UINT16(s, sizeof(encryptedSignature) + 8); /* wSignatureBlobLen */
|
|
|
|
memcpy(signature, initial_signature, sizeof(initial_signature));
|
|
|
|
md5 = crypto_md5_init();
|
|
if (!md5)
|
|
{
|
|
WLog_ERR(TAG, "unable to allocate a md5");
|
|
return;
|
|
}
|
|
|
|
crypto_md5_update(md5, sigData, sigDataLen);
|
|
crypto_md5_final(md5, signature);
|
|
|
|
crypto_rsa_private_encrypt(signature, sizeof(signature), TSSK_KEY_LENGTH,
|
|
tssk_modulus, tssk_privateExponent, encryptedSignature);
|
|
|
|
Stream_Write(s, encryptedSignature, sizeof(encryptedSignature));
|
|
Stream_Zero(s, 8);
|
|
}
|
|
|
|
/**
|
|
* Read a client network data block (TS_UD_CS_NET).\n
|
|
* @msdn{cc240512}
|
|
* @param s stream
|
|
* @param settings rdp settings
|
|
*/
|
|
|
|
BOOL gcc_read_client_network_data(wStream* s, rdpMcs* mcs, UINT16 blockLength)
|
|
{
|
|
UINT32 i;
|
|
|
|
if (blockLength < 4)
|
|
return FALSE;
|
|
|
|
Stream_Read_UINT32(s, mcs->channelCount); /* channelCount */
|
|
|
|
if (blockLength < 4 + mcs->channelCount * 12)
|
|
return FALSE;
|
|
|
|
if (mcs->channelCount > 16)
|
|
return FALSE;
|
|
|
|
/* channelDefArray */
|
|
for (i = 0; i < mcs->channelCount; i++)
|
|
{
|
|
/* CHANNEL_DEF */
|
|
Stream_Read(s, mcs->channels[i].Name, 8); /* name (8 bytes) */
|
|
Stream_Read_UINT32(s, mcs->channels[i].options); /* options (4 bytes) */
|
|
mcs->channels[i].ChannelId = mcs->baseChannelId++;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Write a client network data block (TS_UD_CS_NET).\n
|
|
* @msdn{cc240512}
|
|
* @param s stream
|
|
* @param settings rdp settings
|
|
*/
|
|
|
|
void gcc_write_client_network_data(wStream* s, rdpMcs* mcs)
|
|
{
|
|
UINT32 i;
|
|
UINT16 length;
|
|
|
|
if (mcs->channelCount > 0)
|
|
{
|
|
length = mcs->channelCount * 12 + 8;
|
|
gcc_write_user_data_header(s, CS_NET, length);
|
|
|
|
Stream_Write_UINT32(s, mcs->channelCount); /* channelCount */
|
|
|
|
/* channelDefArray */
|
|
for (i = 0; i < mcs->channelCount; i++)
|
|
{
|
|
/* CHANNEL_DEF */
|
|
Stream_Write(s, mcs->channels[i].Name, 8); /* name (8 bytes) */
|
|
Stream_Write_UINT32(s, mcs->channels[i].options); /* options (4 bytes) */
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL gcc_read_server_network_data(wStream* s, rdpMcs* mcs)
|
|
{
|
|
int i;
|
|
UINT16 channelId;
|
|
UINT16 MCSChannelId;
|
|
UINT16 channelCount;
|
|
UINT16 parsedChannelCount;
|
|
|
|
if (Stream_GetRemainingLength(s) < 4)
|
|
return FALSE;
|
|
|
|
Stream_Read_UINT16(s, MCSChannelId); /* MCSChannelId */
|
|
Stream_Read_UINT16(s, channelCount); /* channelCount */
|
|
|
|
parsedChannelCount = channelCount;
|
|
|
|
if (channelCount != mcs->channelCount)
|
|
{
|
|
WLog_ERR(TAG, "requested %d channels, got %d instead",
|
|
mcs->channelCount, channelCount);
|
|
|
|
/* we ensure that the response is not bigger than the request */
|
|
|
|
if (channelCount > mcs->channelCount)
|
|
parsedChannelCount = mcs->channelCount;
|
|
}
|
|
|
|
if (Stream_GetRemainingLength(s) < (size_t) channelCount * 2)
|
|
return FALSE;
|
|
|
|
for (i = 0; i < parsedChannelCount; i++)
|
|
{
|
|
Stream_Read_UINT16(s, channelId); /* channelId */
|
|
mcs->channels[i].ChannelId = channelId;
|
|
}
|
|
|
|
if (channelCount % 2 == 1)
|
|
return Stream_SafeSeek(s, 2); /* padding */
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void gcc_write_server_network_data(wStream* s, rdpMcs* mcs)
|
|
{
|
|
UINT32 i;
|
|
|
|
gcc_write_user_data_header(s, SC_NET, 8 + mcs->channelCount * 2 + (mcs->channelCount % 2 == 1 ? 2 : 0));
|
|
|
|
Stream_Write_UINT16(s, MCS_GLOBAL_CHANNEL_ID); /* MCSChannelId */
|
|
Stream_Write_UINT16(s, mcs->channelCount); /* channelCount */
|
|
|
|
for (i = 0; i < mcs->channelCount; i++)
|
|
{
|
|
Stream_Write_UINT16(s, mcs->channels[i].ChannelId);
|
|
}
|
|
|
|
if (mcs->channelCount % 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
|
|
*/
|
|
|
|
BOOL gcc_read_client_cluster_data(wStream* s, rdpMcs* mcs, UINT16 blockLength)
|
|
{
|
|
UINT32 flags;
|
|
UINT32 redirectedSessionId;
|
|
rdpSettings* settings = mcs->settings;
|
|
|
|
if (blockLength < 8)
|
|
return FALSE;
|
|
|
|
Stream_Read_UINT32(s, flags); /* flags */
|
|
Stream_Read_UINT32(s, redirectedSessionId); /* redirectedSessionId */
|
|
|
|
if (flags & REDIRECTED_SESSIONID_FIELD_VALID)
|
|
settings->RedirectedSessionId = redirectedSessionId;
|
|
|
|
if (blockLength != 8)
|
|
{
|
|
if (Stream_GetRemainingLength(s) >= (blockLength - 8))
|
|
{
|
|
/* The old Microsoft Mac RDP client can send a pad here */
|
|
Stream_Seek(s, (blockLength - 8));
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Write a client cluster data block (TS_UD_CS_CLUSTER).\n
|
|
* @msdn{cc240514}
|
|
* @param s stream
|
|
* @param settings rdp settings
|
|
*/
|
|
|
|
void gcc_write_client_cluster_data(wStream* s, rdpMcs* mcs)
|
|
{
|
|
UINT32 flags;
|
|
rdpSettings* settings = mcs->settings;
|
|
|
|
gcc_write_user_data_header(s, CS_CLUSTER, 12);
|
|
|
|
flags = REDIRECTION_SUPPORTED | (REDIRECTION_VERSION4 << 2);
|
|
|
|
if (settings->ConsoleSession || settings->RedirectedSessionId)
|
|
flags |= REDIRECTED_SESSIONID_FIELD_VALID;
|
|
|
|
Stream_Write_UINT32(s, flags); /* flags */
|
|
Stream_Write_UINT32(s, settings->RedirectedSessionId); /* redirectedSessionID */
|
|
}
|
|
|
|
/**
|
|
* Read a client monitor data block (TS_UD_CS_MONITOR).\n
|
|
* @msdn{dd305336}
|
|
* @param s stream
|
|
* @param settings rdp settings
|
|
*/
|
|
|
|
BOOL gcc_read_client_monitor_data(wStream* s, rdpMcs* mcs, UINT16 blockLength)
|
|
{
|
|
UINT32 index;
|
|
UINT32 flags;
|
|
UINT32 monitorCount;
|
|
MONITOR_DEF* monitorDefArray;
|
|
|
|
if (blockLength < 8)
|
|
return FALSE;
|
|
|
|
Stream_Read_UINT32(s, flags); /* flags */
|
|
Stream_Read_UINT32(s, monitorCount); /* monitorCount */
|
|
|
|
if (blockLength < (8 + (monitorCount * 20)))
|
|
return FALSE;
|
|
|
|
monitorDefArray = (MONITOR_DEF*) malloc(sizeof(MONITOR_DEF) * monitorCount);
|
|
ZeroMemory(monitorDefArray, sizeof(MONITOR_DEF) * monitorCount);
|
|
|
|
for (index = 0; index < monitorCount; index++)
|
|
{
|
|
Stream_Read_UINT32(s, monitorDefArray[index].left); /* left */
|
|
Stream_Read_UINT32(s, monitorDefArray[index].top); /* top */
|
|
Stream_Read_UINT32(s, monitorDefArray[index].right); /* right */
|
|
Stream_Read_UINT32(s, monitorDefArray[index].bottom); /* bottom */
|
|
Stream_Read_UINT32(s, monitorDefArray[index].flags); /* flags */
|
|
}
|
|
|
|
free(monitorDefArray);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Write a client monitor data block (TS_UD_CS_MONITOR).\n
|
|
* @msdn{dd305336}
|
|
* @param s stream
|
|
* @param settings rdp settings
|
|
*/
|
|
|
|
void gcc_write_client_monitor_data(wStream* s, rdpMcs* mcs)
|
|
{
|
|
int i;
|
|
UINT16 length;
|
|
UINT32 left, top, right, bottom, flags;
|
|
rdpSettings* settings = mcs->settings;
|
|
|
|
if (settings->MonitorCount > 1)
|
|
{
|
|
length = (20 * settings->MonitorCount) + 12;
|
|
gcc_write_user_data_header(s, CS_MONITOR, length);
|
|
|
|
Stream_Write_UINT32(s, 0); /* flags */
|
|
Stream_Write_UINT32(s, settings->MonitorCount); /* monitorCount */
|
|
|
|
for (i = 0; i < settings->MonitorCount; i++)
|
|
{
|
|
left = settings->MonitorDefArray[i].x;
|
|
top = settings->MonitorDefArray[i].y;
|
|
right = settings->MonitorDefArray[i].x + settings->MonitorDefArray[i].width - 1;
|
|
bottom = settings->MonitorDefArray[i].y + settings->MonitorDefArray[i].height - 1;
|
|
flags = settings->MonitorDefArray[i].is_primary ? MONITOR_PRIMARY : 0;
|
|
|
|
Stream_Write_UINT32(s, left); /* left */
|
|
Stream_Write_UINT32(s, top); /* top */
|
|
Stream_Write_UINT32(s, right); /* right */
|
|
Stream_Write_UINT32(s, bottom); /* bottom */
|
|
Stream_Write_UINT32(s, flags); /* flags */
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL gcc_read_client_monitor_extended_data(wStream* s, rdpMcs* mcs, UINT16 blockLength)
|
|
{
|
|
UINT32 index;
|
|
UINT32 flags;
|
|
UINT32 monitorCount;
|
|
UINT32 monitorAttributeSize;
|
|
MONITOR_ATTRIBUTES* monitorAttributesArray;
|
|
|
|
if (blockLength < 8)
|
|
return FALSE;
|
|
|
|
Stream_Read_UINT32(s, flags); /* flags */
|
|
Stream_Read_UINT32(s, monitorAttributeSize); /* monitorAttributeSize */
|
|
Stream_Read_UINT32(s, monitorCount); /* monitorCount */
|
|
|
|
if (monitorAttributeSize != 20)
|
|
return FALSE;
|
|
|
|
if (blockLength < (12 + (monitorCount * monitorAttributeSize)))
|
|
return FALSE;
|
|
|
|
monitorAttributesArray = (MONITOR_ATTRIBUTES*) malloc(sizeof(MONITOR_ATTRIBUTES) * monitorCount);
|
|
ZeroMemory(monitorAttributesArray, sizeof(MONITOR_ATTRIBUTES) * monitorCount);
|
|
|
|
for (index = 0; index < monitorCount; index++)
|
|
{
|
|
Stream_Read_UINT32(s, monitorAttributesArray[index].physicalWidth); /* physicalWidth */
|
|
Stream_Read_UINT32(s, monitorAttributesArray[index].physicalHeight); /* physicalHeight */
|
|
Stream_Read_UINT32(s, monitorAttributesArray[index].orientation); /* orientation */
|
|
Stream_Read_UINT32(s, monitorAttributesArray[index].desktopScaleFactor); /* desktopScaleFactor */
|
|
Stream_Read_UINT32(s, monitorAttributesArray[index].deviceScaleFactor); /* deviceScaleFactor */
|
|
}
|
|
|
|
free(monitorAttributesArray);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void gcc_write_client_monitor_extended_data(wStream* s, rdpMcs* mcs)
|
|
{
|
|
|
|
}
|
|
|
|
/**
|
|
* Read a client message channel data block (TS_UD_CS_MCS_MSGCHANNEL).\n
|
|
* @msdn{jj217627}
|
|
* @param s stream
|
|
* @param settings rdp settings
|
|
*/
|
|
|
|
BOOL gcc_read_client_message_channel_data(wStream* s, rdpMcs* mcs, UINT16 blockLength)
|
|
{
|
|
UINT32 flags;
|
|
|
|
if (blockLength < 4)
|
|
return FALSE;
|
|
|
|
Stream_Read_UINT32(s, flags);
|
|
|
|
mcs->messageChannelId = mcs->baseChannelId++;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Write a client message channel data block (TS_UD_CS_MCS_MSGCHANNEL).\n
|
|
* @msdn{jj217627}
|
|
* @param s stream
|
|
* @param settings rdp settings
|
|
*/
|
|
|
|
void gcc_write_client_message_channel_data(wStream* s, rdpMcs* mcs)
|
|
{
|
|
rdpSettings* settings = mcs->settings;
|
|
|
|
if (settings->NetworkAutoDetect ||
|
|
settings->SupportHeartbeatPdu ||
|
|
settings->SupportMultitransport)
|
|
{
|
|
gcc_write_user_data_header(s, CS_MCS_MSGCHANNEL, 8);
|
|
|
|
Stream_Write_UINT32(s, 0); /* flags */
|
|
}
|
|
}
|
|
|
|
BOOL gcc_read_server_message_channel_data(wStream* s, rdpMcs* mcs)
|
|
{
|
|
UINT16 MCSChannelId;
|
|
|
|
if (Stream_GetRemainingLength(s) < 2)
|
|
return FALSE;
|
|
|
|
Stream_Read_UINT16(s, MCSChannelId); /* MCSChannelId */
|
|
|
|
/* Save the MCS message channel id */
|
|
mcs->messageChannelId = MCSChannelId;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void gcc_write_server_message_channel_data(wStream* s, rdpMcs* mcs)
|
|
{
|
|
if (mcs->messageChannelId == 0)
|
|
return;
|
|
|
|
gcc_write_user_data_header(s, SC_MCS_MSGCHANNEL, 6);
|
|
|
|
Stream_Write_UINT16(s, mcs->messageChannelId); /* mcsChannelId (2 bytes) */
|
|
}
|
|
|
|
/**
|
|
* Read a client multitransport channel data block (TS_UD_CS_MULTITRANSPORT).\n
|
|
* @msdn{jj217498}
|
|
* @param s stream
|
|
* @param settings rdp settings
|
|
*/
|
|
|
|
BOOL gcc_read_client_multitransport_channel_data(wStream* s, rdpMcs* mcs, UINT16 blockLength)
|
|
{
|
|
UINT32 flags;
|
|
|
|
if (blockLength < 4)
|
|
return FALSE;
|
|
|
|
Stream_Read_UINT32(s, flags);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Write a client multitransport channel data block (TS_UD_CS_MULTITRANSPORT).\n
|
|
* @msdn{jj217498}
|
|
* @param s stream
|
|
* @param settings rdp settings
|
|
*/
|
|
|
|
void gcc_write_client_multitransport_channel_data(wStream* s, rdpMcs* mcs)
|
|
{
|
|
rdpSettings* settings = mcs->settings;
|
|
|
|
if (settings->MultitransportFlags != 0)
|
|
{
|
|
gcc_write_user_data_header(s, CS_MULTITRANSPORT, 8);
|
|
|
|
Stream_Write_UINT32(s, settings->MultitransportFlags); /* flags */
|
|
}
|
|
}
|
|
|
|
BOOL gcc_read_server_multitransport_channel_data(wStream* s, rdpMcs* mcs)
|
|
{
|
|
UINT32 flags;
|
|
|
|
if (Stream_GetRemainingLength(s) < 4)
|
|
return FALSE;
|
|
|
|
Stream_Read_UINT32(s, flags); /* flags */
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void gcc_write_server_multitransport_channel_data(wStream* s, rdpMcs* mcs)
|
|
{
|
|
UINT32 flags = 0;
|
|
|
|
gcc_write_user_data_header(s, SC_MULTITRANSPORT, 8);
|
|
|
|
Stream_Write_UINT32(s, flags); /* flags (4 bytes) */
|
|
}
|