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.

1057 lines
39 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. /////////////////////////////////////////////////////////////////////////////
  34. // CSetPassword
  35. //---------------------------------------------------------------------------
  36. // Get and set methods for the properties.
  37. //---------------------------------------------------------------------------
  38. STDMETHODIMP CSetPassword::get_sName(BSTR *pVal)
  39. {
  40. *pVal = m_sName;
  41. return S_OK;
  42. }
  43. STDMETHODIMP CSetPassword::put_sName(BSTR newVal)
  44. {
  45. m_sName = newVal;
  46. return S_OK;
  47. }
  48. STDMETHODIMP CSetPassword::get_sDesc(BSTR *pVal)
  49. {
  50. *pVal = m_sDesc;
  51. return S_OK;
  52. }
  53. STDMETHODIMP CSetPassword::put_sDesc(BSTR newVal)
  54. {
  55. m_sDesc = newVal;
  56. return S_OK;
  57. }
  58. //---------------------------------------------------------------------------
  59. // ProcessObject : This method currently records the setting of the "User
  60. // cannot change password" flag for intra-forest user migrations
  61. //---------------------------------------------------------------------------
  62. STDMETHODIMP CSetPassword::PreProcessObject(
  63. IUnknown *pSource, //in- Pointer to the source AD object
  64. IUnknown *pTarget, //in- Pointer to the target AD object
  65. IUnknown *pMainSettings, //in- Varset filled with the settings supplied by user
  66. IUnknown **ppPropsToSet //in,out - Varset filled with Prop-Value pairs that will be set
  67. // once all extension objects are executed.
  68. )
  69. {
  70. /* local variables */
  71. IVarSetPtr pVS = pMainSettings;
  72. tstring sSAMName = L"";
  73. /* function body */
  74. _bstr_t sIntraforest = pVS->get(GET_BSTR(DCTVS_Options_IsIntraforest));
  75. _bstr_t sType = pVS->get(GET_BSTR(DCTVS_CopiedAccount_Type));
  76. _bstr_t sSrc = pVS->get(GET_BSTR(DCTVS_Options_SourceServer));
  77. _bstr_t sAccount = pVS->get(GET_BSTR(DCTVS_CopiedAccount_SourceSam));
  78. if ((!sIntraforest.length()) || (!sType.length()) ||
  79. (!sSrc.length()) || (!sAccount.length()))
  80. return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  81. sSAMName = sAccount;
  82. if (!UStrICmp((WCHAR*)sIntraforest,GET_STRING(IDS_YES)))
  83. {
  84. if (!UStrICmp((WCHAR*)sType,L"user"))
  85. {
  86. //record the password flags set on the source account
  87. RecordPwdFlags(sSrc, sAccount);
  88. //we only want to record the "User cannot change password" flag, so
  89. //clear the others
  90. m_bUMCPNLFlagSet = false;
  91. m_bPNEFlagSet = false;
  92. //store the flag setting in a map
  93. mUCCPMap.insert(CUCCPMap::value_type(sSAMName, m_bUCCPFlagSet));
  94. }
  95. }
  96. //if previously migrated, store that time in a map
  97. _bstr_t sSrcDom = pVS->get(GET_BSTR(DCTVS_Options_SourceDomain));
  98. _bstr_t sTgtDom = pVS->get(GET_BSTR(DCTVS_Options_TargetDomain));
  99. IVarSetPtr pVSMig(__uuidof(VarSet));
  100. IUnknown * pUnk;
  101. IIManageDBPtr pDb(__uuidof(IManageDB));
  102. _variant_t varDate;
  103. pVSMig->QueryInterface(IID_IUnknown, (void**) &pUnk);
  104. HRESULT hrFind = pDb->raw_GetAMigratedObject(sAccount, sSrcDom, sTgtDom, &pUnk);
  105. pUnk->Release();
  106. //if migrated previously, store that time and date in a class map
  107. if (hrFind == S_OK)
  108. {
  109. varDate = pVSMig->get(L"MigratedObjects.Time");
  110. //store the flag setting in a map
  111. mMigTimeMap.insert(CMigTimeMap::value_type(sSAMName, varDate));
  112. }
  113. return S_OK;
  114. }
  115. //---------------------------------------------------------------------------
  116. // ProcessObject : This method sets the password of the target object
  117. // by looking at the settings in the varset.
  118. //---------------------------------------------------------------------------
  119. STDMETHODIMP CSetPassword::ProcessObject(
  120. IUnknown *pSource, //in- Pointer to the source AD object
  121. IUnknown *pTarget, //in- Pointer to the target AD object
  122. IUnknown *pMainSettings, //in- Varset filled with the settings supplied by user
  123. IUnknown **ppPropsToSet //in,out - Varset filled with Prop-Value pairs that will be set
  124. // once all extension objects are executed.
  125. )
  126. {
  127. _bstr_t sType;
  128. _bstr_t sFileName;
  129. IVarSetPtr pVS = pMainSettings;
  130. WCHAR password[LEN_Path];
  131. bool bGenerate = false;
  132. bool bGenerated = false;
  133. DWORD dwMinUC = 0, dwMinLC = 0, dwMinDigits = 1, dwMinSpecial = 0, dwMaxAlpha = 10, dwMinLen = 4;
  134. _variant_t var;
  135. // TErrorDct err;
  136. WCHAR fileName[LEN_Path];
  137. bool bCopiedPwd = false;
  138. HRESULT hrPwd = ERROR_SUCCESS;
  139. // Get the Error log filename from the Varset
  140. var = pVS->get(GET_BSTR(DCTVS_Options_Logfile));
  141. wcscpy(fileName, (WCHAR*)V_BSTR(&var));
  142. VariantInit(&var);
  143. // Open the error log
  144. err.LogOpen(fileName, 1);
  145. _bstr_t sXXX = GET_BSTR(DCTVS_Options_TargetServer);
  146. _variant_t vMach = pVS->get(sXXX);
  147. _variant_t vTgtName = pVS->get(GET_BSTR(DCTVS_CopiedAccount_TargetSam));
  148. _bstr_t sSrc = pVS->get(GET_BSTR(DCTVS_Options_SourceServer));
  149. _bstr_t sSrcDom = pVS->get(GET_BSTR(DCTVS_Options_SourceDomain));
  150. _bstr_t sMach = vMach;
  151. _bstr_t sTgtName = vTgtName;
  152. _bstr_t sAccount = pVS->get(GET_BSTR(DCTVS_CopiedAccount_SourceSam));
  153. _bstr_t sSkip = pVS->get(GET_BSTR(DCTVS_CopiedAccount_DoNotUpdatePassword));
  154. _bstr_t sTgtCN = pVS->get(GET_BSTR(DCTVS_CopiedAccount_TargetName));
  155. _bstr_t sIntraforest = pVS->get(GET_BSTR(DCTVS_Options_IsIntraforest));
  156. _bstr_t sCopyPwd = pVS->get(GET_BSTR(DCTVS_AccountOptions_CopyPasswords));
  157. _bstr_t sPwdDC = pVS->get(GET_BSTR(DCTVS_AccountOptions_PasswordDC));
  158. if ( !UStrICmp((WCHAR*)sSkip,(WCHAR*)sAccount) )
  159. {
  160. return S_OK;
  161. }
  162. if ( sTgtCN.length() == 0 )
  163. sTgtCN = sTgtName;
  164. // strip off the CN= from the beginning of the name, if necessary
  165. if ( !UStrICmp(sTgtCN,"CN=",3) )
  166. {
  167. sTgtCN = _bstr_t(((WCHAR*)sTgtCN)+3);
  168. }
  169. dwMaxAlpha = (LONG)pVS->get(GET_BSTR(DCTVS_AccountOptions_PasswordPolicy_MaxConsecutiveAlpha));
  170. dwMinDigits = (LONG)pVS->get(GET_BSTR(DCTVS_AccountOptions_PasswordPolicy_MinDigit));
  171. dwMinLC = (LONG)pVS->get(GET_BSTR(DCTVS_AccountOptions_PasswordPolicy_MinLower));
  172. dwMinUC = (LONG)pVS->get(GET_BSTR(DCTVS_AccountOptions_PasswordPolicy_MinUpper));
  173. dwMinSpecial = (LONG)pVS->get(GET_BSTR(DCTVS_AccountOptions_PasswordPolicy_MinSpecial));
  174. dwMinLen = (LONG)pVS->get(GET_BSTR(DCTVS_AccountOptions_PasswordPolicy_MinLength));
  175. // if the values are all 0 s then we make up something
  176. if ( (dwMaxAlpha + dwMinDigits + dwMinLC + dwMinUC + dwMinSpecial) == 0 )
  177. {
  178. dwMinDigits = 3;
  179. dwMinSpecial = 3;
  180. dwMinUC = 3;
  181. dwMinLC = 3;
  182. }
  183. sType = pVS->get(GET_BSTR(DCTVS_CopiedAccount_Type)).bstrVal;
  184. //for intra-forest migration of a user, reset the user's
  185. //original "User cannot change password" flag, whose value is
  186. //stored in the map
  187. if ( ! UStrICmp((WCHAR*)sIntraforest,GET_STRING(IDS_YES)) )
  188. {
  189. if ( ! UStrICmp((WCHAR*)sType,L"user" ) )
  190. {
  191. //get the UCCP flag from the map
  192. CUCCPMap::iterator itUCCPMap;
  193. tstring sSam = sAccount;
  194. itUCCPMap = mUCCPMap.find(sSam);
  195. if (itUCCPMap != mUCCPMap.end())
  196. m_bUCCPFlagSet = itUCCPMap->second;
  197. else
  198. m_bUCCPFlagSet = false;
  199. m_bUMCPNLFlagSet = false;
  200. m_bPNEFlagSet = false;
  201. ResetPwdFlags(pTarget, sMach, sTgtName);
  202. return S_OK;
  203. }
  204. }
  205. // Set the password for this account.
  206. if ( (_wcsicmp((WCHAR*)sType,L"user") == 0) || (_wcsicmp((WCHAR*)sType, L"computer") == 0) )
  207. {
  208. if ( !_wcsicmp((WCHAR*)sType,L"user" ) )
  209. {
  210. //we will not migrate passwords if replacing and password reuse policy on the target is 2 or greater
  211. if (!CanCopyPassword(pVS, sSrc, sAccount))
  212. {
  213. err.MsgWrite(0,DCT_MSG_PW_COPY_NOT_TRIED_S,(WCHAR*)sTgtCN);
  214. return S_OK;
  215. }
  216. _bstr_t bstrGenerate = pVS->get(GET_BSTR(DCTVS_AccountOptions_GenerateStrongPasswords));
  217. if (bstrGenerate == GET_BSTR(IDS_YES))
  218. {
  219. bGenerate = true;
  220. }
  221. if (bGenerate)
  222. {
  223. // generate a strong password
  224. BOOL pwGeneratedRc = EaPasswordGenerate(dwMinUC, dwMinLC, dwMinDigits, dwMinSpecial, dwMaxAlpha, dwMinLen, password, LEN_Path);
  225. bGenerated = ( pwGeneratedRc == 0 );
  226. if ( !bGenerated )
  227. wcsncpy(password,(WCHAR*)(sTgtName),15);
  228. // ensure that the password is NULL terminated
  229. password[14] = 0;
  230. }
  231. else
  232. {
  233. // set the password to the first 14 characters of the username
  234. wcsncpy(password,(WCHAR*)(sTgtName),15);
  235. // Convert the password to lower case.
  236. // ensure that the password is NULL terminated
  237. password[14] = 0;
  238. for ( DWORD i = 0; i < wcslen(password); i++ )
  239. password[i] = towlower(password[i]);
  240. // if the password is invalid then generate a password
  241. // if (!IsValidPassword(password))
  242. // {
  243. // err.MsgWrite(0, DCT_MSG_USERNAME_INVALID_FOR_PASSWORD_S, (PCWSTR)sTgtCN);
  244. // EaPasswordGenerate(dwMinUC, dwMinLC, dwMinDigits, dwMinSpecial, dwMaxAlpha, dwMinLen, password, LEN_Path);
  245. // TODO: if password generation fails then what? Should not fail though as that would be a logic error?
  246. // bGenerated = true;
  247. // }
  248. }
  249. }
  250. else
  251. {
  252. // computer, set the password to the first 14 characters of the computer name, in lower case,
  253. // without the trailing $
  254. UStrCpy(password, (WCHAR*)sTgtName, DIM(password));
  255. if ( password[UStrLen(password) - 1] == L'$' )
  256. password[UStrLen(password) - 1] = L'\0'; // remove trailing $ from machine name
  257. password[14] = L'\0'; // truncate to max password length of 14
  258. // Convert the password to lower case.
  259. for ( DWORD i = 0; i < wcslen(password); i++ )
  260. password[i] = towlower(password[i]);
  261. }
  262. // BSTR sPassword = password;
  263. // We are going to use the Net API to set the password.
  264. USER_INFO_1003 pInfo;
  265. DWORD pDw;
  266. WCHAR server[MAX_PATH];
  267. bool bFailedCopyPwd = false;
  268. //place the new password in the info structure
  269. pInfo.usri1003_password = password;
  270. long rc = NetUserSetInfo((WCHAR*)sMach,
  271. (WCHAR*)sTgtName, 1003, (LPBYTE) &pInfo, &pDw);
  272. if ( rc != 0 )
  273. {
  274. if ( bGenerated )
  275. {
  276. err.SysMsgWrite(ErrW,rc,DCT_MSG_PW_GENERATE_FAILED_S,(WCHAR*)sTgtCN);
  277. }
  278. else
  279. {
  280. err.SysMsgWrite(ErrW,rc,DCT_MSG_FAILED_SET_PASSWORD_TO_USERNAME_SD,(WCHAR*)sTgtCN,rc);
  281. if ( rc == NERR_PasswordTooShort )
  282. {
  283. // try to generate a password
  284. EaPasswordGenerate(dwMinUC, dwMinLC, dwMinDigits, dwMinSpecial, dwMaxAlpha, dwMinLen, password, LEN_Path);
  285. rc = NetUserSetInfo((WCHAR*)sMach,(WCHAR*)sTgtName,1003,(LPBYTE)&pInfo,&pDw);
  286. if ( rc )
  287. {
  288. err.SysMsgWrite(ErrW,rc,DCT_MSG_PW_GENERATE_FAILED_S,(WCHAR*)sTgtCN);
  289. }
  290. else //else complex password generated, if requested to copy password
  291. { //we now try to do that and only log that complex pwd generated if copy fails
  292. //if we are migrating the user's password, then set it here
  293. if ( !UStrICmp((WCHAR*)sCopyPwd,GET_STRING(IDS_YES)) )
  294. {
  295. //record the password flags set on the source account
  296. RecordPwdFlags(sSrc, sAccount);
  297. //clear the "User cannot change password" flag if it is set
  298. ClearUserCanChangePwdFlag(sMach, sTgtName);
  299. //set the change password flag to get past the minimum pwd age policy
  300. SetUserMustChangePwdFlag(pTarget);
  301. //prepare the server name
  302. server[0] = L'\\';
  303. server[1] = L'\\';
  304. UStrCpy(server+2,(WCHAR*)sPwdDC);
  305. //call the member function to copy the password. If success, set flag, else
  306. //failed, log the generated password message
  307. if ((hrPwd = CopyPassword(_bstr_t(server), sMach, sAccount, sTgtName, _bstr_t(password))) == ERROR_SUCCESS)
  308. {
  309. err.MsgWrite(0,DCT_MSG_PWCOPIED_S,(WCHAR*)sTgtCN);
  310. bCopiedPwd = true;
  311. //reset the password flags as were on the source account
  312. ResetPwdFlags(pTarget, sMach, sTgtName);
  313. }
  314. else
  315. {
  316. bFailedCopyPwd = true;
  317. }
  318. }//end if migrate password
  319. else //else not copy password, so post the complex password generated message
  320. err.MsgWrite(0,DCT_MSG_PWGENERATED_S,(WCHAR*)sTgtCN);
  321. }
  322. }
  323. }
  324. }
  325. else //else success
  326. {
  327. //if complex password generated, if requested to copy password
  328. //we now try to do that and only log that complex pwd generated if copy fails
  329. if ( bGenerated )
  330. {
  331. //if we are migrating the user's password, then set it here
  332. if ( !UStrICmp((WCHAR*)sCopyPwd,GET_STRING(IDS_YES)) )
  333. {
  334. //record the password flags set on the source account
  335. RecordPwdFlags(sSrc, sAccount);
  336. //clear the "User cannot change password" flag if it is set
  337. ClearUserCanChangePwdFlag(sMach, sTgtName);
  338. //set the change password flag to get past the minimum pwd age policy
  339. SetUserMustChangePwdFlag(pTarget);
  340. //prepare the server name
  341. server[0] = L'\\';
  342. server[1] = L'\\';
  343. UStrCpy(server+2,(WCHAR*)sPwdDC);
  344. //call the member function to copy the password. If success, set flag, else
  345. //failed, log the generated password message
  346. if ((hrPwd = CopyPassword(_bstr_t(server), sMach, sAccount, sTgtName, _bstr_t(password))) == ERROR_SUCCESS)
  347. {
  348. err.MsgWrite(0,DCT_MSG_PWCOPIED_S,(WCHAR*)sTgtCN);
  349. bCopiedPwd = true;
  350. //reset the password flags as were on the source account
  351. ResetPwdFlags(pTarget, sMach, sTgtName);
  352. }
  353. else
  354. {
  355. bFailedCopyPwd = true;
  356. }
  357. }//end if migrate password
  358. else //else not copy password, so post the complex password generated message
  359. err.MsgWrite(0,DCT_MSG_PWGENERATED_S,(WCHAR*)sTgtCN);
  360. }
  361. else
  362. {
  363. err.MsgWrite(0,DCT_MSG_SET_PASSWORD_TO_USERNAME_S,(WCHAR*)sTgtCN);
  364. }
  365. }
  366. //if user being migrated and that user's password was not copied, write
  367. //password to the password file
  368. if ((_wcsicmp((WCHAR*)sType,L"user") == 0) && (bCopiedPwd == false))
  369. {
  370. if ( !m_bTriedToOpenFile )
  371. {
  372. m_bTriedToOpenFile = true;
  373. // we should see if the varset specifies a file name
  374. var = pVS->get(GET_BSTR(DCTVS_AccountOptions_PasswordFile));
  375. sFileName = var;
  376. if ( sFileName.length() > 0 )
  377. {
  378. // we have the file name so lets open it and save the handle.
  379. m_passwordLog.LogOpen(sFileName, TRUE, 1);
  380. }
  381. //failure to get to password file so store it elsewhere and
  382. //post that new file location in the migration log
  383. if (!m_passwordLog.IsOpen() )
  384. {
  385. WCHAR sPasswordFile[MAX_PATH];
  386. if (GetDirectory(sPasswordFile))//place log in default log dir
  387. wcscat(sPasswordFile, L"Logs\\passwords.txt");
  388. else
  389. wcscpy(sPasswordFile, L"c:\\passwords.txt");
  390. m_passwordLog.LogOpen(sPasswordFile, TRUE, 1);//open this log file
  391. if ( m_passwordLog.IsOpen() )
  392. {
  393. if (!bFailedCopyPwd)
  394. err.MsgWrite(0,DCT_MSG_NEW_PASSWORD_LOG_S,(WCHAR*)sFileName,sPasswordFile);
  395. else
  396. err.MsgWrite(0,DCT_MSG_NEW_PASSWORD_LOG_CPY_FAILED_S,sPasswordFile);
  397. }
  398. }
  399. else if (bFailedCopyPwd)
  400. {
  401. WCHAR sPasswordFile[MAX_PATH];
  402. wcscpy(sPasswordFile, (WCHAR*)sFileName);
  403. err.MsgWrite(0,DCT_MSG_NEW_PASSWORD_LOG_CPY_FAILED_S,sPasswordFile);
  404. }
  405. }
  406. // Log the new password in the password log.
  407. if ( m_passwordLog.IsOpen() )
  408. {
  409. m_passwordLog.MsgWrite(L"%ls,%ls",(WCHAR*)(pVS->get(GET_BSTR(DCTVS_CopiedAccount_TargetSam)).bstrVal),password);
  410. }
  411. }//end if migrating user
  412. }//end if migrating user or computer
  413. //change password flags on the account if we did not copy the password
  414. if (( pTarget ) && (!bCopiedPwd))
  415. {
  416. // We want to clear "user cannot change password" and "password never expire flag"
  417. USER_INFO_1008 usr1008;
  418. USER_INFO_20 * usr20;
  419. DWORD errParam = 0;
  420. DWORD rc = NetUserGetInfo((WCHAR*) sSrc, (WCHAR*)sAccount, 20, (LPBYTE *)&usr20);
  421. _bstr_t strDisable = pVS->get(GET_BSTR(DCTVS_AccountOptions_DisableCopiedAccounts));
  422. _bstr_t strSame = pVS->get(GET_BSTR(DCTVS_AccountOptions_TgtStateSameAsSrc));
  423. long val = pVS->get(GET_WSTR(DCTVS_CopiedAccount_UserFlags));
  424. BOOL bDisable = FALSE;
  425. BOOL bSame = FALSE;
  426. if ( ! UStrICmp(strDisable,GET_STRING(IDS_YES)) )
  427. bDisable = TRUE;
  428. if ( ! UStrICmp(strSame,GET_STRING(IDS_YES)) )
  429. bSame = TRUE;
  430. if ( !rc )
  431. {
  432. usr1008.usri1008_flags = usr20->usri20_flags & ~UF_DONT_EXPIRE_PASSWD;
  433. // we won't turn off the user cannot change password, we will just expire the password below.
  434. // This will lock out any account with user cannot change password set, and the admin will
  435. // have to manually unlock the account.
  436. //usr1008.usri1008_flags &= ~UF_PASSWD_CANT_CHANGE;
  437. // for the computer account we need to set the UF_PASSWD_NOTREQD
  438. if ( !_wcsicmp((WCHAR*)sType,L"computer") )
  439. {
  440. usr1008.usri1008_flags |= UF_PASSWD_NOTREQD;
  441. // make sure the disable state for the computer account is the same as for the source computer
  442. if ( usr20->usri20_flags & UF_ACCOUNTDISABLE )
  443. {
  444. usr1008.usri1008_flags |= UF_ACCOUNTDISABLE;
  445. }
  446. else
  447. {
  448. usr1008.usri1008_flags &= ~UF_ACCOUNTDISABLE;
  449. }
  450. }
  451. else
  452. {
  453. // for user accounts, the disable flag is set based on the disable option
  454. // make sure that the disable flag is set properly!
  455. if ((bDisable) || (bSame && (val & UF_ACCOUNTDISABLE)))
  456. {
  457. usr1008.usri1008_flags |= UF_ACCOUNTDISABLE;
  458. }
  459. else
  460. {
  461. usr1008.usri1008_flags &= ~UF_ACCOUNTDISABLE;
  462. }
  463. }
  464. NetUserSetInfo((WCHAR*) sMach, (WCHAR*) sTgtName, 1008, (LPBYTE)&usr1008, &errParam);
  465. NetApiBufferFree(usr20);
  466. }
  467. // Require the users to change the password at next logon since we created new passwords for them
  468. SetUserMustChangePwdFlag(pTarget);
  469. }
  470. err.LogClose();
  471. return S_OK;
  472. }
  473. //---------------------------------------------------------------------------
  474. // ProcessUndo : Since we cant undo the password setting we will ignore this.
  475. //---------------------------------------------------------------------------
  476. STDMETHODIMP CSetPassword::ProcessUndo(
  477. IUnknown *pSource, //in- Pointer to the source AD object
  478. IUnknown *pTarget, //in- Pointer to the target AD object
  479. IUnknown *pMainSettings, //in- Varset filled with the settings supplied by user
  480. IUnknown **ppPropsToSet //in,out - Varset filled with Prop-Value pairs that will be set
  481. // once all extension objects are executed.
  482. )
  483. {
  484. _bstr_t sType;
  485. _bstr_t sSam;
  486. _bstr_t sServer;
  487. _bstr_t sIntraforest;
  488. WCHAR password[LEN_Path];
  489. DWORD rc = 0;
  490. DWORD pDw = 0;
  491. IVarSetPtr pVs = pMainSettings;
  492. sIntraforest = pVs->get(GET_BSTR(DCTVS_Options_IsIntraforest));
  493. sSam = pVs->get(GET_BSTR(DCTVS_CopiedAccount_SourceSam));
  494. sType = pVs->get(GET_BSTR(DCTVS_CopiedAccount_Type));
  495. sServer = pVs->get(GET_BSTR(DCTVS_Options_TargetServer));
  496. if ((!sIntraforest.length()) || (!sType.length()) ||
  497. (!sSam.length()) || (!sServer.length()))
  498. return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  499. //if this is user intra-forest, call preprocess the first time in and
  500. //precess the second
  501. if (!UStrICmp((WCHAR*)sIntraforest,GET_STRING(IDS_YES)))
  502. {
  503. if (!UStrICmp((WCHAR*)sType,L"user"))
  504. {
  505. //see if this SAM is in the undone list (every entry in the list has a ","
  506. //on each side of it
  507. _bstr_t sTemp = _bstr_t(L",") + sSam + _bstr_t(L",");
  508. //if found, that means it is post migration, so call process
  509. if (wcsstr((PCWSTR)m_sUndoneUsers, sTemp))
  510. {
  511. ProcessObject(pSource, pTarget, pMainSettings, ppPropsToSet);
  512. }
  513. else //else pre-migration, so add to the list and call preprocess
  514. {
  515. //add to the list with an ending ","
  516. m_sUndoneUsers += sSam;
  517. m_sUndoneUsers += L",";
  518. PreProcessObject(pSource, pTarget, pMainSettings, ppPropsToSet);
  519. }
  520. }
  521. }
  522. if (!_wcsicmp((WCHAR*) sType, L"computer"))
  523. {
  524. USER_INFO_1003 buf;
  525. rc = 0;
  526. if ( !rc )
  527. {
  528. // Create a lower case password from the sam account name. Do not include the trailing $.
  529. // Password to be maximum of 14 characters.
  530. UStrCpy(password, (WCHAR*)sSam, DIM(password));
  531. if ( password[UStrLen(password) - 1] == L'$' )
  532. password[UStrLen(password) - 1] = L'\0'; // remove trailing $ from machine name
  533. password[14] = L'\0'; // truncate to max password length of 14
  534. for ( DWORD i = 0; i < wcslen(password); i++ )
  535. password[i] = towlower(password[i]);
  536. buf.usri1003_password = password;
  537. rc = NetUserSetInfo((WCHAR*) sServer, (WCHAR*) sSam, 1003, (LPBYTE) &buf, &pDw);
  538. if ( rc == 2221 )
  539. {
  540. WCHAR sam[300];
  541. UStrCpy(sam,(WCHAR*)sSam);
  542. // remove the $ from the sam account name
  543. sam[UStrLen(sam)-1] = 0;
  544. rc = NetUserSetInfo((WCHAR*) sServer, sam, 1003, (LPBYTE) &buf, &pDw);
  545. }
  546. }
  547. }
  548. return HRESULT_FROM_WIN32(rc);
  549. }
  550. BOOL // ret - TRUE if directory found
  551. CSetPassword::GetDirectory(
  552. WCHAR * filename // out - string buffer to store directory name
  553. )
  554. {
  555. DWORD rc = 0;
  556. BOOL bFound = FALSE;
  557. TRegKey key;
  558. rc = key.OpenRead(GET_STRING(IDS_HKLM_DomainAdmin_Key),HKEY_LOCAL_MACHINE);
  559. if ( ! rc )
  560. {
  561. rc = key.ValueGetStr(L"Directory",filename,MAX_PATH);
  562. if ( ! rc )
  563. {
  564. if ( *filename )
  565. bFound = TRUE;
  566. }
  567. }
  568. key.Close();
  569. return bFound;
  570. }
  571. //---------------------------------------------------------------------------
  572. // IsValidPassword Method
  573. //
  574. // This method validates a password by converting the Unicode password string
  575. // to an ANSI string.
  576. //
  577. // If any Unicode characters cannot be directly translated to an ANSI
  578. // character and a default character must be used then the password is
  579. // invalid.
  580. //
  581. // If any Unicode characters translate to multi-byte characters then the
  582. // password is invalid.
  583. //---------------------------------------------------------------------------
  584. #ifndef WC_NO_BEST_FIT_CHARS
  585. #define WC_NO_BEST_FIT_CHARS 0x00000400
  586. #endif
  587. bool CSetPassword::IsValidPassword(LPCWSTR pwszPassword)
  588. {
  589. bool bValid = false;
  590. BOOL bUsedDefaultChar;
  591. CHAR szPassword[PWLEN + 1];
  592. // convert Unicode string to ANSI string
  593. int cch = WideCharToMultiByte(
  594. CP_ACP, // use system ANSI code page
  595. WC_NO_BEST_FIT_CHARS, // do not use best fit characters
  596. pwszPassword,
  597. -1, // assume password string is zero terminated
  598. szPassword,
  599. sizeof (szPassword),
  600. NULL,
  601. &bUsedDefaultChar // will be true if default character used
  602. );
  603. // if no error occurred
  604. if (cch > 0)
  605. {
  606. // if default character was not used
  607. if (bUsedDefaultChar == FALSE)
  608. {
  609. CPINFOEX cpie;
  610. GetCPInfoEx(CP_ACP, 0, &cpie);
  611. // if code page defines a SBCS then password is valid
  612. // otherwise code page defines a DBCS and the password
  613. // must be searched for a multi-byte character
  614. if ((cpie.LeadByte[0] == 0) && (cpie.LeadByte[1] == 0))
  615. {
  616. bValid = true;
  617. }
  618. else
  619. {
  620. // search for multi-byte character
  621. bool bLeadByteFound = false;
  622. for (int ich = 0; ich < cch; ich++)
  623. {
  624. if (IsDBCSLeadByteEx(CP_ACP, szPassword[ich]))
  625. {
  626. bLeadByteFound = true;
  627. break;
  628. }
  629. }
  630. // if no multi-byte character found
  631. // then password is valid
  632. if (!bLeadByteFound)
  633. {
  634. bValid = true;
  635. }
  636. }
  637. }
  638. }
  639. return bValid;
  640. }
  641. /*********************************************************************
  642. * *
  643. * Written by: Paul Thompson *
  644. * Date: 4 SEPT 2000 *
  645. * *
  646. * This function is responsible for copying the user's password *
  647. * in the source domain to its new account in the target domain. *
  648. * We use the Password Migration COM Wrapper to first check the *
  649. * configuration and establish a session with the given Password *
  650. * Export server. Then we copy the password. The configuration *
  651. * check, which establishes a session for this series of operations, *
  652. * is only done once per set of accounts. *
  653. * *
  654. *********************************************************************/
  655. //BEGIN CopyPassword
  656. HRESULT CSetPassword::CopyPassword(_bstr_t srcServer, _bstr_t tgtServer, _bstr_t srcName,
  657. _bstr_t tgtName, _bstr_t password)
  658. {
  659. /* local variables */
  660. HRESULT hr = S_OK;
  661. /* function body */
  662. if (m_pPwdMig == NULL)
  663. {
  664. hr = m_pPwdMig.CreateInstance(__uuidof(PasswordMigration));
  665. if (FAILED(hr))
  666. {
  667. return hr;
  668. }
  669. }
  670. //if we have not checked the password DC's configuration and
  671. //established a session, do it now
  672. if (!m_bEstablishedSession)
  673. hr = m_pPwdMig->raw_EstablishSession(srcServer, tgtServer);
  674. //if success, try copying the password
  675. if (SUCCEEDED(hr))
  676. hr = m_pPwdMig->raw_CopyPassword(srcName, tgtName, password);
  677. //if either failed, print warning in the migration.log
  678. if (FAILED(hr))
  679. {
  680. IErrorInfoPtr pErrorInfo = NULL;
  681. BSTR bstrDescription;
  682. _bstr_t sText = GET_BSTR(IDS_Unspecified_Failure);
  683. //get the rich error information on the failure
  684. if (SUCCEEDED(GetErrorInfo(0, &pErrorInfo)))
  685. {
  686. HRESULT hrTmp = pErrorInfo->GetDescription(&bstrDescription);
  687. if (SUCCEEDED(hrTmp)) //if got rich error info, use it
  688. sText = _bstr_t(bstrDescription, false);
  689. }
  690. //print message in the log
  691. err.MsgWrite(ErrW,DCT_MSG_PW_COPY_FAILED_S,(WCHAR*)tgtName, (WCHAR*)sText);
  692. }
  693. return hr;
  694. }
  695. //END CopyPassword
  696. /*********************************************************************
  697. * *
  698. * Written by: Paul Thompson *
  699. * Date: 5 DEC 2000 *
  700. * *
  701. * This function is responsible for setting the "User must change*
  702. * password at next logon" flag for a given user. We use this prior *
  703. * to copying a user's password and after we have just set it to a *
  704. * new complex password so that we get around the minimum password *
  705. * age policy for the target domain. *
  706. * *
  707. *********************************************************************/
  708. //BEGIN SetUserMustChangePwdFlag
  709. void CSetPassword::SetUserMustChangePwdFlag(IUnknown *pTarget)
  710. {
  711. /* local variables */
  712. IADs * pAds = NULL;
  713. /* function body */
  714. //set the new pwdLastSet value
  715. HRESULT hr = pTarget->QueryInterface(IID_IADs, (void**) &pAds);
  716. if ( SUCCEEDED(hr) )
  717. {
  718. // Require the users to change the password at next logon since we created new passwords for them
  719. VARIANT var;
  720. VariantInit(&var);
  721. V_I4(&var)=0;
  722. V_VT(&var)=VT_I4;
  723. hr = pAds->Put(L"pwdLastSet",var);
  724. hr = pAds->SetInfo();
  725. VariantClear(&var);
  726. if ( pAds ) pAds->Release();
  727. }
  728. }
  729. //END SetUserMustChangePwdFlag
  730. /*********************************************************************
  731. * *
  732. * Written by: Paul Thompson *
  733. * Date: 11 JAN 2001 *
  734. * *
  735. * This function is responsible for setting and clearing the *
  736. * "User cannot change password" flag for a given user, if it was *
  737. * originally set. *
  738. * *
  739. *********************************************************************/
  740. //BEGIN ClearUserCanChangePwdFlag
  741. void CSetPassword::ClearUserCanChangePwdFlag(LPCWSTR pwszMach, LPCWSTR pwszUser)
  742. {
  743. /* local variables */
  744. USER_INFO_3 * pInfo;
  745. DWORD pDw;
  746. long rc;
  747. /* function body */
  748. //get the current flag info for this user
  749. rc = NetUserGetInfo(pwszMach, pwszUser, 3, (LPBYTE *)&pInfo);
  750. if (rc == 0)
  751. {
  752. //clear the "User cannot change password" flag if it is set
  753. if (pInfo->usri3_flags & UF_PASSWD_CANT_CHANGE)
  754. {
  755. pInfo->usri3_flags &= !(UF_PASSWD_CANT_CHANGE);
  756. NetUserSetInfo(pwszMach, pwszUser, 3, (LPBYTE)pInfo, &pDw);
  757. }
  758. NetApiBufferFree((LPVOID) pInfo);
  759. }
  760. }
  761. //END ClearUserCanChangePwdFlag
  762. /*********************************************************************
  763. * *
  764. * Written by: Paul Thompson *
  765. * Date: 22 JAN 2001 *
  766. * *
  767. * This function is responsible for recording the password flags *
  768. * from a given user's source domain account. *
  769. * *
  770. *********************************************************************/
  771. //BEGIN RecordPwdFlags
  772. void CSetPassword::RecordPwdFlags(LPCWSTR pwszMach, LPCWSTR pwszUser)
  773. {
  774. /* local variables */
  775. USER_INFO_3 * pInfo;
  776. long rc;
  777. /* function body */
  778. //get the user password flags
  779. rc = NetUserGetInfo(pwszMach, pwszUser, 3, (LPBYTE *)&pInfo);
  780. if (rc == 0)
  781. {
  782. //record whether the "User cannot change password" flag is set
  783. if (pInfo->usri3_flags & UF_PASSWD_CANT_CHANGE)
  784. m_bUCCPFlagSet = true;//store whether the flag was set
  785. else
  786. m_bUCCPFlagSet = false;
  787. //record whether the "Password never expires" flag is set
  788. if (pInfo->usri3_flags & UF_DONT_EXPIRE_PASSWD)
  789. m_bPNEFlagSet = true;//store whether the flag was set
  790. else
  791. m_bPNEFlagSet = false;
  792. //record whether the "User must change password at next logon" flag is set
  793. if (pInfo->usri3_password_expired)
  794. m_bUMCPNLFlagSet = true;//store whether the flag was set
  795. else
  796. m_bUMCPNLFlagSet = false;
  797. NetApiBufferFree((LPVOID) pInfo);
  798. }
  799. }
  800. //END RecordPwdFlags
  801. /*********************************************************************
  802. * *
  803. * Written by: Paul Thompson *
  804. * Date: 22 JAN 2001 *
  805. * *
  806. * This function is responsible for recording the password flags *
  807. * from a given user's source domain account. *
  808. * *
  809. *********************************************************************/
  810. //BEGIN ResetPwdFlags
  811. void CSetPassword::ResetPwdFlags(IUnknown *pTarget, LPCWSTR pwszMach, LPCWSTR pwszUser)
  812. {
  813. /* local variables */
  814. USER_INFO_3 * pInfo = NULL;
  815. DWORD pDw;
  816. long rc;
  817. /* function body */
  818. //if the "User cannot change password" or "Password never expires"
  819. //flag was original set, reset it
  820. if ((m_bUCCPFlagSet) || (m_bPNEFlagSet))
  821. {
  822. //get the current flag info for this user
  823. rc = NetUserGetInfo(pwszMach, pwszUser, 3, (LPBYTE *)&pInfo);
  824. if (rc == 0)
  825. {
  826. if (m_bUCCPFlagSet)
  827. pInfo->usri3_flags |= UF_PASSWD_CANT_CHANGE;
  828. if (m_bPNEFlagSet)
  829. pInfo->usri3_flags |= UF_DONT_EXPIRE_PASSWD;
  830. NetUserSetInfo(pwszMach, pwszUser, 3, (LPBYTE)pInfo, &pDw);
  831. NetApiBufferFree((LPVOID) pInfo);
  832. }
  833. }
  834. //if the "User must change password at next logon" flag was original set, reset it
  835. if (m_bUMCPNLFlagSet)
  836. SetUserMustChangePwdFlag(pTarget);
  837. }
  838. //END ResetPwdFlags
  839. /*********************************************************************
  840. * *
  841. * Written by: Paul Thompson *
  842. * Date: 7 MAY 2001 *
  843. * *
  844. * This function is responsible for determining if we will indeed*
  845. * set the password. If the user is being re-migrated, copy password*
  846. * was selected, the password reuse policy on the target domain is *
  847. * greater than 1, and the password on the source object has not been*
  848. * changed since the last migration of that user, then we will not *
  849. * touch the password on the existing object on the target domain. *
  850. * *
  851. *********************************************************************/
  852. //BEGIN CanCopyPassword
  853. BOOL CSetPassword::CanCopyPassword(IVarSet * pVarSet, LPCWSTR pwszMach, LPCWSTR pwszUser)
  854. {
  855. /* local constants */
  856. const long MAX_REUSE_NUM_ALLOWED = 1;
  857. /* local variables */
  858. BOOL bCanCopy = TRUE;
  859. _variant_t varDate;
  860. USER_INFO_1 * pInfo = NULL;
  861. long rc;
  862. /* function body */
  863. //if not copying passwords, return TRUE
  864. _bstr_t sCopyPwd = pVarSet->get(GET_BSTR(DCTVS_AccountOptions_CopyPasswords));
  865. if (UStrICmp((WCHAR*)sCopyPwd,GET_STRING(IDS_YES)))
  866. return bCanCopy;
  867. //if not previously migrated, return TRUE
  868. _bstr_t sAccount = pVarSet->get(GET_BSTR(DCTVS_CopiedAccount_SourceSam));
  869. //get the previous migration time from the class map
  870. CMigTimeMap::iterator itTimeMap;
  871. tstring sSam = sAccount;
  872. itTimeMap = mMigTimeMap.find(sSam);
  873. //if previously migrated, get the date this user was last migrated
  874. if (itTimeMap != mMigTimeMap.end())
  875. varDate = itTimeMap->second;
  876. else //else return TRUE
  877. return bCanCopy;
  878. /* if not already done, check password reuse policy on the target domain */
  879. if (m_lPwdHistoryLength == -1)
  880. {
  881. IADsDomain * pDomain;
  882. _bstr_t sDom( L"WinNT://" );
  883. _bstr_t sTgtDom = pVarSet->get(GET_BSTR(DCTVS_Options_TargetDomain));
  884. sDom += sTgtDom;
  885. HRESULT hr = ADsGetObject(sDom, IID_IADsDomain, (void **) &pDomain);
  886. if (SUCCEEDED(hr))
  887. {
  888. //Get the password reuse policy
  889. long lReuse;
  890. hr = pDomain->get_PasswordHistoryLength(&lReuse);
  891. pDomain->Release();
  892. //if successful, store it in a class member variable
  893. if (SUCCEEDED(hr))
  894. m_lPwdHistoryLength = lReuse;
  895. }
  896. }
  897. if ((m_lPwdHistoryLength != -1) && (m_lPwdHistoryLength <= MAX_REUSE_NUM_ALLOWED))
  898. return bCanCopy;
  899. /* if target account was created, we still want to copy password if object was created (Stystus == 1) */
  900. //get the migration status for this user
  901. long lStatus = pVarSet->get(GET_BSTR(DCTVS_CopiedAccount_Status));
  902. //if the status field has the first bit set, then this user was
  903. //created and we should return TRUE to copy the password
  904. if (lStatus & AR_Status_Created)
  905. return bCanCopy;
  906. /* check if the source user's password has been modified since its last migration */
  907. //get time lapsed since last migrated
  908. COleDateTime migTime(varDate.date); //get the time last migrated
  909. COleDateTime curTime = COleDateTime::GetCurrentTime(); //get the current time
  910. COleDateTimeSpan elapsedTime = curTime - migTime;
  911. DWORD migElapsed = elapsedTime.GetTotalSeconds();
  912. //get the time the source password was last set
  913. rc = NetUserGetInfo(pwszMach, pwszUser, 1, (LPBYTE *)&pInfo);
  914. if (rc == 0)
  915. {
  916. DWORD setElapsed = pInfo->usri1_password_age;
  917. NetApiBufferFree((LPVOID) pInfo);
  918. //if not set since last migration, set return to FALSE
  919. if (migElapsed < setElapsed)
  920. bCanCopy = FALSE;
  921. }
  922. return bCanCopy;
  923. }
  924. //END CanCopyPassword