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.

2022 lines
49 KiB

  1. #include "StdAfx.h"
  2. #include "ADMTScript.h"
  3. #include "DomainContainer.h"
  4. #include "Error.h"
  5. #include "GetDcName.h"
  6. #include "AdsiHelpers.h"
  7. #include "NameCracker.h"
  8. #include <map>
  9. #include <memory>
  10. #include <string>
  11. #include <LM.h>
  12. #include <DsGetDC.h>
  13. #include <DsRole.h>
  14. #include <NtLdap.h>
  15. #include <NtDsAPI.h>
  16. #include <ActiveDS.h>
  17. #include <Sddl.h>
  18. #define NO_WBEM
  19. #include "T_SafeVector.h"
  20. #ifndef tstring
  21. typedef std::basic_string<_TCHAR> tstring;
  22. #endif
  23. using namespace _com_util;
  24. using namespace NAMECRACKER;
  25. namespace _DomainContainer
  26. {
  27. void __stdcall GetNamingAttribute(PCTSTR pszDomain, PCTSTR pszClasses, StringSet& setAttributes);
  28. tstring __stdcall CreateFilter(LPCTSTR pszFilter, const StringSet& setNamingAttributes, const StringSet& setExcludeNames);
  29. bool __stdcall IsClass(LPCTSTR pszClass, const _variant_t& vntClass);
  30. IDispatchPtr GetADsObject(_bstr_t strPath);
  31. void ReportADsError(HRESULT hr, const IID& iid = GUID_NULL);
  32. } // namespace _DomainContainer
  33. using namespace _DomainContainer;
  34. const HRESULT DOMAINCONTAINER_E_TOOMANYLEVELS =
  35. MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x200 + 1);
  36. //---------------------------------------------------------------------------
  37. // Domain Class
  38. //---------------------------------------------------------------------------
  39. // Constructors and Destructor ----------------------------------------------
  40. CDomain::CDomain() :
  41. m_bInitialized(false),
  42. m_bUpLevel(false),
  43. m_bNativeMode(false)
  44. {
  45. }
  46. CDomain::~CDomain()
  47. {
  48. }
  49. // Implementation -----------------------------------------------------------
  50. // Initialize Method
  51. //
  52. // Initializes domain parameters such as DNS domain name, Flat (NETBIOS) domain name,
  53. // forest name, domain controller name.
  54. void CDomain::Initialize(_bstr_t strDomainName)
  55. {
  56. if (!strDomainName)
  57. {
  58. AdmtThrowError(
  59. GUID_NULL, GUID_NULL,
  60. E_INVALIDARG,
  61. IDS_E_DOMAIN_NAME_NOT_SPECIFIED
  62. );
  63. }
  64. // retrieve name of a domain controller in the domain
  65. GetDcName(strDomainName, m_strDcNameDns, m_strDcNameFlat);
  66. // retrieve domain information
  67. PDSROLE_PRIMARY_DOMAIN_INFO_BASIC ppdib;
  68. DWORD dwError = DsRoleGetPrimaryDomainInformation(
  69. DomainControllerName(),
  70. DsRolePrimaryDomainInfoBasic,
  71. (BYTE**)&ppdib
  72. );
  73. if (dwError != NO_ERROR)
  74. {
  75. AdmtThrowError(
  76. GUID_NULL, GUID_NULL,
  77. HRESULT_FROM_WIN32(dwError),
  78. IDS_E_CANT_GET_DOMAIN_INFORMATION,
  79. (LPCTSTR)strDomainName
  80. );
  81. }
  82. // initialize data members from domain information
  83. m_bUpLevel = (ppdib->Flags & DSROLE_PRIMARY_DS_RUNNING) ? true : false;
  84. m_bNativeMode = (m_bUpLevel && !(ppdib->Flags & DSROLE_PRIMARY_DS_MIXED_MODE)) ? true : false;
  85. if (ppdib->DomainNameDns)
  86. {
  87. m_strDomainNameDns = ppdib->DomainNameDns;
  88. }
  89. else
  90. {
  91. m_strDomainNameDns = _bstr_t();
  92. }
  93. if (ppdib->DomainNameFlat)
  94. {
  95. m_strDomainNameFlat = ppdib->DomainNameFlat;
  96. }
  97. else
  98. {
  99. m_strDomainNameFlat = _bstr_t();
  100. }
  101. if (ppdib->DomainForestName)
  102. {
  103. m_strForestName = ppdib->DomainForestName;
  104. }
  105. else
  106. {
  107. m_strForestName = _bstr_t();
  108. }
  109. DsRoleFreeMemory(ppdib);
  110. // initialize ADsPath
  111. if (m_bUpLevel)
  112. {
  113. m_strADsPath = _T("LDAP://") + m_strDomainNameDns;
  114. // retrieve global catalog server name for uplevel domains
  115. // m_strGcName = GetGcName();
  116. }
  117. else
  118. {
  119. m_strADsPath = _T("WinNT://") + m_strDomainNameFlat;
  120. }
  121. // retrieve domain sid
  122. m_strDomainSid = GetSid();
  123. // initialize dispatch interface pointer to active directory object
  124. m_sp = GetADsObject(m_strADsPath);
  125. m_bInitialized = true;
  126. }
  127. // GetDcName Method
  128. //
  129. // Retrieves name of domain controller in the given domain.
  130. void CDomain::GetDcName(_bstr_t strDomainName, _bstr_t& strDnsName, _bstr_t& strFlatName)
  131. {
  132. DWORD dwError = GetDcName5(strDomainName, DS_DIRECTORY_SERVICE_PREFERRED, strDnsName, strFlatName);
  133. if (dwError != NO_ERROR)
  134. {
  135. AdmtThrowError(
  136. GUID_NULL, GUID_NULL,
  137. HRESULT_FROM_WIN32(dwError),
  138. IDS_E_CANT_GET_DOMAIN_CONTROLLER,
  139. (LPCTSTR)strDomainName
  140. );
  141. }
  142. }
  143. // GetGcName Method
  144. //
  145. // Retrieves name of global catalog server.
  146. _bstr_t CDomain::GetGcName()
  147. {
  148. _bstr_t strName;
  149. PDOMAIN_CONTROLLER_INFO pdci;
  150. DWORD dwError = DsGetDcName(NULL, m_strForestName, NULL, NULL, DS_GC_SERVER_REQUIRED, &pdci);
  151. if (dwError == NO_ERROR)
  152. {
  153. strName = pdci->DomainControllerName;
  154. NetApiBufferFree(pdci);
  155. }
  156. else
  157. {
  158. AdmtThrowError(
  159. GUID_NULL, GUID_NULL,
  160. HRESULT_FROM_WIN32(dwError),
  161. IDS_E_CANT_GET_GLOBAL_CATALOG_SERVER,
  162. (LPCTSTR)m_strForestName
  163. );
  164. }
  165. return strName;
  166. }
  167. // GetSid Method
  168. _bstr_t CDomain::GetSid()
  169. {
  170. _bstr_t strSid;
  171. PUSER_MODALS_INFO_2 pumi2;
  172. if (NetUserModalsGet(DomainControllerName(), 2, (LPBYTE*)&pumi2) == NERR_Success)
  173. {
  174. if (IsValidSid(pumi2->usrmod2_domain_id))
  175. {
  176. LPTSTR pszSid;
  177. if (ConvertSidToStringSid(pumi2->usrmod2_domain_id, &pszSid))
  178. {
  179. strSid = pszSid;
  180. LocalFree(LocalHandle(pszSid));
  181. }
  182. }
  183. NetApiBufferFree(pumi2);
  184. }
  185. return strSid;
  186. }
  187. // GetContainer Method
  188. //
  189. // Retrieves a container given it's relative canonical path. Will
  190. // optionally create container if create parameter is true and container
  191. // does not already exist.
  192. CContainer CDomain::GetContainer(_bstr_t strRelativeCanonicalPath, bool bCreate)
  193. {
  194. CContainer aContainer;
  195. // return a non-empty container only if the domain is uplevel and a
  196. // relative canonical path is supplied
  197. if (m_bUpLevel && (strRelativeCanonicalPath.length() > 0))
  198. {
  199. if (bCreate)
  200. {
  201. tstring strPath = strRelativeCanonicalPath;
  202. // initialize parent container
  203. // if path separator is specified than initialize parent
  204. // container from ADsPath otherwise this container is the
  205. // parent container
  206. CContainer aParent;
  207. UINT pos = strPath.find_last_of(_T("/\\"));
  208. if (pos != tstring::npos)
  209. {
  210. aParent = GetLDAPPath(GetDistinguishedName(strPath.substr(0, pos).c_str()));
  211. }
  212. else
  213. {
  214. aParent = *this;
  215. }
  216. tstring strName = strPath.substr(pos + 1);
  217. CADsPathName aPathName(aParent.GetPath());
  218. tstring strRDN = _T("CN=") + strName;
  219. aPathName.AddLeafElement(strRDN.c_str());
  220. IDispatchPtr spDispatch;
  221. HRESULT hr = ADsGetObject(aPathName.Retrieve(ADS_FORMAT_X500), __uuidof(IDispatch), (void**)&spDispatch);
  222. if (SUCCEEDED(hr))
  223. {
  224. aContainer = CContainer(spDispatch);
  225. }
  226. else
  227. {
  228. strRDN = _T("OU=") + strName;
  229. aContainer = aParent.CreateContainer(strRDN.c_str());
  230. }
  231. }
  232. else
  233. {
  234. try
  235. {
  236. aContainer = GetLDAPPath(GetDistinguishedName(strRelativeCanonicalPath));
  237. }
  238. catch (_com_error& ce)
  239. {
  240. AdmtThrowError(GUID_NULL, GUID_NULL, ce, IDS_E_CANNOT_GET_CONTAINER, (LPCTSTR)strRelativeCanonicalPath);
  241. }
  242. }
  243. }
  244. return aContainer;
  245. }
  246. // GetLDAPPath
  247. _bstr_t CDomain::GetLDAPPath(_bstr_t strDN)
  248. {
  249. _bstr_t strPath = _T("LDAP://") + m_strDomainNameDns;
  250. if (strDN.length() > 0)
  251. {
  252. strPath += _T("/") + strDN;
  253. }
  254. return strPath;
  255. }
  256. // GetWinNTPath
  257. _bstr_t CDomain::GetWinNTPath(_bstr_t strName)
  258. {
  259. const _TCHAR c_chEscape = _T('\\');
  260. static _TCHAR s_chSpecial[] = _T("\",/<>");
  261. tstring strPath;
  262. _TCHAR* pchOld = strName;
  263. if (pchOld)
  264. {
  265. std::auto_ptr<_TCHAR> spEscapedName(new _TCHAR[strName.length() * 2 + 1]);
  266. if (spEscapedName.get() == NULL)
  267. {
  268. _com_issue_error(E_OUTOFMEMORY);
  269. }
  270. _TCHAR* pchNew = spEscapedName.get();
  271. while (*pchOld)
  272. {
  273. if (_tcschr(s_chSpecial, *pchOld))
  274. {
  275. *pchNew++ = c_chEscape;
  276. }
  277. *pchNew++ = *pchOld++;
  278. }
  279. *pchNew = _T('\0');
  280. strPath += _T("WinNT://");
  281. strPath += m_strDomainNameFlat;
  282. strPath += _T("/");
  283. strPath += spEscapedName.get();
  284. }
  285. return strPath.c_str();
  286. }
  287. // GetDistinguishedName Method
  288. _bstr_t CDomain::GetDistinguishedName(_bstr_t strRelativeCanonicalPath)
  289. {
  290. _bstr_t strDN;
  291. HRESULT hr = S_OK;
  292. HANDLE hDS;
  293. DWORD dwError = DsBind(DomainControllerName(), NULL, &hDS);
  294. if (dwError == NO_ERROR)
  295. {
  296. _bstr_t strCanonicalName = m_strDomainNameDns + _T("/") + strRelativeCanonicalPath;
  297. LPTSTR psz = strCanonicalName;
  298. PDS_NAME_RESULT pnr;
  299. dwError = DsCrackNames(hDS, DS_NAME_NO_FLAGS, DS_CANONICAL_NAME, DS_FQDN_1779_NAME, 1, &psz, &pnr);
  300. if (dwError == NO_ERROR)
  301. {
  302. if (pnr->rItems[0].status == DS_NAME_NO_ERROR)
  303. {
  304. strDN = pnr->rItems[0].pName;
  305. }
  306. else
  307. {
  308. hr = AdmtSetError(
  309. GUID_NULL, GUID_NULL,
  310. E_INVALIDARG,
  311. IDS_E_CANT_GET_DISTINGUISHED_NAME,
  312. (LPCTSTR)strCanonicalName
  313. );
  314. }
  315. DsFreeNameResult(pnr);
  316. }
  317. else
  318. {
  319. hr = AdmtSetError(
  320. GUID_NULL, GUID_NULL,
  321. HRESULT_FROM_WIN32(dwError),
  322. IDS_E_CANT_GET_DISTINGUISHED_NAME,
  323. (LPCTSTR)strCanonicalName
  324. );
  325. }
  326. DsUnBind(&hDS);
  327. }
  328. else
  329. {
  330. hr = AdmtSetError(
  331. GUID_NULL, GUID_NULL,
  332. HRESULT_FROM_WIN32(dwError),
  333. IDS_E_CANT_CONNECT_TO_DIRECTORY_SERVICE,
  334. (LPCTSTR)m_strDomainNameDns
  335. );
  336. }
  337. if (hr != S_OK)
  338. {
  339. _com_issue_error(hr);
  340. }
  341. return strDN;
  342. }
  343. // CreateContainer Method
  344. CContainer CDomain::CreateContainer(_bstr_t strRDN)
  345. {
  346. CContainer aContainer;
  347. if (m_bUpLevel)
  348. {
  349. aContainer = CContainer::CreateContainer(strRDN);
  350. }
  351. else
  352. {
  353. AdmtThrowError(GUID_NULL, GUID_NULL, E_FAIL, IDS_E_CANT_CREATE_CONTAINER_NT4);
  354. }
  355. return aContainer;
  356. }
  357. // QueryContainers Method
  358. void CDomain::QueryContainers(ContainerVector& rContainers)
  359. {
  360. if (m_bUpLevel)
  361. {
  362. CContainer::QueryContainers(rContainers);
  363. }
  364. }
  365. // QueryUsers Method
  366. void CDomain::QueryUsers(bool bRecurse, StringSet& setExcludeNames, CDomainAccounts& rUsers)
  367. {
  368. if (m_bUpLevel)
  369. {
  370. CContainer::QueryUsers(bRecurse, setExcludeNames, rUsers);
  371. }
  372. else
  373. {
  374. QueryUsers4(setExcludeNames, rUsers);
  375. }
  376. }
  377. // QueryUsers Method
  378. void CDomain::QueryUsers(CContainer& rContainer, StringSet& setIncludeNames, StringSet& setExcludeNames, CDomainAccounts& rUsers)
  379. {
  380. if (m_bUpLevel)
  381. {
  382. QueryObjects(rContainer, setIncludeNames, setExcludeNames, _T("user,inetOrgPerson"), rUsers);
  383. }
  384. else
  385. {
  386. QueryUsers4(setIncludeNames, setExcludeNames, rUsers);
  387. }
  388. }
  389. // QueryGroups Method
  390. void CDomain::QueryGroups(bool bRecurse, StringSet& setExcludeNames, CDomainAccounts& rGroups)
  391. {
  392. if (m_bUpLevel)
  393. {
  394. CContainer::QueryGroups(bRecurse, setExcludeNames, rGroups);
  395. }
  396. else
  397. {
  398. QueryGroups4(setExcludeNames, rGroups);
  399. }
  400. }
  401. // QueryGroups Method
  402. void CDomain::QueryGroups(CContainer& rContainer, StringSet& setIncludeNames, StringSet& setExcludeNames, CDomainAccounts& rGroups)
  403. {
  404. if (m_bUpLevel)
  405. {
  406. QueryObjects(rContainer, setIncludeNames, setExcludeNames, _T("group"), rGroups);
  407. }
  408. else
  409. {
  410. QueryGroups4(setIncludeNames, setExcludeNames, rGroups);
  411. }
  412. }
  413. // QueryComputers Method
  414. void CDomain::QueryComputers(bool bIncludeDCs, bool bRecurse, StringSet& setExcludeNames, CDomainAccounts& rComputers)
  415. {
  416. if (m_bUpLevel)
  417. {
  418. CContainer::QueryComputers(bIncludeDCs, bRecurse, setExcludeNames, rComputers);
  419. }
  420. else
  421. {
  422. QueryComputers4(bIncludeDCs, setExcludeNames, rComputers);
  423. }
  424. }
  425. // QueryComputers Method
  426. void CDomain::QueryComputers(CContainer& rContainer, bool bIncludeDCs, StringSet& setIncludeNames, StringSet& setExcludeNames, CDomainAccounts& rComputers)
  427. {
  428. if (m_bUpLevel)
  429. {
  430. QueryObjects(rContainer, setIncludeNames, setExcludeNames, _T("computer"), rComputers);
  431. if (!bIncludeDCs)
  432. {
  433. for (CDomainAccounts::iterator it = rComputers.begin(); it != rComputers.end();)
  434. {
  435. long lUserAccountControl = it->GetUserAccountControl();
  436. if (lUserAccountControl & ADS_UF_SERVER_TRUST_ACCOUNT)
  437. {
  438. _Module.Log(ErrW, IDS_E_CANNOT_MIGRATE_DOMAIN_CONTROLLERS, (LPCTSTR)it->GetADsPath());
  439. it = rComputers.erase(it);
  440. }
  441. else
  442. {
  443. it++;
  444. }
  445. }
  446. }
  447. }
  448. else
  449. {
  450. QueryComputers4(bIncludeDCs, setIncludeNames, setExcludeNames, rComputers);
  451. }
  452. }
  453. // QueryComputersAcrossDomains Method
  454. void CDomain::QueryComputersAcrossDomains(CContainer& rContainer, bool bIncludeDCs, StringSet& setIncludeNames, StringSet& setExcludeNames, CDomainAccounts& rComputers)
  455. {
  456. CDomainToPathMap mapDomainToPath;
  457. mapDomainToPath.Initialize(m_strDomainNameDns, m_strDomainNameFlat, setIncludeNames);
  458. for (CDomainToPathMap::iterator it = mapDomainToPath.begin(); it != mapDomainToPath.end(); it++)
  459. {
  460. _bstr_t strDomainName = it->first;
  461. try
  462. {
  463. CDomain domain;
  464. domain.Initialize(strDomainName);
  465. domain.QueryComputers(rContainer, bIncludeDCs, it->second, setExcludeNames, rComputers);
  466. }
  467. catch (_com_error& ce)
  468. {
  469. _Module.Log(ErrE, IDS_E_CANNOT_PROCESS_ACCOUNTS_IN_DOMAIN, (LPCTSTR)strDomainName, ce.ErrorMessage(), ce.Error());
  470. }
  471. catch (...)
  472. {
  473. _Module.Log(ErrE, IDS_E_CANNOT_PROCESS_ACCOUNTS_IN_DOMAIN, (LPCTSTR)strDomainName, _com_error(E_FAIL).ErrorMessage(), E_FAIL);
  474. }
  475. }
  476. }
  477. // QueryObjects Method
  478. void CDomain::QueryObjects(CContainer& rContainer, StringSet& setIncludeNames, StringSet& setExcludeNames, LPCTSTR pszClass, CDomainAccounts& rAccounts)
  479. {
  480. // copy specified include names to vector
  481. StringVector vecNames;
  482. for (StringSet::const_iterator itInclude = setIncludeNames.begin(); itInclude != setIncludeNames.end(); itInclude++)
  483. {
  484. vecNames.push_back(tstring(*itInclude));
  485. }
  486. // crack names
  487. CNameCracker cracker;
  488. cracker.SetDomainNames(m_strDomainNameDns, m_strDomainNameFlat, DomainControllerName());
  489. cracker.SetDefaultContainer(IADsContainerPtr(rContainer.GetInterface()));
  490. cracker.CrackNames(vecNames);
  491. // log un-resolved names
  492. const StringVector& vecUnResolved = cracker.GetUnResolvedNames();
  493. for (StringVector::const_iterator itUnResolved = vecUnResolved.begin(); itUnResolved != vecUnResolved.end(); itUnResolved++)
  494. {
  495. _Module.Log(ErrW, IDS_E_CANNOT_RESOLVE_NAME, itUnResolved->c_str());
  496. }
  497. // retrieve naming attributes for classes being migrated
  498. StringSet setNamingAttributes;
  499. GetNamingAttribute(m_strDomainNameDns, pszClass, setNamingAttributes);
  500. // initialize compare exclude names
  501. // we need to separate name and samaccountname excluding lists
  502. StringSet setExcludeRDNs;
  503. StringSet setExcludeSamAccountNames;
  504. cracker.SiftExcludeNames(setExcludeNames, setNamingAttributes, setExcludeRDNs, setExcludeSamAccountNames);
  505. CCompareRDNs csExcludeRDNs(setExcludeRDNs);
  506. CCompareStrings csExcludeSamAccountNames(setExcludeSamAccountNames);
  507. // add resolved accounts
  508. const CStringSet& setResolved = cracker.GetResolvedNames();
  509. CADsPathName pathname;
  510. pathname.Set(_T("LDAP"), ADS_SETTYPE_PROVIDER);
  511. pathname.Set(m_strDomainNameDns, ADS_SETTYPE_SERVER);
  512. CDirectoryObject doObject;
  513. doObject.AddAttribute(ATTRIBUTE_OBJECT_CLASS);
  514. doObject.AddAttribute(ATTRIBUTE_OBJECT_SID);
  515. doObject.AddAttribute(ATTRIBUTE_NAME);
  516. doObject.AddAttribute(ATTRIBUTE_USER_PRINCIPAL_NAME);
  517. doObject.AddAttribute(ATTRIBUTE_SAM_ACCOUNT_NAME);
  518. if (_tcsicmp(pszClass, _T("computer")) == 0)
  519. {
  520. doObject.AddAttribute(_T("dNSHostName"));
  521. }
  522. doObject.AddAttribute(ATTRIBUTE_USER_ACCOUNT_CONTROL);
  523. for (CStringSet::const_iterator itResolved = setResolved.begin(); itResolved != setResolved.end(); itResolved++)
  524. {
  525. try
  526. {
  527. // get active directory service path
  528. // Note: the pathname component will, if necessary, escape any special characters
  529. pathname.Set(itResolved->c_str(), ADS_SETTYPE_DN);
  530. _bstr_t strADsPath = pathname.Retrieve(ADS_FORMAT_X500);
  531. _bstr_t strRDN = pathname.GetElement(0L);
  532. // get object attributes
  533. doObject = (LPCTSTR)strADsPath;
  534. doObject.GetAttributes();
  535. // if the object is of the specified account class...
  536. _variant_t vntClass = doObject.GetAttributeValue(ATTRIBUTE_OBJECT_CLASS);
  537. if (IsClass(pszClass, vntClass))
  538. {
  539. // and it does not represent a built-in account...
  540. _variant_t vntSid = doObject.GetAttributeValue(ATTRIBUTE_OBJECT_SID);
  541. if (IsUserRid(vntSid))
  542. {
  543. // then if name is not in exclusion list...
  544. _bstr_t strName = doObject.GetAttributeValue(ATTRIBUTE_NAME);
  545. _variant_t vntSamAccountName = doObject.GetAttributeValue(ATTRIBUTE_SAM_ACCOUNT_NAME);
  546. if (csExcludeRDNs.IsMatch(strRDN) == false &&
  547. (V_VT(&vntSamAccountName) == VT_EMPTY ||
  548. csExcludeSamAccountNames.IsMatch(_bstr_t(vntSamAccountName)) == false))
  549. {
  550. //
  551. // then add account to account list
  552. //
  553. CDomainAccount daAccount;
  554. // active directory service path
  555. daAccount.SetADsPath(strADsPath);
  556. // name attribute
  557. daAccount.SetName(strName);
  558. // user principal name attribute
  559. _variant_t vntUserPrincipalName = doObject.GetAttributeValue(ATTRIBUTE_USER_PRINCIPAL_NAME);
  560. if (V_VT(&vntUserPrincipalName) != VT_EMPTY)
  561. {
  562. daAccount.SetUserPrincipalName(_bstr_t(vntUserPrincipalName));
  563. }
  564. // sam account name attribute
  565. if (V_VT(&vntSamAccountName) != VT_EMPTY)
  566. {
  567. daAccount.SetSamAccountName(_bstr_t(vntSamAccountName));
  568. }
  569. // DNS host name attribute
  570. _variant_t vntDnsHostName = doObject.GetAttributeValue(_T("dNSHostName"));
  571. if (V_VT(&vntDnsHostName) != VT_EMPTY)
  572. {
  573. daAccount.SetDnsHostName(_bstr_t(vntDnsHostName));
  574. }
  575. // user account control attribute
  576. _variant_t vntUserAccountControl = doObject.GetAttributeValue(ATTRIBUTE_USER_ACCOUNT_CONTROL);
  577. if (V_VT(&vntUserAccountControl) != VT_EMPTY)
  578. {
  579. daAccount.SetUserAccountControl(vntUserAccountControl);
  580. }
  581. rAccounts.insert(daAccount);
  582. }
  583. else
  584. {
  585. _Module.Log(ErrW, IDS_E_ACCOUNT_EXCLUDED, itResolved->c_str());
  586. }
  587. }
  588. else
  589. {
  590. _Module.Log(ErrW, IDS_E_CANT_DO_BUILTIN, itResolved->c_str());
  591. }
  592. }
  593. else
  594. {
  595. _Module.Log(ErrW, IDS_E_OBJECT_NOT_OF_CLASS, itResolved->c_str());
  596. }
  597. }
  598. catch (_com_error& ce)
  599. {
  600. ATLTRACE(_T("'%s' : %s : 0x%08lX\n"), itResolved->c_str(), ce.ErrorMessage(), ce.Error());
  601. }
  602. catch (...)
  603. {
  604. ATLTRACE(_T("'%s' : %s : 0x%08lX\n"), itResolved->c_str(), _com_error(E_FAIL).ErrorMessage(), E_FAIL);
  605. }
  606. }
  607. }
  608. // QueryUsers4 Method
  609. void CDomain::QueryUsers4(StringSet& setExcludeNames, CDomainAccounts& rUsers)
  610. {
  611. CCompareStrings aExclude(setExcludeNames);
  612. DWORD dwIndex = 0;
  613. NET_API_STATUS status;
  614. CDomainAccount aUser;
  615. do
  616. {
  617. DWORD dwCount = 0;
  618. PNET_DISPLAY_USER pdu = NULL;
  619. status = NetQueryDisplayInformation(m_strDcNameFlat, 1, dwIndex, 1000, 32768, &dwCount, (PVOID*)&pdu);
  620. if ((status == ERROR_SUCCESS) || (status == ERROR_MORE_DATA))
  621. {
  622. for (PNET_DISPLAY_USER p = pdu; dwCount > 0; dwCount--, p++)
  623. {
  624. if (p->usri1_user_id >= MIN_NON_RESERVED_RID)
  625. {
  626. _bstr_t strName(p->usri1_name);
  627. if (aExclude.IsMatch(strName) == false)
  628. {
  629. aUser.SetADsPath(GetWinNTPath(strName));
  630. aUser.SetName(strName);
  631. rUsers.insert(aUser);
  632. }
  633. }
  634. dwIndex = p->usri1_next_index;
  635. }
  636. }
  637. if (pdu)
  638. {
  639. NetApiBufferFree(pdu);
  640. }
  641. }
  642. while (status == ERROR_MORE_DATA);
  643. }
  644. // QueryUsers4 Method
  645. void CDomain::QueryUsers4(StringSet& setIncludeNames, StringSet& setExcludeNames, CDomainAccounts& rUsers)
  646. {
  647. CCompareStrings aExclude(setExcludeNames);
  648. CDomainAccount aUser;
  649. for (StringSet::iterator it = setIncludeNames.begin(); it != setIncludeNames.end(); it++)
  650. {
  651. _bstr_t strName = *it;
  652. if (aExclude.IsMatch(strName) == false)
  653. {
  654. _bstr_t strADsPath = GetWinNTPath(strName) + _T(",user");
  655. IADsPtr spADs;
  656. HRESULT hr = ADsGetObject(strADsPath, IID_IADs, (VOID**)&spADs);
  657. if (SUCCEEDED(hr))
  658. {
  659. BSTR bstr;
  660. // The WinNT: provider does not return all ADsPaths correctly escaped
  661. // (ie. it does not escape the double quote (") character)
  662. // The member method GetWinNTPath does escape all known special characters.
  663. #if 0
  664. spADs->get_ADsPath(&bstr);
  665. aUser.SetADsPath(_bstr_t(bstr, false));
  666. #else
  667. aUser.SetADsPath(GetWinNTPath(strName));
  668. #endif
  669. spADs->get_Name(&bstr);
  670. aUser.SetName(_bstr_t(bstr, false));
  671. rUsers.insert(aUser);
  672. }
  673. else
  674. {
  675. _Module.Log(ErrE, IDS_E_CANT_ADD_USER, (LPCTSTR)strADsPath, _com_error(hr).ErrorMessage());
  676. }
  677. }
  678. }
  679. }
  680. // QueryGroups4 Method
  681. void CDomain::QueryGroups4(StringSet& setExcludeNames, CDomainAccounts& rGroups)
  682. {
  683. //
  684. // Construct container helper class.
  685. //
  686. CADsContainer container(m_sp);
  687. //
  688. // Set container filter property so that only groups will be enumerated.
  689. //
  690. LPWSTR pszFilter[] = { L"Group" };
  691. VARIANT varFilter;
  692. VariantInit(&varFilter);
  693. CheckError(ADsBuildVarArrayStr(pszFilter, sizeof(pszFilter) / sizeof(pszFilter[0]), &varFilter));
  694. container.SetFilter(_variant_t(varFilter, false));
  695. //
  696. // Retrieve enumerator interface.
  697. //
  698. IEnumVARIANTPtr spEnum = container.GetEnumerator();
  699. //
  700. // Declare and initialize variables for enumerator next method.
  701. //
  702. VARIANT varGroup;
  703. VariantInit(&varGroup);
  704. ULONG ulFetched = 0;
  705. //
  706. // Declare other variables.
  707. //
  708. _bstr_t strAttrObjectSid(L"ObjectSid");
  709. CCompareStrings aExclude(setExcludeNames);
  710. CDomainAccount aGroup;
  711. //
  712. // For each group...
  713. //
  714. while ((spEnum->Next(1L, &varGroup, &ulFetched) == S_OK) && (ulFetched > 0))
  715. {
  716. CADs group(IADsPtr(_variant_t(varGroup, false)));
  717. //
  718. // If not a built-in group.
  719. //
  720. if (IsUserRid(group.Get(strAttrObjectSid)))
  721. {
  722. //
  723. // If name pattern has not been excluded.
  724. //
  725. _bstr_t strName = group.GetName();
  726. if (aExclude.IsMatch(strName) == false)
  727. {
  728. //
  729. // Add group to set of groups to migrate.
  730. //
  731. aGroup.SetADsPath(GetWinNTPath(strName));
  732. aGroup.SetName(strName);
  733. rGroups.insert(aGroup);
  734. }
  735. }
  736. }
  737. }
  738. // QueryGroups4 Method
  739. void CDomain::QueryGroups4(StringSet& setIncludeNames, StringSet& setExcludeNames, CDomainAccounts& rGroups)
  740. {
  741. CCompareStrings aExclude(setExcludeNames);
  742. CDomainAccount aGroup;
  743. for (StringSet::iterator it = setIncludeNames.begin(); it != setIncludeNames.end(); it++)
  744. {
  745. _bstr_t strName = *it;
  746. if (aExclude.IsMatch(strName) == false)
  747. {
  748. _bstr_t strADsPath = GetWinNTPath(strName) + _T(",group");
  749. IADsPtr spADs;
  750. HRESULT hr = ADsGetObject(strADsPath, IID_IADs, (VOID**)&spADs);
  751. if (SUCCEEDED(hr))
  752. {
  753. BSTR bstr;
  754. spADs->get_ADsPath(&bstr);
  755. aGroup.SetADsPath(_bstr_t(bstr, false));
  756. spADs->get_Name(&bstr);
  757. aGroup.SetName(_bstr_t(bstr, false));
  758. rGroups.insert(aGroup);
  759. }
  760. else
  761. {
  762. _Module.Log(ErrE, IDS_E_CANT_ADD_GROUP, (LPCTSTR)strADsPath, _com_error(hr).ErrorMessage());
  763. }
  764. }
  765. }
  766. }
  767. // QueryComputers4 Method
  768. void CDomain::QueryComputers4(bool bIncludeDCs, StringSet& setExcludeNames, CDomainAccounts& rComputers)
  769. {
  770. CCompareStrings aExclude(setExcludeNames);
  771. DWORD dwIndex = 0;
  772. NET_API_STATUS status;
  773. CDomainAccount aComputer;
  774. DWORD dwflags = bIncludeDCs ? UF_WORKSTATION_TRUST_ACCOUNT|UF_SERVER_TRUST_ACCOUNT : UF_WORKSTATION_TRUST_ACCOUNT;
  775. do
  776. {
  777. DWORD dwCount = 0;
  778. PNET_DISPLAY_MACHINE pdm = NULL;
  779. status = NetQueryDisplayInformation(m_strDcNameFlat, 2, dwIndex, 1000, 32768, &dwCount, (PVOID*)&pdm);
  780. if ((status == ERROR_SUCCESS) || (status == ERROR_MORE_DATA))
  781. {
  782. for (PNET_DISPLAY_MACHINE p = pdm; dwCount > 0; dwCount--, p++)
  783. {
  784. if ((p->usri2_user_id >= MIN_NON_RESERVED_RID) && (p->usri2_flags & dwflags))
  785. {
  786. _bstr_t strName(p->usri2_name);
  787. if (aExclude.IsMatch(strName) == false)
  788. {
  789. aComputer.SetADsPath(GetWinNTPath(strName));
  790. aComputer.SetName(strName);
  791. aComputer.SetSamAccountName(strName);
  792. rComputers.insert(aComputer);
  793. }
  794. }
  795. dwIndex = p->usri2_next_index;
  796. }
  797. }
  798. if (pdm)
  799. {
  800. NetApiBufferFree(pdm);
  801. }
  802. }
  803. while (status == ERROR_MORE_DATA);
  804. }
  805. // QueryComputers4 Method
  806. void CDomain::QueryComputers4(bool bIncludeDCs, StringSet& setIncludeNames, StringSet& setExcludeNames, CDomainAccounts& rComputers)
  807. {
  808. typedef std::map<_bstr_t, DWORD, IgnoreCaseStringLess> CMachineMap;
  809. PNET_DISPLAY_MACHINE pndmMachine = NULL;
  810. try
  811. {
  812. CMachineMap map;
  813. DWORD dwIndex = 0;
  814. NET_API_STATUS nasStatus;
  815. do
  816. {
  817. DWORD dwCount = 0;
  818. nasStatus = NetQueryDisplayInformation(m_strDcNameFlat, 2, dwIndex, 256, 32768, &dwCount, (PVOID*)&pndmMachine);
  819. if ((nasStatus == ERROR_SUCCESS) || (nasStatus == ERROR_MORE_DATA))
  820. {
  821. for (PNET_DISPLAY_MACHINE p = pndmMachine; dwCount > 0; dwCount--, p++)
  822. {
  823. map.insert(CMachineMap::value_type(p->usri2_name, p->usri2_flags));
  824. dwIndex = p->usri2_next_index;
  825. }
  826. }
  827. if (pndmMachine)
  828. {
  829. NetApiBufferFree(pndmMachine);
  830. pndmMachine = NULL;
  831. }
  832. }
  833. while (nasStatus == ERROR_MORE_DATA);
  834. if (nasStatus != ERROR_SUCCESS)
  835. {
  836. AdmtThrowError(
  837. GUID_NULL,
  838. GUID_NULL,
  839. HRESULT_FROM_WIN32(nasStatus),
  840. IDS_E_CANT_ENUMERATE_COMPUTERS,
  841. (LPCTSTR)m_strDomainNameFlat
  842. );
  843. }
  844. CCompareStrings aExclude(setExcludeNames);
  845. for (StringSet::iterator it = setIncludeNames.begin(); it != setIncludeNames.end(); it++)
  846. {
  847. tstring str = *it;
  848. if ((str[0] == _T('\\')) || (str[0] == _T('/')))
  849. {
  850. str = str.substr(1);
  851. }
  852. _bstr_t strName = str.c_str();
  853. if (aExclude.IsMatch(strName) == false)
  854. {
  855. _bstr_t strPath = GetWinNTPath(strName);
  856. CMachineMap::iterator it = map.find(strName + _T("$"));
  857. if (it != map.end())
  858. {
  859. if (bIncludeDCs || !(it->second & UF_SERVER_TRUST_ACCOUNT))
  860. {
  861. CDomainAccount aComputer;
  862. aComputer.SetADsPath(strPath);
  863. aComputer.SetName(strName);
  864. aComputer.SetSamAccountName(strName + _T("$"));
  865. rComputers.insert(aComputer);
  866. }
  867. else
  868. {
  869. _Module.Log(ErrW, IDS_E_CANT_MIGRATE_DOMAIN_CONTROLLERS, (LPCTSTR)strPath);
  870. }
  871. }
  872. else
  873. {
  874. _Module.Log(ErrW, IDS_E_CANT_FIND_COMPUTER, (LPCTSTR)strPath);
  875. }
  876. }
  877. }
  878. }
  879. catch (...)
  880. {
  881. if (pndmMachine)
  882. {
  883. NetApiBufferFree(pndmMachine);
  884. }
  885. throw;
  886. }
  887. }
  888. //---------------------------------------------------------------------------
  889. // Container Class
  890. //---------------------------------------------------------------------------
  891. // Constructors and Destructor ----------------------------------------------
  892. CContainer::CContainer()
  893. {
  894. }
  895. CContainer::CContainer(IDispatchPtr sp) :
  896. m_sp(sp)
  897. {
  898. }
  899. CContainer::CContainer(_bstr_t strPath)
  900. {
  901. HRESULT hr = ADsGetObject(strPath, __uuidof(IDispatch), (void**)&m_sp);
  902. if (FAILED(hr))
  903. {
  904. ReportADsError(hr);
  905. _com_issue_error(hr);
  906. }
  907. }
  908. CContainer::CContainer(const CContainer& r) :
  909. m_sp(r.m_sp)
  910. {
  911. }
  912. CContainer::~CContainer()
  913. {
  914. if (m_sp)
  915. {
  916. m_sp.Release();
  917. }
  918. }
  919. // Implementation -----------------------------------------------------------
  920. // operator =
  921. CContainer& CContainer::operator =(_bstr_t strPath)
  922. {
  923. HRESULT hr = ADsGetObject(strPath, __uuidof(IDispatch), (void**)&m_sp);
  924. if (FAILED(hr))
  925. {
  926. ReportADsError(hr);
  927. _com_issue_error(hr);
  928. }
  929. return *this;
  930. }
  931. // operator =
  932. CContainer& CContainer::operator =(const CContainer& r)
  933. {
  934. m_sp = r.m_sp;
  935. return *this;
  936. }
  937. // GetPath Method
  938. _bstr_t CContainer::GetPath()
  939. {
  940. IDirectoryObjectPtr spObject(m_sp);
  941. PADS_OBJECT_INFO poi;
  942. CheckError(spObject->GetObjectInformation(&poi));
  943. // the ADS_OBJECT_INFO member pszObjectDN actually
  944. // specifies the ADsPath not the distinguished name
  945. _bstr_t strPath = poi->pszObjectDN;
  946. FreeADsMem(poi);
  947. return strPath;
  948. }
  949. // GetDomain Method
  950. _bstr_t CContainer::GetDomain()
  951. {
  952. CADsPathName aPathName(GetPath());
  953. return aPathName.Retrieve(ADS_FORMAT_SERVER);
  954. }
  955. // GetName Method
  956. _bstr_t CContainer::GetName()
  957. {
  958. CDirectoryObject aObject(m_sp);
  959. aObject.AddAttribute(ATTRIBUTE_NAME);
  960. aObject.GetAttributes();
  961. return aObject.GetAttributeValue(ATTRIBUTE_NAME);
  962. }
  963. // GetRDN Method
  964. _bstr_t CContainer::GetRDN()
  965. {
  966. IDirectoryObjectPtr spObject(m_sp);
  967. PADS_OBJECT_INFO poi;
  968. CheckError(spObject->GetObjectInformation(&poi));
  969. _bstr_t strRDN = poi->pszRDN;
  970. FreeADsMem(poi);
  971. return strRDN;
  972. }
  973. // CreateContainerHierarchy Method
  974. // this version serves as a wrapper around the next version
  975. // it converts DOMAINCONTAINER_E_TOOMANYLEVELS back to E_NOTIMPL
  976. void CContainer::CreateContainerHierarchy(CContainer& rSource)
  977. {
  978. try
  979. {
  980. CreateContainerHierarchy(rSource, false);
  981. }
  982. catch (_com_error& ce)
  983. {
  984. if (ce.Error() == DOMAINCONTAINER_E_TOOMANYLEVELS)
  985. {
  986. // restore the original HRESULT
  987. _com_raise_error(E_NOTIMPL, ce.ErrorInfo());
  988. }
  989. else
  990. throw;
  991. }
  992. }
  993. // this version does the clean-up in case the hierarchy gets too deep
  994. void CContainer::CreateContainerHierarchy(CContainer& rSource, bool bParentIsCreated)
  995. {
  996. ContainerVector cvContainers;
  997. ContainerVector createdContainers;
  998. rSource.QueryContainers(cvContainers);
  999. bool bCreated;
  1000. for (ContainerVector::iterator it = cvContainers.begin(); it != cvContainers.end(); it++)
  1001. {
  1002. try
  1003. {
  1004. // always assume that the container exists in the first place
  1005. bCreated = false;
  1006. CContainer aTarget = CreateContainer(_T("OU=") + it->GetName(), bCreated);
  1007. // if the container is actually created, we need to make a note so that
  1008. // we could clean it up if necessary
  1009. if (bCreated) {
  1010. createdContainers.push_back(aTarget);
  1011. }
  1012. // now build the hierarchy under the container just created
  1013. aTarget.CreateContainerHierarchy(*it, bCreated);
  1014. }
  1015. catch (_com_error& ce) {
  1016. // if the parent container is not created, we have to clean up all
  1017. // those containers just created right now; otherwise, we can throw
  1018. // the exception and expect cleanup to happen at the parent level
  1019. if (ce.Error() == DOMAINCONTAINER_E_TOOMANYLEVELS
  1020. && bParentIsCreated == false) {
  1021. for (ContainerVector::iterator cleanUpIterator = createdContainers.begin();
  1022. cleanUpIterator != createdContainers.end();
  1023. cleanUpIterator++)
  1024. {
  1025. // use a recursive delete
  1026. cleanUpIterator->DeleteContainersRecursively();
  1027. }
  1028. }
  1029. throw;
  1030. }
  1031. }
  1032. }
  1033. // GetContainer Method
  1034. CContainer CContainer::GetContainer(_bstr_t strName)
  1035. {
  1036. IDispatchPtr spDispatch;
  1037. CADsPathName aPathName(GetPath());
  1038. // try organizational unit first
  1039. aPathName.AddLeafElement(_T("OU=") + strName);
  1040. HRESULT hr = ADsGetObject(aPathName.Retrieve(ADS_FORMAT_X500), __uuidof(IDispatch), (void**)&spDispatch);
  1041. if (FAILED(hr))
  1042. {
  1043. // if (hr == ?)
  1044. // {
  1045. // then try container
  1046. aPathName.RemoveLeafElement();
  1047. aPathName.AddLeafElement(_T("CN=") + strName);
  1048. CheckError(ADsGetObject(aPathName.Retrieve(ADS_FORMAT_X500), __uuidof(IDispatch), (void**)&spDispatch));
  1049. // }
  1050. // else
  1051. // {
  1052. // _com_issue_error(hr);
  1053. // }
  1054. }
  1055. return CContainer(spDispatch);
  1056. }
  1057. // CreateContainer method
  1058. // use this version if you don't use it for CreateContainerHierarchy
  1059. CContainer CContainer::CreateContainer(_bstr_t strRDN)
  1060. {
  1061. bool bCreated = false;
  1062. CContainer aContainer(NULL);
  1063. try
  1064. {
  1065. aContainer = CreateContainer(strRDN, bCreated);
  1066. }
  1067. catch (_com_error& ce)
  1068. {
  1069. if (ce.Error() == DOMAINCONTAINER_E_TOOMANYLEVELS)
  1070. {
  1071. // restore the original HRESULT
  1072. _com_raise_error(E_NOTIMPL, ce.ErrorInfo());
  1073. }
  1074. else
  1075. throw;
  1076. }
  1077. return aContainer;
  1078. }
  1079. // CreateContainer Method
  1080. // this version is used with CreateContainerHierarchy
  1081. // the flag bCreated tells whether the returned container is created or not
  1082. // this method returns DOMAINCONTAINER_E_TOOMANYLEVELS if the hierarchy is too deep
  1083. CContainer CContainer::CreateContainer(_bstr_t strRDN, bool& bCreated)
  1084. {
  1085. IDispatchPtr spDispatch;
  1086. CADsPathName aPathName(GetPath());
  1087. bCreated = false;
  1088. try
  1089. {
  1090. aPathName.AddLeafElement(strRDN);
  1091. }
  1092. catch (_com_error& ce)
  1093. {
  1094. // E_NOTIMPL returned by IADsPathname::AddLeafElement means
  1095. // too many nested levels
  1096. // we throw DOMAINCONTAINER_E_TOOMANYLEVELS
  1097. if (E_NOTIMPL == ce.Error())
  1098. {
  1099. // we catch the error we throw and replace the HRESULT
  1100. try {
  1101. AdmtThrowError(GUID_NULL, GUID_NULL, ce,
  1102. IDS_E_CANT_CREATE_CONTAINER_THAT_DEEPLY_NESTED,
  1103. (LPCTSTR) strRDN, (LPCTSTR) aPathName.Retrieve(ADS_FORMAT_LEAF));
  1104. }
  1105. catch (_com_error& newCE) {
  1106. _com_raise_error(DOMAINCONTAINER_E_TOOMANYLEVELS, newCE.ErrorInfo());
  1107. }
  1108. }
  1109. else
  1110. throw;
  1111. }
  1112. _bstr_t strPath = aPathName.Retrieve(ADS_FORMAT_X500);
  1113. HRESULT hr = ADsGetObject(strPath, __uuidof(IDispatch), (void**)&spDispatch);
  1114. if (FAILED(hr))
  1115. {
  1116. ADSVALUE valueClass;
  1117. valueClass.dwType = ADSTYPE_CASE_IGNORE_STRING;
  1118. valueClass.CaseIgnoreString = L"organizationalUnit";
  1119. ADS_ATTR_INFO aiAttrs[] =
  1120. {
  1121. { L"objectClass", ADS_ATTR_UPDATE, ADSTYPE_CASE_IGNORE_STRING, &valueClass, 1 },
  1122. };
  1123. DWORD dwAttrCount = sizeof(aiAttrs) / sizeof(aiAttrs[0]);
  1124. IDirectoryObjectPtr spObject(m_sp);
  1125. HRESULT hr = spObject->CreateDSObject(strRDN, aiAttrs, dwAttrCount, &spDispatch);
  1126. if (FAILED(hr))
  1127. {
  1128. AdmtThrowError(GUID_NULL, GUID_NULL, hr, IDS_E_CANNOT_CREATE_CONTAINER, (LPCTSTR)strRDN, (LPCTSTR)GetPath());
  1129. }
  1130. else
  1131. {
  1132. // indicate that this container is actually created
  1133. bCreated = true;
  1134. }
  1135. }
  1136. return CContainer(spDispatch);
  1137. }
  1138. // delete the container
  1139. void CContainer::DeleteContainersRecursively()
  1140. {
  1141. IADsDeleteOpsPtr deleteObject(m_sp);
  1142. HRESULT hr = deleteObject->DeleteObject(0);
  1143. if (FAILED(hr))
  1144. {
  1145. ReportADsError(hr, IID_IDirectoryObject);
  1146. _com_issue_error(hr);
  1147. }
  1148. }
  1149. // QueryContainers Method
  1150. void CContainer::QueryContainers(ContainerVector& rContainers)
  1151. {
  1152. CDirectorySearch aSearch(m_sp);
  1153. aSearch.SetFilter(_T("(|(objectCategory=OrganizationalUnit)(&(objectCategory=Container)(|(cn=Computers)(cn=Users))))"));
  1154. aSearch.SetPreferences(ADS_SCOPE_ONELEVEL);
  1155. aSearch.AddAttribute(ATTRIBUTE_ADS_PATH);
  1156. aSearch.Search();
  1157. for (bool bGet = aSearch.GetFirstRow(); bGet; bGet = aSearch.GetNextRow())
  1158. {
  1159. CContainer aContainer(_bstr_t(aSearch.GetAttributeValue(ATTRIBUTE_ADS_PATH)));
  1160. rContainers.push_back(aContainer);
  1161. }
  1162. }
  1163. // QueryUsers Method
  1164. void CContainer::QueryUsers(bool bRecurse, StringSet& setExcludeNames, CDomainAccounts& rUsers)
  1165. {
  1166. // retrieve naming attributes for user and inetOrgPerson classes
  1167. StringSet setNamingAttributes;
  1168. GetNamingAttribute(GetDomain(), _T("user,inetOrgPerson"), setNamingAttributes);
  1169. tstring strFilter = CreateFilter(
  1170. _T("(objectCategory=Person)(|(objectClass=user)(objectClass=inetOrgPerson))")
  1171. _T("(userAccountControl:") LDAP_MATCHING_RULE_BIT_OR_W _T(":=512)"),
  1172. setNamingAttributes,
  1173. setExcludeNames
  1174. );
  1175. CDirectorySearch aSearch(m_sp);
  1176. aSearch.SetFilter(strFilter.c_str());
  1177. aSearch.SetPreferences(bRecurse ? ADS_SCOPE_SUBTREE : ADS_SCOPE_ONELEVEL);
  1178. aSearch.AddAttribute(ATTRIBUTE_OBJECT_SID);
  1179. aSearch.AddAttribute(ATTRIBUTE_ADS_PATH);
  1180. aSearch.AddAttribute(ATTRIBUTE_NAME);
  1181. aSearch.AddAttribute(ATTRIBUTE_USER_PRINCIPAL_NAME);
  1182. aSearch.Search();
  1183. CDomainAccount aUser;
  1184. for (bool bGet = aSearch.GetFirstRow(); bGet; bGet = aSearch.GetNextRow())
  1185. {
  1186. // if not a built-in or well known account
  1187. if (IsUserRid(aSearch.GetAttributeValue(ATTRIBUTE_OBJECT_SID)))
  1188. {
  1189. // add user
  1190. aUser.SetADsPath(_bstr_t(aSearch.GetAttributeValue(ATTRIBUTE_ADS_PATH)));
  1191. aUser.SetName(_bstr_t(aSearch.GetAttributeValue(ATTRIBUTE_NAME)));
  1192. _variant_t vntUserPrincipalName = aSearch.GetAttributeValue(ATTRIBUTE_USER_PRINCIPAL_NAME);
  1193. if (V_VT(&vntUserPrincipalName) != VT_EMPTY)
  1194. {
  1195. aUser.SetUserPrincipalName(_bstr_t(vntUserPrincipalName));
  1196. }
  1197. rUsers.insert(aUser);
  1198. }
  1199. }
  1200. }
  1201. // QueryGroups Method
  1202. void CContainer::QueryGroups(bool bRecurse, StringSet& setExcludeNames, CDomainAccounts& rGroups)
  1203. {
  1204. // retrieve naming attribute for group
  1205. StringSet setNamingAttributes;
  1206. GetNamingAttribute(GetDomain(), _T("group"), setNamingAttributes);
  1207. tstring strFilter = CreateFilter(_T("(objectCategory=Group)"), setNamingAttributes, setExcludeNames);
  1208. CDirectorySearch aSearch(m_sp);
  1209. aSearch.SetFilter(strFilter.c_str());
  1210. aSearch.SetPreferences(bRecurse ? ADS_SCOPE_SUBTREE : ADS_SCOPE_ONELEVEL);
  1211. aSearch.AddAttribute(ATTRIBUTE_OBJECT_SID);
  1212. aSearch.AddAttribute(ATTRIBUTE_ADS_PATH);
  1213. aSearch.AddAttribute(ATTRIBUTE_NAME);
  1214. aSearch.Search();
  1215. CDomainAccount aGroup;
  1216. for (bool bGet = aSearch.GetFirstRow(); bGet; bGet = aSearch.GetNextRow())
  1217. {
  1218. // if not a built-in or well known account
  1219. if (IsUserRid(aSearch.GetAttributeValue(ATTRIBUTE_OBJECT_SID)))
  1220. {
  1221. // add group
  1222. aGroup.SetADsPath(_bstr_t(aSearch.GetAttributeValue(ATTRIBUTE_ADS_PATH)));
  1223. aGroup.SetName(_bstr_t(aSearch.GetAttributeValue(ATTRIBUTE_NAME)));
  1224. rGroups.insert(aGroup);
  1225. }
  1226. }
  1227. }
  1228. // QueryComputers Method
  1229. void CContainer::QueryComputers(bool bIncludeDCs, bool bRecurse, StringSet& setExcludeNames, CDomainAccounts& rComputers)
  1230. {
  1231. // retrieve naming attribute for computer
  1232. StringSet setNamingAttributes;
  1233. GetNamingAttribute(GetDomain(), _T("computer"), setNamingAttributes);
  1234. tstring strFilter;
  1235. // ADS_UF_WORKSTATION_TRUST_ACCOUNT = 0x1000
  1236. // ADS_UF_SERVER_TRUST_ACCOUNT = 0x2000
  1237. if (bIncludeDCs)
  1238. {
  1239. strFilter = CreateFilter(
  1240. _T("(objectCategory=Computer)")
  1241. _T("(userAccountControl:") LDAP_MATCHING_RULE_BIT_OR_W _T(":=4096)"),
  1242. setNamingAttributes,
  1243. setExcludeNames
  1244. );
  1245. }
  1246. else
  1247. {
  1248. strFilter = CreateFilter(
  1249. _T("(objectCategory=Computer)")
  1250. _T("(|(userAccountControl:") LDAP_MATCHING_RULE_BIT_OR_W _T(":=4096)")
  1251. _T("(userAccountControl:") LDAP_MATCHING_RULE_BIT_OR_W _T(":=8192))"),
  1252. setNamingAttributes,
  1253. setExcludeNames
  1254. );
  1255. }
  1256. CDirectorySearch aSearch(m_sp);
  1257. aSearch.SetFilter(strFilter.c_str());
  1258. aSearch.SetPreferences(bRecurse ? ADS_SCOPE_SUBTREE : ADS_SCOPE_ONELEVEL);
  1259. aSearch.AddAttribute(ATTRIBUTE_OBJECT_SID);
  1260. aSearch.AddAttribute(ATTRIBUTE_ADS_PATH);
  1261. aSearch.AddAttribute(ATTRIBUTE_NAME);
  1262. aSearch.AddAttribute(ATTRIBUTE_SAM_ACCOUNT_NAME);
  1263. aSearch.AddAttribute(_T("dNSHostName"));
  1264. aSearch.Search();
  1265. for (bool bGet = aSearch.GetFirstRow(); bGet; bGet = aSearch.GetNextRow())
  1266. {
  1267. // if not a built-in or well known account
  1268. if (IsUserRid(aSearch.GetAttributeValue(ATTRIBUTE_OBJECT_SID)))
  1269. {
  1270. // add computer
  1271. CDomainAccount aComputer;
  1272. aComputer.SetADsPath(_bstr_t(aSearch.GetAttributeValue(ATTRIBUTE_ADS_PATH)));
  1273. aComputer.SetName(_bstr_t(aSearch.GetAttributeValue(ATTRIBUTE_NAME)));
  1274. aComputer.SetSamAccountName(_bstr_t(aSearch.GetAttributeValue(ATTRIBUTE_SAM_ACCOUNT_NAME)));
  1275. _variant_t vntDnsHostName = aSearch.GetAttributeValue(_T("dNSHostName"));
  1276. if (V_VT(&vntDnsHostName) != VT_EMPTY)
  1277. {
  1278. aComputer.SetDnsHostName(_bstr_t(vntDnsHostName));
  1279. }
  1280. rComputers.insert(aComputer);
  1281. }
  1282. }
  1283. }
  1284. //---------------------------------------------------------------------------
  1285. namespace _DomainContainer
  1286. {
  1287. //------------------------------------------------------------------------------
  1288. // GetNamingAttribute Function
  1289. //
  1290. // Synopsis
  1291. // Retrieves naming attributes for specified classes from specified domain.
  1292. //
  1293. // Arguments
  1294. // pszDomain - specifies which domain's schema to retrieve the attributes
  1295. // pszClasses - a comma separated list of classes
  1296. // setAttributes - the set of naming attributes which this function fills in
  1297. //
  1298. // Return
  1299. // None - an exception is thrown if any errors occur.
  1300. //------------------------------------------------------------------------------
  1301. void __stdcall GetNamingAttribute(PCTSTR pszDomain, PCTSTR pszClasses, StringSet& setAttributes)
  1302. {
  1303. _ASSERT(pszDomain != NULL);
  1304. _ASSERT(pszClasses != NULL);
  1305. tstring strADsPath;
  1306. //
  1307. // Bind to rootDSE.
  1308. //
  1309. IADsPtr spRootDSE;
  1310. strADsPath = _T("LDAP://");
  1311. strADsPath += pszDomain;
  1312. strADsPath += _T("/rootDSE");
  1313. CheckError(ADsGetObject(strADsPath.c_str(), __uuidof(IADs), (VOID**)&spRootDSE));
  1314. //
  1315. // Retrieve schema naming context.
  1316. //
  1317. VARIANT var;
  1318. VariantInit(&var);
  1319. CheckError(spRootDSE->Get(_bstr_t(L"schemaNamingContext"), &var));
  1320. _bstr_t strSchemaNamingContext = _variant_t(var, false);
  1321. //
  1322. // Bind to schema naming context.
  1323. //
  1324. IDispatchPtr spDispatch;
  1325. strADsPath = _T("LDAP://");
  1326. strADsPath += pszDomain;
  1327. strADsPath += _T("/");
  1328. strADsPath += strSchemaNamingContext;
  1329. CheckError(ADsGetObject(strADsPath.c_str(), __uuidof(IDispatch), (VOID**)&spDispatch));
  1330. //
  1331. // Search for specified classes and retrieve rDNAttID attribute for each class.
  1332. //
  1333. // generate search filter string
  1334. int nCount;
  1335. tstring strClass;
  1336. tstring strClassFilter;
  1337. tstring strClasses = pszClasses;
  1338. for (nCount = 0; strClasses.empty() == false; nCount++)
  1339. {
  1340. UINT uDelimiter = strClasses.find_first_of(_T(','));
  1341. if (uDelimiter != tstring::npos)
  1342. {
  1343. strClass = strClasses.substr(0, uDelimiter);
  1344. strClasses = strClasses.substr(uDelimiter + 1);
  1345. }
  1346. else
  1347. {
  1348. strClass = strClasses;
  1349. strClasses.erase();
  1350. }
  1351. strClassFilter += _T("(lDAPDisplayName=") + strClass + _T(")");
  1352. }
  1353. _ASSERT(nCount > 0);
  1354. tstring strSearchFilter;
  1355. if (nCount == 1)
  1356. {
  1357. strSearchFilter = _T("(&(objectClass=classSchema)") + strClassFilter + _T("(!isDefunct=TRUE))");
  1358. }
  1359. else
  1360. {
  1361. strSearchFilter = _T("(&(objectClass=classSchema)(|") + strClassFilter + _T(")(!isDefunct=TRUE))");
  1362. }
  1363. // search
  1364. CDirectorySearch search(spDispatch);
  1365. search.SetFilter(strSearchFilter.c_str());
  1366. search.SetPreferences(ADS_SCOPE_SUBTREE);
  1367. search.AddAttribute(_T("rDNAttID"));
  1368. search.Search();
  1369. // for each class add naming attribute to returned set
  1370. if (search.GetFirstRow())
  1371. {
  1372. do
  1373. {
  1374. setAttributes.insert(_bstr_t(search.GetAttributeValue(_T("rDNAttID"))));
  1375. }
  1376. while (search.GetNextRow());
  1377. }
  1378. }
  1379. // CreateFilter Method
  1380. tstring __stdcall CreateFilter(LPCTSTR pszFilter, const StringSet& setNamingAttributes, const StringSet& setExcludeNames)
  1381. {
  1382. tstring strFilter;
  1383. strFilter += _T("(&");
  1384. strFilter += pszFilter;
  1385. if (!setExcludeNames.empty())
  1386. {
  1387. strFilter += _T("(!(|");
  1388. for (StringSet::const_iterator it = setExcludeNames.begin(); it != setExcludeNames.end(); it++)
  1389. {
  1390. const _bstr_t& strPattern = *it;
  1391. PCTSTR pszPattern = strPattern;
  1392. if (pszPattern)
  1393. {
  1394. //
  1395. // If the exclude pattern contains an RDN delimiter character
  1396. // then assume RDN exclude pattern otherwise assume sAMAccountName
  1397. // exclude pattern.
  1398. //
  1399. tstring str = pszPattern;
  1400. UINT uDelimiter = str.find_first_of(RDN_DELIMITER);
  1401. if (uDelimiter == 0)
  1402. {
  1403. //
  1404. // The RDN delimiter character must follow a valid naming attribute
  1405. // therefore if the delimiter occurs at the beginning of the pattern
  1406. // then an error must be generated.
  1407. //
  1408. AdmtThrowError(GUID_NULL, GUID_NULL, E_INVALIDARG, IDS_E_INVALID_FILTER_STRING, pszPattern);
  1409. }
  1410. else if (uDelimiter != tstring::npos)
  1411. {
  1412. //
  1413. // Verify exclude pattern contains a valid naming attribute. The naming attribute
  1414. // must match the naming attribute(s) for the class(es) of objects being migrated.
  1415. //
  1416. tstring strNamingAttribute = str.substr(0, uDelimiter);
  1417. bool bValidNamingAttribute = false;
  1418. StringSet::const_iterator itNA;
  1419. for (itNA = setNamingAttributes.begin(); itNA != setNamingAttributes.end(); itNA++)
  1420. {
  1421. if (_tcsicmp(strNamingAttribute.c_str(), *itNA) == 0)
  1422. {
  1423. bValidNamingAttribute = true;
  1424. break;
  1425. }
  1426. }
  1427. if (bValidNamingAttribute == false)
  1428. {
  1429. AdmtThrowError(GUID_NULL, GUID_NULL, E_INVALIDARG, IDS_E_INVALID_FILTER_STRING, pszPattern);
  1430. }
  1431. strFilter += _T("(");
  1432. strFilter += str;
  1433. strFilter += _T(")");
  1434. }
  1435. else
  1436. {
  1437. strFilter += _T("(sAMAccountName=");
  1438. strFilter += str;
  1439. strFilter += _T(")");
  1440. }
  1441. }
  1442. }
  1443. strFilter += _T("))");
  1444. }
  1445. strFilter += _T(")");
  1446. return strFilter;
  1447. }
  1448. // IsClass
  1449. bool __stdcall IsClass(LPCTSTR pszClass, const _variant_t& vntClass)
  1450. {
  1451. bool bIs = false;
  1452. BSTR bstrObjectClass = NULL;
  1453. if (V_VT(&vntClass) == VT_BSTR)
  1454. {
  1455. bstrObjectClass = V_BSTR(&vntClass);
  1456. }
  1457. else if (V_VT(&vntClass) == (VT_ARRAY|VT_BSTR))
  1458. {
  1459. SAFEARRAY* psa = V_ARRAY(&vntClass);
  1460. if (psa->cDims == 1)
  1461. {
  1462. BSTR* pbstr = reinterpret_cast<BSTR*>(psa->pvData);
  1463. DWORD cbstr = psa->rgsabound[0].cElements;
  1464. if (pbstr)
  1465. {
  1466. bstrObjectClass = pbstr[cbstr - 1];
  1467. }
  1468. }
  1469. }
  1470. else
  1471. {
  1472. _ASSERT(FALSE);
  1473. }
  1474. if (bstrObjectClass)
  1475. {
  1476. LPCTSTR pszBeg = pszClass;
  1477. while (pszBeg)
  1478. {
  1479. LPCTSTR pszEnd = _tcschr(pszBeg, _T(','));
  1480. if (pszEnd)
  1481. {
  1482. if (_tcsnicmp(pszBeg, bstrObjectClass, pszEnd - pszBeg) == 0)
  1483. {
  1484. bIs = true;
  1485. break;
  1486. }
  1487. pszBeg = pszEnd + 1;
  1488. }
  1489. else
  1490. {
  1491. if (_tcsicmp(pszBeg, bstrObjectClass) == 0)
  1492. {
  1493. bIs = true;
  1494. }
  1495. pszBeg = NULL;
  1496. }
  1497. }
  1498. }
  1499. return bIs;
  1500. }
  1501. // GetADsObject
  1502. IDispatchPtr GetADsObject(_bstr_t strPath)
  1503. {
  1504. IDispatch* pdisp;
  1505. HRESULT hr = ADsGetObject(strPath, __uuidof(IDispatch), (void**)&pdisp);
  1506. if (FAILED(hr))
  1507. {
  1508. ReportADsError(hr);
  1509. _com_issue_error(hr);
  1510. }
  1511. return IDispatchPtr(pdisp, false);
  1512. }
  1513. // ReportADsError
  1514. void ReportADsError(HRESULT hr, const IID& iid)
  1515. {
  1516. DWORD dwError;
  1517. WCHAR szName[256];
  1518. WCHAR szError[256];
  1519. ADsGetLastError(&dwError, szError, sizeof(szError) / sizeof(szError[0]), szName, sizeof(szName) / sizeof(szName[0]));
  1520. AtlReportError(GUID_NULL, szError, iid, hr);
  1521. }
  1522. }