2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ob/obsdcach.c
5 * PURPOSE: Security Descriptor Caching
6 * PROGRAMMERS: Aleksey Bragin (aleksey@reactos.org)
9 /* INCLUDES *******************************************************************/
15 /* GLOBALS ********************************************************************/
17 #define SD_CACHE_ENTRIES 0x100
18 OB_SD_CACHE_LIST ObsSecurityDescriptorCache
[SD_CACHE_ENTRIES
];
20 /* PRIVATE FUNCTIONS **********************************************************/
24 ObpSdAcquireLock(IN POB_SD_CACHE_LIST CacheEntry
)
26 /* Acquire the lock */
27 KeEnterCriticalRegion();
28 ExAcquirePushLockExclusive(&CacheEntry
->PushLock
);
33 ObpSdReleaseLock(IN POB_SD_CACHE_LIST CacheEntry
)
35 /* Release the lock */
36 ExReleasePushLockExclusive(&CacheEntry
->PushLock
);
37 KeLeaveCriticalRegion();
42 ObpSdAcquireLockShared(IN POB_SD_CACHE_LIST CacheEntry
)
44 /* Acquire the lock */
45 KeEnterCriticalRegion();
46 ExAcquirePushLockShared(&CacheEntry
->PushLock
);
51 ObpSdReleaseLockShared(IN POB_SD_CACHE_LIST CacheEntry
)
53 /* Release the lock */
54 ExReleasePushLock(&CacheEntry
->PushLock
);
55 KeLeaveCriticalRegion();
64 /* Loop each cache entry */
65 for (i
= 0; i
< SD_CACHE_ENTRIES
; i
++)
67 /* Initialize the lock and the list */
68 InitializeListHead(&ObsSecurityDescriptorCache
[i
].Head
);
69 ExInitializePushLock((PULONG_PTR
)&ObsSecurityDescriptorCache
[i
].PushLock
);
73 return STATUS_SUCCESS
;
78 ObpHash(IN PVOID Buffer
,
85 /* Setup aligned and byte buffers */
88 ppb
= (PUCHAR
)((ULONG_PTR
)Buffer
+ Length
);
89 pp
= (PULONG
)ALIGN_DOWN(pb
+ Length
, ULONG
);
91 /* Loop aligned data */
96 Hash
= _rotl(Hash
, 3);
99 /* Loop non-aligned data */
105 Hash
= _rotl(Hash
, 3);
108 /* Return the hash */
114 ObpHashSecurityDescriptor(IN PSECURITY_DESCRIPTOR SecurityDescriptor
,
117 /* Just hash the entire SD */
118 return ObpHash(SecurityDescriptor
, Length
);
121 PSECURITY_DESCRIPTOR_HEADER
123 ObpCreateCacheEntry(IN PSECURITY_DESCRIPTOR SecurityDescriptor
,
129 PSECURITY_DESCRIPTOR_HEADER SdHeader
;
130 ASSERT(Length
== RtlLengthSecurityDescriptor(SecurityDescriptor
));
132 /* Calculate the memory we'll need to allocate and allocate it */
133 CacheSize
= Length
+ (sizeof(SECURITY_DESCRIPTOR_HEADER
) - sizeof(QUAD
));
134 SdHeader
= ExAllocatePoolWithTag(PagedPool
, CacheSize
, 'cSbO');
135 if (!SdHeader
) return NULL
;
137 /* Setup the header */
138 SdHeader
->RefCount
= RefCount
;
139 SdHeader
->FullHash
= FullHash
;
141 /* Copy the descriptor */
142 RtlCopyMemory(&SdHeader
->SecurityDescriptor
, SecurityDescriptor
, Length
);
150 ObpCompareSecurityDescriptors(IN PSECURITY_DESCRIPTOR Sd1
,
152 IN PSECURITY_DESCRIPTOR Sd2
)
155 ASSERT(Length1
== RtlLengthSecurityDescriptor(Sd1
));
157 /* Get the length of the second SD */
158 Length2
= RtlLengthSecurityDescriptor(Sd2
);
160 /* Compare lengths */
161 if (Length1
!= Length2
) return FALSE
;
163 /* Compare contents */
164 return RtlEqualMemory(Sd1
, Sd2
, Length1
);
169 ObpDestroySecurityDescriptorHeader(IN PSECURITY_DESCRIPTOR_HEADER SdHeader
)
171 ASSERT(SdHeader
->RefCount
== 0);
173 /* Just unlink the SD and return it back to the caller */
174 RemoveEntryList(&SdHeader
->Link
);
180 ObpReferenceSecurityDescriptor(IN POBJECT_HEADER ObjectHeader
)
182 PSECURITY_DESCRIPTOR SecurityDescriptor
;
183 PSECURITY_DESCRIPTOR_HEADER SdHeader
;
184 PEX_FAST_REF FastRef
;
185 EX_FAST_REF OldValue
;
188 /* Acquire a reference to the security descriptor */
189 FastRef
= (PEX_FAST_REF
)&ObjectHeader
->SecurityDescriptor
;
190 OldValue
= ExAcquireFastReference(FastRef
);
192 /* Get the descriptor and reference count */
193 SecurityDescriptor
= ExGetObjectFastReference(OldValue
);
194 Count
= ExGetCountFastReference(OldValue
);
196 /* Check if there's no descriptor or if there's still cached references */
197 if ((Count
>= 1) || !(SecurityDescriptor
))
199 /* Check if this is the last reference */
202 /* Add the extra references that we'll take */
203 SdHeader
= ObpGetHeaderForSd(SecurityDescriptor
);
204 InterlockedExchangeAdd((PLONG
)&SdHeader
->RefCount
, MAX_FAST_REFS
);
206 /* Now insert them */
207 if (!ExInsertFastReference(FastRef
, SecurityDescriptor
))
209 /* Undo the references since we failed */
210 InterlockedExchangeAdd((PLONG
)&SdHeader
->RefCount
,
216 return SecurityDescriptor
;
219 /* Lock the object */
220 ObpAcquireObjectLockShared(ObjectHeader
);
222 /* Get the object header */
223 SecurityDescriptor
= ExGetObjectFastReference(*FastRef
);
224 SdHeader
= ObpGetHeaderForSd(SecurityDescriptor
);
226 /* Do the reference */
227 InterlockedIncrement((PLONG
)&SdHeader
->RefCount
);
229 /* Release the lock and return */
230 ObpReleaseObjectLock(ObjectHeader
);
231 return SecurityDescriptor
;
234 /* PUBLIC FUNCTIONS ***********************************************************/
237 * @name ObReferenceSecurityDescriptor
240 * The ObReferenceSecurityDescriptor routine <FILLMEIN>
242 * @param SecurityDescriptor
248 * @return STATUS_SUCCESS or appropriate error value.
255 ObReferenceSecurityDescriptor(IN PSECURITY_DESCRIPTOR SecurityDescriptor
,
258 PSECURITY_DESCRIPTOR_HEADER SdHeader
;
261 SdHeader
= ObpGetHeaderForSd(SecurityDescriptor
);
263 /* Do the references */
264 InterlockedExchangeAdd((PLONG
)&SdHeader
->RefCount
, Count
);
268 * @name ObDereferenceSecurityDescriptor
271 * The ObDereferenceSecurityDescriptor routine <FILLMEIN>
273 * @param SecurityDescriptor
279 * @return STATUS_SUCCESS or appropriate error value.
286 ObDereferenceSecurityDescriptor(IN PSECURITY_DESCRIPTOR SecurityDescriptor
,
289 PSECURITY_DESCRIPTOR_HEADER SdHeader
;
290 LONG OldValue
, NewValue
;
292 POB_SD_CACHE_LIST CacheEntry
;
295 SdHeader
= ObpGetHeaderForSd(SecurityDescriptor
);
297 /* Get the current reference count */
298 OldValue
= SdHeader
->RefCount
;
300 /* Check if the caller is destroying this SD -- we need the lock for that */
301 while (OldValue
!= Count
)
303 /* He isn't, we can just try to derefeference atomically */
304 NewValue
= InterlockedCompareExchange((PLONG
)&SdHeader
->RefCount
,
307 if (NewValue
== OldValue
) return;
313 /* At this point, we need the lock, so choose an entry */
314 Index
= SdHeader
->FullHash
% SD_CACHE_ENTRIES
;
315 CacheEntry
= &ObsSecurityDescriptorCache
[Index
];
317 /* Acquire the lock for it */
318 ObpSdAcquireLock(CacheEntry
);
319 ASSERT(SdHeader
->RefCount
!= 0);
321 /* Now do the dereference */
322 if (InterlockedExchangeAdd((PLONG
)&SdHeader
->RefCount
, -(LONG
)Count
) == Count
)
324 /* We're down to zero -- destroy the header */
325 SdHeader
= ObpDestroySecurityDescriptorHeader(SdHeader
);
327 /* Release the lock */
328 ObpSdReleaseLock(CacheEntry
);
330 /* Free the header */
331 ExFreePool(SdHeader
);
335 /* Just release the lock */
336 ObpSdReleaseLock(CacheEntry
);
342 * @name ObLogSecurityDescriptor
345 * The ObLogSecurityDescriptor routine <FILLMEIN>
347 * @param InputSecurityDescriptor
350 * @param OutputSecurityDescriptor
356 * @return STATUS_SUCCESS or appropriate error value.
363 ObLogSecurityDescriptor(IN PSECURITY_DESCRIPTOR InputSecurityDescriptor
,
364 OUT PSECURITY_DESCRIPTOR
*OutputSecurityDescriptor
,
367 PSECURITY_DESCRIPTOR_HEADER SdHeader
= NULL
, NewHeader
= NULL
;
368 ULONG Length
, Hash
, Index
;
369 POB_SD_CACHE_LIST CacheEntry
;
371 PLIST_ENTRY NextEntry
;
374 Length
= RtlLengthSecurityDescriptor(InputSecurityDescriptor
);
377 Hash
= ObpHashSecurityDescriptor(InputSecurityDescriptor
, Length
);
379 /* Now select the appropriate cache entry */
380 Index
= Hash
% SD_CACHE_ENTRIES
;
381 CacheEntry
= &ObsSecurityDescriptorCache
[Index
];
384 ObpSdAcquireLockShared(CacheEntry
);
386 /* Start our search */
389 /* Reset result found */
392 /* Loop the hash list */
393 NextEntry
= CacheEntry
->Head
.Flink
;
394 while (NextEntry
!= &CacheEntry
->Head
)
397 SdHeader
= ObpGetHeaderForEntry(NextEntry
);
399 /* Our hashes are ordered, so quickly check if we should stop now */
400 if (SdHeader
->FullHash
> Hash
) break;
402 /* We survived the quick hash check, now check for equalness */
403 if (SdHeader
->FullHash
== Hash
)
405 /* Hashes match, now compare descriptors */
406 Result
= ObpCompareSecurityDescriptors(InputSecurityDescriptor
,
408 &SdHeader
->SecurityDescriptor
);
412 /* Go to the next entry */
413 NextEntry
= NextEntry
->Flink
;
416 /* Check if we found anything */
419 /* Increment its reference count */
420 InterlockedExchangeAdd((PLONG
)&SdHeader
->RefCount
, RefBias
);
422 /* Release the lock */
423 ObpSdReleaseLockShared(CacheEntry
);
425 /* Return the descriptor */
426 *OutputSecurityDescriptor
= &SdHeader
->SecurityDescriptor
;
428 /* Free anything that we may have had to create */
429 if (NewHeader
) ExFreePool(NewHeader
);
430 return STATUS_SUCCESS
;
433 /* Check if we got here, and didn't create a descriptor yet */
436 /* Release the lock */
437 ObpSdReleaseLockShared(CacheEntry
);
439 /* This should be our first time in the loop, create it */
440 NewHeader
= ObpCreateCacheEntry(InputSecurityDescriptor
,
444 if (!NewHeader
) return STATUS_INSUFFICIENT_RESOURCES
;
446 /* Now acquire the exclusive lock and we should hit the right path */
447 ObpSdAcquireLock(CacheEntry
);
451 /* We have inserted the SD, we're fine now */
456 /* Okay, now let's do the insert, we should have the exclusive lock */
457 InsertTailList(NextEntry
, &NewHeader
->Link
);
459 /* Release the lock */
460 ObpSdReleaseLock(CacheEntry
);
463 *OutputSecurityDescriptor
= &NewHeader
->SecurityDescriptor
;
464 return STATUS_SUCCESS
;