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>
36 #define HandleToScm(Handle) (PSIDCACHEMGR)(Handle)
37 #define ScmToHandle(Scm) (HANDLE)(Scm)
39 typedef struct _SIDCACHEMGR
41 volatile LONG RefCount
;
43 CRITICAL_SECTION Lock
;
44 LIST_ENTRY QueueListHead
;
45 struct _SIDQUEUEENTRY
*QueueLookingUp
;
46 LIST_ENTRY CacheListHead
;
51 } SIDCACHEMGR
, *PSIDCACHEMGR
;
54 typedef struct _SIDCACHECALLBACKINFO
56 PSIDREQCOMPLETIONPROC CompletionProc
;
58 } SIDCACHECALLBACKINFO
, *PSIDCACHECALLBACKINFO
;
61 typedef struct _SIDQUEUEENTRY
65 PSIDCACHECALLBACKINFO Callbacks
;
66 /* the SID is appended to this structure */
67 } SIDQUEUEENTRY
, *PSIDQUEUEENTRY
;
70 typedef struct _SIDCACHEENTRY
73 SID_NAME_USE SidNameUse
;
76 /* the SID and strings are appended to this structure */
77 } SIDCACHEENTRY
, *PSIDCACHEENTRY
;
81 FreeQueueEntry(IN PSIDCACHEMGR scm
,
82 IN PSIDQUEUEENTRY QueueEntry
)
84 if (QueueEntry
->ListEntry
.Flink
!= NULL
)
86 RemoveEntryList(&QueueEntry
->ListEntry
);
91 QueueEntry
->Callbacks
);
100 FreeCacheEntry(IN PSIDCACHEMGR scm
,
101 IN PSIDCACHEENTRY CacheEntry
)
103 RemoveEntryList(&CacheEntry
->ListEntry
);
112 CleanupSidCacheMgr(IN PSIDCACHEMGR scm
)
114 LsaClose(scm
->LsaHandle
);
115 CloseHandle(scm
->LookupEvent
);
116 CloseHandle(scm
->LookupThread
);
118 /* delete the queue */
119 while (!IsListEmpty(&scm
->QueueListHead
))
121 PSIDQUEUEENTRY QueueEntry
;
123 QueueEntry
= CONTAINING_RECORD(scm
->QueueListHead
.Flink
,
130 /* delete the cache */
131 while (!IsListEmpty(&scm
->CacheListHead
))
133 PSIDCACHEENTRY CacheEntry
;
135 CacheEntry
= CONTAINING_RECORD(scm
->CacheListHead
.Flink
,
142 DeleteCriticalSection(&scm
->Lock
);
147 ReferenceSidCacheMgr(IN HANDLE SidCacheMgr
)
149 PSIDCACHEMGR scm
= HandleToScm(SidCacheMgr
);
151 if (InterlockedIncrement(&scm
->RefCount
) != 1)
161 DereferenceSidCacheMgr(IN PSIDCACHEMGR scm
)
163 if (InterlockedDecrement(&scm
->RefCount
) == 0)
165 /* Signal the lookup thread so it can terminate */
166 SetEvent(scm
->LookupEvent
);
172 OpenLSAPolicyHandle(IN LPWSTR SystemName
,
173 IN ACCESS_MASK DesiredAccess
,
174 OUT PLSA_HANDLE PolicyHandle
)
176 LSA_OBJECT_ATTRIBUTES LsaObjectAttributes
= {0};
177 LSA_UNICODE_STRING LsaSystemName
, *psn
;
180 if (SystemName
!= NULL
&& SystemName
[0] != L
'\0')
182 LsaSystemName
.Buffer
= SystemName
;
183 LsaSystemName
.Length
= wcslen(SystemName
) * sizeof(WCHAR
);
184 LsaSystemName
.MaximumLength
= LsaSystemName
.Length
+ sizeof(WCHAR
);
185 psn
= &LsaSystemName
;
192 Status
= LsaOpenPolicy(psn
,
193 &LsaObjectAttributes
,
196 if (!NT_SUCCESS(Status
))
198 SetLastError(LsaNtStatusToWinError(Status
));
207 LookupSidInformation(IN PSIDCACHEMGR scm
,
209 OUT PSIDREQRESULT
*ReqResult
)
211 PLSA_REFERENCED_DOMAIN_LIST ReferencedDomain
;
212 PLSA_TRANSLATED_NAME Names
;
213 PLSA_TRUST_INFORMATION Domain
;
214 PLSA_UNICODE_STRING DomainName
;
215 SID_NAME_USE SidNameUse
= SidTypeUnknown
;
216 PPOLICY_ACCOUNT_DOMAIN_INFO PolicyAccountDomainInfo
= NULL
;
218 DWORD AccountNameSize
, DomainNameSize
= 0;
219 PSIDREQRESULT ReqRet
= NULL
;
222 Status
= LsaLookupSids(scm
->LsaHandle
,
227 if (NT_SUCCESS(Status
))
229 SidNameUse
= Names
->Use
;
231 if (ReferencedDomain
!= NULL
&&
232 Names
->DomainIndex
>= 0)
234 Domain
= &ReferencedDomain
->Domains
[Names
->DomainIndex
];
235 DomainName
= &Domain
->Name
;
249 /* query the domain name for BUILTIN accounts */
250 Status
= LsaQueryInformationPolicy(scm
->LsaHandle
,
251 PolicyAccountDomainInformation
,
252 (PVOID
*)&PolicyAccountDomainInfo
);
253 if (NT_SUCCESS(Status
))
255 DomainName
= &PolicyAccountDomainInfo
->DomainName
;
257 /* make the user believe this is a group */
258 SidNameUse
= (PolicyAccountDomainInfo
!= NULL
? SidTypeGroup
: SidTypeUser
);
266 DPRINT("Unhandled SID type: 0x%x\n", Names
->Use
);
271 AccountNameSize
= Names
->Name
.Length
;
272 if (DomainName
!= NULL
)
274 DomainNameSize
= DomainName
->Length
;
277 ReqRet
= HeapAlloc(scm
->Heap
,
279 sizeof(SIDREQRESULT
) +
280 (((AccountNameSize
+ DomainNameSize
) + 2) * sizeof(WCHAR
)));
283 ReqRet
->RefCount
= 1;
284 ReqRet
->AccountName
= (LPWSTR
)(ReqRet
+ 1);
285 ReqRet
->DomainName
= ReqRet
->AccountName
+ (AccountNameSize
/ sizeof(WCHAR
)) + 1;
287 CopyMemory(ReqRet
->AccountName
,
291 if (DomainName
!= NULL
)
293 CopyMemory(ReqRet
->DomainName
,
298 ReqRet
->AccountName
[AccountNameSize
/ sizeof(WCHAR
)] = L
'\0';
299 ReqRet
->DomainName
[DomainNameSize
/ sizeof(WCHAR
)] = L
'\0';
301 ReqRet
->SidNameUse
= SidNameUse
;
304 if (PolicyAccountDomainInfo
!= NULL
)
306 LsaFreeMemory(PolicyAccountDomainInfo
);
309 LsaFreeMemory(ReferencedDomain
);
310 LsaFreeMemory(Names
);
314 else if (Status
== STATUS_NONE_MAPPED
)
329 FindSidInCache(IN PSIDCACHEMGR scm
,
331 OUT PSIDREQRESULT
*ReqResult
)
333 PSIDCACHEENTRY CacheEntry
;
334 PLIST_ENTRY CurrentEntry
;
335 PSIDREQRESULT ReqRes
;
338 /* NOTE: assumes the lists are locked! */
340 CurrentEntry
= &scm
->CacheListHead
;
341 while (CurrentEntry
!= &scm
->CacheListHead
)
343 CacheEntry
= CONTAINING_RECORD(CurrentEntry
,
348 (PSID
)(CacheEntry
+ 1)))
350 SIZE_T ReqResultSize
;
351 ULONG AccountNameLen
, DomainNameLen
;
355 AccountNameLen
= wcslen(CacheEntry
->AccountName
);
356 DomainNameLen
= wcslen(CacheEntry
->DomainName
);
358 ReqResultSize
= sizeof(SIDREQRESULT
) +
359 (((AccountNameLen
+ 1) +
360 (DomainNameLen
+ 1)) * sizeof(WCHAR
));
362 ReqRes
= HeapAlloc(scm
->Heap
,
367 PWSTR Buffer
= (PWSTR
)(ReqRes
+ 1);
369 ReqRes
->RefCount
= 1;
371 ReqRes
->AccountName
= Buffer
;
372 wcscpy(ReqRes
->AccountName
,
373 CacheEntry
->AccountName
);
374 Buffer
+= AccountNameLen
+ 1;
376 ReqRes
->DomainName
= Buffer
;
377 wcscpy(ReqRes
->DomainName
,
378 CacheEntry
->DomainName
);
381 /* return the result, even if we weren't unable to
382 allocate enough memory! */
387 CurrentEntry
= CurrentEntry
->Flink
;
395 CacheLookupResults(IN PSIDCACHEMGR scm
,
397 IN PSIDREQRESULT ReqResult
)
399 PSIDCACHEENTRY CacheEntry
;
401 SIZE_T AccountNameLen
= 0;
402 SIZE_T DomainNameLen
= 0;
403 SIZE_T CacheEntrySize
= sizeof(SIDCACHEENTRY
);
405 /* NOTE: assumes the lists are locked! */
407 SidLen
= GetLengthSid(pSid
);
408 CacheEntrySize
+= SidLen
;
410 AccountNameLen
= wcslen(ReqResult
->AccountName
);
411 CacheEntrySize
+= (AccountNameLen
+ 1) * sizeof(WCHAR
);
413 DomainNameLen
= wcslen(ReqResult
->DomainName
);
414 CacheEntrySize
+= (wcslen(ReqResult
->DomainName
) + 1) * sizeof(WCHAR
);
416 CacheEntry
= HeapAlloc(scm
->Heap
,
419 if (CacheEntry
!= NULL
)
421 PWSTR lpBuf
= (PWSTR
)((ULONG_PTR
)(CacheEntry
+ 1) + SidLen
);
423 CacheEntry
->SidNameUse
= ReqResult
->SidNameUse
;
427 (PSID
)(CacheEntry
+ 1),
430 /* append the strings */
431 CacheEntry
->AccountName
= lpBuf
;
433 ReqResult
->AccountName
);
434 lpBuf
+= AccountNameLen
+ 1;
436 CacheEntry
->DomainName
= lpBuf
;
438 ReqResult
->DomainName
);
439 lpBuf
+= DomainNameLen
+ 1;
441 /* add the entry to the cache list */
442 InsertTailList(&scm
->CacheListHead
,
443 &CacheEntry
->ListEntry
);
449 LookupThreadProc(IN LPVOID lpParameter
)
452 PSIDCACHEMGR scm
= (PSIDCACHEMGR
)lpParameter
;
454 /* Reference the dll to avoid problems in case of accidental
455 FreeLibrary calls... */
456 if (!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
,
457 (LPCWSTR
)hDllInstance
,
463 while (scm
->RefCount
!= 0)
465 PSIDQUEUEENTRY QueueEntry
= NULL
;
467 EnterCriticalSection(&scm
->Lock
);
469 /* get the first item of the queue */
470 if (scm
->QueueListHead
.Flink
!= &scm
->QueueListHead
)
472 QueueEntry
= CONTAINING_RECORD(scm
->QueueListHead
.Flink
,
475 RemoveEntryList(&QueueEntry
->ListEntry
);
476 QueueEntry
->ListEntry
.Flink
= NULL
;
480 LeaveCriticalSection(&scm
->Lock
);
482 /* wait for the next asynchronous lookup queued */
483 WaitForSingleObject(scm
->LookupEvent
,
488 scm
->QueueLookingUp
= QueueEntry
;
490 LeaveCriticalSection(&scm
->Lock
);
492 if (QueueEntry
!= NULL
)
494 PSIDREQRESULT ReqResult
, FoundReqResult
;
495 PSID pSid
= (PSID
)(QueueEntry
+ 1);
497 /* lookup the SID information */
498 if (!LookupSidInformation(scm
,
505 EnterCriticalSection(&scm
->Lock
);
507 /* see if the SID was added to the cache in the meanwhile */
508 if (!FindSidInCache(scm
,
512 if (ReqResult
!= NULL
)
514 /* cache the results */
515 CacheLookupResults(scm
,
522 if (ReqResult
!= NULL
)
524 /* free the information of our lookup and use the cached
526 DereferenceSidReqResult(scm
,
530 ReqResult
= FoundReqResult
;
533 /* notify the callers unless the lookup was cancelled */
534 if (scm
->QueueLookingUp
!= NULL
)
538 while (scm
->QueueLookingUp
!= NULL
&&
539 i
< QueueEntry
->CallbackCount
)
542 PSIDREQCOMPLETIONPROC CompletionProc
;
544 Context
= QueueEntry
->Callbacks
[i
].Context
;
545 CompletionProc
= QueueEntry
->Callbacks
[i
].CompletionProc
;
547 LeaveCriticalSection(&scm
->Lock
);
549 /* call the completion proc without holding the lock! */
550 CompletionProc(ScmToHandle(scm
),
555 EnterCriticalSection(&scm
->Lock
);
560 scm
->QueueLookingUp
= NULL
;
563 LeaveCriticalSection(&scm
->Lock
);
565 /* free the queue item */
571 CleanupSidCacheMgr(scm
);
579 /* dereference the library and exit */
580 FreeLibraryAndExitThread(hModule
,
590 CreateSidCacheMgr(IN HANDLE Heap
,
591 IN LPCWSTR SystemName
)
595 if (SystemName
== NULL
)
598 scm
= HeapAlloc(Heap
,
600 FIELD_OFFSET(SIDCACHEMGR
,
601 SystemName
[wcslen(SystemName
) + 1]));
604 /* zero the static part of the structure */
606 FIELD_OFFSET(SIDCACHEMGR
,
612 wcscpy(scm
->SystemName
,
615 InitializeCriticalSection(&scm
->Lock
);
616 InitializeListHead(&scm
->QueueListHead
);
617 InitializeListHead(&scm
->CacheListHead
);
619 scm
->LookupEvent
= CreateEvent(NULL
,
623 if (scm
->LookupEvent
== NULL
)
628 if (!OpenLSAPolicyHandle(scm
->SystemName
,
629 POLICY_LOOKUP_NAMES
| POLICY_VIEW_LOCAL_INFORMATION
,
635 scm
->LookupThread
= CreateThread(NULL
,
641 if (scm
->LookupThread
== NULL
)
644 if (scm
->LookupEvent
!= NULL
)
646 CloseHandle(scm
->LookupEvent
);
649 if (scm
->LsaHandle
!= NULL
)
651 LsaClose(scm
->LsaHandle
);
662 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
670 DestroySidCacheMgr(IN HANDLE SidCacheMgr
)
672 PSIDCACHEMGR scm
= HandleToScm(SidCacheMgr
);
676 /* remove the keep-alive reference */
677 DereferenceSidCacheMgr(scm
);
683 QueueSidLookup(IN PSIDCACHEMGR scm
,
685 IN PSIDREQCOMPLETIONPROC CompletionProc
,
688 PLIST_ENTRY CurrentEntry
;
689 PSIDQUEUEENTRY QueueEntry
, FoundEntry
= NULL
;
692 /* NOTE: assumes the lists are locked! */
694 if (scm
->QueueLookingUp
!= NULL
&&
696 (PSID
)(scm
->QueueLookingUp
+ 1)))
698 FoundEntry
= scm
->QueueLookingUp
;
702 CurrentEntry
= &scm
->QueueListHead
;
703 while (CurrentEntry
!= &scm
->QueueListHead
)
705 QueueEntry
= CONTAINING_RECORD(CurrentEntry
,
710 (PSID
)(QueueEntry
+ 1)))
712 FoundEntry
= QueueEntry
;
716 CurrentEntry
= CurrentEntry
->Flink
;
720 if (FoundEntry
== NULL
)
722 DWORD SidLength
= GetLengthSid(pSid
);
724 FoundEntry
= HeapAlloc(scm
->Heap
,
726 sizeof(SIDQUEUEENTRY
) + SidLength
);
727 if (FoundEntry
!= NULL
)
730 (PSID
)(FoundEntry
+ 1),
733 FoundEntry
->CallbackCount
= 1;
734 FoundEntry
->Callbacks
= HeapAlloc(scm
->Heap
,
736 sizeof(SIDCACHECALLBACKINFO
));
738 if (FoundEntry
->Callbacks
!= NULL
)
740 FoundEntry
->Callbacks
[0].CompletionProc
= CompletionProc
;
741 FoundEntry
->Callbacks
[0].Context
= Context
;
743 /* append it to the queue */
744 InsertTailList(&scm
->QueueListHead
,
745 &FoundEntry
->ListEntry
);
747 /* signal the lookup event */
748 SetEvent(scm
->LookupEvent
);
754 /* unable to queue it because we couldn't allocate the callbacks
755 array, free the memory and return */
764 PSIDCACHECALLBACKINFO Sidccb
;
766 /* add the callback */
767 Sidccb
= HeapReAlloc(scm
->Heap
,
769 FoundEntry
->Callbacks
,
770 (FoundEntry
->CallbackCount
+ 1) * sizeof(SIDCACHECALLBACKINFO
));
773 FoundEntry
->Callbacks
= Sidccb
;
774 FoundEntry
->Callbacks
[FoundEntry
->CallbackCount
].CompletionProc
= CompletionProc
;
775 FoundEntry
->Callbacks
[FoundEntry
->CallbackCount
++].Context
= Context
;
786 DequeueSidLookup(IN HANDLE SidCacheMgr
,
789 PLIST_ENTRY CurrentEntry
;
790 PSIDQUEUEENTRY QueueEntry
;
793 scm
= ReferenceSidCacheMgr(SidCacheMgr
);
796 EnterCriticalSection(&scm
->Lock
);
798 if (scm
->QueueLookingUp
!= NULL
&&
800 (PSID
)(scm
->QueueLookingUp
+ 1)))
802 /* don't free the queue lookup item! this will be
803 done in the lookup thread */
804 scm
->QueueLookingUp
= NULL
;
808 CurrentEntry
= &scm
->QueueListHead
;
809 while (CurrentEntry
!= &scm
->QueueListHead
)
811 QueueEntry
= CONTAINING_RECORD(CurrentEntry
,
816 (PSID
)(QueueEntry
+ 1)))
823 CurrentEntry
= CurrentEntry
->Flink
;
827 LeaveCriticalSection(&scm
->Lock
);
829 DereferenceSidCacheMgr(scm
);
835 ReferenceSidReqResult(IN HANDLE SidCacheMgr
,
836 IN PSIDREQRESULT ReqResult
)
840 scm
= ReferenceSidCacheMgr(SidCacheMgr
);
843 InterlockedIncrement(&ReqResult
->RefCount
);
845 DereferenceSidCacheMgr(scm
);
851 DereferenceSidReqResult(IN HANDLE SidCacheMgr
,
852 IN PSIDREQRESULT ReqResult
)
856 scm
= ReferenceSidCacheMgr(SidCacheMgr
);
859 if (InterlockedDecrement(&ReqResult
->RefCount
) == 0)
866 DereferenceSidCacheMgr(scm
);
872 LookupSidCache(IN HANDLE SidCacheMgr
,
874 IN PSIDREQCOMPLETIONPROC CompletionProc
,
878 PSIDREQRESULT ReqResult
= NULL
;
881 scm
= ReferenceSidCacheMgr(SidCacheMgr
);
884 EnterCriticalSection(&scm
->Lock
);
886 /* search the cache */
887 Found
= FindSidInCache(scm
,
893 /* the sid is not in the cache, queue it if not already queued */
894 if (!QueueSidLookup(scm
,
899 PSIDREQRESULT FoundReqResult
= NULL
;
901 /* unable to queue it, look it up now */
903 LeaveCriticalSection(&scm
->Lock
);
905 /* lookup everything we need */
906 if (!LookupSidInformation(scm
,
913 EnterCriticalSection(&scm
->Lock
);
915 /* see if the SID was added to the cache in the meanwhile */
916 if (!FindSidInCache(scm
,
920 if (ReqResult
!= NULL
)
922 /* cache the results */
923 CacheLookupResults(scm
,
930 if (ReqResult
!= NULL
)
932 /* free the information of our lookup and use the cached
934 DereferenceSidReqResult(scm
,
938 ReqResult
= FoundReqResult
;
941 Found
= (ReqResult
!= NULL
);
945 LeaveCriticalSection(&scm
->Lock
);
947 /* call the completion callback */
950 CompletionProc(SidCacheMgr
,
955 if (ReqResult
!= NULL
)
963 DereferenceSidCacheMgr(scm
);