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.

577 lines
19 KiB

  1. #include "pch.h"
  2. #include <atlbase.h>
  3. #include <dsgetdc.h> // DsGetDCName and DS structures
  4. #include <lm.h>
  5. #include "strsafe.h"
  6. #pragma hdrstop
  7. /*-----------------------------------------------------------------------------
  8. / GetGlobalCatalogPath
  9. / --------------------
  10. / Look up the GC using DsGcDcName and return a string containing the path.
  11. /
  12. / In:
  13. / pszServer, server to get the path for
  14. / pszBuffer, cchBuffer = buffer to fill
  15. /
  16. / Out:
  17. / HRESULT
  18. /----------------------------------------------------------------------------*/
  19. #define GC_PREFIX L"GC://"
  20. #define CCH_GC_PREFIX 5
  21. HRESULT GetGlobalCatalogPath(LPCWSTR pszServer, LPWSTR pszPath, INT cchBuffer)
  22. {
  23. HRESULT hres;
  24. DWORD dwres;
  25. PDOMAIN_CONTROLLER_INFOW pdci = NULL;
  26. ULONG uFlags = DS_RETURN_DNS_NAME|DS_DIRECTORY_SERVICE_REQUIRED;
  27. TraceEnter(TRACE_SCOPES, "GetGlobalCatalogPath");
  28. dwres = DsGetDcNameW(pszServer, NULL, NULL, NULL, uFlags, &pdci);
  29. if ( ERROR_NO_SUCH_DOMAIN == dwres )
  30. {
  31. TraceMsg("Trying with rediscovery bit set");
  32. dwres = DsGetDcNameW(pszServer, NULL, NULL, NULL, uFlags|DS_FORCE_REDISCOVERY, &pdci);
  33. }
  34. if ( (NO_ERROR != dwres) || !pdci->DnsForestName )
  35. ExitGracefully(hres, E_UNEXPECTED, "Failed to find the GC");
  36. if ( (lstrlenW(pdci->DnsForestName)+CCH_GC_PREFIX) > cchBuffer )
  37. ExitGracefully(hres, E_UNEXPECTED, "Buffer too small for the GC path");
  38. StrCpyNW(pszPath, GC_PREFIX, cchBuffer);
  39. StrCatBuffW(pszPath, pdci->DnsForestName, cchBuffer);
  40. Trace(TEXT("Resulting GC path is: %s"), pszPath);
  41. hres = S_OK;
  42. exit_gracefully:
  43. NetApiBufferFree(pdci);
  44. TraceLeaveResult(hres);
  45. }
  46. /*-----------------------------------------------------------------------------
  47. / Scope handling
  48. /----------------------------------------------------------------------------*/
  49. typedef struct
  50. {
  51. LPSCOPETHREADDATA ptd; // thread data structure
  52. INT index; // insert index into the visible scope list
  53. INT cScopes; // number of items enumerated
  54. LPWSTR pszDefaultDnsDomain; // default domain to be selected
  55. } ENUMSTATE, * LPENUMSTATE;
  56. /*-----------------------------------------------------------------------------
  57. / _ScopeProc
  58. / ----------
  59. / Handle scope messages from for the scope blocks we have allocated.
  60. /
  61. / In:
  62. / pScope -> refernce to scope block
  63. / uMsg = message
  64. / pVoid = arguments to message / = NULL
  65. /
  66. / Out:
  67. / HRESULT
  68. /----------------------------------------------------------------------------*/
  69. HRESULT CALLBACK _ScopeProc(LPCQSCOPE pScope, UINT uMsg, LPVOID pVoid)
  70. {
  71. HRESULT hres = S_OK;
  72. LPDSQUERYSCOPE pDsQueryScope = (LPDSQUERYSCOPE)pScope;
  73. LPWSTR pScopeADsPath = OBJECT_NAME_FROM_SCOPE(pDsQueryScope);
  74. LPWSTR pScopeObjectClass = OBJECT_CLASS_FROM_SCOPE(pDsQueryScope);
  75. IADsPathname* pDsPathname = NULL;
  76. IDsDisplaySpecifier* pdds = NULL;
  77. BSTR bstrProvider = NULL;
  78. BSTR bstrLeaf = NULL;
  79. WCHAR szBuffer[MAX_PATH];
  80. TraceEnter(TRACE_SCOPES, "_ScopeProc");
  81. switch ( uMsg )
  82. {
  83. case CQSM_INITIALIZE:
  84. case CQSM_RELEASE:
  85. break;
  86. case CQSM_GETDISPLAYINFO:
  87. {
  88. LPCQSCOPEDISPLAYINFO pDisplayInfo = (LPCQSCOPEDISPLAYINFO)pVoid;
  89. LPTSTR pDirectoryName;
  90. TraceAssert(pDisplayInfo);
  91. TraceAssert(pDisplayInfo->pDisplayName);
  92. pDisplayInfo->iIndent = pDsQueryScope->iIndent;
  93. hres = CoCreateInstance(CLSID_Pathname, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IADsPathname, &pDsPathname));
  94. FailGracefully(hres, "Failed to get the IADsPathname interface");
  95. hres = pDsPathname->Set(CComBSTR(pScopeADsPath), ADS_SETTYPE_FULL);
  96. FailGracefully(hres, "Failed to set the path of the name");
  97. hres = pDsPathname->Retrieve(ADS_FORMAT_PROVIDER, &bstrProvider);
  98. FailGracefully(hres, "Failed to get the provider");
  99. Trace(TEXT("Provider name is: %s"), bstrProvider);
  100. if ( !StrCmpW(bstrProvider, L"GC") )
  101. {
  102. TraceMsg("Provider is GC: so changing to Entire Directory");
  103. GetModuleFileName(GLOBAL_HINSTANCE, pDisplayInfo->pIconLocation, pDisplayInfo->cchIconLocation);
  104. pDisplayInfo->iIconResID = -IDI_GLOBALCATALOG;
  105. if ( SUCCEEDED(FormatDirectoryName(&pDirectoryName, GLOBAL_HINSTANCE, IDS_GLOBALCATALOG)) )
  106. {
  107. StrCpyN(pDisplayInfo->pDisplayName, pDirectoryName, pDisplayInfo->cchDisplayName);
  108. LocalFreeString(&pDirectoryName);
  109. }
  110. }
  111. else
  112. {
  113. TraceMsg("Non GC provider, so looking up icon and display name");
  114. //
  115. // get the leaf name for the object we want to display in the scope picker for the
  116. // DS.
  117. //
  118. pDsPathname->SetDisplayType(ADS_DISPLAY_VALUE_ONLY);
  119. if ( SUCCEEDED(pDsPathname->Retrieve(ADS_FORMAT_LEAF, &bstrLeaf)) )
  120. {
  121. StrCpyNW(pDisplayInfo->pDisplayName, bstrLeaf, pDisplayInfo->cchDisplayName);
  122. SysFreeString(bstrLeaf);
  123. }
  124. //
  125. // Now retrieve the display specifier information for the object.
  126. //
  127. hres = CoCreateInstance(CLSID_DsDisplaySpecifier, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IDsDisplaySpecifier, &pdds));
  128. FailGracefully(hres, "Failed to get the IDsDisplaySpecifier object");
  129. pdds->GetIconLocation(pScopeObjectClass, DSGIF_GETDEFAULTICON,
  130. pDisplayInfo->pIconLocation, pDisplayInfo->cchIconLocation,
  131. &pDisplayInfo->iIconResID);
  132. }
  133. break;
  134. }
  135. case CQSM_SCOPEEQUAL:
  136. {
  137. LPDSQUERYSCOPE pDsQueryScope2 = (LPDSQUERYSCOPE)pVoid;
  138. LPWSTR pScopeADsPath2 = OBJECT_NAME_FROM_SCOPE(pDsQueryScope2);
  139. Trace(TEXT("Comparing %s against %s"), pScopeADsPath, pScopeADsPath2);
  140. hres = StrCmpIW(pScopeADsPath, pScopeADsPath2) ? S_FALSE:S_OK;
  141. break;
  142. }
  143. default:
  144. hres = E_NOTIMPL;
  145. break;
  146. }
  147. exit_gracefully:
  148. SysFreeString(bstrProvider);
  149. DoRelease(pDsPathname);
  150. DoRelease(pdds);
  151. TraceLeaveResult(hres);
  152. }
  153. /*-----------------------------------------------------------------------------
  154. / AddScope
  155. / --------
  156. / Given an ADs path, get it converted to a scope block and then
  157. / call the add function to add it to the list of scopes we are going to be using.
  158. /
  159. / In:
  160. / ptd -> SCOPETHREADDATA structure
  161. / pDsQuery -> IQueryHandler interface to be AddRef'd
  162. i = index to insert the scope at
  163. / iIndent = horizontal indent
  164. / pPath -> ADS path to store as the scope
  165. / pObjectClass = object class to select
  166. / fSelect = if the scope should be selected
  167. /
  168. / Out:
  169. / HRESULT
  170. /----------------------------------------------------------------------------*/
  171. HRESULT AddScope(HWND hwndFrame, INT index, INT iIndent, LPWSTR pPath, LPWSTR pObjectClass, BOOL fSelect)
  172. {
  173. HRESULT hres;
  174. LPCQSCOPE pScope = NULL;
  175. TraceEnter(TRACE_SCOPES, "AddScope");
  176. Trace(TEXT("index %d, iIndent %d, fSelect %d"), index, iIndent, fSelect);
  177. Trace(TEXT("Object name: %s"), pPath);
  178. Trace(TEXT("Class: %s"), pObjectClass ? pObjectClass:TEXT("<none>"));
  179. hres = AllocScope(&pScope, iIndent, pPath, pObjectClass);
  180. FailGracefully(hres, "Failed to allocate DSQUERYSCOPE");
  181. if ( !SendMessage(hwndFrame, CQFWM_ADDSCOPE, (WPARAM)pScope, MAKELPARAM(fSelect, index)) )
  182. ExitGracefully(hres, E_FAIL, "Failed when sending ADDSCOPE message");
  183. hres = S_OK; // success
  184. exit_gracefully:
  185. if ( pScope )
  186. CoTaskMemFree(pScope);
  187. TraceLeaveResult(hres);
  188. }
  189. /*-----------------------------------------------------------------------------
  190. / AllocScope
  191. / ----------
  192. / Convert the given ADs path into a scope block that can be passed to the
  193. / common query interfaces.
  194. /
  195. / In:
  196. / iIndent = index to indent the scope by
  197. / ppScope = receives the newly allocated scope block
  198. / pPath -> name to package for the DS scope
  199. / pObjectClass -> object class of scope
  200. /
  201. / Out:
  202. / HRESULT
  203. /----------------------------------------------------------------------------*/
  204. HRESULT AllocScope(LPCQSCOPE* ppScope, INT iIndent, LPWSTR pPath, LPWSTR pObjectClass)
  205. {
  206. HRESULT hres;
  207. LPDSQUERYSCOPE pDsQueryScope = NULL;
  208. IADsPathname* pPathname = NULL;
  209. DWORD cb, offset;
  210. TraceEnter(TRACE_SCOPES, "AllocScope");
  211. Trace(TEXT("indent %d"), iIndent);
  212. Trace(TEXT("pPath: %s"), pPath);
  213. Trace(TEXT("pObjectClass: %s"), pObjectClass);
  214. // Allocate a new structure, note that the buffer for the ADs path is variable
  215. // size and lives at the end of the allocation.
  216. cb = SIZEOF(DSQUERYSCOPE) + StringByteSizeW(pPath) + StringByteSizeW(pObjectClass);;
  217. pDsQueryScope = (LPDSQUERYSCOPE)CoTaskMemAlloc(cb);
  218. TraceAssert(pDsQueryScope);
  219. if ( !pDsQueryScope )
  220. ExitGracefully(hres, E_OUTOFMEMORY, "Failed to allocate scope");
  221. pDsQueryScope->cq.cbStruct = cb;
  222. pDsQueryScope->cq.dwFlags = 0;
  223. pDsQueryScope->cq.pScopeProc = _ScopeProc;
  224. pDsQueryScope->cq.lParam = 0;
  225. pDsQueryScope->iIndent = iIndent;
  226. pDsQueryScope->dwOffsetADsPath = SIZEOF(DSQUERYSCOPE);
  227. pDsQueryScope->dwOffsetClass = 0;
  228. StringByteCopyW(pDsQueryScope, pDsQueryScope->dwOffsetADsPath, pPath);
  229. pDsQueryScope->dwOffsetClass = pDsQueryScope->dwOffsetADsPath + StringByteSizeW(pPath);
  230. StringByteCopyW(pDsQueryScope, pDsQueryScope->dwOffsetClass, pObjectClass);
  231. hres = S_OK; // success
  232. exit_gracefully:
  233. if ( ppScope )
  234. *ppScope = SUCCEEDED(hres) ? (LPCQSCOPE)pDsQueryScope:NULL;
  235. DoRelease(pPathname);
  236. TraceLeaveResult(hres);
  237. }
  238. /*-----------------------------------------------------------------------------
  239. / AddScopesThread
  240. / ---------------
  241. / Gather the scopes in the background and pass them to the
  242. / query window to allow it to populate the view scope controls.
  243. /
  244. / In:
  245. / pThreadParams -> structure that defines out thread information
  246. /
  247. / Out:
  248. / -
  249. /----------------------------------------------------------------------------*/
  250. // Walk the DOMAINDESC structures building ADSI paths and adding
  251. // them as search scopes to the scope list by calling AddScope
  252. // with the ADSI path stored in the domainDesc strucutre. If a domainDesc
  253. // entry has any children then recurse (increasing the indent). Otherwise
  254. // just continue through the piers.
  255. HRESULT _AddFromDomainTree(LPENUMSTATE pState, LPDOMAINDESC pDomainDesc, INT indent)
  256. {
  257. HRESULT hres;
  258. WCHAR szBuffer[MAX_PATH];
  259. DWORD dwIndex;
  260. BOOL fDefault = FALSE;
  261. TraceEnter(TRACE_SCOPES, "_AddFromDomainTree");
  262. while ( pDomainDesc )
  263. {
  264. //
  265. // include the server name in the path we are generating if we have one
  266. //
  267. StrCpyNW(szBuffer, L"LDAP://", ARRAYSIZE(szBuffer));
  268. if ( pState->ptd->pServer )
  269. {
  270. StrCatBuffW(szBuffer, pState->ptd->pServer, ARRAYSIZE(szBuffer));
  271. StrCatBuffW(szBuffer, L"/", ARRAYSIZE(szBuffer));
  272. }
  273. hres = StringCchCat(szBuffer, ARRAYSIZE(szBuffer), pDomainDesc->pszNCName);
  274. FailGracefully(hres, "Failed to compute valid path (buffer too small)");
  275. Trace(TEXT("Scope is: %s"), szBuffer);
  276. //
  277. // now check to see if this is the default scope for the machine
  278. //
  279. if ( pState->pszDefaultDnsDomain )
  280. {
  281. if ( !StrCmpIW(pState->pszDefaultDnsDomain, pDomainDesc->pszName) )
  282. {
  283. TraceMsg("Default domain found in the domain list");
  284. fDefault = TRUE;
  285. }
  286. }
  287. //
  288. // add the scope, bumping the counters are required
  289. //
  290. hres = AddScope(pState->ptd->hwndFrame, pState->index, indent,
  291. szBuffer, pDomainDesc->pszObjectClass, fDefault);
  292. FailGracefully(hres, "Failed to add scope");
  293. pState->index++;
  294. pState->cScopes++; // bump the count before recursing
  295. if ( pDomainDesc->pdChildList )
  296. {
  297. hres = _AddFromDomainTree(pState, pDomainDesc->pdChildList, indent+1);
  298. FailGracefully(hres, "Failed to add children");
  299. }
  300. pDomainDesc = pDomainDesc->pdNextSibling;
  301. }
  302. hres = S_OK;
  303. exit_gracefully:
  304. TraceLeaveResult(hres);
  305. }
  306. DWORD WINAPI AddScopesThread(LPVOID pThreadParams)
  307. {
  308. HRESULT hres, hresCoInit;
  309. LPSCOPETHREADDATA ptd = (LPSCOPETHREADDATA)pThreadParams;
  310. IADs *pDsObject = NULL;
  311. IDsBrowseDomainTree* pDsDomains = NULL;
  312. BSTR bstrObjectClass = NULL;
  313. LPDOMAINTREE pDomainTree = NULL;
  314. ENUMSTATE enumState = { 0 };
  315. WCHAR szPath[MAX_PATH];
  316. WCHAR szDefaultDnsDomain[MAX_PATH];
  317. TraceEnter(TRACE_SCOPES, "AddScopesThread");
  318. hres = hresCoInit = CoInitialize(NULL);
  319. FailGracefully(hres, "Failed in CoInitialize");
  320. // Initialize ready to go and enumerate the scopes from the DS, this can be
  321. // quite a lengthy process therefore we live on a seperate thread.
  322. enumState.ptd = ptd;
  323. //enumState.index = 0;
  324. //enumState.cScopes = 0;
  325. //enumState.pszDefaultDnsDomain = NULL;
  326. // If the caller specified a scope we should be using then add it, if this
  327. // scope is already in the list we will end up select it anyway.
  328. if ( ptd->pDefaultScope )
  329. {
  330. Trace(TEXT("Adding default scope is: %s"), ptd->pDefaultScope);
  331. hres = AdminToolsOpenObject(ptd->pDefaultScope, ptd->pUserName, ptd->pPassword,
  332. ADS_SECURE_AUTHENTICATION,
  333. IID_PPV_ARG(IADs, &pDsObject));
  334. if ( SUCCEEDED(hres) )
  335. {
  336. hres = pDsObject->get_Class(&bstrObjectClass);
  337. FailGracefully(hres, "Failed to get the object class");
  338. hres = AddScope(ptd->hwndFrame, 0, 0, ptd->pDefaultScope, bstrObjectClass, TRUE);
  339. FailGracefully(hres, "Failed to add the default scope during AddScopes");
  340. enumState.cScopes++;
  341. }
  342. }
  343. // Enumerate the GC using the GC: ADSI provider, this allows us to
  344. // have a single scope in the list, and avoids us having to pass
  345. // around the GC path to all and sundry.
  346. if ( SUCCEEDED(GetGlobalCatalogPath(ptd->pServer, szPath, ARRAYSIZE(szPath))) )
  347. {
  348. hres = AddScope(ptd->hwndFrame,
  349. enumState.index, 0,
  350. szPath, GC_OBJECTCLASS,
  351. FALSE);
  352. FailGracefully(hres, "Failed to add GC: too to the scope list");
  353. enumState.index++;
  354. enumState.cScopes++;
  355. }
  356. else if ( ptd->pDefaultScope )
  357. {
  358. //
  359. // get the domain the user has logged into, and use it to generate a default
  360. // scope that we can select in the list.
  361. //
  362. DWORD dwres;
  363. PDOMAIN_CONTROLLER_INFOW pdci = NULL;
  364. ULONG uFlags = DS_RETURN_DNS_NAME|DS_DIRECTORY_SERVICE_REQUIRED;
  365. INT cchDefaultDnsDomain;
  366. TraceMsg("No GC discovered, nor was a default scope, so setting default DNS domain accordingly");
  367. dwres = DsGetDcNameW(ptd->pServer, NULL, NULL, NULL, uFlags, &pdci);
  368. if ( ERROR_NO_SUCH_DOMAIN == dwres )
  369. {
  370. TraceMsg("Trying with rediscovery bit set");
  371. dwres = DsGetDcNameW(ptd->pServer, NULL, NULL, NULL, uFlags|DS_FORCE_REDISCOVERY, &pdci);
  372. }
  373. if ( (NO_ERROR == dwres) && pdci->DomainName && (pdci->Flags & DS_DNS_DOMAIN_FLAG) )
  374. {
  375. Trace(TEXT("Default domain name is: %s"), pdci->DomainName);
  376. StrCpyNW(szDefaultDnsDomain, pdci->DnsForestName, ARRAYSIZE(szDefaultDnsDomain));
  377. cchDefaultDnsDomain = lstrlenW(szDefaultDnsDomain)-1;
  378. if ( cchDefaultDnsDomain && szDefaultDnsDomain[cchDefaultDnsDomain] == L'.' )
  379. {
  380. TraceMsg("Removing trailing . from the DNS name");
  381. szDefaultDnsDomain[cchDefaultDnsDomain] = L'\0';
  382. }
  383. enumState.pszDefaultDnsDomain = szDefaultDnsDomain;
  384. }
  385. NetApiBufferFree(pdci);
  386. }
  387. // Get the IDsBrowseDomainTree interface and ask it for the list of
  388. // trusted domains. Once we have that blob add them to the scope list,
  389. // indenting as requried to indicate the relationship. If we found a GC
  390. // then we must indent further, to indicate that all these are to be found
  391. // in the GC (as it encompases the entire org).
  392. hres = CoCreateInstance(CLSID_DsDomainTreeBrowser, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IDsBrowseDomainTree, &pDsDomains));
  393. if ( SUCCEEDED(hres) )
  394. {
  395. hres = pDsDomains->SetComputer(ptd->pServer, ptd->pUserName, ptd->pPassword);
  396. FailGracefully(hres, "Failed when setting computer in the IDsBrowseDomainTree object");
  397. if ( SUCCEEDED(pDsDomains->GetDomains(&pDomainTree, DBDTF_RETURNFQDN)) )
  398. {
  399. Trace(TEXT("Domain count from GetDomains %d"), pDomainTree->dwCount);
  400. hres = _AddFromDomainTree(&enumState, &pDomainTree->aDomains[0], 0);
  401. FailGracefully(hres, "Failed to add from domain tree");
  402. }
  403. }
  404. hres = S_OK; // success
  405. exit_gracefully:
  406. // Release all our dangly bits
  407. DoRelease(pDsObject);
  408. SysFreeString(bstrObjectClass);
  409. if ( !enumState.cScopes )
  410. {
  411. // we have no scopes, therefore lets inform the user and post a close
  412. // message to the parent window so we can close it.
  413. FormatMsgBox(ptd->hwndFrame,
  414. GLOBAL_HINSTANCE, IDS_WINDOWTITLE, IDS_ERR_NOSCOPES,
  415. MB_OK|MB_ICONERROR);
  416. PostMessage(ptd->hwndFrame, WM_SYSCOMMAND, SC_CLOSE, 0L);
  417. }
  418. else
  419. {
  420. // tell tell the frame we ahve added all the scopes we will, that
  421. // way it can issue the query if the caller wants that.
  422. TraceMsg("Informing frame all scopes have been enumerated");
  423. SendMessage(ptd->hwndFrame, CQFWM_ALLSCOPESADDED, 0, 0);
  424. }
  425. if ( pDsDomains )
  426. {
  427. pDsDomains->FreeDomains(&pDomainTree);
  428. DoRelease(pDsDomains);
  429. }
  430. if ( ptd )
  431. {
  432. LocalFreeStringW(&ptd->pDefaultScope);
  433. SecureLocalFreeStringW(&ptd->pServer);
  434. SecureLocalFreeStringW(&ptd->pUserName);
  435. SecureLocalFreeStringW(&ptd->pPassword);
  436. LocalFree((HLOCAL)ptd);
  437. }
  438. if ( SUCCEEDED(hresCoInit) )
  439. CoUninitialize();
  440. TraceLeave();
  441. DllRelease();
  442. ExitThread(0);
  443. return 0;
  444. }