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.
 
 
 
 
 
 

1517 lines
46 KiB

// ServMigr.cpp : Implementation of CServMigr
#include "stdafx.h"
#include "ScmMigr.h"
#include "ServMigr.h"
#include "ErrDct.hpp"
#include "ResStr.h"
#include "Common.hpp"
#include "PWGen.hpp"
#include "EaLen.hpp"
#include "TReg.hpp"
#include "TxtSid.h"
#include "ARExt_i.c"
#include "LsaUtils.h"
#include "crypt.hxx"
#include "GetDcName.h"
#include <lm.h>
#include <dsgetdc.h>
#include <ntdsapi.h>
#include <Sddl.h>
#include "folders.h"
using namespace nsFolders;
//#import "\bin\McsVarSetMin.tlb" no_namespace, named_guids
//#import "\bin\DBManager.tlb" no_namespace, named_guids
//#import "\bin\McsDctWorkerObjects.tlb" no_namespace, named_guids
#import "VarSet.tlb" no_namespace, named_guids rename("property", "aproperty")
//#import "DBMgr.tlb" no_namespace, named_guids //already #imported in ServMigr.h
#import "WorkObj.tlb" no_namespace, named_guids
TErrorDct err;
StringLoader gString;
#define BLOCK_SIZE 160
#define BUFFER_SIZE 400
#define SvcAcctStatus_NotMigratedYet 0
#define SvcAcctStatus_DoNotUpdate 1
#define SvcAcctStatus_Updated 2
#define SvcAcctStatus_UpdateFailed 4
#define SvcAcctStatus_NeverAllowUpdate 8
// these defines are for GetWellKnownSid
#define ADMINISTRATORS 1
#define SYSTEM 7
/////////////////////////////////////////////////////////////////////////////
// CServMigr
STDMETHODIMP CServMigr::ProcessUndo(/*[in]*/ IUnknown * pSource, /*[in]*/ IUnknown * pTarget, /*[in]*/ IUnknown * pMainSettings, /*[in, out]*/ IUnknown ** pPropToSet, /*[in,out]*/ EAMAccountStats* pStats)
{
return E_NOTIMPL;
}
STDMETHODIMP CServMigr::PreProcessObject(/*[in]*/ IUnknown * pSource, /*[in]*/ IUnknown * pTarget, /*[in]*/ IUnknown * pMainSettings, /*[in, out]*/ IUnknown ** pPropToSet, /*[in,out]*/ EAMAccountStats* pStats)
{
return S_OK;
}
STDMETHODIMP
CServMigr::ProcessObject(
/*[in]*/ IUnknown * pSource,
/*[in]*/ IUnknown * pTarget,
/*[in]*/ IUnknown * pMainSettings,
/*[in,out]*/IUnknown ** ppPropsToSet,
/*[in,out]*/ EAMAccountStats* pStats
)
{
HRESULT hr = S_OK;
WCHAR domAccount[500];
WCHAR domTgtAccount[500];
IVarSetPtr pVarSet(pMainSettings);
IIManageDBPtr pDB;
_bstr_t logfile;
_bstr_t srcComputer;
_bstr_t tgtComputer;
IVarSetPtr pData(CLSID_VarSet);
IUnknown * pUnk = NULL;
DWORD rc = 0;
_bstr_t sIntraForest;
BOOL bIntraForest = FALSE;
USER_INFO_2 * uInfo = NULL;
try {
logfile = pVarSet->get(GET_BSTR(DCTVS_Options_Logfile));
if ( logfile.length() )
{
err.LogOpen(logfile,1);
}
pDB = pVarSet->get(GET_BSTR(DCTVS_DBManager));
if ( pDB != NULL )
{
// Check to see if this account is referenced in the service accounts table
m_strSourceDomain = pVarSet->get(GET_BSTR(DCTVS_Options_SourceDomain));
m_strSourceDomainFlat = pVarSet->get(GET_BSTR(DCTVS_Options_SourceDomainFlat));
m_strTargetDomain = pVarSet->get(GET_BSTR(DCTVS_Options_TargetDomain));
m_strTargetDomainFlat = pVarSet->get(GET_BSTR(DCTVS_Options_TargetDomainFlat));
m_strSourceSam = pVarSet->get(GET_BSTR(DCTVS_CopiedAccount_SourceSam));
m_strTargetSam = pVarSet->get(GET_BSTR(DCTVS_CopiedAccount_TargetSam));
srcComputer = pVarSet->get(GET_BSTR(DCTVS_Options_SourceServer));
tgtComputer = pVarSet->get(GET_BSTR(DCTVS_Options_TargetServer));
sIntraForest = pVarSet->get(GET_BSTR(DCTVS_Options_IsIntraforest));
if ( ! UStrICmp((WCHAR*)sIntraForest,GET_STRING(IDS_YES)) )
{
// for intra-forest migration we are moving, not copying, so we don't need to update the password
// Actually, it turns out that ChangeServiceConfig will not let us update just the service account
// and not the passord, so we'll have to go ahead and change the password for the service ac
//bIntraForest = TRUE;
}
//if the SAM account name has a " character in it, it cannot be a service
//account, and therefore we leave
if (wcschr((WCHAR*)m_strSourceSam, L'\"')) {
return S_OK;
}
swprintf(domAccount,L"%s\\%s",(WCHAR*)m_strSourceDomainFlat,(WCHAR*)m_strSourceSam);
swprintf(domTgtAccount,L"%s\\%s",(WCHAR*)m_strTargetDomainFlat,(WCHAR*)m_strTargetSam);
}
}
catch (_com_error& ce) {
hr = ce.Error();
return hr;
}
catch (... )
{
return E_FAIL;
}
try {
hr = pData->QueryInterface(IID_IUnknown,(void**)&pUnk);
if ( SUCCEEDED(hr) )
{
hr = pDB->raw_GetServiceAccount(_bstr_t(domAccount),&pUnk);
}
}
catch (_com_error& ce) {
if (pUnk)
pUnk->Release();
hr = ce.Error();
return hr;
}
catch ( ... )
{
if (pUnk)
pUnk->Release();
return E_FAIL;
}
try {
if ( SUCCEEDED(hr) )
{
pData = pUnk;
pUnk->Release();
pUnk=NULL;
// remove the password must change flag, if set
DWORD parmErr = 0;
WCHAR password[LEN_Password];
long entries = pData->get("ServiceAccountEntries");
if ( (entries != 0) && !bIntraForest ) // if we're moving the account, don't mess with its properties
{
//
// Open password log file if it has not been already opened.
//
if (!m_bTriedToOpenFile)
{
m_bTriedToOpenFile = true;
_bstr_t strPasswordFile = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_PasswordFile));
if (m_passwordLog.LogOpen(strPasswordFile) == FALSE)
{
err.MsgWrite(ErrI, DCT_MSG_SERVICES_WILL_NOT_BE_UPDATED);
if (pStats != NULL)
{
pStats->errors.users++;
}
}
}
rc = NetUserGetInfo(tgtComputer,m_strTargetSam,2,(LPBYTE*)&uInfo);
if ( ! rc )
{
// generate a new, strong, 14 character password for this account,
// and set the password to not expire
rc = EaPasswordGenerate(3,3,3,3,6,14,password,DIM(password));
if (!rc)
{
//
// Only set password to not expire if able
// to write password to password file.
//
if (m_passwordLog.IsOpen())
{
uInfo->usri2_flags |= (UF_DONT_EXPIRE_PASSWD);
}
uInfo->usri2_password = password;
rc = NetUserSetInfo(tgtComputer,m_strTargetSam,2,(LPBYTE)uInfo,&parmErr);
if ( ! rc )
{
if (m_passwordLog.IsOpen())
{
err.MsgWrite(0,DCT_MSG_REMOVED_PWDCHANGE_FLAG_S,(WCHAR*)m_strTargetSam);
}
err.MsgWrite(0,DCT_MSG_PWGENERATED_S,(WCHAR*)m_strTargetSam);
// write the password to the password log file and mark this account, so that the
// SetPassword extension will not reset the password again.
pVarSet->put(GET_BSTR(DCTVS_CopiedAccount_DoNotUpdatePassword),m_strSourceSam);
//
// If password log is open then write password to file
// otherwise set error code so that services are not updated.
//
if (m_passwordLog.IsOpen())
{
m_passwordLog.MsgWrite(L"%ls,%ls",(WCHAR*)m_strTargetSam,password);
}
else
{
rc = ERROR_OPEN_FAILED;
}
}
else
{
if (pStats != NULL)
pStats->errors.users++;
err.SysMsgWrite(ErrE,rc,DCT_MSG_REMOVED_PWDCHANGE_FLAG_FAILED_SD,(WCHAR*)m_strTargetSam,rc);
}
uInfo->usri2_password = NULL;
}
NetApiBufferFree(uInfo);
uInfo = NULL;
}
}
if (entries != 0 )
{
try {
if ( ! rc )
{
WCHAR strSID[200] = L"";
BYTE sid[200];
WCHAR sdomain[LEN_Domain];
SID_NAME_USE snu;
DWORD lenSid = DIM(sid);
DWORD lenDomain = DIM(sdomain);
DWORD lenStrSid = DIM(strSID);
if ( LookupAccountName(tgtComputer,m_strTargetSam,sid,&lenSid,sdomain,&lenDomain,&snu) )
{
if ( GetTextualSid(sid,strSID,&lenStrSid) )
{
// for each reference to the service account, update the SCM
// for intra-forest migration, don't update the password
if ( bIntraForest )
UpdateSCMs(pData,domTgtAccount,NULL,strSID,pDB, pStats);
else
UpdateSCMs(pData,domTgtAccount,password,strSID,pDB, pStats);
}
else
{
if (pStats != NULL)
pStats->errors.users++;
err.SysMsgWrite(ErrE,GetLastError(),DCT_MSG_CANNOT_FIND_ACCOUNT_SSD,m_strTargetSam,tgtComputer,GetLastError());
}
}
else
{
if (pStats != NULL)
pStats->errors.users++;
err.SysMsgWrite(ErrE,GetLastError(),DCT_MSG_CANNOT_FIND_ACCOUNT_SSD,m_strTargetSam,tgtComputer,GetLastError());
}
}
}
catch (_com_error& ce) {
hr = ce.Error();
return hr;
}
catch(...)
{
return E_FAIL;
}
}
}
else
{
if (pStats != NULL)
pStats->errors.users++;
err.SysMsgWrite(ErrE,E_FAIL,DCT_MSG_DB_OBJECT_CREATE_FAILED_D,E_FAIL);
}
err.LogClose();
}
catch (_com_error& ce) {
if (pUnk)
pUnk->Release();
if (uInfo)
NetApiBufferFree(uInfo);
hr = ce.Error();
return hr;
}
catch (... )
{
if (pUnk)
pUnk->Release();
if (uInfo)
NetApiBufferFree(uInfo);
return E_FAIL;
}
return S_OK;
}
STDMETHODIMP CServMigr::get_sDesc(/*[out, retval]*/ BSTR *pVal)
{
(*pVal) = SysAllocString(L"Updates SCM entries for services using migrated accounts.");
return S_OK;
}
STDMETHODIMP CServMigr::put_sDesc(/*[in]*/ BSTR newVal)
{
return E_NOTIMPL;
}
STDMETHODIMP CServMigr::get_sName(/*[out, retval]*/ BSTR *pVal)
{
(*pVal) = SysAllocString(L"Generic Service Account Migration");
return S_OK;
}
STDMETHODIMP CServMigr::put_sName(/*[in]*/ BSTR newVal)
{
return E_NOTIMPL;
}
DWORD
CServMigr::DoUpdate(
WCHAR const * account,
WCHAR const * password,
WCHAR const * strSid,
WCHAR const * computer,
WCHAR const * service,
BOOL bNeedToGrantLOS,
EAMAccountStats *pStats
)
{
DWORD rc = 0;
WCHAR const * ppassword = password;
// if password is empty, set it to NULL
if ( ppassword && ppassword[0] == 0 )
{
ppassword = NULL;
}
else if ( !UStrCmp(password,L"NULL") )
{
ppassword = NULL;
}
// only try to update entries that we need to be updating
// try to connect to the SCM on this machine
SC_HANDLE pScm = OpenSCManager(computer, NULL, SC_MANAGER_ALL_ACCESS );
if ( pScm )
{
// grant the logon as a service right to the target account
if ( bNeedToGrantLOS )
{
LSA_HANDLE hPolicy;
NTSTATUS ntsStatus = OpenPolicy(
const_cast<LPWSTR>(computer),
POLICY_CREATE_ACCOUNT|POLICY_LOOKUP_NAMES,
&hPolicy
);
rc = LsaNtStatusToWinError(ntsStatus);
if (rc == ERROR_SUCCESS)
{
LSA_UNICODE_STRING lsausUserRights;
InitLsaString(&lsausUserRights, _T("SeServiceLogonRight"));
PSID pSid = SidFromString(strSid);
if (pSid)
{
ntsStatus = LsaAddAccountRights(hPolicy, pSid, &lsausUserRights, 1L);
rc = LsaNtStatusToWinError(ntsStatus);
FreeSid(pSid);
}
else
{
rc = ERROR_OUTOFMEMORY;
}
LsaClose(hPolicy);
}
if ( rc )
{
if (pStats != NULL)
pStats->errors.users++;
err.SysMsgWrite(ErrE,rc,DCT_MSG_LOS_GRANT_FAILED_SSD,
account,(WCHAR*)computer,rc);
}
else
{
err.MsgWrite(0,DCT_MSG_LOS_GRANTED_SS,
account,(WCHAR*)computer);
}
}
SC_HANDLE pService = OpenService(pScm,service,SERVICE_ALL_ACCESS);
if ( pService )
{
int nCnt = 0;
/* make sure the same user still starts this service */
//get the source account names
BOOL bSameAccount = TRUE;
_bstr_t sSrcDom, sSrcSAM, sSrcUPN;
_bstr_t sSrcAccount = L"";
_bstr_t sSrcAccountUPN = L"";
//if not given src names (not migrating right now), get them
if ((!m_strSourceDomainFlat) && (!m_strSourceSam))
{
//if got names, get UPN name also
if (RetrieveOriginalAccount(sSrcDom, sSrcSAM))
{
sSrcUPN = GetUPNName(sSrcSAM);
sSrcAccount = sSrcDom + _bstr_t(L"\\") + sSrcSAM;
}
}
else //els if given src names (migrate this object now), use those names
{
sSrcDom = m_strSourceDomainFlat;
sSrcSAM = m_strSourceSam;
sSrcUPN = GetUPNName(sSrcSAM);
sSrcAccount = sSrcDom + _bstr_t(L"\\") + sSrcSAM;
}
//if got names to check, check them
if ((sSrcAccount.length()) || (sSrcUPN.length()))
{
BYTE buf[3000];
QUERY_SERVICE_CONFIG * pConfig = (QUERY_SERVICE_CONFIG *)buf;
DWORD lenNeeded = 0;
// get the information about this service
if (QueryServiceConfig(pService, pConfig, sizeof buf, &lenNeeded))
{
//if not the same account, check UPN name or set to FALSE
if ((sSrcAccount.length()) && (UStrICmp(pConfig->lpServiceStartName,sSrcAccount)))
{
//if UPN name, try it
if (sSrcUPN.length())
{
//if not match either, set flag to FALSE;
if (UStrICmp(pConfig->lpServiceStartName,sSrcUPN))
bSameAccount = FALSE;
}
else //else, not a match
bSameAccount = FALSE;
}
}
}//if got names
//if same account, update the SCM
if (bSameAccount)
{
// update the account and password for the service
while ( !ChangeServiceConfig(pService,
SERVICE_NO_CHANGE, // dwServiceType
SERVICE_NO_CHANGE, // dwStartType
SERVICE_NO_CHANGE, // dwErrorControl
NULL, // lpBinaryPathName
NULL, // lpLoadOrderGroup
NULL, // lpdwTagId
NULL, // lpDependencies
account, // lpServiceStartName
ppassword, // lpPassword
NULL) && nCnt < 5) // lpDisplayName
{
nCnt++;
Sleep(500);
}
if ( nCnt < 5 )
{
err.MsgWrite(0,DCT_MSG_UPDATED_SCM_ENTRY_SS,(WCHAR*)computer,(WCHAR*)service);
}
else
{
rc = GetLastError();
}
}//end if still same account
else //else if not same user, put message in log and return error
{
err.MsgWrite(0,DCT_MSG_UPDATE_SCM_ENTRY_UNMATCHED_SSD,(WCHAR*)computer,(WCHAR*)service,(WCHAR*)sSrcAccount);
rc = DCT_MSG_UPDATE_SCM_ENTRY_UNMATCHED_SSD;
}
CloseServiceHandle(pService);
}
CloseServiceHandle(pScm);
}
else
{
rc = GetLastError();
}
return rc;
}
BOOL
CServMigr::UpdateSCMs(
IUnknown * pVS,
WCHAR const * account,
WCHAR const * password,
WCHAR const * strSid,
IIManageDB * pDB,
EAMAccountStats * pStats
)
{
BOOL bGotThemAll = TRUE;
IVarSetPtr pVarSet = pVS;
LONG nCount = 0;
WCHAR key[LEN_Path];
_bstr_t computer;
_bstr_t service;
long status;
DWORD rc = 0;
BOOL bFirst = TRUE;
WCHAR prevComputer[LEN_Path] = L"";
try {
nCount = pVarSet->get("ServiceAccountEntries");
for ( long i = 0 ; i < nCount ; i++ )
{
swprintf(key,L"Computer.%ld",i);
computer = pVarSet->get(key);
swprintf(key,L"Service.%ld",i);
service = pVarSet->get(key);
swprintf(key,L"ServiceAccountStatus.%ld",i);
status = pVarSet->get(key);
if ( status == SvcAcctStatus_NotMigratedYet || status == SvcAcctStatus_UpdateFailed )
{
if ( UStrICmp(prevComputer,(WCHAR*)computer) )
{
bFirst = TRUE; // reset the 'first' flag when the computer changes
}
try {
rc = DoUpdate(account,password,strSid,computer,service,bFirst/*only grant SeServiceLogonRight once per account*/,
pStats);
bFirst = FALSE;
safecopy(prevComputer,(WCHAR*)computer);
}
catch (...)
{
// Do we need to trigger the counter increment here?
// if (pStats != NULL)
// pStats->errors.users++;
err.DbgMsgWrite(ErrE,L"Exception!");
err.DbgMsgWrite(0,L"Updating %ls on %ls",(WCHAR*)service,(WCHAR*)computer);
err.DbgMsgWrite(0,L"Account=%ls, SID=%ls",(WCHAR*)account,(WCHAR*)strSid);
rc = E_FAIL;
}
if (! rc )
{
// the update was successful
pDB->raw_SetServiceAcctEntryStatus(computer,service,_bstr_t(account),SvcAcctStatus_Updated);
}
else
{
// couldn't connect to this one -- we will need to save this account's password
// in our encrypted storage
pDB->raw_SetServiceAcctEntryStatus(computer,service,NULL,SvcAcctStatus_UpdateFailed);
bGotThemAll = FALSE;
SaveEncryptedPassword(computer,service,account,password);
//if the current service account didn't match, we need not log an error
if (rc != DCT_MSG_UPDATE_SCM_ENTRY_UNMATCHED_SSD)
{
err.SysMsgWrite(ErrE,rc,DCT_MSG_UPDATE_SCM_ENTRY_FAILED_SSD,(WCHAR*)computer,(WCHAR*)service,rc);
pStats->errors.users++;
}
}
}
//else if skipping, still log in file so we can update later
else if (status == SvcAcctStatus_DoNotUpdate)
SaveEncryptedPassword(computer,service,account,password);
}
}
catch ( ... )
{
// Do we need to trigger the counter increment here?
// if (pStats != NULL)
// pStats->errors.users++;
err.DbgMsgWrite(ErrE,L"Exception!");
}
return bGotThemAll;
}
HRESULT
CServMigr::SaveEncryptedPassword(
WCHAR const * server,
WCHAR const * service,
WCHAR const * account,
WCHAR const * password
)
{
HRESULT hr = S_OK;
TNodeListEnum e;
TEntryNode* pNode;
// if entry exists...
for (pNode = (TEntryNode*)e.OpenFirst(&m_List); pNode; pNode = (TEntryNode*)e.Next())
{
if (_wcsicmp(pNode->GetComputer(), server) == 0)
{
if (_wcsicmp(pNode->GetService(), service) == 0)
{
if (_wcsicmp(pNode->GetAccount(), account) == 0)
{
// update password
try {
pNode->SetPassword(password);
}
catch (_com_error& ce) {
hr = ce.Error();
return hr;
}
break;
}
}
}
}
// else...
if (pNode == NULL)
{
// insert new entry
try {
pNode = new TEntryNode(server, service, account, password);
}
catch (_com_error& ce) {
hr = ce.Error();
return hr;
}
if (pNode)
{
m_List.InsertBottom(pNode);
}
else
{
hr = E_OUTOFMEMORY;
}
}
return hr;
}
//////////////////////////////////////////////////////////////////////////////////////
///
/// TEntryList implementation of secure storage for service account passwords
///
///
//////////////////////////////////////////////////////////////////////////////////////
DWORD
TEntryList::LoadFromFile(WCHAR const * filename)
{
DWORD rc = 0;
FILE * hSource = NULL;
HCRYPTPROV hProv = 0;
HCRYPTKEY hKey = 0;
BYTE pbBuffer[BLOCK_SIZE];
WCHAR strData[BLOCK_SIZE * 5] = { 0 };
DWORD dwCount;
int eof = 0;
WCHAR fullpath[LEN_Path];
BYTE *pbKeyBlob = NULL;
DWORD dwBlobLen;
// Get our install directory from the registry, and then append the filename
HKEY hRegKey;
DWORD type;
DWORD lenValue = (sizeof fullpath);
rc = RegOpenKey(HKEY_LOCAL_MACHINE,REGKEY_ADMT,&hRegKey);
if ( ! rc )
{
rc = RegQueryValueEx(hRegKey,L"Directory",0,&type,(LPBYTE)fullpath,&lenValue);
if (! rc )
{
UStrCpy(fullpath+UStrLen(fullpath),filename);
}
RegCloseKey(hRegKey);
}
if (rc != ERROR_SUCCESS)
{
goto done;
}
// Open the source file.
if((hSource = _wfopen(fullpath,L"rb"))==NULL)
{
rc = GetLastError();
goto done;
}
// acquire handle to key container which must exist
if ((hProv = AcquireContext(true)) == 0)
{
rc = GetLastError();
goto done;
}
// Read the key blob length from the source file and allocate it to memory.
fread(&dwBlobLen, sizeof(DWORD), 1, hSource);
if(ferror(hSource) || feof(hSource))
{
rc = GetLastError();
goto done;
}
if((pbKeyBlob = (BYTE*)malloc(dwBlobLen)) == NULL)
{
rc = GetLastError();
goto done;
}
// Read the key blob from the source file.
fread(pbKeyBlob, 1, dwBlobLen, hSource);
if(ferror(hSource) || feof(hSource))
{
rc = GetLastError();
goto done;
}
// Import the key blob into the CSP.
if(!CryptImportKey(hProv, pbKeyBlob, dwBlobLen, 0, 0, &hKey))
{
rc = GetLastError();
goto done;
}
// Decrypt the source file and load the list
do {
// Read up to BLOCK_SIZE bytes from source file.
dwCount = fread(pbBuffer, 1, BLOCK_SIZE, hSource);
if(ferror(hSource))
{
rc = GetLastError();
goto done;
}
eof=feof(hSource);
// Decrypt the data.
if(!CryptDecrypt(hKey, 0, eof, 0, pbBuffer, &dwCount))
{
rc = GetLastError();
goto done;
}
// Read any complete entries from the buffer
// first, add the buffer contents to any leftover information we had read from before
WCHAR * curr = strData;
long len = UStrLen(strData);
WCHAR * nl = NULL;
WCHAR computer[LEN_Computer];
WCHAR service[LEN_Service];
WCHAR account[LEN_Account];
WCHAR password[LEN_Password];
wcsncpy(strData + len,(WCHAR*)pbBuffer, dwCount / sizeof(WCHAR));
strData[len + (dwCount / sizeof(WCHAR))] = 0;
do {
nl = wcschr(curr,L'\n');
if ( nl )
{
*nl = 0;
if ( swscanf(curr,L" %[^\t]\t%[^\t]\t%[^\t]\t%[^\t]\n",computer,service,account,password) )
{
TEntryNode * pNode = NULL;
try {
pNode = new TEntryNode(computer,service,account,password);
}
catch (_com_error& ce) {
rc = ERROR_NOT_ENOUGH_MEMORY;
goto done;
}
InsertBottom(pNode);
}
else
{
rc = E_FAIL;
break;
}
// go on to the next entry
curr = nl + 1;
}
} while ( nl );
// there may be a partial record left in the buffer
// if so, save it for the next read
if ( (*curr) != 0 )
{
memmove(strData,curr,( 1 + UStrLen(curr) ) * (sizeof WCHAR));
}
else
{
strData[0] = L'\0';
}
} while(!feof(hSource));
done:
// Clean up
if(pbKeyBlob)
free(pbKeyBlob);
if(hKey != 0)
CryptDestroyKey(hKey);
if(hProv != 0)
CryptReleaseContext(hProv, 0);
if(hSource != NULL)
fclose(hSource);
return rc;
}
DWORD
TEntryList::SaveToFile(WCHAR const * filename)
{
DWORD rc = 0;
BYTE pbBuffer[BUFFER_SIZE];
DWORD dwCount;
HANDLE hDest = INVALID_HANDLE_VALUE;
BYTE * pbKeyBlob = NULL;
DWORD dwBlobLen;
HCRYPTPROV hProv = 0;
HCRYPTKEY hKey = 0;
HCRYPTKEY hXchgKey = 0;
TEntryNode * pNode;
TNodeListEnum e;
WCHAR fullpath[LEN_Path];
DWORD dwBlockSize;
DWORD cbBlockSize = sizeof(dwBlockSize);
DWORD dwPaddedCount;
DWORD cbWritten;
// Open the destination file.
HKEY hRegKey;
DWORD type;
DWORD lenValue = (sizeof fullpath);
rc = RegOpenKey(HKEY_LOCAL_MACHINE,REGKEY_ADMT,&hRegKey);
if ( ! rc )
{
rc = RegQueryValueEx(hRegKey,L"Directory",0,&type,(LPBYTE)fullpath,&lenValue);
if (! rc )
{
UStrCpy(fullpath+UStrLen(fullpath),filename);
}
RegCloseKey(hRegKey);
}
if (rc != ERROR_SUCCESS)
{
goto done;
}
//
// Delete previous data file if it exists. This obviates the need to change the
// security descriptor on the file as CreateFile does not apply the security
// descriptor if the file is opened but only when created.
//
if (!DeleteFile(fullpath))
{
rc = GetLastError();
if (rc == ERROR_FILE_NOT_FOUND)
{
rc = ERROR_SUCCESS;
}
else
{
goto done;
}
}
//
// Create security descriptor with administrators as owner and permissions on file
// so that only administrators and system have full access to the file.
//
PSECURITY_DESCRIPTOR psd = NULL;
BOOL bConvert = ConvertStringSecurityDescriptorToSecurityDescriptor(
_T("O:BAD:P(A;NP;FA;;;BA)(A;NP;FA;;;SY)"),
SDDL_REVISION_1,
&psd,
NULL
);
if (!bConvert)
{
rc = GetLastError();
goto done;
}
//
// Create file.
//
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = psd;
sa.bInheritHandle = FALSE;
hDest = CreateFile(
fullpath,
GENERIC_WRITE,
0,
&sa,
CREATE_NEW,
FILE_ATTRIBUTE_NORMAL,
NULL
);
rc = GetLastError();
if (psd)
{
LocalFree(psd);
}
if (hDest == INVALID_HANDLE_VALUE)
{
goto done;
}
// acquire handle to key container
if ((hProv = AcquireContext(false)) == 0)
{
rc = GetLastError();
goto done;
}
// Attempt to get handle to exchange key.
if(!CryptGetUserKey(hProv,AT_KEYEXCHANGE,&hKey))
{
if(GetLastError()==NTE_NO_KEY)
{
// Create key exchange key pair.
if(!CryptGenKey(hProv,AT_KEYEXCHANGE,0,&hKey))
{
rc = GetLastError();
goto done;
}
}
else
{
rc = GetLastError();
goto done;
}
}
CryptDestroyKey(hKey);
CryptReleaseContext(hProv,0);
// acquire handle to key container
if ((hProv = AcquireContext(false)) == 0)
{
rc = GetLastError();
goto done;
}
// Get a handle to key exchange key.
if(!CryptGetUserKey(hProv, AT_KEYEXCHANGE, &hXchgKey))
{
rc = GetLastError();
goto done;
}
// Create a random block cipher session key.
if(!CryptGenKey(hProv, CALG_RC2, CRYPT_EXPORTABLE, &hKey))
{
rc = GetLastError();
goto done;
}
// Determine the size of the key blob and allocate memory.
if(!CryptExportKey(hKey, hXchgKey, SIMPLEBLOB, 0, NULL, &dwBlobLen))
{
rc = GetLastError();
goto done;
}
if((pbKeyBlob = (BYTE*)malloc(dwBlobLen)) == NULL)
{
rc = ERROR_NOT_ENOUGH_MEMORY;
goto done;
}
// Export the key into a simple key blob.
if(!CryptExportKey(hKey, hXchgKey, SIMPLEBLOB, 0, pbKeyBlob,
&dwBlobLen))
{
rc = GetLastError();
free(pbKeyBlob);
goto done;
}
// Write the size of the key blob to the destination file.
if (!WriteFile(hDest, &dwBlobLen, sizeof(DWORD), &cbWritten, NULL))
{
rc = GetLastError();
free(pbKeyBlob);
goto done;
}
// Write the key blob to the destination file.
if(!WriteFile(hDest, pbKeyBlob, dwBlobLen, &cbWritten, NULL))
{
rc = GetLastError();
free(pbKeyBlob);
goto done;
}
// Free memory.
free(pbKeyBlob);
// get key cipher's block length in bytes
if (CryptGetKeyParam(hKey, KP_BLOCKLEN, (BYTE*)&dwBlockSize, &cbBlockSize, 0))
{
dwBlockSize /= 8;
}
else
{
rc = GetLastError();
goto done;
}
// Encrypt the item list and write it to the destination file.
for ( pNode = (TEntryNode*)e.OpenFirst(this); pNode ; pNode = (TEntryNode *)e.Next() )
{
// copy an item into the buffer in the following format:
// Computer\tService\tAccount\tPassword
int cchWritten;
const size_t BUFFER_SIZE_IN_WCHARS = sizeof(pbBuffer) / sizeof(wchar_t);
wchar_t* pchLast = &(((wchar_t*)pbBuffer)[BUFFER_SIZE_IN_WCHARS - 1]);
*pchLast = L'\0';
const WCHAR * pszPwd = NULL;
try {
pszPwd = pNode->GetPassword();
}
catch (_com_error& ce) {
rc = ERROR_DECRYPTION_FAILED;
goto done;
}
if ( pszPwd && *pszPwd )
{
cchWritten = _snwprintf(
(wchar_t*)pbBuffer,
BUFFER_SIZE_IN_WCHARS,
L"%s\t%s\t%s\t%s\n",
pNode->GetComputer(),
pNode->GetService(),
pNode->GetAccount(),
pszPwd
);
}
else
{
cchWritten = _snwprintf(
(wchar_t*)pbBuffer,
BUFFER_SIZE_IN_WCHARS,
L"%s\t%s\t%s\t%s\n",
pNode->GetComputer(),
pNode->GetService(),
pNode->GetAccount(),
L"NULL"
);
}
pNode->ReleasePassword();
pszPwd = NULL;
if ((cchWritten < 0) || (*pchLast != L'\0'))
{
rc = ERROR_INSUFFICIENT_BUFFER;
goto done;
}
dwCount = UStrLen((WCHAR*)pbBuffer) * (sizeof WCHAR) ;
// the buffer must be a multiple of the key cipher's block length
// NOTE: this algorithm assumes block length is multiple of sizeof(WCHAR)
if (dwBlockSize > 0)
{
// calculate next multiple greater than count
dwPaddedCount = ((dwCount + dwBlockSize - 1) / dwBlockSize) * dwBlockSize;
// pad buffer with space characters
WCHAR* pch = (WCHAR*)(pbBuffer + dwCount);
for (; dwCount < dwPaddedCount; dwCount += sizeof(WCHAR))
{
*pch++ = L' ';
}
}
// Encrypt the data.
if(!CryptEncrypt(hKey, 0, (pNode->Next() == NULL) , 0, pbBuffer, &dwCount,
BUFFER_SIZE))
{
rc = GetLastError();
goto done;
}
// Write the data to the destination file.
if(!WriteFile(hDest, pbBuffer, dwCount, &cbWritten, NULL))
{
rc = GetLastError();
goto done;
}
}
done:
// Destroy the session key.
if(hKey != 0) CryptDestroyKey(hKey);
// Destroy the key exchange key.
if(hXchgKey != 0) CryptDestroyKey(hXchgKey);
// Release the provider handle.
if(hProv != 0) CryptReleaseContext(hProv, 0);
// Close destination file.
if(hDest != INVALID_HANDLE_VALUE) CloseHandle(hDest);
return rc;
}
// AcquireContext Method
//
// acquire handle to key container within cryptographic service provider (CSP)
//
HCRYPTPROV TEntryList::AcquireContext(bool bContainerMustExist)
{
HCRYPTPROV hProv = 0;
#define KEY_CONTAINER_NAME _T("A69904BC349C4CFEAAEAB038BAB8C3B1")
if (bContainerMustExist)
{
// first try Microsoft Enhanced Cryptographic Provider
if (!CryptAcquireContext(&hProv, KEY_CONTAINER_NAME, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET))
{
if (GetLastError() == NTE_KEYSET_NOT_DEF)
{
// then try Microsoft Base Cryptographic Provider
CryptAcquireContext(&hProv, KEY_CONTAINER_NAME, MS_DEF_PROV, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET);
}
}
}
else
{
// first try Microsoft Enhanced Cryptographic Provider
if (!CryptAcquireContext(&hProv, KEY_CONTAINER_NAME, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET))
{
DWORD dwError = GetLastError();
if ((dwError == NTE_BAD_KEYSET) || (dwError == NTE_KEYSET_NOT_DEF))
{
// then try creating key container in enhanced provider
if (!CryptAcquireContext(&hProv, KEY_CONTAINER_NAME, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET|CRYPT_NEWKEYSET))
{
dwError = GetLastError();
if (dwError == NTE_KEYSET_NOT_DEF)
{
// then try Microsoft Base Cryptographic Provider
if (!CryptAcquireContext(&hProv, KEY_CONTAINER_NAME, MS_DEF_PROV, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET))
{
dwError = GetLastError();
if ((dwError == NTE_BAD_KEYSET) || (dwError == NTE_KEYSET_NOT_DEF))
{
// finally try creating key container in base provider
CryptAcquireContext(&hProv, KEY_CONTAINER_NAME, MS_DEF_PROV, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET|CRYPT_NEWKEYSET);
}
}
}
}
}
}
}
return hProv;
}
STDMETHODIMP CServMigr::TryUpdateSam(BSTR computer,BSTR service,BSTR account)
{
HRESULT hr = S_OK;
// Find the entry in the list, and perform the update
TNodeListEnum e;
TEntryNode * pNode;
BOOL bFound = FALSE;
for ( pNode = (TEntryNode*)e.OpenFirst(&m_List) ; pNode ; pNode = (TEntryNode*)e.Next() )
{
if ( !UStrICmp(computer,pNode->GetComputer())
&& !UStrICmp(service,pNode->GetService())
&& !UStrICmp(account,pNode->GetAccount())
)
{
// found it!
bFound = TRUE;
const WCHAR * pszPwd = NULL;
try {
pszPwd = pNode->GetPassword();
}
catch (_com_error& ce) {
hr = ce.Error();
break;
}
BSTR bstrPwd = SysAllocString(pszPwd);
if ((bstrPwd == NULL) && pszPwd && pszPwd[0])
{
hr = E_OUTOFMEMORY;
pNode->ReleasePassword();
break;
}
hr = TryUpdateSamWithPassword(computer,service,account,bstrPwd );
pNode->ReleasePassword();
SecureZeroMemory(bstrPwd, wcslen(bstrPwd)*sizeof(WCHAR));
SysFreeString(bstrPwd);
break;
}
}
if ( ! bFound )
{
hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
}
return hr;
}
STDMETHODIMP CServMigr::TryUpdateSamWithPassword(BSTR computer,BSTR service,BSTR domAccount,BSTR password)
{
DWORD rc = 0;
WCHAR domain[LEN_Domain];
_bstr_t dc;
WCHAR account[LEN_Account];
WCHAR domStr[LEN_Domain];
BYTE sid[100];
WCHAR strSid[200];
WCHAR * pSlash = wcschr(domAccount,L'\\');
SID_NAME_USE snu;
DWORD lenSid = DIM(sid);
DWORD lenDomStr = DIM(domStr);
DWORD lenStrSid = DIM(strSid);
// split out the domain and account names
if ( pSlash )
{
// UStrCpy(domain,domAccount,pSlash - domAccount + 1);
UStrCpy(domain,domAccount,(int)(pSlash - domAccount + 1));
UStrCpy(account,pSlash+1);
GetAnyDcName5(domain, dc);
// get the SID for the target account
if ( LookupAccountName(dc,account,sid,&lenSid,domStr,&lenDomStr,&snu) )
{
GetTextualSid(sid,strSid,&lenStrSid);
rc = DoUpdate(domAccount,password,strSid,computer,service,TRUE, NULL);
}
else
{
rc = GetLastError();
}
}
else
{
rc = ERROR_NOT_FOUND;
}
return HRESULT_FROM_WIN32(rc);
}
BOOL // ret - TRUE if directory found
CServMigr::GetDirectory(
WCHAR * filename // out - string buffer to store directory name
)
{
DWORD rc = 0;
BOOL bFound = FALSE;
TRegKey key;
rc = key.OpenRead(GET_STRING(IDS_HKLM_DomainAdmin_Key),HKEY_LOCAL_MACHINE);
if ( ! rc )
{
rc = key.ValueGetStr(L"Directory",filename,MAX_PATH);
if ( ! rc )
{
if ( *filename )
bFound = TRUE;
}
}
key.Close();
return bFound;
}
/*********************************************************************
* *
* Written by: Paul Thompson *
* Date: 28 MAY 2001 *
* *
* This function is responsible for retrieving the original *
* service account of the given migrated service account. We use the*
* target service account name and domain to lookup its source *
* account name and domain from the Migrated Objects table. *
* This function returns TRUE or FALSE and if TRUE, fills in the *
* given BSTRs for soure domain and source SAM name. *
* *
*********************************************************************/
//BEGIN RetrieveOriginalAccount
BOOL CServMigr::RetrieveOriginalAccount(_bstr_t &sSrcDom, _bstr_t &sSrcSAM)
{
/* local constants */
const long ONLY_ONE_MATCHED = 1;
/* local variables */
WCHAR sTemp[MAX_PATH];
BOOL bSuccess = FALSE;
IUnknown * pUnk = NULL;
/* function body */
try
{
IVarSetPtr pVSMig(__uuidof(VarSet));
IIManageDBPtr pDb(__uuidof(IManageDB));
//see if any target account fitting this SAM name and domain have been migrated
pVSMig->QueryInterface(IID_IUnknown, (void**) &pUnk);
HRESULT hrFind = pDb->raw_GetMigratedObjectsByTarget(m_strTargetDomain, m_strTargetSam, &pUnk);
pUnk->Release();
pUnk = NULL;
//if migrated only one account to this name, then fill the return strings
if (hrFind == S_OK)
{
//get objects number matching this description
long nMatched = pVSMig->get(L"MigratedObjects");
//if only one found, fill output strings
if (nMatched == ONLY_ONE_MATCHED)
{
swprintf(sTemp,L"MigratedObjects.0.%s",GET_STRING(DB_SourceDomain));
sSrcDom = pVSMig->get(sTemp);
swprintf(sTemp,L"MigratedObjects.0.%s",GET_STRING(DB_SourceSamName));
sSrcSAM = pVSMig->get(sTemp);
bSuccess = TRUE; //set success flag
}//end if found only one
}//end if found at least one
}
catch ( ... )
{
if (pUnk)
pUnk->Release();
bSuccess = false;
}
return bSuccess;
}
//END RetrieveOriginalAccount
/*********************************************************************
* *
* Written by: Paul Thompson *
* Date: 28 MAY 2001 *
* *
* This function is responsible for retrieving the UPN name of *
* the given account. The given account should be in NT4 format *
* (Domain\Username). The return will be the UPN or empty if not *
* retrieved. *
* *
*********************************************************************/
//BEGIN GetUPNName
_bstr_t CServMigr::GetUPNName(_bstr_t sSrcSAM)
{
/* local variables */
HRESULT hr;
_bstr_t sUPN = L"";
HANDLE hDs = NULL;
/* function body */
//bind to the source domain
DWORD dwError = DsBind(NULL,m_strSourceDomain,&hDs);
//now try to call DSCrackNames to get the UPN name
if ((dwError == ERROR_SUCCESS) && hDs)
{
PDS_NAME_RESULT pNamesOut = NULL;
WCHAR * pNamesIn[1];
_bstr_t sSrcAccount = m_strSourceDomainFlat + _bstr_t(L"\\") + m_strSourceSam;
pNamesIn[0] = (WCHAR*)sSrcAccount;
hr = DsCrackNames(hDs,DS_NAME_NO_FLAGS,DS_NT4_ACCOUNT_NAME,DS_USER_PRINCIPAL_NAME,1,pNamesIn,&pNamesOut);
DsUnBind(&hDs);
hDs = NULL;
//if got UPN name, store it
if ( !hr )
{
if ( pNamesOut->rItems[0].status == DS_NAME_NO_ERROR )
sUPN = pNamesOut->rItems[0].pName;
DsFreeNameResult(pNamesOut); //free the results
}//end if cracked name
}//end if bound
return sUPN;
}
//END GetUPNName