Leaked source code of windows server 2003
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.

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