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.
507 lines
12 KiB
507 lines
12 KiB
/*++
|
|
|
|
Copyright (c) 1998 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
keycache.h
|
|
|
|
Abstract:
|
|
|
|
This module contains routines for accessing cached masterkeys.
|
|
|
|
Author:
|
|
|
|
Scott Field (sfield) 07-Nov-98
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include <pch.cpp>
|
|
#pragma hdrstop
|
|
|
|
//
|
|
// masterkey cache.
|
|
//
|
|
|
|
typedef struct {
|
|
LIST_ENTRY Next;
|
|
LUID LogonId;
|
|
GUID guidMasterKey;
|
|
FILETIME ftLastAccess;
|
|
DWORD cbMasterKey;
|
|
BYTE pbMasterKey[ 64 ];
|
|
} MASTERKEY_CACHE_ENTRY, *PMASTERKEY_CACHE_ENTRY, *LPMASTERKEY_CACHE_ENTRY;
|
|
|
|
RTL_CRITICAL_SECTION g_MasterKeyCacheCritSect;
|
|
LIST_ENTRY g_MasterKeyCacheList;
|
|
|
|
|
|
|
|
BOOL
|
|
RemoveMasterKeyCache(
|
|
IN PLUID pLogonId
|
|
);
|
|
|
|
|
|
#if DBG
|
|
void
|
|
DumpMasterKeyEntry(
|
|
PMASTERKEY_CACHE_ENTRY pCacheEntry)
|
|
{
|
|
WCHAR wszguidMasterKey[MAX_GUID_SZ_CHARS];
|
|
#if 0
|
|
BYTE rgbMasterKey[256];
|
|
DWORD cbMasterKey;
|
|
#endif
|
|
|
|
D_DebugLog((DEB_TRACE, "LogonId: %d.%d\n", pCacheEntry->LogonId.LowPart, pCacheEntry->LogonId.HighPart));
|
|
|
|
if( MyGuidToStringW( &pCacheEntry->guidMasterKey, wszguidMasterKey ) != 0 )
|
|
{
|
|
D_DebugLog((DEB_TRACE, "Invalid GUID:\n"));
|
|
D_DPAPIDumpHexData(DEB_TRACE, " ", (PBYTE)&pCacheEntry->guidMasterKey, sizeof(pCacheEntry->guidMasterKey));
|
|
}
|
|
else
|
|
{
|
|
D_DebugLog((DEB_TRACE, "GUID: %ws\n", wszguidMasterKey));
|
|
}
|
|
|
|
#if 0
|
|
cbMasterKey = min(pCacheEntry->cbMasterKey, sizeof(rgbMasterKey));
|
|
CopyMemory(rgbMasterKey, pCacheEntry->pbMasterKey, cbMasterKey);
|
|
|
|
LsaProtectMemory(rgbMasterKey, cbMasterKey);
|
|
|
|
D_DebugLog((DEB_TRACE, "Master key:\n"));
|
|
D_DPAPIDumpHexData(DEB_TRACE, " ", rgbMasterKey, cbMasterKey);
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
#if DBG
|
|
void
|
|
DumpMasterKeyCache(void)
|
|
{
|
|
PLIST_ENTRY ListEntry;
|
|
PLIST_ENTRY ListHead;
|
|
PMASTERKEY_CACHE_ENTRY pCacheEntry;
|
|
ULONG i = 0;
|
|
|
|
RtlEnterCriticalSection( &g_MasterKeyCacheCritSect );
|
|
|
|
D_DebugLog((DEB_TRACE, "Master key cache\n"));
|
|
|
|
ListHead = &g_MasterKeyCacheList;
|
|
|
|
for( ListEntry = ListHead->Flink;
|
|
ListEntry != ListHead;
|
|
ListEntry = ListEntry->Flink )
|
|
{
|
|
pCacheEntry = CONTAINING_RECORD( ListEntry, MASTERKEY_CACHE_ENTRY, Next );
|
|
|
|
D_DebugLog((DEB_TRACE, "---- %d ----\n", ++i));
|
|
|
|
DumpMasterKeyEntry(pCacheEntry);
|
|
}
|
|
|
|
RtlLeaveCriticalSection( &g_MasterKeyCacheCritSect );
|
|
}
|
|
#endif
|
|
|
|
|
|
BOOL
|
|
SearchMasterKeyCache(
|
|
IN PLUID pLogonId,
|
|
IN GUID *pguidMasterKey,
|
|
IN OUT PBYTE *ppbMasterKey,
|
|
OUT PDWORD pcbMasterKey
|
|
)
|
|
/*++
|
|
|
|
Search the masterkey sorted masterkey cache by pLogonId then by
|
|
pguidMasterKey.
|
|
|
|
On success, return value is true, and ppbMasterKey will point to a buffer
|
|
allocated on behalf of the caller containing the specified masterkey.
|
|
The caller must free the buffer using SSFree().
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY ListEntry;
|
|
PLIST_ENTRY ListHead;
|
|
BOOL fSuccess = FALSE;
|
|
|
|
#if DBG
|
|
WCHAR wszguidMasterKey[MAX_GUID_SZ_CHARS];
|
|
#endif
|
|
|
|
RtlEnterCriticalSection( &g_MasterKeyCacheCritSect );
|
|
|
|
#if DBG
|
|
D_DebugLog((DEB_TRACE, "SearchMasterKeyCache\n"));
|
|
D_DebugLog((DEB_TRACE, "LogonId: %d.%d\n", pLogonId->LowPart, pLogonId->HighPart));
|
|
|
|
if( MyGuidToStringW( pguidMasterKey, wszguidMasterKey ) != 0 )
|
|
{
|
|
D_DebugLog((DEB_TRACE, "Invalid GUID:\n"));
|
|
D_DPAPIDumpHexData(DEB_TRACE, " ", (PBYTE)pguidMasterKey, sizeof(GUID));
|
|
}
|
|
else
|
|
{
|
|
D_DebugLog((DEB_TRACE, "GUID: %ws\n", wszguidMasterKey));
|
|
}
|
|
|
|
//DumpMasterKeyCache();
|
|
#endif
|
|
|
|
ListHead = &g_MasterKeyCacheList;
|
|
|
|
for( ListEntry = ListHead->Flink;
|
|
ListEntry != ListHead;
|
|
ListEntry = ListEntry->Flink ) {
|
|
|
|
PMASTERKEY_CACHE_ENTRY pCacheEntry;
|
|
signed int comparator;
|
|
|
|
pCacheEntry = CONTAINING_RECORD( ListEntry, MASTERKEY_CACHE_ENTRY, Next );
|
|
|
|
//
|
|
// search by LogonId, then by GUID.
|
|
//
|
|
|
|
comparator = memcmp(pLogonId, &pCacheEntry->LogonId, sizeof(LUID));
|
|
|
|
if( comparator < 0 )
|
|
continue;
|
|
|
|
if( comparator > 0 )
|
|
break;
|
|
|
|
comparator = memcmp(pguidMasterKey, &pCacheEntry->guidMasterKey, sizeof(GUID));
|
|
|
|
if( comparator < 0 )
|
|
continue;
|
|
|
|
if( comparator > 0 )
|
|
break;
|
|
|
|
//
|
|
// match found.
|
|
//
|
|
|
|
*pcbMasterKey = pCacheEntry->cbMasterKey;
|
|
*ppbMasterKey = (PBYTE)SSAlloc( *pcbMasterKey );
|
|
if( *ppbMasterKey != NULL ) {
|
|
CopyMemory( *ppbMasterKey, pCacheEntry->pbMasterKey, *pcbMasterKey );
|
|
fSuccess = TRUE;
|
|
}
|
|
|
|
|
|
//
|
|
// update last access time.
|
|
//
|
|
|
|
GetSystemTimeAsFileTime( &pCacheEntry->ftLastAccess );
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
RtlLeaveCriticalSection( &g_MasterKeyCacheCritSect );
|
|
|
|
if( fSuccess ) {
|
|
|
|
//
|
|
// decrypt (in-place) the returned encrypted cache entry.
|
|
//
|
|
|
|
LsaUnprotectMemory( *ppbMasterKey, *pcbMasterKey );
|
|
}
|
|
|
|
D_DebugLog((DEB_TRACE, "SearchMasterKeyCache returned %s\n", fSuccess ? "FOUND" : "NOT FOUND"));
|
|
|
|
return fSuccess;
|
|
}
|
|
|
|
|
|
BOOL
|
|
InsertMasterKeyCache(
|
|
IN PLUID pLogonId,
|
|
IN GUID *pguidMasterKey,
|
|
IN PBYTE pbMasterKey,
|
|
IN DWORD cbMasterKey
|
|
)
|
|
/*++
|
|
|
|
Insert the specified masterkey into the cahce sorted by pLogonId then by
|
|
pguidMasterKey.
|
|
|
|
The return value is TRUE on success.
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY ListEntry;
|
|
PLIST_ENTRY ListHead;
|
|
PMASTERKEY_CACHE_ENTRY pCacheEntry;
|
|
PMASTERKEY_CACHE_ENTRY pThisCacheEntry = NULL;
|
|
BOOL fInserted = FALSE;
|
|
|
|
D_DebugLog((DEB_TRACE, "InsertMasterKeyCache\n"));
|
|
|
|
if( cbMasterKey > sizeof(pCacheEntry->pbMasterKey) )
|
|
return FALSE;
|
|
|
|
pCacheEntry = (PMASTERKEY_CACHE_ENTRY)SSAlloc( sizeof( MASTERKEY_CACHE_ENTRY ) );
|
|
if( pCacheEntry == NULL )
|
|
return FALSE;
|
|
|
|
CopyMemory( &pCacheEntry->LogonId, pLogonId, sizeof(LUID) );
|
|
CopyMemory( &pCacheEntry->guidMasterKey, pguidMasterKey, sizeof(GUID) );
|
|
pCacheEntry->cbMasterKey = cbMasterKey;
|
|
CopyMemory( pCacheEntry->pbMasterKey, pbMasterKey, cbMasterKey );
|
|
|
|
LsaProtectMemory( pCacheEntry->pbMasterKey, cbMasterKey );
|
|
|
|
GetSystemTimeAsFileTime( &pCacheEntry->ftLastAccess );
|
|
|
|
RtlEnterCriticalSection( &g_MasterKeyCacheCritSect );
|
|
|
|
#if DBG
|
|
DumpMasterKeyEntry(pCacheEntry);
|
|
#endif
|
|
|
|
ListHead = &g_MasterKeyCacheList;
|
|
|
|
for( ListEntry = ListHead->Flink;
|
|
ListEntry != ListHead;
|
|
ListEntry = ListEntry->Flink ) {
|
|
|
|
signed int comparator;
|
|
|
|
pThisCacheEntry = CONTAINING_RECORD( ListEntry, MASTERKEY_CACHE_ENTRY, Next );
|
|
|
|
//
|
|
// insert into list sorted by LogonId, then sorted by GUID.
|
|
//
|
|
|
|
comparator = memcmp(pLogonId, &pThisCacheEntry->LogonId, sizeof(LUID));
|
|
|
|
if( comparator < 0 )
|
|
continue;
|
|
|
|
if( comparator == 0 ) {
|
|
comparator = memcmp( pguidMasterKey, &pThisCacheEntry->guidMasterKey, sizeof(GUID));
|
|
|
|
if( comparator < 0 )
|
|
continue;
|
|
|
|
if( comparator == 0 ) {
|
|
|
|
//
|
|
// don't insert duplicate records.
|
|
// this would only happen in a race condition with multiple threads.
|
|
//
|
|
|
|
RtlSecureZeroMemory( pCacheEntry, sizeof(MASTERKEY_CACHE_ENTRY) );
|
|
SSFree( pCacheEntry );
|
|
fInserted = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// insert prior to current record.
|
|
//
|
|
|
|
InsertHeadList( pThisCacheEntry->Next.Blink, &pCacheEntry->Next );
|
|
fInserted = TRUE;
|
|
break;
|
|
}
|
|
|
|
if( !fInserted ) {
|
|
if( pThisCacheEntry == NULL ) {
|
|
InsertHeadList( ListHead, &pCacheEntry->Next );
|
|
} else {
|
|
InsertHeadList( &pThisCacheEntry->Next, &pCacheEntry->Next );
|
|
}
|
|
}
|
|
|
|
#if DBG
|
|
//DumpMasterKeyCache();
|
|
#endif
|
|
|
|
RtlLeaveCriticalSection( &g_MasterKeyCacheCritSect );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
PurgeMasterKeyCache(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Purge masterkey cache of timed-out entries, or entries associated with
|
|
terminated logon sessions.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// build active session table.
|
|
//
|
|
|
|
// don't touch entries that have an entry in active session table.
|
|
// assume LUID_SYSTEM in table.
|
|
//
|
|
|
|
// entries not in table: discard after 15 minute timeout.
|
|
//
|
|
|
|
|
|
// if entry in table, find next LUID
|
|
// else, if entry expired, check timeout. if expired, remove.
|
|
//
|
|
|
|
PLIST_ENTRY ListEntry;
|
|
PLIST_ENTRY ListHead;
|
|
|
|
|
|
RtlEnterCriticalSection( &g_MasterKeyCacheCritSect );
|
|
|
|
ListHead = &g_MasterKeyCacheList;
|
|
|
|
for( ListEntry = ListHead->Flink;
|
|
ListEntry != ListHead;
|
|
ListEntry = ListEntry->Flink ) {
|
|
|
|
PMASTERKEY_CACHE_ENTRY pCacheEntry;
|
|
// signed int comparator;
|
|
|
|
pCacheEntry = CONTAINING_RECORD( ListEntry, MASTERKEY_CACHE_ENTRY, Next );
|
|
}
|
|
|
|
|
|
RtlLeaveCriticalSection( &g_MasterKeyCacheCritSect );
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
RemoveMasterKeyCache(
|
|
IN PLUID pLogonId
|
|
)
|
|
/*++
|
|
|
|
Remove all entries from the masterkey cache corresponding to the specified
|
|
pLogonId.
|
|
|
|
The purpose of this routine is to purge the masterkey cache of entries
|
|
associated with (now) non-existent logon sessions.
|
|
|
|
--*/
|
|
{
|
|
|
|
PLIST_ENTRY ListEntry;
|
|
PLIST_ENTRY ListHead;
|
|
|
|
RtlEnterCriticalSection( &g_MasterKeyCacheCritSect );
|
|
|
|
D_DebugLog((DEB_TRACE, "RemoveMasterKeyCache\n"));
|
|
D_DebugLog((DEB_TRACE, "LogonId: %d.%d\n", pLogonId->LowPart, pLogonId->HighPart));
|
|
|
|
ListHead = &g_MasterKeyCacheList;
|
|
|
|
for( ListEntry = ListHead->Flink;
|
|
ListEntry != ListHead;
|
|
ListEntry = ListEntry->Flink ) {
|
|
|
|
PMASTERKEY_CACHE_ENTRY pCacheEntry;
|
|
signed int comparator;
|
|
|
|
pCacheEntry = CONTAINING_RECORD( ListEntry, MASTERKEY_CACHE_ENTRY, Next );
|
|
|
|
//
|
|
// remove all entries with matching LogonId.
|
|
//
|
|
|
|
comparator = memcmp(pLogonId, &pCacheEntry->LogonId, sizeof(LUID));
|
|
|
|
if( comparator > 0 )
|
|
break;
|
|
|
|
if( comparator < 0 )
|
|
continue;
|
|
|
|
//
|
|
// match found.
|
|
//
|
|
|
|
RemoveEntryList( &pCacheEntry->Next );
|
|
|
|
RtlSecureZeroMemory( pCacheEntry, sizeof(MASTERKEY_CACHE_ENTRY) );
|
|
SSFree( pCacheEntry );
|
|
}
|
|
|
|
|
|
RtlLeaveCriticalSection( &g_MasterKeyCacheCritSect );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
InitializeKeyCache(
|
|
VOID
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
Status = RtlInitializeCriticalSection( &g_MasterKeyCacheCritSect );
|
|
if(!NT_SUCCESS(Status))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
InitializeListHead( &g_MasterKeyCacheList );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
DeleteKeyCache(
|
|
VOID
|
|
)
|
|
{
|
|
|
|
//
|
|
// remove all list entries.
|
|
//
|
|
|
|
RtlEnterCriticalSection( &g_MasterKeyCacheCritSect );
|
|
|
|
while ( !IsListEmpty( &g_MasterKeyCacheList ) ) {
|
|
|
|
PMASTERKEY_CACHE_ENTRY pCacheEntry;
|
|
|
|
pCacheEntry = CONTAINING_RECORD(
|
|
g_MasterKeyCacheList.Flink,
|
|
MASTERKEY_CACHE_ENTRY,
|
|
Next
|
|
);
|
|
|
|
RemoveEntryList( &pCacheEntry->Next );
|
|
|
|
RtlSecureZeroMemory( pCacheEntry, sizeof(MASTERKEY_CACHE_ENTRY) );
|
|
SSFree( pCacheEntry );
|
|
}
|
|
|
|
RtlLeaveCriticalSection( &g_MasterKeyCacheCritSect );
|
|
|
|
RtlDeleteCriticalSection( &g_MasterKeyCacheCritSect );
|
|
}
|
|
|