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