// 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 #include //#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; }