Blame libfreerdp/core/nego.c

Packit Service fa4841
/**
Packit Service fa4841
 * FreeRDP: A Remote Desktop Protocol Implementation
Packit Service fa4841
 * RDP Protocol Security Negotiation
Packit Service fa4841
 *
Packit Service fa4841
 * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
Packit Service fa4841
 * Copyright 2014 Norbert Federa <norbert.federa@thincast.com>
Packit Service fa4841
 * Copyright 2015 Thincast Technologies GmbH
Packit Service fa4841
 * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
Packit Service fa4841
 *
Packit Service fa4841
 * Licensed under the Apache License, Version 2.0 (the "License");
Packit Service fa4841
 * you may not use this file except in compliance with the License.
Packit Service fa4841
 * You may obtain a copy of the License at
Packit Service fa4841
 *
Packit Service fa4841
 *     http://www.apache.org/licenses/LICENSE-2.0
Packit Service fa4841
 *
Packit Service fa4841
 * Unless required by applicable law or agreed to in writing, software
Packit Service fa4841
 * distributed under the License is distributed on an "AS IS" BASIS,
Packit Service fa4841
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit Service fa4841
 * See the License for the specific language governing permissions and
Packit Service fa4841
 * limitations under the License.
Packit Service fa4841
 */
Packit Service fa4841
Packit Service fa4841
#ifdef HAVE_CONFIG_H
Packit Service fa4841
#include "config.h"
Packit Service fa4841
#endif
Packit Service fa4841
Packit Service fa4841
#include <winpr/crt.h>
Packit Service fa4841
Packit Service fa4841
#include <freerdp/log.h>
Packit Service fa4841
Packit Service fa4841
#include "tpkt.h"
Packit Service fa4841
Packit Service fa4841
#include "nego.h"
Packit Service fa4841
Packit Service fa4841
#include "transport.h"
Packit Service fa4841
Packit Service fa4841
#define TAG FREERDP_TAG("core.nego")
Packit Service fa4841
Packit Service bb5c11
static const char* const NEGO_STATE_STRINGS[] =
Packit Service fa4841
{
Packit Service bb5c11
	"NEGO_STATE_INITIAL",
Packit Service bb5c11
	"NEGO_STATE_EXT",
Packit Service bb5c11
	"NEGO_STATE_NLA",
Packit Service bb5c11
	"NEGO_STATE_TLS",
Packit Service bb5c11
	"NEGO_STATE_RDP",
Packit Service bb5c11
	"NEGO_STATE_FAIL",
Packit Service bb5c11
	"NEGO_STATE_FINAL"
Packit Service fa4841
};
Packit Service fa4841
Packit Service bb5c11
static const char PROTOCOL_SECURITY_STRINGS[9][4] =
Packit Service fa4841
{
Packit Service bb5c11
	"RDP",
Packit Service bb5c11
	"TLS",
Packit Service bb5c11
	"NLA",
Packit Service bb5c11
	"UNK",
Packit Service bb5c11
	"UNK",
Packit Service bb5c11
	"UNK",
Packit Service bb5c11
	"UNK",
Packit Service bb5c11
	"UNK",
Packit Service bb5c11
	"EXT"
Packit Service bb5c11
};
Packit Service fa4841
Packit Service fa4841
static BOOL nego_transport_connect(rdpNego* nego);
Packit Service fa4841
static BOOL nego_transport_disconnect(rdpNego* nego);
Packit Service fa4841
static BOOL nego_security_connect(rdpNego* nego);
Packit Service fa4841
static BOOL nego_send_preconnection_pdu(rdpNego* nego);
Packit Service fa4841
static BOOL nego_recv_response(rdpNego* nego);
Packit Service fa4841
static void nego_send(rdpNego* nego);
Packit Service bb5c11
static void nego_process_negotiation_request(rdpNego* nego, wStream* s);
Packit Service bb5c11
static void nego_process_negotiation_response(rdpNego* nego, wStream* s);
Packit Service bb5c11
static void nego_process_negotiation_failure(rdpNego* nego, wStream* s);
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Negotiate protocol security and connect.
Packit Service fa4841
 * @param nego
Packit Service fa4841
 * @return
Packit Service fa4841
 */
Packit Service fa4841
Packit Service fa4841
BOOL nego_connect(rdpNego* nego)
Packit Service fa4841
{
Packit Service fa4841
	rdpSettings* settings = nego->transport->settings;
Packit Service fa4841
Packit Service fa4841
	if (nego->state == NEGO_STATE_INITIAL)
Packit Service fa4841
	{
Packit Service bb5c11
		if (nego->EnabledProtocols[PROTOCOL_EXT])
Packit Service fa4841
		{
Packit Service fa4841
			nego->state = NEGO_STATE_EXT;
Packit Service fa4841
		}
Packit Service bb5c11
		else if (nego->EnabledProtocols[PROTOCOL_NLA])
Packit Service fa4841
		{
Packit Service fa4841
			nego->state = NEGO_STATE_NLA;
Packit Service fa4841
		}
Packit Service bb5c11
		else if (nego->EnabledProtocols[PROTOCOL_TLS])
Packit Service fa4841
		{
Packit Service fa4841
			nego->state = NEGO_STATE_TLS;
Packit Service fa4841
		}
Packit Service fa4841
		else if (nego->EnabledProtocols[PROTOCOL_RDP])
Packit Service fa4841
		{
Packit Service fa4841
			nego->state = NEGO_STATE_RDP;
Packit Service fa4841
		}
Packit Service fa4841
		else
Packit Service fa4841
		{
Packit Service fa4841
			WLog_ERR(TAG, "No security protocol is enabled");
Packit Service fa4841
			nego->state = NEGO_STATE_FAIL;
Packit Service fa4841
			return FALSE;
Packit Service fa4841
		}
Packit Service fa4841
Packit Service fa4841
		if (!nego->NegotiateSecurityLayer)
Packit Service fa4841
		{
Packit Service fa4841
			WLog_DBG(TAG, "Security Layer Negotiation is disabled");
Packit Service fa4841
			/* attempt only the highest enabled protocol (see nego_attempt_*) */
Packit Service bb5c11
			nego->EnabledProtocols[PROTOCOL_NLA] = FALSE;
Packit Service bb5c11
			nego->EnabledProtocols[PROTOCOL_TLS] = FALSE;
Packit Service fa4841
			nego->EnabledProtocols[PROTOCOL_RDP] = FALSE;
Packit Service bb5c11
			nego->EnabledProtocols[PROTOCOL_EXT] = FALSE;
Packit Service fa4841
Packit Service fa4841
			if (nego->state == NEGO_STATE_EXT)
Packit Service fa4841
			{
Packit Service bb5c11
				nego->EnabledProtocols[PROTOCOL_EXT] = TRUE;
Packit Service bb5c11
				nego->EnabledProtocols[PROTOCOL_NLA] = TRUE;
Packit Service bb5c11
				nego->SelectedProtocol = PROTOCOL_EXT;
Packit Service fa4841
			}
Packit Service fa4841
			else if (nego->state == NEGO_STATE_NLA)
Packit Service fa4841
			{
Packit Service bb5c11
				nego->EnabledProtocols[PROTOCOL_NLA] = TRUE;
Packit Service bb5c11
				nego->SelectedProtocol = PROTOCOL_NLA;
Packit Service fa4841
			}
Packit Service fa4841
			else if (nego->state == NEGO_STATE_TLS)
Packit Service fa4841
			{
Packit Service bb5c11
				nego->EnabledProtocols[PROTOCOL_TLS] = TRUE;
Packit Service bb5c11
				nego->SelectedProtocol = PROTOCOL_TLS;
Packit Service fa4841
			}
Packit Service fa4841
			else if (nego->state == NEGO_STATE_RDP)
Packit Service fa4841
			{
Packit Service fa4841
				nego->EnabledProtocols[PROTOCOL_RDP] = TRUE;
Packit Service fa4841
				nego->SelectedProtocol = PROTOCOL_RDP;
Packit Service fa4841
			}
Packit Service fa4841
		}
Packit Service fa4841
Packit Service fa4841
		if (nego->SendPreconnectionPdu)
Packit Service fa4841
		{
Packit Service fa4841
			if (!nego_send_preconnection_pdu(nego))
Packit Service fa4841
			{
Packit Service fa4841
				WLog_ERR(TAG, "Failed to send preconnection pdu");
Packit Service fa4841
				nego->state = NEGO_STATE_FINAL;
Packit Service fa4841
				return FALSE;
Packit Service fa4841
			}
Packit Service fa4841
		}
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	if (!nego->NegotiateSecurityLayer)
Packit Service fa4841
	{
Packit Service fa4841
		nego->state = NEGO_STATE_FINAL;
Packit Service fa4841
	}
Packit Service fa4841
	else
Packit Service fa4841
	{
Packit Service fa4841
		do
Packit Service fa4841
		{
Packit Service bb5c11
			WLog_DBG(TAG, "state: %s", NEGO_STATE_STRINGS[nego->state]);
Packit Service fa4841
			nego_send(nego);
Packit Service fa4841
Packit Service fa4841
			if (nego->state == NEGO_STATE_FAIL)
Packit Service fa4841
			{
Packit Service fa4841
				if (freerdp_get_last_error(nego->transport->context) == FREERDP_ERROR_SUCCESS)
Packit Service fa4841
					WLog_ERR(TAG, "Protocol Security Negotiation Failure");
Packit Service fa4841
Packit Service fa4841
				nego->state = NEGO_STATE_FINAL;
Packit Service fa4841
				return FALSE;
Packit Service fa4841
			}
Packit Service bb5c11
		}
Packit Service bb5c11
		while (nego->state != NEGO_STATE_FINAL);
Packit Service fa4841
	}
Packit Service fa4841
Packit Service bb5c11
	WLog_DBG(TAG, "Negotiated %s security", PROTOCOL_SECURITY_STRINGS[nego->SelectedProtocol]);
Packit Service fa4841
	/* update settings with negotiated protocol security */
Packit Service fa4841
	settings->RequestedProtocols = nego->RequestedProtocols;
Packit Service fa4841
	settings->SelectedProtocol = nego->SelectedProtocol;
Packit Service fa4841
	settings->NegotiationFlags = nego->flags;
Packit Service fa4841
Packit Service fa4841
	if (nego->SelectedProtocol == PROTOCOL_RDP)
Packit Service fa4841
	{
Packit Service fa4841
		settings->UseRdpSecurityLayer = TRUE;
Packit Service fa4841
Packit Service fa4841
		if (!settings->EncryptionMethods)
Packit Service fa4841
		{
Packit Service fa4841
			/**
Packit Service fa4841
			 * Advertise all supported encryption methods if the client
Packit Service fa4841
			 * implementation did not set any security methods
Packit Service fa4841
			 */
Packit Service fa4841
			settings->EncryptionMethods = ENCRYPTION_METHOD_40BIT | ENCRYPTION_METHOD_56BIT |
Packit Service fa4841
			                              ENCRYPTION_METHOD_128BIT | ENCRYPTION_METHOD_FIPS;
Packit Service fa4841
		}
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	/* finally connect security layer (if not already done) */
Packit Service fa4841
	if (!nego_security_connect(nego))
Packit Service fa4841
	{
Packit Service fa4841
		WLog_DBG(TAG, "Failed to connect with %s security",
Packit Service bb5c11
		         PROTOCOL_SECURITY_STRINGS[nego->SelectedProtocol]);
Packit Service fa4841
		return FALSE;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	return TRUE;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
BOOL nego_disconnect(rdpNego* nego)
Packit Service fa4841
{
Packit Service fa4841
	nego->state = NEGO_STATE_INITIAL;
Packit Service fa4841
	return nego_transport_disconnect(nego);
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
/* connect to selected security layer */
Packit Service fa4841
BOOL nego_security_connect(rdpNego* nego)
Packit Service fa4841
{
Packit Service fa4841
	if (!nego->TcpConnected)
Packit Service fa4841
	{
Packit Service fa4841
		nego->SecurityConnected = FALSE;
Packit Service fa4841
	}
Packit Service fa4841
	else if (!nego->SecurityConnected)
Packit Service fa4841
	{
Packit Service bb5c11
		if (nego->SelectedProtocol == PROTOCOL_NLA)
Packit Service fa4841
		{
Packit Service bb5c11
			WLog_DBG(TAG, "nego_security_connect with PROTOCOL_NLA");
Packit Service fa4841
			nego->SecurityConnected = transport_connect_nla(nego->transport);
Packit Service fa4841
		}
Packit Service bb5c11
		else if (nego->SelectedProtocol == PROTOCOL_TLS)
Packit Service fa4841
		{
Packit Service bb5c11
			WLog_DBG(TAG, "nego_security_connect with PROTOCOL_TLS");
Packit Service fa4841
			nego->SecurityConnected = transport_connect_tls(nego->transport);
Packit Service fa4841
		}
Packit Service fa4841
		else if (nego->SelectedProtocol == PROTOCOL_RDP)
Packit Service fa4841
		{
Packit Service fa4841
			WLog_DBG(TAG, "nego_security_connect with PROTOCOL_RDP");
Packit Service fa4841
			nego->SecurityConnected = transport_connect_rdp(nego->transport);
Packit Service fa4841
		}
Packit Service fa4841
		else
Packit Service fa4841
		{
Packit Service bb5c11
			WLog_ERR(TAG, "cannot connect security layer because no protocol has been selected yet.");
Packit Service fa4841
		}
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	return nego->SecurityConnected;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Connect TCP layer.
Packit Service fa4841
 * @param nego
Packit Service fa4841
 * @return
Packit Service fa4841
 */
Packit Service fa4841
Packit Service fa4841
static BOOL nego_tcp_connect(rdpNego* nego)
Packit Service fa4841
{
Packit Service fa4841
	if (!nego->TcpConnected)
Packit Service fa4841
	{
Packit Service fa4841
		if (nego->GatewayEnabled)
Packit Service fa4841
		{
Packit Service fa4841
			if (nego->GatewayBypassLocal)
Packit Service fa4841
			{
Packit Service fa4841
				/* Attempt a direct connection first, and then fallback to using the gateway */
Packit Service bb5c11
				WLog_INFO(TAG, "Detecting if host can be reached locally. - This might take some time.");
Packit Service fa4841
				WLog_INFO(TAG, "To disable auto detection use /gateway-usage-method:direct");
Packit Service fa4841
				transport_set_gateway_enabled(nego->transport, FALSE);
Packit Service bb5c11
				nego->TcpConnected = transport_connect(nego->transport, nego->hostname, nego->port, 1);
Packit Service fa4841
			}
Packit Service fa4841
Packit Service fa4841
			if (!nego->TcpConnected)
Packit Service fa4841
			{
Packit Service fa4841
				transport_set_gateway_enabled(nego->transport, TRUE);
Packit Service bb5c11
				nego->TcpConnected = transport_connect(nego->transport, nego->hostname, nego->port, 15);
Packit Service fa4841
			}
Packit Service fa4841
		}
Packit Service fa4841
		else
Packit Service fa4841
		{
Packit Service fa4841
			nego->TcpConnected = transport_connect(nego->transport, nego->hostname, nego->port, 15);
Packit Service fa4841
		}
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	return nego->TcpConnected;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Connect TCP layer. For direct approach, connect security layer as well.
Packit Service fa4841
 * @param nego
Packit Service fa4841
 * @return
Packit Service fa4841
 */
Packit Service fa4841
Packit Service fa4841
BOOL nego_transport_connect(rdpNego* nego)
Packit Service fa4841
{
Packit Service fa4841
	if (!nego_tcp_connect(nego))
Packit Service fa4841
		return FALSE;
Packit Service fa4841
Packit Service fa4841
	if (nego->TcpConnected && !nego->NegotiateSecurityLayer)
Packit Service fa4841
		return nego_security_connect(nego);
Packit Service fa4841
Packit Service fa4841
	return nego->TcpConnected;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Disconnect TCP layer.
Packit Service fa4841
 * @param nego
Packit Service fa4841
 * @return
Packit Service fa4841
 */
Packit Service fa4841
Packit Service fa4841
BOOL nego_transport_disconnect(rdpNego* nego)
Packit Service fa4841
{
Packit Service fa4841
	if (nego->TcpConnected)
Packit Service fa4841
		transport_disconnect(nego->transport);
Packit Service fa4841
Packit Service fa4841
	nego->TcpConnected = FALSE;
Packit Service fa4841
	nego->SecurityConnected = FALSE;
Packit Service fa4841
	return TRUE;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Send preconnection information if enabled.
Packit Service fa4841
 * @param nego
Packit Service fa4841
 * @return
Packit Service fa4841
 */
Packit Service fa4841
Packit Service fa4841
BOOL nego_send_preconnection_pdu(rdpNego* nego)
Packit Service fa4841
{
Packit Service fa4841
	wStream* s;
Packit Service fa4841
	UINT32 cbSize;
Packit Service fa4841
	UINT16 cchPCB = 0;
Packit Service fa4841
	WCHAR* wszPCB = NULL;
Packit Service fa4841
	WLog_DBG(TAG, "Sending preconnection PDU");
Packit Service fa4841
Packit Service fa4841
	if (!nego_tcp_connect(nego))
Packit Service fa4841
		return FALSE;
Packit Service fa4841
Packit Service fa4841
	/* it's easier to always send the version 2 PDU, and it's just 2 bytes overhead */
Packit Service fa4841
	cbSize = PRECONNECTION_PDU_V2_MIN_SIZE;
Packit Service fa4841
Packit Service fa4841
	if (nego->PreconnectionBlob)
Packit Service fa4841
	{
Packit Service bb5c11
		cchPCB = (UINT16) ConvertToUnicode(CP_UTF8, 0, nego->PreconnectionBlob, -1, &wszPCB, 0);
Packit Service fa4841
		cchPCB += 1; /* zero-termination */
Packit Service fa4841
		cbSize += cchPCB * 2;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	s = Stream_New(NULL, cbSize);
Packit Service fa4841
Packit Service fa4841
	if (!s)
Packit Service fa4841
	{
Packit Service fa4841
		free(wszPCB);
Packit Service fa4841
		WLog_ERR(TAG, "Stream_New failed!");
Packit Service fa4841
		return FALSE;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service bb5c11
	Stream_Write_UINT32(s, cbSize); /* cbSize */
Packit Service bb5c11
	Stream_Write_UINT32(s, 0); /* Flags */
Packit Service bb5c11
	Stream_Write_UINT32(s, PRECONNECTION_PDU_V2); /* Version */
Packit Service fa4841
	Stream_Write_UINT32(s, nego->PreconnectionId); /* Id */
Packit Service bb5c11
	Stream_Write_UINT16(s, cchPCB); /* cchPCB */
Packit Service fa4841
Packit Service fa4841
	if (wszPCB)
Packit Service fa4841
	{
Packit Service fa4841
		Stream_Write(s, wszPCB, cchPCB * 2); /* wszPCB */
Packit Service fa4841
		free(wszPCB);
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	Stream_SealLength(s);
Packit Service fa4841
Packit Service fa4841
	if (transport_write(nego->transport, s) < 0)
Packit Service fa4841
	{
Packit Service fa4841
		Stream_Free(s, TRUE);
Packit Service fa4841
		return FALSE;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	Stream_Free(s, TRUE);
Packit Service fa4841
	return TRUE;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Attempt negotiating NLA + TLS extended security.
Packit Service fa4841
 * @param nego
Packit Service fa4841
 */
Packit Service fa4841
Packit Service fa4841
static void nego_attempt_ext(rdpNego* nego)
Packit Service fa4841
{
Packit Service bb5c11
	nego->RequestedProtocols = PROTOCOL_NLA | PROTOCOL_TLS | PROTOCOL_EXT;
Packit Service fa4841
	WLog_DBG(TAG, "Attempting NLA extended security");
Packit Service fa4841
Packit Service fa4841
	if (!nego_transport_connect(nego))
Packit Service fa4841
	{
Packit Service fa4841
		nego->state = NEGO_STATE_FAIL;
Packit Service fa4841
		return;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	if (!nego_send_negotiation_request(nego))
Packit Service fa4841
	{
Packit Service fa4841
		nego->state = NEGO_STATE_FAIL;
Packit Service fa4841
		return;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	if (!nego_recv_response(nego))
Packit Service fa4841
	{
Packit Service fa4841
		nego->state = NEGO_STATE_FAIL;
Packit Service fa4841
		return;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service bb5c11
	WLog_DBG(TAG, "state: %s", NEGO_STATE_STRINGS[nego->state]);
Packit Service fa4841
Packit Service fa4841
	if (nego->state != NEGO_STATE_FINAL)
Packit Service fa4841
	{
Packit Service fa4841
		nego_transport_disconnect(nego);
Packit Service fa4841
Packit Service bb5c11
		if (nego->EnabledProtocols[PROTOCOL_NLA])
Packit Service fa4841
			nego->state = NEGO_STATE_NLA;
Packit Service bb5c11
		else if (nego->EnabledProtocols[PROTOCOL_TLS])
Packit Service fa4841
			nego->state = NEGO_STATE_TLS;
Packit Service fa4841
		else if (nego->EnabledProtocols[PROTOCOL_RDP])
Packit Service fa4841
			nego->state = NEGO_STATE_RDP;
Packit Service fa4841
		else
Packit Service fa4841
			nego->state = NEGO_STATE_FAIL;
Packit Service fa4841
	}
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Attempt negotiating NLA + TLS security.
Packit Service fa4841
 * @param nego
Packit Service fa4841
 */
Packit Service fa4841
Packit Service fa4841
static void nego_attempt_nla(rdpNego* nego)
Packit Service fa4841
{
Packit Service bb5c11
	nego->RequestedProtocols = PROTOCOL_NLA | PROTOCOL_TLS;
Packit Service fa4841
	WLog_DBG(TAG, "Attempting NLA security");
Packit Service fa4841
Packit Service fa4841
	if (!nego_transport_connect(nego))
Packit Service fa4841
	{
Packit Service fa4841
		nego->state = NEGO_STATE_FAIL;
Packit Service fa4841
		return;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	if (!nego_send_negotiation_request(nego))
Packit Service fa4841
	{
Packit Service fa4841
		nego->state = NEGO_STATE_FAIL;
Packit Service fa4841
		return;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	if (!nego_recv_response(nego))
Packit Service fa4841
	{
Packit Service fa4841
		nego->state = NEGO_STATE_FAIL;
Packit Service fa4841
		return;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service bb5c11
	WLog_DBG(TAG, "state: %s", NEGO_STATE_STRINGS[nego->state]);
Packit Service fa4841
Packit Service fa4841
	if (nego->state != NEGO_STATE_FINAL)
Packit Service fa4841
	{
Packit Service fa4841
		nego_transport_disconnect(nego);
Packit Service fa4841
Packit Service bb5c11
		if (nego->EnabledProtocols[PROTOCOL_TLS])
Packit Service fa4841
			nego->state = NEGO_STATE_TLS;
Packit Service fa4841
		else if (nego->EnabledProtocols[PROTOCOL_RDP])
Packit Service fa4841
			nego->state = NEGO_STATE_RDP;
Packit Service fa4841
		else
Packit Service fa4841
			nego->state = NEGO_STATE_FAIL;
Packit Service fa4841
	}
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Attempt negotiating TLS security.
Packit Service fa4841
 * @param nego
Packit Service fa4841
 */
Packit Service fa4841
Packit Service fa4841
static void nego_attempt_tls(rdpNego* nego)
Packit Service fa4841
{
Packit Service bb5c11
	nego->RequestedProtocols = PROTOCOL_TLS;
Packit Service fa4841
	WLog_DBG(TAG, "Attempting TLS security");
Packit Service fa4841
Packit Service fa4841
	if (!nego_transport_connect(nego))
Packit Service fa4841
	{
Packit Service fa4841
		nego->state = NEGO_STATE_FAIL;
Packit Service fa4841
		return;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	if (!nego_send_negotiation_request(nego))
Packit Service fa4841
	{
Packit Service fa4841
		nego->state = NEGO_STATE_FAIL;
Packit Service fa4841
		return;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	if (!nego_recv_response(nego))
Packit Service fa4841
	{
Packit Service fa4841
		nego->state = NEGO_STATE_FAIL;
Packit Service fa4841
		return;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	if (nego->state != NEGO_STATE_FINAL)
Packit Service fa4841
	{
Packit Service fa4841
		nego_transport_disconnect(nego);
Packit Service fa4841
Packit Service fa4841
		if (nego->EnabledProtocols[PROTOCOL_RDP])
Packit Service fa4841
			nego->state = NEGO_STATE_RDP;
Packit Service fa4841
		else
Packit Service fa4841
			nego->state = NEGO_STATE_FAIL;
Packit Service fa4841
	}
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Attempt negotiating standard RDP security.
Packit Service fa4841
 * @param nego
Packit Service fa4841
 */
Packit Service fa4841
Packit Service fa4841
static void nego_attempt_rdp(rdpNego* nego)
Packit Service fa4841
{
Packit Service fa4841
	nego->RequestedProtocols = PROTOCOL_RDP;
Packit Service fa4841
	WLog_DBG(TAG, "Attempting RDP security");
Packit Service fa4841
Packit Service fa4841
	if (!nego_transport_connect(nego))
Packit Service fa4841
	{
Packit Service fa4841
		nego->state = NEGO_STATE_FAIL;
Packit Service fa4841
		return;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	if (!nego_send_negotiation_request(nego))
Packit Service fa4841
	{
Packit Service fa4841
		nego->state = NEGO_STATE_FAIL;
Packit Service fa4841
		return;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	if (!nego_recv_response(nego))
Packit Service fa4841
	{
Packit Service fa4841
		nego->state = NEGO_STATE_FAIL;
Packit Service fa4841
		return;
Packit Service fa4841
	}
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Wait to receive a negotiation response
Packit Service fa4841
 * @param nego
Packit Service fa4841
 */
Packit Service fa4841
Packit Service fa4841
BOOL nego_recv_response(rdpNego* nego)
Packit Service fa4841
{
Packit Service fa4841
	int status;
Packit Service fa4841
	wStream* s;
Packit Service fa4841
	s = Stream_New(NULL, 1024);
Packit Service fa4841
Packit Service fa4841
	if (!s)
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "Stream_New failed!");
Packit Service fa4841
		return FALSE;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	status = transport_read_pdu(nego->transport, s);
Packit Service fa4841
Packit Service fa4841
	if (status < 0)
Packit Service fa4841
	{
Packit Service fa4841
		Stream_Free(s, TRUE);
Packit Service fa4841
		return FALSE;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	status = nego_recv(nego->transport, s, nego);
Packit Service fa4841
	Stream_Free(s, TRUE);
Packit Service fa4841
Packit Service fa4841
	if (status < 0)
Packit Service fa4841
		return FALSE;
Packit Service fa4841
Packit Service fa4841
	return TRUE;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Receive protocol security negotiation message.\n
Packit Service fa4841
 * @msdn{cc240501}
Packit Service fa4841
 * @param transport transport
Packit Service fa4841
 * @param s stream
Packit Service fa4841
 * @param extra nego pointer
Packit Service fa4841
 */
Packit Service fa4841
Packit Service fa4841
int nego_recv(rdpTransport* transport, wStream* s, void* extra)
Packit Service fa4841
{
Packit Service fa4841
	BYTE li;
Packit Service fa4841
	BYTE type;
Packit Service fa4841
	UINT16 length;
Packit Service bb5c11
	rdpNego* nego = (rdpNego*) extra;
Packit Service fa4841
Packit Service fa4841
	if (!tpkt_read_header(s, &length))
Packit Service fa4841
		return -1;
Packit Service fa4841
Packit Service bb5c11
	if (length == 0)
Packit Service bb5c11
		return -1;
Packit Service bb5c11
Packit Service bb5c11
	if (!tpdu_read_connection_confirm(s, &li))
Packit Service fa4841
		return -1;
Packit Service fa4841
Packit Service fa4841
	if (li > 6)
Packit Service fa4841
	{
Packit Service fa4841
		/* rdpNegData (optional) */
Packit Service fa4841
		Stream_Read_UINT8(s, type); /* Type */
Packit Service fa4841
Packit Service fa4841
		switch (type)
Packit Service fa4841
		{
Packit Service fa4841
			case TYPE_RDP_NEG_RSP:
Packit Service bb5c11
				nego_process_negotiation_response(nego, s);
Packit Service bb5c11
				WLog_DBG(TAG, "selected_protocol: %"PRIu32"", nego->SelectedProtocol);
Packit Service fa4841
Packit Service fa4841
				/* enhanced security selected ? */
Packit Service fa4841
Packit Service fa4841
				if (nego->SelectedProtocol)
Packit Service fa4841
				{
Packit Service bb5c11
					if ((nego->SelectedProtocol == PROTOCOL_NLA) &&
Packit Service bb5c11
					    (!nego->EnabledProtocols[PROTOCOL_NLA]))
Packit Service fa4841
					{
Packit Service fa4841
						nego->state = NEGO_STATE_FAIL;
Packit Service fa4841
					}
Packit Service fa4841
Packit Service bb5c11
					if ((nego->SelectedProtocol == PROTOCOL_TLS) &&
Packit Service bb5c11
					    (!nego->EnabledProtocols[PROTOCOL_TLS]))
Packit Service fa4841
					{
Packit Service fa4841
						nego->state = NEGO_STATE_FAIL;
Packit Service fa4841
					}
Packit Service fa4841
				}
Packit Service fa4841
				else if (!nego->EnabledProtocols[PROTOCOL_RDP])
Packit Service fa4841
				{
Packit Service fa4841
					nego->state = NEGO_STATE_FAIL;
Packit Service fa4841
				}
Packit Service fa4841
Packit Service fa4841
				break;
Packit Service fa4841
Packit Service fa4841
			case TYPE_RDP_NEG_FAILURE:
Packit Service bb5c11
				nego_process_negotiation_failure(nego, s);
Packit Service fa4841
				break;
Packit Service fa4841
		}
Packit Service fa4841
	}
Packit Service fa4841
	else if (li == 6)
Packit Service fa4841
	{
Packit Service fa4841
		WLog_DBG(TAG, "no rdpNegData");
Packit Service fa4841
Packit Service fa4841
		if (!nego->EnabledProtocols[PROTOCOL_RDP])
Packit Service fa4841
			nego->state = NEGO_STATE_FAIL;
Packit Service fa4841
		else
Packit Service fa4841
			nego->state = NEGO_STATE_FINAL;
Packit Service fa4841
	}
Packit Service fa4841
	else
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "invalid negotiation response");
Packit Service fa4841
		nego->state = NEGO_STATE_FAIL;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	return 0;
Packit Service fa4841
}
Packit Service fa4841
Packit Service bb5c11
Packit Service fa4841
/**
Packit Service fa4841
 * Read optional routing token or cookie of X.224 Connection Request PDU.
Packit Service fa4841
 * @msdn{cc240470}
Packit Service fa4841
 * @param nego
Packit Service fa4841
 * @param s stream
Packit Service fa4841
 */
Packit Service fa4841
Packit Service fa4841
static BOOL nego_read_request_token_or_cookie(rdpNego* nego, wStream* s)
Packit Service fa4841
{
Packit Service fa4841
	/* routingToken and cookie are optional and mutually exclusive!
Packit Service fa4841
	 *
Packit Service fa4841
	 * routingToken (variable): An optional and variable-length routing
Packit Service fa4841
	 * token (used for load balancing) terminated by a 0x0D0A two-byte
Packit Service fa4841
	 * sequence: (check [MSFT-SDLBTS] for details!)
Packit Service fa4841
	 * Cookie:[space]msts=[ip address].[port].[reserved][\x0D\x0A]
Packit Service fa4841
	 *
Packit Service fa4841
	 * cookie (variable): An optional and variable-length ANSI character
Packit Service fa4841
	 * string terminated by a 0x0D0A two-byte sequence:
Packit Service fa4841
	 * Cookie:[space]mstshash=[ANSISTRING][\x0D\x0A]
Packit Service fa4841
	 */
Packit Service fa4841
	BYTE* str = NULL;
Packit Service fa4841
	UINT16 crlf = 0;
Packit Service fa4841
	size_t pos, len;
Packit Service fa4841
	BOOL result = FALSE;
Packit Service fa4841
	BOOL isToken = FALSE;
Packit Service fa4841
	str = Stream_Pointer(s);
Packit Service fa4841
	pos = Stream_GetPosition(s);
Packit Service fa4841
Packit Service fa4841
	/* minimum length for token is 15 */
Packit Service bb5c11
	if (Stream_GetRemainingLength(s) < 15)
Packit Service fa4841
		return TRUE;
Packit Service fa4841
Packit Service fa4841
	if (memcmp(Stream_Pointer(s), "Cookie: mstshash=", 17) != 0)
Packit Service fa4841
	{
Packit Service fa4841
		isToken = TRUE;
Packit Service fa4841
	}
Packit Service fa4841
	else
Packit Service fa4841
	{
Packit Service fa4841
		/* not a token, minimum length for cookie is 19 */
Packit Service bb5c11
		if (Stream_GetRemainingLength(s) < 19)
Packit Service fa4841
			return TRUE;
Packit Service fa4841
Packit Service fa4841
		Stream_Seek(s, 17);
Packit Service fa4841
	}
Packit Service fa4841
Packit Service bb5c11
	while (Stream_GetRemainingLength(s) >= 2)
Packit Service fa4841
	{
Packit Service fa4841
		Stream_Read_UINT16(s, crlf);
Packit Service fa4841
Packit Service fa4841
		if (crlf == 0x0A0D)
Packit Service fa4841
			break;
Packit Service fa4841
Packit Service fa4841
		Stream_Rewind(s, 1);
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	if (crlf == 0x0A0D)
Packit Service fa4841
	{
Packit Service fa4841
		Stream_Rewind(s, 2);
Packit Service fa4841
		len = Stream_GetPosition(s) - pos;
Packit Service fa4841
		Stream_Write_UINT16(s, 0);
Packit Service fa4841
Packit Service bb5c11
		if (strlen((char*)str) == len)
Packit Service fa4841
		{
Packit Service fa4841
			if (isToken)
Packit Service fa4841
				result = nego_set_routing_token(nego, str, len);
Packit Service fa4841
			else
Packit Service fa4841
				result = nego_set_cookie(nego, (char*)str);
Packit Service fa4841
		}
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	if (!result)
Packit Service fa4841
	{
Packit Service fa4841
		Stream_SetPosition(s, pos);
Packit Service bb5c11
		WLog_ERR(TAG, "invalid %s received",
Packit Service bb5c11
		         isToken ? "routing token" : "cookie");
Packit Service fa4841
	}
Packit Service fa4841
	else
Packit Service fa4841
	{
Packit Service bb5c11
		WLog_DBG(TAG, "received %s [%s]",
Packit Service bb5c11
		         isToken ? "routing token" : "cookie", str);
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	return result;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Read protocol security negotiation request message.\n
Packit Service fa4841
 * @param nego
Packit Service fa4841
 * @param s stream
Packit Service fa4841
 */
Packit Service fa4841
Packit Service fa4841
BOOL nego_read_request(rdpNego* nego, wStream* s)
Packit Service fa4841
{
Packit Service fa4841
	BYTE li;
Packit Service fa4841
	BYTE type;
Packit Service fa4841
	UINT16 length;
Packit Service fa4841
Packit Service fa4841
	if (!tpkt_read_header(s, &length))
Packit Service fa4841
		return FALSE;
Packit Service fa4841
Packit Service bb5c11
	if (!tpdu_read_connection_request(s, &li))
Packit Service fa4841
		return FALSE;
Packit Service fa4841
Packit Service fa4841
	if (li != Stream_GetRemainingLength(s) + 6)
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "Incorrect TPDU length indicator.");
Packit Service fa4841
		return FALSE;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	if (!nego_read_request_token_or_cookie(nego, s))
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "Failed to parse routing token or cookie.");
Packit Service fa4841
		return FALSE;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	if (Stream_GetRemainingLength(s) >= 8)
Packit Service fa4841
	{
Packit Service fa4841
		/* rdpNegData (optional) */
Packit Service fa4841
		Stream_Read_UINT8(s, type); /* Type */
Packit Service fa4841
Packit Service fa4841
		if (type != TYPE_RDP_NEG_REQ)
Packit Service fa4841
		{
Packit Service bb5c11
			WLog_ERR(TAG, "Incorrect negotiation request type %"PRIu8"", type);
Packit Service fa4841
			return FALSE;
Packit Service fa4841
		}
Packit Service fa4841
Packit Service bb5c11
		nego_process_negotiation_request(nego, s);
Packit Service fa4841
	}
Packit Service fa4841
Packit Service bb5c11
	return TRUE;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Send protocol security negotiation message.
Packit Service fa4841
 * @param nego
Packit Service fa4841
 */
Packit Service fa4841
Packit Service fa4841
void nego_send(rdpNego* nego)
Packit Service fa4841
{
Packit Service fa4841
	if (nego->state == NEGO_STATE_EXT)
Packit Service fa4841
		nego_attempt_ext(nego);
Packit Service fa4841
	else if (nego->state == NEGO_STATE_NLA)
Packit Service fa4841
		nego_attempt_nla(nego);
Packit Service fa4841
	else if (nego->state == NEGO_STATE_TLS)
Packit Service fa4841
		nego_attempt_tls(nego);
Packit Service fa4841
	else if (nego->state == NEGO_STATE_RDP)
Packit Service fa4841
		nego_attempt_rdp(nego);
Packit Service fa4841
	else
Packit Service fa4841
		WLog_ERR(TAG, "invalid negotiation state for sending");
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Send RDP Negotiation Request (RDP_NEG_REQ).\n
Packit Service fa4841
 * @msdn{cc240500}\n
Packit Service fa4841
 * @msdn{cc240470}
Packit Service fa4841
 * @param nego
Packit Service fa4841
 */
Packit Service fa4841
Packit Service fa4841
BOOL nego_send_negotiation_request(rdpNego* nego)
Packit Service fa4841
{
Packit Service fa4841
	wStream* s;
Packit Service bb5c11
	int length;
Packit Service fa4841
	size_t bm, em;
Packit Service fa4841
	BYTE flags = 0;
Packit Service bb5c11
	int cookie_length;
Packit Service fa4841
	s = Stream_New(NULL, 512);
Packit Service fa4841
Packit Service fa4841
	if (!s)
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "Stream_New failed!");
Packit Service fa4841
		return FALSE;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	length = TPDU_CONNECTION_REQUEST_LENGTH;
Packit Service fa4841
	bm = Stream_GetPosition(s);
Packit Service fa4841
	Stream_Seek(s, length);
Packit Service fa4841
Packit Service fa4841
	if (nego->RoutingToken)
Packit Service fa4841
	{
Packit Service fa4841
		Stream_Write(s, nego->RoutingToken, nego->RoutingTokenLength);
Packit Service fa4841
Packit Service fa4841
		/* Ensure Routing Token is correctly terminated - may already be present in string */
Packit Service fa4841
Packit Service fa4841
		if ((nego->RoutingTokenLength > 2) &&
Packit Service fa4841
		    (nego->RoutingToken[nego->RoutingTokenLength - 2] == 0x0D) &&
Packit Service fa4841
		    (nego->RoutingToken[nego->RoutingTokenLength - 1] == 0x0A))
Packit Service fa4841
		{
Packit Service fa4841
			WLog_DBG(TAG, "Routing token looks correctly terminated - use verbatim");
Packit Service fa4841
			length += nego->RoutingTokenLength;
Packit Service fa4841
		}
Packit Service fa4841
		else
Packit Service fa4841
		{
Packit Service fa4841
			WLog_DBG(TAG, "Adding terminating CRLF to routing token");
Packit Service fa4841
			Stream_Write_UINT8(s, 0x0D); /* CR */
Packit Service fa4841
			Stream_Write_UINT8(s, 0x0A); /* LF */
Packit Service fa4841
			length += nego->RoutingTokenLength + 2;
Packit Service fa4841
		}
Packit Service fa4841
	}
Packit Service fa4841
	else if (nego->cookie)
Packit Service fa4841
	{
Packit Service fa4841
		cookie_length = strlen(nego->cookie);
Packit Service fa4841
Packit Service bb5c11
		if (cookie_length > (int) nego->CookieMaxLength)
Packit Service fa4841
			cookie_length = nego->CookieMaxLength;
Packit Service fa4841
Packit Service fa4841
		Stream_Write(s, "Cookie: mstshash=", 17);
Packit Service bb5c11
		Stream_Write(s, (BYTE*) nego->cookie, cookie_length);
Packit Service fa4841
		Stream_Write_UINT8(s, 0x0D); /* CR */
Packit Service fa4841
		Stream_Write_UINT8(s, 0x0A); /* LF */
Packit Service fa4841
		length += cookie_length + 19;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service bb5c11
	WLog_DBG(TAG, "RequestedProtocols: %"PRIu32"", nego->RequestedProtocols);
Packit Service fa4841
Packit Service fa4841
	if ((nego->RequestedProtocols > PROTOCOL_RDP) || (nego->sendNegoData))
Packit Service fa4841
	{
Packit Service fa4841
		/* RDP_NEG_DATA must be present for TLS and NLA */
Packit Service fa4841
		if (nego->RestrictedAdminModeRequired)
Packit Service fa4841
			flags |= RESTRICTED_ADMIN_MODE_REQUIRED;
Packit Service fa4841
Packit Service fa4841
		Stream_Write_UINT8(s, TYPE_RDP_NEG_REQ);
Packit Service fa4841
		Stream_Write_UINT8(s, flags);
Packit Service bb5c11
		Stream_Write_UINT16(s, 8); /* RDP_NEG_DATA length (8) */
Packit Service fa4841
		Stream_Write_UINT32(s, nego->RequestedProtocols); /* requestedProtocols */
Packit Service fa4841
		length += 8;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	em = Stream_GetPosition(s);
Packit Service fa4841
	Stream_SetPosition(s, bm);
Packit Service bb5c11
	tpkt_write_header(s, length);
Packit Service bb5c11
	tpdu_write_connection_request(s, length - 5);
Packit Service fa4841
	Stream_SetPosition(s, em);
Packit Service fa4841
	Stream_SealLength(s);
Packit Service bb5c11
Packit Service bb5c11
	if (transport_write(nego->transport, s) < 0)
Packit Service bb5c11
	{
Packit Service bb5c11
		Stream_Free(s, TRUE);
Packit Service bb5c11
		return FALSE;
Packit Service bb5c11
	}
Packit Service bb5c11
Packit Service fa4841
	Stream_Free(s, TRUE);
Packit Service bb5c11
	return TRUE;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Process Negotiation Request from Connection Request message.
Packit Service fa4841
 * @param nego
Packit Service fa4841
 * @param s
Packit Service fa4841
 */
Packit Service fa4841
Packit Service bb5c11
void nego_process_negotiation_request(rdpNego* nego, wStream* s)
Packit Service fa4841
{
Packit Service fa4841
	BYTE flags;
Packit Service fa4841
	UINT16 length;
Packit Service fa4841
	Stream_Read_UINT8(s, flags);
Packit Service fa4841
	Stream_Read_UINT16(s, length);
Packit Service fa4841
	Stream_Read_UINT32(s, nego->RequestedProtocols);
Packit Service bb5c11
	WLog_DBG(TAG, "RDP_NEG_REQ: RequestedProtocol: 0x%08"PRIX32"", nego->RequestedProtocols);
Packit Service fa4841
	nego->state = NEGO_STATE_FINAL;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Process Negotiation Response from Connection Confirm message.
Packit Service fa4841
 * @param nego
Packit Service fa4841
 * @param s
Packit Service fa4841
 */
Packit Service fa4841
Packit Service bb5c11
void nego_process_negotiation_response(rdpNego* nego, wStream* s)
Packit Service fa4841
{
Packit Service fa4841
	UINT16 length;
Packit Service fa4841
	WLog_DBG(TAG, "RDP_NEG_RSP");
Packit Service fa4841
Packit Service fa4841
	if (Stream_GetRemainingLength(s) < 7)
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "Invalid RDP_NEG_RSP");
Packit Service fa4841
		nego->state = NEGO_STATE_FAIL;
Packit Service bb5c11
		return;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	Stream_Read_UINT8(s, nego->flags);
Packit Service fa4841
	Stream_Read_UINT16(s, length);
Packit Service fa4841
	Stream_Read_UINT32(s, nego->SelectedProtocol);
Packit Service fa4841
	nego->state = NEGO_STATE_FINAL;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Process Negotiation Failure from Connection Confirm message.
Packit Service fa4841
 * @param nego
Packit Service fa4841
 * @param s
Packit Service fa4841
 */
Packit Service fa4841
Packit Service bb5c11
void nego_process_negotiation_failure(rdpNego* nego, wStream* s)
Packit Service fa4841
{
Packit Service fa4841
	BYTE flags;
Packit Service fa4841
	UINT16 length;
Packit Service fa4841
	UINT32 failureCode;
Packit Service fa4841
	WLog_DBG(TAG, "RDP_NEG_FAILURE");
Packit Service fa4841
	Stream_Read_UINT8(s, flags);
Packit Service fa4841
	Stream_Read_UINT16(s, length);
Packit Service fa4841
	Stream_Read_UINT32(s, failureCode);
Packit Service fa4841
Packit Service fa4841
	switch (failureCode)
Packit Service fa4841
	{
Packit Service fa4841
		case SSL_REQUIRED_BY_SERVER:
Packit Service fa4841
			WLog_WARN(TAG, "Error: SSL_REQUIRED_BY_SERVER");
Packit Service fa4841
			break;
Packit Service fa4841
Packit Service fa4841
		case SSL_NOT_ALLOWED_BY_SERVER:
Packit Service fa4841
			WLog_WARN(TAG, "Error: SSL_NOT_ALLOWED_BY_SERVER");
Packit Service fa4841
			nego->sendNegoData = TRUE;
Packit Service fa4841
			break;
Packit Service fa4841
Packit Service fa4841
		case SSL_CERT_NOT_ON_SERVER:
Packit Service fa4841
			WLog_ERR(TAG, "Error: SSL_CERT_NOT_ON_SERVER");
Packit Service fa4841
			nego->sendNegoData = TRUE;
Packit Service fa4841
			break;
Packit Service fa4841
Packit Service fa4841
		case INCONSISTENT_FLAGS:
Packit Service fa4841
			WLog_ERR(TAG, "Error: INCONSISTENT_FLAGS");
Packit Service fa4841
			break;
Packit Service fa4841
Packit Service fa4841
		case HYBRID_REQUIRED_BY_SERVER:
Packit Service fa4841
			WLog_WARN(TAG, "Error: HYBRID_REQUIRED_BY_SERVER");
Packit Service fa4841
			break;
Packit Service fa4841
Packit Service fa4841
		default:
Packit Service bb5c11
			WLog_ERR(TAG, "Error: Unknown protocol security error %"PRIu32"", failureCode);
Packit Service fa4841
			break;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	nego->state = NEGO_STATE_FAIL;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Send RDP Negotiation Response (RDP_NEG_RSP).\n
Packit Service fa4841
 * @param nego
Packit Service fa4841
 */
Packit Service fa4841
Packit Service fa4841
BOOL nego_send_negotiation_response(rdpNego* nego)
Packit Service fa4841
{
Packit Service bb5c11
	int length;
Packit Service fa4841
	size_t bm, em;
Packit Service fa4841
	BOOL status;
Packit Service fa4841
	wStream* s;
Packit Service fa4841
	BYTE flags;
Packit Service fa4841
	rdpSettings* settings;
Packit Service fa4841
	status = TRUE;
Packit Service fa4841
	settings = nego->transport->settings;
Packit Service fa4841
	s = Stream_New(NULL, 512);
Packit Service fa4841
Packit Service fa4841
	if (!s)
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "Stream_New failed!");
Packit Service fa4841
		return FALSE;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	length = TPDU_CONNECTION_CONFIRM_LENGTH;
Packit Service fa4841
	bm = Stream_GetPosition(s);
Packit Service fa4841
	Stream_Seek(s, length);
Packit Service fa4841
Packit Service fa4841
	if (nego->SelectedProtocol & PROTOCOL_FAILED_NEGO)
Packit Service fa4841
	{
Packit Service fa4841
		UINT32 errorCode = (nego->SelectedProtocol & ~PROTOCOL_FAILED_NEGO);
Packit Service fa4841
		flags = 0;
Packit Service fa4841
		Stream_Write_UINT8(s, TYPE_RDP_NEG_FAILURE);
Packit Service fa4841
		Stream_Write_UINT8(s, flags); /* flags */
Packit Service bb5c11
		Stream_Write_UINT16(s, 8); /* RDP_NEG_DATA length (8) */
Packit Service fa4841
		Stream_Write_UINT32(s, errorCode);
Packit Service fa4841
		length += 8;
Packit Service fa4841
		status = FALSE;
Packit Service fa4841
	}
Packit Service fa4841
	else
Packit Service fa4841
	{
Packit Service fa4841
		flags = EXTENDED_CLIENT_DATA_SUPPORTED;
Packit Service fa4841
Packit Service fa4841
		if (settings->SupportGraphicsPipeline)
Packit Service fa4841
			flags |= DYNVC_GFX_PROTOCOL_SUPPORTED;
Packit Service fa4841
Packit Service fa4841
		/* RDP_NEG_DATA must be present for TLS, NLA, and RDP */
Packit Service fa4841
		Stream_Write_UINT8(s, TYPE_RDP_NEG_RSP);
Packit Service bb5c11
		Stream_Write_UINT8(s, flags); /* flags */
Packit Service bb5c11
		Stream_Write_UINT16(s, 8); /* RDP_NEG_DATA length (8) */
Packit Service fa4841
		Stream_Write_UINT32(s, nego->SelectedProtocol); /* selectedProtocol */
Packit Service fa4841
		length += 8;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	em = Stream_GetPosition(s);
Packit Service fa4841
	Stream_SetPosition(s, bm);
Packit Service fa4841
	tpkt_write_header(s, length);
Packit Service fa4841
	tpdu_write_connection_confirm(s, length - 5);
Packit Service fa4841
	Stream_SetPosition(s, em);
Packit Service fa4841
	Stream_SealLength(s);
Packit Service fa4841
Packit Service fa4841
	if (transport_write(nego->transport, s) < 0)
Packit Service fa4841
	{
Packit Service fa4841
		Stream_Free(s, TRUE);
Packit Service fa4841
		return FALSE;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	Stream_Free(s, TRUE);
Packit Service fa4841
Packit Service fa4841
	if (status)
Packit Service fa4841
	{
Packit Service fa4841
		/* update settings with negotiated protocol security */
Packit Service fa4841
		settings->RequestedProtocols = nego->RequestedProtocols;
Packit Service fa4841
		settings->SelectedProtocol = nego->SelectedProtocol;
Packit Service fa4841
Packit Service fa4841
		if (settings->SelectedProtocol == PROTOCOL_RDP)
Packit Service fa4841
		{
Packit Service fa4841
			settings->TlsSecurity = FALSE;
Packit Service fa4841
			settings->NlaSecurity = FALSE;
Packit Service fa4841
			settings->RdpSecurity = TRUE;
Packit Service fa4841
			settings->UseRdpSecurityLayer = TRUE;
Packit Service fa4841
Packit Service fa4841
			if (settings->EncryptionLevel == ENCRYPTION_LEVEL_NONE)
Packit Service fa4841
			{
Packit Service fa4841
				/**
Packit Service fa4841
				 * If the server implementation did not explicitely set a
Packit Service fa4841
				 * encryption level we default to client compatible
Packit Service fa4841
				 */
Packit Service fa4841
				settings->EncryptionLevel = ENCRYPTION_LEVEL_CLIENT_COMPATIBLE;
Packit Service fa4841
			}
Packit Service fa4841
Packit Service fa4841
			if (settings->LocalConnection)
Packit Service fa4841
			{
Packit Service fa4841
				/**
Packit Service fa4841
				 * Note: This hack was firstly introduced in commit 95f5e115 to
Packit Service fa4841
				 * disable the unnecessary encryption with peers connecting to
Packit Service fa4841
				 * 127.0.0.1 or local unix sockets.
Packit Service fa4841
				 * This also affects connections via port tunnels! (e.g. ssh -L)
Packit Service fa4841
				 */
Packit Service fa4841
				WLog_INFO(TAG, "Turning off encryption for local peer with standard rdp security");
Packit Service fa4841
				settings->UseRdpSecurityLayer = FALSE;
Packit Service fa4841
				settings->EncryptionLevel = ENCRYPTION_LEVEL_NONE;
Packit Service fa4841
			}
Packit Service fa4841
Packit Service fa4841
			if (!settings->RdpServerRsaKey && !settings->RdpKeyFile && !settings->RdpKeyContent)
Packit Service fa4841
			{
Packit Service fa4841
				WLog_ERR(TAG, "Missing server certificate");
Packit Service fa4841
				return FALSE;
Packit Service fa4841
			}
Packit Service fa4841
		}
Packit Service bb5c11
		else if (settings->SelectedProtocol == PROTOCOL_TLS)
Packit Service fa4841
		{
Packit Service fa4841
			settings->TlsSecurity = TRUE;
Packit Service fa4841
			settings->NlaSecurity = FALSE;
Packit Service fa4841
			settings->RdpSecurity = FALSE;
Packit Service fa4841
			settings->UseRdpSecurityLayer = FALSE;
Packit Service fa4841
			settings->EncryptionLevel = ENCRYPTION_LEVEL_NONE;
Packit Service fa4841
		}
Packit Service bb5c11
		else if (settings->SelectedProtocol == PROTOCOL_NLA)
Packit Service fa4841
		{
Packit Service fa4841
			settings->TlsSecurity = TRUE;
Packit Service fa4841
			settings->NlaSecurity = TRUE;
Packit Service fa4841
			settings->RdpSecurity = FALSE;
Packit Service fa4841
			settings->UseRdpSecurityLayer = FALSE;
Packit Service fa4841
			settings->EncryptionLevel = ENCRYPTION_LEVEL_NONE;
Packit Service fa4841
		}
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	return status;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Initialize NEGO state machine.
Packit Service fa4841
 * @param nego
Packit Service fa4841
 */
Packit Service fa4841
Packit Service fa4841
void nego_init(rdpNego* nego)
Packit Service fa4841
{
Packit Service fa4841
	nego->state = NEGO_STATE_INITIAL;
Packit Service fa4841
	nego->RequestedProtocols = PROTOCOL_RDP;
Packit Service fa4841
	nego->CookieMaxLength = DEFAULT_COOKIE_MAX_LENGTH;
Packit Service fa4841
	nego->sendNegoData = FALSE;
Packit Service fa4841
	nego->flags = 0;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Create a new NEGO state machine instance.
Packit Service fa4841
 * @param transport
Packit Service fa4841
 * @return
Packit Service fa4841
 */
Packit Service fa4841
Packit Service fa4841
rdpNego* nego_new(rdpTransport* transport)
Packit Service fa4841
{
Packit Service bb5c11
	rdpNego* nego = (rdpNego*) calloc(1, sizeof(rdpNego));
Packit Service fa4841
Packit Service fa4841
	if (!nego)
Packit Service fa4841
		return NULL;
Packit Service fa4841
Packit Service fa4841
	nego->transport = transport;
Packit Service fa4841
	nego_init(nego);
Packit Service fa4841
	return nego;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Free NEGO state machine.
Packit Service fa4841
 * @param nego
Packit Service fa4841
 */
Packit Service fa4841
Packit Service fa4841
void nego_free(rdpNego* nego)
Packit Service fa4841
{
Packit Service fa4841
	if (nego)
Packit Service fa4841
	{
Packit Service fa4841
		free(nego->RoutingToken);
Packit Service fa4841
		free(nego->cookie);
Packit Service fa4841
		free(nego);
Packit Service fa4841
	}
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Set target hostname and port.
Packit Service fa4841
 * @param nego
Packit Service fa4841
 * @param hostname
Packit Service fa4841
 * @param port
Packit Service fa4841
 */
Packit Service fa4841
Packit Service bb5c11
void nego_set_target(rdpNego* nego, char* hostname, int port)
Packit Service fa4841
{
Packit Service fa4841
	nego->hostname = hostname;
Packit Service fa4841
	nego->port = port;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Enable security layer negotiation.
Packit Service fa4841
 * @param nego pointer to the negotiation structure
Packit Service bb5c11
 * @param enable_rdp whether to enable security layer negotiation (TRUE for enabled, FALSE for disabled)
Packit Service fa4841
 */
Packit Service fa4841
Packit Service fa4841
void nego_set_negotiation_enabled(rdpNego* nego, BOOL NegotiateSecurityLayer)
Packit Service fa4841
{
Packit Service bb5c11
	WLog_DBG(TAG, "Enabling security layer negotiation: %s", NegotiateSecurityLayer ? "TRUE" : "FALSE");
Packit Service fa4841
	nego->NegotiateSecurityLayer = NegotiateSecurityLayer;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Enable restricted admin mode.
Packit Service fa4841
 * @param nego pointer to the negotiation structure
Packit Service bb5c11
 * @param enable_restricted whether to enable security layer negotiation (TRUE for enabled, FALSE for disabled)
Packit Service fa4841
 */
Packit Service fa4841
Packit Service fa4841
void nego_set_restricted_admin_mode_required(rdpNego* nego, BOOL RestrictedAdminModeRequired)
Packit Service fa4841
{
Packit Service bb5c11
	WLog_DBG(TAG, "Enabling restricted admin mode: %s", RestrictedAdminModeRequired ? "TRUE" : "FALSE");
Packit Service fa4841
	nego->RestrictedAdminModeRequired = RestrictedAdminModeRequired;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
void nego_set_gateway_enabled(rdpNego* nego, BOOL GatewayEnabled)
Packit Service fa4841
{
Packit Service fa4841
	nego->GatewayEnabled = GatewayEnabled;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
void nego_set_gateway_bypass_local(rdpNego* nego, BOOL GatewayBypassLocal)
Packit Service fa4841
{
Packit Service fa4841
	nego->GatewayBypassLocal = GatewayBypassLocal;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Enable RDP security protocol.
Packit Service fa4841
 * @param nego pointer to the negotiation structure
Packit Service fa4841
 * @param enable_rdp whether to enable normal RDP protocol (TRUE for enabled, FALSE for disabled)
Packit Service fa4841
 */
Packit Service fa4841
Packit Service fa4841
void nego_enable_rdp(rdpNego* nego, BOOL enable_rdp)
Packit Service fa4841
{
Packit Service fa4841
	WLog_DBG(TAG, "Enabling RDP security: %s", enable_rdp ? "TRUE" : "FALSE");
Packit Service fa4841
	nego->EnabledProtocols[PROTOCOL_RDP] = enable_rdp;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Enable TLS security protocol.
Packit Service fa4841
 * @param nego pointer to the negotiation structure
Packit Service fa4841
 * @param enable_tls whether to enable TLS + RDP protocol (TRUE for enabled, FALSE for disabled)
Packit Service fa4841
 */
Packit Service fa4841
Packit Service fa4841
void nego_enable_tls(rdpNego* nego, BOOL enable_tls)
Packit Service fa4841
{
Packit Service fa4841
	WLog_DBG(TAG, "Enabling TLS security: %s", enable_tls ? "TRUE" : "FALSE");
Packit Service bb5c11
	nego->EnabledProtocols[PROTOCOL_TLS] = enable_tls;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Enable NLA security protocol.
Packit Service fa4841
 * @param nego pointer to the negotiation structure
Packit Service bb5c11
 * @param enable_nla whether to enable network level authentication protocol (TRUE for enabled, FALSE for disabled)
Packit Service fa4841
 */
Packit Service fa4841
Packit Service fa4841
void nego_enable_nla(rdpNego* nego, BOOL enable_nla)
Packit Service fa4841
{
Packit Service fa4841
	WLog_DBG(TAG, "Enabling NLA security: %s", enable_nla ? "TRUE" : "FALSE");
Packit Service bb5c11
	nego->EnabledProtocols[PROTOCOL_NLA] = enable_nla;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Enable NLA extended security protocol.
Packit Service fa4841
 * @param nego pointer to the negotiation structure
Packit Service bb5c11
 * @param enable_ext whether to enable network level authentication extended protocol (TRUE for enabled, FALSE for disabled)
Packit Service fa4841
 */
Packit Service fa4841
Packit Service fa4841
void nego_enable_ext(rdpNego* nego, BOOL enable_ext)
Packit Service fa4841
{
Packit Service fa4841
	WLog_DBG(TAG, "Enabling NLA extended security: %s", enable_ext ? "TRUE" : "FALSE");
Packit Service bb5c11
	nego->EnabledProtocols[PROTOCOL_EXT] = enable_ext;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Set routing token.
Packit Service fa4841
 * @param nego
Packit Service fa4841
 * @param RoutingToken
Packit Service fa4841
 * @param RoutingTokenLength
Packit Service fa4841
 */
Packit Service fa4841
Packit Service fa4841
BOOL nego_set_routing_token(rdpNego* nego, BYTE* RoutingToken, DWORD RoutingTokenLength)
Packit Service fa4841
{
Packit Service fa4841
	free(nego->RoutingToken);
Packit Service fa4841
	nego->RoutingTokenLength = RoutingTokenLength;
Packit Service bb5c11
	nego->RoutingToken = (BYTE*) malloc(nego->RoutingTokenLength);
Packit Service fa4841
Packit Service fa4841
	if (!nego->RoutingToken)
Packit Service fa4841
		return FALSE;
Packit Service fa4841
Packit Service fa4841
	CopyMemory(nego->RoutingToken, RoutingToken, nego->RoutingTokenLength);
Packit Service fa4841
	return TRUE;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Set cookie.
Packit Service fa4841
 * @param nego
Packit Service fa4841
 * @param cookie
Packit Service fa4841
 */
Packit Service fa4841
Packit Service fa4841
BOOL nego_set_cookie(rdpNego* nego, char* cookie)
Packit Service fa4841
{
Packit Service fa4841
	if (nego->cookie)
Packit Service fa4841
	{
Packit Service fa4841
		free(nego->cookie);
Packit Service fa4841
		nego->cookie = NULL;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	if (!cookie)
Packit Service fa4841
		return TRUE;
Packit Service fa4841
Packit Service fa4841
	nego->cookie = _strdup(cookie);
Packit Service fa4841
Packit Service fa4841
	if (!nego->cookie)
Packit Service fa4841
		return FALSE;
Packit Service fa4841
Packit Service fa4841
	return TRUE;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Set cookie maximum length
Packit Service fa4841
 * @param nego
Packit Service fa4841
 * @param CookieMaxLength
Packit Service fa4841
 */
Packit Service fa4841
Packit Service fa4841
void nego_set_cookie_max_length(rdpNego* nego, UINT32 CookieMaxLength)
Packit Service fa4841
{
Packit Service fa4841
	nego->CookieMaxLength = CookieMaxLength;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Enable / disable preconnection PDU.
Packit Service fa4841
 * @param nego
Packit Service fa4841
 * @param send_pcpdu
Packit Service fa4841
 */
Packit Service fa4841
Packit Service fa4841
void nego_set_send_preconnection_pdu(rdpNego* nego, BOOL SendPreconnectionPdu)
Packit Service fa4841
{
Packit Service fa4841
	nego->SendPreconnectionPdu = SendPreconnectionPdu;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Set preconnection id.
Packit Service fa4841
 * @param nego
Packit Service fa4841
 * @param id
Packit Service fa4841
 */
Packit Service fa4841
Packit Service fa4841
void nego_set_preconnection_id(rdpNego* nego, UINT32 PreconnectionId)
Packit Service fa4841
{
Packit Service fa4841
	nego->PreconnectionId = PreconnectionId;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Set preconnection blob.
Packit Service fa4841
 * @param nego
Packit Service fa4841
 * @param blob
Packit Service fa4841
 */
Packit Service fa4841
Packit Service fa4841
void nego_set_preconnection_blob(rdpNego* nego, char* PreconnectionBlob)
Packit Service fa4841
{
Packit Service fa4841
	nego->PreconnectionBlob = PreconnectionBlob;
Packit Service fa4841
}