windows-nt/Source/XPSP1/NT/admin/admt/arext/scmmigr/servmigr.cpp

1111 lines
34 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
// 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 <lm.h>
#include <dsgetdc.h>
//#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
/////////////////////////////////////////////////////////////////////////////
// CServMigr
STDMETHODIMP CServMigr::ProcessUndo(/*[in]*/ IUnknown * pSource, /*[in]*/ IUnknown * pTarget, /*[in]*/ IUnknown * pMainSettings, /*[in, out]*/ IUnknown ** pPropToSet)
{
return E_NOTIMPL;
}
STDMETHODIMP CServMigr::PreProcessObject(/*[in]*/ IUnknown * pSource, /*[in]*/ IUnknown * pTarget, /*[in]*/ IUnknown * pMainSettings, /*[in, out]*/ IUnknown ** pPropToSet)
{
return S_OK;
}
STDMETHODIMP
CServMigr::ProcessObject(
/*[in]*/ IUnknown * pSource,
/*[in]*/ IUnknown * pTarget,
/*[in]*/ IUnknown * pMainSettings,
/*[in,out]*/IUnknown ** ppPropsToSet
)
{
HRESULT hr = S_OK;
// BOOL bFailedToUpdateOne = FALSE;
WCHAR domAccount[500];
WCHAR domTgtAccount[500];
_bstr_t domain;
_bstr_t account;
IVarSetPtr pVarSet(pMainSettings);
IIManageDBPtr pDB;
_bstr_t logfile;
_bstr_t srcComputer;
_bstr_t tgtComputer;
_bstr_t tgtAccount;
_bstr_t tgtDomain;
IVarSetPtr pData(CLSID_VarSet);
IUnknown * pUnk = NULL;
DWORD rc = 0;
_bstr_t sIntraForest;
BOOL bIntraForest = FALSE;
WCHAR * lastOperation = NULL;
try {
logfile = pVarSet->get(GET_BSTR(DCTVS_Options_Logfile));
lastOperation = L"Get Log file name";
if ( logfile.length() )
{
err.LogOpen(logfile,1);
lastOperation = L"Open log";
}
pDB = pVarSet->get(GET_BSTR(DCTVS_DBManager));
lastOperation = L"Got DBManager pointer";
if ( pDB != NULL )
{
lastOperation = L"DBManager is not null";
// Check to see if this account is referenced in the service accounts table
domain = pVarSet->get(GET_BSTR(DCTVS_Options_SourceDomain));
tgtDomain = pVarSet->get(GET_BSTR(DCTVS_Options_TargetDomain));
account = pVarSet->get(GET_BSTR(DCTVS_CopiedAccount_SourceSam));
tgtAccount = 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));
lastOperation = L"got names from varset";
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
lastOperation = L"checking whether this migration is intraforest";
// 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(account, L'\"'))
return S_OK;
lastOperation = L"preparing to build domain\\username strings";
swprintf(domAccount,L"%s\\%s",(WCHAR*)domain,(WCHAR*)account);
swprintf(domTgtAccount,L"%s\\%s",(WCHAR*)tgtDomain,(WCHAR*)tgtAccount);
lastOperation = L"done building domain\\username strings";
}
}catch (... )
{
err.DbgMsgWrite(ErrE,L"Exception!...LastOperation=%ls",lastOperation);
}
try {
hr = pData->QueryInterface(IID_IUnknown,(void**)&pUnk);
lastOperation = L"Got IUnknown pointer to varset";
if ( SUCCEEDED(hr) )
{
lastOperation = L"Checking database for this account";
hr = pDB->raw_GetServiceAccount(SysAllocString(domAccount),&pUnk);
lastOperation = L"Got service account info";
}
}
catch ( ... )
{
err.DbgMsgWrite(ErrE,L"Exception!...lastOperation=%ls",lastOperation);
}
try {
if ( SUCCEEDED(hr) )
{
lastOperation = L"preparing to check service account info";
pData = pUnk;
lastOperation = L"freeing old pointer";
pUnk->Release();
lastOperation = L"freed old pointer";
// remove the password must change flag, if set
USER_INFO_2 * uInfo = NULL;
DWORD parmErr = 0;
WCHAR password[LEN_Password];
long entries = pData->get("ServiceAccountEntries");
lastOperation = L"got entry count";
if ( (entries != 0) && !bIntraForest ) // if we're moving the account, don't mess with its properties
{
lastOperation = L"getting target user info";
rc = NetUserGetInfo(tgtComputer,tgtAccount,2,(LPBYTE*)&uInfo);
lastOperation = L"got target user info";
if ( ! rc )
{
// generate a new, strong, 14 character password for this account,
// and set the password to not expire
lastOperation = L"generating password";
EaPasswordGenerate(3,3,3,3,6,14,password,DIM(password));
lastOperation = L"generated password";
uInfo->usri2_flags |= (UF_DONT_EXPIRE_PASSWD);
lastOperation = L"updating flags";
uInfo->usri2_password = password;
lastOperation = L"setting user info";
rc = NetUserSetInfo(tgtComputer,tgtAccount,2,(LPBYTE)uInfo,&parmErr);
lastOperation = L"set user info";
if ( ! rc )
{
lastOperation = L"writing log messages";
err.MsgWrite(0,DCT_MSG_REMOVED_PWDCHANGE_FLAG_S,(WCHAR*)tgtAccount);
lastOperation = L"writing log messages2";
err.MsgWrite(0,DCT_MSG_PWGENERATED_S,(WCHAR*)tgtAccount);
lastOperation = L"wrote log messages";
// 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),account);
lastOperation = L"put flag into varset";
_bstr_t sFileName = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_PasswordFile));
lastOperation = L"got name for password file";
if ( !m_bTriedToOpenFile )
{
lastOperation = L"trying to open password file";
m_bTriedToOpenFile = true;
lastOperation = L"set password file flag";
// we should see if the varset specifies a file name
if ( sFileName.length() > 0 )
{
lastOperation = L"password file has a name";
// we have the file name so lets open it and save the handle.
m_passwordLog.LogOpen(sFileName, TRUE, 1);
lastOperation = L"opened password file";
}
//failure to get to password file so store it elsewhere and
//post that new file location in the migration log
if (!m_passwordLog.IsOpen() )
{
WCHAR sPasswordFile[MAX_PATH];
if (GetDirectory(sPasswordFile))//place log in default log dir
wcscat(sPasswordFile, L"Logs\\passwords.txt");
else
wcscpy(sPasswordFile, L"c:\\passwords.txt");
m_passwordLog.LogOpen(sPasswordFile, TRUE, 1);//open this log file
if ( m_passwordLog.IsOpen() )
err.MsgWrite(0,DCT_MSG_NEW_PASSWORD_LOG_S,(WCHAR*)sFileName,sPasswordFile);
}
}
lastOperation = L"done with pwd file opening";
// Log the new password in the password log.
if ( m_passwordLog.IsOpen() )
{
lastOperation = L"preparing to write to password file";
m_passwordLog.MsgWrite(L"%ls,%ls",(WCHAR*)tgtAccount,password);
lastOperation = L"wrote to password file";
}
}
lastOperation = L"done with user set info";
uInfo->usri2_password = NULL;
lastOperation = L"cleared password property";
NetApiBufferFree(uInfo);
lastOperation = L"freed buffer";
}
lastOperation = L"done with get user info";
if ( rc )
{
lastOperation = L"writing error message";
err.SysMsgWrite(ErrE,rc,DCT_MSG_REMOVED_PWDCHANGE_FLAG_FAILED_SD,(WCHAR*)tgtAccount,rc);
lastOperation = L"done writing error message";
}
}
if (entries != 0 )
{
lastOperation = L"There are some entries";
try {
if ( ! rc )
{
lastOperation = L"no errors";
WCHAR strSID[200] = L"";
BYTE sid[200];
WCHAR domain[LEN_Domain];
SID_NAME_USE snu;
DWORD lenSid = DIM(sid);
DWORD lenDomain = DIM(domain);
DWORD lenStrSid = DIM(strSID);
lastOperation = L"Looking up account name";
if ( LookupAccountName(tgtComputer,tgtAccount,sid,&lenSid,domain,&lenDomain,&snu) )
{
lastOperation = L"getting txt for sid";
if ( GetTextualSid(sid,strSID,&lenStrSid) )
{
lastOperation = L"got txt for sid";
// 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);
else
UpdateSCMs(pData,domTgtAccount,password,strSID,pDB);
lastOperation = L"did scm updates";
}
else
{
lastOperation = L"didn't get text for sid";
err.SysMsgWrite(ErrE,GetLastError(),DCT_MSG_CANNOT_FIND_ACCOUNT_SSD,tgtAccount,tgtComputer,GetLastError());
}
}
else
{
lastOperation = L"didn't find account name";
err.SysMsgWrite(ErrE,GetLastError(),DCT_MSG_CANNOT_FIND_ACCOUNT_SSD,tgtAccount,tgtComputer,GetLastError());
lastOperation = L"wrote LookupAccountName error message";
}
}
}
catch(...)
{
err.DbgMsgWrite(ErrE,L"Exception!, last=%ls",lastOperation);
}
lastOperation = L"123";
}
lastOperation = L"444444";
lastOperation = L"656346";
}
else
{
lastOperation = L"234234242";
err.SysMsgWrite(ErrE,E_FAIL,DCT_MSG_DB_OBJECT_CREATE_FAILED_D,E_FAIL);
lastOperation = L"asdfsadf";
}
lastOperation = L"ddddd";
err.LogClose();
}
catch (... )
{
err.DbgMsgWrite(ErrE,L"An exception occurred. LastOperation=%ls",lastOperation);
}
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
)
{
DWORD rc = 0;
IUserRightsPtr pRights;
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
rc = pRights.CreateInstance(CLSID_UserRights);
if ( FAILED(rc) )
{
pRights = NULL;
}
SC_HANDLE pScm = OpenSCManager(computer, NULL, SC_MANAGER_ALL_ACCESS );
if ( pScm )
{
// grant the logon as a service right to the target account
if ( pRights != NULL )
{
if ( bNeedToGrantLOS )
{
rc = pRights->raw_AddUserRight(SysAllocString(computer),SysAllocString(strSid),SysAllocString(L"SeServiceLogonRight"));
if ( rc )
{
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;
// 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();
}
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
)
{
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*/);
bFirst = FALSE;
safecopy(prevComputer,(WCHAR*)computer);
}
catch (...)
{
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,SysAllocString(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,SysAllocString(account),SvcAcctStatus_UpdateFailed);
bGotThemAll = FALSE;
SaveEncryptedPassword(computer,service,account,password);
err.SysMsgWrite(ErrE,rc,DCT_MSG_UPDATE_SCM_ENTRY_FAILED_SSD,(WCHAR*)computer,(WCHAR*)service,rc);
}
}
}
}
catch ( ... )
{
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
pNode->SetPassword(password);
break;
}
}
}
}
// else...
if (pNode == NULL)
{
// insert new entry
pNode = new TEntryNode(server, service, account, password);
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,L"Software\\Mission Critical Software\\DomainAdmin",&hRegKey);
if ( ! rc )
{
rc = RegQueryValueEx(hRegKey,L"Directory",0,&type,(LPBYTE)fullpath,&lenValue);
if (! rc )
{
UStrCpy(fullpath+UStrLen(fullpath),filename);
}
RegCloseKey(hRegKey);
}
// 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 = new TEntryNode(computer,service,account,password);
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));
}
} 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;
FILE * hDest;
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;
// Open the destination file.
HKEY hRegKey;
DWORD type;
DWORD lenValue = (sizeof fullpath);
rc = RegOpenKey(HKEY_LOCAL_MACHINE,L"Software\\Mission Critical Software\\DomainAdmin",&hRegKey);
if ( ! rc )
{
rc = RegQueryValueEx(hRegKey,L"Directory",0,&type,(LPBYTE)fullpath,&lenValue);
if (! rc )
{
UStrCpy(fullpath+UStrLen(fullpath),filename);
}
RegCloseKey(hRegKey);
}
if( (hDest = _wfopen(fullpath,L"wb")) == NULL)
{
rc = GetLastError();
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.
fwrite(&dwBlobLen, sizeof(DWORD), 1, hDest);
if(ferror(hDest))
{
rc = GetLastError();
free(pbKeyBlob);
goto done;
}
// Write the key blob to the destination file.
fwrite(pbKeyBlob, 1, dwBlobLen, hDest);
if(ferror(hDest))
{
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
if ( pNode->GetPassword() && *pNode->GetPassword() )
{
swprintf((WCHAR*)pbBuffer,L"%s\t%s\t%s\t%s\n",pNode->GetComputer(),pNode->GetService(),pNode->GetAccount(),pNode->GetPassword());
}
else
{
swprintf((WCHAR*)pbBuffer,L"%s\t%s\t%s\t%s\n",pNode->GetComputer(),pNode->GetService(),pNode->GetAccount(),L"NULL");
}
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 / 2)) / 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.
fwrite(pbBuffer, 1, dwCount, hDest);
if(ferror(hDest))
{
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 != NULL) fclose(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;
hr = TryUpdateSamWithPassword(computer,service,account,SysAllocString(pNode->GetPassword()) );
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];
WCHAR dc[LEN_Computer];
WCHAR account[LEN_Account];
WCHAR domStr[LEN_Domain];
BYTE sid[100];
WCHAR strSid[200];
WCHAR * pSlash = wcschr(domAccount,L'\\');
DOMAIN_CONTROLLER_INFO * pInfo = NULL;
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);
rc = DsGetDcName(NULL,domain,NULL,NULL,0,&pInfo);
if (! rc )
{
safecopy(dc,pInfo->DomainControllerName);
NetApiBufferFree(pInfo);
}
// 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);
}
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;
}