sync with trunk (r49238)
[reactos.git] / ntoskrnl / ob / obsdcach.c
1 /*
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)
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 /* GLOBALS ********************************************************************/
16
17 #define SD_CACHE_ENTRIES 0x100
18 OB_SD_CACHE_LIST ObsSecurityDescriptorCache[SD_CACHE_ENTRIES];
19
20 /* PRIVATE FUNCTIONS **********************************************************/
21
22 FORCEINLINE
23 VOID
24 ObpSdAcquireLock(IN POB_SD_CACHE_LIST CacheEntry)
25 {
26 /* Acquire the lock */
27 KeEnterCriticalRegion();
28 ExAcquirePushLockExclusive(&CacheEntry->PushLock);
29 }
30
31 FORCEINLINE
32 VOID
33 ObpSdReleaseLock(IN POB_SD_CACHE_LIST CacheEntry)
34 {
35 /* Release the lock */
36 ExReleasePushLockExclusive(&CacheEntry->PushLock);
37 KeLeaveCriticalRegion();
38 }
39
40 FORCEINLINE
41 VOID
42 ObpSdAcquireLockShared(IN POB_SD_CACHE_LIST CacheEntry)
43 {
44 /* Acquire the lock */
45 KeEnterCriticalRegion();
46 ExAcquirePushLockShared(&CacheEntry->PushLock);
47 }
48
49 FORCEINLINE
50 VOID
51 ObpSdReleaseLockShared(IN POB_SD_CACHE_LIST CacheEntry)
52 {
53 /* Release the lock */
54 ExReleasePushLock(&CacheEntry->PushLock);
55 KeLeaveCriticalRegion();
56 }
57
58 NTSTATUS
59 NTAPI
60 ObpInitSdCache(VOID)
61 {
62 ULONG i;
63
64 /* Loop each cache entry */
65 for (i = 0; i < SD_CACHE_ENTRIES; i++)
66 {
67 /* Initialize the lock and the list */
68 InitializeListHead(&ObsSecurityDescriptorCache[i].Head);
69 ExInitializePushLock((PULONG_PTR)&ObsSecurityDescriptorCache[i].PushLock);
70 }
71
72 /* Return success */
73 return STATUS_SUCCESS;
74 }
75
76 ULONG
77 NTAPI
78 ObpHash(IN PVOID Buffer,
79 IN ULONG Length)
80 {
81 PULONG p, pp;
82 PUCHAR pb, ppb;
83 ULONG Hash = 0;
84
85 /* Setup aligned and byte buffers */
86 p = Buffer;
87 pb = (PUCHAR)p;
88 ppb = (PUCHAR)((ULONG_PTR)Buffer + Length);
89 pp = (PULONG)ALIGN_DOWN(pb + Length, ULONG);
90
91 /* Loop aligned data */
92 while (p < pp)
93 {
94 /* XOR-rotate */
95 Hash ^= *p++;
96 Hash = _rotl(Hash, 3);
97 }
98
99 /* Loop non-aligned data */
100 pb = (PUCHAR)p;
101 while (pb < ppb)
102 {
103 /* XOR-rotate */
104 Hash ^= *pb++;
105 Hash = _rotl(Hash, 3);
106 }
107
108 /* Return the hash */
109 return Hash;
110 }
111
112 ULONG
113 NTAPI
114 ObpHashSecurityDescriptor(IN PSECURITY_DESCRIPTOR SecurityDescriptor,
115 IN ULONG Length)
116 {
117 /* Just hash the entire SD */
118 return ObpHash(SecurityDescriptor, Length);
119 }
120
121 PSECURITY_DESCRIPTOR_HEADER
122 NTAPI
123 ObpCreateCacheEntry(IN PSECURITY_DESCRIPTOR SecurityDescriptor,
124 IN ULONG Length,
125 IN ULONG FullHash,
126 IN ULONG RefCount)
127 {
128 ULONG CacheSize;
129 PSECURITY_DESCRIPTOR_HEADER SdHeader;
130 ASSERT(Length == RtlLengthSecurityDescriptor(SecurityDescriptor));
131
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;
136
137 /* Setup the header */
138 SdHeader->RefCount = RefCount;
139 SdHeader->FullHash = FullHash;
140
141 /* Copy the descriptor */
142 RtlCopyMemory(&SdHeader->SecurityDescriptor, SecurityDescriptor, Length);
143
144 /* Return it */
145 return SdHeader;
146 }
147
148 BOOLEAN
149 NTAPI
150 ObpCompareSecurityDescriptors(IN PSECURITY_DESCRIPTOR Sd1,
151 IN ULONG Length1,
152 IN PSECURITY_DESCRIPTOR Sd2)
153 {
154 ULONG Length2;
155 ASSERT(Length1 == RtlLengthSecurityDescriptor(Sd1));
156
157 /* Get the length of the second SD */
158 Length2 = RtlLengthSecurityDescriptor(Sd2);
159
160 /* Compare lengths */
161 if (Length1 != Length2) return FALSE;
162
163 /* Compare contents */
164 return RtlEqualMemory(Sd1, Sd2, Length1);
165 }
166
167 PVOID
168 NTAPI
169 ObpDestroySecurityDescriptorHeader(IN PSECURITY_DESCRIPTOR_HEADER SdHeader)
170 {
171 ASSERT(SdHeader->RefCount == 0);
172
173 /* Just unlink the SD and return it back to the caller */
174 RemoveEntryList(&SdHeader->Link);
175 return SdHeader;
176 }
177
178 PSECURITY_DESCRIPTOR
179 NTAPI
180 ObpReferenceSecurityDescriptor(IN POBJECT_HEADER ObjectHeader)
181 {
182 PSECURITY_DESCRIPTOR SecurityDescriptor;
183 PSECURITY_DESCRIPTOR_HEADER SdHeader;
184 PEX_FAST_REF FastRef;
185 EX_FAST_REF OldValue;
186 ULONG_PTR Count;
187
188 /* Acquire a reference to the security descriptor */
189 FastRef = (PEX_FAST_REF)&ObjectHeader->SecurityDescriptor;
190 OldValue = ExAcquireFastReference(FastRef);
191
192 /* Get the descriptor and reference count */
193 SecurityDescriptor = ExGetObjectFastReference(OldValue);
194 Count = ExGetCountFastReference(OldValue);
195
196 /* Check if there's no descriptor or if there's still cached references */
197 if ((Count >= 1) || !(SecurityDescriptor))
198 {
199 /* Check if this is the last reference */
200 if (Count == 1)
201 {
202 /* Add the extra references that we'll take */
203 SdHeader = ObpGetHeaderForSd(SecurityDescriptor);
204 InterlockedExchangeAdd((PLONG)&SdHeader->RefCount, MAX_FAST_REFS);
205
206 /* Now insert them */
207 if (!ExInsertFastReference(FastRef, SecurityDescriptor))
208 {
209 /* Undo the references since we failed */
210 InterlockedExchangeAdd((PLONG)&SdHeader->RefCount,
211 -MAX_FAST_REFS);
212 }
213 }
214
215 /* Return the SD */
216 return SecurityDescriptor;
217 }
218
219 /* Lock the object */
220 ObpAcquireObjectLockShared(ObjectHeader);
221
222 /* Get the object header */
223 SecurityDescriptor = ExGetObjectFastReference(*FastRef);
224 SdHeader = ObpGetHeaderForSd(SecurityDescriptor);
225
226 /* Do the reference */
227 InterlockedIncrement((PLONG)&SdHeader->RefCount);
228
229 /* Release the lock and return */
230 ObpReleaseObjectLock(ObjectHeader);
231 return SecurityDescriptor;
232 }
233
234 /* PUBLIC FUNCTIONS ***********************************************************/
235
236 /*++
237 * @name ObReferenceSecurityDescriptor
238 * @implemented NT5.2
239 *
240 * The ObReferenceSecurityDescriptor routine <FILLMEIN>
241 *
242 * @param SecurityDescriptor
243 * <FILLMEIN>
244 *
245 * @param Count
246 * <FILLMEIN>
247 *
248 * @return STATUS_SUCCESS or appropriate error value.
249 *
250 * @remarks None.
251 *
252 *--*/
253 VOID
254 NTAPI
255 ObReferenceSecurityDescriptor(IN PSECURITY_DESCRIPTOR SecurityDescriptor,
256 IN ULONG Count)
257 {
258 PSECURITY_DESCRIPTOR_HEADER SdHeader;
259
260 /* Get the header */
261 SdHeader = ObpGetHeaderForSd(SecurityDescriptor);
262
263 /* Do the references */
264 InterlockedExchangeAdd((PLONG)&SdHeader->RefCount, Count);
265 }
266
267 /*++
268 * @name ObDereferenceSecurityDescriptor
269 * @implemented NT5.2
270 *
271 * The ObDereferenceSecurityDescriptor routine <FILLMEIN>
272 *
273 * @param SecurityDescriptor
274 * <FILLMEIN>
275 *
276 * @param Count
277 * <FILLMEIN>
278 *
279 * @return STATUS_SUCCESS or appropriate error value.
280 *
281 * @remarks None.
282 *
283 *--*/
284 VOID
285 NTAPI
286 ObDereferenceSecurityDescriptor(IN PSECURITY_DESCRIPTOR SecurityDescriptor,
287 IN ULONG Count)
288 {
289 PSECURITY_DESCRIPTOR_HEADER SdHeader;
290 LONG OldValue, NewValue;
291 ULONG Index;
292 POB_SD_CACHE_LIST CacheEntry;
293
294 /* Get the header */
295 SdHeader = ObpGetHeaderForSd(SecurityDescriptor);
296
297 /* Get the current reference count */
298 OldValue = SdHeader->RefCount;
299
300 /* Check if the caller is destroying this SD -- we need the lock for that */
301 while (OldValue != Count)
302 {
303 /* He isn't, we can just try to derefeference atomically */
304 NewValue = InterlockedCompareExchange((PLONG)&SdHeader->RefCount,
305 OldValue - Count,
306 OldValue);
307 if (NewValue == OldValue) return;
308
309 /* Try again */
310 OldValue = NewValue;
311 }
312
313 /* At this point, we need the lock, so choose an entry */
314 Index = SdHeader->FullHash % SD_CACHE_ENTRIES;
315 CacheEntry = &ObsSecurityDescriptorCache[Index];
316
317 /* Acquire the lock for it */
318 ObpSdAcquireLock(CacheEntry);
319 ASSERT(SdHeader->RefCount != 0);
320
321 /* Now do the dereference */
322 if (InterlockedExchangeAdd((PLONG)&SdHeader->RefCount, -(LONG)Count) == Count)
323 {
324 /* We're down to zero -- destroy the header */
325 SdHeader = ObpDestroySecurityDescriptorHeader(SdHeader);
326
327 /* Release the lock */
328 ObpSdReleaseLock(CacheEntry);
329
330 /* Free the header */
331 ExFreePool(SdHeader);
332 }
333 else
334 {
335 /* Just release the lock */
336 ObpSdReleaseLock(CacheEntry);
337 }
338
339 }
340
341 /*++
342 * @name ObLogSecurityDescriptor
343 * @implemented NT5.2
344 *
345 * The ObLogSecurityDescriptor routine <FILLMEIN>
346 *
347 * @param InputSecurityDescriptor
348 * <FILLMEIN>
349 *
350 * @param OutputSecurityDescriptor
351 * <FILLMEIN>
352 *
353 * @param RefBias
354 * <FILLMEIN>
355 *
356 * @return STATUS_SUCCESS or appropriate error value.
357 *
358 * @remarks None.
359 *
360 *--*/
361 NTSTATUS
362 NTAPI
363 ObLogSecurityDescriptor(IN PSECURITY_DESCRIPTOR InputSecurityDescriptor,
364 OUT PSECURITY_DESCRIPTOR *OutputSecurityDescriptor,
365 IN ULONG RefBias)
366 {
367 PSECURITY_DESCRIPTOR_HEADER SdHeader = NULL, NewHeader = NULL;
368 ULONG Length, Hash, Index;
369 POB_SD_CACHE_LIST CacheEntry;
370 BOOLEAN Result;
371 PLIST_ENTRY NextEntry;
372
373 /* Get the length */
374 Length = RtlLengthSecurityDescriptor(InputSecurityDescriptor);
375
376 /* Get the hash */
377 Hash = ObpHashSecurityDescriptor(InputSecurityDescriptor, Length);
378
379 /* Now select the appropriate cache entry */
380 Index = Hash % SD_CACHE_ENTRIES;
381 CacheEntry = &ObsSecurityDescriptorCache[Index];
382
383 /* Lock it shared */
384 ObpSdAcquireLockShared(CacheEntry);
385
386 /* Start our search */
387 while (TRUE)
388 {
389 /* Reset result found */
390 Result = FALSE;
391
392 /* Loop the hash list */
393 NextEntry = CacheEntry->Head.Flink;
394 while (NextEntry != &CacheEntry->Head)
395 {
396 /* Get the header */
397 SdHeader = ObpGetHeaderForEntry(NextEntry);
398
399 /* Our hashes are ordered, so quickly check if we should stop now */
400 if (SdHeader->FullHash > Hash) break;
401
402 /* We survived the quick hash check, now check for equalness */
403 if (SdHeader->FullHash == Hash)
404 {
405 /* Hashes match, now compare descriptors */
406 Result = ObpCompareSecurityDescriptors(InputSecurityDescriptor,
407 Length,
408 &SdHeader->SecurityDescriptor);
409 if (Result) break;
410 }
411
412 /* Go to the next entry */
413 NextEntry = NextEntry->Flink;
414 }
415
416 /* Check if we found anything */
417 if (Result)
418 {
419 /* Increment its reference count */
420 InterlockedExchangeAdd((PLONG)&SdHeader->RefCount, RefBias);
421
422 /* Release the lock */
423 ObpSdReleaseLockShared(CacheEntry);
424
425 /* Return the descriptor */
426 *OutputSecurityDescriptor = &SdHeader->SecurityDescriptor;
427
428 /* Free anything that we may have had to create */
429 if (NewHeader) ExFreePool(NewHeader);
430 return STATUS_SUCCESS;
431 }
432
433 /* Check if we got here, and didn't create a descriptor yet */
434 if (!NewHeader)
435 {
436 /* Release the lock */
437 ObpSdReleaseLockShared(CacheEntry);
438
439 /* This should be our first time in the loop, create it */
440 NewHeader = ObpCreateCacheEntry(InputSecurityDescriptor,
441 Length,
442 Hash,
443 RefBias);
444 if (!NewHeader) return STATUS_INSUFFICIENT_RESOURCES;
445
446 /* Now acquire the exclusive lock and we should hit the right path */
447 ObpSdAcquireLock(CacheEntry);
448 }
449 else
450 {
451 /* We have inserted the SD, we're fine now */
452 break;
453 }
454 }
455
456 /* Okay, now let's do the insert, we should have the exclusive lock */
457 InsertTailList(NextEntry, &NewHeader->Link);
458
459 /* Release the lock */
460 ObpSdReleaseLock(CacheEntry);
461
462 /* Return the SD*/
463 *OutputSecurityDescriptor = &NewHeader->SecurityDescriptor;
464 return STATUS_SUCCESS;
465 }
466
467 /* EOF */