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.

934 lines
30 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. /////////////////////////////////////////////////////////////////////////////
  23. // CChangeDomain
  24. #include "LSAUtils.h"
  25. //#import "\bin\McsVarSetMin.tlb" no_namespace
  26. #import "NetEnum.tlb" no_namespace
  27. #import "VarSet.tlb" no_namespace rename("property", "aproperty")
  28. //#include <ntdsapi.h>
  29. //#include <iads.h>
  30. #include <lm.h> // for NetXxx API
  31. #include <lmjoin.h>
  32. //#include <mapiwin.h>
  33. #include <winbase.h>
  34. TErrorDct errLocal;
  35. // constants from lmjoin.h needed for NetJoinDomain which does not exist on NT 4.
  36. #define NETSETUP_JOIN_DOMAIN 0x00000001 // If not present, workgroup is joined
  37. #define NETSETUP_ACCT_CREATE 0x00000002 // Do the server side account creation/rename
  38. #define NETSETUP_ACCT_DELETE 0x00000004 // Delete the account when a domain is left
  39. #define NETSETUP_WIN9X_UPGRADE 0x00000010 // Invoked during upgrade of Windows 9x to
  40. // Windows NT
  41. #define NETSETUP_DOMAIN_JOIN_IF_JOINED 0x00000020 // Allow the client to join a new domain
  42. // even if it is already joined to a domain
  43. #define NETSETUP_JOIN_UNSECURE 0x00000040 // Performs an unsecure join
  44. #define NETSETUP_INSTALL_INVOCATION 0x00040000 // The APIs were invoked during install
  45. typedef NET_API_STATUS
  46. NET_API_FUNCTION
  47. PNETJOINDOMAIN(
  48. IN LPCWSTR lpServer OPTIONAL,
  49. IN LPCWSTR lpDomain,
  50. IN LPCWSTR lpAccountOU, OPTIONAL
  51. IN LPCWSTR lpAccount OPTIONAL,
  52. IN LPCWSTR lpPassword OPTIONAL,
  53. IN DWORD fJoinOptions
  54. );
  55. HINSTANCE hDll = NULL;
  56. PNETJOINDOMAIN * gNetJoinDomain = NULL;
  57. BOOL GetNetJoinDomainFunction()
  58. {
  59. BOOL bSuccess = FALSE;
  60. hDll = LoadLibrary(L"NetApi32.dll");
  61. if ( hDll )
  62. {
  63. gNetJoinDomain = (PNETJOINDOMAIN*)GetProcAddress(hDll,"NetJoinDomain");
  64. if ( gNetJoinDomain )
  65. {
  66. bSuccess = TRUE;
  67. }
  68. }
  69. return bSuccess;
  70. }
  71. typedef HRESULT (CALLBACK * ADSGETOBJECT)(LPWSTR, REFIID, void**);
  72. extern ADSGETOBJECT ADsGetObject;
  73. /*BOOL GetLDAPPath(WCHAR * sTgtDomain, WCHAR * sSAM, WCHAR * sPath)
  74. {
  75. if ( pOptions->tgtDomainVer < 5 )
  76. {
  77. // for NT4 we can just build the path and send it back.
  78. wsprintf(sPath, L"WinNT://%s/%s", pOptions->tgtDomain, sSam);
  79. return S_OK;
  80. }
  81. // Use the net object enumerator to lookup the account in the target domain.
  82. INetObjEnumeratorPtr pQuery(__uuidof(NetObjEnumerator));
  83. IEnumVARIANT * pEnum = NULL;
  84. SAFEARRAYBOUND bd = { 1, 0 };
  85. SAFEARRAY * pszColNames;
  86. BSTR HUGEP * pData = NULL;
  87. BSTR sData[] = { L"aDSPath" };
  88. WCHAR sQuery[LEN_Path];
  89. WCHAR sDomPath[LEN_Path];
  90. DWORD ret = 0;
  91. _variant_t var, varVal;
  92. HRESULT hr = S_OK;
  93. WCHAR tgtNamingContext[LEN_Path];
  94. WCHAR aPath[LEN_Path];
  95. IADs * pAds;
  96. BOOL bFoundNC = FALSE;
  97. BOOL bConverted = FALSE;
  98. errLocal.DbgMsgWrite(0,L"GetLDAPPath for %ls in %ls", sSAM, sTgtDomain);
  99. wsprintf(aPath, L"LDAP://%s/rootDSE", sTgtDomain);
  100. hr = ADsGetObject(aPath, IID_IADs, (void**)&pAds);
  101. if ( SUCCEEDED(hr) )
  102. {
  103. errLocal.DbgMsgWrite(0,L"ADsGetObject Succeeded");
  104. hr = pAds->Get(L"defaultNamingContext", &var);
  105. if ( SUCCEEDED(hr) )
  106. {
  107. pAds->Release();
  108. wcscpy(tgtNamingContext, (WCHAR*) V_BSTR(&var));
  109. errLocal.DbgMsgWrite(0,L"NamingContext is %ls", tgtNamingContext);
  110. bFoundNC = TRUE;
  111. }
  112. }
  113. if (!bFoundNC)
  114. return FALSE;
  115. wsprintf(sDomPath, L"LDAP://%s/%s", sTgtDomain, tgtNamingContext);
  116. WCHAR sTempSamName[LEN_Path];
  117. wcscpy(sTempSamName, sSAM);
  118. if ( sTempSamName[0] == L' ' )
  119. {
  120. WCHAR sTemp[LEN_Path];
  121. wsprintf(sTemp, L"\\20%s", sTempSamName + 1);
  122. wcscpy(sTempSamName, sTemp);
  123. }
  124. wsprintf(sQuery, L"(sAMAccountName=%s)", sTempSamName);
  125. errLocal.DbgMsgWrite(0,L"Query for %ls", sQuery);
  126. hr = pQuery->raw_SetQuery(sDomPath, sTgtDomain, sQuery, ADS_SCOPE_SUBTREE, FALSE);
  127. // Set up the columns that we want back from the query ( in this case we need SAM accountname )
  128. pszColNames = ::SafeArrayCreate(VT_BSTR, 1, &bd);
  129. hr = ::SafeArrayAccessData(pszColNames, (void HUGEP **)&pData);
  130. if ( SUCCEEDED(hr) )
  131. pData[0] = sData[0];
  132. if ( SUCCEEDED(hr) )
  133. hr = ::SafeArrayUnaccessData(pszColNames);
  134. if ( SUCCEEDED(hr) )
  135. hr = pQuery->raw_SetColumns(pszColNames);
  136. // Time to execute the plan.
  137. if ( SUCCEEDED(hr) )
  138. hr = pQuery->raw_Execute(&pEnum);
  139. if ( SUCCEEDED(hr) )
  140. {
  141. // if this worked that means we can only have one thing in the result.
  142. if ( (pEnum->Next(1, &var, &ret) == S_OK) && ( ret > 0 ) )
  143. {
  144. SAFEARRAY * pArray = var.parray;
  145. long ndx = 0;
  146. hr = SafeArrayGetElement(pArray,&ndx,&varVal);
  147. if ( SUCCEEDED(hr) )
  148. {
  149. wcscpy(sPath, (WCHAR*)varVal.bstrVal);
  150. errLocal.DbgMsgWrite(0,L"Got LDAP of %ls", sPath);
  151. bConverted = TRUE;
  152. }
  153. else
  154. hr = HRESULT_FROM_WIN32(NERR_UserNotFound);
  155. }
  156. else
  157. hr = HRESULT_FROM_WIN32(NERR_UserNotFound);
  158. VariantInit(&var);
  159. }
  160. if ( pEnum ) pEnum->Release();
  161. return bConverted;
  162. }
  163. */
  164. /*BOOL GetLDAPPath(WCHAR* Domain, WCHAR* Account, WCHAR* sLDAPPath, WCHAR* srcDomainDNS)
  165. {
  166. PDS_NAME_RESULT pNamesOut = NULL;
  167. WCHAR * pNamesIn[1];
  168. HANDLE hDs = NULL;
  169. WCHAR OrigAccount[MAX_PATH];
  170. BOOL bConverted = FALSE;
  171. errLocal.DbgMsgWrite(0,L"Parameters %ls, %ls, %ls", Domain, Account, srcDomainDNS);
  172. wcscpy(sLDAPPath, L"LDAP://");
  173. wcscat(sLDAPPath, Domain);
  174. _wcsupr(sLDAPPath);
  175. errLocal.DbgMsgWrite(0,L"Start of LDAP Path, %ls", sLDAPPath);
  176. wcscpy(OrigAccount, Domain);
  177. wcscat(OrigAccount, L"\\");
  178. wcscat(OrigAccount, Account);
  179. errLocal.DbgMsgWrite(0,L"Oringinal Account Name, %ls", OrigAccount);
  180. pNamesIn[0] = &OrigAccount[0];
  181. //bind to that source domain
  182. HRESULT hr = DsBind(NULL,srcDomainDNS,&hDs);
  183. if ( !hr )
  184. {
  185. errLocal.DbgMsgWrite(0,L"Bound to %ls", srcDomainDNS);
  186. //get UPN name of this account from DSCrackNames
  187. hr = DsCrackNames(hDs,DS_NAME_NO_FLAGS,DS_NT4_ACCOUNT_NAME,DS_FQDN_1779_NAME,1,pNamesIn,&pNamesOut);
  188. if ( !hr )
  189. { //if got the UPN name, retry DB query for that account in the
  190. errLocal.DbgMsgWrite(0,L"DSCrackNames Succeeded");
  191. //service account database
  192. if ( pNamesOut->rItems[0].status == DS_NAME_NO_ERROR )
  193. {
  194. wcscat(sLDAPPath, pNamesOut->rItems[0].pName);
  195. errLocal.DbgMsgWrite(0,L"New LDAP path, %ls", sLDAPPath);
  196. bConverted = TRUE;
  197. }
  198. else if (pNamesOut->rItems[0].status == DS_NAME_ERROR_DOMAIN_ONLY)
  199. errLocal.DbgMsgWrite(0,L"DS_NAME_ERROR_DOMAIN_ONLY = %ls", pNamesOut->rItems[0].pDomain);
  200. else if (pNamesOut->rItems[0].status == DS_NAME_ERROR_NO_MAPPING)
  201. errLocal.DbgMsgWrite(0,L"DS_NAME_ERROR_NO_MAPPING");
  202. else if (pNamesOut->rItems[0].status == DS_NAME_ERROR_NOT_FOUND)
  203. errLocal.DbgMsgWrite(0,L"DS_NAME_ERROR_NOT_FOUND");
  204. else if (pNamesOut->rItems[0].status == DS_NAME_ERROR_NOT_UNIQUE)
  205. errLocal.DbgMsgWrite(0,L"DS_NAME_ERROR_NOT_UNIQUE");
  206. else if (pNamesOut->rItems[0].status == DS_NAME_ERROR_RESOLVING)
  207. errLocal.DbgMsgWrite(0,L"DS_NAME_ERROR_RESOLVING");
  208. else
  209. errLocal.DbgMsgWrite(0,L"General DSCrackNames Error");
  210. DsFreeNameResult(pNamesOut);
  211. }
  212. }
  213. return bConverted;
  214. }
  215. */
  216. STDMETHODIMP
  217. CChangeDomain::ChangeToDomain(
  218. BSTR activeComputerName, // in - computer name currently being used (old name if simultaneously renaming and changing domain)
  219. BSTR domain, // in - domain to move computer to
  220. 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)
  221. BSTR * errReturn // out- string describing any errors that occurred
  222. )
  223. {
  224. HRESULT hr = S_OK;
  225. return hr;
  226. }
  227. STDMETHODIMP
  228. CChangeDomain::ChangeToDomainWithSid(
  229. BSTR activeComputerName, // in - computer name currently being used (old name if simultaneously renaming and changing domain)
  230. BSTR domain, // in - domain to move computer to
  231. BSTR domainSid, // in - sid of domain, as string
  232. BSTR domainController, // in - domain controller to use
  233. 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)
  234. BSTR srcPath, // in - source account original LDAP path
  235. BSTR * errReturn // out- string describing any errors that occurred
  236. )
  237. {
  238. HRESULT hr = S_OK;
  239. LSA_HANDLE PolicyHandle = NULL;
  240. LPWSTR Workstation; // target machine of policy update
  241. WCHAR Password[LEN_Password];
  242. PSID DomainSid=NULL; // Sid representing domain to trust
  243. LPWSTR PrimaryDC=NULL; // name of that domain's PDC
  244. PSERVER_INFO_101 si101 = NULL;
  245. DWORD Type;
  246. NET_API_STATUS nas;
  247. NTSTATUS Status;
  248. WCHAR errMsg[1000];
  249. BOOL bSessionEstablished = TRUE;
  250. WCHAR TrustedDomainName[LEN_Domain];
  251. WCHAR LocalMachine[MAX_PATH] = L"";
  252. DWORD lenLocalMachine = DIM(LocalMachine);
  253. LPWSTR activeWorkstation = L"";
  254. // WCHAR * errString = GET_BSTR(IDS_Unspecified_Failure);
  255. // WCHAR sPaths[MAX_PATH];
  256. // BOOL bLDAP;
  257. // initialize output parameters
  258. (*errReturn) = NULL;
  259. // commenting out the line below will write a log file that is useful for debugging
  260. // errLocal.LogOpen(L"C:\\ChangeDomain.log",0);
  261. //sleep for 3 minutes so I can attach in the debugger
  262. //errLocal.DbgMsgWrite(0,L"Started 3 minute sleep");
  263. //SleepEx(120000, FALSE);
  264. //errLocal.DbgMsgWrite(0,L"Ended 3 minute sleep");
  265. // use the target name, if provided
  266. if ( newComputerName && UStrLen((WCHAR*)newComputerName) )
  267. {
  268. Workstation = (WCHAR*)newComputerName;
  269. if ( ! activeComputerName || ! UStrLen((WCHAR*)activeComputerName) )
  270. {
  271. activeWorkstation = LocalMachine;
  272. }
  273. else
  274. {
  275. activeWorkstation = (WCHAR*)activeComputerName;
  276. }
  277. }
  278. else
  279. {
  280. if (! activeComputerName || ! UStrLen((WCHAR*)activeComputerName) )
  281. {
  282. GetComputerName(LocalMachine,&lenLocalMachine);
  283. Workstation = LocalMachine;
  284. activeWorkstation = L"";
  285. }
  286. else
  287. {
  288. Workstation = (WCHAR*)activeComputerName;
  289. activeWorkstation = Workstation;
  290. }
  291. }
  292. wcscpy(TrustedDomainName,(WCHAR*)domain);
  293. if ( Workstation[0] == L'\\' )
  294. Workstation += 2;
  295. // Use a default password
  296. for ( UINT p = 0 ; p < wcslen(Workstation) ; p++ )
  297. Password[p] = towlower(Workstation[p]);
  298. Password[wcslen(Workstation)] = 0;
  299. // ensure that the password is truncated at 14 characters
  300. Password[14] = 0;
  301. //
  302. // insure the target machine is NOT a DC, as this operation is
  303. // only appropriate against a workstation.
  304. //
  305. do // once
  306. {
  307. nas = NetServerGetInfo(activeWorkstation, 101, (LPBYTE *)&si101);
  308. if(nas != NERR_Success)
  309. {
  310. hr = HRESULT_FROM_WIN32(nas);
  311. errLocal.DbgMsgWrite(0,L"NetServerGetInfo failed, rc=%ld",nas);
  312. break;
  313. }
  314. errLocal.DbgMsgWrite(0,L"NetServerGetInfo succeeded, version=%ld.",si101->sv101_version_major);
  315. errLocal.DbgMsgWrite(0,L"Workstation=%ls, Password=%ls, activeWorkstation=%ls",Workstation,Password,activeWorkstation);
  316. GetNetJoinDomainFunction();
  317. if ( si101->sv101_version_major >= 5 && gNetJoinDomain != NULL )
  318. // if ( si101->sv101_version_major >= 5 )
  319. {
  320. BOOL bImpersonating = FALSE;
  321. if ( m_account.length() )
  322. {
  323. errLocal.DbgMsgWrite(0,L"Trying to establish session using credentials %ls\\%ls",(WCHAR*)m_domain,(WCHAR*)m_account);
  324. HANDLE hLogon;
  325. if ( LogonUser(m_account,m_domain,m_password,LOGON32_LOGON_INTERACTIVE,LOGON32_PROVIDER_DEFAULT,&hLogon) )
  326. {
  327. errLocal.DbgMsgWrite(0,L"LogonUser succeeded!");
  328. if ( ! ImpersonateLoggedOnUser(hLogon) )
  329. {
  330. errLocal.SysMsgWrite(ErrE,GetLastError(),DCT_MSG_IMPERSONATION_FAILED_SSD,(WCHAR*)m_domain,(WCHAR*)m_account,GetLastError());
  331. }
  332. else
  333. {
  334. errLocal.DbgMsgWrite(0,L"Impersonating!");
  335. bImpersonating = TRUE;
  336. }
  337. CloseHandle(hLogon);
  338. }
  339. else
  340. {
  341. errLocal.DbgMsgWrite(0,L"LogonUser failed,rc=%ld",GetLastError());
  342. }
  343. }
  344. /* bLDAP = GetLDAPPath(TrustedDomainName, Workstation, sPaths);
  345. if (bLDAP)
  346. errLocal.DbgMsgWrite(0,L"GetLDAPPath Success with %ls", sPaths);
  347. else
  348. errLocal.DbgMsgWrite(0,L"GetLDAPPath Failure");
  349. */
  350. // Use NetJoinDomain
  351. /* WCHAR amachine[MAX_PATH];
  352. if (UStrLen((WCHAR*)activeWorkstation))
  353. wcscpy(amachine, (WCHAR*)activeWorkstation);
  354. else
  355. wcscpy(amachine, LocalMachine);
  356. errLocal.DbgMsgWrite(0,L"Calling NetUnjoinDomain(%ls,%ls,xxxxxx)",amachine,(WCHAR*)m_domainAccount);
  357. hr = NetUnjoinDomain(amachine,m_domainAccount,m_password,NETSETUP_ACCT_DELETE);
  358. if (hr == NERR_Success)
  359. {
  360. errLocal.DbgMsgWrite(0,L"Calling NetJoinDomain(%ls,%ls,%ls,xxxxxx)",amachine,(WCHAR*)TrustedDomainName,
  361. (WCHAR*)m_domainAccount);
  362. hr = NetJoinDomain(amachine,TrustedDomainName,
  363. NULL,m_domainAccount,m_password,NETSETUP_JOIN_DOMAIN | NETSETUP_DOMAIN_JOIN_IF_JOINED | NETSETUP_JOIN_UNSECURE);
  364. errLocal.DbgMsgWrite(0,L"NetJoinDomain returned %ld",hr);
  365. if (hr == ERROR_NO_TRUST_SAM_ACCOUNT)
  366. {
  367. IADs * pADs = NULL;
  368. VARIANT var;
  369. VariantInit(&var);
  370. long ControlNum;
  371. errLocal.DbgMsgWrite(0,L"NetJoinDomain failed");
  372. // if (bLDAP)
  373. if (TRUE)
  374. {
  375. errLocal.DbgMsgWrite(0,L"Call ADsGetObject for %ls", srcPath);
  376. hr = ADsGetObject(srcPath,IID_IADs,(void**)&pADs);
  377. if ( SUCCEEDED(hr) )
  378. {
  379. errLocal.DbgMsgWrite(0,L"ADsGetObject Succeeded for %ls", srcPath);
  380. hr = pADs->Get(SysAllocString(L"userAccountControl"),&var);
  381. if ( SUCCEEDED(hr) )
  382. {
  383. ControlNum = var.lVal;
  384. errLocal.DbgMsgWrite(0,L"userAccountControl was %ld", ControlNum);
  385. ControlNum -= 2;
  386. errLocal.DbgMsgWrite(0,L"userAccountControl will be %ld", ControlNum);
  387. var.lVal = ControlNum;
  388. hr = pADs->Put(SysAllocString(L"userAccountControl"),var);
  389. VariantClear(&var);
  390. if ( SUCCEEDED(hr) )
  391. {
  392. errLocal.DbgMsgWrite(0,L"userAccountControl now set to %ld", ControlNum);
  393. hr = pADs->SetInfo();
  394. if ( SUCCEEDED(hr) )
  395. {
  396. errLocal.DbgMsgWrite(0,L"userAccountControl Set!");
  397. hr = NetJoinDomain(amachine,TrustedDomainName,
  398. NULL,m_domainAccount,m_password,NETSETUP_JOIN_DOMAIN | NETSETUP_DOMAIN_JOIN_IF_JOINED | NETSETUP_JOIN_UNSECURE);
  399. errLocal.DbgMsgWrite(0,L"NetJoinDomain returned %ld",hr);
  400. }
  401. }
  402. }
  403. if ( FAILED(hr) )
  404. errLocal.DbgMsgWrite(0,L"Failed to get userAccountControl for %ls", srcPath);
  405. pADs->Release();
  406. }
  407. }
  408. }
  409. else if ( hr )
  410. {
  411. hr = HRESULT_FROM_WIN32(hr);
  412. break;
  413. }
  414. }
  415. */ errLocal.DbgMsgWrite(0,L"Calling NetJoinDomain(%ls,%ls,%ls,xxxxxx)",(WCHAR*)activeWorkstation,(WCHAR*)TrustedDomainName,
  416. (WCHAR*)m_domainAccount);
  417. // if specified, use preferred domain controller
  418. _bstr_t strDomain = domain;
  419. if (domainController && *domainController)
  420. {
  421. strDomain += L"\\";
  422. strDomain += domainController;
  423. }
  424. hr = (*gNetJoinDomain)(activeWorkstation,strDomain,
  425. NULL,m_domainAccount,m_password,NETSETUP_JOIN_DOMAIN | NETSETUP_DOMAIN_JOIN_IF_JOINED | NETSETUP_JOIN_UNSECURE);
  426. errLocal.DbgMsgWrite(0,L"NetJoinDomain returned %ld",hr);
  427. if ( hr )
  428. {
  429. hr = HRESULT_FROM_WIN32(hr);
  430. break;
  431. }
  432. if ( bImpersonating )
  433. {
  434. errLocal.DbgMsgWrite(0,L"Reverting to self.");
  435. RevertToSelf();
  436. }
  437. }
  438. else
  439. {
  440. errLocal.DbgMsgWrite(0,L"NetJoinDomain function is not available. Using LSA APIs instead.");
  441. // Use LSA APIs
  442. Type = si101->sv101_type;
  443. if( (Type & SV_TYPE_DOMAIN_CTRL) ||
  444. (Type & SV_TYPE_DOMAIN_BAKCTRL) )
  445. {
  446. swprintf(errMsg,GET_STRING(IDS_NotAllowedOnDomainController));
  447. errLocal.DbgMsgWrite(ErrE,L"Cannot migrate domain controller");
  448. hr = E_NOTIMPL;
  449. break;
  450. }
  451. errLocal.DbgMsgWrite(0,L"This computer is not a Domain Controller.");
  452. //
  453. // do not allow a workstation to trust itself
  454. //
  455. if(lstrcmpiW(TrustedDomainName, Workstation) == 0)
  456. {
  457. swprintf(errMsg,GET_STRING(IDS_CannotTrustSelf),
  458. TrustedDomainName);
  459. errLocal.DbgMsgWrite(0,L"Cannot trust workstation itself");
  460. hr = E_INVALIDARG;
  461. break;
  462. }
  463. if( lstrlenW(TrustedDomainName ) > MAX_COMPUTERNAME_LENGTH )
  464. {
  465. TrustedDomainName[MAX_COMPUTERNAME_LENGTH] = L'\0'; // truncate
  466. }
  467. //
  468. // get the name of the domain DC
  469. //
  470. if(!GetDomainDCName(TrustedDomainName,&PrimaryDC))
  471. {
  472. swprintf(errMsg,GET_STRING(IDS_CannotGetDCName),TrustedDomainName);
  473. hr = HRESULT_FROM_WIN32(GetLastError());
  474. errLocal.DbgMsgWrite(ErrE,L"GetDomainDCName(%ls), rc=%ld",TrustedDomainName,GetLastError());
  475. break;
  476. }
  477. errLocal.DbgMsgWrite(0,L"GetDomainDCName succeeded.");
  478. if ( ! m_bNoChange )
  479. {
  480. //
  481. // build the DomainSid of the domain to trust
  482. //
  483. DomainSid = SidFromString(domainSid);
  484. if(!DomainSid )
  485. {
  486. //DisplayError(errMsg,"GetDomainSid", GetLastError(),NULL);
  487. hr = HRESULT_FROM_WIN32(GetLastError());
  488. errLocal.DbgMsgWrite(ErrE,L"GetDomainSid(%ls), rc=%ld",PrimaryDC,GetLastError());
  489. break;
  490. }
  491. errLocal.DbgMsgWrite(0,L"GetDomainSid succeeded.");
  492. }
  493. errLocal.DbgMsgWrite(0,L"m_bNoChange=%ld, version=%ld",m_bNoChange,si101->sv101_version_major);
  494. if ( (!m_bNoChange) && (si101->sv101_version_major < 4) )
  495. {
  496. // For NT 3.51 machines, we must move the computer to a workgroup, and
  497. // then move it into the new domain
  498. hr = ChangeToWorkgroup(SysAllocString(activeWorkstation),SysAllocString(L"WORKGROUP"),errReturn);
  499. QueryWorkstationTrustedDomainInfo(PolicyHandle,DomainSid,m_bNoChange);
  500. if ( FAILED(hr) )
  501. {
  502. errLocal.DbgMsgWrite(0,L"ChangeToWorkgroup failed, hr=%lx",hr);
  503. }
  504. else
  505. {
  506. errLocal.DbgMsgWrite(0,L"ChangeToWorkgroup succeeded, hr=%lx",hr);
  507. }
  508. }
  509. //
  510. // see if the computer account exists on the domain
  511. //
  512. //
  513. // open the policy on this computer
  514. //
  515. Status = OpenPolicy(
  516. activeWorkstation,
  517. DELETE | // to remove a trust
  518. POLICY_VIEW_LOCAL_INFORMATION | // to view trusts
  519. POLICY_CREATE_SECRET | // for password set operation
  520. POLICY_TRUST_ADMIN, // for trust creation
  521. &PolicyHandle
  522. );
  523. if( Status != STATUS_SUCCESS )
  524. {
  525. hr = HRESULT_FROM_WIN32(LsaNtStatusToWinError(Status));
  526. errLocal.DbgMsgWrite(ErrE,L"OpenPolicy failed,hr=%lx",hr);
  527. break;
  528. }
  529. errLocal.DbgMsgWrite(ErrE,L"OpenPolicy succeeded.");
  530. if ( ! m_bNoChange )
  531. {
  532. QueryWorkstationTrustedDomainInfo(PolicyHandle,DomainSid,m_bNoChange);
  533. Status = SetWorkstationTrustedDomainInfo(
  534. PolicyHandle,
  535. DomainSid,
  536. TrustedDomainName,
  537. Password,
  538. errMsg
  539. );
  540. }
  541. if( Status != STATUS_SUCCESS )
  542. {
  543. hr = HRESULT_FROM_WIN32(LsaNtStatusToWinError(Status));
  544. errLocal.DbgMsgWrite(ErrE,L"SetWorkstationTrustedDomainInfo failed,hr=%lx",Status);
  545. break;
  546. }
  547. errLocal.DbgMsgWrite(ErrE,L"SetWkstaTrustedDomainInfo succeeded.");
  548. //
  549. // Update the primary domain to match the specified trusted domain
  550. //
  551. if (! m_bNoChange )
  552. {
  553. Status = SetPrimaryDomain(PolicyHandle, DomainSid, TrustedDomainName);
  554. if(Status != STATUS_SUCCESS)
  555. {
  556. // DisplayNtStatus(errMsg,"SetPrimaryDomain", Status,NULL);
  557. hr = HRESULT_FROM_WIN32(LsaNtStatusToWinError(Status));
  558. errLocal.DbgMsgWrite(ErrE,L"SetPrimaryDomain failed,hr=%lx",hr);
  559. break;
  560. }
  561. errLocal.DbgMsgWrite(ErrE,L"SetPrimaryDomain succeeded.");
  562. }
  563. NetApiBufferFree(si101);
  564. }
  565. } while (false); // do once
  566. // Cleanup
  567. //
  568. // free the buffer allocated for the PDC computer name
  569. //
  570. if(PrimaryDC)
  571. {
  572. NetApiBufferFree(PrimaryDC);
  573. }
  574. //LocalFree(Workstation);
  575. //
  576. // free the Sid which was allocated for the TrustedDomain Sid
  577. //
  578. if(DomainSid)
  579. {
  580. FreeSid(DomainSid);
  581. }
  582. //
  583. // close the policy handle
  584. //
  585. if ( PolicyHandle )
  586. {
  587. LsaClose(PolicyHandle);
  588. }
  589. if ( bSessionEstablished )
  590. {
  591. EstablishSession(activeWorkstation,m_domain,m_account,m_password,FALSE);
  592. }
  593. /* if ( hDll )
  594. {
  595. FreeLibrary(hDll);
  596. hDll = NULL;
  597. }
  598. */ if ( FAILED(hr) )
  599. {
  600. (*errReturn) = NULL;
  601. }
  602. return hr;
  603. }
  604. STDMETHODIMP
  605. CChangeDomain::ChangeToWorkgroup(
  606. BSTR Computer, // in - name of computer to update
  607. BSTR Workgroup, // in - name of workgroup to join
  608. BSTR * errReturn // out- text describing error if failure
  609. )
  610. {
  611. HRESULT hr = S_OK;
  612. LSA_HANDLE PolicyHandle;
  613. LPWSTR Workstation; // target machine of policy update
  614. LPWSTR TrustedDomainName; // domain to join
  615. // LPWSTR PrimaryDC=NULL; // name of that domain's PDC
  616. PSERVER_INFO_101 si101;
  617. DWORD Type;
  618. NET_API_STATUS nas;
  619. NTSTATUS Status;
  620. WCHAR errMsg[1000];
  621. // BOOL bSessionEstablished = FALSE;
  622. // initialize output parameters
  623. (*errReturn) = NULL;
  624. Workstation = (WCHAR*)Computer;
  625. TrustedDomainName = (WCHAR*)Workgroup;
  626. errLocal.DbgMsgWrite(0,L"Changing to workgroup...");
  627. //
  628. // insure the target machine is NOT a DC, as this operation is
  629. // only appropriate against a workstation.
  630. //
  631. do // once
  632. {
  633. /*if ( m_account.length() )
  634. {
  635. // Establish our credentials to the target machine
  636. if (! EstablishSession(Workstation,m_domain,m_account,m_password, TRUE) )
  637. {
  638. // DisplayError(errMsg,"EstablishSession",GetLastError(),NULL);
  639. hr = GetLastError();
  640. }
  641. else
  642. {
  643. bSessionEstablished = TRUE;
  644. }
  645. }
  646. */
  647. nas = NetServerGetInfo(Workstation, 101, (LPBYTE *)&si101);
  648. if(nas != NERR_Success)
  649. {
  650. //DisplayError(errMsg, "NetServerGetInfo", nas,NULL);
  651. hr = E_FAIL;
  652. break;
  653. }
  654. Type = si101->sv101_type;
  655. NetApiBufferFree(si101);
  656. if( (Type & SV_TYPE_DOMAIN_CTRL) ||
  657. (Type & SV_TYPE_DOMAIN_BAKCTRL) )
  658. {
  659. swprintf(errMsg,L"Operation is not valid on a domain controller.\n");
  660. hr = E_FAIL;
  661. break;
  662. }
  663. //
  664. // do not allow a workstation to trust itself
  665. //
  666. if(lstrcmpiW(TrustedDomainName, Workstation) == 0)
  667. {
  668. swprintf(errMsg,L"Error: Domain %ls cannot be a member of itself.\n",
  669. TrustedDomainName);
  670. hr = E_FAIL;
  671. break;
  672. }
  673. if( lstrlenW(TrustedDomainName ) > MAX_COMPUTERNAME_LENGTH )
  674. {
  675. TrustedDomainName[MAX_COMPUTERNAME_LENGTH] = L'\0'; // truncate
  676. }
  677. //
  678. // open the policy on this computer
  679. //
  680. Status = OpenPolicy(
  681. Workstation,
  682. DELETE | // to remove a trust
  683. POLICY_VIEW_LOCAL_INFORMATION | // to view trusts
  684. POLICY_CREATE_SECRET | // for password set operation
  685. POLICY_TRUST_ADMIN, // for trust creation
  686. &PolicyHandle
  687. );
  688. if( Status != STATUS_SUCCESS )
  689. {
  690. //DisplayNtStatus(errMsg,"OpenPolicy", Status,NULL);
  691. hr = LsaNtStatusToWinError(Status);
  692. break;
  693. }
  694. if( Status != STATUS_SUCCESS )
  695. {
  696. hr = E_FAIL;
  697. break;
  698. }
  699. //
  700. // Update the primary domain to match the specified trusted domain
  701. //
  702. if (! m_bNoChange )
  703. {
  704. Status = SetPrimaryDomain(PolicyHandle, NULL, TrustedDomainName);
  705. if(Status != STATUS_SUCCESS)
  706. {
  707. //DisplayNtStatus(errMsg,"SetPrimaryDomain", Status,NULL);
  708. hr = LsaNtStatusToWinError(Status);
  709. break;
  710. }
  711. }
  712. } while (false); // do once
  713. // Cleanup
  714. //
  715. // close the policy handle
  716. //
  717. LsaClose(PolicyHandle);
  718. /*if ( bSessionEstablished )
  719. {
  720. EstablishSession(Workstation,m_domain,m_account,m_password,FALSE);
  721. }
  722. */
  723. if ( FAILED(hr) )
  724. {
  725. hr = S_FALSE;
  726. (*errReturn) = SysAllocString(errMsg);
  727. }
  728. return hr;
  729. }
  730. STDMETHODIMP
  731. CChangeDomain::ConnectAs(
  732. BSTR domain, // in - domain name to use for credentials
  733. BSTR user, // in - account name to use for credentials
  734. BSTR password // in - password to use for credentials
  735. )
  736. {
  737. m_domain = domain;
  738. m_account = user;
  739. m_password = password;
  740. m_domainAccount = domain;
  741. m_domainAccount += L"\\";
  742. m_domainAccount += user;
  743. return S_OK;
  744. }
  745. STDMETHODIMP
  746. CChangeDomain::get_NoChange(
  747. BOOL * pVal // out- flag, whether to write changes
  748. )
  749. {
  750. (*pVal) = m_bNoChange;
  751. return S_OK;
  752. }
  753. STDMETHODIMP
  754. CChangeDomain::put_NoChange(
  755. BOOL newVal // in - flag, whether to write changes
  756. )
  757. {
  758. m_bNoChange = newVal;
  759. return S_OK;
  760. }
  761. // ChangeDomain worknode: Changes the domain affiliation of a workstation or server
  762. // (This operation cannot be performed on domain controllers)
  763. //
  764. // VarSet syntax:
  765. //
  766. // Input:
  767. // ChangeDomain.Computer: <ComputerName>
  768. // ChangeDomain.TargetDomain: <Domain>
  769. // ChangeDomain.DomainIsWorkgroup: <Yes|No> default is No
  770. // ChangeDomain.ConnectAs.Domain: <Domain> optional credentials to use
  771. // ChangeDomain.ConnectAs.User : <Username>
  772. // ChangeDomain.ConnectAs.Password : <Password>
  773. //
  774. // Output:
  775. // ChangeDomain.ErrorText : <string-error message>
  776. // This function is not currently used by the domain migration tool.
  777. // it is here to provide an alternate API for scripting clients
  778. STDMETHODIMP
  779. CChangeDomain::Process(
  780. IUnknown * pWorkItem
  781. )
  782. {
  783. HRESULT hr = S_OK;
  784. IVarSetPtr pVarSet = pWorkItem;
  785. _bstr_t computer;
  786. _bstr_t domain;
  787. _bstr_t text;
  788. BOOL bWorkgroup = FALSE;
  789. BSTR errText = NULL;
  790. computer = pVarSet->get(L"ChangeDomain.Computer");
  791. domain = pVarSet->get(L"ChangeDomain.TargetDomain");
  792. text = pVarSet->get(L"ChangeDomain.DomainIsWorkgroup");
  793. if ( ! UStrICmp(text,GET_STRING(IDS_YES)) )
  794. {
  795. bWorkgroup = TRUE;
  796. }
  797. _bstr_t userdomain = pVarSet->get(L"ChangeDomain.ConnectAs.Domain");
  798. _bstr_t username = pVarSet->get(L"ChangeDomain.ConnectAs.User");
  799. _bstr_t password = pVarSet->get(L"ChangeDomain.ConnectAs.Password");
  800. if ( username.length() )
  801. {
  802. ConnectAs(userdomain,username,password);
  803. }
  804. if ( bWorkgroup )
  805. {
  806. hr = ChangeToWorkgroup(computer,domain,&errText);
  807. }
  808. else
  809. {
  810. hr = ChangeToDomain(computer,domain,NULL,&errText);
  811. }
  812. if ( text.length() )
  813. {
  814. pVarSet->put(L"ChangeDomain.ErrorText",errText);
  815. }
  816. return hr;
  817. }