Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1903 lines
55 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 1997 - 1999
  6. //
  7. // File: sidcache.cpp
  8. //
  9. // This file contains the implementation of a SID/Name cache.
  10. //
  11. //--------------------------------------------------------------------------
  12. #include "aclpriv.h"
  13. #if(_WIN32_WINNT >= 0x0500)
  14. #include <dsgetdc.h> // DsGetDcName
  15. #include <iads.h>
  16. #endif
  17. #define SECURITY_WIN32
  18. #include <security.h> // TranslateName
  19. #include <lm.h> // NetApiBufferFree
  20. #include <shlwapi.h> // StrChr, StrRChr
  21. // 10 minutes
  22. #define SID_CACHE_AGE_LIMIT (10*60*1000)
  23. TCHAR const c_szNTProvider[] = TEXT("WinNT://");
  24. #define NTPROV_LEN (ARRAYSIZE(c_szNTProvider)-1)
  25. #define ACLUI_ALIGNED_SID_LENGTH(p) ((PtrAlignSize(RtlLengthSid((p)))))
  26. PSIDCACHE g_pSidCache = NULL;
  27. PSIDCACHE GetSidCache()
  28. {
  29. if (NULL == g_pSidCache)
  30. {
  31. // The cache starts with an extra ref here that will be released
  32. // during our DLL_PROCESS_DETACH
  33. g_pSidCache = new CSidCache;
  34. if (g_pSidCache)
  35. {
  36. g_pSidCache->AddRef();
  37. }
  38. }
  39. else
  40. {
  41. g_pSidCache->AddRef();
  42. }
  43. return g_pSidCache;
  44. }
  45. void FreeSidCache()
  46. {
  47. if (g_pSidCache)
  48. {
  49. g_pSidCache->Release();
  50. g_pSidCache = NULL;
  51. }
  52. }
  53. //
  54. // CSidCache implementation
  55. //
  56. CSidCache::CSidCache()
  57. : m_pszCachedServer(NULL), m_pszCachedDomain(NULL),
  58. m_hInitThread(NULL), m_pszLastDc(NULL), m_pszLastDomain(NULL),
  59. m_cRef(1)
  60. {
  61. HINSTANCE hInstThisDll;
  62. DWORD dwThreadID;
  63. ZeroMemory(m_dpaSidHashTable, SIZEOF(m_dpaSidHashTable));
  64. ExceptionPropagatingInitializeCriticalSection(&m_csHashTableLock);
  65. ExceptionPropagatingInitializeCriticalSection(&m_csDomainNameLock);
  66. ExceptionPropagatingInitializeCriticalSection(&m_csDcNameLock);
  67. // Give the thread we are about to create a ref to the dll,
  68. // so that the dll will remain for the lifetime of the thread
  69. hInstThisDll = LoadLibrary(c_szDllName);
  70. if (hInstThisDll != NULL)
  71. {
  72. // also do an AddRef() for the worker thread to release later
  73. AddRef();
  74. // Start a thread to cache the well-known and built-in SIDs
  75. m_hInitThread = CreateThread(NULL, 0, InitThread, this, 0, &dwThreadID);
  76. if (!m_hInitThread)
  77. {
  78. // Failed to create the thread, do cleanup
  79. FreeLibrary(hInstThisDll);
  80. Release();
  81. }
  82. }
  83. }
  84. ULONG
  85. CSidCache::AddRef()
  86. {
  87. return InterlockedIncrement(&m_cRef);
  88. }
  89. ULONG
  90. CSidCache::Release()
  91. {
  92. if (InterlockedDecrement(&m_cRef) == 0)
  93. {
  94. delete this;
  95. return 0;
  96. }
  97. return m_cRef;
  98. }
  99. CSidCache::~CSidCache()
  100. {
  101. int i;
  102. TraceEnter(TRACE_SIDCACHE, "CSidCache::~CSidCache");
  103. Lock();
  104. for (i = 0; i < BUCKET_COUNT; i++)
  105. {
  106. DestroyDPA(m_dpaSidHashTable[i]);
  107. m_dpaSidHashTable[i] = NULL;
  108. }
  109. Unlock();
  110. LockDomain();
  111. LocalFreeString(&m_pszCachedServer);
  112. LocalFreeString(&m_pszCachedDomain);
  113. UnlockDomain();
  114. LockDc();
  115. LocalFreeString(&m_pszLastDc);
  116. LocalFreeString(&m_pszLastDomain);
  117. UnlockDc();
  118. DeleteCriticalSection(&m_csHashTableLock);
  119. DeleteCriticalSection(&m_csDomainNameLock);
  120. DeleteCriticalSection(&m_csDcNameLock);
  121. if (m_hInitThread != NULL)
  122. {
  123. CloseHandle(m_hInitThread);
  124. }
  125. TraceLeaveVoid();
  126. }
  127. BOOL
  128. CSidCache::LookupSids(HDPA hSids,
  129. LPCTSTR pszServer,
  130. LPSECURITYINFO2 psi2,
  131. PUSER_LIST *ppUserList)
  132. {
  133. BOOL fResult = FALSE;
  134. TraceEnter(TRACE_SIDCACHE, "CSidCache::LookupSids");
  135. TraceAssert(hSids != NULL);
  136. if (NULL == hSids)
  137. {
  138. SetLastError(ERROR_INVALID_PARAMETER);
  139. TraceLeaveValue(FALSE);
  140. }
  141. if (NULL != ppUserList)
  142. *ppUserList = NULL;
  143. if (0 != DPA_GetPtrCount(hSids))
  144. {
  145. HDPA hEntryList = DPA_Create(4);
  146. if (NULL == hEntryList)
  147. TraceLeaveValue(FALSE);
  148. InternalLookupSids(hSids, pszServer, psi2, hEntryList);
  149. if (0 != DPA_GetPtrCount(hEntryList) && NULL != ppUserList)
  150. fResult = BuildUserList(hEntryList, pszServer, ppUserList);
  151. DPA_Destroy(hEntryList);
  152. }
  153. TraceLeaveValue(fResult);
  154. }
  155. BOOL
  156. CSidCache::LookupSidsAsync(HDPA hSids,
  157. LPCTSTR pszServer,
  158. LPSECURITYINFO2 psi2,
  159. HWND hWndNotify,
  160. UINT uMsgNotify)
  161. {
  162. BOOL fResult = FALSE;
  163. TraceEnter(TRACE_SIDCACHE, "CSidCache::LookupSids");
  164. TraceAssert(hSids != NULL);
  165. if (NULL == hSids)
  166. {
  167. SetLastError(ERROR_INVALID_PARAMETER);
  168. TraceLeaveValue(FALSE);
  169. }
  170. if (0 != DPA_GetPtrCount(hSids))
  171. {
  172. fResult = InternalLookupSids(hSids,
  173. pszServer,
  174. psi2,
  175. NULL,
  176. hWndNotify,
  177. uMsgNotify);
  178. }
  179. TraceLeaveValue(fResult);
  180. }
  181. #if(_WIN32_WINNT >= 0x0500)
  182. BOOL
  183. CSidCache::LookupNames(PDS_SELECTION_LIST pDsSelList,
  184. LPCTSTR pszServer,
  185. PUSER_LIST *ppUserList,
  186. BOOL bStandalone)
  187. {
  188. BOOL fResult = FALSE;
  189. HDPA hEntryList;
  190. TraceEnter(TRACE_SIDCACHE, "CSidCache::LookupNames");
  191. TraceAssert(pDsSelList != NULL);
  192. TraceAssert(ppUserList != NULL);
  193. if (NULL == pDsSelList)
  194. {
  195. SetLastError(ERROR_INVALID_PARAMETER);
  196. TraceLeaveValue(FALSE);
  197. }
  198. if (NULL != ppUserList)
  199. *ppUserList = NULL;
  200. hEntryList = DPA_Create(4);
  201. if (NULL == hEntryList)
  202. TraceLeaveValue(FALSE);
  203. InternalLookupNames(pDsSelList, pszServer, hEntryList, bStandalone);
  204. if (0 != DPA_GetPtrCount(hEntryList))
  205. {
  206. fResult = TRUE; // so far, so good
  207. if (NULL != ppUserList)
  208. fResult = BuildUserList(hEntryList, pszServer, ppUserList);
  209. }
  210. DPA_Destroy(hEntryList);
  211. TraceLeaveValue(fResult);
  212. }
  213. #endif // #if(_WIN32_WINNT >= 0x0500)
  214. void
  215. CSidCache::GetDomainName(LPCTSTR pszServer, LPTSTR pszDomain, ULONG cchDomain)
  216. {
  217. TraceEnter(TRACE_SIDCACHE, "CSidCache::GetDomainName");
  218. TraceAssert(NULL != pszDomain);
  219. TraceAssert(0 != cchDomain);
  220. pszDomain[0] = TEXT('\0');
  221. LockDomain();
  222. if (m_pszCachedDomain == NULL ||
  223. (pszServer == NULL && m_pszCachedServer != NULL) ||
  224. (pszServer != NULL && (m_pszCachedServer == NULL ||
  225. CompareString(LOCALE_USER_DEFAULT,
  226. 0,
  227. pszServer,
  228. -1,
  229. m_pszCachedServer,
  230. -1) != CSTR_EQUAL)))
  231. {
  232. //
  233. // It's a different server than last time, so ask LSA
  234. // for the domain name.
  235. //
  236. LocalFreeString(&m_pszCachedDomain);
  237. LocalFreeString(&m_pszCachedServer);
  238. if (pszServer != NULL)
  239. LocalAllocString(&m_pszCachedServer, pszServer);
  240. LSA_HANDLE hLSA = GetLSAConnection(pszServer, POLICY_VIEW_LOCAL_INFORMATION);
  241. if (hLSA != NULL)
  242. {
  243. PPOLICY_ACCOUNT_DOMAIN_INFO pDomainInfo = NULL;
  244. LsaQueryInformationPolicy(hLSA,
  245. PolicyAccountDomainInformation,
  246. (PVOID*)&pDomainInfo);
  247. if (pDomainInfo != NULL)
  248. {
  249. CopyUnicodeString(&m_pszCachedDomain, &pDomainInfo->DomainName);
  250. LsaFreeMemory(pDomainInfo);
  251. Trace((TEXT("Domain for %s is %s"), pszServer, m_pszCachedDomain));
  252. }
  253. LsaClose(hLSA);
  254. }
  255. else if (NULL != pszServer) // use the server name
  256. {
  257. // Skip leading backslashes
  258. while (TEXT('\\') == *pszServer)
  259. pszServer++;
  260. LocalAllocString(&m_pszCachedDomain, pszServer);
  261. if (m_pszCachedDomain)
  262. {
  263. // If there is a period, truncate the name at that point so
  264. // that something like "nttest.microsoft.com" becomes "nttest"
  265. LPTSTR pszDot = StrChr(m_pszCachedDomain, TEXT('.'));
  266. if (pszDot)
  267. *pszDot = TEXT('\0');
  268. }
  269. }
  270. }
  271. if (m_pszCachedDomain)
  272. lstrcpyn(pszDomain, m_pszCachedDomain, cchDomain);
  273. UnlockDomain();
  274. TraceLeaveVoid();
  275. }
  276. DWORD
  277. _GetDcName(LPCTSTR pszServer, LPCTSTR pszDomain, LPTSTR *ppszDC)
  278. {
  279. DWORD dwErr;
  280. if (!ppszDC)
  281. return ERROR_INVALID_PARAMETER;
  282. *ppszDC = NULL;
  283. #if(_WIN32_WINNT >= 0x0500)
  284. PDOMAIN_CONTROLLER_INFO pDCInfo = NULL;
  285. TraceMsg("Calling DsGetDcName");
  286. dwErr = DsGetDcName(pszServer,
  287. pszDomain,
  288. NULL,
  289. NULL,
  290. DS_IS_FLAT_NAME,
  291. &pDCInfo);
  292. if (ERROR_SUCCESS == dwErr)
  293. {
  294. TraceAssert(NULL != pDCInfo);
  295. LocalAllocString(ppszDC, pDCInfo->DomainControllerName);
  296. NetApiBufferFree(pDCInfo);
  297. }
  298. #else
  299. LPTSTR pszDcName = NULL;
  300. // NetGetAnyDCName only works for trusted domains, but is faster
  301. // and returns either PDC or BDC. NetGetDCName returns only PDC.
  302. TraceMsg("Calling NetGetAnyDCName / NetGetDCName");
  303. dwErr = NetGetAnyDCName(pszServer, pszDomain, (LPBYTE*)&pszDcName);
  304. if (ERROR_NO_SUCH_DOMAIN == dwErr)
  305. dwErr = NetGetDCName(pszServer, pszDomain, (LPBYTE*)&pszDcName);
  306. if (pszDcName)
  307. {
  308. LocalAllocString(ppszDC, pszDcName);
  309. NetApiBufferFree(pszDcName);
  310. }
  311. #endif
  312. if (ERROR_SUCCESS == dwErr && !*ppszDC)
  313. dwErr = ERROR_OUTOFMEMORY;
  314. return dwErr;
  315. }
  316. void
  317. CSidCache::GetDcName(LPCTSTR pszDomain, LPTSTR pszDC, ULONG cchDC)
  318. {
  319. TraceEnter(TRACE_SIDCACHE, "CSidCache::GetDcName");
  320. TraceAssert(NULL != pszDC);
  321. TraceAssert(0 != cchDC);
  322. pszDC[0] = TEXT('\0');
  323. LockDc();
  324. if (m_pszLastDc == NULL ||
  325. (pszDomain == NULL && m_pszLastDomain != NULL) ||
  326. (pszDomain != NULL && (m_pszLastDomain == NULL ||
  327. CompareString(LOCALE_USER_DEFAULT,
  328. 0,
  329. pszDomain,
  330. -1,
  331. m_pszLastDomain,
  332. -1) != CSTR_EQUAL)))
  333. {
  334. //
  335. // It's a different domain than last time, so look for a DC
  336. //
  337. LocalFreeString(&m_pszLastDc);
  338. LocalFreeString(&m_pszLastDomain);
  339. if (pszDomain != NULL)
  340. LocalAllocString(&m_pszLastDomain, pszDomain);
  341. _GetDcName(NULL, pszDomain, &m_pszLastDc);
  342. Trace((TEXT("DC for %s is %s"), pszDomain, m_pszLastDc));
  343. }
  344. if (m_pszLastDc)
  345. lstrcpyn(pszDC, m_pszLastDc, cchDC);
  346. UnlockDc();
  347. TraceLeaveVoid();
  348. }
  349. BSTR
  350. CSidCache::GetNT4DisplayName(LPCTSTR pszAccount,
  351. LPCTSTR pszName,
  352. LPCTSTR pszServer,
  353. BOOL bStandalone)
  354. {
  355. BSTR strResult = NULL;
  356. TCHAR szComputer[UNCLEN];
  357. LPTSTR pszT = NULL;
  358. PUSER_INFO_2 pui = NULL;
  359. if (!pszAccount || !*pszAccount || !pszName || !*pszName)
  360. return NULL;
  361. TraceEnter(TRACE_SIDCACHE, "CSidCache::GetNT4DisplayName");
  362. if (!bStandalone
  363. && (pszT = StrChr(pszAccount, TEXT('\\'))))
  364. {
  365. // Copy the domain name
  366. TCHAR szDomain[DNLEN];
  367. lstrcpyn(szDomain,
  368. pszAccount,
  369. min((size_t)(pszT - pszAccount + 1), ARRAYSIZE(szDomain)));
  370. // See if we can use pszServer for NetUserGetInfo
  371. TCHAR szAccountDomain[DNLEN];
  372. szAccountDomain[0] = TEXT('\0');
  373. GetDomainName(pszServer, szAccountDomain, ARRAYSIZE(szAccountDomain));
  374. if (lstrcmpi(szDomain, szAccountDomain))
  375. {
  376. // Different domain, find a DC
  377. szComputer[0] = TEXT('\0');
  378. GetDcName(szDomain, szComputer, ARRAYSIZE(szComputer));
  379. if (TEXT('\0') != szComputer[0])
  380. pszServer = szComputer;
  381. }
  382. }
  383. TraceMsg("Calling NetUserGetInfo");
  384. if (NERR_Success == NetUserGetInfo(pszServer, pszName, 2, (LPBYTE *)&pui)
  385. && NULL != pui->usri2_full_name
  386. && *pui->usri2_full_name)
  387. {
  388. strResult = SysAllocString(pui->usri2_full_name);
  389. }
  390. NetApiBufferFree(pui);
  391. Trace((TEXT("Returning Full Name '%s' for '%s'"), strResult, pszAccount));
  392. TraceLeaveValue(strResult);
  393. }
  394. int
  395. CSidCache::HashSid(PSID pSid)
  396. {
  397. DWORD dwHash = 0;
  398. if (NULL != pSid)
  399. {
  400. PBYTE pbSid = (PBYTE)pSid;
  401. PBYTE pbEndSid = pbSid + GetLengthSid(pSid);
  402. while (pbSid < pbEndSid)
  403. dwHash += *pbSid++;
  404. }
  405. return dwHash % BUCKET_COUNT;
  406. }
  407. int CALLBACK
  408. CSidCache::CompareSid(LPVOID p1, LPVOID p2, LPARAM lParam)
  409. {
  410. int nResult = 0;
  411. PSID_CACHE_ENTRY pEntry1 = (PSID_CACHE_ENTRY)p1;
  412. PSID_CACHE_ENTRY pEntry2 = (PSID_CACHE_ENTRY)p2;
  413. PSID pSid1 = NULL;
  414. PSID pSid2 = NULL;
  415. if (pEntry1)
  416. pSid1 = pEntry1->pSid;
  417. else if (lParam)
  418. pSid1 = (PSID)lParam;
  419. if (pEntry2)
  420. pSid2 = pEntry2->pSid;
  421. if (pSid1 == NULL)
  422. nResult = -1;
  423. else if (pSid2 == NULL)
  424. nResult = 1;
  425. else
  426. {
  427. DWORD dwLength = GetLengthSid(pSid1);
  428. // Compare SID lengths
  429. nResult = dwLength - GetLengthSid(pSid2);
  430. if (nResult == 0)
  431. {
  432. // Lengths are equal, compare the bits
  433. PBYTE pbSid1 = (PBYTE)pSid1;
  434. PBYTE pbSid2 = (PBYTE)pSid2;
  435. // Could compare Identifier Authorities and SubAuthorities instead
  436. while (nResult == 0 && dwLength != 0)
  437. {
  438. dwLength--;
  439. nResult = *pbSid1++ - *pbSid2++;
  440. }
  441. }
  442. }
  443. return nResult;
  444. }
  445. PSID_CACHE_ENTRY
  446. CSidCache::FindSid(PSID pSid)
  447. {
  448. PSID_CACHE_ENTRY pEntry = NULL;
  449. int iBucket;
  450. TraceEnter(TRACE_SIDCACHE, "CSidCache::FindSid");
  451. TraceAssert(pSid != NULL);
  452. TraceAssert(IsValidSid(pSid));
  453. iBucket = HashSid(pSid);
  454. Lock();
  455. if (m_dpaSidHashTable[iBucket] != NULL)
  456. {
  457. int iEntry = DPA_Search(m_dpaSidHashTable[iBucket],
  458. NULL,
  459. 0,
  460. CompareSid,
  461. (LPARAM)pSid,
  462. DPAS_SORTED);
  463. if (iEntry != -1)
  464. {
  465. pEntry = (PSID_CACHE_ENTRY)DPA_FastGetPtr(m_dpaSidHashTable[iBucket],
  466. iEntry);
  467. TraceAssert(pEntry != NULL);
  468. TraceAssert(EqualSid(pSid, pEntry->pSid));
  469. if (0 != pEntry->dwLastAccessTime)
  470. {
  471. DWORD dwCurrentTime = GetTickCount();
  472. if ((dwCurrentTime - pEntry->dwLastAccessTime) > SID_CACHE_AGE_LIMIT)
  473. {
  474. // The entry has aged out, remove it.
  475. Trace((TEXT("Removing stale entry: %s"), pEntry->pszName));
  476. DPA_DeletePtr(m_dpaSidHashTable[iBucket], iEntry);
  477. LocalFree(pEntry);
  478. pEntry = NULL;
  479. }
  480. else
  481. pEntry->dwLastAccessTime = dwCurrentTime;
  482. }
  483. }
  484. }
  485. Unlock();
  486. TraceLeaveValue(pEntry);
  487. }
  488. PSID_CACHE_ENTRY
  489. CSidCache::MakeEntry(PSID pSid,
  490. SID_NAME_USE SidType,
  491. LPCTSTR pszName,
  492. LPCTSTR pszLogonName)
  493. {
  494. PSID_CACHE_ENTRY pEntry = NULL;
  495. ULONG cbSid;
  496. ULONG cbName = 0;
  497. ULONG cbLogonName = 0;
  498. TraceEnter(TRACE_SIDCACHE, "CSidCache::MakeEntry");
  499. TraceAssert(pSid != NULL);
  500. cbSid = GetLengthSid(pSid);
  501. if (NULL != pszName && *pszName)
  502. cbName = StringByteSize(pszName);
  503. if (NULL != pszLogonName && *pszLogonName)
  504. cbLogonName = StringByteSize(pszLogonName);
  505. pEntry = (PSID_CACHE_ENTRY)LocalAlloc(LPTR,
  506. SIZEOF(SID_CACHE_ENTRY)
  507. + cbSid
  508. + cbName
  509. + cbLogonName);
  510. if (pEntry != NULL)
  511. {
  512. PBYTE pData = (PBYTE)(pEntry+1);
  513. pEntry->SidType = SidType;
  514. pEntry->pSid = (PSID)pData;
  515. CopyMemory(pData, pSid, cbSid);
  516. pData += cbSid;
  517. if (0 != cbName)
  518. {
  519. pEntry->pszName = (LPCTSTR)pData;
  520. CopyMemory(pData, pszName, cbName);
  521. pData += cbName;
  522. }
  523. if (0 != cbLogonName)
  524. {
  525. pEntry->pszLogonName = (LPCTSTR)pData;
  526. CopyMemory(pData, pszLogonName, cbLogonName);
  527. //pData += cbLogonName;
  528. }
  529. // Well-known entries never age out
  530. if (SidTypeWellKnownGroup == SidType || IsAliasSid(pSid))
  531. pEntry->dwLastAccessTime = 0;
  532. else
  533. pEntry->dwLastAccessTime = GetTickCount();
  534. }
  535. TraceLeaveValue(pEntry);
  536. }
  537. BOOL
  538. CSidCache::AddEntry(PSID_CACHE_ENTRY pEntry)
  539. {
  540. BOOL fResult = FALSE;
  541. int iSidBucket;
  542. TraceEnter(TRACE_SIDCACHE, "CSidCache::AddEntry");
  543. TraceAssert(pEntry != NULL);
  544. if (NULL == pEntry)
  545. TraceLeaveValue(FALSE);
  546. iSidBucket = HashSid(pEntry->pSid);
  547. Lock();
  548. if (m_dpaSidHashTable[iSidBucket] == NULL)
  549. m_dpaSidHashTable[iSidBucket] = DPA_Create(4);
  550. if (NULL != m_dpaSidHashTable[iSidBucket])
  551. {
  552. DPA_AppendPtr(m_dpaSidHashTable[iSidBucket], pEntry);
  553. DPA_Sort(m_dpaSidHashTable[iSidBucket], CompareSid, 0);
  554. fResult = TRUE;
  555. }
  556. Unlock();
  557. TraceLeaveValue(fResult);
  558. }
  559. BOOL
  560. CSidCache::BuildUserList(HDPA hEntryList,
  561. LPCTSTR pszServer,
  562. PUSER_LIST *ppUserList)
  563. {
  564. ULONG cEntries;
  565. TCHAR szAliasDomain[MAX_PATH];
  566. PSID_CACHE_ENTRY pEntry;
  567. ULONG cb = 0;
  568. ULONG cSidsLen = 0;
  569. ULONG cbAliasDomain = 0;
  570. ULONG i;
  571. TraceEnter(TRACE_SIDCACHE, "CSidCache::BuildUserList");
  572. TraceAssert(hEntryList != NULL);
  573. TraceAssert(ppUserList != NULL);
  574. cEntries = DPA_GetPtrCount(hEntryList);
  575. TraceAssert(0 != cEntries);
  576. //
  577. // This name replaces "BUILTIN" for Alias SIDs
  578. //
  579. GetDomainName(pszServer, szAliasDomain, ARRAYSIZE(szAliasDomain));
  580. cbAliasDomain = StringByteSize(szAliasDomain);
  581. //
  582. // Add the sizes
  583. //
  584. cb = SIZEOF(USER_LIST) + ((cEntries - 1) * SIZEOF(USER_INFO));
  585. for (i = 0; i < cEntries; i++)
  586. {
  587. pEntry = (PSID_CACHE_ENTRY)DPA_FastGetPtr(hEntryList, i);
  588. TraceAssert(NULL != pEntry);
  589. cSidsLen += ACLUI_ALIGNED_SID_LENGTH(pEntry->pSid);
  590. if (SidTypeAlias == pEntry->SidType)
  591. cb += cbAliasDomain;
  592. else if (pEntry->pszLogonName)
  593. cb += StringByteSize(pEntry->pszLogonName);
  594. if (pEntry->pszName)
  595. cb += StringByteSize(pEntry->pszName);
  596. }
  597. cb += cSidsLen;
  598. //
  599. // Allocate and build the return buffer
  600. //
  601. *ppUserList = (PUSER_LIST)LocalAlloc(LPTR, cb);
  602. if (NULL == *ppUserList)
  603. TraceLeaveValue(FALSE);
  604. (*ppUserList)->cUsers = cEntries;
  605. PBYTE pData = NULL;
  606. PBYTE pCharData = NULL;
  607. //
  608. //NTRAID#NTBUG9-364410-2001/20/23-hiteshr
  609. //Sids were non aligned if cEntries > 1
  610. //
  611. pData = (PBYTE)&(*ppUserList)->rgUsers[cEntries];
  612. pCharData = pData + cSidsLen;
  613. for (i = 0; i < cEntries; i++)
  614. {
  615. pEntry = (PSID_CACHE_ENTRY)DPA_FastGetPtr(hEntryList, i);
  616. TraceAssert(NULL != pEntry);
  617. (*ppUserList)->rgUsers[i].SidType = pEntry->SidType;
  618. TraceAssert(NULL != pEntry->pSid);
  619. (*ppUserList)->rgUsers[i].pSid = (PSID)pData;
  620. cb = GetLengthSid(pEntry->pSid);
  621. CopyMemory(pData, pEntry->pSid, cb);
  622. pData += cb;
  623. if (SidTypeAlias == pEntry->SidType)
  624. {
  625. (*ppUserList)->rgUsers[i].pszLogonName = (LPCTSTR)pCharData;
  626. // Copy the "BUILTIN" domain name
  627. if (cbAliasDomain)
  628. {
  629. CopyMemory(pCharData, szAliasDomain, cbAliasDomain);
  630. pCharData += cbAliasDomain - SIZEOF(TCHAR);
  631. if (NULL != pEntry->pszName)
  632. *(LPTSTR)pCharData = TEXT('\\');
  633. else
  634. *(LPTSTR)pCharData = TEXT('\0');
  635. pCharData += SIZEOF(TCHAR);
  636. }
  637. // The rest of the name is copied below
  638. }
  639. else if (NULL != pEntry->pszLogonName)
  640. {
  641. (*ppUserList)->rgUsers[i].pszLogonName = (LPCTSTR)pCharData;
  642. cb = StringByteSize(pEntry->pszLogonName);
  643. CopyMemory(pCharData, pEntry->pszLogonName, cb);
  644. pCharData += cb;
  645. }
  646. if (NULL != pEntry->pszName)
  647. {
  648. (*ppUserList)->rgUsers[i].pszName = (LPCTSTR)pCharData;
  649. cb = StringByteSize(pEntry->pszName);
  650. CopyMemory(pCharData, pEntry->pszName, cb);
  651. pCharData += cb;
  652. }
  653. }
  654. TraceLeaveValue(TRUE);
  655. }
  656. //
  657. // Wrapper around sspi's TranslateName that automatically handles
  658. // the buffer sizing
  659. //
  660. HRESULT
  661. TranslateNameInternal(LPCTSTR pszAccountName,
  662. EXTENDED_NAME_FORMAT AccountNameFormat,
  663. EXTENDED_NAME_FORMAT DesiredNameFormat,
  664. BSTR *pstrTranslatedName)
  665. {
  666. #if(_WIN32_WINNT >= 0x0500)
  667. #if DBG
  668. //
  669. // These match up with the EXTENDED_NAME_FORMAT enumeration.
  670. // They're for debugger output only.
  671. //
  672. static const LPCTSTR rgpszFmt[] = {
  673. TEXT("NameUnknown"),
  674. TEXT("FullyQualifiedDN"),
  675. TEXT("NameSamCompatible"),
  676. TEXT("NameDisplay"),
  677. TEXT("NameDomainSimple"),
  678. TEXT("NameEnterpriseSimple"),
  679. TEXT("NameUniqueId"),
  680. TEXT("NameCanonical"),
  681. TEXT("NameUserPrincipal"),
  682. TEXT("NameCanonicalEx"),
  683. TEXT("NameServicePrincipal") };
  684. #endif // DBG
  685. TraceEnter(TRACE_SIDCACHE, "TranslateNameInternal");
  686. Trace((TEXT("Calling TranslateName for \"%s\""), pszAccountName));
  687. Trace((TEXT("Translating %s -> %s"),
  688. rgpszFmt[AccountNameFormat], rgpszFmt[DesiredNameFormat]));
  689. if (!pszAccountName || !*pszAccountName || !pstrTranslatedName)
  690. TraceLeaveResult(E_INVALIDARG);
  691. HRESULT hr = NOERROR;
  692. //
  693. // cchTrans is static so that if a particular installation's
  694. // account names are really long, we'll not be resizing the
  695. // buffer for each account.
  696. //
  697. static ULONG cchTrans = MAX_PATH;
  698. ULONG cch = cchTrans;
  699. *pstrTranslatedName = SysAllocStringLen(NULL, cch);
  700. if (NULL == *pstrTranslatedName)
  701. ExitGracefully(hr, E_OUTOFMEMORY, "Unable to allocate name buffer");
  702. **pstrTranslatedName = L'\0';
  703. //
  704. // TranslateName is delay-loaded from secur32.dll using the linker's
  705. // delay-load mechanism. Therefore, wrap with an exception handler.
  706. //
  707. __try
  708. {
  709. while(!::TranslateName(pszAccountName,
  710. AccountNameFormat,
  711. DesiredNameFormat,
  712. *pstrTranslatedName,
  713. &cch))
  714. {
  715. if (ERROR_INSUFFICIENT_BUFFER == GetLastError())
  716. {
  717. Trace((TEXT("Resizing buffer to %d chars"), cch));
  718. if (!SysReAllocStringLen(pstrTranslatedName, NULL, cch))
  719. ExitGracefully(hr, E_OUTOFMEMORY, "Unable to reallocate name buffer");
  720. **pstrTranslatedName = L'\0';
  721. }
  722. else
  723. {
  724. hr = E_FAIL;
  725. break;
  726. }
  727. }
  728. cchTrans = max(cch, cchTrans);
  729. }
  730. __except(EXCEPTION_EXECUTE_HANDLER)
  731. {
  732. hr = E_FAIL;
  733. }
  734. exit_gracefully:
  735. if (FAILED(hr))
  736. {
  737. SysFreeString(*pstrTranslatedName);
  738. *pstrTranslatedName = NULL;
  739. }
  740. TraceLeaveResult(hr);
  741. #else
  742. return E_NOTIMPL;
  743. #endif // _WIN32_WINNT >= 0x0500
  744. }
  745. void
  746. CSidCache::GetUserFriendlyName(LPCTSTR pszSamLogonName,
  747. LPCTSTR pszSamAccountName,
  748. LPCTSTR pszServer,
  749. BOOL bUseSamCompatibleInfo,
  750. BOOL bIsStandalone,
  751. BSTR *pstrLogonName,
  752. BSTR *pstrDisplayName)
  753. {
  754. BSTR strFQDN = NULL;
  755. TraceEnter(TRACE_SIDCACHE, "CSidCache::GetUserFriendlyName");
  756. TraceAssert(NULL != pszSamLogonName);
  757. //
  758. // Start by getting the FQDN. Cracking is most efficient when the
  759. // FQDN is the starting point.
  760. //
  761. // TranslateName takes a while to complete, so bUseSamCompatibleInfo
  762. // should be TRUE whenever possible, e.g. for local accounts on a non-DC
  763. // or anything where we know a FQDN doesn't exist.
  764. //
  765. if (!bUseSamCompatibleInfo &&
  766. FAILED(TranslateNameInternal(pszSamLogonName,
  767. NameSamCompatible,
  768. NameFullyQualifiedDN,
  769. &strFQDN)))
  770. {
  771. //
  772. // No FQDN available for this account. Must be an NT4
  773. // account. Return SAM-compatible info to the caller.
  774. //
  775. bUseSamCompatibleInfo = TRUE;
  776. }
  777. if (NULL != pstrLogonName)
  778. {
  779. *pstrLogonName = NULL;
  780. if (!bUseSamCompatibleInfo)
  781. {
  782. TranslateNameInternal(strFQDN,
  783. NameFullyQualifiedDN,
  784. NameUserPrincipal,
  785. pstrLogonName);
  786. }
  787. }
  788. if (NULL != pstrDisplayName)
  789. {
  790. *pstrDisplayName = NULL;
  791. if (bUseSamCompatibleInfo ||
  792. FAILED(TranslateNameInternal(strFQDN,
  793. NameFullyQualifiedDN,
  794. NameDisplay,
  795. pstrDisplayName)))
  796. {
  797. *pstrDisplayName = GetNT4DisplayName(pszSamLogonName,
  798. pszSamAccountName,
  799. pszServer,
  800. bIsStandalone);
  801. }
  802. }
  803. SysFreeString(strFQDN);
  804. TraceLeaveVoid();
  805. }
  806. BOOL
  807. CSidCache::InternalLookupSids(HDPA hSids,
  808. LPCTSTR pszServer,
  809. LPSECURITYINFO2 psi2,
  810. HDPA hEntryList,
  811. HWND hWndNotify,
  812. UINT uMsgNotify)
  813. {
  814. ULONG cSids;
  815. HDPA hUnknownSids;
  816. PSID_CACHE_ENTRY pEntry;
  817. ULONG i;
  818. TraceEnter(TRACE_SIDCACHE, "CSidCache::InternalLookupSids");
  819. TraceAssert(hSids != NULL);
  820. if (hSids == NULL)
  821. {
  822. SetLastError(ERROR_INVALID_PARAMETER);
  823. TraceLeaveValue(FALSE);
  824. }
  825. cSids = DPA_GetPtrCount(hSids);
  826. TraceAssert(0 != cSids);
  827. hUnknownSids = DPA_Create(4);
  828. if (NULL == hUnknownSids)
  829. TraceLeaveValue(FALSE);
  830. //
  831. // See if any exist in the cache already
  832. //
  833. for (i = 0; i < cSids; i++)
  834. {
  835. pEntry = FindSid((PSID)DPA_FastGetPtr(hSids, i));
  836. if (pEntry)
  837. {
  838. if (hWndNotify)
  839. PostMessage(hWndNotify, uMsgNotify, 0, (LPARAM)pEntry->pSid);
  840. else if (hEntryList)
  841. DPA_AppendPtr(hEntryList, pEntry);
  842. }
  843. else
  844. DPA_AppendPtr(hUnknownSids, DPA_FastGetPtr(hSids, i));
  845. }
  846. //
  847. // Call LSA to lookup any that we don't have cached
  848. //
  849. if (0 != DPA_GetPtrCount(hUnknownSids))
  850. {
  851. if (!psi2 ||
  852. FAILED(LookupSidsFromObject(hUnknownSids, psi2, hEntryList)))
  853. {
  854. LookupSidsHelper(hUnknownSids,
  855. pszServer,
  856. hEntryList,
  857. hWndNotify,
  858. uMsgNotify);
  859. }
  860. }
  861. DPA_Destroy(hUnknownSids);
  862. TraceLeaveValue(TRUE);
  863. }
  864. #if(_WIN32_WINNT >= 0x0500)
  865. #include <adsnms.h> // USER_CLASS_NAME, etc.
  866. #else
  867. #define COMPUTER_CLASS_NAME TEXT("Computer")
  868. #define USER_CLASS_NAME TEXT("User")
  869. #define GROUP_CLASS_NAME TEXT("Group")
  870. #define GLOBALGROUP_CLASS_NAME TEXT("GlobalGroup")
  871. #define LOCALGROUP_CLASS_NAME TEXT("LocalGroup")
  872. #endif
  873. TCHAR const c_szForeignSecurityPrincipal[] = TEXT("foreignSecurityPrincipal");
  874. static const struct
  875. {
  876. LPCTSTR pszClass;
  877. SID_NAME_USE sidType;
  878. } c_aSidClasses[] =
  879. {
  880. USER_CLASS_NAME, SidTypeUser,
  881. GROUP_CLASS_NAME, SidTypeGroup,
  882. GLOBALGROUP_CLASS_NAME, SidTypeGroup,
  883. LOCALGROUP_CLASS_NAME, SidTypeGroup,
  884. COMPUTER_CLASS_NAME, SidTypeComputer,
  885. c_szForeignSecurityPrincipal, SidTypeGroup,
  886. };
  887. SID_NAME_USE
  888. GetSidType(PSID pSid, LPCTSTR pszClass)
  889. {
  890. SID_NAME_USE sidType = SidTypeUnknown;
  891. TraceEnter(TRACE_SIDCACHE, "GetSidType");
  892. if (pSid)
  893. {
  894. TraceAssert(IsValidSid(pSid));
  895. if (EqualSystemSid(pSid, UI_SID_World) || IsCreatorSid(pSid))
  896. TraceLeaveValue(SidTypeWellKnownGroup);
  897. if (IsAliasSid(pSid))
  898. TraceLeaveValue(SidTypeAlias);
  899. if (*GetSidSubAuthorityCount(pSid) == 1 && IsNTAuthority(pSid))
  900. {
  901. DWORD sa = *GetSidSubAuthority(pSid, 0);
  902. if (sa && sa <= SECURITY_RESTRICTED_CODE_RID && sa != SECURITY_LOGON_IDS_RID)
  903. TraceLeaveValue(SidTypeWellKnownGroup);
  904. if (SECURITY_LOCAL_SYSTEM_RID == sa)
  905. TraceLeaveValue(SidTypeWellKnownGroup);
  906. }
  907. }
  908. if (pszClass)
  909. {
  910. // Didn't recognize the SID, try the class name
  911. for (int i = 0; i < ARRAYSIZE(c_aSidClasses); i++)
  912. {
  913. if (!lstrcmpi(pszClass, c_aSidClasses[i].pszClass))
  914. TraceLeaveValue(c_aSidClasses[i].sidType);
  915. }
  916. Trace((TEXT("Unexpected class type: %s"), pszClass));
  917. }
  918. // Don't know what type it is, so take a guess. This is just
  919. // for picking an icon, so it doesn't matter too much.
  920. TraceLeaveValue(SidTypeUser); // SidTypeGroup would be just as valid
  921. }
  922. HRESULT
  923. CSidCache::LookupSidsFromObject(HDPA hSids,
  924. LPSECURITYINFO2 psi2,
  925. HDPA hEntryList)
  926. {
  927. HRESULT hr;
  928. ULONG cSids;
  929. LPDATAOBJECT pdoNames = NULL;
  930. STGMEDIUM medium = {0};
  931. FORMATETC fe = { (CLIPFORMAT)g_cfSidInfoList, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
  932. PSID_INFO_LIST pSidList = NULL;
  933. UINT i;
  934. TraceEnter(TRACE_SIDCACHE, "CSidCache::LookupSidsFromObject");
  935. TraceAssert(hSids != NULL);
  936. TraceAssert(psi2 != NULL);
  937. cSids = DPA_GetPtrCount(hSids);
  938. TraceAssert(cSids != 0);
  939. hr = psi2->LookupSids(cSids, DPA_GetPtrPtr(hSids), &pdoNames);
  940. FailGracefully(hr, "ISecurityInformation2::LookupSids failed");
  941. hr = pdoNames->GetData(&fe, &medium);
  942. FailGracefully(hr, "Unable to get CFSTR_ACLUI_SID_INFO_LIST from DataObject");
  943. pSidList = (PSID_INFO_LIST)GlobalLock(medium.hGlobal);
  944. if (!pSidList)
  945. ExitGracefully(hr, E_FAIL, "Unable to lock stgmedium.hGlobal");
  946. TraceAssert(pSidList->cItems > 0);
  947. for (i = 0; i < pSidList->cItems; i++)
  948. {
  949. PSID_CACHE_ENTRY pEntry = MakeEntry(pSidList->aSidInfo[i].pSid,
  950. GetSidType(pSidList->aSidInfo[i].pSid,
  951. pSidList->aSidInfo[i].pwzClass),
  952. pSidList->aSidInfo[i].pwzCommonName,
  953. pSidList->aSidInfo[i].pwzUPN);
  954. if (pEntry)
  955. {
  956. if (AddEntry(pEntry))
  957. {
  958. if (hEntryList)
  959. DPA_AppendPtr(hEntryList, pEntry);
  960. }
  961. else
  962. LocalFree(pEntry);
  963. }
  964. }
  965. exit_gracefully:
  966. if (pSidList)
  967. GlobalUnlock(medium.hGlobal);
  968. ReleaseStgMedium(&medium);
  969. DoRelease(pdoNames);
  970. TraceLeaveResult(hr);
  971. }
  972. BOOL
  973. CSidCache::LookupSidsHelper(HDPA hSids,
  974. LPCTSTR pszServer,
  975. HDPA hEntryList,
  976. HWND hWndNotify,
  977. UINT uMsgNotify,
  978. BOOL bSecondTry)
  979. {
  980. BOOL fResult = FALSE;
  981. ULONG cSids;
  982. LSA_HANDLE hlsa = NULL;
  983. PLSA_REFERENCED_DOMAIN_LIST pRefDomains = NULL;
  984. PLSA_TRANSLATED_NAME pTranslatedNames = NULL;
  985. DWORD dwStatus;
  986. BOOL bIsDC = FALSE;
  987. BOOL bIsStandalone = FALSE;
  988. HDPA hUnknownSids = NULL;
  989. TraceEnter(TRACE_SIDCACHE, "CSidCache::LookupSidsHelper");
  990. TraceAssert(hSids != NULL);
  991. cSids = DPA_GetPtrCount(hSids);
  992. if (!cSids)
  993. TraceLeaveValue(FALSE);
  994. //
  995. // Call LSA to lookup SIDs for the names
  996. //
  997. hlsa = GetLSAConnection(pszServer, POLICY_LOOKUP_NAMES);
  998. if (NULL == hlsa && NULL != pszServer && !bSecondTry)
  999. {
  1000. // Try the local machine
  1001. pszServer = NULL;
  1002. hlsa = GetLSAConnection(NULL, POLICY_LOOKUP_NAMES);
  1003. }
  1004. if (NULL == hlsa)
  1005. TraceLeaveValue(FALSE);
  1006. dwStatus = LsaLookupSids(hlsa,
  1007. cSids,
  1008. DPA_GetPtrPtr(hSids),
  1009. &pRefDomains,
  1010. &pTranslatedNames);
  1011. bIsStandalone = IsStandalone(pszServer, &bIsDC);
  1012. if (STATUS_SUCCESS == dwStatus || STATUS_SOME_NOT_MAPPED == dwStatus)
  1013. {
  1014. TraceAssert(pTranslatedNames);
  1015. TraceAssert(pRefDomains);
  1016. //
  1017. // Build cache entries with NT4 style names
  1018. //
  1019. for (ULONG i = 0; i < cSids; i++)
  1020. {
  1021. BOOL bTryUPN = TRUE;
  1022. BSTR strLogonName = NULL;
  1023. BSTR strDisplayName = NULL;
  1024. LPTSTR pszDeletedAccount = NULL;
  1025. LPTSTR pszSID = NULL;
  1026. PLSA_TRANSLATED_NAME pLsaName = &pTranslatedNames[i];
  1027. PLSA_TRUST_INFORMATION pLsaDomain = NULL;
  1028. PSID pSid = DPA_FastGetPtr(hSids, i);
  1029. TCHAR szAccountName[MAX_PATH];
  1030. TCHAR szDomainName[MAX_PATH];
  1031. BOOL bNoCache = FALSE;
  1032. szAccountName[0] = TEXT('\0');
  1033. szDomainName[0] = TEXT('\0');
  1034. // Get the referenced domain, if any
  1035. if (pLsaName->DomainIndex >= 0 && pRefDomains)
  1036. {
  1037. TraceAssert((ULONG)pLsaName->DomainIndex < pRefDomains->Entries);
  1038. pLsaDomain = &pRefDomains->Domains[pLsaName->DomainIndex];
  1039. }
  1040. // Make NULL-terminated copies of the domain and account name strings
  1041. CopyUnicodeString(szAccountName, ARRAYSIZE(szAccountName), &pLsaName->Name);
  1042. if (pLsaDomain)
  1043. CopyUnicodeString(szDomainName, ARRAYSIZE(szDomainName), &pLsaDomain->Name);
  1044. // Some optimization to avoid TranslateName when possible
  1045. if (!bIsDC)
  1046. {
  1047. if (bIsStandalone)
  1048. {
  1049. // Non-DC, standalone, therefore no UPN
  1050. bTryUPN = FALSE;
  1051. }
  1052. else if (SidTypeUser == pLsaName->Use)
  1053. {
  1054. TCHAR szTargetDomain[DNLEN];
  1055. szTargetDomain[0] = TEXT('\0');
  1056. GetDomainName(pszServer, szTargetDomain, ARRAYSIZE(szTargetDomain));
  1057. if (CSTR_EQUAL == CompareString(LOCALE_USER_DEFAULT,
  1058. NORM_IGNORECASE,
  1059. szTargetDomain,
  1060. -1,
  1061. szDomainName,
  1062. -1))
  1063. {
  1064. // Local account on non-DC, therefore no UPN
  1065. bTryUPN = FALSE;
  1066. }
  1067. }
  1068. }
  1069. //
  1070. // Build NT4 "domain\user" style name
  1071. //
  1072. if (szDomainName[0] != TEXT('\0'))
  1073. {
  1074. lstrcat(szDomainName, TEXT("\\"));
  1075. lstrcat(szDomainName, szAccountName);
  1076. }
  1077. // What we've got so far is our baseline.
  1078. // Adjust these based on SID type.
  1079. LPTSTR pszName = szAccountName;
  1080. LPTSTR pszLogonName = szDomainName;
  1081. switch (pLsaName->Use)
  1082. {
  1083. case SidTypeUser: // 1
  1084. // Get "User Principal Name" etc.
  1085. GetUserFriendlyName(pszLogonName,
  1086. pszName,
  1087. pszServer,
  1088. !bTryUPN,
  1089. bIsStandalone,
  1090. &strLogonName,
  1091. &strDisplayName);
  1092. if (strLogonName)
  1093. pszLogonName = strLogonName;
  1094. if (strDisplayName)
  1095. pszName = strDisplayName;
  1096. break;
  1097. case SidTypeGroup: // 2
  1098. case SidTypeDomain: // 3
  1099. // nothing
  1100. break;
  1101. case SidTypeAlias: // 4
  1102. if (!IsAliasSid(pSid))
  1103. {
  1104. // Sometimes get SidTypeAlias for non-BUILTIN sids,
  1105. // e.g. Domain Local Groups. Treat these as groups
  1106. // so we don't replace the Domain name.
  1107. // Raid #383755
  1108. pLsaName->Use = SidTypeGroup;
  1109. break;
  1110. }
  1111. // else Fall Through
  1112. case SidTypeWellKnownGroup: // 5
  1113. // No logon name for these
  1114. pszLogonName = NULL;
  1115. break;
  1116. case SidTypeDeletedAccount: // 6
  1117. // Display "Account Deleted(Sid)"
  1118. ConvertSidToStringSid(pSid, &pszSID);
  1119. if(FormatStringID(&pszDeletedAccount,
  1120. ::hModule,
  1121. IDS_SID_DELETED_1,
  1122. pszSID))
  1123. {
  1124. if (pszSID)
  1125. LocalFreeString(&pszSID);
  1126. if (pszDeletedAccount)
  1127. pszName = pszDeletedAccount;
  1128. pszLogonName = NULL;
  1129. }
  1130. else
  1131. {
  1132. bNoCache = TRUE;
  1133. }
  1134. break;
  1135. case SidTypeInvalid: // 7
  1136. bNoCache = TRUE;
  1137. break;
  1138. case SidTypeUnknown: // 8
  1139. // Some SIDs can only be looked up on a DC, so
  1140. // if pszServer is not a DC, remember them and
  1141. // look them up on a DC after this loop is done.
  1142. if (!bSecondTry && !bIsStandalone && !bIsDC)
  1143. {
  1144. if (!hUnknownSids)
  1145. hUnknownSids = DPA_Create(4);
  1146. if (hUnknownSids)
  1147. DPA_AppendPtr(hUnknownSids, pSid);
  1148. bNoCache = TRUE;
  1149. }
  1150. else
  1151. {
  1152. // Display "Account Unknown(Sid)"
  1153. ConvertSidToStringSid(pSid, &pszSID);
  1154. if(FormatStringID(&pszDeletedAccount,
  1155. ::hModule,
  1156. IDS_SID_UNKNOWN_1,
  1157. pszSID))
  1158. {
  1159. if (pszSID)
  1160. LocalFreeString(&pszSID);
  1161. if (pszDeletedAccount)
  1162. pszName = pszDeletedAccount;
  1163. pszLogonName = NULL;
  1164. }
  1165. else
  1166. {
  1167. bNoCache = TRUE;
  1168. }
  1169. }
  1170. break;
  1171. #if(_WIN32_WINNT >= 0x0500)
  1172. case SidTypeComputer: // 9
  1173. if (*pszName)
  1174. {
  1175. // Strip the trailing '$'
  1176. int nLen = lstrlen(pszName);
  1177. if (nLen && pszName[nLen-1] == TEXT('$'))
  1178. {
  1179. pszName[nLen-1] = TEXT('\0');
  1180. }
  1181. }
  1182. break;
  1183. #endif
  1184. }
  1185. if (!bNoCache)
  1186. {
  1187. //
  1188. // Make a cache entry and save it
  1189. //
  1190. PSID_CACHE_ENTRY pEntry = MakeEntry(pSid,
  1191. pLsaName->Use,
  1192. pszName,
  1193. pszLogonName);
  1194. if (pEntry)
  1195. {
  1196. if (AddEntry(pEntry))
  1197. {
  1198. fResult = TRUE; // we added something to the cache
  1199. if (hWndNotify)
  1200. PostMessage(hWndNotify, uMsgNotify, 0, (LPARAM)pEntry->pSid);
  1201. else if (hEntryList)
  1202. DPA_AppendPtr(hEntryList, pEntry);
  1203. }
  1204. else
  1205. LocalFree(pEntry);
  1206. }
  1207. }
  1208. if (strLogonName)
  1209. SysFreeString(strLogonName);
  1210. if (strDisplayName)
  1211. SysFreeString(strDisplayName);
  1212. LocalFreeString(&pszDeletedAccount);
  1213. }
  1214. }
  1215. else if (STATUS_NONE_MAPPED == dwStatus && !bSecondTry && !bIsStandalone && !bIsDC)
  1216. {
  1217. hUnknownSids = DPA_Clone(hSids, NULL);
  1218. }
  1219. // Cleanup
  1220. if (pTranslatedNames)
  1221. LsaFreeMemory(pTranslatedNames);
  1222. if (pRefDomains)
  1223. LsaFreeMemory(pRefDomains);
  1224. LsaClose(hlsa);
  1225. if (hUnknownSids)
  1226. {
  1227. //
  1228. // Some (or all) SIDs were unknown on the target machine,
  1229. // try a DC for the target machine's primary domain.
  1230. //
  1231. // This typically happens for certain Alias SIDs, such
  1232. // as Print Operators and System Operators, for which LSA
  1233. // only returns names if the lookup is done on a DC.
  1234. //
  1235. LPTSTR pszDC = NULL;
  1236. TraceAssert(!bSecondTry);
  1237. // We don't bother trying if standalone, and don't
  1238. // do this if the target machine is already a DC.
  1239. TraceAssert(!bIsStandalone && !bIsDC);
  1240. _GetDcName(pszServer, NULL, &pszDC);
  1241. if (pszDC)
  1242. {
  1243. // Recurse
  1244. if (LookupSidsHelper(hUnknownSids,
  1245. pszDC,
  1246. hEntryList,
  1247. hWndNotify,
  1248. uMsgNotify,
  1249. TRUE))
  1250. {
  1251. fResult = TRUE;
  1252. }
  1253. LocalFree(pszDC);
  1254. }
  1255. DPA_Destroy(hUnknownSids);
  1256. }
  1257. TraceLeaveValue(fResult);
  1258. }
  1259. #if(_WIN32_WINNT >= 0x0500)
  1260. BSTR GetNT4AccountName(LPTSTR pszWinNTPath)
  1261. {
  1262. // pszWinNTPath is expected to look like
  1263. // "WinNT://domain/user"
  1264. // or
  1265. // "WinNT://domain/machine/user"
  1266. //
  1267. // The "WinNT://" part is optional.
  1268. //
  1269. // In either case, we want the last 2 elements,
  1270. // e.g. "domain/user" and "machine/user".
  1271. //
  1272. // The approach is to find the next to last '/' and add 1.
  1273. // If there are less than 2 slashes, return the original string.
  1274. BSTR strResult = NULL;
  1275. LPTSTR pszResult = pszWinNTPath;
  1276. if (pszWinNTPath)
  1277. {
  1278. LPTSTR pszSlash = StrRChr(pszWinNTPath, pszWinNTPath + lstrlen(pszWinNTPath) - 1, TEXT('/'));
  1279. if (pszSlash)
  1280. {
  1281. pszSlash = StrRChr(pszWinNTPath, pszSlash-1, TEXT('/'));
  1282. if (pszSlash)
  1283. pszResult = pszSlash + 1;
  1284. }
  1285. }
  1286. if (pszResult)
  1287. {
  1288. strResult = SysAllocString(pszResult);
  1289. if (strResult)
  1290. {
  1291. // At this point, there is at most one forward slash
  1292. // in the string. Convert it to a backslash.
  1293. LPTSTR pszSlash = StrChr(strResult, TEXT('/'));
  1294. if (pszSlash)
  1295. *pszSlash = TEXT('\\');
  1296. }
  1297. }
  1298. return strResult;
  1299. }
  1300. BOOL
  1301. _LookupName(LPCTSTR pszServer,
  1302. LPCTSTR pszAccount,
  1303. PSID *ppSid,
  1304. SID_NAME_USE *pSidType)
  1305. {
  1306. BOOL fResult = FALSE;
  1307. BYTE buffer[sizeof(SID) + SID_MAX_SUB_AUTHORITIES*sizeof(ULONG)];
  1308. PSID pSid = (PSID)buffer;
  1309. DWORD cbSid = sizeof(buffer);
  1310. TCHAR szDomain[MAX_PATH];
  1311. DWORD cchDomain = ARRAYSIZE(szDomain);
  1312. SID_NAME_USE sidType;
  1313. fResult = LookupAccountName(pszServer,
  1314. pszAccount,
  1315. pSid,
  1316. &cbSid,
  1317. szDomain,
  1318. &cchDomain,
  1319. &sidType);
  1320. if (fResult)
  1321. {
  1322. *ppSid = LocalAllocSid(pSid);
  1323. if (*ppSid)
  1324. {
  1325. if (pSidType)
  1326. *pSidType = sidType;
  1327. }
  1328. else
  1329. fResult = FALSE;
  1330. }
  1331. return fResult;
  1332. }
  1333. BOOL
  1334. CSidCache::InternalLookupNames(PDS_SELECTION_LIST pDsSelList,
  1335. LPCTSTR pszServer,
  1336. HDPA hEntryList,
  1337. BOOL bStandalone)
  1338. {
  1339. BOOL fResult = FALSE;
  1340. ULONG cNames;
  1341. HDPA hSids = NULL;
  1342. PSID_CACHE_ENTRY pEntry;
  1343. ULONG i;
  1344. ULONG cNoSID = 0;
  1345. HRESULT hrCom = E_FAIL;
  1346. IADsPathname *pPath = NULL;
  1347. TraceEnter(TRACE_SIDCACHE, "CSidCache::InternalLookupNames");
  1348. TraceAssert(pDsSelList != NULL);
  1349. TraceAssert(hEntryList != NULL);
  1350. if (pDsSelList == NULL || hEntryList == NULL)
  1351. {
  1352. SetLastError(ERROR_INVALID_PARAMETER);
  1353. TraceLeaveValue(FALSE);
  1354. }
  1355. cNames = pDsSelList->cItems;
  1356. TraceAssert(cNames != 0);
  1357. if (0 == cNames)
  1358. {
  1359. SetLastError(ERROR_INVALID_PARAMETER);
  1360. TraceLeaveValue(FALSE);
  1361. }
  1362. hSids = DPA_Create(4);
  1363. for (i = 0; i < cNames; i++)
  1364. {
  1365. PSID pSid = NULL;
  1366. PSID pSidFree = NULL;
  1367. LPVARIANT pvarSid = pDsSelList->aDsSelection[i].pvarFetchedAttributes;
  1368. SID_NAME_USE sidType = SidTypeUnknown;
  1369. BSTR strNT4Name = NULL;
  1370. if (NULL == pvarSid || (VT_ARRAY | VT_UI1) != V_VT(pvarSid)
  1371. || FAILED(SafeArrayAccessData(V_ARRAY(pvarSid), &pSid)))
  1372. {
  1373. // If there's no SID, then we can't use it in an ACL
  1374. Trace((TEXT("No SID returned for %s"), pDsSelList->aDsSelection[i].pwzADsPath));
  1375. // If it's the NT provider, try to lookup the SID by name
  1376. if (CSTR_EQUAL == CompareString(LOCALE_USER_DEFAULT,
  1377. 0,
  1378. c_szNTProvider,
  1379. NTPROV_LEN,
  1380. pDsSelList->aDsSelection[i].pwzADsPath,
  1381. NTPROV_LEN))
  1382. {
  1383. strNT4Name = GetNT4AccountName(pDsSelList->aDsSelection[i].pwzADsPath + NTPROV_LEN);
  1384. if (strNT4Name)
  1385. {
  1386. Trace((TEXT("Using LSA to lookup SID for %s"), strNT4Name));
  1387. if (_LookupName(pszServer, strNT4Name, &pSidFree, &sidType))
  1388. {
  1389. pSid = pSidFree;
  1390. }
  1391. }
  1392. }
  1393. if (NULL == pSid)
  1394. {
  1395. cNoSID++;
  1396. continue;
  1397. }
  1398. }
  1399. TraceAssert(NULL != pSid);
  1400. // Is it already in the cache?
  1401. pEntry = FindSid(pSid);
  1402. if (pEntry)
  1403. {
  1404. DPA_AppendPtr(hEntryList, pEntry);
  1405. }
  1406. else
  1407. {
  1408. // Not cached, try to make an entry using the info returned
  1409. // by the object picker.
  1410. if (SidTypeUnknown == sidType)
  1411. sidType = GetSidType(pSid, pDsSelList->aDsSelection[i].pwzClass);
  1412. if (!lstrcmpi(c_szForeignSecurityPrincipal, pDsSelList->aDsSelection[i].pwzClass))
  1413. {
  1414. // Object picker returns non-localized names for these (the
  1415. // DS Configuration Container is not localized). Look up the
  1416. // localized name from LSA. 175278
  1417. // This happens automatically below (pEntry is NULL).
  1418. }
  1419. else if (SidTypeAlias == sidType || SidTypeWellKnownGroup == sidType)
  1420. {
  1421. // Only need the name
  1422. pEntry = MakeEntry(pSid,
  1423. sidType,
  1424. pDsSelList->aDsSelection[i].pwzName,
  1425. NULL);
  1426. }
  1427. else if (pDsSelList->aDsSelection[i].pwzUPN && *pDsSelList->aDsSelection[i].pwzUPN)
  1428. {
  1429. // We have both name and UPN
  1430. pEntry = MakeEntry(pSid,
  1431. sidType,
  1432. pDsSelList->aDsSelection[i].pwzName,
  1433. pDsSelList->aDsSelection[i].pwzUPN);
  1434. }
  1435. else if (CSTR_EQUAL == CompareString(LOCALE_USER_DEFAULT,
  1436. 0,
  1437. c_szNTProvider,
  1438. NTPROV_LEN,
  1439. pDsSelList->aDsSelection[i].pwzADsPath,
  1440. NTPROV_LEN))
  1441. {
  1442. // It's downlevel ("WinNT://blah")
  1443. if (NULL == strNT4Name)
  1444. strNT4Name = GetNT4AccountName(pDsSelList->aDsSelection[i].pwzADsPath + NTPROV_LEN);
  1445. if (strNT4Name)
  1446. {
  1447. // We have the NT4 name, now look for a Friendly Name
  1448. BSTR strDisplay = GetNT4DisplayName(strNT4Name,
  1449. pDsSelList->aDsSelection[i].pwzName,
  1450. pszServer,
  1451. bStandalone);
  1452. pEntry = MakeEntry(pSid,
  1453. sidType,
  1454. strDisplay ? strDisplay : pDsSelList->aDsSelection[i].pwzName,
  1455. strNT4Name);
  1456. SysFreeString(strDisplay);
  1457. }
  1458. }
  1459. else
  1460. {
  1461. // It's not a downlevel, so it must be
  1462. // 1. WellKnown/Universal (no ADsPath)
  1463. // or
  1464. // 2. Uplevel ("GC:" or "LDAP:") but
  1465. // has no UPN
  1466. //
  1467. // If it has an ADs path, try to get an
  1468. // NT4 name such as "NTDEV\Domain Users".
  1469. //
  1470. // Note that wellknown things such "Authenticated User"
  1471. // can fall under either 1 or 2 above, depending on what
  1472. // scope it was selected from. That's why we try to pick
  1473. // them off higher up.
  1474. TraceAssert(NULL == strNT4Name);
  1475. if (pDsSelList->aDsSelection[i].pwzADsPath &&
  1476. *pDsSelList->aDsSelection[i].pwzADsPath)
  1477. {
  1478. // DsCrackNames doesn't accept full ADs paths, so use
  1479. // IADsPathname to retrieve the DN (no provider/server).
  1480. if (FAILED(hrCom))
  1481. hrCom = CoInitialize(NULL);
  1482. if (!pPath)
  1483. {
  1484. CoCreateInstance(CLSID_Pathname,
  1485. NULL,
  1486. CLSCTX_INPROC_SERVER,
  1487. IID_IADsPathname,
  1488. (LPVOID*)&pPath);
  1489. }
  1490. if (pPath)
  1491. {
  1492. BSTR strT;
  1493. if (SUCCEEDED(pPath->Set(pDsSelList->aDsSelection[i].pwzADsPath,
  1494. ADS_SETTYPE_FULL)))
  1495. {
  1496. if (SUCCEEDED(pPath->Retrieve(ADS_FORMAT_X500_DN,
  1497. &strT)))
  1498. {
  1499. // Try to get an NT4 account name
  1500. TranslateNameInternal(strT,
  1501. NameFullyQualifiedDN,
  1502. NameSamCompatible,
  1503. &strNT4Name);
  1504. SysFreeString(strT);
  1505. }
  1506. if (!strNT4Name)
  1507. {
  1508. // Retrieve or CrackName failed. Try to build
  1509. // an NT4-style name from the server name.
  1510. if (SUCCEEDED(pPath->Retrieve(ADS_FORMAT_SERVER,
  1511. &strT)))
  1512. {
  1513. TCHAR szNT4Name[MAX_PATH];
  1514. GetDomainName(strT, szNT4Name, ARRAYSIZE(szNT4Name));
  1515. PathAppend(szNT4Name, pDsSelList->aDsSelection[i].pwzName);
  1516. strNT4Name = SysAllocString(szNT4Name);
  1517. SysFreeString(strT);
  1518. }
  1519. }
  1520. }
  1521. }
  1522. }
  1523. pEntry = MakeEntry(pSid,
  1524. sidType,
  1525. pDsSelList->aDsSelection[i].pwzName,
  1526. strNT4Name);
  1527. }
  1528. //
  1529. // Do we have a cache entry yet?
  1530. //
  1531. if (pEntry)
  1532. {
  1533. if (AddEntry(pEntry))
  1534. {
  1535. DPA_AppendPtr(hEntryList, pEntry);
  1536. }
  1537. else
  1538. {
  1539. LocalFree(pEntry);
  1540. pEntry = NULL;
  1541. }
  1542. }
  1543. if (!pEntry && hSids)
  1544. {
  1545. // Look up the SID the hard way
  1546. Trace((TEXT("Using LSA to lookup %s"), pDsSelList->aDsSelection[i].pwzADsPath));
  1547. PSID pSidCopy = LocalAllocSid(pSid);
  1548. if (pSidCopy)
  1549. {
  1550. DPA_AppendPtr(hSids, pSidCopy);
  1551. }
  1552. }
  1553. }
  1554. SysFreeString(strNT4Name);
  1555. if (pSidFree)
  1556. LocalFree(pSidFree);
  1557. else
  1558. SafeArrayUnaccessData(V_ARRAY(pvarSid));
  1559. }
  1560. TraceAssert(0 == cNoSID);
  1561. //
  1562. // Call LSA to lookup names for the SIDs that aren't cached yet
  1563. //
  1564. if (hSids && 0 != DPA_GetPtrCount(hSids))
  1565. LookupSidsHelper(hSids, pszServer, hEntryList);
  1566. if (NULL != hSids)
  1567. DestroyDPA(hSids);
  1568. DoRelease(pPath);
  1569. if (SUCCEEDED(hrCom))
  1570. CoUninitialize();
  1571. TraceLeaveValue(TRUE);
  1572. }
  1573. #endif // #if(_WIN32_WINNT >= 0x0500)
  1574. DWORD WINAPI
  1575. CSidCache::InitThread(LPVOID pvThreadData)
  1576. {
  1577. PSIDCACHE pThis = (PSIDCACHE)pvThreadData;
  1578. // Our caller already gave us a ref on the dll to prevent the race window where
  1579. // we are created but we the dll is freed before we can call LoadLibrary()
  1580. // HINSTANCE hInstThisDll = LoadLibrary(c_szDllName);
  1581. TraceEnter(TRACE_SIDCACHE, "CSidCache::InitThread");
  1582. if (pThis)
  1583. {
  1584. // Lookup some well-known SIDs to pre-load the cache
  1585. HDPA hSids;
  1586. hSids = DPA_Create(COUNT_SYSTEM_SID_TYPES);
  1587. if (hSids)
  1588. {
  1589. for (int i = 0; i < COUNT_SYSTEM_SID_TYPES; i++)
  1590. {
  1591. DPA_AppendPtr(hSids, QuerySystemSid((UI_SystemSid)i));
  1592. }
  1593. pThis->LookupSidsHelper(hSids, NULL, NULL, NULL, 0);
  1594. DPA_Destroy(hSids);
  1595. }
  1596. pThis->Release();
  1597. }
  1598. TraceLeave();
  1599. FreeLibraryAndExitThread(GetModuleHandle(c_szDllName), 0);
  1600. return 0;
  1601. }