xref: /reactos/dll/win32/aclui/sidcache.c (revision 2196a06f)
1 /*
2  * ReactOS Access Control List Editor
3  * Copyright (C) 2004-2005 ReactOS Team
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19 /*
20  * PROJECT:         ReactOS Access Control List Editor
21  * FILE:            lib/aclui/sidcache.c
22  * PURPOSE:         Access Control List Editor
23  * PROGRAMMER:      Thomas Weidenmueller <w3seek@reactos.com>
24  *
25  * UPDATE HISTORY:
26  *      12/10/2005  Created
27  */
28 
29 #include "precomp.h"
30 
31 #include <ntsecapi.h>
32 
33 #define NDEBUG
34 #include <debug.h>
35 
36 #define HandleToScm(Handle) (PSIDCACHEMGR)(Handle)
37 #define ScmToHandle(Scm) (HANDLE)(Scm)
38 
39 typedef struct _SIDCACHEMGR
40 {
41     volatile LONG RefCount;
42     LSA_HANDLE LsaHandle;
43     CRITICAL_SECTION Lock;
44     LIST_ENTRY QueueListHead;
45     struct _SIDQUEUEENTRY *QueueLookingUp;
46     LIST_ENTRY CacheListHead;
47     HANDLE Heap;
48     HANDLE LookupEvent;
49     HANDLE LookupThread;
50     WCHAR SystemName[1];
51 } SIDCACHEMGR, *PSIDCACHEMGR;
52 
53 
54 typedef struct _SIDCACHECALLBACKINFO
55 {
56     PSIDREQCOMPLETIONPROC CompletionProc;
57     PVOID Context;
58 } SIDCACHECALLBACKINFO, *PSIDCACHECALLBACKINFO;
59 
60 
61 typedef struct _SIDQUEUEENTRY
62 {
63     LIST_ENTRY ListEntry;
64     ULONG CallbackCount;
65     PSIDCACHECALLBACKINFO Callbacks;
66     /* the SID is appended to this structure */
67 } SIDQUEUEENTRY, *PSIDQUEUEENTRY;
68 
69 
70 typedef struct _SIDCACHEENTRY
71 {
72     LIST_ENTRY ListEntry;
73     SID_NAME_USE SidNameUse;
74     PWSTR AccountName;
75     PWSTR DomainName;
76     /* the SID and strings are appended to this structure */
77 } SIDCACHEENTRY, *PSIDCACHEENTRY;
78 
79 
80 static VOID
81 FreeQueueEntry(IN PSIDCACHEMGR scm,
82                IN PSIDQUEUEENTRY QueueEntry)
83 {
84     if (QueueEntry->ListEntry.Flink != NULL)
85     {
86         RemoveEntryList(&QueueEntry->ListEntry);
87     }
88 
89     HeapFree(scm->Heap,
90              0,
91              QueueEntry->Callbacks);
92 
93     HeapFree(scm->Heap,
94              0,
95              QueueEntry);
96 }
97 
98 
99 static VOID
100 FreeCacheEntry(IN PSIDCACHEMGR scm,
101                IN PSIDCACHEENTRY CacheEntry)
102 {
103     RemoveEntryList(&CacheEntry->ListEntry);
104 
105     HeapFree(scm->Heap,
106              0,
107              CacheEntry);
108 }
109 
110 
111 static VOID
112 CleanupSidCacheMgr(IN PSIDCACHEMGR scm)
113 {
114     LsaClose(scm->LsaHandle);
115     CloseHandle(scm->LookupEvent);
116     CloseHandle(scm->LookupThread);
117 
118     /* delete the queue */
119     while (!IsListEmpty(&scm->QueueListHead))
120     {
121         PSIDQUEUEENTRY QueueEntry;
122 
123         QueueEntry = CONTAINING_RECORD(scm->QueueListHead.Flink,
124                                        SIDQUEUEENTRY,
125                                        ListEntry);
126         FreeQueueEntry(scm,
127                        QueueEntry);
128     }
129 
130     /* delete the cache */
131     while (!IsListEmpty(&scm->CacheListHead))
132     {
133         PSIDCACHEENTRY CacheEntry;
134 
135         CacheEntry = CONTAINING_RECORD(scm->CacheListHead.Flink,
136                                        SIDCACHEENTRY,
137                                        ListEntry);
138         FreeCacheEntry(scm,
139                        CacheEntry);
140     }
141 
142     DeleteCriticalSection(&scm->Lock);
143 }
144 
145 
146 static PSIDCACHEMGR
147 ReferenceSidCacheMgr(IN HANDLE SidCacheMgr)
148 {
149     PSIDCACHEMGR scm = HandleToScm(SidCacheMgr);
150 
151     if (InterlockedIncrement(&scm->RefCount) != 1)
152     {
153         return scm;
154     }
155 
156     return NULL;
157 }
158 
159 
160 static VOID
161 DereferenceSidCacheMgr(IN PSIDCACHEMGR scm)
162 {
163     if (InterlockedDecrement(&scm->RefCount) == 0)
164     {
165         /* Signal the lookup thread so it can terminate */
166         SetEvent(scm->LookupEvent);
167     }
168 }
169 
170 
171 static BOOL
172 OpenLSAPolicyHandle(IN LPWSTR SystemName,
173                     IN ACCESS_MASK DesiredAccess,
174                     OUT PLSA_HANDLE PolicyHandle)
175 {
176     LSA_OBJECT_ATTRIBUTES LsaObjectAttributes = {0};
177     LSA_UNICODE_STRING LsaSystemName, *psn;
178     NTSTATUS Status;
179     SIZE_T NameLength;
180 
181     if (SystemName != NULL && SystemName[0] != L'\0')
182     {
183         NameLength = wcslen(SystemName);
184         if (NameLength > UNICODE_STRING_MAX_CHARS)
185         {
186             return FALSE;
187         }
188 
189         LsaSystemName.Buffer = SystemName;
190         LsaSystemName.Length = NameLength * sizeof(WCHAR);
191         LsaSystemName.MaximumLength = LsaSystemName.Length + sizeof(WCHAR);
192         psn = &LsaSystemName;
193     }
194     else
195     {
196         psn = NULL;
197     }
198 
199     Status = LsaOpenPolicy(psn,
200                            &LsaObjectAttributes,
201                            DesiredAccess,
202                            PolicyHandle);
203     if (!NT_SUCCESS(Status))
204     {
205         SetLastError(LsaNtStatusToWinError(Status));
206         return FALSE;
207     }
208 
209     return TRUE;
210 }
211 
212 
213 static BOOL
214 LookupSidInformation(IN PSIDCACHEMGR scm,
215                      IN PSID pSid,
216                      OUT PSIDREQRESULT *ReqResult)
217 {
218     PLSA_REFERENCED_DOMAIN_LIST ReferencedDomain;
219     PLSA_TRANSLATED_NAME Names;
220     PLSA_TRUST_INFORMATION Domain;
221     PLSA_UNICODE_STRING DomainName;
222     SID_NAME_USE SidNameUse = SidTypeUnknown;
223     PPOLICY_ACCOUNT_DOMAIN_INFO PolicyAccountDomainInfo = NULL;
224     NTSTATUS Status;
225     DWORD AccountNameSize, DomainNameSize = 0;
226     PSIDREQRESULT ReqRet = NULL;
227     BOOL Ret = FALSE;
228 
229     Status = LsaLookupSids(scm->LsaHandle,
230                            1,
231                            &pSid,
232                            &ReferencedDomain,
233                            &Names);
234     if (NT_SUCCESS(Status))
235     {
236         SidNameUse = Names->Use;
237 
238         if (ReferencedDomain != NULL &&
239             Names->DomainIndex >= 0)
240         {
241             Domain = &ReferencedDomain->Domains[Names->DomainIndex];
242             DomainName = &Domain->Name;
243         }
244         else
245         {
246             Domain = NULL;
247             DomainName = NULL;
248         }
249 
250         switch (SidNameUse)
251         {
252             case SidTypeAlias:
253             {
254                 if (Domain != NULL)
255                 {
256                     /* query the domain name for BUILTIN accounts */
257                     Status = LsaQueryInformationPolicy(scm->LsaHandle,
258                                                        PolicyAccountDomainInformation,
259                                                        (PVOID*)&PolicyAccountDomainInfo);
260                     if (NT_SUCCESS(Status))
261                     {
262                         DomainName = &PolicyAccountDomainInfo->DomainName;
263 
264                         /* make the user believe this is a group */
265                         SidNameUse = (PolicyAccountDomainInfo != NULL ? SidTypeGroup : SidTypeUser);
266                     }
267                 }
268                 break;
269             }
270 
271             default:
272             {
273                 DPRINT("Unhandled SID type: 0x%x\n", Names->Use);
274                 break;
275             }
276         }
277 
278         AccountNameSize = Names->Name.Length;
279         if (DomainName != NULL)
280         {
281             DomainNameSize = DomainName->Length;
282         }
283 
284         ReqRet = HeapAlloc(scm->Heap,
285                            0,
286                            sizeof(SIDREQRESULT) +
287                                (((AccountNameSize + DomainNameSize) + 2) * sizeof(WCHAR)));
288         if (ReqRet != NULL)
289         {
290             ReqRet->RefCount = 1;
291             ReqRet->AccountName = (LPWSTR)(ReqRet + 1);
292             ReqRet->DomainName = ReqRet->AccountName + (AccountNameSize / sizeof(WCHAR)) + 1;
293 
294             CopyMemory(ReqRet->AccountName,
295                        Names->Name.Buffer,
296                        Names->Name.Length);
297 
298             if (DomainName != NULL)
299             {
300                 CopyMemory(ReqRet->DomainName,
301                            DomainName->Buffer,
302                            DomainName->Length);
303             }
304 
305             ReqRet->AccountName[AccountNameSize / sizeof(WCHAR)] = L'\0';
306             ReqRet->DomainName[DomainNameSize / sizeof(WCHAR)] = L'\0';
307 
308             ReqRet->SidNameUse = SidNameUse;
309         }
310 
311         if (PolicyAccountDomainInfo != NULL)
312         {
313             LsaFreeMemory(PolicyAccountDomainInfo);
314         }
315 
316         LsaFreeMemory(ReferencedDomain);
317         LsaFreeMemory(Names);
318 
319         Ret = TRUE;
320     }
321     else if (Status == STATUS_NONE_MAPPED)
322     {
323         Ret = TRUE;
324     }
325 
326     if (Ret)
327     {
328         *ReqResult = ReqRet;
329     }
330 
331     return Ret;
332 }
333 
334 
335 static BOOL
336 FindSidInCache(IN PSIDCACHEMGR scm,
337                IN PSID pSid,
338                OUT PSIDREQRESULT *ReqResult)
339 {
340     PSIDCACHEENTRY CacheEntry;
341     PLIST_ENTRY CurrentEntry;
342     PSIDREQRESULT ReqRes;
343     BOOL Ret = FALSE;
344 
345     /* NOTE: assumes the lists are locked! */
346 
347     for (CurrentEntry = scm->CacheListHead.Flink;
348          CurrentEntry != &scm->CacheListHead;
349          CurrentEntry = CurrentEntry->Flink)
350     {
351         CacheEntry = CONTAINING_RECORD(CurrentEntry,
352                                        SIDCACHEENTRY,
353                                        ListEntry);
354 
355         if (EqualSid(pSid,
356                      (PSID)(CacheEntry + 1)))
357         {
358             SIZE_T ReqResultSize;
359             ULONG AccountNameLen, DomainNameLen;
360 
361             Ret = TRUE;
362 
363             AccountNameLen = wcslen(CacheEntry->AccountName);
364             DomainNameLen = wcslen(CacheEntry->DomainName);
365 
366             ReqResultSize = sizeof(SIDREQRESULT) +
367                                 (((AccountNameLen + 1) +
368                                   (DomainNameLen + 1)) * sizeof(WCHAR));
369 
370             ReqRes = HeapAlloc(scm->Heap,
371                                0,
372                                ReqResultSize);
373             if (ReqRes != NULL)
374             {
375                 PWSTR Buffer = (PWSTR)(ReqRes + 1);
376 
377                 ReqRes->RefCount = 1;
378 
379                 ReqRes->AccountName = Buffer;
380                 wcscpy(ReqRes->AccountName,
381                        CacheEntry->AccountName);
382                 Buffer += AccountNameLen + 1;
383 
384                 ReqRes->DomainName = Buffer;
385                 wcscpy(ReqRes->DomainName,
386                        CacheEntry->DomainName);
387             }
388 
389             /* return the result, even if we weren't unable to
390                allocate enough memory! */
391             *ReqResult = ReqRes;
392             break;
393         }
394     }
395 
396     return Ret;
397 }
398 
399 
400 static VOID
401 CacheLookupResults(IN PSIDCACHEMGR scm,
402                    IN PSID pSid,
403                    IN PSIDREQRESULT ReqResult)
404 {
405     PSIDCACHEENTRY CacheEntry;
406     DWORD SidLen;
407     SIZE_T AccountNameLen = 0;
408     SIZE_T DomainNameLen = 0;
409     SIZE_T CacheEntrySize = sizeof(SIDCACHEENTRY);
410 
411     /* NOTE: assumes the lists are locked! */
412 
413     SidLen = GetLengthSid(pSid);
414     CacheEntrySize += SidLen;
415 
416     AccountNameLen = wcslen(ReqResult->AccountName);
417     CacheEntrySize += (AccountNameLen + 1) * sizeof(WCHAR);
418 
419     DomainNameLen = wcslen(ReqResult->DomainName);
420     CacheEntrySize += (wcslen(ReqResult->DomainName) + 1) * sizeof(WCHAR);
421 
422     CacheEntry = HeapAlloc(scm->Heap,
423                            0,
424                            CacheEntrySize);
425     if (CacheEntry != NULL)
426     {
427         PWSTR lpBuf = (PWSTR)((ULONG_PTR)(CacheEntry + 1) + SidLen);
428 
429         CacheEntry->SidNameUse = ReqResult->SidNameUse;
430 
431         /* append the SID */
432         CopySid(SidLen,
433                 (PSID)(CacheEntry + 1),
434                 pSid);
435 
436         /* append the strings */
437         CacheEntry->AccountName = lpBuf;
438         wcscpy(lpBuf,
439                ReqResult->AccountName);
440         lpBuf += AccountNameLen + 1;
441 
442         CacheEntry->DomainName = lpBuf;
443         wcscpy(lpBuf,
444                ReqResult->DomainName);
445         lpBuf += DomainNameLen + 1;
446 
447         /* add the entry to the cache list */
448         InsertTailList(&scm->CacheListHead,
449                        &CacheEntry->ListEntry);
450     }
451 }
452 
453 
454 static DWORD WINAPI
455 LookupThreadProc(IN LPVOID lpParameter)
456 {
457     HMODULE hModule;
458     PSIDCACHEMGR scm = (PSIDCACHEMGR)lpParameter;
459 
460     /* Reference the dll to avoid problems in case of accidental
461        FreeLibrary calls... */
462     if (!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
463                             (LPCWSTR)hDllInstance,
464                             &hModule))
465     {
466         hModule = NULL;
467     }
468 
469     while (scm->RefCount != 0)
470     {
471         PSIDQUEUEENTRY QueueEntry = NULL;
472 
473         EnterCriticalSection(&scm->Lock);
474 
475         /* get the first item of the queue */
476         if (scm->QueueListHead.Flink != &scm->QueueListHead)
477         {
478             QueueEntry = CONTAINING_RECORD(scm->QueueListHead.Flink,
479                                            SIDQUEUEENTRY,
480                                            ListEntry);
481             RemoveEntryList(&QueueEntry->ListEntry);
482             QueueEntry->ListEntry.Flink = NULL;
483         }
484         else
485         {
486             LeaveCriticalSection(&scm->Lock);
487 
488             /* wait for the next asynchronous lookup queued */
489             WaitForSingleObject(scm->LookupEvent,
490                                 INFINITE);
491             continue;
492         }
493 
494         scm->QueueLookingUp = QueueEntry;
495 
496         LeaveCriticalSection(&scm->Lock);
497 
498         if (QueueEntry != NULL)
499         {
500             PSIDREQRESULT ReqResult, FoundReqResult;
501             PSID pSid = (PSID)(QueueEntry + 1);
502 
503             /* lookup the SID information */
504             if (!LookupSidInformation(scm,
505                                       pSid,
506                                       &ReqResult))
507             {
508                 ReqResult = NULL;
509             }
510 
511             EnterCriticalSection(&scm->Lock);
512 
513             /* see if the SID was added to the cache in the meanwhile */
514             if (!FindSidInCache(scm,
515                                 pSid,
516                                 &FoundReqResult))
517             {
518                 if (ReqResult != NULL)
519                 {
520                     /* cache the results */
521                     CacheLookupResults(scm,
522                                        pSid,
523                                        ReqResult);
524                 }
525             }
526             else
527             {
528                 if (ReqResult != NULL)
529                 {
530                     /* free the information of our lookup and use the cached
531                        information*/
532                     DereferenceSidReqResult(scm,
533                                             ReqResult);
534                 }
535 
536                 ReqResult = FoundReqResult;
537             }
538 
539             /* notify the callers unless the lookup was cancelled */
540             if (scm->QueueLookingUp != NULL)
541             {
542                 ULONG i = 0;
543 
544                 while (scm->QueueLookingUp != NULL &&
545                        i < QueueEntry->CallbackCount)
546                 {
547                     PVOID Context;
548                     PSIDREQCOMPLETIONPROC CompletionProc;
549 
550                     Context = QueueEntry->Callbacks[i].Context;
551                     CompletionProc = QueueEntry->Callbacks[i].CompletionProc;
552 
553                     LeaveCriticalSection(&scm->Lock);
554 
555                     /* call the completion proc without holding the lock! */
556                     CompletionProc(ScmToHandle(scm),
557                                    pSid,
558                                    ReqResult,
559                                    Context);
560 
561                     EnterCriticalSection(&scm->Lock);
562 
563                     i++;
564                 }
565 
566                 scm->QueueLookingUp = NULL;
567             }
568 
569             LeaveCriticalSection(&scm->Lock);
570 
571             /* free the queue item */
572             FreeQueueEntry(scm,
573                            QueueEntry);
574         }
575     }
576 
577     CleanupSidCacheMgr(scm);
578 
579     HeapFree(scm->Heap,
580              0,
581              scm);
582 
583     if (hModule != NULL)
584     {
585         /* dereference the library and exit */
586         FreeLibraryAndExitThread(hModule,
587                                  0);
588     }
589 
590     return 0;
591 }
592 
593 
594 
595 HANDLE
596 CreateSidCacheMgr(IN HANDLE Heap,
597                   IN LPCWSTR SystemName)
598 {
599     PSIDCACHEMGR scm;
600 
601     if (SystemName == NULL)
602         SystemName = L"";
603 
604     scm = HeapAlloc(Heap,
605                     0,
606                     FIELD_OFFSET(SIDCACHEMGR,
607                                  SystemName[wcslen(SystemName) + 1]));
608     if (scm != NULL)
609     {
610         /* zero the static part of the structure */
611         ZeroMemory(scm,
612                    FIELD_OFFSET(SIDCACHEMGR,
613                                 SystemName));
614 
615         scm->RefCount = 1;
616         scm->Heap = Heap;
617 
618         wcscpy(scm->SystemName,
619                SystemName);
620 
621         InitializeCriticalSection(&scm->Lock);
622         InitializeListHead(&scm->QueueListHead);
623         InitializeListHead(&scm->CacheListHead);
624 
625         scm->LookupEvent = CreateEvent(NULL,
626                                        FALSE,
627                                        FALSE,
628                                        NULL);
629         if (scm->LookupEvent == NULL)
630         {
631             goto Cleanup;
632         }
633 
634         if (!OpenLSAPolicyHandle(scm->SystemName,
635                                  POLICY_LOOKUP_NAMES | POLICY_VIEW_LOCAL_INFORMATION,
636                                  &scm->LsaHandle))
637         {
638             goto Cleanup;
639         }
640 
641         scm->LookupThread = CreateThread(NULL,
642                                          0,
643                                          LookupThreadProc,
644                                          scm,
645                                          0,
646                                          NULL);
647         if (scm->LookupThread == NULL)
648         {
649 Cleanup:
650             if (scm->LookupEvent != NULL)
651             {
652                 CloseHandle(scm->LookupEvent);
653             }
654 
655             if (scm->LsaHandle != NULL)
656             {
657                 LsaClose(scm->LsaHandle);
658             }
659 
660             HeapFree(Heap,
661                      0,
662                      scm);
663             scm = NULL;
664         }
665     }
666     else
667     {
668         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
669     }
670 
671     return (HANDLE)scm;
672 }
673 
674 
675 VOID
676 DestroySidCacheMgr(IN HANDLE SidCacheMgr)
677 {
678     PSIDCACHEMGR scm = HandleToScm(SidCacheMgr);
679 
680     if (scm != NULL)
681     {
682         /* remove the keep-alive reference */
683         DereferenceSidCacheMgr(scm);
684     }
685 }
686 
687 
688 static BOOL
689 QueueSidLookup(IN PSIDCACHEMGR scm,
690                IN PSID pSid,
691                IN PSIDREQCOMPLETIONPROC CompletionProc,
692                IN PVOID Context)
693 {
694     PLIST_ENTRY CurrentEntry;
695     PSIDQUEUEENTRY QueueEntry, FoundEntry = NULL;
696     BOOL Ret = FALSE;
697 
698     /* NOTE: assumes the lists are locked! */
699 
700     if (scm->QueueLookingUp != NULL &&
701         EqualSid(pSid,
702                  (PSID)(scm->QueueLookingUp + 1)))
703     {
704         FoundEntry = scm->QueueLookingUp;
705     }
706     else
707     {
708         for (CurrentEntry = scm->QueueListHead.Flink;
709              CurrentEntry != &scm->QueueListHead;
710              CurrentEntry = CurrentEntry->Flink)
711         {
712             QueueEntry = CONTAINING_RECORD(CurrentEntry,
713                                            SIDQUEUEENTRY,
714                                            ListEntry);
715 
716             if (EqualSid(pSid,
717                          (PSID)(QueueEntry + 1)))
718             {
719                 FoundEntry = QueueEntry;
720                 break;
721             }
722         }
723     }
724 
725     if (FoundEntry == NULL)
726     {
727         DWORD SidLength = GetLengthSid(pSid);
728 
729         FoundEntry = HeapAlloc(scm->Heap,
730                                0,
731                                sizeof(SIDQUEUEENTRY) + SidLength);
732         if (FoundEntry != NULL)
733         {
734             CopySid(SidLength,
735                     (PSID)(FoundEntry + 1),
736                     pSid);
737 
738             FoundEntry->CallbackCount = 1;
739             FoundEntry->Callbacks = HeapAlloc(scm->Heap,
740                                               0,
741                                               sizeof(SIDCACHECALLBACKINFO));
742 
743             if (FoundEntry->Callbacks != NULL)
744             {
745                 FoundEntry->Callbacks[0].CompletionProc = CompletionProc;
746                 FoundEntry->Callbacks[0].Context = Context;
747 
748                 /* append it to the queue */
749                 InsertTailList(&scm->QueueListHead,
750                                &FoundEntry->ListEntry);
751 
752                 /* signal the lookup event */
753                 SetEvent(scm->LookupEvent);
754 
755                 Ret = TRUE;
756             }
757             else
758             {
759                 /* unable to queue it because we couldn't allocate the callbacks
760                    array, free the memory and return */
761                 HeapFree(scm->Heap,
762                          0,
763                          FoundEntry);
764             }
765         }
766     }
767     else
768     {
769         PSIDCACHECALLBACKINFO Sidccb;
770 
771         /* add the callback */
772         Sidccb = HeapReAlloc(scm->Heap,
773                              0,
774                              FoundEntry->Callbacks,
775                              (FoundEntry->CallbackCount + 1) * sizeof(SIDCACHECALLBACKINFO));
776         if (Sidccb != NULL)
777         {
778             FoundEntry->Callbacks = Sidccb;
779             FoundEntry->Callbacks[FoundEntry->CallbackCount].CompletionProc = CompletionProc;
780             FoundEntry->Callbacks[FoundEntry->CallbackCount++].Context = Context;
781 
782             Ret = TRUE;
783         }
784     }
785 
786     return Ret;
787 }
788 
789 
790 VOID
791 DequeueSidLookup(IN HANDLE SidCacheMgr,
792                  IN PSID pSid)
793 {
794     PLIST_ENTRY CurrentEntry;
795     PSIDQUEUEENTRY QueueEntry;
796     PSIDCACHEMGR scm;
797 
798     scm = ReferenceSidCacheMgr(SidCacheMgr);
799     if (scm != NULL)
800     {
801         EnterCriticalSection(&scm->Lock);
802 
803         if (scm->QueueLookingUp != NULL &&
804             EqualSid(pSid,
805                      (PSID)(scm->QueueLookingUp + 1)))
806         {
807             /* don't free the queue lookup item! this will be
808                done in the lookup thread */
809             scm->QueueLookingUp = NULL;
810         }
811         else
812         {
813             for (CurrentEntry = scm->QueueListHead.Flink;
814                  CurrentEntry != &scm->QueueListHead;
815                  CurrentEntry = CurrentEntry->Flink)
816             {
817                 QueueEntry = CONTAINING_RECORD(CurrentEntry,
818                                                SIDQUEUEENTRY,
819                                                ListEntry);
820 
821                 if (EqualSid(pSid,
822                              (PSID)(QueueEntry + 1)))
823                 {
824                     FreeQueueEntry(scm,
825                                    QueueEntry);
826                     break;
827                 }
828             }
829         }
830 
831         LeaveCriticalSection(&scm->Lock);
832 
833         DereferenceSidCacheMgr(scm);
834     }
835 }
836 
837 
838 VOID
839 ReferenceSidReqResult(IN HANDLE SidCacheMgr,
840                       IN PSIDREQRESULT ReqResult)
841 {
842     PSIDCACHEMGR scm;
843 
844     scm = ReferenceSidCacheMgr(SidCacheMgr);
845     if (scm != NULL)
846     {
847         InterlockedIncrement(&ReqResult->RefCount);
848 
849         DereferenceSidCacheMgr(scm);
850     }
851 }
852 
853 
854 VOID
855 DereferenceSidReqResult(IN HANDLE SidCacheMgr,
856                         IN PSIDREQRESULT ReqResult)
857 {
858     PSIDCACHEMGR scm;
859 
860     scm = ReferenceSidCacheMgr(SidCacheMgr);
861     if (scm != NULL)
862     {
863         if (InterlockedDecrement(&ReqResult->RefCount) == 0)
864         {
865             HeapFree(scm->Heap,
866                      0,
867                      ReqResult);
868         }
869 
870         DereferenceSidCacheMgr(scm);
871     }
872 }
873 
874 
875 BOOL
876 LookupSidCache(IN HANDLE SidCacheMgr,
877                IN PSID pSid,
878                IN PSIDREQCOMPLETIONPROC CompletionProc,
879                IN PVOID Context)
880 {
881     BOOL Found = FALSE;
882     PSIDREQRESULT ReqResult = NULL;
883     PSIDCACHEMGR scm;
884 
885     scm = ReferenceSidCacheMgr(SidCacheMgr);
886     if (scm != NULL)
887     {
888         EnterCriticalSection(&scm->Lock);
889 
890         /* search the cache */
891         Found = FindSidInCache(scm,
892                                pSid,
893                                &ReqResult);
894 
895         if (!Found)
896         {
897             /* the sid is not in the cache, queue it if not already queued */
898             if (!QueueSidLookup(scm,
899                                 pSid,
900                                 CompletionProc,
901                                 Context))
902             {
903                 PSIDREQRESULT FoundReqResult = NULL;
904 
905                 /* unable to queue it, look it up now */
906 
907                 LeaveCriticalSection(&scm->Lock);
908 
909                 /* lookup everything we need */
910                 if (!LookupSidInformation(scm,
911                                           pSid,
912                                           &ReqResult))
913                 {
914                     ReqResult = NULL;
915                 }
916 
917                 EnterCriticalSection(&scm->Lock);
918 
919                 /* see if the SID was added to the cache in the meanwhile */
920                 if (!FindSidInCache(scm,
921                                     pSid,
922                                     &FoundReqResult))
923                 {
924                     if (ReqResult != NULL)
925                     {
926                         /* cache the results */
927                         CacheLookupResults(scm,
928                                            pSid,
929                                            ReqResult);
930                     }
931                 }
932                 else
933                 {
934                     if (ReqResult != NULL)
935                     {
936                         /* free the information of our lookup and use the cached
937                            information*/
938                         DereferenceSidReqResult(scm,
939                                                 ReqResult);
940                     }
941 
942                     ReqResult = FoundReqResult;
943                 }
944 
945                 Found = (ReqResult != NULL);
946             }
947         }
948 
949         LeaveCriticalSection(&scm->Lock);
950 
951         /* call the completion callback */
952         if (Found)
953         {
954             CompletionProc(SidCacheMgr,
955                            pSid,
956                            ReqResult,
957                            Context);
958 
959             if (ReqResult != NULL)
960             {
961                 HeapFree(scm->Heap,
962                          0,
963                          ReqResult);
964             }
965         }
966 
967         DereferenceSidCacheMgr(scm);
968     }
969 
970     return Found;
971 }
972