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.

1005 lines
31 KiB

  1. #include "stdafx.h"
  2. #include "MsPwdMig.h"
  3. #include "PasswordMigration.h"
  4. #include <NtSecApi.h>
  5. #include <io.h>
  6. #include <winioctl.h>
  7. #include <lm.h>
  8. #include <eh.h>
  9. #include <ActiveDS.h>
  10. #include <Dsrole.h>
  11. #include "TReg.hpp"
  12. #include "pwdfuncs.h"
  13. #include "PWGen.hpp"
  14. #include "UString.hpp"
  15. #include "PwRpcUtl.h"
  16. #include "PwdSvc.h"
  17. #include "PwdSvc_c.c"
  18. #include "Error.h"
  19. #include "GetDcName.h"
  20. #pragma comment(lib, "netapi32.lib")
  21. #pragma comment(lib, "adsiid.lib")
  22. #pragma comment(lib, "activeds.lib")
  23. #pragma comment(lib, "commonlib.lib")
  24. using namespace _com_util;
  25. namespace
  26. {
  27. struct SSeException
  28. {
  29. SSeException(UINT uCode) :
  30. uCode(uCode)
  31. {
  32. }
  33. UINT uCode;
  34. };
  35. void SeTranslator(unsigned int u, EXCEPTION_POINTERS* pepExceptions)
  36. {
  37. throw SSeException(u);
  38. }
  39. #ifdef ADMT_TRACE
  40. void _cdecl ADMTTRACE(LPCTSTR pszFormat, ...)
  41. {
  42. if (pszFormat)
  43. {
  44. _TCHAR szMessage[2048];
  45. va_list args;
  46. va_start(args, pszFormat);
  47. _vsntprintf(szMessage, 2048, pszFormat, args);
  48. va_end(args);
  49. OutputDebugString(szMessage);
  50. }
  51. }
  52. #else
  53. inline void _cdecl ADMTTRACE(LPCTSTR pszFormat, ...)
  54. {
  55. }
  56. #endif
  57. #ifndef IADsPtr
  58. _COM_SMARTPTR_TYPEDEF(IADs, IID_IADs);
  59. #endif
  60. #ifndef IADsGroupPtr
  61. _COM_SMARTPTR_TYPEDEF(IADsGroup, IID_IADsGroup);
  62. #endif
  63. #ifndef IADsMembersPtr
  64. _COM_SMARTPTR_TYPEDEF(IADsMembers, IID_IADsMembers);
  65. #endif
  66. }
  67. // namespace
  68. //---------------------------------------------------------------------------
  69. // CPasswordMigration
  70. //---------------------------------------------------------------------------
  71. // Constructor
  72. CPasswordMigration::CPasswordMigration() :
  73. m_bSessionEstablished(false),
  74. m_hBinding(NULL),
  75. m_sBinding(NULL),
  76. m_pTargetCrypt(NULL)
  77. {
  78. }
  79. // Destructor
  80. CPasswordMigration::~CPasswordMigration()
  81. {
  82. delete m_pTargetCrypt;
  83. if (m_bSessionEstablished)
  84. {
  85. _se_translator_function pfnSeTranslatorOld = _set_se_translator((_se_translator_function)SeTranslator);
  86. try
  87. {
  88. PwdBindDestroy(&m_hBinding,&m_sBinding);
  89. }
  90. catch (SSeException& e)
  91. {
  92. ADMTTRACE(_T("PwdBindDestroy: %s (%u)\n"), _com_error(HRESULT_FROM_WIN32(e.uCode)).ErrorMessage(), e.uCode);
  93. }
  94. _set_se_translator(pfnSeTranslatorOld);
  95. }
  96. }
  97. //
  98. // IPasswordMigration Implementation ----------------------------------------
  99. //
  100. // EstablishSession Method
  101. STDMETHODIMP CPasswordMigration::EstablishSession(BSTR bstrSourceServer, BSTR bstrTargetServer)
  102. {
  103. HRESULT hr = S_OK;
  104. USES_CONVERSION;
  105. LPCWSTR pszSourceServer = OLE2CW(bstrSourceServer);
  106. LPCWSTR pszTargetServer = OLE2CW(bstrTargetServer);
  107. ADMTTRACE(_T("E CPasswordMigration::EstablishSession(SourceServer='%s', TargetServer='%s')\n"), pszSourceServer, pszTargetServer);
  108. try
  109. {
  110. m_bSessionEstablished = false;
  111. CheckPasswordDC(pszSourceServer, pszTargetServer);
  112. m_bSessionEstablished = true;
  113. }
  114. catch (_com_error& ce)
  115. {
  116. hr = SetError(ce, IDS_E_CANNOT_ESTABLISH_SESSION);
  117. }
  118. catch (...)
  119. {
  120. hr = SetError(E_UNEXPECTED, IDS_E_CANNOT_ESTABLISH_SESSION);
  121. }
  122. ADMTTRACE(_T("L CPasswordMigration::EstablishSession() : %s 0x%08lX\n"), _com_error(hr).ErrorMessage(), hr);
  123. return hr;
  124. }
  125. // CopyPassword Method
  126. STDMETHODIMP CPasswordMigration::CopyPassword(BSTR bstrSourceAccount, BSTR bstrTargetAccount, BSTR bstrTargetPassword)
  127. {
  128. HRESULT hr = S_OK;
  129. USES_CONVERSION;
  130. LPCTSTR pszSourceAccount = OLE2CT(bstrSourceAccount);
  131. LPCTSTR pszTargetAccount = OLE2CT(bstrTargetAccount);
  132. LPCTSTR pszTargetPassword = OLE2CT(bstrTargetPassword);
  133. ADMTTRACE(_T("E CPasswordMigration::CopyPassword(SourceAccount='%s', TargetAccount='%s', TargetPassword='%s')\n"), pszSourceAccount, pszTargetAccount, pszTargetPassword);
  134. try
  135. {
  136. // if session established then...
  137. if (m_bSessionEstablished)
  138. {
  139. // copy password
  140. CopyPasswordImpl(pszSourceAccount, pszTargetAccount, pszTargetPassword);
  141. }
  142. else
  143. {
  144. // else return error
  145. ThrowError(PM_E_SESSION_NOT_ESTABLISHED, IDS_E_SESSION_NOT_ESTABLISHED);
  146. }
  147. }
  148. catch (_com_error& ce)
  149. {
  150. hr = SetError(ce, IDS_E_CANNOT_COPY_PASSWORD);
  151. }
  152. catch (...)
  153. {
  154. hr = SetError(E_UNEXPECTED, IDS_E_CANNOT_COPY_PASSWORD);
  155. }
  156. ADMTTRACE(_T("L CPasswordMigration::CopyPassword() : %s 0x%08lX\n"), _com_error(hr).ErrorMessage(), hr);
  157. return hr;
  158. }
  159. // GenerateKey Method
  160. STDMETHODIMP CPasswordMigration::GenerateKey(BSTR bstrSourceDomainName, BSTR bstrKeyFilePath, BSTR bstrPassword)
  161. {
  162. HRESULT hr = S_OK;
  163. USES_CONVERSION;
  164. LPCTSTR pszSourceDomainName = OLE2CT(bstrSourceDomainName);
  165. LPCTSTR pszKeyFilePath = OLE2CT(bstrKeyFilePath);
  166. LPCTSTR pszPassword = OLE2CT(bstrPassword);
  167. ADMTTRACE(_T("E CPasswordMigration::GenerateKey(SourceDomainName='%s', KeyFilePath='%s', Password='%s')\n"), pszSourceDomainName, pszKeyFilePath, pszPassword);
  168. try
  169. {
  170. //
  171. // Retrieve flat (NetBIOS) name of domain and use it for storing the key. The flat
  172. // name is used because the registry supports a maximum key name length of 256
  173. // Unicode characters. The combination of a DNS name and GUID string to uniquely
  174. // identify the key as belonging to this component could exceed this maximum length.
  175. //
  176. _bstr_t strFlatName;
  177. _bstr_t strDnsName;
  178. DWORD dwError = GetDomainNames5(pszSourceDomainName, strFlatName, strDnsName);
  179. if (dwError != ERROR_SUCCESS)
  180. {
  181. _com_issue_error(HRESULT_FROM_WIN32(dwError));
  182. }
  183. // The only reason the flat name would be empty at this point would be if
  184. // the _bstr_t object was unable to allocate the internal Data_t object.
  185. if (!strFlatName)
  186. {
  187. _com_issue_error(E_OUTOFMEMORY);
  188. }
  189. //
  190. // Generate key.
  191. //
  192. GenerateKeyImpl(strFlatName, pszKeyFilePath, pszPassword);
  193. }
  194. catch (_com_error& ce)
  195. {
  196. hr = SetError(ce, IDS_E_CANNOT_GENERATE_KEY);
  197. }
  198. catch (...)
  199. {
  200. hr = SetError(E_UNEXPECTED, IDS_E_CANNOT_GENERATE_KEY);
  201. }
  202. ADMTTRACE(_T("L CPasswordMigration::GenerateKey() : %s 0x%08lX\n"), _com_error(hr).ErrorMessage(), hr);
  203. return hr;
  204. }
  205. //
  206. // Implementation -----------------------------------------------------------
  207. //
  208. // GenerateKeyImpl Method
  209. void CPasswordMigration::GenerateKeyImpl(LPCTSTR pszDomain, LPCTSTR pszFile, LPCTSTR pszPassword)
  210. {
  211. //
  212. // validate source domain name
  213. //
  214. if ((pszDomain == NULL) || (pszDomain[0] == NULL))
  215. {
  216. ThrowError(E_INVALIDARG, IDS_E_KEY_DOMAIN_NOT_SPECIFIED);
  217. }
  218. //
  219. // validate key file path
  220. //
  221. if ((pszFile == NULL) || (pszFile[0] == NULL))
  222. {
  223. ThrowError(E_INVALIDARG, IDS_E_KEY_FILE_NOT_SPECIFIED);
  224. }
  225. _TCHAR szDrive[_MAX_DRIVE];
  226. _TCHAR szExt[_MAX_EXT];
  227. _tsplitpath(pszFile, szDrive, NULL, NULL, szExt);
  228. // verify drive is a local drive
  229. _TCHAR szDrivePath[_MAX_PATH];
  230. _tmakepath(szDrivePath, szDrive, _T("\\"), NULL, NULL);
  231. if (GetDriveType(szDrivePath) == DRIVE_REMOTE)
  232. {
  233. ThrowError(E_INVALIDARG, IDS_E_KEY_FILE_NOT_LOCAL_DRIVE, pszFile);
  234. }
  235. // verify file extension is correct
  236. if (_tcsicmp(szExt, _T(".pes")) != 0)
  237. {
  238. ThrowError(E_INVALIDARG, IDS_E_KEY_FILE_EXTENSION_INVALID, szExt);
  239. }
  240. //
  241. // create encryption key and write to specified file
  242. //
  243. // create encryption key
  244. _variant_t vntKey;
  245. try
  246. {
  247. CTargetCrypt crypt;
  248. vntKey = crypt.CreateEncryptionKey(pszDomain, pszPassword);
  249. }
  250. catch (_com_error& ce)
  251. {
  252. //
  253. // The message 'keyset not defined' is returned when
  254. // the enhanced provider (128 bit) is not available
  255. // therefore return a more meaningful message to user.
  256. //
  257. if (ce.Error() == NTE_KEYSET_NOT_DEF)
  258. {
  259. ThrowError(ce, IDS_E_HIGH_ENCRYPTION_NOT_INSTALLED);
  260. }
  261. else
  262. {
  263. throw;
  264. }
  265. }
  266. // write encrypted key bytes to file
  267. HANDLE hFile = CreateFile(pszFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  268. if (hFile == INVALID_HANDLE_VALUE)
  269. {
  270. ThrowError(HRESULT_FROM_WIN32(GetLastError()), IDS_E_KEY_CANT_CREATE_FILE, pszFile);
  271. }
  272. DWORD dwWritten;
  273. BOOL bWritten = WriteFile(hFile, vntKey.parray->pvData, vntKey.parray->rgsabound[0].cElements, &dwWritten, NULL);
  274. CloseHandle(hFile);
  275. if (!bWritten)
  276. {
  277. ThrowError(HRESULT_FROM_WIN32(GetLastError()), IDS_E_KEY_CANT_WRITE_FILE, pszFile);
  278. }
  279. SecureZeroMemory(GET_BYTE_ARRAY_DATA(vntKey), GET_BYTE_ARRAY_SIZE(vntKey));
  280. }
  281. #pragma optimize ("", off)
  282. /*********************************************************************
  283. * *
  284. * Written by: Paul Thompson *
  285. * Date: 15 DEC 2000 *
  286. * *
  287. * This function is a wrapper around a password DC "CheckConfig" *
  288. * call that can be used by the GUI and scripting to test the given *
  289. * DC. *
  290. * First we connect to a remote Lsa notification package dll, *
  291. * which should be installed on a DC in the source domain. The *
  292. * connect will be encrypted RPC. The configuration check, which *
  293. * establishes a temporary session for this check. *
  294. * We will also check anonymous user right access on the target *
  295. * domain. *
  296. * *
  297. * 2001-04-19 Mark Oluper - updated for client component *
  298. *********************************************************************/
  299. //BEGIN CheckPasswordDC
  300. void CPasswordMigration::CheckPasswordDC(LPCWSTR srcServer, LPCWSTR tgtServer)
  301. {
  302. ADMTTRACE(_T("E CPasswordMigration::CheckPasswordDC(srcServer='%s', tgtServer='%s')\n"), srcServer, tgtServer);
  303. /* local constants */
  304. const DWORD c_dwMinUC = 3;
  305. const DWORD c_dwMinLC = 3;
  306. const DWORD c_dwMinDigits = 3;
  307. const DWORD c_dwMinSpecial = 3;
  308. const DWORD c_dwMaxAlpha = 0;
  309. const DWORD c_dwMinLen = 14;
  310. /* local variables */
  311. DWORD rc = 0;
  312. WCHAR testPwd[PASSWORD_BUFFER_SIZE];
  313. WCHAR tempPwd[PASSWORD_BUFFER_SIZE];
  314. _variant_t varSession;
  315. _variant_t varTestPwd;
  316. /* function body */
  317. // USES_CONVERSION;
  318. if ((srcServer == NULL) || (srcServer[0] == NULL))
  319. {
  320. ThrowError(E_INVALIDARG, IDS_E_SOURCE_SERVER_NOT_SPECIFIED);
  321. }
  322. if ((tgtServer == NULL) || (tgtServer[0] == NULL))
  323. {
  324. ThrowError(E_INVALIDARG, IDS_E_TARGET_SERVER_NOT_SPECIFIED);
  325. }
  326. //make sure the server names start with "\\"
  327. if ((srcServer[0] != L'\\') && (srcServer[1] != L'\\'))
  328. {
  329. m_strSourceServer = L"\\\\";
  330. m_strSourceServer += srcServer;
  331. }
  332. else
  333. m_strSourceServer = srcServer;
  334. if ((tgtServer[0] != L'\\') && (tgtServer[1] != L'\\'))
  335. {
  336. m_strTargetServer = L"\\\\";
  337. m_strTargetServer += tgtServer;
  338. }
  339. else
  340. m_strTargetServer = tgtServer;
  341. //get the password DC's domain NETBIOS name
  342. GetDomainName(m_strSourceServer, m_strSourceDomainDNS, m_strSourceDomainFlat);
  343. //get the target DC's domain DNS name
  344. GetDomainName(m_strTargetServer, m_strTargetDomainDNS, m_strTargetDomainFlat);
  345. //
  346. // Verify that target domain allows anonymous access.
  347. //
  348. // Windows 2000 Server
  349. // 'Everyone' must be a member of the 'Pre-Windows 2000 Compatible Access' group.
  350. //
  351. // Windows Server
  352. // 'Everyone' must be a member of the 'Pre-Windows 2000 Compatible Access' group
  353. // and either 'Anonymous Logon' must be a member of this group or the
  354. // 'Network access: Let everyone permissions apply to anonymous users' must be
  355. // enabled in the 'Security Options' for domain controllers.
  356. //
  357. bool bEveryoneIsMember;
  358. bool bAnonymousIsMember;
  359. CheckPreWindows2000CompatibleAccessGroupMembers(bEveryoneIsMember, bAnonymousIsMember);
  360. if (bEveryoneIsMember == false)
  361. {
  362. ThrowError(
  363. __uuidof(PasswordMigration),
  364. __uuidof(IPasswordMigration),
  365. PM_E_EVERYONE_NOT_MEMBEROF_COMPATIBILITY_GROUP,
  366. IDS_E_EVERYONE_NOT_MEMBEROF_GROUP,
  367. (LPCTSTR)m_strTargetDomainDNS
  368. );
  369. }
  370. if (!bAnonymousIsMember && !DoesAnonymousHaveEveryoneAccess(m_strTargetServer))
  371. {
  372. ThrowError(
  373. __uuidof(PasswordMigration),
  374. __uuidof(IPasswordMigration),
  375. PM_E_EVERYONE_DOES_NOT_INCLUDE_ANONYMOUS,
  376. IDS_E_EVERYONE_DOES_NOT_INCLUDE_ANONYMOUS,
  377. (LPCTSTR)m_strTargetDomainDNS
  378. );
  379. }
  380. //if the high encryption pack has not been installed on this target
  381. //DC, then return that information
  382. try
  383. {
  384. if (m_pTargetCrypt == NULL)
  385. {
  386. m_pTargetCrypt = new CTargetCrypt;
  387. }
  388. }
  389. catch (_com_error& ce)
  390. {
  391. if (ce.Error() == 0x80090019)
  392. ThrowError(__uuidof(PasswordMigration), __uuidof(IPasswordMigration), PM_E_HIGH_ENCRYPTION_NOT_INSTALLED, IDS_E_HIGH_ENCRYPTION_NOT_INSTALLED);
  393. else
  394. throw;
  395. }
  396. //
  397. // Note that PwdBindCreate will destroy the binding if m_hBinding is non-NULL.
  398. //
  399. rc = PwdBindCreate(m_strSourceServer, &m_hBinding, &m_sBinding, TRUE);
  400. if(rc != ERROR_SUCCESS)
  401. {
  402. _com_issue_error(HRESULT_FROM_WIN32(rc));
  403. }
  404. try
  405. {
  406. try
  407. {
  408. //create a session key that will be used to encrypt the user's
  409. //password for this set of accounts
  410. varSession = m_pTargetCrypt->CreateSession(m_strSourceDomainFlat);
  411. }
  412. catch (_com_error& ce)
  413. {
  414. if (ce.Error() == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
  415. {
  416. ThrowError(__uuidof(PasswordMigration), __uuidof(IPasswordMigration), PM_E_NO_ENCRYPTION_KEY_FOR_DOMAIN, IDS_E_NO_ENCRYPTION_KEY_FOR_DOMAIN, (LPCTSTR)m_strSourceDomainFlat);
  417. }
  418. else
  419. {
  420. ThrowError(ce, IDS_E_GENERATE_SESSION_KEY_FAILED);
  421. }
  422. }
  423. //now create a complex password used by the "CheckConfig" call in
  424. //a challenge response. If the returned password matches, then
  425. //the source DC has the proper encryption key.
  426. if (EaPasswordGenerate(c_dwMinUC, c_dwMinLC, c_dwMinDigits, c_dwMinSpecial, c_dwMaxAlpha, c_dwMinLen, testPwd, PASSWORD_BUFFER_SIZE))
  427. {
  428. ThrowError(__uuidof(PasswordMigration), __uuidof(IPasswordMigration), PM_E_GENERATE_SESSION_PASSWORD_FAILED, IDS_E_GENERATE_SESSION_PASSWORD_FAILED);
  429. }
  430. //encrypt the password with the session key
  431. try
  432. {
  433. varTestPwd = m_pTargetCrypt->Encrypt(_bstr_t(testPwd));
  434. }
  435. catch (...)
  436. {
  437. ThrowError(__uuidof(PasswordMigration), __uuidof(IPasswordMigration), PM_E_GENERATE_SESSION_PASSWORD_FAILED, IDS_E_GENERATE_SESSION_PASSWORD_FAILED);
  438. }
  439. _se_translator_function pfnSeTranslatorOld = _set_se_translator((_se_translator_function)SeTranslator);
  440. HRESULT hr;
  441. try
  442. {
  443. //check to see if the server-side DLL is ready to process
  444. //password migration requests
  445. hr = PwdcCheckConfig(
  446. m_hBinding,
  447. GET_BYTE_ARRAY_SIZE(varSession),
  448. GET_BYTE_ARRAY_DATA(varSession),
  449. GET_BYTE_ARRAY_SIZE(varTestPwd),
  450. GET_BYTE_ARRAY_DATA(varTestPwd),
  451. tempPwd
  452. );
  453. }
  454. catch (SSeException& e)
  455. {
  456. if (e.uCode == RPC_S_SERVER_UNAVAILABLE)
  457. ThrowError(__uuidof(PasswordMigration), __uuidof(IPasswordMigration), PM_E_PASSWORD_MIGRATION_NOT_RUNNING, IDS_E_PASSWORD_MIGRATION_NOT_RUNNING);
  458. else
  459. _com_issue_error(HRESULT_FROM_WIN32(e.uCode));
  460. }
  461. _set_se_translator(pfnSeTranslatorOld);
  462. if (SUCCEEDED(hr))
  463. {
  464. if (UStrICmp(tempPwd,testPwd))
  465. ThrowError(__uuidof(PasswordMigration), __uuidof(IPasswordMigration), PM_E_ENCRYPTION_KEYS_DO_NOT_MATCH, IDS_E_ENCRYPTION_KEYS_DO_NOT_MATCH);
  466. }
  467. else if (hr == PM_E_PASSWORD_MIGRATION_NOT_ENABLED)
  468. {
  469. ThrowError(__uuidof(PasswordMigration), __uuidof(IPasswordMigration), PM_E_PASSWORD_MIGRATION_NOT_ENABLED, IDS_E_PASSWORD_MIGRATION_NOT_ENABLED);
  470. }
  471. else if ((m_strSourceDomainDNS.length() == 0) && ((hr == NTE_FAIL) || (hr == NTE_BAD_DATA)))
  472. {
  473. //
  474. // This case is only applicable for NT4.
  475. //
  476. ThrowError(__uuidof(PasswordMigration), __uuidof(IPasswordMigration), hr, IDS_E_ENCRYPTION_KEYS_DO_NOT_MATCH);
  477. }
  478. else
  479. {
  480. _com_issue_error(hr);
  481. }
  482. }
  483. catch (...)
  484. {
  485. PwdBindDestroy(&m_hBinding, &m_sBinding);
  486. throw;
  487. }
  488. SecureZeroMemory(GET_BYTE_ARRAY_DATA(varSession), GET_BYTE_ARRAY_SIZE(varSession));
  489. ADMTTRACE(_T("L CPasswordMigration::CheckPasswordDC()\n"));
  490. }
  491. //END CheckPasswordDC
  492. #pragma optimize ("", on)
  493. //---------------------------------------------------------------------------
  494. // CopyPassword Method
  495. //
  496. // Copies password via password migration server component installed on a
  497. // password export server.
  498. //
  499. // 2001-04-19 Mark Oluper - re-wrote original written by Paul Thompson to
  500. // incorporate changes required for client component
  501. //---------------------------------------------------------------------------
  502. void CPasswordMigration::CopyPasswordImpl(LPCTSTR pszSourceAccount, LPCTSTR pszTargetAccount, LPCTSTR pszPassword)
  503. {
  504. ADMTTRACE(_T("E CPasswordMigration::CopyPasswordImpl(SourceAccount='%s', TargetAccount='%s', Password='%s')\n"), pszSourceAccount, pszTargetAccount, pszPassword);
  505. if ((pszSourceAccount == NULL) || (pszSourceAccount[0] == NULL))
  506. {
  507. ThrowError(E_INVALIDARG, IDS_E_SOURCE_ACCOUNT_NOT_SPECIFIED);
  508. }
  509. if ((pszTargetAccount == NULL) || (pszTargetAccount[0] == NULL))
  510. {
  511. ThrowError(E_INVALIDARG, IDS_E_TARGET_ACCOUNT_NOT_SPECIFIED);
  512. }
  513. // encrypt password
  514. _variant_t vntEncryptedPassword = m_pTargetCrypt->Encrypt(pszPassword);
  515. // copy password
  516. HRESULT hr = PwdcCopyPassword(
  517. m_hBinding,
  518. m_strTargetServer,
  519. pszSourceAccount,
  520. pszTargetAccount,
  521. GET_BYTE_ARRAY_SIZE(vntEncryptedPassword),
  522. GET_BYTE_ARRAY_DATA(vntEncryptedPassword)
  523. );
  524. SecureZeroMemory(GET_BYTE_ARRAY_DATA(vntEncryptedPassword), GET_BYTE_ARRAY_SIZE(vntEncryptedPassword));
  525. if (FAILED(hr))
  526. {
  527. _com_issue_error(hr);
  528. }
  529. ADMTTRACE(_T("L CPasswordMigration::CopyPasswordImpl()\n"));
  530. }
  531. //---------------------------------------------------------------------------
  532. // GetDomainName Function
  533. //
  534. // Retrieves both the domain DNS name if available and the domain flat or
  535. // NetBIOS name for the specified server.
  536. //
  537. // 2001-04-19 Mark Oluper - initial
  538. //---------------------------------------------------------------------------
  539. void CPasswordMigration::GetDomainName(LPCTSTR pszServer, _bstr_t& strNameDNS, _bstr_t& strNameFlat)
  540. {
  541. ADMTTRACE(_T("E CPasswordMigration::GetDomainName(Server='%s', ...)\n"), pszServer);
  542. PDSROLE_PRIMARY_DOMAIN_INFO_BASIC ppdib;
  543. DWORD dwError = DsRoleGetPrimaryDomainInformation(pszServer, DsRolePrimaryDomainInfoBasic, (BYTE**)&ppdib);
  544. if (dwError != NO_ERROR)
  545. {
  546. ThrowError(HRESULT_FROM_WIN32(dwError), IDS_E_CANNOT_GET_DOMAIN_NAME, pszServer);
  547. }
  548. strNameDNS = ppdib->DomainNameDns;
  549. strNameFlat = ppdib->DomainNameFlat;
  550. DsRoleFreeMemory(ppdib);
  551. ADMTTRACE(_T("L CPasswordMigration::GetDomainName(..., NameDNS='%s', NameFlat='%s')\n"), (LPCTSTR)strNameDNS, (LPCTSTR)strNameFlat);
  552. }
  553. //---------------------------------------------------------------------------
  554. // CheckPreWindows2000CompatibleAccessGroupMembers Method
  555. //
  556. // Synopsis
  557. // Checks if Everyone and Anonymous Logon are members of the Pre-Windows
  558. // 2000 Compatible Access group.
  559. //
  560. // Arguments
  561. // OUT bEveryone - set to true if Everyone is a member
  562. // OUT bAnonymous - set to true if Anonymous Logon is a member
  563. //---------------------------------------------------------------------------
  564. void CPasswordMigration::CheckPreWindows2000CompatibleAccessGroupMembers(bool& bEveryone, bool& bAnonymous)
  565. {
  566. //
  567. // Initialize return values.
  568. //
  569. bEveryone = false;
  570. bAnonymous = false;
  571. //
  572. // Generate ADsPath to built-in Pre-Windows 2000 Compatible Access group.
  573. //
  574. // Note that the GetPathToPreW2KCAGroup function returns partial DN with trailing comma.
  575. //
  576. _bstr_t strGroupPath;
  577. strGroupPath = _T("LDAP://");
  578. strGroupPath += m_strTargetDomainDNS;
  579. strGroupPath += _T("/");
  580. strGroupPath += GetPathToPreW2KCAGroup();
  581. strGroupPath += GetDefaultNamingContext(m_strTargetDomainDNS);
  582. //
  583. // Bind to the enumerator of the members interface
  584. // of the Pre-Windows 2000 Compatible Access group.
  585. //
  586. IADsGroupPtr spGroup;
  587. CheckError(ADsGetObject(strGroupPath, IID_IADsGroup, (VOID**)&spGroup));
  588. IADsMembersPtr spMembers;
  589. CheckError(spGroup->Members(&spMembers));
  590. IUnknownPtr spunkEnum;
  591. CheckError(spMembers->get__NewEnum(&spunkEnum));
  592. IEnumVARIANTPtr spEnum(spunkEnum);
  593. //
  594. // Initialize variables used to retrieve SID of each member.
  595. //
  596. VARIANT varMember;
  597. VariantInit(&varMember);
  598. ULONG ulFetched;
  599. PWSTR pszAttrs[] = { L"objectSid" };
  600. VARIANT varAttrs;
  601. VariantInit(&varAttrs);
  602. CheckError(ADsBuildVarArrayStr(pszAttrs, sizeof(pszAttrs) / sizeof(pszAttrs[0]), &varAttrs));
  603. _variant_t vntAttrs(varAttrs, false);
  604. VARIANT varObjectSid;
  605. VariantInit(&varObjectSid);
  606. //
  607. // Generate well known Everyone and Anonymous Logon SIDs.
  608. //
  609. SID_IDENTIFIER_AUTHORITY siaWorld = SECURITY_WORLD_SID_AUTHORITY;
  610. SID_IDENTIFIER_AUTHORITY siaNT = SECURITY_NT_AUTHORITY;
  611. PSID pEveryoneSid = NULL;
  612. AllocateAndInitializeSid(&siaWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &pEveryoneSid);
  613. PSID pAnonymousSid = NULL;
  614. AllocateAndInitializeSid(&siaNT, 1, SECURITY_ANONYMOUS_LOGON_RID, 0, 0, 0, 0, 0, 0, 0, &pAnonymousSid);
  615. if ((pEveryoneSid == NULL) || (pAnonymousSid == NULL))
  616. {
  617. if (pAnonymousSid)
  618. {
  619. FreeSid(pAnonymousSid);
  620. }
  621. if (pEveryoneSid)
  622. {
  623. FreeSid(pEveryoneSid);
  624. }
  625. _com_issue_error(E_OUTOFMEMORY);
  626. }
  627. //
  628. // Enumerate members...if Everyone or Anonymous Logon
  629. // is a member then set corresponding parameter to true.
  630. //
  631. try
  632. {
  633. while ((spEnum->Next(1ul, &varMember, &ulFetched) == S_OK) && (ulFetched == 1ul))
  634. {
  635. IADsPtr spMember(IDispatchPtr(_variant_t(varMember, false)));
  636. CheckError(spMember->GetInfoEx(vntAttrs, 0));
  637. CheckError(spMember->Get(pszAttrs[0], &varObjectSid));
  638. if (V_VT(&varObjectSid) == (VT_ARRAY|VT_UI1))
  639. {
  640. PSID pSid = (PSID) GET_BYTE_ARRAY_DATA(varObjectSid);
  641. if (pSid && IsValidSid(pSid))
  642. {
  643. if (EqualSid(pSid, pEveryoneSid))
  644. {
  645. bEveryone = true;
  646. }
  647. else if (EqualSid(pSid, pAnonymousSid))
  648. {
  649. bAnonymous = true;
  650. }
  651. }
  652. }
  653. VariantClear(&varObjectSid);
  654. if (bEveryone && bAnonymous)
  655. {
  656. break;
  657. }
  658. }
  659. }
  660. catch (...)
  661. {
  662. FreeSid(pAnonymousSid);
  663. FreeSid(pEveryoneSid);
  664. throw;
  665. }
  666. FreeSid(pAnonymousSid);
  667. FreeSid(pEveryoneSid);
  668. }
  669. /*********************************************************************
  670. * *
  671. * Written by: Paul Thompson *
  672. * Date: 12 APR 2001 *
  673. * *
  674. * This function is responsible for creating a path to the *
  675. * "Pre-Windows 2000 Compatible Access" builtin group from its well- *
  676. * known RID. This path will then be used by *
  677. * "IsEveryoneInPW2KCAGroup" to see if "Everyone" is in that group. *
  678. * *
  679. * 2001-12-09 moluper - updated to return default path instead of *
  680. * empty path *
  681. *********************************************************************/
  682. //BEGIN GetPathToPreW2KCAGroup
  683. _bstr_t CPasswordMigration::GetPathToPreW2KCAGroup()
  684. {
  685. /* local constants */
  686. const _TCHAR BUILTIN_RDN[] = _T(",CN=Builtin,");
  687. const _TCHAR PRE_WINDOWS_2000_COMPATIBLE_ACCESS_RDN[] = _T("CN=Pre-Windows 2000 Compatible Access");
  688. /* local variables */
  689. SID_IDENTIFIER_AUTHORITY siaNtAuthority = SECURITY_NT_AUTHORITY;
  690. PSID psidPreW2KCAGroup;
  691. _bstr_t sPath = _bstr_t(PRE_WINDOWS_2000_COMPATIBLE_ACCESS_RDN) + BUILTIN_RDN;
  692. WCHAR account[MAX_PATH];
  693. WCHAR domain[MAX_PATH];
  694. DWORD lenAccount = MAX_PATH;
  695. DWORD lenDomain = MAX_PATH;
  696. SID_NAME_USE snu;
  697. /* function body */
  698. //create the SID for the "Pre-Windows 2000 Compatible Access" group
  699. if (!AllocateAndInitializeSid(&siaNtAuthority,
  700. 2,
  701. SECURITY_BUILTIN_DOMAIN_RID,
  702. DOMAIN_ALIAS_RID_PREW2KCOMPACCESS,
  703. 0, 0, 0, 0, 0, 0,
  704. &psidPreW2KCAGroup))
  705. return sPath;
  706. //lookup the name attached to this SID
  707. if (!LookupAccountSid(NULL, psidPreW2KCAGroup, account, &lenAccount, domain, &lenDomain, &snu))
  708. return sPath;
  709. sPath = _bstr_t(L"CN=") + _bstr_t(account) + _bstr_t(BUILTIN_RDN);
  710. FreeSid(psidPreW2KCAGroup); //free the SID
  711. return sPath;
  712. }
  713. //END GetPathToPreW2KCAGroup
  714. /*********************************************************************
  715. * *
  716. * Written by: Paul Thompson *
  717. * Date: 6 APR 2001 *
  718. * *
  719. * This function is responsible for checking if anonymous user *
  720. * has been granted Everyone access if the target domain is Whistler *
  721. * or newer. This function is a helper function for *
  722. * "CheckPasswordDC". *
  723. * If the "Let Everyone permissions apply to anonymous users" *
  724. * security option has been enabled, then the LSA registry value of *
  725. * "everyoneincludesanonymous" will be set to 0x1. We will check *
  726. * registry value. *
  727. * *
  728. *********************************************************************/
  729. //BEGIN DoesAnonymousHaveEveryoneAccess
  730. BOOL CPasswordMigration::DoesAnonymousHaveEveryoneAccess(LPCWSTR tgtServer)
  731. {
  732. /* local constants */
  733. const int WINDOWS_2000_BUILD_NUMBER = 2195;
  734. /* local variables */
  735. TRegKey verKey, lsaKey, regComputer;
  736. BOOL bAccess = TRUE;
  737. DWORD rc = 0;
  738. DWORD rval;
  739. WCHAR sBuildNum[MAX_PATH];
  740. /* function body */
  741. //connect to the DC's HKLM registry key
  742. rc = regComputer.Connect(HKEY_LOCAL_MACHINE, tgtServer);
  743. if (rc == ERROR_SUCCESS)
  744. {
  745. //see if this machine is running Windows XP or newer by checking the
  746. //build number in the registry. If not, then we don't need to check
  747. //for the new security option
  748. rc = verKey.OpenRead(L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",&regComputer);
  749. if (rc == ERROR_SUCCESS)
  750. {
  751. //get the CurrentBuildNumber string
  752. rc = verKey.ValueGetStr(L"CurrentBuildNumber", sBuildNum, MAX_PATH);
  753. if (rc == ERROR_SUCCESS)
  754. {
  755. int nBuild = _wtoi(sBuildNum);
  756. if (nBuild <= WINDOWS_2000_BUILD_NUMBER)
  757. return bAccess;
  758. }
  759. }
  760. //if Windows XP or greater, check for the option being enabled
  761. //open the LSA key
  762. rc = lsaKey.OpenRead(L"System\\CurrentControlSet\\Control\\Lsa",&regComputer);
  763. if (rc == ERROR_SUCCESS)
  764. {
  765. //get the value of the "everyoneincludesanonymous" value
  766. rc = lsaKey.ValueGetDWORD(L"everyoneincludesanonymous",&rval);
  767. if (rc == ERROR_SUCCESS)
  768. {
  769. if (rval == 0)
  770. bAccess = FALSE;
  771. }
  772. else
  773. bAccess = FALSE;
  774. }
  775. }
  776. return bAccess;
  777. }
  778. //END DoesAnonymousHaveEveryoneAccess
  779. //---------------------------------------------------------------------------
  780. // GetDefaultNamingContext Method
  781. //
  782. // Synopsis
  783. // Retrieves the default naming context for the specified domain.
  784. //
  785. // Arguments
  786. // IN strDomain - name of domain
  787. //
  788. // Return
  789. // Default naming context for specified domain.
  790. //---------------------------------------------------------------------------
  791. _bstr_t CPasswordMigration::GetDefaultNamingContext(_bstr_t strDomain)
  792. {
  793. _bstr_t strDefaultNamingContext;
  794. //
  795. // Bind to rootDSE of specified domain and
  796. // retrieve default naming context.
  797. //
  798. IADsPtr spRootDse;
  799. _bstr_t strPath = _T("LDAP://") + strDomain + _T("/rootDSE");
  800. HRESULT hr = ADsGetObject(strPath, IID_IADs, (VOID**)&spRootDse);
  801. if (SUCCEEDED(hr))
  802. {
  803. VARIANT var;
  804. VariantInit(&var);
  805. hr = spRootDse->Get(_T("defaultNamingContext"), &var);
  806. if (SUCCEEDED(hr))
  807. {
  808. strDefaultNamingContext = _variant_t(var, false);
  809. }
  810. }
  811. return strDefaultNamingContext;
  812. }