You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
555 lines
14 KiB
555 lines
14 KiB
/*++
|
|
|
|
Copyright (c) 2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
cache.cxx
|
|
|
|
Abstract:
|
|
|
|
Injectee
|
|
|
|
Author:
|
|
|
|
Larry Zhu (LZhu) December 1, 2001 Created
|
|
|
|
Environment:
|
|
|
|
User Mode
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.hxx"
|
|
#pragma hdrstop
|
|
|
|
#include <lmcons.h>
|
|
#include <ntsamp.h>
|
|
#include <md5.h>
|
|
#include <hmac.h>
|
|
#include <rc4.h>
|
|
|
|
#include "cache.hxx"
|
|
|
|
BOOL
|
|
DllMain(
|
|
IN HANDLE hModule,
|
|
IN DWORD dwReason,
|
|
IN DWORD dwReserved
|
|
)
|
|
{
|
|
BOOL bRet;
|
|
switch (dwReason)
|
|
{
|
|
case DLL_THREAD_ATTACH:
|
|
break;
|
|
|
|
case DLL_THREAD_DETACH:
|
|
break;
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
break;
|
|
|
|
case DLL_PROCESS_ATTACH:
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
bRet = DllMainDefaultHandler(hModule, dwReason, dwReserved);
|
|
|
|
DebugPrintf(SSPI_LOG, "DllMain leaving %#x\n", bRet);
|
|
|
|
return bRet;
|
|
}
|
|
|
|
int
|
|
RunIt(
|
|
IN ULONG cbParameters,
|
|
IN VOID* pvParameters
|
|
)
|
|
{
|
|
//
|
|
// RunItDefaultHandler calls Start() and adds try except
|
|
//
|
|
|
|
DWORD dwErr;
|
|
|
|
dwErr = RunItDefaultHandler(cbParameters, pvParameters);
|
|
|
|
DebugPrintf(SSPI_LOG, "RunIt leaving %#x\n", dwErr);
|
|
|
|
return dwErr;
|
|
|
|
}
|
|
|
|
#if 0
|
|
|
|
Return Values for Start():
|
|
|
|
ERROR_NO_MORE_USER_HANDLES unload repeatedly
|
|
ERROR_SERVER_HAS_OPEN_HANDLES no unload at all
|
|
others unload once
|
|
|
|
#endif 0
|
|
|
|
int
|
|
Start(
|
|
IN ULONG cbParameters,
|
|
IN VOID* pvParameters
|
|
)
|
|
{
|
|
DWORD dwErr = ERROR_SUCCESS;
|
|
|
|
CHAR* pNlpCacheEncryptionKey = NULL;
|
|
LIST_ENTRY* pNlpActiveCtes = NULL;
|
|
|
|
SspiPrintHex(SSPI_LOG, TEXT("Start Parameters"), cbParameters, pvParameters);
|
|
|
|
if (cbParameters == sizeof(ULONG_PTR) + sizeof(ULONG_PTR))
|
|
{
|
|
TNtStatus Status;
|
|
|
|
pNlpCacheEncryptionKey = * ((CHAR**) pvParameters);
|
|
pNlpActiveCtes = * ((LIST_ENTRY**) ((CHAR*)pvParameters + sizeof(ULONG_PTR)));
|
|
|
|
Status DBGCHK = EnumerateNlpCacheEntries(pNlpCacheEncryptionKey, pNlpActiveCtes);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
SspiPrint(SSPI_LOG, TEXT("Operation succeeded\n"));
|
|
}
|
|
else
|
|
{
|
|
SspiPrint(SSPI_LOG, TEXT("Operation failed\n"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dwErr = ERROR_INVALID_PARAMETER;
|
|
SspiPrint(SSPI_LOG, TEXT("Start received invalid parameter\n"));
|
|
}
|
|
|
|
return dwErr;
|
|
}
|
|
|
|
int
|
|
Init(
|
|
IN ULONG argc,
|
|
IN PCSTR argv[],
|
|
OUT ULONG* pcbParameters,
|
|
OUT VOID** ppvParameters
|
|
)
|
|
{
|
|
DWORD dwErr = ERROR_SUCCESS;
|
|
CHAR Parameters[REMOTE_PACKET_SIZE] = {0};
|
|
ULONG cbBuffer = sizeof(Parameters);
|
|
ULONG cbParameter = 0;
|
|
|
|
CHAR* pNlpCacheEncryptionKey = NULL;
|
|
LIST_ENTRY* pNlpActiveCtes = NULL;
|
|
|
|
*pcbParameters = 0;
|
|
*ppvParameters = NULL;
|
|
|
|
if (argc == 2)
|
|
{
|
|
DebugPrintf(SSPI_LOG, "argv[0] %s, argv[1] %s\n", argv[0], argv[1]);
|
|
|
|
pNlpCacheEncryptionKey = (CHAR*) (ULONG_PTR) strtol(argv[0], NULL, 0);
|
|
pNlpActiveCtes = (LIST_ENTRY*) (ULONG_PTR) strtol(argv[1], NULL, 0);
|
|
|
|
SspiPrint(SSPI_LOG, TEXT("msv1_0!NlpCacheEncryptionKey %p, addr of msv1_0!NlpActiveCtes %p\n"), pNlpCacheEncryptionKey, pNlpActiveCtes);
|
|
|
|
memcpy(Parameters, &pNlpCacheEncryptionKey, sizeof(ULONG_PTR));
|
|
cbParameter = sizeof(ULONG_PTR);
|
|
|
|
memcpy(Parameters + cbParameter, &pNlpActiveCtes, sizeof(ULONG_PTR));
|
|
cbParameter += sizeof(ULONG_PTR);
|
|
}
|
|
else // return "Usage" in ppvParameters, must be a NULL terminated string
|
|
{
|
|
strcpy(Parameters, "<value of msv1_0!NlpCacheEncryptionKey> <addr of msv1_0!NlpActiveCtes>");
|
|
cbParameter = strlen(Parameters) + 1;
|
|
|
|
dwErr = ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
*ppvParameters = new CHAR[cbParameter];
|
|
if (*ppvParameters)
|
|
{
|
|
*pcbParameters = cbParameter;
|
|
memcpy(*ppvParameters, Parameters, *pcbParameters);
|
|
}
|
|
else
|
|
{
|
|
dwErr = ERROR_OUTOFMEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
return dwErr;
|
|
}
|
|
|
|
NTSTATUS
|
|
NlpMakeCacheEntryName(
|
|
IN ULONG EntryIndex,
|
|
OUT UNICODE_STRING* pName
|
|
)
|
|
{
|
|
TNtStatus NtStatus = STATUS_SUCCESS;
|
|
|
|
UNICODE_STRING TmpString = {0};
|
|
|
|
WCHAR TmpStringBuffer[256] = {0};
|
|
|
|
if (EntryIndex > NLP_MAX_LOGON_CACHE_COUNT)
|
|
{
|
|
DebugPrintf(SSPI_ERROR, "NlpMakeCacheEntryName EntryIndex %#x exceeds NLP_MAX_LOGON_CACHE_COUNT %#x\n", EntryIndex, NLP_MAX_LOGON_CACHE_COUNT);
|
|
NtStatus DBGCHK = STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (NT_SUCCESS(NtStatus))
|
|
{
|
|
pName->Length = 0;
|
|
NtStatus DBGCHK = RtlAppendUnicodeToString(pName, L"NL$");
|
|
}
|
|
|
|
if (NT_SUCCESS(NtStatus))
|
|
{
|
|
TmpString.MaximumLength = 16;
|
|
TmpString.Length = 0;
|
|
TmpString.Buffer = TmpStringBuffer;
|
|
NtStatus DBGCHK = RtlIntegerToUnicodeString(
|
|
(EntryIndex + 1), // make 1 based index
|
|
10, // Base 10
|
|
&TmpString
|
|
);
|
|
}
|
|
|
|
if (NT_SUCCESS(NtStatus))
|
|
{
|
|
NtStatus DBGCHK = RtlAppendUnicodeStringToString(pName, &TmpString);
|
|
}
|
|
|
|
return NtStatus;
|
|
}
|
|
|
|
NTSTATUS
|
|
NlpOpenCache(
|
|
OUT HANDLE* phNlpCache
|
|
)
|
|
{
|
|
TNtStatus NtStatus;
|
|
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
UNICODE_STRING ObjectName;
|
|
|
|
ObjectName.Length = ObjectName.MaximumLength = CACHE_NAME_SIZE;
|
|
ObjectName.Buffer = CACHE_NAME;
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&ObjectName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
0, // RootDirectory
|
|
NULL // default is reasonable from SYSTEM context
|
|
);
|
|
|
|
NtStatus DBGCHK = NtOpenKey(
|
|
phNlpCache,
|
|
KEY_READ,
|
|
&ObjectAttributes
|
|
);
|
|
|
|
return NtStatus;
|
|
}
|
|
|
|
NTSTATUS
|
|
NlpReadCacheEntryByIndex(
|
|
IN ULONG Index,
|
|
OUT PLOGON_CACHE_ENTRY* ppCacheEntry,
|
|
OUT PULONG pcbEntrySize
|
|
)
|
|
{
|
|
TNtStatus NtStatus = STATUS_SUCCESS;
|
|
|
|
UNICODE_STRING ValueName = {0};
|
|
HANDLE hNlpCache = NULL;
|
|
|
|
WCHAR szNameBuffer[256] = {0};
|
|
|
|
ULONG cbRequiredSize = 0;
|
|
|
|
PKEY_VALUE_FULL_INFORMATION pRegInfo = NULL;
|
|
|
|
PLOGON_CACHE_ENTRY pRCacheEntry = NULL; // CacheEntry in registry buffer
|
|
|
|
BYTE FastBuffer[4098] = {0};
|
|
PBYTE pSlowBuffer = NULL;
|
|
|
|
ValueName.Buffer = szNameBuffer;
|
|
ValueName.MaximumLength = sizeof(szNameBuffer) - sizeof(WCHAR);
|
|
ValueName.Length = 0;
|
|
NtStatus DBGCHK = NlpMakeCacheEntryName(Index, &ValueName);
|
|
|
|
if (NT_SUCCESS(NtStatus))
|
|
{
|
|
SspiPrint(SSPI_LOG, TEXT("NlpReadCacheEntryByIndex %#x, ValueName %wZ\n"), Index, &ValueName);
|
|
|
|
NtStatus DBGCHK = NlpOpenCache(&hNlpCache);
|
|
}
|
|
|
|
if (NT_SUCCESS(NtStatus))
|
|
{
|
|
pRegInfo = (PKEY_VALUE_FULL_INFORMATION)FastBuffer;
|
|
cbRequiredSize = sizeof(FastBuffer);
|
|
|
|
DBGCFG2(NtStatus, STATUS_BUFFER_TOO_SMALL, STATUS_BUFFER_OVERFLOW);
|
|
|
|
//
|
|
// perform first query to find out how much buffer to allocate
|
|
//
|
|
|
|
NtStatus DBGCHK = NtQueryValueKey(
|
|
hNlpCache,
|
|
&ValueName,
|
|
KeyValueFullInformation,
|
|
pRegInfo,
|
|
cbRequiredSize,
|
|
&cbRequiredSize
|
|
);
|
|
|
|
if ( ( ((NTSTATUS) NtStatus) == STATUS_BUFFER_TOO_SMALL )
|
|
|| ( ((NTSTATUS) NtStatus) == STATUS_BUFFER_OVERFLOW ) )
|
|
{
|
|
|
|
SspiPrint(SSPI_WARN, TEXT("NlpReadCacheEntryByIndex NtQueryValueKey requires %#x bytes\n"), cbRequiredSize);
|
|
|
|
//
|
|
// allocate buffer then do query again, this time receiving data
|
|
//
|
|
|
|
pSlowBuffer = new BYTE[cbRequiredSize];
|
|
NtStatus DBGCHK = pSlowBuffer ? STATUS_SUCCESS : STATUS_NO_MEMORY;
|
|
|
|
if (NT_SUCCESS(NtStatus))
|
|
{
|
|
pRegInfo = (PKEY_VALUE_FULL_INFORMATION)pSlowBuffer;
|
|
|
|
NtStatus DBGCHK = NtQueryValueKey(
|
|
hNlpCache,
|
|
&ValueName,
|
|
KeyValueFullInformation,
|
|
pRegInfo,
|
|
cbRequiredSize,
|
|
&cbRequiredSize
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(NtStatus))
|
|
{
|
|
if (pRegInfo->DataLength == 0 )
|
|
{
|
|
NtStatus DBGCHK = STATUS_INTERNAL_DB_CORRUPTION;
|
|
*ppCacheEntry = NULL;
|
|
*pcbEntrySize = 0;
|
|
}
|
|
else
|
|
{
|
|
pRCacheEntry = (PLOGON_CACHE_ENTRY) ((PCHAR)pRegInfo + pRegInfo->DataOffset);
|
|
*pcbEntrySize = pRegInfo->DataLength;
|
|
|
|
(*ppCacheEntry) = (PLOGON_CACHE_ENTRY) new CHAR[*pcbEntrySize];
|
|
|
|
NtStatus DBGCHK = *ppCacheEntry ? STATUS_SUCCESS : STATUS_NO_MEMORY;
|
|
|
|
if (NT_SUCCESS(NtStatus))
|
|
{
|
|
RtlCopyMemory((*ppCacheEntry), pRCacheEntry, (*pcbEntrySize) );
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pSlowBuffer)
|
|
{
|
|
delete [] pSlowBuffer;
|
|
}
|
|
|
|
if (hNlpCache)
|
|
{
|
|
NtClose(hNlpCache);
|
|
}
|
|
|
|
return NtStatus;
|
|
}
|
|
|
|
NTSTATUS
|
|
NlpDecryptCacheEntry(
|
|
IN CHAR NlpCacheEncryptionKey[NLP_CACHE_ENCRYPTION_KEY_LEN],
|
|
IN ULONG EntrySize,
|
|
IN OUT PLOGON_CACHE_ENTRY pCacheEntry
|
|
)
|
|
{
|
|
TNtStatus NtStatus = STATUS_SUCCESS;
|
|
|
|
HMACMD5_CTX hmacCtx;
|
|
RC4_KEYSTRUCT rc4key;
|
|
CHAR DerivedKey[ MD5DIGESTLEN ];
|
|
|
|
CHAR MAC[ MD5DIGESTLEN ];
|
|
|
|
PBYTE pbData;
|
|
ULONG cbData;
|
|
|
|
// DebugPrintHex(SSPI_LOG, "NlpDecryptCacheEntry NlpCacheEncryptionKey",
|
|
// NLP_CACHE_ENCRYPTION_KEY_LEN, NlpCacheEncryptionKey);
|
|
|
|
if ( pCacheEntry->Revision < NLP_CACHE_REVISION_NT_5_0 )
|
|
{
|
|
NtStatus DBGCHK = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// derive encryption key from global machine LSA secret, and random
|
|
// cache entry key.
|
|
//
|
|
|
|
if (NT_SUCCESS(NtStatus))
|
|
{
|
|
HMACMD5Init(&hmacCtx, (PUCHAR) NlpCacheEncryptionKey, NLP_CACHE_ENCRYPTION_KEY_LEN);
|
|
HMACMD5Update(&hmacCtx, (PUCHAR) pCacheEntry->RandomKey, sizeof(pCacheEntry->RandomKey));
|
|
HMACMD5Final(&hmacCtx, (PUCHAR) DerivedKey);
|
|
|
|
//
|
|
// begin decrypting at the cachepasswords field.
|
|
//
|
|
|
|
pbData = (PBYTE)&(pCacheEntry->CachePasswords);
|
|
|
|
//
|
|
// data length is EntrySize - header up to CachePasswords.
|
|
//
|
|
|
|
cbData = EntrySize - (ULONG)( pbData - (PBYTE)pCacheEntry );
|
|
|
|
//
|
|
// now decrypt it...
|
|
//
|
|
|
|
rc4_key( &rc4key, sizeof(DerivedKey), (PUCHAR) DerivedKey );
|
|
rc4( &rc4key, cbData, pbData );
|
|
|
|
//
|
|
// compute MAC on decrypted data for integrity checking.
|
|
//
|
|
|
|
HMACMD5Init(&hmacCtx, (PUCHAR) DerivedKey, sizeof(DerivedKey));
|
|
HMACMD5Update(&hmacCtx, pbData, cbData);
|
|
HMACMD5Final(&hmacCtx, (PUCHAR) MAC);
|
|
|
|
RtlZeroMemory( DerivedKey, sizeof(DerivedKey) );
|
|
|
|
//
|
|
// verify MAC.
|
|
//
|
|
|
|
if (memcmp( MAC, pCacheEntry->MAC, sizeof(MAC) ) != 0)
|
|
{
|
|
NtStatus DBGCHK = STATUS_LOGON_FAILURE;
|
|
}
|
|
}
|
|
|
|
return NtStatus;
|
|
}
|
|
|
|
NTSTATUS
|
|
EnumerateNlpCacheEntries(
|
|
IN CHAR NlpCacheEncryptionKey[NLP_CACHE_ENCRYPTION_KEY_LEN],
|
|
IN LIST_ENTRY* pNlpActiveCtes
|
|
)
|
|
{
|
|
TNtStatus NtStatus = STATUS_SUCCESS;
|
|
ULONG i = 0;
|
|
|
|
SspiPrint(SSPI_LOG, TEXT("EnumerateNlpCacheEntries NlpCacheEncryptionKey %p, NlpActiveCtes %p\n"),
|
|
NlpCacheEncryptionKey, pNlpActiveCtes);
|
|
|
|
for (PNLP_CTE pNext = (PNLP_CTE) pNlpActiveCtes->Flink;
|
|
NT_SUCCESS(NtStatus) && (pNext != (PNLP_CTE)pNlpActiveCtes);
|
|
pNext = (PNLP_CTE)pNext->Link.Flink)
|
|
{
|
|
LOGON_CACHE_ENTRY* pCacheEntry = NULL;
|
|
ULONG cbEntrySize = 0;
|
|
UNICODE_STRING CachedUser = {0};
|
|
UNICODE_STRING CachedDomain = {0};
|
|
UNICODE_STRING CachedDnsDomain = {0};
|
|
UNICODE_STRING CachedUpn = {0};
|
|
|
|
SspiPrint(SSPI_LOG, TEXT("*************#%#x) _NLP_CTE %p, Index %#x*******\n"), i++, pNext, pNext->Index);
|
|
|
|
NtStatus DBGCHK = NlpReadCacheEntryByIndex(
|
|
pNext->Index,
|
|
&pCacheEntry,
|
|
&cbEntrySize
|
|
);
|
|
|
|
if (NT_SUCCESS(NtStatus))
|
|
{
|
|
NtStatus DBGCHK = (pCacheEntry->Revision >= NLP_CACHE_REVISION_NT_1_0B) ? STATUS_SUCCESS : STATUS_INTERNAL_ERROR;
|
|
}
|
|
|
|
if (NT_SUCCESS(NtStatus))
|
|
{
|
|
NtStatus DBGCHK = NlpDecryptCacheEntry(
|
|
NlpCacheEncryptionKey,
|
|
cbEntrySize,
|
|
pCacheEntry
|
|
);
|
|
}
|
|
|
|
if (NT_SUCCESS(NtStatus))
|
|
{
|
|
CachedUser.Length = CachedUser.MaximumLength = pCacheEntry->UserNameLength;
|
|
|
|
NtStatus DBGCHK = (pCacheEntry->Revision >= NLP_CACHE_REVISION_NT_5_0) ? STATUS_SUCCESS : STATUS_INTERNAL_ERROR;
|
|
}
|
|
|
|
if (NT_SUCCESS(NtStatus))
|
|
{
|
|
CachedUser.Buffer = (PWSTR) ((PBYTE) pCacheEntry + sizeof(LOGON_CACHE_ENTRY));
|
|
|
|
CachedDomain.Length = CachedDomain.MaximumLength = pCacheEntry->DomainNameLength;
|
|
CachedDomain.Buffer = (PWSTR)((LPBYTE)CachedUser.Buffer
|
|
+ ROUND_UP_COUNT(pCacheEntry->UserNameLength, sizeof(ULONG)));
|
|
|
|
CachedDnsDomain.Length = CachedDnsDomain.MaximumLength = pCacheEntry->DnsDomainNameLength;
|
|
CachedDnsDomain.Buffer = (PWSTR)((PBYTE)CachedDomain.Buffer
|
|
+ ROUND_UP_COUNT(pCacheEntry->DomainNameLength, sizeof(ULONG)));
|
|
|
|
CachedUpn.Length = CachedUpn.MaximumLength = pCacheEntry->UpnLength;
|
|
CachedUpn.Buffer = (PWSTR)((PBYTE)CachedDnsDomain.Buffer
|
|
+ ROUND_UP_COUNT(pCacheEntry->DnsDomainNameLength, sizeof(ULONG)));
|
|
|
|
SspiPrint(SSPI_LOG,
|
|
TEXT("domain \"%wZ\", dns domain \"%wZ\", upn \"%wZ\", user \"%wZ\", flags %#x, ")
|
|
TEXT("UserId %#x, PrimaryGroupId %#x, GroupCount %#x, LogonPackage %#x\n"),
|
|
&CachedDomain, &CachedDnsDomain, &CachedUpn, &CachedUser, pCacheEntry->CacheFlags,
|
|
pCacheEntry->UserId, pCacheEntry->PrimaryGroupId, pCacheEntry->GroupCount, pCacheEntry->LogonPackage);
|
|
}
|
|
|
|
if (pCacheEntry)
|
|
{
|
|
delete [] pCacheEntry;
|
|
}
|
|
}
|
|
|
|
return NtStatus;
|
|
}
|
|
|