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
,
586 SystemName
[wcslen(SystemName
) + 1]));
589 /* zero the static part of the structure */
591 sizeof(SIDCACHEMGR
));
596 wcscpy(scm
->SystemName
,
599 InitializeCriticalSection(&scm
->Lock
);
600 InitializeListHead(&scm
->QueueListHead
);
601 InitializeListHead(&scm
->CacheListHead
);
603 scm
->LookupEvent
= CreateEvent(NULL
,
607 if (scm
->LookupEvent
== NULL
)
612 if (!OpenLSAPolicyHandle(scm
->SystemName
,
613 POLICY_LOOKUP_NAMES
| POLICY_VIEW_LOCAL_INFORMATION
,
619 scm
->LookupThread
= CreateThread(NULL
,
625 if (scm
->LookupThread
== NULL
)
628 if (scm
->LookupEvent
!= NULL
)
630 CloseHandle(scm
->LookupEvent
);
633 if (scm
->LsaHandle
!= NULL
)
635 LsaClose(scm
->LsaHandle
);
646 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
654 DestroySidCacheMgr(IN HANDLE SidCacheMgr
)
656 PSIDCACHEMGR scm
= HandleToScm(SidCacheMgr
);
660 /* remove the keep-alive reference */
661 DereferenceSidCacheMgr(scm
);
667 QueueSidLookup(IN PSIDCACHEMGR scm
,
669 IN PSIDREQCOMPLETIONPROC CompletionProc
,
672 PLIST_ENTRY CurrentEntry
;
673 PSIDQUEUEENTRY QueueEntry
, FoundEntry
= NULL
;
676 /* NOTE: assumes the lists are locked! */
678 if (scm
->QueueLookingUp
!= NULL
&&
680 (PSID
)(scm
->QueueLookingUp
+ 1)))
682 FoundEntry
= scm
->QueueLookingUp
;
686 CurrentEntry
= &scm
->QueueListHead
;
687 while (CurrentEntry
!= &scm
->QueueListHead
)
689 QueueEntry
= CONTAINING_RECORD(CurrentEntry
,
694 (PSID
)(QueueEntry
+ 1)))
696 FoundEntry
= QueueEntry
;
700 CurrentEntry
= CurrentEntry
->Flink
;
704 if (FoundEntry
== NULL
)
706 DWORD SidLength
= GetLengthSid(pSid
);
708 FoundEntry
= HeapAlloc(scm
->Heap
,
710 sizeof(SIDQUEUEENTRY
) + SidLength
);
711 if (FoundEntry
!= NULL
)
714 (PSID
)(FoundEntry
+ 1),
717 FoundEntry
->CallbackCount
= 1;
718 FoundEntry
->Callbacks
= HeapAlloc(scm
->Heap
,
720 sizeof(SIDCACHECALLBACKINFO
));
722 if (FoundEntry
->Callbacks
!= NULL
)
724 FoundEntry
->Callbacks
[0].CompletionProc
= CompletionProc
;
725 FoundEntry
->Callbacks
[0].Context
= Context
;
727 /* append it to the queue */
728 InsertTailList(&scm
->QueueListHead
,
729 &FoundEntry
->ListEntry
);
731 /* signal the lookup event */
732 SetEvent(scm
->LookupEvent
);
738 /* unable to queue it because we couldn't allocate the callbacks
739 array, free the memory and return */
748 PSIDCACHECALLBACKINFO Sidccb
;
750 /* add the callback */
751 Sidccb
= HeapReAlloc(scm
->Heap
,
753 FoundEntry
->Callbacks
,
754 (FoundEntry
->CallbackCount
+ 1) * sizeof(SIDCACHECALLBACKINFO
));
757 FoundEntry
->Callbacks
= Sidccb
;
758 FoundEntry
->Callbacks
[FoundEntry
->CallbackCount
].CompletionProc
= CompletionProc
;
759 FoundEntry
->Callbacks
[FoundEntry
->CallbackCount
++].Context
= Context
;
770 DequeueSidLookup(IN HANDLE SidCacheMgr
,
773 PLIST_ENTRY CurrentEntry
;
774 PSIDQUEUEENTRY QueueEntry
;
777 scm
= ReferenceSidCacheMgr(SidCacheMgr
);
780 EnterCriticalSection(&scm
->Lock
);
782 if (scm
->QueueLookingUp
!= NULL
&&
784 (PSID
)(scm
->QueueLookingUp
+ 1)))
786 /* don't free the queue lookup item! this will be
787 done in the lookup thread */
788 scm
->QueueLookingUp
= NULL
;
792 CurrentEntry
= &scm
->QueueListHead
;
793 while (CurrentEntry
!= &scm
->QueueListHead
)
795 QueueEntry
= CONTAINING_RECORD(CurrentEntry
,
800 (PSID
)(QueueEntry
+ 1)))
807 CurrentEntry
= CurrentEntry
->Flink
;
811 LeaveCriticalSection(&scm
->Lock
);
813 DereferenceSidCacheMgr(scm
);
819 ReferenceSidReqResult(IN HANDLE SidCacheMgr
,
820 IN PSIDREQRESULT ReqResult
)
824 scm
= ReferenceSidCacheMgr(SidCacheMgr
);
827 InterlockedIncrement(&ReqResult
->RefCount
);
829 DereferenceSidCacheMgr(scm
);
835 DereferenceSidReqResult(IN HANDLE SidCacheMgr
,
836 IN PSIDREQRESULT ReqResult
)
840 scm
= ReferenceSidCacheMgr(SidCacheMgr
);
843 if (InterlockedDecrement(&ReqResult
->RefCount
) == 0)
850 DereferenceSidCacheMgr(scm
);
856 LookupSidCache(IN HANDLE SidCacheMgr
,
858 IN PSIDREQCOMPLETIONPROC CompletionProc
,
862 PSIDREQRESULT ReqResult
= NULL
;
865 scm
= ReferenceSidCacheMgr(SidCacheMgr
);
868 EnterCriticalSection(&scm
->Lock
);
870 /* search the cache */
871 Found
= FindSidInCache(scm
,
877 /* the sid is not in the cache, queue it if not already queued */
878 if (!QueueSidLookup(scm
,
883 PSIDREQRESULT FoundReqResult
= NULL
;
885 /* unable to queue it, look it up now */
887 LeaveCriticalSection(&scm
->Lock
);
889 /* lookup everything we need */
890 if (!LookupSidInformation(scm
,
897 EnterCriticalSection(&scm
->Lock
);
899 /* see if the SID was added to the cache in the meanwhile */
900 if (!FindSidInCache(scm
,
904 if (ReqResult
!= NULL
)
906 /* cache the results */
907 CacheLookupResults(scm
,
914 if (ReqResult
!= NULL
)
916 /* free the information of our lookup and use the cached
918 DereferenceSidReqResult(scm
,
922 ReqResult
= FoundReqResult
;
925 Found
= (ReqResult
!= NULL
);
929 LeaveCriticalSection(&scm
->Lock
);
931 /* call the completion callback */
934 CompletionProc(SidCacheMgr
,
939 if (ReqResult
!= NULL
)
947 DereferenceSidCacheMgr(scm
);