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
39 volatile LONG RefCount
;
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 LsaClose(scm
->LsaHandle
);
113 CloseHandle(scm
->LookupEvent
);
114 CloseHandle(scm
->LookupThread
);
116 /* delete the queue */
117 while (!IsListEmpty(&scm
->QueueListHead
))
119 PSIDQUEUEENTRY QueueEntry
;
121 QueueEntry
= CONTAINING_RECORD(scm
->QueueListHead
.Flink
,
128 /* delete the cache */
129 while (!IsListEmpty(&scm
->CacheListHead
))
131 PSIDCACHEENTRY CacheEntry
;
133 CacheEntry
= CONTAINING_RECORD(scm
->CacheListHead
.Flink
,
140 DeleteCriticalSection(&scm
->Lock
);
145 ReferenceSidCacheMgr(IN HANDLE SidCacheMgr
)
147 PSIDCACHEMGR scm
= HandleToScm(SidCacheMgr
);
149 if (InterlockedIncrement(&scm
->RefCount
) != 1)
159 DereferenceSidCacheMgr(IN PSIDCACHEMGR scm
)
161 if (InterlockedDecrement(&scm
->RefCount
) == 0)
163 /* Signal the lookup thread so it can terminate */
164 SetEvent(scm
->LookupEvent
);
170 OpenLSAPolicyHandle(IN LPWSTR SystemName
,
171 IN ACCESS_MASK DesiredAccess
,
172 OUT PLSA_HANDLE PolicyHandle
)
174 LSA_OBJECT_ATTRIBUTES LsaObjectAttributes
= {0};
175 LSA_UNICODE_STRING LsaSystemName
, *psn
;
178 if (SystemName
!= NULL
&& SystemName
[0] != L
'\0')
180 LsaSystemName
.Buffer
= SystemName
;
181 LsaSystemName
.Length
= (USHORT
)(wcslen(SystemName
) * sizeof(WCHAR
)); /* FIXME: possible overflow */
182 LsaSystemName
.MaximumLength
= LsaSystemName
.Length
+ sizeof(WCHAR
);
183 psn
= &LsaSystemName
;
190 Status
= LsaOpenPolicy(psn
,
191 &LsaObjectAttributes
,
194 if (!NT_SUCCESS(Status
))
196 SetLastError(LsaNtStatusToWinError(Status
));
205 LookupSidInformation(IN PSIDCACHEMGR scm
,
207 OUT PSIDREQRESULT
*ReqResult
)
209 PLSA_REFERENCED_DOMAIN_LIST ReferencedDomain
;
210 PLSA_TRANSLATED_NAME Names
;
211 PLSA_TRUST_INFORMATION Domain
;
212 PLSA_UNICODE_STRING DomainName
;
213 SID_NAME_USE SidNameUse
= SidTypeUnknown
;
214 PPOLICY_ACCOUNT_DOMAIN_INFO PolicyAccountDomainInfo
= NULL
;
216 DWORD SidLength
, AccountNameSize
, DomainNameSize
= 0;
217 PSIDREQRESULT ReqRet
= NULL
;
220 Status
= LsaLookupSids(scm
->LsaHandle
,
225 if (NT_SUCCESS(Status
))
227 SidLength
= GetLengthSid(pSid
);
228 SidNameUse
= Names
->Use
;
230 if (ReferencedDomain
!= NULL
&&
231 Names
->DomainIndex
>= 0)
233 Domain
= &ReferencedDomain
->Domains
[Names
->DomainIndex
];
234 DomainName
= &Domain
->Name
;
248 /* query the domain name for BUILTIN accounts */
249 Status
= LsaQueryInformationPolicy(scm
->LsaHandle
,
250 PolicyAccountDomainInformation
,
251 (PVOID
*)&PolicyAccountDomainInfo
);
252 if (NT_SUCCESS(Status
))
254 DomainName
= &PolicyAccountDomainInfo
->DomainName
;
256 /* make the user believe this is a group */
257 SidNameUse
= (PolicyAccountDomainInfo
!= NULL
? SidTypeGroup
: SidTypeUser
);
265 DPRINT("Unhandled SID type: 0x%x\n", Names
->Use
);
270 AccountNameSize
= Names
->Name
.Length
;
271 if (DomainName
!= NULL
)
273 DomainNameSize
= DomainName
->Length
;
276 ReqRet
= HeapAlloc(scm
->Heap
,
278 sizeof(SIDREQRESULT
) +
279 (((AccountNameSize
+ DomainNameSize
) + 2) * sizeof(WCHAR
)));
282 ReqRet
->RefCount
= 1;
283 ReqRet
->AccountName
= (LPWSTR
)(ReqRet
+ 1);
284 ReqRet
->DomainName
= ReqRet
->AccountName
+ (AccountNameSize
/ sizeof(WCHAR
)) + 1;
286 CopyMemory(ReqRet
->AccountName
,
290 if (DomainName
!= NULL
)
292 CopyMemory(ReqRet
->DomainName
,
297 ReqRet
->AccountName
[AccountNameSize
/ sizeof(WCHAR
)] = L
'\0';
298 ReqRet
->DomainName
[DomainNameSize
/ sizeof(WCHAR
)] = L
'\0';
300 ReqRet
->SidNameUse
= SidNameUse
;
303 if (PolicyAccountDomainInfo
!= NULL
)
305 LsaFreeMemory(PolicyAccountDomainInfo
);
308 LsaFreeMemory(ReferencedDomain
);
309 LsaFreeMemory(Names
);
313 else if (Status
== STATUS_NONE_MAPPED
)
328 FindSidInCache(IN PSIDCACHEMGR scm
,
330 OUT PSIDREQRESULT
*ReqResult
)
332 PSIDCACHEENTRY CacheEntry
;
333 PLIST_ENTRY CurrentEntry
;
334 PSIDREQRESULT ReqRes
;
337 /* NOTE: assumes the lists are locked! */
339 CurrentEntry
= &scm
->CacheListHead
;
340 while (CurrentEntry
!= &scm
->CacheListHead
)
342 CacheEntry
= CONTAINING_RECORD(CurrentEntry
,
347 (PSID
)(CacheEntry
+ 1)))
349 SIZE_T ReqResultSize
;
350 ULONG AccountNameLen
, DomainNameLen
;
354 AccountNameLen
= wcslen(CacheEntry
->AccountName
);
355 DomainNameLen
= wcslen(CacheEntry
->DomainName
);
357 ReqResultSize
= sizeof(SIDREQRESULT
) +
358 (((AccountNameLen
+ 1) +
359 (DomainNameLen
+ 1)) * sizeof(WCHAR
));
361 ReqRes
= HeapAlloc(scm
->Heap
,
366 PWSTR Buffer
= (PWSTR
)(ReqRes
+ 1);
368 ReqRes
->RefCount
= 1;
370 ReqRes
->AccountName
= Buffer
;
371 wcscpy(ReqRes
->AccountName
,
372 CacheEntry
->AccountName
);
373 Buffer
+= AccountNameLen
+ 1;
375 ReqRes
->DomainName
= Buffer
;
376 wcscpy(ReqRes
->DomainName
,
377 CacheEntry
->DomainName
);
380 /* return the result, even if we weren't unable to
381 allocate enough memory! */
386 CurrentEntry
= CurrentEntry
->Flink
;
394 CacheLookupResults(IN PSIDCACHEMGR scm
,
396 IN PSIDREQRESULT ReqResult
)
398 PSIDCACHEENTRY CacheEntry
;
400 SIZE_T AccountNameLen
= 0;
401 SIZE_T DomainNameLen
= 0;
402 SIZE_T CacheEntrySize
= sizeof(SIDCACHEENTRY
);
404 /* NOTE: assumes the lists are locked! */
406 SidLen
= GetLengthSid(pSid
);
407 CacheEntrySize
+= SidLen
;
409 AccountNameLen
= wcslen(ReqResult
->AccountName
);
410 CacheEntrySize
+= (AccountNameLen
+ 1) * sizeof(WCHAR
);
412 DomainNameLen
= wcslen(ReqResult
->DomainName
);
413 CacheEntrySize
+= (wcslen(ReqResult
->DomainName
) + 1) * sizeof(WCHAR
);
415 CacheEntry
= HeapAlloc(scm
->Heap
,
418 if (CacheEntry
!= NULL
)
420 PWSTR lpBuf
= (PWSTR
)((ULONG_PTR
)(CacheEntry
+ 1) + SidLen
);
422 CacheEntry
->SidNameUse
= ReqResult
->SidNameUse
;
426 (PSID
)(CacheEntry
+ 1),
429 /* append the strings */
430 CacheEntry
->AccountName
= lpBuf
;
432 ReqResult
->AccountName
);
433 lpBuf
+= AccountNameLen
+ 1;
435 CacheEntry
->DomainName
= lpBuf
;
437 ReqResult
->DomainName
);
438 lpBuf
+= DomainNameLen
+ 1;
440 /* add the entry to the cache list */
441 InsertTailList(&scm
->CacheListHead
,
442 &CacheEntry
->ListEntry
);
448 LookupThreadProc(IN LPVOID lpParameter
)
451 PSIDCACHEMGR scm
= (PSIDCACHEMGR
)lpParameter
;
453 /* Reference the dll to avoid problems in case of accidental
454 FreeLibrary calls... */
455 if (!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
,
456 (LPCWSTR
)hDllInstance
,
462 while (scm
->RefCount
!= 0)
464 PSIDQUEUEENTRY QueueEntry
= NULL
;
466 EnterCriticalSection(&scm
->Lock
);
468 /* get the first item of the queue */
469 if (scm
->QueueListHead
.Flink
!= &scm
->QueueListHead
)
471 QueueEntry
= CONTAINING_RECORD(scm
->QueueListHead
.Flink
,
474 RemoveEntryList(&QueueEntry
->ListEntry
);
475 QueueEntry
->ListEntry
.Flink
= NULL
;
479 LeaveCriticalSection(&scm
->Lock
);
481 /* wait for the next asynchronous lookup queued */
482 WaitForSingleObject(scm
->LookupEvent
,
487 scm
->QueueLookingUp
= QueueEntry
;
489 LeaveCriticalSection(&scm
->Lock
);
491 if (QueueEntry
!= NULL
)
493 PSIDREQRESULT ReqResult
, FoundReqResult
;
494 PSID pSid
= (PSID
)(QueueEntry
+ 1);
496 /* lookup the SID information */
497 if (!LookupSidInformation(scm
,
504 EnterCriticalSection(&scm
->Lock
);
506 /* see if the SID was added to the cache in the meanwhile */
507 if (!FindSidInCache(scm
,
511 if (ReqResult
!= NULL
)
513 /* cache the results */
514 CacheLookupResults(scm
,
521 if (ReqResult
!= NULL
)
523 /* free the information of our lookup and use the cached
525 DereferenceSidReqResult(scm
,
529 ReqResult
= FoundReqResult
;
532 /* notify the callers unless the lookup was cancelled */
533 if (scm
->QueueLookingUp
!= NULL
)
537 while (scm
->QueueLookingUp
!= NULL
&&
538 i
< QueueEntry
->CallbackCount
)
541 PSIDREQCOMPLETIONPROC CompletionProc
;
543 Context
= QueueEntry
->Callbacks
[i
].Context
;
544 CompletionProc
= QueueEntry
->Callbacks
[i
].CompletionProc
;
546 LeaveCriticalSection(&scm
->Lock
);
548 /* call the completion proc without holding the lock! */
549 CompletionProc(ScmToHandle(scm
),
554 EnterCriticalSection(&scm
->Lock
);
559 scm
->QueueLookingUp
= NULL
;
562 LeaveCriticalSection(&scm
->Lock
);
564 /* free the queue item */
570 CleanupSidCacheMgr(scm
);
578 /* dereference the library and exit */
579 FreeLibraryAndExitThread(hModule
,
589 CreateSidCacheMgr(IN HANDLE Heap
,
590 IN LPCWSTR SystemName
)
594 if (SystemName
== NULL
)
597 scm
= HeapAlloc(Heap
,
599 FIELD_OFFSET(SIDCACHEMGR
,
600 SystemName
[wcslen(SystemName
) + 1]));
603 /* zero the static part of the structure */
605 FIELD_OFFSET(SIDCACHEMGR
,
611 wcscpy(scm
->SystemName
,
614 InitializeCriticalSection(&scm
->Lock
);
615 InitializeListHead(&scm
->QueueListHead
);
616 InitializeListHead(&scm
->CacheListHead
);
618 scm
->LookupEvent
= CreateEvent(NULL
,
622 if (scm
->LookupEvent
== NULL
)
627 if (!OpenLSAPolicyHandle(scm
->SystemName
,
628 POLICY_LOOKUP_NAMES
| POLICY_VIEW_LOCAL_INFORMATION
,
634 scm
->LookupThread
= CreateThread(NULL
,
640 if (scm
->LookupThread
== NULL
)
643 if (scm
->LookupEvent
!= NULL
)
645 CloseHandle(scm
->LookupEvent
);
648 if (scm
->LsaHandle
!= NULL
)
650 LsaClose(scm
->LsaHandle
);
661 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
669 DestroySidCacheMgr(IN HANDLE SidCacheMgr
)
671 PSIDCACHEMGR scm
= HandleToScm(SidCacheMgr
);
675 /* remove the keep-alive reference */
676 DereferenceSidCacheMgr(scm
);
682 QueueSidLookup(IN PSIDCACHEMGR scm
,
684 IN PSIDREQCOMPLETIONPROC CompletionProc
,
687 PLIST_ENTRY CurrentEntry
;
688 PSIDQUEUEENTRY QueueEntry
, FoundEntry
= NULL
;
691 /* NOTE: assumes the lists are locked! */
693 if (scm
->QueueLookingUp
!= NULL
&&
695 (PSID
)(scm
->QueueLookingUp
+ 1)))
697 FoundEntry
= scm
->QueueLookingUp
;
701 CurrentEntry
= &scm
->QueueListHead
;
702 while (CurrentEntry
!= &scm
->QueueListHead
)
704 QueueEntry
= CONTAINING_RECORD(CurrentEntry
,
709 (PSID
)(QueueEntry
+ 1)))
711 FoundEntry
= QueueEntry
;
715 CurrentEntry
= CurrentEntry
->Flink
;
719 if (FoundEntry
== NULL
)
721 DWORD SidLength
= GetLengthSid(pSid
);
723 FoundEntry
= HeapAlloc(scm
->Heap
,
725 sizeof(SIDQUEUEENTRY
) + SidLength
);
726 if (FoundEntry
!= NULL
)
729 (PSID
)(FoundEntry
+ 1),
732 FoundEntry
->CallbackCount
= 1;
733 FoundEntry
->Callbacks
= HeapAlloc(scm
->Heap
,
735 sizeof(SIDCACHECALLBACKINFO
));
737 if (FoundEntry
->Callbacks
!= NULL
)
739 FoundEntry
->Callbacks
[0].CompletionProc
= CompletionProc
;
740 FoundEntry
->Callbacks
[0].Context
= Context
;
742 /* append it to the queue */
743 InsertTailList(&scm
->QueueListHead
,
744 &FoundEntry
->ListEntry
);
746 /* signal the lookup event */
747 SetEvent(scm
->LookupEvent
);
753 /* unable to queue it because we couldn't allocate the callbacks
754 array, free the memory and return */
763 PSIDCACHECALLBACKINFO Sidccb
;
765 /* add the callback */
766 Sidccb
= HeapReAlloc(scm
->Heap
,
768 FoundEntry
->Callbacks
,
769 (FoundEntry
->CallbackCount
+ 1) * sizeof(SIDCACHECALLBACKINFO
));
772 FoundEntry
->Callbacks
= Sidccb
;
773 FoundEntry
->Callbacks
[FoundEntry
->CallbackCount
].CompletionProc
= CompletionProc
;
774 FoundEntry
->Callbacks
[FoundEntry
->CallbackCount
++].Context
= Context
;
785 DequeueSidLookup(IN HANDLE SidCacheMgr
,
788 PLIST_ENTRY CurrentEntry
;
789 PSIDQUEUEENTRY QueueEntry
;
792 scm
= ReferenceSidCacheMgr(SidCacheMgr
);
795 EnterCriticalSection(&scm
->Lock
);
797 if (scm
->QueueLookingUp
!= NULL
&&
799 (PSID
)(scm
->QueueLookingUp
+ 1)))
801 /* don't free the queue lookup item! this will be
802 done in the lookup thread */
803 scm
->QueueLookingUp
= NULL
;
807 CurrentEntry
= &scm
->QueueListHead
;
808 while (CurrentEntry
!= &scm
->QueueListHead
)
810 QueueEntry
= CONTAINING_RECORD(CurrentEntry
,
815 (PSID
)(QueueEntry
+ 1)))
822 CurrentEntry
= CurrentEntry
->Flink
;
826 LeaveCriticalSection(&scm
->Lock
);
828 DereferenceSidCacheMgr(scm
);
834 ReferenceSidReqResult(IN HANDLE SidCacheMgr
,
835 IN PSIDREQRESULT ReqResult
)
839 scm
= ReferenceSidCacheMgr(SidCacheMgr
);
842 InterlockedIncrement(&ReqResult
->RefCount
);
844 DereferenceSidCacheMgr(scm
);
850 DereferenceSidReqResult(IN HANDLE SidCacheMgr
,
851 IN PSIDREQRESULT ReqResult
)
855 scm
= ReferenceSidCacheMgr(SidCacheMgr
);
858 if (InterlockedDecrement(&ReqResult
->RefCount
) == 0)
865 DereferenceSidCacheMgr(scm
);
871 LookupSidCache(IN HANDLE SidCacheMgr
,
873 IN PSIDREQCOMPLETIONPROC CompletionProc
,
877 PSIDREQRESULT ReqResult
= NULL
;
880 scm
= ReferenceSidCacheMgr(SidCacheMgr
);
883 EnterCriticalSection(&scm
->Lock
);
885 /* search the cache */
886 Found
= FindSidInCache(scm
,
892 /* the sid is not in the cache, queue it if not already queued */
893 if (!QueueSidLookup(scm
,
898 PSIDREQRESULT FoundReqResult
= NULL
;
900 /* unable to queue it, look it up now */
902 LeaveCriticalSection(&scm
->Lock
);
904 /* lookup everything we need */
905 if (!LookupSidInformation(scm
,
912 EnterCriticalSection(&scm
->Lock
);
914 /* see if the SID was added to the cache in the meanwhile */
915 if (!FindSidInCache(scm
,
919 if (ReqResult
!= NULL
)
921 /* cache the results */
922 CacheLookupResults(scm
,
929 if (ReqResult
!= NULL
)
931 /* free the information of our lookup and use the cached
933 DereferenceSidReqResult(scm
,
937 ReqResult
= FoundReqResult
;
940 Found
= (ReqResult
!= NULL
);
944 LeaveCriticalSection(&scm
->Lock
);
946 /* call the completion callback */
949 CompletionProc(SidCacheMgr
,
954 if (ReqResult
!= NULL
)
962 DereferenceSidCacheMgr(scm
);