xref: /reactos/ntoskrnl/ob/obsdcach.c (revision 29fa274d)
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 */
468