diff --git a/dlls/advapi32/advapi32_misc.h b/dlls/advapi32/advapi32_misc.h index 3e58b046465..cb7f7ca3209 100644 --- a/dlls/advapi32/advapi32_misc.h +++ b/dlls/advapi32/advapi32_misc.h @@ -32,4 +32,22 @@ BOOL lookup_local_user_name(const LSA_UNICODE_STRING*, PSID, LPDWORD, LPWSTR, LP WCHAR *SERV_dup(const char *str) DECLSPEC_HIDDEN; NTSTATUS SERV_QueryServiceObjectSecurity(SC_HANDLE, SECURITY_INFORMATION, PSECURITY_DESCRIPTOR, DWORD, LPDWORD) DECLSPEC_HIDDEN; +/* heap allocation helpers */ +static void *heap_alloc( size_t len ) __WINE_ALLOC_SIZE(1); +static inline void *heap_alloc( size_t len ) +{ + return HeapAlloc( GetProcessHeap(), 0, len ); +} + +static void *heap_realloc( void *mem, size_t len ) __WINE_ALLOC_SIZE(2); +static inline void *heap_realloc( void *mem, size_t len ) +{ + return HeapReAlloc( GetProcessHeap(), 0, mem, len ); +} + +static inline BOOL heap_free( void *mem ) +{ + return HeapFree( GetProcessHeap(), 0, mem ); +} + #endif /* __WINE_ADVAPI32MISC_H */ diff --git a/dlls/advapi32/lsa.c b/dlls/advapi32/lsa.c index 3565ee2d56a..45cc48e948b 100644 --- a/dlls/advapi32/lsa.c +++ b/dlls/advapi32/lsa.c @@ -344,6 +344,56 @@ static INT build_domain(PLSA_REFERENCED_DOMAIN_LIST currentList, PLSA_UNICODE_ST return currentList->Entries-1; } +/* Adds domain info to referenced domain list. + Domain list is stored as plain buffer, layout is: + + LSA_REFERENCED_DOMAIN_LIST, + LSA_TRUST_INFORMATION array, + domain data array of + { + domain name data (WCHAR buffer), + SID data + } + + Parameters: + list [I] referenced list pointer + domain [I] domain name string + data [IO] pointer to domain data array +*/ +static LONG lsa_reflist_add_domain(LSA_REFERENCED_DOMAIN_LIST *list, LSA_UNICODE_STRING *domain, char **data) +{ + ULONG sid_size = 0,domain_size = 0; + BOOL handled = FALSE; + SID_NAME_USE use; + LONG i; + + for (i = 0; i < list->Entries; i++) + { + /* try to reuse index */ + if ((list->Domains[i].Name.Length == domain->Length) && + (!strncmpiW(list->Domains[i].Name.Buffer, domain->Buffer, (domain->Length / sizeof(WCHAR))))) + { + return i; + } + } + + /* no matching domain found, store name */ + list->Domains[list->Entries].Name.Length = domain->Length; + list->Domains[list->Entries].Name.MaximumLength = domain->MaximumLength; + list->Domains[list->Entries].Name.Buffer = (WCHAR*)*data; + memcpy(list->Domains[list->Entries].Name.Buffer, domain->Buffer, domain->MaximumLength); + *data += domain->MaximumLength; + + /* get and store SID data */ + list->Domains[list->Entries].Sid = *data; + lookup_name(domain, NULL, &sid_size, NULL, &domain_size, &use, &handled); + domain_size = 0; + lookup_name(domain, list->Domains[list->Entries].Sid, &sid_size, NULL, &domain_size, &use, &handled); + *data += sid_size; + + return list->Entries++; +} + /****************************************************************************** * LsaLookupNames2 [ADVAPI32.@] * @@ -450,70 +500,127 @@ NTSTATUS WINAPI LsaLookupNames2( LSA_HANDLE policy, ULONG flags, ULONG count, * Failure: STATUS_NONE_MAPPED or NTSTATUS code. */ NTSTATUS WINAPI LsaLookupSids( - IN LSA_HANDLE PolicyHandle, - IN ULONG Count, - IN PSID *Sids, - OUT PLSA_REFERENCED_DOMAIN_LIST *ReferencedDomains, - OUT PLSA_TRANSLATED_NAME *Names ) + LSA_HANDLE PolicyHandle, + ULONG Count, + PSID *Sids, + LSA_REFERENCED_DOMAIN_LIST **ReferencedDomains, + LSA_TRANSLATED_NAME **Names) { - ULONG i, mapped, size; + ULONG i, mapped, name_fullsize, domain_fullsize; ULONG name_size, domain_size; + LSA_UNICODE_STRING domain; + WCHAR *name_buffer; + char *domain_data; SID_NAME_USE use; - TRACE("(%p,%u,%p,%p,%p) stub\n", PolicyHandle, Count, Sids, - ReferencedDomains, Names); + TRACE("(%p, %u, %p, %p, %p)\n", PolicyHandle, Count, Sids, ReferencedDomains, Names); - size = sizeof(LSA_TRANSLATED_NAME) * Count; - if (!(*Names = HeapAlloc( GetProcessHeap(), 0, size) )) return STATUS_NO_MEMORY; - if (!(*ReferencedDomains = HeapAlloc( GetProcessHeap(), 0, sizeof(LSA_REFERENCED_DOMAIN_LIST)) )) + /* this length does not include actual string length yet */ + name_fullsize = sizeof(LSA_TRANSLATED_NAME) * Count; + if (!(*Names = heap_alloc(name_fullsize))) return STATUS_NO_MEMORY; + /* maximum count of stored domain infos is Count, allocate it like that cause really needed + count could only be computed after sid data is retrieved */ + domain_fullsize = sizeof(LSA_REFERENCED_DOMAIN_LIST) + sizeof(LSA_TRUST_INFORMATION)*Count; + if (!(*ReferencedDomains = heap_alloc(domain_fullsize))) { - HeapFree( GetProcessHeap(), 0, *Names); + heap_free(*Names); return STATUS_NO_MEMORY; } (*ReferencedDomains)->Entries = 0; - (*ReferencedDomains)->Domains = NULL; + (*ReferencedDomains)->Domains = (LSA_TRUST_INFORMATION*)((char*)*ReferencedDomains + sizeof(LSA_REFERENCED_DOMAIN_LIST)); + + /* Get full names data length and full length needed to store domain name and SID */ + for (i = 0; i < Count; i++) + { + (*Names)[i].Use = SidTypeUnknown; + (*Names)[i].DomainIndex = -1; + (*Names)[i].Name.Buffer = NULL; + + memset(&(*ReferencedDomains)->Domains[i], 0, sizeof(LSA_TRUST_INFORMATION)); + + name_size = domain_size = 0; + if (!LookupAccountSidW(NULL, Sids[i], NULL, &name_size, NULL, &domain_size, &use) && + GetLastError() == ERROR_INSUFFICIENT_BUFFER) + { + if (name_size) + { + (*Names)[i].Name.Length = (name_size - 1) * sizeof(WCHAR); + (*Names)[i].Name.MaximumLength = name_size * sizeof(WCHAR); + name_fullsize += (*Names)[i].Name.MaximumLength; + } + else + { + (*Names)[i].Name.Length = 0; + (*Names)[i].Name.MaximumLength = 0; + } + + /* This potentially allocates more than needed, cause different names will reuse same domain index. + Also it's not possible to store domain name length right here for the same reason. */ + if (domain_size) + { + ULONG sid_size = 0; + BOOL handled = FALSE; + WCHAR *name; + + domain_fullsize += domain_size * sizeof(WCHAR); + + /* get domain SID size too */ + name = heap_alloc(domain_size * sizeof(WCHAR)); + *name = 0; + LookupAccountSidW(NULL, Sids[i], NULL, &name_size, name, &domain_size, &use); + + domain.Buffer = name; + domain.Length = domain_size * sizeof(WCHAR); + domain.MaximumLength = domain_size * sizeof(WCHAR); + + lookup_name(&domain, NULL, &sid_size, NULL, &domain_size, &use, &handled); + domain_fullsize += sid_size; + + heap_free(name); + } + } + } + + /* now we have full length needed for both */ + *Names = heap_realloc(*Names, name_fullsize); + name_buffer = (WCHAR*)((char*)*Names + sizeof(LSA_TRANSLATED_NAME)*Count); + + *ReferencedDomains = heap_realloc(*ReferencedDomains, domain_fullsize); + /* fix pointer after reallocation */ + (*ReferencedDomains)->Domains = (LSA_TRUST_INFORMATION*)((char*)*ReferencedDomains + sizeof(LSA_REFERENCED_DOMAIN_LIST)); + domain_data = (char*)(*ReferencedDomains)->Domains + sizeof(LSA_TRUST_INFORMATION)*Count; mapped = 0; for (i = 0; i < Count; i++) { name_size = domain_size = 0; - (*Names)[i].Use = SidTypeUnknown; - (*Names)[i].DomainIndex = -1; - (*Names)[i].Name.Length = 0; - (*Names)[i].Name.MaximumLength = 0; - (*Names)[i].Name.Buffer = NULL; if (!LookupAccountSidW(NULL, Sids[i], NULL, &name_size, NULL, &domain_size, &use) && GetLastError() == ERROR_INSUFFICIENT_BUFFER) { - LSA_UNICODE_STRING domain; - mapped++; if (domain_size) { domain.Length = (domain_size - 1) * sizeof(WCHAR); - domain.MaximumLength = domain_size*sizeof(WCHAR); - domain.Buffer = HeapAlloc(GetProcessHeap(),0,domain.MaximumLength); - } - else - { - domain.Length = 0; - domain.MaximumLength = 0; - domain.Buffer = NULL; + domain.MaximumLength = domain_size * sizeof(WCHAR); + domain.Buffer = heap_alloc(domain.MaximumLength); } - (*Names)[i].Name.Length = (name_size - 1) * sizeof(WCHAR); - (*Names)[i].Name.MaximumLength = name_size * sizeof(WCHAR); - (*Names)[i].Name.Buffer = HeapAlloc(GetProcessHeap(), 0, name_size * sizeof(WCHAR)); + (*Names)[i].Name.Buffer = name_buffer; LookupAccountSidW(NULL, Sids[i], (*Names)[i].Name.Buffer, &name_size, domain.Buffer, &domain_size, &use); (*Names)[i].Use = use; if (domain_size) - (*Names)[i].DomainIndex = build_domain(*ReferencedDomains, &domain); + { + (*Names)[i].DomainIndex = lsa_reflist_add_domain(*ReferencedDomains, &domain, &domain_data); + heap_free(domain.Buffer); + } } + + name_buffer += name_size; } - TRACE("mapped %u out of %u\n",mapped,Count); + TRACE("mapped %u out of %u\n", mapped, Count); if (mapped == Count) return STATUS_SUCCESS; if (mapped) return STATUS_SOME_NOT_MAPPED; diff --git a/dlls/advapi32/tests/lsa.c b/dlls/advapi32/tests/lsa.c index e5b84c7526e..d03d9d614bc 100644 --- a/dlls/advapi32/tests/lsa.c +++ b/dlls/advapi32/tests/lsa.c @@ -43,6 +43,7 @@ static NTSTATUS (WINAPI *pLsaOpenPolicy)(PLSA_UNICODE_STRING,PLSA_OBJECT_ATTRIBU static NTSTATUS (WINAPI *pLsaQueryInformationPolicy)(LSA_HANDLE,POLICY_INFORMATION_CLASS,PVOID*); static BOOL (WINAPI *pConvertSidToStringSidA)(PSID,LPSTR*); static NTSTATUS (WINAPI *pLsaLookupNames2)(LSA_HANDLE,ULONG,ULONG,PLSA_UNICODE_STRING,PLSA_REFERENCED_DOMAIN_LIST*,PLSA_TRANSLATED_SID2*); +static NTSTATUS (WINAPI *pLsaLookupSids)(LSA_HANDLE,ULONG,PSID*,LSA_REFERENCED_DOMAIN_LIST**,LSA_TRANSLATED_NAME**); static BOOL init(void) { @@ -55,6 +56,7 @@ static BOOL init(void) pLsaQueryInformationPolicy = (void*)GetProcAddress(hadvapi32, "LsaQueryInformationPolicy"); pConvertSidToStringSidA = (void*)GetProcAddress(hadvapi32, "ConvertSidToStringSidA"); pLsaLookupNames2 = (void*)GetProcAddress(hadvapi32, "LsaLookupNames2"); + pLsaLookupSids = (void*)GetProcAddress(hadvapi32, "LsaLookupSids"); if (pLsaClose && pLsaEnumerateAccountRights && pLsaFreeMemory && pLsaOpenPolicy && pLsaQueryInformationPolicy && pConvertSidToStringSidA) return TRUE; @@ -351,6 +353,55 @@ static void test_LsaLookupNames2(void) ok(status == STATUS_SUCCESS, "LsaClose() failed, returned 0x%08x\n", status); } +static void test_LsaLookupSids(void) +{ + LSA_REFERENCED_DOMAIN_LIST *list; + LSA_OBJECT_ATTRIBUTES attrs; + LSA_TRANSLATED_NAME *names; + LSA_HANDLE policy; + TOKEN_USER *user; + NTSTATUS status; + HANDLE token; + DWORD size; + BOOL ret; + + memset(&attrs, 0, sizeof(attrs)); + attrs.Length = sizeof(attrs); + + status = pLsaOpenPolicy(NULL, &attrs, POLICY_LOOKUP_NAMES, &policy); + ok(status == STATUS_SUCCESS, "got 0x%08x\n", status); + + ret = OpenProcessToken(GetCurrentProcess(), MAXIMUM_ALLOWED, &token); + ok(ret, "got %d\n", ret); + + ret = GetTokenInformation(token, TokenUser, NULL, 0, &size); + ok(!ret, "gotr %d\n", ret); + + user = HeapAlloc(GetProcessHeap(), 0, size); + ret = GetTokenInformation(token, TokenUser, user, size, &size); + ok(ret, "got %d\n", ret); + + status = pLsaLookupSids(policy, 1, &user->User.Sid, &list, &names); + ok(status == STATUS_SUCCESS, "got 0x%08x\n", status); + + ok(list->Entries > 0, "got %d\n", list->Entries); + if (list->Entries) + { + ok((char*)list->Domains - (char*)list > 0, "%p, %p\n", list, list->Domains); + ok((char*)list->Domains[0].Sid - (char*)list->Domains > 0, "%p, %p\n", list->Domains, list->Domains[0].Sid); + } + + pLsaFreeMemory(names); + pLsaFreeMemory(list); + + HeapFree(GetProcessHeap(), 0, user); + + CloseHandle(token); + + status = pLsaClose(policy); + ok(status == STATUS_SUCCESS, "got 0x%08x\n", status); +} + START_TEST(lsa) { if (!init()) { @@ -360,4 +411,5 @@ START_TEST(lsa) test_lsa(); test_LsaLookupNames2(); + test_LsaLookupSids(); }