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.

1190 lines
34 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name:
  4. cmsecache.c
  5. Abstract:
  6. This module implements the security cache.
  7. Author:
  8. Dragos C. Sambotin (dragoss) 09-Sep-1999
  9. --*/
  10. #include "cmp.h"
  11. #define SECURITY_CACHE_GROW_INCREMENTS 0x10
  12. #ifdef HIVE_SECURITY_STATS
  13. ULONG
  14. CmpCheckForSecurityDuplicates(
  15. IN OUT PCMHIVE CmHive
  16. );
  17. #endif
  18. BOOLEAN
  19. CmpFindMatchingDescriptorCell(
  20. IN PCMHIVE CmHive,
  21. IN PSECURITY_DESCRIPTOR SecurityDescriptor,
  22. IN ULONG Type,
  23. OUT PHCELL_INDEX MatchingCell,
  24. OUT OPTIONAL PCM_KEY_SECURITY_CACHE *CachedSecurityPointer
  25. );
  26. PCM_KEY_SECURITY_CACHE
  27. CmpFindReusableCellFromCache(IN PCMHIVE CmHive,
  28. IN HCELL_INDEX SecurityCell,
  29. IN ULONG PreviousCount
  30. );
  31. #ifdef ALLOC_PRAGMA
  32. #pragma alloc_text(PAGE,CmpSecConvKey)
  33. #pragma alloc_text(PAGE,CmpInitSecurityCache)
  34. #pragma alloc_text(PAGE,CmpDestroySecurityCache)
  35. #pragma alloc_text(PAGE,CmpRebuildSecurityCache)
  36. #pragma alloc_text(PAGE,CmpAddSecurityCellToCache)
  37. #pragma alloc_text(PAGE,CmpFindSecurityCellCacheIndex)
  38. #pragma alloc_text(PAGE,CmpAdjustSecurityCacheSize)
  39. #pragma alloc_text(PAGE,CmpRemoveFromSecurityCache)
  40. #pragma alloc_text(PAGE,CmpFindMatchingDescriptorCell)
  41. #pragma alloc_text(PAGE,CmpAssignSecurityToKcb)
  42. #pragma alloc_text(PAGE,CmpFindReusableCellFromCache)
  43. #ifdef HIVE_SECURITY_STATS
  44. #pragma alloc_text(PAGE,CmpCheckForSecurityDuplicates)
  45. #endif
  46. #pragma alloc_text(PAGE,CmpBuildSecurityCellMappingArray)
  47. #endif
  48. ULONG
  49. CmpSecConvKey(
  50. IN ULONG DescriptorLength,
  51. IN PULONG Descriptor
  52. )
  53. /*++
  54. Routine Description:
  55. Computes the ConvKey for the given security descriptor.
  56. The algorithm is stollen from the NTFS security hash.
  57. (it was proven to be efficient there; why shouldn't do the same ?)
  58. For speed in the hash, we consider the security descriptor as an array
  59. of ULONGs. The fragment at the end that is ignored should not affect
  60. the collision nature of this hash.
  61. Arguments:
  62. DescriptorLength - length (in bytes) of the sd
  63. Descriptor - actual sd to cache
  64. Return Value:
  65. ConvKey
  66. Note:
  67. We may want to convert this to a macro
  68. --*/
  69. {
  70. ULONG Count;
  71. ULONG Hash = 0;
  72. PAGED_CODE();
  73. Count = DescriptorLength / 4;
  74. while (Count--) {
  75. Hash = ((Hash << 3) | (Hash >> (32-3))) + *Descriptor++;
  76. }
  77. return Hash;
  78. }
  79. VOID
  80. CmpInitSecurityCache(
  81. IN OUT PCMHIVE CmHive
  82. )
  83. {
  84. ULONG i;
  85. PAGED_CODE();
  86. CmHive->SecurityCache = NULL;
  87. CmHive->SecurityCacheSize = 0;
  88. CmHive->SecurityCount = 0;
  89. CmHive->SecurityHitHint = -1; // no hint
  90. for( i=0;i<CmpSecHashTableSize;i++) {
  91. InitializeListHead(&(CmHive->SecurityHash[i]));
  92. }
  93. }
  94. NTSTATUS
  95. CmpAddSecurityCellToCache (
  96. IN OUT PCMHIVE CmHive,
  97. IN HCELL_INDEX SecurityCell,
  98. IN BOOLEAN BuildUp,
  99. IN PCM_KEY_SECURITY_CACHE SecurityCached
  100. )
  101. /*++
  102. Routine Description:
  103. This routine adds the specified security cell to the cache of the
  104. specified hive. It takes care of cache allocation (grow) as well.
  105. At build up time, cache size grows with a PAGE_SIZE, to avoid memory
  106. fragmentation. After the table is builded, it's size is adjusted (most
  107. of the hives never add new security cells). Then, at run-time, the size
  108. grows with 16 entries at a time (same reason)
  109. The cache is ordered by the cell's index, so we can do a binary search on
  110. cells retrieval.
  111. Arguments:
  112. CmHive - the hive to which the security cell belongs
  113. SecurityCell - the security cell to be added to the cache
  114. BuildUp - specifies that this is build up time
  115. SecurityCached - if not NULL it means we have it already allocated (this happens when rebuilding the cache).
  116. Return Value:
  117. NTSTATUS - STATUS_SUCCESS if the operation is successful and an
  118. appropriate error status otherwise (i.e STATUS_INSUFFICIENT_RESOURCES).
  119. Note:
  120. If the security cell is already IN the cache; this function will return TRUE.
  121. --*/
  122. {
  123. ULONG Index;
  124. PCM_KEY_SECURITY Security;
  125. PAGED_CODE();
  126. if( CmpFindSecurityCellCacheIndex (CmHive,SecurityCell,&Index) == TRUE ) {
  127. //
  128. // cell already exist in the cache; return;
  129. //
  130. return STATUS_SUCCESS;
  131. }
  132. //
  133. // if this fails, we're doomed !
  134. //
  135. ASSERT( (PAGE_SIZE % sizeof(CM_KEY_SECURITY_CACHE_ENTRY)) == 0 );
  136. //
  137. // check if the cache can accomodate a new cell
  138. //
  139. if( CmHive->SecurityCount == CmHive->SecurityCacheSize ) {
  140. //
  141. // We're at the limit with the cache; we need to extend it by a page
  142. //
  143. // OBS: this takes care of the first allocation too, as SecurityCount
  144. // and SecurityCacheSize are both initialized with 0
  145. //
  146. PCM_KEY_SECURITY_CACHE_ENTRY Temp;
  147. // store the actual buffer
  148. Temp = CmHive->SecurityCache;
  149. //
  150. // compute the new size and allocate a new buffer
  151. //
  152. if( BuildUp == TRUE ) {
  153. //
  154. // We are building up the cache; grow the table in page increments
  155. //
  156. ASSERT( ((CmHive->SecurityCacheSize * sizeof(CM_KEY_SECURITY_CACHE_ENTRY)) % PAGE_SIZE) == 0 );
  157. CmHive->SecurityCacheSize += (PAGE_SIZE / sizeof(CM_KEY_SECURITY_CACHE_ENTRY));
  158. } else {
  159. //
  160. // normal case (running time); a new security cell is added; grow the
  161. // table with a fixed number of increments (to avoid fragmentation, in
  162. // case of an Office install :-) )
  163. //
  164. CmHive->SecurityCacheSize += SECURITY_CACHE_GROW_INCREMENTS;
  165. }
  166. CmRetryExAllocatePoolWithTag(PagedPool, CmHive->SecurityCacheSize * sizeof(CM_KEY_SECURITY_CACHE_ENTRY),
  167. CM_SECCACHE_TAG|PROTECTED_POOL,CmHive->SecurityCache);
  168. if( CmHive->SecurityCache == NULL ) {
  169. //
  170. // bad luck; bail out
  171. //
  172. CmHive->SecurityCache = Temp;
  173. CmHive->SecurityCacheSize = CmHive->SecurityCount;
  174. return STATUS_INSUFFICIENT_RESOURCES;
  175. }
  176. //
  177. // copy existing data in the new location and free the old buffer
  178. //
  179. RtlCopyMemory(CmHive->SecurityCache,Temp,CmHive->SecurityCount*sizeof(CM_KEY_SECURITY_CACHE_ENTRY));
  180. if( Temp != NULL ) {
  181. ExFreePoolWithTag(Temp, CM_SECCACHE_TAG|PROTECTED_POOL );
  182. } else {
  183. ASSERT( CmHive->SecurityCount == 0 );
  184. }
  185. }
  186. //
  187. // try first to get the security cell from the hive; if this fails, there is no point to go on
  188. //
  189. Security = (PCM_KEY_SECURITY)HvGetCell(&(CmHive->Hive),SecurityCell);
  190. if( Security == NULL ){
  191. //
  192. // we failed to map the view containing this cell; bail out
  193. //
  194. return STATUS_INSUFFICIENT_RESOURCES;
  195. }
  196. if( !SecurityCached ) {
  197. ULONG Size;
  198. //
  199. // compute the size for the cached security structure
  200. //
  201. Size = FIELD_OFFSET(CM_KEY_SECURITY_CACHE,Descriptor) + Security->DescriptorLength;
  202. //
  203. // think forward: allocate and initialize a copy for the security cell, in order to store it in the cache
  204. //
  205. CmRetryExAllocatePoolWithTag(PagedPool,Size,CM_SECCACHE_TAG|PROTECTED_POOL,SecurityCached);
  206. if(SecurityCached == NULL) {
  207. //
  208. // bad luck; bail out
  209. //
  210. HvReleaseCell(&(CmHive->Hive),SecurityCell);
  211. return STATUS_INSUFFICIENT_RESOURCES;
  212. }
  213. }
  214. //
  215. // from now on, nothing can go wrong !
  216. //
  217. RtlCopyMemory(&(SecurityCached->Descriptor),&(Security->Descriptor),Security->DescriptorLength);
  218. SecurityCached->Cell = SecurityCell;
  219. SecurityCached->DescriptorLength = Security->DescriptorLength;
  220. //
  221. // now add this to the hash table
  222. //
  223. SecurityCached->ConvKey = CmpSecConvKey(Security->DescriptorLength,(PULONG)(&(Security->Descriptor)));
  224. // add it to the end of the list with this conv key
  225. InsertTailList( &(CmHive->SecurityHash[SecurityCached->ConvKey % CmpSecHashTableSize]),
  226. &(SecurityCached->List)
  227. );
  228. HvReleaseCell(&(CmHive->Hive),SecurityCell);
  229. //
  230. // At this point we are sure we have space for at least one more entry
  231. // Move data to make room for the new entry
  232. //
  233. if( Index < CmHive->SecurityCount ) {
  234. //
  235. // RtlMoveMemory will take care of the overlapping problem
  236. //
  237. RtlMoveMemory( ((PUCHAR)CmHive->SecurityCache) + (Index+1)*sizeof(CM_KEY_SECURITY_CACHE_ENTRY), // destination
  238. ((PUCHAR)CmHive->SecurityCache) + Index*sizeof(CM_KEY_SECURITY_CACHE_ENTRY), // source
  239. (CmHive->SecurityCount - Index)*sizeof(CM_KEY_SECURITY_CACHE_ENTRY) // size
  240. );
  241. }
  242. //
  243. // setup the new entry
  244. //
  245. CmHive->SecurityCache[Index].Cell = SecurityCell;
  246. CmHive->SecurityCache[Index].CachedSecurity = SecurityCached;
  247. // update the count
  248. CmHive->SecurityCount++;
  249. return STATUS_SUCCESS;
  250. }
  251. BOOLEAN
  252. CmpFindSecurityCellCacheIndex (
  253. IN PCMHIVE CmHive,
  254. IN HCELL_INDEX SecurityCell,
  255. OUT PULONG Index
  256. )
  257. /*++
  258. Routine Description:
  259. Search (binary) for the specified cellindex in the security cache.
  260. Returns the index of the cache entry where the cell is cached or
  261. it should be added
  262. Arguments:
  263. CmHive - the hive to which the security cell belongs
  264. SecurityCell - the security cell to search for
  265. Index - out param to pass the index at which the cell is (or it should be)
  266. Return Value:
  267. TRUE - the cell was found (at *Index)
  268. FALSE - the cell is not in the cache (it should be added at *Index)
  269. --*/
  270. {
  271. ULONG High;
  272. ULONG Low;
  273. ULONG Current;
  274. USHORT State = 0; // state of the operation: 0 - normal binary search
  275. // 1 - last low
  276. // 2 - last high
  277. LONG Result;
  278. LONG Tmp1,Tmp2;
  279. PAGED_CODE();
  280. if( CmHive->SecurityCount == 0 ) {
  281. //
  282. // there is no cell in the security cache
  283. //
  284. *Index = 0;
  285. return FALSE;
  286. }
  287. // sanity asserts
  288. ASSERT( CmHive->SecurityCount <= CmHive->SecurityCacheSize );
  289. ASSERT( CmHive->SecurityCache != NULL );
  290. High = CmHive->SecurityCount - 1;
  291. Low = 0;
  292. if( (CmHive->SecurityHitHint >= 0) && ( (ULONG)CmHive->SecurityHitHint <= High) ) {
  293. //
  294. // try the last search
  295. //
  296. Current = CmHive->SecurityHitHint;
  297. } else {
  298. Current = High/2;
  299. }
  300. // sign adjustment
  301. Tmp1 = SecurityCell & ~HCELL_TYPE_MASK;
  302. if( SecurityCell & HCELL_TYPE_MASK ) {
  303. Tmp1 = -Tmp1;
  304. }
  305. while( TRUE ) {
  306. Tmp2 = CmHive->SecurityCache[Current].Cell & ~HCELL_TYPE_MASK;
  307. // sign adjustment
  308. if( CmHive->SecurityCache[Current].Cell & HCELL_TYPE_MASK ) {
  309. Tmp2 = -Tmp2;
  310. }
  311. Result = Tmp1 - Tmp2;
  312. if (Result == 0) {
  313. //
  314. // Success, return data to caller and exit
  315. //
  316. *Index = Current;
  317. //
  318. // we have a hit! update the count and exit
  319. //
  320. CmHive->SecurityHitHint = Current;
  321. return TRUE;
  322. }
  323. //
  324. // compute the next index to try
  325. //
  326. switch(State) {
  327. case 0:
  328. //
  329. // normal binary search state
  330. //
  331. if( Result < 0 ) {
  332. High = Current;
  333. } else {
  334. Low = Current;
  335. }
  336. if ((High - Low) <= 1) {
  337. //
  338. // advance to the new state
  339. //
  340. Current = Low;
  341. State = 1;
  342. } else {
  343. Current = Low + ( (High-Low) / 2 );
  344. }
  345. break;
  346. case 1:
  347. //
  348. // last low state
  349. //
  350. // this should be true
  351. ASSERT( Current == Low );
  352. if (Result < 0) {
  353. //
  354. // does not exist, under
  355. //
  356. *Index = Current;
  357. return FALSE;
  358. } else if( Low == High ) {
  359. //
  360. // low and high are identical; but current is bigger than them; insert after
  361. //
  362. *Index = Current + 1;
  363. return FALSE;
  364. } else {
  365. //
  366. // advance to the new state; i.e. look at high
  367. //
  368. State = 2;
  369. Current = High;
  370. }
  371. break;
  372. case 2:
  373. //
  374. // last high state; if we got here, High = Low +1 and Current == High
  375. //
  376. ASSERT( Current == High);
  377. ASSERT( High == (Low + 1) );
  378. if( Result < 0 ) {
  379. //
  380. // under High, but above Low; we should insert it here
  381. //
  382. *Index = Current;
  383. return FALSE;
  384. } else {
  385. //
  386. // above High;
  387. //
  388. *Index = Current + 1;
  389. return FALSE;
  390. }
  391. break;
  392. default:
  393. ASSERT( FALSE );
  394. break;
  395. }
  396. }
  397. //
  398. // we shouldn't get here !!!
  399. //
  400. ASSERT( FALSE );
  401. return FALSE;
  402. }
  403. BOOLEAN
  404. CmpAdjustSecurityCacheSize (
  405. IN PCMHIVE CmHive
  406. )
  407. /*++
  408. Routine Description:
  409. Adjust the scusrity cache size for the specified hive. This function
  410. should be called after all the security cells for the hive were cached,
  411. in order to give back extra memory used in the process.
  412. Arguments:
  413. CmHive - the hive to which the security cell belongs
  414. Return Value:
  415. TRUE - success
  416. FALSE - failure - the size remains the same
  417. --*/
  418. {
  419. PCM_KEY_SECURITY_CACHE_ENTRY Buffer;
  420. PAGED_CODE();
  421. if( CmHive->SecurityCount < CmHive->SecurityCacheSize ) {
  422. //
  423. // cache size is bigger than what we need; there is a good chance
  424. // nobody will ever add new security cells to this hive, so go on
  425. // and free the extra space
  426. //
  427. //
  428. // allocate a new buffer with the exact size we need
  429. //
  430. CmRetryExAllocatePoolWithTag(PagedPool, CmHive->SecurityCount * sizeof(CM_KEY_SECURITY_CACHE_ENTRY),
  431. CM_SECCACHE_TAG|PROTECTED_POOL,Buffer);
  432. if( Buffer == NULL ) {
  433. //
  434. // the system is low on resources; leave the cache as it is
  435. //
  436. return FALSE;
  437. }
  438. //
  439. // copy significant data inot the new buffer
  440. //
  441. RtlCopyMemory(Buffer,CmHive->SecurityCache,CmHive->SecurityCount*sizeof(CM_KEY_SECURITY_CACHE_ENTRY));
  442. //
  443. // free the old buffer and update cache members
  444. //
  445. ExFreePoolWithTag(CmHive->SecurityCache, CM_SECCACHE_TAG|PROTECTED_POOL );
  446. CmHive->SecurityCache = Buffer;
  447. CmHive->SecurityCacheSize = CmHive->SecurityCount;
  448. }
  449. return TRUE;
  450. }
  451. VOID
  452. CmpRemoveFromSecurityCache (
  453. IN OUT PCMHIVE CmHive,
  454. IN HCELL_INDEX SecurityCell
  455. )
  456. /*++
  457. Routine Description:
  458. Removes the specified security cell from the security cache.
  459. (only if present !)
  460. For performance (and memory fragmentation) reasons, it does not
  461. change (shrink) the cache size.
  462. Arguments:
  463. CmHive - the hive to which the security cell belongs
  464. SecurityCell - the security cell to be removed from the cache
  465. Return Value:
  466. <none>
  467. --*/
  468. {
  469. ULONG Index;
  470. PAGED_CODE();
  471. if( CmpFindSecurityCellCacheIndex (CmHive,SecurityCell,&Index) == FALSE ) {
  472. //
  473. // cell is not in the cache
  474. //
  475. return;
  476. }
  477. ASSERT( CmHive->SecurityCache[Index].Cell == SecurityCell );
  478. ASSERT( CmHive->SecurityCache[Index].CachedSecurity->Cell == SecurityCell );
  479. //
  480. // remove the cached structure from the hash
  481. //
  482. CmpRemoveEntryList(&(CmHive->SecurityCache[Index].CachedSecurity->List));
  483. //
  484. // free up the cached security cell;
  485. //
  486. ExFreePoolWithTag(CmHive->SecurityCache[Index].CachedSecurity, CM_SECCACHE_TAG|PROTECTED_POOL );
  487. //
  488. // move memory to reflect the new size, and update the cache count
  489. //
  490. RtlMoveMemory( ((PUCHAR)CmHive->SecurityCache) + Index*sizeof(CM_KEY_SECURITY_CACHE_ENTRY), // destination
  491. ((PUCHAR)CmHive->SecurityCache) + (Index+1)*sizeof(CM_KEY_SECURITY_CACHE_ENTRY), // source
  492. (CmHive->SecurityCount - Index - 1)*sizeof(CM_KEY_SECURITY_CACHE_ENTRY) // size
  493. );
  494. CmHive->SecurityCount--;
  495. }
  496. VOID
  497. CmpDestroySecurityCache (
  498. IN OUT PCMHIVE CmHive
  499. )
  500. /*++
  501. Routine Description:
  502. Frees up all the cached security cells and the cache itself
  503. Arguments:
  504. CmHive - the hive to which the security cell belongs
  505. Return Value:
  506. <none>
  507. --*/
  508. {
  509. ULONG i;
  510. PAGED_CODE();
  511. for( i=0;i<CmHive->SecurityCount;i++) {
  512. CmpRemoveEntryList(&(CmHive->SecurityCache[i].CachedSecurity->List));
  513. ExFreePoolWithTag(CmHive->SecurityCache[i].CachedSecurity, CM_SECCACHE_TAG|PROTECTED_POOL );
  514. }
  515. if( CmHive->SecurityCount != 0 ) {
  516. ASSERT( CmHive->SecurityCache != NULL );
  517. ExFreePoolWithTag(CmHive->SecurityCache, CM_SECCACHE_TAG|PROTECTED_POOL );
  518. }
  519. CmHive->SecurityCache = NULL;
  520. CmHive->SecurityCacheSize = CmHive->SecurityCount = 0;
  521. }
  522. PCM_KEY_SECURITY_CACHE
  523. CmpFindReusableCellFromCache(IN PCMHIVE CmHive,
  524. IN HCELL_INDEX SecurityCell,
  525. IN ULONG PreviousCount)
  526. /*++
  527. Routine Description:
  528. Attempts to find the smallest cell which can accomodate the current security cell.
  529. Then moves it at the end and return a pointer to it. Shifts the array towards the end
  530. as we are going to extend the cache
  531. Security doesn't change too often, so chances are that we will be able to reuse the cells 90%
  532. of the time.
  533. If one cannot be found, the last cell in the array is freed. A new one will be allocated inside
  534. CmpAddSecurityCellToCache
  535. Arguments:
  536. CmHive - the hive to which the security cell belongs
  537. SecurityCell - the security cell
  538. PreviousCount - the end of the array
  539. Return Value:
  540. the cached cell or NULL
  541. --*/
  542. {
  543. ULONG Size;
  544. PCM_KEY_SECURITY Security;
  545. PCM_KEY_SECURITY_CACHE SecurityCached;
  546. ULONG i;
  547. ULONG SmallestSize = 0;
  548. ULONG SmallestIndex = 0;
  549. PAGED_CODE();
  550. ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
  551. //
  552. // try first to get the security cell from the hive; if this fails, there is no point to go on
  553. //
  554. Security = (PCM_KEY_SECURITY)HvGetCell(&(CmHive->Hive),SecurityCell);
  555. if( Security == NULL ){
  556. //
  557. // we failed to map the view containing this cell; bail out
  558. //
  559. goto ErrorExit;
  560. }
  561. //
  562. // compute the size for the cached security structure
  563. //
  564. Size = Security->DescriptorLength;
  565. HvReleaseCell(&(CmHive->Hive),SecurityCell);
  566. //
  567. // Now that we know the desired size, start iterating the array to find a suitable entry.
  568. //
  569. for(i = CmHive->SecurityCount; i < PreviousCount; i++) {
  570. SecurityCached = CmHive->SecurityCache[i].CachedSecurity;
  571. if( SecurityCached->DescriptorLength == Size ) {
  572. Found:
  573. //
  574. // we have found one matching the exact size; move it at the end
  575. // shift to the end one entry as we are going to extend the cache
  576. //
  577. // this factored down translates to:
  578. //
  579. RtlMoveMemory( ((PUCHAR)CmHive->SecurityCache) + (CmHive->SecurityCount+1)*sizeof(CM_KEY_SECURITY_CACHE_ENTRY), // destination
  580. ((PUCHAR)CmHive->SecurityCache) + CmHive->SecurityCount*sizeof(CM_KEY_SECURITY_CACHE_ENTRY), // source
  581. (i - CmHive->SecurityCount)*sizeof(CM_KEY_SECURITY_CACHE_ENTRY) // size
  582. );
  583. return SecurityCached;
  584. }
  585. //
  586. // if not; record the smallest suitable entry
  587. //
  588. if( SecurityCached->DescriptorLength > Size ) {
  589. if( (SmallestSize == 0) ||
  590. (SmallestSize > SecurityCached->DescriptorLength)
  591. ) {
  592. //
  593. // first one or this one is smaller
  594. //
  595. SmallestSize = SecurityCached->DescriptorLength;
  596. SmallestIndex = i;
  597. }
  598. }
  599. }
  600. if( SmallestSize != 0 ) {
  601. SecurityCached = CmHive->SecurityCache[SmallestIndex].CachedSecurity;
  602. ASSERT( SecurityCached->DescriptorLength == SmallestSize );
  603. ASSERT( Size < SmallestSize );
  604. i = SmallestIndex;
  605. goto Found;
  606. }
  607. ErrorExit:
  608. ExFreePoolWithTag(CmHive->SecurityCache[PreviousCount - 1].CachedSecurity, CM_SECCACHE_TAG|PROTECTED_POOL );
  609. //
  610. // shift to the end one entry as we are going to extend the cache
  611. //
  612. RtlMoveMemory( ((PUCHAR)CmHive->SecurityCache) + (CmHive->SecurityCount+1)*sizeof(CM_KEY_SECURITY_CACHE_ENTRY), // destination
  613. ((PUCHAR)CmHive->SecurityCache) + CmHive->SecurityCount*sizeof(CM_KEY_SECURITY_CACHE_ENTRY), // source
  614. (PreviousCount - CmHive->SecurityCount - 1)*sizeof(CM_KEY_SECURITY_CACHE_ENTRY) // size
  615. );
  616. return NULL;
  617. }
  618. BOOLEAN
  619. CmpRebuildSecurityCache(
  620. IN OUT PCMHIVE CmHive
  621. )
  622. /*++
  623. Routine Description:
  624. Rebuilds the security cache by reiterating all security cells
  625. and adding them to the cache; this routine is intended for hive
  626. refresh operations
  627. Arguments:
  628. CmHive - the hive to which the security cell belongs
  629. Return Value:
  630. TRUE or FALSE
  631. --*/
  632. {
  633. PCM_KEY_NODE RootNode;
  634. PCM_KEY_SECURITY SecurityCell;
  635. HCELL_INDEX ListAnchor;
  636. HCELL_INDEX NextCell;
  637. HCELL_INDEX LastCell = 0;
  638. PHHIVE Hive;
  639. PRELEASE_CELL_ROUTINE ReleaseCellRoutine;
  640. BOOLEAN Result = TRUE;
  641. ULONG PreviousCount;
  642. PCM_KEY_SECURITY_CACHE SecCache;
  643. PAGED_CODE();
  644. ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
  645. //
  646. // avoid extra work
  647. //
  648. Hive = &(CmHive->Hive);
  649. ReleaseCellRoutine = Hive->ReleaseCellRoutine;
  650. Hive->ReleaseCellRoutine = NULL;
  651. //
  652. // be smart and reuse the cache; For each cell, we'll iterate through the cache
  653. // find an entry big enough to accomodate the current cell, move it at the end
  654. // and then reuse it.
  655. //
  656. // first, reinitialize the hash table.
  657. //
  658. for( PreviousCount=0;PreviousCount<CmpSecHashTableSize;PreviousCount++) {
  659. InitializeListHead(&(CmHive->SecurityHash[PreviousCount]));
  660. }
  661. //
  662. // this we use to keep track of how many valid cells were in the cache previously
  663. //
  664. PreviousCount = CmHive->SecurityCount;
  665. //
  666. // start building an pseudo-empty one.
  667. //
  668. CmHive->SecurityCount = 0;
  669. if (!HvIsCellAllocated(Hive,Hive->BaseBlock->RootCell)) {
  670. //
  671. // root cell HCELL_INDEX is bogus
  672. //
  673. Result = FALSE;
  674. goto JustReturn;
  675. }
  676. RootNode = (PCM_KEY_NODE) HvGetCell(Hive, Hive->BaseBlock->RootCell);
  677. if( RootNode == NULL ) {
  678. //
  679. // we couldn't map a view for the bin containing this cell
  680. //
  681. Result = FALSE;
  682. goto JustReturn;
  683. }
  684. ListAnchor = NextCell = RootNode->Security;
  685. do {
  686. if (!HvIsCellAllocated(Hive, NextCell)) {
  687. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_SEC,"CM: CmpRebuildSecurityCache\n"));
  688. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_SEC," NextCell: %08lx is invalid HCELL_INDEX\n",NextCell));
  689. Result = FALSE;
  690. goto JustReturn;
  691. }
  692. SecurityCell = (PCM_KEY_SECURITY) HvGetCell(Hive, NextCell);
  693. if( SecurityCell == NULL ) {
  694. //
  695. // we couldn't map a view for the bin containing this cell
  696. //
  697. Result = FALSE;
  698. goto JustReturn;
  699. }
  700. if (NextCell != ListAnchor) {
  701. //
  702. // Check to make sure that our Blink points to where we just
  703. // came from.
  704. //
  705. if (SecurityCell->Blink != LastCell) {
  706. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_SEC," Invalid Blink (%ld) on security cell %ld\n",SecurityCell->Blink, NextCell));
  707. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_SEC," should point to %ld\n", LastCell));
  708. Result = FALSE;
  709. goto JustReturn;
  710. }
  711. }
  712. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_SEC,"CmpValidSD: SD shared by %d nodes\n",SecurityCell->ReferenceCount));
  713. if (!SeValidSecurityDescriptor(SecurityCell->DescriptorLength, &SecurityCell->Descriptor)) {
  714. #if DBG
  715. CmpDumpSecurityDescriptor(&SecurityCell->Descriptor,"INVALID DESCRIPTOR");
  716. #endif
  717. Result = FALSE;
  718. goto JustReturn;
  719. }
  720. SecCache = CmpFindReusableCellFromCache(CmHive,NextCell,PreviousCount);
  721. if( !NT_SUCCESS(CmpAddSecurityCellToCache ( CmHive,NextCell,TRUE,SecCache) ) ) {
  722. Result = FALSE;
  723. goto JustReturn;
  724. }
  725. LastCell = NextCell;
  726. NextCell = SecurityCell->Flink;
  727. } while ( NextCell != ListAnchor );
  728. JustReturn:
  729. Hive->ReleaseCellRoutine = ReleaseCellRoutine;
  730. return Result;
  731. }
  732. BOOLEAN
  733. CmpFindMatchingDescriptorCell(
  734. IN PCMHIVE CmHive,
  735. IN PSECURITY_DESCRIPTOR SecurityDescriptor,
  736. IN ULONG Type,
  737. OUT PHCELL_INDEX MatchingCell,
  738. OUT OPTIONAL PCM_KEY_SECURITY_CACHE *CachedSecurityPointer
  739. )
  740. /*++
  741. Routine Description:
  742. This routine attempts to find a security descriptor in the hive that
  743. is identical to the one passed in. If it finds one, it returns its
  744. cell index.
  745. Obsolete:
  746. Currently, this routine checks the security descriptors of the parent
  747. and siblings of the node to find a match.
  748. New:
  749. It looks for the sd in the security cache for this hive. This will
  750. eliminate duplicates and make the search process faster.
  751. Arguments:
  752. CmHive - Supplies a pointer to the hive control structure for the node.
  753. Needed to get access to the cache
  754. SecurityDescriptor - Supplies the cooked security descriptor which
  755. should be searched for.
  756. Type - Indicates whether the Security Descriptor that matches must
  757. be in Stable or Volatile store
  758. MatchingCell - Returns the cell index of a security cell whose
  759. security descriptor is identical to SecurityDescriptor.
  760. Valid only if TRUE is returned.
  761. CachedSecurityPointer - pointer to the cached security (for update reasons)
  762. Return Value:
  763. TRUE - Matching security descriptor found. MatchingCell returns the
  764. cell index of the matching security descriptor.
  765. FALSE - No matching security descriptor found. MatchingCell is invalid.
  766. --*/
  767. {
  768. ULONG DescriptorLength;
  769. ULONG ConvKey;
  770. PLIST_ENTRY ListAnchor;
  771. PLIST_ENTRY Current;
  772. PCM_KEY_SECURITY_CACHE CachedSecurity;
  773. PAGED_CODE();
  774. DescriptorLength = RtlLengthSecurityDescriptor(SecurityDescriptor);
  775. //
  776. // calculate the conv key
  777. //
  778. ConvKey = CmpSecConvKey(DescriptorLength,(PULONG)SecurityDescriptor);
  779. ListAnchor = &(CmHive->SecurityHash[ConvKey % CmpSecHashTableSize]);
  780. if( IsListEmpty(ListAnchor) == TRUE ) {
  781. return FALSE;
  782. }
  783. //
  784. // iterate through the list of colisions for this convkey
  785. // start with teh first element in list
  786. //
  787. Current = (PLIST_ENTRY)(ListAnchor->Flink);
  788. while( Current != ListAnchor ){
  789. //
  790. // get the current cached security
  791. //
  792. CachedSecurity = CONTAINING_RECORD(Current,
  793. CM_KEY_SECURITY_CACHE,
  794. List);
  795. //
  796. // see if it matches with the given descriptor;
  797. //
  798. if( (CachedSecurity->ConvKey == ConvKey) && // same convkey
  799. (Type == HvGetCellType(CachedSecurity->Cell)) && // same cell type
  800. (DescriptorLength == CachedSecurity->DescriptorLength) && // same length
  801. (RtlEqualMemory(SecurityDescriptor, // and, finally, bit-wise identical
  802. &(CachedSecurity->Descriptor),
  803. DescriptorLength))
  804. ) {
  805. //
  806. // we have found a match
  807. //
  808. *MatchingCell = CachedSecurity->Cell;
  809. if (ARGUMENT_PRESENT(CachedSecurityPointer)) {
  810. *CachedSecurityPointer = CachedSecurity;
  811. }
  812. return TRUE;
  813. }
  814. //
  815. // advance to the next element
  816. //
  817. Current = (PLIST_ENTRY)(Current->Flink);
  818. }
  819. // sorry, no match
  820. return FALSE;
  821. }
  822. VOID
  823. CmpAssignSecurityToKcb(
  824. IN PCM_KEY_CONTROL_BLOCK Kcb,
  825. IN HCELL_INDEX SecurityCell
  826. )
  827. /*++
  828. Routine Description:
  829. Establishes the connection between the KCB and the cached security
  830. descriptor.
  831. As most of the time this is called after the security cell has been
  832. linked to the Key Node, and because the binary search starts with
  833. the last cell looked up, we will not hit a performance impact here.
  834. Arguments:
  835. Kcb - the KCb to which this security cell needs to be attached
  836. SecurityCell - Security cell for the kcb
  837. Return Value:
  838. NONE; bugchecks on error
  839. --*/
  840. {
  841. ULONG Index;
  842. PCMHIVE CmHive;
  843. PAGED_CODE();
  844. if( SecurityCell == HCELL_NIL ) {
  845. Kcb->CachedSecurity = NULL;
  846. return;
  847. }
  848. CmHive = (PCMHIVE)(Kcb->KeyHive);
  849. //
  850. // get the security descriptor from cache
  851. //
  852. if( CmpFindSecurityCellCacheIndex (CmHive,SecurityCell,&Index) == FALSE ) {
  853. Kcb->CachedSecurity = NULL;
  854. //
  855. // we are doomed !!!
  856. //
  857. CM_BUGCHECK( REGISTRY_ERROR,BAD_SECURITY_CACHE,1,Kcb,SecurityCell);
  858. }
  859. //
  860. // success; link the cached security to this KCB
  861. //
  862. Kcb->CachedSecurity = CmHive->SecurityCache[Index].CachedSecurity;
  863. }
  864. #ifdef HIVE_SECURITY_STATS
  865. ULONG
  866. CmpCheckForSecurityDuplicates(
  867. IN OUT PCMHIVE CmHive
  868. )
  869. /*++
  870. Routine Description:
  871. Iterates through the security cache for the specified hive and detects
  872. if there are any security descriptors which are duplicated
  873. Arguments:
  874. CmHive - the hive in question
  875. Return Value:
  876. number of duplicates (it should be 0)
  877. --*/
  878. {
  879. ULONG i,j,Duplicates = 0;
  880. PCM_KEY_SECURITY_CACHE CachedSecurity1,CachedSecurity2;
  881. HCELL_INDEX Cell1,Cell2;
  882. PAGED_CODE();
  883. for( i=0;i<CmHive->SecurityCount - 1;i++) {
  884. CachedSecurity1 = CmHive->SecurityCache[i].CachedSecurity;
  885. Cell1 = CmHive->SecurityCache[i].Cell;
  886. ASSERT( Cell1 == CachedSecurity1->Cell );
  887. for( j=i+1;j<CmHive->SecurityCount;j++) {
  888. CachedSecurity2 = CmHive->SecurityCache[j].CachedSecurity;
  889. Cell2 = CmHive->SecurityCache[j].Cell;
  890. ASSERT( Cell2 == CachedSecurity2->Cell );
  891. if ((CachedSecurity1->DescriptorLength == CachedSecurity2->DescriptorLength) &&
  892. (HvGetCellType(Cell1) == HvGetCellType(Cell2)) &&
  893. (RtlEqualMemory(&(CachedSecurity1->Descriptor),
  894. &(CachedSecurity2->Descriptor),
  895. CachedSecurity1->DescriptorLength))) {
  896. ASSERT( CachedSecurity1->ConvKey == CachedSecurity2->ConvKey );
  897. //
  898. // we've found a duplicate cell;
  899. //
  900. #ifndef _CM_LDR_
  901. DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"Duplicate security cell found in Hive %p Cell1=%8lx Cell2 = %8lx\n",(&(CmHive->Hive)),Cell1,Cell2);
  902. #endif //_CM_LDR_
  903. Duplicates++;
  904. break;
  905. }
  906. }
  907. }
  908. return Duplicates;
  909. }
  910. #endif
  911. BOOLEAN
  912. CmpBuildSecurityCellMappingArray(
  913. IN PCMHIVE CmHive
  914. )
  915. /*++
  916. Routine Description:
  917. Iterates through the security cache for the specified hive and
  918. build the array of mappings.
  919. Arguments:
  920. CmHive - the hive in question
  921. Return Value:
  922. TRUE/FALSE
  923. --*/
  924. {
  925. ULONG i;
  926. PAGED_CODE();
  927. ASSERT( CmHive->CellRemapArray == NULL );
  928. CmHive->CellRemapArray = ExAllocatePool(PagedPool,sizeof(CM_CELL_REMAP_BLOCK)*CmHive->SecurityCount);
  929. if( CmHive->CellRemapArray == NULL ) {
  930. return FALSE;
  931. }
  932. for( i=0;i<CmHive->SecurityCount;i++) {
  933. CmHive->CellRemapArray[i].OldCell = CmHive->SecurityCache[i].Cell;
  934. if( HvGetCellType(CmHive->SecurityCache[i].Cell) == (ULONG)Volatile ) {
  935. //
  936. // we preserve volatile cells
  937. //
  938. CmHive->CellRemapArray[i].NewCell = CmHive->SecurityCache[i].Cell;
  939. } else {
  940. CmHive->CellRemapArray[i].NewCell = HCELL_NIL;
  941. }
  942. }
  943. return TRUE;
  944. }