|
|
// 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
|