Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1445 lines
56 KiB

  1. //---------------------------------------------------------------------------
  2. // SetPass.cpp
  3. //
  4. // Comment: This is a COM object extension for the MCS DCTAccountReplicator.
  5. // This object implements the IExtendAccountMigration interface.
  6. // The process method of this object sets the password for the
  7. // target account according to the users specification.
  8. //
  9. // (c) Copyright 1995-1998, Mission Critical Software, Inc., All Rights Reserved
  10. //
  11. // Proprietary and confidential to Mission Critical Software, Inc.
  12. //---------------------------------------------------------------------------
  13. #include "stdafx.h"
  14. #include "ResStr.h"
  15. #include <lm.h>
  16. #include "rpc.h"
  17. #include "EaLen.hpp"
  18. #include <activeds.h>
  19. #include "ARExt.h"
  20. #include "ARExt_i.c"
  21. #include "ErrDCT.hpp"
  22. #include "PWGen.hpp"
  23. #include "TReg.hpp"
  24. #import "DBMgr.tlb" no_namespace
  25. StringLoader gString;
  26. TErrorDct err;
  27. #include "SetPwd.h"
  28. #include "SetPass.h"
  29. #include "pwdfuncs.h"
  30. #include "PwRpcUtl.h"
  31. #include "PwdSvc.h"
  32. #define AR_Status_Created (0x00000001)
  33. #define AR_Status_PasswordCopied (0x00000200)
  34. #define AR_Status_PasswordError (0x00000400)
  35. #define SECONDS_PER_DAY 86400.0
  36. /////////////////////////////////////////////////////////////////////////////
  37. // CSetPassword
  38. //---------------------------------------------------------------------------
  39. // Get and set methods for the properties.
  40. //---------------------------------------------------------------------------
  41. STDMETHODIMP CSetPassword::get_sName(BSTR *pVal)
  42. {
  43. *pVal = m_sName;
  44. return S_OK;
  45. }
  46. STDMETHODIMP CSetPassword::put_sName(BSTR newVal)
  47. {
  48. m_sName = newVal;
  49. return S_OK;
  50. }
  51. STDMETHODIMP CSetPassword::get_sDesc(BSTR *pVal)
  52. {
  53. *pVal = m_sDesc;
  54. return S_OK;
  55. }
  56. STDMETHODIMP CSetPassword::put_sDesc(BSTR newVal)
  57. {
  58. m_sDesc = newVal;
  59. return S_OK;
  60. }
  61. //---------------------------------------------------------------------------
  62. // ProcessObject : This method currently records the setting of the "User
  63. // cannot change password" flag for intra-forest user migrations
  64. //---------------------------------------------------------------------------
  65. STDMETHODIMP CSetPassword::PreProcessObject(
  66. IUnknown *pSource, //in- Pointer to the source AD object
  67. IUnknown *pTarget, //in- Pointer to the target AD object
  68. IUnknown *pMainSettings, //in- Varset filled with the settings supplied by user
  69. IUnknown **ppPropsToSet, //in,out - Varset filled with Prop-Value pairs that will be set
  70. // once all extension objects are executed.
  71. EAMAccountStats* pStats
  72. )
  73. {
  74. /* local variables */
  75. IVarSetPtr pVS = pMainSettings;
  76. CString sSAMName = L"";
  77. /* function body */
  78. _bstr_t sIntraforest = pVS->get(GET_BSTR(DCTVS_Options_IsIntraforest));
  79. _bstr_t sType = pVS->get(GET_BSTR(DCTVS_CopiedAccount_Type));
  80. _bstr_t sSrc = pVS->get(GET_BSTR(DCTVS_Options_SourceServer));
  81. _bstr_t sAccount = pVS->get(GET_BSTR(DCTVS_CopiedAccount_SourceSam));
  82. if ((!sIntraforest.length()) || (!sType.length()) ||
  83. (!sSrc.length()) || (!sAccount.length()))
  84. return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  85. sSAMName = (WCHAR*)sAccount;
  86. if (!UStrICmp((WCHAR*)sIntraforest,GET_STRING(IDS_YES)))
  87. {
  88. if (!UStrICmp((WCHAR*)sType,L"user") || !UStrICmp((WCHAR*)sType,L"inetOrgPerson"))
  89. {
  90. //record the password flags set on the source account
  91. RecordPwdFlags(sSrc, sAccount);
  92. //we only want to record the "User cannot change password" flag, so
  93. //clear the others
  94. m_bUMCPNLFlagSet = false;
  95. m_bPNEFlagSet = false;
  96. //store the flag setting in a map as a string
  97. if (m_bUCCPFlagSet)
  98. mUCCPMap.SetAt(sSAMName, L"TRUE");
  99. else
  100. mUCCPMap.SetAt(sSAMName, L"FALSE");
  101. }
  102. }
  103. //if previously migrated, store that time in a map
  104. _bstr_t sSrcDom = pVS->get(GET_BSTR(DCTVS_Options_SourceDomain));
  105. _bstr_t sTgtDom = pVS->get(GET_BSTR(DCTVS_Options_TargetDomain));
  106. IVarSetPtr pVSMig(__uuidof(VarSet));
  107. IUnknown * pUnk;
  108. IIManageDBPtr pDb(__uuidof(IManageDB));
  109. _variant_t varDate;
  110. pVSMig->QueryInterface(IID_IUnknown, (void**) &pUnk);
  111. HRESULT hrFind = pDb->raw_GetAMigratedObject(sAccount, sSrcDom, sTgtDom, &pUnk);
  112. pUnk->Release();
  113. //if migrated previously, store that time and date in a class map in string
  114. //format
  115. if (hrFind == S_OK)
  116. {
  117. //
  118. // if object's password was previously copied
  119. //
  120. long lStatusOld = pVSMig->get(L"MigratedObjects.status");
  121. if (lStatusOld & AR_Status_PasswordCopied)
  122. {
  123. //
  124. // set password copied flag in account status
  125. // as the account replicator does not preserve
  126. // status for objects being replaced
  127. //
  128. _bstr_t strStatus = GET_BSTR(DCTVS_CopiedAccount_Status);
  129. long lStatusNew = pVS->get(strStatus);
  130. lStatusNew |= AR_Status_PasswordCopied;
  131. pVS->put(strStatus, lStatusNew);
  132. //
  133. // retrieve time when object was migrated and save
  134. //
  135. varDate = pVSMig->get(L"MigratedObjects.Time");
  136. if (varDate.vt & VT_DATE)
  137. {
  138. COleDateTime aDate = varDate.date;
  139. //save the date and time in a string
  140. CString sDate = aDate.Format(L"%B %d, %Y %H:%M:%S");
  141. //store the date\time string in a map
  142. mMigTimeMap.SetAt(sSAMName, sDate);
  143. }
  144. }
  145. }
  146. return S_OK;
  147. }
  148. //---------------------------------------------------------------------------
  149. // ProcessObject : This method sets the password of the target object
  150. // by looking at the settings in the varset.
  151. //---------------------------------------------------------------------------
  152. STDMETHODIMP CSetPassword::ProcessObject(
  153. IUnknown *pSource, //in- Pointer to the source AD object
  154. IUnknown *pTarget, //in- Pointer to the target AD object
  155. IUnknown *pMainSettings, //in- Varset filled with the settings supplied by user
  156. IUnknown **ppPropsToSet, //in,out - Varset filled with Prop-Value pairs that will be set
  157. // once all extension objects are executed.
  158. EAMAccountStats* pStats
  159. )
  160. {
  161. _bstr_t sType;
  162. IVarSetPtr pVS = pMainSettings;
  163. WCHAR password[LEN_Path];
  164. bool bGenerate = false;
  165. bool bGenerated = false;
  166. DWORD dwMinUC = 0, dwMinLC = 0, dwMinDigits = 1, dwMinSpecial = 0, dwMaxAlpha = 10, dwMinLen = 4;
  167. _variant_t var;
  168. WCHAR fileName[LEN_Path];
  169. bool bCopiedPwd = false;
  170. HRESULT hrPwd = ERROR_SUCCESS;
  171. sType = pVS->get(GET_BSTR(DCTVS_CopiedAccount_Type));
  172. //
  173. // Group objects don't have a password so do not attempt to process.
  174. //
  175. if (UStrICmp((WCHAR*)sType, L"group") == 0)
  176. {
  177. return S_OK;
  178. }
  179. // Get the Error log filename from the Varset
  180. var = pVS->get(GET_BSTR(DCTVS_Options_Logfile));
  181. wcscpy(fileName, (WCHAR*)V_BSTR(&var));
  182. VariantInit(&var);
  183. // Open the error log
  184. err.LogOpen(fileName, 1);
  185. _bstr_t sSrc = pVS->get(GET_BSTR(DCTVS_Options_SourceServer));
  186. _bstr_t sSrcDom = pVS->get(GET_BSTR(DCTVS_Options_SourceDomain));
  187. _bstr_t sMach = pVS->get(GET_BSTR(DCTVS_Options_TargetServer));
  188. _bstr_t sTgtName = pVS->get(GET_BSTR(DCTVS_CopiedAccount_TargetSam));
  189. _bstr_t sAccount = pVS->get(GET_BSTR(DCTVS_CopiedAccount_SourceSam));
  190. _bstr_t sSkip = pVS->get(GET_BSTR(DCTVS_CopiedAccount_DoNotUpdatePassword));
  191. _bstr_t sTgtCN = pVS->get(GET_BSTR(DCTVS_CopiedAccount_TargetName));
  192. _bstr_t sIntraforest = pVS->get(GET_BSTR(DCTVS_Options_IsIntraforest));
  193. _bstr_t sCopyPwd = pVS->get(GET_BSTR(DCTVS_AccountOptions_CopyPasswords));
  194. _bstr_t sPwdDC = pVS->get(GET_BSTR(DCTVS_AccountOptions_PasswordDC));
  195. if ( !UStrICmp((WCHAR*)sSkip,(WCHAR*)sAccount) )
  196. {
  197. return S_OK;
  198. }
  199. //
  200. // Retrieve the operating system version of the password server. If it is
  201. // NT 4 or earlier then the flat or NetBIOS name of the target domain
  202. // controller must be specified.
  203. //
  204. if (m_nPasswordServerVersion == 0)
  205. {
  206. PWKSTA_INFO_100 pwiInfo;
  207. NET_API_STATUS nasStatus = NetWkstaGetInfo(sPwdDC, 100, (LPBYTE*)&pwiInfo);
  208. if (nasStatus == NERR_Success)
  209. {
  210. m_nPasswordServerVersion = pwiInfo->wki100_ver_major;
  211. NetApiBufferFree(pwiInfo);
  212. }
  213. else
  214. {
  215. err.SysMsgWrite(ErrW, nasStatus, DCT_MSG_PW_CANT_GET_PES_OS_VERSION_S, (WCHAR*)sPwdDC);
  216. }
  217. }
  218. if ((m_nPasswordServerVersion > 0) && (m_nPasswordServerVersion < 5))
  219. {
  220. sMach = pVS->get(GET_BSTR(DCTVS_Options_TargetServerFlat));
  221. }
  222. if ( sTgtCN.length() == 0 )
  223. sTgtCN = sTgtName;
  224. // strip off the CN= from the beginning of the name, if necessary
  225. if ( !UStrICmp(sTgtCN,"CN=",3) )
  226. {
  227. sTgtCN = _bstr_t(((WCHAR*)sTgtCN)+3);
  228. }
  229. dwMaxAlpha = (LONG)pVS->get(GET_BSTR(DCTVS_AccountOptions_PasswordPolicy_MaxConsecutiveAlpha));
  230. dwMinDigits = (LONG)pVS->get(GET_BSTR(DCTVS_AccountOptions_PasswordPolicy_MinDigit));
  231. dwMinLC = (LONG)pVS->get(GET_BSTR(DCTVS_AccountOptions_PasswordPolicy_MinLower));
  232. dwMinUC = (LONG)pVS->get(GET_BSTR(DCTVS_AccountOptions_PasswordPolicy_MinUpper));
  233. dwMinSpecial = (LONG)pVS->get(GET_BSTR(DCTVS_AccountOptions_PasswordPolicy_MinSpecial));
  234. dwMinLen = (LONG)pVS->get(GET_BSTR(DCTVS_AccountOptions_PasswordPolicy_MinLength));
  235. // if the values are all 0 s then we make up something
  236. if ( (dwMaxAlpha + dwMinDigits + dwMinLC + dwMinUC + dwMinSpecial) == 0 )
  237. {
  238. dwMinDigits = 3;
  239. dwMinSpecial = 3;
  240. dwMinUC = 3;
  241. dwMinLC = 3;
  242. }
  243. //for intra-forest migration of a user, reset the user's
  244. //original "User cannot change password" flag, whose value is
  245. //stored in the map as a string
  246. if ( ! UStrICmp((WCHAR*)sIntraforest,GET_STRING(IDS_YES)) )
  247. {
  248. if ( !UStrICmp((WCHAR*)sType,L"user") || !UStrICmp((WCHAR*)sType,L"inetOrgPerson") )
  249. {
  250. //get the UCCP string from the map
  251. CString sFlag;
  252. if (mUCCPMap.Lookup((WCHAR*)sAccount, sFlag))
  253. {
  254. if (sFlag == L"TRUE")
  255. m_bUCCPFlagSet = true;
  256. else
  257. m_bUCCPFlagSet = false;
  258. }//end if found in map
  259. else
  260. m_bUCCPFlagSet = false;
  261. m_bUMCPNLFlagSet = false;
  262. m_bPNEFlagSet = false;
  263. ResetPwdFlags(pTarget, sMach, sTgtName);
  264. return S_OK;
  265. }
  266. }
  267. // Set the password for this account.
  268. if ( (_wcsicmp((WCHAR*)sType,L"user") == 0) || (_wcsicmp((WCHAR*)sType,L"inetOrgPerson") == 0) || (_wcsicmp((WCHAR*)sType, L"computer") == 0) )
  269. {
  270. if ( !_wcsicmp((WCHAR*)sType,L"user") || !_wcsicmp((WCHAR*)sType,L"inetOrgPerson") )
  271. {
  272. //we will not migrate passwords if replacing and password reuse policy on the target is 2 or greater
  273. UINT uMsgId = 0;
  274. if (!CanCopyPassword(pVS, sSrc, sAccount, uMsgId))
  275. {
  276. switch (uMsgId)
  277. {
  278. // The source account password has not changed since the last migration.
  279. case 0:
  280. err.MsgWrite(0, DCT_MSG_PW_COPY_NOT_TRIED_S, (WCHAR*)sTgtCN);
  281. break;
  282. // The 'User Must Change Password at Next Logon' is selected for this user.
  283. case DCT_MSG_PW_COPY_NOT_TRIED_USER_MUST_CHANGE_S:
  284. err.MsgWrite(0, DCT_MSG_PW_COPY_NOT_TRIED_USER_MUST_CHANGE_S, (WCHAR*)sTgtCN);
  285. break;
  286. // Unable to determine the password age as the password last
  287. // changed time is later than the current time on this machine.
  288. case DCT_MSG_PW_COPY_NOT_TRIED_TIMEDIFF_SS:
  289. err.MsgWrite(0, DCT_MSG_PW_COPY_NOT_TRIED_TIMEDIFF_SS, (WCHAR*)sTgtCN, (WCHAR*)sMach);
  290. break;
  291. // This case should not occur but generate default message in case.
  292. default:
  293. _ASSERT(FALSE);
  294. err.MsgWrite(0, DCT_MSG_PW_COPY_NOT_TRIED_S, (WCHAR*)sTgtCN);
  295. break;
  296. }
  297. return S_OK;
  298. }
  299. _bstr_t bstrGenerate = pVS->get(GET_BSTR(DCTVS_AccountOptions_GenerateStrongPasswords));
  300. if (bstrGenerate == GET_BSTR(IDS_YES))
  301. {
  302. bGenerate = true;
  303. }
  304. if (bGenerate)
  305. {
  306. // generate a strong password
  307. DWORD dwError = EaPasswordGenerate(dwMinUC, dwMinLC, dwMinDigits, dwMinSpecial, dwMaxAlpha, dwMinLen, password, LEN_Path);
  308. bGenerated = ( dwError == ERROR_SUCCESS );
  309. if ( !bGenerated )
  310. {
  311. //
  312. // If unable to generate password then mark account to be disabled.
  313. //
  314. MarkAccountError(pVS);
  315. if (pStats != NULL)
  316. pStats->errors.users++;
  317. err.SysMsgWrite(ErrE, dwError, DCT_MSG_PW_STRONG_GENERATE_FAILED_S, (WCHAR*)sTgtCN);
  318. err.LogClose();
  319. return S_OK;
  320. }
  321. // ensure that the password is NULL terminated
  322. password[14] = 0;
  323. }
  324. else
  325. {
  326. // set the password to the first 14 characters of the username
  327. wcsncpy(password,(WCHAR*)(sTgtName),15);
  328. // Convert the password to lower case.
  329. // ensure that the password is NULL terminated
  330. password[14] = 0;
  331. }
  332. }
  333. else
  334. {
  335. // computer, set the password to the first 14 characters of the computer name, in lower case,
  336. // without the trailing $
  337. UStrCpy(password, (WCHAR*)sTgtName, DIM(password));
  338. if ( password[UStrLen(password) - 1] == L'$' )
  339. password[UStrLen(password) - 1] = L'\0'; // remove trailing $ from machine name
  340. password[14] = L'\0'; // truncate to max password length of 14
  341. // Convert the password to lower case.
  342. for ( DWORD i = 0; i < wcslen(password); i++ )
  343. password[i] = towlower(password[i]);
  344. }
  345. // We are going to use the Net API to set the password.
  346. USER_INFO_1003 pInfo;
  347. DWORD pDw;
  348. WCHAR server[MAX_PATH];
  349. bool bFailedCopyPwd = false;
  350. //place the new password in the info structure
  351. pInfo.usri1003_password = password;
  352. long rc = NetUserSetInfo((WCHAR*)sMach,
  353. (WCHAR*)sTgtName, 1003, (LPBYTE) &pInfo, &pDw);
  354. if ( rc != 0 )
  355. {
  356. if ( bGenerated )
  357. {
  358. //
  359. // If unable to set password then mark account to be disabled.
  360. //
  361. MarkAccountError(pVS);
  362. if (pStats != NULL)
  363. pStats->errors.users++;
  364. err.SysMsgWrite(ErrE,rc,DCT_MSG_PW_GENERATE_FAILED_S,(WCHAR*)sTgtCN);
  365. err.LogClose();
  366. return S_OK;
  367. }
  368. else
  369. {
  370. if (pStats != NULL)
  371. pStats->warnings.users++;
  372. err.SysMsgWrite(ErrW,rc,DCT_MSG_FAILED_SET_PASSWORD_TO_USERNAME_SD,(WCHAR*)sTgtCN,rc);
  373. if ( rc == NERR_PasswordTooShort )
  374. {
  375. // try to generate a password
  376. DWORD dwError = EaPasswordGenerate(dwMinUC, dwMinLC, dwMinDigits, dwMinSpecial, dwMaxAlpha, dwMinLen, password, LEN_Path);
  377. if (dwError != ERROR_SUCCESS)
  378. {
  379. //
  380. // If unable to generate password then mark account to be disabled.
  381. //
  382. MarkAccountError(pVS);
  383. if (pStats != NULL)
  384. pStats->errors.users++;
  385. err.SysMsgWrite(ErrE, dwError, DCT_MSG_PW_STRONG_GENERATE_FAILED_S, (WCHAR*)sTgtCN);
  386. err.LogClose();
  387. return S_OK;
  388. }
  389. rc = NetUserSetInfo((WCHAR*)sMach,(WCHAR*)sTgtName,1003,(LPBYTE)&pInfo,&pDw);
  390. if ( rc )
  391. {
  392. //
  393. // If unable to set password then mark account to be disabled.
  394. //
  395. MarkAccountError(pVS);
  396. if (pStats != NULL)
  397. pStats->errors.users++;
  398. err.SysMsgWrite(ErrE,rc,DCT_MSG_PW_GENERATE_FAILED_S,(WCHAR*)sTgtCN);
  399. err.LogClose();
  400. return S_OK;
  401. }
  402. else //else complex password generated, if requested to copy password
  403. { //we now try to do that and only log that complex pwd generated if copy fails
  404. //if we are migrating the user's password, then set it here
  405. if ( !UStrICmp((WCHAR*)sCopyPwd,GET_STRING(IDS_YES)) )
  406. {
  407. //record the password flags set on the source account
  408. RecordPwdFlags(sSrc, sAccount);
  409. //clear the "User cannot change password" flag if it is set
  410. ClearUserCanChangePwdFlag(sMach, sTgtName);
  411. //set the change password flag to get past the minimum pwd age policy
  412. SetUserMustChangePwdFlag(pTarget);
  413. //prepare the server name
  414. server[0] = L'\\';
  415. server[1] = L'\\';
  416. UStrCpy(server+2,(WCHAR*)sPwdDC);
  417. //call the member function to copy the password. If success, set flag, else
  418. //failed, log the generated password message
  419. if ((hrPwd = CopyPassword(_bstr_t(server), sMach, sAccount, sTgtName, _bstr_t(password), pStats)) == ERROR_SUCCESS)
  420. {
  421. err.MsgWrite(0,DCT_MSG_PWCOPIED_S,(WCHAR*)sTgtCN);
  422. bCopiedPwd = true;
  423. //reset the password flags as were on the source account
  424. ResetPwdFlags(pTarget, sMach, sTgtName);
  425. }
  426. else
  427. {
  428. bFailedCopyPwd = true;
  429. }
  430. }//end if migrate password
  431. else //else not copy password, so post the complex password generated message
  432. err.MsgWrite(0,DCT_MSG_PWGENERATED_S,(WCHAR*)sTgtCN);
  433. }
  434. }
  435. }
  436. }
  437. else //else success
  438. {
  439. //if complex password generated, if requested to copy password
  440. //we now try to do that and only log that complex pwd generated if copy fails
  441. if ( bGenerated )
  442. {
  443. //if we are migrating the user's password, then set it here
  444. if ( !UStrICmp((WCHAR*)sCopyPwd,GET_STRING(IDS_YES)) )
  445. {
  446. //record the password flags set on the source account
  447. RecordPwdFlags(sSrc, sAccount);
  448. //clear the "User cannot change password" flag if it is set
  449. ClearUserCanChangePwdFlag(sMach, sTgtName);
  450. //set the change password flag to get past the minimum pwd age policy
  451. SetUserMustChangePwdFlag(pTarget);
  452. //prepare the server name
  453. server[0] = L'\\';
  454. server[1] = L'\\';
  455. UStrCpy(server+2,(WCHAR*)sPwdDC);
  456. //call the member function to copy the password. If success, set flag, else
  457. //failed, log the generated password message
  458. if ((hrPwd = CopyPassword(_bstr_t(server), sMach, sAccount, sTgtName, _bstr_t(password), pStats)) == ERROR_SUCCESS)
  459. {
  460. err.MsgWrite(0,DCT_MSG_PWCOPIED_S,(WCHAR*)sTgtCN);
  461. bCopiedPwd = true;
  462. //reset the password flags as were on the source account
  463. ResetPwdFlags(pTarget, sMach, sTgtName);
  464. }
  465. else
  466. {
  467. bFailedCopyPwd = true;
  468. }
  469. }//end if migrate password
  470. else //else not copy password, so post the complex password generated message
  471. err.MsgWrite(0,DCT_MSG_PWGENERATED_S,(WCHAR*)sTgtCN);
  472. }
  473. else
  474. {
  475. err.MsgWrite(0,DCT_MSG_SET_PASSWORD_TO_USERNAME_S,(WCHAR*)sTgtCN);
  476. }
  477. }
  478. //if user being migrated and that user's password was not copied, write
  479. //password to the password file
  480. if (((_wcsicmp((WCHAR*)sType,L"user") == 0) || (_wcsicmp((WCHAR*)sType,L"inetOrgPerson") == 0)) && (bCopiedPwd == false))
  481. {
  482. //
  483. // Open password log file if it has not been already opened.
  484. //
  485. if (!m_bTriedToOpenFile)
  486. {
  487. m_bTriedToOpenFile = true;
  488. _bstr_t strPasswordFile = pVS->get(GET_BSTR(DCTVS_AccountOptions_PasswordFile));
  489. if (m_passwordLog.LogOpen(strPasswordFile) == FALSE)
  490. {
  491. if (pStats != NULL)
  492. {
  493. pStats->errors.users++;
  494. }
  495. }
  496. }
  497. // Log the new password in the password log.
  498. if ( m_passwordLog.IsOpen() )
  499. {
  500. m_passwordLog.MsgWrite(L"%ls,%ls",(WCHAR*)(pVS->get(GET_BSTR(DCTVS_CopiedAccount_TargetSam)).bstrVal),password);
  501. }
  502. }//end if migrating user
  503. }//end if migrating user or computer
  504. //change password flags on the account if we did not copy the password
  505. if (( pTarget ) && (!bCopiedPwd))
  506. {
  507. // We want to clear "user cannot change password" and "password never expire flag"
  508. USER_INFO_1008 usr1008;
  509. USER_INFO_20 * usr20;
  510. DWORD errParam = 0;
  511. DWORD rc = NetUserGetInfo((WCHAR*) sSrc, (WCHAR*)sAccount, 20, (LPBYTE *)&usr20);
  512. _bstr_t strDisable = pVS->get(GET_BSTR(DCTVS_AccountOptions_DisableCopiedAccounts));
  513. _bstr_t strSame = pVS->get(GET_BSTR(DCTVS_AccountOptions_TgtStateSameAsSrc));
  514. long val = pVS->get(GET_WSTR(DCTVS_CopiedAccount_UserFlags));
  515. BOOL bDisable = FALSE;
  516. BOOL bSame = FALSE;
  517. if ( ! UStrICmp(strDisable,GET_STRING(IDS_YES)) )
  518. bDisable = TRUE;
  519. if ( ! UStrICmp(strSame,GET_STRING(IDS_YES)) )
  520. bSame = TRUE;
  521. if ( !rc )
  522. {
  523. usr1008.usri1008_flags = usr20->usri20_flags & ~UF_DONT_EXPIRE_PASSWD;
  524. // we won't turn off the user cannot change password, we will just expire the password below.
  525. // This will lock out any account with user cannot change password set, and the admin will
  526. // have to manually unlock the account.
  527. //usr1008.usri1008_flags &= ~UF_PASSWD_CANT_CHANGE;
  528. // for the computer account we need to set the UF_PASSWD_NOTREQD
  529. if ( !_wcsicmp((WCHAR*)sType,L"computer") )
  530. {
  531. usr1008.usri1008_flags |= UF_PASSWD_NOTREQD;
  532. // make sure the disable state for the computer account is the same as for the source computer
  533. if ( usr20->usri20_flags & UF_ACCOUNTDISABLE )
  534. {
  535. usr1008.usri1008_flags |= UF_ACCOUNTDISABLE;
  536. }
  537. else
  538. {
  539. usr1008.usri1008_flags &= ~UF_ACCOUNTDISABLE;
  540. }
  541. }
  542. else
  543. {
  544. // for user accounts, the disable flag is set based on the disable option
  545. // make sure that the disable flag is set properly!
  546. if ((bDisable) || (bSame && (val & UF_ACCOUNTDISABLE)))
  547. {
  548. usr1008.usri1008_flags |= UF_ACCOUNTDISABLE;
  549. }
  550. else
  551. {
  552. usr1008.usri1008_flags &= ~UF_ACCOUNTDISABLE;
  553. }
  554. }
  555. NetUserSetInfo((WCHAR*) sMach, (WCHAR*) sTgtName, 1008, (LPBYTE)&usr1008, &errParam);
  556. NetApiBufferFree(usr20);
  557. }
  558. else
  559. {
  560. MarkAccountError(pVS);
  561. if (pStats != NULL)
  562. pStats->errors.users++;
  563. err.SysMsgWrite(ErrE, rc, DCT_MSG_PW_UNABLE_RETRIEVE_FLAGS_SS, (WCHAR*)sTgtName, (WCHAR*)sAccount);
  564. }
  565. // Require the users to change the password at next logon since we created new passwords for them
  566. SetUserMustChangePwdFlag(pTarget);
  567. }
  568. //
  569. // set or clear password copied flag in the persisted object status
  570. // this flag is used to determine whether the password was previously copied
  571. //
  572. _bstr_t strStatus = GET_BSTR(DCTVS_CopiedAccount_Status);
  573. long lStatus = pVS->get(strStatus);
  574. if (bCopiedPwd)
  575. {
  576. lStatus |= AR_Status_PasswordCopied;
  577. }
  578. else
  579. {
  580. lStatus &= ~AR_Status_PasswordCopied;
  581. }
  582. pVS->put(strStatus, lStatus);
  583. err.LogClose();
  584. SecureZeroMemory(password, sizeof(password));
  585. return S_OK;
  586. }
  587. //---------------------------------------------------------------------------
  588. // ProcessUndo : Since we cant undo the password setting we will ignore this.
  589. //---------------------------------------------------------------------------
  590. STDMETHODIMP CSetPassword::ProcessUndo(
  591. IUnknown *pSource, //in- Pointer to the source AD object
  592. IUnknown *pTarget, //in- Pointer to the target AD object
  593. IUnknown *pMainSettings, //in- Varset filled with the settings supplied by user
  594. IUnknown **ppPropsToSet, //in,out - Varset filled with Prop-Value pairs that will be set
  595. // once all extension objects are executed.
  596. EAMAccountStats* pStats
  597. )
  598. {
  599. _bstr_t sType;
  600. _bstr_t sSam;
  601. _bstr_t sServer;
  602. _bstr_t sIntraforest;
  603. WCHAR password[LEN_Path];
  604. DWORD rc = 0;
  605. DWORD pDw = 0;
  606. IVarSetPtr pVs = pMainSettings;
  607. sIntraforest = pVs->get(GET_BSTR(DCTVS_Options_IsIntraforest));
  608. sSam = pVs->get(GET_BSTR(DCTVS_CopiedAccount_SourceSam));
  609. sType = pVs->get(GET_BSTR(DCTVS_CopiedAccount_Type));
  610. sServer = pVs->get(GET_BSTR(DCTVS_Options_TargetServer));
  611. if ((!sIntraforest.length()) || (!sType.length()) ||
  612. (!sSam.length()) || (!sServer.length()))
  613. return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  614. //if this is user intra-forest, call preprocess the first time in and
  615. //precess the second
  616. if (!UStrICmp((WCHAR*)sIntraforest,GET_STRING(IDS_YES)))
  617. {
  618. if (!UStrICmp((WCHAR*)sType,L"user") || !UStrICmp((WCHAR*)sType,L"inetOrgPerson"))
  619. {
  620. //see if this SAM is in the undone list (every entry in the list has a ","
  621. //on each side of it
  622. _bstr_t sTemp = _bstr_t(L",") + sSam + _bstr_t(L",");
  623. //if found, that means it is post migration, so call process
  624. if (wcsstr((PCWSTR)m_sUndoneUsers, sTemp))
  625. {
  626. ProcessObject(pSource, pTarget, pMainSettings, ppPropsToSet, pStats);
  627. }
  628. else //else pre-migration, so add to the list and call preprocess
  629. {
  630. //add to the list with an ending ","
  631. m_sUndoneUsers += sSam;
  632. m_sUndoneUsers += L",";
  633. PreProcessObject(pSource, pTarget, pMainSettings, ppPropsToSet, pStats);
  634. }
  635. }
  636. }
  637. if (!_wcsicmp((WCHAR*) sType, L"computer"))
  638. {
  639. USER_INFO_1003 buf;
  640. rc = 0;
  641. if ( !rc )
  642. {
  643. // Create a lower case password from the sam account name. Do not include the trailing $.
  644. // Password to be maximum of 14 characters.
  645. UStrCpy(password, (WCHAR*)sSam, DIM(password));
  646. if ( password[UStrLen(password) - 1] == L'$' )
  647. password[UStrLen(password) - 1] = L'\0'; // remove trailing $ from machine name
  648. password[14] = L'\0'; // truncate to max password length of 14
  649. for ( DWORD i = 0; i < wcslen(password); i++ )
  650. password[i] = towlower(password[i]);
  651. buf.usri1003_password = password;
  652. rc = NetUserSetInfo((WCHAR*) sServer, (WCHAR*) sSam, 1003, (LPBYTE) &buf, &pDw);
  653. if ( rc == 2221 )
  654. {
  655. WCHAR sam[300];
  656. UStrCpy(sam,(WCHAR*)sSam);
  657. // remove the $ from the sam account name
  658. sam[UStrLen(sam)-1] = 0;
  659. rc = NetUserSetInfo((WCHAR*) sServer, sam, 1003, (LPBYTE) &buf, &pDw);
  660. }
  661. }
  662. }
  663. return HRESULT_FROM_WIN32(rc);
  664. }
  665. BOOL // ret - TRUE if directory found
  666. CSetPassword::GetDirectory(
  667. WCHAR * filename // out - string buffer to store directory name
  668. )
  669. {
  670. DWORD rc = 0;
  671. BOOL bFound = FALSE;
  672. TRegKey key;
  673. rc = key.OpenRead(GET_STRING(IDS_HKLM_DomainAdmin_Key),HKEY_LOCAL_MACHINE);
  674. if ( ! rc )
  675. {
  676. rc = key.ValueGetStr(L"Directory",filename,MAX_PATH);
  677. if ( ! rc )
  678. {
  679. if ( *filename )
  680. bFound = TRUE;
  681. }
  682. }
  683. key.Close();
  684. return bFound;
  685. }
  686. //---------------------------------------------------------------------------
  687. // IsValidPassword Method
  688. //
  689. // This method validates a password by converting the Unicode password string
  690. // to an ANSI string.
  691. //
  692. // If any Unicode characters cannot be directly translated to an ANSI
  693. // character and a default character must be used then the password is
  694. // invalid.
  695. //
  696. // If any Unicode characters translate to multi-byte characters then the
  697. // password is invalid.
  698. //---------------------------------------------------------------------------
  699. #ifndef WC_NO_BEST_FIT_CHARS
  700. #define WC_NO_BEST_FIT_CHARS 0x00000400
  701. #endif
  702. bool CSetPassword::IsValidPassword(LPCWSTR pwszPassword)
  703. {
  704. bool bValid = false;
  705. BOOL bUsedDefaultChar;
  706. CHAR szPassword[PWLEN + 1];
  707. // convert Unicode string to ANSI string
  708. int cch = WideCharToMultiByte(
  709. CP_ACP, // use system ANSI code page
  710. WC_NO_BEST_FIT_CHARS, // do not use best fit characters
  711. pwszPassword,
  712. -1, // assume password string is zero terminated
  713. szPassword,
  714. sizeof (szPassword),
  715. NULL,
  716. &bUsedDefaultChar // will be true if default character used
  717. );
  718. // if no error occurred
  719. if (cch > 0)
  720. {
  721. // if default character was not used
  722. if (bUsedDefaultChar == FALSE)
  723. {
  724. CPINFOEX cpie;
  725. GetCPInfoEx(CP_ACP, 0, &cpie);
  726. // if code page defines a SBCS then password is valid
  727. // otherwise code page defines a DBCS and the password
  728. // must be searched for a multi-byte character
  729. if ((cpie.LeadByte[0] == 0) && (cpie.LeadByte[1] == 0))
  730. {
  731. bValid = true;
  732. }
  733. else
  734. {
  735. // search for multi-byte character
  736. bool bLeadByteFound = false;
  737. for (int ich = 0; ich < cch; ich++)
  738. {
  739. if (IsDBCSLeadByteEx(CP_ACP, szPassword[ich]))
  740. {
  741. bLeadByteFound = true;
  742. break;
  743. }
  744. }
  745. // if no multi-byte character found
  746. // then password is valid
  747. if (!bLeadByteFound)
  748. {
  749. bValid = true;
  750. }
  751. }
  752. }
  753. }
  754. SecureZeroMemory(szPassword, sizeof(szPassword));
  755. return bValid;
  756. }
  757. /*********************************************************************
  758. * *
  759. * Written by: Paul Thompson *
  760. * Date: 4 SEPT 2000 *
  761. * *
  762. * This function is responsible for copying the user's password *
  763. * in the source domain to its new account in the target domain. *
  764. * We use the Password Migration COM Wrapper to first check the *
  765. * configuration and establish a session with the given Password *
  766. * Export server. Then we copy the password. The configuration *
  767. * check, which establishes a session for this series of operations, *
  768. * is only done once per set of accounts. *
  769. * *
  770. *********************************************************************/
  771. //BEGIN CopyPassword
  772. HRESULT CSetPassword::CopyPassword(_bstr_t srcServer, _bstr_t tgtServer, _bstr_t srcName,
  773. _bstr_t tgtName, _bstr_t password, EAMAccountStats* pStats)
  774. {
  775. /* local variables */
  776. HRESULT hr = S_OK;
  777. /* function body */
  778. if (m_pPwdMig == NULL)
  779. {
  780. hr = m_pPwdMig.CreateInstance(__uuidof(PasswordMigration));
  781. if (FAILED(hr))
  782. {
  783. return hr;
  784. }
  785. }
  786. //
  787. // If session has not been established or needs to be re-established
  788. // then establish a session with the password export server. Note that
  789. // EstablishSession will un-establish an existing session first before
  790. // re-establishing a new session.
  791. //
  792. if (m_bEstablishedSession == false)
  793. {
  794. hr = m_pPwdMig->raw_EstablishSession(srcServer, tgtServer);
  795. if (SUCCEEDED(hr))
  796. {
  797. m_bEstablishedSession = true;
  798. }
  799. }
  800. //
  801. // Copy the password.
  802. // If unsuccessful then force session to be re-established.
  803. //
  804. if (SUCCEEDED(hr))
  805. {
  806. hr = m_pPwdMig->raw_CopyPassword(srcName, tgtName, password);
  807. if (FAILED(hr))
  808. {
  809. m_bEstablishedSession = false;
  810. }
  811. }
  812. //
  813. // The only time copy password can fail with ERROR_PASSWORD_RESTRICTION is when password history
  814. // has been enabled on the target domain and ADMT is attempting to recopy a password that has
  815. // been previously copied to the target. This scenario can happen if the adminstrator migrates
  816. // users with the copy password option, then migrates users with the generate complex password
  817. // option and then finally migrates users with the copy password option again.
  818. //
  819. // The only way out of this situation is to simply use the brute force technique of setting
  820. // the user's password to a series of complex passwords in order to push any known passwords
  821. // out of the password history before finally attempting to copy the source password again.
  822. //
  823. if (hr == HRESULT_FROM_WIN32(ERROR_PASSWORD_RESTRICTION))
  824. {
  825. //
  826. // This solution uses the set password API to push the new passwords into the
  827. // password history list. The set password API is preferred because the new
  828. // password is not subject to any password policies in effect. Note that the
  829. // set password API only works if the caller has adminstrator privileges.
  830. //
  831. WCHAR szPasswordOld[LEN_Path];
  832. WCHAR szPasswordNew[LEN_Path];
  833. USER_INFO_1003 uiInfo = { szPasswordNew };
  834. wcscpy(szPasswordOld, password);
  835. for (long lCount = m_lPwdHistoryLength; lCount > 0; lCount--)
  836. {
  837. if (EaPasswordGenerate(5, 5, 2, 2, 10, 14, szPasswordNew, LEN_Path) == ERROR_SUCCESS)
  838. {
  839. NET_API_STATUS nasStatus = NetUserSetInfo(tgtServer, tgtName, 1003, (LPBYTE)&uiInfo, NULL);
  840. if (nasStatus == ERROR_SUCCESS)
  841. {
  842. wcscpy(szPasswordOld, szPasswordNew);
  843. }
  844. else
  845. {
  846. break;
  847. }
  848. }
  849. }
  850. //
  851. // Must expire password so that the password export server may change password otherwise
  852. // the minimum password age policy may prevent the password change.
  853. //
  854. // Attempt to retrieve user information at level 4 which is only supported on .NET and later
  855. // but note that level 3 may not be supported on a .NET server when the SAM SID compatibility
  856. // mode is SAM_SID_COMPATIBILITY_STRICT. If failed to retrieve level 4 then retrieve level 3.
  857. //
  858. // Note that an error attempting to expire password is ignored as the copy password call will
  859. // fail if the minimum password age policy is in effect but will succeed otherwise.
  860. //
  861. PUSER_INFO_4 pui4 = NULL;
  862. NET_API_STATUS nasStatus = NetUserGetInfo(tgtServer, tgtName, 4, (LPBYTE*)&pui4);
  863. if (nasStatus == ERROR_SUCCESS)
  864. {
  865. pui4->usri4_password_expired = TRUE;
  866. NetUserSetInfo(tgtServer, tgtName, 4, (LPBYTE)pui4, NULL);
  867. NetApiBufferFree(pui4);
  868. }
  869. else
  870. {
  871. if (nasStatus == ERROR_INVALID_LEVEL)
  872. {
  873. PUSER_INFO_3 pui3 = NULL;
  874. nasStatus = NetUserGetInfo(tgtServer, tgtName, 3, (LPBYTE*)&pui3);
  875. if (nasStatus == ERROR_SUCCESS)
  876. {
  877. pui3->usri3_password_expired = TRUE;
  878. NetUserSetInfo(tgtServer, tgtName, 3, (LPBYTE)pui3, NULL);
  879. NetApiBufferFree(pui3);
  880. }
  881. }
  882. }
  883. //
  884. // Attempt to copy password.
  885. //
  886. hr = m_pPwdMig->raw_CopyPassword(srcName, tgtName, szPasswordOld);
  887. SecureZeroMemory(szPasswordOld, sizeof(szPasswordOld));
  888. SecureZeroMemory(szPasswordNew, sizeof(szPasswordNew));
  889. }
  890. //if either failed, print warning in the migration.log
  891. if (FAILED(hr))
  892. {
  893. IErrorInfoPtr pErrorInfo = NULL;
  894. BSTR bstrDescription;
  895. _bstr_t sText = GET_BSTR(IDS_Unspecified_Failure);
  896. //get the rich error information on the failure
  897. if (SUCCEEDED(GetErrorInfo(0, &pErrorInfo)))
  898. {
  899. HRESULT hrTmp = pErrorInfo->GetDescription(&bstrDescription);
  900. if (SUCCEEDED(hrTmp)) //if got rich error info, use it
  901. sText = _bstr_t(bstrDescription, false);
  902. }
  903. //print message in the log
  904. if (pStats != NULL)
  905. pStats->warnings.users++;
  906. err.MsgWrite(ErrW,DCT_MSG_PW_COPY_FAILED_S,(WCHAR*)tgtName, (WCHAR*)sText);
  907. }
  908. return hr;
  909. }
  910. //END CopyPassword
  911. /*********************************************************************
  912. * *
  913. * Written by: Paul Thompson *
  914. * Date: 5 DEC 2000 *
  915. * *
  916. * This function is responsible for setting the "User must change*
  917. * password at next logon" flag for a given user. We use this prior *
  918. * to copying a user's password and after we have just set it to a *
  919. * new complex password so that we get around the minimum password *
  920. * age policy for the target domain. *
  921. * *
  922. *********************************************************************/
  923. //BEGIN SetUserMustChangePwdFlag
  924. void CSetPassword::SetUserMustChangePwdFlag(IUnknown *pTarget)
  925. {
  926. /* local variables */
  927. IADs * pAds = NULL;
  928. /* function body */
  929. //set the new pwdLastSet value
  930. HRESULT hr = pTarget->QueryInterface(IID_IADs, (void**) &pAds);
  931. if ( SUCCEEDED(hr) )
  932. {
  933. // Require the users to change the password at next logon since we created new passwords for them
  934. VARIANT var;
  935. VariantInit(&var);
  936. V_I4(&var)=0;
  937. V_VT(&var)=VT_I4;
  938. hr = pAds->Put(L"pwdLastSet",var);
  939. hr = pAds->SetInfo();
  940. VariantClear(&var);
  941. if ( pAds ) pAds->Release();
  942. }
  943. }
  944. //END SetUserMustChangePwdFlag
  945. /*********************************************************************
  946. * *
  947. * Written by: Paul Thompson *
  948. * Date: 11 JAN 2001 *
  949. * *
  950. * This function is responsible for setting and clearing the *
  951. * "User cannot change password" flag for a given user, if it was *
  952. * originally set. *
  953. * *
  954. *********************************************************************/
  955. //BEGIN ClearUserCanChangePwdFlag
  956. void CSetPassword::ClearUserCanChangePwdFlag(LPCWSTR pwszMach, LPCWSTR pwszUser)
  957. {
  958. /* local variables */
  959. USER_INFO_3 * pInfo;
  960. DWORD pDw;
  961. long rc;
  962. /* function body */
  963. //get the current flag info for this user
  964. rc = NetUserGetInfo(pwszMach, pwszUser, 3, (LPBYTE *)&pInfo);
  965. if (rc == 0)
  966. {
  967. //clear the "User cannot change password" flag if it is set
  968. if (pInfo->usri3_flags & UF_PASSWD_CANT_CHANGE)
  969. {
  970. pInfo->usri3_flags &= !(UF_PASSWD_CANT_CHANGE);
  971. NetUserSetInfo(pwszMach, pwszUser, 3, (LPBYTE)pInfo, &pDw);
  972. }
  973. NetApiBufferFree((LPVOID) pInfo);
  974. }
  975. }
  976. //END ClearUserCanChangePwdFlag
  977. /*********************************************************************
  978. * *
  979. * Written by: Paul Thompson *
  980. * Date: 22 JAN 2001 *
  981. * *
  982. * This function is responsible for recording the password flags *
  983. * from a given user's source domain account. *
  984. * *
  985. *********************************************************************/
  986. //BEGIN RecordPwdFlags
  987. void CSetPassword::RecordPwdFlags(LPCWSTR pwszMach, LPCWSTR pwszUser)
  988. {
  989. /* local variables */
  990. USER_INFO_3 * pInfo;
  991. long rc;
  992. /* function body */
  993. //get the user password flags
  994. rc = NetUserGetInfo(pwszMach, pwszUser, 3, (LPBYTE *)&pInfo);
  995. if (rc == 0)
  996. {
  997. //record whether the "User cannot change password" flag is set
  998. if (pInfo->usri3_flags & UF_PASSWD_CANT_CHANGE)
  999. m_bUCCPFlagSet = true;//store whether the flag was set
  1000. else
  1001. m_bUCCPFlagSet = false;
  1002. //record whether the "Password never expires" flag is set
  1003. if (pInfo->usri3_flags & UF_DONT_EXPIRE_PASSWD)
  1004. m_bPNEFlagSet = true;//store whether the flag was set
  1005. else
  1006. m_bPNEFlagSet = false;
  1007. //record whether the "User must change password at next logon" flag is set
  1008. if (pInfo->usri3_password_expired)
  1009. m_bUMCPNLFlagSet = true;//store whether the flag was set
  1010. else
  1011. m_bUMCPNLFlagSet = false;
  1012. NetApiBufferFree((LPVOID) pInfo);
  1013. }
  1014. }
  1015. //END RecordPwdFlags
  1016. /*********************************************************************
  1017. * *
  1018. * Written by: Paul Thompson *
  1019. * Date: 22 JAN 2001 *
  1020. * *
  1021. * This function is responsible for recording the password flags *
  1022. * from a given user's source domain account. *
  1023. * *
  1024. *********************************************************************/
  1025. //BEGIN ResetPwdFlags
  1026. void CSetPassword::ResetPwdFlags(IUnknown *pTarget, LPCWSTR pwszMach, LPCWSTR pwszUser)
  1027. {
  1028. /* local variables */
  1029. USER_INFO_3 * pInfo = NULL;
  1030. DWORD pDw;
  1031. long rc;
  1032. /* function body */
  1033. //if the "User cannot change password" or "Password never expires"
  1034. //flag was original set, reset it
  1035. if ((m_bUCCPFlagSet) || (m_bPNEFlagSet))
  1036. {
  1037. //get the current flag info for this user
  1038. rc = NetUserGetInfo(pwszMach, pwszUser, 3, (LPBYTE *)&pInfo);
  1039. if (rc == 0)
  1040. {
  1041. if (m_bUCCPFlagSet)
  1042. pInfo->usri3_flags |= UF_PASSWD_CANT_CHANGE;
  1043. if (m_bPNEFlagSet)
  1044. pInfo->usri3_flags |= UF_DONT_EXPIRE_PASSWD;
  1045. NetUserSetInfo(pwszMach, pwszUser, 3, (LPBYTE)pInfo, &pDw);
  1046. NetApiBufferFree((LPVOID) pInfo);
  1047. }
  1048. }
  1049. //if the "User must change password at next logon" flag was original set, reset it
  1050. if (m_bUMCPNLFlagSet)
  1051. SetUserMustChangePwdFlag(pTarget);
  1052. }
  1053. //END ResetPwdFlags
  1054. /*********************************************************************
  1055. * *
  1056. * Written by: Paul Thompson *
  1057. * Date: 7 MAY 2001 *
  1058. * *
  1059. * This function is responsible for determining if we will indeed*
  1060. * set the password. If the user is being re-migrated, copy password*
  1061. * was selected, the password reuse policy on the target domain is *
  1062. * greater than 1, and the password on the source object has not been*
  1063. * changed since the last migration of that user, then we will not *
  1064. * touch the password on the existing object on the target domain. *
  1065. * *
  1066. *********************************************************************/
  1067. //BEGIN CanCopyPassword
  1068. BOOL CSetPassword::CanCopyPassword(IVarSet * pVarSet, LPCWSTR pwszMach, LPCWSTR pwszUser, UINT& uMsgId)
  1069. {
  1070. /* local constants */
  1071. const long MAX_REUSE_NUM_ALLOWED = 1;
  1072. /* local variables */
  1073. BOOL bCanCopy = TRUE;
  1074. _variant_t varDate;
  1075. long rc;
  1076. /* function body */
  1077. //init the message id to 0
  1078. uMsgId = 0;
  1079. //if not copying passwords, return TRUE
  1080. _bstr_t sCopyPwd = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_CopyPasswords));
  1081. if (UStrICmp((WCHAR*)sCopyPwd,GET_STRING(IDS_YES)))
  1082. return bCanCopy;
  1083. //
  1084. // Always retrieve the target domain's password history length as
  1085. // the copy password code may need the value even if the account
  1086. // was previously migrated without the copy password option.
  1087. //
  1088. /* if not already done, check password reuse policy on the target domain */
  1089. if (m_lPwdHistoryLength == -1)
  1090. {
  1091. IADsDomain * pDomain;
  1092. _bstr_t sDom( L"WinNT://" );
  1093. _bstr_t sTgtDom = pVarSet->get(GET_BSTR(DCTVS_Options_TargetDomain));
  1094. sDom += sTgtDom;
  1095. HRESULT hr = ADsGetObject(sDom, IID_IADsDomain, (void **) &pDomain);
  1096. if (SUCCEEDED(hr))
  1097. {
  1098. //Get the password reuse policy
  1099. long lReuse;
  1100. hr = pDomain->get_PasswordHistoryLength(&lReuse);
  1101. pDomain->Release();
  1102. //if successful, store it in a class member variable
  1103. if (SUCCEEDED(hr))
  1104. m_lPwdHistoryLength = lReuse;
  1105. }
  1106. }
  1107. //if not previously migrated, return TRUE
  1108. _bstr_t sAccount = pVarSet->get(GET_BSTR(DCTVS_CopiedAccount_SourceSam));
  1109. //get the previous migration time from the class map
  1110. CString sDate;
  1111. //if previously migrated, get the date this user was last migrated
  1112. if (mMigTimeMap.Lookup((WCHAR*)sAccount, sDate))
  1113. {
  1114. //convert the date string to a variant (VT_DATE)
  1115. COleDateTime aDate;
  1116. if (aDate.ParseDateTime((LPCTSTR)sDate))
  1117. {
  1118. varDate.vt = VT_DATE;
  1119. varDate.date = DATE(aDate);
  1120. }
  1121. else
  1122. return bCanCopy;
  1123. }
  1124. else //else return TRUE
  1125. return bCanCopy;
  1126. if ((m_lPwdHistoryLength != -1) && (m_lPwdHistoryLength <= MAX_REUSE_NUM_ALLOWED))
  1127. return bCanCopy;
  1128. /* if target account was created, we still want to copy password if object was created (Stystus == 1) */
  1129. //get the migration status for this user
  1130. long lStatus = pVarSet->get(GET_BSTR(DCTVS_CopiedAccount_Status));
  1131. //if the status field has the first bit set, then this user was
  1132. //created and we should return TRUE to copy the password
  1133. if (lStatus & AR_Status_Created)
  1134. return bCanCopy;
  1135. //
  1136. // Check whether source account password has been
  1137. // changed since the object has been migrated.
  1138. //
  1139. DWORD dwPasswordAge = 0;
  1140. DWORD dwPasswordExpired = 0;
  1141. //
  1142. // Attempt to retrieve user information at level 4 which is only supported on .NET and later
  1143. // but note that level 3 may not be supported on a .NET server when the SAM SID compatibility
  1144. // mode is SAM_SID_COMPATIBILITY_STRICT. If failed to retrieve level 4 then retrieve level 3.
  1145. //
  1146. PUSER_INFO_4 pui4 = NULL;
  1147. rc = NetUserGetInfo(pwszMach, pwszUser, 4, (LPBYTE*)&pui4);
  1148. if (rc == ERROR_SUCCESS)
  1149. {
  1150. dwPasswordAge = pui4->usri4_password_age;
  1151. dwPasswordExpired = pui4->usri4_password_expired;
  1152. NetApiBufferFree(pui4);
  1153. }
  1154. else
  1155. {
  1156. if (rc == ERROR_INVALID_LEVEL)
  1157. {
  1158. PUSER_INFO_3 pui3 = NULL;
  1159. rc = NetUserGetInfo(pwszMach, pwszUser, 3, (LPBYTE*)&pui3);
  1160. if (rc == ERROR_SUCCESS)
  1161. {
  1162. dwPasswordAge = pui3->usri3_password_age;
  1163. dwPasswordExpired = pui3->usri3_password_expired;
  1164. NetApiBufferFree(pui3);
  1165. }
  1166. }
  1167. }
  1168. if (rc == ERROR_SUCCESS)
  1169. {
  1170. //
  1171. // Retrieve password age in seconds.
  1172. //
  1173. if (dwPasswordAge > 0)
  1174. {
  1175. //
  1176. // Retrieve current system time and then subtract password age
  1177. // from the current system time to arrive at the time that the
  1178. // password was last set. Note that for the DATE type that whole
  1179. // numbers represent days and the fractional part represents a
  1180. // fraction of one day.
  1181. //
  1182. DATE dateCurrent;
  1183. SYSTEMTIME stCurrent;
  1184. GetLocalTime(&stCurrent);
  1185. SystemTimeToVariantTime(&stCurrent, &dateCurrent);
  1186. DATE datePassword = dateCurrent - (double(dwPasswordAge) / SECONDS_PER_DAY);
  1187. //
  1188. // If the time the object was last migrated is later than or equal
  1189. // to the time the password was last set then don't copy password.
  1190. //
  1191. if (varDate.date >= datePassword)
  1192. {
  1193. bCanCopy = FALSE;
  1194. }
  1195. }
  1196. else
  1197. {
  1198. //
  1199. // The password age cannot be determined. There are two possible
  1200. // reasons that the password age cannot be determined. The 'User
  1201. // Must Change Password at Next Logon' might be checked on the
  1202. // source account or the system time on this computer is less
  1203. // than the time the password was last changed.
  1204. //
  1205. //
  1206. // If the password expired value is not zero than we will assume that
  1207. // the 'User Must Change Password at Next Logon' must be checked and
  1208. // there is not a time difference issue.
  1209. //
  1210. if (dwPasswordExpired)
  1211. {
  1212. uMsgId = DCT_MSG_PW_COPY_NOT_TRIED_USER_MUST_CHANGE_S;
  1213. }
  1214. else
  1215. {
  1216. uMsgId = DCT_MSG_PW_COPY_NOT_TRIED_TIMEDIFF_SS;
  1217. }
  1218. bCanCopy = FALSE;
  1219. }
  1220. }
  1221. return bCanCopy;
  1222. }
  1223. //END CanCopyPassword
  1224. //-----------------------------------------------------------------------------
  1225. // MarkAccountError
  1226. //
  1227. // Synopsis
  1228. // Sets the error status bit in the account status. This is used by the
  1229. // disable account component as an indication that the account should remain
  1230. // disabled.
  1231. //
  1232. // Paramters
  1233. // pVarSet - varset containing account information
  1234. //-----------------------------------------------------------------------------
  1235. void CSetPassword::MarkAccountError(IVarSet* pVarSet)
  1236. {
  1237. //
  1238. // set or clear password copied flag in the persisted object status
  1239. // this flag is used to determine whether the password was previously copied
  1240. //
  1241. _bstr_t strStatus = GET_BSTR(DCTVS_CopiedAccount_Status);
  1242. long lStatus = pVarSet->get(strStatus);
  1243. lStatus |= AR_Status_PasswordError;
  1244. pVarSet->put(strStatus, lStatus);
  1245. }