From 9c7b7ab56165cf72ff002ab2e0dccfcedb96e3ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Sun, 15 Feb 2015 16:04:59 -0500 Subject: [PATCH] libfreerdp-core: make NLA event-driven --- libfreerdp/core/connection.c | 22 ++--- libfreerdp/core/connection.h | 29 +++---- libfreerdp/core/mcs.c | 25 ++++++ libfreerdp/core/mcs.h | 2 + libfreerdp/core/nla.c | 154 +++++++++++++++++++++++------------ libfreerdp/core/nla.h | 3 + libfreerdp/core/rdp.c | 18 ++++ libfreerdp/core/rdp.h | 2 + libfreerdp/core/transport.c | 55 +++++-------- 9 files changed, 194 insertions(+), 116 deletions(-) diff --git a/libfreerdp/core/connection.c b/libfreerdp/core/connection.c index b6da323a4..348f32630 100644 --- a/libfreerdp/core/connection.c +++ b/libfreerdp/core/connection.c @@ -289,22 +289,10 @@ BOOL rdp_client_connect(rdpRdp* rdp) rdp->transport->ReceiveExtra = rdp; transport_set_blocking_mode(rdp->transport, FALSE); - rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_CONNECT); - - if (!mcs_send_connect_initial(rdp->mcs)) + if (rdp->state != CONNECTION_STATE_NLA) { - if (!connectErrorCode) - { - connectErrorCode = MCSCONNECTINITIALERROR; - } - - if (!freerdp_get_last_error(rdp->context)) - { - freerdp_set_last_error(rdp->context, FREERDP_ERROR_MCS_CONNECT_INITIAL_ERROR); - } - - WLog_ERR(TAG, "Error: unable to send MCS Connect Initial"); - return FALSE; + if (!mcs_client_begin(rdp->mcs)) + return FALSE; } while (rdp->state != CONNECTION_STATE_ACTIVE) @@ -898,6 +886,10 @@ int rdp_client_transition_to_state(rdpRdp* rdp, int state) rdp->state = CONNECTION_STATE_NEGO; break; + case CONNECTION_STATE_NLA: + rdp->state = CONNECTION_STATE_NLA; + break; + case CONNECTION_STATE_MCS_CONNECT: rdp->state = CONNECTION_STATE_MCS_CONNECT; break; diff --git a/libfreerdp/core/connection.h b/libfreerdp/core/connection.h index 00a0100fe..a326f2fec 100644 --- a/libfreerdp/core/connection.h +++ b/libfreerdp/core/connection.h @@ -31,20 +31,21 @@ enum CONNECTION_STATE { - CONNECTION_STATE_INITIAL = 0, - CONNECTION_STATE_NEGO = 1, - CONNECTION_STATE_MCS_CONNECT = 2, - CONNECTION_STATE_MCS_ERECT_DOMAIN = 3, - CONNECTION_STATE_MCS_ATTACH_USER = 4, - CONNECTION_STATE_MCS_CHANNEL_JOIN = 5, - CONNECTION_STATE_RDP_SECURITY_COMMENCEMENT = 6, - CONNECTION_STATE_SECURE_SETTINGS_EXCHANGE = 7, - CONNECTION_STATE_CONNECT_TIME_AUTO_DETECT = 8, - CONNECTION_STATE_LICENSING = 9, - CONNECTION_STATE_MULTITRANSPORT_BOOTSTRAPPING = 10, - CONNECTION_STATE_CAPABILITIES_EXCHANGE = 11, - CONNECTION_STATE_FINALIZATION = 12, - CONNECTION_STATE_ACTIVE = 13 + CONNECTION_STATE_INITIAL, + CONNECTION_STATE_NEGO, + CONNECTION_STATE_NLA, + CONNECTION_STATE_MCS_CONNECT, + CONNECTION_STATE_MCS_ERECT_DOMAIN, + CONNECTION_STATE_MCS_ATTACH_USER, + CONNECTION_STATE_MCS_CHANNEL_JOIN, + CONNECTION_STATE_RDP_SECURITY_COMMENCEMENT, + CONNECTION_STATE_SECURE_SETTINGS_EXCHANGE, + CONNECTION_STATE_CONNECT_TIME_AUTO_DETECT, + CONNECTION_STATE_LICENSING, + CONNECTION_STATE_MULTITRANSPORT_BOOTSTRAPPING, + CONNECTION_STATE_CAPABILITIES_EXCHANGE, + CONNECTION_STATE_FINALIZATION, + CONNECTION_STATE_ACTIVE }; BOOL rdp_client_connect(rdpRdp* rdp); diff --git a/libfreerdp/core/mcs.c b/libfreerdp/core/mcs.c index 7e1fefdce..d1603b930 100644 --- a/libfreerdp/core/mcs.c +++ b/libfreerdp/core/mcs.c @@ -30,6 +30,7 @@ #include "tpdu.h" #include "tpkt.h" #include "client.h" +#include "connection.h" #define TAG FREERDP_TAG("core") @@ -1049,6 +1050,30 @@ BOOL mcs_send_disconnect_provider_ultimatum(rdpMcs* mcs) return (status < 0) ? FALSE : TRUE; } +BOOL mcs_client_begin(rdpMcs* mcs) +{ + rdpContext* context = mcs->transport->context; + + if (!mcs_send_connect_initial(mcs)) + { + if (!connectErrorCode) + { + connectErrorCode = MCSCONNECTINITIALERROR; + } + + if (!freerdp_get_last_error(context)) + { + freerdp_set_last_error(context, FREERDP_ERROR_MCS_CONNECT_INITIAL_ERROR); + } + + WLog_ERR(TAG, "Error: unable to send MCS Connect Initial"); + return FALSE; + } + + rdp_client_transition_to_state(context->rdp, CONNECTION_STATE_MCS_CONNECT); + return TRUE; +} + /** * Instantiate new MCS module. * @param transport transport diff --git a/libfreerdp/core/mcs.h b/libfreerdp/core/mcs.h index b1c4c4a06..c91721d57 100644 --- a/libfreerdp/core/mcs.h +++ b/libfreerdp/core/mcs.h @@ -186,6 +186,8 @@ BOOL mcs_send_disconnect_provider_ultimatum(rdpMcs* mcs); BOOL mcs_read_domain_mcspdu_header(wStream* s, enum DomainMCSPDU* domainMCSPDU, UINT16* length); void mcs_write_domain_mcspdu_header(wStream* s, enum DomainMCSPDU domainMCSPDU, UINT16 length, BYTE options); +BOOL mcs_client_begin(rdpMcs* mcs); + rdpMcs* mcs_new(rdpTransport* transport); void mcs_free(rdpMcs* mcs); diff --git a/libfreerdp/core/nla.c b/libfreerdp/core/nla.c index bfa00cdc9..b7a1a5b58 100644 --- a/libfreerdp/core/nla.c +++ b/libfreerdp/core/nla.c @@ -251,60 +251,64 @@ int nla_client_init(rdpNla* nla) return 1; } -int nla_client_authenticate(rdpNla* nla) +int nla_client_begin(rdpNla* nla) { if (nla_client_init(nla) < 1) return -1; - if (nla->state == NLA_STATE_INITIAL) + if (nla->state != NLA_STATE_INITIAL) + return -1; + + nla->outputBufferDesc.ulVersion = SECBUFFER_VERSION; + nla->outputBufferDesc.cBuffers = 1; + nla->outputBufferDesc.pBuffers = &nla->outputBuffer; + nla->outputBuffer.BufferType = SECBUFFER_TOKEN; + nla->outputBuffer.cbBuffer = nla->cbMaxToken; + nla->outputBuffer.pvBuffer = malloc(nla->outputBuffer.cbBuffer); + + if (!nla->outputBuffer.pvBuffer) + return -1; + + nla->status = nla->table->InitializeSecurityContext(&nla->credentials, + NULL, nla->ServicePrincipalName, nla->fContextReq, 0, + SECURITY_NATIVE_DREP, NULL, 0, &nla->context, + &nla->outputBufferDesc, &nla->pfContextAttr, &nla->expiration); + + if ((nla->status == SEC_I_COMPLETE_AND_CONTINUE) || (nla->status == SEC_I_COMPLETE_NEEDED)) { - nla->outputBufferDesc.ulVersion = SECBUFFER_VERSION; - nla->outputBufferDesc.cBuffers = 1; - nla->outputBufferDesc.pBuffers = &nla->outputBuffer; - nla->outputBuffer.BufferType = SECBUFFER_TOKEN; - nla->outputBuffer.cbBuffer = nla->cbMaxToken; - nla->outputBuffer.pvBuffer = malloc(nla->outputBuffer.cbBuffer); + if (nla->table->CompleteAuthToken) + nla->table->CompleteAuthToken(&nla->context, &nla->outputBufferDesc); - if (!nla->outputBuffer.pvBuffer) - return -1; - - nla->status = nla->table->InitializeSecurityContext(&nla->credentials, - NULL, nla->ServicePrincipalName, nla->fContextReq, 0, - SECURITY_NATIVE_DREP, NULL, 0, &nla->context, - &nla->outputBufferDesc, &nla->pfContextAttr, &nla->expiration); - - if ((nla->status == SEC_I_COMPLETE_AND_CONTINUE) || (nla->status == SEC_I_COMPLETE_NEEDED)) - { - if (nla->table->CompleteAuthToken) - nla->table->CompleteAuthToken(&nla->context, &nla->outputBufferDesc); - - if (nla->status == SEC_I_COMPLETE_NEEDED) - nla->status = SEC_E_OK; - else if (nla->status == SEC_I_COMPLETE_AND_CONTINUE) - nla->status = SEC_I_CONTINUE_NEEDED; - } - - if (nla->status != SEC_I_CONTINUE_NEEDED) - return -1; - - if (nla->outputBuffer.cbBuffer < 1) - return -1; - - nla->negoToken.pvBuffer = nla->outputBuffer.pvBuffer; - nla->negoToken.cbBuffer = nla->outputBuffer.cbBuffer; - - WLog_DBG(TAG, "Sending Authentication Token"); - winpr_HexDump(TAG, WLOG_DEBUG, nla->negoToken.pvBuffer, nla->negoToken.cbBuffer); - - nla_send(nla); - nla_buffer_free(nla); - - nla->state = NLA_STATE_NEGO_TOKEN; + if (nla->status == SEC_I_COMPLETE_NEEDED) + nla->status = SEC_E_OK; + else if (nla->status == SEC_I_COMPLETE_AND_CONTINUE) + nla->status = SEC_I_CONTINUE_NEEDED; } - if (nla_recv(nla) < 0) + if (nla->status != SEC_I_CONTINUE_NEEDED) return -1; + if (nla->outputBuffer.cbBuffer < 1) + return -1; + + nla->negoToken.pvBuffer = nla->outputBuffer.pvBuffer; + nla->negoToken.cbBuffer = nla->outputBuffer.cbBuffer; + + WLog_DBG(TAG, "Sending Authentication Token"); + winpr_HexDump(TAG, WLOG_DEBUG, nla->negoToken.pvBuffer, nla->negoToken.cbBuffer); + + nla_send(nla); + nla_buffer_free(nla); + + nla->state = NLA_STATE_NEGO_TOKEN; + + return 1; +} + +int nla_client_recv(rdpNla* nla) +{ + int status = -1; + if (nla->state == NLA_STATE_NEGO_TOKEN) { nla->inputBufferDesc.ulVersion = SECBUFFER_VERSION; @@ -369,12 +373,9 @@ int nla_client_authenticate(rdpNla* nla) nla_buffer_free(nla); nla->state = NLA_STATE_PUB_KEY_AUTH; + status = 1; } - - if (nla_recv(nla) < 0) - return -1; - - if (nla->state == NLA_STATE_PUB_KEY_AUTH) + else if (nla->state == NLA_STATE_PUB_KEY_AUTH) { /* Verify Server Public Key Echo */ nla->status = nla_decrypt_public_key_echo(nla); @@ -398,13 +399,47 @@ int nla_client_authenticate(rdpNla* nla) nla_send(nla); nla_buffer_free(nla); - nla->state = NLA_STATE_AUTH_INFO; - - /* Free resources */ nla->table->FreeCredentialsHandle(&nla->credentials); nla->table->FreeContextBuffer(nla->pPackageInfo); + + nla->state = NLA_STATE_AUTH_INFO; + status = 1; } + return status; +} + +int nla_client_authenticate(rdpNla* nla) +{ + wStream* s; + int status; + + s = Stream_New(NULL, 4096); + + if (nla_client_begin(nla) < 1) + return -1; + + while (nla->state < NLA_STATE_AUTH_INFO) + { + Stream_SetPosition(s, 0); + + status = transport_read_pdu(nla->transport, s); + + if (status < 0) + { + WLog_ERR(TAG, "nla_client_authenticate failure"); + Stream_Free(s, TRUE); + return -1; + } + + status = nla_recv_pdu(nla, s); + + if (status < 0) + return -1; + } + + Stream_Free(s, TRUE); + return 1; } @@ -1110,7 +1145,7 @@ void nla_send(rdpNla* nla) Stream_Free(s, TRUE); } -int nla_recv_ts_request(rdpNla* nla, wStream* s) +int nla_decode_ts_request(rdpNla* nla, wStream* s) { int length; UINT32 version; @@ -1175,6 +1210,17 @@ int nla_recv_ts_request(rdpNla* nla, wStream* s) return 1; } +int nla_recv_pdu(rdpNla* nla, wStream* s) +{ + if (nla_decode_ts_request(nla, s) < 1) + return -1; + + if (nla_client_recv(nla) < 1) + return -1; + + return 1; +} + int nla_recv(rdpNla* nla) { wStream* s; @@ -1191,7 +1237,7 @@ int nla_recv(rdpNla* nla) return -1; } - if (nla_recv_ts_request(nla, s) < 1) + if (nla_recv_pdu(nla, s) < 1) return -1; Stream_Free(s, TRUE); diff --git a/libfreerdp/core/nla.h b/libfreerdp/core/nla.h index 6efd62d78..18f413eb8 100644 --- a/libfreerdp/core/nla.h +++ b/libfreerdp/core/nla.h @@ -85,6 +85,9 @@ struct rdp_nla int nla_authenticate(rdpNla* nla); LPTSTR nla_make_spn(const char* ServiceClass, const char* hostname); +int nla_client_begin(rdpNla* nla); +int nla_recv_pdu(rdpNla* nla, wStream* s); + rdpNla* nla_new(freerdp* instance, rdpTransport* transport, rdpSettings* settings); void nla_free(rdpNla* nla); diff --git a/libfreerdp/core/rdp.c b/libfreerdp/core/rdp.c index 9aa5c650c..1ab32b69a 100644 --- a/libfreerdp/core/rdp.c +++ b/libfreerdp/core/rdp.c @@ -1187,6 +1187,23 @@ int rdp_recv_callback(rdpTransport* transport, wStream* s, void* extra) switch (rdp->state) { + case CONNECTION_STATE_NLA: + if (nla_recv_pdu(rdp->nla, s) < 1) + return -1; + + if (rdp->nla->state == NLA_STATE_AUTH_INFO) + { + transport_set_nla_mode(rdp->transport, FALSE); + + nla_free(rdp->nla); + rdp->nla = NULL; + + if (!mcs_client_begin(rdp->mcs)) + return -1; + } + + break; + case CONNECTION_STATE_MCS_CONNECT: if (!mcs_recv_connect_response(rdp->mcs, s)) { @@ -1528,6 +1545,7 @@ void rdp_free(rdpRdp* rdp) fastpath_free(rdp->fastpath); nego_free(rdp->nego); mcs_free(rdp->mcs); + nla_free(rdp->nla); redirection_free(rdp->redirection); autodetect_free(rdp->autodetect); heartbeat_free(rdp->heartbeat); diff --git a/libfreerdp/core/rdp.h b/libfreerdp/core/rdp.h index d1acdf07d..c53273f77 100644 --- a/libfreerdp/core/rdp.h +++ b/libfreerdp/core/rdp.h @@ -25,6 +25,7 @@ #include "config.h" #endif +#include "nla.h" #include "mcs.h" #include "tpkt.h" #include "bulk.h" @@ -132,6 +133,7 @@ struct rdp_rdp int state; freerdp* instance; rdpContext* context; + rdpNla* nla; rdpMcs* mcs; rdpNego* nego; rdpBulk* bulk; diff --git a/libfreerdp/core/transport.c b/libfreerdp/core/transport.c index 282362cbc..deb5865f6 100644 --- a/libfreerdp/core/transport.c +++ b/libfreerdp/core/transport.c @@ -158,62 +158,51 @@ BOOL transport_connect_tls(rdpTransport* transport) BOOL transport_connect_nla(rdpTransport* transport) { - rdpNla* nla; - freerdp* instance; - rdpSettings* settings; - settings = transport->settings; - instance = (freerdp*) settings->instance; + rdpContext* context = transport->context; + rdpSettings* settings = context->settings; + freerdp* instance = context->instance; + rdpRdp* rdp = context->rdp; if (!transport_connect_tls(transport)) return FALSE; - /* Network Level Authentication */ - if (!settings->Authentication) return TRUE; - if (!transport->nla) + rdp->nla = nla_new(instance, transport, settings); + + if (!rdp->nla) + return FALSE; + + transport_set_nla_mode(transport, TRUE); + + if (settings->AuthenticationServiceClass) { - transport->nla = nla_new(instance, transport, settings); + rdp->nla->ServicePrincipalName = + nla_make_spn(settings->AuthenticationServiceClass, settings->ServerHostname); - if (!transport->nla) + if (!rdp->nla->ServicePrincipalName) return FALSE; - - transport_set_nla_mode(transport, TRUE); - - if (settings->AuthenticationServiceClass) - { - transport->nla->ServicePrincipalName = - nla_make_spn(settings->AuthenticationServiceClass, settings->ServerHostname); - - if (!transport->nla->ServicePrincipalName) - return FALSE; - } } - nla = transport->nla; - - if (nla_authenticate(nla) < 0) + if (nla_client_begin(rdp->nla) < 0) { if (!connectErrorCode) connectErrorCode = AUTHENTICATIONERROR; - if (!freerdp_get_last_error(instance->context)) - { - freerdp_set_last_error(instance->context, FREERDP_ERROR_AUTHENTICATION_FAILED); - } + if (!freerdp_get_last_error(context)) + freerdp_set_last_error(context, FREERDP_ERROR_AUTHENTICATION_FAILED); WLog_ERR(TAG, "Authentication failure, check credentials." "If credentials are valid, the NTLMSSP implementation may be to blame."); + transport_set_nla_mode(transport, FALSE); - nla_free(nla); - transport->nla = NULL; + return FALSE; } - transport_set_nla_mode(transport, FALSE); - nla_free(nla); - transport->nla = NULL; + rdp_client_transition_to_state(rdp, CONNECTION_STATE_NLA); + return TRUE; }