From: Alex Bligh Date: Fri, 14 Sep 2012 12:08:27 +0000 (+0100) Subject: Backport of pre-connect Hyper-V code X-Git-Url: http://git.alex.org.uk?ds=sidebyside Backport of pre-connect Hyper-V code --- diff --git a/include/freerdp/settings.h b/include/freerdp/settings.h index 5a9acd8..44a60cf 100644 --- a/include/freerdp/settings.h +++ b/include/freerdp/settings.h @@ -253,7 +253,8 @@ struct rdp_settings uint32 encryption_level; /* 28 */ boolean authentication; /* 29 */ uint32 negotiationFlags; /* 30 */ - uint32 paddingB[48 - 31]; /* 31 */ + boolean security_layer_negotiation; /* 31 */ + uint32 paddingB[48 - 32]; /* 32 */ /* Connection Settings */ uint32 port; /* 48 */ @@ -270,7 +271,11 @@ struct rdp_settings boolean compression; /* 59 */ uint32 performance_flags; /* 60 */ rdpBlob* password_cookie; /* 61 */ - uint32 paddingC[80 - 62]; /* 62 */ + uint32 paddingBackport[71 - 62]; /* 62 */ + boolean send_preconnection_pdu; /* 71 */ + uint32 preconnection_id; /* 72 */ + char* preconnection_blob; /* 73 */ + uint32 paddingC[80 - 74]; /* 74 */ /* User Interface Parameters */ boolean sw_gdi; /* 80 */ diff --git a/libfreerdp-core/connection.c b/libfreerdp-core/connection.c index fb4fde3..1c130f9 100644 --- a/libfreerdp-core/connection.c +++ b/libfreerdp-core/connection.c @@ -22,6 +22,7 @@ #include "input.h" #include "connection.h" +#include "transport.h" /** * Connection Sequence @@ -61,47 +62,37 @@ 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; diff --git a/libfreerdp-core/connection.h b/libfreerdp-core/connection.h index eb5c046..8d05bed 100644 --- a/libfreerdp-core/connection.h +++ b/libfreerdp-core/connection.h @@ -25,7 +25,6 @@ #include "tpdu.h" #include "nego.h" #include "mcs.h" -#include "transport.h" #include "activation.h" #include diff --git a/libfreerdp-core/nego.c b/libfreerdp-core/nego.c index 7eb810b..64bde26 100644 --- a/libfreerdp-core/nego.c +++ b/libfreerdp-core/nego.c @@ -27,6 +27,8 @@ #include "nego.h" +#include "transport.h" + static const char* const NEGO_STATE_STRINGS[] = { "NEGO_STATE_INITIAL", @@ -44,6 +46,8 @@ static const char PROTOCOL_SECURITY_STRINGS[3][4] = "NLA" }; +boolean nego_security_connect(rdpNego* nego); + /** * Negotiate protocol security and connect. * @param nego @@ -61,7 +65,31 @@ boolean nego_connect(rdpNego* 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 @@ -93,9 +121,35 @@ boolean nego_connect(rdpNego* nego) 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 @@ -104,21 +158,25 @@ boolean nego_connect(rdpNego* 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; } /** @@ -127,16 +185,66 @@ boolean nego_tcp_connect(rdpNego* nego) * @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 */ @@ -147,7 +255,7 @@ void nego_attempt_nla(rdpNego* nego) DEBUG_NEGO("Attempting NLA security"); - if (!nego_tcp_connect(nego)) + if (!nego_transport_connect(nego)) { nego->state = NEGO_STATE_FAIL; return; @@ -167,7 +275,7 @@ void nego_attempt_nla(rdpNego* nego) 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; @@ -189,7 +297,7 @@ void nego_attempt_tls(rdpNego* nego) DEBUG_NEGO("Attempting TLS security"); - if (!nego_tcp_connect(nego)) + if (!nego_transport_connect(nego)) { nego->state = NEGO_STATE_FAIL; return; @@ -209,7 +317,7 @@ void nego_attempt_tls(rdpNego* nego) 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; @@ -229,7 +337,7 @@ void nego_attempt_rdp(rdpNego* nego) DEBUG_NEGO("Attempting RDP security"); - if (!nego_tcp_connect(nego)) + if (!nego_transport_connect(nego)) { nego->state = NEGO_STATE_FAIL; return; @@ -258,7 +366,7 @@ boolean nego_recv_response(rdpNego* nego) 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); } /** @@ -662,6 +770,18 @@ void nego_set_target(rdpNego* nego, char* hostname, int port) } /** + * 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) @@ -718,3 +838,36 @@ void nego_set_cookie(rdpNego* nego, char* cookie) { 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; +} diff --git a/libfreerdp-core/nego.h b/libfreerdp-core/nego.h index 800b3b4..cf30c94 100644 --- a/libfreerdp-core/nego.h +++ b/libfreerdp-core/nego.h @@ -68,24 +68,40 @@ enum RDP_NEG_MSG #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); @@ -105,11 +121,15 @@ rdpNego* nego_new(struct rdp_transport * transport); 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__) diff --git a/libfreerdp-core/settings.c b/libfreerdp-core/settings.c index dbafa5b..95b6e46 100644 --- a/libfreerdp-core/settings.c +++ b/libfreerdp-core/settings.c @@ -51,6 +51,7 @@ rdpSettings* settings_new(void* instance) settings->nla_security = true; settings->tls_security = true; settings->rdp_security = true; + settings->security_layer_negotiation = true; settings->client_build = 2600; settings->kbd_type = 0; settings->kbd_subtype = 0; diff --git a/libfreerdp-utils/args.c b/libfreerdp-utils/args.c index cf10519..77d4036 100644 --- a/libfreerdp-utils/args.c +++ b/libfreerdp-utils/args.c @@ -95,6 +95,7 @@ int freerdp_parse_args(rdpSettings* settings, int argc, char** argv, " --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" @@ -102,6 +103,8 @@ int freerdp_parse_args(rdpSettings* settings, int argc, char** argv, " --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 @@ -532,6 +535,10 @@ int freerdp_parse_args(rdpSettings* settings, int argc, char** argv, 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++; @@ -621,6 +628,28 @@ int freerdp_parse_args(rdpSettings* settings, int argc, char** argv, { 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);