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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 /* $Id: aclui.c 19715 2005-11-28 01:10:49Z weiden $
21 * PROJECT: ReactOS Access Control List Editor
22 * FILE: lib/aclui/sidcache.c
23 * PURPOSE: Access Control List Editor
24 * PROGRAMMER: Thomas Weidenmueller <w3seek@reactos.com>
34 #define HandleToScm(Handle) (PSIDCACHEMGR)(Handle)
35 #define ScmToHandle(Scm) (HANDLE)(Scm)
37 typedef struct _SIDCACHEMGR
41 CRITICAL_SECTION Lock
;
42 LIST_ENTRY QueueListHead
;
43 struct _SIDQUEUEENTRY
*QueueLookingUp
;
44 LIST_ENTRY CacheListHead
;
49 } SIDCACHEMGR
, *PSIDCACHEMGR
;
52 typedef struct _SIDCACHECALLBACKINFO
54 PSIDREQCOMPLETIONPROC CompletionProc
;
56 } SIDCACHECALLBACKINFO
, *PSIDCACHECALLBACKINFO
;
59 typedef struct _SIDQUEUEENTRY
63 PSIDCACHECALLBACKINFO Callbacks
;
64 /* the SID is appended to this structure */
65 } SIDQUEUEENTRY
, *PSIDQUEUEENTRY
;
68 typedef struct _SIDCACHEENTRY
71 SID_NAME_USE SidNameUse
;
74 /* the SID and strings are appended to this structure */
75 } SIDCACHEENTRY
, *PSIDCACHEENTRY
;
79 FreeQueueEntry(IN PSIDCACHEMGR scm
,
80 IN PSIDQUEUEENTRY QueueEntry
)
82 if (QueueEntry
->ListEntry
.Flink
!= NULL
)
84 RemoveEntryList(&QueueEntry
->ListEntry
);
89 QueueEntry
->Callbacks
);
98 FreeCacheEntry(IN PSIDCACHEMGR scm
,
99 IN PSIDCACHEENTRY CacheEntry
)
101 RemoveEntryList(&CacheEntry
->ListEntry
);
110 CleanupSidCacheMgr(IN PSIDCACHEMGR scm
)
112 /* make sure the lookup thread runs down */
113 SetEvent(scm
->LookupEvent
);
114 WaitForSingleObject(scm
->LookupThread
,
118 LsaClose(scm
->LsaHandle
);
119 CloseHandle(scm
->LookupEvent
);
120 CloseHandle(scm
->LookupThread
);
122 /* delete the queue */
123 while (!IsListEmpty(&scm
->QueueListHead
))
125 PSIDQUEUEENTRY QueueEntry
;
127 QueueEntry
= CONTAINING_RECORD(scm
->QueueListHead
.Flink
,
134 /* delete the cache */
135 while (!IsListEmpty(&scm
->CacheListHead
))
137 PSIDCACHEENTRY CacheEntry
;
139 CacheEntry
= CONTAINING_RECORD(scm
->CacheListHead
.Flink
,
146 DeleteCriticalSection(&scm
->Lock
);
151 ReferenceSidCacheMgr(IN HANDLE SidCacheMgr
)
153 PSIDCACHEMGR scm
= HandleToScm(SidCacheMgr
);
155 if (InterlockedIncrement(&scm
->RefCount
) != 1)
165 DereferenceSidCacheMgr(IN PSIDCACHEMGR scm
)
167 if (InterlockedDecrement(&scm
->RefCount
) == 0)
169 CleanupSidCacheMgr(scm
);
179 OpenLSAPolicyHandle(IN LPWSTR SystemName
,
180 IN ACCESS_MASK DesiredAccess
,
181 OUT PLSA_HANDLE PolicyHandle
)
183 LSA_OBJECT_ATTRIBUTES LsaObjectAttributes
= {0};
184 LSA_UNICODE_STRING LsaSystemName
, *psn
;
187 if (SystemName
!= NULL
&& SystemName
[0] != L
'\0')
189 LsaSystemName
.Buffer
= SystemName
;
190 LsaSystemName
.Length
= wcslen(SystemName
) * sizeof(WCHAR
);
191 LsaSystemName
.MaximumLength
= LsaSystemName
.Length
+ sizeof(WCHAR
);
192 psn
= &LsaSystemName
;
199 Status
= LsaOpenPolicy(psn
,
200 &LsaObjectAttributes
,
203 if (!NT_SUCCESS(Status
))
205 SetLastError(LsaNtStatusToWinError(Status
));
214 LookupSidInformation(IN PSIDCACHEMGR scm
,
216 OUT PSIDREQRESULT
*ReqResult
)
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
;
225 DWORD SidLength
, AccountNameSize
, DomainNameSize
= 0;
226 PSIDREQRESULT ReqRet
= NULL
;
229 Status
= LsaLookupSids(scm
->LsaHandle
,
234 if (NT_SUCCESS(Status
))
236 SidLength
= GetLengthSid(pSid
);
237 SidNameUse
= Names
->Use
;
239 if (ReferencedDomain
!= NULL
&&
240 Names
->DomainIndex
>= 0)
242 Domain
= &ReferencedDomain
->Domains
[Names
->DomainIndex
];
243 DomainName
= &Domain
->Name
;
257 /* query the domain name for BUILTIN accounts */
258 Status
= LsaQueryInformationPolicy(scm
->LsaHandle
,
259 PolicyAccountDomainInformation
,
260 (PVOID
*)&PolicyAccountDomainInfo
);
261 if (NT_SUCCESS(Status
))
263 DomainName
= &PolicyAccountDomainInfo
->DomainName
;
265 /* make the user believe this is a group */
266 SidNameUse
= (PolicyAccountDomainInfo
!= NULL
? SidTypeGroup
: SidTypeUser
);
274 DPRINT("Unhandled SID type: 0x%x\n", Names
->Use
);
279 AccountNameSize
= Names
->Name
.Length
;
280 if (DomainName
!= NULL
)
282 DomainNameSize
= DomainName
->Length
;
285 ReqRet
= HeapAlloc(scm
->Heap
,
287 sizeof(SIDREQRESULT
) +
288 (((AccountNameSize
+ DomainNameSize
) + 2) * sizeof(WCHAR
)));
291 ReqRet
->RefCount
= 1;
292 ReqRet
->AccountName
= (LPWSTR
)(ReqRet
+ 1);
293 ReqRet
->DomainName
= ReqRet
->AccountName
+ (AccountNameSize
/ sizeof(WCHAR
)) + 1;
295 CopyMemory(ReqRet
->AccountName
,
299 if (DomainName
!= NULL
)
301 CopyMemory(ReqRet
->DomainName
,
306 ReqRet
->AccountName
[AccountNameSize
/ sizeof(WCHAR
)] = L
'\0';
307 ReqRet
->DomainName
[DomainNameSize
/ sizeof(WCHAR
)] = L
'\0';
309 ReqRet
->SidNameUse
= SidNameUse
;
312 if (PolicyAccountDomainInfo
!= NULL
)
314 LsaFreeMemory(PolicyAccountDomainInfo
);
317 LsaFreeMemory(ReferencedDomain
);
318 LsaFreeMemory(Names
);
322 else if (Status
== STATUS_NONE_MAPPED
)
337 FindSidInCache(IN PSIDCACHEMGR scm
,
339 OUT PSIDREQRESULT
*ReqResult
)
341 PSIDCACHEENTRY CacheEntry
;
342 PLIST_ENTRY CurrentEntry
;
343 PSIDREQRESULT ReqRes
;
346 /* NOTE: assumes the lists are locked! */
348 CurrentEntry
= &scm
->CacheListHead
;
349 while (CurrentEntry
!= &scm
->CacheListHead
)
351 CacheEntry
= CONTAINING_RECORD(CurrentEntry
,
356 (PSID
)(CacheEntry
+ 1)))
358 SIZE_T ReqResultSize
;
359 ULONG AccountNameLen
, DomainNameLen
;
363 AccountNameLen
= wcslen(CacheEntry
->AccountName
);
364 DomainNameLen
= wcslen(CacheEntry
->DomainName
);
366 ReqResultSize
= sizeof(SIDREQRESULT
) +
367 (((AccountNameLen
+ 1) +
368 (DomainNameLen
+ 1)) * sizeof(WCHAR
));
370 ReqRes
= HeapAlloc(scm
->Heap
,
375 PWSTR Buffer
= (PWSTR
)(ReqRes
+ 1);
377 ReqRes
->RefCount
= 1;
379 ReqRes
->AccountName
= Buffer
;
380 wcscpy(ReqRes
->AccountName
,
381 CacheEntry
->AccountName
);
382 Buffer
+= AccountNameLen
+ 1;
384 ReqRes
->DomainName
= Buffer
;
385 wcscpy(ReqRes
->DomainName
,
386 CacheEntry
->DomainName
);
389 /* return the result, even if we weren't unable to
390 allocate enough memory! */
395 CurrentEntry
= CurrentEntry
->Flink
;
403 CacheLookupResults(IN PSIDCACHEMGR scm
,
405 IN PSIDREQRESULT ReqResult
)
407 PSIDCACHEENTRY CacheEntry
;
409 SIZE_T AccountNameLen
= 0;
410 SIZE_T DomainNameLen
= 0;
411 SIZE_T CacheEntrySize
= sizeof(SIDCACHEENTRY
);
413 /* NOTE: assumes the lists are locked! */
415 SidLen
= GetLengthSid(pSid
);
416 CacheEntrySize
+= SidLen
;
418 AccountNameLen
= wcslen(ReqResult
->AccountName
);
419 CacheEntrySize
+= (AccountNameLen
+ 1) * sizeof(WCHAR
);
421 DomainNameLen
= wcslen(ReqResult
->DomainName
);
422 CacheEntrySize
+= (wcslen(ReqResult
->DomainName
) + 1) * sizeof(WCHAR
);
424 CacheEntry
= HeapAlloc(scm
->Heap
,
427 if (CacheEntry
!= NULL
)
429 PWSTR lpBuf
= (PWSTR
)((ULONG_PTR
)(CacheEntry
+ 1) + SidLen
);
431 CacheEntry
->SidNameUse
= ReqResult
->SidNameUse
;
435 (PSID
)(CacheEntry
+ 1),
438 /* append the strings */
439 CacheEntry
->AccountName
= lpBuf
;
441 ReqResult
->AccountName
);
442 lpBuf
+= AccountNameLen
+ 1;
444 CacheEntry
->DomainName
= lpBuf
;
446 ReqResult
->DomainName
);
447 lpBuf
+= DomainNameLen
+ 1;
449 /* add the entry to the cache list */
450 InsertTailList(&scm
->CacheListHead
,
451 &CacheEntry
->ListEntry
);
457 LookupThreadProc(IN LPVOID lpParameter
)
459 PSIDCACHEMGR scm
= (PSIDCACHEMGR
)lpParameter
;
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 */
575 CreateSidCacheMgr(IN HANDLE Heap
,
576 IN LPCWSTR SystemName
)
580 if (SystemName
== NULL
)
583 scm
= HeapAlloc(Heap
,
585 FIELD_OFFSET(SIDCACHEMGR
,
587 ((wcslen(SystemName
) + 1) * sizeof(WCHAR
)));
590 /* zero the static part of the structure */
592 sizeof(SIDCACHEMGR
));
597 wcscpy(scm
->SystemName
,
600 InitializeCriticalSection(&scm
->Lock
);
601 InitializeListHead(&scm
->QueueListHead
);
602 InitializeListHead(&scm
->CacheListHead
);
604 scm
->LookupEvent
= CreateEvent(NULL
,
608 if (scm
->LookupEvent
== NULL
)
613 if (!OpenLSAPolicyHandle(scm
->SystemName
,
614 POLICY_LOOKUP_NAMES
| POLICY_VIEW_LOCAL_INFORMATION
,
620 scm
->LookupThread
= CreateThread(NULL
,
626 if (scm
->LookupThread
== NULL
)
629 if (scm
->LookupEvent
!= NULL
)
631 CloseHandle(scm
->LookupEvent
);
634 if (scm
->LsaHandle
!= NULL
)
636 LsaClose(scm
->LsaHandle
);
647 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
655 DestroySidCacheMgr(IN HANDLE SidCacheMgr
)
657 PSIDCACHEMGR scm
= HandleToScm(SidCacheMgr
);
661 /* remove the keep-alive reference */
662 DereferenceSidCacheMgr(scm
);
668 QueueSidLookup(IN PSIDCACHEMGR scm
,
670 IN PSIDREQCOMPLETIONPROC CompletionProc
,
673 PLIST_ENTRY CurrentEntry
;
674 PSIDQUEUEENTRY QueueEntry
, FoundEntry
= NULL
;
677 /* NOTE: assumes the lists are locked! */
679 if (scm
->QueueLookingUp
!= NULL
&&
681 (PSID
)(scm
->QueueLookingUp
+ 1)))
683 FoundEntry
= scm
->QueueLookingUp
;
687 CurrentEntry
= &scm
->QueueListHead
;
688 while (CurrentEntry
!= &scm
->QueueListHead
)
690 QueueEntry
= CONTAINING_RECORD(CurrentEntry
,
695 (PSID
)(QueueEntry
+ 1)))
697 FoundEntry
= QueueEntry
;
701 CurrentEntry
= CurrentEntry
->Flink
;
705 if (FoundEntry
== NULL
)
707 DWORD SidLength
= GetLengthSid(pSid
);
709 FoundEntry
= HeapAlloc(scm
->Heap
,
711 sizeof(SIDQUEUEENTRY
) + SidLength
);
712 if (FoundEntry
!= NULL
)
715 (PSID
)(FoundEntry
+ 1),
718 FoundEntry
->CallbackCount
= 1;
719 FoundEntry
->Callbacks
= HeapAlloc(scm
->Heap
,
721 sizeof(SIDCACHECALLBACKINFO
));
723 if (FoundEntry
->Callbacks
!= NULL
)
725 FoundEntry
->Callbacks
[0].CompletionProc
= CompletionProc
;
726 FoundEntry
->Callbacks
[0].Context
= Context
;
728 /* append it to the queue */
729 InsertTailList(&scm
->QueueListHead
,
730 &FoundEntry
->ListEntry
);
732 /* signal the lookup event */
733 SetEvent(scm
->LookupEvent
);
739 /* unable to queue it because we couldn't allocate the callbacks
740 array, free the memory and return */
749 PSIDCACHECALLBACKINFO Sidccb
;
751 /* add the callback */
752 Sidccb
= HeapReAlloc(scm
->Heap
,
754 FoundEntry
->Callbacks
,
755 (FoundEntry
->CallbackCount
+ 1) * sizeof(SIDCACHECALLBACKINFO
));
758 FoundEntry
->Callbacks
= Sidccb
;
759 FoundEntry
->Callbacks
[FoundEntry
->CallbackCount
].CompletionProc
= CompletionProc
;
760 FoundEntry
->Callbacks
[FoundEntry
->CallbackCount
++].Context
= Context
;
771 DequeueSidLookup(IN HANDLE SidCacheMgr
,
774 PLIST_ENTRY CurrentEntry
;
775 PSIDQUEUEENTRY QueueEntry
;
778 scm
= ReferenceSidCacheMgr(SidCacheMgr
);
781 EnterCriticalSection(&scm
->Lock
);
783 if (scm
->QueueLookingUp
!= NULL
&&
785 (PSID
)(scm
->QueueLookingUp
+ 1)))
787 /* don't free the queue lookup item! this will be
788 done in the lookup thread */
789 scm
->QueueLookingUp
= NULL
;
793 CurrentEntry
= &scm
->QueueListHead
;
794 while (CurrentEntry
!= &scm
->QueueListHead
)
796 QueueEntry
= CONTAINING_RECORD(CurrentEntry
,
801 (PSID
)(QueueEntry
+ 1)))
808 CurrentEntry
= CurrentEntry
->Flink
;
812 LeaveCriticalSection(&scm
->Lock
);
814 DereferenceSidCacheMgr(scm
);
820 ReferenceSidReqResult(IN HANDLE SidCacheMgr
,
821 IN PSIDREQRESULT ReqResult
)
825 scm
= ReferenceSidCacheMgr(SidCacheMgr
);
828 InterlockedIncrement(&ReqResult
->RefCount
);
830 DereferenceSidCacheMgr(scm
);
836 DereferenceSidReqResult(IN HANDLE SidCacheMgr
,
837 IN PSIDREQRESULT ReqResult
)
841 scm
= ReferenceSidCacheMgr(SidCacheMgr
);
844 if (InterlockedDecrement(&ReqResult
->RefCount
) == 0)
851 DereferenceSidCacheMgr(scm
);
857 LookupSidCache(IN HANDLE SidCacheMgr
,
859 IN PSIDREQCOMPLETIONPROC CompletionProc
,
863 PSIDREQRESULT ReqResult
= NULL
;
866 scm
= ReferenceSidCacheMgr(SidCacheMgr
);
869 EnterCriticalSection(&scm
->Lock
);
871 /* search the cache */
872 Found
= FindSidInCache(scm
,
878 /* the sid is not in the cache, queue it if not already queued */
879 if (!QueueSidLookup(scm
,
884 PSIDREQRESULT FoundReqResult
= NULL
;
886 /* unable to queue it, look it up now */
888 LeaveCriticalSection(&scm
->Lock
);
890 /* lookup everything we need */
891 if (!LookupSidInformation(scm
,
898 EnterCriticalSection(&scm
->Lock
);
900 /* see if the SID was added to the cache in the meanwhile */
901 if (!FindSidInCache(scm
,
905 if (ReqResult
!= NULL
)
907 /* cache the results */
908 CacheLookupResults(scm
,
915 if (ReqResult
!= NULL
)
917 /* free the information of our lookup and use the cached
919 DereferenceSidReqResult(scm
,
923 ReqResult
= FoundReqResult
;
926 Found
= (ReqResult
!= NULL
);
930 LeaveCriticalSection(&scm
->Lock
);
932 /* call the completion callback */
935 CompletionProc(SidCacheMgr
,
940 if (ReqResult
!= NULL
)
948 DereferenceSidCacheMgr(scm
);