FreeRDP/libfreerdp/core/nego.c

1921 lines
48 KiB
C
Raw Normal View History

/**
2012-10-09 07:02:04 +04:00
* FreeRDP: A Remote Desktop Protocol Implementation
* RDP Protocol Security Negotiation
*
* Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
Standard RDP Security Layer Levels/Method Overhaul [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
2014-12-12 04:17:12 +03:00
* Copyright 2014 Norbert Federa <norbert.federa@thincast.com>
* Copyright 2015 Thincast Technologies GmbH
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@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.
*/
2022-02-16 13:20:38 +03:00
#include <freerdp/config.h>
#include <winpr/crt.h>
#include <winpr/assert.h>
#include <winpr/stream.h>
#include <freerdp/log.h>
#include "tpkt.h"
#include "nego.h"
#include "aad.h"
#include "transport.h"
#define TAG FREERDP_TAG("core.nego")
2018-11-20 18:38:06 +03:00
struct rdp_nego
{
2018-11-20 19:03:50 +03:00
UINT16 port;
2018-11-20 18:38:06 +03:00
UINT32 flags;
2018-11-20 19:03:50 +03:00
const char* hostname;
2018-11-20 18:38:06 +03:00
char* cookie;
BYTE* RoutingToken;
DWORD RoutingTokenLength;
BOOL SendPreconnectionPdu;
UINT32 PreconnectionId;
2020-11-18 09:51:45 +03:00
const char* PreconnectionBlob;
2018-11-20 18:38:06 +03:00
NEGO_STATE state;
BOOL TcpConnected;
BOOL SecurityConnected;
UINT32 CookieMaxLength;
BOOL sendNegoData;
UINT32 SelectedProtocol;
UINT32 RequestedProtocols;
BOOL NegotiateSecurityLayer;
BOOL EnabledProtocols[32];
2018-11-20 18:38:06 +03:00
BOOL RestrictedAdminModeRequired;
BOOL GatewayEnabled;
BOOL GatewayBypassLocal;
rdpTransport* transport;
};
2019-09-04 16:46:47 +03:00
static const char* nego_state_string(NEGO_STATE state)
{
static const char* const NEGO_STATE_STRINGS[] = { "NEGO_STATE_INITIAL", "NEGO_STATE_RDSTLS",
"NEGO_STATE_AAD", "NEGO_STATE_EXT",
"NEGO_STATE_NLA", "NEGO_STATE_TLS",
"NEGO_STATE_RDP", "NEGO_STATE_FAIL",
"NEGO_STATE_FINAL", "NEGO_STATE_INVALID" };
2019-09-04 16:46:47 +03:00
if (state >= ARRAYSIZE(NEGO_STATE_STRINGS))
return NEGO_STATE_STRINGS[ARRAYSIZE(NEGO_STATE_STRINGS) - 1];
return NEGO_STATE_STRINGS[state];
}
2019-09-04 16:46:47 +03:00
static const char* protocol_security_string(UINT32 security)
{
static const char* PROTOCOL_SECURITY_STRINGS[] = { "RDP", "TLS", "NLA", "UNK", "RDSTLS",
"UNK", "UNK", "UNK", "EXT", "UNK",
"UNK", "UNK", "UNK", "UNK", "UNK",
"UNK", "AAD", "UNK", "UNK", "UNK" };
2019-09-04 16:46:47 +03:00
if (security >= ARRAYSIZE(PROTOCOL_SECURITY_STRINGS))
return PROTOCOL_SECURITY_STRINGS[ARRAYSIZE(PROTOCOL_SECURITY_STRINGS) - 1];
return PROTOCOL_SECURITY_STRINGS[security];
}
static BOOL nego_transport_connect(rdpNego* nego);
static BOOL nego_transport_disconnect(rdpNego* nego);
2015-01-12 15:44:04 +03:00
static BOOL nego_security_connect(rdpNego* nego);
static BOOL nego_send_preconnection_pdu(rdpNego* nego);
static BOOL nego_recv_response(rdpNego* nego);
static void nego_send(rdpNego* nego);
static BOOL nego_process_negotiation_request(rdpNego* nego, wStream* s);
static BOOL nego_process_negotiation_response(rdpNego* nego, wStream* s);
static BOOL nego_process_negotiation_failure(rdpNego* nego, wStream* s);
static UINT32 nego_get_next_selected_protocol(rdpNego* nego)
{
WINPR_ASSERT(nego);
if (nego->EnabledProtocols[PROTOCOL_RDSAAD])
return PROTOCOL_RDSAAD;
if (nego->EnabledProtocols[PROTOCOL_RDSTLS])
return PROTOCOL_RDSTLS;
if (nego->EnabledProtocols[PROTOCOL_HYBRID_EX] || nego->EnabledProtocols[PROTOCOL_HYBRID])
return PROTOCOL_HYBRID_EX;
if (nego->EnabledProtocols[PROTOCOL_HYBRID])
return PROTOCOL_HYBRID;
if (nego->EnabledProtocols[PROTOCOL_SSL])
return PROTOCOL_SSL;
if (nego->EnabledProtocols[PROTOCOL_RDP])
return PROTOCOL_RDP;
WLog_ERR(TAG, "Invalid NEGO state 0x%08" PRIx32, nego_get_state(nego));
return 0;
}
/**
* Negotiate protocol security and connect.
2022-12-09 16:35:03 +03:00
*
* @param nego A pointer to the NEGO struct
*
* @return \b TRUE for success, \b FALSE otherwise
*/
BOOL nego_connect(rdpNego* nego)
{
2021-09-06 12:01:36 +03:00
rdpContext* context;
rdpSettings* settings;
WINPR_ASSERT(nego);
2021-09-06 12:01:36 +03:00
context = transport_get_context(nego->transport);
WINPR_ASSERT(context);
settings = context->settings;
WINPR_ASSERT(settings);
2021-09-06 12:01:36 +03:00
if (nego_get_state(nego) == NEGO_STATE_INITIAL)
{
if (nego->EnabledProtocols[PROTOCOL_RDSAAD])
{
nego_set_state(nego, NEGO_STATE_AAD);
}
else if (nego->EnabledProtocols[PROTOCOL_RDSTLS])
{
nego_set_state(nego, NEGO_STATE_RDSTLS);
}
else if (nego->EnabledProtocols[PROTOCOL_HYBRID_EX])
{
nego_set_state(nego, NEGO_STATE_EXT);
}
else if (nego->EnabledProtocols[PROTOCOL_HYBRID])
{
nego_set_state(nego, NEGO_STATE_NLA);
}
else if (nego->EnabledProtocols[PROTOCOL_SSL])
{
nego_set_state(nego, NEGO_STATE_TLS);
}
2015-02-03 01:16:32 +03:00
else if (nego->EnabledProtocols[PROTOCOL_RDP])
{
nego_set_state(nego, NEGO_STATE_RDP);
}
else
{
2015-02-02 19:50:56 +03:00
WLog_ERR(TAG, "No security protocol is enabled");
nego_set_state(nego, NEGO_STATE_FAIL);
2014-04-10 23:07:53 +04:00
return FALSE;
}
if (!nego->NegotiateSecurityLayer)
{
2015-02-02 19:50:56 +03:00
WLog_DBG(TAG, "Security Layer Negotiation is disabled");
2012-08-01 00:58:41 +04:00
/* attempt only the highest enabled protocol (see nego_attempt_*) */
nego->EnabledProtocols[PROTOCOL_RDSAAD] = FALSE;
nego->EnabledProtocols[PROTOCOL_HYBRID] = FALSE;
nego->EnabledProtocols[PROTOCOL_SSL] = FALSE;
2015-02-03 01:16:32 +03:00
nego->EnabledProtocols[PROTOCOL_RDP] = FALSE;
nego->EnabledProtocols[PROTOCOL_HYBRID_EX] = FALSE;
nego->EnabledProtocols[PROTOCOL_RDSTLS] = FALSE;
UINT32 SelectedProtocol = 0;
switch (nego_get_state(nego))
2012-08-01 00:58:41 +04:00
{
case NEGO_STATE_AAD:
nego->EnabledProtocols[PROTOCOL_RDSAAD] = TRUE;
SelectedProtocol = PROTOCOL_RDSAAD;
break;
case NEGO_STATE_RDSTLS:
nego->EnabledProtocols[PROTOCOL_RDSTLS] = TRUE;
SelectedProtocol = PROTOCOL_RDSTLS;
break;
case NEGO_STATE_EXT:
nego->EnabledProtocols[PROTOCOL_HYBRID_EX] = TRUE;
nego->EnabledProtocols[PROTOCOL_HYBRID] = TRUE;
SelectedProtocol = PROTOCOL_HYBRID_EX;
break;
case NEGO_STATE_NLA:
nego->EnabledProtocols[PROTOCOL_HYBRID] = TRUE;
SelectedProtocol = PROTOCOL_HYBRID;
break;
case NEGO_STATE_TLS:
nego->EnabledProtocols[PROTOCOL_SSL] = TRUE;
SelectedProtocol = PROTOCOL_SSL;
break;
case NEGO_STATE_RDP:
nego->EnabledProtocols[PROTOCOL_RDP] = TRUE;
SelectedProtocol = PROTOCOL_RDP;
break;
default:
WLog_ERR(TAG, "Invalid NEGO state 0x%08" PRIx32, nego_get_state(nego));
return FALSE;
2012-08-01 00:58:41 +04:00
}
if (!nego_set_selected_protocol(nego, SelectedProtocol))
return FALSE;
}
2015-02-03 01:16:32 +03:00
if (nego->SendPreconnectionPdu)
{
2015-02-03 01:16:32 +03:00
if (!nego_send_preconnection_pdu(nego))
{
WLog_ERR(TAG, "Failed to send preconnection pdu");
nego_set_state(nego, NEGO_STATE_FINAL);
2015-02-03 01:16:32 +03:00
return FALSE;
}
}
}
if (!nego->NegotiateSecurityLayer)
{
nego_set_state(nego, NEGO_STATE_FINAL);
}
else
{
do
{
WLog_DBG(TAG, "state: %s", nego_state_string(nego_get_state(nego)));
nego_send(nego);
if (nego_get_state(nego) == NEGO_STATE_FAIL)
{
2021-09-06 12:01:36 +03:00
if (freerdp_get_last_error(transport_get_context(nego->transport)) ==
FREERDP_ERROR_SUCCESS)
WLog_ERR(TAG, "Protocol Security Negotiation Failure");
nego_set_state(nego, NEGO_STATE_FINAL);
return FALSE;
}
} while (nego_get_state(nego) != NEGO_STATE_FINAL);
}
2019-09-04 16:46:47 +03:00
WLog_DBG(TAG, "Negotiated %s security", protocol_security_string(nego->SelectedProtocol));
/* update settings with negotiated protocol security */
2023-01-18 13:03:33 +03:00
if (!freerdp_settings_set_uint32(settings, FreeRDP_RequestedProtocols,
nego->RequestedProtocols))
return FALSE;
if (!freerdp_settings_set_uint32(settings, FreeRDP_SelectedProtocol, nego->SelectedProtocol))
return FALSE;
if (!freerdp_settings_set_uint32(settings, FreeRDP_NegotiationFlags, nego->flags))
return FALSE;
2015-02-03 01:16:32 +03:00
if (nego->SelectedProtocol == PROTOCOL_RDP)
{
2023-01-18 13:03:33 +03:00
if (!freerdp_settings_set_bool(settings, FreeRDP_UseRdpSecurityLayer, TRUE))
return FALSE;
Standard RDP Security Layer Levels/Method Overhaul [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
2014-12-12 04:17:12 +03:00
2023-01-18 13:03:33 +03:00
if (freerdp_settings_get_uint32(settings, FreeRDP_EncryptionMethods) == 0)
Standard RDP Security Layer Levels/Method Overhaul [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
2014-12-12 04:17:12 +03:00
{
/**
* Advertise all supported encryption methods if the client
* implementation did not set any security methods
*/
2023-01-18 13:03:33 +03:00
if (!freerdp_settings_set_uint32(settings, FreeRDP_EncryptionMethods,
ENCRYPTION_METHOD_40BIT | ENCRYPTION_METHOD_56BIT |
ENCRYPTION_METHOD_128BIT | ENCRYPTION_METHOD_FIPS))
return FALSE;
Standard RDP Security Layer Levels/Method Overhaul [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
2014-12-12 04:17:12 +03:00
}
}
/* finally connect security layer (if not already done) */
if (!nego_security_connect(nego))
{
WLog_DBG(TAG, "Failed to connect with %s security",
2019-09-04 16:46:47 +03:00
protocol_security_string(nego->SelectedProtocol));
return FALSE;
}
return TRUE;
}
2015-01-12 15:44:04 +03:00
BOOL nego_disconnect(rdpNego* nego)
{
WINPR_ASSERT(nego);
nego_set_state(nego, NEGO_STATE_INITIAL);
2015-01-12 15:44:04 +03:00
return nego_transport_disconnect(nego);
}
static BOOL nego_try_connect(rdpNego* nego)
{
WINPR_ASSERT(nego);
switch (nego->SelectedProtocol)
{
case PROTOCOL_RDSAAD:
WLog_DBG(TAG, "nego_security_connect with PROTOCOL_RDSAAD");
nego->SecurityConnected = transport_connect_aad(nego->transport);
break;
case PROTOCOL_RDSTLS:
WLog_DBG(TAG, "nego_security_connect with PROTOCOL_RDSTLS");
nego->SecurityConnected = transport_connect_rdstls(nego->transport);
break;
case PROTOCOL_HYBRID:
WLog_DBG(TAG, "nego_security_connect with PROTOCOL_HYBRID");
2015-02-03 01:16:32 +03:00
nego->SecurityConnected = transport_connect_nla(nego->transport);
break;
case PROTOCOL_SSL:
WLog_DBG(TAG, "nego_security_connect with PROTOCOL_SSL");
2015-02-03 01:16:32 +03:00
nego->SecurityConnected = transport_connect_tls(nego->transport);
break;
case PROTOCOL_RDP:
2015-02-02 19:50:56 +03:00
WLog_DBG(TAG, "nego_security_connect with PROTOCOL_RDP");
2015-02-03 01:16:32 +03:00
nego->SecurityConnected = transport_connect_rdp(nego->transport);
break;
default:
2019-11-06 17:24:51 +03:00
WLog_ERR(TAG,
"cannot connect security layer because no protocol has been selected yet.");
return FALSE;
}
return nego->SecurityConnected;
}
/* connect to selected security layer */
BOOL nego_security_connect(rdpNego* nego)
{
WINPR_ASSERT(nego);
if (!nego->TcpConnected)
{
nego->SecurityConnected = FALSE;
}
else if (!nego->SecurityConnected)
{
if (!nego_try_connect(nego))
return FALSE;
}
2015-02-03 01:16:32 +03:00
return nego->SecurityConnected;
}
static BOOL nego_tcp_connect(rdpNego* nego)
{
2021-09-06 12:01:36 +03:00
rdpContext* context;
WINPR_ASSERT(nego);
2015-02-03 01:16:32 +03:00
if (!nego->TcpConnected)
{
2021-09-06 12:01:36 +03:00
UINT32 TcpConnectTimeout;
context = transport_get_context(nego->transport);
WINPR_ASSERT(context);
TcpConnectTimeout =
freerdp_settings_get_uint32(context->settings, FreeRDP_TcpConnectTimeout);
2021-06-02 17:51:49 +03:00
if (nego->GatewayEnabled)
{
if (nego->GatewayBypassLocal)
{
/* Attempt a direct connection first, and then fallback to using the gateway */
2019-11-06 17:24:51 +03:00
WLog_INFO(TAG,
"Detecting if host can be reached locally. - This might take some time.");
WLog_INFO(TAG, "To disable auto detection use /gateway-usage-method:direct");
transport_set_gateway_enabled(nego->transport, FALSE);
2021-06-02 17:51:49 +03:00
nego->TcpConnected = transport_connect(nego->transport, nego->hostname, nego->port,
TcpConnectTimeout);
}
2015-02-03 01:16:32 +03:00
if (!nego->TcpConnected)
{
transport_set_gateway_enabled(nego->transport, TRUE);
2021-06-02 17:51:49 +03:00
nego->TcpConnected = transport_connect(nego->transport, nego->hostname, nego->port,
TcpConnectTimeout);
}
}
else
{
2021-06-02 17:51:49 +03:00
nego->TcpConnected =
transport_connect(nego->transport, nego->hostname, nego->port, TcpConnectTimeout);
}
}
2015-02-03 01:16:32 +03:00
return nego->TcpConnected;
}
/**
* Connect TCP layer. For direct approach, connect security layer as well.
2022-12-09 16:35:03 +03:00
*
* @param nego A pointer to the NEGO struct
*
* @return \b TRUE for success, \b FALSE otherwise
*/
BOOL nego_transport_connect(rdpNego* nego)
{
WINPR_ASSERT(nego);
if (!nego_tcp_connect(nego))
return FALSE;
2015-02-03 01:16:32 +03:00
if (nego->TcpConnected && !nego->NegotiateSecurityLayer)
return nego_security_connect(nego);
2015-02-03 01:16:32 +03:00
return nego->TcpConnected;
}
/**
* Disconnect TCP layer.
2022-12-09 16:35:03 +03:00
*
* @param nego A pointer to the NEGO struct
*
* @return \b TRUE for success, \b FALSE otherwise
*/
BOOL nego_transport_disconnect(rdpNego* nego)
{
WINPR_ASSERT(nego);
2015-02-03 01:16:32 +03:00
if (nego->TcpConnected)
transport_disconnect(nego->transport);
2015-02-03 01:16:32 +03:00
nego->TcpConnected = FALSE;
nego->SecurityConnected = FALSE;
return TRUE;
}
/**
* Send preconnection information if enabled.
2022-12-09 16:35:03 +03:00
*
* @param nego A pointer to the NEGO struct
*
* @return \b TRUE for success, \b FALSE otherwise
*/
BOOL nego_send_preconnection_pdu(rdpNego* nego)
{
wStream* s;
2012-10-09 11:26:39 +04:00
UINT32 cbSize;
UINT16 cchPCB = 0;
WCHAR* wszPCB = NULL;
WINPR_ASSERT(nego);
2015-02-02 19:50:56 +03:00
WLog_DBG(TAG, "Sending preconnection PDU");
if (!nego_tcp_connect(nego))
return FALSE;
/* it's easier to always send the version 2 PDU, and it's just 2 bytes overhead */
cbSize = PRECONNECTION_PDU_V2_MIN_SIZE;
2015-02-03 01:16:32 +03:00
if (nego->PreconnectionBlob)
{
size_t len = 0;
wszPCB = ConvertUtf8ToWCharAlloc(nego->PreconnectionBlob, &len);
if (len > UINT16_MAX - 1)
{
free(wszPCB);
return FALSE;
}
cchPCB = len;
cchPCB += 1; /* zero-termination */
cbSize += cchPCB * sizeof(WCHAR);
}
s = Stream_New(NULL, cbSize);
if (!s)
{
free(wszPCB);
WLog_ERR(TAG, "Stream_New failed!");
return FALSE;
}
2019-11-06 17:24:51 +03:00
Stream_Write_UINT32(s, cbSize); /* cbSize */
Stream_Write_UINT32(s, 0); /* Flags */
Stream_Write_UINT32(s, PRECONNECTION_PDU_V2); /* Version */
2015-02-03 01:16:32 +03:00
Stream_Write_UINT32(s, nego->PreconnectionId); /* Id */
2019-11-06 17:24:51 +03:00
Stream_Write_UINT16(s, cchPCB); /* cchPCB */
if (wszPCB)
{
Stream_Write(s, wszPCB, cchPCB * sizeof(WCHAR)); /* wszPCB */
free(wszPCB);
}
Stream_SealLength(s);
if (transport_write(nego->transport, s) < 0)
2013-09-06 13:07:33 +04:00
{
Stream_Free(s, TRUE);
return FALSE;
2013-09-06 13:07:33 +04:00
}
Stream_Free(s, TRUE);
return TRUE;
}
static void nego_attempt_rdstls(rdpNego* nego)
{
WINPR_ASSERT(nego);
nego->RequestedProtocols = PROTOCOL_RDSTLS | PROTOCOL_SSL;
WLog_DBG(TAG, "Attempting RDSTLS security");
if (!nego_transport_connect(nego))
{
nego_set_state(nego, NEGO_STATE_FAIL);
return;
}
if (!nego_send_negotiation_request(nego))
{
nego_set_state(nego, NEGO_STATE_FAIL);
return;
}
if (!nego_recv_response(nego))
{
nego_set_state(nego, NEGO_STATE_FAIL);
return;
}
WLog_DBG(TAG, "state: %s", nego_state_string(nego_get_state(nego)));
if (nego_get_state(nego) != NEGO_STATE_FINAL)
{
nego_transport_disconnect(nego);
if (nego->EnabledProtocols[PROTOCOL_HYBRID_EX])
nego_set_state(nego, NEGO_STATE_EXT);
else if (nego->EnabledProtocols[PROTOCOL_HYBRID])
nego_set_state(nego, NEGO_STATE_NLA);
else if (nego->EnabledProtocols[PROTOCOL_SSL])
nego_set_state(nego, NEGO_STATE_TLS);
else if (nego->EnabledProtocols[PROTOCOL_RDP])
nego_set_state(nego, NEGO_STATE_RDP);
else
nego_set_state(nego, NEGO_STATE_FAIL);
}
}
static void nego_attempt_rdsaad(rdpNego* nego)
{
WINPR_ASSERT(nego);
nego->RequestedProtocols = PROTOCOL_RDSAAD;
WLog_DBG(TAG, "Attempting RDS AAD Auth security");
if (!nego_transport_connect(nego))
{
nego_set_state(nego, NEGO_STATE_FAIL);
return;
}
if (!nego_send_negotiation_request(nego))
{
nego_set_state(nego, NEGO_STATE_FAIL);
return;
}
if (!nego_recv_response(nego))
{
nego_set_state(nego, NEGO_STATE_FAIL);
return;
}
WLog_DBG(TAG, "state: %s", nego_state_string(nego_get_state(nego)));
if (nego_get_state(nego) != NEGO_STATE_FINAL)
{
nego_transport_disconnect(nego);
if (nego->EnabledProtocols[PROTOCOL_HYBRID_EX])
nego_set_state(nego, NEGO_STATE_EXT);
else if (nego->EnabledProtocols[PROTOCOL_HYBRID])
nego_set_state(nego, NEGO_STATE_NLA);
else if (nego->EnabledProtocols[PROTOCOL_SSL])
nego_set_state(nego, NEGO_STATE_TLS);
else if (nego->EnabledProtocols[PROTOCOL_RDP])
nego_set_state(nego, NEGO_STATE_RDP);
else
nego_set_state(nego, NEGO_STATE_FAIL);
}
}
static void nego_attempt_ext(rdpNego* nego)
{
WINPR_ASSERT(nego);
nego->RequestedProtocols = PROTOCOL_HYBRID | PROTOCOL_SSL | PROTOCOL_HYBRID_EX;
2015-02-02 19:50:56 +03:00
WLog_DBG(TAG, "Attempting NLA extended security");
if (!nego_transport_connect(nego))
{
nego_set_state(nego, NEGO_STATE_FAIL);
return;
}
if (!nego_send_negotiation_request(nego))
{
nego_set_state(nego, NEGO_STATE_FAIL);
return;
}
if (!nego_recv_response(nego))
{
nego_set_state(nego, NEGO_STATE_FAIL);
return;
}
WLog_DBG(TAG, "state: %s", nego_state_string(nego_get_state(nego)));
if (nego_get_state(nego) != NEGO_STATE_FINAL)
{
nego_transport_disconnect(nego);
if (nego->EnabledProtocols[PROTOCOL_HYBRID])
nego_set_state(nego, NEGO_STATE_NLA);
else if (nego->EnabledProtocols[PROTOCOL_SSL])
nego_set_state(nego, NEGO_STATE_TLS);
2015-02-03 01:16:32 +03:00
else if (nego->EnabledProtocols[PROTOCOL_RDP])
nego_set_state(nego, NEGO_STATE_RDP);
else
nego_set_state(nego, NEGO_STATE_FAIL);
}
}
static void nego_attempt_nla(rdpNego* nego)
{
WINPR_ASSERT(nego);
nego->RequestedProtocols = PROTOCOL_HYBRID | PROTOCOL_SSL;
2015-02-02 19:50:56 +03:00
WLog_DBG(TAG, "Attempting NLA security");
if (!nego_transport_connect(nego))
{
nego_set_state(nego, NEGO_STATE_FAIL);
return;
}
if (!nego_send_negotiation_request(nego))
{
nego_set_state(nego, NEGO_STATE_FAIL);
return;
}
if (!nego_recv_response(nego))
{
nego_set_state(nego, NEGO_STATE_FAIL);
return;
}
WLog_DBG(TAG, "state: %s", nego_state_string(nego_get_state(nego)));
if (nego_get_state(nego) != NEGO_STATE_FINAL)
{
nego_transport_disconnect(nego);
if (nego->EnabledProtocols[PROTOCOL_SSL])
nego_set_state(nego, NEGO_STATE_TLS);
2015-02-03 01:16:32 +03:00
else if (nego->EnabledProtocols[PROTOCOL_RDP])
nego_set_state(nego, NEGO_STATE_RDP);
else
nego_set_state(nego, NEGO_STATE_FAIL);
}
}
static void nego_attempt_tls(rdpNego* nego)
{
WINPR_ASSERT(nego);
nego->RequestedProtocols = PROTOCOL_SSL;
2015-02-02 19:50:56 +03:00
WLog_DBG(TAG, "Attempting TLS security");
if (!nego_transport_connect(nego))
{
nego_set_state(nego, NEGO_STATE_FAIL);
return;
}
if (!nego_send_negotiation_request(nego))
{
nego_set_state(nego, NEGO_STATE_FAIL);
return;
}
if (!nego_recv_response(nego))
{
nego_set_state(nego, NEGO_STATE_FAIL);
return;
}
if (nego_get_state(nego) != NEGO_STATE_FINAL)
{
nego_transport_disconnect(nego);
2015-02-03 01:16:32 +03:00
if (nego->EnabledProtocols[PROTOCOL_RDP])
nego_set_state(nego, NEGO_STATE_RDP);
else
nego_set_state(nego, NEGO_STATE_FAIL);
}
}
static void nego_attempt_rdp(rdpNego* nego)
{
WINPR_ASSERT(nego);
2015-02-03 01:16:32 +03:00
nego->RequestedProtocols = PROTOCOL_RDP;
2015-02-02 19:50:56 +03:00
WLog_DBG(TAG, "Attempting RDP security");
if (!nego_transport_connect(nego))
{
nego_set_state(nego, NEGO_STATE_FAIL);
return;
}
if (!nego_send_negotiation_request(nego))
{
nego_set_state(nego, NEGO_STATE_FAIL);
return;
}
if (!nego_recv_response(nego))
{
nego_set_state(nego, NEGO_STATE_FAIL);
return;
}
}
/**
* Wait to receive a negotiation response
2022-12-09 16:35:03 +03:00
*
* @param nego A pointer to the NEGO struct
*
* @return \b TRUE for success, \b FALSE for failure
*/
BOOL nego_recv_response(rdpNego* nego)
{
int status;
wStream* s;
WINPR_ASSERT(nego);
s = Stream_New(NULL, 1024);
if (!s)
{
WLog_ERR(TAG, "Stream_New failed!");
return FALSE;
}
status = transport_read_pdu(nego->transport, s);
if (status < 0)
{
Stream_Free(s, TRUE);
2013-01-14 02:37:50 +04:00
return FALSE;
}
status = nego_recv(nego->transport, s, nego);
2013-08-28 18:42:23 +04:00
Stream_Free(s, TRUE);
2013-11-08 02:37:58 +04:00
if (status < 0)
return FALSE;
return TRUE;
}
/**
2022-12-09 16:35:03 +03:00
* Receive protocol security negotiation message.
* msdn{cc240501}
*
* @param transport The transport to read from
* @param s A stream to read the received data from
* @param extra nego pointer
2022-12-09 16:35:03 +03:00
*
* @return \b 0 for success, \b -1 for failure
*/
int nego_recv(rdpTransport* transport, wStream* s, void* extra)
{
BYTE li;
BYTE type;
UINT16 length;
2019-11-06 17:24:51 +03:00
rdpNego* nego = (rdpNego*)extra;
WINPR_ASSERT(nego);
if (!tpkt_read_header(s, &length))
return -1;
if (!tpdu_read_connection_confirm(s, &li, length))
return -1;
if (li > 6)
{
/* rdpNegData (optional) */
2013-05-09 00:09:16 +04:00
Stream_Read_UINT8(s, type); /* Type */
switch (type)
{
case TYPE_RDP_NEG_RSP:
if (!nego_process_negotiation_response(nego, s))
return -1;
2019-11-06 17:24:51 +03:00
WLog_DBG(TAG, "selected_protocol: %" PRIu32 "", nego->SelectedProtocol);
2012-03-31 18:09:19 +04:00
/* enhanced security selected ? */
2015-02-03 01:16:32 +03:00
if (nego->SelectedProtocol)
{
if ((nego->SelectedProtocol == PROTOCOL_RDSAAD) &&
(!nego->EnabledProtocols[PROTOCOL_RDSAAD]))
{
nego_set_state(nego, NEGO_STATE_FAIL);
}
if ((nego->SelectedProtocol == PROTOCOL_HYBRID) &&
(!nego->EnabledProtocols[PROTOCOL_HYBRID]))
{
nego_set_state(nego, NEGO_STATE_FAIL);
}
if ((nego->SelectedProtocol == PROTOCOL_SSL) &&
(!nego->EnabledProtocols[PROTOCOL_SSL]))
{
nego_set_state(nego, NEGO_STATE_FAIL);
}
}
2015-02-03 01:16:32 +03:00
else if (!nego->EnabledProtocols[PROTOCOL_RDP])
{
nego_set_state(nego, NEGO_STATE_FAIL);
}
break;
case TYPE_RDP_NEG_FAILURE:
if (!nego_process_negotiation_failure(nego, s))
return -1;
break;
}
}
else if (li == 6)
{
2015-02-02 19:50:56 +03:00
WLog_DBG(TAG, "no rdpNegData");
2015-02-03 01:16:32 +03:00
if (!nego->EnabledProtocols[PROTOCOL_RDP])
nego_set_state(nego, NEGO_STATE_FAIL);
else
nego_set_state(nego, NEGO_STATE_FINAL);
}
else
{
2015-02-02 19:50:56 +03:00
WLog_ERR(TAG, "invalid negotiation response");
nego_set_state(nego, NEGO_STATE_FAIL);
}
if (!tpkt_ensure_stream_consumed(s, length))
return -1;
return 0;
}
/**
* Read optional routing token or cookie of X.224 Connection Request PDU.
2022-12-09 16:35:03 +03:00
* msdn{cc240470}
*/
static BOOL nego_read_request_token_or_cookie(rdpNego* nego, wStream* s)
{
/* routingToken and cookie are optional and mutually exclusive!
*
* routingToken (variable): An optional and variable-length routing
* token (used for load balancing) terminated by a 0x0D0A two-byte
* sequence: (check [MSFT-SDLBTS] for details!)
* Cookie:[space]msts=[ip address].[port].[reserved][\x0D\x0A]
*
* cookie (variable): An optional and variable-length ANSI character
* string terminated by a 0x0D0A two-byte sequence:
* Cookie:[space]mstshash=[ANSISTRING][\x0D\x0A]
*/
BYTE* str = NULL;
UINT16 crlf = 0;
size_t pos, len;
BOOL result = FALSE;
BOOL isToken = FALSE;
size_t remain = Stream_GetRemainingLength(s);
WINPR_ASSERT(nego);
str = Stream_Pointer(s);
pos = Stream_GetPosition(s);
/* minimum length for token is 15 */
if (remain < 15)
return TRUE;
if (memcmp(Stream_Pointer(s), "Cookie: mstshash=", 17) != 0)
{
if (memcmp(Stream_Pointer(s), "Cookie: msts=", 13) != 0)
{
/* remaining bytes are neither a token nor a cookie */
return TRUE;
}
isToken = TRUE;
}
else
{
/* not a token, minimum length for cookie is 19 */
if (remain < 19)
return TRUE;
Stream_Seek(s, 17);
}
while (Stream_GetRemainingLength(s) >= 2)
{
Stream_Read_UINT16(s, crlf);
if (crlf == 0x0A0D)
break;
Stream_Rewind(s, 1);
}
if (crlf == 0x0A0D)
{
Stream_Rewind(s, 2);
len = Stream_GetPosition(s) - pos;
Stream_Write_UINT16(s, 0);
2019-11-05 16:55:33 +03:00
if (strnlen((char*)str, len) == len)
{
if (isToken)
result = nego_set_routing_token(nego, str, len);
else
result = nego_set_cookie(nego, (char*)str);
}
}
if (!result)
{
Stream_SetPosition(s, pos);
2019-11-06 17:24:51 +03:00
WLog_ERR(TAG, "invalid %s received", isToken ? "routing token" : "cookie");
}
else
{
2019-11-06 17:24:51 +03:00
WLog_DBG(TAG, "received %s [%s]", isToken ? "routing token" : "cookie", str);
}
return result;
}
/**
2022-12-09 16:35:03 +03:00
* Read protocol security negotiation request message.
*
* @param nego A pointer to the NEGO struct
* @param s A stream to read from
*
* @return \b TRUE for success, \b FALSE for failure
*/
BOOL nego_read_request(rdpNego* nego, wStream* s)
{
BYTE li;
BYTE type;
UINT16 length;
WINPR_ASSERT(nego);
WINPR_ASSERT(s);
if (!tpkt_read_header(s, &length))
return FALSE;
if (!tpdu_read_connection_request(s, &li, length))
return FALSE;
if (li != Stream_GetRemainingLength(s) + 6)
{
2015-02-03 01:16:32 +03:00
WLog_ERR(TAG, "Incorrect TPDU length indicator.");
return FALSE;
}
if (!nego_read_request_token_or_cookie(nego, s))
{
WLog_ERR(TAG, "Failed to parse routing token or cookie.");
return FALSE;
}
if (Stream_GetRemainingLength(s) >= 8)
{
/* rdpNegData (optional) */
2013-05-09 00:09:16 +04:00
Stream_Read_UINT8(s, type); /* Type */
if (type != TYPE_RDP_NEG_REQ)
{
2019-11-06 17:24:51 +03:00
WLog_ERR(TAG, "Incorrect negotiation request type %" PRIu8 "", type);
return FALSE;
}
if (!nego_process_negotiation_request(nego, s))
return FALSE;
}
return tpkt_ensure_stream_consumed(s, length);
}
/**
* Send protocol security negotiation message.
2022-12-09 16:35:03 +03:00
*
* @param nego A pointer to the NEGO struct
*/
void nego_send(rdpNego* nego)
{
WINPR_ASSERT(nego);
switch (nego_get_state(nego))
{
case NEGO_STATE_AAD:
nego_attempt_rdsaad(nego);
break;
case NEGO_STATE_RDSTLS:
nego_attempt_rdstls(nego);
break;
case NEGO_STATE_EXT:
nego_attempt_ext(nego);
break;
case NEGO_STATE_NLA:
nego_attempt_nla(nego);
break;
case NEGO_STATE_TLS:
nego_attempt_tls(nego);
break;
case NEGO_STATE_RDP:
nego_attempt_rdp(nego);
break;
default:
WLog_ERR(TAG, "invalid negotiation state for sending");
break;
}
}
/**
2022-12-09 16:35:03 +03:00
* Send RDP Negotiation Request (RDP_NEG_REQ).
* msdn{cc240500}
* msdn{cc240470}
*
* @param nego A pointer to the NEGO struct
*
* @return \b TRUE for success, \b FALSE otherwise
*/
BOOL nego_send_negotiation_request(rdpNego* nego)
{
2018-11-20 19:03:50 +03:00
BOOL rc = FALSE;
wStream* s;
2018-11-20 19:03:50 +03:00
size_t length;
size_t bm, em;
2013-11-06 10:51:55 +04:00
BYTE flags = 0;
2018-11-20 19:03:50 +03:00
size_t cookie_length;
s = Stream_New(NULL, 512);
WINPR_ASSERT(nego);
if (!s)
{
WLog_ERR(TAG, "Stream_New failed!");
return FALSE;
}
length = TPDU_CONNECTION_REQUEST_LENGTH;
bm = Stream_GetPosition(s);
Stream_Seek(s, length);
if (nego->RoutingToken)
{
2013-05-09 00:09:16 +04:00
Stream_Write(s, nego->RoutingToken, nego->RoutingTokenLength);
2015-02-03 01:16:32 +03:00
/* Ensure Routing Token is correctly terminated - may already be present in string */
2015-02-03 01:16:32 +03:00
if ((nego->RoutingTokenLength > 2) &&
(nego->RoutingToken[nego->RoutingTokenLength - 2] == 0x0D) &&
(nego->RoutingToken[nego->RoutingTokenLength - 1] == 0x0A))
{
2015-02-02 19:50:56 +03:00
WLog_DBG(TAG, "Routing token looks correctly terminated - use verbatim");
length += nego->RoutingTokenLength;
}
else
{
2015-02-02 19:50:56 +03:00
WLog_DBG(TAG, "Adding terminating CRLF to routing token");
Stream_Write_UINT8(s, 0x0D); /* CR */
Stream_Write_UINT8(s, 0x0A); /* LF */
length += nego->RoutingTokenLength + 2;
}
}
else if (nego->cookie)
{
cookie_length = strlen(nego->cookie);
2018-11-20 19:03:50 +03:00
if (cookie_length > nego->CookieMaxLength)
2015-02-03 01:16:32 +03:00
cookie_length = nego->CookieMaxLength;
2013-05-09 00:09:16 +04:00
Stream_Write(s, "Cookie: mstshash=", 17);
2019-11-06 17:24:51 +03:00
Stream_Write(s, (BYTE*)nego->cookie, cookie_length);
2013-05-09 00:09:16 +04:00
Stream_Write_UINT8(s, 0x0D); /* CR */
Stream_Write_UINT8(s, 0x0A); /* LF */
length += cookie_length + 19;
}
2019-11-06 17:24:51 +03:00
WLog_DBG(TAG, "RequestedProtocols: %" PRIu32 "", nego->RequestedProtocols);
2015-02-03 01:16:32 +03:00
if ((nego->RequestedProtocols > PROTOCOL_RDP) || (nego->sendNegoData))
{
/* RDP_NEG_DATA must be present for TLS and NLA */
2013-11-06 10:51:55 +04:00
if (nego->RestrictedAdminModeRequired)
flags |= RESTRICTED_ADMIN_MODE_REQUIRED;
2013-05-09 00:09:16 +04:00
Stream_Write_UINT8(s, TYPE_RDP_NEG_REQ);
2013-11-06 10:51:55 +04:00
Stream_Write_UINT8(s, flags);
2019-11-06 17:24:51 +03:00
Stream_Write_UINT16(s, 8); /* RDP_NEG_DATA length (8) */
2015-02-03 01:16:32 +03:00
Stream_Write_UINT32(s, nego->RequestedProtocols); /* requestedProtocols */
length += 8;
}
2019-10-16 19:49:24 +03:00
if (length > UINT16_MAX)
2018-11-20 19:03:50 +03:00
goto fail;
em = Stream_GetPosition(s);
Stream_SetPosition(s, bm);
if (!tpkt_write_header(s, (UINT16)length))
goto fail;
if (!tpdu_write_connection_request(s, (UINT16)length - 5))
goto fail;
Stream_SetPosition(s, em);
Stream_SealLength(s);
2018-11-20 19:03:50 +03:00
rc = (transport_write(nego->transport, s) >= 0);
fail:
Stream_Free(s, TRUE);
2018-11-20 19:03:50 +03:00
return rc;
}
2021-11-30 12:01:22 +03:00
static BOOL nego_process_correlation_info(rdpNego* nego, wStream* s)
{
UINT8 type, flags, x;
UINT16 length;
BYTE correlationId[16] = { 0 };
if (!Stream_CheckAndLogRequiredLength(TAG, s, 36))
2021-11-30 12:01:22 +03:00
{
WLog_ERR(TAG, "RDP_NEG_REQ::flags CORRELATION_INFO_PRESENT but data is missing");
return FALSE;
}
Stream_Read_UINT8(s, type);
if (type != TYPE_RDP_CORRELATION_INFO)
{
WLog_ERR(TAG, "(RDP_NEG_CORRELATION_INFO::type != TYPE_RDP_CORRELATION_INFO");
return FALSE;
}
Stream_Read_UINT8(s, flags);
if (flags != 0)
{
WLog_ERR(TAG, "(RDP_NEG_CORRELATION_INFO::flags != 0");
return FALSE;
}
Stream_Read_UINT16(s, length);
if (length != 36)
{
WLog_ERR(TAG, "(RDP_NEG_CORRELATION_INFO::length != 36");
return FALSE;
}
Stream_Read(s, correlationId, sizeof(correlationId));
if ((correlationId[0] == 0x00) || (correlationId[0] == 0xF4))
{
WLog_ERR(TAG, "(RDP_NEG_CORRELATION_INFO::correlationId[0] has invalid value 0x%02" PRIx8,
correlationId[0]);
return FALSE;
}
for (x = 0; x < ARRAYSIZE(correlationId); x++)
{
if (correlationId[x] == 0x0D)
{
WLog_ERR(TAG,
"(RDP_NEG_CORRELATION_INFO::correlationId[%" PRIu8
"] has invalid value 0x%02" PRIx8,
x, correlationId[x]);
return FALSE;
}
}
Stream_Seek(s, 16); /* skip reserved bytes */
WLog_INFO(TAG,
"RDP_NEG_CORRELATION_INFO::correlationId = { %02 " PRIx8 ", %02" PRIx8 ", %02" PRIx8
", %02" PRIx8 ", %02" PRIx8 ", %02" PRIx8 ", %02" PRIx8 ", %02" PRIx8 ", %02" PRIx8
", %02" PRIx8 ", %02" PRIx8 ", %02" PRIx8 ", %02" PRIx8 ", %02" PRIx8 ", %02" PRIx8
", %02" PRIx8 " }",
correlationId[0], correlationId[1], correlationId[2], correlationId[3],
correlationId[4], correlationId[5], correlationId[6], correlationId[7],
correlationId[8], correlationId[9], correlationId[10], correlationId[11],
correlationId[12], correlationId[13], correlationId[14], correlationId[15]);
return TRUE;
}
BOOL nego_process_negotiation_request(rdpNego* nego, wStream* s)
{
BYTE flags;
UINT16 length;
WINPR_ASSERT(nego);
WINPR_ASSERT(s);
if (!Stream_CheckAndLogRequiredLength(TAG, s, 7))
return FALSE;
2013-05-09 00:09:16 +04:00
Stream_Read_UINT8(s, flags);
2021-11-30 12:01:22 +03:00
if ((flags & ~(RESTRICTED_ADMIN_MODE_REQUIRED | REDIRECTED_AUTHENTICATION_MODE_REQUIRED |
CORRELATION_INFO_PRESENT)) != 0)
{
WLog_ERR(TAG, "RDP_NEG_REQ::flags invalid value 0x%02" PRIx8, flags);
return FALSE;
}
if (flags & RESTRICTED_ADMIN_MODE_REQUIRED)
WLog_INFO(TAG, "RDP_NEG_REQ::flags RESTRICTED_ADMIN_MODE_REQUIRED");
if (flags & REDIRECTED_AUTHENTICATION_MODE_REQUIRED)
{
WLog_ERR(TAG, "RDP_NEG_REQ::flags REDIRECTED_AUTHENTICATION_MODE_REQUIRED: FreeRDP does "
"not support Remote Credential Guard");
return FALSE;
}
2013-05-09 00:09:16 +04:00
Stream_Read_UINT16(s, length);
2021-11-30 12:01:22 +03:00
if (length != 8)
{
WLog_ERR(TAG, "RDP_NEG_REQ::length != 8");
return FALSE;
}
2015-02-03 01:16:32 +03:00
Stream_Read_UINT32(s, nego->RequestedProtocols);
2021-11-30 12:01:22 +03:00
if (flags & CORRELATION_INFO_PRESENT)
{
if (!nego_process_correlation_info(nego, s))
return FALSE;
}
2019-11-06 17:24:51 +03:00
WLog_DBG(TAG, "RDP_NEG_REQ: RequestedProtocol: 0x%08" PRIX32 "", nego->RequestedProtocols);
nego_set_state(nego, NEGO_STATE_FINAL);
return TRUE;
}
2021-11-30 12:01:22 +03:00
static const char* nego_rdp_neg_rsp_flags_str(UINT32 flags)
{
static char buffer[1024] = { 0 };
_snprintf(buffer, ARRAYSIZE(buffer), "[0x%02" PRIx32 "] ", flags);
if (flags & EXTENDED_CLIENT_DATA_SUPPORTED)
2022-06-03 11:25:30 +03:00
winpr_str_append("EXTENDED_CLIENT_DATA_SUPPORTED", buffer, sizeof(buffer), "|");
2021-11-30 12:01:22 +03:00
if (flags & DYNVC_GFX_PROTOCOL_SUPPORTED)
2022-06-03 11:25:30 +03:00
winpr_str_append("DYNVC_GFX_PROTOCOL_SUPPORTED", buffer, sizeof(buffer), "|");
2021-11-30 12:01:22 +03:00
if (flags & RDP_NEGRSP_RESERVED)
2022-06-03 11:25:30 +03:00
winpr_str_append("RDP_NEGRSP_RESERVED", buffer, sizeof(buffer), "|");
2021-11-30 12:01:22 +03:00
if (flags & RESTRICTED_ADMIN_MODE_SUPPORTED)
2022-06-03 11:25:30 +03:00
winpr_str_append("RESTRICTED_ADMIN_MODE_SUPPORTED", buffer, sizeof(buffer), "|");
2021-11-30 12:01:22 +03:00
if (flags & REDIRECTED_AUTHENTICATION_MODE_SUPPORTED)
2022-06-03 11:25:30 +03:00
winpr_str_append("REDIRECTED_AUTHENTICATION_MODE_SUPPORTED", buffer, sizeof(buffer), "|");
2021-11-30 12:01:22 +03:00
if ((flags &
~(EXTENDED_CLIENT_DATA_SUPPORTED | DYNVC_GFX_PROTOCOL_SUPPORTED | RDP_NEGRSP_RESERVED |
RESTRICTED_ADMIN_MODE_SUPPORTED | REDIRECTED_AUTHENTICATION_MODE_SUPPORTED)))
2022-06-03 11:25:30 +03:00
winpr_str_append("UNKNOWN", buffer, sizeof(buffer), "|");
2021-11-30 12:01:22 +03:00
return buffer;
}
BOOL nego_process_negotiation_response(rdpNego* nego, wStream* s)
{
UINT16 length;
WINPR_ASSERT(nego);
WINPR_ASSERT(s);
if (!Stream_CheckAndLogRequiredLength(TAG, s, 7))
{
nego_set_state(nego, NEGO_STATE_FAIL);
return FALSE;
}
2013-05-09 00:09:16 +04:00
Stream_Read_UINT8(s, nego->flags);
WLog_DBG(TAG, "RDP_NEG_RSP::flags = { %s }", nego_rdp_neg_rsp_flags_str(nego->flags));
2021-11-30 12:01:22 +03:00
2013-05-09 00:09:16 +04:00
Stream_Read_UINT16(s, length);
2021-11-30 12:01:22 +03:00
if (length != 8)
{
WLog_ERR(TAG, "RDP_NEG_RSP::length != 8");
nego_set_state(nego, NEGO_STATE_FAIL);
return FALSE;
}
UINT32 SelectedProtocol;
Stream_Read_UINT32(s, SelectedProtocol);
if (!nego_set_selected_protocol(nego, SelectedProtocol))
return FALSE;
return nego_set_state(nego, NEGO_STATE_FINAL);
}
/**
* Process Negotiation Failure from Connection Confirm message.
2022-12-09 16:35:03 +03:00
* @param nego A pointer to the NEGO struct
* @param s The stream to read from
*
* @return \b TRUE for success, \b FALSE otherwise
*/
BOOL nego_process_negotiation_failure(rdpNego* nego, wStream* s)
{
BYTE flags;
UINT16 length;
2012-10-09 11:26:39 +04:00
UINT32 failureCode;
WINPR_ASSERT(nego);
WINPR_ASSERT(s);
2015-02-02 19:50:56 +03:00
WLog_DBG(TAG, "RDP_NEG_FAILURE");
if (!Stream_CheckAndLogRequiredLength(TAG, s, 7))
return FALSE;
2013-05-09 00:09:16 +04:00
Stream_Read_UINT8(s, flags);
2021-11-30 12:01:22 +03:00
if (flags != 0)
{
WLog_WARN(TAG, "RDP_NEG_FAILURE::flags = 0x%02" PRIx8, flags);
2021-11-30 12:01:22 +03:00
return FALSE;
}
2013-05-09 00:09:16 +04:00
Stream_Read_UINT16(s, length);
2021-11-30 12:01:22 +03:00
if (length != 8)
{
WLog_ERR(TAG, "RDP_NEG_FAILURE::length != 8");
return FALSE;
}
2013-05-09 00:09:16 +04:00
Stream_Read_UINT32(s, failureCode);
switch (failureCode)
{
case SSL_REQUIRED_BY_SERVER:
2015-02-18 23:36:57 +03:00
WLog_WARN(TAG, "Error: SSL_REQUIRED_BY_SERVER");
break;
case SSL_NOT_ALLOWED_BY_SERVER:
2015-02-18 23:36:57 +03:00
WLog_WARN(TAG, "Error: SSL_NOT_ALLOWED_BY_SERVER");
nego->sendNegoData = TRUE;
break;
case SSL_CERT_NOT_ON_SERVER:
2015-02-02 19:50:56 +03:00
WLog_ERR(TAG, "Error: SSL_CERT_NOT_ON_SERVER");
nego->sendNegoData = TRUE;
break;
case INCONSISTENT_FLAGS:
2015-02-02 19:50:56 +03:00
WLog_ERR(TAG, "Error: INCONSISTENT_FLAGS");
break;
case HYBRID_REQUIRED_BY_SERVER:
2015-02-18 23:36:57 +03:00
WLog_WARN(TAG, "Error: HYBRID_REQUIRED_BY_SERVER");
break;
default:
2019-11-06 17:24:51 +03:00
WLog_ERR(TAG, "Error: Unknown protocol security error %" PRIu32 "", failureCode);
break;
}
nego_set_state(nego, NEGO_STATE_FAIL);
return TRUE;
}
/**
2022-12-09 16:35:03 +03:00
* Send RDP Negotiation Response (RDP_NEG_RSP).
* @param nego A pointer to the NEGO struct
*/
BOOL nego_send_negotiation_response(rdpNego* nego)
{
2018-11-20 19:03:50 +03:00
UINT16 length;
size_t bm, em;
BOOL status;
wStream* s;
BYTE flags;
2021-09-06 12:01:36 +03:00
rdpContext* context;
rdpSettings* settings;
WINPR_ASSERT(nego);
2021-09-06 12:01:36 +03:00
context = transport_get_context(nego->transport);
WINPR_ASSERT(context);
2021-09-06 12:01:36 +03:00
settings = context->settings;
WINPR_ASSERT(settings);
s = Stream_New(NULL, 512);
if (!s)
{
WLog_ERR(TAG, "Stream_New failed!");
return FALSE;
}
length = TPDU_CONNECTION_CONFIRM_LENGTH;
bm = Stream_GetPosition(s);
Stream_Seek(s, length);
if (nego->SelectedProtocol & PROTOCOL_FAILED_NEGO)
{
UINT32 errorCode = (nego->SelectedProtocol & ~PROTOCOL_FAILED_NEGO);
flags = 0;
2013-05-09 00:09:16 +04:00
Stream_Write_UINT8(s, TYPE_RDP_NEG_FAILURE);
Stream_Write_UINT8(s, flags); /* flags */
2019-11-06 17:24:51 +03:00
Stream_Write_UINT16(s, 8); /* RDP_NEG_DATA length (8) */
Stream_Write_UINT32(s, errorCode);
length += 8;
}
else
{
flags = EXTENDED_CLIENT_DATA_SUPPORTED;
2023-01-18 13:03:33 +03:00
if (freerdp_settings_get_bool(settings, FreeRDP_SupportGraphicsPipeline))
flags |= DYNVC_GFX_PROTOCOL_SUPPORTED;
/* RDP_NEG_DATA must be present for TLS, NLA, RDP and RDSTLS */
Stream_Write_UINT8(s, TYPE_RDP_NEG_RSP);
2019-11-06 17:24:51 +03:00
Stream_Write_UINT8(s, flags); /* flags */
Stream_Write_UINT16(s, 8); /* RDP_NEG_DATA length (8) */
2015-02-03 01:16:32 +03:00
Stream_Write_UINT32(s, nego->SelectedProtocol); /* selectedProtocol */
length += 8;
}
em = Stream_GetPosition(s);
Stream_SetPosition(s, bm);
status = tpkt_write_header(s, length);
if (status)
2013-09-06 13:07:33 +04:00
{
tpdu_write_connection_confirm(s, length - 5);
Stream_SetPosition(s, em);
Stream_SealLength(s);
status = (transport_write(nego->transport, s) >= 0);
}
Stream_Free(s, TRUE);
if (status)
{
/* update settings with negotiated protocol security */
2023-01-18 13:03:33 +03:00
if (!freerdp_settings_set_uint32(settings, FreeRDP_RequestedProtocols,
nego->RequestedProtocols))
return FALSE;
if (!freerdp_settings_set_uint32(settings, FreeRDP_SelectedProtocol,
nego->SelectedProtocol))
return FALSE;
switch (nego->SelectedProtocol)
{
case PROTOCOL_RDP:
if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, FALSE))
return FALSE;
if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE))
return FALSE;
if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, TRUE))
return FALSE;
if (!freerdp_settings_set_bool(settings, FreeRDP_UseRdpSecurityLayer, TRUE))
2023-01-18 13:03:33 +03:00
return FALSE;
if (freerdp_settings_get_uint32(settings, FreeRDP_EncryptionLevel) ==
ENCRYPTION_LEVEL_NONE)
{
/**
* If the server implementation did not explicitely set a
* encryption level we default to client compatible
*/
if (!freerdp_settings_set_uint32(settings, FreeRDP_EncryptionLevel,
ENCRYPTION_LEVEL_CLIENT_COMPATIBLE))
return FALSE;
}
if (freerdp_settings_get_bool(settings, FreeRDP_LocalConnection))
{
/**
* Note: This hack was firstly introduced in commit 95f5e115 to
* disable the unnecessary encryption with peers connecting to
* 127.0.0.1 or local unix sockets.
* This also affects connections via port tunnels! (e.g. ssh -L)
*/
WLog_INFO(TAG,
"Turning off encryption for local peer with standard rdp security");
if (!freerdp_settings_set_bool(settings, FreeRDP_UseRdpSecurityLayer, FALSE))
return FALSE;
if (!freerdp_settings_set_uint32(settings, FreeRDP_EncryptionLevel,
ENCRYPTION_LEVEL_NONE))
return FALSE;
}
else if (!freerdp_settings_get_pointer(settings, FreeRDP_RdpServerRsaKey))
{
WLog_ERR(TAG, "Missing server certificate");
return FALSE;
}
break;
case PROTOCOL_SSL:
if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, TRUE))
return FALSE;
if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE))
return FALSE;
if (!freerdp_settings_set_bool(settings, FreeRDP_RdstlsSecurity, FALSE))
return FALSE;
if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, FALSE))
return FALSE;
2023-01-18 13:03:33 +03:00
if (!freerdp_settings_set_bool(settings, FreeRDP_UseRdpSecurityLayer, FALSE))
return FALSE;
2023-01-18 13:03:33 +03:00
if (!freerdp_settings_set_uint32(settings, FreeRDP_EncryptionLevel,
ENCRYPTION_LEVEL_NONE))
return FALSE;
break;
case PROTOCOL_HYBRID:
if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, TRUE))
return FALSE;
if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, TRUE))
return FALSE;
if (!freerdp_settings_set_bool(settings, FreeRDP_RdstlsSecurity, FALSE))
return FALSE;
if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, FALSE))
return FALSE;
if (!freerdp_settings_set_bool(settings, FreeRDP_UseRdpSecurityLayer, FALSE))
return FALSE;
if (!freerdp_settings_set_uint32(settings, FreeRDP_EncryptionLevel,
ENCRYPTION_LEVEL_NONE))
return FALSE;
break;
case PROTOCOL_RDSTLS:
if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, TRUE))
return FALSE;
if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE))
return FALSE;
if (!freerdp_settings_set_bool(settings, FreeRDP_RdstlsSecurity, TRUE))
return FALSE;
if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, FALSE))
return FALSE;
if (!freerdp_settings_set_bool(settings, FreeRDP_UseRdpSecurityLayer, FALSE))
return FALSE;
2023-01-18 13:03:33 +03:00
if (!freerdp_settings_set_uint32(settings, FreeRDP_EncryptionLevel,
ENCRYPTION_LEVEL_NONE))
return FALSE;
break;
default:
break;
}
}
return status;
}
/**
* Initialize NEGO state machine.
2022-12-09 16:35:03 +03:00
* @param nego A pointer to the NEGO struct
*/
void nego_init(rdpNego* nego)
{
WINPR_ASSERT(nego);
nego_set_state(nego, NEGO_STATE_INITIAL);
2015-02-03 01:16:32 +03:00
nego->RequestedProtocols = PROTOCOL_RDP;
nego->CookieMaxLength = DEFAULT_COOKIE_MAX_LENGTH;
nego->sendNegoData = FALSE;
nego->flags = 0;
}
/**
* Create a new NEGO state machine instance.
2022-12-09 16:35:03 +03:00
*
* @param transport The transport to use
*
* @return A pointer to the allocated NEGO instance or NULL
*/
rdpNego* nego_new(rdpTransport* transport)
{
2019-11-06 17:24:51 +03:00
rdpNego* nego = (rdpNego*)calloc(1, sizeof(rdpNego));
if (!nego)
return NULL;
nego->transport = transport;
nego_init(nego);
return nego;
}
/**
* Free NEGO state machine.
2022-12-09 16:35:03 +03:00
* @param nego A pointer to the NEGO struct
*/
void nego_free(rdpNego* nego)
{
2015-05-11 10:07:39 +03:00
if (nego)
{
free(nego->RoutingToken);
free(nego->cookie);
free(nego);
}
}
/**
* Set target hostname and port.
2022-12-09 16:35:03 +03:00
* @param nego A pointer to the NEGO struct
* @param hostname The hostname to set
* @param port The port to set
*
* @return \b TRUE for success, \b FALSE otherwise
*/
2018-11-20 19:03:50 +03:00
BOOL nego_set_target(rdpNego* nego, const char* hostname, UINT16 port)
{
2018-11-20 19:03:50 +03:00
if (!nego || !hostname)
return FALSE;
nego->hostname = hostname;
nego->port = port;
2018-11-20 19:03:50 +03:00
return TRUE;
}
/**
* Enable security layer negotiation.
2022-12-09 16:35:03 +03:00
* @param nego A pointer to the NEGO struct pointer to the negotiation structure
* @param NegotiateSecurityLayer whether to enable security layer negotiation (TRUE for enabled,
* FALSE for disabled)
*/
void nego_set_negotiation_enabled(rdpNego* nego, BOOL NegotiateSecurityLayer)
{
2019-11-06 17:24:51 +03:00
WLog_DBG(TAG, "Enabling security layer negotiation: %s",
NegotiateSecurityLayer ? "TRUE" : "FALSE");
nego->NegotiateSecurityLayer = NegotiateSecurityLayer;
}
2013-11-06 10:51:55 +04:00
/**
* Enable restricted admin mode.
2022-12-09 16:35:03 +03:00
* @param nego A pointer to the NEGO struct pointer to the negotiation structure
* @param RestrictedAdminModeRequired whether to enable security layer negotiation (TRUE for
* enabled, FALSE for disabled)
2013-11-06 10:51:55 +04:00
*/
void nego_set_restricted_admin_mode_required(rdpNego* nego, BOOL RestrictedAdminModeRequired)
{
2019-11-06 17:24:51 +03:00
WLog_DBG(TAG, "Enabling restricted admin mode: %s",
RestrictedAdminModeRequired ? "TRUE" : "FALSE");
2013-11-06 10:51:55 +04:00
nego->RestrictedAdminModeRequired = RestrictedAdminModeRequired;
}
void nego_set_gateway_enabled(rdpNego* nego, BOOL GatewayEnabled)
{
nego->GatewayEnabled = GatewayEnabled;
}
void nego_set_gateway_bypass_local(rdpNego* nego, BOOL GatewayBypassLocal)
{
nego->GatewayBypassLocal = GatewayBypassLocal;
}
/**
* Enable RDP security protocol.
2022-12-09 16:35:03 +03:00
* @param nego A pointer to the NEGO struct pointer to the negotiation structure
* @param enable_rdp whether to enable normal RDP protocol (TRUE for enabled, FALSE for disabled)
*/
void nego_enable_rdp(rdpNego* nego, BOOL enable_rdp)
{
2015-02-02 19:50:56 +03:00
WLog_DBG(TAG, "Enabling RDP security: %s", enable_rdp ? "TRUE" : "FALSE");
2015-02-03 01:16:32 +03:00
nego->EnabledProtocols[PROTOCOL_RDP] = enable_rdp;
}
/**
* Enable TLS security protocol.
2022-12-09 16:35:03 +03:00
* @param nego A pointer to the NEGO struct pointer to the negotiation structure
* @param enable_tls whether to enable TLS + RDP protocol (TRUE for enabled, FALSE for disabled)
*/
2013-11-06 10:51:55 +04:00
void nego_enable_tls(rdpNego* nego, BOOL enable_tls)
{
2015-02-02 19:50:56 +03:00
WLog_DBG(TAG, "Enabling TLS security: %s", enable_tls ? "TRUE" : "FALSE");
nego->EnabledProtocols[PROTOCOL_SSL] = enable_tls;
}
/**
* Enable NLA security protocol.
2022-12-09 16:35:03 +03:00
* @param nego A pointer to the NEGO struct pointer to the negotiation structure
2019-11-06 17:24:51 +03:00
* @param enable_nla whether to enable network level authentication protocol (TRUE for enabled,
* FALSE for disabled)
*/
void nego_enable_nla(rdpNego* nego, BOOL enable_nla)
{
2015-02-02 19:50:56 +03:00
WLog_DBG(TAG, "Enabling NLA security: %s", enable_nla ? "TRUE" : "FALSE");
nego->EnabledProtocols[PROTOCOL_HYBRID] = enable_nla;
}
/**
* Enable RDSTLS security protocol.
* @param nego A pointer to the NEGO struct pointer to the negotiation structure
* @param enable_rdstls whether to enable RDSTLS protocol (TRUE for enabled,
* FALSE for disabled)
*/
void nego_enable_rdstls(rdpNego* nego, BOOL enable_rdstls)
{
WLog_DBG(TAG, "Enabling RDSTLS security: %s", enable_rdstls ? "TRUE" : "FALSE");
nego->EnabledProtocols[PROTOCOL_RDSTLS] = enable_rdstls;
}
/**
* Enable NLA extended security protocol.
2022-12-09 16:35:03 +03:00
* @param nego A pointer to the NEGO struct pointer to the negotiation structure
2019-11-06 17:24:51 +03:00
* @param enable_ext whether to enable network level authentication extended protocol (TRUE for
* enabled, FALSE for disabled)
*/
void nego_enable_ext(rdpNego* nego, BOOL enable_ext)
{
2015-02-02 19:50:56 +03:00
WLog_DBG(TAG, "Enabling NLA extended security: %s", enable_ext ? "TRUE" : "FALSE");
nego->EnabledProtocols[PROTOCOL_HYBRID_EX] = enable_ext;
}
/**
* Enable RDS AAD security protocol.
* @param nego A pointer to the NEGO struct pointer to the negotiation structure
* @param enable_ext whether to enable RDS AAD Auth protocol (TRUE for
* enabled, FALSE for disabled)
*/
void nego_enable_aad(rdpNego* nego, BOOL enable_aad)
{
if (aad_is_supported())
{
WLog_DBG(TAG, "Enabling RDS AAD security: %s", enable_aad ? "TRUE" : "FALSE");
nego->EnabledProtocols[PROTOCOL_RDSAAD] = enable_aad;
}
else
{
WLog_WARN(TAG, "This build does not support AAD security, disabling.");
}
}
/**
* Set routing token.
2022-12-09 16:35:03 +03:00
* @param nego A pointer to the NEGO struct
* @param RoutingToken A pointer to the routing token
* @param RoutingTokenLength The lenght of the routing token
*
* @return \b TRUE for success, \b FALSE otherwise
*/
2020-11-18 09:51:45 +03:00
BOOL nego_set_routing_token(rdpNego* nego, const BYTE* RoutingToken, DWORD RoutingTokenLength)
{
2018-11-29 13:06:41 +03:00
if (RoutingTokenLength == 0)
return FALSE;
free(nego->RoutingToken);
2012-09-24 12:40:32 +04:00
nego->RoutingTokenLength = RoutingTokenLength;
2019-11-06 17:24:51 +03:00
nego->RoutingToken = (BYTE*)malloc(nego->RoutingTokenLength);
2014-04-10 23:07:53 +04:00
if (!nego->RoutingToken)
return FALSE;
CopyMemory(nego->RoutingToken, RoutingToken, nego->RoutingTokenLength);
2014-04-10 23:07:53 +04:00
return TRUE;
}
/**
* Set cookie.
2022-12-09 16:35:03 +03:00
* @param nego A pointer to the NEGO struct
* @param cookie A pointer to the cookie string
*
* @return \b TRUE for success, \b FALSE otherwise
*/
2020-11-18 09:51:45 +03:00
BOOL nego_set_cookie(rdpNego* nego, const char* cookie)
{
if (nego->cookie)
{
free(nego->cookie);
2015-05-11 10:07:39 +03:00
nego->cookie = NULL;
}
if (!cookie)
return TRUE;
nego->cookie = _strdup(cookie);
2014-04-10 23:07:53 +04:00
if (!nego->cookie)
return FALSE;
2014-04-10 23:07:53 +04:00
return TRUE;
}
/**
* Set cookie maximum length
2022-12-09 16:35:03 +03:00
* @param nego A pointer to the NEGO struct
* @param CookieMaxLength the length to set
*/
2015-02-03 01:16:32 +03:00
void nego_set_cookie_max_length(rdpNego* nego, UINT32 CookieMaxLength)
{
2015-02-03 01:16:32 +03:00
nego->CookieMaxLength = CookieMaxLength;
}
/**
* Enable / disable preconnection PDU.
2022-12-09 16:35:03 +03:00
* @param nego A pointer to the NEGO struct
* @param SendPreconnectionPdu The value to set
*/
2015-02-03 01:16:32 +03:00
void nego_set_send_preconnection_pdu(rdpNego* nego, BOOL SendPreconnectionPdu)
{
2015-02-03 01:16:32 +03:00
nego->SendPreconnectionPdu = SendPreconnectionPdu;
}
/**
* Set preconnection id.
2022-12-09 16:35:03 +03:00
* @param nego A pointer to the NEGO struct
* @param PreconnectionId the ID to set
*/
2015-02-03 01:16:32 +03:00
void nego_set_preconnection_id(rdpNego* nego, UINT32 PreconnectionId)
{
2015-02-03 01:16:32 +03:00
nego->PreconnectionId = PreconnectionId;
}
/**
* Set preconnection blob.
2022-12-09 16:35:03 +03:00
* @param nego A pointer to the NEGO struct
* @param PreconnectionBlob A pointer to the blob to use
*/
2020-11-18 09:51:45 +03:00
void nego_set_preconnection_blob(rdpNego* nego, const char* PreconnectionBlob)
{
2015-02-03 01:16:32 +03:00
nego->PreconnectionBlob = PreconnectionBlob;
}
2018-11-20 18:38:06 +03:00
UINT32 nego_get_selected_protocol(rdpNego* nego)
{
if (!nego)
return 0;
return nego->SelectedProtocol;
}
BOOL nego_set_selected_protocol(rdpNego* nego, UINT32 SelectedProtocol)
{
WINPR_ASSERT(nego);
2018-11-20 18:38:06 +03:00
nego->SelectedProtocol = SelectedProtocol;
return TRUE;
}
UINT32 nego_get_requested_protocols(rdpNego* nego)
{
if (!nego)
return 0;
return nego->RequestedProtocols;
}
BOOL nego_set_requested_protocols(rdpNego* nego, UINT32 RequestedProtocols)
{
if (!nego)
return FALSE;
nego->RequestedProtocols = RequestedProtocols;
return TRUE;
}
NEGO_STATE nego_get_state(rdpNego* nego)
{
if (!nego)
return NEGO_STATE_FAIL;
return nego->state;
}
BOOL nego_set_state(rdpNego* nego, NEGO_STATE state)
{
if (!nego)
return FALSE;
nego->state = state;
return TRUE;
}
SEC_WINNT_AUTH_IDENTITY* nego_get_identity(rdpNego* nego)
{
2021-09-06 12:01:36 +03:00
rdpNla* nla;
2018-11-20 18:38:06 +03:00
if (!nego)
return NULL;
2021-09-06 12:01:36 +03:00
nla = transport_get_nla(nego->transport);
return nla_get_identity(nla);
2018-11-20 18:38:06 +03:00
}
void nego_free_nla(rdpNego* nego)
{
if (!nego || !nego->transport)
return;
2021-09-06 12:01:36 +03:00
transport_set_nla(nego->transport, NULL);
2018-11-20 18:38:06 +03:00
}
First version of an RDP proxy (#5372) * server: Add proxy dir with barebones server * sever/proxy: Remove licensing * server/proxy: Add client files * server/proxy: rm binary * server/proxy: Formatting * server/proxy: Fixed includes and added basic client creation functionality * server/proxy: Remove licensing and fix ifndef * proxy/server: Fix cmake indentation * server/proxy: Fix licensing * server/proxy: Forward connection on peer_post_connect * server/proxy: Fix function signature * server/proxy: Changed function signature of proxy_client_start * server/proxy: Now peer_post_connect calls proxy_client_start in a new thread * pfreerdp.c: Clean up useless comments and logs * server/proxy: Fix license * server/proxy: Remove all non-connection related data from proxy_context * server/proxy: Move Log Tag definition to pf_log.h * server/proxy: Move context definition to pf_context * server/proxy: Delete pfreerdp.h * pfreerdp.c: Move context callbacks to pf_context.c * server/proxy: Update CMakeLists.txt * pf_channels: Use new proxy context API * pf_client: Move context to pf_context * pf_client.c: Remove unnessecary event handling * server/proxy: Formatting * proxy/server: Move server logic to pf_server.c * server/proxy: Handle client disconnection * Merge stash * pf_server.c: Open GFX Connection to client * server: CMakeLists: build proxy along with other servers * server: proxy: get target server from rdpNego->RoutingToken Iv'e omitted a check from which im not sure is right. Should check in docs * server/proxy: Handle remote server -> client disconnection * server/proxy: Move common function to pf_common.c * server/proxy: Move common function to pf_common.c * rdpgfx.h: Add reference to freerdp.h for rdpContext * pf_channels: Pipe GFX on channel connection * server/proxy: Add pf_rdpgfx for proxy gfx callbacks * pf_client: Declare dynvc and gfx capabilities on connection * server/proxy: Add graphics callbacks * server/proxy: Add graphics callbacks * pf_server: Listen to channel events * Pass user settings to server * pf_server: Proxy mouse events * fixup! server/proxy: Add graphics callbacks * pf_client: Fix setting initialization * Merge feat/proxy-gfx to feat/proxy * pf_server: Fix double freed credentials * server/proxy: Remove unnecessary call to freerdp_client_settings_parse_command_line * server/proxy: Refactor re-activation code * server/proxy: Run format scripts * server/proxy: Fix segfault when post_disconnect return FALSE * server/proxy: Refactor proxy_settings_mirror * server/proxy: Redirect credentials * server/proxy: move proxy_settings_mirror to pf_common.c * server/proxy: Redirect desktop_resize event * pf_client: Remove interactive CLI auth methods * fixup! server/proxy: Redirect credentials * server/proxy: Rename proxy_mirror_settings to pf_common_copy_settings * pf_server.c: Fixed non-freed context When the disconnection is forced by the target server, the function `pf_server_handle_client_disconnection` isn't called. Therefore, the context of the connection between the proxy to tagrget isn't freed. * fixup! pf_server.c: Fixed non-freed context * pf_client: Prefix all client methods with pf_client * pf_context: Add init client to proxy context method * pf_server: Confirm all GFX caps regardless of settings * pf_server: Prefix all methods with pf_server * pf_server: Move variable decleration to start of method * pf_server: Fix client setting * pf_server: Fix GFX init method * pf_server: Move variable decleration to start of methods * server/proxy: Formatting * Merge feat/proxy * pf_server: Proxy synchronize event * pf_server: Proxy refresh rect update events * pf_server: Proxy suppress output messages * server/proxy: Fix licensing * server/proxy: Move client input callbacks to pf_input * server/proxy: Move client update callbacks to pf_update * server/proxy: Fix non-terminated target host string * Feat/proxy config (#2) * server/proxy: Add config loading support * server/proxy: Add config file * server/proxy: Format code * server/proxy: Code refactor, rename update_register_callbacks and input_register_callbacks * server/proxy: Update config file * server/proxy: Remove config.ini from root directory * Remove comment from config file * server/proxy: Fix leak in pf_server_load_config * server/proxy: Add rdpServerProxy struct and embed it in proxyContext * server/proxy: Load configuration and pass it inside every proxyContext instance * server/proxy: Move rdpProxyServer to proxy.h * server/proxy: Use configuration while proxying input events * server/proxy: Update CMakeLists * server/proxy: Refactor pf_input.c * server/proxy: Add AllowedChannels, DeniedChannels in configuration * server/proxy: Remove unnecessary variable from parse_channels_from_str * server/proxy: Update config file * server/proxy: config: Rename to * server/proxy: config: Add mode - blacklist/whitelist * server/proxy: Refactor, fix NULL deref * server/proxy: Add license to proxy.h * server/proxy: Fix newline in pf_config.c * server/proxy: config: Rename Mode to WhitelistMode * Add target in config. Add checks for configuration validity (#3) * Add target in config. Add checks for configuration validity * Update config file * libfreerdp: nego: revert commented out check of routingToken length * pf_server: Fix target host info from RoutingToken * pf_server: Remove hardcoded lenght of routing token prefix * Feat/refactor context (#8) * Refactor main structs * Update CMakeLists.txt * pf_server.c: Free pdata at the end of the connection * Run format scripts * Rename tf to pc * Fix licenses * pf_server: Refactor names of structs and functions * proxy: gfx: sync caps (#4) * proxy: gfx: sync caps * proxy: gfx: sync caps, hook gfx client's OnClose() call and close server resources * fixup! Feat/refactor context (#8) * fixup! fixup! Feat/refactor context (#8) * rdpgfx/client: Fix rdpgfx_recv_caps_confirm_pdu caps set length parsing * Run format scripts * proxy config.ini: Change default port to 3389 * pf_rdpgfx: Limit caps version to freerdp's supported versions * Gfx OnOpen() wait for dynvc ready (#10) * proxy/gfx: Wait for dynvc ready state before open * pf_channels: Initialize pc->gfx * pf_rdpgfx: Add log and fix comments * rdpgfx: Fix GFX v10.6 PDUs parsing and naming according to the spec * pf_rdpgfx: Proxy rdpgfx v10.6 PDUs * gfx client: Publish FrameAck sending and add auto ack flag * proxy/gfx: Forward frame ack messages * pf_context: Forward domain on connection * pf_rdpgfx: Change max supported caps to 10.6 * proxy: Update config * server/proxy: Use configuration in pf_server_handle_client * rdpgfx/client: Fix size of surface_to_scaled_window, surface_to_window * pf_rdpgfx: Fix formatting * pf_server.c: Fix comments * Move pf_server_rdpgfx_init to pf_rdpgfx * server/proxy/CMakeLists.txt: Fix formatting * pf_client.c: Add comment in proxy_server_reactivate * Fixed const correctness of gfx function pointer Signed-off-by: Mati Shabtay <matishabtay@gmail.com> * server: proxy: update copyright * server: proxy: wrap rdpNego and add a getter for routing token * Refactor routing token getter (#14) * Refactor routing token getter * pf_server_parse_target_from_routing_token change routing_token_length to be DWORD * libfreerdp/core/nego.c: Run format script * pf_server: Run format script * server/proxy: Fix os msbuild tests * pf_channels.c: Remove unused channels * pf_client: Remove unused callbacks * proxy: Remove encomsp callbacks from proxy's client * client/rdpgfx_main.c: Fix msbuild test * pf_config.c: Use StrSep instead of strsep for Windows builds * Removed nego struct from direct access. Signed-off-by: Mati Shabtay <matishabtay@gmail.com> * proxy: Rename binary to freerdp-proxy * rdpgfx_main.c: Revert unwanted double change to send_supported_caps * Cleaned up proxy server code. * All internal functions static * Added simple command line argument to supply a config file * Silence compiler warnings Signed-off-by: kubistika <kmizrachi18@gmail.com>
2019-05-17 15:32:54 +03:00
const BYTE* nego_get_routing_token(rdpNego* nego, DWORD* RoutingTokenLength)
{
if (!nego)
return NULL;
if (RoutingTokenLength)
*RoutingTokenLength = nego->RoutingTokenLength;
return nego->RoutingToken;
}