2 * ReactOS Access Control List Editor
3 * Copyright (C) 2004-2005 ReactOS Team
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.
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.
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
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>
33 #define HandleToScm(Handle) (PSIDCACHEMGR)(Handle)
34 #define ScmToHandle(Scm) (HANDLE)(Scm)
36 typedef struct _SIDCACHEMGR
38 volatile LONG RefCount
;
40 CRITICAL_SECTION Lock
;
41 LIST_ENTRY QueueListHead
;
42 struct _SIDQUEUEENTRY
*QueueLookingUp
;
43 LIST_ENTRY CacheListHead
;
48 } SIDCACHEMGR
, *PSIDCACHEMGR
;
51 typedef struct _SIDCACHECALLBACKINFO
53 PSIDREQCOMPLETIONPROC CompletionProc
;
55 } SIDCACHECALLBACKINFO
, *PSIDCACHECALLBACKINFO
;
58 typedef struct _SIDQUEUEENTRY
62 PSIDCACHECALLBACKINFO Callbacks
;
63 /* the SID is appended to this structure */
64 } SIDQUEUEENTRY
, *PSIDQUEUEENTRY
;
67 typedef struct _SIDCACHEENTRY
70 SID_NAME_USE SidNameUse
;
73 /* the SID and strings are appended to this structure */
74 } SIDCACHEENTRY
, *PSIDCACHEENTRY
;
78 FreeQueueEntry(IN PSIDCACHEMGR scm
,
79 IN PSIDQUEUEENTRY QueueEntry
)
81 if (QueueEntry
->ListEntry
.Flink
!= NULL
)
83 RemoveEntryList(&QueueEntry
->ListEntry
);
88 QueueEntry
->Callbacks
);
97 FreeCacheEntry(IN PSIDCACHEMGR scm
,
98 IN PSIDCACHEENTRY CacheEntry
)
100 RemoveEntryList(&CacheEntry
->ListEntry
);
109 CleanupSidCacheMgr(IN PSIDCACHEMGR scm
)
111 LsaClose(scm
->LsaHandle
);
112 CloseHandle(scm
->LookupEvent
);
113 CloseHandle(scm
->LookupThread
);
115 /* delete the queue */
116 while (!IsListEmpty(&scm
->QueueListHead
))
118 PSIDQUEUEENTRY QueueEntry
;
120 QueueEntry
= CONTAINING_RECORD(scm
->QueueListHead
.Flink
,
127 /* delete the cache */
128 while (!IsListEmpty(&scm
->CacheListHead
))
130 PSIDCACHEENTRY CacheEntry
;
132 CacheEntry
= CONTAINING_RECORD(scm
->CacheListHead
.Flink
,
139 DeleteCriticalSection(&scm
->Lock
);
144 ReferenceSidCacheMgr(IN HANDLE SidCacheMgr
)
146 PSIDCACHEMGR scm
= HandleToScm(SidCacheMgr
);
148 if (InterlockedIncrement(&scm
->RefCount
) != 1)
158 DereferenceSidCacheMgr(IN PSIDCACHEMGR scm
)
160 if (InterlockedDecrement(&scm
->RefCount
) == 0)
162 /* Signal the lookup thread so it can terminate */
163 SetEvent(scm
->LookupEvent
);
169 OpenLSAPolicyHandle(IN LPWSTR SystemName
,
170 IN ACCESS_MASK DesiredAccess
,
171 OUT PLSA_HANDLE PolicyHandle
)
173 LSA_OBJECT_ATTRIBUTES LsaObjectAttributes
= {0};
174 LSA_UNICODE_STRING LsaSystemName
, *psn
;
177 if (SystemName
!= NULL
&& SystemName
[0] != L
'\0')
179 LsaSystemName
.Buffer
= SystemName
;
180 LsaSystemName
.Length
= wcslen(SystemName
) * sizeof(WCHAR
);
181 LsaSystemName
.MaximumLength
= LsaSystemName
.Length
+ sizeof(WCHAR
);
182 psn
= &LsaSystemName
;
189 Status
= LsaOpenPolicy(psn
,
190 &LsaObjectAttributes
,
193 if (!NT_SUCCESS(Status
))
195 SetLastError(LsaNtStatusToWinError(Status
));
204 LookupSidInformation(IN PSIDCACHEMGR scm
,
206 OUT PSIDREQRESULT
*ReqResult
)
208 PLSA_REFERENCED_DOMAIN_LIST ReferencedDomain
;
209 PLSA_TRANSLATED_NAME Names
;
210 PLSA_TRUST_INFORMATION Domain
;
211 PLSA_UNICODE_STRING DomainName
;
212 SID_NAME_USE SidNameUse
= SidTypeUnknown
;
213 PPOLICY_ACCOUNT_DOMAIN_INFO PolicyAccountDomainInfo
= NULL
;
215 DWORD SidLength
, AccountNameSize
, DomainNameSize
= 0;
216 PSIDREQRESULT ReqRet
= NULL
;
219 Status
= LsaLookupSids(scm
->LsaHandle
,
224 if (NT_SUCCESS(Status
))
226 SidLength
= GetLengthSid(pSid
);
227 SidNameUse
= Names
->Use
;
229 if (ReferencedDomain
!= NULL
&&
230 Names
->DomainIndex
>= 0)
232 Domain
= &ReferencedDomain
->Domains
[Names
->DomainIndex
];
233 DomainName
= &Domain
->Name
;
247 /* query the domain name for BUILTIN accounts */
248 Status
= LsaQueryInformationPolicy(scm
->LsaHandle
,
249 PolicyAccountDomainInformation
,
250 (PVOID
*)&PolicyAccountDomainInfo
);
251 if (NT_SUCCESS(Status
))
253 DomainName
= &PolicyAccountDomainInfo
->DomainName
;
255 /* make the user believe this is a group */
256 SidNameUse
= (PolicyAccountDomainInfo
!= NULL
? SidTypeGroup
: SidTypeUser
);
264 DPRINT("Unhandled SID type: 0x%x\n", Names
->Use
);
269 AccountNameSize
= Names
->Name
.Length
;
270 if (DomainName
!= NULL
)
272 DomainNameSize
= DomainName
->Length
;
275 ReqRet
= HeapAlloc(scm
->Heap
,
277 sizeof(SIDREQRESULT
) +
278 (((AccountNameSize
+ DomainNameSize
) + 2) * sizeof(WCHAR
)));
281 ReqRet
->RefCount
= 1;
282 ReqRet
->AccountName
= (LPWSTR
)(ReqRet
+ 1);
283 ReqRet
->DomainName
= ReqRet
->AccountName
+ (AccountNameSize
/ sizeof(WCHAR
)) + 1;
285 CopyMemory(ReqRet
->AccountName
,
289 if (DomainName
!= NULL
)
291 CopyMemory(ReqRet
->DomainName
,
296 ReqRet
->AccountName
[AccountNameSize
/ sizeof(WCHAR
)] = L
'\0';
297 ReqRet
->DomainName
[DomainNameSize
/ sizeof(WCHAR
)] = L
'\0';
299 ReqRet
->SidNameUse
= SidNameUse
;
302 if (PolicyAccountDomainInfo
!= NULL
)
304 LsaFreeMemory(PolicyAccountDomainInfo
);
307 LsaFreeMemory(ReferencedDomain
);
308 LsaFreeMemory(Names
);
312 else if (Status
== STATUS_NONE_MAPPED
)
327 FindSidInCache(IN PSIDCACHEMGR scm
,
329 OUT PSIDREQRESULT
*ReqResult
)
331 PSIDCACHEENTRY CacheEntry
;
332 PLIST_ENTRY CurrentEntry
;
333 PSIDREQRESULT ReqRes
;
336 /* NOTE: assumes the lists are locked! */
338 CurrentEntry
= &scm
->CacheListHead
;
339 while (CurrentEntry
!= &scm
->CacheListHead
)
341 CacheEntry
= CONTAINING_RECORD(CurrentEntry
,
346 (PSID
)(CacheEntry
+ 1)))
348 SIZE_T ReqResultSize
;
349 ULONG AccountNameLen
, DomainNameLen
;
353 AccountNameLen
= wcslen(CacheEntry
->AccountName
);
354 DomainNameLen
= wcslen(CacheEntry
->DomainName
);
356 ReqResultSize
= sizeof(SIDREQRESULT
) +
357 (((AccountNameLen
+ 1) +
358 (DomainNameLen
+ 1)) * sizeof(WCHAR
));
360 ReqRes
= HeapAlloc(scm
->Heap
,
365 PWSTR Buffer
= (PWSTR
)(ReqRes
+ 1);
367 ReqRes
->RefCount
= 1;
369 ReqRes
->AccountName
= Buffer
;
370 wcscpy(ReqRes
->AccountName
,
371 CacheEntry
->AccountName
);
372 Buffer
+= AccountNameLen
+ 1;
374 ReqRes
->DomainName
= Buffer
;
375 wcscpy(ReqRes
->DomainName
,
376 CacheEntry
->DomainName
);
379 /* return the result, even if we weren't unable to
380 allocate enough memory! */
385 CurrentEntry
= CurrentEntry
->Flink
;
393 CacheLookupResults(IN PSIDCACHEMGR scm
,
395 IN PSIDREQRESULT ReqResult
)
397 PSIDCACHEENTRY CacheEntry
;
399 SIZE_T AccountNameLen
= 0;
400 SIZE_T DomainNameLen
= 0;
401 SIZE_T CacheEntrySize
= sizeof(SIDCACHEENTRY
);
403 /* NOTE: assumes the lists are locked! */
405 SidLen
= GetLengthSid(pSid
);
406 CacheEntrySize
+= SidLen
;
408 AccountNameLen
= wcslen(ReqResult
->AccountName
);
409 CacheEntrySize
+= (AccountNameLen
+ 1) * sizeof(WCHAR
);
411 DomainNameLen
= wcslen(ReqResult
->DomainName
);
412 CacheEntrySize
+= (wcslen(ReqResult
->DomainName
) + 1) * sizeof(WCHAR
);
414 CacheEntry
= HeapAlloc(scm
->Heap
,
417 if (CacheEntry
!= NULL
)
419 PWSTR lpBuf
= (PWSTR
)((ULONG_PTR
)(CacheEntry
+ 1) + SidLen
);
421 CacheEntry
->SidNameUse
= ReqResult
->SidNameUse
;
425 (PSID
)(CacheEntry
+ 1),
428 /* append the strings */
429 CacheEntry
->AccountName
= lpBuf
;
431 ReqResult
->AccountName
);
432 lpBuf
+= AccountNameLen
+ 1;
434 CacheEntry
->DomainName
= lpBuf
;
436 ReqResult
->DomainName
);
437 lpBuf
+= DomainNameLen
+ 1;
439 /* add the entry to the cache list */
440 InsertTailList(&scm
->CacheListHead
,
441 &CacheEntry
->ListEntry
);
447 LookupThreadProc(IN LPVOID lpParameter
)
450 PSIDCACHEMGR scm
= (PSIDCACHEMGR
)lpParameter
;
452 /* Reference the dll to avoid problems in case of accidental
453 FreeLibrary calls... */
454 if (!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
,
455 (LPCWSTR
)hDllInstance
,
461 while (scm
->RefCount
!= 0)
463 PSIDQUEUEENTRY QueueEntry
= NULL
;
465 EnterCriticalSection(&scm
->Lock
);
467 /* get the first item of the queue */
468 if (scm
->QueueListHead
.Flink
!= &scm
->QueueListHead
)
470 QueueEntry
= CONTAINING_RECORD(scm
->QueueListHead
.Flink
,
473 RemoveEntryList(&QueueEntry
->ListEntry
);
474 QueueEntry
->ListEntry
.Flink
= NULL
;
478 LeaveCriticalSection(&scm
->Lock
);
480 /* wait for the next asynchronous lookup queued */
481 WaitForSingleObject(scm
->LookupEvent
,
486 scm
->QueueLookingUp
= QueueEntry
;
488 LeaveCriticalSection(&scm
->Lock
);
490 if (QueueEntry
!= NULL
)
492 PSIDREQRESULT ReqResult
, FoundReqResult
;
493 PSID pSid
= (PSID
)(QueueEntry
+ 1);
495 /* lookup the SID information */
496 if (!LookupSidInformation(scm
,
503 EnterCriticalSection(&scm
->Lock
);
505 /* see if the SID was added to the cache in the meanwhile */
506 if (!FindSidInCache(scm
,
510 if (ReqResult
!= NULL
)
512 /* cache the results */
513 CacheLookupResults(scm
,
520 if (ReqResult
!= NULL
)
522 /* free the information of our lookup and use the cached
524 DereferenceSidReqResult(scm
,
528 ReqResult
= FoundReqResult
;
531 /* notify the callers unless the lookup was cancelled */
532 if (scm
->QueueLookingUp
!= NULL
)
536 while (scm
->QueueLookingUp
!= NULL
&&
537 i
< QueueEntry
->CallbackCount
)
540 PSIDREQCOMPLETIONPROC CompletionProc
;
542 Context
= QueueEntry
->Callbacks
[i
].Context
;
543 CompletionProc
= QueueEntry
->Callbacks
[i
].CompletionProc
;
545 LeaveCriticalSection(&scm
->Lock
);
547 /* call the completion proc without holding the lock! */
548 CompletionProc(ScmToHandle(scm
),
553 EnterCriticalSection(&scm
->Lock
);
558 scm
->QueueLookingUp
= NULL
;
561 LeaveCriticalSection(&scm
->Lock
);
563 /* free the queue item */
569 CleanupSidCacheMgr(scm
);
577 /* dereference the library and exit */
578 FreeLibraryAndExitThread(hModule
,
588 CreateSidCacheMgr(IN HANDLE Heap
,
589 IN LPCWSTR SystemName
)
593 if (SystemName
== NULL
)
596 scm
= HeapAlloc(Heap
,
598 FIELD_OFFSET(SIDCACHEMGR
,
599 SystemName
[wcslen(SystemName
) + 1]));
602 /* zero the static part of the structure */
604 FIELD_OFFSET(SIDCACHEMGR
,
610 wcscpy(scm
->SystemName
,
613 InitializeCriticalSection(&scm
->Lock
);
614 InitializeListHead(&scm
->QueueListHead
);
615 InitializeListHead(&scm
->CacheListHead
);
617 scm
->LookupEvent
= CreateEvent(NULL
,
621 if (scm
->LookupEvent
== NULL
)
626 if (!OpenLSAPolicyHandle(scm
->SystemName
,
627 POLICY_LOOKUP_NAMES
| POLICY_VIEW_LOCAL_INFORMATION
,
633 scm
->LookupThread
= CreateThread(NULL
,
639 if (scm
->LookupThread
== NULL
)
642 if (scm
->LookupEvent
!= NULL
)
644 CloseHandle(scm
->LookupEvent
);
647 if (scm
->LsaHandle
!= NULL
)
649 LsaClose(scm
->LsaHandle
);
660 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
668 DestroySidCacheMgr(IN HANDLE SidCacheMgr
)
670 PSIDCACHEMGR scm
= HandleToScm(SidCacheMgr
);
674 /* remove the keep-alive reference */
675 DereferenceSidCacheMgr(scm
);
681 QueueSidLookup(IN PSIDCACHEMGR scm
,
683 IN PSIDREQCOMPLETIONPROC CompletionProc
,
686 PLIST_ENTRY CurrentEntry
;
687 PSIDQUEUEENTRY QueueEntry
, FoundEntry
= NULL
;
690 /* NOTE: assumes the lists are locked! */
692 if (scm
->QueueLookingUp
!= NULL
&&
694 (PSID
)(scm
->QueueLookingUp
+ 1)))
696 FoundEntry
= scm
->QueueLookingUp
;
700 CurrentEntry
= &scm
->QueueListHead
;
701 while (CurrentEntry
!= &scm
->QueueListHead
)
703 QueueEntry
= CONTAINING_RECORD(CurrentEntry
,
708 (PSID
)(QueueEntry
+ 1)))
710 FoundEntry
= QueueEntry
;
714 CurrentEntry
= CurrentEntry
->Flink
;
718 if (FoundEntry
== NULL
)
720 DWORD SidLength
= GetLengthSid(pSid
);
722 FoundEntry
= HeapAlloc(scm
->Heap
,
724 sizeof(SIDQUEUEENTRY
) + SidLength
);
725 if (FoundEntry
!= NULL
)
728 (PSID
)(FoundEntry
+ 1),
731 FoundEntry
->CallbackCount
= 1;
732 FoundEntry
->Callbacks
= HeapAlloc(scm
->Heap
,
734 sizeof(SIDCACHECALLBACKINFO
));
736 if (FoundEntry
->Callbacks
!= NULL
)
738 FoundEntry
->Callbacks
[0].CompletionProc
= CompletionProc
;
739 FoundEntry
->Callbacks
[0].Context
= Context
;
741 /* append it to the queue */
742 InsertTailList(&scm
->QueueListHead
,
743 &FoundEntry
->ListEntry
);
745 /* signal the lookup event */
746 SetEvent(scm
->LookupEvent
);
752 /* unable to queue it because we couldn't allocate the callbacks
753 array, free the memory and return */
762 PSIDCACHECALLBACKINFO Sidccb
;
764 /* add the callback */
765 Sidccb
= HeapReAlloc(scm
->Heap
,
767 FoundEntry
->Callbacks
,
768 (FoundEntry
->CallbackCount
+ 1) * sizeof(SIDCACHECALLBACKINFO
));
771 FoundEntry
->Callbacks
= Sidccb
;
772 FoundEntry
->Callbacks
[FoundEntry
->CallbackCount
].CompletionProc
= CompletionProc
;
773 FoundEntry
->Callbacks
[FoundEntry
->CallbackCount
++].Context
= Context
;
784 DequeueSidLookup(IN HANDLE SidCacheMgr
,
787 PLIST_ENTRY CurrentEntry
;
788 PSIDQUEUEENTRY QueueEntry
;
791 scm
= ReferenceSidCacheMgr(SidCacheMgr
);
794 EnterCriticalSection(&scm
->Lock
);
796 if (scm
->QueueLookingUp
!= NULL
&&
798 (PSID
)(scm
->QueueLookingUp
+ 1)))
800 /* don't free the queue lookup item! this will be
801 done in the lookup thread */
802 scm
->QueueLookingUp
= NULL
;
806 CurrentEntry
= &scm
->QueueListHead
;
807 while (CurrentEntry
!= &scm
->QueueListHead
)
809 QueueEntry
= CONTAINING_RECORD(CurrentEntry
,
814 (PSID
)(QueueEntry
+ 1)))
821 CurrentEntry
= CurrentEntry
->Flink
;
825 LeaveCriticalSection(&scm
->Lock
);
827 DereferenceSidCacheMgr(scm
);
833 ReferenceSidReqResult(IN HANDLE SidCacheMgr
,
834 IN PSIDREQRESULT ReqResult
)
838 scm
= ReferenceSidCacheMgr(SidCacheMgr
);
841 InterlockedIncrement(&ReqResult
->RefCount
);
843 DereferenceSidCacheMgr(scm
);
849 DereferenceSidReqResult(IN HANDLE SidCacheMgr
,
850 IN PSIDREQRESULT ReqResult
)
854 scm
= ReferenceSidCacheMgr(SidCacheMgr
);
857 if (InterlockedDecrement(&ReqResult
->RefCount
) == 0)
864 DereferenceSidCacheMgr(scm
);
870 LookupSidCache(IN HANDLE SidCacheMgr
,
872 IN PSIDREQCOMPLETIONPROC CompletionProc
,
876 PSIDREQRESULT ReqResult
= NULL
;
879 scm
= ReferenceSidCacheMgr(SidCacheMgr
);
882 EnterCriticalSection(&scm
->Lock
);
884 /* search the cache */
885 Found
= FindSidInCache(scm
,
891 /* the sid is not in the cache, queue it if not already queued */
892 if (!QueueSidLookup(scm
,
897 PSIDREQRESULT FoundReqResult
= NULL
;
899 /* unable to queue it, look it up now */
901 LeaveCriticalSection(&scm
->Lock
);
903 /* lookup everything we need */
904 if (!LookupSidInformation(scm
,
911 EnterCriticalSection(&scm
->Lock
);
913 /* see if the SID was added to the cache in the meanwhile */
914 if (!FindSidInCache(scm
,
918 if (ReqResult
!= NULL
)
920 /* cache the results */
921 CacheLookupResults(scm
,
928 if (ReqResult
!= NULL
)
930 /* free the information of our lookup and use the cached
932 DereferenceSidReqResult(scm
,
936 ReqResult
= FoundReqResult
;
939 Found
= (ReqResult
!= NULL
);
943 LeaveCriticalSection(&scm
->Lock
);
945 /* call the completion callback */
948 CompletionProc(SidCacheMgr
,
953 if (ReqResult
!= NULL
)
961 DereferenceSidCacheMgr(scm
);