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)
This commit is contained in:
parent
4cc1ba5b1d
commit
4bfb4dddbf
@ -28,6 +28,8 @@
|
||||
#include <freerdp/autodetect.h>
|
||||
|
||||
#include <winpr/sspi.h>
|
||||
#include <winpr/ntlm.h>
|
||||
|
||||
|
||||
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
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include <freerdp/log.h>
|
||||
#include <freerdp/crypto/tls.h>
|
||||
#include <freerdp/build-config.h>
|
||||
#include <freerdp/peer.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/sam.h>
|
||||
@ -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)
|
||||
{
|
||||
nla->table->SetContextAttributes(&nla->context,
|
||||
SECPKG_ATTR_AUTH_NTLM_SAM_FILE, nla->SamFile, strlen(nla->SamFile) + 1);
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -25,11 +25,16 @@
|
||||
#include <string.h>
|
||||
#include <winpr/winpr.h>
|
||||
#include <winpr/wtypes.h>
|
||||
#include <winpr/sspi.h>
|
||||
|
||||
#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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
#include <winpr/nt.h>
|
||||
#include <winpr/crypto.h>
|
||||
#include <winpr/ntlm.h>
|
||||
|
||||
#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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user