From 4bfb4dddbf3034486f6b54384701dda345fbcbba Mon Sep 17 00:00:00 2001 From: davewheel Date: Mon, 15 May 2017 18:52:39 +0200 Subject: [PATCH] Add a callback to provide NTLM hashes on server-side Adds a callback that allows servers to compute NTLM hashes by themselves. The typical use of this callback is to provide a function that gives precomputed hash values. Sponsored by: Wheel Systems (http://www.wheelsystems.com) --- include/freerdp/peer.h | 3 + libfreerdp/core/nla.c | 28 +++++++-- winpr/include/winpr/ntlm.h | 5 ++ winpr/include/winpr/sspi.h | 2 + winpr/libwinpr/sspi/NTLM/ntlm.c | 101 +++++++++++++++++++------------- winpr/libwinpr/sspi/NTLM/ntlm.h | 5 ++ winpr/libwinpr/sspi/NTLM/ntlm_compute.c | 25 +++++++- winpr/libwinpr/sspi/sspi_winpr.c | 3 +- 8 files changed, 122 insertions(+), 50 deletions(-) diff --git a/include/freerdp/peer.h b/include/freerdp/peer.h index 20d04b3..5be2140 100644 --- a/include/freerdp/peer.h +++ b/include/freerdp/peer.h @@ -28,6 +28,8 @@ #include #include +#include + typedef BOOL (*psPeerContextNew)(freerdp_peer* peer, rdpContext* context); typedef void (*psPeerContextFree)(freerdp_peer* peer, rdpContext* context); @@ -118,6 +120,7 @@ struct rdp_freerdp_peer psPeerGetEventHandles GetEventHandles; psPeerAdjustMonitorsLayout AdjustMonitorsLayout; psPeerClientCapabilities ClientCapabilities; + psPeerComputeNtlmHash ComputeNtlmHash; }; #ifdef __cplusplus diff --git a/libfreerdp/core/nla.c b/libfreerdp/core/nla.c index 5ada7db..1970e1a 100644 --- a/libfreerdp/core/nla.c +++ b/libfreerdp/core/nla.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -715,10 +716,28 @@ int nla_server_authenticate(rdpNla* nla) if ((nla->status == SEC_I_COMPLETE_AND_CONTINUE) || (nla->status == SEC_I_COMPLETE_NEEDED)) { - if (nla->SamFile) + freerdp_peer *peer = nla->instance->context->peer; + + if (peer->ComputeNtlmHash) + { + SECURITY_STATUS status; + + status = nla->table->SetContextAttributes(&nla->context, SECPKG_ATTR_AUTH_NTLM_HASH_CB, peer->ComputeNtlmHash, 0); + if (status != SEC_E_OK) + { + WLog_ERR(TAG, "SetContextAttributesA(hash cb) status %s [0x%08"PRIX32"]", GetSecurityStatusString(status), status); + } + + status = nla->table->SetContextAttributes(&nla->context, SECPKG_ATTR_AUTH_NTLM_HASH_CB_DATA, peer, 0); + if (status != SEC_E_OK) + { + WLog_ERR(TAG, "SetContextAttributesA(hash cb data) status %s [0x%08"PRIX32"]", GetSecurityStatusString(status), status); + } + } + else if (nla->SamFile) { - nla->table->SetContextAttributes(&nla->context, - SECPKG_ATTR_AUTH_NTLM_SAM_FILE, nla->SamFile, strlen(nla->SamFile) + 1); + nla->table->SetContextAttributes(&nla->context, SECPKG_ATTR_AUTH_NTLM_SAM_FILE, nla->SamFile, + strlen(nla->SamFile) + 1); } if (nla->table->CompleteAuthToken) @@ -758,7 +777,7 @@ int nla_server_authenticate(rdpNla* nla) } nla->havePubKeyAuth = TRUE; - nla->status = nla->table->QueryContextAttributes(&nla->context, SECPKG_ATTR_SIZES, + nla->status = nla->table->QueryContextAttributes(&nla->context, SECPKG_ATTR_SIZES, &nla->ContextSizes); if (nla->status != SEC_E_OK) @@ -1746,7 +1765,6 @@ rdpNla* nla_new(freerdp* instance, rdpTransport* transport, rdpSettings* setting return NULL; nla->identity = calloc(1, sizeof(SEC_WINNT_AUTH_IDENTITY)); - if (!nla->identity) { free(nla); diff --git a/winpr/include/winpr/ntlm.h b/winpr/include/winpr/ntlm.h index 8b62ebd..64b35b5 100644 --- a/winpr/include/winpr/ntlm.h +++ b/winpr/include/winpr/ntlm.h @@ -25,11 +25,16 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { #endif +typedef SECURITY_STATUS (*psPeerComputeNtlmHash)(void *client, const SEC_WINNT_AUTH_IDENTITY *authIdentity, + const SecBuffer *ntproofvalue, const BYTE *randkey, const BYTE *mic, const SecBuffer *micvalue, + BYTE *ntlmhash); + WINPR_API BYTE* NTOWFv1W(LPWSTR Password, UINT32 PasswordLength, BYTE* NtHash); WINPR_API BYTE* NTOWFv1A(LPSTR Password, UINT32 PasswordLength, BYTE* NtHash); diff --git a/winpr/include/winpr/sspi.h b/winpr/include/winpr/sspi.h index 790a6e7..53a033a 100644 --- a/winpr/include/winpr/sspi.h +++ b/winpr/include/winpr/sspi.h @@ -1014,6 +1014,8 @@ extern "C" { #define SECPKG_ATTR_AUTH_NTLM_RANDKEY 1105 #define SECPKG_ATTR_AUTH_NTLM_MIC 1106 #define SECPKG_ATTR_AUTH_NTLM_MIC_VALUE 1107 +#define SECPKG_ATTR_AUTH_NTLM_HASH_CB 1108 +#define SECPKG_ATTR_AUTH_NTLM_HASH_CB_DATA 1109 struct _SecPkgContext_AuthIdentity diff --git a/winpr/libwinpr/sspi/NTLM/ntlm.c b/winpr/libwinpr/sspi/NTLM/ntlm.c index 8adcee2..376eba8 100644 --- a/winpr/libwinpr/sspi/NTLM/ntlm.c +++ b/winpr/libwinpr/sspi/NTLM/ntlm.c @@ -675,6 +675,53 @@ SECURITY_STATUS SEC_ENTRY ntlm_DeleteSecurityContext(PCtxtHandle phContext) return SEC_E_OK; } +SECURITY_STATUS ntlm_computeProofValue(NTLM_CONTEXT *ntlm, SecBuffer *ntproof) +{ + BYTE* blob; + SecBuffer* target = &ntlm->ChallengeTargetInfo; + + if (!sspi_SecBufferAlloc(ntproof, 36 + target->cbBuffer)) + return SEC_E_INSUFFICIENT_MEMORY; + + blob = (BYTE *)ntproof->pvBuffer; + + CopyMemory(blob, ntlm->ServerChallenge, 8); /* Server challenge. */ + blob[8] = 1; /* Response version. */ + blob[9] = 1; /* Highest response version understood by the client. */ + /* Reserved 6B. */ + + CopyMemory(&blob[16], ntlm->Timestamp, 8); /* Time. */ + CopyMemory(&blob[24], ntlm->ClientChallenge, 8); /* Client challenge. */ + /* Reserved 4B. */ + /* Server name. */ + CopyMemory(&blob[36], target->pvBuffer, target->cbBuffer); + + return SEC_E_OK; + +} + +SECURITY_STATUS ntlm_computeMicValue(NTLM_CONTEXT *ntlm, SecBuffer *micvalue) +{ + BYTE* blob; + ULONG msgSize = ntlm->NegotiateMessage.cbBuffer + ntlm->ChallengeMessage.cbBuffer + + ntlm->AuthenticateMessage.cbBuffer; + + if (!sspi_SecBufferAlloc(micvalue, msgSize)) + return SEC_E_INSUFFICIENT_MEMORY; + + blob = (BYTE *) micvalue->pvBuffer; + CopyMemory(blob, ntlm->NegotiateMessage.pvBuffer, ntlm->NegotiateMessage.cbBuffer); + blob += ntlm->NegotiateMessage.cbBuffer; + CopyMemory(blob, ntlm->ChallengeMessage.pvBuffer, ntlm->ChallengeMessage.cbBuffer); + blob += ntlm->ChallengeMessage.cbBuffer; + CopyMemory(blob, ntlm->AuthenticateMessage.pvBuffer, ntlm->AuthenticateMessage.cbBuffer); + blob += ntlm->MessageIntegrityCheckOffset; + ZeroMemory(blob, 16); + + return SEC_E_OK; +} + + /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa379337/ */ SECURITY_STATUS SEC_ENTRY ntlm_QueryContextAttributesW(PCtxtHandle phContext, ULONG ulAttribute, @@ -739,30 +786,7 @@ SECURITY_STATUS SEC_ENTRY ntlm_QueryContextAttributesW(PCtxtHandle phContext, UL } else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_NTPROOF_VALUE) { - BYTE* blob; - SecBuffer* ntproof, *target; - ntproof = (SecBuffer*)pBuffer; - target = &context->ChallengeTargetInfo; - - if (!sspi_SecBufferAlloc(ntproof, 36 + target->cbBuffer)) - return (SEC_E_INSUFFICIENT_MEMORY); - - blob = (BYTE*)ntproof->pvBuffer; - /* Server challenge. */ - CopyMemory(blob, context->ServerChallenge, 8); - /* Response version. */ - blob[8] = 1; - /* Highest response version understood by the client. */ - blob[9] = 1; - /* Reserved 6B. */ - /* Time. */ - CopyMemory(&blob[16], context->Timestamp, 8); - /* Client challenge. */ - CopyMemory(&blob[24], context->ClientChallenge, 8); - /* Reserved 4B. */ - /* Server name. */ - CopyMemory(&blob[36], target->pvBuffer, target->cbBuffer); - return (SEC_E_OK); + return ntlm_computeProofValue(context, (SecBuffer*)pBuffer); } else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_RANDKEY) { @@ -790,24 +814,7 @@ SECURITY_STATUS SEC_ENTRY ntlm_QueryContextAttributesW(PCtxtHandle phContext, UL } else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_MIC_VALUE) { - BYTE* blob; - SecBuffer* micvalue; - ULONG msgSize = context->NegotiateMessage.cbBuffer + context->ChallengeMessage.cbBuffer + - context->AuthenticateMessage.cbBuffer; - micvalue = (SecBuffer*) pBuffer; - - if (!sspi_SecBufferAlloc(micvalue, msgSize)) - return (SEC_E_INSUFFICIENT_MEMORY); - - blob = (BYTE*) micvalue->pvBuffer; - CopyMemory(blob, context->NegotiateMessage.pvBuffer, context->NegotiateMessage.cbBuffer); - blob += context->NegotiateMessage.cbBuffer; - CopyMemory(blob, context->ChallengeMessage.pvBuffer, context->ChallengeMessage.cbBuffer); - blob += context->ChallengeMessage.cbBuffer; - CopyMemory(blob, context->AuthenticateMessage.pvBuffer, context->AuthenticateMessage.cbBuffer); - blob += context->MessageIntegrityCheckOffset; - ZeroMemory(blob, 16); - return (SEC_E_OK); + return ntlm_computeMicValue(context, (SecBuffer*) pBuffer); } return SEC_E_UNSUPPORTED_FUNCTION; @@ -935,6 +942,16 @@ SECURITY_STATUS SEC_ENTRY ntlm_SetContextAttributesW(PCtxtHandle phContext, ULON CopyMemory(context->ServerChallenge, AuthNtlmServerChallenge->ServerChallenge, 8); return SEC_E_OK; } + else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_HASH_CB) + { + context->HashCallback = (psPeerComputeNtlmHash)pBuffer; + return SEC_E_OK; + } + else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_HASH_CB_DATA) + { + context->HashCallbackArg = pBuffer; + return SEC_E_OK; + } return SEC_E_UNSUPPORTED_FUNCTION; } diff --git a/winpr/libwinpr/sspi/NTLM/ntlm.h b/winpr/libwinpr/sspi/NTLM/ntlm.h index 5a6256c..af0690b 100644 --- a/winpr/libwinpr/sspi/NTLM/ntlm.h +++ b/winpr/libwinpr/sspi/NTLM/ntlm.h @@ -25,6 +25,7 @@ #include #include +#include #include "../sspi.h" @@ -273,11 +274,15 @@ struct _NTLM_CONTEXT BYTE ServerSealingKey[16]; BYTE MessageIntegrityCheck[16]; UINT32 MessageIntegrityCheckOffset; + psPeerComputeNtlmHash HashCallback; + void *HashCallbackArg; }; typedef struct _NTLM_CONTEXT NTLM_CONTEXT; NTLM_CONTEXT* ntlm_ContextNew(void); void ntlm_ContextFree(NTLM_CONTEXT* context); +SECURITY_STATUS ntlm_computeProofValue(NTLM_CONTEXT *ntlm, SecBuffer *ntproof); +SECURITY_STATUS ntlm_computeMicValue(NTLM_CONTEXT *ntlm, SecBuffer *micvalue); #ifdef WITH_DEBUG_NLA #define WITH_DEBUG_NTLM diff --git a/winpr/libwinpr/sspi/NTLM/ntlm_compute.c b/winpr/libwinpr/sspi/NTLM/ntlm_compute.c index e8b570b..a1130c4 100644 --- a/winpr/libwinpr/sspi/NTLM/ntlm_compute.c +++ b/winpr/libwinpr/sspi/NTLM/ntlm_compute.c @@ -304,9 +304,31 @@ int ntlm_compute_ntlm_v2_hash(NTLM_CONTEXT* context, BYTE* hash) (LPWSTR) credentials->identity.User, credentials->identity.UserLength * 2, (LPWSTR) credentials->identity.Domain, credentials->identity.DomainLength * 2, (BYTE*) hash); } + else if (context->HashCallback) + { + int ret; + SecBuffer proofValue, micValue; + + if (ntlm_computeProofValue(context, &proofValue) != SEC_E_OK) + return -1; + + if (ntlm_computeMicValue(context, &micValue) != SEC_E_OK) + { + sspi_SecBufferFree(&proofValue); + return -1; + } + + ret = context->HashCallback(context->HashCallbackArg, &credentials->identity, &proofValue, + context->EncryptedRandomSessionKey, context->MessageIntegrityCheck, &micValue, + hash); + + sspi_SecBufferFree(&proofValue); + sspi_SecBufferFree(&micValue); + return ret ? 1 : -1; + } else if (context->UseSamFileDatabase) { - ntlm_fetch_ntlm_v2_hash(context, hash); + return ntlm_fetch_ntlm_v2_hash(context, hash); } return 1; @@ -373,7 +395,6 @@ int ntlm_compute_ntlm_v2_response(NTLM_CONTEXT* context) blob = (BYTE*) ntlm_v2_temp.pvBuffer; /* Compute the NTLMv2 hash */ - if (ntlm_compute_ntlm_v2_hash(context, (BYTE*) context->NtlmV2Hash) < 0) return -1; diff --git a/winpr/libwinpr/sspi/sspi_winpr.c b/winpr/libwinpr/sspi/sspi_winpr.c index 1ebc788..12d7b7c 100644 --- a/winpr/libwinpr/sspi/sspi_winpr.c +++ b/winpr/libwinpr/sspi/sspi_winpr.c @@ -273,7 +273,8 @@ void sspi_SecBufferFree(PSecBuffer SecBuffer) if (!SecBuffer) return; - memset(SecBuffer->pvBuffer, 0, SecBuffer->cbBuffer); + if (SecBuffer->pvBuffer) + memset(SecBuffer->pvBuffer, 0, SecBuffer->cbBuffer); free(SecBuffer->pvBuffer); SecBuffer->pvBuffer = NULL; SecBuffer->cbBuffer = 0; -- 2.7.4