Source code of Windows XP (NT5)
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.

1110 lines
34 KiB

  1. // ServMigr.cpp : Implementation of CServMigr
  2. #include "stdafx.h"
  3. #include "ScmMigr.h"
  4. #include "ServMigr.h"
  5. #include "ErrDct.hpp"
  6. #include "ResStr.h"
  7. #include "Common.hpp"
  8. #include "PWGen.hpp"
  9. #include "EaLen.hpp"
  10. #include "TReg.hpp"
  11. #include "TxtSid.h"
  12. #include "ARExt_i.c"
  13. #include <lm.h>
  14. #include <dsgetdc.h>
  15. //#import "\bin\McsVarSetMin.tlb" no_namespace, named_guids
  16. //#import "\bin\DBManager.tlb" no_namespace, named_guids
  17. //#import "\bin\McsDctWorkerObjects.tlb" no_namespace, named_guids
  18. #import "VarSet.tlb" no_namespace, named_guids rename("property", "aproperty")
  19. //#import "DBMgr.tlb" no_namespace, named_guids //already #imported in ServMigr.h
  20. #import "WorkObj.tlb" no_namespace, named_guids
  21. TErrorDct err;
  22. StringLoader gString;
  23. #define BLOCK_SIZE 160
  24. #define BUFFER_SIZE 400
  25. #define SvcAcctStatus_NotMigratedYet 0
  26. #define SvcAcctStatus_DoNotUpdate 1
  27. #define SvcAcctStatus_Updated 2
  28. #define SvcAcctStatus_UpdateFailed 4
  29. #define SvcAcctStatus_NeverAllowUpdate 8
  30. /////////////////////////////////////////////////////////////////////////////
  31. // CServMigr
  32. STDMETHODIMP CServMigr::ProcessUndo(/*[in]*/ IUnknown * pSource, /*[in]*/ IUnknown * pTarget, /*[in]*/ IUnknown * pMainSettings, /*[in, out]*/ IUnknown ** pPropToSet)
  33. {
  34. return E_NOTIMPL;
  35. }
  36. STDMETHODIMP CServMigr::PreProcessObject(/*[in]*/ IUnknown * pSource, /*[in]*/ IUnknown * pTarget, /*[in]*/ IUnknown * pMainSettings, /*[in, out]*/ IUnknown ** pPropToSet)
  37. {
  38. return S_OK;
  39. }
  40. STDMETHODIMP
  41. CServMigr::ProcessObject(
  42. /*[in]*/ IUnknown * pSource,
  43. /*[in]*/ IUnknown * pTarget,
  44. /*[in]*/ IUnknown * pMainSettings,
  45. /*[in,out]*/IUnknown ** ppPropsToSet
  46. )
  47. {
  48. HRESULT hr = S_OK;
  49. // BOOL bFailedToUpdateOne = FALSE;
  50. WCHAR domAccount[500];
  51. WCHAR domTgtAccount[500];
  52. _bstr_t domain;
  53. _bstr_t account;
  54. IVarSetPtr pVarSet(pMainSettings);
  55. IIManageDBPtr pDB;
  56. _bstr_t logfile;
  57. _bstr_t srcComputer;
  58. _bstr_t tgtComputer;
  59. _bstr_t tgtAccount;
  60. _bstr_t tgtDomain;
  61. IVarSetPtr pData(CLSID_VarSet);
  62. IUnknown * pUnk = NULL;
  63. DWORD rc = 0;
  64. _bstr_t sIntraForest;
  65. BOOL bIntraForest = FALSE;
  66. WCHAR * lastOperation = NULL;
  67. try {
  68. logfile = pVarSet->get(GET_BSTR(DCTVS_Options_Logfile));
  69. lastOperation = L"Get Log file name";
  70. if ( logfile.length() )
  71. {
  72. err.LogOpen(logfile,1);
  73. lastOperation = L"Open log";
  74. }
  75. pDB = pVarSet->get(GET_BSTR(DCTVS_DBManager));
  76. lastOperation = L"Got DBManager pointer";
  77. if ( pDB != NULL )
  78. {
  79. lastOperation = L"DBManager is not null";
  80. // Check to see if this account is referenced in the service accounts table
  81. domain = pVarSet->get(GET_BSTR(DCTVS_Options_SourceDomain));
  82. tgtDomain = pVarSet->get(GET_BSTR(DCTVS_Options_TargetDomain));
  83. account = pVarSet->get(GET_BSTR(DCTVS_CopiedAccount_SourceSam));
  84. tgtAccount = pVarSet->get(GET_BSTR(DCTVS_CopiedAccount_TargetSam));
  85. srcComputer = pVarSet->get(GET_BSTR(DCTVS_Options_SourceServer));
  86. tgtComputer = pVarSet->get(GET_BSTR(DCTVS_Options_TargetServer));
  87. sIntraForest = pVarSet->get(GET_BSTR(DCTVS_Options_IsIntraforest));
  88. lastOperation = L"got names from varset";
  89. if ( ! UStrICmp((WCHAR*)sIntraForest,GET_STRING(IDS_YES)) )
  90. {
  91. // for intra-forest migration we are moving, not copying, so we don't need to update the password
  92. lastOperation = L"checking whether this migration is intraforest";
  93. // Actually, it turns out that ChangeServiceConfig will not let us update just the service account
  94. // and not the passord, so we'll have to go ahead and change the password for the service ac
  95. //bIntraForest = TRUE;
  96. }
  97. //if the SAM account name has a " character in it, it cannot be a service
  98. //account, and therefore we leave
  99. if (wcschr(account, L'\"'))
  100. return S_OK;
  101. lastOperation = L"preparing to build domain\\username strings";
  102. swprintf(domAccount,L"%s\\%s",(WCHAR*)domain,(WCHAR*)account);
  103. swprintf(domTgtAccount,L"%s\\%s",(WCHAR*)tgtDomain,(WCHAR*)tgtAccount);
  104. lastOperation = L"done building domain\\username strings";
  105. }
  106. }catch (... )
  107. {
  108. err.DbgMsgWrite(ErrE,L"Exception!...LastOperation=%ls",lastOperation);
  109. }
  110. try {
  111. hr = pData->QueryInterface(IID_IUnknown,(void**)&pUnk);
  112. lastOperation = L"Got IUnknown pointer to varset";
  113. if ( SUCCEEDED(hr) )
  114. {
  115. lastOperation = L"Checking database for this account";
  116. hr = pDB->raw_GetServiceAccount(SysAllocString(domAccount),&pUnk);
  117. lastOperation = L"Got service account info";
  118. }
  119. }
  120. catch ( ... )
  121. {
  122. err.DbgMsgWrite(ErrE,L"Exception!...lastOperation=%ls",lastOperation);
  123. }
  124. try {
  125. if ( SUCCEEDED(hr) )
  126. {
  127. lastOperation = L"preparing to check service account info";
  128. pData = pUnk;
  129. lastOperation = L"freeing old pointer";
  130. pUnk->Release();
  131. lastOperation = L"freed old pointer";
  132. // remove the password must change flag, if set
  133. USER_INFO_2 * uInfo = NULL;
  134. DWORD parmErr = 0;
  135. WCHAR password[LEN_Password];
  136. long entries = pData->get("ServiceAccountEntries");
  137. lastOperation = L"got entry count";
  138. if ( (entries != 0) && !bIntraForest ) // if we're moving the account, don't mess with its properties
  139. {
  140. lastOperation = L"getting target user info";
  141. rc = NetUserGetInfo(tgtComputer,tgtAccount,2,(LPBYTE*)&uInfo);
  142. lastOperation = L"got target user info";
  143. if ( ! rc )
  144. {
  145. // generate a new, strong, 14 character password for this account,
  146. // and set the password to not expire
  147. lastOperation = L"generating password";
  148. EaPasswordGenerate(3,3,3,3,6,14,password,DIM(password));
  149. lastOperation = L"generated password";
  150. uInfo->usri2_flags |= (UF_DONT_EXPIRE_PASSWD);
  151. lastOperation = L"updating flags";
  152. uInfo->usri2_password = password;
  153. lastOperation = L"setting user info";
  154. rc = NetUserSetInfo(tgtComputer,tgtAccount,2,(LPBYTE)uInfo,&parmErr);
  155. lastOperation = L"set user info";
  156. if ( ! rc )
  157. {
  158. lastOperation = L"writing log messages";
  159. err.MsgWrite(0,DCT_MSG_REMOVED_PWDCHANGE_FLAG_S,(WCHAR*)tgtAccount);
  160. lastOperation = L"writing log messages2";
  161. err.MsgWrite(0,DCT_MSG_PWGENERATED_S,(WCHAR*)tgtAccount);
  162. lastOperation = L"wrote log messages";
  163. // write the password to the password log file and mark this account, so that the
  164. // SetPassword extension will not reset the password again.
  165. pVarSet->put(GET_BSTR(DCTVS_CopiedAccount_DoNotUpdatePassword),account);
  166. lastOperation = L"put flag into varset";
  167. _bstr_t sFileName = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_PasswordFile));
  168. lastOperation = L"got name for password file";
  169. if ( !m_bTriedToOpenFile )
  170. {
  171. lastOperation = L"trying to open password file";
  172. m_bTriedToOpenFile = true;
  173. lastOperation = L"set password file flag";
  174. // we should see if the varset specifies a file name
  175. if ( sFileName.length() > 0 )
  176. {
  177. lastOperation = L"password file has a name";
  178. // we have the file name so lets open it and save the handle.
  179. m_passwordLog.LogOpen(sFileName, TRUE, 1);
  180. lastOperation = L"opened password file";
  181. }
  182. //failure to get to password file so store it elsewhere and
  183. //post that new file location in the migration log
  184. if (!m_passwordLog.IsOpen() )
  185. {
  186. WCHAR sPasswordFile[MAX_PATH];
  187. if (GetDirectory(sPasswordFile))//place log in default log dir
  188. wcscat(sPasswordFile, L"Logs\\passwords.txt");
  189. else
  190. wcscpy(sPasswordFile, L"c:\\passwords.txt");
  191. m_passwordLog.LogOpen(sPasswordFile, TRUE, 1);//open this log file
  192. if ( m_passwordLog.IsOpen() )
  193. err.MsgWrite(0,DCT_MSG_NEW_PASSWORD_LOG_S,(WCHAR*)sFileName,sPasswordFile);
  194. }
  195. }
  196. lastOperation = L"done with pwd file opening";
  197. // Log the new password in the password log.
  198. if ( m_passwordLog.IsOpen() )
  199. {
  200. lastOperation = L"preparing to write to password file";
  201. m_passwordLog.MsgWrite(L"%ls,%ls",(WCHAR*)tgtAccount,password);
  202. lastOperation = L"wrote to password file";
  203. }
  204. }
  205. lastOperation = L"done with user set info";
  206. uInfo->usri2_password = NULL;
  207. lastOperation = L"cleared password property";
  208. NetApiBufferFree(uInfo);
  209. lastOperation = L"freed buffer";
  210. }
  211. lastOperation = L"done with get user info";
  212. if ( rc )
  213. {
  214. lastOperation = L"writing error message";
  215. err.SysMsgWrite(ErrE,rc,DCT_MSG_REMOVED_PWDCHANGE_FLAG_FAILED_SD,(WCHAR*)tgtAccount,rc);
  216. lastOperation = L"done writing error message";
  217. }
  218. }
  219. if (entries != 0 )
  220. {
  221. lastOperation = L"There are some entries";
  222. try {
  223. if ( ! rc )
  224. {
  225. lastOperation = L"no errors";
  226. WCHAR strSID[200] = L"";
  227. BYTE sid[200];
  228. WCHAR domain[LEN_Domain];
  229. SID_NAME_USE snu;
  230. DWORD lenSid = DIM(sid);
  231. DWORD lenDomain = DIM(domain);
  232. DWORD lenStrSid = DIM(strSID);
  233. lastOperation = L"Looking up account name";
  234. if ( LookupAccountName(tgtComputer,tgtAccount,sid,&lenSid,domain,&lenDomain,&snu) )
  235. {
  236. lastOperation = L"getting txt for sid";
  237. if ( GetTextualSid(sid,strSID,&lenStrSid) )
  238. {
  239. lastOperation = L"got txt for sid";
  240. // for each reference to the service account, update the SCM
  241. // for intra-forest migration, don't update the password
  242. if ( bIntraForest )
  243. UpdateSCMs(pData,domTgtAccount,NULL,strSID,pDB);
  244. else
  245. UpdateSCMs(pData,domTgtAccount,password,strSID,pDB);
  246. lastOperation = L"did scm updates";
  247. }
  248. else
  249. {
  250. lastOperation = L"didn't get text for sid";
  251. err.SysMsgWrite(ErrE,GetLastError(),DCT_MSG_CANNOT_FIND_ACCOUNT_SSD,tgtAccount,tgtComputer,GetLastError());
  252. }
  253. }
  254. else
  255. {
  256. lastOperation = L"didn't find account name";
  257. err.SysMsgWrite(ErrE,GetLastError(),DCT_MSG_CANNOT_FIND_ACCOUNT_SSD,tgtAccount,tgtComputer,GetLastError());
  258. lastOperation = L"wrote LookupAccountName error message";
  259. }
  260. }
  261. }
  262. catch(...)
  263. {
  264. err.DbgMsgWrite(ErrE,L"Exception!, last=%ls",lastOperation);
  265. }
  266. lastOperation = L"123";
  267. }
  268. lastOperation = L"444444";
  269. lastOperation = L"656346";
  270. }
  271. else
  272. {
  273. lastOperation = L"234234242";
  274. err.SysMsgWrite(ErrE,E_FAIL,DCT_MSG_DB_OBJECT_CREATE_FAILED_D,E_FAIL);
  275. lastOperation = L"asdfsadf";
  276. }
  277. lastOperation = L"ddddd";
  278. err.LogClose();
  279. }
  280. catch (... )
  281. {
  282. err.DbgMsgWrite(ErrE,L"An exception occurred. LastOperation=%ls",lastOperation);
  283. }
  284. return S_OK;
  285. }
  286. STDMETHODIMP CServMigr::get_sDesc(/*[out, retval]*/ BSTR *pVal)
  287. {
  288. (*pVal) = SysAllocString(L"Updates SCM entries for services using migrated accounts.");
  289. return S_OK;
  290. }
  291. STDMETHODIMP CServMigr::put_sDesc(/*[in]*/ BSTR newVal)
  292. {
  293. return E_NOTIMPL;
  294. }
  295. STDMETHODIMP CServMigr::get_sName(/*[out, retval]*/ BSTR *pVal)
  296. {
  297. (*pVal) = SysAllocString(L"Generic Service Account Migration");
  298. return S_OK;
  299. }
  300. STDMETHODIMP CServMigr::put_sName(/*[in]*/ BSTR newVal)
  301. {
  302. return E_NOTIMPL;
  303. }
  304. DWORD
  305. CServMigr::DoUpdate(
  306. WCHAR const * account,
  307. WCHAR const * password,
  308. WCHAR const * strSid,
  309. WCHAR const * computer,
  310. WCHAR const * service,
  311. BOOL bNeedToGrantLOS
  312. )
  313. {
  314. DWORD rc = 0;
  315. IUserRightsPtr pRights;
  316. WCHAR const * ppassword = password;
  317. // if password is empty, set it to NULL
  318. if ( ppassword && ppassword[0] == 0 )
  319. {
  320. ppassword = NULL;
  321. }
  322. else if ( !UStrCmp(password,L"NULL") )
  323. {
  324. ppassword = NULL;
  325. }
  326. // only try to update entries that we need to be updating
  327. // try to connect to the SCM on this machine
  328. rc = pRights.CreateInstance(CLSID_UserRights);
  329. if ( FAILED(rc) )
  330. {
  331. pRights = NULL;
  332. }
  333. SC_HANDLE pScm = OpenSCManager(computer, NULL, SC_MANAGER_ALL_ACCESS );
  334. if ( pScm )
  335. {
  336. // grant the logon as a service right to the target account
  337. if ( pRights != NULL )
  338. {
  339. if ( bNeedToGrantLOS )
  340. {
  341. rc = pRights->raw_AddUserRight(SysAllocString(computer),SysAllocString(strSid),SysAllocString(L"SeServiceLogonRight"));
  342. if ( rc )
  343. {
  344. err.SysMsgWrite(ErrE,rc,DCT_MSG_LOS_GRANT_FAILED_SSD,
  345. account,(WCHAR*)computer,rc);
  346. }
  347. else
  348. {
  349. err.MsgWrite(0,DCT_MSG_LOS_GRANTED_SS,
  350. account,(WCHAR*)computer);
  351. }
  352. }
  353. }
  354. SC_HANDLE pService = OpenService(pScm,service,SERVICE_ALL_ACCESS);
  355. if ( pService )
  356. {
  357. int nCnt = 0;
  358. // update the account and password for the service
  359. while ( !ChangeServiceConfig(pService,
  360. SERVICE_NO_CHANGE, // dwServiceType
  361. SERVICE_NO_CHANGE, // dwStartType
  362. SERVICE_NO_CHANGE, // dwErrorControl
  363. NULL, // lpBinaryPathName
  364. NULL, // lpLoadOrderGroup
  365. NULL, // lpdwTagId
  366. NULL, // lpDependencies
  367. account, // lpServiceStartName
  368. ppassword, // lpPassword
  369. NULL) && nCnt < 5) // lpDisplayName
  370. {
  371. nCnt++;
  372. Sleep(500);
  373. }
  374. if ( nCnt < 5 )
  375. {
  376. err.MsgWrite(0,DCT_MSG_UPDATED_SCM_ENTRY_SS,(WCHAR*)computer,(WCHAR*)service);
  377. }
  378. else
  379. {
  380. rc = GetLastError();
  381. }
  382. CloseServiceHandle(pService);
  383. }
  384. CloseServiceHandle(pScm);
  385. }
  386. else
  387. {
  388. rc = GetLastError();
  389. }
  390. return rc;
  391. }
  392. BOOL
  393. CServMigr::UpdateSCMs(
  394. IUnknown * pVS,
  395. WCHAR const * account,
  396. WCHAR const * password,
  397. WCHAR const * strSid,
  398. IIManageDB * pDB
  399. )
  400. {
  401. BOOL bGotThemAll = TRUE;
  402. IVarSetPtr pVarSet = pVS;
  403. LONG nCount = 0;
  404. WCHAR key[LEN_Path];
  405. _bstr_t computer;
  406. _bstr_t service;
  407. long status;
  408. DWORD rc = 0;
  409. BOOL bFirst = TRUE;
  410. WCHAR prevComputer[LEN_Path] = L"";
  411. try {
  412. nCount = pVarSet->get("ServiceAccountEntries");
  413. for ( long i = 0 ; i < nCount ; i++ )
  414. {
  415. swprintf(key,L"Computer.%ld",i);
  416. computer = pVarSet->get(key);
  417. swprintf(key,L"Service.%ld",i);
  418. service = pVarSet->get(key);
  419. swprintf(key,L"ServiceAccountStatus.%ld",i);
  420. status = pVarSet->get(key);
  421. if ( status == SvcAcctStatus_NotMigratedYet || status == SvcAcctStatus_UpdateFailed )
  422. {
  423. if ( UStrICmp(prevComputer,(WCHAR*)computer) )
  424. {
  425. bFirst = TRUE; // reset the 'first' flag when the computer changes
  426. }
  427. try {
  428. rc = DoUpdate(account,password,strSid,computer,service,bFirst/*only grant SeServiceLogonRight once per account*/);
  429. bFirst = FALSE;
  430. safecopy(prevComputer,(WCHAR*)computer);
  431. }
  432. catch (...)
  433. {
  434. err.DbgMsgWrite(ErrE,L"Exception!");
  435. err.DbgMsgWrite(0,L"Updating %ls on %ls",(WCHAR*)service,(WCHAR*)computer);
  436. err.DbgMsgWrite(0,L"Account=%ls, SID=%ls",(WCHAR*)account,(WCHAR*)strSid);
  437. rc = E_FAIL;
  438. }
  439. if (! rc )
  440. {
  441. // the update was successful
  442. pDB->raw_SetServiceAcctEntryStatus(computer,service,SysAllocString(account),SvcAcctStatus_Updated);
  443. }
  444. else
  445. {
  446. // couldn't connect to this one -- we will need to save this account's password
  447. // in our encrypted storage
  448. pDB->raw_SetServiceAcctEntryStatus(computer,service,SysAllocString(account),SvcAcctStatus_UpdateFailed);
  449. bGotThemAll = FALSE;
  450. SaveEncryptedPassword(computer,service,account,password);
  451. err.SysMsgWrite(ErrE,rc,DCT_MSG_UPDATE_SCM_ENTRY_FAILED_SSD,(WCHAR*)computer,(WCHAR*)service,rc);
  452. }
  453. }
  454. }
  455. }
  456. catch ( ... )
  457. {
  458. err.DbgMsgWrite(ErrE,L"Exception!");
  459. }
  460. return bGotThemAll;
  461. }
  462. HRESULT
  463. CServMigr::SaveEncryptedPassword(
  464. WCHAR const * server,
  465. WCHAR const * service,
  466. WCHAR const * account,
  467. WCHAR const * password
  468. )
  469. {
  470. HRESULT hr = S_OK;
  471. TNodeListEnum e;
  472. TEntryNode* pNode;
  473. // if entry exists...
  474. for (pNode = (TEntryNode*)e.OpenFirst(&m_List); pNode; pNode = (TEntryNode*)e.Next())
  475. {
  476. if (_wcsicmp(pNode->GetComputer(), server) == 0)
  477. {
  478. if (_wcsicmp(pNode->GetService(), service) == 0)
  479. {
  480. if (_wcsicmp(pNode->GetAccount(), account) == 0)
  481. {
  482. // update password
  483. pNode->SetPassword(password);
  484. break;
  485. }
  486. }
  487. }
  488. }
  489. // else...
  490. if (pNode == NULL)
  491. {
  492. // insert new entry
  493. pNode = new TEntryNode(server, service, account, password);
  494. if (pNode)
  495. {
  496. m_List.InsertBottom(pNode);
  497. }
  498. else
  499. {
  500. hr = E_OUTOFMEMORY;
  501. }
  502. }
  503. return hr;
  504. }
  505. //////////////////////////////////////////////////////////////////////////////////////
  506. ///
  507. /// TEntryList implementation of secure storage for service account passwords
  508. ///
  509. ///
  510. //////////////////////////////////////////////////////////////////////////////////////
  511. DWORD
  512. TEntryList::LoadFromFile(WCHAR const * filename)
  513. {
  514. DWORD rc = 0;
  515. FILE * hSource = NULL;
  516. HCRYPTPROV hProv = 0;
  517. HCRYPTKEY hKey = 0;
  518. BYTE pbBuffer[BLOCK_SIZE];
  519. WCHAR strData[BLOCK_SIZE * 5] = { 0 };
  520. DWORD dwCount;
  521. int eof = 0;
  522. WCHAR fullpath[LEN_Path];
  523. BYTE *pbKeyBlob = NULL;
  524. DWORD dwBlobLen;
  525. // Get our install directory from the registry, and then append the filename
  526. HKEY hRegKey;
  527. DWORD type;
  528. DWORD lenValue = (sizeof fullpath);
  529. rc = RegOpenKey(HKEY_LOCAL_MACHINE,L"Software\\Mission Critical Software\\DomainAdmin",&hRegKey);
  530. if ( ! rc )
  531. {
  532. rc = RegQueryValueEx(hRegKey,L"Directory",0,&type,(LPBYTE)fullpath,&lenValue);
  533. if (! rc )
  534. {
  535. UStrCpy(fullpath+UStrLen(fullpath),filename);
  536. }
  537. RegCloseKey(hRegKey);
  538. }
  539. // Open the source file.
  540. if((hSource = _wfopen(fullpath,L"rb"))==NULL)
  541. {
  542. rc = GetLastError();
  543. goto done;
  544. }
  545. // acquire handle to key container which must exist
  546. if ((hProv = AcquireContext(true)) == 0)
  547. {
  548. rc = GetLastError();
  549. goto done;
  550. }
  551. // Read the key blob length from the source file and allocate it to memory.
  552. fread(&dwBlobLen, sizeof(DWORD), 1, hSource);
  553. if(ferror(hSource) || feof(hSource))
  554. {
  555. rc = GetLastError();
  556. goto done;
  557. }
  558. if((pbKeyBlob = (BYTE*)malloc(dwBlobLen)) == NULL)
  559. {
  560. rc = GetLastError();
  561. goto done;
  562. }
  563. // Read the key blob from the source file.
  564. fread(pbKeyBlob, 1, dwBlobLen, hSource);
  565. if(ferror(hSource) || feof(hSource))
  566. {
  567. rc = GetLastError();
  568. goto done;
  569. }
  570. // Import the key blob into the CSP.
  571. if(!CryptImportKey(hProv, pbKeyBlob, dwBlobLen, 0, 0, &hKey))
  572. {
  573. rc = GetLastError();
  574. goto done;
  575. }
  576. // Decrypt the source file and load the list
  577. do {
  578. // Read up to BLOCK_SIZE bytes from source file.
  579. dwCount = fread(pbBuffer, 1, BLOCK_SIZE, hSource);
  580. if(ferror(hSource))
  581. {
  582. rc = GetLastError();
  583. goto done;
  584. }
  585. eof=feof(hSource);
  586. // Decrypt the data.
  587. if(!CryptDecrypt(hKey, 0, eof, 0, pbBuffer, &dwCount))
  588. {
  589. rc = GetLastError();
  590. goto done;
  591. }
  592. // Read any complete entries from the buffer
  593. // first, add the buffer contents to any leftover information we had read from before
  594. WCHAR * curr = strData;
  595. long len = UStrLen(strData);
  596. WCHAR * nl = NULL;
  597. WCHAR computer[LEN_Computer];
  598. WCHAR service[LEN_Service];
  599. WCHAR account[LEN_Account];
  600. WCHAR password[LEN_Password];
  601. wcsncpy(strData + len,(WCHAR*)pbBuffer, dwCount / sizeof(WCHAR));
  602. strData[len + (dwCount / sizeof(WCHAR))] = 0;
  603. do {
  604. nl = wcschr(curr,L'\n');
  605. if ( nl )
  606. {
  607. *nl = 0;
  608. if ( swscanf(curr,L" %[^\t]\t%[^\t]\t%[^\t]\t%[^\t]\n",computer,service,account,password) )
  609. {
  610. TEntryNode * pNode = new TEntryNode(computer,service,account,password);
  611. InsertBottom(pNode);
  612. }
  613. else
  614. {
  615. rc = E_FAIL;
  616. break;
  617. }
  618. // go on to the next entry
  619. curr = nl + 1;
  620. }
  621. } while ( nl );
  622. // there may be a partial record left in the buffer
  623. // if so, save it for the next read
  624. if ( (*curr) != 0 )
  625. {
  626. memmove(strData,curr,( 1 + UStrLen(curr) ) * (sizeof WCHAR));
  627. }
  628. } while(!feof(hSource));
  629. done:
  630. // Clean up
  631. if(pbKeyBlob)
  632. free(pbKeyBlob);
  633. if(hKey != 0)
  634. CryptDestroyKey(hKey);
  635. if(hProv != 0)
  636. CryptReleaseContext(hProv, 0);
  637. if(hSource != NULL)
  638. fclose(hSource);
  639. return rc;
  640. }
  641. DWORD
  642. TEntryList::SaveToFile(WCHAR const * filename)
  643. {
  644. DWORD rc = 0;
  645. BYTE pbBuffer[BUFFER_SIZE];
  646. DWORD dwCount;
  647. FILE * hDest;
  648. BYTE * pbKeyBlob = NULL;
  649. DWORD dwBlobLen;
  650. HCRYPTPROV hProv = 0;
  651. HCRYPTKEY hKey = 0;
  652. HCRYPTKEY hXchgKey = 0;
  653. TEntryNode * pNode;
  654. TNodeListEnum e;
  655. WCHAR fullpath[LEN_Path];
  656. DWORD dwBlockSize;
  657. DWORD cbBlockSize = sizeof(dwBlockSize);
  658. DWORD dwPaddedCount;
  659. // Open the destination file.
  660. HKEY hRegKey;
  661. DWORD type;
  662. DWORD lenValue = (sizeof fullpath);
  663. rc = RegOpenKey(HKEY_LOCAL_MACHINE,L"Software\\Mission Critical Software\\DomainAdmin",&hRegKey);
  664. if ( ! rc )
  665. {
  666. rc = RegQueryValueEx(hRegKey,L"Directory",0,&type,(LPBYTE)fullpath,&lenValue);
  667. if (! rc )
  668. {
  669. UStrCpy(fullpath+UStrLen(fullpath),filename);
  670. }
  671. RegCloseKey(hRegKey);
  672. }
  673. if( (hDest = _wfopen(fullpath,L"wb")) == NULL)
  674. {
  675. rc = GetLastError();
  676. goto done;
  677. }
  678. // acquire handle to key container
  679. if ((hProv = AcquireContext(false)) == 0)
  680. {
  681. rc = GetLastError();
  682. goto done;
  683. }
  684. // Attempt to get handle to exchange key.
  685. if(!CryptGetUserKey(hProv,AT_KEYEXCHANGE,&hKey))
  686. {
  687. if(GetLastError()==NTE_NO_KEY)
  688. {
  689. // Create key exchange key pair.
  690. if(!CryptGenKey(hProv,AT_KEYEXCHANGE,0,&hKey))
  691. {
  692. rc = GetLastError();
  693. goto done;
  694. }
  695. }
  696. else
  697. {
  698. rc = GetLastError();
  699. goto done;
  700. }
  701. }
  702. CryptDestroyKey(hKey);
  703. CryptReleaseContext(hProv,0);
  704. // acquire handle to key container
  705. if ((hProv = AcquireContext(false)) == 0)
  706. {
  707. rc = GetLastError();
  708. goto done;
  709. }
  710. // Get a handle to key exchange key.
  711. if(!CryptGetUserKey(hProv, AT_KEYEXCHANGE, &hXchgKey))
  712. {
  713. rc = GetLastError();
  714. goto done;
  715. }
  716. // Create a random block cipher session key.
  717. if(!CryptGenKey(hProv, CALG_RC2, CRYPT_EXPORTABLE, &hKey))
  718. {
  719. rc = GetLastError();
  720. goto done;
  721. }
  722. // Determine the size of the key blob and allocate memory.
  723. if(!CryptExportKey(hKey, hXchgKey, SIMPLEBLOB, 0, NULL, &dwBlobLen))
  724. {
  725. rc = GetLastError();
  726. goto done;
  727. }
  728. if((pbKeyBlob = (BYTE*)malloc(dwBlobLen)) == NULL)
  729. {
  730. rc = ERROR_NOT_ENOUGH_MEMORY;
  731. goto done;
  732. }
  733. // Export the key into a simple key blob.
  734. if(!CryptExportKey(hKey, hXchgKey, SIMPLEBLOB, 0, pbKeyBlob,
  735. &dwBlobLen))
  736. {
  737. rc = GetLastError();
  738. free(pbKeyBlob);
  739. goto done;
  740. }
  741. // Write the size of the key blob to the destination file.
  742. fwrite(&dwBlobLen, sizeof(DWORD), 1, hDest);
  743. if(ferror(hDest))
  744. {
  745. rc = GetLastError();
  746. free(pbKeyBlob);
  747. goto done;
  748. }
  749. // Write the key blob to the destination file.
  750. fwrite(pbKeyBlob, 1, dwBlobLen, hDest);
  751. if(ferror(hDest))
  752. {
  753. rc = GetLastError();
  754. free(pbKeyBlob);
  755. goto done;
  756. }
  757. // Free memory.
  758. free(pbKeyBlob);
  759. // get key cipher's block length in bytes
  760. if (CryptGetKeyParam(hKey, KP_BLOCKLEN, (BYTE*)&dwBlockSize, &cbBlockSize, 0))
  761. {
  762. dwBlockSize /= 8;
  763. }
  764. else
  765. {
  766. rc = GetLastError();
  767. goto done;
  768. }
  769. // Encrypt the item list and write it to the destination file.
  770. for ( pNode = (TEntryNode*)e.OpenFirst(this); pNode ; pNode = (TEntryNode *)e.Next() )
  771. {
  772. // copy an item into the buffer in the following format:
  773. // Computer\tService\tAccount\tPassword
  774. if ( pNode->GetPassword() && *pNode->GetPassword() )
  775. {
  776. swprintf((WCHAR*)pbBuffer,L"%s\t%s\t%s\t%s\n",pNode->GetComputer(),pNode->GetService(),pNode->GetAccount(),pNode->GetPassword());
  777. }
  778. else
  779. {
  780. swprintf((WCHAR*)pbBuffer,L"%s\t%s\t%s\t%s\n",pNode->GetComputer(),pNode->GetService(),pNode->GetAccount(),L"NULL");
  781. }
  782. dwCount = UStrLen((WCHAR*)pbBuffer) * (sizeof WCHAR) ;
  783. // the buffer must be a multiple of the key cipher's block length
  784. // NOTE: this algorithm assumes block length is multiple of sizeof(WCHAR)
  785. if (dwBlockSize > 0)
  786. {
  787. // calculate next multiple greater than count
  788. dwPaddedCount = ((dwCount + (dwBlockSize / 2)) / dwBlockSize) * dwBlockSize;
  789. // pad buffer with space characters
  790. WCHAR* pch = (WCHAR*)(pbBuffer + dwCount);
  791. for (; dwCount < dwPaddedCount; dwCount += sizeof(WCHAR))
  792. {
  793. *pch++ = L' ';
  794. }
  795. }
  796. // Encrypt the data.
  797. if(!CryptEncrypt(hKey, 0, (pNode->Next() == NULL) , 0, pbBuffer, &dwCount,
  798. BUFFER_SIZE))
  799. {
  800. rc = GetLastError();
  801. goto done;
  802. }
  803. // Write the data to the destination file.
  804. fwrite(pbBuffer, 1, dwCount, hDest);
  805. if(ferror(hDest))
  806. {
  807. rc = GetLastError();
  808. goto done;
  809. }
  810. }
  811. done:
  812. // Destroy the session key.
  813. if(hKey != 0) CryptDestroyKey(hKey);
  814. // Destroy the key exchange key.
  815. if(hXchgKey != 0) CryptDestroyKey(hXchgKey);
  816. // Release the provider handle.
  817. if(hProv != 0) CryptReleaseContext(hProv, 0);
  818. // Close destination file.
  819. if(hDest != NULL) fclose(hDest);
  820. return rc;
  821. }
  822. // AcquireContext Method
  823. //
  824. // acquire handle to key container within cryptographic service provider (CSP)
  825. //
  826. HCRYPTPROV TEntryList::AcquireContext(bool bContainerMustExist)
  827. {
  828. HCRYPTPROV hProv = 0;
  829. #define KEY_CONTAINER_NAME _T("A69904BC349C4CFEAAEAB038BAB8C3B1")
  830. if (bContainerMustExist)
  831. {
  832. // first try Microsoft Enhanced Cryptographic Provider
  833. if (!CryptAcquireContext(&hProv, KEY_CONTAINER_NAME, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET))
  834. {
  835. if (GetLastError() == NTE_KEYSET_NOT_DEF)
  836. {
  837. // then try Microsoft Base Cryptographic Provider
  838. CryptAcquireContext(&hProv, KEY_CONTAINER_NAME, MS_DEF_PROV, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET);
  839. }
  840. }
  841. }
  842. else
  843. {
  844. // first try Microsoft Enhanced Cryptographic Provider
  845. if (!CryptAcquireContext(&hProv, KEY_CONTAINER_NAME, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET))
  846. {
  847. DWORD dwError = GetLastError();
  848. if ((dwError == NTE_BAD_KEYSET) || (dwError == NTE_KEYSET_NOT_DEF))
  849. {
  850. // then try creating key container in enhanced provider
  851. if (!CryptAcquireContext(&hProv, KEY_CONTAINER_NAME, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET|CRYPT_NEWKEYSET))
  852. {
  853. dwError = GetLastError();
  854. if (dwError == NTE_KEYSET_NOT_DEF)
  855. {
  856. // then try Microsoft Base Cryptographic Provider
  857. if (!CryptAcquireContext(&hProv, KEY_CONTAINER_NAME, MS_DEF_PROV, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET))
  858. {
  859. dwError = GetLastError();
  860. if ((dwError == NTE_BAD_KEYSET) || (dwError == NTE_KEYSET_NOT_DEF))
  861. {
  862. // finally try creating key container in base provider
  863. CryptAcquireContext(&hProv, KEY_CONTAINER_NAME, MS_DEF_PROV, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET|CRYPT_NEWKEYSET);
  864. }
  865. }
  866. }
  867. }
  868. }
  869. }
  870. }
  871. return hProv;
  872. }
  873. STDMETHODIMP CServMigr::TryUpdateSam(BSTR computer,BSTR service,BSTR account)
  874. {
  875. HRESULT hr = S_OK;
  876. // Find the entry in the list, and perform the update
  877. TNodeListEnum e;
  878. TEntryNode * pNode;
  879. BOOL bFound = FALSE;
  880. for ( pNode = (TEntryNode*)e.OpenFirst(&m_List) ; pNode ; pNode = (TEntryNode*)e.Next() )
  881. {
  882. if ( !UStrICmp(computer,pNode->GetComputer())
  883. && !UStrICmp(service,pNode->GetService())
  884. && !UStrICmp(account,pNode->GetAccount())
  885. )
  886. {
  887. // found it!
  888. bFound = TRUE;
  889. hr = TryUpdateSamWithPassword(computer,service,account,SysAllocString(pNode->GetPassword()) );
  890. break;
  891. }
  892. }
  893. if ( ! bFound )
  894. {
  895. hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
  896. }
  897. return hr;
  898. }
  899. STDMETHODIMP CServMigr::TryUpdateSamWithPassword(BSTR computer,BSTR service,BSTR domAccount,BSTR password)
  900. {
  901. DWORD rc = 0;
  902. WCHAR domain[LEN_Domain];
  903. WCHAR dc[LEN_Computer];
  904. WCHAR account[LEN_Account];
  905. WCHAR domStr[LEN_Domain];
  906. BYTE sid[100];
  907. WCHAR strSid[200];
  908. WCHAR * pSlash = wcschr(domAccount,L'\\');
  909. DOMAIN_CONTROLLER_INFO * pInfo = NULL;
  910. SID_NAME_USE snu;
  911. DWORD lenSid = DIM(sid);
  912. DWORD lenDomStr = DIM(domStr);
  913. DWORD lenStrSid = DIM(strSid);
  914. // split out the domain and account names
  915. if ( pSlash )
  916. {
  917. // UStrCpy(domain,domAccount,pSlash - domAccount + 1);
  918. UStrCpy(domain,domAccount,(int)(pSlash - domAccount + 1));
  919. UStrCpy(account,pSlash+1);
  920. rc = DsGetDcName(NULL,domain,NULL,NULL,0,&pInfo);
  921. if (! rc )
  922. {
  923. safecopy(dc,pInfo->DomainControllerName);
  924. NetApiBufferFree(pInfo);
  925. }
  926. // get the SID for the target account
  927. if ( LookupAccountName(dc,account,sid,&lenSid,domStr,&lenDomStr,&snu) )
  928. {
  929. GetTextualSid(sid,strSid,&lenStrSid);
  930. rc = DoUpdate(domAccount,password,strSid,computer,service,TRUE);
  931. }
  932. else
  933. {
  934. rc = GetLastError();
  935. }
  936. }
  937. else
  938. {
  939. rc = ERROR_NOT_FOUND;
  940. }
  941. return HRESULT_FROM_WIN32(rc);
  942. }
  943. BOOL // ret - TRUE if directory found
  944. CServMigr::GetDirectory(
  945. WCHAR * filename // out - string buffer to store directory name
  946. )
  947. {
  948. DWORD rc = 0;
  949. BOOL bFound = FALSE;
  950. TRegKey key;
  951. rc = key.OpenRead(GET_STRING(IDS_HKLM_DomainAdmin_Key),HKEY_LOCAL_MACHINE);
  952. if ( ! rc )
  953. {
  954. rc = key.ValueGetStr(L"Directory",filename,MAX_PATH);
  955. if ( ! rc )
  956. {
  957. if ( *filename )
  958. bFound = TRUE;
  959. }
  960. }
  961. key.Close();
  962. return bFound;
  963. }