xref: /reactos/ntoskrnl/ob/obsdcach.c (revision c501d811)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS kernel
4  * FILE:            ntoskrnl/ob/sdcache.c
5  * PURPOSE:         No purpose listed.
6  *
7  * PROGRAMMERS:     David Welch (welch@cwcom.net)
8  */
9 
10 /* INCLUDES *******************************************************************/
11 
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15 
16 /* GLOBALS ********************************************************************/
17 
18 #define SD_CACHE_ENTRIES 0x100
19 OB_SD_CACHE_LIST ObsSecurityDescriptorCache[SD_CACHE_ENTRIES];
20 
21 /* PRIVATE FUNCTIONS **********************************************************/
22 
23 VOID
24 FORCEINLINE
25 ObpSdAcquireLock(IN POB_SD_CACHE_LIST CacheEntry)
26 {
27     /* Acquire the lock */
28     KeEnterCriticalRegion();
29     ExAcquirePushLockExclusive(&CacheEntry->PushLock);
30 }
31 
32 VOID
33 FORCEINLINE
34 ObpSdReleaseLock(IN POB_SD_CACHE_LIST CacheEntry)
35 {
36     /* Release the lock */
37     ExReleasePushLockExclusive(&CacheEntry->PushLock);
38     KeLeaveCriticalRegion();
39 }
40 
41 VOID
42 FORCEINLINE
43 ObpSdAcquireLockShared(IN POB_SD_CACHE_LIST CacheEntry)
44 {
45     /* Acquire the lock */
46     KeEnterCriticalRegion();
47     ExAcquirePushLockShared(&CacheEntry->PushLock);
48 }
49 
50 VOID
51 FORCEINLINE
52 ObpSdReleaseLockShared(IN POB_SD_CACHE_LIST CacheEntry)
53 {
54     /* Release the lock */
55     ExReleasePushLock(&CacheEntry->PushLock);
56     KeLeaveCriticalRegion();
57 }
58 
59 NTSTATUS
60 NTAPI
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((PULONG_PTR)&ObsSecurityDescriptorCache[i].PushLock);
71     }
72 
73     /* Return success */
74     return STATUS_SUCCESS;
75 }
76 
77 ULONG
78 NTAPI
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     ppb = (PUCHAR)((ULONG_PTR)Buffer + Length);
89     pp = (PULONG)ALIGN_DOWN(p + 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, TAG('O', 'b', 'S', 'c'));
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 
185     /* Get the SD */
186     SecurityDescriptor = ObjectHeader->SecurityDescriptor;
187     if (!SecurityDescriptor)
188     {
189         /* No SD, nothing to do */
190         return NULL;
191     }
192 
193     /* Lock the object */
194     ObpAcquireObjectLockShared(ObjectHeader);
195 
196     /* Get the object header */
197     SdHeader = ObpGetHeaderForSd(SecurityDescriptor);
198 
199     /* Do the reference */
200     InterlockedIncrement((PLONG)&SdHeader->RefCount);
201 
202     /* Release the lock and return */
203     ObpReleaseObjectLock(ObjectHeader);
204     return SecurityDescriptor;
205 }
206 
207 /* PUBLIC FUNCTIONS ***********************************************************/
208 
209 /*++
210  * @name ObReferenceSecurityDescriptor
211  * @implemented NT5.2
212  *
213  *     The ObReferenceSecurityDescriptor routine <FILLMEIN>
214  *
215  * @param SecurityDescriptor
216  *        <FILLMEIN>
217  *
218  * @param Count
219  *        <FILLMEIN>
220  *
221  * @return STATUS_SUCCESS or appropriate error value.
222  *
223  * @remarks None.
224  *
225  *--*/
226 VOID
227 NTAPI
228 ObReferenceSecurityDescriptor(IN PSECURITY_DESCRIPTOR SecurityDescriptor,
229                               IN ULONG Count)
230 {
231     PSECURITY_DESCRIPTOR_HEADER SdHeader;
232 
233     /* Get the header */
234     SdHeader = ObpGetHeaderForSd(SecurityDescriptor);
235 
236     /* Do the references */
237     InterlockedExchangeAdd((PLONG)&SdHeader->RefCount, Count);
238 }
239 
240 /*++
241  * @name ObDereferenceSecurityDescriptor
242  * @implemented NT5.2
243  *
244  *     The ObDereferenceSecurityDescriptor routine <FILLMEIN>
245  *
246  * @param SecurityDescriptor
247  *        <FILLMEIN>
248  *
249  * @param Count
250  *        <FILLMEIN>
251  *
252  * @return STATUS_SUCCESS or appropriate error value.
253  *
254  * @remarks None.
255  *
256  *--*/
257 VOID
258 NTAPI
259 ObDereferenceSecurityDescriptor(IN PSECURITY_DESCRIPTOR SecurityDescriptor,
260                                 IN ULONG Count)
261 {
262     PSECURITY_DESCRIPTOR_HEADER SdHeader;
263     LONG OldValue, NewValue;
264     ULONG Index;
265     POB_SD_CACHE_LIST CacheEntry;
266 
267     /* Get the header */
268     SdHeader = ObpGetHeaderForSd(SecurityDescriptor);
269 
270     /* Get the current reference count */
271     OldValue = SdHeader->RefCount;
272 
273     /* Check if the caller is destroying this SD -- we need the lock for that */
274     while (OldValue != Count)
275     {
276         /* He isn't, we can just try to derefeference atomically */
277         NewValue = InterlockedCompareExchange((PLONG)&SdHeader->RefCount,
278                                               OldValue - Count,
279                                               OldValue);
280         if (NewValue == OldValue) return;
281 
282         /* Try again */
283         OldValue = NewValue;
284     }
285 
286     /* At this point, we need the lock, so choose an entry */
287     Index = SdHeader->FullHash % SD_CACHE_ENTRIES;
288     CacheEntry = &ObsSecurityDescriptorCache[Index];
289 
290     /* Acquire the lock for it */
291     ObpSdAcquireLock(CacheEntry);
292     ASSERT(SdHeader->RefCount != 0);
293 
294     /* Now do the dereference */
295     if (InterlockedExchangeAdd((PLONG)&SdHeader->RefCount, -(LONG)Count) == Count)
296     {
297         /* We're down to zero -- destroy the header */
298         SdHeader = ObpDestroySecurityDescriptorHeader(SdHeader);
299 
300         /* Release the lock */
301         ObpSdReleaseLock(CacheEntry);
302 
303         /* Free the header */
304         ExFreePool(SdHeader);
305     }
306     else
307     {
308         /* Just release the lock */
309         ObpSdReleaseLock(CacheEntry);
310     }
311 
312 }
313 
314 /*++
315 * @name ObLogSecurityDescriptor
316 * @implemented NT5.2
317 *
318 *     The ObLogSecurityDescriptor routine <FILLMEIN>
319 *
320 * @param InputSecurityDescriptor
321 *        <FILLMEIN>
322 *
323 * @param OutputSecurityDescriptor
324 *        <FILLMEIN>
325 *
326 * @param RefBias
327 *        <FILLMEIN>
328 *
329 * @return STATUS_SUCCESS or appropriate error value.
330 *
331 * @remarks None.
332 *
333 *--*/
334 NTSTATUS
335 NTAPI
336 ObLogSecurityDescriptor(IN PSECURITY_DESCRIPTOR InputSecurityDescriptor,
337                         OUT PSECURITY_DESCRIPTOR *OutputSecurityDescriptor,
338                         IN ULONG RefBias)
339 {
340     PSECURITY_DESCRIPTOR_HEADER SdHeader = NULL, NewHeader  = NULL;
341     ULONG Length, Hash, Index;
342     POB_SD_CACHE_LIST CacheEntry;
343     BOOLEAN Result;
344     PLIST_ENTRY NextEntry;
345 
346     /* Get the length */
347     Length = RtlLengthSecurityDescriptor(InputSecurityDescriptor);
348 
349     /* Get the hash */
350     Hash = ObpHashSecurityDescriptor(InputSecurityDescriptor, Length);
351 
352     /* Now select the appropriate cache entry */
353     Index = Hash % SD_CACHE_ENTRIES;
354     CacheEntry = &ObsSecurityDescriptorCache[Index];
355 
356     /* Lock it shared */
357     ObpSdAcquireLockShared(CacheEntry);
358 
359     /* Start our search */
360     while (TRUE)
361     {
362         /* Reset result found */
363         Result = FALSE;
364 
365         /* Loop the hash list */
366         NextEntry = CacheEntry->Head.Flink;
367         while (NextEntry != &CacheEntry->Head)
368         {
369             /* Get the header */
370             SdHeader = ObpGetHeaderForEntry(NextEntry);
371 
372             /* Our hashes are ordered, so quickly check if we should stop now */
373             if (SdHeader->FullHash > Hash) break;
374 
375             /* We survived the quick hash check, now check for equalness */
376             if (SdHeader->FullHash == Hash)
377             {
378                 /* Hashes match, now compare descriptors */
379                 Result = ObpCompareSecurityDescriptors(InputSecurityDescriptor,
380                                                        Length,
381                                                        &SdHeader->SecurityDescriptor);
382                 if (Result) break;
383             }
384 
385             /* Go to the next entry */
386             NextEntry = NextEntry->Flink;
387         }
388 
389         /* Check if we found anything */
390         if (Result)
391         {
392             /* Increment its reference count */
393             InterlockedExchangeAdd((PLONG)&SdHeader->RefCount, RefBias);
394 
395             /* Release the lock */
396             ObpSdReleaseLockShared(CacheEntry);
397 
398             /* Return the descriptor */
399             *OutputSecurityDescriptor = &SdHeader->SecurityDescriptor;
400 
401             /* Free anything that we may have had to create */
402             if (NewHeader) ExFreePool(NewHeader);
403             return STATUS_SUCCESS;
404         }
405 
406         /* Check if we got here, and didn't create a descriptor yet */
407         if (!NewHeader)
408         {
409             /* Release the lock */
410             ObpSdReleaseLockShared(CacheEntry);
411 
412             /* This should be our first time in the loop, create it */
413             NewHeader = ObpCreateCacheEntry(InputSecurityDescriptor,
414                                             Length,
415                                             Hash,
416                                             RefBias);
417             if (!NewHeader) return STATUS_INSUFFICIENT_RESOURCES;
418 
419             /* Now acquire the exclusive lock and we should hit the right path */
420             ObpSdAcquireLock(CacheEntry);
421         }
422         else
423         {
424             /* We have inserted the SD, we're fine now */
425             break;
426         }
427     }
428 
429     /* Okay, now let's do the insert, we should have the exclusive lock */
430     InsertTailList(NextEntry, &NewHeader->Link);
431 
432     /* Release the lock */
433     ObpSdReleaseLock(CacheEntry);
434 
435     /* Return the SD*/
436     *OutputSecurityDescriptor = &NewHeader->SecurityDescriptor;
437     return STATUS_SUCCESS;
438 }
439 
440 /* EOF */
441