/*--------------------------------------------------------------------------- File: ChangeDomain.cpp Comments: Implementation of COM object that changes the domain affiliation on a computer. (c) Copyright 1999, Mission Critical Software, Inc., All Rights Reserved Proprietary and confidential to Mission Critical Software, Inc. REVISION LOG ENTRY Revision By: Christy Boles Revised on 02/15/99 11:21:07 --------------------------------------------------------------------------- */ // ChangeDomain.cpp : Implementation of CChangeDomain #include "stdafx.h" #include "WorkObj.h" #include "ChDom.h" #include "Common.hpp" #include "UString.hpp" #include "EaLen.hpp" #include "ResStr.h" #include "ErrDct.hpp" #include "TxtSid.h" ///////////////////////////////////////////////////////////////////////////// // CChangeDomain #include "LSAUtils.h" //#import "\bin\McsVarSetMin.tlb" no_namespace #import "NetEnum.tlb" no_namespace #import "VarSet.tlb" no_namespace rename("property", "aproperty") //#include //#include #include // for NetXxx API #include //#include #include TErrorDct errLocal; // constants from lmjoin.h needed for NetJoinDomain which does not exist on NT 4. #define NETSETUP_JOIN_DOMAIN 0x00000001 // If not present, workgroup is joined #define NETSETUP_ACCT_CREATE 0x00000002 // Do the server side account creation/rename #define NETSETUP_ACCT_DELETE 0x00000004 // Delete the account when a domain is left #define NETSETUP_WIN9X_UPGRADE 0x00000010 // Invoked during upgrade of Windows 9x to // Windows NT #define NETSETUP_DOMAIN_JOIN_IF_JOINED 0x00000020 // Allow the client to join a new domain // even if it is already joined to a domain #define NETSETUP_JOIN_UNSECURE 0x00000040 // Performs an unsecure join #define NETSETUP_INSTALL_INVOCATION 0x00040000 // The APIs were invoked during install typedef NET_API_STATUS NET_API_FUNCTION PNETJOINDOMAIN( IN LPCWSTR lpServer OPTIONAL, IN LPCWSTR lpDomain, IN LPCWSTR lpAccountOU, OPTIONAL IN LPCWSTR lpAccount OPTIONAL, IN LPCWSTR lpPassword OPTIONAL, IN DWORD fJoinOptions ); HINSTANCE hDll = NULL; PNETJOINDOMAIN * gNetJoinDomain = NULL; BOOL GetNetJoinDomainFunction() { BOOL bSuccess = FALSE; hDll = LoadLibrary(L"NetApi32.dll"); if ( hDll ) { gNetJoinDomain = (PNETJOINDOMAIN*)GetProcAddress(hDll,"NetJoinDomain"); if ( gNetJoinDomain ) { bSuccess = TRUE; } } return bSuccess; } typedef HRESULT (CALLBACK * ADSGETOBJECT)(LPWSTR, REFIID, void**); extern ADSGETOBJECT ADsGetObject; /*BOOL GetLDAPPath(WCHAR * sTgtDomain, WCHAR * sSAM, WCHAR * sPath) { if ( pOptions->tgtDomainVer < 5 ) { // for NT4 we can just build the path and send it back. wsprintf(sPath, L"WinNT://%s/%s", pOptions->tgtDomain, sSam); return S_OK; } // Use the net object enumerator to lookup the account in the target domain. INetObjEnumeratorPtr pQuery(__uuidof(NetObjEnumerator)); IEnumVARIANT * pEnum = NULL; SAFEARRAYBOUND bd = { 1, 0 }; SAFEARRAY * pszColNames; BSTR HUGEP * pData = NULL; BSTR sData[] = { L"aDSPath" }; WCHAR sQuery[LEN_Path]; WCHAR sDomPath[LEN_Path]; DWORD ret = 0; _variant_t var, varVal; HRESULT hr = S_OK; WCHAR tgtNamingContext[LEN_Path]; WCHAR aPath[LEN_Path]; IADs * pAds; BOOL bFoundNC = FALSE; BOOL bConverted = FALSE; errLocal.DbgMsgWrite(0,L"GetLDAPPath for %ls in %ls", sSAM, sTgtDomain); wsprintf(aPath, L"LDAP://%s/rootDSE", sTgtDomain); hr = ADsGetObject(aPath, IID_IADs, (void**)&pAds); if ( SUCCEEDED(hr) ) { errLocal.DbgMsgWrite(0,L"ADsGetObject Succeeded"); hr = pAds->Get(L"defaultNamingContext", &var); if ( SUCCEEDED(hr) ) { pAds->Release(); wcscpy(tgtNamingContext, (WCHAR*) V_BSTR(&var)); errLocal.DbgMsgWrite(0,L"NamingContext is %ls", tgtNamingContext); bFoundNC = TRUE; } } if (!bFoundNC) return FALSE; wsprintf(sDomPath, L"LDAP://%s/%s", sTgtDomain, tgtNamingContext); WCHAR sTempSamName[LEN_Path]; wcscpy(sTempSamName, sSAM); if ( sTempSamName[0] == L' ' ) { WCHAR sTemp[LEN_Path]; wsprintf(sTemp, L"\\20%s", sTempSamName + 1); wcscpy(sTempSamName, sTemp); } wsprintf(sQuery, L"(sAMAccountName=%s)", sTempSamName); errLocal.DbgMsgWrite(0,L"Query for %ls", sQuery); hr = pQuery->raw_SetQuery(sDomPath, sTgtDomain, sQuery, ADS_SCOPE_SUBTREE, FALSE); // Set up the columns that we want back from the query ( in this case we need SAM accountname ) pszColNames = ::SafeArrayCreate(VT_BSTR, 1, &bd); hr = ::SafeArrayAccessData(pszColNames, (void HUGEP **)&pData); if ( SUCCEEDED(hr) ) pData[0] = sData[0]; if ( SUCCEEDED(hr) ) hr = ::SafeArrayUnaccessData(pszColNames); if ( SUCCEEDED(hr) ) hr = pQuery->raw_SetColumns(pszColNames); // Time to execute the plan. if ( SUCCEEDED(hr) ) hr = pQuery->raw_Execute(&pEnum); if ( SUCCEEDED(hr) ) { // if this worked that means we can only have one thing in the result. if ( (pEnum->Next(1, &var, &ret) == S_OK) && ( ret > 0 ) ) { SAFEARRAY * pArray = var.parray; long ndx = 0; hr = SafeArrayGetElement(pArray,&ndx,&varVal); if ( SUCCEEDED(hr) ) { wcscpy(sPath, (WCHAR*)varVal.bstrVal); errLocal.DbgMsgWrite(0,L"Got LDAP of %ls", sPath); bConverted = TRUE; } else hr = HRESULT_FROM_WIN32(NERR_UserNotFound); } else hr = HRESULT_FROM_WIN32(NERR_UserNotFound); VariantInit(&var); } if ( pEnum ) pEnum->Release(); return bConverted; } */ /*BOOL GetLDAPPath(WCHAR* Domain, WCHAR* Account, WCHAR* sLDAPPath, WCHAR* srcDomainDNS) { PDS_NAME_RESULT pNamesOut = NULL; WCHAR * pNamesIn[1]; HANDLE hDs = NULL; WCHAR OrigAccount[MAX_PATH]; BOOL bConverted = FALSE; errLocal.DbgMsgWrite(0,L"Parameters %ls, %ls, %ls", Domain, Account, srcDomainDNS); wcscpy(sLDAPPath, L"LDAP://"); wcscat(sLDAPPath, Domain); _wcsupr(sLDAPPath); errLocal.DbgMsgWrite(0,L"Start of LDAP Path, %ls", sLDAPPath); wcscpy(OrigAccount, Domain); wcscat(OrigAccount, L"\\"); wcscat(OrigAccount, Account); errLocal.DbgMsgWrite(0,L"Oringinal Account Name, %ls", OrigAccount); pNamesIn[0] = &OrigAccount[0]; //bind to that source domain HRESULT hr = DsBind(NULL,srcDomainDNS,&hDs); if ( !hr ) { errLocal.DbgMsgWrite(0,L"Bound to %ls", srcDomainDNS); //get UPN name of this account from DSCrackNames hr = DsCrackNames(hDs,DS_NAME_NO_FLAGS,DS_NT4_ACCOUNT_NAME,DS_FQDN_1779_NAME,1,pNamesIn,&pNamesOut); if ( !hr ) { //if got the UPN name, retry DB query for that account in the errLocal.DbgMsgWrite(0,L"DSCrackNames Succeeded"); //service account database if ( pNamesOut->rItems[0].status == DS_NAME_NO_ERROR ) { wcscat(sLDAPPath, pNamesOut->rItems[0].pName); errLocal.DbgMsgWrite(0,L"New LDAP path, %ls", sLDAPPath); bConverted = TRUE; } else if (pNamesOut->rItems[0].status == DS_NAME_ERROR_DOMAIN_ONLY) errLocal.DbgMsgWrite(0,L"DS_NAME_ERROR_DOMAIN_ONLY = %ls", pNamesOut->rItems[0].pDomain); else if (pNamesOut->rItems[0].status == DS_NAME_ERROR_NO_MAPPING) errLocal.DbgMsgWrite(0,L"DS_NAME_ERROR_NO_MAPPING"); else if (pNamesOut->rItems[0].status == DS_NAME_ERROR_NOT_FOUND) errLocal.DbgMsgWrite(0,L"DS_NAME_ERROR_NOT_FOUND"); else if (pNamesOut->rItems[0].status == DS_NAME_ERROR_NOT_UNIQUE) errLocal.DbgMsgWrite(0,L"DS_NAME_ERROR_NOT_UNIQUE"); else if (pNamesOut->rItems[0].status == DS_NAME_ERROR_RESOLVING) errLocal.DbgMsgWrite(0,L"DS_NAME_ERROR_RESOLVING"); else errLocal.DbgMsgWrite(0,L"General DSCrackNames Error"); DsFreeNameResult(pNamesOut); } } return bConverted; } */ STDMETHODIMP CChangeDomain::ChangeToDomain( BSTR activeComputerName, // in - computer name currently being used (old name if simultaneously renaming and changing domain) BSTR domain, // in - domain to move computer to 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) BSTR * errReturn // out- string describing any errors that occurred ) { HRESULT hr = S_OK; return hr; } STDMETHODIMP CChangeDomain::ChangeToDomainWithSid( BSTR activeComputerName, // in - computer name currently being used (old name if simultaneously renaming and changing domain) BSTR domain, // in - domain to move computer to BSTR domainSid, // in - sid of domain, as string BSTR domainController, // in - domain controller to use 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) BSTR srcPath, // in - source account original LDAP path BSTR * errReturn // out- string describing any errors that occurred ) { HRESULT hr = S_OK; LSA_HANDLE PolicyHandle = NULL; LPWSTR Workstation; // target machine of policy update WCHAR Password[LEN_Password]; PSID DomainSid=NULL; // Sid representing domain to trust LPWSTR PrimaryDC=NULL; // name of that domain's PDC PSERVER_INFO_101 si101 = NULL; DWORD Type; NET_API_STATUS nas; NTSTATUS Status; WCHAR errMsg[1000]; BOOL bSessionEstablished = TRUE; WCHAR TrustedDomainName[LEN_Domain]; WCHAR LocalMachine[MAX_PATH] = L""; DWORD lenLocalMachine = DIM(LocalMachine); LPWSTR activeWorkstation = L""; // WCHAR * errString = GET_BSTR(IDS_Unspecified_Failure); // WCHAR sPaths[MAX_PATH]; // BOOL bLDAP; // initialize output parameters (*errReturn) = NULL; // commenting out the line below will write a log file that is useful for debugging // errLocal.LogOpen(L"C:\\ChangeDomain.log",0); //sleep for 3 minutes so I can attach in the debugger //errLocal.DbgMsgWrite(0,L"Started 3 minute sleep"); //SleepEx(120000, FALSE); //errLocal.DbgMsgWrite(0,L"Ended 3 minute sleep"); // use the target name, if provided if ( newComputerName && UStrLen((WCHAR*)newComputerName) ) { Workstation = (WCHAR*)newComputerName; if ( ! activeComputerName || ! UStrLen((WCHAR*)activeComputerName) ) { activeWorkstation = LocalMachine; } else { activeWorkstation = (WCHAR*)activeComputerName; } } else { if (! activeComputerName || ! UStrLen((WCHAR*)activeComputerName) ) { GetComputerName(LocalMachine,&lenLocalMachine); Workstation = LocalMachine; activeWorkstation = L""; } else { Workstation = (WCHAR*)activeComputerName; activeWorkstation = Workstation; } } wcscpy(TrustedDomainName,(WCHAR*)domain); if ( Workstation[0] == L'\\' ) Workstation += 2; // Use a default password for ( UINT p = 0 ; p < wcslen(Workstation) ; p++ ) Password[p] = towlower(Workstation[p]); Password[wcslen(Workstation)] = 0; // ensure that the password is truncated at 14 characters Password[14] = 0; // // insure the target machine is NOT a DC, as this operation is // only appropriate against a workstation. // do // once { nas = NetServerGetInfo(activeWorkstation, 101, (LPBYTE *)&si101); if(nas != NERR_Success) { hr = HRESULT_FROM_WIN32(nas); errLocal.DbgMsgWrite(0,L"NetServerGetInfo failed, rc=%ld",nas); break; } errLocal.DbgMsgWrite(0,L"NetServerGetInfo succeeded, version=%ld.",si101->sv101_version_major); errLocal.DbgMsgWrite(0,L"Workstation=%ls, Password=%ls, activeWorkstation=%ls",Workstation,Password,activeWorkstation); GetNetJoinDomainFunction(); if ( si101->sv101_version_major >= 5 && gNetJoinDomain != NULL ) // if ( si101->sv101_version_major >= 5 ) { BOOL bImpersonating = FALSE; if ( m_account.length() ) { errLocal.DbgMsgWrite(0,L"Trying to establish session using credentials %ls\\%ls",(WCHAR*)m_domain,(WCHAR*)m_account); HANDLE hLogon; if ( LogonUser(m_account,m_domain,m_password,LOGON32_LOGON_INTERACTIVE,LOGON32_PROVIDER_DEFAULT,&hLogon) ) { errLocal.DbgMsgWrite(0,L"LogonUser succeeded!"); if ( ! ImpersonateLoggedOnUser(hLogon) ) { errLocal.SysMsgWrite(ErrE,GetLastError(),DCT_MSG_IMPERSONATION_FAILED_SSD,(WCHAR*)m_domain,(WCHAR*)m_account,GetLastError()); } else { errLocal.DbgMsgWrite(0,L"Impersonating!"); bImpersonating = TRUE; } CloseHandle(hLogon); } else { errLocal.DbgMsgWrite(0,L"LogonUser failed,rc=%ld",GetLastError()); } } /* bLDAP = GetLDAPPath(TrustedDomainName, Workstation, sPaths); if (bLDAP) errLocal.DbgMsgWrite(0,L"GetLDAPPath Success with %ls", sPaths); else errLocal.DbgMsgWrite(0,L"GetLDAPPath Failure"); */ // Use NetJoinDomain /* WCHAR amachine[MAX_PATH]; if (UStrLen((WCHAR*)activeWorkstation)) wcscpy(amachine, (WCHAR*)activeWorkstation); else wcscpy(amachine, LocalMachine); errLocal.DbgMsgWrite(0,L"Calling NetUnjoinDomain(%ls,%ls,xxxxxx)",amachine,(WCHAR*)m_domainAccount); hr = NetUnjoinDomain(amachine,m_domainAccount,m_password,NETSETUP_ACCT_DELETE); if (hr == NERR_Success) { errLocal.DbgMsgWrite(0,L"Calling NetJoinDomain(%ls,%ls,%ls,xxxxxx)",amachine,(WCHAR*)TrustedDomainName, (WCHAR*)m_domainAccount); hr = NetJoinDomain(amachine,TrustedDomainName, NULL,m_domainAccount,m_password,NETSETUP_JOIN_DOMAIN | NETSETUP_DOMAIN_JOIN_IF_JOINED | NETSETUP_JOIN_UNSECURE); errLocal.DbgMsgWrite(0,L"NetJoinDomain returned %ld",hr); if (hr == ERROR_NO_TRUST_SAM_ACCOUNT) { IADs * pADs = NULL; VARIANT var; VariantInit(&var); long ControlNum; errLocal.DbgMsgWrite(0,L"NetJoinDomain failed"); // if (bLDAP) if (TRUE) { errLocal.DbgMsgWrite(0,L"Call ADsGetObject for %ls", srcPath); hr = ADsGetObject(srcPath,IID_IADs,(void**)&pADs); if ( SUCCEEDED(hr) ) { errLocal.DbgMsgWrite(0,L"ADsGetObject Succeeded for %ls", srcPath); hr = pADs->Get(SysAllocString(L"userAccountControl"),&var); if ( SUCCEEDED(hr) ) { ControlNum = var.lVal; errLocal.DbgMsgWrite(0,L"userAccountControl was %ld", ControlNum); ControlNum -= 2; errLocal.DbgMsgWrite(0,L"userAccountControl will be %ld", ControlNum); var.lVal = ControlNum; hr = pADs->Put(SysAllocString(L"userAccountControl"),var); VariantClear(&var); if ( SUCCEEDED(hr) ) { errLocal.DbgMsgWrite(0,L"userAccountControl now set to %ld", ControlNum); hr = pADs->SetInfo(); if ( SUCCEEDED(hr) ) { errLocal.DbgMsgWrite(0,L"userAccountControl Set!"); hr = NetJoinDomain(amachine,TrustedDomainName, NULL,m_domainAccount,m_password,NETSETUP_JOIN_DOMAIN | NETSETUP_DOMAIN_JOIN_IF_JOINED | NETSETUP_JOIN_UNSECURE); errLocal.DbgMsgWrite(0,L"NetJoinDomain returned %ld",hr); } } } if ( FAILED(hr) ) errLocal.DbgMsgWrite(0,L"Failed to get userAccountControl for %ls", srcPath); pADs->Release(); } } } else if ( hr ) { hr = HRESULT_FROM_WIN32(hr); break; } } */ errLocal.DbgMsgWrite(0,L"Calling NetJoinDomain(%ls,%ls,%ls,xxxxxx)",(WCHAR*)activeWorkstation,(WCHAR*)TrustedDomainName, (WCHAR*)m_domainAccount); // if specified, use preferred domain controller _bstr_t strDomain = domain; if (domainController && *domainController) { strDomain += L"\\"; strDomain += domainController; } hr = (*gNetJoinDomain)(activeWorkstation,strDomain, NULL,m_domainAccount,m_password,NETSETUP_JOIN_DOMAIN | NETSETUP_DOMAIN_JOIN_IF_JOINED | NETSETUP_JOIN_UNSECURE); errLocal.DbgMsgWrite(0,L"NetJoinDomain returned %ld",hr); if ( hr ) { hr = HRESULT_FROM_WIN32(hr); break; } if ( bImpersonating ) { errLocal.DbgMsgWrite(0,L"Reverting to self."); RevertToSelf(); } } else { errLocal.DbgMsgWrite(0,L"NetJoinDomain function is not available. Using LSA APIs instead."); // Use LSA APIs Type = si101->sv101_type; if( (Type & SV_TYPE_DOMAIN_CTRL) || (Type & SV_TYPE_DOMAIN_BAKCTRL) ) { swprintf(errMsg,GET_STRING(IDS_NotAllowedOnDomainController)); errLocal.DbgMsgWrite(ErrE,L"Cannot migrate domain controller"); hr = E_NOTIMPL; break; } errLocal.DbgMsgWrite(0,L"This computer is not a Domain Controller."); // // do not allow a workstation to trust itself // if(lstrcmpiW(TrustedDomainName, Workstation) == 0) { swprintf(errMsg,GET_STRING(IDS_CannotTrustSelf), TrustedDomainName); errLocal.DbgMsgWrite(0,L"Cannot trust workstation itself"); hr = E_INVALIDARG; break; } if( lstrlenW(TrustedDomainName ) > MAX_COMPUTERNAME_LENGTH ) { TrustedDomainName[MAX_COMPUTERNAME_LENGTH] = L'\0'; // truncate } // // get the name of the domain DC // if(!GetDomainDCName(TrustedDomainName,&PrimaryDC)) { swprintf(errMsg,GET_STRING(IDS_CannotGetDCName),TrustedDomainName); hr = HRESULT_FROM_WIN32(GetLastError()); errLocal.DbgMsgWrite(ErrE,L"GetDomainDCName(%ls), rc=%ld",TrustedDomainName,GetLastError()); break; } errLocal.DbgMsgWrite(0,L"GetDomainDCName succeeded."); if ( ! m_bNoChange ) { // // build the DomainSid of the domain to trust // DomainSid = SidFromString(domainSid); if(!DomainSid ) { //DisplayError(errMsg,"GetDomainSid", GetLastError(),NULL); hr = HRESULT_FROM_WIN32(GetLastError()); errLocal.DbgMsgWrite(ErrE,L"GetDomainSid(%ls), rc=%ld",PrimaryDC,GetLastError()); break; } errLocal.DbgMsgWrite(0,L"GetDomainSid succeeded."); } errLocal.DbgMsgWrite(0,L"m_bNoChange=%ld, version=%ld",m_bNoChange,si101->sv101_version_major); if ( (!m_bNoChange) && (si101->sv101_version_major < 4) ) { // For NT 3.51 machines, we must move the computer to a workgroup, and // then move it into the new domain hr = ChangeToWorkgroup(SysAllocString(activeWorkstation),SysAllocString(L"WORKGROUP"),errReturn); QueryWorkstationTrustedDomainInfo(PolicyHandle,DomainSid,m_bNoChange); if ( FAILED(hr) ) { errLocal.DbgMsgWrite(0,L"ChangeToWorkgroup failed, hr=%lx",hr); } else { errLocal.DbgMsgWrite(0,L"ChangeToWorkgroup succeeded, hr=%lx",hr); } } // // see if the computer account exists on the domain // // // open the policy on this computer // Status = OpenPolicy( activeWorkstation, DELETE | // to remove a trust POLICY_VIEW_LOCAL_INFORMATION | // to view trusts POLICY_CREATE_SECRET | // for password set operation POLICY_TRUST_ADMIN, // for trust creation &PolicyHandle ); if( Status != STATUS_SUCCESS ) { hr = HRESULT_FROM_WIN32(LsaNtStatusToWinError(Status)); errLocal.DbgMsgWrite(ErrE,L"OpenPolicy failed,hr=%lx",hr); break; } errLocal.DbgMsgWrite(ErrE,L"OpenPolicy succeeded."); if ( ! m_bNoChange ) { QueryWorkstationTrustedDomainInfo(PolicyHandle,DomainSid,m_bNoChange); Status = SetWorkstationTrustedDomainInfo( PolicyHandle, DomainSid, TrustedDomainName, Password, errMsg ); } if( Status != STATUS_SUCCESS ) { hr = HRESULT_FROM_WIN32(LsaNtStatusToWinError(Status)); errLocal.DbgMsgWrite(ErrE,L"SetWorkstationTrustedDomainInfo failed,hr=%lx",Status); break; } errLocal.DbgMsgWrite(ErrE,L"SetWkstaTrustedDomainInfo succeeded."); // // Update the primary domain to match the specified trusted domain // if (! m_bNoChange ) { Status = SetPrimaryDomain(PolicyHandle, DomainSid, TrustedDomainName); if(Status != STATUS_SUCCESS) { // DisplayNtStatus(errMsg,"SetPrimaryDomain", Status,NULL); hr = HRESULT_FROM_WIN32(LsaNtStatusToWinError(Status)); errLocal.DbgMsgWrite(ErrE,L"SetPrimaryDomain failed,hr=%lx",hr); break; } errLocal.DbgMsgWrite(ErrE,L"SetPrimaryDomain succeeded."); } NetApiBufferFree(si101); } } while (false); // do once // Cleanup // // free the buffer allocated for the PDC computer name // if(PrimaryDC) { NetApiBufferFree(PrimaryDC); } //LocalFree(Workstation); // // free the Sid which was allocated for the TrustedDomain Sid // if(DomainSid) { FreeSid(DomainSid); } // // close the policy handle // if ( PolicyHandle ) { LsaClose(PolicyHandle); } if ( bSessionEstablished ) { EstablishSession(activeWorkstation,m_domain,m_account,m_password,FALSE); } /* if ( hDll ) { FreeLibrary(hDll); hDll = NULL; } */ if ( FAILED(hr) ) { (*errReturn) = NULL; } return hr; } STDMETHODIMP CChangeDomain::ChangeToWorkgroup( BSTR Computer, // in - name of computer to update BSTR Workgroup, // in - name of workgroup to join BSTR * errReturn // out- text describing error if failure ) { HRESULT hr = S_OK; LSA_HANDLE PolicyHandle; LPWSTR Workstation; // target machine of policy update LPWSTR TrustedDomainName; // domain to join // LPWSTR PrimaryDC=NULL; // name of that domain's PDC PSERVER_INFO_101 si101; DWORD Type; NET_API_STATUS nas; NTSTATUS Status; WCHAR errMsg[1000]; // BOOL bSessionEstablished = FALSE; // initialize output parameters (*errReturn) = NULL; Workstation = (WCHAR*)Computer; TrustedDomainName = (WCHAR*)Workgroup; errLocal.DbgMsgWrite(0,L"Changing to workgroup..."); // // insure the target machine is NOT a DC, as this operation is // only appropriate against a workstation. // do // once { /*if ( m_account.length() ) { // Establish our credentials to the target machine if (! EstablishSession(Workstation,m_domain,m_account,m_password, TRUE) ) { // DisplayError(errMsg,"EstablishSession",GetLastError(),NULL); hr = GetLastError(); } else { bSessionEstablished = TRUE; } } */ nas = NetServerGetInfo(Workstation, 101, (LPBYTE *)&si101); if(nas != NERR_Success) { //DisplayError(errMsg, "NetServerGetInfo", nas,NULL); hr = E_FAIL; break; } Type = si101->sv101_type; NetApiBufferFree(si101); if( (Type & SV_TYPE_DOMAIN_CTRL) || (Type & SV_TYPE_DOMAIN_BAKCTRL) ) { swprintf(errMsg,L"Operation is not valid on a domain controller.\n"); hr = E_FAIL; break; } // // do not allow a workstation to trust itself // if(lstrcmpiW(TrustedDomainName, Workstation) == 0) { swprintf(errMsg,L"Error: Domain %ls cannot be a member of itself.\n", TrustedDomainName); hr = E_FAIL; break; } if( lstrlenW(TrustedDomainName ) > MAX_COMPUTERNAME_LENGTH ) { TrustedDomainName[MAX_COMPUTERNAME_LENGTH] = L'\0'; // truncate } // // open the policy on this computer // Status = OpenPolicy( Workstation, DELETE | // to remove a trust POLICY_VIEW_LOCAL_INFORMATION | // to view trusts POLICY_CREATE_SECRET | // for password set operation POLICY_TRUST_ADMIN, // for trust creation &PolicyHandle ); if( Status != STATUS_SUCCESS ) { //DisplayNtStatus(errMsg,"OpenPolicy", Status,NULL); hr = LsaNtStatusToWinError(Status); break; } if( Status != STATUS_SUCCESS ) { hr = E_FAIL; break; } // // Update the primary domain to match the specified trusted domain // if (! m_bNoChange ) { Status = SetPrimaryDomain(PolicyHandle, NULL, TrustedDomainName); if(Status != STATUS_SUCCESS) { //DisplayNtStatus(errMsg,"SetPrimaryDomain", Status,NULL); hr = LsaNtStatusToWinError(Status); break; } } } while (false); // do once // Cleanup // // close the policy handle // LsaClose(PolicyHandle); /*if ( bSessionEstablished ) { EstablishSession(Workstation,m_domain,m_account,m_password,FALSE); } */ if ( FAILED(hr) ) { hr = S_FALSE; (*errReturn) = SysAllocString(errMsg); } return hr; } STDMETHODIMP CChangeDomain::ConnectAs( BSTR domain, // in - domain name to use for credentials BSTR user, // in - account name to use for credentials BSTR password // in - password to use for credentials ) { m_domain = domain; m_account = user; m_password = password; m_domainAccount = domain; m_domainAccount += L"\\"; m_domainAccount += user; return S_OK; } STDMETHODIMP CChangeDomain::get_NoChange( BOOL * pVal // out- flag, whether to write changes ) { (*pVal) = m_bNoChange; return S_OK; } STDMETHODIMP CChangeDomain::put_NoChange( BOOL newVal // in - flag, whether to write changes ) { m_bNoChange = newVal; return S_OK; } // ChangeDomain worknode: Changes the domain affiliation of a workstation or server // (This operation cannot be performed on domain controllers) // // VarSet syntax: // // Input: // ChangeDomain.Computer: // ChangeDomain.TargetDomain: // ChangeDomain.DomainIsWorkgroup: default is No // ChangeDomain.ConnectAs.Domain: optional credentials to use // ChangeDomain.ConnectAs.User : // ChangeDomain.ConnectAs.Password : // // Output: // ChangeDomain.ErrorText : // This function is not currently used by the domain migration tool. // it is here to provide an alternate API for scripting clients STDMETHODIMP CChangeDomain::Process( IUnknown * pWorkItem ) { HRESULT hr = S_OK; IVarSetPtr pVarSet = pWorkItem; _bstr_t computer; _bstr_t domain; _bstr_t text; BOOL bWorkgroup = FALSE; BSTR errText = NULL; computer = pVarSet->get(L"ChangeDomain.Computer"); domain = pVarSet->get(L"ChangeDomain.TargetDomain"); text = pVarSet->get(L"ChangeDomain.DomainIsWorkgroup"); if ( ! UStrICmp(text,GET_STRING(IDS_YES)) ) { bWorkgroup = TRUE; } _bstr_t userdomain = pVarSet->get(L"ChangeDomain.ConnectAs.Domain"); _bstr_t username = pVarSet->get(L"ChangeDomain.ConnectAs.User"); _bstr_t password = pVarSet->get(L"ChangeDomain.ConnectAs.Password"); if ( username.length() ) { ConnectAs(userdomain,username,password); } if ( bWorkgroup ) { hr = ChangeToWorkgroup(computer,domain,&errText); } else { hr = ChangeToDomain(computer,domain,NULL,&errText); } if ( text.length() ) { pVarSet->put(L"ChangeDomain.ErrorText",errText); } return hr; }