DWORD NtlmGetWorkstationFromResponse( IN PNTLM_RESPONSE_MESSAGE_V1 pRespMsg, IN DWORD dwRespMsgSize, IN BOOLEAN bUnicode, OUT PSTR* ppWorkstation ) { DWORD dwError = LW_ERROR_SUCCESS; PCHAR pName = NULL; DWORD dwNameLength = 0; PBYTE pBuffer = NULL; PNTLM_SEC_BUFFER pSecBuffer = &pRespMsg->Workstation; *ppWorkstation = NULL; if (dwRespMsgSize < sizeof(*pRespMsg)) { dwError = ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); } if (LW_LTOH32(pSecBuffer->dwOffset) + LW_LTOH16(pSecBuffer->usLength) > dwRespMsgSize) { dwError = ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); } dwNameLength = LW_LTOH16(pSecBuffer->usLength); pBuffer = LW_LTOH32(pSecBuffer->dwOffset) + (PBYTE)pRespMsg; if (!bUnicode) { dwError = LwAllocateMemory(dwNameLength + 1, OUT_PPVOID(&pName)); BAIL_ON_LSA_ERROR(dwError); memcpy(pName, pBuffer, dwNameLength); } else { dwError = NtlmGetCStringFromUnicodeBuffer( pBuffer, dwNameLength, &pName); BAIL_ON_LSA_ERROR(dwError); } cleanup: *ppWorkstation = pName; return dwError; error: LW_SAFE_FREE_STRING(pName); goto cleanup; }
VOID RtlpDecodeLittleEndianSid( IN PSID LittleEndianSid, OUT PSID Sid ) { ULONG i = 0; Sid->Revision = LW_LTOH8(LittleEndianSid->Revision); Sid->SubAuthorityCount = LW_LTOH8(LittleEndianSid->SubAuthorityCount); // sequence of bytes Sid->IdentifierAuthority = LittleEndianSid->IdentifierAuthority; for (i = 0; i < Sid->SubAuthorityCount; i++) { Sid->SubAuthority[i] = LW_LTOH32(LittleEndianSid->SubAuthority[i]); } }
DWORD NtlmValidateResponse( IN HANDLE Handle, IN NTLM_CRED_HANDLE hCred, IN PNTLM_RESPONSE_MESSAGE_V1 pRespMsg, IN DWORD dwRespMsgSize, IN PNTLM_CONTEXT pChlngCtxt, OUT BYTE pSessionKey[NTLM_SESSION_KEY_SIZE] ) { DWORD dwError = LW_ERROR_SUCCESS; PNTLM_CREDENTIALS pCred = (PNTLM_CREDENTIALS)hCred; LSA_AUTH_USER_PARAMS Params; PLSA_AUTH_USER_INFO pUserInfo = NULL; PBYTE pLMRespBuffer = NULL; PBYTE pNTRespBuffer = NULL; LW_LSA_DATA_BLOB Challenge; LW_LSA_DATA_BLOB LMResp; LW_LSA_DATA_BLOB NTResp; PSTR pUserName = NULL; PSTR pDomainName = NULL; PSTR pWorkstation = NULL; PSTR pDomainInstance = NULL; BYTE sessionNonce[MD5_DIGEST_LENGTH]; BYTE sessionHashUntrunc[MD5_DIGEST_LENGTH]; BOOLEAN bInLock = FALSE; memset(&Params, 0, sizeof(Params)); memset(&Challenge, 0, sizeof(Challenge)); memset(&LMResp, 0, sizeof(LMResp)); memset(&NTResp, 0, sizeof(NTResp)); // sanity check if (!pRespMsg || ! pChlngCtxt) { dwError = LW_ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); } if (dwRespMsgSize < sizeof(*pRespMsg)) { dwError = ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); } dwError = LwAllocateMemory( LW_LTOH16(pRespMsg->LmResponse.usLength), OUT_PPVOID(&pLMRespBuffer)); BAIL_ON_LSA_ERROR(dwError); dwError = LwAllocateMemory( LW_LTOH16(pRespMsg->NtResponse.usLength), OUT_PPVOID(&pNTRespBuffer)); BAIL_ON_LSA_ERROR(dwError); // The username, domain, and workstation values might come back as Unicode. // We could technically prevent this by not allowing NTLM_FLAG_UNICODE to be // set during the negotiation phase, but that seems like an odd restriction // for now. dwError = NtlmGetUserNameFromResponse( pRespMsg, dwRespMsgSize, pChlngCtxt->NegotiatedFlags & NTLM_FLAG_UNICODE, &pUserName); BAIL_ON_LSA_ERROR(dwError); dwError = NtlmGetDomainNameFromResponse( pRespMsg, dwRespMsgSize, pChlngCtxt->NegotiatedFlags & NTLM_FLAG_UNICODE, &pDomainName); BAIL_ON_LSA_ERROR(dwError); if (pDomainName[0] == 0) { LW_SAFE_FREE_STRING(pDomainName); } dwError = NtlmGetWorkstationFromResponse( pRespMsg, dwRespMsgSize, pChlngCtxt->NegotiatedFlags & NTLM_FLAG_UNICODE, &pWorkstation); BAIL_ON_LSA_ERROR(dwError); if (LW_LTOH32(pRespMsg->LmResponse.dwOffset) + LW_LTOH16(pRespMsg->LmResponse.usLength) > dwRespMsgSize) { dwError = ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); } memcpy( pLMRespBuffer, (PBYTE)pRespMsg + LW_LTOH32(pRespMsg->LmResponse.dwOffset), LW_LTOH16(pRespMsg->LmResponse.usLength)); if (LW_LTOH32(pRespMsg->NtResponse.dwOffset) + LW_LTOH16(pRespMsg->NtResponse.usLength) > dwRespMsgSize) { dwError = ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); } memcpy( pNTRespBuffer, (PBYTE)pRespMsg + LW_LTOH32(pRespMsg->NtResponse.dwOffset), LW_LTOH16(pRespMsg->NtResponse.usLength)); if (LW_LTOH16(pRespMsg->NtResponse.usLength) == 24 && pChlngCtxt->NegotiatedFlags & NTLM_FLAG_NTLM2) { // The client sent an NTLM2 session response. That means we need to // calculate the challenge the client used. if (LW_LTOH16(pRespMsg->LmResponse.usLength) < 8) { dwError = LW_ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); } // Calculate the session nonce first memcpy(sessionNonce + 0, pChlngCtxt->Challenge, 8); memcpy(sessionNonce + 8, pLMRespBuffer, 8); MD5(sessionNonce, 16, sessionHashUntrunc); Challenge.dwLen = NTLM_CHALLENGE_SIZE; Challenge.pData = sessionHashUntrunc; } else { Challenge.dwLen = NTLM_CHALLENGE_SIZE; Challenge.pData = pChlngCtxt->Challenge; } LMResp.dwLen = LW_LTOH16(pRespMsg->LmResponse.usLength); LMResp.pData = pLMRespBuffer; NTResp.dwLen = LW_LTOH16(pRespMsg->NtResponse.usLength); NTResp.pData = pNTRespBuffer; Params.AuthType = LSA_AUTH_CHAP; Params.pass.chap.pChallenge = &Challenge; Params.pass.chap.pLM_resp = &LMResp; Params.pass.chap.pNT_resp = &NTResp; Params.pszAccountName = pUserName; Params.pszDomain = pDomainName; Params.pszWorkstation = pWorkstation; if (pCred) { NTLM_LOCK_MUTEX(bInLock, &pCred->Mutex); if (pCred->pszDomainName) { dwError = LwAllocateStringPrintf( &pDomainInstance, ":%s", pCred->pszDomainName); BAIL_ON_LSA_ERROR(dwError); } NTLM_UNLOCK_MUTEX(bInLock, &pCred->Mutex); } dwError = LsaSrvAuthenticateUserEx( Handle, pDomainInstance, &Params, &pUserInfo ); BAIL_ON_LSA_ERROR(dwError); LW_ASSERT(pUserInfo->pSessionKey->dwLen == NTLM_SESSION_KEY_SIZE); if (LW_LTOH16(pRespMsg->NtResponse.usLength) == 24 && pChlngCtxt->NegotiatedFlags & NTLM_FLAG_NTLM2) { HMAC( EVP_md5(), pUserInfo->pSessionKey->pData, NTLM_SESSION_KEY_SIZE, sessionNonce, 16, pSessionKey, NULL); } else { memcpy(pSessionKey, pUserInfo->pSessionKey->pData, NTLM_SESSION_KEY_SIZE); } pChlngCtxt->pUserInfo = pUserInfo; pUserInfo = NULL; cleanup: if (pCred) { NTLM_UNLOCK_MUTEX(bInLock, &pCred->Mutex); } if (pUserInfo) { LsaFreeAuthUserInfo(&pUserInfo); } LW_SAFE_FREE_MEMORY(pLMRespBuffer); LW_SAFE_FREE_MEMORY(pNTRespBuffer); LW_SAFE_FREE_STRING(pUserName); LW_SAFE_FREE_STRING(pDomainName); LW_SAFE_FREE_STRING(pWorkstation); LW_SAFE_FREE_STRING(pDomainInstance); return dwError; error: goto cleanup; }
DWORD NtlmCreateValidatedContext( IN PNTLM_RESPONSE_MESSAGE_V1 pNtlmRespMsg, IN DWORD dwMsgSize, IN PNTLM_CONTEXT pNtlmCtxtChlng, IN PBYTE pSessionKey, IN DWORD dwSessionKeyLen, IN NTLM_CRED_HANDLE hCred, OUT PNTLM_CONTEXT *ppNtlmContext ) { DWORD dwError = LW_ERROR_SUCCESS; PNTLM_CONTEXT pNtlmContext = NULL; SEC_CHAR* pUserName = NULL; SEC_CHAR* pDomainName = NULL; PNTLM_RESPONSE_MESSAGE_V2 pV2Message = NULL; RC4_KEY Rc4Key; *ppNtlmContext = NULL; dwError = NtlmCreateContext(hCred, &pNtlmContext); BAIL_ON_LSA_ERROR(dwError); pNtlmContext->NtlmState = NtlmStateResponse; pNtlmContext->NegotiatedFlags = pNtlmCtxtChlng->NegotiatedFlags; dwError = LwAllocateStringPrintf( &pNtlmContext->pszClientUsername, "%s\\%s", pNtlmCtxtChlng->pUserInfo->pszDomain, pNtlmCtxtChlng->pUserInfo->pszAccount); BAIL_ON_LSA_ERROR(dwError); memcpy(pNtlmContext->SessionKey, pSessionKey, NTLM_SESSION_KEY_SIZE); pNtlmContext->cbSessionKeyLen = dwSessionKeyLen; pNtlmContext->bInitiatedSide = FALSE; if (pNtlmContext->NegotiatedFlags & NTLM_FLAG_KEY_EXCH) { pV2Message = (PNTLM_RESPONSE_MESSAGE_V2)pNtlmRespMsg; if (dwMsgSize < sizeof(*pV2Message)) { dwError = ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); } if (LW_LTOH32(pV2Message->SessionKey.dwOffset) + LW_LTOH16(pV2Message->SessionKey.usLength) > dwMsgSize) { dwError = ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); } if (LW_LTOH16(pV2Message->SessionKey.usLength) != NTLM_SESSION_KEY_SIZE) { dwError = ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); } RC4_set_key( &Rc4Key, pNtlmContext->cbSessionKeyLen, pNtlmContext->SessionKey); RC4(&Rc4Key, NTLM_SESSION_KEY_SIZE, LW_LTOH32(pV2Message->SessionKey.dwOffset) + (PBYTE)pV2Message, pNtlmContext->SessionKey); } dwError = NtlmInitializeKeys(pNtlmContext); BAIL_ON_LSA_ERROR(dwError); cleanup: LW_SAFE_FREE_MEMORY(pUserName); LW_SAFE_FREE_MEMORY(pDomainName); *ppNtlmContext = pNtlmContext; return dwError; error: if (pNtlmContext) { NtlmFreeContext(&pNtlmContext); } goto cleanup; }
DWORD NtlmCreateChallengeContext( IN const NTLM_NEGOTIATE_MESSAGE_V1* pNtlmNegMsg, IN NTLM_CRED_HANDLE hCred, OUT PNTLM_CONTEXT *ppNtlmContext, OUT PSecBuffer pOutput ) { DWORD dwError = LW_ERROR_SUCCESS; PNTLM_CREDENTIALS pCred = (PNTLM_CREDENTIALS)hCred; PNTLM_CONTEXT pNtlmContext = NULL; DWORD dwMessageSize = 0; PNTLM_CHALLENGE_MESSAGE pMessage = NULL; PSTR pServerName = NULL; PSTR pDomainName = NULL; PSTR pDnsServerName = NULL; PSTR pDnsDomainName = NULL; BOOLEAN bInLock = FALSE; *ppNtlmContext = NULL; dwError = NtlmCreateContext(hCred, &pNtlmContext); BAIL_ON_LSA_ERROR(dwError); if (pCred) { NTLM_LOCK_MUTEX(bInLock, &pCred->Mutex); dwError = NtlmGetNameInformation( pCred->pszDomainName, &pServerName, &pDomainName, &pDnsServerName, &pDnsDomainName); BAIL_ON_LSA_ERROR(dwError); NTLM_UNLOCK_MUTEX(bInLock, &pCred->Mutex); } else { dwError = NtlmGetNameInformation( NULL, &pServerName, &pDomainName, &pDnsServerName, &pDnsDomainName); BAIL_ON_LSA_ERROR(dwError); } dwError = NtlmGetRandomBuffer( pNtlmContext->Challenge, NTLM_CHALLENGE_SIZE ); BAIL_ON_LSA_ERROR(dwError); dwError = NtlmCreateChallengeMessage( pNtlmNegMsg, pServerName, pDomainName, pDnsServerName, pDnsDomainName, (PBYTE)&gW2KSpoof, pNtlmContext->Challenge, &dwMessageSize, &pMessage ); BAIL_ON_LSA_ERROR(dwError); pNtlmContext->NegotiatedFlags = LW_LTOH32(pMessage->NtlmFlags); pOutput->cbBuffer = dwMessageSize; pOutput->BufferType = SECBUFFER_TOKEN; pOutput->pvBuffer = pMessage; pNtlmContext->NtlmState = NtlmStateChallenge; cleanup: *ppNtlmContext = pNtlmContext; LW_SAFE_FREE_STRING(pServerName); LW_SAFE_FREE_STRING(pDomainName); LW_SAFE_FREE_STRING(pDnsServerName); LW_SAFE_FREE_STRING(pDnsDomainName); if (pCred) { NTLM_UNLOCK_MUTEX(bInLock, &pCred->Mutex); } return dwError; error: LW_SAFE_FREE_MEMORY(pMessage); if (pNtlmContext) { NtlmReleaseContext(&pNtlmContext); *ppNtlmContext = NULL; } pOutput->cbBuffer = 0; pOutput->BufferType = 0; pOutput->pvBuffer = NULL; goto cleanup; }
DWORD NtlmCreateResponseContext( IN PNTLM_CHALLENGE_MESSAGE pChlngMsg, IN NTLM_CRED_HANDLE hCred, IN BOOLEAN bDoAnonymous, OUT PNTLM_CONTEXT* ppNtlmContext, OUT PSecBuffer pOutput ) { DWORD dwError = LW_ERROR_SUCCESS; PNTLM_RESPONSE_MESSAGE_V1 pMessage = NULL; PCSTR pUserNameTemp = NULL; PCSTR pPassword = NULL; PNTLM_CONTEXT pNtlmContext = NULL; PBYTE pMasterKey = NULL; BYTE LmUserSessionKey[NTLM_SESSION_KEY_SIZE] = {0}; BYTE NtlmUserSessionKey[NTLM_SESSION_KEY_SIZE] = {0}; BYTE LanManagerSessionKey[NTLM_SESSION_KEY_SIZE] = {0}; BYTE SecondaryKey[NTLM_SESSION_KEY_SIZE] = {0}; PLSA_LOGIN_NAME_INFO pUserNameInfo = NULL; DWORD dwMessageSize = 0; NTLM_CONFIG config; DWORD dwNtRespType = 0; DWORD dwLmRespType = 0; *ppNtlmContext = NULL; dwError = NtlmReadRegistry(&config); BAIL_ON_LSA_ERROR(dwError); if (bDoAnonymous) { pUserNameTemp = ""; pPassword = ""; } else { NtlmGetCredentialInfo( hCred, &pUserNameTemp, &pPassword, NULL); if (!pUserNameTemp[0] && !pPassword[0]) { bDoAnonymous = TRUE; } } if (bDoAnonymous) { dwError = LwAllocateMemory( sizeof(*pUserNameInfo), OUT_PPVOID(&pUserNameInfo)); BAIL_ON_LSA_ERROR(dwError); dwError = LwAllocateString( "", &pUserNameInfo->pszName); BAIL_ON_LSA_ERROR(dwError); dwError = LwAllocateString( "", &pUserNameInfo->pszDomain); BAIL_ON_LSA_ERROR(dwError); } else { dwError = LsaSrvCrackDomainQualifiedName( pUserNameTemp, &pUserNameInfo); BAIL_ON_LSA_ERROR(dwError); } dwError = NtlmCreateContext(hCred, &pNtlmContext); BAIL_ON_LSA_ERROR(dwError); dwError = LwAllocateString( pUserNameTemp, &pNtlmContext->pszClientUsername); BAIL_ON_LSA_ERROR(dwError); if (bDoAnonymous) { dwNtRespType = NTLM_RESPONSE_TYPE_ANON_NTLM; dwLmRespType = NTLM_RESPONSE_TYPE_ANON_LM; } else if (config.bSendNTLMv2) { dwNtRespType = NTLM_RESPONSE_TYPE_NTLMv2; // TODO: the correct thing is to use LMv2 dwLmRespType = NTLM_RESPONSE_TYPE_LM; } else if(LW_LTOH32(pChlngMsg->NtlmFlags) & NTLM_FLAG_NTLM2) { dwLmRespType = NTLM_RESPONSE_TYPE_NTLM2; dwNtRespType = NTLM_RESPONSE_TYPE_NTLM2; } else { dwNtRespType = NTLM_RESPONSE_TYPE_NTLM; dwLmRespType = NTLM_RESPONSE_TYPE_LM; } dwError = NtlmCreateResponseMessage( pChlngMsg, pUserNameInfo->pszName, pUserNameInfo->pszDomain, pPassword, (PBYTE)&gXpSpoof, dwNtRespType, dwLmRespType, &dwMessageSize, &pMessage, LmUserSessionKey, NtlmUserSessionKey ); BAIL_ON_LSA_ERROR(dwError); // As a side effect of creating the response, we must also set/produce the // master session key... pMasterKey = NtlmUserSessionKey; if (LW_LTOH32(pChlngMsg->NtlmFlags) & NTLM_FLAG_LM_KEY) { NtlmGenerateLanManagerSessionKey( pMessage, LmUserSessionKey, LanManagerSessionKey); pMasterKey = LanManagerSessionKey; } if (LW_LTOH32(pChlngMsg->NtlmFlags) & NTLM_FLAG_KEY_EXCH) { // This is the key we will use for session security... dwError = NtlmGetRandomBuffer( SecondaryKey, NTLM_SESSION_KEY_SIZE); BAIL_ON_LSA_ERROR(dwError); // Encrypt it with the "master key" set above and send it along with the // response NtlmStoreSecondaryKey( pMasterKey, SecondaryKey, pMessage); pMasterKey = SecondaryKey; } NtlmWeakenSessionKey( pChlngMsg, pMasterKey, &pNtlmContext->cbSessionKeyLen); memcpy(pNtlmContext->SessionKey, pMasterKey, NTLM_SESSION_KEY_SIZE); pNtlmContext->NegotiatedFlags = LW_LTOH32(pChlngMsg->NtlmFlags); pOutput->cbBuffer = dwMessageSize; pOutput->BufferType = SECBUFFER_TOKEN; pOutput->pvBuffer = pMessage; pNtlmContext->NtlmState = NtlmStateResponse; pNtlmContext->bInitiatedSide = TRUE; pNtlmContext->bDoAnonymous = bDoAnonymous; dwError = NtlmInitializeKeys(pNtlmContext); BAIL_ON_LSA_ERROR(dwError); cleanup: if (pUserNameInfo) { LsaSrvFreeNameInfo(pUserNameInfo); } *ppNtlmContext = pNtlmContext; return dwError; error: LW_SAFE_FREE_MEMORY(pMessage); if (pNtlmContext) { NtlmFreeContext(&pNtlmContext); } pOutput->cbBuffer = 0; pOutput->BufferType = 0; pOutput->pvBuffer = NULL; goto cleanup; }