#include "input.h"
#include "connection.h"
+#include "transport.h"
/**
* Connection Sequence
boolean rdp_client_connect(rdpRdp* rdp)
{
- boolean status;
- uint32 selectedProtocol;
rdpSettings* settings = rdp->settings;
nego_init(rdp->nego);
nego_set_target(rdp->nego, settings->hostname, settings->port);
nego_set_cookie(rdp->nego, settings->username);
+ nego_set_send_preconnection_pdu(rdp->nego, settings->send_preconnection_pdu);
+ nego_set_preconnection_id(rdp->nego, settings->preconnection_id);
+ nego_set_preconnection_blob(rdp->nego, settings->preconnection_blob);
+
+ nego_set_negotiation_enabled(rdp->nego, settings->security_layer_negotiation);
nego_enable_rdp(rdp->nego, settings->rdp_security);
nego_enable_nla(rdp->nego, settings->nla_security);
nego_enable_tls(rdp->nego, settings->tls_security);
- if (nego_connect(rdp->nego) != true)
+ if (!nego_connect(rdp->nego))
{
- printf("Error: protocol security negotiation failure\n");
+ printf("Error: protocol security negotiation or connection failure\n");
return false;
}
- selectedProtocol = rdp->nego->selected_protocol;
-
- if ((selectedProtocol & PROTOCOL_TLS) || (selectedProtocol == PROTOCOL_RDP))
+ if ((rdp->nego->selected_protocol & PROTOCOL_TLS) || (rdp->nego->selected_protocol == PROTOCOL_RDP))
{
if ((settings->username != NULL) && ((settings->password != NULL) || (settings->password_cookie != NULL && settings->password_cookie->length > 0)))
settings->autologon = true;
}
- status = false;
- if (selectedProtocol & PROTOCOL_NLA)
- status = transport_connect_nla(rdp->transport);
- else if (selectedProtocol & PROTOCOL_TLS)
- status = transport_connect_tls(rdp->transport);
- else if (selectedProtocol == PROTOCOL_RDP) /* 0 */
- status = transport_connect_rdp(rdp->transport);
-
- if (status != true)
- return false;
-
rdp_set_blocking_mode(rdp, false);
rdp->state = CONNECTION_STATE_NEGO;
rdp->finalize_sc_pdus = 0;
- if (mcs_send_connect_initial(rdp->mcs) != true)
+ if (!mcs_send_connect_initial(rdp->mcs))
{
printf("Error: unable to send MCS Connect Initial\n");
return false;
#include "nego.h"
+#include "transport.h"
+
static const char* const NEGO_STATE_STRINGS[] =
{
"NEGO_STATE_INITIAL",
"NLA"
};
+boolean nego_security_connect(rdpNego* nego);
+
/**
* Negotiate protocol security and connect.
* @param nego
else if (nego->enabled_protocols[PROTOCOL_RDP] > 0)
nego->state = NEGO_STATE_RDP;
else
+ {
+ DEBUG_NEGO("No security protocol is enabled");
nego->state = NEGO_STATE_FAIL;
+ }
+
+ if (!nego->security_layer_negotiation_enabled)
+ {
+ DEBUG_NEGO("Security Layer Negotiation is disabled");
+ nego->enabled_protocols[PROTOCOL_NLA] = 0;
+ nego->enabled_protocols[PROTOCOL_TLS] = 0;
+ nego->enabled_protocols[PROTOCOL_RDP] = 0;
+ if(nego->state == NEGO_STATE_NLA)
+ nego->enabled_protocols[PROTOCOL_NLA] = 1;
+ else if (nego->state == NEGO_STATE_TLS)
+ nego->enabled_protocols[PROTOCOL_TLS] = 1;
+ else if (nego->state == NEGO_STATE_RDP)
+ nego->enabled_protocols[PROTOCOL_RDP] = 1;
+ }
+
+ if(!nego_send_preconnection_pdu(nego))
+ {
+ DEBUG_NEGO("Failed to send preconnection information");
+ nego->state = NEGO_STATE_FINAL;
+ return false;
+ }
}
do
nego->transport->settings->encryption_level = ENCRYPTION_LEVEL_CLIENT_COMPATIBLE;
}
+ /* finally connect security layer (if not already done) */
+ if(!nego_security_connect(nego))
+ {
+ DEBUG_NEGO("Failed to connect with %s security", PROTOCOL_SECURITY_STRINGS[nego->selected_protocol]);
+ return false;
+ }
+
return true;
}
+/* connect to selected security layer */
+boolean nego_security_connect(rdpNego* nego)
+{
+ if(!nego->tcp_connected)
+ {
+ nego->security_connected = false;
+ }
+ else if (!nego->security_connected)
+ {
+ if (nego->enabled_protocols[PROTOCOL_NLA] > 0)
+ nego->security_connected = transport_connect_nla(nego->transport);
+ else if (nego->enabled_protocols[PROTOCOL_TLS] > 0)
+ nego->security_connected = transport_connect_tls(nego->transport);
+ else if (nego->enabled_protocols[PROTOCOL_RDP] > 0)
+ nego->security_connected = transport_connect_rdp(nego->transport);
+ }
+ return nego->security_connected;
+}
+
/**
* Connect TCP layer.
* @param nego
boolean nego_tcp_connect(rdpNego* nego)
{
- if (nego->tcp_connected == 0)
- {
- if (transport_connect(nego->transport, nego->hostname, nego->port) == false)
- {
- nego->tcp_connected = 0;
- return false;
- }
- else
- {
- nego->tcp_connected = 1;
- return true;
- }
- }
+ if (!nego->tcp_connected)
+ nego->tcp_connected = transport_connect(nego->transport, nego->hostname, nego->port);
+ return nego->tcp_connected;
+}
- return true;
+/**
+ * Connect TCP layer. For direct approach, connect security layer as well.
+ * @param nego
+ * @return
+ */
+
+boolean nego_transport_connect(rdpNego* nego)
+{
+ nego_tcp_connect(nego);
+
+ if (nego->tcp_connected && !nego->security_layer_negotiation_enabled)
+ return nego_security_connect(nego);
+
+ return nego->tcp_connected;
}
/**
* @return
*/
-int nego_tcp_disconnect(rdpNego* nego)
+int nego_transport_disconnect(rdpNego* nego)
{
if (nego->tcp_connected)
transport_disconnect(nego->transport);
nego->tcp_connected = 0;
+ nego->security_connected = 0;
return 1;
}
/**
+ * Send preconnection information if enabled.
+ * @param nego
+ * @return
+ */
+
+boolean nego_send_preconnection_pdu(rdpNego* nego)
+{
+ STREAM* s;
+ uint32 cbSize;
+ UNICONV* uniconv;
+ uint16 cchPCB_times2 = 0;
+ char* wszPCB = NULL;
+
+ if(!nego->send_preconnection_pdu)
+ return true;
+
+ DEBUG_NEGO("Sending preconnection PDU");
+ if(!nego_tcp_connect(nego))
+ return false;
+
+ /* it's easier to always send the version 2 PDU, and it's just 2 bytes overhead */
+ cbSize = PRECONNECTION_PDU_V2_MIN_SIZE;
+ if(nego->preconnection_blob) {
+ uniconv = freerdp_uniconv_new();
+ wszPCB = freerdp_uniconv_out(uniconv, nego->preconnection_blob, &cchPCB_times2);
+ freerdp_uniconv_free(uniconv);
+ cchPCB_times2 += 2; /* zero-termination */
+ cbSize += cchPCB_times2;
+ }
+
+ s = transport_send_stream_init(nego->transport, cbSize);
+ stream_write_uint32(s, cbSize); /* cbSize */
+ stream_write_uint32(s, 0); /* Flags */
+ stream_write_uint32(s, PRECONNECTION_PDU_V2); /* Version */
+ stream_write_uint32(s, nego->preconnection_id); /* Id */
+ stream_write_uint16(s, cchPCB_times2 / 2); /* cchPCB */
+ if(wszPCB)
+ {
+ stream_write(s, wszPCB, cchPCB_times2); /* wszPCB */
+ xfree(wszPCB);
+ }
+
+ if (transport_write(nego->transport, s) < 0)
+ return false;
+
+ return true;
+}
+
+/**
* Attempt negotiating NLA + TLS security.
* @param nego
*/
DEBUG_NEGO("Attempting NLA security");
- if (!nego_tcp_connect(nego))
+ if (!nego_transport_connect(nego))
{
nego->state = NEGO_STATE_FAIL;
return;
if (nego->state != NEGO_STATE_FINAL)
{
- nego_tcp_disconnect(nego);
+ nego_transport_disconnect(nego);
if (nego->enabled_protocols[PROTOCOL_TLS] > 0)
nego->state = NEGO_STATE_TLS;
DEBUG_NEGO("Attempting TLS security");
- if (!nego_tcp_connect(nego))
+ if (!nego_transport_connect(nego))
{
nego->state = NEGO_STATE_FAIL;
return;
if (nego->state != NEGO_STATE_FINAL)
{
- nego_tcp_disconnect(nego);
+ nego_transport_disconnect(nego);
if (nego->enabled_protocols[PROTOCOL_RDP] > 0)
nego->state = NEGO_STATE_RDP;
DEBUG_NEGO("Attempting RDP security");
- if (!nego_tcp_connect(nego))
+ if (!nego_transport_connect(nego))
{
nego->state = NEGO_STATE_FAIL;
return;
STREAM* s = transport_recv_stream_init(nego->transport, 1024);
if (transport_read(nego->transport, s) < 0)
return false;
- return nego_recv(nego->transport, s, nego->transport->recv_extra);
+ return nego_recv(nego->transport, s, nego);
}
/**
}
/**
+ * Enable security layer negotiation.
+ * @param nego pointer to the negotiation structure
+ * @param enable_rdp whether to enable security layer negotiation (true for enabled, false for disabled)
+ */
+
+void nego_set_negotiation_enabled(rdpNego* nego, boolean security_layer_negotiation_enabled)
+{
+ DEBUG_NEGO("Enabling security layer negotiation: %s", security_layer_negotiation_enabled ? "true" : "false");
+ nego->security_layer_negotiation_enabled = security_layer_negotiation_enabled;
+}
+
+/**
* Enable RDP security protocol.
* @param nego pointer to the negotiation structure
* @param enable_rdp whether to enable normal RDP protocol (true for enabled, false for disabled)
{
nego->cookie = cookie;
}
+
+/**
+ * Enable / disable preconnection PDU.
+ * @param nego
+ * @param send_pcpdu
+ */
+
+void nego_set_send_preconnection_pdu(rdpNego* nego, boolean send_pcpdu)
+{
+ nego->send_preconnection_pdu = send_pcpdu;
+}
+
+/**
+ * Set preconnection id.
+ * @param nego
+ * @param id
+ */
+
+void nego_set_preconnection_id(rdpNego* nego, uint32 id)
+{
+ nego->preconnection_id = id;
+}
+
+/**
+ * Set preconnection blob.
+ * @param nego
+ * @param blob
+ */
+
+void nego_set_preconnection_blob(rdpNego* nego, char* blob)
+{
+ nego->preconnection_blob = blob;
+}
#define EXTENDED_CLIENT_DATA_SUPPORTED 0x01
+#define PRECONNECTION_PDU_V1_SIZE 16
+#define PRECONNECTION_PDU_V2_MIN_SIZE (PRECONNECTION_PDU_V1_SIZE+2)
+
+#define PRECONNECTION_PDU_V1 1
+#define PRECONNECTION_PDU_V2 2
+
struct rdp_nego
{
int port;
uint32 flags;
char* hostname;
char* cookie;
- NEGO_STATE state;
- int tcp_connected;
rdpBlob* routing_token;
+ boolean send_preconnection_pdu;
+ uint32 preconnection_id;
+ char* preconnection_blob;
+
+ NEGO_STATE state;
+ boolean tcp_connected;
+ boolean security_connected;
+
uint32 selected_protocol;
uint32 requested_protocols;
+ boolean security_layer_negotiation_enabled;
uint8 enabled_protocols[3];
+
rdpTransport* transport;
};
typedef struct rdp_nego rdpNego;
boolean nego_connect(rdpNego* nego);
+boolean nego_send_preconnection_pdu(rdpNego* nego);
+
void nego_attempt_nla(rdpNego* nego);
void nego_attempt_tls(rdpNego* nego);
void nego_attempt_rdp(rdpNego* nego);
void nego_free(rdpNego* nego);
void nego_init(rdpNego* nego);
void nego_set_target(rdpNego* nego, char* hostname, int port);
+void nego_set_negotiation_enabled(rdpNego* nego, boolean security_layer_negotiation_enabled);
void nego_enable_rdp(rdpNego* nego, boolean enable_rdp);
void nego_enable_nla(rdpNego* nego, boolean enable_nla);
void nego_enable_tls(rdpNego* nego, boolean enable_tls);
void nego_set_routing_token(rdpNego* nego, rdpBlob* routing_token);
void nego_set_cookie(rdpNego* nego, char* cookie);
+void nego_set_send_preconnection_pdu(rdpNego* nego, boolean send_pcpdu);
+void nego_set_preconnection_id(rdpNego* nego, uint32 id);
+void nego_set_preconnection_blob(rdpNego* nego, char* blob);
#ifdef WITH_DEBUG_NEGO
#define DEBUG_NEGO(fmt, ...) DEBUG_CLASS(NEGO, fmt, ## __VA_ARGS__)
" --disable-full-window-drag: disables full window drag\n"
" --disable-menu-animations: disables menu animations\n"
" --disable-theming: disables theming\n"
+ " --no-nego: disable negotiation of security layer and enforce highest enabled security protocol\n"
" --no-rdp: disable Standard RDP encryption\n"
" --no-tls: disable TLS encryption\n"
" --no-nla: disable network level authentication\n"
" --ignore-certificate: ignore verification of logon certificate\n"
" --sec: force protocol security (rdp, tls or nla)\n"
" --secure-checksum: use salted checksums with Standard RDP encryption\n"
+ " --pcid: preconnection id\n"
+ " --pcb: preconnection blob\n"
" --version: print version information\n"
"\n", argv[0]);
return FREERDP_ARGS_PARSE_HELP; //TODO: What is the correct return
return FREERDP_ARGS_PARSE_FAILURE;
}
}
+ else if (strcmp("--no-nego", argv[index]) == 0)
+ {
+ settings->security_layer_negotiation = false;
+ }
else if (strcmp("--plugin", argv[index]) == 0)
{
index++;
{
settings->secure_checksum = true;
}
+ else if (strcmp("--pcid", argv[index]) == 0)
+ {
+ index++;
+ if (index == argc)
+ {
+ printf("missing preconnection id value\n");
+ return -1;
+ }
+ settings->send_preconnection_pdu = true;
+ settings->preconnection_id = atoi(argv[index]);
+ }
+ else if (strcmp("--pcb", argv[index]) == 0)
+ {
+ index++;
+ if (index == argc)
+ {
+ printf("missing preconnection blob value\n");
+ return -1;
+ }
+ settings->send_preconnection_pdu = true;
+ settings->preconnection_blob = xstrdup(argv[index]);
+ }
else if (strcmp("--version", argv[index]) == 0)
{
printf("This is FreeRDP version %s\n", FREERDP_VERSION_FULL);