From 460fef545d8df2b9473c800ea082ce2472e56a8d Mon Sep 17 00:00:00 2001 From: akallabeth Date: Fri, 16 Apr 2021 15:48:09 +0200 Subject: [PATCH] Refactored NLA * Simplified client/server state machine * Encapsulated steps in functions * Added proper debug logging so that state changes are easy to follow --- libfreerdp/core/nla.c | 1785 ++++++++++++++++++++--------------------- libfreerdp/core/nla.h | 3 + 2 files changed, 890 insertions(+), 898 deletions(-) diff --git a/libfreerdp/core/nla.c b/libfreerdp/core/nla.c index 16aeae0f5..ec66d86ce 100644 --- a/libfreerdp/core/nla.c +++ b/libfreerdp/core/nla.c @@ -27,9 +27,7 @@ #include -#ifndef _WIN32 -#include -#endif +#include #include #include @@ -102,8 +100,8 @@ struct rdp_nla { BOOL server; NLA_STATE state; - int sendSeqNum; - int recvSeqNum; + ULONG sendSeqNum; + ULONG recvSeqNum; freerdp* instance; CtxtHandle context; LPTSTR SspiModule; @@ -111,11 +109,7 @@ struct rdp_nla rdpSettings* settings; rdpTransport* transport; UINT32 cbMaxToken; -#if defined(UNICODE) - SEC_WCHAR* packageName; -#else - SEC_CHAR* packageName; -#endif + CHAR* packageName; UINT32 version; UINT32 peerVersion; UINT32 errorCode; @@ -127,26 +121,25 @@ struct rdp_nla SECURITY_STATUS status; CredHandle credentials; TimeStamp expiration; - PSecPkgInfo pPackageInfo; - SecBuffer inputBuffer; - SecBuffer outputBuffer; - SecBufferDesc inputBufferDesc; - SecBufferDesc outputBufferDesc; + + /* Lifetime of buffer nla_new -> nla_free */ + SecBuffer ClientNonce; /* Depending on protocol version a random nonce or a value read from the + server. */ + SecBuffer negoToken; SecBuffer pubKeyAuth; SecBuffer authInfo; - SecBuffer ClientNonce; SecBuffer PublicKey; SecBuffer tsCredentials; + LPTSTR ServicePrincipalName; SEC_WINNT_AUTH_IDENTITY* identity; PSecurityFunctionTable table; SecPkgContext_Sizes ContextSizes; }; -static BOOL nla_send(rdpNla* nla, const char* msg); -static int nla_recv(rdpNla* nla, const char* msg); -static void nla_buffer_print(rdpNla* nla); +static BOOL nla_send(rdpNla* nla); +static int nla_server_recv(rdpNla* nla); static void nla_buffer_free(rdpNla* nla); static SECURITY_STATUS nla_encrypt_public_key_echo(rdpNla* nla); static SECURITY_STATUS nla_encrypt_public_key_hash(rdpNla* nla); @@ -157,15 +150,331 @@ static SECURITY_STATUS nla_decrypt_ts_credentials(rdpNla* nla); static BOOL nla_read_ts_password_creds(rdpNla* nla, wStream* s); static void nla_identity_free(SEC_WINNT_AUTH_IDENTITY* identity); -#define ber_sizeof_sequence_octet_string(length) \ - ber_sizeof_contextual_tag(ber_sizeof_octet_string(length)) + ber_sizeof_octet_string(length) - -static INLINE size_t ber_write_sequence_octet_string(wStream* stream, BYTE context, - const BYTE* value, size_t length) +static BOOL nla_Digest_Update_From_SecBuffer(WINPR_DIGEST_CTX* ctx, const SecBuffer* buffer) { - size_t rv = ber_write_contextual_tag(stream, context, ber_sizeof_octet_string(length), TRUE); - rv += ber_write_octet_string(stream, value, length); - return rv; + if (!buffer) + return FALSE; + return winpr_Digest_Update(ctx, buffer->pvBuffer, buffer->cbBuffer); +} + +static BOOL nla_sec_buffer_alloc(SecBuffer* buffer, size_t size) +{ + sspi_SecBufferFree(buffer); + if (!sspi_SecBufferAlloc(buffer, size)) + return FALSE; + buffer->BufferType = SECBUFFER_TOKEN; + return TRUE; +} + +static BOOL nla_sec_buffer_alloc_from_data(SecBuffer* buffer, const BYTE* data, size_t offset, + size_t size) +{ + BYTE* pb; + if (!nla_sec_buffer_alloc(buffer, offset + size)) + return FALSE; + pb = buffer->pvBuffer; + memcpy(&pb[offset], data, size); + return TRUE; +} + +static BOOL nla_sec_buffer_alloc_from_buffer(SecBuffer* buffer, const SecBuffer* data, + size_t offset) +{ + return nla_sec_buffer_alloc_from_data(buffer, data->pvBuffer, offset, data->cbBuffer); +} + +static BOOL nla_decode_to_buffer(wStream* s, SecBuffer* buffer) +{ + BOOL rc = FALSE; + size_t length; + if (!s || !buffer) + return FALSE; + if (!ber_read_octet_string_tag(s, &length) || /* OCTET STRING */ + Stream_GetRemainingLength(s) < length) + return FALSE; + + rc = nla_sec_buffer_alloc_from_data(buffer, Stream_Pointer(s), 0, length); + Stream_Seek(s, length); + return rc; +} + +static BOOL nla_set_package_name(rdpNla* nla, const TCHAR* name) +{ + if (!nla) + return FALSE; + free(nla->packageName); + nla->packageName = NULL; + if (name) + { +#if defined(UNICODE) + ConvertFromUnicode(CP_UTF8, 0, name, -1, &nla->packageName, 0, NULL, NULL); +#else + nla->packageName = _strdup(name); +#endif + if (!nla->packageName) + return FALSE; + } + return TRUE; +} + +static SECURITY_STATUS nla_update_package_name(rdpNla* nla) +{ + BOOL rc; + SECURITY_STATUS status; + PSecPkgInfo pPackageInfo; + + if (!nla || !nla->table) + { + WLog_ERR(TAG, "[%s] nla->table=%p->%p", __FUNCTION__, nla, nla ? nla->table : NULL); + return ERROR_INTERNAL_ERROR; + } + + if (!nla->table->QuerySecurityPackageInfo || !nla->table->FreeContextBuffer) + { + WLog_ERR(TAG, "[%s] QuerySecurityPackageInfo=%p, FreeContextBuffer=%p", __FUNCTION__, + nla->table->QuerySecurityPackageInfo, nla->table->FreeContextBuffer); + return ERROR_INTERNAL_ERROR; + } + status = nla->table->QuerySecurityPackageInfo(NLA_PKG_NAME, &pPackageInfo); + + if (status != SEC_E_OK) + { + WLog_ERR(TAG, "QuerySecurityPackageInfo status %s [0x%08" PRIX32 "]", + GetSecurityStatusString(status), status); + return status; + } + + nla->cbMaxToken = pPackageInfo->cbMaxToken; + rc = nla_set_package_name(nla, pPackageInfo->Name); + status = nla->table->FreeContextBuffer(pPackageInfo); + if (status != SEC_E_OK) + { + WLog_ERR(TAG, "FreeContextBuffer status %s [0x%08" PRIX32 "]", + GetSecurityStatusString(status), status); + return status; + } + if (!rc) + return ERROR_WINS_INTERNAL; + + return status; +} + +static SECURITY_STATUS nla_query_context_sizes(rdpNla* nla) +{ + SECURITY_STATUS status; + + if (!nla || !nla->table) + { + WLog_ERR(TAG, "[%s] nla->table=%p->%p", __FUNCTION__, nla, nla ? nla->table : NULL); + return SEC_E_INVALID_PARAMETER; + } + if (!nla->table->QueryContextAttributes) + { + WLog_ERR(TAG, "[%s] QueryContextAttributes=%p", __FUNCTION__, + nla->table->QuerySecurityPackageInfo); + return ERROR_INTERNAL_ERROR; + } + status = + nla->table->QueryContextAttributes(&nla->context, SECPKG_ATTR_SIZES, &nla->ContextSizes); + + if (status != SEC_E_OK) + { + WLog_ERR(TAG, "QueryContextAttributes SECPKG_ATTR_SIZES failure %s [0x%08" PRIX32 "]", + GetSecurityStatusString(status), status); + } + return status; +} + +static SECURITY_STATUS nla_initialize_security_context(rdpNla* nla, BOOL initial, + SecBufferDesc* pInputBufferDesc, + SecBufferDesc* pOutputBufferDesc) +{ + SECURITY_STATUS status; + PCtxtHandle ctx = NULL; + + if (!nla || !nla->table) + { + WLog_ERR(TAG, "[%s] nla->table=%p->%p", __FUNCTION__, nla, nla ? nla->table : NULL); + return SEC_E_INVALID_PARAMETER; + } + if (!nla->table->InitializeSecurityContext) + { + WLog_ERR(TAG, "[%s] InitializeSecurityContext=%p", __FUNCTION__, + nla->table->InitializeSecurityContext); + return ERROR_INTERNAL_ERROR; + } + if (!initial) + ctx = &nla->context; + + status = nla->table->InitializeSecurityContext( + &nla->credentials, ctx, nla->ServicePrincipalName, nla->fContextReq, 0, + SECURITY_NATIVE_DREP, pInputBufferDesc, 0, &nla->context, pOutputBufferDesc, + &nla->pfContextAttr, &nla->expiration); + + switch (status) + { + case SEC_E_INCOMPLETE_MESSAGE: + case SEC_E_OK: + case SEC_I_COMPLETE_AND_CONTINUE: + case SEC_I_COMPLETE_NEEDED: + case SEC_I_CONTINUE_NEEDED: + case SEC_I_INCOMPLETE_CREDENTIALS: + WLog_DBG(TAG, " InitializeSecurityContext status %s [0x%08" PRIX32 "]", + GetSecurityStatusString(status), status); + break; + + default: + WLog_WARN(TAG, " InitializeSecurityContext failure status %s [0x%08" PRIX32 "]", + GetSecurityStatusString(status), status); + break; + } + + return status; +} + +static BOOL nla_complete_auth(rdpNla* nla, PSecBufferDesc pOutputBufferDesc) +{ + SECURITY_STATUS status = SEC_E_INVALID_TOKEN; + + if (!nla || !nla->table) + { + WLog_ERR(TAG, "[%s] nla->table=%p->%p", __FUNCTION__, nla, nla ? nla->table : NULL); + return SEC_E_INVALID_PARAMETER; + } + if (!nla->table->CompleteAuthToken) + { + WLog_ERR(TAG, "[%s] CompleteAuthToken=%p", __FUNCTION__, nla->table->CompleteAuthToken); + return ERROR_INTERNAL_ERROR; + } + + nla->status = nla->table->CompleteAuthToken(&nla->context, pOutputBufferDesc); + + if (nla->status != SEC_E_OK) + { + WLog_WARN(TAG, "CompleteAuthToken status %s [0x%08" PRIX32 "]", + GetSecurityStatusString(status), status); + return FALSE; + } + + return TRUE; +} + +static SECURITY_STATUS nla_decrypt(rdpNla* nla, SecBuffer* buffer, size_t headerLength) +{ + ULONG pfQOP = 0; + SECURITY_STATUS status = SEC_E_INVALID_PARAMETER; + SecBuffer Buffers[2]; + SecBufferDesc Message; + + if (!nla || !nla->table) + { + WLog_ERR(TAG, "[%s] nla->table=%p->%p", __FUNCTION__, nla, nla ? nla->table : NULL); + return SEC_E_INVALID_PARAMETER; + } + if (!nla->table->DecryptMessage) + { + WLog_ERR(TAG, "[%s] DecryptMessage=%p", __FUNCTION__, nla->table->DecryptMessage); + return ERROR_INTERNAL_ERROR; + } + + if (!buffer || (headerLength > buffer->cbBuffer)) + { + return SEC_E_INVALID_PARAMETER; + } + + Buffers[0].BufferType = SECBUFFER_TOKEN; /* Signature */ + Buffers[0].cbBuffer = (ULONG)headerLength; + Buffers[0].pvBuffer = &((BYTE*)buffer->pvBuffer)[0]; + Buffers[1].BufferType = SECBUFFER_DATA; /* Encrypted TLS Public Key */ + Buffers[1].cbBuffer = (ULONG)(buffer->cbBuffer - headerLength); + Buffers[1].pvBuffer = &((BYTE*)buffer->pvBuffer)[headerLength]; + Message.cBuffers = 2; + + Message.ulVersion = SECBUFFER_VERSION; + Message.pBuffers = Buffers; + status = nla->table->DecryptMessage(&nla->context, &Message, nla->recvSeqNum++, &pfQOP); + + if (status != SEC_E_OK) + { + WLog_ERR(TAG, "DecryptMessage failure %s [%08" PRIX32 "]", GetSecurityStatusString(status), + status); + } + + return status; +} + +static SECURITY_STATUS nla_encrypt(rdpNla* nla, SecBuffer* buffer, size_t headerLength) +{ + SECURITY_STATUS status = SEC_E_INVALID_PARAMETER; + SecBuffer Buffers[2]; + SecBufferDesc Message; + + if (!nla || !nla->table) + { + WLog_ERR(TAG, "[%s] nla->table=%p->%p", __FUNCTION__, nla, nla ? nla->table : NULL); + return SEC_E_INVALID_PARAMETER; + } + if (!nla->table->EncryptMessage) + { + WLog_ERR(TAG, "[%s] EncryptMessage=%p", __FUNCTION__, nla->table->EncryptMessage); + return ERROR_INTERNAL_ERROR; + } + + if (!buffer || (headerLength > buffer->cbBuffer)) + { + return SEC_E_INVALID_PARAMETER; + } + + Message.cBuffers = 2; + Buffers[0].BufferType = SECBUFFER_TOKEN; /* Signature */ + Buffers[0].cbBuffer = (ULONG)headerLength; + Buffers[0].pvBuffer = &((BYTE*)buffer->pvBuffer)[0]; + Buffers[1].BufferType = SECBUFFER_DATA; + Buffers[1].cbBuffer = (ULONG)(buffer->cbBuffer - headerLength); + Buffers[1].pvBuffer = &((BYTE*)buffer->pvBuffer)[headerLength]; + + Message.pBuffers = Buffers; + Message.ulVersion = SECBUFFER_VERSION; + status = nla->table->EncryptMessage(&nla->context, 0, &Message, nla->sendSeqNum++); + + if (status != SEC_E_OK) + { + WLog_ERR(TAG, "EncryptMessage status %s [0x%08" PRIX32 "]", GetSecurityStatusString(status), + status); + return status; + } + + if ((Message.cBuffers == 2) && (Buffers[0].cbBuffer < nla->ContextSizes.cbSecurityTrailer)) + { + /* IMPORTANT: EncryptMessage may not use all the signature space, so we need to shrink the + * excess between the buffers */ + MoveMemory(((BYTE*)Buffers[0].pvBuffer) + Buffers[0].cbBuffer, Buffers[1].pvBuffer, + Buffers[1].cbBuffer); + buffer->cbBuffer = Buffers[0].cbBuffer + Buffers[1].cbBuffer; + } + + return status; +} + +static size_t ber_sizeof_sequence_octet_string(size_t length) +{ + size_t rc = ber_sizeof_contextual_tag(ber_sizeof_octet_string(length)); + rc += ber_sizeof_octet_string(length); + return rc; +} + +static size_t ber_write_sequence_octet_string(wStream* stream, BYTE context, const BYTE* value, + size_t length) +{ + size_t rc = ber_write_contextual_tag(stream, context, ber_sizeof_octet_string(length), TRUE); + rc += ber_write_octet_string(stream, value, length); + return rc; +} + +static size_t ber_write_sequence_octet_string_from_secbuffer(wStream* stream, BYTE context, + const SecBuffer* buffer) +{ + return ber_write_sequence_octet_string(stream, context, buffer->pvBuffer, buffer->cbBuffer); } /* CredSSP Client-To-Server Binding Hash\0 */ @@ -240,7 +549,7 @@ static int nla_client_init(rdpNla* nla) rdpSettings* settings = nla->settings; WINPR_SAM* sam; WINPR_SAM_ENTRY* entry; - nla->state = NLA_STATE_INITIAL; + nla_set_state(nla, NLA_STATE_INITIAL); if (settings->RestrictedAdminModeRequired) settings->DisableCredentialsDelegation = TRUE; @@ -279,7 +588,7 @@ static int nla_client_init(rdpNla* nla) { if (settings->RestrictedAdminModeRequired) { - if (!is_empty(settings->PasswordHash)) + if ((settings->PasswordHash) && (strlen(settings->PasswordHash) > 0)) PromptPassword = FALSE; } } @@ -290,7 +599,6 @@ static int nla_client_init(rdpNla* nla) { if (freerdp_shall_disconnect(instance)) return 0; - if (!instance->Authenticate) { freerdp_set_last_error_log(instance->context, @@ -318,7 +626,7 @@ static int nla_client_init(rdpNla* nla) } else { - if (settings->RedirectionPassword && settings->RedirectionPasswordLength > 0) + if (settings->RedirectionPassword && (settings->RedirectionPasswordLength > 0)) { if (sspi_SetAuthIdentityWithUnicodePassword( nla->identity, settings->Username, settings->Domain, @@ -368,13 +676,12 @@ static int nla_client_init(rdpNla* nla) return -1; } - if (!sspi_SecBufferAlloc(&nla->PublicKey, tls->PublicKeyLength)) + if (!nla_sec_buffer_alloc_from_data(&nla->PublicKey, tls->PublicKey, 0, tls->PublicKeyLength)) { WLog_ERR(TAG, "Failed to allocate sspi secBuffer"); return -1; } - CopyMemory(nla->PublicKey.pvBuffer, tls->PublicKey, tls->PublicKeyLength); length = sizeof(TERMSRV_SPN_PREFIX) + strlen(settings->ServerHostname); spn = (SEC_CHAR*)malloc(length + 1); @@ -390,29 +697,10 @@ static int nla_client_init(rdpNla* nla) nla->ServicePrincipalName = spn; #endif nla->table = InitSecurityInterfaceEx(0); -#ifdef WITH_GSSAPI /* KERBEROS SSP */ - nla->status = nla->table->QuerySecurityPackageInfo(KERBEROS_SSP_NAME, &nla->pPackageInfo); - + nla->status = nla_update_package_name(nla); if (nla->status != SEC_E_OK) - { - WLog_ERR(TAG, "QuerySecurityPackageInfo status %s [0x%08" PRIX32 "]", - GetSecurityStatusString(nla->status), nla->status); return -1; - } -#else /* NTLM SSP */ - nla->status = nla->table->QuerySecurityPackageInfo(NLA_PKG_NAME, &nla->pPackageInfo); - - if (nla->status != SEC_E_OK) - { - WLog_ERR(TAG, "QuerySecurityPackageInfo status %s [0x%08" PRIX32 "]", - GetSecurityStatusString(nla->status), nla->status); - return -1; - } - -#endif - nla->cbMaxToken = nla->pPackageInfo->cbMaxToken; - nla->packageName = nla->pPackageInfo->Name; WLog_DBG(TAG, "%s %" PRIu32 " : packageName=%ls ; cbMaxToken=%d", __FUNCTION__, __LINE__, nla->packageName, nla->cbMaxToken); nla->status = nla->table->AcquireCredentialsHandle(NULL, NLA_PKG_NAME, SECPKG_CRED_OUTBOUND, @@ -429,8 +717,7 @@ static int nla_client_init(rdpNla* nla) nla->haveContext = FALSE; nla->haveInputBuffer = FALSE; nla->havePubKeyAuth = FALSE; - ZeroMemory(&nla->inputBuffer, sizeof(SecBuffer)); - ZeroMemory(&nla->outputBuffer, sizeof(SecBuffer)); + ZeroMemory(&nla->ContextSizes, sizeof(SecPkgContext_Sizes)); /* * from tspkg.dll: 0x00000132 @@ -443,258 +730,242 @@ static int nla_client_init(rdpNla* nla) return 1; } +static BOOL nla_client_send_token(rdpNla* nla, SecBufferDesc* token) +{ + BOOL rc = FALSE; + SecBuffer* buffer; + + if (!nla || !token) + { + WLog_ERR(TAG, "[%s] nla=%p, token=%p", __FUNCTION__, nla, token); + goto fail; + } + + if (token->cBuffers != 1) + { + WLog_ERR(TAG, "[%s] token->cBuffers=%" PRIu32, __FUNCTION__, token->cBuffers); + goto fail; + } + + buffer = token->pBuffers; + if (buffer->cbBuffer < 1) + { + WLog_ERR(TAG, "[%s] token->pBuffers[0].cbBuffer=%" PRIu32, __FUNCTION__, buffer->cbBuffer); + goto fail; + } + + if (!nla_sec_buffer_alloc_from_buffer(&nla->negoToken, &token->pBuffers[0], 0)) + goto fail; + + if (!nla_send(nla)) + goto fail; + + rc = TRUE; + +fail: + + return rc; +} + int nla_client_begin(rdpNla* nla) { + int rc = -1; + SecBuffer outputBuffer = { 0 }; + SecBufferDesc outputBufferDesc = { 0 }; if (nla_client_init(nla) < 1) - return -1; + goto fail; - if (nla->state != NLA_STATE_INITIAL) - return -1; + if (nla_get_state(nla) != NLA_STATE_INITIAL) + goto fail; - 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); + outputBufferDesc.ulVersion = SECBUFFER_VERSION; + outputBufferDesc.cBuffers = 1; + outputBufferDesc.pBuffers = &outputBuffer; - if (!nla->outputBuffer.pvBuffer) - return -1; + if (!nla_sec_buffer_alloc(&outputBuffer, nla->cbMaxToken)) + goto fail; - 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); - WLog_VRB(TAG, " InitializeSecurityContext status %s [0x%08" PRIX32 "]", - GetSecurityStatusString(nla->status), nla->status); - - /* Handle kerberos context initialization failure. - * After kerberos failed initialize NTLM context */ - if (nla->status == SEC_E_NO_CREDENTIALS) + nla->status = nla_initialize_security_context(nla, TRUE, NULL, &outputBufferDesc); + switch (nla->status) { - 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); - WLog_VRB(TAG, " InitializeSecurityContext status %s [0x%08" PRIX32 "]", + case SEC_E_OK: /* Complete, send output token if >0 */ + if (!nla_client_send_token(nla, &outputBufferDesc)) + goto fail; + nla_set_state(nla, NLA_STATE_FINAL); + break; + case SEC_I_COMPLETE_AND_CONTINUE: /* CompleteAuthToken -> server -> + InitializeSecurityContext again */ + if (!nla_complete_auth(nla, &outputBufferDesc)) + goto fail; + nla_set_state(nla, NLA_STATE_NEGO_TOKEN); + break; + case SEC_I_COMPLETE_NEEDED: /* CompleteAuthToken -> finish */ + if (!nla_complete_auth(nla, &outputBufferDesc)) + goto fail; + nla_set_state(nla, NLA_STATE_FINAL); + break; + case SEC_I_CONTINUE_NEEDED: /* out token -> server -> InitializeSecurityContext again */ + if (!nla_client_send_token(nla, &outputBufferDesc)) + goto fail; + nla_set_state(nla, NLA_STATE_NEGO_TOKEN); + break; + case SEC_I_INCOMPLETE_CREDENTIALS: + case SEC_E_INCOMPLETE_MESSAGE: + default: + break; + } + + rc = 1; +fail: + sspi_SecBufferFree(&outputBuffer); + return rc; +} + +static int nla_client_recv_nego_token(rdpNla* nla) +{ + int rc = -1; + SECURITY_STATUS status; + SecBuffer inputBuffer = { 0 }; + SecBuffer outputBuffer = { 0 }; + SecBufferDesc inputBufferDesc = { 0 }; + SecBufferDesc outputBufferDesc = { 0 }; + + inputBufferDesc.ulVersion = SECBUFFER_VERSION; + inputBufferDesc.cBuffers = 1; + inputBufferDesc.pBuffers = &inputBuffer; + if (!nla_sec_buffer_alloc_from_buffer(&inputBuffer, &nla->negoToken, 0)) + goto fail; + + outputBufferDesc.ulVersion = SECBUFFER_VERSION; + outputBufferDesc.cBuffers = 1; + outputBufferDesc.pBuffers = &outputBuffer; + if (!nla_sec_buffer_alloc(&outputBuffer, nla->cbMaxToken)) + goto fail; + + nla->status = nla_initialize_security_context(nla, FALSE, &inputBufferDesc, &outputBufferDesc); + + switch (nla->status) + { + case SEC_I_COMPLETE_AND_CONTINUE: + if (!nla_complete_auth(nla, &outputBufferDesc)) + goto fail; + if (!nla_client_send_token(nla, &outputBufferDesc)) + goto fail; + break; + case SEC_I_COMPLETE_NEEDED: /* CompleteAuthToken -> finish */ + if (!nla_complete_auth(nla, &outputBufferDesc)) + goto fail; + nla_set_state(nla, NLA_STATE_NEGO_TOKEN); + break; + case SEC_E_OK: /* completed */ + nla->havePubKeyAuth = TRUE; + status = nla_query_context_sizes(nla); + + if (status != SEC_E_OK) + goto fail; + + if (nla->peerVersion < 5) + status = nla_encrypt_public_key_echo(nla); + else + status = nla_encrypt_public_key_hash(nla); + + if (status != SEC_E_OK) + goto fail; + + if (!nla_client_send_token(nla, &outputBufferDesc)) + goto fail; + nla_set_state(nla, NLA_STATE_PUB_KEY_AUTH); + break; + + case SEC_I_CONTINUE_NEEDED: /* send -> retry */ + if (!nla_client_send_token(nla, &outputBufferDesc)) + goto fail; + nla_set_state(nla, NLA_STATE_NEGO_TOKEN); + break; + + default: + WLog_ERR(TAG, "Unexpected NLA return %s", GetSecurityStatusString(nla->status)); + goto fail; + } + + rc = 1; +fail: + sspi_SecBufferFree(&inputBuffer); + sspi_SecBufferFree(&outputBuffer); + return rc; +} + +static int nla_client_recv_pub_key_auth(rdpNla* nla) +{ + /* Verify Server Public Key Echo */ + if (nla->peerVersion < 5) + nla->status = nla_decrypt_public_key_echo(nla); + else + nla->status = nla_decrypt_public_key_hash(nla); + + nla_buffer_free(nla); + + if (nla->status != SEC_E_OK) + { + WLog_ERR(TAG, "Could not verify public key echo %s [0x%08" PRIX32 "]", GetSecurityStatusString(nla->status), nla->status); - - if (nla->status) - { - SECURITY_STATUS status = - nla->table->QuerySecurityPackageInfo(NTLM_SSP_NAME, &nla->pPackageInfo); - - if (status != SEC_E_OK) - { - WLog_ERR(TAG, "QuerySecurityPackageInfo status %s [0x%08" PRIX32 "]", - GetSecurityStatusString(nla->status), status); - return -1; - } - - nla->cbMaxToken = nla->pPackageInfo->cbMaxToken; - nla->packageName = nla->pPackageInfo->Name; - } + return -1; } - if ((nla->status == SEC_I_COMPLETE_AND_CONTINUE) || (nla->status == SEC_I_COMPLETE_NEEDED)) - { - if (nla->table->CompleteAuthToken) - { - SECURITY_STATUS status; - status = nla->table->CompleteAuthToken(&nla->context, &nla->outputBufferDesc); + /* Send encrypted credentials */ + nla->status = nla_encrypt_ts_credentials(nla); - if (status != SEC_E_OK) - { - WLog_WARN(TAG, "CompleteAuthToken status %s [0x%08" PRIX32 "]", - GetSecurityStatusString(status), status); - return -1; - } - } - - 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) + if (nla->status != SEC_E_OK) return -1; - if (nla->outputBuffer.cbBuffer < 1) - return -1; - - nla->negoToken.pvBuffer = nla->outputBuffer.pvBuffer; - nla->negoToken.cbBuffer = nla->outputBuffer.cbBuffer; - - if (!nla_send(nla, "Client: Sending Authentication Token")) + if (!nla_send(nla)) { nla_buffer_free(nla); return -1; } nla_buffer_free(nla); - nla->state = NLA_STATE_NEGO_TOKEN; + + if (SecIsValidHandle(&nla->credentials)) + { + nla->table->FreeCredentialsHandle(&nla->credentials); + SecInvalidateHandle(&nla->credentials); + } + + if (nla->status != SEC_E_OK) + { + WLog_ERR(TAG, "FreeCredentialsHandle status %s [0x%08" PRIX32 "]", + GetSecurityStatusString(nla->status), nla->status); + } + + if (nla->status != SEC_E_OK) + return -1; + + nla_set_state(nla, NLA_STATE_AUTH_INFO); return 1; } static int nla_client_recv(rdpNla* nla) { - int status = -1; - - if (nla->state == NLA_STATE_NEGO_TOKEN) + switch (nla_get_state(nla)) { - 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); + case NLA_STATE_NEGO_TOKEN: + return nla_client_recv_nego_token(nla); - if (!nla->outputBuffer.pvBuffer) + case NLA_STATE_PUB_KEY_AUTH: + return nla_client_recv_pub_key_auth(nla); + + case NLA_STATE_FINAL: + default: + WLog_ERR(TAG, "NLA in invalid client receive state %s", + nla_get_state_str(nla_get_state(nla))); 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); - WLog_VRB(TAG, "InitializeSecurityContext %s [0x%08" PRIX32 "]", - GetSecurityStatusString(nla->status), nla->status); - 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) - { - SECURITY_STATUS status; - status = nla->table->CompleteAuthToken(&nla->context, &nla->outputBufferDesc); - - if (status != SEC_E_OK) - { - WLog_WARN(TAG, "CompleteAuthToken status %s [0x%08" PRIX32 "]", - GetSecurityStatusString(status), status); - return -1; - } - } - - 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_E_OK) - { - nla->havePubKeyAuth = TRUE; - nla->status = nla->table->QueryContextAttributes(&nla->context, SECPKG_ATTR_SIZES, - &nla->ContextSizes); - - if (nla->status != SEC_E_OK) - { - WLog_ERR(TAG, - "QueryContextAttributes SECPKG_ATTR_SIZES failure %s [0x%08" PRIX32 "]", - GetSecurityStatusString(nla->status), nla->status); - return -1; - } - - if (nla->peerVersion < 5) - nla->status = nla_encrypt_public_key_echo(nla); - else - nla->status = nla_encrypt_public_key_hash(nla); - - if (nla->status != SEC_E_OK) - return -1; - } - - nla->negoToken.pvBuffer = nla->outputBuffer.pvBuffer; - nla->negoToken.cbBuffer = nla->outputBuffer.cbBuffer; - - if (!nla_send(nla, "Client: Sending Authentication Token")) - { - nla_buffer_free(nla); - return -1; - } - - nla_buffer_free(nla); - - if (nla->status == SEC_E_OK) - nla->state = NLA_STATE_PUB_KEY_AUTH; - - status = 1; } - else if (nla->state == NLA_STATE_PUB_KEY_AUTH) - { - /* Verify Server Public Key Echo */ - if (nla->peerVersion < 5) - nla->status = nla_decrypt_public_key_echo(nla); - else - nla->status = nla_decrypt_public_key_hash(nla); - - nla_buffer_free(nla); - - if (nla->status != SEC_E_OK) - { - WLog_ERR(TAG, "Could not verify public key echo %s [0x%08" PRIX32 "]", - GetSecurityStatusString(nla->status), nla->status); - 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 %s [0x%08" PRIX32 "]", - GetSecurityStatusString(nla->status), nla->status); - return -1; - } - - if (!nla_send(nla, "Client: Sending PubKeyAuth Token")) - { - nla_buffer_free(nla); - return -1; - } - - nla_buffer_free(nla); - - if (SecIsValidHandle(&nla->credentials)) - { - nla->table->FreeCredentialsHandle(&nla->credentials); - SecInvalidateHandle(&nla->credentials); - } - - if (nla->status != SEC_E_OK) - { - WLog_ERR(TAG, "FreeCredentialsHandle status %s [0x%08" PRIX32 "]", - GetSecurityStatusString(nla->status), nla->status); - } - - nla->status = nla->table->FreeContextBuffer(nla->pPackageInfo); - - if (nla->status != SEC_E_OK) - { - WLog_ERR(TAG, "FreeContextBuffer status %s [0x%08" PRIX32 "]", - GetSecurityStatusString(nla->status), nla->status); - } - - if (nla->status != SEC_E_OK) - return -1; - - nla->state = NLA_STATE_AUTH_INFO; - status = 1; - } - - return status; } static int nla_client_authenticate(rdpNla* nla) { + int rc = -1; wStream* s; int status; s = Stream_New(NULL, 4096); @@ -706,12 +977,9 @@ static int nla_client_authenticate(rdpNla* nla) } if (nla_client_begin(nla) < 1) - { - Stream_Free(s, TRUE); - return -1; - } + goto fail; - while (nla->state < NLA_STATE_AUTH_INFO) + while (nla_get_state(nla) < NLA_STATE_AUTH_INFO) { Stream_SetPosition(s, 0); status = transport_read_pdu(nla->transport, s); @@ -719,21 +987,19 @@ static int nla_client_authenticate(rdpNla* nla) if (status < 0) { WLog_ERR(TAG, "nla_client_authenticate failure"); - Stream_Free(s, TRUE); - return -1; + goto fail; } status = nla_recv_pdu(nla, s); if (status < 0) - { - Stream_Free(s, TRUE); - return -1; - } + goto fail; } + rc = 1; +fail: Stream_Free(s, TRUE); - return 1; + return rc; } /** @@ -745,14 +1011,12 @@ static int nla_server_init(rdpNla* nla) { rdpTls* tls = nla->transport->tls; - if (!sspi_SecBufferAlloc(&nla->PublicKey, tls->PublicKeyLength)) + if (!nla_sec_buffer_alloc_from_data(&nla->PublicKey, tls->PublicKey, 0, tls->PublicKeyLength)) { WLog_ERR(TAG, "Failed to allocate SecBuffer for public key"); return -1; } - CopyMemory(nla->PublicKey.pvBuffer, tls->PublicKey, tls->PublicKeyLength); - if (nla->SspiModule) { HMODULE hSSPI; @@ -779,17 +1043,11 @@ static int nla_server_init(rdpNla* nla) nla->table = InitSecurityInterfaceEx(0); } - nla->status = nla->table->QuerySecurityPackageInfo(NLA_PKG_NAME, &nla->pPackageInfo); + nla->status = nla_update_package_name(nla); if (nla->status != SEC_E_OK) - { - WLog_ERR(TAG, "QuerySecurityPackageInfo status %s [0x%08" PRIX32 "]", - GetSecurityStatusString(nla->status), nla->status); return -1; - } - nla->cbMaxToken = nla->pPackageInfo->cbMaxToken; - nla->packageName = nla->pPackageInfo->Name; nla->status = nla->table->AcquireCredentialsHandle(NULL, NLA_PKG_NAME, SECPKG_CRED_INBOUND, NULL, NULL, NULL, NULL, &nla->credentials, &nla->expiration); @@ -804,10 +1062,7 @@ static int nla_server_init(rdpNla* nla) nla->haveContext = FALSE; nla->haveInputBuffer = FALSE; nla->havePubKeyAuth = FALSE; - ZeroMemory(&nla->inputBuffer, sizeof(SecBuffer)); - ZeroMemory(&nla->outputBuffer, sizeof(SecBuffer)); - ZeroMemory(&nla->inputBufferDesc, sizeof(SecBufferDesc)); - ZeroMemory(&nla->outputBufferDesc, sizeof(SecBufferDesc)); + ZeroMemory(&nla->ContextSizes, sizeof(SecPkgContext_Sizes)); /* * from tspkg.dll: 0x00000112 @@ -823,9 +1078,32 @@ static int nla_server_init(rdpNla* nla) nla->fContextReq |= ASC_REQ_REPLAY_DETECT; nla->fContextReq |= ASC_REQ_SEQUENCE_DETECT; nla->fContextReq |= ASC_REQ_EXTENDED_ERROR; + nla_set_state(nla, NLA_STATE_INITIAL); return 1; } +static wStream* nla_server_recv_stream(rdpNla* nla) +{ + wStream* s = NULL; + int status = -1; + s = Stream_New(NULL, 4096); + + if (!s) + goto fail; + + status = transport_read_pdu(nla->transport, s); + +fail: + if (status < 0) + { + WLog_ERR(TAG, "nla_recv() error: %d", status); + Stream_Free(s, TRUE); + goto fail; + } + + return s; +} + /** * Authenticate with client using CredSSP (server). * @param credssp @@ -834,47 +1112,75 @@ static int nla_server_init(rdpNla* nla) static int nla_server_authenticate(rdpNla* nla) { + int res = -1; if (nla_server_init(nla) < 1) - return -1; + goto fail; + + /* Client is starting, here es the state machine: + * + * -- NLA_STATE_INITIAL --> NLA_STATE_INITIAL + * ----->> sending... + * ----->> protocol version 6 + * ----->> nego token + * ----->> client nonce + * <<----- receiving... + * <<----- protocol version 6 + * <<----- nego token + * ----->> sending... + * ----->> protocol version 6 + * ----->> nego token + * ----->> public key auth + * ----->> client nonce + * -- NLA_STATE_NEGO_TOKEN --> NLA_STATE_PUB_KEY_AUTH + * <<----- receiving... + * <<----- protocol version 6 + * <<----- public key info + * ----->> sending... + * ----->> protocol version 6 + * ----->> auth info + * ----->> client nonce + * -- NLA_STATE_PUB_KEY_AUTH --> NLA_STATE + */ while (TRUE) { + int rc = -1; + SecBuffer inputBuffer = { 0 }; + SecBuffer outputBuffer = { 0 }; + SecBufferDesc inputBufferDesc = { 0 }; + SecBufferDesc outputBufferDesc = { 0 }; + /* receive authentication token */ - nla->inputBufferDesc.ulVersion = SECBUFFER_VERSION; - nla->inputBufferDesc.cBuffers = 1; - nla->inputBufferDesc.pBuffers = &nla->inputBuffer; - nla->inputBuffer.BufferType = SECBUFFER_TOKEN; + inputBufferDesc.ulVersion = SECBUFFER_VERSION; + inputBufferDesc.cBuffers = 1; + inputBufferDesc.pBuffers = &inputBuffer; - if (nla_recv(nla, "Receiving Authentication Token") < 0) - return -1; + if (nla_server_recv(nla) < 0) + goto fail; - nla->inputBuffer.pvBuffer = nla->negoToken.pvBuffer; - nla->inputBuffer.cbBuffer = nla->negoToken.cbBuffer; - - if (nla->negoToken.cbBuffer < 1) + WLog_DBG(TAG, "Receiving Authentication Token"); + if (!nla_sec_buffer_alloc_from_buffer(&inputBuffer, &nla->negoToken, 0)) { WLog_ERR(TAG, "CredSSP: invalid negoToken!"); - return -1; + goto fail; } - 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); + outputBufferDesc.ulVersion = SECBUFFER_VERSION; + outputBufferDesc.cBuffers = 1; + outputBufferDesc.pBuffers = &outputBuffer; - if (!nla->outputBuffer.pvBuffer) - return -1; + if (!nla_sec_buffer_alloc(&outputBuffer, nla->cbMaxToken)) + goto fail; nla->status = nla->table->AcceptSecurityContext( - &nla->credentials, nla->haveContext ? &nla->context : NULL, &nla->inputBufferDesc, - nla->fContextReq, SECURITY_NATIVE_DREP, &nla->context, &nla->outputBufferDesc, + &nla->credentials, nla->haveContext ? &nla->context : NULL, &inputBufferDesc, + nla->fContextReq, SECURITY_NATIVE_DREP, &nla->context, &outputBufferDesc, &nla->pfContextAttr, &nla->expiration); WLog_VRB(TAG, "AcceptSecurityContext status %s [0x%08" PRIX32 "]", GetSecurityStatusString(nla->status), nla->status); - nla->negoToken.pvBuffer = nla->outputBuffer.pvBuffer; - nla->negoToken.cbBuffer = nla->outputBuffer.cbBuffer; + + if (!nla_sec_buffer_alloc_from_buffer(&nla->negoToken, &outputBuffer, 0)) + goto fail; if ((nla->status == SEC_I_COMPLETE_AND_CONTINUE) || (nla->status == SEC_I_COMPLETE_NEEDED)) { @@ -907,50 +1213,31 @@ static int nla_server_authenticate(rdpNla* nla) nla->SamFile, strlen(nla->SamFile) + 1); } - if (nla->table->CompleteAuthToken) - { - SECURITY_STATUS status; - status = nla->table->CompleteAuthToken(&nla->context, &nla->outputBufferDesc); - - if (status != SEC_E_OK) - { - WLog_WARN(TAG, "CompleteAuthToken status %s [0x%08" PRIX32 "]", - GetSecurityStatusString(status), status); - return -1; - } - } - - 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_complete_auth(nla, &outputBufferDesc)) + goto fail; } if (nla->status == SEC_E_OK) { - if (nla->outputBuffer.cbBuffer != 0) + if (outputBuffer.cbBuffer != 0) { - if (!nla_send(nla, "Server: Sending response")) + if (!nla_send(nla)) { nla_buffer_free(nla); - return -1; + goto fail; } - if (nla_recv(nla, "Receiving pubkey Token") < 0) - return -1; + if (nla_server_recv(nla) < 0) + goto fail; + + WLog_DBG(TAG, "Receiving pubkey Token"); } nla->havePubKeyAuth = TRUE; - nla->status = nla->table->QueryContextAttributes(&nla->context, SECPKG_ATTR_SIZES, - &nla->ContextSizes); + nla->status = nla_query_context_sizes(nla); if (nla->status != SEC_E_OK) - { - WLog_ERR(TAG, - "QueryContextAttributes SECPKG_ATTR_SIZES failure %s [0x%08" PRIX32 "]", - GetSecurityStatusString(nla->status), nla->status); - return -1; - } + goto fail; if (nla->peerVersion < 5) nla->status = nla_decrypt_public_key_echo(nla); @@ -962,12 +1249,10 @@ static int nla_server_authenticate(rdpNla* nla) WLog_ERR(TAG, "Error: could not verify client's public key echo %s [0x%08" PRIX32 "]", GetSecurityStatusString(nla->status), nla->status); - return -1; + goto fail; } sspi_SecBufferFree(&nla->negoToken); - nla->negoToken.pvBuffer = NULL; - nla->negoToken.cbBuffer = 0; if (nla->peerVersion < 5) nla->status = nla_encrypt_public_key_echo(nla); @@ -975,7 +1260,17 @@ static int nla_server_authenticate(rdpNla* nla) nla->status = nla_encrypt_public_key_hash(nla); if (nla->status != SEC_E_OK) - return -1; + goto fail; + + rc = 1; + fail: + sspi_SecBufferFree(&inputBuffer); + sspi_SecBufferFree(&outputBuffer); + if (rc < 0) + { + res = rc; + goto fail_auth; + } } if ((nla->status != SEC_E_OK) && (nla->status != SEC_I_CONTINUE_NEEDED)) @@ -1004,19 +1299,19 @@ static int nla_server_authenticate(rdpNla* nla) WLog_ERR(TAG, "AcceptSecurityContext status %s [0x%08" PRIX32 "]", GetSecurityStatusString(nla->status), nla->status); - nla_send(nla, "Server: Sending AcceptSecurityContext error status"); - return -1; /* Access Denied */ + nla_send(nla); + goto fail_auth; /* Access Denied */ } /* send authentication token */ - if (!nla_send(nla, "Server: Sending Authentication Token")) + WLog_DBG(TAG, "Sending Authentication Token"); + + if (!nla_send(nla)) { nla_buffer_free(nla); - return -1; + goto fail_auth; } - nla_buffer_free(nla); - if (nla->status != SEC_I_CONTINUE_NEEDED) break; @@ -1025,8 +1320,8 @@ static int nla_server_authenticate(rdpNla* nla) /* Receive encrypted credentials */ - if (nla_recv(nla, "Receive Encryption Credentials") < 0) - return -1; + if (nla_server_recv(nla) < 0) + goto fail_auth; nla->status = nla_decrypt_ts_credentials(nla); @@ -1034,7 +1329,7 @@ static int nla_server_authenticate(rdpNla* nla) { WLog_ERR(TAG, "Could not decrypt TSCredentials status %s [0x%08" PRIX32 "]", GetSecurityStatusString(nla->status), nla->status); - return -1; + goto fail_auth; } nla->status = nla->table->ImpersonateSecurityContext(&nla->context); @@ -1043,7 +1338,7 @@ static int nla_server_authenticate(rdpNla* nla) { WLog_ERR(TAG, "ImpersonateSecurityContext status %s [0x%08" PRIX32 "]", GetSecurityStatusString(nla->status), nla->status); - return -1; + goto fail_auth; } else { @@ -1053,20 +1348,14 @@ static int nla_server_authenticate(rdpNla* nla) { WLog_ERR(TAG, "RevertSecurityContext status %s [0x%08" PRIX32 "]", GetSecurityStatusString(nla->status), nla->status); - return -1; + goto fail_auth; } } - nla->status = nla->table->FreeContextBuffer(nla->pPackageInfo); - - if (nla->status != SEC_E_OK) - { - WLog_ERR(TAG, "DeleteSecurityContext status %s [0x%08" PRIX32 "]", - GetSecurityStatusString(nla->status), nla->status); - return -1; - } - - return 1; + res = 1; +fail_auth: + nla_buffer_free(nla); + return res; } /** @@ -1083,9 +1372,9 @@ int nla_authenticate(rdpNla* nla) return nla_client_authenticate(nla); } -static void ap_integer_increment_le(BYTE* number, int size) +static void ap_integer_increment_le(BYTE* number, size_t size) { - int index; + size_t index; for (index = 0; index < size; index++) { @@ -1102,9 +1391,9 @@ static void ap_integer_increment_le(BYTE* number, int size) } } -static void ap_integer_decrement_le(BYTE* number, int size) +static void ap_integer_decrement_le(BYTE* number, size_t size) { - int index; + size_t index; for (index = 0; index < size; index++) { @@ -1123,87 +1412,38 @@ static void ap_integer_decrement_le(BYTE* number, int size) SECURITY_STATUS nla_encrypt_public_key_echo(rdpNla* nla) { - SecBuffer Buffers[2] = { { 0 } }; - SecBufferDesc Message; SECURITY_STATUS status; - ULONG public_key_length; - const BOOL krb = - (_tcsncmp(nla->packageName, KERBEROS_SSP_NAME, ARRAYSIZE(KERBEROS_SSP_NAME)) == 0); - const BOOL nego = (_tcsncmp(nla->packageName, NEGO_SSP_NAME, ARRAYSIZE(NEGO_SSP_NAME)) == 0); - const BOOL ntlm = (_tcsncmp(nla->packageName, NTLM_SSP_NAME, ARRAYSIZE(NTLM_SSP_NAME)) == 0); - public_key_length = nla->PublicKey.cbBuffer; - sspi_SecBufferFree(&nla->pubKeyAuth); - if (!sspi_SecBufferAlloc(&nla->pubKeyAuth, - public_key_length + nla->ContextSizes.cbSecurityTrailer)) + if (!nla_sec_buffer_alloc_from_buffer(&nla->pubKeyAuth, &nla->PublicKey, + nla->ContextSizes.cbSecurityTrailer)) return SEC_E_INSUFFICIENT_MEMORY; - if (krb) - { - Message.cBuffers = 1; - Buffers[0].BufferType = SECBUFFER_DATA; /* TLS Public Key */ - Buffers[0].cbBuffer = public_key_length; - Buffers[0].pvBuffer = nla->pubKeyAuth.pvBuffer; - CopyMemory(Buffers[0].pvBuffer, nla->PublicKey.pvBuffer, Buffers[0].cbBuffer); - } - else if (ntlm || nego) - { - Message.cBuffers = 2; - Buffers[0].BufferType = SECBUFFER_TOKEN; /* Signature */ - Buffers[0].cbBuffer = nla->ContextSizes.cbSecurityTrailer; - Buffers[0].pvBuffer = nla->pubKeyAuth.pvBuffer; - Buffers[1].BufferType = SECBUFFER_DATA; /* TLS Public Key */ - Buffers[1].cbBuffer = public_key_length; - Buffers[1].pvBuffer = - ((BYTE*)nla->pubKeyAuth.pvBuffer) + nla->ContextSizes.cbSecurityTrailer; - CopyMemory(Buffers[1].pvBuffer, nla->PublicKey.pvBuffer, Buffers[1].cbBuffer); - } - - if (!krb && nla->server) + if (!nla->server) { + BYTE* data = &((BYTE*)nla->PublicKey.pvBuffer)[nla->ContextSizes.cbSecurityTrailer]; /* server echos the public key +1 */ - ap_integer_increment_le((BYTE*)Buffers[1].pvBuffer, Buffers[1].cbBuffer); + ap_integer_increment_le(data, nla->PublicKey.cbBuffer); } - Message.ulVersion = SECBUFFER_VERSION; - Message.pBuffers = (PSecBuffer)&Buffers; - status = nla->table->EncryptMessage(&nla->context, 0, &Message, nla->sendSeqNum++); - + status = nla_encrypt(nla, &nla->pubKeyAuth, nla->ContextSizes.cbSecurityTrailer); if (status != SEC_E_OK) - { - WLog_ERR(TAG, "EncryptMessage status %s [0x%08" PRIX32 "]", GetSecurityStatusString(status), - status); return status; - } - - if (Message.cBuffers == 2 && Buffers[0].cbBuffer < nla->ContextSizes.cbSecurityTrailer) - { - /* IMPORTANT: EncryptMessage may not use all the signature space, so we need to shrink the - * excess between the buffers */ - MoveMemory(((BYTE*)Buffers[0].pvBuffer) + Buffers[0].cbBuffer, Buffers[1].pvBuffer, - Buffers[1].cbBuffer); - nla->pubKeyAuth.cbBuffer = Buffers[0].cbBuffer + Buffers[1].cbBuffer; - } return status; } SECURITY_STATUS nla_encrypt_public_key_hash(rdpNla* nla) { - SecBuffer Buffers[2] = { { 0 } }; - SecBufferDesc Message; SECURITY_STATUS status = SEC_E_INTERNAL_ERROR; WINPR_DIGEST_CTX* sha256 = NULL; - const BOOL krb = - (_tcsncmp(nla->packageName, KERBEROS_SSP_NAME, ARRAYSIZE(KERBEROS_SSP_NAME)) == 0); + BYTE* hash; const ULONG auth_data_length = (nla->ContextSizes.cbSecurityTrailer + WINPR_SHA256_DIGEST_LENGTH); const BYTE* hashMagic = nla->server ? ServerClientHashMagic : ClientServerHashMagic; const size_t hashSize = nla->server ? sizeof(ServerClientHashMagic) : sizeof(ClientServerHashMagic); - sspi_SecBufferFree(&nla->pubKeyAuth); - if (!sspi_SecBufferAlloc(&nla->pubKeyAuth, auth_data_length)) + if (!nla_sec_buffer_alloc(&nla->pubKeyAuth, auth_data_length)) { status = SEC_E_INSUFFICIENT_MEMORY; goto out; @@ -1220,57 +1460,20 @@ SECURITY_STATUS nla_encrypt_public_key_hash(rdpNla* nla) if (!winpr_Digest_Update(sha256, hashMagic, hashSize)) goto out; - if (!winpr_Digest_Update(sha256, nla->ClientNonce.pvBuffer, nla->ClientNonce.cbBuffer)) + if (!nla_Digest_Update_From_SecBuffer(sha256, &nla->ClientNonce)) goto out; /* SubjectPublicKey */ - if (!winpr_Digest_Update(sha256, nla->PublicKey.pvBuffer, nla->PublicKey.cbBuffer)) + if (!nla_Digest_Update_From_SecBuffer(sha256, &nla->PublicKey)) goto out; - if (krb) - { - Message.cBuffers = 1; - Buffers[0].BufferType = SECBUFFER_DATA; /* SHA256 hash */ - Buffers[0].cbBuffer = WINPR_SHA256_DIGEST_LENGTH; - Buffers[0].pvBuffer = nla->pubKeyAuth.pvBuffer; - - if (!winpr_Digest_Final(sha256, Buffers[0].pvBuffer, Buffers[0].cbBuffer)) - goto out; - } - else - { - Message.cBuffers = 2; - Buffers[0].BufferType = SECBUFFER_TOKEN; /* Signature */ - Buffers[0].cbBuffer = nla->ContextSizes.cbSecurityTrailer; - Buffers[0].pvBuffer = nla->pubKeyAuth.pvBuffer; - Buffers[1].BufferType = SECBUFFER_DATA; /* SHA256 hash */ - Buffers[1].cbBuffer = WINPR_SHA256_DIGEST_LENGTH; - Buffers[1].pvBuffer = - ((BYTE*)nla->pubKeyAuth.pvBuffer) + nla->ContextSizes.cbSecurityTrailer; - - if (!winpr_Digest_Final(sha256, Buffers[1].pvBuffer, Buffers[1].cbBuffer)) - goto out; - } - - Message.pBuffers = (PSecBuffer)&Buffers; - Message.ulVersion = SECBUFFER_VERSION; - status = nla->table->EncryptMessage(&nla->context, 0, &Message, nla->sendSeqNum++); + hash = &((BYTE*)nla->pubKeyAuth.pvBuffer)[nla->ContextSizes.cbSecurityTrailer]; + if (!winpr_Digest_Final(sha256, hash, WINPR_SHA256_DIGEST_LENGTH)) + goto out; + status = nla_encrypt(nla, &nla->pubKeyAuth, nla->ContextSizes.cbSecurityTrailer); if (status != SEC_E_OK) - { - WLog_ERR(TAG, "EncryptMessage status %s [0x%08" PRIX32 "]", GetSecurityStatusString(status), - status); goto out; - } - - if (Message.cBuffers == 2 && Buffers[0].cbBuffer < nla->ContextSizes.cbSecurityTrailer) - { - /* IMPORTANT: EncryptMessage may not use all the signature space, so we need to shrink the - * excess between the buffers */ - MoveMemory(((BYTE*)Buffers[0].pvBuffer) + Buffers[0].cbBuffer, Buffers[1].pvBuffer, - Buffers[1].cbBuffer); - nla->pubKeyAuth.cbBuffer = Buffers[0].cbBuffer + Buffers[1].cbBuffer; - } out: winpr_Digest_Free(sha256); @@ -1279,83 +1482,32 @@ out: SECURITY_STATUS nla_decrypt_public_key_echo(rdpNla* nla) { - ULONG length; - BYTE* buffer = NULL; - ULONG pfQOP = 0; BYTE* public_key1 = NULL; BYTE* public_key2 = NULL; ULONG public_key_length = 0; - int signature_length; - SecBuffer Buffers[2] = { { 0 } }; - SecBufferDesc Message; - BOOL krb, ntlm, nego; + size_t signature_length; SECURITY_STATUS status = SEC_E_INVALID_TOKEN; + PSecBuffer pubKeyAuth; if (!nla) goto fail; - krb = (_tcsncmp(nla->packageName, KERBEROS_SSP_NAME, ARRAYSIZE(KERBEROS_SSP_NAME)) == 0); - nego = (_tcsncmp(nla->packageName, NEGO_SSP_NAME, ARRAYSIZE(NEGO_SSP_NAME)) == 0); - ntlm = (_tcsncmp(nla->packageName, NTLM_SSP_NAME, ARRAYSIZE(NTLM_SSP_NAME)) == 0); - signature_length = nla->pubKeyAuth.cbBuffer - nla->PublicKey.cbBuffer; + pubKeyAuth = &nla->pubKeyAuth; - if ((signature_length < 0) || ((UINT32)signature_length > nla->ContextSizes.cbSecurityTrailer)) + signature_length = pubKeyAuth->cbBuffer - nla->PublicKey.cbBuffer; + if ((pubKeyAuth->cbBuffer < nla->PublicKey.cbBuffer) || + (signature_length > nla->ContextSizes.cbSecurityTrailer)) { - WLog_ERR(TAG, "unexpected pubKeyAuth buffer size: %" PRIu32 "", nla->pubKeyAuth.cbBuffer); + WLog_ERR(TAG, "unexpected pubKeyAuth buffer size: %" PRIu32 "", pubKeyAuth->cbBuffer); goto fail; } - length = nla->pubKeyAuth.cbBuffer; - buffer = (BYTE*)malloc(length); - - if (!buffer) - { - status = SEC_E_INSUFFICIENT_MEMORY; - goto fail; - } - - if (krb) - { - CopyMemory(buffer, nla->pubKeyAuth.pvBuffer, length); - Buffers[0].BufferType = SECBUFFER_DATA; /* Wrapped and encrypted TLS Public Key */ - Buffers[0].cbBuffer = length; - Buffers[0].pvBuffer = buffer; - Message.cBuffers = 1; - } - else if (ntlm || nego) - { - CopyMemory(buffer, nla->pubKeyAuth.pvBuffer, length); - public_key_length = nla->PublicKey.cbBuffer; - Buffers[0].BufferType = SECBUFFER_TOKEN; /* Signature */ - Buffers[0].cbBuffer = signature_length; - Buffers[0].pvBuffer = buffer; - Buffers[1].BufferType = SECBUFFER_DATA; /* Encrypted TLS Public Key */ - Buffers[1].cbBuffer = length - signature_length; - Buffers[1].pvBuffer = buffer + signature_length; - Message.cBuffers = 2; - } - - Message.ulVersion = SECBUFFER_VERSION; - Message.pBuffers = (PSecBuffer)&Buffers; - status = nla->table->DecryptMessage(&nla->context, &Message, nla->recvSeqNum++, &pfQOP); - + status = nla_decrypt(nla, pubKeyAuth, signature_length); if (status != SEC_E_OK) - { - WLog_ERR(TAG, "DecryptMessage failure %s [%08" PRIX32 "]", GetSecurityStatusString(status), - status); goto fail; - } - if (krb) - { - public_key1 = public_key2 = (BYTE*)nla->pubKeyAuth.pvBuffer; - public_key_length = length; - } - else if (ntlm || nego) - { - public_key1 = (BYTE*)nla->PublicKey.pvBuffer; - public_key2 = (BYTE*)Buffers[1].pvBuffer; - } + public_key1 = &((BYTE*)nla->PublicKey.pvBuffer)[0]; + public_key2 = &((BYTE*)pubKeyAuth->pvBuffer)[pubKeyAuth->cbBuffer]; if (!nla->server) { @@ -1378,73 +1530,37 @@ SECURITY_STATUS nla_decrypt_public_key_echo(rdpNla* nla) status = SEC_E_OK; fail: - free(buffer); + return status; } SECURITY_STATUS nla_decrypt_public_key_hash(rdpNla* nla) { - unsigned long length; - BYTE* buffer = NULL; - ULONG pfQOP = 0; - int signature_length; - SecBuffer Buffers[2] = { { 0 } }; - SecBufferDesc Message; + size_t signature_length; WINPR_DIGEST_CTX* sha256 = NULL; BYTE serverClientHash[WINPR_SHA256_DIGEST_LENGTH]; SECURITY_STATUS status = SEC_E_INVALID_TOKEN; - const BOOL krb = - (_tcsncmp(nla->packageName, KERBEROS_SSP_NAME, ARRAYSIZE(KERBEROS_SSP_NAME)) == 0); const BYTE* hashMagic = nla->server ? ClientServerHashMagic : ServerClientHashMagic; + const BYTE* decryptedHash; const size_t hashSize = nla->server ? sizeof(ClientServerHashMagic) : sizeof(ServerClientHashMagic); - signature_length = nla->pubKeyAuth.cbBuffer - WINPR_SHA256_DIGEST_LENGTH; + PSecBuffer pubKeyAuth = &nla->pubKeyAuth; - if ((signature_length < 0) || (signature_length > (int)nla->ContextSizes.cbSecurityTrailer)) + signature_length = pubKeyAuth->cbBuffer - WINPR_SHA256_DIGEST_LENGTH; + + if ((pubKeyAuth->cbBuffer < WINPR_SHA256_DIGEST_LENGTH) || + (signature_length > nla->ContextSizes.cbSecurityTrailer)) { - WLog_ERR(TAG, "unexpected pubKeyAuth buffer size: %" PRIu32 "", nla->pubKeyAuth.cbBuffer); + WLog_ERR(TAG, "unexpected pubKeyAuth buffer size: %" PRIu32 "", pubKeyAuth->cbBuffer); goto fail; } - length = nla->pubKeyAuth.cbBuffer; - buffer = (BYTE*)malloc(length); - - if (!buffer) - { - status = SEC_E_INSUFFICIENT_MEMORY; - goto fail; - } - - if (krb) - { - CopyMemory(buffer, nla->pubKeyAuth.pvBuffer, length); - Buffers[0].BufferType = SECBUFFER_DATA; /* Encrypted Hash */ - Buffers[0].cbBuffer = length; - Buffers[0].pvBuffer = buffer; - Message.cBuffers = 1; - } - else - { - CopyMemory(buffer, nla->pubKeyAuth.pvBuffer, length); - Buffers[0].BufferType = SECBUFFER_TOKEN; /* Signature */ - Buffers[0].cbBuffer = signature_length; - Buffers[0].pvBuffer = buffer; - Buffers[1].BufferType = SECBUFFER_DATA; /* Encrypted Hash */ - Buffers[1].cbBuffer = WINPR_SHA256_DIGEST_LENGTH; - Buffers[1].pvBuffer = buffer + signature_length; - Message.cBuffers = 2; - } - - Message.ulVersion = SECBUFFER_VERSION; - Message.pBuffers = (PSecBuffer)&Buffers; - status = nla->table->DecryptMessage(&nla->context, &Message, nla->recvSeqNum++, &pfQOP); - + status = nla_decrypt(nla, pubKeyAuth, signature_length); if (status != SEC_E_OK) - { - WLog_ERR(TAG, "DecryptMessage failure %s [%08" PRIX32 "]", GetSecurityStatusString(status), - status); goto fail; - } + + status = SEC_E_INTERNAL_ERROR; + decryptedHash = &((const BYTE*)pubKeyAuth->pvBuffer)[signature_length]; /* generate SHA256 of following data: ServerClientHashMagic, Nonce, SubjectPublicKey */ if (!(sha256 = winpr_Digest_New())) @@ -1457,18 +1573,18 @@ SECURITY_STATUS nla_decrypt_public_key_hash(rdpNla* nla) if (!winpr_Digest_Update(sha256, hashMagic, hashSize)) goto fail; - if (!winpr_Digest_Update(sha256, nla->ClientNonce.pvBuffer, nla->ClientNonce.cbBuffer)) + if (!nla_Digest_Update_From_SecBuffer(sha256, &nla->ClientNonce)) goto fail; /* SubjectPublicKey */ - if (!winpr_Digest_Update(sha256, nla->PublicKey.pvBuffer, nla->PublicKey.cbBuffer)) + if (!nla_Digest_Update_From_SecBuffer(sha256, &nla->PublicKey)) goto fail; if (!winpr_Digest_Final(sha256, serverClientHash, sizeof(serverClientHash))) goto fail; /* verify hash */ - if (memcmp(serverClientHash, Buffers[krb ? 0 : 1].pvBuffer, WINPR_SHA256_DIGEST_LENGTH) != 0) + if (memcmp(serverClientHash, decryptedHash, WINPR_SHA256_DIGEST_LENGTH) != 0) { WLog_ERR(TAG, "Could not verify server's hash"); status = SEC_E_MESSAGE_ALTERED; /* DO NOT SEND CREDENTIALS! */ @@ -1477,7 +1593,6 @@ SECURITY_STATUS nla_decrypt_public_key_hash(rdpNla* nla) status = SEC_E_OK; fail: - free(buffer); winpr_Digest_Free(sha256); return status; } @@ -1620,17 +1735,17 @@ static size_t nla_write_ts_password_creds(rdpNla* nla, wStream* s) return size; } -static BOOL nla_read_ts_credentials(rdpNla* nla, PSecBuffer ts_credentials) +static BOOL nla_read_ts_credentials(rdpNla* nla, SecBuffer* data, size_t offset) { wStream* s; size_t length; size_t ts_password_creds_length = 0; - BOOL ret; + BOOL ret = FALSE; - if (!ts_credentials || !ts_credentials->pvBuffer) + if (!data) return FALSE; - s = Stream_New(ts_credentials->pvBuffer, ts_credentials->cbBuffer); + s = Stream_New(data->pvBuffer, data->cbBuffer); if (!s) { @@ -1638,6 +1753,9 @@ static BOOL nla_read_ts_credentials(rdpNla* nla, PSecBuffer ts_credentials) return FALSE; } + if (!Stream_SafeSeek(s, offset)) + goto fail; + /* TSCredentials (SEQUENCE) */ ret = ber_read_sequence_tag(s, &length) && /* [0] credType (INTEGER) */ @@ -1646,6 +1764,7 @@ static BOOL nla_read_ts_credentials(rdpNla* nla, PSecBuffer ts_credentials) ber_read_contextual_tag(s, 1, &length, TRUE) && ber_read_octet_string_tag(s, &ts_password_creds_length) && nla_read_ts_password_creds(nla, s); +fail: Stream_Free(s, FALSE); return ret; } @@ -1677,9 +1796,9 @@ static BOOL nla_encode_ts_credentials(rdpNla* nla) { wStream* s; size_t length; - int DomainLength = 0; - int UserLength = 0; - int PasswordLength = 0; + UINT32 DomainLength = 0; + UINT32 UserLength = 0; + UINT32 PasswordLength = 0; if (nla->identity) { @@ -1699,7 +1818,7 @@ static BOOL nla_encode_ts_credentials(rdpNla* nla) length = ber_sizeof_sequence(nla_sizeof_ts_credentials(nla)); - if (!sspi_SecBufferAlloc(&nla->tsCredentials, length)) + if (!nla_sec_buffer_alloc(&nla->tsCredentials, length)) { WLog_ERR(TAG, "sspi_SecBufferAlloc failed!"); return FALSE; @@ -1730,77 +1849,25 @@ static BOOL nla_encode_ts_credentials(rdpNla* nla) static SECURITY_STATUS nla_encrypt_ts_credentials(rdpNla* nla) { - SecBuffer Buffers[2] = { { 0 } }; - SecBufferDesc Message; SECURITY_STATUS status; - const BOOL krb = - (_tcsncmp(nla->packageName, KERBEROS_SSP_NAME, ARRAYSIZE(KERBEROS_SSP_NAME)) == 0); - const BOOL nego = (_tcsncmp(nla->packageName, NEGO_SSP_NAME, ARRAYSIZE(NEGO_SSP_NAME)) == 0); - const BOOL ntlm = (_tcsncmp(nla->packageName, NTLM_SSP_NAME, ARRAYSIZE(NTLM_SSP_NAME)) == 0); if (!nla_encode_ts_credentials(nla)) return SEC_E_INSUFFICIENT_MEMORY; - if (!sspi_SecBufferAlloc(&nla->authInfo, - nla->tsCredentials.cbBuffer + nla->ContextSizes.cbSecurityTrailer)) + if (!nla_sec_buffer_alloc_from_buffer(&nla->authInfo, &nla->tsCredentials, + nla->ContextSizes.cbSecurityTrailer)) return SEC_E_INSUFFICIENT_MEMORY; - if (krb) - { - Buffers[0].BufferType = SECBUFFER_DATA; /* TSCredentials */ - Buffers[0].cbBuffer = nla->tsCredentials.cbBuffer; - Buffers[0].pvBuffer = nla->authInfo.pvBuffer; - CopyMemory(Buffers[0].pvBuffer, nla->tsCredentials.pvBuffer, Buffers[0].cbBuffer); - Message.cBuffers = 1; - } - else if (ntlm || nego) - { - Buffers[0].BufferType = SECBUFFER_TOKEN; /* Signature */ - Buffers[0].cbBuffer = nla->ContextSizes.cbSecurityTrailer; - Buffers[0].pvBuffer = nla->authInfo.pvBuffer; - MoveMemory(Buffers[0].pvBuffer, nla->authInfo.pvBuffer, Buffers[0].cbBuffer); - Buffers[1].BufferType = SECBUFFER_DATA; /* TSCredentials */ - Buffers[1].cbBuffer = nla->tsCredentials.cbBuffer; - Buffers[1].pvBuffer = &((BYTE*)nla->authInfo.pvBuffer)[Buffers[0].cbBuffer]; - CopyMemory(Buffers[1].pvBuffer, nla->tsCredentials.pvBuffer, Buffers[1].cbBuffer); - Message.cBuffers = 2; - } - - Message.ulVersion = SECBUFFER_VERSION; - Message.pBuffers = (PSecBuffer)&Buffers; - status = nla->table->EncryptMessage(&nla->context, 0, &Message, nla->sendSeqNum++); - + status = nla_encrypt(nla, &nla->authInfo, nla->ContextSizes.cbSecurityTrailer); if (status != SEC_E_OK) - { - WLog_ERR(TAG, "EncryptMessage failure %s [0x%08" PRIX32 "]", - GetSecurityStatusString(status), status); return status; - } - - if (Message.cBuffers == 2 && Buffers[0].cbBuffer < nla->ContextSizes.cbSecurityTrailer) - { - /* IMPORTANT: EncryptMessage may not use all the signature space, so we need to shrink the - * excess between the buffers */ - MoveMemory(((BYTE*)Buffers[0].pvBuffer) + Buffers[0].cbBuffer, Buffers[1].pvBuffer, - Buffers[1].cbBuffer); - nla->authInfo.cbBuffer = Buffers[0].cbBuffer + Buffers[1].cbBuffer; - } return SEC_E_OK; } static SECURITY_STATUS nla_decrypt_ts_credentials(rdpNla* nla) { - int length; - BYTE* buffer; - ULONG pfQOP; - SecBuffer Buffers[2] = { { 0 } }; - SecBufferDesc Message; SECURITY_STATUS status; - const BOOL krb = - (_tcsncmp(nla->packageName, KERBEROS_SSP_NAME, ARRAYSIZE(KERBEROS_SSP_NAME)) == 0); - const BOOL nego = (_tcsncmp(nla->packageName, NEGO_SSP_NAME, ARRAYSIZE(NEGO_SSP_NAME)) == 0); - const BOOL ntlm = (_tcsncmp(nla->packageName, NTLM_SSP_NAME, ARRAYSIZE(NTLM_SSP_NAME)) == 0); if (nla->authInfo.cbBuffer < 1) { @@ -1808,51 +1875,13 @@ static SECURITY_STATUS nla_decrypt_ts_credentials(rdpNla* nla) return SEC_E_INVALID_TOKEN; } - length = nla->authInfo.cbBuffer; - buffer = (BYTE*)malloc(length); - - if (!buffer) - return SEC_E_INSUFFICIENT_MEMORY; - - if (krb) - { - CopyMemory(buffer, nla->authInfo.pvBuffer, length); - Buffers[0].BufferType = SECBUFFER_DATA; /* Wrapped and encrypted TSCredentials */ - Buffers[0].cbBuffer = length; - Buffers[0].pvBuffer = buffer; - Message.cBuffers = 1; - } - else if (ntlm || nego) - { - CopyMemory(buffer, nla->authInfo.pvBuffer, length); - Buffers[0].BufferType = SECBUFFER_TOKEN; /* Signature */ - Buffers[0].cbBuffer = nla->ContextSizes.cbSecurityTrailer; - Buffers[0].pvBuffer = buffer; - Buffers[1].BufferType = SECBUFFER_DATA; /* TSCredentials */ - Buffers[1].cbBuffer = length - nla->ContextSizes.cbSecurityTrailer; - Buffers[1].pvBuffer = &buffer[Buffers[0].cbBuffer]; - Message.cBuffers = 2; - } - - Message.ulVersion = SECBUFFER_VERSION; - Message.pBuffers = (PSecBuffer)&Buffers; - status = nla->table->DecryptMessage(&nla->context, &Message, nla->recvSeqNum++, &pfQOP); - + status = nla_decrypt(nla, &nla->authInfo, nla->ContextSizes.cbSecurityTrailer); if (status != SEC_E_OK) - { - WLog_ERR(TAG, "DecryptMessage failure %s [0x%08" PRIX32 "]", - GetSecurityStatusString(status), status); - free(buffer); return status; - } - if (!nla_read_ts_credentials(nla, &Buffers[1])) - { - free(buffer); + if (!nla_read_ts_credentials(nla, &nla->authInfo, nla->ContextSizes.cbSecurityTrailer)) return SEC_E_INSUFFICIENT_MEMORY; - } - free(buffer); return SEC_E_OK; } @@ -1863,8 +1892,11 @@ static size_t nla_sizeof_nego_token(size_t length) return length; } -static size_t nla_sizeof_nego_tokens(size_t length) +static size_t nla_sizeof_nego_tokens(const SecBuffer* buffer) { + size_t length = buffer->cbBuffer; + if (length == 0) + return 0; length = nla_sizeof_nego_token(length); length += ber_sizeof_sequence_tag(length); length += ber_sizeof_sequence_tag(length); @@ -1872,22 +1904,31 @@ static size_t nla_sizeof_nego_tokens(size_t length) return length; } -static size_t nla_sizeof_pub_key_auth(size_t length) +static size_t nla_sizeof_pub_key_auth(const SecBuffer* buffer) { + size_t length = buffer->cbBuffer; + if (length == 0) + return 0; length = ber_sizeof_octet_string(length); length += ber_sizeof_contextual_tag(length); return length; } -static size_t nla_sizeof_auth_info(size_t length) +static size_t nla_sizeof_auth_info(const SecBuffer* buffer) { + size_t length = buffer->cbBuffer; + if (length == 0) + return 0; length = ber_sizeof_octet_string(length); length += ber_sizeof_contextual_tag(length); return length; } -static size_t nla_sizeof_client_nonce(size_t length) +static size_t nla_sizeof_client_nonce(const SecBuffer* buffer) { + size_t length = buffer->cbBuffer; + if (length == 0) + return 0; length = ber_sizeof_octet_string(length); length += ber_sizeof_contextual_tag(length); return length; @@ -1900,34 +1941,54 @@ static size_t nla_sizeof_ts_request(size_t length) return length; } +static BOOL nla_client_write_nego_token(wStream* s, const SecBuffer* negoToken) +{ + const size_t nego_tokens_length = nla_sizeof_nego_tokens(negoToken); + + if (Stream_GetRemainingCapacity(s) < nego_tokens_length) + return FALSE; + + if (nego_tokens_length > 0) + { + size_t length; + + WLog_DBG(TAG, " ----->> nego token"); + length = + ber_write_contextual_tag(s, 1, + ber_sizeof_sequence(ber_sizeof_sequence( + ber_sizeof_sequence_octet_string(negoToken->cbBuffer))), + TRUE); /* NegoData */ + length += + ber_write_sequence_tag(s, ber_sizeof_sequence(ber_sizeof_sequence_octet_string( + negoToken->cbBuffer))); /* SEQUENCE OF NegoDataItem */ + length += ber_write_sequence_tag( + s, ber_sizeof_sequence_octet_string(negoToken->cbBuffer)); /* NegoDataItem */ + length += + ber_write_sequence_octet_string_from_secbuffer(s, 0, negoToken); /* OCTET STRING */ + + if (length != nego_tokens_length) + return FALSE; + } + + return TRUE; +} /** * Send CredSSP message. * @param credssp */ -BOOL nla_send(rdpNla* nla, const char* msg) +BOOL nla_send(rdpNla* nla) { - BOOL rc = TRUE; + BOOL rc = FALSE; wStream* s; size_t length; size_t ts_request_length; - size_t nego_tokens_length = 0; - size_t pub_key_auth_length = 0; - size_t auth_info_length = 0; size_t error_code_context_length = 0; size_t error_code_length = 0; - size_t client_nonce_length = 0; - WLog_DBG(TAG, "%s", msg); - nla_buffer_print(nla); - - nego_tokens_length = - (nla->negoToken.cbBuffer > 0) ? nla_sizeof_nego_tokens(nla->negoToken.cbBuffer) : 0; - pub_key_auth_length = - (nla->pubKeyAuth.cbBuffer > 0) ? nla_sizeof_pub_key_auth(nla->pubKeyAuth.cbBuffer) : 0; - auth_info_length = - (nla->authInfo.cbBuffer > 0) ? nla_sizeof_auth_info(nla->authInfo.cbBuffer) : 0; - client_nonce_length = - (nla->ClientNonce.cbBuffer > 0) ? nla_sizeof_client_nonce(nla->ClientNonce.cbBuffer) : 0; + const size_t nego_tokens_length = nla_sizeof_nego_tokens(&nla->negoToken); + const size_t pub_key_auth_length = nla_sizeof_pub_key_auth(&nla->pubKeyAuth); + const size_t auth_info_length = nla_sizeof_auth_info(&nla->authInfo); + const size_t client_nonce_length = nla_sizeof_client_nonce(&nla->ClientNonce); if (nla->peerVersion >= 3 && nla->peerVersion != 5 && nla->errorCode != 0) { @@ -1946,60 +2007,43 @@ BOOL nla_send(rdpNla* nla, const char* msg) return FALSE; } + WLog_DBG(TAG, "----->> sending..."); /* TSRequest */ ber_write_sequence_tag(s, ts_request_length); /* SEQUENCE */ /* [0] version */ ber_write_contextual_tag(s, 0, 3, TRUE); + + WLog_DBG(TAG, " ----->> protocol version %" PRIu32, nla->version); ber_write_integer(s, nla->version); /* INTEGER */ /* [1] negoTokens (NegoData) */ - if (nego_tokens_length > 0) - { - length = ber_write_contextual_tag( - s, 1, - ber_sizeof_sequence( - ber_sizeof_sequence(ber_sizeof_sequence_octet_string(nla->negoToken.cbBuffer))), - TRUE); /* NegoData */ - length += - ber_write_sequence_tag(s, ber_sizeof_sequence(ber_sizeof_sequence_octet_string( - nla->negoToken.cbBuffer))); /* SEQUENCE OF NegoDataItem */ - length += ber_write_sequence_tag( - s, ber_sizeof_sequence_octet_string(nla->negoToken.cbBuffer)); /* NegoDataItem */ - length += ber_write_sequence_octet_string(s, 0, (BYTE*)nla->negoToken.pvBuffer, - nla->negoToken.cbBuffer); /* OCTET STRING */ - - if (length != nego_tokens_length) - { - Stream_Free(s, TRUE); - return FALSE; - } - } + if (!nla_client_write_nego_token(s, &nla->negoToken)) + goto fail; /* [2] authInfo (OCTET STRING) */ if (auth_info_length > 0) { - if (ber_write_sequence_octet_string(s, 2, nla->authInfo.pvBuffer, nla->authInfo.cbBuffer) != + WLog_DBG(TAG, " ----->> auth info"); + if (ber_write_sequence_octet_string_from_secbuffer(s, 2, &nla->authInfo) != auth_info_length) - { - Stream_Free(s, TRUE); - return FALSE; - } + goto fail; } /* [3] pubKeyAuth (OCTET STRING) */ if (pub_key_auth_length > 0) { - if (ber_write_sequence_octet_string(s, 3, nla->pubKeyAuth.pvBuffer, - nla->pubKeyAuth.cbBuffer) != pub_key_auth_length) - { - Stream_Free(s, TRUE); - return FALSE; - } + WLog_DBG(TAG, " ----->> public key auth"); + if (ber_write_sequence_octet_string_from_secbuffer(s, 3, &nla->pubKeyAuth) != + pub_key_auth_length) + goto fail; } /* [4] errorCode (INTEGER) */ if (error_code_length > 0) { + char buffer[1024]; + WLog_DBG(TAG, " ----->> error code %s 0x%08" PRIx32, + winpr_strerror(nla->errorCode, buffer, sizeof(buffer)), nla->errorCode); ber_write_contextual_tag(s, 4, error_code_length, TRUE); ber_write_integer(s, nla->errorCode); } @@ -2007,97 +2051,78 @@ BOOL nla_send(rdpNla* nla, const char* msg) /* [5] clientNonce (OCTET STRING) */ if (client_nonce_length > 0) { - if (ber_write_sequence_octet_string(s, 5, nla->ClientNonce.pvBuffer, - nla->ClientNonce.cbBuffer) != client_nonce_length) - { - Stream_Free(s, TRUE); - return FALSE; - } + WLog_DBG(TAG, " ----->> client nonce"); + if (ber_write_sequence_octet_string_from_secbuffer(s, 5, &nla->ClientNonce) != + client_nonce_length) + goto fail; } - Stream_SealLength(s); - + WLog_DBG(TAG, "[%" PRIuz " bytes]", Stream_GetPosition(s)); if (transport_write(nla->transport, s) < 0) - rc = FALSE; + goto fail; + rc = TRUE; +fail: Stream_Free(s, TRUE); return rc; } static int nla_decode_ts_request(rdpNla* nla, wStream* s) { + int rc = -1; size_t length; UINT32 version = 0; + WLog_DBG(TAG, "<<----- receiving..."); + /* TSRequest */ if (!ber_read_sequence_tag(s, &length) || !ber_read_contextual_tag(s, 0, &length, TRUE) || !ber_read_integer(s, &version)) { - return -1; + goto fail; } + WLog_DBG(TAG, " <<----- protocol version %" PRIu32, version); if (nla->peerVersion == 0) - { - WLog_DBG(TAG, "CredSSP protocol support %" PRIu32 ", peer supports %" PRIu32, nla->version, - version); nla->peerVersion = version; - } /* if the peer suddenly changed its version - kick it */ if (nla->peerVersion != version) { WLog_ERR(TAG, "CredSSP peer changed protocol version from %" PRIu32 " to %" PRIu32, nla->peerVersion, version); - return -1; + goto fail; } /* [1] negoTokens (NegoData) */ if (ber_read_contextual_tag(s, 1, &length, TRUE) != FALSE) { - if (!ber_read_sequence_tag(s, &length) || /* SEQUENCE OF NegoDataItem */ - !ber_read_sequence_tag(s, &length) || /* NegoDataItem */ - !ber_read_contextual_tag(s, 0, &length, TRUE) || /* [0] negoToken */ - !ber_read_octet_string_tag(s, &length) || /* OCTET STRING */ - Stream_GetRemainingLength(s) < length) + WLog_DBG(TAG, " <<----- nego token"); + if (!ber_read_sequence_tag(s, &length) || /* SEQUENCE OF NegoDataItem */ + !ber_read_sequence_tag(s, &length) || /* NegoDataItem */ + !ber_read_contextual_tag(s, 0, &length, TRUE)) { - return -1; + goto fail; } - sspi_SecBufferFree(&nla->negoToken); - if (!sspi_SecBufferAlloc(&nla->negoToken, length)) - return -1; - - Stream_Read(s, nla->negoToken.pvBuffer, length); - nla->negoToken.cbBuffer = length; + if (!nla_decode_to_buffer(s, &nla->negoToken)) + goto fail; } /* [2] authInfo (OCTET STRING) */ if (ber_read_contextual_tag(s, 2, &length, TRUE) != FALSE) { - if (!ber_read_octet_string_tag(s, &length) || /* OCTET STRING */ - Stream_GetRemainingLength(s) < length) - return -1; - - if (!sspi_SecBufferAlloc(&nla->authInfo, length)) - return -1; - - Stream_Read(s, nla->authInfo.pvBuffer, length); - nla->authInfo.cbBuffer = length; + WLog_DBG(TAG, " <<----- auth info"); + if (!nla_decode_to_buffer(s, &nla->authInfo)) + goto fail; } /* [3] pubKeyAuth (OCTET STRING) */ if (ber_read_contextual_tag(s, 3, &length, TRUE) != FALSE) { - if (!ber_read_octet_string_tag(s, &length) || /* OCTET STRING */ - Stream_GetRemainingLength(s) < length) - return -1; - - sspi_SecBufferFree(&nla->pubKeyAuth); - if (!sspi_SecBufferAlloc(&nla->pubKeyAuth, length)) - return -1; - - Stream_Read(s, nla->pubKeyAuth.pvBuffer, length); - nla->pubKeyAuth.cbBuffer = length; + WLog_DBG(TAG, " <<----- public key info"); + if (!nla_decode_to_buffer(s, &nla->pubKeyAuth)) + goto fail; } /* [4] errorCode (INTEGER) */ @@ -2105,31 +2130,27 @@ static int nla_decode_ts_request(rdpNla* nla, wStream* s) { if (ber_read_contextual_tag(s, 4, &length, TRUE) != FALSE) { + char buffer[1024]; if (!ber_read_integer(s, &nla->errorCode)) - return -1; - WLog_WARN(TAG, "SPNEGO received NTSTATUS: %s [0x%08" PRIX32 "] from server", - GetSecurityStatusString(nla->errorCode), nla->errorCode); + goto fail; + WLog_DBG(TAG, " <<----- error code %s 0x%08" PRIx32, + winpr_strerror(nla->errorCode, buffer, sizeof(buffer)), nla->errorCode); } if (nla->peerVersion >= 5) { if (ber_read_contextual_tag(s, 5, &length, TRUE) != FALSE) { - if (!ber_read_octet_string_tag(s, &length) || /* OCTET STRING */ - Stream_GetRemainingLength(s) < length) - return -1; - - sspi_SecBufferFree(&nla->ClientNonce); - if (!sspi_SecBufferAlloc(&nla->ClientNonce, length)) - return -1; - - Stream_Read(s, nla->ClientNonce.pvBuffer, length); - nla->ClientNonce.cbBuffer = length; + WLog_DBG(TAG, " <<----- client nonce"); + if (!nla_decode_to_buffer(s, &nla->ClientNonce)) + goto fail; } } } + rc = 1; +fail: - return 1; + return rc; } int nla_recv_pdu(rdpNla* nla, wStream* s) @@ -2184,8 +2205,7 @@ int nla_recv_pdu(rdpNla* nla, wStream* s) break; default: - WLog_ERR(TAG, "SPNEGO failed with NTSTATUS: %s [0x%08" PRIX32 "]", - GetSecurityStatusString(nla->errorCode), nla->errorCode); + WLog_ERR(TAG, "SPNEGO failed with NTSTATUS: 0x%08" PRIX32 "", nla->errorCode); code = FREERDP_ERROR_AUTHENTICATION_FAILED; break; } @@ -2194,71 +2214,20 @@ int nla_recv_pdu(rdpNla* nla, wStream* s) return -1; } - if (nla_client_recv(nla) < 1) - return -1; - - return 1; + return nla_client_recv(nla); } -int nla_recv(rdpNla* nla, const char* msg) +int nla_server_recv(rdpNla* nla) { - wStream* s; int status; - s = Stream_New(NULL, 4096); - + wStream* s = nla_server_recv_stream(nla); if (!s) - { - WLog_ERR(TAG, "[%s] Stream_New failed!", __FUNCTION__); - return -1; - } - - status = transport_read_pdu(nla->transport, s); - - if (status < 0) - { - WLog_ERR(TAG, "[%s] error: %d", __FUNCTION__, status); - Stream_Free(s, TRUE); - return -1; - } - - if (nla_decode_ts_request(nla, s) < 1) - { - WLog_ERR(TAG, "[%s] Invalid data received, aborting", __FUNCTION__); - Stream_Free(s, TRUE); - return -1; - } + goto fail; + status = nla_decode_ts_request(nla, s); +fail: Stream_Free(s, TRUE); - WLog_DBG(TAG, "[%s] %s", __FUNCTION__, msg); - nla_buffer_print(nla); - return 1; -} - -void nla_buffer_print(rdpNla* nla) -{ - if (nla->negoToken.cbBuffer > 0) - { - WLog_DBG(TAG, "NLA.negoToken (length = %" PRIu32 "):", nla->negoToken.cbBuffer); -#if defined(WITH_DEBUG_NLA) - winpr_HexDump(TAG, WLOG_DEBUG, nla->negoToken.pvBuffer, nla->negoToken.cbBuffer); -#endif - } - - if (nla->pubKeyAuth.cbBuffer > 0) - { - WLog_DBG(TAG, "NLA.pubKeyAuth (length = %" PRIu32 "):", nla->pubKeyAuth.cbBuffer); -#if defined(WITH_DEBUG_NLA) - winpr_HexDump(TAG, WLOG_DEBUG, nla->pubKeyAuth.pvBuffer, nla->pubKeyAuth.cbBuffer); -#endif - } - - if (nla->authInfo.cbBuffer > 0) - { - WLog_DBG(TAG, "NLA.authInfo (length = %" PRIu32 "):", nla->authInfo.cbBuffer); -#if defined(WITH_DEBUG_NLA) - winpr_HexDump(TAG, WLOG_DEBUG, nla->authInfo.pvBuffer, nla->authInfo.cbBuffer); -#endif - } + return status; } void nla_buffer_free(rdpNla* nla) @@ -2270,7 +2239,7 @@ void nla_buffer_free(rdpNla* nla) LPTSTR nla_make_spn(const char* ServiceClass, const char* hostname) { - DWORD status; + DWORD status = ERROR_INTERNAL_ERROR; DWORD SpnLength; LPTSTR hostnameX = NULL; LPTSTR ServiceClassX = NULL; @@ -2284,49 +2253,37 @@ LPTSTR nla_make_spn(const char* ServiceClass, const char* hostname) #endif if (!hostnameX || !ServiceClassX) - { - free(hostnameX); - free(ServiceClassX); - return NULL; - } + goto fail; if (!ServiceClass) { ServicePrincipalName = (LPTSTR)_tcsdup(hostnameX); - free(ServiceClassX); - free(hostnameX); - return ServicePrincipalName; + status = ERROR_SUCCESS; + goto fail; } SpnLength = 0; status = DsMakeSpn(ServiceClassX, hostnameX, NULL, 0, NULL, &SpnLength, NULL); if (status != ERROR_BUFFER_OVERFLOW) - { - free(ServiceClassX); - free(hostnameX); - return NULL; - } + goto fail; ServicePrincipalName = (LPTSTR)calloc(SpnLength, sizeof(TCHAR)); if (!ServicePrincipalName) - { - free(ServiceClassX); - free(hostnameX); - return NULL; - } + goto fail; status = DsMakeSpn(ServiceClassX, hostnameX, NULL, 0, NULL, &SpnLength, ServicePrincipalName); + if (status != ERROR_SUCCESS) + goto fail; + +fail: if (status != ERROR_SUCCESS) { free(ServicePrincipalName); - free(ServiceClassX); - free(hostnameX); - return NULL; + ServicePrincipalName = NULL; } - free(ServiceClassX); free(hostnameX); return ServicePrincipalName; @@ -2371,7 +2328,7 @@ rdpNla* nla_new(freerdp* instance, rdpTransport* transport, rdpSettings* setting } /* init to 0 or we end up freeing a bad pointer if the alloc fails */ - if (!sspi_SecBufferAlloc(&nla->ClientNonce, NonceLength)) + if (!nla_sec_buffer_alloc(&nla->ClientNonce, NonceLength)) goto cleanup; /* generate random 32-byte nonce */ @@ -2459,12 +2416,15 @@ void nla_free(rdpNla* nla) free(nla->SamFile); nla->SamFile = NULL; + + nla_buffer_free(nla); sspi_SecBufferFree(&nla->ClientNonce); sspi_SecBufferFree(&nla->PublicKey); sspi_SecBufferFree(&nla->tsCredentials); + free(nla->ServicePrincipalName); nla_identity_free(nla->identity); - nla_buffer_free(nla); + nla_set_package_name(nla, NULL); free(nla); } @@ -2489,6 +2449,7 @@ BOOL nla_set_state(rdpNla* nla, NLA_STATE state) if (!nla) return FALSE; + WLog_DBG(TAG, "-- %s\t--> %s", nla_get_state_str(nla->state), nla_get_state_str(state)); nla->state = state; return TRUE; } @@ -2523,3 +2484,31 @@ BOOL nla_revert_to_self(rdpNla* nla) return (nla->table->RevertSecurityContext(&nla->context) == SEC_E_OK); } + +const char* nla_get_state_str(NLA_STATE state) +{ + switch (state) + { + case NLA_STATE_INITIAL: + return "NLA_STATE_INITIAL"; + case NLA_STATE_NEGO_TOKEN: + return "NLA_STATE_NEGO_TOKEN"; + case NLA_STATE_PUB_KEY_AUTH: + return "NLA_STATE_PUB_KEY_AUTH"; + case NLA_STATE_AUTH_INFO: + return "NLA_STATE_AUTH_INFO"; + case NLA_STATE_POST_NEGO: + return "NLA_STATE_POST_NEGO"; + case NLA_STATE_FINAL: + return "NLA_STATE_FINAL"; + default: + return "UNKNOWN"; + } +} + +DWORD nla_get_error(rdpNla* nla) +{ + if (!nla) + return ERROR_INTERNAL_ERROR; + return nla->errorCode; +} diff --git a/libfreerdp/core/nla.h b/libfreerdp/core/nla.h index 30079cb34..a36a71abb 100644 --- a/libfreerdp/core/nla.h +++ b/libfreerdp/core/nla.h @@ -57,6 +57,9 @@ FREERDP_LOCAL SEC_WINNT_AUTH_IDENTITY* nla_get_identity(rdpNla* nla); FREERDP_LOCAL NLA_STATE nla_get_state(rdpNla* nla); FREERDP_LOCAL BOOL nla_set_state(rdpNla* nla, NLA_STATE state); +FREERDP_LOCAL const char* nla_get_state_str(NLA_STATE state); + +FREERDP_LOCAL DWORD nla_get_error(rdpNla* nla); FREERDP_LOCAL BOOL nla_set_service_principal(rdpNla* nla, LPTSTR principal);