|
|
/*++
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; }
|