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.
1231 lines
32 KiB
1231 lines
32 KiB
/*++
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Copyright (c) 1995 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
certnotf.cxx
|
|
|
|
Abstract:
|
|
|
|
This module contains the code for the class to deal with CAPI store change notifications
|
|
|
|
Author:
|
|
|
|
Alex Mallet [amallet] 17-Dec-1997
|
|
|
|
Revision History:
|
|
--*/
|
|
|
|
|
|
|
|
#include "tcpdllp.hxx"
|
|
#pragma hdrstop
|
|
|
|
#include <winbase.h>
|
|
#include <dbgutil.h>
|
|
#include <ole2.h>
|
|
#include <imd.h>
|
|
#include <nturtl.h>
|
|
#include <certnotf.hxx>
|
|
|
|
|
|
|
|
#if DBG
|
|
#define VALIDATE_HEAP() DBG_ASSERT( RtlValidateProcessHeaps() )
|
|
#else
|
|
#define VALIDATE_HEAP()
|
|
#endif
|
|
|
|
DWORD STORE_CHANGE_ENTRY::m_dwNumEntries = 0;
|
|
CRITICAL_SECTION *STORE_CHANGE_NOTIFIER::m_pStoreListCS = NULL;
|
|
|
|
|
|
STORE_CHANGE_NOTIFIER::STORE_CHANGE_NOTIFIER() :
|
|
m_dwSignature(NOTIFIER_GOOD_SIG)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Constructor
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Returns:
|
|
|
|
Nothing
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Critical sections
|
|
//
|
|
if ( !STORE_CHANGE_NOTIFIER::m_pStoreListCS )
|
|
{
|
|
STORE_CHANGE_NOTIFIER::m_pStoreListCS = new CRITICAL_SECTION;
|
|
|
|
if ( STORE_CHANGE_NOTIFIER::m_pStoreListCS )
|
|
{
|
|
INITIALIZE_CRITICAL_SECTION(STORE_CHANGE_NOTIFIER::m_pStoreListCS);
|
|
}
|
|
else
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"Failed to allocate memory for store list critical section !\n"));
|
|
}
|
|
}
|
|
|
|
InitializeListHead( &m_StoreList );
|
|
}
|
|
|
|
STORE_CHANGE_NOTIFIER::~STORE_CHANGE_NOTIFIER()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Destructor
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Returns:
|
|
|
|
Nothing
|
|
|
|
--*/
|
|
{
|
|
DBG_ASSERT( CheckSignature() );
|
|
|
|
STORE_CHANGE_NOTIFIER::Lock();
|
|
|
|
//
|
|
// Clean up all the stores, so we don't get bitten in the @$$ when trying to
|
|
// clean up the STORE_CHANGE_ENTRY objects : CertCloseStore() calls RtlDeregisterWait()
|
|
// which can't be called from inside a callback function, so we need to clean up
|
|
// the cert stores before triggering any of the callbacks used to clean up the
|
|
// STORE_CHANGE_ENTRY objects.
|
|
//
|
|
LIST_ENTRY *pListEntry;
|
|
STORE_CHANGE_ENTRY *pStoreEntry;
|
|
|
|
for ( pListEntry = m_StoreList.Flink;
|
|
pListEntry != &m_StoreList;
|
|
pListEntry = pListEntry->Flink )
|
|
{
|
|
pStoreEntry = CONTAINING_RECORD( pListEntry, STORE_CHANGE_ENTRY, m_StoreListEntry );
|
|
|
|
CertCloseStore( pStoreEntry->m_hCertStore,
|
|
0 );
|
|
pStoreEntry->m_hCertStore = NULL;
|
|
}
|
|
|
|
//
|
|
// Go through both the active and inactive store entries and start the process to
|
|
// clean them up [they'll actually be cleaned up on a different thread, in a callback from
|
|
// the thread pool].
|
|
//
|
|
while ( !IsListEmpty( &m_StoreList ) )
|
|
{
|
|
pStoreEntry = CONTAINING_RECORD( m_StoreList.Flink,
|
|
STORE_CHANGE_ENTRY,
|
|
m_StoreListEntry );
|
|
|
|
RemoveEntryList( &(pStoreEntry->m_StoreListEntry) );
|
|
|
|
InitializeListHead( &(pStoreEntry->m_StoreListEntry) );
|
|
|
|
StartCleanup( pStoreEntry );
|
|
}
|
|
|
|
STORE_CHANGE_NOTIFIER::Unlock();
|
|
|
|
//
|
|
// The STORE_CHANGE_ENTRY objects are cleaned up on a different so we loop and wait
|
|
// until they've all been cleaned up, to avoid problems with a thread/DLL going away
|
|
// before proper cleanup has occurred.
|
|
//
|
|
DWORD dwNumWaits = 0;
|
|
DWORD dwNumEntries = 0;
|
|
while ( ( dwNumEntries = STORE_CHANGE_ENTRY::QueryStoreEntryCount() ) &&
|
|
dwNumWaits < 30 * 5 ) //sleep for 2 secs => 30x/min, wait for 5 mins
|
|
{
|
|
Sleep( 2000 );
|
|
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"Waiting %d seconds for %d store entries to be cleaned up\n",
|
|
dwNumWaits * 2,
|
|
dwNumEntries));
|
|
|
|
dwNumWaits++;
|
|
}
|
|
|
|
if ( dwNumEntries != 0 )
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"WARNING : Failed to clean up all STORE_CHANGE_ENTRY objects, %d left\n",
|
|
dwNumEntries));
|
|
}
|
|
else
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"Cleaned up all store entries \n"));
|
|
}
|
|
|
|
m_dwSignature = NOTIFIER_BAD_SIG;
|
|
|
|
}
|
|
|
|
VOID STORE_CHANGE_NOTIFIER::ReleaseRegisteredStores()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Releases all the events that have been registered for store change notifications
|
|
and closes the stores that were being watched.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Returns:
|
|
|
|
Nothing
|
|
|
|
--*/
|
|
|
|
{
|
|
LIST_ENTRY * pEntry;
|
|
|
|
DBG_ASSERT( CheckSignature() );
|
|
|
|
STORE_CHANGE_NOTIFIER::Lock();
|
|
|
|
for ( pEntry = m_StoreList.Flink;
|
|
pEntry != &m_StoreList;
|
|
pEntry = pEntry->Flink )
|
|
{
|
|
STORE_CHANGE_ENTRY *pStoreEntry = CONTAINING_RECORD( pEntry,
|
|
STORE_CHANGE_ENTRY,
|
|
m_StoreListEntry );
|
|
|
|
StartCleanup( pStoreEntry );
|
|
}
|
|
|
|
STORE_CHANGE_NOTIFIER::Unlock();
|
|
}
|
|
|
|
|
|
VOID STORE_CHANGE_NOTIFIER::StartCleanup( IN STORE_CHANGE_ENTRY *pEntry )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Start the cleanup for a STORE_CHANGE_ENTRY object.
|
|
This function MUST be called between calls to STORE_CHANGE_NOTIFIER::Lock()/Unlock() !
|
|
|
|
Arguments:
|
|
|
|
pEntry - STORE_CHANGE_ENTRY to clean up.
|
|
|
|
Returns:
|
|
|
|
Nothing
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// Grab the lock, mark the entry as being ready for deletion,signal the event associated
|
|
// with it and release a reference to the entry . We hold the lock, so we have exclusive
|
|
// access to the entries. Possible execution paths :
|
|
//
|
|
// #1. No callback comes in while we're executing this code. We set the deletion bit and
|
|
// signal the event. If the callback now fires, it smacks into
|
|
// the lock and waits its turn. If it doesn't fire, that's OK too. We unlock and go on our
|
|
// merry way. At some point in time, the callback -will- fire, because we didn't call
|
|
// UnregisterWait(). Then, in the callback, we see the "delete me" flag and delete the
|
|
// entry. Also, the wait is cancelled because we acquired the wait handle with the
|
|
// WT_EXECUTEDELETEWAIT flag, which removes the wait immediately after calling the callback.
|
|
//
|
|
// #2. A callback comes in while we're executing this code. It waits to acquire the lock,
|
|
// and by the time it acquires it we've set the deletion bit and signalled the event.
|
|
// The callback sees the deletion bit and deletes the entry. The wait is cancelled, and
|
|
// so it doesn't matter that we signalled the event.
|
|
//
|
|
|
|
pEntry->MarkForDelete();
|
|
|
|
SetEvent( pEntry->QueryStoreEvent() );
|
|
|
|
}
|
|
|
|
BOOL STORE_CHANGE_NOTIFIER::IsStoreRegisteredForChange( IN LPTSTR pszStoreName,
|
|
IN NOTIFFNCPTR pFncPtr,
|
|
IN LPVOID pvParam )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check whether the store described by the parameters already has an event registered
|
|
for change notifications.
|
|
|
|
Arguments:
|
|
|
|
pszStoreName - name of cert store
|
|
pFncPtr - pointer to notification function. If pFncPtr == INVALID_FNC_PTR, checks for any
|
|
notification functions
|
|
pvParam - arg to notification function; if pFncPtr == INVALID_FNC_PTR, is ignored
|
|
|
|
Returns:
|
|
|
|
TRUE if store is registered, FALSE if not.
|
|
|
|
--*/
|
|
{
|
|
DBG_ASSERT( CheckSignature() );
|
|
|
|
DBG_ASSERT( pszStoreName );
|
|
DBG_ASSERT( pFncPtr );
|
|
|
|
PSTORE_CHANGE_ENTRY pEntry = InternalIsStoreRegisteredForChange( pszStoreName,
|
|
pFncPtr,
|
|
pvParam ) ;
|
|
|
|
|
|
return ( pEntry ? TRUE : FALSE );
|
|
|
|
}
|
|
|
|
PSTORE_CHANGE_ENTRY
|
|
STORE_CHANGE_NOTIFIER::InternalIsStoreRegisteredForChange( IN LPTSTR pszStoreName,
|
|
IN NOTIFFNCPTR pFncPtr,
|
|
IN LPVOID pvParam )
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check whether the store described by the parameters already has an event registered
|
|
for change notifications.
|
|
|
|
Arguments:
|
|
|
|
pszStoreName - name of cert store
|
|
pFncPtr - pointer to notification function. If pFncPtr == INVALID_FNC_PTR, checks for any
|
|
notification functions
|
|
pvParam - arg for notification function. Ignored if pFncPtr == INVALID_FNC_PTR
|
|
|
|
Returns:
|
|
|
|
pointer to STORE_ENTRY structure that contains the registration info, NULL if non-existent.
|
|
|
|
--*/
|
|
|
|
{
|
|
DBG_ASSERT( CheckSignature() );
|
|
|
|
DBG_ASSERT( pszStoreName );
|
|
DBG_ASSERT( pFncPtr );
|
|
|
|
STORE_CHANGE_ENTRY *pStoreEntry = NULL, *pMatchingEntry = NULL;
|
|
LIST_ENTRY *pEntry;
|
|
BOOL fFound = FALSE;
|
|
|
|
STORE_CHANGE_NOTIFIER::Lock();
|
|
|
|
for ( pEntry = m_StoreList.Flink;
|
|
pEntry != &m_StoreList;
|
|
pEntry = pEntry->Flink )
|
|
{
|
|
pStoreEntry = CONTAINING_RECORD( pEntry, STORE_CHANGE_ENTRY, m_StoreListEntry );
|
|
|
|
if ( pStoreEntry->Matches( pszStoreName,
|
|
pFncPtr,
|
|
pvParam ) )
|
|
{
|
|
pMatchingEntry = pStoreEntry;
|
|
break;
|
|
}
|
|
}
|
|
|
|
STORE_CHANGE_NOTIFIER::Unlock();
|
|
|
|
return pMatchingEntry;
|
|
}
|
|
|
|
|
|
BOOL STORE_CHANGE_NOTIFIER::RegisterStoreForChange( IN LPTSTR pszStoreName,
|
|
IN HCERTSTORE hStore,
|
|
IN NOTIFFNCPTR pFncPtr,
|
|
IN LPVOID pvParam )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Register the store for change notifications
|
|
|
|
Critical Sections acquired : m_pStoreListCS, m_pStoreArrayCS
|
|
|
|
Arguments:
|
|
|
|
pszStoreName - name of cert store
|
|
hCertStore - handle to cert store
|
|
pFncPtr - pointer to notification function
|
|
pvParam - arg to notification function
|
|
|
|
Returns:
|
|
|
|
TRUE if store was registered, FALSE if not
|
|
--*/
|
|
{
|
|
DBG_ASSERT( CheckSignature() );
|
|
|
|
DBG_ASSERT( pszStoreName );
|
|
DBG_ASSERT( pFncPtr );
|
|
|
|
BOOL fAlreadyRegistered = FALSE;
|
|
BOOL fSuccess = FALSE;
|
|
PSTORE_CHANGE_ENTRY pStoreEntry = NULL;
|
|
|
|
STORE_CHANGE_NOTIFIER::Lock();
|
|
|
|
//
|
|
// Check whether there already some notifications on this store
|
|
//
|
|
pStoreEntry = InternalIsStoreRegisteredForChange( pszStoreName,
|
|
(NOTIFFNCPTR) INVALID_FNC_PTR,
|
|
NULL );
|
|
|
|
fAlreadyRegistered = (pStoreEntry == NULL ? FALSE : TRUE );
|
|
|
|
//
|
|
// If this is a totally new store, need to allocate and fill in new store watch entry
|
|
//
|
|
if ( !pStoreEntry )
|
|
{
|
|
HCERTSTORE hCertStore = NULL;
|
|
HANDLE hStoreEvent = NULL;
|
|
HANDLE hWaitHandle = NULL;
|
|
|
|
pStoreEntry = new STORE_CHANGE_ENTRY( this,
|
|
pszStoreName,
|
|
hStore,
|
|
pFncPtr,
|
|
pvParam) ;
|
|
|
|
if ( !pStoreEntry || pStoreEntry->GetLastError() )
|
|
{
|
|
if ( pStoreEntry )
|
|
{
|
|
SetLastError( pStoreEntry->GetLastError() );
|
|
}
|
|
goto EndRegisterStore;
|
|
}
|
|
|
|
//
|
|
// Add the entire entry to the list of stores to be watched
|
|
//
|
|
InsertTailList( &m_StoreList, &pStoreEntry->m_StoreListEntry );
|
|
}
|
|
//
|
|
// Else, possibly update the notification functions to be called - only update
|
|
// if there isn't already a copy of this function with the same parameters
|
|
//
|
|
else
|
|
{
|
|
DBG_ASSERT( pStoreEntry->QueryStoreHandle() &&
|
|
pStoreEntry->QueryStoreEvent() &&
|
|
pStoreEntry->QueryNotifier() &&
|
|
pStoreEntry->QueryWaitHandle() );
|
|
|
|
if ( !pStoreEntry->ContainsNotifFnc( pFncPtr,
|
|
pvParam ) )
|
|
{
|
|
if ( !pStoreEntry->AddNotifFnc( pFncPtr,
|
|
pvParam ) )
|
|
{
|
|
SetLastError( pStoreEntry->GetLastError() );
|
|
goto EndRegisterStore;
|
|
}
|
|
}
|
|
}
|
|
|
|
fSuccess = TRUE;
|
|
|
|
EndRegisterStore:
|
|
|
|
if ( !fSuccess )
|
|
{
|
|
//
|
|
// If we failed to register the store and we allocated a new STORE_CHANGE_ENTRY
|
|
// object, clean it up. Note that ref count is only set if everything succeeds
|
|
//
|
|
if ( !fAlreadyRegistered && pStoreEntry )
|
|
{
|
|
delete pStoreEntry;
|
|
}
|
|
|
|
DBGPRINTF((DBG_CONTEXT,"Failed to register store %s : 0x%x\n",
|
|
pszStoreName, GetLastError()));
|
|
}
|
|
|
|
STORE_CHANGE_NOTIFIER::Unlock();
|
|
|
|
return fSuccess;
|
|
}
|
|
|
|
|
|
VOID STORE_CHANGE_NOTIFIER::UnregisterStore( IN LPTSTR pszStoreName,
|
|
IN NOTIFFNCPTR pNotifFnc,
|
|
IN LPVOID pvParam )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Unregister a notification function for a store
|
|
|
|
Arguments:
|
|
|
|
pszStoreName - name of store
|
|
pNotifFnc - notification function to deregister. If pNotifFnc == INVALID_FNC_PTR,
|
|
all notifications for that store are removed
|
|
pvParam - arg to notification function. Ignored if pNotifFnc == INVALID_FNC_PTR
|
|
|
|
--*/
|
|
{
|
|
DBG_ASSERT( CheckSignature() );
|
|
|
|
DBG_ASSERT( pszStoreName );
|
|
DBG_ASSERT( pNotifFnc );
|
|
|
|
STORE_CHANGE_ENTRY *pStoreEntry;
|
|
LIST_ENTRY *pEntry;
|
|
BOOL fRemoveAll = (pNotifFnc == (NOTIFFNCPTR) INVALID_FNC_PTR ? TRUE : FALSE );
|
|
|
|
STORE_CHANGE_NOTIFIER::Lock();
|
|
|
|
//
|
|
// Iterate through the active list to find it
|
|
//
|
|
for ( pEntry = m_StoreList.Flink;
|
|
pEntry != &m_StoreList;
|
|
pEntry = pEntry->Flink )
|
|
{
|
|
pStoreEntry = CONTAINING_RECORD( pEntry, STORE_CHANGE_ENTRY, m_StoreListEntry );
|
|
|
|
if ( pStoreEntry->Matches( pszStoreName,
|
|
pNotifFnc,
|
|
pvParam ) )
|
|
{
|
|
//
|
|
// If we're removing all notifications for this store, or there will be no
|
|
// notification functions left for this store, clean everything up
|
|
//
|
|
if ( fRemoveAll || ( pStoreEntry->RemoveNotifFnc( pNotifFnc,
|
|
pvParam ) &&
|
|
!pStoreEntry->HasNotifFncs() ) )
|
|
{
|
|
StartCleanup( pStoreEntry );
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
STORE_CHANGE_NOTIFIER::Unlock();
|
|
}
|
|
|
|
|
|
#if DBG
|
|
VOID STORE_CHANGE_NOTIFIER::DumpRegisteredStores()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dumps all the stores currently being watched
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Returns:
|
|
|
|
Nothing
|
|
--*/
|
|
|
|
{
|
|
STORE_CHANGE_ENTRY *pStoreEntry = NULL;
|
|
LIST_ENTRY *pEntry1 = NULL, *pEntry2 = NULL;
|
|
|
|
DBG_ASSERT( CheckSignature() );
|
|
|
|
STORE_CHANGE_NOTIFIER::Lock();
|
|
|
|
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"------------------------------------------------------------------------\nRegistered Stores : \n"));
|
|
|
|
for ( pEntry1 = m_StoreList.Flink;
|
|
pEntry1 != &m_StoreList;
|
|
pEntry1 = pEntry1->Flink )
|
|
{
|
|
pStoreEntry = CONTAINING_RECORD( pEntry1,
|
|
STORE_CHANGE_ENTRY,
|
|
m_StoreListEntry );
|
|
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"Store %s, store handle 0x%x, event handle 0x%x, wait handle 0x%x has the ff functions registered : \n",
|
|
pStoreEntry->QueryStoreName(),
|
|
pStoreEntry->QueryStoreHandle(),
|
|
pStoreEntry->QueryStoreEvent(),
|
|
pStoreEntry->QueryWaitHandle() ));
|
|
|
|
LIST_ENTRY *pFncs = pStoreEntry->QueryNotifFncChain();
|
|
|
|
for ( pEntry2 = pFncs->Flink;
|
|
pEntry2 != pFncs;
|
|
pEntry2 = pEntry2->Flink )
|
|
{
|
|
PNOTIF_FNC_CHAIN_ENTRY pNFE = CONTAINING_RECORD( pEntry2,
|
|
NOTIF_FNC_CHAIN_ENTRY,
|
|
ListEntry );
|
|
|
|
DBGPRINTF((DBG_CONTEXT,"Function 0x%x, parameter 0x%x\n",
|
|
pNFE->pNotifFnc,
|
|
pNFE->pvParam ));
|
|
}
|
|
|
|
}
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"------------------------------------------------------------------------\n"));
|
|
|
|
STORE_CHANGE_NOTIFIER::Unlock();
|
|
}
|
|
#endif //DBG
|
|
|
|
|
|
VOID NTAPI STORE_CHANGE_NOTIFIER::NotifFncCaller( IN PVOID pvCallbackArg,
|
|
IN BOOLEAN fUnused )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the function called when one of the events we registered for is signalled.
|
|
The context passed in can be in one of three states :
|
|
|
|
1. Valid : has an associated chain of notification functions that are to be called.
|
|
2. Invalid : don't do any processing, because we're not interested in that store anymore
|
|
ie UnregisterStore() has been called on it
|
|
3. To Be Deleted : the context is to be deleted, because we're shutting down
|
|
|
|
Arguments:
|
|
|
|
pvCallbackArg - context pointer
|
|
fUnused - boolean, not used. [part of WAITFORTIMERCALLBACKFUNC prototype, which this function
|
|
has to conform to]
|
|
|
|
--*/
|
|
{
|
|
LIST_ENTRY * pNotifFncChain = NULL;
|
|
PSTORE_CHANGE_ENTRY pEntry = NULL;
|
|
|
|
if ( !pvCallbackArg )
|
|
{
|
|
DBG_ASSERT( FALSE ); //make sure we barf in debug build
|
|
|
|
return;
|
|
}
|
|
|
|
pEntry = (PSTORE_CHANGE_ENTRY) pvCallbackArg;
|
|
|
|
//
|
|
// Make sure we have exclusive access
|
|
//
|
|
STORE_CHANGE_NOTIFIER::Lock();
|
|
|
|
DBG_ASSERT( pEntry->CheckSignature() );
|
|
|
|
//
|
|
// If we signalled the event ourselves because we need to clean up this context,
|
|
// just delete it - we know we won't get any more callbacks, since we acquired the
|
|
// wait handle with WTEXECUTEDELETEWAIT. We don't need to remove it from a list
|
|
// because it has already been removed prior to this callback being generated.
|
|
// [in the destructor for STORE_CHANGE_NOTIFIER]
|
|
//
|
|
if ( !pEntry->IsMarkedForDelete() && !pEntry->IsInvalid() )
|
|
{
|
|
//
|
|
// Make a copy of the list of notification functions to call, so that even if
|
|
// one of the functions called changes the list, we still have a kosher copy to
|
|
// work with.
|
|
//
|
|
// Note that another assumption is that if functions A and B are both in the list
|
|
// being walked, and A is called before B, it doesn't destroy anything that B uses.
|
|
//
|
|
|
|
pNotifFncChain = CopyNotifFncChain( pEntry->QueryNotifFncChain() );
|
|
|
|
//
|
|
// Remove this entry from the active list and delete it: it'll never get notified again
|
|
// because we acquired it with the WTEXECUTEDELETEWAIT flag
|
|
//
|
|
|
|
}
|
|
|
|
RemoveEntryList( &(pEntry->m_StoreListEntry) );
|
|
|
|
delete pEntry;
|
|
|
|
STORE_CHANGE_NOTIFIER::Unlock();
|
|
|
|
// Call the notification functions now, after releasing the lock. This
|
|
// prevents deadlock with SSPIFILT. In particular the scenario where:
|
|
//
|
|
// SSPIFILT__AddFullyQualifiedItem acquires SSPI then StoreChange
|
|
// NotifyFncCaller acquires StoreChange then SSPI
|
|
//
|
|
// should be avoided
|
|
|
|
if ( pNotifFncChain )
|
|
{
|
|
NOTIF_FNC_CHAIN_ENTRY * pChainEntry = NULL;
|
|
LIST_ENTRY * pListEntry = NULL;
|
|
|
|
//
|
|
// Walk the list, call the functions and be generally studly ...
|
|
//
|
|
|
|
for ( pListEntry = pNotifFncChain->Flink;
|
|
pListEntry != pNotifFncChain;
|
|
pListEntry = pListEntry->Flink )
|
|
{
|
|
pChainEntry = CONTAINING_RECORD( pListEntry, NOTIF_FNC_CHAIN_ENTRY,
|
|
ListEntry );
|
|
|
|
if ( pChainEntry->pNotifFnc )
|
|
{
|
|
#ifdef NOTIFICATION_DBG
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"Calling notification fnc %p, arg %p\n",
|
|
pChainEntry->pNotifFnc, pChainEntry->pvParam));
|
|
#endif
|
|
|
|
pChainEntry->pNotifFnc( pChainEntry->pvParam );
|
|
|
|
}
|
|
}
|
|
|
|
DeallocateNotifFncChain( pNotifFncChain );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
STORE_CHANGE_ENTRY::STORE_CHANGE_ENTRY( STORE_CHANGE_NOTIFIER *pNotifier,
|
|
LPSTR pszStoreName,
|
|
HCERTSTORE hStore,
|
|
NOTIFFNCPTR pFncPtr,
|
|
PVOID pvParam ) :
|
|
m_dwSignature( STORE_ENTRY_GOOD_SIG ),
|
|
m_pNotifier( pNotifier ),
|
|
m_dwRefCount( -1 ),
|
|
m_dwError( 0 ),
|
|
m_hCertStore( NULL ),
|
|
m_hStoreEvent( NULL ),
|
|
m_hWaitHandle( NULL ),
|
|
m_fDeleteMe( FALSE ),
|
|
m_fInvalid( FALSE ),
|
|
m_strStoreName( pszStoreName )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Constructor
|
|
|
|
Arguments:
|
|
|
|
pNotifier - parent notifier object
|
|
pszStoreName - name of store to be watched
|
|
hStore - handle to store to be watched
|
|
pFncPtr - notification function to call when store changes
|
|
pvParam - arg to notification function
|
|
|
|
Returns:
|
|
|
|
Nothing
|
|
|
|
--*/
|
|
{
|
|
PNOTIF_FNC_CHAIN_ENTRY pNewNotifFnEntry = NULL;
|
|
|
|
INITIALIZE_CRITICAL_SECTION( &m_CS );
|
|
|
|
InitializeListHead( &m_NotifFncChain );
|
|
|
|
//
|
|
// Duplicate store handle to watch
|
|
//
|
|
m_hCertStore = CertDuplicateStore( hStore );
|
|
|
|
//
|
|
// Create the event to be signalled when store changes
|
|
//
|
|
if ( !(m_hStoreEvent = CreateEvent( NULL, //default attributes,
|
|
TRUE,
|
|
FALSE, //initally non-signalled
|
|
NULL ) ) ) //no name
|
|
{
|
|
m_dwError = GetLastError();
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Register with wait thread pool
|
|
//
|
|
#if 1
|
|
if ( !NT_SUCCESS( RtlRegisterWait( &m_hWaitHandle,
|
|
m_hStoreEvent,
|
|
STORE_CHANGE_NOTIFIER::NotifFncCaller,
|
|
(PVOID) this,
|
|
INFINITE,
|
|
WT_EXECUTEONLYONCE ) ) )
|
|
#else
|
|
|
|
if ( !(m_hWaitHandle = RegisterWaitForSingleObjectEx( m_hStoreEvent,
|
|
STORE_CHANGE_NOTIFIER::NotifFncCaller,
|
|
(PVOID) this,
|
|
INFINITE,
|
|
WT_EXECUTEONLYONCE ) ) )
|
|
#endif
|
|
{
|
|
m_dwError = GetLastError();
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Register for change events on the store
|
|
//
|
|
if ( !CertControlStore( m_hCertStore,
|
|
0,
|
|
CERT_STORE_CTRL_NOTIFY_CHANGE,
|
|
(LPVOID) &m_hStoreEvent) )
|
|
{
|
|
m_dwError = GetLastError();
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Create a new chain of notification functions
|
|
//
|
|
pNewNotifFnEntry = new NOTIF_FNC_CHAIN_ENTRY;
|
|
if ( !pNewNotifFnEntry )
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"Couldn't allocate new notification function chain : 0x%x\n",
|
|
GetLastError()));
|
|
m_dwError = ERROR_OUTOFMEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
pNewNotifFnEntry->pNotifFnc = pFncPtr;
|
|
pNewNotifFnEntry->pvParam = pvParam;
|
|
|
|
//
|
|
// Add the function to the chain
|
|
//
|
|
InsertTailList( &m_NotifFncChain, &pNewNotifFnEntry->ListEntry );
|
|
|
|
//
|
|
// Increment number of entry objects, to help in cleanup later
|
|
//
|
|
STORE_CHANGE_ENTRY::IncrementStoreEntryCount();
|
|
|
|
cleanup:
|
|
|
|
//
|
|
// Cleanup that's only done on error
|
|
//
|
|
if ( m_dwError != 0 )
|
|
{
|
|
if ( m_hWaitHandle )
|
|
{
|
|
RtlDeregisterWait( m_hWaitHandle );
|
|
m_hWaitHandle = NULL;
|
|
}
|
|
|
|
if ( m_hStoreEvent )
|
|
{
|
|
CloseHandle( m_hStoreEvent );
|
|
m_hStoreEvent = NULL;
|
|
}
|
|
|
|
if ( m_hCertStore )
|
|
{
|
|
CertCloseStore( m_hCertStore,
|
|
0 );
|
|
m_hCertStore = NULL;
|
|
}
|
|
|
|
//
|
|
// Go through the chain of notification functions and clean it up
|
|
//
|
|
while ( !IsListEmpty(&(m_NotifFncChain)) )
|
|
{
|
|
NOTIF_FNC_CHAIN_ENTRY *pChainEntry = CONTAINING_RECORD( m_NotifFncChain.Flink,
|
|
NOTIF_FNC_CHAIN_ENTRY,
|
|
ListEntry );
|
|
RemoveEntryList( &(pChainEntry->ListEntry) );
|
|
|
|
delete pChainEntry;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
STORE_CHANGE_ENTRY::~STORE_CHANGE_ENTRY()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Destructor
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return value:
|
|
|
|
None
|
|
--*/
|
|
|
|
{
|
|
DBG_ASSERT( CheckSignature() );
|
|
|
|
//
|
|
// No need to call RtlDeregisterWait() for the handle, since it's already been
|
|
// deregistered [after having been used in the callback]
|
|
//
|
|
|
|
//
|
|
// Clean up store change event
|
|
//
|
|
if ( m_hStoreEvent )
|
|
{
|
|
CloseHandle( m_hStoreEvent );
|
|
m_hStoreEvent = NULL;
|
|
}
|
|
|
|
//
|
|
// Close cert store
|
|
//
|
|
if ( m_hCertStore )
|
|
{
|
|
CertCloseStore( m_hCertStore,
|
|
0 );
|
|
m_hCertStore = NULL;
|
|
}
|
|
|
|
//
|
|
// Go through the chain of notification functions and clean it up
|
|
//
|
|
while ( !IsListEmpty(&(m_NotifFncChain)) )
|
|
{
|
|
NOTIF_FNC_CHAIN_ENTRY *pChainEntry = CONTAINING_RECORD( m_NotifFncChain.Flink,
|
|
NOTIF_FNC_CHAIN_ENTRY,
|
|
ListEntry );
|
|
RemoveEntryList( &(pChainEntry->ListEntry) );
|
|
|
|
delete pChainEntry;
|
|
}
|
|
|
|
DeleteCriticalSection( &m_CS );
|
|
|
|
//
|
|
// Another one bites the dust ...
|
|
//
|
|
STORE_CHANGE_ENTRY::DecrementStoreEntryCount();
|
|
|
|
m_dwSignature = STORE_ENTRY_BAD_SIG;
|
|
|
|
}
|
|
|
|
|
|
BOOL STORE_CHANGE_ENTRY::ContainsNotifFnc( IN NOTIFFNCPTR pFncPtr,
|
|
IN LPVOID pvParam )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Checks whether the given store watch entry contains the specified notification function with
|
|
the specified args
|
|
|
|
Arguments:
|
|
|
|
pFncPtr - pointer to notification function
|
|
pvParam - arg to notification function
|
|
Returns:
|
|
|
|
True if function is found, false otherwise
|
|
--*/
|
|
|
|
{
|
|
NOTIF_FNC_CHAIN_ENTRY *pChainEntry;
|
|
LIST_ENTRY *pEntry = NULL;
|
|
BOOL fFound = FALSE;
|
|
|
|
Lock();
|
|
|
|
for ( pEntry = m_NotifFncChain.Flink;
|
|
pEntry != &m_NotifFncChain;
|
|
pEntry = pEntry->Flink )
|
|
{
|
|
pChainEntry = CONTAINING_RECORD( pEntry, NOTIF_FNC_CHAIN_ENTRY, ListEntry );
|
|
|
|
if ( pChainEntry->pNotifFnc == pFncPtr && pChainEntry->pvParam == pvParam )
|
|
{
|
|
fFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
Unlock();
|
|
|
|
return fFound;
|
|
}
|
|
|
|
|
|
BOOL STORE_CHANGE_ENTRY::AddNotifFnc( IN NOTIFFNCPTR pFncPtr,
|
|
IN LPVOID pvParam )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Adds a notification function to a store entry
|
|
|
|
Arguments:
|
|
|
|
pFncPtr - pointer to notification function
|
|
pvParam - arg to notification function
|
|
|
|
Returns:
|
|
|
|
TRUE if function is added, FALSE otherwise
|
|
--*/
|
|
|
|
{
|
|
PNOTIF_FNC_CHAIN_ENTRY pNewFnc = new NOTIF_FNC_CHAIN_ENTRY;
|
|
|
|
if ( !pNewFnc )
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"Failed to get new notif fnc entry : 0x%x\n",
|
|
GetLastError()));
|
|
|
|
m_dwError = ERROR_OUTOFMEMORY;
|
|
return FALSE;
|
|
}
|
|
|
|
pNewFnc->pNotifFnc = pFncPtr;
|
|
pNewFnc->pvParam = pvParam;
|
|
|
|
Lock();
|
|
|
|
InsertTailList( &m_NotifFncChain, &pNewFnc->ListEntry );
|
|
|
|
Unlock();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL STORE_CHANGE_ENTRY::RemoveNotifFnc( IN NOTIFFNCPTR pFncPtr,
|
|
IN LPVOID pvParam )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Removes a notification function from a store entry
|
|
|
|
Arguments:
|
|
|
|
pFncPtr - pointer to notification function
|
|
pvParam - arg to notification function
|
|
|
|
Returns:
|
|
|
|
Noting
|
|
--*/
|
|
{
|
|
NOTIF_FNC_CHAIN_ENTRY *pChainEntry;
|
|
LIST_ENTRY *pEntry = NULL;
|
|
|
|
Lock();
|
|
|
|
for ( pEntry = m_NotifFncChain.Flink;
|
|
pEntry != &m_NotifFncChain;
|
|
pEntry = pEntry->Flink )
|
|
{
|
|
pChainEntry = CONTAINING_RECORD( pEntry, NOTIF_FNC_CHAIN_ENTRY, ListEntry );
|
|
|
|
if ( pChainEntry->pNotifFnc == pFncPtr && pChainEntry->pvParam == pvParam )
|
|
{
|
|
RemoveEntryList( pEntry );
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
Unlock();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL STORE_CHANGE_ENTRY::Matches( IN LPSTR pszStoreName,
|
|
IN NOTIFFNCPTR pFncPtr,
|
|
IN PVOID pvParam )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Checks whether a given store change object matches the given store/function combination
|
|
|
|
Arguments:
|
|
|
|
pszStoreName - name of cert store
|
|
pFncPtr - pointer to notification function; may be INVALID_FNC_PTR if any function will match
|
|
pvParam - parameter for function pointed to by pFncPtr
|
|
|
|
Return Value:
|
|
|
|
TRUE if it matches, FALSE if not
|
|
|
|
--*/
|
|
{
|
|
BOOL fFound = FALSE;
|
|
|
|
Lock();
|
|
|
|
if ( ( ( m_strStoreName.IsEmpty() && pszStoreName == NULL) ||
|
|
!strcmp(m_strStoreName.QueryStr(), pszStoreName) ) &&
|
|
( pFncPtr == (NOTIFFNCPTR) INVALID_FNC_PTR ||
|
|
ContainsNotifFnc( pFncPtr,
|
|
pvParam ) ) )
|
|
{
|
|
fFound = TRUE;
|
|
}
|
|
|
|
Unlock();
|
|
|
|
return fFound;
|
|
}
|
|
|
|
|
|
LIST_ENTRY* CopyNotifFncChain( LIST_ENTRY *pNotifFncChain )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Function that copies a chain of notification functions
|
|
|
|
Arguments:
|
|
|
|
pNotifFncChain - pointer to chain to be copied
|
|
|
|
Return Value:
|
|
|
|
Pointer to copied chain, NULL on failure
|
|
|
|
--*/
|
|
{
|
|
LIST_ENTRY *pNewChain = new LIST_ENTRY;
|
|
|
|
if ( !pNewChain )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
InitializeListHead( pNewChain );
|
|
|
|
NOTIF_FNC_CHAIN_ENTRY *pChainEntry, *pNewChainEntry;
|
|
LIST_ENTRY *pListEntry;
|
|
|
|
for ( pListEntry = pNotifFncChain->Flink;
|
|
pListEntry != pNotifFncChain;
|
|
pListEntry = pListEntry->Flink )
|
|
{
|
|
pChainEntry = CONTAINING_RECORD( pListEntry, NOTIF_FNC_CHAIN_ENTRY,
|
|
ListEntry );
|
|
|
|
|
|
pNewChainEntry = new NOTIF_FNC_CHAIN_ENTRY;
|
|
|
|
if ( !pNewChainEntry )
|
|
{
|
|
DeallocateNotifFncChain( pNewChain );
|
|
return NULL;
|
|
}
|
|
|
|
pNewChainEntry->pNotifFnc = pChainEntry->pNotifFnc;
|
|
pNewChainEntry->pvParam = pChainEntry->pvParam;
|
|
|
|
InsertTailList( pNewChain, &(pNewChainEntry->ListEntry) );
|
|
}
|
|
|
|
return ( pNewChain );
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID DeallocateNotifFncChain( LIST_ENTRY *pChain )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Function that cleans up resources associated with a notification function chain
|
|
|
|
Arguments:
|
|
|
|
pChain - chain to be cleaned up
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
if ( !pChain )
|
|
{
|
|
return;
|
|
}
|
|
|
|
NOTIF_FNC_CHAIN_ENTRY *pChainEntry;
|
|
LIST_ENTRY *pListEntry;
|
|
|
|
|
|
while ( !IsListEmpty( pChain ) )
|
|
{
|
|
pChainEntry = CONTAINING_RECORD( pChain->Flink,
|
|
NOTIF_FNC_CHAIN_ENTRY,
|
|
ListEntry );
|
|
|
|
RemoveEntryList( &(pChainEntry->ListEntry) );
|
|
|
|
delete pChainEntry;
|
|
}
|
|
|
|
delete pChain;
|
|
}
|
|
|
|
|
|
|