diff --git a/include/freerdp/crypto/tls.h b/include/freerdp/crypto/tls.h index 06dc97c68..340737502 100644 --- a/include/freerdp/crypto/tls.h +++ b/include/freerdp/crypto/tls.h @@ -85,24 +85,24 @@ struct rdp_tls }; #ifdef __cplusplus - extern "C" { +extern "C" { #endif -FREERDP_API int tls_connect(rdpTls* tls, BIO *underlying); -FREERDP_API BOOL tls_accept(rdpTls* tls, BIO *underlying, rdpSettings *settings); +FREERDP_API int tls_connect(rdpTls* tls, BIO* underlying); +FREERDP_API BOOL tls_accept(rdpTls* tls, BIO* underlying, rdpSettings* settings); FREERDP_API BOOL tls_send_alert(rdpTls* tls); FREERDP_API int tls_write_all(rdpTls* tls, const BYTE* data, int length); FREERDP_API int tls_set_alert_code(rdpTls* tls, int level, int description); -FREERDP_API BOOL tls_match_hostname(char *pattern, int pattern_length, char *hostname); +FREERDP_API BOOL tls_match_hostname(char* pattern, int pattern_length, char* hostname); FREERDP_API int tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, int port); FREERDP_API void tls_print_certificate_error(char* hostname, UINT16 port, - char* fingerprint, char* hosts_file); + char* fingerprint, char* hosts_file); FREERDP_API void tls_print_certificate_name_mismatch_error( - char* hostname, UINT16 port, char* common_name, char** alt_names, - int alt_names_count); + char* hostname, UINT16 port, char* common_name, char** alt_names, + int alt_names_count); FREERDP_API BOOL tls_print_error(char* func, SSL* connection, int value); @@ -110,7 +110,7 @@ FREERDP_API rdpTls* tls_new(rdpSettings* settings); FREERDP_API void tls_free(rdpTls* tls); #ifdef __cplusplus - } +} #endif #endif /* FREERDP_CRYPTO_TLS_H */ diff --git a/include/freerdp/settings.h b/include/freerdp/settings.h index bf4e375a2..5970b8f82 100644 --- a/include/freerdp/settings.h +++ b/include/freerdp/settings.h @@ -868,7 +868,9 @@ struct rdp_settings ALIGN64 char* PasswordHash; /* 24 */ ALIGN64 BOOL WaitForOutputBufferFlush; /* 25 */ ALIGN64 UINT32 MaxTimeInCheckLoop; /* 26 */ - UINT64 padding0064[64 - 27]; /* 27 */ + ALIGN64 char* AcceptedCert; /* 27 */ + ALIGN64 UINT32 AcceptedCertLength; /* 28 */ + UINT64 padding0064[64 - 29]; /* 29 */ UINT64 padding0128[128 - 64]; /* 64 */ /** @@ -1073,7 +1075,9 @@ struct rdp_settings ALIGN64 UINT32 TargetNetAddressCount; /* 1228 */ ALIGN64 char** TargetNetAddresses; /* 1229 */ ALIGN64 UINT32* TargetNetPorts; /* 1230 */ - UINT64 padding1280[1280 - 1231]; /* 1231 */ + ALIGN64 char* RedirectionAcceptedCert; /* 1231 */ + ALIGN64 UINT32 RedirectionAcceptedCertLength;/* 1232 */ + UINT64 padding1280[1280 - 1233]; /* 1233 */ /** * Security @@ -1185,7 +1189,9 @@ struct rdp_settings ALIGN64 BOOL GatewayHttpTransport; /* 1995 */ ALIGN64 BOOL GatewayUdpTransport; /* 1996 */ ALIGN64 char* GatewayAccessToken; /* 1997 */ - UINT64 padding2015[2015 - 1998]; /* 1998 */ + ALIGN64 char* GatewayAcceptedCert; /* 1998 */ + ALIGN64 UINT32 GatewayAcceptedCertLength; /* 1999 */ + UINT64 padding2015[2015 - 2000]; /* 2000 */ /* Proxy */ ALIGN64 UINT32 ProxyType; /* 2015 */ @@ -1501,7 +1507,7 @@ FREERDP_API int freerdp_addin_set_argument(ADDIN_ARGV* args, char* argument); FREERDP_API int freerdp_addin_replace_argument(ADDIN_ARGV* args, char* previous, char* argument); FREERDP_API int freerdp_addin_set_argument_value(ADDIN_ARGV* args, char* option, char* value); FREERDP_API int freerdp_addin_replace_argument_value(ADDIN_ARGV* args, char* previous, char* option, - char* value); + char* value); FREERDP_API BOOL freerdp_device_collection_add(rdpSettings* settings, RDPDR_DEVICE* device); FREERDP_API RDPDR_DEVICE* freerdp_device_collection_find(rdpSettings* settings, const char* name); @@ -1511,13 +1517,13 @@ FREERDP_API void freerdp_device_collection_free(rdpSettings* settings); FREERDP_API BOOL freerdp_static_channel_collection_add(rdpSettings* settings, ADDIN_ARGV* channel); FREERDP_API ADDIN_ARGV* freerdp_static_channel_collection_find(rdpSettings* settings, - const char* name); + const char* name); FREERDP_API ADDIN_ARGV* freerdp_static_channel_clone(ADDIN_ARGV* channel); FREERDP_API void freerdp_static_channel_collection_free(rdpSettings* settings); FREERDP_API BOOL freerdp_dynamic_channel_collection_add(rdpSettings* settings, ADDIN_ARGV* channel); FREERDP_API ADDIN_ARGV* freerdp_dynamic_channel_collection_find(rdpSettings* settings, - const char* name); + const char* name); FREERDP_API ADDIN_ARGV* freerdp_dynamic_channel_clone(ADDIN_ARGV* channel); FREERDP_API void freerdp_dynamic_channel_collection_free(rdpSettings* settings); @@ -1528,7 +1534,7 @@ FREERDP_API void freerdp_performance_flags_split(rdpSettings* settings); FREERDP_API void freerdp_set_gateway_usage_method(rdpSettings* settings, UINT32 GatewayUsageMethod); FREERDP_API void freerdp_update_gateway_usage_method(rdpSettings* settings, UINT32 GatewayEnabled, - UINT32 GatewayBypassLocal); + UINT32 GatewayBypassLocal); FREERDP_API BOOL freerdp_get_param_bool(rdpSettings* settings, int id); FREERDP_API int freerdp_set_param_bool(rdpSettings* settings, int id, BOOL param); diff --git a/libfreerdp/core/connection.c b/libfreerdp/core/connection.c index 05a902f0f..d13c0c8df 100644 --- a/libfreerdp/core/connection.c +++ b/libfreerdp/core/connection.c @@ -167,6 +167,8 @@ * and server-side applications). */ +static int rdp_client_connect_finalize(rdpRdp* rdp); + /** * Establish RDP Connection based on the settings given in the 'rdp' parameter. * @msdn{cc240452} @@ -351,6 +353,35 @@ BOOL rdp_client_disconnect_and_clear(rdpRdp* rdp) return TRUE; } +static BOOL rdp_client_reconnect_channels(rdpRdp* rdp) +{ + BOOL status; + rdpContext* context; + rdpChannels* channels; + + if (!rdp || !rdp->context || !rdp->context->channels) + return FALSE; + + context = rdp->context; + channels = context->channels; + + if (context->instance->ConnectionCallbackState == CLIENT_STATE_INITIAL) + return FALSE; + + if (context->instance->ConnectionCallbackState == CLIENT_STATE_PRECONNECT_PASSED) + { + if (!IFCALLRESULT(FALSE, context->instance->PostConnect, context->instance)) + return FALSE; + + context->instance->ConnectionCallbackState = CLIENT_STATE_POSTCONNECT_PASSED; + } + + if (context->instance->ConnectionCallbackState == CLIENT_STATE_POSTCONNECT_PASSED) + status = (freerdp_channels_post_connect(context->channels, context->instance) == CHANNEL_RC_OK); + + return status; +} + BOOL rdp_client_redirect(rdpRdp* rdp) { BOOL status; @@ -424,8 +455,8 @@ BOOL rdp_client_redirect(rdpRdp* rdp) status = rdp_client_connect(rdp); - if (status && (context->instance->ConnectionCallbackState == CLIENT_STATE_POSTCONNECT_PASSED)) - status = (freerdp_channels_post_connect(context->channels, context->instance) == CHANNEL_RC_OK); + if (status) + status = rdp_client_reconnect_channels(rdp); return status; } @@ -447,13 +478,13 @@ BOOL rdp_client_reconnect(rdpRdp* rdp) status = rdp_client_connect(rdp); - if (status && (context->instance->ConnectionCallbackState == CLIENT_STATE_POSTCONNECT_PASSED)) - status = (freerdp_channels_post_connect(channels, context->instance) == CHANNEL_RC_OK); + if (status) + status = rdp_client_reconnect_channels(rdp); return status; } -static BYTE fips_ivec[8] = { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF }; +static const BYTE fips_ivec[8] = { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF }; static BOOL rdp_client_establish_keys(rdpRdp* rdp) { diff --git a/libfreerdp/core/connection.h b/libfreerdp/core/connection.h index 78dbba574..f811c748d 100644 --- a/libfreerdp/core/connection.h +++ b/libfreerdp/core/connection.h @@ -53,8 +53,7 @@ enum CLIENT_CONNECTION_STATE { CLIENT_STATE_INITIAL, CLIENT_STATE_PRECONNECT_PASSED, - CLIENT_STATE_POSTCONNECT_PASSED, - CLIENT_STATE_POSTDISCONNECT_PASSED + CLIENT_STATE_POSTCONNECT_PASSED }; FREERDP_LOCAL BOOL rdp_client_connect(rdpRdp* rdp); @@ -67,7 +66,6 @@ FREERDP_LOCAL BOOL rdp_client_connect_mcs_channel_join_confirm(rdpRdp* rdp, FREERDP_LOCAL BOOL rdp_client_connect_auto_detect(rdpRdp* rdp, wStream* s); FREERDP_LOCAL int rdp_client_connect_license(rdpRdp* rdp, wStream* s); FREERDP_LOCAL int rdp_client_connect_demand_active(rdpRdp* rdp, wStream* s); -FREERDP_LOCAL int rdp_client_connect_finalize(rdpRdp* rdp); FREERDP_LOCAL int rdp_client_transition_to_state(rdpRdp* rdp, int state); FREERDP_LOCAL BOOL rdp_server_accept_nego(rdpRdp* rdp, wStream* s); diff --git a/libfreerdp/core/freerdp.c b/libfreerdp/core/freerdp.c index d6fb99bc8..41a47c897 100644 --- a/libfreerdp/core/freerdp.c +++ b/libfreerdp/core/freerdp.c @@ -197,82 +197,82 @@ BOOL freerdp_connect(freerdp* instance) goto freerdp_connect_finally; } - if (!status) - goto freerdp_connect_finally; + if (instance->settings->DumpRemoteFx) + { + instance->update->pcap_rfx = pcap_open(instance->settings->DumpRemoteFxFile, + TRUE); + + if (instance->update->pcap_rfx) + instance->update->dump_rfx = TRUE; + } if (status) { - UINT status2; - - if (instance->settings->DumpRemoteFx) - { - instance->update->pcap_rfx = pcap_open(instance->settings->DumpRemoteFxFile, - TRUE); - - if (instance->update->pcap_rfx) - instance->update->dump_rfx = TRUE; - } - IFCALLRET(instance->PostConnect, status, instance); instance->ConnectionCallbackState = CLIENT_STATE_POSTCONNECT_PASSED; if (status) status2 = freerdp_channels_post_connect(instance->context->channels, instance); + } + else + { + if (freerdp_get_last_error(instance->context) == FREERDP_ERROR_CONNECT_TRANSPORT_FAILED) + status = freerdp_reconnect(instance); + } - if (!status || (status2 != CHANNEL_RC_OK) - || !update_post_connect(instance->update)) - { - WLog_ERR(TAG, "freerdp_post_connect failed"); + if (!status || (status2 != CHANNEL_RC_OK) + || !update_post_connect(instance->update)) + { + WLog_ERR(TAG, "freerdp_post_connect failed"); - if (!freerdp_get_last_error(rdp->context)) - freerdp_set_last_error(instance->context, FREERDP_ERROR_POST_CONNECT_FAILED); + if (!freerdp_get_last_error(rdp->context)) + freerdp_set_last_error(instance->context, FREERDP_ERROR_POST_CONNECT_FAILED); - status = FALSE; + status = FALSE; + goto freerdp_connect_finally; + } + + if (instance->settings->PlayRemoteFx) + { + wStream* s; + rdpUpdate* update; + pcap_record record; + update = instance->update; + update->pcap_rfx = pcap_open(settings->PlayRemoteFxFile, FALSE); + status = FALSE; + + if (!update->pcap_rfx) goto freerdp_connect_finally; + else + update->play_rfx = TRUE; + + status = TRUE; + + while (pcap_has_next_record(update->pcap_rfx) && status) + { + pcap_get_next_record_header(update->pcap_rfx, &record); + + if (!(s = StreamPool_Take(rdp->transport->ReceivePool, record.length))) + break; + + record.data = Stream_Buffer(s); + pcap_get_next_record_content(update->pcap_rfx, &record); + Stream_SetLength(s, record.length); + Stream_SetPosition(s, 0); + + if (!update->BeginPaint(update->context)) + status = FALSE; + else if (update_recv_surfcmds(update, s) < 0) + status = FALSE; + else if (!update->EndPaint(update->context)) + status = FALSE; + + Stream_Release(s); } - if (instance->settings->PlayRemoteFx) - { - wStream* s; - rdpUpdate* update; - pcap_record record; - update = instance->update; - update->pcap_rfx = pcap_open(settings->PlayRemoteFxFile, FALSE); - status = FALSE; - - if (!update->pcap_rfx) - goto freerdp_connect_finally; - else - update->play_rfx = TRUE; - - status = TRUE; - - while (pcap_has_next_record(update->pcap_rfx) && status) - { - pcap_get_next_record_header(update->pcap_rfx, &record); - - if (!(s = StreamPool_Take(rdp->transport->ReceivePool, record.length))) - break; - - record.data = Stream_Buffer(s); - pcap_get_next_record_content(update->pcap_rfx, &record); - Stream_SetLength(s, record.length); - Stream_SetPosition(s, 0); - - if (!update->BeginPaint(update->context)) - status = FALSE; - else if (update_recv_surfcmds(update, s) < 0) - status = FALSE; - else if (!update->EndPaint(update->context)) - status = FALSE; - - Stream_Release(s); - } - - pcap_close(update->pcap_rfx); - update->pcap_rfx = NULL; - goto freerdp_connect_finally; - } + pcap_close(update->pcap_rfx); + update->pcap_rfx = NULL; + goto freerdp_connect_finally; } if (rdp->errorInfo == ERRINFO_SERVER_INSUFFICIENT_PRIVILEGES) @@ -397,12 +397,22 @@ BOOL freerdp_check_event_handles(rdpContext* context) status = checkChannelErrorEvent(context); if (!status) + { + if (freerdp_get_last_error(context) == FREERDP_ERROR_SUCCESS) + WLog_ERR(TAG, "checkChannelErrorEvent() failed - %"PRIi32"", status); + return FALSE; + } if (context->settings->AsyncInput) { - status = freerdp_message_queue_process_pending_messages( + int rc = freerdp_message_queue_process_pending_messages( context->instance, FREERDP_INPUT_MESSAGE_QUEUE); + + if (rc < 0) + return FALSE; + else + status = TRUE; } return status; @@ -504,7 +514,6 @@ BOOL freerdp_disconnect(freerdp* instance) } IFCALL(instance->PostDisconnect, instance); - instance->ConnectionCallbackState = CLIENT_STATE_POSTDISCONNECT_PASSED; if (instance->update->pcap_rfx) { diff --git a/libfreerdp/core/nego.c b/libfreerdp/core/nego.c index 2bcba6baa..e7211ab10 100644 --- a/libfreerdp/core/nego.c +++ b/libfreerdp/core/nego.c @@ -63,6 +63,12 @@ static const char PROTOCOL_SECURITY_STRINGS[9][4] = static BOOL nego_transport_connect(rdpNego* nego); static BOOL nego_transport_disconnect(rdpNego* nego); static BOOL nego_security_connect(rdpNego* nego); +static BOOL nego_send_preconnection_pdu(rdpNego* nego); +static BOOL nego_recv_response(rdpNego* nego); +static void nego_send(rdpNego* nego); +static void nego_process_negotiation_request(rdpNego* nego, wStream* s); +static void nego_process_negotiation_response(rdpNego* nego, wStream* s); +static void nego_process_negotiation_failure(rdpNego* nego, wStream* s); /** * Negotiate protocol security and connect. @@ -103,7 +109,6 @@ BOOL nego_connect(rdpNego* nego) { WLog_DBG(TAG, "Security Layer Negotiation is disabled"); /* attempt only the highest enabled protocol (see nego_attempt_*) */ - nego->EnabledProtocols[PROTOCOL_NLA] = FALSE; nego->EnabledProtocols[PROTOCOL_TLS] = FALSE; nego->EnabledProtocols[PROTOCOL_RDP] = FALSE; @@ -152,13 +157,13 @@ BOOL nego_connect(rdpNego* nego) do { WLog_DBG(TAG, "state: %s", NEGO_STATE_STRINGS[nego->state]); - nego_send(nego); if (nego->state == NEGO_STATE_FAIL) { if (freerdp_get_last_error(nego->transport->context) == FREERDP_ERROR_SUCCESS) WLog_ERR(TAG, "Protocol Security Negotiation Failure"); + nego->state = NEGO_STATE_FINAL; return FALSE; } @@ -167,7 +172,6 @@ BOOL nego_connect(rdpNego* nego) } WLog_DBG(TAG, "Negotiated %s security", PROTOCOL_SECURITY_STRINGS[nego->SelectedProtocol]); - /* update settings with negotiated protocol security */ settings->RequestedProtocols = nego->RequestedProtocols; settings->SelectedProtocol = nego->SelectedProtocol; @@ -183,14 +187,16 @@ BOOL nego_connect(rdpNego* nego) * Advertise all supported encryption methods if the client * implementation did not set any security methods */ - settings->EncryptionMethods = ENCRYPTION_METHOD_40BIT | ENCRYPTION_METHOD_56BIT | ENCRYPTION_METHOD_128BIT | ENCRYPTION_METHOD_FIPS; + settings->EncryptionMethods = ENCRYPTION_METHOD_40BIT | ENCRYPTION_METHOD_56BIT | + ENCRYPTION_METHOD_128BIT | ENCRYPTION_METHOD_FIPS; } } /* finally connect security layer (if not already done) */ if (!nego_security_connect(nego)) { - WLog_DBG(TAG, "Failed to connect with %s security", PROTOCOL_SECURITY_STRINGS[nego->SelectedProtocol]); + WLog_DBG(TAG, "Failed to connect with %s security", + PROTOCOL_SECURITY_STRINGS[nego->SelectedProtocol]); return FALSE; } @@ -242,7 +248,7 @@ BOOL nego_security_connect(rdpNego* nego) * @return */ -BOOL nego_tcp_connect(rdpNego* nego) +static BOOL nego_tcp_connect(rdpNego* nego) { if (!nego->TcpConnected) { @@ -280,7 +286,8 @@ BOOL nego_tcp_connect(rdpNego* nego) BOOL nego_transport_connect(rdpNego* nego) { - nego_tcp_connect(nego); + if (!nego_tcp_connect(nego)) + return FALSE; if (nego->TcpConnected && !nego->NegotiateSecurityLayer) return nego_security_connect(nego); @@ -301,7 +308,6 @@ BOOL nego_transport_disconnect(rdpNego* nego) nego->TcpConnected = FALSE; nego->SecurityConnected = FALSE; - return TRUE; } @@ -317,7 +323,6 @@ BOOL nego_send_preconnection_pdu(rdpNego* nego) UINT32 cbSize; UINT16 cchPCB = 0; WCHAR* wszPCB = NULL; - WLog_DBG(TAG, "Sending preconnection PDU"); if (!nego_tcp_connect(nego)) @@ -334,6 +339,7 @@ BOOL nego_send_preconnection_pdu(rdpNego* nego) } s = Stream_New(NULL, cbSize); + if (!s) { WLog_ERR(TAG, "Stream_New failed!"); @@ -361,7 +367,6 @@ BOOL nego_send_preconnection_pdu(rdpNego* nego) } Stream_Free(s, TRUE); - return TRUE; } @@ -370,10 +375,9 @@ BOOL nego_send_preconnection_pdu(rdpNego* nego) * @param nego */ -void nego_attempt_ext(rdpNego* nego) +static void nego_attempt_ext(rdpNego* nego) { nego->RequestedProtocols = PROTOCOL_NLA | PROTOCOL_TLS | PROTOCOL_EXT; - WLog_DBG(TAG, "Attempting NLA extended security"); if (!nego_transport_connect(nego)) @@ -416,10 +420,9 @@ void nego_attempt_ext(rdpNego* nego) * @param nego */ -void nego_attempt_nla(rdpNego* nego) +static void nego_attempt_nla(rdpNego* nego) { nego->RequestedProtocols = PROTOCOL_NLA | PROTOCOL_TLS; - WLog_DBG(TAG, "Attempting NLA security"); if (!nego_transport_connect(nego)) @@ -460,10 +463,9 @@ void nego_attempt_nla(rdpNego* nego) * @param nego */ -void nego_attempt_tls(rdpNego* nego) +static void nego_attempt_tls(rdpNego* nego) { nego->RequestedProtocols = PROTOCOL_TLS; - WLog_DBG(TAG, "Attempting TLS security"); if (!nego_transport_connect(nego)) @@ -500,10 +502,9 @@ void nego_attempt_tls(rdpNego* nego) * @param nego */ -void nego_attempt_rdp(rdpNego* nego) +static void nego_attempt_rdp(rdpNego* nego) { nego->RequestedProtocols = PROTOCOL_RDP; - WLog_DBG(TAG, "Attempting RDP security"); if (!nego_transport_connect(nego)) @@ -534,7 +535,6 @@ BOOL nego_recv_response(rdpNego* nego) { int status; wStream* s; - s = Stream_New(NULL, 1024); if (!s) @@ -552,7 +552,6 @@ BOOL nego_recv_response(rdpNego* nego) } status = nego_recv(nego->transport, s, nego); - Stream_Free(s, TRUE); if (status < 0) @@ -588,14 +587,12 @@ int nego_recv(rdpTransport* transport, wStream* s, void* extra) if (li > 6) { /* rdpNegData (optional) */ - Stream_Read_UINT8(s, type); /* Type */ switch (type) { case TYPE_RDP_NEG_RSP: nego_process_negotiation_response(nego, s); - WLog_DBG(TAG, "selected_protocol: %"PRIu32"", nego->SelectedProtocol); /* enhanced security selected ? */ @@ -603,12 +600,13 @@ int nego_recv(rdpTransport* transport, wStream* s, void* extra) if (nego->SelectedProtocol) { if ((nego->SelectedProtocol == PROTOCOL_NLA) && - (!nego->EnabledProtocols[PROTOCOL_NLA])) + (!nego->EnabledProtocols[PROTOCOL_NLA])) { nego->state = NEGO_STATE_FAIL; } + if ((nego->SelectedProtocol == PROTOCOL_TLS) && - (!nego->EnabledProtocols[PROTOCOL_TLS])) + (!nego->EnabledProtocols[PROTOCOL_TLS])) { nego->state = NEGO_STATE_FAIL; } @@ -617,6 +615,7 @@ int nego_recv(rdpTransport* transport, wStream* s, void* extra) { nego->state = NEGO_STATE_FAIL; } + break; case TYPE_RDP_NEG_FAILURE: @@ -663,13 +662,11 @@ static BOOL nego_read_request_token_or_cookie(rdpNego* nego, wStream* s) * string terminated by a 0x0D0A two-byte sequence: * Cookie:[space]mstshash=[ANSISTRING][\x0D\x0A] */ - - BYTE *str = NULL; + BYTE* str = NULL; UINT16 crlf = 0; size_t pos, len; BOOL result = FALSE; BOOL isToken = FALSE; - str = Stream_Pointer(s); pos = Stream_GetPosition(s); @@ -693,8 +690,10 @@ static BOOL nego_read_request_token_or_cookie(rdpNego* nego, wStream* s) while (Stream_GetRemainingLength(s) >= 2) { Stream_Read_UINT16(s, crlf); + if (crlf == 0x0A0D) break; + Stream_Rewind(s, 1); } @@ -703,6 +702,7 @@ static BOOL nego_read_request_token_or_cookie(rdpNego* nego, wStream* s) Stream_Rewind(s, 2); len = Stream_GetPosition(s) - pos; Stream_Write_UINT16(s, 0); + if (strlen((char*)str) == len) { if (isToken) @@ -716,12 +716,12 @@ static BOOL nego_read_request_token_or_cookie(rdpNego* nego, wStream* s) { Stream_SetPosition(s, pos); WLog_ERR(TAG, "invalid %s received", - isToken ? "routing token" : "cookie"); + isToken ? "routing token" : "cookie"); } else { WLog_DBG(TAG, "received %s [%s]", - isToken ? "routing token" : "cookie", str); + isToken ? "routing token" : "cookie", str); } return result; @@ -760,7 +760,6 @@ BOOL nego_read_request(rdpNego* nego, wStream* s) if (Stream_GetRemainingLength(s) >= 8) { /* rdpNegData (optional) */ - Stream_Read_UINT8(s, type); /* Type */ if (type != TYPE_RDP_NEG_REQ) @@ -808,8 +807,8 @@ BOOL nego_send_negotiation_request(rdpNego* nego) size_t bm, em; BYTE flags = 0; int cookie_length; - s = Stream_New(NULL, 512); + if (!s) { WLog_ERR(TAG, "Stream_New failed!"); @@ -827,8 +826,8 @@ BOOL nego_send_negotiation_request(rdpNego* nego) /* Ensure Routing Token is correctly terminated - may already be present in string */ if ((nego->RoutingTokenLength > 2) && - (nego->RoutingToken[nego->RoutingTokenLength - 2] == 0x0D) && - (nego->RoutingToken[nego->RoutingTokenLength - 1] == 0x0A)) + (nego->RoutingToken[nego->RoutingTokenLength - 2] == 0x0D) && + (nego->RoutingToken[nego->RoutingTokenLength - 1] == 0x0A)) { WLog_DBG(TAG, "Routing token looks correctly terminated - use verbatim"); length += nego->RoutingTokenLength; @@ -860,7 +859,6 @@ BOOL nego_send_negotiation_request(rdpNego* nego) if ((nego->RequestedProtocols > PROTOCOL_RDP) || (nego->sendNegoData)) { /* RDP_NEG_DATA must be present for TLS and NLA */ - if (nego->RestrictedAdminModeRequired) flags |= RESTRICTED_ADMIN_MODE_REQUIRED; @@ -876,7 +874,6 @@ BOOL nego_send_negotiation_request(rdpNego* nego) tpkt_write_header(s, length); tpdu_write_connection_request(s, length - 5); Stream_SetPosition(s, em); - Stream_SealLength(s); if (transport_write(nego->transport, s) < 0) @@ -886,7 +883,6 @@ BOOL nego_send_negotiation_request(rdpNego* nego) } Stream_Free(s, TRUE); - return TRUE; } @@ -900,13 +896,10 @@ void nego_process_negotiation_request(rdpNego* nego, wStream* s) { BYTE flags; UINT16 length; - Stream_Read_UINT8(s, flags); Stream_Read_UINT16(s, length); Stream_Read_UINT32(s, nego->RequestedProtocols); - WLog_DBG(TAG, "RDP_NEG_REQ: RequestedProtocol: 0x%08"PRIX32"", nego->RequestedProtocols); - nego->state = NEGO_STATE_FINAL; } @@ -919,7 +912,6 @@ void nego_process_negotiation_request(rdpNego* nego, wStream* s) void nego_process_negotiation_response(rdpNego* nego, wStream* s) { UINT16 length; - WLog_DBG(TAG, "RDP_NEG_RSP"); if (Stream_GetRemainingLength(s) < 7) @@ -932,7 +924,6 @@ void nego_process_negotiation_response(rdpNego* nego, wStream* s) Stream_Read_UINT8(s, nego->flags); Stream_Read_UINT16(s, length); Stream_Read_UINT32(s, nego->SelectedProtocol); - nego->state = NEGO_STATE_FINAL; } @@ -947,9 +938,7 @@ void nego_process_negotiation_failure(rdpNego* nego, wStream* s) BYTE flags; UINT16 length; UINT32 failureCode; - WLog_DBG(TAG, "RDP_NEG_FAILURE"); - Stream_Read_UINT8(s, flags); Stream_Read_UINT16(s, length); Stream_Read_UINT32(s, failureCode); @@ -999,11 +988,10 @@ BOOL nego_send_negotiation_response(rdpNego* nego) wStream* s; BYTE flags; rdpSettings* settings; - status = TRUE; settings = nego->transport->settings; - s = Stream_New(NULL, 512); + if (!s) { WLog_ERR(TAG, "Stream_New failed!"); @@ -1018,11 +1006,9 @@ BOOL nego_send_negotiation_response(rdpNego* nego) { UINT32 errorCode = (nego->SelectedProtocol & ~PROTOCOL_FAILED_NEGO); flags = 0; - Stream_Write_UINT8(s, TYPE_RDP_NEG_FAILURE); Stream_Write_UINT8(s, flags); /* flags */ Stream_Write_UINT16(s, 8); /* RDP_NEG_DATA length (8) */ - Stream_Write_UINT32(s, errorCode); length += 8; status = FALSE; @@ -1047,7 +1033,6 @@ BOOL nego_send_negotiation_response(rdpNego* nego) tpkt_write_header(s, length); tpdu_write_connection_confirm(s, length - 5); Stream_SetPosition(s, em); - Stream_SealLength(s); if (transport_write(nego->transport, s) < 0) @@ -1148,9 +1133,7 @@ rdpNego* nego_new(rdpTransport* transport) return NULL; nego->transport = transport; - nego_init(nego); - return nego; } @@ -1276,8 +1259,10 @@ BOOL nego_set_routing_token(rdpNego* nego, BYTE* RoutingToken, DWORD RoutingToke free(nego->RoutingToken); nego->RoutingTokenLength = RoutingTokenLength; nego->RoutingToken = (BYTE*) malloc(nego->RoutingTokenLength); + if (!nego->RoutingToken) return FALSE; + CopyMemory(nego->RoutingToken, RoutingToken, nego->RoutingTokenLength); return TRUE; } @@ -1300,8 +1285,10 @@ BOOL nego_set_cookie(rdpNego* nego, char* cookie) return TRUE; nego->cookie = _strdup(cookie); + if (!nego->cookie) return FALSE; + return TRUE; } diff --git a/libfreerdp/core/nego.h b/libfreerdp/core/nego.h index c03d68d86..af3696de1 100644 --- a/libfreerdp/core/nego.h +++ b/libfreerdp/core/nego.h @@ -120,22 +120,10 @@ typedef struct rdp_nego rdpNego; FREERDP_LOCAL BOOL nego_connect(rdpNego* nego); FREERDP_LOCAL BOOL nego_disconnect(rdpNego* nego); -FREERDP_LOCAL BOOL nego_send_preconnection_pdu(rdpNego* nego); - -FREERDP_LOCAL void nego_attempt_ext(rdpNego* nego); -FREERDP_LOCAL void nego_attempt_nla(rdpNego* nego); -FREERDP_LOCAL void nego_attempt_tls(rdpNego* nego); -FREERDP_LOCAL void nego_attempt_rdp(rdpNego* nego); - -FREERDP_LOCAL void nego_send(rdpNego* nego); FREERDP_LOCAL int nego_recv(rdpTransport* transport, wStream* s, void* extra); -FREERDP_LOCAL BOOL nego_recv_response(rdpNego* nego); FREERDP_LOCAL BOOL nego_read_request(rdpNego* nego, wStream* s); FREERDP_LOCAL BOOL nego_send_negotiation_request(rdpNego* nego); -FREERDP_LOCAL void nego_process_negotiation_request(rdpNego* nego, wStream* s); -FREERDP_LOCAL void nego_process_negotiation_response(rdpNego* nego, wStream* s); -FREERDP_LOCAL void nego_process_negotiation_failure(rdpNego* nego, wStream* s); FREERDP_LOCAL BOOL nego_send_negotiation_response(rdpNego* nego); FREERDP_LOCAL rdpNego* nego_new(rdpTransport* transport); diff --git a/libfreerdp/core/settings.c b/libfreerdp/core/settings.c index d76fb0ccf..27e50c18d 100644 --- a/libfreerdp/core/settings.c +++ b/libfreerdp/core/settings.c @@ -66,7 +66,7 @@ static const char client_dll[] = "C:\\Windows\\System32\\mstscax.dll"; #define GLYPH_CACHE_KEY CLIENT_KEY "\\GlyphCache" #define POINTER_CACHE_KEY CLIENT_KEY "\\PointerCache" -void settings_client_load_hkey_local_machine(rdpSettings* settings) +static void settings_client_load_hkey_local_machine(rdpSettings* settings) { HKEY hKey; LONG status; @@ -218,7 +218,7 @@ void settings_client_load_hkey_local_machine(rdpSettings* settings) } } -void settings_server_load_hkey_local_machine(rdpSettings* settings) +static void settings_server_load_hkey_local_machine(rdpSettings* settings) { HKEY hKey; LONG status; @@ -242,7 +242,7 @@ void settings_server_load_hkey_local_machine(rdpSettings* settings) RegCloseKey(hKey); } -void settings_load_hkey_local_machine(rdpSettings* settings) +static void settings_load_hkey_local_machine(rdpSettings* settings) { if (settings->ServerMode) settings_server_load_hkey_local_machine(settings); @@ -250,7 +250,7 @@ void settings_load_hkey_local_machine(rdpSettings* settings) settings_client_load_hkey_local_machine(settings); } -BOOL settings_get_computer_name(rdpSettings* settings) +static BOOL settings_get_computer_name(rdpSettings* settings) { DWORD nSize = 0; CHAR* computerName; @@ -653,6 +653,7 @@ rdpSettings* freerdp_settings_clone(rdpSettings* settings) CHECKED_STRDUP(Password); /* 22 */ CHECKED_STRDUP(Domain); /* 23 */ CHECKED_STRDUP(PasswordHash); /* 24 */ + CHECKED_STRDUP(AcceptedCert); /* 27 */ _settings->ClientHostname = NULL; /* 134 */ _settings->ClientProductId = NULL; /* 135 */ CHECKED_STRDUP(AlternateShell); /* 640 */ @@ -668,6 +669,7 @@ rdpSettings* freerdp_settings_clone(rdpSettings* settings) CHECKED_STRDUP(AllowedTlsCiphers); /* 1101 */ CHECKED_STRDUP(NtlmSamFile); /* 1103 */ CHECKED_STRDUP(PreconnectionBlob); /* 1155 */ + CHECKED_STRDUP(RedirectionAcceptedCert); /* 1231 */ CHECKED_STRDUP(KerberosKdc); /* 1344 */ CHECKED_STRDUP(KerberosRealm); /* 1345 */ CHECKED_STRDUP(CertificateName); /* 1409 */ @@ -692,6 +694,7 @@ rdpSettings* freerdp_settings_clone(rdpSettings* settings) CHECKED_STRDUP(GatewayPassword); /* 1988 */ CHECKED_STRDUP(GatewayDomain); /* 1989 */ CHECKED_STRDUP(GatewayAccessToken); /* 1997 */ + CHECKED_STRDUP(GatewayAcceptedCert); /* 1998 */ CHECKED_STRDUP(ProxyHostname); /* 2016 */ CHECKED_STRDUP(RemoteApplicationName); /* 2113 */ CHECKED_STRDUP(RemoteApplicationIcon); /* 2114 */ @@ -772,7 +775,7 @@ rdpSettings* freerdp_settings_clone(rdpSettings* settings) if (_settings->ChannelDefArraySize > 0) { _settings->ChannelDefArray = (CHANNEL_DEF*) calloc(settings->ChannelDefArraySize, - sizeof(CHANNEL_DEF)); + sizeof(CHANNEL_DEF)); if (!_settings->ChannelDefArray) goto out_fail; @@ -789,7 +792,7 @@ rdpSettings* freerdp_settings_clone(rdpSettings* settings) if (_settings->MonitorDefArraySize > 0) { _settings->MonitorDefArray = (rdpMonitor*) calloc(settings->MonitorDefArraySize, - sizeof(rdpMonitor)); + sizeof(rdpMonitor)); if (!_settings->MonitorDefArray) goto out_fail; @@ -1032,6 +1035,7 @@ void freerdp_settings_free(rdpSettings* settings) free(settings->Password); free(settings->Domain); free(settings->PasswordHash); + free(settings->AcceptedCert); free(settings->AlternateShell); free(settings->ShellWorkingDirectory); free(settings->ComputerName); @@ -1076,6 +1080,7 @@ void freerdp_settings_free(rdpSettings* settings) free(settings->RedirectionDomain); free(settings->RedirectionPassword); free(settings->RedirectionTsvUrl); + free(settings->RedirectionAcceptedCert); free(settings->RemoteAssistanceSessionId); free(settings->RemoteAssistancePassword); free(settings->RemoteAssistancePassStub); @@ -1086,6 +1091,7 @@ void freerdp_settings_free(rdpSettings* settings) free(settings->GatewayPassword); free(settings->GatewayDomain); free(settings->GatewayAccessToken); + free(settings->GatewayAcceptedCert); free(settings->CertificateName); free(settings->DynamicDSTTimeZoneKeyName); free(settings->PreconnectionBlob); diff --git a/libfreerdp/crypto/tls.c b/libfreerdp/crypto/tls.c index 42be8e20f..62e6b0857 100644 --- a/libfreerdp/crypto/tls.c +++ b/libfreerdp/crypto/tls.c @@ -1109,6 +1109,175 @@ BOOL tls_match_hostname(char* pattern, int pattern_length, char* hostname) return FALSE; } +static BOOL is_accepted(rdpTls* tls, const BYTE* pem, size_t length) +{ + rdpSettings* settings = tls->settings; + char* AccpetedKey; + UINT32 AcceptedKeyLength; + + if (tls->isGatewayTransport) + { + AccpetedKey = settings->GatewayAcceptedCert; + AcceptedKeyLength = settings->GatewayAcceptedCertLength; + } + else if (settings->RedirectionFlags != 0) + { + AccpetedKey = settings->RedirectionAcceptedCert; + AcceptedKeyLength = settings->RedirectionAcceptedCertLength; + } + else + { + AccpetedKey = settings->AcceptedCert; + AcceptedKeyLength = settings->AcceptedCertLength; + } + + if (AcceptedKeyLength > 0) + { + if (AcceptedKeyLength == length) + { + if (memcmp(AccpetedKey, pem, AcceptedKeyLength) == 0) + return TRUE; + } + } + + if (tls->isGatewayTransport) + { + free(settings->GatewayAcceptedCert); + settings->GatewayAcceptedCert = NULL; + settings->GatewayAcceptedCertLength = 0; + } + else if (settings->RedirectionFlags != 0) + { + free(settings->RedirectionAcceptedCert); + settings->RedirectionAcceptedCert = NULL; + settings->RedirectionAcceptedCertLength = 0; + } + else + { + free(settings->AcceptedCert); + settings->AcceptedCert = NULL; + settings->AcceptedCertLength = 0; + } + + return FALSE; +} + +static BOOL accept_cert(rdpTls* tls, const BYTE* pem, size_t length) +{ + rdpSettings* settings = tls->settings; + + if (tls->isGatewayTransport) + { + settings->GatewayAcceptedCert = pem; + settings->GatewayAcceptedCertLength = length; + } + else if (settings->RedirectionFlags != 0) + { + settings->RedirectionAcceptedCert = pem; + settings->RedirectionAcceptedCertLength = length; + } + else + { + settings->AcceptedCert = pem; + settings->AcceptedCertLength = length; + } + + return TRUE; +} + +static BOOL tls_extract_pem(CryptoCert cert, BYTE** PublicKey, DWORD* PublicKeyLength) +{ + BIO* bio; + int status; + size_t offset; + int length = 0; + BOOL rc = FALSE; + BYTE* pemCert = NULL; + + if (!PublicKey || !PublicKeyLength) + return FALSE; + + *PublicKey = NULL; + *PublicKeyLength = 0; + /** + * Don't manage certificates internally, leave it up entirely to the external client implementation + */ + bio = BIO_new(BIO_s_mem()); + + if (!bio) + { + WLog_ERR(TAG, "BIO_new() failure"); + return FALSE; + } + + status = PEM_write_bio_X509(bio, cert->px509); + + if (status < 0) + { + WLog_ERR(TAG, "PEM_write_bio_X509 failure: %d", status); + goto fail; + } + + offset = 0; + length = 2048; + pemCert = (BYTE*) malloc(length + 1); + + if (!pemCert) + { + WLog_ERR(TAG, "error allocating pemCert"); + goto fail; + } + + status = BIO_read(bio, pemCert, length); + + if (status < 0) + { + WLog_ERR(TAG, "failed to read certificate"); + goto fail; + } + + offset += status; + + while (offset >= length) + { + int new_len; + BYTE* new_cert; + new_len = length * 2; + new_cert = (BYTE*) realloc(pemCert, new_len + 1); + + if (!new_cert) + goto fail; + + length = new_len; + pemCert = new_cert; + status = BIO_read(bio, &pemCert[offset], length); + + if (status < 0) + break; + + offset += status; + } + + if (status < 0) + { + WLog_ERR(TAG, "failed to read certificate"); + goto fail; + } + + length = offset; + pemCert[length] = '\0'; + *PublicKey = pemCert; + *PublicKeyLength = length; + rc = TRUE; +fail: + + if (!rc) + free(pemCert); + + BIO_free(bio); + return rc; +} + int tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, int port) { @@ -1123,83 +1292,23 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, BOOL hostname_match = FALSE; BOOL verification_status = FALSE; rdpCertificateData* certificate_data; + freerdp* instance = (freerdp*) tls->settings->instance; + DWORD length; + BYTE* pemCert; + + if (!tls_extract_pem(cert, &pemCert, &length)) + return -1; + + /* Check, if we already accepted this key. */ + if (is_accepted(tls, pemCert, length)) + { + free(pemCert); + return 1; + } if (tls->settings->ExternalCertificateManagement) { - BIO* bio; - int status; - int length; - int offset; - BYTE* pemCert; - freerdp* instance = (freerdp*) tls->settings->instance; - /** - * Don't manage certificates internally, leave it up entirely to the external client implementation - */ - bio = BIO_new(BIO_s_mem()); - - if (!bio) - { - WLog_ERR(TAG, "BIO_new() failure"); - return -1; - } - - status = PEM_write_bio_X509(bio, cert->px509); - - if (status < 0) - { - WLog_ERR(TAG, "PEM_write_bio_X509 failure: %d", status); - return -1; - } - - offset = 0; - length = 2048; - pemCert = (BYTE*) malloc(length + 1); - - if (!pemCert) - { - WLog_ERR(TAG, "error allocating pemCert"); - return -1; - } - - status = BIO_read(bio, pemCert, length); - - if (status < 0) - { - WLog_ERR(TAG, "failed to read certificate"); - return -1; - } - - offset += status; - - while (offset >= length) - { - int new_len; - BYTE* new_cert; - new_len = length * 2; - new_cert = (BYTE*) realloc(pemCert, new_len + 1); - - if (!new_cert) - return -1; - - length = new_len; - pemCert = new_cert; - status = BIO_read(bio, &pemCert[offset], length); - - if (status < 0) - break; - - offset += status; - } - - if (status < 0) - { - WLog_ERR(TAG, "failed to read certificate"); - return -1; - } - - length = offset; - pemCert[length] = '\0'; - status = -1; + int status = -1; if (instance->VerifyX509Certificate) status = instance->VerifyX509Certificate(instance, pemCert, length, hostname, @@ -1207,8 +1316,12 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, else WLog_ERR(TAG, "No VerifyX509Certificate callback registered!"); - free(pemCert); - BIO_free(bio); + if (status > 0) + { + accept_cert(tls, pemCert, length); + } + else + free(pemCert); if (status < 0) { @@ -1222,10 +1335,16 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, /* ignore certificate verification if user explicitly required it (discouraged) */ if (tls->settings->IgnoreCertificate) + { + free(pemCert); return 1; /* success! */ + } if (!tls->isGatewayTransport && tls->settings->AuthenticationLevel == 0) + { + free(pemCert); return 1; /* success! */ + } /* if user explicitly specified a certificate name, use it instead of the hostname */ if (!tls->isGatewayTransport && tls->settings->CertificateName) @@ -1273,7 +1392,6 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, char* issuer; char* subject; char* fingerprint; - freerdp* instance = (freerdp*) tls->settings->instance; DWORD accept_certificate = 0; issuer = crypto_cert_issuer(cert->px509); subject = crypto_cert_subject(cert->px509); @@ -1384,6 +1502,15 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, crypto_cert_subject_alt_name_free(alt_names_count, alt_names_lengths, alt_names); + if (verification_status > 0) + { + accept_cert(tls, pemCert, length); + } + else + { + free(pemCert); + } + return (verification_status == 0) ? 0 : 1; }