Add a callback to provide NTLM hashes on server-side
authordavewheel <dave@wheel>
Mon, 15 May 2017 16:52:39 +0000 (18:52 +0200)
committerdavewheel <dave@wheel>
Thu, 18 May 2017 12:24:24 +0000 (14:24 +0200)
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
libfreerdp/core/nla.c
winpr/include/winpr/ntlm.h
winpr/include/winpr/sspi.h
winpr/libwinpr/sspi/NTLM/ntlm.c
winpr/libwinpr/sspi/NTLM/ntlm.h
winpr/libwinpr/sspi/NTLM/ntlm_compute.c
winpr/libwinpr/sspi/sspi_winpr.c

index 20d04b3..5be2140 100644 (file)
@@ -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
index 5ada7db..1970e1a 100644 (file)
@@ -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)
+                       {
+                               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);
index 8b62ebd..64b35b5 100644 (file)
 #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);
 
index 790a6e7..53a033a 100644 (file)
@@ -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
index 8adcee2..376eba8 100644 (file)
@@ -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;
 }
index 5a6256c..af0690b 100644 (file)
@@ -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
index e8b570b..a1130c4 100644 (file)
@@ -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;
 
index 1ebc788..12d7b7c 100644 (file)
@@ -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;