windows-nt/Source/XPSP1/NT/shell/osshell/security/aclui/sidcache.cpp

1904 lines
55 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1997 - 1999
//
// File: sidcache.cpp
//
// This file contains the implementation of a SID/Name cache.
//
//--------------------------------------------------------------------------
#include "aclpriv.h"
#if(_WIN32_WINNT >= 0x0500)
#include <dsgetdc.h> // DsGetDcName
#include <iads.h>
#endif
#define SECURITY_WIN32
#include <security.h> // TranslateName
#include <lm.h> // NetApiBufferFree
#include <shlwapi.h> // StrChr, StrRChr
// 10 minutes
#define SID_CACHE_AGE_LIMIT (10*60*1000)
TCHAR const c_szNTProvider[] = TEXT("WinNT://");
#define NTPROV_LEN (ARRAYSIZE(c_szNTProvider)-1)
#define ACLUI_ALIGNED_SID_LENGTH(p) ((PtrAlignSize(RtlLengthSid((p)))))
PSIDCACHE g_pSidCache = NULL;
PSIDCACHE GetSidCache()
{
if (NULL == g_pSidCache)
{
// The cache starts with an extra ref here that will be released
// during our DLL_PROCESS_DETACH
g_pSidCache = new CSidCache;
if (g_pSidCache)
{
g_pSidCache->AddRef();
}
}
else
{
g_pSidCache->AddRef();
}
return g_pSidCache;
}
void FreeSidCache()
{
if (g_pSidCache)
{
g_pSidCache->Release();
g_pSidCache = NULL;
}
}
//
// CSidCache implementation
//
CSidCache::CSidCache()
: m_pszCachedServer(NULL), m_pszCachedDomain(NULL),
m_hInitThread(NULL), m_pszLastDc(NULL), m_pszLastDomain(NULL),
m_cRef(1)
{
HINSTANCE hInstThisDll;
DWORD dwThreadID;
ZeroMemory(m_dpaSidHashTable, SIZEOF(m_dpaSidHashTable));
ExceptionPropagatingInitializeCriticalSection(&m_csHashTableLock);
ExceptionPropagatingInitializeCriticalSection(&m_csDomainNameLock);
ExceptionPropagatingInitializeCriticalSection(&m_csDcNameLock);
// Give the thread we are about to create a ref to the dll,
// so that the dll will remain for the lifetime of the thread
hInstThisDll = LoadLibrary(c_szDllName);
if (hInstThisDll != NULL)
{
// also do an AddRef() for the worker thread to release later
AddRef();
// Start a thread to cache the well-known and built-in SIDs
m_hInitThread = CreateThread(NULL, 0, InitThread, this, 0, &dwThreadID);
if (!m_hInitThread)
{
// Failed to create the thread, do cleanup
FreeLibrary(hInstThisDll);
Release();
}
}
}
ULONG
CSidCache::AddRef()
{
return InterlockedIncrement(&m_cRef);
}
ULONG
CSidCache::Release()
{
if (InterlockedDecrement(&m_cRef) == 0)
{
delete this;
return 0;
}
return m_cRef;
}
CSidCache::~CSidCache()
{
int i;
TraceEnter(TRACE_SIDCACHE, "CSidCache::~CSidCache");
Lock();
for (i = 0; i < BUCKET_COUNT; i++)
{
DestroyDPA(m_dpaSidHashTable[i]);
m_dpaSidHashTable[i] = NULL;
}
Unlock();
LockDomain();
LocalFreeString(&m_pszCachedServer);
LocalFreeString(&m_pszCachedDomain);
UnlockDomain();
LockDc();
LocalFreeString(&m_pszLastDc);
LocalFreeString(&m_pszLastDomain);
UnlockDc();
DeleteCriticalSection(&m_csHashTableLock);
DeleteCriticalSection(&m_csDomainNameLock);
DeleteCriticalSection(&m_csDcNameLock);
if (m_hInitThread != NULL)
{
CloseHandle(m_hInitThread);
}
TraceLeaveVoid();
}
BOOL
CSidCache::LookupSids(HDPA hSids,
LPCTSTR pszServer,
LPSECURITYINFO2 psi2,
PUSER_LIST *ppUserList)
{
BOOL fResult = FALSE;
TraceEnter(TRACE_SIDCACHE, "CSidCache::LookupSids");
TraceAssert(hSids != NULL);
if (NULL == hSids)
{
SetLastError(ERROR_INVALID_PARAMETER);
TraceLeaveValue(FALSE);
}
if (NULL != ppUserList)
*ppUserList = NULL;
if (0 != DPA_GetPtrCount(hSids))
{
HDPA hEntryList = DPA_Create(4);
if (NULL == hEntryList)
TraceLeaveValue(FALSE);
InternalLookupSids(hSids, pszServer, psi2, hEntryList);
if (0 != DPA_GetPtrCount(hEntryList) && NULL != ppUserList)
fResult = BuildUserList(hEntryList, pszServer, ppUserList);
DPA_Destroy(hEntryList);
}
TraceLeaveValue(fResult);
}
BOOL
CSidCache::LookupSidsAsync(HDPA hSids,
LPCTSTR pszServer,
LPSECURITYINFO2 psi2,
HWND hWndNotify,
UINT uMsgNotify)
{
BOOL fResult = FALSE;
TraceEnter(TRACE_SIDCACHE, "CSidCache::LookupSids");
TraceAssert(hSids != NULL);
if (NULL == hSids)
{
SetLastError(ERROR_INVALID_PARAMETER);
TraceLeaveValue(FALSE);
}
if (0 != DPA_GetPtrCount(hSids))
{
fResult = InternalLookupSids(hSids,
pszServer,
psi2,
NULL,
hWndNotify,
uMsgNotify);
}
TraceLeaveValue(fResult);
}
#if(_WIN32_WINNT >= 0x0500)
BOOL
CSidCache::LookupNames(PDS_SELECTION_LIST pDsSelList,
LPCTSTR pszServer,
PUSER_LIST *ppUserList,
BOOL bStandalone)
{
BOOL fResult = FALSE;
HDPA hEntryList;
TraceEnter(TRACE_SIDCACHE, "CSidCache::LookupNames");
TraceAssert(pDsSelList != NULL);
TraceAssert(ppUserList != NULL);
if (NULL == pDsSelList)
{
SetLastError(ERROR_INVALID_PARAMETER);
TraceLeaveValue(FALSE);
}
if (NULL != ppUserList)
*ppUserList = NULL;
hEntryList = DPA_Create(4);
if (NULL == hEntryList)
TraceLeaveValue(FALSE);
InternalLookupNames(pDsSelList, pszServer, hEntryList, bStandalone);
if (0 != DPA_GetPtrCount(hEntryList))
{
fResult = TRUE; // so far, so good
if (NULL != ppUserList)
fResult = BuildUserList(hEntryList, pszServer, ppUserList);
}
DPA_Destroy(hEntryList);
TraceLeaveValue(fResult);
}
#endif // #if(_WIN32_WINNT >= 0x0500)
void
CSidCache::GetDomainName(LPCTSTR pszServer, LPTSTR pszDomain, ULONG cchDomain)
{
TraceEnter(TRACE_SIDCACHE, "CSidCache::GetDomainName");
TraceAssert(NULL != pszDomain);
TraceAssert(0 != cchDomain);
pszDomain[0] = TEXT('\0');
LockDomain();
if (m_pszCachedDomain == NULL ||
(pszServer == NULL && m_pszCachedServer != NULL) ||
(pszServer != NULL && (m_pszCachedServer == NULL ||
CompareString(LOCALE_USER_DEFAULT,
0,
pszServer,
-1,
m_pszCachedServer,
-1) != CSTR_EQUAL)))
{
//
// It's a different server than last time, so ask LSA
// for the domain name.
//
LocalFreeString(&m_pszCachedDomain);
LocalFreeString(&m_pszCachedServer);
if (pszServer != NULL)
LocalAllocString(&m_pszCachedServer, pszServer);
LSA_HANDLE hLSA = GetLSAConnection(pszServer, POLICY_VIEW_LOCAL_INFORMATION);
if (hLSA != NULL)
{
PPOLICY_ACCOUNT_DOMAIN_INFO pDomainInfo = NULL;
LsaQueryInformationPolicy(hLSA,
PolicyAccountDomainInformation,
(PVOID*)&pDomainInfo);
if (pDomainInfo != NULL)
{
CopyUnicodeString(&m_pszCachedDomain, &pDomainInfo->DomainName);
LsaFreeMemory(pDomainInfo);
Trace((TEXT("Domain for %s is %s"), pszServer, m_pszCachedDomain));
}
LsaClose(hLSA);
}
else if (NULL != pszServer) // use the server name
{
// Skip leading backslashes
while (TEXT('\\') == *pszServer)
pszServer++;
LocalAllocString(&m_pszCachedDomain, pszServer);
if (m_pszCachedDomain)
{
// If there is a period, truncate the name at that point so
// that something like "nttest.microsoft.com" becomes "nttest"
LPTSTR pszDot = StrChr(m_pszCachedDomain, TEXT('.'));
if (pszDot)
*pszDot = TEXT('\0');
}
}
}
if (m_pszCachedDomain)
lstrcpyn(pszDomain, m_pszCachedDomain, cchDomain);
UnlockDomain();
TraceLeaveVoid();
}
DWORD
_GetDcName(LPCTSTR pszServer, LPCTSTR pszDomain, LPTSTR *ppszDC)
{
DWORD dwErr;
if (!ppszDC)
return ERROR_INVALID_PARAMETER;
*ppszDC = NULL;
#if(_WIN32_WINNT >= 0x0500)
PDOMAIN_CONTROLLER_INFO pDCInfo = NULL;
TraceMsg("Calling DsGetDcName");
dwErr = DsGetDcName(pszServer,
pszDomain,
NULL,
NULL,
DS_IS_FLAT_NAME,
&pDCInfo);
if (ERROR_SUCCESS == dwErr)
{
TraceAssert(NULL != pDCInfo);
LocalAllocString(ppszDC, pDCInfo->DomainControllerName);
NetApiBufferFree(pDCInfo);
}
#else
LPTSTR pszDcName = NULL;
// NetGetAnyDCName only works for trusted domains, but is faster
// and returns either PDC or BDC. NetGetDCName returns only PDC.
TraceMsg("Calling NetGetAnyDCName / NetGetDCName");
dwErr = NetGetAnyDCName(pszServer, pszDomain, (LPBYTE*)&pszDcName);
if (ERROR_NO_SUCH_DOMAIN == dwErr)
dwErr = NetGetDCName(pszServer, pszDomain, (LPBYTE*)&pszDcName);
if (pszDcName)
{
LocalAllocString(ppszDC, pszDcName);
NetApiBufferFree(pszDcName);
}
#endif
if (ERROR_SUCCESS == dwErr && !*ppszDC)
dwErr = ERROR_OUTOFMEMORY;
return dwErr;
}
void
CSidCache::GetDcName(LPCTSTR pszDomain, LPTSTR pszDC, ULONG cchDC)
{
TraceEnter(TRACE_SIDCACHE, "CSidCache::GetDcName");
TraceAssert(NULL != pszDC);
TraceAssert(0 != cchDC);
pszDC[0] = TEXT('\0');
LockDc();
if (m_pszLastDc == NULL ||
(pszDomain == NULL && m_pszLastDomain != NULL) ||
(pszDomain != NULL && (m_pszLastDomain == NULL ||
CompareString(LOCALE_USER_DEFAULT,
0,
pszDomain,
-1,
m_pszLastDomain,
-1) != CSTR_EQUAL)))
{
//
// It's a different domain than last time, so look for a DC
//
LocalFreeString(&m_pszLastDc);
LocalFreeString(&m_pszLastDomain);
if (pszDomain != NULL)
LocalAllocString(&m_pszLastDomain, pszDomain);
_GetDcName(NULL, pszDomain, &m_pszLastDc);
Trace((TEXT("DC for %s is %s"), pszDomain, m_pszLastDc));
}
if (m_pszLastDc)
lstrcpyn(pszDC, m_pszLastDc, cchDC);
UnlockDc();
TraceLeaveVoid();
}
BSTR
CSidCache::GetNT4DisplayName(LPCTSTR pszAccount,
LPCTSTR pszName,
LPCTSTR pszServer,
BOOL bStandalone)
{
BSTR strResult = NULL;
TCHAR szComputer[UNCLEN];
LPTSTR pszT = NULL;
PUSER_INFO_2 pui = NULL;
if (!pszAccount || !*pszAccount || !pszName || !*pszName)
return NULL;
TraceEnter(TRACE_SIDCACHE, "CSidCache::GetNT4DisplayName");
if (!bStandalone
&& (pszT = StrChr(pszAccount, TEXT('\\'))))
{
// Copy the domain name
TCHAR szDomain[DNLEN];
lstrcpyn(szDomain,
pszAccount,
min((size_t)(pszT - pszAccount + 1), ARRAYSIZE(szDomain)));
// See if we can use pszServer for NetUserGetInfo
TCHAR szAccountDomain[DNLEN];
szAccountDomain[0] = TEXT('\0');
GetDomainName(pszServer, szAccountDomain, ARRAYSIZE(szAccountDomain));
if (lstrcmpi(szDomain, szAccountDomain))
{
// Different domain, find a DC
szComputer[0] = TEXT('\0');
GetDcName(szDomain, szComputer, ARRAYSIZE(szComputer));
if (TEXT('\0') != szComputer[0])
pszServer = szComputer;
}
}
TraceMsg("Calling NetUserGetInfo");
if (NERR_Success == NetUserGetInfo(pszServer, pszName, 2, (LPBYTE *)&pui)
&& NULL != pui->usri2_full_name
&& *pui->usri2_full_name)
{
strResult = SysAllocString(pui->usri2_full_name);
}
NetApiBufferFree(pui);
Trace((TEXT("Returning Full Name '%s' for '%s'"), strResult, pszAccount));
TraceLeaveValue(strResult);
}
int
CSidCache::HashSid(PSID pSid)
{
DWORD dwHash = 0;
if (NULL != pSid)
{
PBYTE pbSid = (PBYTE)pSid;
PBYTE pbEndSid = pbSid + GetLengthSid(pSid);
while (pbSid < pbEndSid)
dwHash += *pbSid++;
}
return dwHash % BUCKET_COUNT;
}
int CALLBACK
CSidCache::CompareSid(LPVOID p1, LPVOID p2, LPARAM lParam)
{
int nResult = 0;
PSID_CACHE_ENTRY pEntry1 = (PSID_CACHE_ENTRY)p1;
PSID_CACHE_ENTRY pEntry2 = (PSID_CACHE_ENTRY)p2;
PSID pSid1 = NULL;
PSID pSid2 = NULL;
if (pEntry1)
pSid1 = pEntry1->pSid;
else if (lParam)
pSid1 = (PSID)lParam;
if (pEntry2)
pSid2 = pEntry2->pSid;
if (pSid1 == NULL)
nResult = -1;
else if (pSid2 == NULL)
nResult = 1;
else
{
DWORD dwLength = GetLengthSid(pSid1);
// Compare SID lengths
nResult = dwLength - GetLengthSid(pSid2);
if (nResult == 0)
{
// Lengths are equal, compare the bits
PBYTE pbSid1 = (PBYTE)pSid1;
PBYTE pbSid2 = (PBYTE)pSid2;
// Could compare Identifier Authorities and SubAuthorities instead
while (nResult == 0 && dwLength != 0)
{
dwLength--;
nResult = *pbSid1++ - *pbSid2++;
}
}
}
return nResult;
}
PSID_CACHE_ENTRY
CSidCache::FindSid(PSID pSid)
{
PSID_CACHE_ENTRY pEntry = NULL;
int iBucket;
TraceEnter(TRACE_SIDCACHE, "CSidCache::FindSid");
TraceAssert(pSid != NULL);
TraceAssert(IsValidSid(pSid));
iBucket = HashSid(pSid);
Lock();
if (m_dpaSidHashTable[iBucket] != NULL)
{
int iEntry = DPA_Search(m_dpaSidHashTable[iBucket],
NULL,
0,
CompareSid,
(LPARAM)pSid,
DPAS_SORTED);
if (iEntry != -1)
{
pEntry = (PSID_CACHE_ENTRY)DPA_FastGetPtr(m_dpaSidHashTable[iBucket],
iEntry);
TraceAssert(pEntry != NULL);
TraceAssert(EqualSid(pSid, pEntry->pSid));
if (0 != pEntry->dwLastAccessTime)
{
DWORD dwCurrentTime = GetTickCount();
if ((dwCurrentTime - pEntry->dwLastAccessTime) > SID_CACHE_AGE_LIMIT)
{
// The entry has aged out, remove it.
Trace((TEXT("Removing stale entry: %s"), pEntry->pszName));
DPA_DeletePtr(m_dpaSidHashTable[iBucket], iEntry);
LocalFree(pEntry);
pEntry = NULL;
}
else
pEntry->dwLastAccessTime = dwCurrentTime;
}
}
}
Unlock();
TraceLeaveValue(pEntry);
}
PSID_CACHE_ENTRY
CSidCache::MakeEntry(PSID pSid,
SID_NAME_USE SidType,
LPCTSTR pszName,
LPCTSTR pszLogonName)
{
PSID_CACHE_ENTRY pEntry = NULL;
ULONG cbSid;
ULONG cbName = 0;
ULONG cbLogonName = 0;
TraceEnter(TRACE_SIDCACHE, "CSidCache::MakeEntry");
TraceAssert(pSid != NULL);
cbSid = GetLengthSid(pSid);
if (NULL != pszName && *pszName)
cbName = StringByteSize(pszName);
if (NULL != pszLogonName && *pszLogonName)
cbLogonName = StringByteSize(pszLogonName);
pEntry = (PSID_CACHE_ENTRY)LocalAlloc(LPTR,
SIZEOF(SID_CACHE_ENTRY)
+ cbSid
+ cbName
+ cbLogonName);
if (pEntry != NULL)
{
PBYTE pData = (PBYTE)(pEntry+1);
pEntry->SidType = SidType;
pEntry->pSid = (PSID)pData;
CopyMemory(pData, pSid, cbSid);
pData += cbSid;
if (0 != cbName)
{
pEntry->pszName = (LPCTSTR)pData;
CopyMemory(pData, pszName, cbName);
pData += cbName;
}
if (0 != cbLogonName)
{
pEntry->pszLogonName = (LPCTSTR)pData;
CopyMemory(pData, pszLogonName, cbLogonName);
//pData += cbLogonName;
}
// Well-known entries never age out
if (SidTypeWellKnownGroup == SidType || IsAliasSid(pSid))
pEntry->dwLastAccessTime = 0;
else
pEntry->dwLastAccessTime = GetTickCount();
}
TraceLeaveValue(pEntry);
}
BOOL
CSidCache::AddEntry(PSID_CACHE_ENTRY pEntry)
{
BOOL fResult = FALSE;
int iSidBucket;
TraceEnter(TRACE_SIDCACHE, "CSidCache::AddEntry");
TraceAssert(pEntry != NULL);
if (NULL == pEntry)
TraceLeaveValue(FALSE);
iSidBucket = HashSid(pEntry->pSid);
Lock();
if (m_dpaSidHashTable[iSidBucket] == NULL)
m_dpaSidHashTable[iSidBucket] = DPA_Create(4);
if (NULL != m_dpaSidHashTable[iSidBucket])
{
DPA_AppendPtr(m_dpaSidHashTable[iSidBucket], pEntry);
DPA_Sort(m_dpaSidHashTable[iSidBucket], CompareSid, 0);
fResult = TRUE;
}
Unlock();
TraceLeaveValue(fResult);
}
BOOL
CSidCache::BuildUserList(HDPA hEntryList,
LPCTSTR pszServer,
PUSER_LIST *ppUserList)
{
ULONG cEntries;
TCHAR szAliasDomain[MAX_PATH];
PSID_CACHE_ENTRY pEntry;
ULONG cb = 0;
ULONG cSidsLen = 0;
ULONG cbAliasDomain = 0;
ULONG i;
TraceEnter(TRACE_SIDCACHE, "CSidCache::BuildUserList");
TraceAssert(hEntryList != NULL);
TraceAssert(ppUserList != NULL);
cEntries = DPA_GetPtrCount(hEntryList);
TraceAssert(0 != cEntries);
//
// This name replaces "BUILTIN" for Alias SIDs
//
GetDomainName(pszServer, szAliasDomain, ARRAYSIZE(szAliasDomain));
cbAliasDomain = StringByteSize(szAliasDomain);
//
// Add the sizes
//
cb = SIZEOF(USER_LIST) + ((cEntries - 1) * SIZEOF(USER_INFO));
for (i = 0; i < cEntries; i++)
{
pEntry = (PSID_CACHE_ENTRY)DPA_FastGetPtr(hEntryList, i);
TraceAssert(NULL != pEntry);
cSidsLen += ACLUI_ALIGNED_SID_LENGTH(pEntry->pSid);
if (SidTypeAlias == pEntry->SidType)
cb += cbAliasDomain;
else if (pEntry->pszLogonName)
cb += StringByteSize(pEntry->pszLogonName);
if (pEntry->pszName)
cb += StringByteSize(pEntry->pszName);
}
cb += cSidsLen;
//
// Allocate and build the return buffer
//
*ppUserList = (PUSER_LIST)LocalAlloc(LPTR, cb);
if (NULL == *ppUserList)
TraceLeaveValue(FALSE);
(*ppUserList)->cUsers = cEntries;
PBYTE pData = NULL;
PBYTE pCharData = NULL;
//
//NTRAID#NTBUG9-364410-2001/20/23-hiteshr
//Sids were non aligned if cEntries > 1
//
pData = (PBYTE)&(*ppUserList)->rgUsers[cEntries];
pCharData = pData + cSidsLen;
for (i = 0; i < cEntries; i++)
{
pEntry = (PSID_CACHE_ENTRY)DPA_FastGetPtr(hEntryList, i);
TraceAssert(NULL != pEntry);
(*ppUserList)->rgUsers[i].SidType = pEntry->SidType;
TraceAssert(NULL != pEntry->pSid);
(*ppUserList)->rgUsers[i].pSid = (PSID)pData;
cb = GetLengthSid(pEntry->pSid);
CopyMemory(pData, pEntry->pSid, cb);
pData += cb;
if (SidTypeAlias == pEntry->SidType)
{
(*ppUserList)->rgUsers[i].pszLogonName = (LPCTSTR)pCharData;
// Copy the "BUILTIN" domain name
if (cbAliasDomain)
{
CopyMemory(pCharData, szAliasDomain, cbAliasDomain);
pCharData += cbAliasDomain - SIZEOF(TCHAR);
if (NULL != pEntry->pszName)
*(LPTSTR)pCharData = TEXT('\\');
else
*(LPTSTR)pCharData = TEXT('\0');
pCharData += SIZEOF(TCHAR);
}
// The rest of the name is copied below
}
else if (NULL != pEntry->pszLogonName)
{
(*ppUserList)->rgUsers[i].pszLogonName = (LPCTSTR)pCharData;
cb = StringByteSize(pEntry->pszLogonName);
CopyMemory(pCharData, pEntry->pszLogonName, cb);
pCharData += cb;
}
if (NULL != pEntry->pszName)
{
(*ppUserList)->rgUsers[i].pszName = (LPCTSTR)pCharData;
cb = StringByteSize(pEntry->pszName);
CopyMemory(pCharData, pEntry->pszName, cb);
pCharData += cb;
}
}
TraceLeaveValue(TRUE);
}
//
// Wrapper around sspi's TranslateName that automatically handles
// the buffer sizing
//
HRESULT
TranslateNameInternal(LPCTSTR pszAccountName,
EXTENDED_NAME_FORMAT AccountNameFormat,
EXTENDED_NAME_FORMAT DesiredNameFormat,
BSTR *pstrTranslatedName)
{
#if(_WIN32_WINNT >= 0x0500)
#if DBG
//
// These match up with the EXTENDED_NAME_FORMAT enumeration.
// They're for debugger output only.
//
static const LPCTSTR rgpszFmt[] = {
TEXT("NameUnknown"),
TEXT("FullyQualifiedDN"),
TEXT("NameSamCompatible"),
TEXT("NameDisplay"),
TEXT("NameDomainSimple"),
TEXT("NameEnterpriseSimple"),
TEXT("NameUniqueId"),
TEXT("NameCanonical"),
TEXT("NameUserPrincipal"),
TEXT("NameCanonicalEx"),
TEXT("NameServicePrincipal") };
#endif // DBG
TraceEnter(TRACE_SIDCACHE, "TranslateNameInternal");
Trace((TEXT("Calling TranslateName for \"%s\""), pszAccountName));
Trace((TEXT("Translating %s -> %s"),
rgpszFmt[AccountNameFormat], rgpszFmt[DesiredNameFormat]));
if (!pszAccountName || !*pszAccountName || !pstrTranslatedName)
TraceLeaveResult(E_INVALIDARG);
HRESULT hr = NOERROR;
//
// cchTrans is static so that if a particular installation's
// account names are really long, we'll not be resizing the
// buffer for each account.
//
static ULONG cchTrans = MAX_PATH;
ULONG cch = cchTrans;
*pstrTranslatedName = SysAllocStringLen(NULL, cch);
if (NULL == *pstrTranslatedName)
ExitGracefully(hr, E_OUTOFMEMORY, "Unable to allocate name buffer");
**pstrTranslatedName = L'\0';
//
// TranslateName is delay-loaded from secur32.dll using the linker's
// delay-load mechanism. Therefore, wrap with an exception handler.
//
__try
{
while(!::TranslateName(pszAccountName,
AccountNameFormat,
DesiredNameFormat,
*pstrTranslatedName,
&cch))
{
if (ERROR_INSUFFICIENT_BUFFER == GetLastError())
{
Trace((TEXT("Resizing buffer to %d chars"), cch));
if (!SysReAllocStringLen(pstrTranslatedName, NULL, cch))
ExitGracefully(hr, E_OUTOFMEMORY, "Unable to reallocate name buffer");
**pstrTranslatedName = L'\0';
}
else
{
hr = E_FAIL;
break;
}
}
cchTrans = max(cch, cchTrans);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
hr = E_FAIL;
}
exit_gracefully:
if (FAILED(hr))
{
SysFreeString(*pstrTranslatedName);
*pstrTranslatedName = NULL;
}
TraceLeaveResult(hr);
#else
return E_NOTIMPL;
#endif // _WIN32_WINNT >= 0x0500
}
void
CSidCache::GetUserFriendlyName(LPCTSTR pszSamLogonName,
LPCTSTR pszSamAccountName,
LPCTSTR pszServer,
BOOL bUseSamCompatibleInfo,
BOOL bIsStandalone,
BSTR *pstrLogonName,
BSTR *pstrDisplayName)
{
BSTR strFQDN = NULL;
TraceEnter(TRACE_SIDCACHE, "CSidCache::GetUserFriendlyName");
TraceAssert(NULL != pszSamLogonName);
//
// Start by getting the FQDN. Cracking is most efficient when the
// FQDN is the starting point.
//
// TranslateName takes a while to complete, so bUseSamCompatibleInfo
// should be TRUE whenever possible, e.g. for local accounts on a non-DC
// or anything where we know a FQDN doesn't exist.
//
if (!bUseSamCompatibleInfo &&
FAILED(TranslateNameInternal(pszSamLogonName,
NameSamCompatible,
NameFullyQualifiedDN,
&strFQDN)))
{
//
// No FQDN available for this account. Must be an NT4
// account. Return SAM-compatible info to the caller.
//
bUseSamCompatibleInfo = TRUE;
}
if (NULL != pstrLogonName)
{
*pstrLogonName = NULL;
if (!bUseSamCompatibleInfo)
{
TranslateNameInternal(strFQDN,
NameFullyQualifiedDN,
NameUserPrincipal,
pstrLogonName);
}
}
if (NULL != pstrDisplayName)
{
*pstrDisplayName = NULL;
if (bUseSamCompatibleInfo ||
FAILED(TranslateNameInternal(strFQDN,
NameFullyQualifiedDN,
NameDisplay,
pstrDisplayName)))
{
*pstrDisplayName = GetNT4DisplayName(pszSamLogonName,
pszSamAccountName,
pszServer,
bIsStandalone);
}
}
SysFreeString(strFQDN);
TraceLeaveVoid();
}
BOOL
CSidCache::InternalLookupSids(HDPA hSids,
LPCTSTR pszServer,
LPSECURITYINFO2 psi2,
HDPA hEntryList,
HWND hWndNotify,
UINT uMsgNotify)
{
ULONG cSids;
HDPA hUnknownSids;
PSID_CACHE_ENTRY pEntry;
ULONG i;
TraceEnter(TRACE_SIDCACHE, "CSidCache::InternalLookupSids");
TraceAssert(hSids != NULL);
if (hSids == NULL)
{
SetLastError(ERROR_INVALID_PARAMETER);
TraceLeaveValue(FALSE);
}
cSids = DPA_GetPtrCount(hSids);
TraceAssert(0 != cSids);
hUnknownSids = DPA_Create(4);
if (NULL == hUnknownSids)
TraceLeaveValue(FALSE);
//
// See if any exist in the cache already
//
for (i = 0; i < cSids; i++)
{
pEntry = FindSid((PSID)DPA_FastGetPtr(hSids, i));
if (pEntry)
{
if (hWndNotify)
PostMessage(hWndNotify, uMsgNotify, 0, (LPARAM)pEntry->pSid);
else if (hEntryList)
DPA_AppendPtr(hEntryList, pEntry);
}
else
DPA_AppendPtr(hUnknownSids, DPA_FastGetPtr(hSids, i));
}
//
// Call LSA to lookup any that we don't have cached
//
if (0 != DPA_GetPtrCount(hUnknownSids))
{
if (!psi2 ||
FAILED(LookupSidsFromObject(hUnknownSids, psi2, hEntryList)))
{
LookupSidsHelper(hUnknownSids,
pszServer,
hEntryList,
hWndNotify,
uMsgNotify);
}
}
DPA_Destroy(hUnknownSids);
TraceLeaveValue(TRUE);
}
#if(_WIN32_WINNT >= 0x0500)
#include <adsnms.h> // USER_CLASS_NAME, etc.
#else
#define COMPUTER_CLASS_NAME TEXT("Computer")
#define USER_CLASS_NAME TEXT("User")
#define GROUP_CLASS_NAME TEXT("Group")
#define GLOBALGROUP_CLASS_NAME TEXT("GlobalGroup")
#define LOCALGROUP_CLASS_NAME TEXT("LocalGroup")
#endif
TCHAR const c_szForeignSecurityPrincipal[] = TEXT("foreignSecurityPrincipal");
static const struct
{
LPCTSTR pszClass;
SID_NAME_USE sidType;
} c_aSidClasses[] =
{
USER_CLASS_NAME, SidTypeUser,
GROUP_CLASS_NAME, SidTypeGroup,
GLOBALGROUP_CLASS_NAME, SidTypeGroup,
LOCALGROUP_CLASS_NAME, SidTypeGroup,
COMPUTER_CLASS_NAME, SidTypeComputer,
c_szForeignSecurityPrincipal, SidTypeGroup,
};
SID_NAME_USE
GetSidType(PSID pSid, LPCTSTR pszClass)
{
SID_NAME_USE sidType = SidTypeUnknown;
TraceEnter(TRACE_SIDCACHE, "GetSidType");
if (pSid)
{
TraceAssert(IsValidSid(pSid));
if (EqualSystemSid(pSid, UI_SID_World) || IsCreatorSid(pSid))
TraceLeaveValue(SidTypeWellKnownGroup);
if (IsAliasSid(pSid))
TraceLeaveValue(SidTypeAlias);
if (*GetSidSubAuthorityCount(pSid) == 1 && IsNTAuthority(pSid))
{
DWORD sa = *GetSidSubAuthority(pSid, 0);
if (sa && sa <= SECURITY_RESTRICTED_CODE_RID && sa != SECURITY_LOGON_IDS_RID)
TraceLeaveValue(SidTypeWellKnownGroup);
if (SECURITY_LOCAL_SYSTEM_RID == sa)
TraceLeaveValue(SidTypeWellKnownGroup);
}
}
if (pszClass)
{
// Didn't recognize the SID, try the class name
for (int i = 0; i < ARRAYSIZE(c_aSidClasses); i++)
{
if (!lstrcmpi(pszClass, c_aSidClasses[i].pszClass))
TraceLeaveValue(c_aSidClasses[i].sidType);
}
Trace((TEXT("Unexpected class type: %s"), pszClass));
}
// Don't know what type it is, so take a guess. This is just
// for picking an icon, so it doesn't matter too much.
TraceLeaveValue(SidTypeUser); // SidTypeGroup would be just as valid
}
HRESULT
CSidCache::LookupSidsFromObject(HDPA hSids,
LPSECURITYINFO2 psi2,
HDPA hEntryList)
{
HRESULT hr;
ULONG cSids;
LPDATAOBJECT pdoNames = NULL;
STGMEDIUM medium = {0};
FORMATETC fe = { (CLIPFORMAT)g_cfSidInfoList, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
PSID_INFO_LIST pSidList = NULL;
UINT i;
TraceEnter(TRACE_SIDCACHE, "CSidCache::LookupSidsFromObject");
TraceAssert(hSids != NULL);
TraceAssert(psi2 != NULL);
cSids = DPA_GetPtrCount(hSids);
TraceAssert(cSids != 0);
hr = psi2->LookupSids(cSids, DPA_GetPtrPtr(hSids), &pdoNames);
FailGracefully(hr, "ISecurityInformation2::LookupSids failed");
hr = pdoNames->GetData(&fe, &medium);
FailGracefully(hr, "Unable to get CFSTR_ACLUI_SID_INFO_LIST from DataObject");
pSidList = (PSID_INFO_LIST)GlobalLock(medium.hGlobal);
if (!pSidList)
ExitGracefully(hr, E_FAIL, "Unable to lock stgmedium.hGlobal");
TraceAssert(pSidList->cItems > 0);
for (i = 0; i < pSidList->cItems; i++)
{
PSID_CACHE_ENTRY pEntry = MakeEntry(pSidList->aSidInfo[i].pSid,
GetSidType(pSidList->aSidInfo[i].pSid,
pSidList->aSidInfo[i].pwzClass),
pSidList->aSidInfo[i].pwzCommonName,
pSidList->aSidInfo[i].pwzUPN);
if (pEntry)
{
if (AddEntry(pEntry))
{
if (hEntryList)
DPA_AppendPtr(hEntryList, pEntry);
}
else
LocalFree(pEntry);
}
}
exit_gracefully:
if (pSidList)
GlobalUnlock(medium.hGlobal);
ReleaseStgMedium(&medium);
DoRelease(pdoNames);
TraceLeaveResult(hr);
}
BOOL
CSidCache::LookupSidsHelper(HDPA hSids,
LPCTSTR pszServer,
HDPA hEntryList,
HWND hWndNotify,
UINT uMsgNotify,
BOOL bSecondTry)
{
BOOL fResult = FALSE;
ULONG cSids;
LSA_HANDLE hlsa = NULL;
PLSA_REFERENCED_DOMAIN_LIST pRefDomains = NULL;
PLSA_TRANSLATED_NAME pTranslatedNames = NULL;
DWORD dwStatus;
BOOL bIsDC = FALSE;
BOOL bIsStandalone = FALSE;
HDPA hUnknownSids = NULL;
TraceEnter(TRACE_SIDCACHE, "CSidCache::LookupSidsHelper");
TraceAssert(hSids != NULL);
cSids = DPA_GetPtrCount(hSids);
if (!cSids)
TraceLeaveValue(FALSE);
//
// Call LSA to lookup SIDs for the names
//
hlsa = GetLSAConnection(pszServer, POLICY_LOOKUP_NAMES);
if (NULL == hlsa && NULL != pszServer && !bSecondTry)
{
// Try the local machine
pszServer = NULL;
hlsa = GetLSAConnection(NULL, POLICY_LOOKUP_NAMES);
}
if (NULL == hlsa)
TraceLeaveValue(FALSE);
dwStatus = LsaLookupSids(hlsa,
cSids,
DPA_GetPtrPtr(hSids),
&pRefDomains,
&pTranslatedNames);
bIsStandalone = IsStandalone(pszServer, &bIsDC);
if (STATUS_SUCCESS == dwStatus || STATUS_SOME_NOT_MAPPED == dwStatus)
{
TraceAssert(pTranslatedNames);
TraceAssert(pRefDomains);
//
// Build cache entries with NT4 style names
//
for (ULONG i = 0; i < cSids; i++)
{
BOOL bTryUPN = TRUE;
BSTR strLogonName = NULL;
BSTR strDisplayName = NULL;
LPTSTR pszDeletedAccount = NULL;
LPTSTR pszSID = NULL;
PLSA_TRANSLATED_NAME pLsaName = &pTranslatedNames[i];
PLSA_TRUST_INFORMATION pLsaDomain = NULL;
PSID pSid = DPA_FastGetPtr(hSids, i);
TCHAR szAccountName[MAX_PATH];
TCHAR szDomainName[MAX_PATH];
BOOL bNoCache = FALSE;
szAccountName[0] = TEXT('\0');
szDomainName[0] = TEXT('\0');
// Get the referenced domain, if any
if (pLsaName->DomainIndex >= 0 && pRefDomains)
{
TraceAssert((ULONG)pLsaName->DomainIndex < pRefDomains->Entries);
pLsaDomain = &pRefDomains->Domains[pLsaName->DomainIndex];
}
// Make NULL-terminated copies of the domain and account name strings
CopyUnicodeString(szAccountName, ARRAYSIZE(szAccountName), &pLsaName->Name);
if (pLsaDomain)
CopyUnicodeString(szDomainName, ARRAYSIZE(szDomainName), &pLsaDomain->Name);
// Some optimization to avoid TranslateName when possible
if (!bIsDC)
{
if (bIsStandalone)
{
// Non-DC, standalone, therefore no UPN
bTryUPN = FALSE;
}
else if (SidTypeUser == pLsaName->Use)
{
TCHAR szTargetDomain[DNLEN];
szTargetDomain[0] = TEXT('\0');
GetDomainName(pszServer, szTargetDomain, ARRAYSIZE(szTargetDomain));
if (CSTR_EQUAL == CompareString(LOCALE_USER_DEFAULT,
NORM_IGNORECASE,
szTargetDomain,
-1,
szDomainName,
-1))
{
// Local account on non-DC, therefore no UPN
bTryUPN = FALSE;
}
}
}
//
// Build NT4 "domain\user" style name
//
if (szDomainName[0] != TEXT('\0'))
{
lstrcat(szDomainName, TEXT("\\"));
lstrcat(szDomainName, szAccountName);
}
// What we've got so far is our baseline.
// Adjust these based on SID type.
LPTSTR pszName = szAccountName;
LPTSTR pszLogonName = szDomainName;
switch (pLsaName->Use)
{
case SidTypeUser: // 1
// Get "User Principal Name" etc.
GetUserFriendlyName(pszLogonName,
pszName,
pszServer,
!bTryUPN,
bIsStandalone,
&strLogonName,
&strDisplayName);
if (strLogonName)
pszLogonName = strLogonName;
if (strDisplayName)
pszName = strDisplayName;
break;
case SidTypeGroup: // 2
case SidTypeDomain: // 3
// nothing
break;
case SidTypeAlias: // 4
if (!IsAliasSid(pSid))
{
// Sometimes get SidTypeAlias for non-BUILTIN sids,
// e.g. Domain Local Groups. Treat these as groups
// so we don't replace the Domain name.
// Raid #383755
pLsaName->Use = SidTypeGroup;
break;
}
// else Fall Through
case SidTypeWellKnownGroup: // 5
// No logon name for these
pszLogonName = NULL;
break;
case SidTypeDeletedAccount: // 6
// Display "Account Deleted(Sid)"
ConvertSidToStringSid(pSid, &pszSID);
if(FormatStringID(&pszDeletedAccount,
::hModule,
IDS_SID_DELETED_1,
pszSID))
{
if (pszSID)
LocalFreeString(&pszSID);
if (pszDeletedAccount)
pszName = pszDeletedAccount;
pszLogonName = NULL;
}
else
{
bNoCache = TRUE;
}
break;
case SidTypeInvalid: // 7
bNoCache = TRUE;
break;
case SidTypeUnknown: // 8
// Some SIDs can only be looked up on a DC, so
// if pszServer is not a DC, remember them and
// look them up on a DC after this loop is done.
if (!bSecondTry && !bIsStandalone && !bIsDC)
{
if (!hUnknownSids)
hUnknownSids = DPA_Create(4);
if (hUnknownSids)
DPA_AppendPtr(hUnknownSids, pSid);
bNoCache = TRUE;
}
else
{
// Display "Account Unknown(Sid)"
ConvertSidToStringSid(pSid, &pszSID);
if(FormatStringID(&pszDeletedAccount,
::hModule,
IDS_SID_UNKNOWN_1,
pszSID))
{
if (pszSID)
LocalFreeString(&pszSID);
if (pszDeletedAccount)
pszName = pszDeletedAccount;
pszLogonName = NULL;
}
else
{
bNoCache = TRUE;
}
}
break;
#if(_WIN32_WINNT >= 0x0500)
case SidTypeComputer: // 9
if (*pszName)
{
// Strip the trailing '$'
int nLen = lstrlen(pszName);
if (nLen && pszName[nLen-1] == TEXT('$'))
{
pszName[nLen-1] = TEXT('\0');
}
}
break;
#endif
}
if (!bNoCache)
{
//
// Make a cache entry and save it
//
PSID_CACHE_ENTRY pEntry = MakeEntry(pSid,
pLsaName->Use,
pszName,
pszLogonName);
if (pEntry)
{
if (AddEntry(pEntry))
{
fResult = TRUE; // we added something to the cache
if (hWndNotify)
PostMessage(hWndNotify, uMsgNotify, 0, (LPARAM)pEntry->pSid);
else if (hEntryList)
DPA_AppendPtr(hEntryList, pEntry);
}
else
LocalFree(pEntry);
}
}
if (strLogonName)
SysFreeString(strLogonName);
if (strDisplayName)
SysFreeString(strDisplayName);
LocalFreeString(&pszDeletedAccount);
}
}
else if (STATUS_NONE_MAPPED == dwStatus && !bSecondTry && !bIsStandalone && !bIsDC)
{
hUnknownSids = DPA_Clone(hSids, NULL);
}
// Cleanup
if (pTranslatedNames)
LsaFreeMemory(pTranslatedNames);
if (pRefDomains)
LsaFreeMemory(pRefDomains);
LsaClose(hlsa);
if (hUnknownSids)
{
//
// Some (or all) SIDs were unknown on the target machine,
// try a DC for the target machine's primary domain.
//
// This typically happens for certain Alias SIDs, such
// as Print Operators and System Operators, for which LSA
// only returns names if the lookup is done on a DC.
//
LPTSTR pszDC = NULL;
TraceAssert(!bSecondTry);
// We don't bother trying if standalone, and don't
// do this if the target machine is already a DC.
TraceAssert(!bIsStandalone && !bIsDC);
_GetDcName(pszServer, NULL, &pszDC);
if (pszDC)
{
// Recurse
if (LookupSidsHelper(hUnknownSids,
pszDC,
hEntryList,
hWndNotify,
uMsgNotify,
TRUE))
{
fResult = TRUE;
}
LocalFree(pszDC);
}
DPA_Destroy(hUnknownSids);
}
TraceLeaveValue(fResult);
}
#if(_WIN32_WINNT >= 0x0500)
BSTR GetNT4AccountName(LPTSTR pszWinNTPath)
{
// pszWinNTPath is expected to look like
// "WinNT://domain/user"
// or
// "WinNT://domain/machine/user"
//
// The "WinNT://" part is optional.
//
// In either case, we want the last 2 elements,
// e.g. "domain/user" and "machine/user".
//
// The approach is to find the next to last '/' and add 1.
// If there are less than 2 slashes, return the original string.
BSTR strResult = NULL;
LPTSTR pszResult = pszWinNTPath;
if (pszWinNTPath)
{
LPTSTR pszSlash = StrRChr(pszWinNTPath, pszWinNTPath + lstrlen(pszWinNTPath) - 1, TEXT('/'));
if (pszSlash)
{
pszSlash = StrRChr(pszWinNTPath, pszSlash-1, TEXT('/'));
if (pszSlash)
pszResult = pszSlash + 1;
}
}
if (pszResult)
{
strResult = SysAllocString(pszResult);
if (strResult)
{
// At this point, there is at most one forward slash
// in the string. Convert it to a backslash.
LPTSTR pszSlash = StrChr(strResult, TEXT('/'));
if (pszSlash)
*pszSlash = TEXT('\\');
}
}
return strResult;
}
BOOL
_LookupName(LPCTSTR pszServer,
LPCTSTR pszAccount,
PSID *ppSid,
SID_NAME_USE *pSidType)
{
BOOL fResult = FALSE;
BYTE buffer[sizeof(SID) + SID_MAX_SUB_AUTHORITIES*sizeof(ULONG)];
PSID pSid = (PSID)buffer;
DWORD cbSid = sizeof(buffer);
TCHAR szDomain[MAX_PATH];
DWORD cchDomain = ARRAYSIZE(szDomain);
SID_NAME_USE sidType;
fResult = LookupAccountName(pszServer,
pszAccount,
pSid,
&cbSid,
szDomain,
&cchDomain,
&sidType);
if (fResult)
{
*ppSid = LocalAllocSid(pSid);
if (*ppSid)
{
if (pSidType)
*pSidType = sidType;
}
else
fResult = FALSE;
}
return fResult;
}
BOOL
CSidCache::InternalLookupNames(PDS_SELECTION_LIST pDsSelList,
LPCTSTR pszServer,
HDPA hEntryList,
BOOL bStandalone)
{
BOOL fResult = FALSE;
ULONG cNames;
HDPA hSids = NULL;
PSID_CACHE_ENTRY pEntry;
ULONG i;
ULONG cNoSID = 0;
HRESULT hrCom = E_FAIL;
IADsPathname *pPath = NULL;
TraceEnter(TRACE_SIDCACHE, "CSidCache::InternalLookupNames");
TraceAssert(pDsSelList != NULL);
TraceAssert(hEntryList != NULL);
if (pDsSelList == NULL || hEntryList == NULL)
{
SetLastError(ERROR_INVALID_PARAMETER);
TraceLeaveValue(FALSE);
}
cNames = pDsSelList->cItems;
TraceAssert(cNames != 0);
if (0 == cNames)
{
SetLastError(ERROR_INVALID_PARAMETER);
TraceLeaveValue(FALSE);
}
hSids = DPA_Create(4);
for (i = 0; i < cNames; i++)
{
PSID pSid = NULL;
PSID pSidFree = NULL;
LPVARIANT pvarSid = pDsSelList->aDsSelection[i].pvarFetchedAttributes;
SID_NAME_USE sidType = SidTypeUnknown;
BSTR strNT4Name = NULL;
if (NULL == pvarSid || (VT_ARRAY | VT_UI1) != V_VT(pvarSid)
|| FAILED(SafeArrayAccessData(V_ARRAY(pvarSid), &pSid)))
{
// If there's no SID, then we can't use it in an ACL
Trace((TEXT("No SID returned for %s"), pDsSelList->aDsSelection[i].pwzADsPath));
// If it's the NT provider, try to lookup the SID by name
if (CSTR_EQUAL == CompareString(LOCALE_USER_DEFAULT,
0,
c_szNTProvider,
NTPROV_LEN,
pDsSelList->aDsSelection[i].pwzADsPath,
NTPROV_LEN))
{
strNT4Name = GetNT4AccountName(pDsSelList->aDsSelection[i].pwzADsPath + NTPROV_LEN);
if (strNT4Name)
{
Trace((TEXT("Using LSA to lookup SID for %s"), strNT4Name));
if (_LookupName(pszServer, strNT4Name, &pSidFree, &sidType))
{
pSid = pSidFree;
}
}
}
if (NULL == pSid)
{
cNoSID++;
continue;
}
}
TraceAssert(NULL != pSid);
// Is it already in the cache?
pEntry = FindSid(pSid);
if (pEntry)
{
DPA_AppendPtr(hEntryList, pEntry);
}
else
{
// Not cached, try to make an entry using the info returned
// by the object picker.
if (SidTypeUnknown == sidType)
sidType = GetSidType(pSid, pDsSelList->aDsSelection[i].pwzClass);
if (!lstrcmpi(c_szForeignSecurityPrincipal, pDsSelList->aDsSelection[i].pwzClass))
{
// Object picker returns non-localized names for these (the
// DS Configuration Container is not localized). Look up the
// localized name from LSA. 175278
// This happens automatically below (pEntry is NULL).
}
else if (SidTypeAlias == sidType || SidTypeWellKnownGroup == sidType)
{
// Only need the name
pEntry = MakeEntry(pSid,
sidType,
pDsSelList->aDsSelection[i].pwzName,
NULL);
}
else if (pDsSelList->aDsSelection[i].pwzUPN && *pDsSelList->aDsSelection[i].pwzUPN)
{
// We have both name and UPN
pEntry = MakeEntry(pSid,
sidType,
pDsSelList->aDsSelection[i].pwzName,
pDsSelList->aDsSelection[i].pwzUPN);
}
else if (CSTR_EQUAL == CompareString(LOCALE_USER_DEFAULT,
0,
c_szNTProvider,
NTPROV_LEN,
pDsSelList->aDsSelection[i].pwzADsPath,
NTPROV_LEN))
{
// It's downlevel ("WinNT://blah")
if (NULL == strNT4Name)
strNT4Name = GetNT4AccountName(pDsSelList->aDsSelection[i].pwzADsPath + NTPROV_LEN);
if (strNT4Name)
{
// We have the NT4 name, now look for a Friendly Name
BSTR strDisplay = GetNT4DisplayName(strNT4Name,
pDsSelList->aDsSelection[i].pwzName,
pszServer,
bStandalone);
pEntry = MakeEntry(pSid,
sidType,
strDisplay ? strDisplay : pDsSelList->aDsSelection[i].pwzName,
strNT4Name);
SysFreeString(strDisplay);
}
}
else
{
// It's not a downlevel, so it must be
// 1. WellKnown/Universal (no ADsPath)
// or
// 2. Uplevel ("GC:" or "LDAP:") but
// has no UPN
//
// If it has an ADs path, try to get an
// NT4 name such as "NTDEV\Domain Users".
//
// Note that wellknown things such "Authenticated User"
// can fall under either 1 or 2 above, depending on what
// scope it was selected from. That's why we try to pick
// them off higher up.
TraceAssert(NULL == strNT4Name);
if (pDsSelList->aDsSelection[i].pwzADsPath &&
*pDsSelList->aDsSelection[i].pwzADsPath)
{
// DsCrackNames doesn't accept full ADs paths, so use
// IADsPathname to retrieve the DN (no provider/server).
if (FAILED(hrCom))
hrCom = CoInitialize(NULL);
if (!pPath)
{
CoCreateInstance(CLSID_Pathname,
NULL,
CLSCTX_INPROC_SERVER,
IID_IADsPathname,
(LPVOID*)&pPath);
}
if (pPath)
{
BSTR strT;
if (SUCCEEDED(pPath->Set(pDsSelList->aDsSelection[i].pwzADsPath,
ADS_SETTYPE_FULL)))
{
if (SUCCEEDED(pPath->Retrieve(ADS_FORMAT_X500_DN,
&strT)))
{
// Try to get an NT4 account name
TranslateNameInternal(strT,
NameFullyQualifiedDN,
NameSamCompatible,
&strNT4Name);
SysFreeString(strT);
}
if (!strNT4Name)
{
// Retrieve or CrackName failed. Try to build
// an NT4-style name from the server name.
if (SUCCEEDED(pPath->Retrieve(ADS_FORMAT_SERVER,
&strT)))
{
TCHAR szNT4Name[MAX_PATH];
GetDomainName(strT, szNT4Name, ARRAYSIZE(szNT4Name));
PathAppend(szNT4Name, pDsSelList->aDsSelection[i].pwzName);
strNT4Name = SysAllocString(szNT4Name);
SysFreeString(strT);
}
}
}
}
}
pEntry = MakeEntry(pSid,
sidType,
pDsSelList->aDsSelection[i].pwzName,
strNT4Name);
}
//
// Do we have a cache entry yet?
//
if (pEntry)
{
if (AddEntry(pEntry))
{
DPA_AppendPtr(hEntryList, pEntry);
}
else
{
LocalFree(pEntry);
pEntry = NULL;
}
}
if (!pEntry && hSids)
{
// Look up the SID the hard way
Trace((TEXT("Using LSA to lookup %s"), pDsSelList->aDsSelection[i].pwzADsPath));
PSID pSidCopy = LocalAllocSid(pSid);
if (pSidCopy)
{
DPA_AppendPtr(hSids, pSidCopy);
}
}
}
SysFreeString(strNT4Name);
if (pSidFree)
LocalFree(pSidFree);
else
SafeArrayUnaccessData(V_ARRAY(pvarSid));
}
TraceAssert(0 == cNoSID);
//
// Call LSA to lookup names for the SIDs that aren't cached yet
//
if (hSids && 0 != DPA_GetPtrCount(hSids))
LookupSidsHelper(hSids, pszServer, hEntryList);
if (NULL != hSids)
DestroyDPA(hSids);
DoRelease(pPath);
if (SUCCEEDED(hrCom))
CoUninitialize();
TraceLeaveValue(TRUE);
}
#endif // #if(_WIN32_WINNT >= 0x0500)
DWORD WINAPI
CSidCache::InitThread(LPVOID pvThreadData)
{
PSIDCACHE pThis = (PSIDCACHE)pvThreadData;
// Our caller already gave us a ref on the dll to prevent the race window where
// we are created but we the dll is freed before we can call LoadLibrary()
// HINSTANCE hInstThisDll = LoadLibrary(c_szDllName);
TraceEnter(TRACE_SIDCACHE, "CSidCache::InitThread");
if (pThis)
{
// Lookup some well-known SIDs to pre-load the cache
HDPA hSids;
hSids = DPA_Create(COUNT_SYSTEM_SID_TYPES);
if (hSids)
{
for (int i = 0; i < COUNT_SYSTEM_SID_TYPES; i++)
{
DPA_AppendPtr(hSids, QuerySystemSid((UI_SystemSid)i));
}
pThis->LookupSidsHelper(hSids, NULL, NULL, NULL, 0);
DPA_Destroy(hSids);
}
pThis->Release();
}
TraceLeave();
FreeLibraryAndExitThread(GetModuleHandle(c_szDllName), 0);
return 0;
}