Leaked source code of windows server 2003
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.

633 lines
14 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name :
  4. certstore.cxx
  5. Abstract:
  6. Wrapper of a certificate store
  7. Author:
  8. Bilal Alam (BAlam) 29-March-2000
  9. Environment:
  10. Win32 - User Mode
  11. Project:
  12. Stream Filter Worker Process
  13. --*/
  14. #include "precomp.hxx"
  15. LIST_ENTRY CERT_STORE::sm_ToBeDeletedListHead;
  16. CRITICAL_SECTION CERT_STORE::sm_csToBeDeletedList;
  17. BOOL CERT_STORE::sm_fInitcsToBeDeletedList;
  18. CERT_STORE_HASH * CERT_STORE::sm_pCertStoreHash;
  19. HANDLE CERT_STORE::sm_hDeletionThread = NULL;
  20. BOOL CERT_STORE::sm_fDeletionThreadShutdown = FALSE;
  21. HANDLE CERT_STORE::sm_hWakeupEvent = NULL;
  22. CERT_STORE::CERT_STORE()
  23. : _cRefs( 1 ),
  24. _hStore( NULL ),
  25. _hWaitHandle( NULL ),
  26. _hStoreChangeEvent( NULL )
  27. {
  28. _dwSignature = CERT_STORE_SIGNATURE;
  29. }
  30. CERT_STORE::~CERT_STORE()
  31. {
  32. _dwSignature = CERT_STORE_SIGNATURE_FREE;
  33. if ( _hWaitHandle != NULL )
  34. {
  35. // don't continue until callback completed
  36. UnregisterWaitEx( _hWaitHandle,
  37. INVALID_HANDLE_VALUE );
  38. _hWaitHandle = NULL;
  39. }
  40. if ( _hStoreChangeEvent != NULL )
  41. {
  42. CloseHandle( _hStoreChangeEvent );
  43. _hStoreChangeEvent = NULL;
  44. }
  45. if ( _hStore != NULL )
  46. {
  47. CertCloseStore( _hStore, 0 );
  48. _hStore = NULL;
  49. }
  50. }
  51. //static
  52. VOID
  53. WINAPI
  54. CERT_STORE::CertStoreChangeRoutine(
  55. VOID * pvContext,
  56. BOOLEAN fTimedOut
  57. )
  58. /*++
  59. Routine Description:
  60. Called when a certificate store has changed
  61. Arguments:
  62. pvContext - Points to CERT_STORE which changed
  63. fTimedOut - Should always be FALSE since our wait is INFINITE
  64. Return Value:
  65. HRESULT
  66. Warning:
  67. Do not touch anything inside pCertStore (pvContext)
  68. This callback may happen when pCertStore is already being destroyed
  69. It is still safe to flush caches based on pCertStore because
  70. nothing inside pCertStore is accessed when flushing caches
  71. and in the case that the old pCertStore address got reused
  72. and placed in the cache again, in the worst case fresh item is flushed
  73. and will be reloaded again
  74. --*/
  75. {
  76. UNREFERENCED_PARAMETER( fTimedOut );
  77. CERT_STORE * pCertStore = NULL;
  78. DBG_ASSERT( pvContext != NULL );
  79. DBG_ASSERT( fTimedOut == FALSE );
  80. pCertStore = (CERT_STORE*) pvContext;
  81. //
  82. // Remove the thing from the hash table for one
  83. //
  84. sm_pCertStoreHash->DeleteRecord( pCertStore );
  85. //
  86. // Instruct the server certificate cache to flush any certs which
  87. // were referencing this cert store.
  88. // Also flush all CTLs that were referencing cert store
  89. //
  90. SERVER_CERT::FlushByStore( pCertStore );
  91. IIS_CTL::FlushByStore( pCertStore );
  92. }
  93. //static
  94. DWORD
  95. WINAPI
  96. CERT_STORE::DeletionWorkerThread(
  97. VOID * pvContext
  98. )
  99. /*++
  100. Routine Description:
  101. thread handling deletion of the CERT_STORE instances
  102. to prevent deadlocks that would happen if deletion of the
  103. CERT_STORE instance happened on the Change notification
  104. callback thread
  105. Arguments:
  106. pvContext - not used
  107. Return Value:
  108. HRESULT
  109. Warning:
  110. --*/
  111. {
  112. UNREFERENCED_PARAMETER( pvContext );
  113. for(;;)
  114. {
  115. DWORD dwWaitStatus;
  116. dwWaitStatus =
  117. WaitForSingleObject( sm_hWakeupEvent,
  118. INFINITE // time-out interval
  119. );
  120. DBG_ASSERT ( dwWaitStatus == WAIT_OBJECT_0 );
  121. if( sm_fDeletionThreadShutdown == TRUE )
  122. {
  123. return NO_ERROR;
  124. }
  125. //
  126. // handle the Wakeup event
  127. //
  128. DeleteAllPendingInstances();
  129. }
  130. }
  131. //static
  132. VOID
  133. CERT_STORE::DeleteAllPendingInstances(
  134. VOID
  135. )
  136. /*++
  137. Routine Description:
  138. delete all CERT_STORE instances that are on the ToBeDeleted list
  139. Arguments:
  140. none
  141. Return Value:
  142. HRESULT
  143. --*/
  144. {
  145. LIST_ENTRY * pCurrentEntry = NULL;
  146. for(;;)
  147. {
  148. //
  149. // Loop through each element on the list and call destructor on it
  150. //
  151. pCurrentEntry = NULL;
  152. EnterCriticalSection( &sm_csToBeDeletedList );
  153. if (! IsListEmpty( &sm_ToBeDeletedListHead ) )
  154. {
  155. pCurrentEntry = RemoveHeadList( &sm_ToBeDeletedListHead );
  156. }
  157. LeaveCriticalSection( &sm_csToBeDeletedList );
  158. if ( pCurrentEntry == NULL )
  159. {
  160. break;
  161. }
  162. CERT_STORE * pCertStore =
  163. CONTAINING_RECORD( pCurrentEntry,
  164. CERT_STORE,
  165. _ToBeDeletedListEntry );
  166. //
  167. // Note: Never call the destructor under the
  168. // critical section because that may cause deadlock
  169. // Destructor is waiting for completion callback to return and there
  170. // used to be a deadlock with sm_csToBeDeletedList, WriteLock on the
  171. // IIS_CTL hash table and the wait on the change notification callback completion
  172. //
  173. delete pCertStore;
  174. }
  175. }
  176. HRESULT
  177. CERT_STORE::Open(
  178. STRU & strStoreName
  179. )
  180. /*++
  181. Routine Description:
  182. Open specified certificate store
  183. Arguments:
  184. strStoreName - name of certificate store to open
  185. Return Value:
  186. HRESULT
  187. --*/
  188. {
  189. HRESULT hr = NO_ERROR;
  190. BOOL fRet = TRUE;
  191. DBG_ASSERT( CheckSignature() );
  192. //
  193. // Remember the name
  194. //
  195. hr = _strStoreName.Copy( strStoreName );
  196. if ( FAILED( hr ) )
  197. {
  198. return hr;
  199. }
  200. DBG_ASSERT( _hStore == NULL );
  201. //
  202. // Get the handle
  203. //
  204. _hStore = CertOpenStore( CERT_STORE_PROV_SYSTEM,
  205. 0,
  206. NULL,
  207. CERT_SYSTEM_STORE_LOCAL_MACHINE,
  208. strStoreName.QueryStr() );
  209. if ( _hStore == NULL )
  210. {
  211. return HRESULT_FROM_WIN32( GetLastError() );
  212. }
  213. //
  214. // Setup a change notification so that we are informed the cert store
  215. // has changed
  216. //
  217. _hStoreChangeEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  218. if ( _hStoreChangeEvent == NULL )
  219. {
  220. return HRESULT_FROM_WIN32( GetLastError() );
  221. }
  222. fRet = RegisterWaitForSingleObject( &_hWaitHandle,
  223. _hStoreChangeEvent,
  224. CERT_STORE::CertStoreChangeRoutine,
  225. this,
  226. INFINITE,
  227. WT_EXECUTEONLYONCE );
  228. if ( !fRet )
  229. {
  230. DBG_ASSERT( _hWaitHandle == NULL );
  231. return HRESULT_FROM_WIN32( GetLastError() );
  232. }
  233. fRet = CertControlStore( _hStore,
  234. 0,
  235. CERT_STORE_CTRL_NOTIFY_CHANGE,
  236. &_hStoreChangeEvent );
  237. if ( !fRet )
  238. {
  239. return HRESULT_FROM_WIN32( GetLastError() );
  240. }
  241. return NO_ERROR;
  242. }
  243. //static
  244. HRESULT
  245. CERT_STORE::Initialize(
  246. VOID
  247. )
  248. /*++
  249. Routine Description:
  250. Initialize CERT_STORE globals
  251. Arguments:
  252. None
  253. Return Value:
  254. HRESULT
  255. --*/
  256. {
  257. HRESULT hr = E_FAIL;
  258. InitializeListHead( &sm_ToBeDeletedListHead );
  259. BOOL fRet = InitializeCriticalSectionAndSpinCount(
  260. &sm_csToBeDeletedList,
  261. 0x80000000 /* precreate event */ |
  262. IIS_DEFAULT_CS_SPIN_COUNT );
  263. if ( !fRet )
  264. {
  265. hr = HRESULT_FROM_WIN32( GetLastError() );
  266. goto Failed;
  267. }
  268. sm_fInitcsToBeDeletedList = TRUE;
  269. //
  270. // Setup wakeup event used for communication
  271. // with the thread handling deletion of CERT_STORE instances
  272. //
  273. sm_hWakeupEvent = CreateEvent( NULL, // event attributes
  274. FALSE, // FALSE mean auto reset event
  275. FALSE, // initial state
  276. NULL // name
  277. );
  278. if ( sm_hWakeupEvent == NULL )
  279. {
  280. hr = HRESULT_FROM_WIN32( GetLastError() );
  281. goto Failed;
  282. }
  283. DBG_ASSERT( sm_pCertStoreHash == NULL );
  284. sm_pCertStoreHash = new CERT_STORE_HASH();
  285. if ( sm_pCertStoreHash == NULL )
  286. {
  287. hr = HRESULT_FROM_WIN32( GetLastError() );
  288. goto Failed;
  289. }
  290. sm_fDeletionThreadShutdown = FALSE;
  291. sm_hDeletionThread = ::CreateThread(
  292. NULL, // default security descriptor
  293. 16000, // Initial size as configured
  294. CERT_STORE::DeletionWorkerThread, // thread function
  295. NULL, // thread argument
  296. 0, // create running
  297. NULL // don't care for thread identifier
  298. );
  299. if ( sm_hDeletionThread == NULL )
  300. {
  301. hr = HRESULT_FROM_WIN32( GetLastError() );
  302. goto Failed;
  303. }
  304. return NO_ERROR;
  305. Failed:
  306. Terminate();
  307. return hr;
  308. }
  309. //static
  310. VOID
  311. CERT_STORE::Terminate(
  312. VOID
  313. )
  314. /*++
  315. Routine Description:
  316. Terminate CERT_STORE globals
  317. Cleanup is expected to be called before Terminate()
  318. if Initialize() completed with success
  319. Arguments:
  320. None
  321. Return Value:
  322. None
  323. --*/
  324. {
  325. DWORD dwWaitStatus = 0;
  326. if ( sm_hDeletionThread != NULL )
  327. {
  328. DBG_ASSERT( sm_ToBeDeletedListHead.Flink == NULL );
  329. InterlockedExchange( (LPLONG) &sm_fDeletionThreadShutdown, 1 );
  330. SetEvent( sm_hWakeupEvent );
  331. //
  332. // Issue: Jaroslad
  333. // what is SetEvent fails?
  334. //
  335. dwWaitStatus = WaitForSingleObject( sm_hDeletionThread,
  336. INFINITE );
  337. DBG_ASSERT( dwWaitStatus == WAIT_OBJECT_0 );
  338. CloseHandle( sm_hDeletionThread );
  339. sm_hDeletionThread = NULL;
  340. }
  341. if ( sm_pCertStoreHash != NULL )
  342. {
  343. delete sm_pCertStoreHash;
  344. sm_pCertStoreHash = NULL;
  345. }
  346. if ( sm_hWakeupEvent != NULL )
  347. {
  348. CloseHandle( sm_hWakeupEvent );
  349. sm_hWakeupEvent = NULL;
  350. }
  351. if ( sm_fInitcsToBeDeletedList )
  352. {
  353. DeleteCriticalSection( &sm_csToBeDeletedList );
  354. sm_fInitcsToBeDeletedList = FALSE;
  355. }
  356. }
  357. //static
  358. VOID
  359. CERT_STORE::Cleanup(
  360. VOID
  361. )
  362. /*++
  363. Routine Description:
  364. Cleanup CERT_STORE hash table
  365. This function must be called before the Terminate() call
  366. When Cleanup() is called, the overall cleanup should have reached
  367. the stage where there are no more external references
  368. to a CERT_STORE instance
  369. Arguments:
  370. None
  371. Return Value:
  372. None
  373. --*/
  374. {
  375. DWORD dwWaitStatus = 0;
  376. //
  377. // delete all the cached CERT_STORE instances
  378. //
  379. sm_pCertStoreHash->Clear();
  380. //
  381. // indicate that Deletion thread is to shut down
  382. //
  383. InterlockedExchange( (LPLONG) &sm_fDeletionThreadShutdown, 1 );
  384. SetEvent( sm_hWakeupEvent );
  385. //
  386. // Issue: Jaroslad
  387. // what is SetEvent fails?
  388. //
  389. //
  390. // Wait for Deletion thread to shut down
  391. //
  392. dwWaitStatus = WaitForSingleObject( sm_hDeletionThread,
  393. INFINITE );
  394. DBG_ASSERT( dwWaitStatus == WAIT_OBJECT_0 );
  395. CloseHandle( sm_hDeletionThread );
  396. sm_hDeletionThread = NULL;
  397. //
  398. // delete all the deletion pending instances of CERT_STORE
  399. //
  400. DeleteAllPendingInstances();
  401. }
  402. //static
  403. HRESULT
  404. CERT_STORE::OpenStore(
  405. STRU & strStoreName,
  406. CERT_STORE ** ppStore
  407. )
  408. /*++
  409. Routine Description:
  410. Open certificate store from cache
  411. Arguments:
  412. strStoreName - Store name to open
  413. ppStore - Filled with store on success
  414. Return Value:
  415. HRESULT
  416. --*/
  417. {
  418. HRESULT hr = NO_ERROR;
  419. CERT_STORE * pCertStore = NULL;
  420. LK_RETCODE lkrc;
  421. if ( ppStore == NULL )
  422. {
  423. DBG_ASSERT( FALSE );
  424. hr = HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  425. goto Finished;
  426. }
  427. *ppStore = NULL;
  428. //
  429. // Lookup in cache first
  430. //
  431. DBG_ASSERT( sm_pCertStoreHash != NULL );
  432. lkrc = sm_pCertStoreHash->FindKey( strStoreName.QueryStr(),
  433. &pCertStore );
  434. if ( lkrc != LK_SUCCESS )
  435. {
  436. //
  437. // OK. Create one and add to cache
  438. //
  439. pCertStore = new CERT_STORE();
  440. if ( pCertStore == NULL )
  441. {
  442. hr = HRESULT_FROM_WIN32( GetLastError() );
  443. goto Finished;
  444. }
  445. hr = pCertStore->Open( strStoreName );
  446. if ( FAILED( hr ) )
  447. {
  448. goto Finished;
  449. }
  450. lkrc = sm_pCertStoreHash->InsertRecord( pCertStore );
  451. //
  452. // Ignore the error. We will do the right thing if we couldn't
  453. // add to hash (i.e. no extra reference happens and callers deref
  454. // will delete the object as desired)
  455. //
  456. }
  457. DBG_ASSERT( pCertStore != NULL );
  458. *ppStore = pCertStore;
  459. return NO_ERROR;
  460. Finished:
  461. if ( pCertStore != NULL )
  462. {
  463. pCertStore->DereferenceStore();
  464. pCertStore = NULL;
  465. }
  466. return hr;
  467. }