2011-07-01 02:48:48 +04:00
|
|
|
/**
|
2012-10-09 07:02:04 +04:00
|
|
|
* FreeRDP: A Remote Desktop Protocol Implementation
|
2011-07-01 02:48:48 +04:00
|
|
|
* 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>
|
2015-05-29 14:46:50 +03:00
|
|
|
* Copyright 2015 Thincast Technologies GmbH
|
|
|
|
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
|
2011-07-01 02:48:48 +04:00
|
|
|
*
|
|
|
|
* 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>
|
2012-08-15 01:09:01 +04:00
|
|
|
|
2012-11-01 04:38:48 +04:00
|
|
|
#include <winpr/crt.h>
|
2021-08-26 17:17:51 +03:00
|
|
|
#include <winpr/assert.h>
|
2022-04-19 15:29:17 +03:00
|
|
|
#include <winpr/stream.h>
|
2012-11-01 04:38:48 +04:00
|
|
|
|
2014-09-12 16:36:29 +04:00
|
|
|
#include <freerdp/log.h>
|
|
|
|
|
2011-07-03 20:42:35 +04:00
|
|
|
#include "tpkt.h"
|
|
|
|
|
2011-07-01 02:48:48 +04:00
|
|
|
#include "nego.h"
|
|
|
|
|
2012-07-25 14:29:49 +04:00
|
|
|
#include "transport.h"
|
|
|
|
|
2014-09-12 16:36:29 +04:00
|
|
|
#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;
|
2023-03-08 04:06:47 +03:00
|
|
|
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)
|
2011-07-10 09:48:10 +04:00
|
|
|
{
|
2023-03-07 13:58:28 +03:00
|
|
|
static const char* const NEGO_STATE_STRINGS[] = { "NEGO_STATE_INITIAL", "NEGO_STATE_RDSTLS",
|
2023-03-08 04:06:47 +03:00
|
|
|
"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];
|
|
|
|
}
|
2011-07-10 09:48:10 +04:00
|
|
|
|
2019-09-04 16:46:47 +03:00
|
|
|
static const char* protocol_security_string(UINT32 security)
|
2011-07-10 09:48:10 +04:00
|
|
|
{
|
2023-03-07 13:58:28 +03:00
|
|
|
static const char* PROTOCOL_SECURITY_STRINGS[] = { "RDP", "TLS", "NLA", "UNK", "RDSTLS",
|
2023-03-08 04:06:47 +03:00
|
|
|
"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];
|
|
|
|
}
|
2011-07-10 09:48:10 +04:00
|
|
|
|
2015-01-14 13:35:19 +03:00
|
|
|
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);
|
2018-07-10 13:21:38 +03:00
|
|
|
static BOOL nego_send_preconnection_pdu(rdpNego* nego);
|
|
|
|
static BOOL nego_recv_response(rdpNego* nego);
|
|
|
|
static void nego_send(rdpNego* nego);
|
2020-04-15 18:21:46 +03:00
|
|
|
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);
|
2012-07-25 20:50:19 +04:00
|
|
|
|
2011-07-01 02:48:48 +04:00
|
|
|
/**
|
|
|
|
* 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
|
2011-07-01 02:48:48 +04:00
|
|
|
*/
|
|
|
|
|
2012-10-09 10:38:39 +04:00
|
|
|
BOOL nego_connect(rdpNego* nego)
|
2011-07-01 02:48:48 +04:00
|
|
|
{
|
2021-09-06 12:01:36 +03:00
|
|
|
rdpContext* context;
|
2021-08-26 17:17:51 +03:00
|
|
|
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;
|
2021-08-26 17:17:51 +03:00
|
|
|
WINPR_ASSERT(settings);
|
2021-09-06 12:01:36 +03:00
|
|
|
|
2021-08-26 17:17:51 +03:00
|
|
|
if (nego_get_state(nego) == NEGO_STATE_INITIAL)
|
2011-07-01 02:48:48 +04:00
|
|
|
{
|
2023-03-08 04:06:47 +03:00
|
|
|
if (nego->EnabledProtocols[PROTOCOL_RDSAAD])
|
|
|
|
{
|
|
|
|
nego_set_state(nego, NEGO_STATE_AAD);
|
|
|
|
}
|
|
|
|
else if (nego->EnabledProtocols[PROTOCOL_RDSTLS])
|
2023-03-07 13:58:28 +03:00
|
|
|
{
|
|
|
|
nego_set_state(nego, NEGO_STATE_RDSTLS);
|
|
|
|
}
|
|
|
|
else if (nego->EnabledProtocols[PROTOCOL_HYBRID_EX])
|
2012-11-01 04:38:48 +04:00
|
|
|
{
|
2021-08-26 17:17:51 +03:00
|
|
|
nego_set_state(nego, NEGO_STATE_EXT);
|
2012-11-01 04:38:48 +04:00
|
|
|
}
|
2018-11-20 19:27:47 +03:00
|
|
|
else if (nego->EnabledProtocols[PROTOCOL_HYBRID])
|
2012-11-01 04:38:48 +04:00
|
|
|
{
|
2021-08-26 17:17:51 +03:00
|
|
|
nego_set_state(nego, NEGO_STATE_NLA);
|
2012-11-01 04:38:48 +04:00
|
|
|
}
|
2018-11-20 19:27:47 +03:00
|
|
|
else if (nego->EnabledProtocols[PROTOCOL_SSL])
|
2012-11-01 04:38:48 +04:00
|
|
|
{
|
2021-08-26 17:17:51 +03:00
|
|
|
nego_set_state(nego, NEGO_STATE_TLS);
|
2012-11-01 04:38:48 +04:00
|
|
|
}
|
2015-02-03 01:16:32 +03:00
|
|
|
else if (nego->EnabledProtocols[PROTOCOL_RDP])
|
2012-11-01 04:38:48 +04:00
|
|
|
{
|
2021-08-26 17:17:51 +03:00
|
|
|
nego_set_state(nego, NEGO_STATE_RDP);
|
2012-11-01 04:38:48 +04:00
|
|
|
}
|
2011-07-01 02:48:48 +04:00
|
|
|
else
|
2012-07-25 14:29:49 +04:00
|
|
|
{
|
2015-02-02 19:50:56 +03:00
|
|
|
WLog_ERR(TAG, "No security protocol is enabled");
|
2021-08-26 17:17:51 +03:00
|
|
|
nego_set_state(nego, NEGO_STATE_FAIL);
|
2014-04-10 23:07:53 +04:00
|
|
|
return FALSE;
|
2012-07-25 14:29:49 +04:00
|
|
|
}
|
|
|
|
|
2013-01-10 20:19:57 +04:00
|
|
|
if (!nego->NegotiateSecurityLayer)
|
2012-07-25 14:29:49 +04:00
|
|
|
{
|
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_*) */
|
2023-03-08 04:06:47 +03:00
|
|
|
nego->EnabledProtocols[PROTOCOL_RDSAAD] = FALSE;
|
2018-11-20 19:27:47 +03:00
|
|
|
nego->EnabledProtocols[PROTOCOL_HYBRID] = FALSE;
|
|
|
|
nego->EnabledProtocols[PROTOCOL_SSL] = FALSE;
|
2015-02-03 01:16:32 +03:00
|
|
|
nego->EnabledProtocols[PROTOCOL_RDP] = FALSE;
|
2018-11-20 19:27:47 +03:00
|
|
|
nego->EnabledProtocols[PROTOCOL_HYBRID_EX] = FALSE;
|
2023-03-07 13:58:28 +03:00
|
|
|
nego->EnabledProtocols[PROTOCOL_RDSTLS] = FALSE;
|
2012-11-01 04:38:48 +04:00
|
|
|
|
2021-08-26 17:17:51 +03:00
|
|
|
switch (nego_get_state(nego))
|
2012-08-01 00:58:41 +04:00
|
|
|
{
|
2023-03-08 04:06:47 +03:00
|
|
|
case NEGO_STATE_AAD:
|
|
|
|
nego->EnabledProtocols[PROTOCOL_RDSAAD] = TRUE;
|
|
|
|
nego->SelectedProtocol = PROTOCOL_RDSAAD;
|
|
|
|
break;
|
2023-03-07 13:58:28 +03:00
|
|
|
case NEGO_STATE_RDSTLS:
|
|
|
|
nego->EnabledProtocols[PROTOCOL_RDSTLS] = TRUE;
|
|
|
|
nego->SelectedProtocol = PROTOCOL_RDSTLS;
|
|
|
|
break;
|
2021-08-26 17:17:51 +03:00
|
|
|
case NEGO_STATE_EXT:
|
|
|
|
nego->EnabledProtocols[PROTOCOL_HYBRID_EX] = TRUE;
|
|
|
|
nego->EnabledProtocols[PROTOCOL_HYBRID] = TRUE;
|
|
|
|
nego->SelectedProtocol = PROTOCOL_HYBRID_EX;
|
|
|
|
break;
|
|
|
|
case NEGO_STATE_NLA:
|
|
|
|
nego->EnabledProtocols[PROTOCOL_HYBRID] = TRUE;
|
|
|
|
nego->SelectedProtocol = PROTOCOL_HYBRID;
|
|
|
|
break;
|
|
|
|
case NEGO_STATE_TLS:
|
|
|
|
nego->EnabledProtocols[PROTOCOL_SSL] = TRUE;
|
|
|
|
nego->SelectedProtocol = PROTOCOL_SSL;
|
|
|
|
break;
|
|
|
|
case NEGO_STATE_RDP:
|
|
|
|
nego->EnabledProtocols[PROTOCOL_RDP] = TRUE;
|
|
|
|
nego->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
|
|
|
}
|
2012-07-25 14:29:49 +04:00
|
|
|
}
|
2012-07-25 20:46:43 +04:00
|
|
|
|
2015-02-03 01:16:32 +03:00
|
|
|
if (nego->SendPreconnectionPdu)
|
2012-07-25 20:46:43 +04:00
|
|
|
{
|
2015-02-03 01:16:32 +03:00
|
|
|
if (!nego_send_preconnection_pdu(nego))
|
|
|
|
{
|
|
|
|
WLog_ERR(TAG, "Failed to send preconnection pdu");
|
2021-08-26 17:17:51 +03:00
|
|
|
nego_set_state(nego, NEGO_STATE_FINAL);
|
2015-02-03 01:16:32 +03:00
|
|
|
return FALSE;
|
|
|
|
}
|
2012-07-25 20:46:43 +04:00
|
|
|
}
|
2011-07-01 02:48:48 +04:00
|
|
|
}
|
|
|
|
|
2016-05-11 22:52:36 +03:00
|
|
|
if (!nego->NegotiateSecurityLayer)
|
2011-07-01 02:48:48 +04:00
|
|
|
{
|
2021-08-26 17:17:51 +03:00
|
|
|
nego_set_state(nego, NEGO_STATE_FINAL);
|
2016-05-11 22:52:36 +03:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
do
|
|
|
|
{
|
2021-08-26 17:17:51 +03:00
|
|
|
WLog_DBG(TAG, "state: %s", nego_state_string(nego_get_state(nego)));
|
2016-05-11 22:52:36 +03:00
|
|
|
nego_send(nego);
|
2011-07-03 23:34:15 +04:00
|
|
|
|
2021-08-26 17:17:51 +03:00
|
|
|
if (nego_get_state(nego) == NEGO_STATE_FAIL)
|
2016-05-11 22:52:36 +03:00
|
|
|
{
|
2021-09-06 12:01:36 +03:00
|
|
|
if (freerdp_get_last_error(transport_get_context(nego->transport)) ==
|
|
|
|
FREERDP_ERROR_SUCCESS)
|
2018-04-11 10:00:32 +03:00
|
|
|
WLog_ERR(TAG, "Protocol Security Negotiation Failure");
|
2018-07-10 13:04:27 +03:00
|
|
|
|
2021-08-26 17:17:51 +03:00
|
|
|
nego_set_state(nego, NEGO_STATE_FINAL);
|
2016-05-11 22:52:36 +03:00
|
|
|
return FALSE;
|
|
|
|
}
|
2021-08-26 17:17:51 +03:00
|
|
|
} while (nego_get_state(nego) != NEGO_STATE_FINAL);
|
2011-07-01 02:48:48 +04:00
|
|
|
}
|
|
|
|
|
2019-09-04 16:46:47 +03:00
|
|
|
WLog_DBG(TAG, "Negotiated %s security", protocol_security_string(nego->SelectedProtocol));
|
2011-07-07 21:37:48 +04:00
|
|
|
/* 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;
|
2011-07-07 21:37:48 +04:00
|
|
|
|
2015-02-03 01:16:32 +03:00
|
|
|
if (nego->SelectedProtocol == PROTOCOL_RDP)
|
2011-10-05 02:54:38 +04:00
|
|
|
{
|
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
|
|
|
}
|
2011-10-05 02:54:38 +04:00
|
|
|
}
|
|
|
|
|
2012-07-25 20:50:19 +04:00
|
|
|
/* finally connect security layer (if not already done) */
|
2012-11-15 05:46:51 +04:00
|
|
|
if (!nego_security_connect(nego))
|
2012-07-25 20:50:19 +04:00
|
|
|
{
|
2018-07-10 13:04:27 +03:00
|
|
|
WLog_DBG(TAG, "Failed to connect with %s security",
|
2019-09-04 16:46:47 +03:00
|
|
|
protocol_security_string(nego->SelectedProtocol));
|
2012-10-09 10:31:28 +04:00
|
|
|
return FALSE;
|
2012-07-25 20:50:19 +04:00
|
|
|
}
|
|
|
|
|
2012-10-09 10:31:28 +04:00
|
|
|
return TRUE;
|
2011-07-01 02:48:48 +04:00
|
|
|
}
|
|
|
|
|
2015-01-12 15:44:04 +03:00
|
|
|
BOOL nego_disconnect(rdpNego* nego)
|
|
|
|
{
|
2021-08-26 17:17:51 +03:00
|
|
|
WINPR_ASSERT(nego);
|
|
|
|
nego_set_state(nego, NEGO_STATE_INITIAL);
|
2015-01-12 15:44:04 +03:00
|
|
|
return nego_transport_disconnect(nego);
|
|
|
|
}
|
|
|
|
|
2012-07-25 14:29:49 +04:00
|
|
|
/* connect to selected security layer */
|
2012-10-09 10:38:39 +04:00
|
|
|
BOOL nego_security_connect(rdpNego* nego)
|
2012-07-25 14:29:49 +04:00
|
|
|
{
|
2021-08-26 17:17:51 +03:00
|
|
|
WINPR_ASSERT(nego);
|
2015-02-03 01:16:32 +03:00
|
|
|
if (!nego->TcpConnected)
|
2012-07-25 14:29:49 +04:00
|
|
|
{
|
2015-02-03 01:16:32 +03:00
|
|
|
nego->SecurityConnected = FALSE;
|
2012-07-25 14:29:49 +04:00
|
|
|
}
|
2015-02-03 01:16:32 +03:00
|
|
|
else if (!nego->SecurityConnected)
|
2012-07-25 14:29:49 +04:00
|
|
|
{
|
2023-03-08 04:06:47 +03:00
|
|
|
if (nego->SelectedProtocol == PROTOCOL_RDSAAD)
|
|
|
|
{
|
|
|
|
WLog_DBG(TAG, "nego_security_connect with PROTOCOL_RDSAAD");
|
|
|
|
nego->SecurityConnected = transport_connect_aad(nego->transport);
|
|
|
|
}
|
|
|
|
else if (nego->SelectedProtocol == PROTOCOL_RDSTLS)
|
2023-03-07 13:58:28 +03:00
|
|
|
{
|
|
|
|
WLog_DBG(TAG, "nego_security_connect with PROTOCOL_RDSTLS");
|
|
|
|
nego->SecurityConnected = transport_connect_rdstls(nego->transport);
|
|
|
|
}
|
|
|
|
else if (nego->SelectedProtocol == PROTOCOL_HYBRID)
|
2012-07-27 16:43:57 +04:00
|
|
|
{
|
2018-11-20 19:27:47 +03:00
|
|
|
WLog_DBG(TAG, "nego_security_connect with PROTOCOL_HYBRID");
|
2015-02-03 01:16:32 +03:00
|
|
|
nego->SecurityConnected = transport_connect_nla(nego->transport);
|
2012-07-27 16:43:57 +04:00
|
|
|
}
|
2018-11-20 19:27:47 +03:00
|
|
|
else if (nego->SelectedProtocol == PROTOCOL_SSL)
|
2012-07-27 16:43:57 +04:00
|
|
|
{
|
2018-11-20 19:27:47 +03:00
|
|
|
WLog_DBG(TAG, "nego_security_connect with PROTOCOL_SSL");
|
2015-02-03 01:16:32 +03:00
|
|
|
nego->SecurityConnected = transport_connect_tls(nego->transport);
|
2012-07-27 16:43:57 +04:00
|
|
|
}
|
2015-02-03 01:16:32 +03:00
|
|
|
else if (nego->SelectedProtocol == PROTOCOL_RDP)
|
2012-07-27 16:43:57 +04:00
|
|
|
{
|
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);
|
2012-07-27 16:43:57 +04:00
|
|
|
}
|
2012-08-01 01:07:48 +04:00
|
|
|
else
|
|
|
|
{
|
2019-11-06 17:24:51 +03:00
|
|
|
WLog_ERR(TAG,
|
|
|
|
"cannot connect security layer because no protocol has been selected yet.");
|
2012-08-01 01:07:48 +04:00
|
|
|
}
|
2012-07-25 14:29:49 +04:00
|
|
|
}
|
2012-10-26 02:38:51 +04:00
|
|
|
|
2015-02-03 01:16:32 +03:00
|
|
|
return nego->SecurityConnected;
|
2012-07-25 14:29:49 +04:00
|
|
|
}
|
|
|
|
|
2018-07-10 13:04:27 +03:00
|
|
|
static BOOL nego_tcp_connect(rdpNego* nego)
|
2011-07-01 02:48:48 +04:00
|
|
|
{
|
2021-09-06 12:01:36 +03:00
|
|
|
rdpContext* context;
|
2021-08-26 17:17:51 +03:00
|
|
|
WINPR_ASSERT(nego);
|
2015-02-03 01:16:32 +03:00
|
|
|
if (!nego->TcpConnected)
|
2014-03-24 22:44:18 +04:00
|
|
|
{
|
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
|
|
|
|
2014-04-20 01:31:12 +04:00
|
|
|
if (nego->GatewayEnabled)
|
2014-03-24 22:44:18 +04:00
|
|
|
{
|
2014-04-20 01:31:12 +04:00
|
|
|
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.");
|
2014-12-08 21:00:05 +03:00
|
|
|
WLog_INFO(TAG, "To disable auto detection use /gateway-usage-method:direct");
|
2014-04-20 01:31:12 +04:00
|
|
|
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);
|
2014-04-20 01:31:12 +04:00
|
|
|
}
|
2014-03-24 22:44:18 +04:00
|
|
|
|
2015-02-03 01:16:32 +03:00
|
|
|
if (!nego->TcpConnected)
|
2014-03-24 22:44:18 +04:00
|
|
|
{
|
|
|
|
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);
|
2014-03-24 22:44:18 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-06-02 17:51:49 +03:00
|
|
|
nego->TcpConnected =
|
|
|
|
transport_connect(nego->transport, nego->hostname, nego->port, TcpConnectTimeout);
|
2014-03-24 22:44:18 +04:00
|
|
|
}
|
|
|
|
}
|
2012-09-09 01:49:37 +04:00
|
|
|
|
2015-02-03 01:16:32 +03:00
|
|
|
return nego->TcpConnected;
|
2012-07-25 14:29:49 +04:00
|
|
|
}
|
2011-07-01 02:48:48 +04:00
|
|
|
|
2012-07-25 14:29:49 +04:00
|
|
|
/**
|
|
|
|
* 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
|
2012-07-25 14:29:49 +04:00
|
|
|
*/
|
|
|
|
|
2012-10-09 10:38:39 +04:00
|
|
|
BOOL nego_transport_connect(rdpNego* nego)
|
2012-07-25 14:29:49 +04:00
|
|
|
{
|
2021-08-26 17:17:51 +03:00
|
|
|
WINPR_ASSERT(nego);
|
2018-07-10 13:04:27 +03:00
|
|
|
if (!nego_tcp_connect(nego))
|
|
|
|
return FALSE;
|
2012-07-25 14:29:49 +04:00
|
|
|
|
2015-02-03 01:16:32 +03:00
|
|
|
if (nego->TcpConnected && !nego->NegotiateSecurityLayer)
|
2012-07-25 20:46:43 +04:00
|
|
|
return nego_security_connect(nego);
|
2012-07-25 14:29:49 +04:00
|
|
|
|
2015-02-03 01:16:32 +03:00
|
|
|
return nego->TcpConnected;
|
2011-07-01 02:48:48 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
2011-07-01 02:48:48 +04:00
|
|
|
*/
|
|
|
|
|
2015-01-14 13:35:19 +03:00
|
|
|
BOOL nego_transport_disconnect(rdpNego* nego)
|
2011-07-01 02:48:48 +04:00
|
|
|
{
|
2021-08-26 17:17:51 +03:00
|
|
|
WINPR_ASSERT(nego);
|
2015-02-03 01:16:32 +03:00
|
|
|
if (nego->TcpConnected)
|
2011-07-03 20:42:35 +04:00
|
|
|
transport_disconnect(nego->transport);
|
2011-07-01 02:48:48 +04:00
|
|
|
|
2015-02-03 01:16:32 +03:00
|
|
|
nego->TcpConnected = FALSE;
|
|
|
|
nego->SecurityConnected = FALSE;
|
2015-01-14 13:35:19 +03:00
|
|
|
return TRUE;
|
2011-07-01 02:48:48 +04:00
|
|
|
}
|
|
|
|
|
2012-07-25 20:46:43 +04:00
|
|
|
/**
|
|
|
|
* 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
|
2012-07-25 20:46:43 +04:00
|
|
|
*/
|
|
|
|
|
2012-10-09 10:38:39 +04:00
|
|
|
BOOL nego_send_preconnection_pdu(rdpNego* nego)
|
2012-07-25 20:46:43 +04:00
|
|
|
{
|
2013-03-21 23:19:33 +04:00
|
|
|
wStream* s;
|
2012-10-09 11:26:39 +04:00
|
|
|
UINT32 cbSize;
|
2012-10-09 11:01:37 +04:00
|
|
|
UINT16 cchPCB = 0;
|
2012-09-24 03:49:13 +04:00
|
|
|
WCHAR* wszPCB = NULL;
|
2021-08-26 17:17:51 +03:00
|
|
|
|
|
|
|
WINPR_ASSERT(nego);
|
|
|
|
|
2015-02-02 19:50:56 +03:00
|
|
|
WLog_DBG(TAG, "Sending preconnection PDU");
|
2012-07-29 03:30:21 +04:00
|
|
|
|
|
|
|
if (!nego_tcp_connect(nego))
|
2012-10-09 10:31:28 +04:00
|
|
|
return FALSE;
|
2012-07-25 20:46:43 +04:00
|
|
|
|
|
|
|
/* it's easier to always send the version 2 PDU, and it's just 2 bytes overhead */
|
|
|
|
cbSize = PRECONNECTION_PDU_V2_MIN_SIZE;
|
2012-07-29 03:30:21 +04:00
|
|
|
|
2015-02-03 01:16:32 +03:00
|
|
|
if (nego->PreconnectionBlob)
|
2012-07-29 03:30:21 +04:00
|
|
|
{
|
2022-10-28 09:09:27 +03:00
|
|
|
size_t len = 0;
|
|
|
|
wszPCB = ConvertUtf8ToWCharAlloc(nego->PreconnectionBlob, &len);
|
|
|
|
if (len > UINT16_MAX - 1)
|
|
|
|
{
|
|
|
|
free(wszPCB);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
cchPCB = len;
|
2012-09-24 03:49:13 +04:00
|
|
|
cchPCB += 1; /* zero-termination */
|
2022-10-28 09:09:27 +03:00
|
|
|
cbSize += cchPCB * sizeof(WCHAR);
|
2012-07-25 20:46:43 +04:00
|
|
|
}
|
|
|
|
|
2013-05-15 21:17:29 +04:00
|
|
|
s = Stream_New(NULL, cbSize);
|
2018-07-10 13:04:27 +03:00
|
|
|
|
2015-05-18 12:28:00 +03:00
|
|
|
if (!s)
|
2015-05-29 14:46:50 +03:00
|
|
|
{
|
2018-08-17 17:16:37 +03:00
|
|
|
free(wszPCB);
|
2015-05-29 14:46:50 +03:00
|
|
|
WLog_ERR(TAG, "Stream_New failed!");
|
2015-05-18 12:28:00 +03:00
|
|
|
return FALSE;
|
2015-05-29 14:46:50 +03:00
|
|
|
}
|
2013-05-15 21:17:29 +04:00
|
|
|
|
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 */
|
2012-07-29 03:30:21 +04:00
|
|
|
|
|
|
|
if (wszPCB)
|
2012-07-25 20:46:43 +04:00
|
|
|
{
|
2022-10-28 09:09:27 +03:00
|
|
|
Stream_Write(s, wszPCB, cchPCB * sizeof(WCHAR)); /* wszPCB */
|
2012-10-09 07:21:26 +04:00
|
|
|
free(wszPCB);
|
2012-07-25 20:46:43 +04:00
|
|
|
}
|
|
|
|
|
2013-05-15 20:14:26 +04:00
|
|
|
Stream_SealLength(s);
|
|
|
|
|
2012-07-25 20:46:43 +04:00
|
|
|
if (transport_write(nego->transport, s) < 0)
|
2013-09-06 13:07:33 +04:00
|
|
|
{
|
|
|
|
Stream_Free(s, TRUE);
|
2012-10-09 10:31:28 +04:00
|
|
|
return FALSE;
|
2013-09-06 13:07:33 +04:00
|
|
|
}
|
2012-07-25 20:46:43 +04:00
|
|
|
|
2013-05-15 21:17:29 +04:00
|
|
|
Stream_Free(s, TRUE);
|
2012-10-09 10:31:28 +04:00
|
|
|
return TRUE;
|
2012-07-25 20:46:43 +04:00
|
|
|
}
|
|
|
|
|
2023-03-07 13:58:28 +03:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-08 04:06:47 +03:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-10 13:21:38 +03:00
|
|
|
static void nego_attempt_ext(rdpNego* nego)
|
2012-11-01 04:38:48 +04:00
|
|
|
{
|
2021-08-26 17:17:51 +03:00
|
|
|
WINPR_ASSERT(nego);
|
2018-11-20 19:27:47 +03:00
|
|
|
nego->RequestedProtocols = PROTOCOL_HYBRID | PROTOCOL_SSL | PROTOCOL_HYBRID_EX;
|
2015-02-02 19:50:56 +03:00
|
|
|
WLog_DBG(TAG, "Attempting NLA extended security");
|
2012-11-01 04:38:48 +04:00
|
|
|
|
|
|
|
if (!nego_transport_connect(nego))
|
|
|
|
{
|
2021-08-26 17:17:51 +03:00
|
|
|
nego_set_state(nego, NEGO_STATE_FAIL);
|
2012-11-01 04:38:48 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!nego_send_negotiation_request(nego))
|
|
|
|
{
|
2021-08-26 17:17:51 +03:00
|
|
|
nego_set_state(nego, NEGO_STATE_FAIL);
|
2012-11-01 04:38:48 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!nego_recv_response(nego))
|
|
|
|
{
|
2021-08-26 17:17:51 +03:00
|
|
|
nego_set_state(nego, NEGO_STATE_FAIL);
|
2012-11-01 04:38:48 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-08-26 17:17:51 +03:00
|
|
|
WLog_DBG(TAG, "state: %s", nego_state_string(nego_get_state(nego)));
|
2012-11-01 04:38:48 +04:00
|
|
|
|
2021-08-26 17:17:51 +03:00
|
|
|
if (nego_get_state(nego) != NEGO_STATE_FINAL)
|
2012-11-01 04:38:48 +04:00
|
|
|
{
|
|
|
|
nego_transport_disconnect(nego);
|
|
|
|
|
2018-11-20 19:27:47 +03:00
|
|
|
if (nego->EnabledProtocols[PROTOCOL_HYBRID])
|
2021-08-26 17:17:51 +03:00
|
|
|
nego_set_state(nego, NEGO_STATE_NLA);
|
2018-11-20 19:27:47 +03:00
|
|
|
else if (nego->EnabledProtocols[PROTOCOL_SSL])
|
2021-08-26 17:17:51 +03:00
|
|
|
nego_set_state(nego, NEGO_STATE_TLS);
|
2015-02-03 01:16:32 +03:00
|
|
|
else if (nego->EnabledProtocols[PROTOCOL_RDP])
|
2021-08-26 17:17:51 +03:00
|
|
|
nego_set_state(nego, NEGO_STATE_RDP);
|
2012-11-01 04:38:48 +04:00
|
|
|
else
|
2021-08-26 17:17:51 +03:00
|
|
|
nego_set_state(nego, NEGO_STATE_FAIL);
|
2012-11-01 04:38:48 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-10 13:21:38 +03:00
|
|
|
static void nego_attempt_nla(rdpNego* nego)
|
2011-07-01 02:48:48 +04:00
|
|
|
{
|
2021-08-26 17:17:51 +03:00
|
|
|
WINPR_ASSERT(nego);
|
2018-11-20 19:27:47 +03:00
|
|
|
nego->RequestedProtocols = PROTOCOL_HYBRID | PROTOCOL_SSL;
|
2015-02-02 19:50:56 +03:00
|
|
|
WLog_DBG(TAG, "Attempting NLA security");
|
2011-07-03 20:42:35 +04:00
|
|
|
|
2012-07-25 14:29:49 +04:00
|
|
|
if (!nego_transport_connect(nego))
|
2011-08-13 08:40:14 +04:00
|
|
|
{
|
2021-08-26 17:17:51 +03:00
|
|
|
nego_set_state(nego, NEGO_STATE_FAIL);
|
2011-08-13 08:40:14 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-09-16 19:16:16 +04:00
|
|
|
if (!nego_send_negotiation_request(nego))
|
|
|
|
{
|
2021-08-26 17:17:51 +03:00
|
|
|
nego_set_state(nego, NEGO_STATE_FAIL);
|
2011-09-16 19:16:16 +04:00
|
|
|
return;
|
|
|
|
}
|
2011-07-03 20:42:35 +04:00
|
|
|
|
2011-09-16 19:16:16 +04:00
|
|
|
if (!nego_recv_response(nego))
|
|
|
|
{
|
2021-08-26 17:17:51 +03:00
|
|
|
nego_set_state(nego, NEGO_STATE_FAIL);
|
2011-09-16 19:16:16 +04:00
|
|
|
return;
|
|
|
|
}
|
2011-07-01 02:48:48 +04:00
|
|
|
|
2021-08-26 17:17:51 +03:00
|
|
|
WLog_DBG(TAG, "state: %s", nego_state_string(nego_get_state(nego)));
|
2012-11-01 04:38:48 +04:00
|
|
|
|
2021-08-26 17:17:51 +03:00
|
|
|
if (nego_get_state(nego) != NEGO_STATE_FINAL)
|
2011-07-01 02:48:48 +04:00
|
|
|
{
|
2012-07-25 14:29:49 +04:00
|
|
|
nego_transport_disconnect(nego);
|
2011-07-01 02:48:48 +04:00
|
|
|
|
2018-11-20 19:27:47 +03:00
|
|
|
if (nego->EnabledProtocols[PROTOCOL_SSL])
|
2021-08-26 17:17:51 +03:00
|
|
|
nego_set_state(nego, NEGO_STATE_TLS);
|
2015-02-03 01:16:32 +03:00
|
|
|
else if (nego->EnabledProtocols[PROTOCOL_RDP])
|
2021-08-26 17:17:51 +03:00
|
|
|
nego_set_state(nego, NEGO_STATE_RDP);
|
2011-07-01 02:48:48 +04:00
|
|
|
else
|
2021-08-26 17:17:51 +03:00
|
|
|
nego_set_state(nego, NEGO_STATE_FAIL);
|
2011-07-01 02:48:48 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-10 13:21:38 +03:00
|
|
|
static void nego_attempt_tls(rdpNego* nego)
|
2011-07-01 02:48:48 +04:00
|
|
|
{
|
2021-08-26 17:17:51 +03:00
|
|
|
WINPR_ASSERT(nego);
|
2018-11-20 19:27:47 +03:00
|
|
|
nego->RequestedProtocols = PROTOCOL_SSL;
|
2015-02-02 19:50:56 +03:00
|
|
|
WLog_DBG(TAG, "Attempting TLS security");
|
2011-07-03 20:42:35 +04:00
|
|
|
|
2012-07-25 14:29:49 +04:00
|
|
|
if (!nego_transport_connect(nego))
|
2011-08-13 08:40:14 +04:00
|
|
|
{
|
2021-08-26 17:17:51 +03:00
|
|
|
nego_set_state(nego, NEGO_STATE_FAIL);
|
2011-08-13 08:40:14 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-09-16 19:16:16 +04:00
|
|
|
if (!nego_send_negotiation_request(nego))
|
|
|
|
{
|
2021-08-26 17:17:51 +03:00
|
|
|
nego_set_state(nego, NEGO_STATE_FAIL);
|
2011-09-16 19:16:16 +04:00
|
|
|
return;
|
|
|
|
}
|
2011-07-03 20:42:35 +04:00
|
|
|
|
2011-09-16 19:16:16 +04:00
|
|
|
if (!nego_recv_response(nego))
|
|
|
|
{
|
2021-08-26 17:17:51 +03:00
|
|
|
nego_set_state(nego, NEGO_STATE_FAIL);
|
2011-09-16 19:16:16 +04:00
|
|
|
return;
|
|
|
|
}
|
2011-07-01 02:48:48 +04:00
|
|
|
|
2021-08-26 17:17:51 +03:00
|
|
|
if (nego_get_state(nego) != NEGO_STATE_FINAL)
|
2011-07-01 02:48:48 +04:00
|
|
|
{
|
2012-07-25 14:29:49 +04:00
|
|
|
nego_transport_disconnect(nego);
|
2011-07-01 02:48:48 +04:00
|
|
|
|
2015-02-03 01:16:32 +03:00
|
|
|
if (nego->EnabledProtocols[PROTOCOL_RDP])
|
2021-08-26 17:17:51 +03:00
|
|
|
nego_set_state(nego, NEGO_STATE_RDP);
|
2011-07-01 02:48:48 +04:00
|
|
|
else
|
2021-08-26 17:17:51 +03:00
|
|
|
nego_set_state(nego, NEGO_STATE_FAIL);
|
2011-07-01 02:48:48 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-10 13:21:38 +03:00
|
|
|
static void nego_attempt_rdp(rdpNego* nego)
|
2011-07-01 02:48:48 +04:00
|
|
|
{
|
2021-08-26 17:17:51 +03:00
|
|
|
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");
|
2011-07-03 20:42:35 +04:00
|
|
|
|
2012-07-25 14:29:49 +04:00
|
|
|
if (!nego_transport_connect(nego))
|
2011-08-13 08:40:14 +04:00
|
|
|
{
|
2021-08-26 17:17:51 +03:00
|
|
|
nego_set_state(nego, NEGO_STATE_FAIL);
|
2011-08-13 08:40:14 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-09-16 19:16:16 +04:00
|
|
|
if (!nego_send_negotiation_request(nego))
|
|
|
|
{
|
2021-08-26 17:17:51 +03:00
|
|
|
nego_set_state(nego, NEGO_STATE_FAIL);
|
2011-09-16 19:16:16 +04:00
|
|
|
return;
|
|
|
|
}
|
2011-07-01 02:48:48 +04:00
|
|
|
|
2011-09-16 19:16:16 +04:00
|
|
|
if (!nego_recv_response(nego))
|
|
|
|
{
|
2021-08-26 17:17:51 +03:00
|
|
|
nego_set_state(nego, NEGO_STATE_FAIL);
|
2011-09-16 19:16:16 +04:00
|
|
|
return;
|
|
|
|
}
|
2011-07-07 21:37:48 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
2011-07-07 21:37:48 +04:00
|
|
|
*/
|
|
|
|
|
2012-10-09 10:38:39 +04:00
|
|
|
BOOL nego_recv_response(rdpNego* nego)
|
2011-07-07 21:37:48 +04:00
|
|
|
{
|
2013-05-15 23:54:33 +04:00
|
|
|
int status;
|
|
|
|
wStream* s;
|
2021-08-26 17:17:51 +03:00
|
|
|
|
|
|
|
WINPR_ASSERT(nego);
|
2013-05-15 23:54:33 +04:00
|
|
|
s = Stream_New(NULL, 1024);
|
2014-09-25 01:23:12 +04:00
|
|
|
|
2014-03-26 02:13:08 +04:00
|
|
|
if (!s)
|
2015-05-29 14:46:50 +03:00
|
|
|
{
|
|
|
|
WLog_ERR(TAG, "Stream_New failed!");
|
2014-03-26 02:13:08 +04:00
|
|
|
return FALSE;
|
2015-05-29 14:46:50 +03:00
|
|
|
}
|
2013-05-15 23:54:33 +04:00
|
|
|
|
2014-07-22 13:19:38 +04:00
|
|
|
status = transport_read_pdu(nego->transport, s);
|
2014-09-25 01:23:12 +04:00
|
|
|
|
2013-05-15 23:54:33 +04:00
|
|
|
if (status < 0)
|
|
|
|
{
|
|
|
|
Stream_Free(s, TRUE);
|
2013-01-14 02:37:50 +04:00
|
|
|
return FALSE;
|
2013-05-15 23:54:33 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
2013-05-15 23:54:33 +04:00
|
|
|
if (status < 0)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
return TRUE;
|
2011-07-01 02:48:48 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
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
|
2011-07-05 05:35:32 +04:00
|
|
|
* @param extra nego pointer
|
2022-12-09 16:35:03 +03:00
|
|
|
*
|
|
|
|
* @return \b 0 for success, \b -1 for failure
|
2011-07-01 02:48:48 +04:00
|
|
|
*/
|
|
|
|
|
2013-03-21 23:19:33 +04:00
|
|
|
int nego_recv(rdpTransport* transport, wStream* s, void* extra)
|
2011-07-01 02:48:48 +04:00
|
|
|
{
|
2012-10-09 11:01:37 +04:00
|
|
|
BYTE li;
|
|
|
|
BYTE type;
|
2012-12-12 02:24:52 +04:00
|
|
|
UINT16 length;
|
2019-11-06 17:24:51 +03:00
|
|
|
rdpNego* nego = (rdpNego*)extra;
|
2011-07-01 02:48:48 +04:00
|
|
|
|
2021-08-26 17:17:51 +03:00
|
|
|
WINPR_ASSERT(nego);
|
2017-05-29 11:50:22 +03:00
|
|
|
if (!tpkt_read_header(s, &length))
|
|
|
|
return -1;
|
2012-12-12 02:24:52 +04:00
|
|
|
|
2020-02-20 10:22:19 +03:00
|
|
|
if (!tpdu_read_connection_confirm(s, &li, length))
|
2013-01-12 17:49:01 +04:00
|
|
|
return -1;
|
2011-07-03 21:49:06 +04:00
|
|
|
|
|
|
|
if (li > 6)
|
2011-07-01 02:48:48 +04:00
|
|
|
{
|
2011-07-03 21:49:06 +04:00
|
|
|
/* rdpNegData (optional) */
|
2013-05-09 00:09:16 +04:00
|
|
|
Stream_Read_UINT8(s, type); /* Type */
|
2011-07-03 21:49:06 +04:00
|
|
|
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case TYPE_RDP_NEG_RSP:
|
2020-04-15 18:21:46 +03:00
|
|
|
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 ? */
|
2012-06-20 04:10:49 +04:00
|
|
|
|
2015-02-03 01:16:32 +03:00
|
|
|
if (nego->SelectedProtocol)
|
2012-06-20 04:10:49 +04:00
|
|
|
{
|
2023-03-08 04:06:47 +03:00
|
|
|
if ((nego->SelectedProtocol == PROTOCOL_RDSAAD) &&
|
|
|
|
(!nego->EnabledProtocols[PROTOCOL_RDSAAD]))
|
|
|
|
{
|
|
|
|
nego_set_state(nego, NEGO_STATE_FAIL);
|
|
|
|
}
|
2018-11-20 19:27:47 +03:00
|
|
|
if ((nego->SelectedProtocol == PROTOCOL_HYBRID) &&
|
|
|
|
(!nego->EnabledProtocols[PROTOCOL_HYBRID]))
|
2012-06-20 04:10:49 +04:00
|
|
|
{
|
2021-08-26 17:17:51 +03:00
|
|
|
nego_set_state(nego, NEGO_STATE_FAIL);
|
2012-06-20 04:10:49 +04:00
|
|
|
}
|
2018-07-10 13:04:27 +03:00
|
|
|
|
2018-11-20 19:27:47 +03:00
|
|
|
if ((nego->SelectedProtocol == PROTOCOL_SSL) &&
|
|
|
|
(!nego->EnabledProtocols[PROTOCOL_SSL]))
|
2012-06-20 04:10:49 +04:00
|
|
|
{
|
2021-08-26 17:17:51 +03:00
|
|
|
nego_set_state(nego, NEGO_STATE_FAIL);
|
2012-06-20 04:10:49 +04:00
|
|
|
}
|
|
|
|
}
|
2015-02-03 01:16:32 +03:00
|
|
|
else if (!nego->EnabledProtocols[PROTOCOL_RDP])
|
2012-06-20 04:10:49 +04:00
|
|
|
{
|
2021-08-26 17:17:51 +03:00
|
|
|
nego_set_state(nego, NEGO_STATE_FAIL);
|
2012-06-20 04:10:49 +04:00
|
|
|
}
|
2018-07-10 13:04:27 +03:00
|
|
|
|
2011-07-03 21:49:06 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case TYPE_RDP_NEG_FAILURE:
|
2020-04-15 18:21:46 +03:00
|
|
|
if (!nego_process_negotiation_failure(nego, s))
|
|
|
|
return -1;
|
2011-07-03 21:49:06 +04:00
|
|
|
break;
|
|
|
|
}
|
2011-07-01 02:48:48 +04:00
|
|
|
}
|
2012-12-12 02:24:52 +04:00
|
|
|
else if (li == 6)
|
2011-07-07 21:37:48 +04:00
|
|
|
{
|
2015-02-02 19:50:56 +03:00
|
|
|
WLog_DBG(TAG, "no rdpNegData");
|
2012-06-20 04:10:49 +04:00
|
|
|
|
2015-02-03 01:16:32 +03:00
|
|
|
if (!nego->EnabledProtocols[PROTOCOL_RDP])
|
2021-08-26 17:17:51 +03:00
|
|
|
nego_set_state(nego, NEGO_STATE_FAIL);
|
2012-03-29 22:06:17 +04:00
|
|
|
else
|
2021-08-26 17:17:51 +03:00
|
|
|
nego_set_state(nego, NEGO_STATE_FINAL);
|
2011-07-07 21:37:48 +04:00
|
|
|
}
|
2012-12-12 02:24:52 +04:00
|
|
|
else
|
|
|
|
{
|
2015-02-02 19:50:56 +03:00
|
|
|
WLog_ERR(TAG, "invalid negotiation response");
|
2021-08-26 17:17:51 +03:00
|
|
|
nego_set_state(nego, NEGO_STATE_FAIL);
|
2012-12-12 02:24:52 +04:00
|
|
|
}
|
2011-07-03 21:49:06 +04:00
|
|
|
|
2020-02-20 10:22:19 +03:00
|
|
|
if (!tpkt_ensure_stream_consumed(s, length))
|
|
|
|
return -1;
|
2013-01-07 02:24:08 +04:00
|
|
|
return 0;
|
2011-07-01 02:48:48 +04:00
|
|
|
}
|
|
|
|
|
2015-03-30 14:07:03 +03:00
|
|
|
/**
|
|
|
|
* Read optional routing token or cookie of X.224 Connection Request PDU.
|
2022-12-09 16:35:03 +03:00
|
|
|
* msdn{cc240470}
|
2015-03-30 14:07:03 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
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]
|
|
|
|
*/
|
2018-07-10 13:04:27 +03:00
|
|
|
BYTE* str = NULL;
|
2015-03-30 14:07:03 +03:00
|
|
|
UINT16 crlf = 0;
|
2017-12-11 12:25:21 +03:00
|
|
|
size_t pos, len;
|
2015-03-30 14:07:03 +03:00
|
|
|
BOOL result = FALSE;
|
|
|
|
BOOL isToken = FALSE;
|
2019-10-29 12:18:09 +03:00
|
|
|
size_t remain = Stream_GetRemainingLength(s);
|
2021-08-26 17:17:51 +03:00
|
|
|
|
|
|
|
WINPR_ASSERT(nego);
|
|
|
|
|
2015-03-30 14:07:03 +03:00
|
|
|
str = Stream_Pointer(s);
|
|
|
|
pos = Stream_GetPosition(s);
|
|
|
|
|
2018-04-24 15:34:45 +03:00
|
|
|
/* minimum length for token is 15 */
|
2019-10-29 12:18:09 +03:00
|
|
|
if (remain < 15)
|
2015-03-30 14:07:03 +03:00
|
|
|
return TRUE;
|
|
|
|
|
2018-04-24 16:00:00 +03:00
|
|
|
if (memcmp(Stream_Pointer(s), "Cookie: mstshash=", 17) != 0)
|
2015-03-30 14:07:03 +03:00
|
|
|
{
|
2021-10-16 19:22:58 +03:00
|
|
|
if (memcmp(Stream_Pointer(s), "Cookie: msts=", 13) != 0)
|
|
|
|
{
|
|
|
|
/* remaining bytes are neither a token nor a cookie */
|
|
|
|
return TRUE;
|
|
|
|
}
|
2015-03-30 14:07:03 +03:00
|
|
|
isToken = TRUE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-04-24 15:34:45 +03:00
|
|
|
/* not a token, minimum length for cookie is 19 */
|
2019-10-29 12:18:09 +03:00
|
|
|
if (remain < 19)
|
2015-03-30 14:07:03 +03:00
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
Stream_Seek(s, 17);
|
|
|
|
}
|
|
|
|
|
2022-04-28 10:56:11 +03:00
|
|
|
while (Stream_GetRemainingLength(s) >= 2)
|
2015-03-30 14:07:03 +03:00
|
|
|
{
|
|
|
|
Stream_Read_UINT16(s, crlf);
|
2018-07-10 13:04:27 +03:00
|
|
|
|
2015-03-30 14:07:03 +03:00
|
|
|
if (crlf == 0x0A0D)
|
|
|
|
break;
|
2018-07-10 13:04:27 +03:00
|
|
|
|
2015-03-30 14:07:03 +03:00
|
|
|
Stream_Rewind(s, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (crlf == 0x0A0D)
|
|
|
|
{
|
|
|
|
Stream_Rewind(s, 2);
|
|
|
|
len = Stream_GetPosition(s) - pos;
|
|
|
|
Stream_Write_UINT16(s, 0);
|
2018-07-10 13:04:27 +03:00
|
|
|
|
2019-11-05 16:55:33 +03:00
|
|
|
if (strnlen((char*)str, len) == len)
|
2015-03-30 14:07:03 +03:00
|
|
|
{
|
|
|
|
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");
|
2015-03-30 14:07:03 +03:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-11-06 17:24:51 +03:00
|
|
|
WLog_DBG(TAG, "received %s [%s]", isToken ? "routing token" : "cookie", str);
|
2015-03-30 14:07:03 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2011-08-18 19:15:28 +04:00
|
|
|
/**
|
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
|
2011-08-18 19:15:28 +04:00
|
|
|
*/
|
|
|
|
|
2013-03-21 23:19:33 +04:00
|
|
|
BOOL nego_read_request(rdpNego* nego, wStream* s)
|
2011-08-18 19:15:28 +04:00
|
|
|
{
|
2012-10-09 11:01:37 +04:00
|
|
|
BYTE li;
|
|
|
|
BYTE type;
|
2017-05-29 11:50:22 +03:00
|
|
|
UINT16 length;
|
2011-08-18 19:15:28 +04:00
|
|
|
|
2021-08-26 17:17:51 +03:00
|
|
|
WINPR_ASSERT(nego);
|
|
|
|
WINPR_ASSERT(s);
|
|
|
|
|
2017-05-29 11:50:22 +03:00
|
|
|
if (!tpkt_read_header(s, &length))
|
|
|
|
return FALSE;
|
2013-04-13 02:03:56 +04:00
|
|
|
|
2020-02-20 10:22:19 +03:00
|
|
|
if (!tpdu_read_connection_request(s, &li, length))
|
2013-01-12 17:49:01 +04:00
|
|
|
return FALSE;
|
2012-02-12 21:46:53 +04:00
|
|
|
|
2013-04-30 06:35:15 +04:00
|
|
|
if (li != Stream_GetRemainingLength(s) + 6)
|
2011-08-18 19:15:28 +04:00
|
|
|
{
|
2015-02-03 01:16:32 +03:00
|
|
|
WLog_ERR(TAG, "Incorrect TPDU length indicator.");
|
2012-10-09 10:31:28 +04:00
|
|
|
return FALSE;
|
2011-08-18 19:15:28 +04:00
|
|
|
}
|
|
|
|
|
2015-03-30 14:07:03 +03:00
|
|
|
if (!nego_read_request_token_or_cookie(nego, s))
|
2011-08-18 19:15:28 +04:00
|
|
|
{
|
2015-03-30 14:07:03 +03:00
|
|
|
WLog_ERR(TAG, "Failed to parse routing token or cookie.");
|
|
|
|
return FALSE;
|
2011-08-18 19:15:28 +04:00
|
|
|
}
|
|
|
|
|
2013-04-30 06:35:15 +04:00
|
|
|
if (Stream_GetRemainingLength(s) >= 8)
|
2011-08-18 19:15:28 +04:00
|
|
|
{
|
|
|
|
/* rdpNegData (optional) */
|
2013-05-09 00:09:16 +04:00
|
|
|
Stream_Read_UINT8(s, type); /* Type */
|
2012-06-20 04:10:49 +04:00
|
|
|
|
2011-08-18 19:15:28 +04:00
|
|
|
if (type != TYPE_RDP_NEG_REQ)
|
|
|
|
{
|
2019-11-06 17:24:51 +03:00
|
|
|
WLog_ERR(TAG, "Incorrect negotiation request type %" PRIu8 "", type);
|
2012-10-09 10:31:28 +04:00
|
|
|
return FALSE;
|
2011-08-18 19:15:28 +04:00
|
|
|
}
|
|
|
|
|
2020-04-15 18:21:46 +03:00
|
|
|
if (!nego_process_negotiation_request(nego, s))
|
|
|
|
return FALSE;
|
2011-08-18 19:15:28 +04:00
|
|
|
}
|
|
|
|
|
2020-02-20 10:22:19 +03:00
|
|
|
return tpkt_ensure_stream_consumed(s, length);
|
2011-08-18 19:15:28 +04:00
|
|
|
}
|
|
|
|
|
2011-07-01 02:48:48 +04:00
|
|
|
/**
|
|
|
|
* Send protocol security negotiation message.
|
2022-12-09 16:35:03 +03:00
|
|
|
*
|
|
|
|
* @param nego A pointer to the NEGO struct
|
2011-07-01 02:48:48 +04:00
|
|
|
*/
|
|
|
|
|
2011-07-07 21:37:48 +04:00
|
|
|
void nego_send(rdpNego* nego)
|
2011-07-01 02:48:48 +04:00
|
|
|
{
|
2021-08-26 17:17:51 +03:00
|
|
|
WINPR_ASSERT(nego);
|
|
|
|
|
|
|
|
switch (nego_get_state(nego))
|
|
|
|
{
|
2023-03-08 04:06:47 +03:00
|
|
|
case NEGO_STATE_AAD:
|
|
|
|
nego_attempt_rdsaad(nego);
|
|
|
|
break;
|
2023-03-07 13:58:28 +03:00
|
|
|
case NEGO_STATE_RDSTLS:
|
|
|
|
nego_attempt_rdstls(nego);
|
|
|
|
break;
|
2021-08-26 17:17:51 +03:00
|
|
|
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;
|
|
|
|
}
|
2011-07-01 02:48:48 +04:00
|
|
|
}
|
|
|
|
|
2011-07-05 05:35:32 +04:00
|
|
|
/**
|
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
|
2011-07-05 05:35:32 +04:00
|
|
|
*/
|
|
|
|
|
2012-10-09 10:38:39 +04:00
|
|
|
BOOL nego_send_negotiation_request(rdpNego* nego)
|
2011-07-03 20:42:35 +04:00
|
|
|
{
|
2018-11-20 19:03:50 +03:00
|
|
|
BOOL rc = FALSE;
|
2013-03-21 23:19:33 +04:00
|
|
|
wStream* s;
|
2018-11-20 19:03:50 +03:00
|
|
|
size_t length;
|
2017-12-11 12:25:21 +03:00
|
|
|
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;
|
2013-05-15 21:17:29 +04:00
|
|
|
s = Stream_New(NULL, 512);
|
2018-07-10 13:04:27 +03:00
|
|
|
|
2021-08-26 17:17:51 +03:00
|
|
|
WINPR_ASSERT(nego);
|
2015-05-29 14:46:50 +03:00
|
|
|
if (!s)
|
|
|
|
{
|
|
|
|
WLog_ERR(TAG, "Stream_New failed!");
|
|
|
|
return FALSE;
|
|
|
|
}
|
2013-05-15 21:17:29 +04:00
|
|
|
|
2011-07-12 02:46:36 +04:00
|
|
|
length = TPDU_CONNECTION_REQUEST_LENGTH;
|
2013-05-15 22:42:37 +04:00
|
|
|
bm = Stream_GetPosition(s);
|
2013-04-30 06:35:15 +04:00
|
|
|
Stream_Seek(s, length);
|
2011-07-03 20:42:35 +04:00
|
|
|
|
2013-04-11 19:51:10 +04:00
|
|
|
if (nego->RoutingToken)
|
2011-09-04 02:21:21 +04:00
|
|
|
{
|
2013-05-09 00:09:16 +04:00
|
|
|
Stream_Write(s, nego->RoutingToken, nego->RoutingTokenLength);
|
2015-02-03 01:16:32 +03:00
|
|
|
|
2014-05-16 19:19:22 +04: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) &&
|
2018-07-10 13:04:27 +03:00
|
|
|
(nego->RoutingToken[nego->RoutingTokenLength - 2] == 0x0D) &&
|
|
|
|
(nego->RoutingToken[nego->RoutingTokenLength - 1] == 0x0A))
|
2014-05-16 19:19:22 +04:00
|
|
|
{
|
2015-02-02 19:50:56 +03:00
|
|
|
WLog_DBG(TAG, "Routing token looks correctly terminated - use verbatim");
|
2017-12-11 12:25:21 +03:00
|
|
|
length += nego->RoutingTokenLength;
|
2014-05-16 19:19:22 +04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-02-02 19:50:56 +03:00
|
|
|
WLog_DBG(TAG, "Adding terminating CRLF to routing token");
|
2014-05-16 19:19:22 +04:00
|
|
|
Stream_Write_UINT8(s, 0x0D); /* CR */
|
|
|
|
Stream_Write_UINT8(s, 0x0A); /* LF */
|
|
|
|
length += nego->RoutingTokenLength + 2;
|
|
|
|
}
|
2011-09-04 02:21:21 +04:00
|
|
|
}
|
2013-04-11 19:51:10 +04:00
|
|
|
else if (nego->cookie)
|
2011-07-03 20:42:35 +04:00
|
|
|
{
|
2012-10-26 02:38:51 +04:00
|
|
|
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;
|
2012-10-26 02:38:51 +04:00
|
|
|
|
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 */
|
2011-07-03 20:42:35 +04:00
|
|
|
length += cookie_length + 19;
|
|
|
|
}
|
|
|
|
|
2019-11-06 17:24:51 +03:00
|
|
|
WLog_DBG(TAG, "RequestedProtocols: %" PRIu32 "", nego->RequestedProtocols);
|
2012-09-09 01:49:37 +04:00
|
|
|
|
2015-02-03 01:16:32 +03:00
|
|
|
if ((nego->RequestedProtocols > PROTOCOL_RDP) || (nego->sendNegoData))
|
2011-07-03 21:49:06 +04:00
|
|
|
{
|
|
|
|
/* 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 */
|
2011-07-03 21:49:06 +04:00
|
|
|
length += 8;
|
|
|
|
}
|
|
|
|
|
2019-10-16 19:49:24 +03:00
|
|
|
if (length > UINT16_MAX)
|
2018-11-20 19:03:50 +03:00
|
|
|
goto fail;
|
|
|
|
|
2013-05-15 22:42:37 +04:00
|
|
|
em = Stream_GetPosition(s);
|
|
|
|
Stream_SetPosition(s, bm);
|
2022-10-21 10:19:29 +03:00
|
|
|
if (!tpkt_write_header(s, (UINT16)length))
|
|
|
|
goto fail;
|
|
|
|
if (!tpdu_write_connection_request(s, (UINT16)length - 5))
|
|
|
|
goto fail;
|
2013-05-15 22:42:37 +04:00
|
|
|
Stream_SetPosition(s, em);
|
2013-05-15 20:14:26 +04:00
|
|
|
Stream_SealLength(s);
|
2018-11-20 19:03:50 +03:00
|
|
|
rc = (transport_write(nego->transport, s) >= 0);
|
|
|
|
fail:
|
2013-05-15 21:17:29 +04:00
|
|
|
Stream_Free(s, TRUE);
|
2018-11-20 19:03:50 +03:00
|
|
|
return rc;
|
2011-07-03 20:42:35 +04:00
|
|
|
}
|
|
|
|
|
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 };
|
|
|
|
|
2022-04-19 15:29:17 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-04-15 18:21:46 +03:00
|
|
|
BOOL nego_process_negotiation_request(rdpNego* nego, wStream* s)
|
2011-08-18 19:15:28 +04:00
|
|
|
{
|
2012-10-09 11:01:37 +04:00
|
|
|
BYTE flags;
|
|
|
|
UINT16 length;
|
2020-04-15 18:21:46 +03:00
|
|
|
|
2021-08-26 17:17:51 +03:00
|
|
|
WINPR_ASSERT(nego);
|
|
|
|
WINPR_ASSERT(s);
|
|
|
|
|
2022-04-19 15:29:17 +03:00
|
|
|
if (!Stream_CheckAndLogRequiredLength(TAG, s, 7))
|
2020-04-15 18:21:46 +03:00
|
|
|
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);
|
2021-08-26 17:17:51 +03:00
|
|
|
nego_set_state(nego, NEGO_STATE_FINAL);
|
2020-04-15 18:21:46 +03:00
|
|
|
return TRUE;
|
2011-08-18 19:15:28 +04:00
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
}
|
2011-07-01 02:48:48 +04:00
|
|
|
|
2020-04-15 18:21:46 +03:00
|
|
|
BOOL nego_process_negotiation_response(rdpNego* nego, wStream* s)
|
2011-07-01 02:48:48 +04:00
|
|
|
{
|
2012-10-09 11:01:37 +04:00
|
|
|
UINT16 length;
|
2011-07-03 21:49:06 +04:00
|
|
|
|
2021-08-26 17:17:51 +03:00
|
|
|
WINPR_ASSERT(nego);
|
|
|
|
WINPR_ASSERT(s);
|
|
|
|
|
2022-04-19 15:29:17 +03:00
|
|
|
if (!Stream_CheckAndLogRequiredLength(TAG, s, 7))
|
2013-01-12 17:49:01 +04:00
|
|
|
{
|
2021-08-26 17:17:51 +03:00
|
|
|
nego_set_state(nego, NEGO_STATE_FAIL);
|
2020-04-15 18:21:46 +03:00
|
|
|
return FALSE;
|
2013-01-12 17:49:01 +04:00
|
|
|
}
|
|
|
|
|
2013-05-09 00:09:16 +04:00
|
|
|
Stream_Read_UINT8(s, nego->flags);
|
2022-03-03 12:48:15 +03:00
|
|
|
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;
|
|
|
|
}
|
2015-02-03 01:16:32 +03:00
|
|
|
Stream_Read_UINT32(s, nego->SelectedProtocol);
|
2021-08-26 17:17:51 +03:00
|
|
|
nego_set_state(nego, NEGO_STATE_FINAL);
|
2020-04-15 18:21:46 +03:00
|
|
|
return TRUE;
|
2011-07-01 02:48:48 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
2011-07-01 02:48:48 +04:00
|
|
|
*/
|
|
|
|
|
2020-04-15 18:21:46 +03:00
|
|
|
BOOL nego_process_negotiation_failure(rdpNego* nego, wStream* s)
|
2011-07-01 02:48:48 +04:00
|
|
|
{
|
2012-10-09 11:01:37 +04:00
|
|
|
BYTE flags;
|
|
|
|
UINT16 length;
|
2012-10-09 11:26:39 +04:00
|
|
|
UINT32 failureCode;
|
2021-08-26 17:17:51 +03:00
|
|
|
|
|
|
|
WINPR_ASSERT(nego);
|
|
|
|
WINPR_ASSERT(s);
|
|
|
|
|
2015-02-02 19:50:56 +03:00
|
|
|
WLog_DBG(TAG, "RDP_NEG_FAILURE");
|
2022-04-19 15:29:17 +03:00
|
|
|
if (!Stream_CheckAndLogRequiredLength(TAG, s, 7))
|
2020-04-15 18:21:46 +03:00
|
|
|
return FALSE;
|
2022-04-19 15:29:17 +03:00
|
|
|
|
2013-05-09 00:09:16 +04:00
|
|
|
Stream_Read_UINT8(s, flags);
|
2021-11-30 12:01:22 +03:00
|
|
|
if (flags != 0)
|
|
|
|
{
|
2022-11-21 12:40:15 +03:00
|
|
|
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);
|
2011-07-01 02:48:48 +04:00
|
|
|
|
|
|
|
switch (failureCode)
|
|
|
|
{
|
|
|
|
case SSL_REQUIRED_BY_SERVER:
|
2015-02-18 23:36:57 +03:00
|
|
|
WLog_WARN(TAG, "Error: SSL_REQUIRED_BY_SERVER");
|
2011-07-01 02:48:48 +04:00
|
|
|
break;
|
2013-04-13 02:03:56 +04:00
|
|
|
|
2011-07-01 02:48:48 +04:00
|
|
|
case SSL_NOT_ALLOWED_BY_SERVER:
|
2015-02-18 23:36:57 +03:00
|
|
|
WLog_WARN(TAG, "Error: SSL_NOT_ALLOWED_BY_SERVER");
|
2013-09-19 03:16:48 +04:00
|
|
|
nego->sendNegoData = TRUE;
|
2011-07-01 02:48:48 +04:00
|
|
|
break;
|
2013-04-13 02:03:56 +04:00
|
|
|
|
2011-07-01 02:48:48 +04:00
|
|
|
case SSL_CERT_NOT_ON_SERVER:
|
2015-02-02 19:50:56 +03:00
|
|
|
WLog_ERR(TAG, "Error: SSL_CERT_NOT_ON_SERVER");
|
2013-04-13 02:03:56 +04:00
|
|
|
nego->sendNegoData = TRUE;
|
2011-07-01 02:48:48 +04:00
|
|
|
break;
|
2013-04-13 02:03:56 +04:00
|
|
|
|
2011-07-01 02:48:48 +04:00
|
|
|
case INCONSISTENT_FLAGS:
|
2015-02-02 19:50:56 +03:00
|
|
|
WLog_ERR(TAG, "Error: INCONSISTENT_FLAGS");
|
2011-07-01 02:48:48 +04:00
|
|
|
break;
|
2013-04-13 02:03:56 +04:00
|
|
|
|
2011-07-01 02:48:48 +04:00
|
|
|
case HYBRID_REQUIRED_BY_SERVER:
|
2015-02-18 23:36:57 +03:00
|
|
|
WLog_WARN(TAG, "Error: HYBRID_REQUIRED_BY_SERVER");
|
2011-07-01 02:48:48 +04:00
|
|
|
break;
|
2013-04-13 02:03:56 +04:00
|
|
|
|
2011-07-01 02:48:48 +04:00
|
|
|
default:
|
2019-11-06 17:24:51 +03:00
|
|
|
WLog_ERR(TAG, "Error: Unknown protocol security error %" PRIu32 "", failureCode);
|
2011-07-01 02:48:48 +04:00
|
|
|
break;
|
|
|
|
}
|
2011-07-03 21:49:06 +04:00
|
|
|
|
2021-08-26 17:17:51 +03:00
|
|
|
nego_set_state(nego, NEGO_STATE_FAIL);
|
2020-04-15 18:21:46 +03:00
|
|
|
return TRUE;
|
2011-07-01 02:48:48 +04:00
|
|
|
}
|
|
|
|
|
2011-08-18 19:15:28 +04:00
|
|
|
/**
|
2022-12-09 16:35:03 +03:00
|
|
|
* Send RDP Negotiation Response (RDP_NEG_RSP).
|
|
|
|
* @param nego A pointer to the NEGO struct
|
2011-08-18 19:15:28 +04:00
|
|
|
*/
|
|
|
|
|
2012-10-09 10:38:39 +04:00
|
|
|
BOOL nego_send_negotiation_response(rdpNego* nego)
|
2011-08-18 19:15:28 +04:00
|
|
|
{
|
2018-11-20 19:03:50 +03:00
|
|
|
UINT16 length;
|
2017-12-11 12:25:21 +03:00
|
|
|
size_t bm, em;
|
2012-10-09 10:38:39 +04:00
|
|
|
BOOL status;
|
2013-05-15 22:42:37 +04:00
|
|
|
wStream* s;
|
2013-10-22 07:33:25 +04:00
|
|
|
BYTE flags;
|
2021-09-06 12:01:36 +03:00
|
|
|
rdpContext* context;
|
2012-06-20 04:10:49 +04:00
|
|
|
rdpSettings* settings;
|
2021-08-26 17:17:51 +03:00
|
|
|
|
|
|
|
WINPR_ASSERT(nego);
|
2021-09-06 12:01:36 +03:00
|
|
|
context = transport_get_context(nego->transport);
|
|
|
|
WINPR_ASSERT(context);
|
2021-08-26 17:17:51 +03:00
|
|
|
|
2021-09-06 12:01:36 +03:00
|
|
|
settings = context->settings;
|
2021-08-26 17:17:51 +03:00
|
|
|
WINPR_ASSERT(settings);
|
|
|
|
|
2013-05-15 21:17:29 +04:00
|
|
|
s = Stream_New(NULL, 512);
|
2018-07-10 13:04:27 +03:00
|
|
|
|
2014-03-26 02:13:08 +04:00
|
|
|
if (!s)
|
2015-05-29 14:46:50 +03:00
|
|
|
{
|
|
|
|
WLog_ERR(TAG, "Stream_New failed!");
|
2014-03-26 02:13:08 +04:00
|
|
|
return FALSE;
|
2015-05-29 14:46:50 +03:00
|
|
|
}
|
2013-05-15 21:17:29 +04:00
|
|
|
|
2011-08-18 19:15:28 +04:00
|
|
|
length = TPDU_CONNECTION_CONFIRM_LENGTH;
|
2013-05-15 22:42:37 +04:00
|
|
|
bm = Stream_GetPosition(s);
|
2013-04-30 06:35:15 +04:00
|
|
|
Stream_Seek(s, length);
|
2011-08-18 19:15:28 +04:00
|
|
|
|
2015-02-11 23:38:32 +03:00
|
|
|
if (nego->SelectedProtocol & PROTOCOL_FAILED_NEGO)
|
2012-01-25 20:08:10 +04:00
|
|
|
{
|
2015-02-11 23:38:32 +03:00
|
|
|
UINT32 errorCode = (nego->SelectedProtocol & ~PROTOCOL_FAILED_NEGO);
|
2013-10-22 07:33:25 +04:00
|
|
|
flags = 0;
|
2013-05-09 00:09:16 +04:00
|
|
|
Stream_Write_UINT8(s, TYPE_RDP_NEG_FAILURE);
|
2013-10-22 07:33:25 +04:00
|
|
|
Stream_Write_UINT8(s, flags); /* flags */
|
2019-11-06 17:24:51 +03:00
|
|
|
Stream_Write_UINT16(s, 8); /* RDP_NEG_DATA length (8) */
|
2015-02-11 23:38:32 +03:00
|
|
|
Stream_Write_UINT32(s, errorCode);
|
2012-01-25 20:08:10 +04:00
|
|
|
length += 8;
|
|
|
|
}
|
2014-08-13 19:01:43 +04:00
|
|
|
else
|
|
|
|
{
|
|
|
|
flags = EXTENDED_CLIENT_DATA_SUPPORTED;
|
|
|
|
|
2023-01-18 13:03:33 +03:00
|
|
|
if (freerdp_settings_get_bool(settings, FreeRDP_SupportGraphicsPipeline))
|
2014-08-13 19:01:43 +04:00
|
|
|
flags |= DYNVC_GFX_PROTOCOL_SUPPORTED;
|
|
|
|
|
2023-03-07 13:58:28 +03:00
|
|
|
/* RDP_NEG_DATA must be present for TLS, NLA, RDP and RDSTLS */
|
2014-08-13 19:01:43 +04:00
|
|
|
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 */
|
2014-08-13 19:01:43 +04:00
|
|
|
length += 8;
|
|
|
|
}
|
2011-08-18 19:15:28 +04:00
|
|
|
|
2013-05-15 22:42:37 +04:00
|
|
|
em = Stream_GetPosition(s);
|
|
|
|
Stream_SetPosition(s, bm);
|
2020-11-19 17:21:49 +03:00
|
|
|
status = tpkt_write_header(s, length);
|
|
|
|
if (status)
|
2013-09-06 13:07:33 +04:00
|
|
|
{
|
2020-11-19 17:21:49 +03:00
|
|
|
tpdu_write_connection_confirm(s, length - 5);
|
|
|
|
Stream_SetPosition(s, em);
|
|
|
|
Stream_SealLength(s);
|
2011-08-19 13:39:37 +04:00
|
|
|
|
2020-11-19 17:21:49 +03:00
|
|
|
status = (transport_write(nego->transport, s) >= 0);
|
|
|
|
}
|
2013-05-15 21:17:29 +04:00
|
|
|
Stream_Free(s, TRUE);
|
|
|
|
|
2012-06-20 04:10:49 +04:00
|
|
|
if (status)
|
2012-01-25 20:08:10 +04:00
|
|
|
{
|
|
|
|
/* 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;
|
2011-09-16 19:16:16 +04:00
|
|
|
|
2023-01-18 13:03:33 +03:00
|
|
|
if (nego->SelectedProtocol == PROTOCOL_RDP)
|
2012-01-25 20:08:10 +04:00
|
|
|
{
|
2023-01-18 13:03:33 +03:00
|
|
|
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))
|
|
|
|
return FALSE;
|
2012-06-20 04:10:49 +04:00
|
|
|
|
2023-01-18 13:03:33 +03:00
|
|
|
if (freerdp_settings_get_uint32(settings, FreeRDP_EncryptionLevel) ==
|
|
|
|
ENCRYPTION_LEVEL_NONE)
|
2012-05-22 19:21:09 +04:00
|
|
|
{
|
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
|
|
|
/**
|
|
|
|
* If the server implementation did not explicitely set a
|
|
|
|
* encryption level we default to client compatible
|
|
|
|
*/
|
2023-01-18 13:03:33 +03:00
|
|
|
if (!freerdp_settings_set_uint32(settings, FreeRDP_EncryptionLevel,
|
|
|
|
ENCRYPTION_LEVEL_CLIENT_COMPATIBLE))
|
|
|
|
return FALSE;
|
2012-05-22 19:21:09 +04:00
|
|
|
}
|
2012-06-20 04:10:49 +04:00
|
|
|
|
2023-01-18 13:03:33 +03:00
|
|
|
if (freerdp_settings_get_bool(settings, FreeRDP_LocalConnection))
|
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
|
|
|
{
|
|
|
|
/**
|
|
|
|
* 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");
|
2023-01-18 13:03:33 +03:00
|
|
|
if (!freerdp_settings_set_bool(settings, FreeRDP_UseRdpSecurityLayer, FALSE))
|
|
|
|
return FALSE;
|
|
|
|
if (!freerdp_settings_set_uint32(settings, FreeRDP_EncryptionLevel,
|
|
|
|
ENCRYPTION_LEVEL_NONE))
|
|
|
|
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-02-03 18:20:30 +03:00
|
|
|
else if (!freerdp_settings_get_pointer(settings, FreeRDP_RdpServerRsaKey))
|
2014-08-13 19:01:43 +04:00
|
|
|
{
|
2014-09-12 16:36:29 +04:00
|
|
|
WLog_ERR(TAG, "Missing server certificate");
|
2012-10-09 10:31:28 +04:00
|
|
|
return FALSE;
|
2014-08-13 19:01:43 +04:00
|
|
|
}
|
2012-01-25 20:08:10 +04:00
|
|
|
}
|
2023-01-18 13:03:33 +03:00
|
|
|
else if (nego->SelectedProtocol == PROTOCOL_SSL)
|
2012-01-25 20:08:10 +04:00
|
|
|
{
|
2023-01-18 13:03:33 +03:00
|
|
|
if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, TRUE))
|
|
|
|
return FALSE;
|
|
|
|
if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE))
|
|
|
|
return FALSE;
|
2023-03-07 13:58:28 +03:00
|
|
|
if (!freerdp_settings_set_bool(settings, FreeRDP_RdstlsSecurity, FALSE))
|
|
|
|
return FALSE;
|
2023-01-18 13:03:33 +03:00
|
|
|
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;
|
2012-01-25 20:08:10 +04:00
|
|
|
}
|
2023-01-18 13:03:33 +03:00
|
|
|
else if (nego->SelectedProtocol == PROTOCOL_HYBRID)
|
2012-01-25 20:08:10 +04:00
|
|
|
{
|
2023-01-18 13:03:33 +03:00
|
|
|
if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, TRUE))
|
|
|
|
return FALSE;
|
|
|
|
if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, TRUE))
|
|
|
|
return FALSE;
|
2023-03-07 13:58:28 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
else if (nego->SelectedProtocol == 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;
|
2023-01-18 13:03:33 +03:00
|
|
|
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;
|
2012-01-25 20:08:10 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-20 04:10:49 +04:00
|
|
|
return status;
|
2011-08-18 19:15:28 +04:00
|
|
|
}
|
|
|
|
|
2011-07-03 20:42:35 +04:00
|
|
|
/**
|
|
|
|
* Initialize NEGO state machine.
|
2022-12-09 16:35:03 +03:00
|
|
|
* @param nego A pointer to the NEGO struct
|
2011-07-03 20:42:35 +04:00
|
|
|
*/
|
|
|
|
|
2011-07-07 21:37:48 +04:00
|
|
|
void nego_init(rdpNego* nego)
|
2011-07-03 20:42:35 +04:00
|
|
|
{
|
2021-08-26 17:17:51 +03:00
|
|
|
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;
|
2013-09-19 03:16:48 +04:00
|
|
|
nego->sendNegoData = FALSE;
|
2011-12-18 21:10:56 +04:00
|
|
|
nego->flags = 0;
|
2011-07-03 20:42:35 +04:00
|
|
|
}
|
|
|
|
|
2011-07-01 02:48:48 +04:00
|
|
|
/**
|
|
|
|
* 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
|
2011-07-01 02:48:48 +04:00
|
|
|
*/
|
|
|
|
|
2013-11-01 22:13:09 +04:00
|
|
|
rdpNego* nego_new(rdpTransport* transport)
|
2011-07-01 02:48:48 +04:00
|
|
|
{
|
2019-11-06 17:24:51 +03:00
|
|
|
rdpNego* nego = (rdpNego*)calloc(1, sizeof(rdpNego));
|
2015-02-15 22:54:10 +03:00
|
|
|
|
2014-03-26 02:13:08 +04:00
|
|
|
if (!nego)
|
|
|
|
return NULL;
|
2011-07-01 02:48:48 +04:00
|
|
|
|
2014-03-26 02:13:08 +04:00
|
|
|
nego->transport = transport;
|
|
|
|
nego_init(nego);
|
2011-07-01 02:48:48 +04:00
|
|
|
return nego;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2011-07-03 20:42:35 +04:00
|
|
|
* Free NEGO state machine.
|
2022-12-09 16:35:03 +03:00
|
|
|
* @param nego A pointer to the NEGO struct
|
2011-07-01 02:48:48 +04:00
|
|
|
*/
|
|
|
|
|
2011-07-07 21:37:48 +04:00
|
|
|
void nego_free(rdpNego* nego)
|
2011-07-01 02:48:48 +04:00
|
|
|
{
|
2015-05-11 10:07:39 +03:00
|
|
|
if (nego)
|
|
|
|
{
|
|
|
|
free(nego->RoutingToken);
|
|
|
|
free(nego->cookie);
|
|
|
|
free(nego);
|
|
|
|
}
|
2011-07-01 02:48:48 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2011-07-03 20:42:35 +04:00
|
|
|
* 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
|
2011-07-01 02:48:48 +04:00
|
|
|
*/
|
|
|
|
|
2018-11-20 19:03:50 +03:00
|
|
|
BOOL nego_set_target(rdpNego* nego, const char* hostname, UINT16 port)
|
2011-07-01 02:48:48 +04:00
|
|
|
{
|
2018-11-20 19:03:50 +03:00
|
|
|
if (!nego || !hostname)
|
|
|
|
return FALSE;
|
|
|
|
|
2011-07-03 20:42:35 +04:00
|
|
|
nego->hostname = hostname;
|
|
|
|
nego->port = port;
|
2018-11-20 19:03:50 +03:00
|
|
|
return TRUE;
|
2011-07-03 20:42:35 +04:00
|
|
|
}
|
|
|
|
|
2012-07-25 14:29:49 +04:00
|
|
|
/**
|
|
|
|
* 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)
|
2012-07-25 14:29:49 +04:00
|
|
|
*/
|
|
|
|
|
2013-01-10 20:19:57 +04:00
|
|
|
void nego_set_negotiation_enabled(rdpNego* nego, BOOL NegotiateSecurityLayer)
|
2012-07-25 14:29:49 +04:00
|
|
|
{
|
2019-11-06 17:24:51 +03:00
|
|
|
WLog_DBG(TAG, "Enabling security layer negotiation: %s",
|
|
|
|
NegotiateSecurityLayer ? "TRUE" : "FALSE");
|
2013-01-10 20:19:57 +04:00
|
|
|
nego->NegotiateSecurityLayer = NegotiateSecurityLayer;
|
2012-07-25 14:29:49 +04:00
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-03-24 22:44:18 +04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2011-07-03 20:42:35 +04:00
|
|
|
/**
|
2011-07-31 07:51:26 +04:00
|
|
|
* 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
|
2012-10-09 10:31:28 +04:00
|
|
|
* @param enable_rdp whether to enable normal RDP protocol (TRUE for enabled, FALSE for disabled)
|
2011-07-31 07:51:26 +04:00
|
|
|
*/
|
|
|
|
|
2012-10-09 10:38:39 +04:00
|
|
|
void nego_enable_rdp(rdpNego* nego, BOOL enable_rdp)
|
2011-07-31 07:51:26 +04:00
|
|
|
{
|
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;
|
2011-07-31 07:51:26 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
2012-10-09 10:31:28 +04:00
|
|
|
* @param enable_tls whether to enable TLS + RDP protocol (TRUE for enabled, FALSE for disabled)
|
2011-07-31 07:51:26 +04:00
|
|
|
*/
|
2013-11-06 10:51:55 +04:00
|
|
|
|
2012-10-09 10:38:39 +04:00
|
|
|
void nego_enable_tls(rdpNego* nego, BOOL enable_tls)
|
2011-07-31 07:51:26 +04:00
|
|
|
{
|
2015-02-02 19:50:56 +03:00
|
|
|
WLog_DBG(TAG, "Enabling TLS security: %s", enable_tls ? "TRUE" : "FALSE");
|
2018-11-20 19:27:47 +03:00
|
|
|
nego->EnabledProtocols[PROTOCOL_SSL] = enable_tls;
|
2011-07-31 07:51:26 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
2011-07-03 20:42:35 +04:00
|
|
|
*/
|
|
|
|
|
2012-10-09 10:38:39 +04:00
|
|
|
void nego_enable_nla(rdpNego* nego, BOOL enable_nla)
|
2011-07-03 20:42:35 +04:00
|
|
|
{
|
2015-02-02 19:50:56 +03:00
|
|
|
WLog_DBG(TAG, "Enabling NLA security: %s", enable_nla ? "TRUE" : "FALSE");
|
2018-11-20 19:27:47 +03:00
|
|
|
nego->EnabledProtocols[PROTOCOL_HYBRID] = enable_nla;
|
2011-07-03 20:42:35 +04:00
|
|
|
}
|
|
|
|
|
2023-03-07 13:58:28 +03:00
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
2012-11-01 04:38:48 +04:00
|
|
|
/**
|
|
|
|
* 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)
|
2012-11-01 04:38:48 +04:00
|
|
|
*/
|
|
|
|
|
|
|
|
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");
|
2018-11-20 19:27:47 +03:00
|
|
|
nego->EnabledProtocols[PROTOCOL_HYBRID_EX] = enable_ext;
|
2012-11-01 04:38:48 +04:00
|
|
|
}
|
|
|
|
|
2023-03-08 04:06:47 +03:00
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
|
|
|
WLog_DBG(TAG, "Enabling RDS AAD security: %s", enable_aad ? "TRUE" : "FALSE");
|
|
|
|
nego->EnabledProtocols[PROTOCOL_RDSAAD] = enable_aad;
|
|
|
|
}
|
|
|
|
|
2011-07-03 20:42:35 +04:00
|
|
|
/**
|
|
|
|
* 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
|
2011-07-03 20:42:35 +04:00
|
|
|
*/
|
|
|
|
|
2020-11-18 09:51:45 +03:00
|
|
|
BOOL nego_set_routing_token(rdpNego* nego, const BYTE* RoutingToken, DWORD RoutingTokenLength)
|
2011-07-03 20:42:35 +04:00
|
|
|
{
|
2018-11-29 13:06:41 +03:00
|
|
|
if (RoutingTokenLength == 0)
|
|
|
|
return FALSE;
|
|
|
|
|
2013-11-04 01:25:56 +04:00
|
|
|
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);
|
2018-07-10 13:04:27 +03:00
|
|
|
|
2014-04-10 23:07:53 +04:00
|
|
|
if (!nego->RoutingToken)
|
|
|
|
return FALSE;
|
2018-07-10 13:04:27 +03:00
|
|
|
|
2013-11-04 01:25:56 +04:00
|
|
|
CopyMemory(nego->RoutingToken, RoutingToken, nego->RoutingTokenLength);
|
2014-04-10 23:07:53 +04:00
|
|
|
return TRUE;
|
2011-07-03 20:42:35 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
2011-07-03 20:42:35 +04:00
|
|
|
*/
|
|
|
|
|
2020-11-18 09:51:45 +03:00
|
|
|
BOOL nego_set_cookie(rdpNego* nego, const char* cookie)
|
2011-07-03 20:42:35 +04:00
|
|
|
{
|
2012-12-12 09:49:15 +04:00
|
|
|
if (nego->cookie)
|
2014-04-16 19:04:49 +04:00
|
|
|
{
|
2012-12-12 09:49:15 +04:00
|
|
|
free(nego->cookie);
|
2015-05-11 10:07:39 +03:00
|
|
|
nego->cookie = NULL;
|
2014-04-16 19:04:49 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!cookie)
|
|
|
|
return TRUE;
|
2012-12-12 09:49:15 +04:00
|
|
|
|
|
|
|
nego->cookie = _strdup(cookie);
|
2018-07-10 13:04:27 +03:00
|
|
|
|
2014-04-10 23:07:53 +04:00
|
|
|
if (!nego->cookie)
|
|
|
|
return FALSE;
|
2018-07-10 13:04:27 +03:00
|
|
|
|
2014-04-10 23:07:53 +04:00
|
|
|
return TRUE;
|
2011-07-01 02:48:48 +04:00
|
|
|
}
|
2012-07-25 20:46:43 +04:00
|
|
|
|
2012-10-26 02:38:51 +04:00
|
|
|
/**
|
|
|
|
* 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
|
2012-10-26 02:38:51 +04:00
|
|
|
*/
|
|
|
|
|
2015-02-03 01:16:32 +03:00
|
|
|
void nego_set_cookie_max_length(rdpNego* nego, UINT32 CookieMaxLength)
|
2012-10-26 02:38:51 +04:00
|
|
|
{
|
2015-02-03 01:16:32 +03:00
|
|
|
nego->CookieMaxLength = CookieMaxLength;
|
2012-10-26 02:38:51 +04:00
|
|
|
}
|
|
|
|
|
2012-07-25 20:46:43 +04:00
|
|
|
/**
|
|
|
|
* 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
|
2012-07-25 20:46:43 +04:00
|
|
|
*/
|
|
|
|
|
2015-02-03 01:16:32 +03:00
|
|
|
void nego_set_send_preconnection_pdu(rdpNego* nego, BOOL SendPreconnectionPdu)
|
2012-07-25 20:46:43 +04:00
|
|
|
{
|
2015-02-03 01:16:32 +03:00
|
|
|
nego->SendPreconnectionPdu = SendPreconnectionPdu;
|
2012-07-25 20:46:43 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
2012-07-25 20:46:43 +04:00
|
|
|
*/
|
|
|
|
|
2015-02-03 01:16:32 +03:00
|
|
|
void nego_set_preconnection_id(rdpNego* nego, UINT32 PreconnectionId)
|
2012-07-25 20:46:43 +04:00
|
|
|
{
|
2015-02-03 01:16:32 +03:00
|
|
|
nego->PreconnectionId = PreconnectionId;
|
2012-07-25 20:46:43 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
2012-07-25 20:46:43 +04:00
|
|
|
*/
|
|
|
|
|
2020-11-18 09:51:45 +03:00
|
|
|
void nego_set_preconnection_blob(rdpNego* nego, const char* PreconnectionBlob)
|
2012-07-25 20:46:43 +04:00
|
|
|
{
|
2015-02-03 01:16:32 +03:00
|
|
|
nego->PreconnectionBlob = PreconnectionBlob;
|
2012-07-25 20:46:43 +04:00
|
|
|
}
|
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)
|
|
|
|
{
|
|
|
|
if (!nego)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
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
|
|
|
}
|
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;
|
|
|
|
}
|