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

703 lines
16 KiB

  1. //
  2. // Implementation of ICloneSecurityPrincipal::Connect
  3. //
  4. // sburns 5-10-99
  5. #include "headers.hxx"
  6. #include "resource.h"
  7. #include "common.hpp"
  8. #include "implmain.hpp"
  9. CloneSecurityPrincipal::Connection::Connection()
  10. :
  11. dstComputer(0),
  12. dstDomainSamHandle(INVALID_HANDLE_VALUE),
  13. dstDsBindHandle(INVALID_HANDLE_VALUE),
  14. m_pldap(0),
  15. srcComputer(0),
  16. srcDcDnsName(),
  17. srcDomainSamHandle(INVALID_HANDLE_VALUE)
  18. {
  19. LOG_CTOR(CloneSecurityPrincipal::Connection);
  20. }
  21. CloneSecurityPrincipal::Connection::~Connection()
  22. {
  23. LOG_DTOR(CloneSecurityPrincipal::Connection);
  24. Disconnect();
  25. }
  26. HRESULT
  27. ValidateDCAndDomainParameters(
  28. const String& srcDC,
  29. const String& srcDomain,
  30. const String& dstDC,
  31. const String& dstDomain)
  32. {
  33. LOG_FUNCTION(ValidateDCAndDomainParameters);
  34. HRESULT hr = S_OK;
  35. do
  36. {
  37. if (srcDC.empty() && srcDomain.empty())
  38. {
  39. hr = E_INVALIDARG;
  40. SetComError(IDS_MUST_SPECIFY_SRC_DC_OR_DOMAIN);
  41. BREAK_ON_FAILED_HRESULT(hr);
  42. }
  43. if (dstDC.empty() && dstDomain.empty())
  44. {
  45. hr = E_INVALIDARG;
  46. SetComError(IDS_MUST_SPECIFY_DST_DC_OR_DOMAIN);
  47. BREAK_ON_FAILED_HRESULT(hr);
  48. }
  49. if (!srcDC.empty() && !dstDC.empty())
  50. {
  51. if (srcDC.icompare(dstDC) == 0)
  52. {
  53. // may not be the same dc
  54. hr = E_INVALIDARG;
  55. SetComError(IDS_SRC_DC_EQUALS_DST_DC);
  56. BREAK_ON_FAILED_HRESULT(hr);
  57. }
  58. }
  59. if (!srcDomain.empty() && dstDomain.empty())
  60. {
  61. if (srcDomain.icompare(dstDomain) == 0)
  62. {
  63. // may not be the same domain
  64. hr = E_INVALIDARG;
  65. SetComError(IDS_SRC_DOMAIN_EQUALS_DST_DOMAIN);
  66. BREAK_ON_FAILED_HRESULT(hr);
  67. }
  68. }
  69. }
  70. while (0);
  71. return hr;
  72. }
  73. // Creates a Computer object representing the domain controller specified, or
  74. // located domain controller for the domain specified. Does additional
  75. // validation of the dc and domain parameters.
  76. HRESULT
  77. CreateComputer(
  78. const String& dc,
  79. const String& domain,
  80. Computer*& computer)
  81. {
  82. LOG_FUNCTION(CreateComputer);
  83. ASSERT(computer == 0);
  84. computer = 0;
  85. HRESULT hr = S_OK;
  86. do
  87. {
  88. if (dc.empty())
  89. {
  90. // source DC was not specified: find a writeable DC
  91. // must have supplied the source domain: we checked for that
  92. // in an earlier call to ValidateDCAndDomainParameters
  93. ASSERT(!domain.empty());
  94. if (domain.empty())
  95. {
  96. hr = E_INVALIDARG;
  97. SetComError(IDS_MUST_SPECIFY_SRC_DC_OR_DOMAIN);
  98. break;
  99. }
  100. DOMAIN_CONTROLLER_INFO* info = 0;
  101. hr =
  102. Win32ToHresult(
  103. MyDsGetDcName(
  104. 0,
  105. domain,
  106. DS_WRITABLE_REQUIRED | DS_DIRECTORY_SERVICE_PREFERRED,
  107. info));
  108. LOG_HRESULT(hr);
  109. if (FAILED(hr))
  110. {
  111. SetComError(
  112. String::format(
  113. IDS_CANT_FIND_DC,
  114. domain.c_str(),
  115. GetErrorMessage(hr).c_str()));
  116. break;
  117. }
  118. if (info && info->DomainControllerName)
  119. {
  120. computer = new Computer(info->DomainControllerName);
  121. ::NetApiBufferFree(info);
  122. }
  123. else
  124. {
  125. // should always get a result if successful
  126. ASSERT(false);
  127. hr = E_FAIL;
  128. break;
  129. }
  130. }
  131. else
  132. {
  133. // source dc was supplied
  134. computer = new Computer(dc);
  135. }
  136. }
  137. while (0);
  138. return hr;
  139. }
  140. // HRESULT
  141. // Authenticate(
  142. // const Computer& computer,
  143. // const String& username,
  144. // const String& userDomain,
  145. // const String& password)
  146. // {
  147. // LOG_FUNCTION(Authenticate);
  148. //
  149. // // attempt to authenticate to the computer.
  150. // String name = computer.NameWithBackslashes();
  151. //
  152. // NETRESOURCE nr;
  153. // memset(&nr, 0, sizeof(nr));
  154. //
  155. // nr.dwType = RESOURCETYPE_ANY;
  156. // nr.lpRemoteName = const_cast<String::value_type*>(name.c_str());
  157. //
  158. // // see KB articles Q218497, Q180548, Q183366 for the pitfalls here...
  159. //
  160. // String u;
  161. // if (userDomain.empty())
  162. // {
  163. // u = username;
  164. // }
  165. // else
  166. // {
  167. // ASSERT(!username.empty());
  168. // u = userDomain + L"\\" + username;
  169. // }
  170. //
  171. // LOG(L"Calling WNetAddConnection2");
  172. // LOG(String::format(L"username : %1", u.empty() ? L"(null)" : u.c_str()));
  173. //
  174. // HRESULT hr =
  175. // Win32ToHresult(
  176. // ::WNetAddConnection2(
  177. // &nr,
  178. // password.c_str(),
  179. // u.empty() ? 0 : u.c_str(),
  180. // 0));
  181. //
  182. // LOG_HRESULT(hr);
  183. //
  184. // if (FAILED(hr))
  185. // {
  186. // SetComError(
  187. // String::format(
  188. // IDS_UNABLE_TO_CONNECT,
  189. // name.c_str(),
  190. // GetErrorMessage(hr).c_str()));
  191. // }
  192. //
  193. // return hr;
  194. // }
  195. HRESULT
  196. ValidateInitializedComputer(
  197. const Computer& computer,
  198. const String& domain)
  199. {
  200. LOG_FUNCTION(ValidateInitializedComputer);
  201. HRESULT hr = S_OK;
  202. do
  203. {
  204. if (!computer.IsDomainController())
  205. {
  206. hr = E_INVALIDARG;
  207. SetComError(
  208. String::format(
  209. IDS_COMPUTER_IS_NOT_DC,
  210. computer.GetNetbiosName().c_str()));
  211. break;
  212. }
  213. if (!domain.empty())
  214. {
  215. // check that the DC is really a DC of the specified domain
  216. if (
  217. computer.GetDomainDnsName().icompare(domain) != 0
  218. && computer.GetDomainNetbiosName().icompare(domain) != 0)
  219. {
  220. hr = E_INVALIDARG;
  221. SetComError(
  222. String::format(
  223. IDS_NOT_DC_FOR_WRONG_DOMAIN,
  224. computer.GetNetbiosName().c_str(),
  225. domain.c_str()));
  226. break;
  227. }
  228. }
  229. }
  230. while (0);
  231. return hr;
  232. }
  233. // Returns an open handle to the SAM database for the named domain on the
  234. // given DC. Should be freed with SamCloseHandle.
  235. HRESULT
  236. OpenSamDomain(
  237. const String& dcName,
  238. const String& domainNetBiosName,
  239. SAM_HANDLE& resultHandle)
  240. {
  241. LOG_FUNCTION2(OpenSamDomain, dcName);
  242. ASSERT(!dcName.empty());
  243. resultHandle = INVALID_HANDLE_VALUE;
  244. HRESULT hr = S_OK;
  245. SAM_HANDLE serverHandle = INVALID_HANDLE_VALUE;
  246. PSID domainSID = 0;
  247. do
  248. {
  249. UNICODE_STRING serverName;
  250. memset(&serverName, 0, sizeof(serverName));
  251. ::RtlInitUnicodeString(&serverName, dcName.c_str());
  252. LOG(L"Calling SamConnect");
  253. hr =
  254. NtStatusToHRESULT(
  255. ::SamConnect(
  256. &serverName,
  257. &serverHandle,
  258. MAXIMUM_ALLOWED,
  259. 0));
  260. if (FAILED(hr))
  261. {
  262. SetComError(
  263. String::format(
  264. IDS_UNABLE_TO_CONNECT_TO_SAM_SERVER,
  265. dcName.c_str(),
  266. GetErrorMessage(hr).c_str()));
  267. break;
  268. }
  269. UNICODE_STRING domainName;
  270. memset(&domainName, 0, sizeof(domainName));
  271. ::RtlInitUnicodeString(&domainName, domainNetBiosName.c_str());
  272. hr =
  273. NtStatusToHRESULT(
  274. ::SamLookupDomainInSamServer(
  275. serverHandle,
  276. &domainName,
  277. &domainSID));
  278. if (FAILED(hr))
  279. {
  280. SetComError(
  281. String::format(
  282. IDS_UNABLE_TO_LOOKUP_SAM_DOMAIN,
  283. domainNetBiosName.c_str(),
  284. GetErrorMessage(hr).c_str()));
  285. break;
  286. }
  287. hr =
  288. NtStatusToHRESULT(
  289. ::SamOpenDomain(
  290. serverHandle,
  291. MAXIMUM_ALLOWED,
  292. domainSID,
  293. &resultHandle));
  294. if (FAILED(hr))
  295. {
  296. SetComError(
  297. String::format(
  298. IDS_UNABLE_TO_OPEN_SAM_DOMAIN,
  299. domainNetBiosName.c_str(),
  300. GetErrorMessage(hr).c_str()));
  301. break;
  302. }
  303. }
  304. while (0);
  305. if (serverHandle != INVALID_HANDLE_VALUE)
  306. {
  307. ::SamCloseHandle(serverHandle);
  308. }
  309. if (domainSID)
  310. {
  311. ::SamFreeMemory(domainSID);
  312. }
  313. return hr;
  314. }
  315. HRESULT
  316. DetermineSourceDcDnsName(
  317. const String& srcDcNetbiosName,
  318. const String& srcDomainDnsName,
  319. String& srcDcDnsName)
  320. {
  321. LOG_FUNCTION(DetermineSourceDcDnsName);
  322. ASSERT(!srcDcNetbiosName.empty());
  323. srcDcDnsName.erase();
  324. if (srcDomainDnsName.empty())
  325. {
  326. // The computer is not a DS DC, so we don't need its DNS name.
  327. LOG(L"source DC is not a DS DC");
  328. return S_OK;
  329. }
  330. HRESULT hr = S_OK;
  331. HANDLE hds = 0;
  332. do
  333. {
  334. // Bind to self
  335. hr =
  336. MyDsBind(
  337. srcDcNetbiosName,
  338. srcDomainDnsName,
  339. hds);
  340. if (FAILED(hr))
  341. {
  342. SetComError(
  343. String::format(
  344. IDS_BIND_FAILED,
  345. srcDcNetbiosName.c_str(),
  346. GetErrorMessage(hr).c_str()));
  347. break;
  348. }
  349. // find all the dc's for my domain. the list should contain
  350. // srcDcNetbiosName.
  351. DS_DOMAIN_CONTROLLER_INFO_1W* info = 0;
  352. DWORD infoCount = 0;
  353. hr =
  354. MyDsGetDomainControllerInfo(
  355. hds,
  356. srcDomainDnsName,
  357. infoCount,
  358. info);
  359. if (FAILED(hr))
  360. {
  361. SetComError(
  362. String::format(
  363. IDS_GET_DC_INFO_FAILED,
  364. GetErrorMessage(hr).c_str()));
  365. break;
  366. }
  367. // there should be at least 1 entry, the source DC itself
  368. ASSERT(infoCount);
  369. ASSERT(info);
  370. if (info)
  371. {
  372. for (DWORD i = 0; i < infoCount; i++)
  373. {
  374. if (info[i].NetbiosName)
  375. {
  376. LOG(info[i].NetbiosName);
  377. if (srcDcNetbiosName.icompare(info[i].NetbiosName) == 0)
  378. {
  379. // we found ourselves in the list
  380. LOG(L"netbios name found");
  381. if (info[i].DnsHostName)
  382. {
  383. LOG(L"dns hostname found!");
  384. srcDcDnsName = info[i].DnsHostName;
  385. break;
  386. }
  387. }
  388. }
  389. }
  390. }
  391. ::DsFreeDomainControllerInfo(1, infoCount, info);
  392. if (srcDcDnsName.empty())
  393. {
  394. hr = E_FAIL;
  395. SetComError(
  396. String::format(
  397. IDS_CANT_FIND_SRC_DC_DNS_NAME,
  398. srcDcNetbiosName.c_str()));
  399. break;
  400. }
  401. LOG(srcDcDnsName);
  402. }
  403. while (0);
  404. if (hds)
  405. {
  406. ::DsUnBind(&hds);
  407. hds = 0;
  408. }
  409. return hr;
  410. }
  411. HRESULT
  412. CloneSecurityPrincipal::Connection::Connect(
  413. const String& srcDC,
  414. const String& srcDomain,
  415. const String& dstDC,
  416. const String& dstDomain)
  417. {
  418. LOG_FUNCTION(CloneSecurityPrincipal::Connection::Connect);
  419. HRESULT hr = S_OK;
  420. do
  421. {
  422. hr = ValidateDCAndDomainParameters(srcDC, srcDomain, dstDC, dstDomain);
  423. BREAK_ON_FAILED_HRESULT(hr);
  424. hr = CreateComputer(srcDC, srcDomain, srcComputer);
  425. BREAK_ON_FAILED_HRESULT(hr);
  426. hr = CreateComputer(dstDC, dstDomain, dstComputer);
  427. BREAK_ON_FAILED_HRESULT(hr);
  428. // hr =
  429. // Authenticate(
  430. // *srcComputer,
  431. // srcUsername,
  432. // srcUserDomain,
  433. // srcPassword);
  434. // BREAK_ON_FAILED_HRESULT(hr);
  435. hr = srcComputer->Refresh();
  436. if (FAILED(hr))
  437. {
  438. SetComError(
  439. String::format(
  440. IDS_UNABLE_TO_READ_COMPUTER_INFO,
  441. srcComputer->GetNetbiosName().c_str(),
  442. GetErrorMessage(hr).c_str()));
  443. break;
  444. }
  445. hr = ValidateInitializedComputer(*srcComputer, srcDomain);
  446. BREAK_ON_FAILED_HRESULT(hr);
  447. hr = dstComputer->Refresh();
  448. if (FAILED(hr))
  449. {
  450. SetComError(
  451. String::format(
  452. IDS_UNABLE_TO_READ_COMPUTER_INFO,
  453. dstComputer->GetNetbiosName().c_str(),
  454. GetErrorMessage(hr).c_str()));
  455. break;
  456. }
  457. hr = ValidateInitializedComputer(*dstComputer, dstDomain);
  458. BREAK_ON_FAILED_HRESULT(hr);
  459. // bind to the destination DC.
  460. ASSERT(dstDsBindHandle == INVALID_HANDLE_VALUE);
  461. hr =
  462. MyDsBind(
  463. dstComputer->GetNetbiosName(),
  464. String(),
  465. dstDsBindHandle);
  466. if (FAILED(hr))
  467. {
  468. SetComError(
  469. String::format(
  470. IDS_BIND_FAILED,
  471. dstComputer->GetNetbiosName().c_str(),
  472. GetErrorMessage(hr).c_str()));
  473. break;
  474. }
  475. //
  476. // open ldap connection to dstDC
  477. //
  478. m_pldap = ldap_open(const_cast<String::value_type*>(dstDC.c_str()), LDAP_PORT);
  479. if (!m_pldap)
  480. {
  481. hr = Win::GetLastErrorAsHresult();
  482. SetComError(
  483. String::format(
  484. IDS_LDAPOPEN_FAILED,
  485. dstComputer->GetNetbiosName().c_str(),
  486. GetErrorMessage(hr).c_str()));
  487. break;
  488. }
  489. // SEC_WINNT_AUTH_IDENTITY authInfo;
  490. // authInfo.User = const_cast<wchar_t*>(dstUsername.c_str());
  491. // authInfo.UserLength = dstUsername.length();
  492. // authInfo.Domain = const_cast<wchar_t*>(dstUserDomain.c_str());
  493. // authInfo.DomainLength = dstUserDomain.length();
  494. // authInfo.Password = const_cast<wchar_t*>(dstPassword.c_str());
  495. // authInfo.PasswordLength = dstPassword.length();
  496. // authInfo.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
  497. DWORD dwErr = ldap_bind_s(
  498. m_pldap,
  499. NULL,
  500. (TCHAR *) 0,
  501. LDAP_AUTH_NEGOTIATE);
  502. if (LDAP_SUCCESS != dwErr)
  503. {
  504. hr = Win::GetLastErrorAsHresult();
  505. ldap_unbind_s(m_pldap);
  506. m_pldap = 0;
  507. SetComError(
  508. String::format(
  509. IDS_LDAPBIND_FAILED,
  510. dstComputer->GetNetbiosName().c_str(),
  511. GetErrorMessage(hr).c_str()));
  512. break;
  513. }
  514. // obtain sam handles to source and dst domains
  515. ASSERT(srcDomainSamHandle == INVALID_HANDLE_VALUE);
  516. hr =
  517. OpenSamDomain(
  518. srcComputer->GetNetbiosName(),
  519. srcComputer->GetDomainNetbiosName(),
  520. srcDomainSamHandle);
  521. BREAK_ON_FAILED_HRESULT(hr);
  522. ASSERT(dstDomainSamHandle == INVALID_HANDLE_VALUE);
  523. hr =
  524. OpenSamDomain(
  525. dstComputer->GetNetbiosName(),
  526. dstComputer->GetDomainNetbiosName(),
  527. dstDomainSamHandle);
  528. BREAK_ON_FAILED_HRESULT(hr);
  529. hr =
  530. DetermineSourceDcDnsName(
  531. srcComputer->GetNetbiosName(),
  532. srcComputer->GetDomainDnsName(),
  533. srcDcDnsName);
  534. BREAK_ON_FAILED_HRESULT(hr);
  535. }
  536. while (0);
  537. if (FAILED(hr))
  538. {
  539. Disconnect();
  540. }
  541. return hr;
  542. }
  543. bool
  544. CloneSecurityPrincipal::Connection::IsConnected() const
  545. {
  546. LOG_FUNCTION(CloneSecurityPrincipal::Connection::IsConnected);
  547. bool result =
  548. srcComputer
  549. && dstComputer
  550. && (dstDsBindHandle != INVALID_HANDLE_VALUE)
  551. && (srcDomainSamHandle != INVALID_HANDLE_VALUE);
  552. LOG(
  553. String::format(
  554. L"object %1 connected.",
  555. result ? L"is" : L"is NOT"));
  556. return result;
  557. }
  558. void
  559. CloneSecurityPrincipal::Connection::Disconnect()
  560. {
  561. LOG_FUNCTION(CloneSecurityPrincipal::Connection::Disconnect);
  562. // may be called if Connect fails, so we might be in a partially
  563. // connected state. So we need to check the handle values.
  564. if (srcDomainSamHandle != INVALID_HANDLE_VALUE)
  565. {
  566. ::SamCloseHandle(srcDomainSamHandle);
  567. srcDomainSamHandle = INVALID_HANDLE_VALUE;
  568. }
  569. if (dstDsBindHandle != INVALID_HANDLE_VALUE)
  570. {
  571. ::DsUnBind(&dstDsBindHandle);
  572. dstDsBindHandle = INVALID_HANDLE_VALUE;
  573. }
  574. if (m_pldap)
  575. {
  576. ldap_unbind_s(m_pldap);
  577. m_pldap = 0;
  578. }
  579. delete dstComputer;
  580. dstComputer = 0;
  581. delete srcComputer;
  582. srcComputer = 0;
  583. }