libfreerdp-core: make NLA event-driven

This commit is contained in:
Marc-André Moreau 2015-02-15 16:04:59 -05:00
parent eddfee56a3
commit 9c7b7ab561
9 changed files with 194 additions and 116 deletions

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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;
}