Source code of Windows XP (NT5)
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
31 KiB

  1. /*++
  2. Copyright (c) 1995 Microsoft Corporation
  3. Module Name:
  4. certnotf.cxx
  5. Abstract:
  6. This module contains the code for the class to deal with CAPI store change notifications
  7. Author:
  8. Alex Mallet [amallet] 17-Dec-1997
  9. Revision History:
  10. --*/
  11. #include "tcpdllp.hxx"
  12. #pragma hdrstop
  13. #include <winbase.h>
  14. #include <dbgutil.h>
  15. #include <ole2.h>
  16. #include <imd.h>
  17. #include <nturtl.h>
  18. #include <certnotf.hxx>
  19. #if DBG
  20. #define VALIDATE_HEAP() DBG_ASSERT( RtlValidateProcessHeaps() )
  21. #else
  22. #define VALIDATE_HEAP()
  23. #endif
  24. DWORD STORE_CHANGE_ENTRY::m_dwNumEntries = 0;
  25. CRITICAL_SECTION *STORE_CHANGE_NOTIFIER::m_pStoreListCS = NULL;
  26. STORE_CHANGE_NOTIFIER::STORE_CHANGE_NOTIFIER() :
  27. m_dwSignature(NOTIFIER_GOOD_SIG)
  28. /*++
  29. Routine Description:
  30. Constructor
  31. Arguments:
  32. None
  33. Returns:
  34. Nothing
  35. --*/
  36. {
  37. //
  38. // Critical sections
  39. //
  40. if ( !STORE_CHANGE_NOTIFIER::m_pStoreListCS )
  41. {
  42. STORE_CHANGE_NOTIFIER::m_pStoreListCS = new CRITICAL_SECTION;
  43. if ( STORE_CHANGE_NOTIFIER::m_pStoreListCS )
  44. {
  45. INITIALIZE_CRITICAL_SECTION(STORE_CHANGE_NOTIFIER::m_pStoreListCS);
  46. }
  47. else
  48. {
  49. DBGPRINTF((DBG_CONTEXT,
  50. "Failed to allocate memory for store list critical section !\n"));
  51. }
  52. }
  53. InitializeListHead( &m_StoreList );
  54. }
  55. STORE_CHANGE_NOTIFIER::~STORE_CHANGE_NOTIFIER()
  56. /*++
  57. Routine Description:
  58. Destructor
  59. Arguments:
  60. None
  61. Returns:
  62. Nothing
  63. --*/
  64. {
  65. DBG_ASSERT( CheckSignature() );
  66. STORE_CHANGE_NOTIFIER::Lock();
  67. //
  68. // Clean up all the stores, so we don't get bitten in the @$$ when trying to
  69. // clean up the STORE_CHANGE_ENTRY objects : CertCloseStore() calls RtlDeregisterWait()
  70. // which can't be called from inside a callback function, so we need to clean up
  71. // the cert stores before triggering any of the callbacks used to clean up the
  72. // STORE_CHANGE_ENTRY objects.
  73. //
  74. LIST_ENTRY *pListEntry;
  75. STORE_CHANGE_ENTRY *pStoreEntry;
  76. for ( pListEntry = m_StoreList.Flink;
  77. pListEntry != &m_StoreList;
  78. pListEntry = pListEntry->Flink )
  79. {
  80. pStoreEntry = CONTAINING_RECORD( pListEntry, STORE_CHANGE_ENTRY, m_StoreListEntry );
  81. CertCloseStore( pStoreEntry->m_hCertStore,
  82. 0 );
  83. pStoreEntry->m_hCertStore = NULL;
  84. }
  85. //
  86. // Go through both the active and inactive store entries and start the process to
  87. // clean them up [they'll actually be cleaned up on a different thread, in a callback from
  88. // the thread pool].
  89. //
  90. while ( !IsListEmpty( &m_StoreList ) )
  91. {
  92. pStoreEntry = CONTAINING_RECORD( m_StoreList.Flink,
  93. STORE_CHANGE_ENTRY,
  94. m_StoreListEntry );
  95. RemoveEntryList( &(pStoreEntry->m_StoreListEntry) );
  96. InitializeListHead( &(pStoreEntry->m_StoreListEntry) );
  97. StartCleanup( pStoreEntry );
  98. }
  99. STORE_CHANGE_NOTIFIER::Unlock();
  100. //
  101. // The STORE_CHANGE_ENTRY objects are cleaned up on a different so we loop and wait
  102. // until they've all been cleaned up, to avoid problems with a thread/DLL going away
  103. // before proper cleanup has occurred.
  104. //
  105. DWORD dwNumWaits = 0;
  106. DWORD dwNumEntries = 0;
  107. while ( ( dwNumEntries = STORE_CHANGE_ENTRY::QueryStoreEntryCount() ) &&
  108. dwNumWaits < 30 * 5 ) //sleep for 2 secs => 30x/min, wait for 5 mins
  109. {
  110. Sleep( 2000 );
  111. DBGPRINTF((DBG_CONTEXT,
  112. "Waiting %d seconds for %d store entries to be cleaned up\n",
  113. dwNumWaits * 2,
  114. dwNumEntries));
  115. dwNumWaits++;
  116. }
  117. if ( dwNumEntries != 0 )
  118. {
  119. DBGPRINTF((DBG_CONTEXT,
  120. "WARNING : Failed to clean up all STORE_CHANGE_ENTRY objects, %d left\n",
  121. dwNumEntries));
  122. }
  123. else
  124. {
  125. DBGPRINTF((DBG_CONTEXT,
  126. "Cleaned up all store entries \n"));
  127. }
  128. m_dwSignature = NOTIFIER_BAD_SIG;
  129. }
  130. VOID STORE_CHANGE_NOTIFIER::ReleaseRegisteredStores()
  131. /*++
  132. Routine Description:
  133. Releases all the events that have been registered for store change notifications
  134. and closes the stores that were being watched.
  135. Arguments:
  136. None
  137. Returns:
  138. Nothing
  139. --*/
  140. {
  141. LIST_ENTRY * pEntry;
  142. DBG_ASSERT( CheckSignature() );
  143. STORE_CHANGE_NOTIFIER::Lock();
  144. for ( pEntry = m_StoreList.Flink;
  145. pEntry != &m_StoreList;
  146. pEntry = pEntry->Flink )
  147. {
  148. STORE_CHANGE_ENTRY *pStoreEntry = CONTAINING_RECORD( pEntry,
  149. STORE_CHANGE_ENTRY,
  150. m_StoreListEntry );
  151. StartCleanup( pStoreEntry );
  152. }
  153. STORE_CHANGE_NOTIFIER::Unlock();
  154. }
  155. VOID STORE_CHANGE_NOTIFIER::StartCleanup( IN STORE_CHANGE_ENTRY *pEntry )
  156. /*++
  157. Routine Description:
  158. Start the cleanup for a STORE_CHANGE_ENTRY object.
  159. This function MUST be called between calls to STORE_CHANGE_NOTIFIER::Lock()/Unlock() !
  160. Arguments:
  161. pEntry - STORE_CHANGE_ENTRY to clean up.
  162. Returns:
  163. Nothing
  164. --*/
  165. {
  166. //
  167. // Grab the lock, mark the entry as being ready for deletion,signal the event associated
  168. // with it and release a reference to the entry . We hold the lock, so we have exclusive
  169. // access to the entries. Possible execution paths :
  170. //
  171. // #1. No callback comes in while we're executing this code. We set the deletion bit and
  172. // signal the event. If the callback now fires, it smacks into
  173. // the lock and waits its turn. If it doesn't fire, that's OK too. We unlock and go on our
  174. // merry way. At some point in time, the callback -will- fire, because we didn't call
  175. // UnregisterWait(). Then, in the callback, we see the "delete me" flag and delete the
  176. // entry. Also, the wait is cancelled because we acquired the wait handle with the
  177. // WT_EXECUTEDELETEWAIT flag, which removes the wait immediately after calling the callback.
  178. //
  179. // #2. A callback comes in while we're executing this code. It waits to acquire the lock,
  180. // and by the time it acquires it we've set the deletion bit and signalled the event.
  181. // The callback sees the deletion bit and deletes the entry. The wait is cancelled, and
  182. // so it doesn't matter that we signalled the event.
  183. //
  184. pEntry->MarkForDelete();
  185. SetEvent( pEntry->QueryStoreEvent() );
  186. }
  187. BOOL STORE_CHANGE_NOTIFIER::IsStoreRegisteredForChange( IN LPTSTR pszStoreName,
  188. IN NOTIFFNCPTR pFncPtr,
  189. IN LPVOID pvParam )
  190. /*++
  191. Routine Description:
  192. Check whether the store described by the parameters already has an event registered
  193. for change notifications.
  194. Arguments:
  195. pszStoreName - name of cert store
  196. pFncPtr - pointer to notification function. If pFncPtr == INVALID_FNC_PTR, checks for any
  197. notification functions
  198. pvParam - arg to notification function; if pFncPtr == INVALID_FNC_PTR, is ignored
  199. Returns:
  200. TRUE if store is registered, FALSE if not.
  201. --*/
  202. {
  203. DBG_ASSERT( CheckSignature() );
  204. DBG_ASSERT( pszStoreName );
  205. DBG_ASSERT( pFncPtr );
  206. PSTORE_CHANGE_ENTRY pEntry = InternalIsStoreRegisteredForChange( pszStoreName,
  207. pFncPtr,
  208. pvParam ) ;
  209. return ( pEntry ? TRUE : FALSE );
  210. }
  211. PSTORE_CHANGE_ENTRY
  212. STORE_CHANGE_NOTIFIER::InternalIsStoreRegisteredForChange( IN LPTSTR pszStoreName,
  213. IN NOTIFFNCPTR pFncPtr,
  214. IN LPVOID pvParam )
  215. /*++
  216. Routine Description:
  217. Check whether the store described by the parameters already has an event registered
  218. for change notifications.
  219. Arguments:
  220. pszStoreName - name of cert store
  221. pFncPtr - pointer to notification function. If pFncPtr == INVALID_FNC_PTR, checks for any
  222. notification functions
  223. pvParam - arg for notification function. Ignored if pFncPtr == INVALID_FNC_PTR
  224. Returns:
  225. pointer to STORE_ENTRY structure that contains the registration info, NULL if non-existent.
  226. --*/
  227. {
  228. DBG_ASSERT( CheckSignature() );
  229. DBG_ASSERT( pszStoreName );
  230. DBG_ASSERT( pFncPtr );
  231. STORE_CHANGE_ENTRY *pStoreEntry = NULL, *pMatchingEntry = NULL;
  232. LIST_ENTRY *pEntry;
  233. BOOL fFound = FALSE;
  234. STORE_CHANGE_NOTIFIER::Lock();
  235. for ( pEntry = m_StoreList.Flink;
  236. pEntry != &m_StoreList;
  237. pEntry = pEntry->Flink )
  238. {
  239. pStoreEntry = CONTAINING_RECORD( pEntry, STORE_CHANGE_ENTRY, m_StoreListEntry );
  240. if ( pStoreEntry->Matches( pszStoreName,
  241. pFncPtr,
  242. pvParam ) )
  243. {
  244. pMatchingEntry = pStoreEntry;
  245. break;
  246. }
  247. }
  248. STORE_CHANGE_NOTIFIER::Unlock();
  249. return pMatchingEntry;
  250. }
  251. BOOL STORE_CHANGE_NOTIFIER::RegisterStoreForChange( IN LPTSTR pszStoreName,
  252. IN HCERTSTORE hStore,
  253. IN NOTIFFNCPTR pFncPtr,
  254. IN LPVOID pvParam )
  255. /*++
  256. Routine Description:
  257. Register the store for change notifications
  258. Critical Sections acquired : m_pStoreListCS, m_pStoreArrayCS
  259. Arguments:
  260. pszStoreName - name of cert store
  261. hCertStore - handle to cert store
  262. pFncPtr - pointer to notification function
  263. pvParam - arg to notification function
  264. Returns:
  265. TRUE if store was registered, FALSE if not
  266. --*/
  267. {
  268. DBG_ASSERT( CheckSignature() );
  269. DBG_ASSERT( pszStoreName );
  270. DBG_ASSERT( pFncPtr );
  271. BOOL fAlreadyRegistered = FALSE;
  272. BOOL fSuccess = FALSE;
  273. PSTORE_CHANGE_ENTRY pStoreEntry = NULL;
  274. STORE_CHANGE_NOTIFIER::Lock();
  275. //
  276. // Check whether there already some notifications on this store
  277. //
  278. pStoreEntry = InternalIsStoreRegisteredForChange( pszStoreName,
  279. (NOTIFFNCPTR) INVALID_FNC_PTR,
  280. NULL );
  281. fAlreadyRegistered = (pStoreEntry == NULL ? FALSE : TRUE );
  282. //
  283. // If this is a totally new store, need to allocate and fill in new store watch entry
  284. //
  285. if ( !pStoreEntry )
  286. {
  287. HCERTSTORE hCertStore = NULL;
  288. HANDLE hStoreEvent = NULL;
  289. HANDLE hWaitHandle = NULL;
  290. pStoreEntry = new STORE_CHANGE_ENTRY( this,
  291. pszStoreName,
  292. hStore,
  293. pFncPtr,
  294. pvParam) ;
  295. if ( !pStoreEntry || pStoreEntry->GetLastError() )
  296. {
  297. if ( pStoreEntry )
  298. {
  299. SetLastError( pStoreEntry->GetLastError() );
  300. }
  301. goto EndRegisterStore;
  302. }
  303. //
  304. // Add the entire entry to the list of stores to be watched
  305. //
  306. InsertTailList( &m_StoreList, &pStoreEntry->m_StoreListEntry );
  307. }
  308. //
  309. // Else, possibly update the notification functions to be called - only update
  310. // if there isn't already a copy of this function with the same parameters
  311. //
  312. else
  313. {
  314. DBG_ASSERT( pStoreEntry->QueryStoreHandle() &&
  315. pStoreEntry->QueryStoreEvent() &&
  316. pStoreEntry->QueryNotifier() &&
  317. pStoreEntry->QueryWaitHandle() );
  318. if ( !pStoreEntry->ContainsNotifFnc( pFncPtr,
  319. pvParam ) )
  320. {
  321. if ( !pStoreEntry->AddNotifFnc( pFncPtr,
  322. pvParam ) )
  323. {
  324. SetLastError( pStoreEntry->GetLastError() );
  325. goto EndRegisterStore;
  326. }
  327. }
  328. }
  329. fSuccess = TRUE;
  330. EndRegisterStore:
  331. if ( !fSuccess )
  332. {
  333. //
  334. // If we failed to register the store and we allocated a new STORE_CHANGE_ENTRY
  335. // object, clean it up. Note that ref count is only set if everything succeeds
  336. //
  337. if ( !fAlreadyRegistered && pStoreEntry )
  338. {
  339. delete pStoreEntry;
  340. }
  341. DBGPRINTF((DBG_CONTEXT,"Failed to register store %s : 0x%x\n",
  342. pszStoreName, GetLastError()));
  343. }
  344. STORE_CHANGE_NOTIFIER::Unlock();
  345. return fSuccess;
  346. }
  347. VOID STORE_CHANGE_NOTIFIER::UnregisterStore( IN LPTSTR pszStoreName,
  348. IN NOTIFFNCPTR pNotifFnc,
  349. IN LPVOID pvParam )
  350. /*++
  351. Routine Description:
  352. Unregister a notification function for a store
  353. Arguments:
  354. pszStoreName - name of store
  355. pNotifFnc - notification function to deregister. If pNotifFnc == INVALID_FNC_PTR,
  356. all notifications for that store are removed
  357. pvParam - arg to notification function. Ignored if pNotifFnc == INVALID_FNC_PTR
  358. --*/
  359. {
  360. DBG_ASSERT( CheckSignature() );
  361. DBG_ASSERT( pszStoreName );
  362. DBG_ASSERT( pNotifFnc );
  363. STORE_CHANGE_ENTRY *pStoreEntry;
  364. LIST_ENTRY *pEntry;
  365. BOOL fRemoveAll = (pNotifFnc == (NOTIFFNCPTR) INVALID_FNC_PTR ? TRUE : FALSE );
  366. STORE_CHANGE_NOTIFIER::Lock();
  367. //
  368. // Iterate through the active list to find it
  369. //
  370. for ( pEntry = m_StoreList.Flink;
  371. pEntry != &m_StoreList;
  372. pEntry = pEntry->Flink )
  373. {
  374. pStoreEntry = CONTAINING_RECORD( pEntry, STORE_CHANGE_ENTRY, m_StoreListEntry );
  375. if ( pStoreEntry->Matches( pszStoreName,
  376. pNotifFnc,
  377. pvParam ) )
  378. {
  379. //
  380. // If we're removing all notifications for this store, or there will be no
  381. // notification functions left for this store, clean everything up
  382. //
  383. if ( fRemoveAll || ( pStoreEntry->RemoveNotifFnc( pNotifFnc,
  384. pvParam ) &&
  385. !pStoreEntry->HasNotifFncs() ) )
  386. {
  387. StartCleanup( pStoreEntry );
  388. }
  389. break;
  390. }
  391. }
  392. STORE_CHANGE_NOTIFIER::Unlock();
  393. }
  394. #if DBG
  395. VOID STORE_CHANGE_NOTIFIER::DumpRegisteredStores()
  396. /*++
  397. Routine Description:
  398. Dumps all the stores currently being watched
  399. Arguments:
  400. None
  401. Returns:
  402. Nothing
  403. --*/
  404. {
  405. STORE_CHANGE_ENTRY *pStoreEntry = NULL;
  406. LIST_ENTRY *pEntry1 = NULL, *pEntry2 = NULL;
  407. DBG_ASSERT( CheckSignature() );
  408. STORE_CHANGE_NOTIFIER::Lock();
  409. DBGPRINTF((DBG_CONTEXT,
  410. "------------------------------------------------------------------------\nRegistered Stores : \n"));
  411. for ( pEntry1 = m_StoreList.Flink;
  412. pEntry1 != &m_StoreList;
  413. pEntry1 = pEntry1->Flink )
  414. {
  415. pStoreEntry = CONTAINING_RECORD( pEntry1,
  416. STORE_CHANGE_ENTRY,
  417. m_StoreListEntry );
  418. DBGPRINTF((DBG_CONTEXT,
  419. "Store %s, store handle 0x%x, event handle 0x%x, wait handle 0x%x has the ff functions registered : \n",
  420. pStoreEntry->QueryStoreName(),
  421. pStoreEntry->QueryStoreHandle(),
  422. pStoreEntry->QueryStoreEvent(),
  423. pStoreEntry->QueryWaitHandle() ));
  424. LIST_ENTRY *pFncs = pStoreEntry->QueryNotifFncChain();
  425. for ( pEntry2 = pFncs->Flink;
  426. pEntry2 != pFncs;
  427. pEntry2 = pEntry2->Flink )
  428. {
  429. PNOTIF_FNC_CHAIN_ENTRY pNFE = CONTAINING_RECORD( pEntry2,
  430. NOTIF_FNC_CHAIN_ENTRY,
  431. ListEntry );
  432. DBGPRINTF((DBG_CONTEXT,"Function 0x%x, parameter 0x%x\n",
  433. pNFE->pNotifFnc,
  434. pNFE->pvParam ));
  435. }
  436. }
  437. DBGPRINTF((DBG_CONTEXT,
  438. "------------------------------------------------------------------------\n"));
  439. STORE_CHANGE_NOTIFIER::Unlock();
  440. }
  441. #endif //DBG
  442. VOID NTAPI STORE_CHANGE_NOTIFIER::NotifFncCaller( IN PVOID pvCallbackArg,
  443. IN BOOLEAN fUnused )
  444. /*++
  445. Routine Description:
  446. This is the function called when one of the events we registered for is signalled.
  447. The context passed in can be in one of three states :
  448. 1. Valid : has an associated chain of notification functions that are to be called.
  449. 2. Invalid : don't do any processing, because we're not interested in that store anymore
  450. ie UnregisterStore() has been called on it
  451. 3. To Be Deleted : the context is to be deleted, because we're shutting down
  452. Arguments:
  453. pvCallbackArg - context pointer
  454. fUnused - boolean, not used. [part of WAITFORTIMERCALLBACKFUNC prototype, which this function
  455. has to conform to]
  456. --*/
  457. {
  458. LIST_ENTRY * pNotifFncChain = NULL;
  459. PSTORE_CHANGE_ENTRY pEntry = NULL;
  460. if ( !pvCallbackArg )
  461. {
  462. DBG_ASSERT( FALSE ); //make sure we barf in debug build
  463. return;
  464. }
  465. pEntry = (PSTORE_CHANGE_ENTRY) pvCallbackArg;
  466. //
  467. // Make sure we have exclusive access
  468. //
  469. STORE_CHANGE_NOTIFIER::Lock();
  470. DBG_ASSERT( pEntry->CheckSignature() );
  471. //
  472. // If we signalled the event ourselves because we need to clean up this context,
  473. // just delete it - we know we won't get any more callbacks, since we acquired the
  474. // wait handle with WTEXECUTEDELETEWAIT. We don't need to remove it from a list
  475. // because it has already been removed prior to this callback being generated.
  476. // [in the destructor for STORE_CHANGE_NOTIFIER]
  477. //
  478. if ( !pEntry->IsMarkedForDelete() && !pEntry->IsInvalid() )
  479. {
  480. //
  481. // Make a copy of the list of notification functions to call, so that even if
  482. // one of the functions called changes the list, we still have a kosher copy to
  483. // work with.
  484. //
  485. // Note that another assumption is that if functions A and B are both in the list
  486. // being walked, and A is called before B, it doesn't destroy anything that B uses.
  487. //
  488. pNotifFncChain = CopyNotifFncChain( pEntry->QueryNotifFncChain() );
  489. //
  490. // Remove this entry from the active list and delete it: it'll never get notified again
  491. // because we acquired it with the WTEXECUTEDELETEWAIT flag
  492. //
  493. }
  494. RemoveEntryList( &(pEntry->m_StoreListEntry) );
  495. delete pEntry;
  496. STORE_CHANGE_NOTIFIER::Unlock();
  497. // Call the notification functions now, after releasing the lock. This
  498. // prevents deadlock with SSPIFILT. In particular the scenario where:
  499. //
  500. // SSPIFILT__AddFullyQualifiedItem acquires SSPI then StoreChange
  501. // NotifyFncCaller acquires StoreChange then SSPI
  502. //
  503. // should be avoided
  504. if ( pNotifFncChain )
  505. {
  506. NOTIF_FNC_CHAIN_ENTRY * pChainEntry = NULL;
  507. LIST_ENTRY * pListEntry = NULL;
  508. //
  509. // Walk the list, call the functions and be generally studly ...
  510. //
  511. for ( pListEntry = pNotifFncChain->Flink;
  512. pListEntry != pNotifFncChain;
  513. pListEntry = pListEntry->Flink )
  514. {
  515. pChainEntry = CONTAINING_RECORD( pListEntry, NOTIF_FNC_CHAIN_ENTRY,
  516. ListEntry );
  517. if ( pChainEntry->pNotifFnc )
  518. {
  519. #ifdef NOTIFICATION_DBG
  520. DBGPRINTF((DBG_CONTEXT,
  521. "Calling notification fnc %p, arg %p\n",
  522. pChainEntry->pNotifFnc, pChainEntry->pvParam));
  523. #endif
  524. pChainEntry->pNotifFnc( pChainEntry->pvParam );
  525. }
  526. }
  527. DeallocateNotifFncChain( pNotifFncChain );
  528. }
  529. return;
  530. }
  531. STORE_CHANGE_ENTRY::STORE_CHANGE_ENTRY( STORE_CHANGE_NOTIFIER *pNotifier,
  532. LPSTR pszStoreName,
  533. HCERTSTORE hStore,
  534. NOTIFFNCPTR pFncPtr,
  535. PVOID pvParam ) :
  536. m_dwSignature( STORE_ENTRY_GOOD_SIG ),
  537. m_pNotifier( pNotifier ),
  538. m_dwRefCount( -1 ),
  539. m_dwError( 0 ),
  540. m_hCertStore( NULL ),
  541. m_hStoreEvent( NULL ),
  542. m_hWaitHandle( NULL ),
  543. m_fDeleteMe( FALSE ),
  544. m_fInvalid( FALSE ),
  545. m_strStoreName( pszStoreName )
  546. /*++
  547. Routine Description:
  548. Constructor
  549. Arguments:
  550. pNotifier - parent notifier object
  551. pszStoreName - name of store to be watched
  552. hStore - handle to store to be watched
  553. pFncPtr - notification function to call when store changes
  554. pvParam - arg to notification function
  555. Returns:
  556. Nothing
  557. --*/
  558. {
  559. PNOTIF_FNC_CHAIN_ENTRY pNewNotifFnEntry = NULL;
  560. INITIALIZE_CRITICAL_SECTION( &m_CS );
  561. InitializeListHead( &m_NotifFncChain );
  562. //
  563. // Duplicate store handle to watch
  564. //
  565. m_hCertStore = CertDuplicateStore( hStore );
  566. //
  567. // Create the event to be signalled when store changes
  568. //
  569. if ( !(m_hStoreEvent = CreateEvent( NULL, //default attributes,
  570. TRUE,
  571. FALSE, //initally non-signalled
  572. NULL ) ) ) //no name
  573. {
  574. m_dwError = GetLastError();
  575. return;
  576. }
  577. //
  578. // Register with wait thread pool
  579. //
  580. #if 1
  581. if ( !NT_SUCCESS( RtlRegisterWait( &m_hWaitHandle,
  582. m_hStoreEvent,
  583. STORE_CHANGE_NOTIFIER::NotifFncCaller,
  584. (PVOID) this,
  585. INFINITE,
  586. WT_EXECUTEONLYONCE ) ) )
  587. #else
  588. if ( !(m_hWaitHandle = RegisterWaitForSingleObjectEx( m_hStoreEvent,
  589. STORE_CHANGE_NOTIFIER::NotifFncCaller,
  590. (PVOID) this,
  591. INFINITE,
  592. WT_EXECUTEONLYONCE ) ) )
  593. #endif
  594. {
  595. m_dwError = GetLastError();
  596. goto cleanup;
  597. }
  598. //
  599. // Register for change events on the store
  600. //
  601. if ( !CertControlStore( m_hCertStore,
  602. 0,
  603. CERT_STORE_CTRL_NOTIFY_CHANGE,
  604. (LPVOID) &m_hStoreEvent) )
  605. {
  606. m_dwError = GetLastError();
  607. goto cleanup;
  608. }
  609. //
  610. // Create a new chain of notification functions
  611. //
  612. pNewNotifFnEntry = new NOTIF_FNC_CHAIN_ENTRY;
  613. if ( !pNewNotifFnEntry )
  614. {
  615. DBGPRINTF((DBG_CONTEXT,
  616. "Couldn't allocate new notification function chain : 0x%x\n",
  617. GetLastError()));
  618. m_dwError = ERROR_OUTOFMEMORY;
  619. goto cleanup;
  620. }
  621. pNewNotifFnEntry->pNotifFnc = pFncPtr;
  622. pNewNotifFnEntry->pvParam = pvParam;
  623. //
  624. // Add the function to the chain
  625. //
  626. InsertTailList( &m_NotifFncChain, &pNewNotifFnEntry->ListEntry );
  627. //
  628. // Increment number of entry objects, to help in cleanup later
  629. //
  630. STORE_CHANGE_ENTRY::IncrementStoreEntryCount();
  631. cleanup:
  632. //
  633. // Cleanup that's only done on error
  634. //
  635. if ( m_dwError != 0 )
  636. {
  637. if ( m_hWaitHandle )
  638. {
  639. RtlDeregisterWait( m_hWaitHandle );
  640. m_hWaitHandle = NULL;
  641. }
  642. if ( m_hStoreEvent )
  643. {
  644. CloseHandle( m_hStoreEvent );
  645. m_hStoreEvent = NULL;
  646. }
  647. if ( m_hCertStore )
  648. {
  649. CertCloseStore( m_hCertStore,
  650. 0 );
  651. m_hCertStore = NULL;
  652. }
  653. //
  654. // Go through the chain of notification functions and clean it up
  655. //
  656. while ( !IsListEmpty(&(m_NotifFncChain)) )
  657. {
  658. NOTIF_FNC_CHAIN_ENTRY *pChainEntry = CONTAINING_RECORD( m_NotifFncChain.Flink,
  659. NOTIF_FNC_CHAIN_ENTRY,
  660. ListEntry );
  661. RemoveEntryList( &(pChainEntry->ListEntry) );
  662. delete pChainEntry;
  663. }
  664. }
  665. }
  666. STORE_CHANGE_ENTRY::~STORE_CHANGE_ENTRY()
  667. /*++
  668. Routine Description:
  669. Destructor
  670. Arguments:
  671. None
  672. Return value:
  673. None
  674. --*/
  675. {
  676. DBG_ASSERT( CheckSignature() );
  677. //
  678. // No need to call RtlDeregisterWait() for the handle, since it's already been
  679. // deregistered [after having been used in the callback]
  680. //
  681. //
  682. // Clean up store change event
  683. //
  684. if ( m_hStoreEvent )
  685. {
  686. CloseHandle( m_hStoreEvent );
  687. m_hStoreEvent = NULL;
  688. }
  689. //
  690. // Close cert store
  691. //
  692. if ( m_hCertStore )
  693. {
  694. CertCloseStore( m_hCertStore,
  695. 0 );
  696. m_hCertStore = NULL;
  697. }
  698. //
  699. // Go through the chain of notification functions and clean it up
  700. //
  701. while ( !IsListEmpty(&(m_NotifFncChain)) )
  702. {
  703. NOTIF_FNC_CHAIN_ENTRY *pChainEntry = CONTAINING_RECORD( m_NotifFncChain.Flink,
  704. NOTIF_FNC_CHAIN_ENTRY,
  705. ListEntry );
  706. RemoveEntryList( &(pChainEntry->ListEntry) );
  707. delete pChainEntry;
  708. }
  709. DeleteCriticalSection( &m_CS );
  710. //
  711. // Another one bites the dust ...
  712. //
  713. STORE_CHANGE_ENTRY::DecrementStoreEntryCount();
  714. m_dwSignature = STORE_ENTRY_BAD_SIG;
  715. }
  716. BOOL STORE_CHANGE_ENTRY::ContainsNotifFnc( IN NOTIFFNCPTR pFncPtr,
  717. IN LPVOID pvParam )
  718. /*++
  719. Routine Description:
  720. Checks whether the given store watch entry contains the specified notification function with
  721. the specified args
  722. Arguments:
  723. pFncPtr - pointer to notification function
  724. pvParam - arg to notification function
  725. Returns:
  726. True if function is found, false otherwise
  727. --*/
  728. {
  729. NOTIF_FNC_CHAIN_ENTRY *pChainEntry;
  730. LIST_ENTRY *pEntry = NULL;
  731. BOOL fFound = FALSE;
  732. Lock();
  733. for ( pEntry = m_NotifFncChain.Flink;
  734. pEntry != &m_NotifFncChain;
  735. pEntry = pEntry->Flink )
  736. {
  737. pChainEntry = CONTAINING_RECORD( pEntry, NOTIF_FNC_CHAIN_ENTRY, ListEntry );
  738. if ( pChainEntry->pNotifFnc == pFncPtr && pChainEntry->pvParam == pvParam )
  739. {
  740. fFound = TRUE;
  741. break;
  742. }
  743. }
  744. Unlock();
  745. return fFound;
  746. }
  747. BOOL STORE_CHANGE_ENTRY::AddNotifFnc( IN NOTIFFNCPTR pFncPtr,
  748. IN LPVOID pvParam )
  749. /*++
  750. Routine Description:
  751. Adds a notification function to a store entry
  752. Arguments:
  753. pFncPtr - pointer to notification function
  754. pvParam - arg to notification function
  755. Returns:
  756. TRUE if function is added, FALSE otherwise
  757. --*/
  758. {
  759. PNOTIF_FNC_CHAIN_ENTRY pNewFnc = new NOTIF_FNC_CHAIN_ENTRY;
  760. if ( !pNewFnc )
  761. {
  762. DBGPRINTF((DBG_CONTEXT,
  763. "Failed to get new notif fnc entry : 0x%x\n",
  764. GetLastError()));
  765. m_dwError = ERROR_OUTOFMEMORY;
  766. return FALSE;
  767. }
  768. pNewFnc->pNotifFnc = pFncPtr;
  769. pNewFnc->pvParam = pvParam;
  770. Lock();
  771. InsertTailList( &m_NotifFncChain, &pNewFnc->ListEntry );
  772. Unlock();
  773. return TRUE;
  774. }
  775. BOOL STORE_CHANGE_ENTRY::RemoveNotifFnc( IN NOTIFFNCPTR pFncPtr,
  776. IN LPVOID pvParam )
  777. /*++
  778. Routine Description:
  779. Removes a notification function from a store entry
  780. Arguments:
  781. pFncPtr - pointer to notification function
  782. pvParam - arg to notification function
  783. Returns:
  784. Noting
  785. --*/
  786. {
  787. NOTIF_FNC_CHAIN_ENTRY *pChainEntry;
  788. LIST_ENTRY *pEntry = NULL;
  789. Lock();
  790. for ( pEntry = m_NotifFncChain.Flink;
  791. pEntry != &m_NotifFncChain;
  792. pEntry = pEntry->Flink )
  793. {
  794. pChainEntry = CONTAINING_RECORD( pEntry, NOTIF_FNC_CHAIN_ENTRY, ListEntry );
  795. if ( pChainEntry->pNotifFnc == pFncPtr && pChainEntry->pvParam == pvParam )
  796. {
  797. RemoveEntryList( pEntry );
  798. break;
  799. }
  800. }
  801. Unlock();
  802. return TRUE;
  803. }
  804. BOOL STORE_CHANGE_ENTRY::Matches( IN LPSTR pszStoreName,
  805. IN NOTIFFNCPTR pFncPtr,
  806. IN PVOID pvParam )
  807. /*++
  808. Routine Description:
  809. Checks whether a given store change object matches the given store/function combination
  810. Arguments:
  811. pszStoreName - name of cert store
  812. pFncPtr - pointer to notification function; may be INVALID_FNC_PTR if any function will match
  813. pvParam - parameter for function pointed to by pFncPtr
  814. Return Value:
  815. TRUE if it matches, FALSE if not
  816. --*/
  817. {
  818. BOOL fFound = FALSE;
  819. Lock();
  820. if ( ( ( m_strStoreName.IsEmpty() && pszStoreName == NULL) ||
  821. !strcmp(m_strStoreName.QueryStr(), pszStoreName) ) &&
  822. ( pFncPtr == (NOTIFFNCPTR) INVALID_FNC_PTR ||
  823. ContainsNotifFnc( pFncPtr,
  824. pvParam ) ) )
  825. {
  826. fFound = TRUE;
  827. }
  828. Unlock();
  829. return fFound;
  830. }
  831. LIST_ENTRY* CopyNotifFncChain( LIST_ENTRY *pNotifFncChain )
  832. /*++
  833. Routine Description:
  834. Function that copies a chain of notification functions
  835. Arguments:
  836. pNotifFncChain - pointer to chain to be copied
  837. Return Value:
  838. Pointer to copied chain, NULL on failure
  839. --*/
  840. {
  841. LIST_ENTRY *pNewChain = new LIST_ENTRY;
  842. if ( !pNewChain )
  843. {
  844. return NULL;
  845. }
  846. InitializeListHead( pNewChain );
  847. NOTIF_FNC_CHAIN_ENTRY *pChainEntry, *pNewChainEntry;
  848. LIST_ENTRY *pListEntry;
  849. for ( pListEntry = pNotifFncChain->Flink;
  850. pListEntry != pNotifFncChain;
  851. pListEntry = pListEntry->Flink )
  852. {
  853. pChainEntry = CONTAINING_RECORD( pListEntry, NOTIF_FNC_CHAIN_ENTRY,
  854. ListEntry );
  855. pNewChainEntry = new NOTIF_FNC_CHAIN_ENTRY;
  856. if ( !pNewChainEntry )
  857. {
  858. DeallocateNotifFncChain( pNewChain );
  859. return NULL;
  860. }
  861. pNewChainEntry->pNotifFnc = pChainEntry->pNotifFnc;
  862. pNewChainEntry->pvParam = pChainEntry->pvParam;
  863. InsertTailList( pNewChain, &(pNewChainEntry->ListEntry) );
  864. }
  865. return ( pNewChain );
  866. }
  867. VOID DeallocateNotifFncChain( LIST_ENTRY *pChain )
  868. /*++
  869. Routine Description:
  870. Function that cleans up resources associated with a notification function chain
  871. Arguments:
  872. pChain - chain to be cleaned up
  873. Return Value:
  874. None
  875. --*/
  876. {
  877. if ( !pChain )
  878. {
  879. return;
  880. }
  881. NOTIF_FNC_CHAIN_ENTRY *pChainEntry;
  882. LIST_ENTRY *pListEntry;
  883. while ( !IsListEmpty( pChain ) )
  884. {
  885. pChainEntry = CONTAINING_RECORD( pChain->Flink,
  886. NOTIF_FNC_CHAIN_ENTRY,
  887. ListEntry );
  888. RemoveEntryList( &(pChainEntry->ListEntry) );
  889. delete pChainEntry;
  890. }
  891. delete pChain;
  892. }