libfreerdp-core: prepare client-side NLA for event-driven structure

This commit is contained in:
Marc-André Moreau 2015-02-15 14:54:10 -05:00
parent ab5fdcc3f1
commit eddfee56a3
8 changed files with 197 additions and 159 deletions

View File

@ -263,6 +263,8 @@ BOOL rdp_client_connect(rdpRdp* rdp)
return FALSE;
}
rdp_client_transition_to_state(rdp, CONNECTION_STATE_NEGO);
if (!nego_connect(rdp->nego))
{
if (!freerdp_get_last_error(rdp->context))
@ -281,10 +283,13 @@ BOOL rdp_client_connect(rdpRdp* rdp)
settings->AutoLogonEnabled = TRUE;
}
rdp_set_blocking_mode(rdp, FALSE);
/* everything beyond this point is event-driven and non blocking */
rdp_client_transition_to_state(rdp, CONNECTION_STATE_NEGO);
rdp->finalize_sc_pdus = 0;
rdp->transport->ReceiveCallback = rdp_recv_callback;
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))
{
@ -648,38 +653,6 @@ end2:
return ret;
}
BOOL rdp_client_connect_mcs_connect_response(rdpRdp* rdp, wStream* s)
{
if (!mcs_recv_connect_response(rdp->mcs, s))
{
WLog_ERR(TAG, "rdp_client_connect_mcs_connect_response: mcs_recv_connect_response failed");
return FALSE;
}
if (!mcs_send_erect_domain_request(rdp->mcs))
return FALSE;
if (!mcs_send_attach_user_request(rdp->mcs))
return FALSE;
rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_ATTACH_USER);
return TRUE;
}
BOOL rdp_client_connect_mcs_attach_user_confirm(rdpRdp* rdp, wStream* s)
{
if (!mcs_recv_attach_user_confirm(rdp->mcs, s))
return FALSE;
if (!mcs_send_channel_join_request(rdp->mcs, rdp->mcs->userId))
return FALSE;
rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_CHANNEL_JOIN);
return TRUE;
}
BOOL rdp_client_connect_mcs_channel_join_confirm(rdpRdp* rdp, wStream* s)
{
UINT32 i;

View File

@ -51,8 +51,6 @@ BOOL rdp_client_connect(rdpRdp* rdp);
BOOL rdp_client_disconnect(rdpRdp* rdp);
BOOL rdp_client_reconnect(rdpRdp* rdp);
BOOL rdp_client_redirect(rdpRdp* rdp);
BOOL rdp_client_connect_mcs_connect_response(rdpRdp* rdp, wStream* s);
BOOL rdp_client_connect_mcs_attach_user_confirm(rdpRdp* rdp, wStream* s);
BOOL rdp_client_connect_mcs_channel_join_confirm(rdpRdp* rdp, wStream* s);
BOOL rdp_client_connect_auto_detect(rdpRdp* rdp, wStream* s);
int rdp_client_connect_license(rdpRdp* rdp, wStream* s);

View File

@ -1033,8 +1033,6 @@ void nego_init(rdpNego* nego)
{
nego->state = NEGO_STATE_INITIAL;
nego->RequestedProtocols = PROTOCOL_RDP;
nego->transport->ReceiveCallback = nego_recv;
nego->transport->ReceiveExtra = (void*) nego;
nego->CookieMaxLength = DEFAULT_COOKIE_MAX_LENGTH;
nego->sendNegoData = FALSE;
nego->flags = 0;
@ -1049,11 +1047,12 @@ void nego_init(rdpNego* nego)
rdpNego* nego_new(rdpTransport* transport)
{
rdpNego* nego = (rdpNego*) calloc(1, sizeof(rdpNego));
if (!nego)
return NULL;
nego->transport = transport;
nego_init(nego);
return nego;

View File

@ -105,7 +105,7 @@ SECURITY_STATUS nla_decrypt_ts_credentials(rdpNla* nla);
* @param credssp
*/
int nla_ntlm_client_init(rdpNla* nla)
int nla_client_init(rdpNla* nla)
{
char* spn;
int length;
@ -117,6 +117,8 @@ int nla_ntlm_client_init(rdpNla* nla)
settings = nla->settings;
instance = (freerdp*) settings->instance;
nla->state = NLA_STATE_INITIAL;
if (settings->RestrictedAdminModeRequired)
settings->DisableCredentialsDelegation = TRUE;
@ -251,10 +253,10 @@ int nla_ntlm_client_init(rdpNla* nla)
int nla_client_authenticate(rdpNla* nla)
{
if (nla_ntlm_client_init(nla) < 1)
if (nla_client_init(nla) < 1)
return -1;
while (TRUE)
if (nla->state == NLA_STATE_INITIAL)
{
nla->outputBufferDesc.ulVersion = SECBUFFER_VERSION;
nla->outputBufferDesc.cBuffers = 1;
@ -267,17 +269,69 @@ int nla_client_authenticate(rdpNla* nla)
return -1;
nla->status = nla->table->InitializeSecurityContext(&nla->credentials,
(nla->haveContext) ? &nla->context : NULL,
nla->ServicePrincipalName, nla->fContextReq, 0,
SECURITY_NATIVE_DREP, (nla->haveInputBuffer) ? &nla->inputBufferDesc : NULL,
0, &nla->context, &nla->outputBufferDesc, &nla->pfContextAttr, &nla->expiration);
NULL, nla->ServicePrincipalName, nla->fContextReq, 0,
SECURITY_NATIVE_DREP, NULL, 0, &nla->context,
&nla->outputBufferDesc, &nla->pfContextAttr, &nla->expiration);
if (nla->haveInputBuffer && (nla->inputBuffer.pvBuffer))
if ((nla->status == SEC_I_COMPLETE_AND_CONTINUE) || (nla->status == SEC_I_COMPLETE_NEEDED))
{
free(nla->inputBuffer.pvBuffer);
nla->inputBuffer.pvBuffer = NULL;
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_recv(nla) < 0)
return -1;
if (nla->state == NLA_STATE_NEGO_TOKEN)
{
nla->inputBufferDesc.ulVersion = SECBUFFER_VERSION;
nla->inputBufferDesc.cBuffers = 1;
nla->inputBufferDesc.pBuffers = &nla->inputBuffer;
nla->inputBuffer.BufferType = SECBUFFER_TOKEN;
nla->inputBuffer.pvBuffer = nla->negoToken.pvBuffer;
nla->inputBuffer.cbBuffer = nla->negoToken.cbBuffer;
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,
&nla->context, nla->ServicePrincipalName, nla->fContextReq, 0,
SECURITY_NATIVE_DREP, &nla->inputBufferDesc,
0, &nla->context, &nla->outputBufferDesc, &nla->pfContextAttr, &nla->expiration);
free(nla->inputBuffer.pvBuffer);
nla->inputBuffer.pvBuffer = NULL;
if ((nla->status == SEC_I_COMPLETE_AND_CONTINUE) || (nla->status == SEC_I_COMPLETE_NEEDED))
{
if (nla->table->CompleteAuthToken)
@ -302,71 +356,55 @@ int nla_client_authenticate(rdpNla* nla)
nla_encrypt_public_key_echo(nla);
}
/* send authentication token to server */
if (nla->outputBuffer.cbBuffer > 0)
{
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);
}
if (nla->status != SEC_I_CONTINUE_NEEDED)
break;
/* receive server response and place in input buffer */
nla->inputBufferDesc.ulVersion = SECBUFFER_VERSION;
nla->inputBufferDesc.cBuffers = 1;
nla->inputBufferDesc.pBuffers = &nla->inputBuffer;
nla->inputBuffer.BufferType = SECBUFFER_TOKEN;
if (nla_recv(nla) < 0)
if (nla->outputBuffer.cbBuffer < 1)
return -1;
WLog_DBG(TAG, "Receiving Authentication Token (%d)", (int) nla->negoToken.cbBuffer);
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->inputBuffer.pvBuffer = nla->negoToken.pvBuffer;
nla->inputBuffer.cbBuffer = nla->negoToken.cbBuffer;
nla->haveInputBuffer = TRUE;
nla->haveContext = TRUE;
nla_send(nla);
nla_buffer_free(nla);
nla->state = NLA_STATE_PUB_KEY_AUTH;
}
/* Encrypted Public Key +1 */
if (nla_recv(nla) < 0)
return -1;
/* Verify Server Public Key Echo */
nla->status = nla_decrypt_public_key_echo(nla);
nla_buffer_free(nla);
if (nla->status != SEC_E_OK)
if (nla->state == NLA_STATE_PUB_KEY_AUTH)
{
WLog_ERR(TAG, "Could not verify public key echo!");
return -1;
/* Verify Server Public Key Echo */
nla->status = nla_decrypt_public_key_echo(nla);
nla_buffer_free(nla);
if (nla->status != SEC_E_OK)
{
WLog_ERR(TAG, "Could not verify public key echo!");
return -1;
}
/* Send encrypted credentials */
nla->status = nla_encrypt_ts_credentials(nla);
if (nla->status != SEC_E_OK)
{
WLog_ERR(TAG, "nla_encrypt_ts_credentials status: 0x%08X", nla->status);
return -1;
}
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);
}
/* Send encrypted credentials */
nla->status = nla_encrypt_ts_credentials(nla);
if (nla->status != SEC_E_OK)
{
WLog_ERR(TAG, "nla_encrypt_ts_credentials status: 0x%08X", nla->status);
return -1;
}
nla_send(nla);
nla_buffer_free(nla);
/* Free resources */
nla->table->FreeCredentialsHandle(&nla->credentials);
nla->table->FreeContextBuffer(nla->pPackageInfo);
return 1;
}
@ -375,7 +413,7 @@ int nla_client_authenticate(rdpNla* nla)
* @param credssp
*/
int nla_ntlm_server_init(rdpNla* nla)
int nla_server_init(rdpNla* nla)
{
rdpTls* tls = nla->transport->tls;
@ -460,7 +498,7 @@ int nla_ntlm_server_init(rdpNla* nla)
int nla_server_authenticate(rdpNla* nla)
{
if (nla_ntlm_server_init(nla) < 1)
if (nla_server_init(nla) < 1)
return -1;
while (TRUE)
@ -1072,30 +1110,11 @@ void nla_send(rdpNla* nla)
Stream_Free(s, TRUE);
}
/**
* Receive CredSSP message.
* @param credssp
* @return
*/
int nla_recv(rdpNla* nla)
int nla_recv_ts_request(rdpNla* nla, wStream* s)
{
wStream* s;
int length;
int status;
UINT32 version;
s = Stream_New(NULL, 4096);
status = transport_read_pdu(nla->transport, s);
if (status < 0)
{
WLog_ERR(TAG, "nla_recv() error: %d", status);
Stream_Free(s, TRUE);
return -1;
}
/* TSRequest */
if (!ber_read_sequence_tag(s, &length) ||
!ber_read_contextual_tag(s, 0, &length, TRUE) ||
@ -1153,6 +1172,28 @@ int nla_recv(rdpNla* nla)
nla->pubKeyAuth.cbBuffer = length;
}
return 1;
}
int nla_recv(rdpNla* nla)
{
wStream* s;
int status;
s = Stream_New(NULL, 4096);
status = transport_read_pdu(nla->transport, s);
if (status < 0)
{
WLog_ERR(TAG, "nla_recv() error: %d", status);
Stream_Free(s, TRUE);
return -1;
}
if (nla_recv_ts_request(nla, s) < 1)
return -1;
Stream_Free(s, TRUE);
return 1;
}

View File

@ -35,9 +35,20 @@ typedef struct rdp_nla rdpNla;
#include "transport.h"
enum _NLA_STATE
{
NLA_STATE_INITIAL,
NLA_STATE_NEGO_TOKEN,
NLA_STATE_PUB_KEY_AUTH,
NLA_STATE_AUTH_INFO,
NLA_STATE_FINAL
};
typedef enum _NLA_STATE NLA_STATE;
struct rdp_nla
{
BOOL server;
NLA_STATE state;
int sendSeqNum;
int recvSeqNum;
freerdp* instance;

View File

@ -361,7 +361,7 @@ BOOL rdp_read_header(rdpRdp* rdp, wStream* s, UINT16* length, UINT16* channelId)
rdp_set_error_info(rdp, ERRINFO_RPC_INITIATED_DISCONNECT);
}
WLog_ERR(TAG, "DisconnectProviderUltimatum: reason: %d", reason);
WLog_ERR(TAG, "DisconnectProviderUltimatum: reason: %d", reason);
rdp->disconnect = TRUE;
EventArgsInit(&e, "freerdp");
@ -748,7 +748,7 @@ int rdp_recv_data_pdu(rdpRdp* rdp, wStream* s)
if (Stream_GetRemainingLength(s) < (size_t) SrcSize)
{
WLog_ERR(TAG, "bulk_decompress: not enough bytes for compressedLength %d", compressedLength);
WLog_ERR(TAG, "bulk_decompress: not enough bytes for compressedLength %d", compressedLength);
return -1;
}
@ -763,7 +763,7 @@ int rdp_recv_data_pdu(rdpRdp* rdp, wStream* s)
}
else
{
WLog_ERR(TAG, "bulk_decompress() failed");
WLog_ERR(TAG, "bulk_decompress() failed");
return -1;
}
@ -970,13 +970,13 @@ BOOL rdp_decrypt(rdpRdp* rdp, wStream* s, int length, UINT16 securityFlags)
if (!security_fips_decrypt(Stream_Pointer(s), length, rdp))
{
WLog_ERR(TAG, "FATAL: cannot decrypt");
WLog_ERR(TAG, "FATAL: cannot decrypt");
return FALSE; /* TODO */
}
if (!security_fips_check_signature(Stream_Pointer(s), length - pad, sig, rdp))
{
WLog_ERR(TAG, "FATAL: invalid packet signature");
WLog_ERR(TAG, "FATAL: invalid packet signature");
return FALSE; /* TODO */
}
@ -1000,7 +1000,7 @@ BOOL rdp_decrypt(rdpRdp* rdp, wStream* s, int length, UINT16 securityFlags)
if (memcmp(wmac, cmac, sizeof(wmac)) != 0)
{
WLog_ERR(TAG, "WARNING: invalid packet signature");
WLog_ERR(TAG, "WARNING: invalid packet signature");
/*
* Because Standard RDP Security is totally broken,
* and cannot protect against MITM, don't treat signature
@ -1032,7 +1032,7 @@ static int rdp_recv_tpkt_pdu(rdpRdp* rdp, wStream* s)
if (!rdp_read_header(rdp, s, &length, &channelId))
{
WLog_ERR(TAG, "Incorrect RDP header.");
WLog_ERR(TAG, "Incorrect RDP header.");
return -1;
}
@ -1053,7 +1053,7 @@ static int rdp_recv_tpkt_pdu(rdpRdp* rdp, wStream* s)
{
if (!rdp_decrypt(rdp, s, length - 4, securityFlags))
{
WLog_ERR(TAG, "rdp_decrypt failed");
WLog_ERR(TAG, "rdp_decrypt failed");
return -1;
}
}
@ -1088,7 +1088,7 @@ static int rdp_recv_tpkt_pdu(rdpRdp* rdp, wStream* s)
case PDU_TYPE_DATA:
if (rdp_recv_data_pdu(rdp, s) < 0)
{
WLog_ERR(TAG, "rdp_recv_data_pdu failed");
WLog_ERR(TAG, "rdp_recv_data_pdu failed");
return -1;
}
break;
@ -1108,7 +1108,7 @@ static int rdp_recv_tpkt_pdu(rdpRdp* rdp, wStream* s)
break;
default:
WLog_ERR(TAG, "incorrect PDU type: 0x%04X", pduType);
WLog_ERR(TAG, "incorrect PDU type: 0x%04X", pduType);
break;
}
@ -1140,7 +1140,7 @@ static int rdp_recv_fastpath_pdu(rdpRdp* rdp, wStream* s)
if ((length == 0) || (length > Stream_GetRemainingLength(s)))
{
WLog_ERR(TAG, "incorrect FastPath PDU header length %d", length);
WLog_ERR(TAG, "incorrect FastPath PDU header length %d", length);
return -1;
}
@ -1168,7 +1168,7 @@ static int rdp_recv_pdu(rdpRdp* rdp, wStream* s)
return rdp_recv_fastpath_pdu(rdp, s);
}
static int rdp_recv_callback(rdpTransport* transport, wStream* s, void* extra)
int rdp_recv_callback(rdpTransport* transport, wStream* s, void* extra)
{
int status = 0;
rdpRdp* rdp = (rdpRdp*) extra;
@ -1179,8 +1179,7 @@ static int rdp_recv_callback(rdpTransport* transport, wStream* s, void* extra)
* enters the active state, an auto-detect PDU can be received
* on the MCS message channel.
*/
if ((rdp->state > CONNECTION_STATE_MCS_CHANNEL_JOIN) &&
(rdp->state < CONNECTION_STATE_ACTIVE))
if ((rdp->state > CONNECTION_STATE_MCS_CHANNEL_JOIN) && (rdp->state < CONNECTION_STATE_ACTIVE))
{
if (rdp_client_connect_auto_detect(rdp, s))
return 0;
@ -1188,14 +1187,42 @@ static int rdp_recv_callback(rdpTransport* transport, wStream* s, void* extra)
switch (rdp->state)
{
case CONNECTION_STATE_NEGO:
if (!rdp_client_connect_mcs_connect_response(rdp, s))
status = -1;
case CONNECTION_STATE_MCS_CONNECT:
if (!mcs_recv_connect_response(rdp->mcs, s))
{
WLog_ERR(TAG, "mcs_recv_connect_response failure");
return -1;
}
if (!mcs_send_erect_domain_request(rdp->mcs))
{
WLog_ERR(TAG, "mcs_send_erect_domain_request failure");
return -1;
}
if (!mcs_send_attach_user_request(rdp->mcs))
{
WLog_ERR(TAG, "mcs_send_attach_user_request failure");
return -1;
}
rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_ATTACH_USER);
break;
case CONNECTION_STATE_MCS_ATTACH_USER:
if (!rdp_client_connect_mcs_attach_user_confirm(rdp, s))
status = -1;
if (!mcs_recv_attach_user_confirm(rdp->mcs, s))
{
WLog_ERR(TAG, "mcs_recv_attach_user_confirm failure");
return -1;
}
if (!mcs_send_channel_join_request(rdp->mcs, rdp->mcs->userId))
{
WLog_ERR(TAG, "mcs_send_channel_join_request failure");
return -1;
}
rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_CHANNEL_JOIN);
break;
case CONNECTION_STATE_MCS_CHANNEL_JOIN:
@ -1226,7 +1253,7 @@ static int rdp_recv_callback(rdpTransport* transport, wStream* s, void* extra)
break;
default:
WLog_ERR(TAG, "Invalid state %d", rdp->state);
WLog_ERR(TAG, "Invalid state %d", rdp->state);
status = -1;
break;
}
@ -1256,18 +1283,6 @@ BOOL rdp_send_error_info(rdpRdp* rdp)
return status;
}
/**
* Set non-blocking mode information.
* @param rdp RDP module
* @param blocking blocking mode
*/
void rdp_set_blocking_mode(rdpRdp* rdp, BOOL blocking)
{
rdp->transport->ReceiveCallback = rdp_recv_callback;
rdp->transport->ReceiveExtra = rdp;
transport_set_blocking_mode(rdp->transport, blocking);
}
int rdp_check_fds(rdpRdp* rdp)
{
int status;

View File

@ -213,7 +213,8 @@ int rdp_recv_out_of_sequence_pdu(rdpRdp* rdp, wStream* s);
void rdp_read_flow_control_pdu(wStream* s, UINT16* type);
void rdp_set_blocking_mode(rdpRdp* rdp, BOOL blocking);
int rdp_recv_callback(rdpTransport* transport, wStream* s, void* extra);
int rdp_check_fds(rdpRdp* rdp);
rdpRdp* rdp_new(rdpContext* context);

View File

@ -435,7 +435,7 @@ int transport_read_pdu(rdpTransport* transport, wStream* s)
/* Make sure there is enough space for the longest header within the stream */
Stream_EnsureCapacity(s, 4);
/* Make sure at least two bytes are read for futher processing */
/* Make sure at least two bytes are read for further processing */
if (position < 2 && (status = transport_read_layer_bytes(transport, s, 2 - position)) != 1)
{
/* No data available at the moment */