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.

650 lines
20 KiB

  1. /*---------------------------------------------------------------------------
  2. File: ChangeDomain.cpp
  3. Comments: Implementation of COM object that changes the domain affiliation on
  4. a computer.
  5. (c) Copyright 1999, Mission Critical Software, Inc., All Rights Reserved
  6. Proprietary and confidential to Mission Critical Software, Inc.
  7. REVISION LOG ENTRY
  8. Revision By: Christy Boles
  9. Revised on 02/15/99 11:21:07
  10. ---------------------------------------------------------------------------
  11. */
  12. // ChangeDomain.cpp : Implementation of CChangeDomain
  13. #include "stdafx.h"
  14. #include "WorkObj.h"
  15. #include "ChDom.h"
  16. #include "Common.hpp"
  17. #include "UString.hpp"
  18. #include "EaLen.hpp"
  19. #include "ResStr.h"
  20. #include "ErrDct.hpp"
  21. #include "TxtSid.h"
  22. #include "TReg.hpp"
  23. /////////////////////////////////////////////////////////////////////////////
  24. // CChangeDomain
  25. #include "LSAUtils.h"
  26. #import "NetEnum.tlb" no_namespace
  27. #import "VarSet.tlb" no_namespace rename("property", "aproperty")
  28. #include <lm.h> // for NetXxx API
  29. #include <winbase.h>
  30. TErrorDct errLocal;
  31. typedef NET_API_STATUS (NET_API_FUNCTION* PNETJOINDOMAIN)
  32. (
  33. IN LPCWSTR lpServer OPTIONAL,
  34. IN LPCWSTR lpDomain,
  35. IN LPCWSTR lpAccountOU, OPTIONAL
  36. IN LPCWSTR lpAccount OPTIONAL,
  37. IN LPCWSTR lpPassword OPTIONAL,
  38. IN DWORD fJoinOptions
  39. );
  40. HINSTANCE hDll = NULL;
  41. PNETJOINDOMAIN pNetJoinDomain = NULL;
  42. BOOL GetNetJoinDomainFunction()
  43. {
  44. BOOL bSuccess = FALSE;
  45. hDll = LoadLibrary(L"NetApi32.dll");
  46. if ( hDll )
  47. {
  48. pNetJoinDomain = (PNETJOINDOMAIN)GetProcAddress(hDll,"NetJoinDomain");
  49. if ( pNetJoinDomain )
  50. {
  51. bSuccess = TRUE;
  52. }
  53. }
  54. return bSuccess;
  55. }
  56. typedef HRESULT (CALLBACK * ADSGETOBJECT)(LPWSTR, REFIID, void**);
  57. extern ADSGETOBJECT ADsGetObject;
  58. STDMETHODIMP
  59. CChangeDomain::ChangeToDomain(
  60. BSTR activeComputerName, // in - computer name currently being used (old name if simultaneously renaming and changing domain)
  61. BSTR domain, // in - domain to move computer to
  62. BSTR newComputerName, // in - computer name the computer will join the new domain as (the name that will be in effect on reboot, if simultaneously renaming and changing domain)
  63. BSTR * errReturn // out- string describing any errors that occurred
  64. )
  65. {
  66. HRESULT hr = S_OK;
  67. return hr;
  68. }
  69. STDMETHODIMP
  70. CChangeDomain::ChangeToDomainWithSid(
  71. BSTR activeComputerName, // in - computer name currently being used (old name if simultaneously renaming and changing domain)
  72. BSTR domain, // in - domain to move computer to
  73. BSTR domainSid, // in - sid of domain, as string
  74. BSTR domainController, // in - domain controller to use
  75. BSTR newComputerName, // in - computer name the computer will join the new domain as (the name that will be in effect on reboot, if simultaneously renaming and changing domain)
  76. BSTR srcPath, // in - source account original LDAP path
  77. BSTR * errReturn // out- string describing any errors that occurred
  78. )
  79. {
  80. USES_CONVERSION;
  81. HRESULT hr = S_OK;
  82. // initialize output parameters
  83. (*errReturn) = NULL;
  84. //
  85. // Use NetJoinDomain API if available (Windows 2000 and later)
  86. // otherwise must use LSA APIs (Windows NT 4 and earlier).
  87. //
  88. if (GetNetJoinDomainFunction())
  89. {
  90. DWORD dwError = ERROR_SUCCESS;
  91. //
  92. // If a preferred domain controller is specified then use it.
  93. //
  94. _bstr_t strNewDomain = domain;
  95. if (SysStringLen(domainController) > 0)
  96. {
  97. //
  98. // The preferred domain controller may only be specified for uplevel
  99. // (W2K or later) domains. During undo of a computer migration the
  100. // target domain may be a downlevel (NT4) domain. If unable to obtain
  101. // operating system version information from the specified domain
  102. // controller or the operating system is downlevel then don't specify
  103. // a preferred domain controller.
  104. //
  105. PWKSTA_INFO_100 pInfo = NULL;
  106. NET_API_STATUS nasStatus = NetWkstaGetInfo(domainController, 100, (LPBYTE*)&pInfo);
  107. if ((nasStatus == NERR_Success) && pInfo)
  108. {
  109. if (pInfo->wki100_ver_major >= 5)
  110. {
  111. NetApiBufferFree(pInfo);
  112. strNewDomain += L"\\";
  113. strNewDomain += domainController;
  114. }
  115. }
  116. }
  117. //
  118. // Join Options
  119. //
  120. const DWORD JOIN_OPTIONS = NETSETUP_JOIN_DOMAIN | NETSETUP_DOMAIN_JOIN_IF_JOINED | NETSETUP_JOIN_UNSECURE;
  121. //
  122. // Check whether a new computer name has been specified.
  123. //
  124. if (SysStringLen(newComputerName) == 0)
  125. {
  126. //
  127. // A new name has not been specified therefore simply
  128. // join the new domain with the current computer name.
  129. //
  130. dwError = pNetJoinDomain(NULL, strNewDomain, NULL, NULL, NULL, JOIN_OPTIONS);
  131. }
  132. else
  133. {
  134. //
  135. // A new name has been specified therefore computer must be re-named during join.
  136. //
  137. // The current APIs only support joining a domain with the current name and then
  138. // re-naming the computer in the domain after the join. Unfortunately the re-name
  139. // in the domain requires the credentials of a security principal with the rights
  140. // to change the name of the computer in the domain or that this process be running
  141. // under the security context of a security principal with the rights to change the
  142. // name of the computer in the domain. Since these requirements cannot be met with
  143. // ADMT the following trick (read hack) must be used.
  144. //
  145. // Set the active computer name in the registry to the new name during the duration
  146. // of the NetJoinDomain call so that the computer is joined to the new domain with
  147. // the new name without requiring a subsequent re-name in the new domain.
  148. //
  149. TRegKey key;
  150. static WCHAR c_szKey[] = L"System\\CurrentControlSet\\Control\\ComputerName\\ActiveComputerName";
  151. dwError = key.Open(c_szKey, HKEY_LOCAL_MACHINE);
  152. if (dwError == ERROR_SUCCESS)
  153. {
  154. static WCHAR c_szKeyValue[] = L"ComputerName";
  155. WCHAR szOldComputerName[MAX_PATH];
  156. dwError = key.ValueGetStr(c_szKeyValue, szOldComputerName, MAX_PATH - 1);
  157. if (dwError == ERROR_SUCCESS)
  158. {
  159. dwError = key.ValueSetStr(c_szKeyValue, OLE2CW(newComputerName));
  160. if (dwError == ERROR_SUCCESS)
  161. {
  162. dwError = pNetJoinDomain(NULL, strNewDomain, NULL, NULL, NULL, JOIN_OPTIONS);
  163. key.ValueSetStr(c_szKeyValue, szOldComputerName);
  164. key.Close();
  165. }
  166. }
  167. }
  168. }
  169. hr = HRESULT_FROM_WIN32(dwError);
  170. }
  171. else
  172. {
  173. do // once
  174. {
  175. LSA_HANDLE PolicyHandle = NULL;
  176. LPWSTR Workstation; // target machine of policy update
  177. WCHAR Password[LEN_Password];
  178. PSID DomainSid=NULL; // Sid representing domain to trust
  179. PSERVER_INFO_101 si101 = NULL;
  180. DWORD Type;
  181. NET_API_STATUS nas;
  182. NTSTATUS Status;
  183. WCHAR errMsg[1000];
  184. WCHAR TrustedDomainName[LEN_Domain];
  185. WCHAR LocalMachine[MAX_PATH] = L"";
  186. DWORD lenLocalMachine = DIM(LocalMachine);
  187. LPWSTR activeWorkstation = L"";
  188. // use the target name, if provided
  189. if ( newComputerName && UStrLen((WCHAR*)newComputerName) )
  190. {
  191. Workstation = (WCHAR*)newComputerName;
  192. if ( ! activeComputerName || ! UStrLen((WCHAR*)activeComputerName) )
  193. {
  194. activeWorkstation = LocalMachine;
  195. }
  196. else
  197. {
  198. activeWorkstation = (WCHAR*)activeComputerName;
  199. }
  200. }
  201. else
  202. {
  203. if (! activeComputerName || ! UStrLen((WCHAR*)activeComputerName) )
  204. {
  205. GetComputerName(LocalMachine,&lenLocalMachine);
  206. Workstation = LocalMachine;
  207. activeWorkstation = L"";
  208. }
  209. else
  210. {
  211. Workstation = (WCHAR*)activeComputerName;
  212. activeWorkstation = Workstation;
  213. }
  214. }
  215. wcscpy(TrustedDomainName,(WCHAR*)domain);
  216. if ( Workstation[0] == L'\\' )
  217. Workstation += 2;
  218. // Use a default password
  219. for ( UINT p = 0 ; p < wcslen(Workstation) ; p++ )
  220. Password[p] = towlower(Workstation[p]);
  221. Password[wcslen(Workstation)] = 0;
  222. // ensure that the password is truncated at 14 characters
  223. Password[14] = 0;
  224. //
  225. // insure the target machine is NOT a DC, as this operation is
  226. // only appropriate against a workstation.
  227. //
  228. nas = NetServerGetInfo(activeWorkstation, 101, (LPBYTE *)&si101);
  229. if(nas != NERR_Success)
  230. {
  231. hr = HRESULT_FROM_WIN32(nas);
  232. break;
  233. }
  234. // Use LSA APIs
  235. Type = si101->sv101_type;
  236. if( (Type & SV_TYPE_DOMAIN_CTRL) ||
  237. (Type & SV_TYPE_DOMAIN_BAKCTRL) )
  238. {
  239. swprintf(errMsg,GET_STRING(IDS_NotAllowedOnDomainController));
  240. hr = E_NOTIMPL;
  241. break;
  242. }
  243. //
  244. // do not allow a workstation to trust itself
  245. //
  246. if(lstrcmpiW(TrustedDomainName, Workstation) == 0)
  247. {
  248. swprintf(errMsg,GET_STRING(IDS_CannotTrustSelf),
  249. TrustedDomainName);
  250. hr = E_INVALIDARG;
  251. break;
  252. }
  253. if( lstrlenW(TrustedDomainName ) > MAX_COMPUTERNAME_LENGTH )
  254. {
  255. TrustedDomainName[MAX_COMPUTERNAME_LENGTH] = L'\0'; // truncate
  256. }
  257. if ( ! m_bNoChange )
  258. {
  259. //
  260. // build the DomainSid of the domain to trust
  261. //
  262. DomainSid = SidFromString(domainSid);
  263. if(!DomainSid )
  264. {
  265. hr = HRESULT_FROM_WIN32(GetLastError());
  266. break;
  267. }
  268. }
  269. if ( (!m_bNoChange) && (si101->sv101_version_major < 4) )
  270. {
  271. // For NT 3.51 machines, we must move the computer to a workgroup, and
  272. // then move it into the new domain
  273. hr = ChangeToWorkgroup(SysAllocString(activeWorkstation),SysAllocString(L"WORKGROUP"),errReturn);
  274. if (FAILED(hr)) {
  275. break;
  276. }
  277. Status = QueryWorkstationTrustedDomainInfo(PolicyHandle,DomainSid,m_bNoChange);
  278. if ( Status != STATUS_SUCCESS )
  279. {
  280. hr = HRESULT_FROM_WIN32(LsaNtStatusToWinError(Status));
  281. break;
  282. }
  283. }
  284. //
  285. // see if the computer account exists on the domain
  286. //
  287. //
  288. // open the policy on this computer
  289. //
  290. Status = OpenPolicy(
  291. activeWorkstation,
  292. DELETE | // to remove a trust
  293. POLICY_VIEW_LOCAL_INFORMATION | // to view trusts
  294. POLICY_CREATE_SECRET | // for password set operation
  295. POLICY_TRUST_ADMIN, // for trust creation
  296. &PolicyHandle
  297. );
  298. if( Status != STATUS_SUCCESS )
  299. {
  300. hr = HRESULT_FROM_WIN32(LsaNtStatusToWinError(Status));
  301. break;
  302. }
  303. if ( ! m_bNoChange )
  304. {
  305. Status = QueryWorkstationTrustedDomainInfo(PolicyHandle,DomainSid,m_bNoChange);
  306. if (Status == STATUS_SUCCESS) {
  307. Status = SetWorkstationTrustedDomainInfo(
  308. PolicyHandle,
  309. DomainSid,
  310. TrustedDomainName,
  311. Password,
  312. errMsg
  313. );
  314. }
  315. }
  316. if( Status != STATUS_SUCCESS )
  317. {
  318. hr = HRESULT_FROM_WIN32(LsaNtStatusToWinError(Status));
  319. break;
  320. }
  321. //
  322. // Update the primary domain to match the specified trusted domain
  323. //
  324. if (! m_bNoChange )
  325. {
  326. Status = SetPrimaryDomain(PolicyHandle, DomainSid, TrustedDomainName);
  327. if(Status != STATUS_SUCCESS)
  328. {
  329. // DisplayNtStatus(errMsg,"SetPrimaryDomain", Status,NULL);
  330. hr = HRESULT_FROM_WIN32(LsaNtStatusToWinError(Status));
  331. break;
  332. }
  333. }
  334. NetApiBufferFree(si101);
  335. // Cleanup
  336. //LocalFree(Workstation);
  337. //
  338. // free the Sid which was allocated for the TrustedDomain Sid
  339. //
  340. if(DomainSid)
  341. {
  342. FreeSid(DomainSid);
  343. }
  344. //
  345. // close the policy handle
  346. //
  347. if ( PolicyHandle )
  348. {
  349. LsaClose(PolicyHandle);
  350. }
  351. } while (false); // do once
  352. if (FAILED(hr))
  353. {
  354. (*errReturn) = NULL;
  355. }
  356. }
  357. return hr;
  358. }
  359. STDMETHODIMP
  360. CChangeDomain::ChangeToWorkgroup(
  361. BSTR Computer, // in - name of computer to update
  362. BSTR Workgroup, // in - name of workgroup to join
  363. BSTR * errReturn // out- text describing error if failure
  364. )
  365. {
  366. HRESULT hr = S_OK;
  367. LSA_HANDLE PolicyHandle = NULL;
  368. LPWSTR Workstation; // target machine of policy update
  369. LPWSTR TrustedDomainName; // domain to join
  370. PSERVER_INFO_101 si101;
  371. DWORD Type;
  372. NET_API_STATUS nas;
  373. NTSTATUS Status;
  374. WCHAR errMsg[1000] = L"";
  375. // BOOL bSessionEstablished = FALSE;
  376. // initialize output parameters
  377. (*errReturn) = NULL;
  378. Workstation = (WCHAR*)Computer;
  379. TrustedDomainName = (WCHAR*)Workgroup;
  380. errLocal.DbgMsgWrite(0,L"Changing to workgroup...");
  381. //
  382. // insure the target machine is NOT a DC, as this operation is
  383. // only appropriate against a workstation.
  384. //
  385. do // once
  386. {
  387. /*if ( m_account.length() )
  388. {
  389. // Establish our credentials to the target machine
  390. if (! EstablishSession(Workstation,m_domain,m_account,m_password, TRUE) )
  391. {
  392. // DisplayError(errMsg,"EstablishSession",GetLastError(),NULL);
  393. hr = GetLastError();
  394. }
  395. else
  396. {
  397. bSessionEstablished = TRUE;
  398. }
  399. }
  400. */
  401. nas = NetServerGetInfo(Workstation, 101, (LPBYTE *)&si101);
  402. if(nas != NERR_Success)
  403. {
  404. //DisplayError(errMsg, "NetServerGetInfo", nas,NULL);
  405. hr = E_FAIL;
  406. break;
  407. }
  408. Type = si101->sv101_type;
  409. NetApiBufferFree(si101);
  410. if( (Type & SV_TYPE_DOMAIN_CTRL) ||
  411. (Type & SV_TYPE_DOMAIN_BAKCTRL) )
  412. {
  413. swprintf(errMsg,L"Operation is not valid on a domain controller.\n");
  414. hr = E_FAIL;
  415. break;
  416. }
  417. //
  418. // do not allow a workstation to trust itself
  419. //
  420. if(lstrcmpiW(TrustedDomainName, Workstation) == 0)
  421. {
  422. swprintf(errMsg,L"Error: Domain %ls cannot be a member of itself.\n",
  423. TrustedDomainName);
  424. hr = E_FAIL;
  425. break;
  426. }
  427. if( lstrlenW(TrustedDomainName ) > MAX_COMPUTERNAME_LENGTH )
  428. {
  429. TrustedDomainName[MAX_COMPUTERNAME_LENGTH] = L'\0'; // truncate
  430. }
  431. //
  432. // open the policy on this computer
  433. //
  434. Status = OpenPolicy(
  435. Workstation,
  436. DELETE | // to remove a trust
  437. POLICY_VIEW_LOCAL_INFORMATION | // to view trusts
  438. POLICY_CREATE_SECRET | // for password set operation
  439. POLICY_TRUST_ADMIN, // for trust creation
  440. &PolicyHandle
  441. );
  442. if( Status != STATUS_SUCCESS )
  443. {
  444. //DisplayNtStatus(errMsg,"OpenPolicy", Status,NULL);
  445. hr = LsaNtStatusToWinError(Status);
  446. break;
  447. }
  448. if( Status != STATUS_SUCCESS )
  449. {
  450. hr = E_FAIL;
  451. break;
  452. }
  453. //
  454. // Update the primary domain to match the specified trusted domain
  455. //
  456. if (! m_bNoChange )
  457. {
  458. Status = SetPrimaryDomain(PolicyHandle, NULL, TrustedDomainName);
  459. if(Status != STATUS_SUCCESS)
  460. {
  461. //DisplayNtStatus(errMsg,"SetPrimaryDomain", Status,NULL);
  462. hr = LsaNtStatusToWinError(Status);
  463. break;
  464. }
  465. }
  466. } while (false); // do once
  467. // Cleanup
  468. //
  469. // close the policy handle
  470. //
  471. if(PolicyHandle)
  472. {
  473. LsaClose(PolicyHandle);
  474. }
  475. /*if ( bSessionEstablished )
  476. {
  477. EstablishSession(Workstation,m_domain,m_account,m_password,FALSE);
  478. }
  479. */
  480. if ( FAILED(hr) )
  481. {
  482. hr = S_FALSE;
  483. (*errReturn) = SysAllocString(errMsg);
  484. }
  485. return hr;
  486. }
  487. STDMETHODIMP
  488. CChangeDomain::ConnectAs(
  489. BSTR domain, // in - domain name to use for credentials
  490. BSTR user, // in - account name to use for credentials
  491. BSTR password // in - password to use for credentials
  492. )
  493. {
  494. m_domain = domain;
  495. m_account = user;
  496. m_password = password;
  497. m_domainAccount = domain;
  498. m_domainAccount += L"\\";
  499. m_domainAccount += user;
  500. return S_OK;
  501. }
  502. STDMETHODIMP
  503. CChangeDomain::get_NoChange(
  504. BOOL * pVal // out- flag, whether to write changes
  505. )
  506. {
  507. (*pVal) = m_bNoChange;
  508. return S_OK;
  509. }
  510. STDMETHODIMP
  511. CChangeDomain::put_NoChange(
  512. BOOL newVal // in - flag, whether to write changes
  513. )
  514. {
  515. m_bNoChange = newVal;
  516. return S_OK;
  517. }
  518. // ChangeDomain worknode: Changes the domain affiliation of a workstation or server
  519. // (This operation cannot be performed on domain controllers)
  520. //
  521. // VarSet syntax:
  522. //
  523. // Input:
  524. // ChangeDomain.Computer: <ComputerName>
  525. // ChangeDomain.TargetDomain: <Domain>
  526. // ChangeDomain.DomainIsWorkgroup: <Yes|No> default is No
  527. // ChangeDomain.ConnectAs.Domain: <Domain> optional credentials to use
  528. // ChangeDomain.ConnectAs.User : <Username>
  529. // ChangeDomain.ConnectAs.Password : <Password>
  530. //
  531. // Output:
  532. // ChangeDomain.ErrorText : <string-error message>
  533. // This function is not currently used by the domain migration tool.
  534. // The actual implementation is removed from the source.
  535. STDMETHODIMP
  536. CChangeDomain::Process(
  537. IUnknown * pWorkItem
  538. )
  539. {
  540. return E_NOTIMPL;
  541. }