From eddfee56a3ebd09d45ecffcfc2a58d7a3e712abd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Sun, 15 Feb 2015 14:54:10 -0500 Subject: [PATCH] libfreerdp-core: prepare client-side NLA for event-driven structure --- libfreerdp/core/connection.c | 43 ++----- libfreerdp/core/connection.h | 2 - libfreerdp/core/nego.c | 5 +- libfreerdp/core/nla.c | 211 +++++++++++++++++++++-------------- libfreerdp/core/nla.h | 11 ++ libfreerdp/core/rdp.c | 79 +++++++------ libfreerdp/core/rdp.h | 3 +- libfreerdp/core/transport.c | 2 +- 8 files changed, 197 insertions(+), 159 deletions(-) diff --git a/libfreerdp/core/connection.c b/libfreerdp/core/connection.c index fd4d8a091..b6da323a4 100644 --- a/libfreerdp/core/connection.c +++ b/libfreerdp/core/connection.c @@ -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; diff --git a/libfreerdp/core/connection.h b/libfreerdp/core/connection.h index 0efe022ac..00a0100fe 100644 --- a/libfreerdp/core/connection.h +++ b/libfreerdp/core/connection.h @@ -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); diff --git a/libfreerdp/core/nego.c b/libfreerdp/core/nego.c index 9c4e117ce..2f6d4d71b 100644 --- a/libfreerdp/core/nego.c +++ b/libfreerdp/core/nego.c @@ -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; diff --git a/libfreerdp/core/nla.c b/libfreerdp/core/nla.c index e4e3e35b5..bfa00cdc9 100644 --- a/libfreerdp/core/nla.c +++ b/libfreerdp/core/nla.c @@ -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; } diff --git a/libfreerdp/core/nla.h b/libfreerdp/core/nla.h index eab3ae1a9..6efd62d78 100644 --- a/libfreerdp/core/nla.h +++ b/libfreerdp/core/nla.h @@ -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; diff --git a/libfreerdp/core/rdp.c b/libfreerdp/core/rdp.c index bf1e1112c..9aa5c650c 100644 --- a/libfreerdp/core/rdp.c +++ b/libfreerdp/core/rdp.c @@ -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; diff --git a/libfreerdp/core/rdp.h b/libfreerdp/core/rdp.h index fb8531b01..d1acdf07d 100644 --- a/libfreerdp/core/rdp.h +++ b/libfreerdp/core/rdp.h @@ -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); diff --git a/libfreerdp/core/transport.c b/libfreerdp/core/transport.c index 25e3efd3f..282362cbc 100644 --- a/libfreerdp/core/transport.c +++ b/libfreerdp/core/transport.c @@ -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 */