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.

6079 lines
196 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 2000
  5. //
  6. // File: dsUtil.cpp
  7. //
  8. // Contents: Utility functions for working with Active Directory
  9. //
  10. // History: 06-Sep-2000 JeffJon Created
  11. //
  12. //
  13. //--------------------------------------------------------------------------
  14. #include "pch.h"
  15. #include "dsutil.h"
  16. #include "dsutil2.h" // GetEscapedElement
  17. #include "dsutilrc.h"
  18. #include "cstrings.h"
  19. #include "secutil.h"
  20. #include <accctrl.h> // OBJECTS_AND_SID
  21. #include <aclapi.h> // GetNamedSecurityInfo etc.
  22. #include <lmaccess.h> // UF_* userAccountControl bits
  23. #include <ntsam.h> // GROUP_TYPE_*
  24. //+--------------------------------------------------------------------------
  25. //
  26. // Member: CDSCmdCredentialObject::CDSCmdCredentialObject
  27. //
  28. // Synopsis: Constructor for the credential management class
  29. //
  30. // Arguments:
  31. //
  32. // Returns:
  33. //
  34. // History: 06-Sep-2000 JeffJon Created
  35. //
  36. //---------------------------------------------------------------------------
  37. CDSCmdCredentialObject::CDSCmdCredentialObject()
  38. : m_pszPassword(0),
  39. m_bUsingCredentials(false)
  40. {
  41. }
  42. //+--------------------------------------------------------------------------
  43. //
  44. // Member: CDSCmdCredentialObject::~CDSCmdCredentialObject
  45. //
  46. // Synopsis: Destructor for the credential management class
  47. //
  48. // Arguments:
  49. //
  50. // Returns:
  51. //
  52. // History: 06-Sep-2000 JeffJon Created
  53. //
  54. //---------------------------------------------------------------------------
  55. CDSCmdCredentialObject::~CDSCmdCredentialObject()
  56. {
  57. if (m_pszPassword)
  58. {
  59. free(m_pszPassword);
  60. m_pszPassword = 0;
  61. }
  62. }
  63. //+--------------------------------------------------------------------------
  64. //
  65. // Member: CDSCmdCredentialObject::SetUsername
  66. //
  67. // Synopsis: Encodes the passed in string and sets the m_pszPassword
  68. // member data.
  69. //
  70. // Arguments: [pszPassword] : unencoded password
  71. //
  72. // Returns: HRESULT : E_OUTOFMEMORY if we failed to allocate space
  73. // for the new password
  74. // E_POINTER if the string passed in is not valid
  75. // S_OK if we succeeded in setting the password
  76. //
  77. // History: 06-Sep-2000 JeffJon Created
  78. //
  79. //---------------------------------------------------------------------------
  80. HRESULT CDSCmdCredentialObject::SetUsername(PCWSTR pszUsername)
  81. {
  82. ENTER_FUNCTION_HR(FULL_LOGGING, CDSCmdCredentialObject::SetUsername, hr);
  83. do // false loop
  84. {
  85. //
  86. // Verify the input parameters
  87. //
  88. if (!pszUsername)
  89. {
  90. hr = E_POINTER;
  91. break;
  92. }
  93. //
  94. // Copy the new username
  95. //
  96. m_sbstrUsername = pszUsername;
  97. DEBUG_OUTPUT(FULL_LOGGING, L"Username = %s", pszUsername);
  98. } while (false);
  99. return hr;
  100. }
  101. //
  102. // A prime number used to seed the encoding and decoding
  103. //
  104. #define NW_ENCODE_SEED3 0x83
  105. //+--------------------------------------------------------------------------
  106. //
  107. // Member: CDSCmdCredentialObject::SetPassword
  108. //
  109. // Synopsis: Encodes the passed in string and sets the m_pszPassword
  110. // member data.
  111. //
  112. // Arguments: [pszPassword] : unencoded password
  113. //
  114. // Returns: HRESULT : E_OUTOFMEMORY if we failed to allocate space
  115. // for the new password
  116. // S_OK if we succeeded in setting the password
  117. //
  118. // History: 06-Sep-2000 JeffJon Created
  119. //
  120. //---------------------------------------------------------------------------
  121. HRESULT CDSCmdCredentialObject::SetPassword(PCWSTR pszPassword)
  122. {
  123. ENTER_FUNCTION_HR(FULL_LOGGING, CDSCmdCredentialObject::SetPassword, hr);
  124. do // false loop
  125. {
  126. UNICODE_STRING Password;
  127. //
  128. // Free the previously allocated password if there was one
  129. //
  130. if (m_pszPassword)
  131. {
  132. free(m_pszPassword);
  133. m_pszPassword = 0;
  134. }
  135. UCHAR Seed = NW_ENCODE_SEED3;
  136. Password.Length = 0;
  137. //
  138. // Allocate space for the new encoded password
  139. //
  140. m_pszPassword = (PWSTR)malloc(sizeof(WCHAR) * (MAX_PASSWORD_LENGTH + 1));
  141. if(!m_pszPassword)
  142. {
  143. DEBUG_OUTPUT(FULL_LOGGING, L"Failed to allocate memory for the new password");
  144. hr = E_OUTOFMEMORY;
  145. break;
  146. }
  147. //
  148. // Copy the unencoded password
  149. //
  150. wcscpy(m_pszPassword, pszPassword);
  151. DEBUG_OUTPUT(FULL_LOGGING, L"Password = %s", pszPassword);
  152. //
  153. // Encode the password in-place
  154. //
  155. RtlInitUnicodeString(&Password, m_pszPassword);
  156. RtlRunEncodeUnicodeString(&Seed, &Password);
  157. } while (false);
  158. return hr;
  159. }
  160. //+--------------------------------------------------------------------------
  161. //
  162. // Member: CDSCmdCredentialObject::GetPassword
  163. //
  164. // Synopsis: Unencodes the password member and returns a string in the
  165. // provided buffer with the clear text password.
  166. //
  167. // Arguments: [pszBuffer - IN] : buffer to receive the clear text password
  168. // [pnWCharCount - IN/OUT] : count of the number of wide characters
  169. // the buffer can take including the NULL
  170. // terminator. If the buffer is too small,
  171. // the amount needed is returned.
  172. //
  173. // Returns: HRESULT : E_INVALIDARG if the buffer is not valid
  174. // E_OUTOFMEMORY if the buffer is too small
  175. // E_FAIL if the password has not been set
  176. // S_OK if we succeeded in getting the password
  177. //
  178. // History: 06-Sep-2000 JeffJon Created
  179. //
  180. //---------------------------------------------------------------------------
  181. HRESULT CDSCmdCredentialObject::GetPassword(PWSTR pszBuffer, UINT* pnWCharCount) const
  182. {
  183. ENTER_FUNCTION_HR(FULL_LOGGING, CDSCmdCredentialObject::GetPassword, hr);
  184. do // false loop
  185. {
  186. UNICODE_STRING Password;
  187. UCHAR Seed = NW_ENCODE_SEED3;
  188. Password.Length = 0;
  189. //
  190. // Verify input parameters
  191. //
  192. if (!pszBuffer ||
  193. !pnWCharCount)
  194. {
  195. ASSERT(pszBuffer);
  196. ASSERT(pnWCharCount);
  197. hr = E_INVALIDARG;
  198. break;
  199. }
  200. //
  201. // Verify there is a password to retrieve
  202. //
  203. if (!m_pszPassword)
  204. {
  205. DEBUG_OUTPUT(FULL_LOGGING, L"No password has been set");
  206. hr = E_FAIL;
  207. break;
  208. }
  209. //
  210. // Check to see if the buffer is large enough
  211. //
  212. UINT uLen = static_cast<UINT>(wcslen(m_pszPassword));
  213. if (uLen + 1 > *pnWCharCount)
  214. {
  215. *pnWCharCount = uLen + 1;
  216. DEBUG_OUTPUT(FULL_LOGGING, L"Failed to allocate enough memory for the password");
  217. hr = E_OUTOFMEMORY;
  218. break;
  219. }
  220. //
  221. // Copy the encoded password into the buffer
  222. //
  223. wcscpy(pszBuffer, m_pszPassword);
  224. //
  225. // Decode the buffer in place
  226. //
  227. RtlInitUnicodeString(&Password, pszBuffer);
  228. RtlRunDecodeUnicodeString(Seed, &Password);
  229. } while (false);
  230. return hr;
  231. }
  232. ////////////////////////////////////////////////////////////////////////////////
  233. //+--------------------------------------------------------------------------
  234. //
  235. // Member: CDSCmdBasePathsInfo::CDSCmdBasePathsInfo
  236. //
  237. // Synopsis: Constructor for the base paths management class
  238. //
  239. // Arguments:
  240. //
  241. // Returns:
  242. //
  243. // History: 06-Sep-2000 JeffJon Created
  244. //
  245. //---------------------------------------------------------------------------
  246. CDSCmdBasePathsInfo::CDSCmdBasePathsInfo()
  247. : m_bInitialized(false),
  248. m_bModeInitialized(false),
  249. m_bDomainMode(true)
  250. {
  251. }
  252. //+--------------------------------------------------------------------------
  253. //
  254. // Member: CDSCmdBasePathsInfo::~CDSCmdBasePathsInfo
  255. //
  256. // Synopsis: Destructor for the base paths management class
  257. //
  258. // Arguments:
  259. //
  260. // Returns:
  261. //
  262. // History: 06-Sep-2000 JeffJon Created
  263. //
  264. //---------------------------------------------------------------------------
  265. CDSCmdBasePathsInfo::~CDSCmdBasePathsInfo()
  266. {
  267. }
  268. //+--------------------------------------------------------------------------
  269. //
  270. // Member: CDSCmdBasePathsInfo::InitializeFromName
  271. //
  272. // Synopsis: Initializes all the member strings for the well known
  273. // naming contexts by connecting to the RootDSE of the server or
  274. // domain that is passed in.
  275. //
  276. // Arguments: [refCredentialObject - IN] : a reference to the credential manager
  277. // [pszServerOrDomain - IN] : a NULL terminated wide character string
  278. // that contains the name of the domain or
  279. // server to connect to
  280. // [bServerName - IN] : Specifies whether the name given by
  281. // pszServerOrDomain is a server name (true)
  282. // or a domain name (false)
  283. //
  284. // Returns: HRESULT : S_OK if everything succeeded
  285. // E_OUTOFMEMORY if an allocation for one of the strings
  286. // failed
  287. // Anything else is a failure code from an ADSI call
  288. //
  289. // History: 06-Sep-2000 JeffJon Created
  290. //
  291. //---------------------------------------------------------------------------
  292. HRESULT CDSCmdBasePathsInfo::InitializeFromName(const CDSCmdCredentialObject& refCredentialObject,
  293. PCWSTR pszServerOrDomain,
  294. bool bServerName)
  295. {
  296. ENTER_FUNCTION_HR(LEVEL5_LOGGING, CDSCmdBasePathsInfo::InitializeFromName, hr);
  297. do // false loop
  298. {
  299. //
  300. // Check to see if we are already initialized
  301. //
  302. if (IsInitialized())
  303. {
  304. DEBUG_OUTPUT(LEVEL5_LOGGING, L"Base paths info already initialized");
  305. break;
  306. }
  307. //
  308. // Create the path to the RootDSE
  309. //
  310. CComBSTR sbstrRootDSE;
  311. sbstrRootDSE = g_bstrLDAPProvider;
  312. if (pszServerOrDomain)
  313. {
  314. sbstrRootDSE += pszServerOrDomain;
  315. sbstrRootDSE += L"/";
  316. }
  317. sbstrRootDSE += g_bstrRootDSE;
  318. //
  319. // Now sbstrRootDSE should either be in the form "LDAP://<serverOrDomain>/RootDSE"
  320. // or "LDAP://RootDSE"
  321. //
  322. //
  323. // Bind to the RootDSE
  324. //
  325. hr = DSCmdOpenObject(refCredentialObject,
  326. sbstrRootDSE,
  327. IID_IADs,
  328. (void**)&m_spRootDSE,
  329. false);
  330. if (FAILED(hr))
  331. {
  332. break;
  333. }
  334. if (bServerName)
  335. {
  336. m_sbstrServerName = pszServerOrDomain;
  337. }
  338. else
  339. {
  340. //
  341. // Get the configuration naming context
  342. //
  343. CComVariant var;
  344. hr = m_spRootDSE->Get(g_bstrConfigNCProperty, &var);
  345. if (FAILED(hr))
  346. {
  347. DEBUG_OUTPUT(LEVEL5_LOGGING, L"Failed to get the Configuration Naming Context: hr = 0x%x", hr);
  348. break;
  349. }
  350. if (var.vt != VT_BSTR)
  351. {
  352. DEBUG_OUTPUT(LEVEL5_LOGGING, L"The variant returned from Get(Config) isn't a VT_BSTR!");
  353. hr = E_FAIL;
  354. break;
  355. }
  356. m_sbstrConfigNamingContext = var.bstrVal;
  357. DEBUG_OUTPUT(LEVEL5_LOGGING, L"ConfigNC = %s", m_sbstrConfigNamingContext);
  358. //
  359. // Get the server name that we are connected to
  360. //
  361. //
  362. // Create the path to the config naming context
  363. //
  364. CComBSTR sbstrConfigPath;
  365. sbstrConfigPath = g_bstrLDAPProvider;
  366. if (pszServerOrDomain)
  367. {
  368. sbstrConfigPath += pszServerOrDomain;
  369. sbstrConfigPath += L"/";
  370. }
  371. sbstrConfigPath += m_sbstrConfigNamingContext;
  372. //
  373. // Bind to the configuration container
  374. //
  375. CComPtr<IADsObjectOptions> spIADsObjectOptions;
  376. hr = DSCmdOpenObject(refCredentialObject,
  377. sbstrConfigPath,
  378. IID_IADsObjectOptions,
  379. (void**)&spIADsObjectOptions,
  380. false);
  381. if (FAILED(hr))
  382. {
  383. break;
  384. }
  385. //
  386. // Retrieve the server name
  387. //
  388. var.Clear();
  389. hr = spIADsObjectOptions->GetOption(ADS_OPTION_SERVERNAME, &var);
  390. if (FAILED(hr))
  391. {
  392. DEBUG_OUTPUT(LEVEL5_LOGGING, L"Failed to get the server name: hr = 0x%x", hr);
  393. break;
  394. }
  395. if (var.vt != VT_BSTR)
  396. {
  397. DEBUG_OUTPUT(LEVEL5_LOGGING, L"The variant returned from GetOption isn't a VT_BSTR!");
  398. hr = E_FAIL;
  399. break;
  400. }
  401. //
  402. // Store the server name
  403. //
  404. m_sbstrServerName = V_BSTR(&var);
  405. DEBUG_OUTPUT(LEVEL5_LOGGING, L"Server name = %s", m_sbstrServerName);
  406. }
  407. //
  408. // Create the provider plus server name string
  409. //
  410. m_sbstrProviderAndServerName = g_bstrLDAPProvider;
  411. m_sbstrProviderAndServerName += m_sbstrServerName;
  412. m_sbstrGCProvider = g_bstrGCProvider;
  413. m_bInitialized = true;
  414. } while (false);
  415. return hr;
  416. }
  417. //+--------------------------------------------------------------------------
  418. //
  419. // Member: CDSCmdBasePathsInfo::GetConfigurationNamingContext
  420. //
  421. // Synopsis: Returns the the DN of the Configuration container
  422. //
  423. // Arguments:
  424. //
  425. // Returns: CComBSTR : A copy of the CComBSTR containing the string
  426. //
  427. // History: 06-Sep-2000 JeffJon Created
  428. //
  429. //---------------------------------------------------------------------------
  430. CComBSTR CDSCmdBasePathsInfo::GetConfigurationNamingContext() const
  431. {
  432. ENTER_FUNCTION(LEVEL5_LOGGING, CDSCmdBasePathsInfo::GetConfigurationNamingContext);
  433. if (IsInitialized() &&
  434. !m_sbstrConfigNamingContext.Length())
  435. {
  436. //
  437. // Get the configuration naming context
  438. //
  439. CComVariant var;
  440. HRESULT hr = m_spRootDSE->Get(g_bstrConfigNCProperty, &var);
  441. if (SUCCEEDED(hr) &&
  442. var.vt == VT_BSTR)
  443. {
  444. m_sbstrConfigNamingContext = var.bstrVal;
  445. DEBUG_OUTPUT(LEVEL5_LOGGING, L"ConfigNC = %s", m_sbstrConfigNamingContext);
  446. }
  447. else
  448. {
  449. DEBUG_OUTPUT(LEVEL5_LOGGING, L"Failed to retrieve the ConfigNC: hr = 0x%x", hr);
  450. }
  451. }
  452. return m_sbstrConfigNamingContext;
  453. }
  454. //+--------------------------------------------------------------------------
  455. //
  456. // Member: CDSCmdBasePathsInfo::GetSchemaNamingContext
  457. //
  458. // Synopsis: Returns the the DN of the Schema container
  459. //
  460. // Arguments:
  461. //
  462. // Returns: CComBSTR : A copy of the CComBSTR containing the string
  463. //
  464. // History: 06-Sep-2000 JeffJon Created
  465. //
  466. //---------------------------------------------------------------------------
  467. CComBSTR CDSCmdBasePathsInfo::GetSchemaNamingContext() const
  468. {
  469. ENTER_FUNCTION(LEVEL5_LOGGING, CDSCmdBasePathsInfo::GetSchemaNamingContext);
  470. if (IsInitialized() &&
  471. !m_sbstrSchemaNamingContext.Length())
  472. {
  473. //
  474. // Get the schema naming context
  475. //
  476. CComVariant var;
  477. HRESULT hr = m_spRootDSE->Get(g_bstrSchemaNCProperty, &var);
  478. if (SUCCEEDED(hr) &&
  479. var.vt == VT_BSTR)
  480. {
  481. m_sbstrSchemaNamingContext = var.bstrVal;
  482. DEBUG_OUTPUT(LEVEL5_LOGGING, L"SchemaNC = %s", m_sbstrConfigNamingContext);
  483. }
  484. else
  485. {
  486. DEBUG_OUTPUT(LEVEL5_LOGGING, L"Failed to retrieve the SchemaNC: hr = 0x%x", hr);
  487. }
  488. }
  489. return m_sbstrSchemaNamingContext;
  490. }
  491. //+--------------------------------------------------------------------------
  492. //
  493. // Member: CDSCmdBasePathsInfo::GetDefaultNamingContext
  494. //
  495. // Synopsis: Returns the the DN of the Domain
  496. //
  497. // Arguments:
  498. //
  499. // Returns: CComBSTR : A copy of the CComBSTR containing the string
  500. //
  501. // History: 06-Sep-2000 JeffJon Created
  502. //
  503. //---------------------------------------------------------------------------
  504. CComBSTR CDSCmdBasePathsInfo::GetDefaultNamingContext() const
  505. {
  506. ENTER_FUNCTION(LEVEL5_LOGGING, CDSCmdBasePathsInfo::GetDefaultNamingContext);
  507. if (IsInitialized() &&
  508. !m_sbstrDefaultNamingContext.Length())
  509. {
  510. //
  511. // Get the schema naming context
  512. //
  513. CComVariant var;
  514. HRESULT hr = m_spRootDSE->Get(g_bstrDefaultNCProperty, &var);
  515. if (SUCCEEDED(hr) &&
  516. var.vt == VT_BSTR)
  517. {
  518. m_sbstrDefaultNamingContext = var.bstrVal;
  519. DEBUG_OUTPUT(LEVEL5_LOGGING, L"DefaultNC = %s", m_sbstrDefaultNamingContext);
  520. }
  521. else
  522. {
  523. DEBUG_OUTPUT(LEVEL5_LOGGING, L"Failed to retrieve the DefaultNC: hr = 0x%x", hr);
  524. }
  525. }
  526. return m_sbstrDefaultNamingContext;
  527. }
  528. //+--------------------------------------------------------------------------
  529. //
  530. // Member: CDSCmdBasePathsInfo::GetDomainMode
  531. //
  532. // Synopsis: Figures out if the domain is in mixed or native mode
  533. //
  534. // Arguments: [refCredObject - IN] : reference to the credential manager
  535. // [bMixedMode - OUT] : Is the domain in mixed mode?
  536. //
  537. // Returns: HRESULT : S_OK if everything succeeded
  538. // Otherwise an ADSI error
  539. //
  540. // History: 24-Oct-2000 JeffJon Created
  541. //
  542. //---------------------------------------------------------------------------
  543. HRESULT CDSCmdBasePathsInfo::GetDomainMode(const CDSCmdCredentialObject& refCredObject,
  544. bool& bMixedMode) const
  545. {
  546. ENTER_FUNCTION_HR(LEVEL5_LOGGING, CDSCmdBasePathsInfo::GetDomainMode, hr);
  547. hr = S_OK;
  548. do // false loop
  549. {
  550. if (!m_bModeInitialized)
  551. {
  552. //
  553. // Get the path to the domainDNS node
  554. //
  555. CComBSTR sbstrDomainDN;
  556. sbstrDomainDN = GetDefaultNamingContext();
  557. CComBSTR sbstrDomainPath;
  558. ComposePathFromDN(sbstrDomainDN, sbstrDomainPath);
  559. //
  560. // Open the domainDNS node
  561. //
  562. CComPtr<IADs> spADs;
  563. hr = DSCmdOpenObject(refCredObject,
  564. sbstrDomainPath,
  565. IID_IADs,
  566. (void**)&spADs,
  567. true);
  568. if (FAILED(hr))
  569. {
  570. break;
  571. }
  572. CComVariant var;
  573. hr = spADs->Get(L"nTMixedDomain", &var);
  574. if (FAILED(hr))
  575. {
  576. DEBUG_OUTPUT(MINIMAL_LOGGING,
  577. L"Failed to retrieve the domain mode: hr = 0x%x",
  578. hr);
  579. break;
  580. }
  581. if (var.vt == VT_I4)
  582. {
  583. m_bDomainMode = (var.lVal != 0);
  584. m_bModeInitialized = true;
  585. }
  586. else
  587. {
  588. DEBUG_OUTPUT(MINIMAL_LOGGING,
  589. L"Variant not an VT_I4!");
  590. m_bDomainMode = true;
  591. m_bModeInitialized = true;
  592. }
  593. }
  594. bMixedMode = m_bDomainMode;
  595. } while (false);
  596. return hr;
  597. }
  598. //+--------------------------------------------------------------------------
  599. //
  600. // Member: CDSCmdBasePathsInfo::ComposePathFromDN
  601. //
  602. // Synopsis: Appends the DN to the provider and server name
  603. //
  604. // Arguments: [pszDN - IN] : pointer to a NULL terminated wide character string
  605. // that contains the DN of the object to make the ADSI
  606. // path to
  607. // [refsbstrPath - OUT] : reference to a CComBSTR that will take
  608. // the full ADSI path
  609. // [nProviderType - OPTIONAL IN] : Option to specify which provider
  610. // to compose the path with
  611. //
  612. // Returns:
  613. //
  614. // History: 06-Sep-2000 JeffJon Created
  615. //
  616. //---------------------------------------------------------------------------
  617. void CDSCmdBasePathsInfo::ComposePathFromDN(PCWSTR pszDN,
  618. CComBSTR& refsbstrPath,
  619. DSCMD_PROVIDER_TYPE nProviderType) const
  620. {
  621. refsbstrPath.Empty();
  622. switch (nProviderType)
  623. {
  624. case DSCMD_LDAP_PROVIDER :
  625. refsbstrPath = GetProviderAndServerName();
  626. break;
  627. case DSCMD_GC_PROVIDER :
  628. refsbstrPath = GetGCProvider();
  629. break;
  630. default :
  631. ASSERT(FALSE);
  632. break;
  633. }
  634. refsbstrPath += L"/";
  635. refsbstrPath += pszDN;
  636. }
  637. //+--------------------------------------------------------------------------
  638. //
  639. // Member: CPathCracker::CPathCracker
  640. //
  641. // Synopsis: Constructor for the path cracker IADsPathname wrapper
  642. //
  643. // Arguments:
  644. //
  645. // Returns:
  646. //
  647. // History: 06-Sep-2000 JeffJon Created
  648. //
  649. //---------------------------------------------------------------------------
  650. CPathCracker::CPathCracker()
  651. {
  652. m_hrCreate = Init();
  653. }
  654. //+--------------------------------------------------------------------------
  655. //
  656. // Member: CPathCracker::Init
  657. //
  658. // Synopsis: Called by the constructor to create the IADsPathname object
  659. // and store it in the m_spIADsPathname member
  660. //
  661. // Arguments:
  662. //
  663. // Returns: HRESULT : the value returned from the CoCreateInstance
  664. //
  665. // History: 06-Sep-2000 JeffJon Created
  666. //
  667. //---------------------------------------------------------------------------
  668. HRESULT CPathCracker::Init()
  669. {
  670. HRESULT hr = ::CoCreateInstance(CLSID_Pathname,
  671. NULL,
  672. CLSCTX_INPROC_SERVER,
  673. IID_IADsPathname,
  674. (void**)&(m_spIADsPathname));
  675. return hr;
  676. }
  677. //+--------------------------------------------------------------------------
  678. //
  679. // Member: CPathCracker::GetParentDN
  680. //
  681. // Synopsis: Simply removes the leaf part of the DN
  682. //
  683. // Arguments: [pszDN - IN] : pointer to a NULL terminated wide string that
  684. // contains the DN of the child
  685. // [refsbstrDN - OUT] : reference to a CComBSTR that is to
  686. // receive the parent DN.
  687. //
  688. // Returns: HRESULT : S_OK if successful, otherwise the value returned
  689. // from the IADsPathname methods
  690. //
  691. // History: 06-Sep-2000 JeffJon Created
  692. //
  693. //---------------------------------------------------------------------------
  694. HRESULT CPathCracker::GetParentDN(PCWSTR pszDN,
  695. CComBSTR& refsbstrDN)
  696. {
  697. ENTER_FUNCTION_HR(FULL_LOGGING, CPathCracker::GetParentDN, hr);
  698. do // false loop
  699. {
  700. //
  701. // Verify parameters
  702. //
  703. if (!pszDN)
  704. {
  705. ASSERT(pszDN);
  706. hr = E_INVALIDARG;
  707. break;
  708. }
  709. refsbstrDN.Empty();
  710. CPathCracker pathCracker;
  711. hr = pathCracker.Set((BSTR)pszDN, ADS_SETTYPE_DN);
  712. if (FAILED(hr))
  713. {
  714. break;
  715. }
  716. hr = pathCracker.RemoveLeafElement();
  717. if (FAILED(hr))
  718. {
  719. break;
  720. }
  721. hr = pathCracker.Retrieve(ADS_FORMAT_X500_DN, &refsbstrDN);
  722. } while (false);
  723. return hr;
  724. }
  725. //+--------------------------------------------------------------------------
  726. //
  727. // Member: CPathCracker::GetObjectRDNFromDN
  728. //
  729. // Synopsis: Returns the leaf part of the DN
  730. //
  731. // Arguments: [pszDN - IN] : pointer to a NULL terminated wide string that
  732. // contains the DN of the child
  733. // [refsbstrRDN - OUT] : reference to a CComBSTR that is to
  734. // receive the leaf RDN.
  735. //
  736. // Returns: HRESULT : S_OK if successful, otherwise the value returned
  737. // from the IADsPathname methods
  738. //
  739. // History: 25-Sep-2000 JeffJon Created
  740. //
  741. //---------------------------------------------------------------------------
  742. HRESULT CPathCracker::GetObjectRDNFromDN(PCWSTR pszDN,
  743. CComBSTR& refsbstrRDN)
  744. {
  745. ENTER_FUNCTION_HR(FULL_LOGGING, CPathCracker::GetObjectRDNFromDN, hr);
  746. do // false loop
  747. {
  748. //
  749. // Verify parameters
  750. //
  751. if (!pszDN)
  752. {
  753. ASSERT(pszDN);
  754. hr = E_INVALIDARG;
  755. break;
  756. }
  757. refsbstrRDN.Empty();
  758. CPathCracker pathCracker;
  759. hr = pathCracker.Set((BSTR)pszDN, ADS_SETTYPE_DN);
  760. if (FAILED(hr))
  761. {
  762. break;
  763. }
  764. hr = pathCracker.SetDisplayType(ADS_DISPLAY_FULL);
  765. if (FAILED(hr))
  766. {
  767. break;
  768. }
  769. hr = pathCracker.Retrieve(ADS_FORMAT_LEAF, &refsbstrRDN);
  770. } while (false);
  771. return hr;
  772. }
  773. //+--------------------------------------------------------------------------
  774. //
  775. // Member: CPathCracker::GetObjectNameFromDN
  776. //
  777. // Synopsis: Returns the value of the leaf part of the DN
  778. //
  779. // Arguments: [pszDN - IN] : pointer to a NULL terminated wide string that
  780. // contains the DN of the child
  781. // [refsbstrRDN - OUT] : reference to a CComBSTR that is to
  782. // receive the leaf Name.
  783. //
  784. // Returns: HRESULT : S_OK if successful, otherwise the value returned
  785. // from the IADsPathname methods
  786. //
  787. // History: 04-Oct-2000 JeffJon Created
  788. //
  789. //---------------------------------------------------------------------------
  790. HRESULT CPathCracker::GetObjectNameFromDN(PCWSTR pszDN,
  791. CComBSTR& refsbstrRDN)
  792. {
  793. ENTER_FUNCTION_HR(FULL_LOGGING, CPathCracker::GetObjectNameFromDN, hr);
  794. do // false loop
  795. {
  796. //
  797. // Verify parameters
  798. //
  799. if (!pszDN)
  800. {
  801. ASSERT(pszDN);
  802. hr = E_INVALIDARG;
  803. break;
  804. }
  805. refsbstrRDN.Empty();
  806. CPathCracker pathCracker;
  807. hr = pathCracker.Set((BSTR)pszDN, ADS_SETTYPE_DN);
  808. if (FAILED(hr))
  809. {
  810. break;
  811. }
  812. hr = pathCracker.SetDisplayType(ADS_DISPLAY_VALUE_ONLY);
  813. if (FAILED(hr))
  814. {
  815. break;
  816. }
  817. hr = pathCracker.Retrieve(ADS_FORMAT_LEAF, &refsbstrRDN);
  818. } while (false);
  819. return hr;
  820. }
  821. //+--------------------------------------------------------------------------
  822. //
  823. // Member: CPathCracker::GetDNFromPath
  824. //
  825. // Synopsis: Returns the DN when given and ADSI path
  826. //
  827. // Arguments: [pszPath - IN] : pointer to a NULL terminated wide string that
  828. // contains the ADSI path of the object
  829. // [refsbstrDN - OUT] : reference to a CComBSTR that is to
  830. // receive the DN.
  831. //
  832. // Returns: HRESULT : S_OK if successful, otherwise the value returned
  833. // from the IADsPathname methods
  834. //
  835. // History: 24-Oct-2000 JeffJon Created
  836. //
  837. //---------------------------------------------------------------------------
  838. HRESULT CPathCracker::GetDNFromPath(PCWSTR pszPath,
  839. CComBSTR& refsbstrDN)
  840. {
  841. ENTER_FUNCTION_HR(FULL_LOGGING, CPathCracker::GetDNFromPath, hr);
  842. do // false loop
  843. {
  844. //
  845. // Verify parameters
  846. //
  847. if (!pszPath)
  848. {
  849. ASSERT(pszPath);
  850. hr = E_INVALIDARG;
  851. break;
  852. }
  853. refsbstrDN.Empty();
  854. CPathCracker pathCracker;
  855. hr = pathCracker.Set((BSTR)pszPath, ADS_SETTYPE_FULL);
  856. if (FAILED(hr))
  857. {
  858. break;
  859. }
  860. hr = pathCracker.SetDisplayType(ADS_DISPLAY_FULL);
  861. if (FAILED(hr))
  862. {
  863. break;
  864. }
  865. hr = pathCracker.Retrieve(ADS_FORMAT_X500_DN, &refsbstrDN);
  866. } while (false);
  867. return hr;
  868. }
  869. ///////////////////////////////////////////////////////////////////////////////////
  870. //+--------------------------------------------------------------------------
  871. //
  872. // Function: DSCmdOpenObject
  873. //
  874. // Synopsis: A wrapper around ADsOpenObject
  875. //
  876. // Arguments: [refCredentialObject - IN] : a reference to a credential management object
  877. // [pszPath - IN] : a pointer to a NULL terminated wide character
  878. // string that contains the ADSI path of the
  879. // object to connect to
  880. // [refIID - IN] : the interface ID of the interface to return
  881. // [ppObject - OUT] : a pointer which is to receive the interface pointer
  882. // [bBindToServer - IN] : true if the path contains a server name,
  883. // false otherwise
  884. //
  885. // Returns: HRESULT : S_OK if everything succeeded
  886. // Anything else is a failure code from an ADSI call
  887. //
  888. // History: 06-Sep-2000 JeffJon Created
  889. //
  890. //---------------------------------------------------------------------------
  891. HRESULT DSCmdOpenObject(const CDSCmdCredentialObject& refCredentialObject,
  892. PCWSTR pszPath,
  893. REFIID refIID,
  894. void** ppObject,
  895. bool bBindToServer)
  896. {
  897. ENTER_FUNCTION_HR(FULL_LOGGING, DSCmdOpenObject, hr);
  898. do // false loop
  899. {
  900. DWORD dwFlags = ADS_SECURE_AUTHENTICATION;
  901. if (!pszPath ||
  902. !ppObject)
  903. {
  904. ASSERT(pszPath);
  905. ASSERT(ppObject);
  906. hr = E_INVALIDARG;
  907. break;
  908. }
  909. if (bBindToServer)
  910. {
  911. //
  912. // If we know we are connecting to a specific server and not domain in general
  913. // then pass the ADS_SERVER_BIND flag to save ADSI the trouble of figuring it out
  914. //
  915. dwFlags |= ADS_SERVER_BIND;
  916. DEBUG_OUTPUT(FULL_LOGGING, L"Using ADS_SERVER_BIND flag");
  917. }
  918. if (refCredentialObject.UsingCredentials())
  919. {
  920. DEBUG_OUTPUT(FULL_LOGGING, L"Using credentials");
  921. WCHAR szPasswordBuffer[MAX_PASSWORD_LENGTH];
  922. UINT sBufferSize = static_cast<UINT>(sizeof(szPasswordBuffer));
  923. hr = refCredentialObject.GetPassword(szPasswordBuffer,
  924. &sBufferSize);
  925. if (FAILED(hr))
  926. {
  927. DEBUG_OUTPUT(FULL_LOGGING, L"GetPassword failed: hr = 0x%x", hr);
  928. DEBUG_OUTPUT(FULL_LOGGING, L"Using NULL password.");
  929. ZeroMemory(&szPasswordBuffer, sizeof(szPasswordBuffer));
  930. }
  931. DEBUG_OUTPUT(FULL_LOGGING, L"Calling ADsOpenObject()");
  932. DEBUG_OUTPUT(FULL_LOGGING, L" path = %s", pszPath);
  933. hr = ADsOpenObject((LPWSTR)pszPath,
  934. refCredentialObject.GetUsername(),
  935. szPasswordBuffer,
  936. dwFlags,
  937. refIID,
  938. ppObject);
  939. //
  940. // If we failed with E_INVALIDARG and we were using ADS_SERVER_BIND
  941. // try calling again without the ADS_SERVER_BIND flag. W2K did not have
  942. // this flag available until SP1.
  943. //
  944. if (hr == E_INVALIDARG &&
  945. (dwFlags & ADS_SERVER_BIND))
  946. {
  947. DEBUG_OUTPUT(FULL_LOGGING, L"ADsOpenObject failed with E_INVALIDARG, trying again without ADS_SERVER_BIND");
  948. dwFlags &= ~ADS_SERVER_BIND;
  949. hr = ADsOpenObject((LPWSTR)pszPath,
  950. refCredentialObject.GetUsername(),
  951. szPasswordBuffer,
  952. dwFlags,
  953. refIID,
  954. ppObject);
  955. }
  956. //
  957. // Make sure to zero out the password after it is used
  958. //
  959. ZeroMemory(szPasswordBuffer, sizeof(szPasswordBuffer));
  960. }
  961. else
  962. {
  963. DEBUG_OUTPUT(FULL_LOGGING, L"Calling ADsOpenObject()");
  964. DEBUG_OUTPUT(FULL_LOGGING, L" path = %s", pszPath);
  965. hr = ADsOpenObject((LPWSTR)pszPath,
  966. NULL,
  967. NULL,
  968. dwFlags,
  969. refIID,
  970. ppObject);
  971. //
  972. // If we failed with E_INVALIDARG and we were using ADS_SERVER_BIND
  973. // try calling again without the ADS_SERVER_BIND flag. W2K did not have
  974. // this flag available until SP1.
  975. //
  976. if (hr == E_INVALIDARG &&
  977. (dwFlags & ADS_SERVER_BIND))
  978. {
  979. DEBUG_OUTPUT(FULL_LOGGING, L"ADsOpenObject failed with E_INVALIDARG, trying again without ADS_SERVER_BIND");
  980. dwFlags &= ~ADS_SERVER_BIND;
  981. hr = ADsOpenObject((LPWSTR)pszPath,
  982. NULL,
  983. NULL,
  984. dwFlags,
  985. refIID,
  986. ppObject);
  987. }
  988. }
  989. DEBUG_OUTPUT(FULL_LOGGING, L"ADsOpenObject() return hr = 0x%x", hr);
  990. } while (false);
  991. return hr;
  992. }
  993. //+--------------------------------------------------------------------------
  994. // Function to be used in the attribute table for evaluating the command line
  995. // strings
  996. //---------------------------------------------------------------------------
  997. //+--------------------------------------------------------------------------
  998. //
  999. // Function: FillAttrInfoFromObjectEntry
  1000. //
  1001. // Synopsis: Fills the ADS_ATTR_INFO from the attribute table associated
  1002. // with the object entry
  1003. //
  1004. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  1005. // to the object being modified
  1006. // [refBasePathsInfo - IN] : reference to an instance of the
  1007. // CDSCmdBasePathsInfo class
  1008. // [refCredentialObject - IN] : reference to an instance of the
  1009. // CDSCmdCredentialObject class
  1010. // [pObjectEntry - IN] : pointer to an entry in the object table
  1011. // that defines the object we are modifying
  1012. // [argRecord - IN] : the argument record structure from the
  1013. // parser table that corresponds to this
  1014. // attribute
  1015. // [dwAttributeIdx - IN] : index into the attribute table for the
  1016. // object in which we are setting
  1017. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  1018. // which this function will fill in
  1019. //
  1020. // Returns: HRESULT : S_OK if everything succeeded
  1021. // E_OUTOFMEMORY if we failed to allocate space for the value
  1022. // E_FAIL if we failed to format the value properly
  1023. //
  1024. // History: 06-Sep-2000 JeffJon Created
  1025. //
  1026. //---------------------------------------------------------------------------
  1027. HRESULT FillAttrInfoFromObjectEntry(PCWSTR /*pszDN*/,
  1028. const CDSCmdBasePathsInfo& refBasePathsInfo,
  1029. const CDSCmdCredentialObject& refCredentialObject,
  1030. const PDSOBJECTTABLEENTRY pObjectEntry,
  1031. const ARG_RECORD& argRecord,
  1032. DWORD dwAttributeIdx,
  1033. PADS_ATTR_INFO* ppAttr)
  1034. {
  1035. ENTER_FUNCTION_HR(LEVEL3_LOGGING, FillAttrInfoFromObjectEntry, hr);
  1036. do // false loop
  1037. {
  1038. //
  1039. // Verify Parameters
  1040. //
  1041. if (!pObjectEntry ||
  1042. !ppAttr)
  1043. {
  1044. ASSERT(pObjectEntry);
  1045. ASSERT(ppAttr);
  1046. hr = E_INVALIDARG;
  1047. break;
  1048. }
  1049. switch (argRecord.fType)
  1050. {
  1051. case ARG_TYPE_STR :
  1052. DEBUG_OUTPUT(LEVEL3_LOGGING, L"argRecord.fType = ARG_TYPE_STR");
  1053. *ppAttr = &(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo);
  1054. if (argRecord.strValue && argRecord.strValue[0] != L'\0')
  1055. {
  1056. //
  1057. // REVIEW_JEFFJON : this is being leaked!
  1058. //
  1059. (*ppAttr)->pADsValues = new ADSVALUE[1];
  1060. if ((*ppAttr)->pADsValues)
  1061. {
  1062. (*ppAttr)->dwNumValues = 1;
  1063. (*ppAttr)->pADsValues->dwType = (*ppAttr)->dwADsType;
  1064. switch ((*ppAttr)->dwADsType)
  1065. {
  1066. case ADSTYPE_DN_STRING :
  1067. {
  1068. //
  1069. // Lets bind to be sure the object exists
  1070. //
  1071. CComBSTR sbstrObjPath;
  1072. refBasePathsInfo.ComposePathFromDN(argRecord.strValue, sbstrObjPath);
  1073. CComPtr<IADs> spIADs;
  1074. hr = DSCmdOpenObject(refCredentialObject,
  1075. sbstrObjPath,
  1076. IID_IADs,
  1077. (void**)&spIADs,
  1078. true);
  1079. if (FAILED(hr))
  1080. {
  1081. DEBUG_OUTPUT(LEVEL3_LOGGING, L"DN object doesn't exist. %s", argRecord.strValue);
  1082. break;
  1083. }
  1084. (*ppAttr)->pADsValues->DNString = argRecord.strValue;
  1085. DEBUG_OUTPUT(LEVEL3_LOGGING, L"ADSTYPE_DN_STRING = %s", argRecord.strValue);
  1086. }
  1087. break;
  1088. case ADSTYPE_CASE_EXACT_STRING :
  1089. (*ppAttr)->pADsValues->CaseExactString = argRecord.strValue;
  1090. DEBUG_OUTPUT(LEVEL3_LOGGING, L"ADSTYPE_CASE_EXACT_STRING = %s", argRecord.strValue);
  1091. break;
  1092. case ADSTYPE_CASE_IGNORE_STRING :
  1093. (*ppAttr)->pADsValues->CaseIgnoreString = argRecord.strValue;
  1094. DEBUG_OUTPUT(LEVEL3_LOGGING, L"ADSTYPE_CASE_IGNORE_STRING = %s", argRecord.strValue);
  1095. break;
  1096. case ADSTYPE_PRINTABLE_STRING :
  1097. (*ppAttr)->pADsValues->PrintableString = argRecord.strValue;
  1098. DEBUG_OUTPUT(LEVEL3_LOGGING, L"ADSTYPE_PRINTABLE_STRING = %s", argRecord.strValue);
  1099. break;
  1100. default :
  1101. hr = E_INVALIDARG;
  1102. break;
  1103. }
  1104. //
  1105. // Set the attribute dirty
  1106. //
  1107. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags |= DS_ATTRIBUTE_DIRTY;
  1108. }
  1109. break;
  1110. }
  1111. else
  1112. {
  1113. DEBUG_OUTPUT(LEVEL3_LOGGING, L"No value present, changing control code to ADS_ATTR_CLEAR");
  1114. //
  1115. // Clear the attribute
  1116. //
  1117. (*ppAttr)->dwControlCode = ADS_ATTR_CLEAR;
  1118. (*ppAttr)->dwNumValues = 0;
  1119. //
  1120. // Set the attribute dirty
  1121. //
  1122. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags |= DS_ATTRIBUTE_DIRTY;
  1123. }
  1124. break;
  1125. default:
  1126. hr = E_INVALIDARG;
  1127. break;
  1128. }
  1129. } while (false);
  1130. return hr;
  1131. }
  1132. //+--------------------------------------------------------------------------
  1133. //
  1134. // Function: ResetObjectPassword
  1135. //
  1136. // Synopsis: Resets the password on any object that supports the IADsUser interface
  1137. //
  1138. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  1139. // to the object being modified
  1140. // [refBasePathsInfo - IN] : reference to an instance of the
  1141. // CDSCmdBasePathsInfo class
  1142. // [refCredentialObject - IN] : reference to an instance of the
  1143. // CDSCmdCredentialObject class
  1144. // [pszNewPassword - IN] : pointer to the new password to set
  1145. //
  1146. // Returns: HRESULT : S_OK if everything succeeded
  1147. // Otherwise its an ADSI failure code
  1148. //
  1149. // History: 12-Sep-2000 JeffJon Created
  1150. //
  1151. //---------------------------------------------------------------------------
  1152. HRESULT ResetObjectPassword(PCWSTR pszDN,
  1153. const CDSCmdBasePathsInfo& refBasePathsInfo,
  1154. const CDSCmdCredentialObject& refCredentialObject,
  1155. PCWSTR pszNewPassword)
  1156. {
  1157. ENTER_FUNCTION_HR(LEVEL3_LOGGING, ResetObjectPassword, hr);
  1158. do // false loop
  1159. {
  1160. if (!pszDN ||
  1161. !pszNewPassword)
  1162. {
  1163. ASSERT(pszDN);
  1164. ASSERT(pszNewPassword);
  1165. hr = E_INVALIDARG;
  1166. break;
  1167. }
  1168. //
  1169. // Convert the DN to a path
  1170. //
  1171. CComBSTR sbstrPath;
  1172. refBasePathsInfo.ComposePathFromDN(pszDN, sbstrPath);
  1173. //
  1174. // Bind and obtain the IADsUser interface to the user object
  1175. //
  1176. CComPtr<IADsUser> spUser;
  1177. hr = DSCmdOpenObject(refCredentialObject,
  1178. sbstrPath,
  1179. IID_IADsUser,
  1180. (void**)&spUser,
  1181. true);
  1182. if (FAILED(hr))
  1183. {
  1184. break;
  1185. }
  1186. hr = spUser->SetPassword((BSTR)pszNewPassword);
  1187. } while (false);
  1188. return hr;
  1189. }
  1190. //+--------------------------------------------------------------------------
  1191. //
  1192. // Function: ResetUserPassword
  1193. //
  1194. // Synopsis: Resets the user's password
  1195. //
  1196. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  1197. // to the object being modified
  1198. // [refBasePathsInfo - IN] : reference to an instance of the
  1199. // CDSCmdBasePathsInfo class
  1200. // [refCredentialObject - IN] : reference to an instance of the
  1201. // CDSCmdCredentialObject class
  1202. // [pObjectEntry - IN] : pointer to an entry in the object table
  1203. // that defines the object we are modifying
  1204. // [argRecord - IN] : the argument record structure from the
  1205. // parser table that corresponds to this
  1206. // attribute
  1207. // [dwAttributeIdx - IN] : index into the attribute table for the
  1208. // object in which we are setting
  1209. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  1210. // which this function will fill in
  1211. //
  1212. // Returns: HRESULT : S_OK if everything succeeded
  1213. // E_FAIL if we failed to format the value properly
  1214. //
  1215. // History: 11-Sep-2000 JeffJon Created
  1216. //
  1217. //---------------------------------------------------------------------------
  1218. HRESULT ResetUserPassword(PCWSTR pszDN,
  1219. const CDSCmdBasePathsInfo& refBasePathsInfo,
  1220. const CDSCmdCredentialObject& refCredentialObject,
  1221. const PDSOBJECTTABLEENTRY pObjectEntry,
  1222. const ARG_RECORD& argRecord,
  1223. DWORD /*dwAttributeIdx*/,
  1224. PADS_ATTR_INFO* ppAttr)
  1225. {
  1226. ENTER_FUNCTION_HR(LEVEL3_LOGGING, ResetUserPassword, hr);
  1227. do // false loop
  1228. {
  1229. //
  1230. // Verify parameters
  1231. //
  1232. if (!pszDN ||
  1233. !pObjectEntry ||
  1234. !ppAttr)
  1235. {
  1236. ASSERT(pszDN);
  1237. ASSERT(pObjectEntry);
  1238. ASSERT(ppAttr);
  1239. hr = E_INVALIDARG;
  1240. break;
  1241. }
  1242. //
  1243. // Don't create a new index in the array of ADS_ATTR_INFO
  1244. //
  1245. *ppAttr = NULL;
  1246. ASSERT(argRecord.bDefined && argRecord.strValue);
  1247. hr = ResetObjectPassword(pszDN,
  1248. refBasePathsInfo,
  1249. refCredentialObject,
  1250. argRecord.strValue);
  1251. if (FAILED(hr))
  1252. {
  1253. DisplayErrorMessage(g_pszDSCommandName,
  1254. pszDN,
  1255. hr,
  1256. IDS_FAILED_SET_PASSWORD);
  1257. hr = S_FALSE;
  1258. break;
  1259. }
  1260. } while (false);
  1261. return hr;
  1262. }
  1263. /*
  1264. //+--------------------------------------------------------------------------
  1265. //
  1266. // Function: ModifyNetWareUserPassword
  1267. //
  1268. // Synopsis: Resets the NetWare user's password
  1269. //
  1270. // Arguments: [pADsUser - IN] : pointer to IADsUser interface for user object
  1271. // [pwzADsPath - IN] : path to the user object
  1272. // [pwzNewPassword - IN] : the new password
  1273. //
  1274. // Returns: HRESULT : S_OK if everything succeeded
  1275. // Otherwise it is the return code from an ADSI call
  1276. //
  1277. // History: 11-Sep-2000 JeffJon Created
  1278. //
  1279. //---------------------------------------------------------------------------
  1280. HRESULT ModifyNetWareUserPassword(IADsUser* pADsUser,
  1281. PCWSTR pwzADsPath,
  1282. PCWSTR pwzNewPassword)
  1283. {
  1284. CComPtr<IDirectoryObject> spDsObj;
  1285. HRESULT hr = pADsUser->QueryInterface(IID_IDirectoryObject, reinterpret_cast<void **>(&spDsObj));
  1286. if (FAILED(hr))
  1287. {
  1288. return hr;
  1289. }
  1290. //
  1291. // Check to see if NWPassword exists on the object
  1292. //
  1293. PWSTR pszAttrs[] = { L"NWPassword" };
  1294. PADS_ATTR_INFO pAttrs = NULL;
  1295. DWORD dwAttrsReturned = 0;
  1296. hr = spDsObj->GetObjectAttributes(pszAttrs,
  1297. 1,
  1298. &pAttrs,
  1299. dwAttrsReturned);
  1300. if (S_OK == hr && dwAttrsReturned == 1 && pAttrs != NULL)
  1301. {
  1302. //
  1303. // This is a NetWare enabled user
  1304. //
  1305. //
  1306. // load fpnwclnt.dll
  1307. //
  1308. HINSTANCE hFPNWClntDll = NULL;
  1309. PMapRidToObjectId pfnMapRidToObjectId = NULL;
  1310. PSwapObjectId pfnSwapObjectId = NULL;
  1311. PReturnNetwareForm pfnReturnNetwareForm = NULL;
  1312. hFPNWClntDll = LoadLibrary(SZ_FPNWCLNT_DLL);
  1313. pfnMapRidToObjectId = reinterpret_cast<PMapRidToObjectId>(GetProcAddress(hFPNWClntDll, SZ_MAPRIDTOOBJECTID));
  1314. pfnSwapObjectId = reinterpret_cast<PSwapObjectId>(GetProcAddress(hFPNWClntDll, SZ_SWAPOBJECTID));
  1315. pfnReturnNetwareForm = reinterpret_cast<PReturnNetwareForm>(GetProcAddress(hFPNWClntDll, SZ_RETURNNETWAREFORM));
  1316. if (!hFPNWClntDll || !pfnMapRidToObjectId || !pfnSwapObjectId || !pfnReturnNetwareForm)
  1317. {
  1318. hr = HRESULT_FROM_WIN32( GetLastError() );
  1319. }
  1320. else
  1321. {
  1322. // get secret key
  1323. PWSTR pwzPath = NULL;
  1324. hr = SkipLDAPPrefix(pwzADsPath, pwzPath);
  1325. if (SUCCEEDED(hr))
  1326. {
  1327. PWSTR pwzPDCName = NULL, pwzSecretKey = NULL;
  1328. hr = GetPDCInfo(pwzPath, pwzPDCName, pwzSecretKey);
  1329. if (SUCCEEDED(hr))
  1330. {
  1331. // get object id
  1332. CStr cstrUserName;
  1333. DWORD dwObjectID = 0, dwSwappedObjectID = 0;
  1334. hr = GetNWUserInfo(spDsObj,
  1335. cstrUserName, // OUT
  1336. dwObjectID, // OUT
  1337. dwSwappedObjectID, // OUT
  1338. pfnMapRidToObjectId,
  1339. pfnSwapObjectId);
  1340. if (SUCCEEDED(hr))
  1341. {
  1342. // change password in the userParms
  1343. LONG err = SetNetWareUserPassword(cstrUserParms,
  1344. pwzSecretKey,
  1345. dwObjectID,
  1346. pwzNewPassword,
  1347. pfnReturnNetwareForm);
  1348. if (NERR_Success == err)
  1349. {
  1350. err = ResetNetWareUserPasswordTime(cstrUserParms, false); // clear the expire flag
  1351. }
  1352. hr = HRESULT_FROM_WIN32(err);
  1353. // write userParms back to DS
  1354. if (SUCCEEDED(hr))
  1355. {
  1356. hr = WriteUserParms(spDsObj, cstrUserParms);
  1357. }
  1358. }
  1359. }
  1360. if (pwzPath)
  1361. delete pwzPath;
  1362. }
  1363. }
  1364. if (hFPNWClntDll)
  1365. {
  1366. FreeLibrary(hFPNWClntDll);
  1367. }
  1368. }
  1369. return hr;
  1370. }
  1371. */
  1372. //+--------------------------------------------------------------------------
  1373. //
  1374. // Function: ResetComputerAccount
  1375. //
  1376. // Synopsis: Resets the computer account
  1377. //
  1378. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  1379. // to the object being modified
  1380. // [refBasePathsInfo - IN] : reference to an instance of the
  1381. // CDSCmdBasePathsInfo class
  1382. // [refCredentialObject - IN] : reference to an instance of the
  1383. // CDSCmdCredentialObject class
  1384. // [pObjectEntry - IN] : pointer to an entry in the object table
  1385. // that defines the object we are modifying
  1386. // [argRecord - IN] : the argument record structure from the
  1387. // parser table that corresponds to this
  1388. // attribute
  1389. // [dwAttributeIdx - IN] : index into the attribute table for the
  1390. // object in which we are setting
  1391. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  1392. // which this function will fill in
  1393. //
  1394. // Returns: HRESULT : S_OK if everything succeeded
  1395. // Otherwise an ADSI failure code
  1396. //
  1397. // History: 12-Sep-2000 JeffJon Created
  1398. //
  1399. //---------------------------------------------------------------------------
  1400. HRESULT ResetComputerAccount(PCWSTR pszDN,
  1401. const CDSCmdBasePathsInfo& refBasePathsInfo,
  1402. const CDSCmdCredentialObject& refCredentialObject,
  1403. const PDSOBJECTTABLEENTRY pObjectEntry,
  1404. const ARG_RECORD& argRecord,
  1405. DWORD /*dwAttributeIdx*/,
  1406. PADS_ATTR_INFO* ppAttr)
  1407. {
  1408. ENTER_FUNCTION_HR(LEVEL3_LOGGING, ResetComputerAccount, hr);
  1409. do // false loop
  1410. {
  1411. //
  1412. // Verify parameters
  1413. //
  1414. if (!pszDN ||
  1415. !pObjectEntry ||
  1416. !ppAttr)
  1417. {
  1418. ASSERT(pszDN);
  1419. ASSERT(pObjectEntry);
  1420. ASSERT(ppAttr);
  1421. hr = E_INVALIDARG;
  1422. break;
  1423. }
  1424. //
  1425. // Don't create a new entry in the ADS_ATTR_INFO array
  1426. //
  1427. *ppAttr = NULL;
  1428. ASSERT(argRecord.bDefined && argRecord.strValue);
  1429. //
  1430. // Retrieve the samAccountName from the computer object
  1431. //
  1432. //
  1433. // Convert the DN to a path
  1434. //
  1435. CComBSTR sbstrPath;
  1436. refBasePathsInfo.ComposePathFromDN(pszDN, sbstrPath);
  1437. //
  1438. // Bind and obtain the IADsUser interface to the user object
  1439. //
  1440. CComPtr<IADs> spADs;
  1441. hr = DSCmdOpenObject(refCredentialObject,
  1442. sbstrPath,
  1443. IID_IADs,
  1444. (void**)&spADs,
  1445. true);
  1446. if (FAILED(hr))
  1447. {
  1448. break;
  1449. }
  1450. CComVariant var;
  1451. hr = spADs->Get(L"samAccountName", &var);
  1452. if (FAILED(hr))
  1453. {
  1454. break;
  1455. }
  1456. ASSERT(var.vt == VT_BSTR);
  1457. //
  1458. // The new password for the computer account is the first
  1459. // 14 characters of the samAccountName minus the '$'.
  1460. //
  1461. WCHAR pszNewPassword[15];
  1462. memset(pszNewPassword, 0, sizeof(WCHAR) * 15);
  1463. wcsncpy(pszNewPassword, var.bstrVal, 14);
  1464. PWSTR pszDollar = wcschr(pszNewPassword, L'$');
  1465. if (pszDollar)
  1466. {
  1467. *pszDollar = L'\0';
  1468. }
  1469. hr = ResetObjectPassword(pszDN,
  1470. refBasePathsInfo,
  1471. refCredentialObject,
  1472. pszNewPassword);
  1473. if (FAILED(hr))
  1474. {
  1475. DisplayErrorMessage(g_pszDSCommandName,
  1476. pszDN,
  1477. hr,
  1478. IDS_FAILED_RESET_COMPUTER);
  1479. hr = S_FALSE;
  1480. break;
  1481. }
  1482. } while (false);
  1483. return hr;
  1484. }
  1485. //+--------------------------------------------------------------------------
  1486. //
  1487. // Function: ReadUserAccountControl
  1488. //
  1489. // Synopsis: Reads the userAccountControl attribute from the object specified
  1490. // by the DN
  1491. //
  1492. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  1493. // to the object being modified
  1494. // [refBasePathsInfo - IN] : reference to an instance of the
  1495. // CDSCmdBasePathsInfo class
  1496. // [refCredentialObject - IN] : reference to an instance of the
  1497. // CDSCmdCredentialObject class
  1498. // [plBits - OUT] : returns the currect userAccountControl bits
  1499. //
  1500. // Returns: HRESULT : S_OK if everything succeeded
  1501. // Otherwise an ADSI failure code
  1502. //
  1503. // History: 12-Sep-2000 JeffJon Created
  1504. //
  1505. //---------------------------------------------------------------------------
  1506. HRESULT ReadUserAccountControl(PCWSTR pszDN,
  1507. const CDSCmdBasePathsInfo& refBasePathsInfo,
  1508. const CDSCmdCredentialObject& refCredentialObject,
  1509. long* plBits)
  1510. {
  1511. ENTER_FUNCTION_HR(LEVEL3_LOGGING, ReadUserAccountControl, hr);
  1512. do // false loop
  1513. {
  1514. if (!pszDN ||
  1515. !plBits)
  1516. {
  1517. ASSERT(pszDN);
  1518. ASSERT(plBits);
  1519. hr = E_INVALIDARG;
  1520. break;
  1521. }
  1522. //
  1523. // Convert the DN to a path
  1524. //
  1525. CComBSTR sbstrPath;
  1526. refBasePathsInfo.ComposePathFromDN(pszDN, sbstrPath);
  1527. //
  1528. // Bind and obtain the IADsUser interface to the user object
  1529. //
  1530. CComPtr<IADs> spADs;
  1531. hr = DSCmdOpenObject(refCredentialObject,
  1532. sbstrPath,
  1533. IID_IADs,
  1534. (void**)&spADs,
  1535. true);
  1536. if (FAILED(hr))
  1537. {
  1538. break;
  1539. }
  1540. CComVariant var;
  1541. hr = spADs->Get(L"userAccountControl", &var);
  1542. if (FAILED(hr))
  1543. {
  1544. break;
  1545. }
  1546. ASSERT(var.vt == VT_I4);
  1547. *plBits = var.lVal;
  1548. } while (false);
  1549. return hr;
  1550. }
  1551. //+--------------------------------------------------------------------------
  1552. //
  1553. // Function: DisableAccount
  1554. //
  1555. // Synopsis: Disables/Enables the account using the UF_ACCOUNTDISABLE bit in the
  1556. // userAccountControl attribute
  1557. //
  1558. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  1559. // to the object being modified
  1560. // [refBasePathsInfo - IN] : reference to an instance of the
  1561. // CDSCmdBasePathsInfo class
  1562. // [refCredentialObject - IN] : reference to an instance of the
  1563. // CDSCmdCredentialObject class
  1564. // [pObjectEntry - IN] : pointer to an entry in the object table
  1565. // that defines the object we are modifying
  1566. // [argRecord - IN] : the argument record structure from the
  1567. // parser table that corresponds to this
  1568. // attribute
  1569. // [dwAttributeIdx - IN] : index into the attribute table for the
  1570. // object in which we are setting
  1571. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  1572. // which this function will fill in
  1573. //
  1574. // Returns: HRESULT : S_OK if everything succeeded
  1575. // Otherwise an ADSI failure code
  1576. //
  1577. // History: 12-Sep-2000 JeffJon Created
  1578. //
  1579. //---------------------------------------------------------------------------
  1580. HRESULT DisableAccount(PCWSTR pszDN,
  1581. const CDSCmdBasePathsInfo& refBasePathsInfo,
  1582. const CDSCmdCredentialObject& refCredentialObject,
  1583. const PDSOBJECTTABLEENTRY pObjectEntry,
  1584. const ARG_RECORD& argRecord,
  1585. DWORD dwAttributeIdx,
  1586. PADS_ATTR_INFO* ppAttr)
  1587. {
  1588. ENTER_FUNCTION_HR(LEVEL3_LOGGING, DisableAccount, hr);
  1589. do // false loop
  1590. {
  1591. //
  1592. // Verify parameters
  1593. //
  1594. if (!pszDN ||
  1595. !pObjectEntry ||
  1596. !ppAttr)
  1597. {
  1598. ASSERT(pszDN);
  1599. ASSERT(pObjectEntry);
  1600. ASSERT(ppAttr);
  1601. hr = E_INVALIDARG;
  1602. break;
  1603. }
  1604. long lUserAccountControl = 0;
  1605. //
  1606. // If the userAccountControl hasn't already been read, do so now
  1607. //
  1608. if (0 == (DS_ATTRIBUTE_READ & pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags))
  1609. {
  1610. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Reading user account control from object");
  1611. hr = ReadUserAccountControl(pszDN,
  1612. refBasePathsInfo,
  1613. refCredentialObject,
  1614. &lUserAccountControl);
  1615. if (FAILED(hr))
  1616. {
  1617. break;
  1618. }
  1619. //
  1620. // Mark the table entry as read
  1621. //
  1622. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags |= DS_ATTRIBUTE_READ;
  1623. *ppAttr = &(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo);
  1624. (*ppAttr)->pADsValues = new ADSVALUE;
  1625. if (!(*ppAttr)->pADsValues)
  1626. {
  1627. hr = E_OUTOFMEMORY;
  1628. break;
  1629. }
  1630. (*ppAttr)->pADsValues->dwType = (*ppAttr)->dwADsType;
  1631. (*ppAttr)->dwNumValues = 1;
  1632. }
  1633. else
  1634. {
  1635. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Using existing userAccountControl from table.");
  1636. if (!pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues)
  1637. {
  1638. ASSERT(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues);
  1639. hr = E_INVALIDARG;
  1640. break;
  1641. }
  1642. lUserAccountControl = pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues->Integer;
  1643. //
  1644. // Don't create a new entry in the ADS_ATTR_INFO array
  1645. //
  1646. *ppAttr = NULL;
  1647. }
  1648. ASSERT(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues);
  1649. if (pObjectEntry->pAttributeTable[dwAttributeIdx]->nAttributeID != NULL &&
  1650. argRecord.bDefined && argRecord.bValue)
  1651. {
  1652. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Adding UF_ACCOUNTDISABLE to the userAccountControl");
  1653. lUserAccountControl |= UF_ACCOUNTDISABLE;
  1654. }
  1655. else
  1656. {
  1657. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Removing UF_ACCOUNTDISABLE from the userAccountControl");
  1658. lUserAccountControl &= ~UF_ACCOUNTDISABLE;
  1659. }
  1660. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues->Integer = lUserAccountControl;
  1661. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags |= DS_ATTRIBUTE_DIRTY;
  1662. } while (false);
  1663. return hr;
  1664. }
  1665. //+--------------------------------------------------------------------------
  1666. //
  1667. // Function: SetMustChangePwd
  1668. //
  1669. // Synopsis: Sets the pwdLastSet attribute
  1670. //
  1671. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  1672. // to the object being modified
  1673. // [refBasePathsInfo - IN] : reference to an instance of the
  1674. // CDSCmdBasePathsInfo class
  1675. // [refCredentialObject - IN] : reference to an instance of the
  1676. // CDSCmdCredentialObject class
  1677. // [pObjectEntry - IN] : pointer to an entry in the object table
  1678. // that defines the object we are modifying
  1679. // [argRecord - IN] : the argument record structure from the
  1680. // parser table that corresponds to this
  1681. // attribute
  1682. // [dwAttributeIdx - IN] : index into the attribute table for the
  1683. // object in which we are setting
  1684. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  1685. // which this function will fill in
  1686. //
  1687. // Returns: HRESULT : S_OK if everything succeeded
  1688. // Otherwise an ADSI failure code
  1689. //
  1690. // History: 15-Sep-2000 JeffJon Created
  1691. //
  1692. //---------------------------------------------------------------------------
  1693. HRESULT SetMustChangePwd(PCWSTR pszDN,
  1694. const CDSCmdBasePathsInfo& /*refBasePathsInfo*/,
  1695. const CDSCmdCredentialObject& /*refCredentialObject*/,
  1696. const PDSOBJECTTABLEENTRY pObjectEntry,
  1697. const ARG_RECORD& argRecord,
  1698. DWORD dwAttributeIdx,
  1699. PADS_ATTR_INFO* ppAttr)
  1700. {
  1701. ENTER_FUNCTION_HR(LEVEL3_LOGGING, SetMustChangePwd, hr);
  1702. do // false loop
  1703. {
  1704. //
  1705. // Verify parameters
  1706. //
  1707. if (!pszDN ||
  1708. !pObjectEntry ||
  1709. !ppAttr)
  1710. {
  1711. ASSERT(pszDN);
  1712. ASSERT(pObjectEntry);
  1713. ASSERT(ppAttr);
  1714. hr = E_INVALIDARG;
  1715. break;
  1716. }
  1717. *ppAttr = &(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo);
  1718. //
  1719. // REVIEW_JEFFJON : this is being leaked!
  1720. //
  1721. (*ppAttr)->pADsValues = new ADSVALUE;
  1722. if ((*ppAttr)->pADsValues)
  1723. {
  1724. (*ppAttr)->dwNumValues = 1;
  1725. (*ppAttr)->pADsValues->dwType = (*ppAttr)->dwADsType;
  1726. if (argRecord.bValue)
  1727. {
  1728. (*ppAttr)->pADsValues->LargeInteger.HighPart = 0;
  1729. (*ppAttr)->pADsValues->LargeInteger.LowPart = 0;
  1730. }
  1731. else
  1732. {
  1733. (*ppAttr)->pADsValues->LargeInteger.HighPart = 0xffffffff;
  1734. (*ppAttr)->pADsValues->LargeInteger.LowPart = 0xffffffff;
  1735. }
  1736. }
  1737. } while (false);
  1738. return hr;
  1739. }
  1740. //+--------------------------------------------------------------------------
  1741. //
  1742. // Function: ChangeMustChangePwd
  1743. //
  1744. // Synopsis: Sets the pwdLastSet attribute
  1745. //
  1746. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  1747. // to the object being modified
  1748. // [refBasePathsInfo - IN] : reference to an instance of the
  1749. // CDSCmdBasePathsInfo class
  1750. // [refCredentialObject - IN] : reference to an instance of the
  1751. // CDSCmdCredentialObject class
  1752. // [pObjectEntry - IN] : pointer to an entry in the object table
  1753. // that defines the object we are modifying
  1754. // [argRecord - IN] : the argument record structure from the
  1755. // parser table that corresponds to this
  1756. // attribute
  1757. // [dwAttributeIdx - IN] : index into the attribute table for the
  1758. // object in which we are setting
  1759. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  1760. // which this function will fill in
  1761. //
  1762. // Returns: HRESULT : S_OK if everything succeeded
  1763. // Otherwise an ADSI failure code
  1764. //
  1765. // History: 15-Sep-2000 JeffJon Created
  1766. //
  1767. //---------------------------------------------------------------------------
  1768. HRESULT ChangeMustChangePwd(PCWSTR pszDN,
  1769. const CDSCmdBasePathsInfo& refBasePathsInfo,
  1770. const CDSCmdCredentialObject& refCredentialObject,
  1771. const PDSOBJECTTABLEENTRY pObjectEntry,
  1772. const ARG_RECORD& argRecord,
  1773. DWORD dwAttributeIdx,
  1774. PADS_ATTR_INFO* ppAttr)
  1775. {
  1776. ENTER_FUNCTION_HR(LEVEL3_LOGGING, ChangeMustChangePwd, hr);
  1777. do // false loop
  1778. {
  1779. //
  1780. // Verify parameters
  1781. //
  1782. if (!pszDN ||
  1783. !pObjectEntry ||
  1784. !ppAttr)
  1785. {
  1786. ASSERT(pszDN);
  1787. ASSERT(pObjectEntry);
  1788. ASSERT(ppAttr);
  1789. hr = E_INVALIDARG;
  1790. break;
  1791. }
  1792. //
  1793. // We will assume they can change their password unless we discover otherwise
  1794. //
  1795. bool bCanChangePassword = true;
  1796. hr = EvaluateCanChangePasswordAces(pszDN,
  1797. refBasePathsInfo,
  1798. refCredentialObject,
  1799. bCanChangePassword);
  1800. if (FAILED(hr))
  1801. {
  1802. DEBUG_OUTPUT(LEVEL5_LOGGING,
  1803. L"EvaluateCanChangePasswordAces failed: hr = 0x%x",
  1804. hr);
  1805. ASSERT(false);
  1806. }
  1807. if (!bCanChangePassword && argRecord.bValue)
  1808. {
  1809. DEBUG_OUTPUT(LEVEL5_LOGGING,
  1810. L"Cannot have must change password and cannot change password");
  1811. DisplayErrorMessage(g_pszDSCommandName, pszDN, S_OK, IDS_MUSTCHPWD_CANCHPWD_CONFLICT);
  1812. *ppAttr = NULL;
  1813. hr = S_FALSE;
  1814. break;
  1815. }
  1816. else
  1817. {
  1818. *ppAttr = &(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo);
  1819. }
  1820. //
  1821. // REVIEW_JEFFJON : this is being leaked!
  1822. //
  1823. (*ppAttr)->pADsValues = new ADSVALUE;
  1824. if ((*ppAttr)->pADsValues)
  1825. {
  1826. (*ppAttr)->dwNumValues = 1;
  1827. (*ppAttr)->pADsValues->dwType = (*ppAttr)->dwADsType;
  1828. if (argRecord.bValue)
  1829. {
  1830. (*ppAttr)->pADsValues->LargeInteger.HighPart = 0;
  1831. (*ppAttr)->pADsValues->LargeInteger.LowPart = 0;
  1832. }
  1833. else
  1834. {
  1835. (*ppAttr)->pADsValues->LargeInteger.HighPart = 0xffffffff;
  1836. (*ppAttr)->pADsValues->LargeInteger.LowPart = 0xffffffff;
  1837. }
  1838. }
  1839. } while (false);
  1840. return hr;
  1841. }
  1842. //+--------------------------------------------------------------------------
  1843. //
  1844. // Function: PwdNeverExpires
  1845. //
  1846. // Synopsis: Sets the UF_DONT_EXPIRE_PASSWD bit in the
  1847. // userAccountControl attribute
  1848. //
  1849. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  1850. // to the object being modified
  1851. // [refBasePathsInfo - IN] : reference to an instance of the
  1852. // CDSCmdBasePathsInfo class
  1853. // [refCredentialObject - IN] : reference to an instance of the
  1854. // CDSCmdCredentialObject class
  1855. // [pObjectEntry - IN] : pointer to an entry in the object table
  1856. // that defines the object we are modifying
  1857. // [argRecord - IN] : the argument record structure from the
  1858. // parser table that corresponds to this
  1859. // attribute
  1860. // [dwAttributeIdx - IN] : index into the attribute table for the
  1861. // object in which we are setting
  1862. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  1863. // which this function will fill in
  1864. //
  1865. // Returns: HRESULT : S_OK if everything succeeded
  1866. // Otherwise an ADSI failure code
  1867. //
  1868. // History: 15-Sep-2000 JeffJon Created
  1869. //
  1870. //---------------------------------------------------------------------------
  1871. HRESULT PwdNeverExpires(PCWSTR pszDN,
  1872. const CDSCmdBasePathsInfo& refBasePathsInfo,
  1873. const CDSCmdCredentialObject& refCredentialObject,
  1874. const PDSOBJECTTABLEENTRY pObjectEntry,
  1875. const ARG_RECORD& argRecord,
  1876. DWORD dwAttributeIdx,
  1877. PADS_ATTR_INFO* ppAttr)
  1878. {
  1879. ENTER_FUNCTION_HR(LEVEL3_LOGGING, PwdNeverExpires, hr);
  1880. do // false loop
  1881. {
  1882. //
  1883. // Verify parameters
  1884. //
  1885. if (!pszDN ||
  1886. !pObjectEntry ||
  1887. !ppAttr)
  1888. {
  1889. ASSERT(pszDN);
  1890. ASSERT(pObjectEntry);
  1891. ASSERT(ppAttr);
  1892. hr = E_INVALIDARG;
  1893. break;
  1894. }
  1895. long lUserAccountControl = 0;
  1896. //
  1897. // If the userAccountControl hasn't already been read, do so now
  1898. //
  1899. if (0 == (DS_ATTRIBUTE_READ & pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags))
  1900. {
  1901. hr = ReadUserAccountControl(pszDN,
  1902. refBasePathsInfo,
  1903. refCredentialObject,
  1904. &lUserAccountControl);
  1905. if (FAILED(hr))
  1906. {
  1907. break;
  1908. }
  1909. //
  1910. // Mark the table entry as read
  1911. //
  1912. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags |= DS_ATTRIBUTE_READ;
  1913. *ppAttr = &(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo);
  1914. (*ppAttr)->pADsValues = new ADSVALUE;
  1915. if (!(*ppAttr)->pADsValues)
  1916. {
  1917. hr = E_OUTOFMEMORY;
  1918. break;
  1919. }
  1920. (*ppAttr)->pADsValues->dwType = (*ppAttr)->dwADsType;
  1921. (*ppAttr)->dwNumValues = 1;
  1922. }
  1923. else
  1924. {
  1925. if (!pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues)
  1926. {
  1927. ASSERT(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues);
  1928. hr = E_INVALIDARG;
  1929. break;
  1930. }
  1931. lUserAccountControl = pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues->Integer;
  1932. //
  1933. // Don't create a new entry in the ADS_ATTR_INFO array
  1934. //
  1935. *ppAttr = NULL;
  1936. }
  1937. ASSERT(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues);
  1938. if (argRecord.bValue)
  1939. {
  1940. lUserAccountControl |= UF_DONT_EXPIRE_PASSWD;
  1941. }
  1942. else
  1943. {
  1944. lUserAccountControl &= ~UF_DONT_EXPIRE_PASSWD;
  1945. }
  1946. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues->Integer = lUserAccountControl;
  1947. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags |= DS_ATTRIBUTE_DIRTY;
  1948. } while (false);
  1949. return hr;
  1950. }
  1951. //+--------------------------------------------------------------------------
  1952. //
  1953. // Function: ReversiblePwd
  1954. //
  1955. // Synopsis: Sets the UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED bit in the
  1956. // userAccountControl attribute
  1957. //
  1958. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  1959. // to the object being modified
  1960. // [refBasePathsInfo - IN] : reference to an instance of the
  1961. // CDSCmdBasePathsInfo class
  1962. // [refCredentialObject - IN] : reference to an instance of the
  1963. // CDSCmdCredentialObject class
  1964. // [pObjectEntry - IN] : pointer to an entry in the object table
  1965. // that defines the object we are modifying
  1966. // [argRecord - IN] : the argument record structure from the
  1967. // parser table that corresponds to this
  1968. // attribute
  1969. // [dwAttributeIdx - IN] : index into the attribute table for the
  1970. // object in which we are setting
  1971. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  1972. // which this function will fill in
  1973. //
  1974. // Returns: HRESULT : S_OK if everything succeeded
  1975. // Otherwise an ADSI failure code
  1976. //
  1977. // History: 15-Sep-2000 JeffJon Created
  1978. //
  1979. //---------------------------------------------------------------------------
  1980. HRESULT ReversiblePwd(PCWSTR pszDN,
  1981. const CDSCmdBasePathsInfo& refBasePathsInfo,
  1982. const CDSCmdCredentialObject& refCredentialObject,
  1983. const PDSOBJECTTABLEENTRY pObjectEntry,
  1984. const ARG_RECORD& argRecord,
  1985. DWORD dwAttributeIdx,
  1986. PADS_ATTR_INFO* ppAttr)
  1987. {
  1988. ENTER_FUNCTION_HR(LEVEL3_LOGGING, ReversiblePwd, hr);
  1989. do // false loop
  1990. {
  1991. //
  1992. // Verify parameters
  1993. //
  1994. if (!pszDN ||
  1995. !pObjectEntry ||
  1996. !ppAttr)
  1997. {
  1998. ASSERT(pszDN);
  1999. ASSERT(pObjectEntry);
  2000. ASSERT(ppAttr);
  2001. hr = E_INVALIDARG;
  2002. break;
  2003. }
  2004. long lUserAccountControl = 0;
  2005. //
  2006. // If the userAccountControl hasn't already been read, do so now
  2007. //
  2008. if (0 == (DS_ATTRIBUTE_READ & pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags))
  2009. {
  2010. hr = ReadUserAccountControl(pszDN,
  2011. refBasePathsInfo,
  2012. refCredentialObject,
  2013. &lUserAccountControl);
  2014. if (FAILED(hr))
  2015. {
  2016. break;
  2017. }
  2018. //
  2019. // Mark the table entry as read
  2020. //
  2021. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags |= DS_ATTRIBUTE_READ;
  2022. *ppAttr = &(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo);
  2023. (*ppAttr)->pADsValues = new ADSVALUE;
  2024. if (!(*ppAttr)->pADsValues)
  2025. {
  2026. hr = E_OUTOFMEMORY;
  2027. break;
  2028. }
  2029. (*ppAttr)->pADsValues->dwType = (*ppAttr)->dwADsType;
  2030. (*ppAttr)->dwNumValues = 1;
  2031. }
  2032. else
  2033. {
  2034. if (!pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues)
  2035. {
  2036. ASSERT(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues);
  2037. hr = E_INVALIDARG;
  2038. break;
  2039. }
  2040. lUserAccountControl = pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues->Integer;
  2041. //
  2042. // Don't create a new entry in the ADS_ATTR_INFO array
  2043. //
  2044. *ppAttr = NULL;
  2045. }
  2046. ASSERT(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues);
  2047. if (argRecord.bValue)
  2048. {
  2049. lUserAccountControl |= UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED;
  2050. }
  2051. else
  2052. {
  2053. lUserAccountControl &= ~UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED;
  2054. }
  2055. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues->Integer = lUserAccountControl;
  2056. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags |= DS_ATTRIBUTE_DIRTY;
  2057. } while (false);
  2058. return hr;
  2059. }
  2060. //+--------------------------------------------------------------------------
  2061. //
  2062. // Function: AccountExpires
  2063. //
  2064. // Synopsis: Sets in how many days the account will expire
  2065. //
  2066. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  2067. // to the object being modified
  2068. // [refBasePathsInfo - IN] : reference to an instance of the
  2069. // CDSCmdBasePathsInfo class
  2070. // [refCredentialObject - IN] : reference to an instance of the
  2071. // CDSCmdCredentialObject class
  2072. // [pObjectEntry - IN] : pointer to an entry in the object table
  2073. // that defines the object we are modifying
  2074. // [argRecord - IN] : the argument record structure from the
  2075. // parser table that corresponds to this
  2076. // attribute
  2077. // [dwAttributeIdx - IN] : index into the attribute table for the
  2078. // object in which we are setting
  2079. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  2080. // which this function will fill in
  2081. //
  2082. // Returns: HRESULT : S_OK if everything succeeded
  2083. // Otherwise an ADSI failure code
  2084. //
  2085. // History: 15-Sep-2000 JeffJon Created
  2086. //
  2087. //---------------------------------------------------------------------------
  2088. const unsigned long DSCMD_FILETIMES_PER_MILLISECOND = 10000;
  2089. const DWORD DSCMD_FILETIMES_PER_SECOND = 1000 * DSCMD_FILETIMES_PER_MILLISECOND;
  2090. const DWORD DSCMD_FILETIMES_PER_MINUTE = 60 * DSCMD_FILETIMES_PER_SECOND;
  2091. const __int64 DSCMD_FILETIMES_PER_HOUR = 60 * (__int64)DSCMD_FILETIMES_PER_MINUTE;
  2092. const __int64 DSCMD_FILETIMES_PER_DAY = 24 * DSCMD_FILETIMES_PER_HOUR;
  2093. const __int64 DSCMD_FILETIMES_PER_MONTH= 30 * DSCMD_FILETIMES_PER_DAY;
  2094. HRESULT AccountExpires(PCWSTR pszDN,
  2095. const CDSCmdBasePathsInfo& /*refBasePathsInfo*/,
  2096. const CDSCmdCredentialObject& /*refCredentialObject*/,
  2097. const PDSOBJECTTABLEENTRY pObjectEntry,
  2098. const ARG_RECORD& argRecord,
  2099. DWORD dwAttributeIdx,
  2100. PADS_ATTR_INFO* ppAttr)
  2101. {
  2102. ENTER_FUNCTION_HR(LEVEL3_LOGGING, AccountExpires, hr);
  2103. do // false loop
  2104. {
  2105. //
  2106. // Verify parameters
  2107. //
  2108. if (!pszDN ||
  2109. !pObjectEntry ||
  2110. !ppAttr)
  2111. {
  2112. ASSERT(pszDN);
  2113. ASSERT(pObjectEntry);
  2114. ASSERT(ppAttr);
  2115. hr = E_INVALIDARG;
  2116. break;
  2117. }
  2118. *ppAttr = &(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo);
  2119. //
  2120. // REVIEW_JEFFJON : this is being leaked
  2121. //
  2122. (*ppAttr)->pADsValues = new ADSVALUE;
  2123. if (!(*ppAttr)->pADsValues)
  2124. {
  2125. hr = E_OUTOFMEMORY;
  2126. break;
  2127. }
  2128. (*ppAttr)->pADsValues->dwType = (*ppAttr)->dwADsType;
  2129. (*ppAttr)->dwNumValues = 1;
  2130. //
  2131. // Note: the table entry for this attribute is ARG_TYPE_INTSTR but the parser
  2132. // will change it to ARG_TYPE_INT if the value starts with digits. If not then
  2133. // the parser will change the type to ARG_TYPE_STR
  2134. //
  2135. if (argRecord.fType == ARG_TYPE_INT)
  2136. {
  2137. //
  2138. // Get the system time and then add the number of days until the account expires
  2139. //
  2140. FILETIME currentFT = {0};
  2141. ::GetSystemTimeAsFileTime(&currentFT);
  2142. LARGE_INTEGER liExpires;
  2143. liExpires.LowPart = currentFT.dwLowDateTime;
  2144. liExpires.HighPart = currentFT.dwHighDateTime;
  2145. //
  2146. // Add one to the day because it is really the start of the next day that the account gets
  2147. // disabled
  2148. //
  2149. __int64 days = argRecord.nValue + 1;
  2150. __int64 nanosecs = days * DSCMD_FILETIMES_PER_DAY;
  2151. (*ppAttr)->pADsValues->LargeInteger.QuadPart = liExpires.QuadPart + nanosecs;
  2152. }
  2153. else if (argRecord.fType == ARG_TYPE_STR)
  2154. {
  2155. CComBSTR sbstrStrValue = argRecord.strValue;
  2156. sbstrStrValue.ToLower();
  2157. if (0 == _wcsicmp(sbstrStrValue, g_bstrNever))
  2158. {
  2159. //
  2160. // Zero signifies that the account never expires
  2161. //
  2162. (*ppAttr)->pADsValues->LargeInteger.HighPart = 0;
  2163. (*ppAttr)->pADsValues->LargeInteger.LowPart = 0;
  2164. }
  2165. else
  2166. {
  2167. hr = E_INVALIDARG;
  2168. break;
  2169. }
  2170. }
  2171. //
  2172. // Mark the attribute as dirty
  2173. //
  2174. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags |= DS_ATTRIBUTE_DIRTY;
  2175. } while (false);
  2176. return hr;
  2177. }
  2178. //+--------------------------------------------------------------------------
  2179. //
  2180. // Function: EvaluateMustChangePassword
  2181. //
  2182. // Synopsis: Determines whether the user must change their password at next logon
  2183. //
  2184. // Arguments: [pszDN - IN] : DN of the object to check
  2185. // [refBasePathsInfo - IN] : reference to the base paths info
  2186. // [refCredentialObject - IN] : reference to the credential manangement object
  2187. // [bMustChangePassword - OUT] : true if the user must change their
  2188. // password at next logon, false otherwise
  2189. //
  2190. // Returns: HRESULT : S_OK if everything succeeded
  2191. // Otherwise an ADSI failure code
  2192. //
  2193. // History: 27-Oct-2000 JeffJon Created
  2194. //
  2195. //---------------------------------------------------------------------------
  2196. HRESULT EvaluateMustChangePassword(PCWSTR pszDN,
  2197. const CDSCmdBasePathsInfo& refBasePathsInfo,
  2198. const CDSCmdCredentialObject& refCredentialObject,
  2199. bool& bMustChangePassword)
  2200. {
  2201. ENTER_FUNCTION_HR(LEVEL5_LOGGING, EvaluateMustChangePassword, hr);
  2202. do // false loop
  2203. {
  2204. //
  2205. // Validate parameters
  2206. //
  2207. if (!pszDN)
  2208. {
  2209. ASSERT(pszDN);
  2210. hr = E_INVALIDARG;
  2211. break;
  2212. }
  2213. bMustChangePassword = false;
  2214. //
  2215. // Compose the path
  2216. //
  2217. CComBSTR sbstrPath;
  2218. refBasePathsInfo.ComposePathFromDN(pszDN, sbstrPath);
  2219. //
  2220. // Open the object
  2221. //
  2222. CComPtr<IDirectoryObject> spDirObject;
  2223. hr = DSCmdOpenObject(refCredentialObject,
  2224. sbstrPath,
  2225. IID_IDirectoryObject,
  2226. (void**)&spDirObject,
  2227. true);
  2228. if (FAILED(hr))
  2229. {
  2230. break;
  2231. }
  2232. static const DWORD dwAttrCount = 1;
  2233. PWSTR pszAttrs[] = { L"pwdLastSet" };
  2234. PADS_ATTR_INFO pAttrInfo = NULL;
  2235. DWORD dwAttrsReturned = 0;
  2236. hr = spDirObject->GetObjectAttributes(pszAttrs,
  2237. dwAttrCount,
  2238. &pAttrInfo,
  2239. &dwAttrsReturned);
  2240. if (FAILED(hr))
  2241. {
  2242. DEBUG_OUTPUT(MINIMAL_LOGGING,
  2243. L"GetObjectAttributes for pwdLastSet failed: hr = 0x%x",
  2244. hr);
  2245. break;
  2246. }
  2247. if (pAttrInfo && dwAttrsReturned && pAttrInfo->dwNumValues)
  2248. {
  2249. if (pAttrInfo->pADsValues->LargeInteger.HighPart == 0 &&
  2250. pAttrInfo->pADsValues->LargeInteger.LowPart == 0)
  2251. {
  2252. DEBUG_OUTPUT(LEVEL5_LOGGING, L"User must change password at next logon");
  2253. bMustChangePassword = true;
  2254. }
  2255. }
  2256. } while (false);
  2257. return hr;
  2258. }
  2259. //+--------------------------------------------------------------------------
  2260. //
  2261. // Function: EvaluateCanChangePasswordAces
  2262. //
  2263. // Synopsis: Looks for explicit entries in the ACL to see if the user can
  2264. // change their password
  2265. //
  2266. // Arguments: [pszDN - IN] : DN of the object to check
  2267. // [refBasePathsInfo - IN] : reference to the base paths info
  2268. // [refCredentialObject - IN] : reference to the credential manangement object
  2269. // [bCanChangePassword - OUT] : false if there are explicit entries
  2270. // that keep the user from changing their
  2271. // password. true otherwise.
  2272. //
  2273. // Returns: HRESULT : S_OK if everything succeeded
  2274. // Otherwise an ADSI failure code
  2275. //
  2276. // History: 27-Oct-2000 JeffJon Created
  2277. //
  2278. //---------------------------------------------------------------------------
  2279. HRESULT EvaluateCanChangePasswordAces(PCWSTR pszDN,
  2280. const CDSCmdBasePathsInfo& refBasePathsInfo,
  2281. const CDSCmdCredentialObject& refCredentialObject,
  2282. bool& bCanChangePassword)
  2283. {
  2284. ENTER_FUNCTION_HR(LEVEL5_LOGGING, EvaluateCanChangePasswordAces, hr);
  2285. do // false loop
  2286. {
  2287. //
  2288. // Validate parameters
  2289. //
  2290. if (!pszDN)
  2291. {
  2292. ASSERT(pszDN);
  2293. hr = E_INVALIDARG;
  2294. break;
  2295. }
  2296. //
  2297. // Compose the path
  2298. //
  2299. CComBSTR sbstrPath;
  2300. refBasePathsInfo.ComposePathFromDN(pszDN, sbstrPath);
  2301. //
  2302. // Open the object
  2303. //
  2304. CComPtr<IDirectoryObject> spDirObject;
  2305. hr = DSCmdOpenObject(refCredentialObject,
  2306. sbstrPath,
  2307. IID_IDirectoryObject,
  2308. (void**)&spDirObject,
  2309. true);
  2310. if (FAILED(hr))
  2311. {
  2312. break;
  2313. }
  2314. SECURITY_DESCRIPTOR_CONTROL sdControl = {0};
  2315. CSimpleAclHolder Dacl;
  2316. hr = DSReadObjectSecurity(spDirObject,
  2317. &sdControl,
  2318. &(Dacl.m_pAcl));
  2319. if (FAILED(hr))
  2320. {
  2321. break;
  2322. }
  2323. //
  2324. // Create and Initialize the Self and World SIDs
  2325. //
  2326. CSidHolder selfSid;
  2327. CSidHolder worldSid;
  2328. PSID pSid = NULL;
  2329. SID_IDENTIFIER_AUTHORITY NtAuth = SECURITY_NT_AUTHORITY,
  2330. WorldAuth = SECURITY_WORLD_SID_AUTHORITY;
  2331. if (!AllocateAndInitializeSid(&NtAuth,
  2332. 1,
  2333. SECURITY_PRINCIPAL_SELF_RID,
  2334. 0, 0, 0, 0, 0, 0, 0,
  2335. &pSid))
  2336. {
  2337. hr = HRESULT_FROM_WIN32(GetLastError());
  2338. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Failed to allocate self SID: hr = 0x%x", hr);
  2339. break;
  2340. }
  2341. selfSid.Attach(pSid, false);
  2342. pSid = NULL;
  2343. if (!AllocateAndInitializeSid(&WorldAuth,
  2344. 1,
  2345. SECURITY_WORLD_RID,
  2346. 0, 0, 0, 0, 0, 0, 0,
  2347. &pSid))
  2348. {
  2349. hr = HRESULT_FROM_WIN32(GetLastError());
  2350. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Failed to allocate world SID: hr = 0x%x", hr);
  2351. break;
  2352. }
  2353. worldSid.Attach(pSid, false);
  2354. pSid = NULL;
  2355. ULONG ulCount = 0, j = 0;
  2356. PEXPLICIT_ACCESS rgEntries = NULL;
  2357. DWORD dwErr = GetExplicitEntriesFromAcl(Dacl.m_pAcl, &ulCount, &rgEntries);
  2358. if (ERROR_SUCCESS != dwErr)
  2359. {
  2360. hr = HRESULT_FROM_WIN32(dwErr);
  2361. DEBUG_OUTPUT(LEVEL3_LOGGING, L"GetExplicitEntriesFromAcl failed: hr = 0x%x", hr);
  2362. break;
  2363. }
  2364. //
  2365. // Are these ACEs already present?
  2366. //
  2367. bool bSelfAllowPresent = false;
  2368. bool bWorldAllowPresent = false;
  2369. bool bSelfDenyPresent = false;
  2370. bool bWorldDenyPresent = false;
  2371. //
  2372. // Loop through looking for the can change password ACE for self and world
  2373. //
  2374. for (j = 0; j < ulCount; j++)
  2375. {
  2376. //
  2377. // Look for deny ACEs
  2378. //
  2379. if ((rgEntries[j].Trustee.TrusteeForm == TRUSTEE_IS_OBJECTS_AND_SID) &&
  2380. (rgEntries[j].grfAccessMode == DENY_ACCESS))
  2381. {
  2382. OBJECTS_AND_SID* pObjectsAndSid = NULL;
  2383. pObjectsAndSid = (OBJECTS_AND_SID*)rgEntries[j].Trustee.ptstrName;
  2384. //
  2385. // Look for the user can change password ACE
  2386. //
  2387. if (IsEqualGUID(pObjectsAndSid->ObjectTypeGuid,
  2388. GUID_CONTROL_UserChangePassword))
  2389. {
  2390. //
  2391. // See if it is for the self SID or the world SID
  2392. //
  2393. if (EqualSid(pObjectsAndSid->pSid, selfSid.Get()))
  2394. {
  2395. //
  2396. // Deny self found
  2397. //
  2398. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Deny self found at rgEntries[%d]", j);
  2399. bSelfDenyPresent = true;
  2400. }
  2401. else if (EqualSid(pObjectsAndSid->pSid, worldSid.Get()))
  2402. {
  2403. //
  2404. // Deny world found
  2405. //
  2406. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Deny world found at rgEntries[%d]", j);
  2407. bWorldDenyPresent = true;
  2408. }
  2409. }
  2410. }
  2411. //
  2412. // Look for allow ACEs
  2413. //
  2414. else if ((rgEntries[j].Trustee.TrusteeForm == TRUSTEE_IS_OBJECTS_AND_SID) &&
  2415. (rgEntries[j].grfAccessMode == GRANT_ACCESS))
  2416. {
  2417. OBJECTS_AND_SID* pObjectsAndSid = NULL;
  2418. pObjectsAndSid = (OBJECTS_AND_SID*)rgEntries[j].Trustee.ptstrName;
  2419. //
  2420. // Look for the user can change password ACE
  2421. //
  2422. if (IsEqualGUID(pObjectsAndSid->ObjectTypeGuid,
  2423. GUID_CONTROL_UserChangePassword))
  2424. {
  2425. //
  2426. // See if it is for the self SID or the world SID
  2427. //
  2428. if (EqualSid(pObjectsAndSid->pSid, selfSid.Get()))
  2429. {
  2430. //
  2431. // Allow self found
  2432. //
  2433. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Allow self found at rgEntries[%d]", j);
  2434. bSelfAllowPresent = true;
  2435. }
  2436. else if (EqualSid(pObjectsAndSid->pSid, worldSid.Get()))
  2437. {
  2438. //
  2439. // Allow world found
  2440. //
  2441. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Allow world found at rgEntries[%d]", j);
  2442. bWorldAllowPresent = true;
  2443. }
  2444. }
  2445. }
  2446. }
  2447. if (bSelfDenyPresent || bWorldDenyPresent)
  2448. {
  2449. //
  2450. // There is an explicit deny so we know that the user cannot change password
  2451. //
  2452. bCanChangePassword = false;
  2453. }
  2454. else if ((!bSelfDenyPresent && !bWorldDenyPresent) &&
  2455. (bSelfAllowPresent || bWorldAllowPresent))
  2456. {
  2457. //
  2458. // There is no explicit deny but there are explicit allows so we know that
  2459. // the user can change password
  2460. //
  2461. bCanChangePassword = true;
  2462. }
  2463. else
  2464. {
  2465. //
  2466. // We are not sure because the explicit entries are not telling us for
  2467. // certain so it all depends on inheritence. Most likely they will
  2468. // be able to change their password unless the admin has changed something
  2469. // higher up or through group membership
  2470. //
  2471. bCanChangePassword = true;
  2472. }
  2473. } while(false);
  2474. return hr;
  2475. }
  2476. //+--------------------------------------------------------------------------
  2477. //
  2478. // Function: ChangeCanChangePassword
  2479. //
  2480. // Synopsis: Sets or removes the Deny Ace on the can change password ACL
  2481. //
  2482. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  2483. // to the object being modified
  2484. // [refBasePathsInfo - IN] : reference to an instance of the
  2485. // CDSCmdBasePathsInfo class
  2486. // [refCredentialObject - IN] : reference to an instance of the
  2487. // CDSCmdCredentialObject class
  2488. // [pObjectEntry - IN] : pointer to an entry in the object table
  2489. // that defines the object we are modifying
  2490. // [argRecord - IN] : the argument record structure from the
  2491. // parser table that corresponds to this
  2492. // attribute
  2493. // [dwAttributeIdx - IN] : index into the attribute table for the
  2494. // object in which we are setting
  2495. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  2496. // which this function will fill in
  2497. //
  2498. // Returns: HRESULT : S_OK if everything succeeded
  2499. // Otherwise an ADSI failure code
  2500. //
  2501. // History: 15-Sep-2000 JeffJon Created
  2502. //
  2503. //---------------------------------------------------------------------------
  2504. HRESULT ChangeCanChangePassword(PCWSTR pszDN,
  2505. const CDSCmdBasePathsInfo& refBasePathsInfo,
  2506. const CDSCmdCredentialObject& refCredentialObject,
  2507. const PDSOBJECTTABLEENTRY pObjectEntry,
  2508. const ARG_RECORD& argRecord,
  2509. DWORD dwAttributeIdx,
  2510. PADS_ATTR_INFO* ppAttr)
  2511. {
  2512. ENTER_FUNCTION_HR(LEVEL3_LOGGING, ChangeCanChangePassword, hr);
  2513. do // false loop
  2514. {
  2515. //
  2516. // Verify parameters
  2517. //
  2518. if (!pszDN ||
  2519. !pObjectEntry ||
  2520. !ppAttr)
  2521. {
  2522. ASSERT(pszDN);
  2523. ASSERT(pObjectEntry);
  2524. ASSERT(ppAttr);
  2525. hr = E_INVALIDARG;
  2526. break;
  2527. }
  2528. *ppAttr = NULL;
  2529. //
  2530. // Read the userAccountControl to make sure we don't have
  2531. // the user must change password bit set
  2532. //
  2533. bool bMustChangePassword = false;
  2534. hr = EvaluateMustChangePassword(pszDN,
  2535. refBasePathsInfo,
  2536. refCredentialObject,
  2537. bMustChangePassword);
  2538. if (FAILED(hr))
  2539. {
  2540. //
  2541. // Lets log it but continue on as if everything was OK
  2542. //
  2543. DEBUG_OUTPUT(LEVEL5_LOGGING,
  2544. L"EvaluateMustChangePassword failed: hr = 0x%x",
  2545. hr);
  2546. }
  2547. if (bMustChangePassword && !argRecord.bValue)
  2548. {
  2549. DEBUG_OUTPUT(LEVEL5_LOGGING,
  2550. L"Cannot have must change password and cannot change password");
  2551. DisplayErrorMessage(g_pszDSCommandName, pszDN, S_OK, IDS_MUSTCHPWD_CANCHPWD_CONFLICT);
  2552. *ppAttr = NULL;
  2553. hr = S_FALSE;
  2554. break;
  2555. }
  2556. hr = SetCanChangePassword(pszDN,
  2557. refBasePathsInfo,
  2558. refCredentialObject,
  2559. pObjectEntry,
  2560. argRecord,
  2561. dwAttributeIdx,
  2562. ppAttr);
  2563. } while (false);
  2564. return hr;
  2565. }
  2566. //+--------------------------------------------------------------------------
  2567. //
  2568. // Function: SetCanChangePassword
  2569. //
  2570. // Synopsis: Sets or removes the Deny Ace on the can change password ACL
  2571. //
  2572. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  2573. // to the object being modified
  2574. // [refBasePathsInfo - IN] : reference to an instance of the
  2575. // CDSCmdBasePathsInfo class
  2576. // [refCredentialObject - IN] : reference to an instance of the
  2577. // CDSCmdCredentialObject class
  2578. // [pObjectEntry - IN] : pointer to an entry in the object table
  2579. // that defines the object we are modifying
  2580. // [argRecord - IN] : the argument record structure from the
  2581. // parser table that corresponds to this
  2582. // attribute
  2583. // [dwAttributeIdx - IN] : index into the attribute table for the
  2584. // object in which we are setting
  2585. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  2586. // which this function will fill in
  2587. //
  2588. // Returns: HRESULT : S_OK if everything succeeded
  2589. // Otherwise an ADSI failure code
  2590. //
  2591. // History: 15-Sep-2000 JeffJon Created
  2592. //
  2593. //---------------------------------------------------------------------------
  2594. HRESULT SetCanChangePassword(PCWSTR pszDN,
  2595. const CDSCmdBasePathsInfo& refBasePathsInfo,
  2596. const CDSCmdCredentialObject& refCredentialObject,
  2597. const PDSOBJECTTABLEENTRY pObjectEntry,
  2598. const ARG_RECORD& argRecord,
  2599. DWORD /*dwAttributeIdx*/,
  2600. PADS_ATTR_INFO* ppAttr)
  2601. {
  2602. ENTER_FUNCTION_HR(LEVEL3_LOGGING, SetCanChangePassword, hr);
  2603. do // false loop
  2604. {
  2605. //
  2606. // Verify parameters
  2607. //
  2608. if (!pszDN ||
  2609. !pObjectEntry ||
  2610. !ppAttr)
  2611. {
  2612. ASSERT(pszDN);
  2613. ASSERT(pObjectEntry);
  2614. ASSERT(ppAttr);
  2615. hr = E_INVALIDARG;
  2616. break;
  2617. }
  2618. *ppAttr = NULL;
  2619. //
  2620. // Compose the path
  2621. //
  2622. CComBSTR sbstrPath;
  2623. refBasePathsInfo.ComposePathFromDN(pszDN, sbstrPath);
  2624. //
  2625. // Open the object
  2626. //
  2627. CComPtr<IDirectoryObject> spDirObject;
  2628. hr = DSCmdOpenObject(refCredentialObject,
  2629. sbstrPath,
  2630. IID_IDirectoryObject,
  2631. (void**)&spDirObject,
  2632. true);
  2633. if (FAILED(hr))
  2634. {
  2635. break;
  2636. }
  2637. SECURITY_DESCRIPTOR_CONTROL sdControl = {0};
  2638. CSimpleAclHolder Dacl;
  2639. hr = DSReadObjectSecurity(spDirObject,
  2640. &sdControl,
  2641. &(Dacl.m_pAcl));
  2642. if (FAILED(hr))
  2643. {
  2644. break;
  2645. }
  2646. //
  2647. // Create and Initialize the Self and World SIDs
  2648. //
  2649. CSidHolder selfSid;
  2650. CSidHolder worldSid;
  2651. PSID pSid = NULL;
  2652. SID_IDENTIFIER_AUTHORITY NtAuth = SECURITY_NT_AUTHORITY,
  2653. WorldAuth = SECURITY_WORLD_SID_AUTHORITY;
  2654. if (!AllocateAndInitializeSid(&NtAuth,
  2655. 1,
  2656. SECURITY_PRINCIPAL_SELF_RID,
  2657. 0, 0, 0, 0, 0, 0, 0,
  2658. &pSid))
  2659. {
  2660. hr = HRESULT_FROM_WIN32(GetLastError());
  2661. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Failed to allocate self SID: hr = 0x%x", hr);
  2662. break;
  2663. }
  2664. selfSid.Attach(pSid, false);
  2665. pSid = NULL;
  2666. if (!AllocateAndInitializeSid(&WorldAuth,
  2667. 1,
  2668. SECURITY_WORLD_RID,
  2669. 0, 0, 0, 0, 0, 0, 0,
  2670. &pSid))
  2671. {
  2672. hr = HRESULT_FROM_WIN32(GetLastError());
  2673. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Failed to allocate world SID: hr = 0x%x", hr);
  2674. break;
  2675. }
  2676. worldSid.Attach(pSid, false);
  2677. pSid = NULL;
  2678. ULONG ulCount = 0, j = 0;
  2679. PEXPLICIT_ACCESS rgEntries = NULL;
  2680. DWORD dwErr = GetExplicitEntriesFromAcl(Dacl.m_pAcl, &ulCount, &rgEntries);
  2681. if (ERROR_SUCCESS != dwErr)
  2682. {
  2683. hr = HRESULT_FROM_WIN32(dwErr);
  2684. DEBUG_OUTPUT(LEVEL3_LOGGING, L"GetExplicitEntriesFromAcl failed: hr = 0x%x", hr);
  2685. break;
  2686. }
  2687. //
  2688. // At most we will be adding two ACEs hence the +2
  2689. //
  2690. PEXPLICIT_ACCESS rgNewEntries = (PEXPLICIT_ACCESS)LocalAlloc(LPTR, sizeof(EXPLICIT_ACCESS)*(ulCount + 2));
  2691. if (!rgNewEntries)
  2692. {
  2693. hr = E_OUTOFMEMORY;
  2694. break;
  2695. }
  2696. DEBUG_OUTPUT(FULL_LOGGING, L"GetExplicitEntriesFromAcl return %d entries", ulCount);
  2697. //
  2698. // Are these ACEs already present?
  2699. //
  2700. bool bSelfAllowPresent = false;
  2701. bool bWorldAllowPresent = false;
  2702. bool bSelfDenyPresent = false;
  2703. bool bWorldDenyPresent = false;
  2704. ULONG ulCurrentEntry = 0;
  2705. //
  2706. // If we are not granting them permission, then put the deny ACE at the top
  2707. //
  2708. if (!argRecord.bValue)
  2709. {
  2710. //
  2711. // initialize the new entries (DENY ACE's)
  2712. //
  2713. OBJECTS_AND_SID rgObjectsAndSid[2];
  2714. memset(rgObjectsAndSid, 0, sizeof(rgObjectsAndSid));
  2715. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Adding the deny self ACE at rgNewEntries[%d]", ulCurrentEntry);
  2716. rgNewEntries[ulCurrentEntry].grfAccessPermissions = ACTRL_DS_CONTROL_ACCESS;
  2717. rgNewEntries[ulCurrentEntry].grfAccessMode = DENY_ACCESS;
  2718. rgNewEntries[ulCurrentEntry].grfInheritance = NO_INHERITANCE;
  2719. //
  2720. // build the trustee structs for change password
  2721. //
  2722. BuildTrusteeWithObjectsAndSid(&(rgNewEntries[ulCurrentEntry].Trustee),
  2723. &(rgObjectsAndSid[0]),
  2724. const_cast<GUID *>(&GUID_CONTROL_UserChangePassword),
  2725. NULL, // inherit guid
  2726. selfSid.Get());
  2727. ulCurrentEntry++;
  2728. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Adding the deny world ACE at rgNewEntries[%d]", ulCurrentEntry);
  2729. rgNewEntries[ulCurrentEntry].grfAccessPermissions = ACTRL_DS_CONTROL_ACCESS;
  2730. rgNewEntries[ulCurrentEntry].grfAccessMode = DENY_ACCESS;
  2731. rgNewEntries[ulCurrentEntry].grfInheritance = NO_INHERITANCE;
  2732. //
  2733. // build the trustee structs for change password
  2734. //
  2735. BuildTrusteeWithObjectsAndSid(&(rgNewEntries[ulCurrentEntry].Trustee),
  2736. &(rgObjectsAndSid[1]),
  2737. const_cast<GUID *>(&GUID_CONTROL_UserChangePassword),
  2738. NULL, // inherit guid
  2739. worldSid.Get());
  2740. ulCurrentEntry++;
  2741. }
  2742. //
  2743. // Loop through all the ACEs and copy them over to the rgNewEntries unless it is
  2744. // an ACE that we want to remove
  2745. //
  2746. for (j = 0; j < ulCount; j++)
  2747. {
  2748. bool bCopyACE = true;
  2749. //
  2750. // Look for deny ACEs
  2751. //
  2752. if ((rgEntries[j].Trustee.TrusteeForm == TRUSTEE_IS_OBJECTS_AND_SID) &&
  2753. (rgEntries[j].grfAccessMode == DENY_ACCESS))
  2754. {
  2755. OBJECTS_AND_SID* pObjectsAndSid = NULL;
  2756. pObjectsAndSid = (OBJECTS_AND_SID*)rgEntries[j].Trustee.ptstrName;
  2757. //
  2758. // Look for the user can change password ACE
  2759. //
  2760. if (IsEqualGUID(pObjectsAndSid->ObjectTypeGuid,
  2761. GUID_CONTROL_UserChangePassword))
  2762. {
  2763. //
  2764. // See if it is for the self SID or the world SID
  2765. //
  2766. if (EqualSid(pObjectsAndSid->pSid, selfSid.Get()))
  2767. {
  2768. //
  2769. // Deny self found
  2770. //
  2771. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Deny self found at rgEntries[%d]", j);
  2772. bSelfDenyPresent = true;
  2773. //
  2774. // Never copy the deny ACE because we added it above for !argRecord.bValue
  2775. //
  2776. bCopyACE = false;
  2777. }
  2778. else if (EqualSid(pObjectsAndSid->pSid, worldSid.Get()))
  2779. {
  2780. //
  2781. // Deny world found
  2782. //
  2783. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Deny world found at rgEntries[%d]", j);
  2784. bWorldDenyPresent = true;
  2785. //
  2786. // Never copy the deny ACE because we added it above for !argRecord.bValue
  2787. //
  2788. bCopyACE = false;
  2789. }
  2790. }
  2791. }
  2792. //
  2793. // Look for allow ACEs
  2794. //
  2795. else if ((rgEntries[j].Trustee.TrusteeForm == TRUSTEE_IS_OBJECTS_AND_SID) &&
  2796. (rgEntries[j].grfAccessMode == GRANT_ACCESS))
  2797. {
  2798. OBJECTS_AND_SID* pObjectsAndSid = NULL;
  2799. pObjectsAndSid = (OBJECTS_AND_SID*)rgEntries[j].Trustee.ptstrName;
  2800. //
  2801. // Look for the user can change password ACE
  2802. //
  2803. if (IsEqualGUID(pObjectsAndSid->ObjectTypeGuid,
  2804. GUID_CONTROL_UserChangePassword))
  2805. {
  2806. //
  2807. // See if it is for the self SID or the world SID
  2808. //
  2809. if (EqualSid(pObjectsAndSid->pSid, selfSid.Get()))
  2810. {
  2811. //
  2812. // Allow self found
  2813. //
  2814. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Allow self found at rgEntries[%d]", j);
  2815. bSelfAllowPresent = true;
  2816. if (!argRecord.bValue)
  2817. {
  2818. bCopyACE = false;
  2819. }
  2820. }
  2821. else if (EqualSid(pObjectsAndSid->pSid, worldSid.Get()))
  2822. {
  2823. //
  2824. // Allow world found
  2825. //
  2826. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Allow world found at rgEntries[%d]", j);
  2827. bWorldAllowPresent = TRUE;
  2828. if (!argRecord.bValue)
  2829. {
  2830. bCopyACE = false;
  2831. }
  2832. }
  2833. }
  2834. }
  2835. if (bCopyACE)
  2836. {
  2837. DEBUG_OUTPUT(FULL_LOGGING,
  2838. L"Copying entry from rgEntries[%d] to rgNewEntries[%d]",
  2839. j,
  2840. ulCurrentEntry);
  2841. rgNewEntries[ulCurrentEntry] = rgEntries[j];
  2842. ulCurrentEntry++;
  2843. }
  2844. }
  2845. //
  2846. // Now add the allow ACEs if they were not present and we are granting user can change pwd
  2847. //
  2848. if (argRecord.bValue)
  2849. {
  2850. if (!bSelfAllowPresent)
  2851. {
  2852. //
  2853. // Need to add the grant self ACE
  2854. //
  2855. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Adding the grant self ACE at rgNewEntries[%d]", ulCurrentEntry);
  2856. OBJECTS_AND_SID rgObjectsAndSid = {0};
  2857. rgNewEntries[ulCurrentEntry].grfAccessPermissions = ACTRL_DS_CONTROL_ACCESS;
  2858. rgNewEntries[ulCurrentEntry].grfAccessMode = GRANT_ACCESS;
  2859. rgNewEntries[ulCurrentEntry].grfInheritance = NO_INHERITANCE;
  2860. BuildTrusteeWithObjectsAndSid(&(rgNewEntries[ulCurrentEntry].Trustee),
  2861. &(rgObjectsAndSid),
  2862. const_cast<GUID*>(&GUID_CONTROL_UserChangePassword),
  2863. NULL, // inherit guid
  2864. selfSid.Get());
  2865. ulCurrentEntry++;
  2866. }
  2867. if (!bWorldAllowPresent)
  2868. {
  2869. //
  2870. // Need to add the grant world ACE
  2871. //
  2872. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Adding the grant world ACE at rgNewEntries[%d]", ulCurrentEntry);
  2873. OBJECTS_AND_SID rgObjectsAndSid = {0};
  2874. rgNewEntries[ulCurrentEntry].grfAccessPermissions = ACTRL_DS_CONTROL_ACCESS;
  2875. rgNewEntries[ulCurrentEntry].grfAccessMode = GRANT_ACCESS;
  2876. rgNewEntries[ulCurrentEntry].grfInheritance = NO_INHERITANCE;
  2877. BuildTrusteeWithObjectsAndSid(&(rgNewEntries[ulCurrentEntry].Trustee),
  2878. &(rgObjectsAndSid),
  2879. const_cast<GUID*>(&GUID_CONTROL_UserChangePassword),
  2880. NULL, // inherit guid
  2881. worldSid.Get());
  2882. ulCurrentEntry++;
  2883. }
  2884. }
  2885. //
  2886. // We should only have added two ACEs at most
  2887. //
  2888. ASSERT(ulCurrentEntry <= ulCount + 2);
  2889. if (ulCurrentEntry > ulCount)
  2890. {
  2891. DEBUG_OUTPUT(MINIMAL_LOGGING,
  2892. L"We probably ran off the end of the array because ulCurrentEntry(%d) is > ulCount(%d)",
  2893. ulCurrentEntry,
  2894. ulCount);
  2895. }
  2896. //
  2897. // Now set the entries in the new ACL
  2898. //
  2899. CSimpleAclHolder NewDacl;
  2900. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Calling SetEntriesInAcl for %d entries", ulCurrentEntry);
  2901. dwErr = SetEntriesInAcl(ulCurrentEntry, rgNewEntries, NULL, &(NewDacl.m_pAcl));
  2902. if (ERROR_SUCCESS != dwErr)
  2903. {
  2904. hr = HRESULT_FROM_WIN32(dwErr);
  2905. DEBUG_OUTPUT(LEVEL3_LOGGING, L"SetEntriesInAcl failed: hr = 0x%x", hr);
  2906. break;
  2907. }
  2908. ASSERT(IsValidAcl(NewDacl.m_pAcl));
  2909. //
  2910. // Free the entries
  2911. //
  2912. if (rgNewEntries)
  2913. {
  2914. LocalFree(rgNewEntries);
  2915. }
  2916. if (ulCount && rgEntries)
  2917. {
  2918. LocalFree(rgEntries);
  2919. }
  2920. //
  2921. // Write the new ACL back as a SecurityDescriptor
  2922. //
  2923. hr = DSWriteObjectSecurity(spDirObject,
  2924. sdControl,
  2925. NewDacl.m_pAcl);
  2926. if (FAILED(hr))
  2927. {
  2928. break;
  2929. }
  2930. } while (false);
  2931. return hr;
  2932. }
  2933. //+--------------------------------------------------------------------------
  2934. //
  2935. // Function: ReadGroupType
  2936. //
  2937. // Synopsis: Reads the group type from the group specified by the given DN
  2938. //
  2939. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  2940. // to the object being modified
  2941. // [refBasePathsInfo - IN] : reference to an instance of the
  2942. // CDSCmdBasePathsInfo class
  2943. // [refCredentialObject - IN] : reference to an instance of the
  2944. // CDSCmdCredentialObject class
  2945. // [plType - OUT] : returns the currect group type
  2946. //
  2947. // Returns: HRESULT : S_OK if everything succeeded
  2948. // Otherwise an ADSI failure code
  2949. //
  2950. // History: 18-Sep-2000 JeffJon Created
  2951. //
  2952. //---------------------------------------------------------------------------
  2953. HRESULT ReadGroupType(PCWSTR pszDN,
  2954. const CDSCmdBasePathsInfo& refBasePathsInfo,
  2955. const CDSCmdCredentialObject& refCredentialObject,
  2956. long* plType)
  2957. {
  2958. ENTER_FUNCTION_HR(LEVEL3_LOGGING, ReadGroupType, hr);
  2959. do // false loop
  2960. {
  2961. if (!pszDN ||
  2962. !plType)
  2963. {
  2964. ASSERT(pszDN);
  2965. ASSERT(plType);
  2966. hr = E_INVALIDARG;
  2967. break;
  2968. }
  2969. //
  2970. // Convert the DN to a path
  2971. //
  2972. CComBSTR sbstrPath;
  2973. refBasePathsInfo.ComposePathFromDN(pszDN, sbstrPath);
  2974. //
  2975. // Bind and obtain the IADs interface to the user object
  2976. //
  2977. CComPtr<IADs> spADs;
  2978. hr = DSCmdOpenObject(refCredentialObject,
  2979. sbstrPath,
  2980. IID_IADs,
  2981. (void**)&spADs,
  2982. true);
  2983. if (FAILED(hr))
  2984. {
  2985. break;
  2986. }
  2987. CComVariant var;
  2988. hr = spADs->Get(L"groupType", &var);
  2989. if (FAILED(hr))
  2990. {
  2991. break;
  2992. }
  2993. ASSERT(var.vt == VT_I4);
  2994. *plType = var.lVal;
  2995. } while (false);
  2996. return hr;
  2997. }
  2998. //+--------------------------------------------------------------------------
  2999. //
  3000. // Function: SetGroupScope
  3001. //
  3002. // Synopsis: Sets the groupType attribute to local/universal/global
  3003. //
  3004. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  3005. // to the object being modified
  3006. // [refBasePathsInfo - IN] : reference to an instance of the
  3007. // CDSCmdBasePathsInfo class
  3008. // [refCredentialObject - IN] : reference to an instance of the
  3009. // CDSCmdCredentialObject class
  3010. // [pObjectEntry - IN] : pointer to an entry in the object table
  3011. // that defines the object we are modifying
  3012. // [argRecord - IN] : the argument record structure from the
  3013. // parser table that corresponds to this
  3014. // attribute
  3015. // [dwAttributeIdx - IN] : index into the attribute table for the
  3016. // object in which we are setting
  3017. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  3018. // which this function will fill in
  3019. //
  3020. // Returns: HRESULT : S_OK if everything succeeded
  3021. // Otherwise an ADSI failure code
  3022. //
  3023. // History: 18-Sep-2000 JeffJon Created
  3024. //
  3025. //---------------------------------------------------------------------------
  3026. HRESULT SetGroupScope(PCWSTR pszDN,
  3027. const CDSCmdBasePathsInfo& refBasePathsInfo,
  3028. const CDSCmdCredentialObject& refCredentialObject,
  3029. const PDSOBJECTTABLEENTRY pObjectEntry,
  3030. const ARG_RECORD& argRecord,
  3031. DWORD dwAttributeIdx,
  3032. PADS_ATTR_INFO* ppAttr)
  3033. {
  3034. ENTER_FUNCTION_HR(LEVEL3_LOGGING, SetGroupScope, hr);
  3035. do // false loop
  3036. {
  3037. //
  3038. // Verify parameters
  3039. //
  3040. if (!pszDN ||
  3041. !pObjectEntry ||
  3042. !ppAttr)
  3043. {
  3044. ASSERT(pszDN);
  3045. ASSERT(pObjectEntry);
  3046. ASSERT(ppAttr);
  3047. hr = E_INVALIDARG;
  3048. break;
  3049. }
  3050. *ppAttr = &(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo);
  3051. //
  3052. // Read the current group type
  3053. //
  3054. bool bUseExistingAttrInfo = false;
  3055. long lGroupType = 0;
  3056. if (!(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags & DS_ATTRIBUTE_READ))
  3057. {
  3058. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Group type has not been read, try reading it now");
  3059. hr = ReadGroupType(pszDN,
  3060. refBasePathsInfo,
  3061. refCredentialObject,
  3062. &lGroupType);
  3063. if (FAILED(hr))
  3064. {
  3065. //
  3066. // Just continue on without knowing since we are trying to set it anyway
  3067. //
  3068. hr = S_OK;
  3069. lGroupType = 0;
  3070. }
  3071. //
  3072. // Mark the attribute as read and allocate space for the new value
  3073. //
  3074. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags |= DS_ATTRIBUTE_READ;
  3075. (*ppAttr)->pADsValues = new ADSVALUE;
  3076. if (!(*ppAttr)->pADsValues)
  3077. {
  3078. hr = E_OUTOFMEMORY;
  3079. break;
  3080. }
  3081. (*ppAttr)->dwNumValues = 1;
  3082. (*ppAttr)->pADsValues->dwType = (*ppAttr)->dwADsType;
  3083. }
  3084. else
  3085. {
  3086. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Group type has been read, just use that one");
  3087. //
  3088. // If the attribute hasn't been set yet create a new value for it
  3089. //
  3090. if (!(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags & DS_ATTRIBUTE_DIRTY))
  3091. {
  3092. (*ppAttr)->pADsValues = new ADSVALUE;
  3093. if (!(*ppAttr)->pADsValues)
  3094. {
  3095. hr = E_OUTOFMEMORY;
  3096. break;
  3097. }
  3098. (*ppAttr)->dwNumValues = 1;
  3099. (*ppAttr)->pADsValues->dwType = (*ppAttr)->dwADsType;
  3100. }
  3101. lGroupType = (*ppAttr)->pADsValues->Integer;
  3102. bUseExistingAttrInfo = true;
  3103. }
  3104. DEBUG_OUTPUT(LEVEL3_LOGGING, L"old grouptype = 0x%x", lGroupType);
  3105. //
  3106. // Remember the security bit
  3107. //
  3108. bool bIsSecurityEnabled = (lGroupType & GROUP_TYPE_SECURITY_ENABLED) != 0;
  3109. //
  3110. // Clear out the old value
  3111. //
  3112. lGroupType = 0;
  3113. //
  3114. // The parser should have already verified that the strValue contains
  3115. // either 'l', 'g', or 'u'
  3116. //
  3117. CComBSTR sbstrInput;
  3118. sbstrInput = argRecord.strValue;
  3119. sbstrInput.ToLower();
  3120. if (sbstrInput == g_bstrGroupScopeLocal)
  3121. {
  3122. //
  3123. // Local group
  3124. //
  3125. lGroupType = GROUP_TYPE_RESOURCE_GROUP;
  3126. }
  3127. else if (sbstrInput == g_bstrGroupScopeGlobal)
  3128. {
  3129. //
  3130. // Global group
  3131. //
  3132. lGroupType = GROUP_TYPE_ACCOUNT_GROUP;
  3133. }
  3134. else if (sbstrInput == g_bstrGroupScopeUniversal)
  3135. {
  3136. //
  3137. // Universal group
  3138. //
  3139. lGroupType = GROUP_TYPE_UNIVERSAL_GROUP;
  3140. }
  3141. else
  3142. {
  3143. *ppAttr = NULL;
  3144. hr = E_INVALIDARG;
  3145. break;
  3146. }
  3147. //
  3148. // Reset the security bit
  3149. //
  3150. if (bIsSecurityEnabled)
  3151. {
  3152. lGroupType |= GROUP_TYPE_SECURITY_ENABLED;
  3153. }
  3154. //
  3155. // Set the new value in the ADS_ATTR_INFO
  3156. //
  3157. (*ppAttr)->pADsValues->Integer = lGroupType;
  3158. DEBUG_OUTPUT(LEVEL3_LOGGING, L"new grouptype = 0x%x", lGroupType);
  3159. //
  3160. // Mark the attribute as dirty
  3161. //
  3162. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags |= DS_ATTRIBUTE_DIRTY;
  3163. //
  3164. // if the attribute was previously read then we don't need to add another ADS_ATTR_INFO
  3165. //
  3166. if (bUseExistingAttrInfo)
  3167. {
  3168. *ppAttr = NULL;
  3169. }
  3170. } while (false);
  3171. return hr;
  3172. }
  3173. //+--------------------------------------------------------------------------
  3174. //
  3175. // Function: ChangeGroupScope
  3176. //
  3177. // Synopsis: Sets the groupType attribute to local/universal/global
  3178. //
  3179. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  3180. // to the object being modified
  3181. // [refBasePathsInfo - IN] : reference to an instance of the
  3182. // CDSCmdBasePathsInfo class
  3183. // [refCredentialObject - IN] : reference to an instance of the
  3184. // CDSCmdCredentialObject class
  3185. // [pObjectEntry - IN] : pointer to an entry in the object table
  3186. // that defines the object we are modifying
  3187. // [argRecord - IN] : the argument record structure from the
  3188. // parser table that corresponds to this
  3189. // attribute
  3190. // [dwAttributeIdx - IN] : index into the attribute table for the
  3191. // object in which we are setting
  3192. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  3193. // which this function will fill in
  3194. //
  3195. // Returns: HRESULT : S_OK if everything succeeded
  3196. // Otherwise an ADSI failure code
  3197. //
  3198. // History: 18-Sep-2000 JeffJon Created
  3199. //
  3200. //---------------------------------------------------------------------------
  3201. HRESULT ChangeGroupScope(PCWSTR pszDN,
  3202. const CDSCmdBasePathsInfo& refBasePathsInfo,
  3203. const CDSCmdCredentialObject& refCredentialObject,
  3204. const PDSOBJECTTABLEENTRY pObjectEntry,
  3205. const ARG_RECORD& argRecord,
  3206. DWORD dwAttributeIdx,
  3207. PADS_ATTR_INFO* ppAttr)
  3208. {
  3209. ENTER_FUNCTION_HR(LEVEL3_LOGGING, SetGroupScope, hr);
  3210. do // false loop
  3211. {
  3212. //
  3213. // Verify parameters
  3214. //
  3215. if (!pszDN ||
  3216. !pObjectEntry ||
  3217. !ppAttr)
  3218. {
  3219. ASSERT(pszDN);
  3220. ASSERT(pObjectEntry);
  3221. ASSERT(ppAttr);
  3222. hr = E_INVALIDARG;
  3223. break;
  3224. }
  3225. //
  3226. // Check the domain mode
  3227. //
  3228. bool bMixedMode = false;
  3229. hr = refBasePathsInfo.GetDomainMode(refCredentialObject,
  3230. bMixedMode);
  3231. if (FAILED(hr))
  3232. {
  3233. *ppAttr = NULL;
  3234. break;
  3235. }
  3236. if (bMixedMode)
  3237. {
  3238. //
  3239. // We don't allow group type to be changed in Mixed Mode
  3240. //
  3241. DisplayErrorMessage(g_pszDSCommandName,
  3242. pszDN,
  3243. E_FAIL,
  3244. IDS_FAILED_CHANGE_GROUP_DOMAIN_VERSION);
  3245. hr = S_FALSE;
  3246. break;
  3247. }
  3248. hr = SetGroupScope(pszDN,
  3249. refBasePathsInfo,
  3250. refCredentialObject,
  3251. pObjectEntry,
  3252. argRecord,
  3253. dwAttributeIdx,
  3254. ppAttr);
  3255. } while (false);
  3256. return hr;
  3257. }
  3258. //+--------------------------------------------------------------------------
  3259. //
  3260. // Function: SetGroupSecurity
  3261. //
  3262. // Synopsis: Sets the groupType to be security enabled or disabled
  3263. //
  3264. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  3265. // to the object being modified
  3266. // [refBasePathsInfo - IN] : reference to an instance of the
  3267. // CDSCmdBasePathsInfo class
  3268. // [refCredentialObject - IN] : reference to an instance of the
  3269. // CDSCmdCredentialObject class
  3270. // [pObjectEntry - IN] : pointer to an entry in the object table
  3271. // that defines the object we are modifying
  3272. // [argRecord - IN] : the argument record structure from the
  3273. // parser table that corresponds to this
  3274. // attribute
  3275. // [dwAttributeIdx - IN] : index into the attribute table for the
  3276. // object in which we are setting
  3277. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  3278. // which this function will fill in
  3279. //
  3280. // Returns: HRESULT : S_OK if everything succeeded
  3281. // Otherwise an ADSI failure code
  3282. //
  3283. // History: 18-Sep-2000 JeffJon Created
  3284. //
  3285. //---------------------------------------------------------------------------
  3286. HRESULT SetGroupSecurity(PCWSTR pszDN,
  3287. const CDSCmdBasePathsInfo& refBasePathsInfo,
  3288. const CDSCmdCredentialObject& refCredentialObject,
  3289. const PDSOBJECTTABLEENTRY pObjectEntry,
  3290. const ARG_RECORD& argRecord,
  3291. DWORD dwAttributeIdx,
  3292. PADS_ATTR_INFO* ppAttr)
  3293. {
  3294. ENTER_FUNCTION_HR(LEVEL3_LOGGING, SetGroupSecurity, hr);
  3295. do // false loop
  3296. {
  3297. //
  3298. // Verify parameters
  3299. //
  3300. if (!pszDN ||
  3301. !pObjectEntry ||
  3302. !ppAttr)
  3303. {
  3304. ASSERT(pszDN);
  3305. ASSERT(pObjectEntry);
  3306. ASSERT(ppAttr);
  3307. hr = E_INVALIDARG;
  3308. break;
  3309. }
  3310. *ppAttr = &(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo);
  3311. //
  3312. // Read the current group type
  3313. //
  3314. bool bUseExistingAttrInfo = false;
  3315. long lGroupType = 0;
  3316. if (!(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags & DS_ATTRIBUTE_READ))
  3317. {
  3318. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Group type has not been read, try reading it now");
  3319. hr = ReadGroupType(pszDN,
  3320. refBasePathsInfo,
  3321. refCredentialObject,
  3322. &lGroupType);
  3323. if (FAILED(hr))
  3324. {
  3325. //
  3326. // Continue on anyway since we are trying to set this attribute
  3327. //
  3328. hr = S_OK;
  3329. lGroupType = 0;
  3330. }
  3331. //
  3332. // Mark the attribute as read and allocate space for the new value
  3333. //
  3334. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags |= DS_ATTRIBUTE_READ;
  3335. (*ppAttr)->pADsValues = new ADSVALUE;
  3336. if (!(*ppAttr)->pADsValues)
  3337. {
  3338. hr = E_OUTOFMEMORY;
  3339. break;
  3340. }
  3341. (*ppAttr)->dwNumValues = 1;
  3342. (*ppAttr)->pADsValues->dwType = (*ppAttr)->dwADsType;
  3343. }
  3344. else
  3345. {
  3346. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Group type has been read, just use that one");
  3347. //
  3348. // if the attribute hasn't been marked dirty allocate space for the value
  3349. //
  3350. if (!(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags & DS_ATTRIBUTE_READ))
  3351. {
  3352. (*ppAttr)->pADsValues = new ADSVALUE;
  3353. if (!(*ppAttr)->pADsValues)
  3354. {
  3355. hr = E_OUTOFMEMORY;
  3356. break;
  3357. }
  3358. (*ppAttr)->dwNumValues = 1;
  3359. (*ppAttr)->pADsValues->dwType = (*ppAttr)->dwADsType;
  3360. }
  3361. lGroupType = (*ppAttr)->pADsValues->Integer;
  3362. bUseExistingAttrInfo = true;
  3363. }
  3364. DEBUG_OUTPUT(LEVEL3_LOGGING, L"old grouptype = 0x%x", lGroupType);
  3365. if (argRecord.bValue)
  3366. {
  3367. lGroupType |= GROUP_TYPE_SECURITY_ENABLED;
  3368. }
  3369. else
  3370. {
  3371. lGroupType &= ~(GROUP_TYPE_SECURITY_ENABLED);
  3372. }
  3373. //
  3374. // Set the new value in the ADS_ATTR_INFO
  3375. //
  3376. (*ppAttr)->pADsValues->Integer = lGroupType;
  3377. DEBUG_OUTPUT(LEVEL3_LOGGING, L"new grouptype = 0x%x", lGroupType);
  3378. //
  3379. // Mark the attribute as dirty
  3380. //
  3381. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags |= DS_ATTRIBUTE_DIRTY;
  3382. //
  3383. // if we are just using the existing ADS_ATTR_INFO don't return a new one
  3384. //
  3385. if (bUseExistingAttrInfo)
  3386. {
  3387. *ppAttr = NULL;
  3388. }
  3389. } while (false);
  3390. return hr;
  3391. }
  3392. //+--------------------------------------------------------------------------
  3393. //
  3394. // Function: ChangeGroupSecurity
  3395. //
  3396. // Synopsis: Sets the groupType to be security enabled or disabled but
  3397. // checks if we are in native mode first
  3398. //
  3399. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  3400. // to the object being modified
  3401. // [refBasePathsInfo - IN] : reference to an instance of the
  3402. // CDSCmdBasePathsInfo class
  3403. // [refCredentialObject - IN] : reference to an instance of the
  3404. // CDSCmdCredentialObject class
  3405. // [pObjectEntry - IN] : pointer to an entry in the object table
  3406. // that defines the object we are modifying
  3407. // [argRecord - IN] : the argument record structure from the
  3408. // parser table that corresponds to this
  3409. // attribute
  3410. // [dwAttributeIdx - IN] : index into the attribute table for the
  3411. // object in which we are setting
  3412. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  3413. // which this function will fill in
  3414. //
  3415. // Returns: HRESULT : S_OK if everything succeeded
  3416. // Otherwise an ADSI failure code
  3417. //
  3418. // History: 18-Sep-2000 JeffJon Created
  3419. //
  3420. //---------------------------------------------------------------------------
  3421. HRESULT ChangeGroupSecurity(PCWSTR pszDN,
  3422. const CDSCmdBasePathsInfo& refBasePathsInfo,
  3423. const CDSCmdCredentialObject& refCredentialObject,
  3424. const PDSOBJECTTABLEENTRY pObjectEntry,
  3425. const ARG_RECORD& argRecord,
  3426. DWORD dwAttributeIdx,
  3427. PADS_ATTR_INFO* ppAttr)
  3428. {
  3429. ENTER_FUNCTION_HR(LEVEL3_LOGGING, SetGroupSecurity, hr);
  3430. do // false loop
  3431. {
  3432. //
  3433. // Verify parameters
  3434. //
  3435. if (!pszDN ||
  3436. !pObjectEntry ||
  3437. !ppAttr)
  3438. {
  3439. ASSERT(pszDN);
  3440. ASSERT(pObjectEntry);
  3441. ASSERT(ppAttr);
  3442. hr = E_INVALIDARG;
  3443. break;
  3444. }
  3445. //
  3446. // Check the domain mode
  3447. //
  3448. bool bMixedMode = false;
  3449. hr = refBasePathsInfo.GetDomainMode(refCredentialObject,
  3450. bMixedMode);
  3451. if (FAILED(hr))
  3452. {
  3453. *ppAttr = NULL;
  3454. break;
  3455. }
  3456. if (bMixedMode)
  3457. {
  3458. //
  3459. // We don't allow group type to be changed in Mixed Mode
  3460. //
  3461. DisplayErrorMessage(g_pszDSCommandName,
  3462. pszDN,
  3463. E_FAIL,
  3464. IDS_FAILED_CHANGE_GROUP_DOMAIN_VERSION);
  3465. hr = S_FALSE;
  3466. break;
  3467. }
  3468. hr = SetGroupSecurity(pszDN,
  3469. refBasePathsInfo,
  3470. refCredentialObject,
  3471. pObjectEntry,
  3472. argRecord,
  3473. dwAttributeIdx,
  3474. ppAttr);
  3475. } while (false);
  3476. return hr;
  3477. }
  3478. //+--------------------------------------------------------------------------
  3479. //
  3480. // Function: ModifyGroupMembers
  3481. //
  3482. // Synopsis: Sets the groupType to be security enabled or disabled
  3483. //
  3484. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  3485. // to the object being modified
  3486. // [refBasePathsInfo - IN] : reference to an instance of the
  3487. // CDSCmdBasePathsInfo class
  3488. // [refCredentialObject - IN] : reference to an instance of the
  3489. // CDSCmdCredentialObject class
  3490. // [pObjectEntry - IN] : pointer to an entry in the object table
  3491. // that defines the object we are modifying
  3492. // [argRecord - IN] : the argument record structure from the
  3493. // parser table that corresponds to this
  3494. // attribute
  3495. // [dwAttributeIdx - IN] : index into the attribute table for the
  3496. // object in which we are setting
  3497. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  3498. // which this function will fill in
  3499. //
  3500. // Returns: HRESULT : S_OK if everything succeeded
  3501. // Otherwise an ADSI failure code
  3502. //
  3503. // History: 18-Sep-2000 JeffJon Created
  3504. //
  3505. //---------------------------------------------------------------------------
  3506. HRESULT ModifyGroupMembers(PCWSTR pszDN,
  3507. const CDSCmdBasePathsInfo& /*refBasePathsInfo*/,
  3508. const CDSCmdCredentialObject& /*refCredentialObject*/,
  3509. const PDSOBJECTTABLEENTRY pObjectEntry,
  3510. const ARG_RECORD& argRecord,
  3511. DWORD dwAttributeIdx,
  3512. PADS_ATTR_INFO* ppAttr)
  3513. {
  3514. ENTER_FUNCTION_HR(LEVEL3_LOGGING, ModifyGroupMembers, hr);
  3515. do // false loop
  3516. {
  3517. //
  3518. // Verify parameters
  3519. //
  3520. if (!pszDN ||
  3521. !pObjectEntry ||
  3522. !ppAttr)
  3523. {
  3524. ASSERT(pszDN);
  3525. ASSERT(pObjectEntry);
  3526. ASSERT(ppAttr);
  3527. hr = E_INVALIDARG;
  3528. break;
  3529. }
  3530. *ppAttr = &(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo);
  3531. UINT nStrings = 0;
  3532. PWSTR* ppszArray = NULL;
  3533. ParseNullSeparatedString(argRecord.strValue,
  3534. &ppszArray,
  3535. &nStrings);
  3536. if (nStrings < 1 ||
  3537. !ppszArray)
  3538. {
  3539. *ppAttr = NULL;
  3540. hr = E_OUTOFMEMORY;
  3541. break;
  3542. }
  3543. (*ppAttr)->pADsValues = new ADSVALUE[nStrings];
  3544. if (!(*ppAttr)->pADsValues)
  3545. {
  3546. *ppAttr = NULL;
  3547. LocalFree(ppszArray);
  3548. hr = E_OUTOFMEMORY;
  3549. break;
  3550. }
  3551. (*ppAttr)->dwNumValues = nStrings;
  3552. for (UINT nIdx = 0; nIdx < nStrings; nIdx++)
  3553. {
  3554. (*ppAttr)->pADsValues[nIdx].dwType = (*ppAttr)->dwADsType;
  3555. (*ppAttr)->pADsValues[nIdx].DNString = ppszArray[nIdx];
  3556. }
  3557. //
  3558. // Mark the attribute as dirty
  3559. //
  3560. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags |= DS_ATTRIBUTE_DIRTY;
  3561. if (ppszArray)
  3562. {
  3563. LocalFree(ppszArray);
  3564. }
  3565. } while (false);
  3566. return hr;
  3567. }
  3568. //+--------------------------------------------------------------------------
  3569. //
  3570. // Function: ReadGroupMembership
  3571. //
  3572. // Synopsis: Reads the group members list
  3573. //
  3574. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  3575. // to the object being modified
  3576. // [refBasePathsInfo - IN] : reference to an instance of the
  3577. // CDSCmdBasePathsInfo class
  3578. // [refCredentialObject - IN] : reference to an instance of the
  3579. // CDSCmdCredentialObject class
  3580. // [ppMembersAttr - OUT] : returns the currect group membership
  3581. // this value must be freed using FreeADsMem
  3582. //
  3583. // Returns: HRESULT : S_OK if everything succeeded
  3584. // Otherwise an ADSI failure code
  3585. //
  3586. // History: 18-Sep-2000 JeffJon Created
  3587. //
  3588. //---------------------------------------------------------------------------
  3589. HRESULT ReadGroupMembership(PCWSTR pszDN,
  3590. const CDSCmdBasePathsInfo& refBasePathsInfo,
  3591. const CDSCmdCredentialObject& refCredentialObject,
  3592. PADS_ATTR_INFO* ppMembersAttr)
  3593. {
  3594. ENTER_FUNCTION_HR(LEVEL3_LOGGING, ReadGroupMembership, hr);
  3595. do // false loop
  3596. {
  3597. if (!pszDN ||
  3598. !ppMembersAttr)
  3599. {
  3600. ASSERT(pszDN);
  3601. ASSERT(ppMembersAttr);
  3602. hr = E_INVALIDARG;
  3603. break;
  3604. }
  3605. //
  3606. // Convert the DN to a path
  3607. //
  3608. CComBSTR sbstrPath;
  3609. refBasePathsInfo.ComposePathFromDN(pszDN, sbstrPath);
  3610. //
  3611. // Bind and obtain the IADs interface to the user object
  3612. //
  3613. CComPtr<IDirectoryObject> spObject;
  3614. hr = DSCmdOpenObject(refCredentialObject,
  3615. sbstrPath,
  3616. IID_IDirectoryObject,
  3617. (void**)&spObject,
  3618. true);
  3619. if (FAILED(hr))
  3620. {
  3621. break;
  3622. }
  3623. DWORD dwNumReturned = 0;
  3624. PWSTR ppszAttrs[] = { L"member" };
  3625. hr = spObject->GetObjectAttributes(ppszAttrs,
  3626. sizeof(ppszAttrs)/sizeof(PWSTR),
  3627. ppMembersAttr,
  3628. &dwNumReturned);
  3629. if (FAILED(hr))
  3630. {
  3631. break;
  3632. }
  3633. } while (false);
  3634. return hr;
  3635. }
  3636. //+--------------------------------------------------------------------------
  3637. //
  3638. // Function: ShowRemoveFromGroupFailure
  3639. //
  3640. // Synopsis: Displays an error message as a result of failure to remove
  3641. // an object from a group
  3642. //
  3643. // Arguments: [hr - IN] : failure code
  3644. // [pszDN - IN] : DN of the group object
  3645. // [pszMember - IN] : DN of the member being removed
  3646. //
  3647. // Returns: HRESULT : S_OK if everything succeeded
  3648. // Otherwise an ADSI failure code
  3649. //
  3650. // History: 06-Dec-2000 JeffJon Created
  3651. //
  3652. //---------------------------------------------------------------------------
  3653. HRESULT ShowRemoveFromGroupFailure(HRESULT hrResult,
  3654. PCWSTR pszDN,
  3655. PCWSTR pszMember)
  3656. {
  3657. ENTER_FUNCTION_HR(LEVEL3_LOGGING, ShowRemoveFromGroupFailure, hr);
  3658. do // false loop
  3659. {
  3660. //
  3661. // Verify parameters
  3662. //
  3663. if (!pszDN ||
  3664. !pszMember)
  3665. {
  3666. ASSERT(pszDN);
  3667. ASSERT(pszMember);
  3668. hr = E_INVALIDARG;
  3669. break;
  3670. }
  3671. bool bShowGenericMessage = true;
  3672. CComBSTR sbstrFormatter;
  3673. bool bLoadFormatString = sbstrFormatter.LoadString(::GetModuleHandle(NULL),
  3674. IDS_ERRMSG_REMOVE_FROM_GROUP);
  3675. if (bLoadFormatString)
  3676. {
  3677. size_t messageLength = wcslen(sbstrFormatter) + wcslen(pszMember);
  3678. PWSTR pszMessage = new WCHAR[messageLength + 1];
  3679. if (pszMessage)
  3680. {
  3681. wsprintf(pszMessage, sbstrFormatter, pszMember);
  3682. DisplayErrorMessage(g_pszDSCommandName,
  3683. pszDN,
  3684. hrResult,
  3685. pszMessage);
  3686. bShowGenericMessage = false;
  3687. delete[] pszMessage;
  3688. pszMessage = 0;
  3689. }
  3690. }
  3691. if (bShowGenericMessage)
  3692. {
  3693. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Failed to load the string IDS_ERRMSG_REMOVE_FROM_GROUP from the resource file");
  3694. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Using the default message instead");
  3695. DisplayErrorMessage(g_pszDSCommandName,
  3696. pszDN,
  3697. hrResult);
  3698. }
  3699. } while (false);
  3700. return hr;
  3701. }
  3702. //+--------------------------------------------------------------------------
  3703. //
  3704. // Function: RemoveGroupMembers
  3705. //
  3706. // Synopsis: Removes the specified members from the group
  3707. //
  3708. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  3709. // to the object being modified
  3710. // [refBasePathsInfo - IN] : reference to an instance of the
  3711. // CDSCmdBasePathsInfo class
  3712. // [refCredentialObject - IN] : reference to an instance of the
  3713. // CDSCmdCredentialObject class
  3714. // [pObjectEntry - IN] : pointer to an entry in the object table
  3715. // that defines the object we are modifying
  3716. // [argRecord - IN] : the argument record structure from the
  3717. // parser table that corresponds to this
  3718. // attribute
  3719. // [dwAttributeIdx - IN] : index into the attribute table for the
  3720. // object in which we are setting
  3721. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  3722. // which this function will fill in
  3723. //
  3724. // Returns: HRESULT : S_OK if everything succeeded
  3725. // Otherwise an ADSI failure code
  3726. //
  3727. // History: 18-Sep-2000 JeffJon Created
  3728. //
  3729. //---------------------------------------------------------------------------
  3730. HRESULT RemoveGroupMembers(PCWSTR pszDN,
  3731. const CDSCmdBasePathsInfo& refBasePathsInfo,
  3732. const CDSCmdCredentialObject& refCredentialObject,
  3733. const PDSOBJECTTABLEENTRY pObjectEntry,
  3734. const ARG_RECORD& argRecord,
  3735. DWORD /*dwAttributeIdx*/,
  3736. PADS_ATTR_INFO* ppAttr)
  3737. {
  3738. ENTER_FUNCTION_HR(LEVEL3_LOGGING, RemoveGroupMembers, hr);
  3739. do // false loop
  3740. {
  3741. //
  3742. // Verify parameters
  3743. //
  3744. if (!pszDN ||
  3745. !pObjectEntry ||
  3746. !ppAttr)
  3747. {
  3748. ASSERT(pszDN);
  3749. ASSERT(pObjectEntry);
  3750. ASSERT(ppAttr);
  3751. hr = E_INVALIDARG;
  3752. break;
  3753. }
  3754. //
  3755. // We won't be returning an attribute
  3756. //
  3757. *ppAttr = 0;
  3758. //
  3759. // Parse the members to be removed
  3760. //
  3761. UINT nStrings = 0;
  3762. PWSTR* ppszArray = NULL;
  3763. ParseNullSeparatedString(argRecord.strValue,
  3764. &ppszArray,
  3765. &nStrings);
  3766. if (nStrings < 1 ||
  3767. !ppszArray)
  3768. {
  3769. *ppAttr = NULL;
  3770. hr = E_OUTOFMEMORY;
  3771. break;
  3772. }
  3773. //
  3774. // Convert the DN to a path
  3775. //
  3776. CComBSTR sbstrPath;
  3777. refBasePathsInfo.ComposePathFromDN(pszDN, sbstrPath);
  3778. //
  3779. // Bind and obtain the IADs interface to the user object
  3780. //
  3781. CComPtr<IADsGroup> spGroup;
  3782. hr = DSCmdOpenObject(refCredentialObject,
  3783. sbstrPath,
  3784. IID_IADsGroup,
  3785. (void**)&spGroup,
  3786. true);
  3787. if (FAILED(hr))
  3788. {
  3789. break;
  3790. }
  3791. //
  3792. // Remove each of the objects from the group
  3793. //
  3794. for (UINT nStringIdx = 0; nStringIdx < nStrings; nStringIdx++)
  3795. {
  3796. //
  3797. // Convert the member DN into a ADSI path
  3798. //
  3799. CComBSTR sbstrMemberPath;
  3800. refBasePathsInfo.ComposePathFromDN(ppszArray[nStringIdx], sbstrMemberPath);
  3801. //
  3802. // Remove the member
  3803. //
  3804. hr = spGroup->Remove(sbstrMemberPath);
  3805. if (FAILED(hr))
  3806. {
  3807. ShowRemoveFromGroupFailure(hr, pszDN, ppszArray[nStringIdx]);
  3808. hr = S_FALSE;
  3809. break;
  3810. }
  3811. }
  3812. if (ppszArray)
  3813. {
  3814. LocalFree(ppszArray);
  3815. }
  3816. } while (false);
  3817. return hr;
  3818. }
  3819. //+--------------------------------------------------------------------------
  3820. //
  3821. // Function: MakeMemberOf
  3822. //
  3823. // Synopsis: Makes the object specified by pszDN a member of the group
  3824. // specified in the argRecord.strValue
  3825. //
  3826. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  3827. // to the object being modified
  3828. // [refBasePathsInfo - IN] : reference to an instance of the
  3829. // CDSCmdBasePathsInfo class
  3830. // [refCredentialObject - IN] : reference to an instance of the
  3831. // CDSCmdCredentialObject class
  3832. // [pObjectEntry - IN] : pointer to an entry in the object table
  3833. // that defines the object we are modifying
  3834. // [argRecord - IN] : the argument record structure from the
  3835. // parser table that corresponds to this
  3836. // attribute
  3837. // [dwAttributeIdx - IN] : index into the attribute table for the
  3838. // object in which we are setting
  3839. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  3840. // which this function will fill in
  3841. //
  3842. // Returns: HRESULT : S_OK if everything succeeded
  3843. // Otherwise an ADSI failure code
  3844. //
  3845. // History: 25-Sep-2000 JeffJon Created
  3846. //
  3847. //---------------------------------------------------------------------------
  3848. HRESULT MakeMemberOf(PCWSTR pszDN,
  3849. const CDSCmdBasePathsInfo& refBasePathsInfo,
  3850. const CDSCmdCredentialObject& refCredentialObject,
  3851. const PDSOBJECTTABLEENTRY pObjectEntry,
  3852. const ARG_RECORD& argRecord,
  3853. DWORD dwAttributeIdx,
  3854. PADS_ATTR_INFO* ppAttr)
  3855. {
  3856. ENTER_FUNCTION_HR(LEVEL3_LOGGING, MakeMemberOf, hr);
  3857. PWSTR* ppszArray = NULL;
  3858. do // false loop
  3859. {
  3860. //
  3861. // Verify parameters
  3862. //
  3863. if (!pszDN ||
  3864. !pObjectEntry ||
  3865. !ppAttr)
  3866. {
  3867. ASSERT(pszDN);
  3868. ASSERT(pObjectEntry);
  3869. ASSERT(ppAttr);
  3870. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Invalid args");
  3871. hr = E_INVALIDARG;
  3872. break;
  3873. }
  3874. //
  3875. // We are going to do all the work here so don't pass back the ADS_ATTR_INFO
  3876. //
  3877. *ppAttr = NULL;
  3878. ADS_ATTR_INFO* pMemberAttr = &(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo);
  3879. UINT nStrings = 0;
  3880. ParseNullSeparatedString(argRecord.strValue,
  3881. &ppszArray,
  3882. &nStrings);
  3883. if (nStrings < 1 ||
  3884. !ppszArray)
  3885. {
  3886. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Failed to parse null separated string list of groups");
  3887. hr = E_OUTOFMEMORY;
  3888. break;
  3889. }
  3890. //
  3891. // Create the value
  3892. //
  3893. ADSVALUE MemberValue = { ADSTYPE_DN_STRING, NULL };
  3894. pMemberAttr->pADsValues = &MemberValue;
  3895. pMemberAttr->dwNumValues = 1;
  3896. pMemberAttr->dwControlCode = ADS_ATTR_APPEND;
  3897. pMemberAttr->pADsValues->DNString = (PWSTR)pszDN;
  3898. //
  3899. // For each group in the list add the object to the group
  3900. //
  3901. for (UINT nIdx = 0; nIdx < nStrings; nIdx++)
  3902. {
  3903. PWSTR pszGroupDN = ppszArray[nIdx];
  3904. ASSERT(pszGroupDN);
  3905. CComBSTR sbstrGroupPath;
  3906. refBasePathsInfo.ComposePathFromDN(pszGroupDN, sbstrGroupPath);
  3907. CComPtr<IDirectoryObject> spDirObject;
  3908. hr = DSCmdOpenObject(refCredentialObject,
  3909. sbstrGroupPath,
  3910. IID_IDirectoryObject,
  3911. (void**)&spDirObject,
  3912. true);
  3913. if (FAILED(hr))
  3914. {
  3915. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Failed to open group object: %s", sbstrGroupPath);
  3916. break;
  3917. }
  3918. DWORD dwNumAttrs = 0;
  3919. hr = spDirObject->SetObjectAttributes(pMemberAttr,
  3920. 1,
  3921. &dwNumAttrs);
  3922. if (FAILED(hr))
  3923. {
  3924. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Failed to set object attributes on group object: %s", sbstrGroupPath);
  3925. break;
  3926. }
  3927. }
  3928. } while (false);
  3929. if (ppszArray)
  3930. {
  3931. LocalFree(ppszArray);
  3932. }
  3933. return hr;
  3934. }
  3935. //+--------------------------------------------------------------------------
  3936. //
  3937. // Function: SetIsGC
  3938. //
  3939. // Synopsis: Makes the server object specified by pszDN into a GC or not
  3940. // by modifying the NTDS Settings object contained within
  3941. //
  3942. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  3943. // to the object being modified
  3944. // [refBasePathsInfo - IN] : reference to an instance of the
  3945. // CDSCmdBasePathsInfo class
  3946. // [refCredentialObject - IN] : reference to an instance of the
  3947. // CDSCmdCredentialObject class
  3948. // [pObjectEntry - IN] : pointer to an entry in the object table
  3949. // that defines the object we are modifying
  3950. // [argRecord - IN] : the argument record structure from the
  3951. // parser table that corresponds to this
  3952. // attribute
  3953. // [dwAttributeIdx - IN] : index into the attribute table for the
  3954. // object in which we are setting
  3955. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  3956. // which this function will fill in
  3957. //
  3958. // Returns: HRESULT : S_OK if everything succeeded
  3959. // Otherwise an ADSI failure code
  3960. //
  3961. // History: 14-Apr-2001 JeffJon Created
  3962. //
  3963. //---------------------------------------------------------------------------
  3964. HRESULT SetIsGC(PCWSTR pszDN,
  3965. const CDSCmdBasePathsInfo& refBasePathsInfo,
  3966. const CDSCmdCredentialObject& refCredentialObject,
  3967. const PDSOBJECTTABLEENTRY pObjectEntry,
  3968. const ARG_RECORD& argRecord,
  3969. DWORD /*dwAttributeIdx*/,
  3970. PADS_ATTR_INFO* ppAttr)
  3971. {
  3972. ENTER_FUNCTION_HR(LEVEL3_LOGGING, SetIsGC, hr);
  3973. PADS_ATTR_INFO pAttrInfo = 0;
  3974. do // false loop
  3975. {
  3976. //
  3977. // Verify parameters
  3978. //
  3979. if (!pszDN ||
  3980. !pObjectEntry ||
  3981. !ppAttr)
  3982. {
  3983. ASSERT(pszDN);
  3984. ASSERT(pObjectEntry);
  3985. ASSERT(ppAttr);
  3986. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Invalid args");
  3987. hr = E_INVALIDARG;
  3988. break;
  3989. }
  3990. //
  3991. // We are going to do all the work here so don't pass back the ADS_ATTR_INFO
  3992. //
  3993. *ppAttr = NULL;
  3994. //
  3995. // Get the NTDS Settings object that is contained within the server of the specified DN
  3996. //
  3997. CComBSTR sbstrSettingsDN = L"CN=NTDS Settings,";
  3998. sbstrSettingsDN += pszDN;
  3999. CComBSTR sbstrSettingsPath;
  4000. refBasePathsInfo.ComposePathFromDN(sbstrSettingsDN, sbstrSettingsPath);
  4001. DEBUG_OUTPUT(LEVEL3_LOGGING,
  4002. L"NTDS Settings path = %s",
  4003. sbstrSettingsPath);
  4004. CComPtr<IDirectoryObject> spDirectoryObject;
  4005. hr = DSCmdOpenObject(refCredentialObject,
  4006. sbstrSettingsPath,
  4007. IID_IDirectoryObject,
  4008. (void**)&spDirectoryObject,
  4009. true);
  4010. if (FAILED(hr))
  4011. {
  4012. break;
  4013. }
  4014. PWSTR pszAttrs[] = { L"options" };
  4015. DWORD dwReturned = 0;
  4016. hr = spDirectoryObject->GetObjectAttributes(pszAttrs, 1, &pAttrInfo, &dwReturned);
  4017. if (FAILED(hr))
  4018. {
  4019. DEBUG_OUTPUT(LEVEL3_LOGGING,
  4020. L"Failed to get old options: hr = 0x%x",
  4021. hr);
  4022. break;
  4023. }
  4024. if (dwReturned < 1 ||
  4025. !pAttrInfo)
  4026. {
  4027. DEBUG_OUTPUT(LEVEL3_LOGGING,
  4028. L"Get options succeeded but no values were returned.");
  4029. hr = E_FAIL;
  4030. break;
  4031. }
  4032. if (argRecord.bDefined &&
  4033. argRecord.bValue)
  4034. {
  4035. pAttrInfo->pADsValues->Integer |= 0x1;
  4036. }
  4037. else
  4038. {
  4039. pAttrInfo->pADsValues->Integer &= ~(0x1);
  4040. }
  4041. dwReturned = 0;
  4042. hr = spDirectoryObject->SetObjectAttributes(pAttrInfo, 1, &dwReturned);
  4043. if (FAILED(hr))
  4044. {
  4045. DEBUG_OUTPUT(LEVEL3_LOGGING,
  4046. L"Failed to set the new options: hr = 0x%x",
  4047. hr);
  4048. break;
  4049. }
  4050. ASSERT(dwReturned == 1);
  4051. } while (false);
  4052. if (pAttrInfo)
  4053. {
  4054. FreeADsMem(pAttrInfo);
  4055. }
  4056. return hr;
  4057. }
  4058. //+--------------------------------------------------------------------------
  4059. //
  4060. // Function: BuildComputerSAMName
  4061. //
  4062. // Synopsis: If the -samname argument was defined use that, else compute
  4063. // the same name from the DN
  4064. //
  4065. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  4066. // to the object being modified
  4067. // [refBasePathsInfo - IN] : reference to an instance of the
  4068. // CDSCmdBasePathsInfo class
  4069. // [refCredentialObject - IN] : reference to an instance of the
  4070. // CDSCmdCredentialObject class
  4071. // [pObjectEntry - IN] : pointer to an entry in the object table
  4072. // that defines the object we are modifying
  4073. // [argRecord - IN] : the argument record structure from the
  4074. // parser table that corresponds to this
  4075. // attribute
  4076. // [dwAttributeIdx - IN] : index into the attribute table for the
  4077. // object in which we are setting
  4078. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  4079. // which this function will fill in
  4080. //
  4081. // Returns: HRESULT : S_OK if everything succeeded
  4082. // Otherwise an ADSI failure code
  4083. //
  4084. // History: 09-Oct-2000 JeffJon Created
  4085. //
  4086. //---------------------------------------------------------------------------
  4087. HRESULT BuildComputerSAMName(PCWSTR pszDN,
  4088. const CDSCmdBasePathsInfo& /*refBasePathsInfo*/,
  4089. const CDSCmdCredentialObject& /*refCredentialObject*/,
  4090. const PDSOBJECTTABLEENTRY pObjectEntry,
  4091. const ARG_RECORD& argRecord,
  4092. DWORD dwAttributeIdx,
  4093. PADS_ATTR_INFO* ppAttr)
  4094. {
  4095. ENTER_FUNCTION_HR(LEVEL3_LOGGING, BuildComputerSAMName, hr);
  4096. do // false loop
  4097. {
  4098. //
  4099. // Verify parameters
  4100. //
  4101. if (!pszDN ||
  4102. !pObjectEntry ||
  4103. !ppAttr)
  4104. {
  4105. ASSERT(pszDN);
  4106. ASSERT(pObjectEntry);
  4107. ASSERT(ppAttr);
  4108. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Invalid args");
  4109. hr = E_INVALIDARG;
  4110. break;
  4111. }
  4112. *ppAttr = &(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo);
  4113. (*ppAttr)->pADsValues = new ADSVALUE[1];
  4114. if (!(*ppAttr)->pADsValues)
  4115. {
  4116. hr = E_OUTOFMEMORY;
  4117. break;
  4118. }
  4119. (*ppAttr)->dwNumValues = 1;
  4120. (*ppAttr)->pADsValues->dwType = (*ppAttr)->dwADsType;
  4121. if (!argRecord.bDefined ||
  4122. !argRecord.strValue)
  4123. {
  4124. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Constructing sAMAccountName for computer");
  4125. //
  4126. // If the object type is group and the sAMAccountName
  4127. // was not specified, construct it from the DN or name
  4128. //
  4129. CONST DWORD computerNameLen = MAX_COMPUTERNAME_LENGTH + 1;
  4130. DWORD Len = computerNameLen;
  4131. WCHAR szDownLevel[computerNameLen];
  4132. ZeroMemory(szDownLevel, computerNameLen * sizeof(WCHAR));
  4133. CComBSTR sbstrName;
  4134. hr = CPathCracker::GetObjectNameFromDN(pszDN,
  4135. sbstrName);
  4136. if (SUCCEEDED(hr))
  4137. {
  4138. //
  4139. // run through the OEM conversion, just to
  4140. // behave the same way as typing in the OEM
  4141. // edit box
  4142. //
  4143. CComBSTR sbstrOemUnicode;
  4144. _UnicodeToOemConvert(sbstrName, sbstrOemUnicode);
  4145. // run through the DNS validation
  4146. if (!DnsHostnameToComputerName(sbstrOemUnicode, szDownLevel, &Len))
  4147. {
  4148. DEBUG_OUTPUT(LEVEL3_LOGGING,
  4149. L"Failed in DnsHostnameToComputerName: GLE = 0x%x",
  4150. GetLastError());
  4151. Len = 0;
  4152. }
  4153. if (Len > 0)
  4154. {
  4155. //
  4156. // Validate the SAM name
  4157. //
  4158. HRESULT hrValidate = ValidateAndModifySAMName(szDownLevel,
  4159. INVALID_NETBIOS_AND_ACCOUNT_NAME_CHARS_WITH_AT);
  4160. if (FAILED(hrValidate))
  4161. {
  4162. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Unable to validate the SamAccountName");
  4163. ASSERT(SUCCEEDED(hrValidate));
  4164. break;
  4165. }
  4166. //
  4167. // Change the last character to a $
  4168. //
  4169. if (Len == MAX_COMPUTERNAME_LENGTH)
  4170. {
  4171. szDownLevel[Len - 1] = L'$';
  4172. }
  4173. else
  4174. {
  4175. szDownLevel[Len] = L'$';
  4176. szDownLevel[Len+1] = L'\0';
  4177. }
  4178. //
  4179. // Allocate enough memory for the string in the command args structure
  4180. // REVIEW_JEFFJON : this is being leaked
  4181. //
  4182. (*ppAttr)->pADsValues->CaseIgnoreString = (LPTSTR)LocalAlloc(LPTR, (wcslen(szDownLevel) + 1) * sizeof(WCHAR) );
  4183. if (!(*ppAttr)->pADsValues->CaseIgnoreString)
  4184. {
  4185. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Failed to allocate space for (*ppAttr)->pADsValues->CaseIgnoreString");
  4186. hr = E_OUTOFMEMORY;
  4187. break;
  4188. }
  4189. //
  4190. // Truncate the name if necessary but copy it to the command args structure
  4191. //
  4192. _tcsncpy((*ppAttr)->pADsValues->CaseIgnoreString,
  4193. szDownLevel,
  4194. wcslen(szDownLevel));
  4195. }
  4196. }
  4197. }
  4198. else
  4199. {
  4200. (*ppAttr)->pADsValues->CaseIgnoreString = argRecord.strValue;
  4201. }
  4202. } while (false);
  4203. return hr;
  4204. }
  4205. //+--------------------------------------------------------------------------
  4206. //
  4207. // Function: BuildGroupSAMName
  4208. //
  4209. // Synopsis: If the -samname argument was defined use that, else compute
  4210. // the same name from the DN
  4211. //
  4212. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  4213. // to the object being modified
  4214. // [refBasePathsInfo - IN] : reference to an instance of the
  4215. // CDSCmdBasePathsInfo class
  4216. // [refCredentialObject - IN] : reference to an instance of the
  4217. // CDSCmdCredentialObject class
  4218. // [pObjectEntry - IN] : pointer to an entry in the object table
  4219. // that defines the object we are modifying
  4220. // [argRecord - IN] : the argument record structure from the
  4221. // parser table that corresponds to this
  4222. // attribute
  4223. // [dwAttributeIdx - IN] : index into the attribute table for the
  4224. // object in which we are setting
  4225. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  4226. // which this function will fill in
  4227. //
  4228. // Returns: HRESULT : S_OK if everything succeeded
  4229. // Otherwise an ADSI failure code
  4230. //
  4231. // History: 09-Oct-2000 JeffJon Created
  4232. //
  4233. //---------------------------------------------------------------------------
  4234. HRESULT BuildGroupSAMName(PCWSTR pszDN,
  4235. const CDSCmdBasePathsInfo& /*refBasePathsInfo*/,
  4236. const CDSCmdCredentialObject& /*refCredentialObject*/,
  4237. const PDSOBJECTTABLEENTRY pObjectEntry,
  4238. const ARG_RECORD& argRecord,
  4239. DWORD dwAttributeIdx,
  4240. PADS_ATTR_INFO* ppAttr)
  4241. {
  4242. ENTER_FUNCTION_HR(LEVEL3_LOGGING, BuildGroupSAMName, hr);
  4243. do // false loop
  4244. {
  4245. //
  4246. // Verify parameters
  4247. //
  4248. if (!pszDN ||
  4249. !pObjectEntry ||
  4250. !ppAttr)
  4251. {
  4252. ASSERT(pszDN);
  4253. ASSERT(pObjectEntry);
  4254. ASSERT(ppAttr);
  4255. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Invalid args");
  4256. hr = E_INVALIDARG;
  4257. break;
  4258. }
  4259. *ppAttr = &(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo);
  4260. (*ppAttr)->pADsValues = new ADSVALUE[1];
  4261. if (!(*ppAttr)->pADsValues)
  4262. {
  4263. hr = E_OUTOFMEMORY;
  4264. break;
  4265. }
  4266. (*ppAttr)->dwNumValues = 1;
  4267. (*ppAttr)->pADsValues->dwType = (*ppAttr)->dwADsType;
  4268. if (!argRecord.bDefined ||
  4269. !argRecord.strValue)
  4270. {
  4271. DEBUG_OUTPUT(MINIMAL_LOGGING, L"Constructing sAMAccountName for group");
  4272. static const UINT nSamLength = 256;
  4273. //
  4274. // If the object type is group and the sAMAccountName
  4275. // was not specified, construct it from the DN or name
  4276. //
  4277. CComBSTR sbstrName;
  4278. hr = CPathCracker::GetObjectNameFromDN(pszDN,
  4279. sbstrName);
  4280. if (SUCCEEDED(hr))
  4281. {
  4282. UINT nNameLen = sbstrName.Length();
  4283. //
  4284. // Allocate enough memory for the string in the command args structure
  4285. // REVIEW_JEFFJON : this is being leaked
  4286. //
  4287. (*ppAttr)->pADsValues->CaseIgnoreString = (LPTSTR)LocalAlloc(LPTR, (nNameLen+2) * sizeof(TCHAR) );
  4288. if (!(*ppAttr)->pADsValues->CaseIgnoreString)
  4289. {
  4290. DEBUG_OUTPUT(MINIMAL_LOGGING, L"Failed to allocate space for (*ppAttr)->pADsValues->CaseIgnoreString");
  4291. hr = E_OUTOFMEMORY;
  4292. break;
  4293. }
  4294. //
  4295. // Truncate the name if necessary but copy it to the command args structure
  4296. //
  4297. _tcsncpy((*ppAttr)->pADsValues->CaseIgnoreString,
  4298. sbstrName,
  4299. (nNameLen > nSamLength) ? nSamLength : nNameLen);
  4300. }
  4301. }
  4302. else
  4303. {
  4304. (*ppAttr)->pADsValues->CaseIgnoreString = argRecord.strValue;
  4305. }
  4306. } while (false);
  4307. return hr;
  4308. }
  4309. //+--------------------------------------------------------------------------
  4310. //
  4311. // Function: SetComputerAccountType
  4312. //
  4313. // Synopsis: Sets the userAccountControl to make the object a workstation
  4314. //
  4315. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  4316. // to the object being modified
  4317. // [refBasePathsInfo - IN] : reference to an instance of the
  4318. // CDSCmdBasePathsInfo class
  4319. // [refCredentialObject - IN] : reference to an instance of the
  4320. // CDSCmdCredentialObject class
  4321. // [pObjectEntry - IN] : pointer to an entry in the object table
  4322. // that defines the object we are modifying
  4323. // [argRecord - IN] : the argument record structure from the
  4324. // parser table that corresponds to this
  4325. // attribute
  4326. // [dwAttributeIdx - IN] : index into the attribute table for the
  4327. // object in which we are setting
  4328. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  4329. // which this function will fill in
  4330. //
  4331. // Returns: HRESULT : S_OK if everything succeeded
  4332. // Otherwise an ADSI failure code
  4333. //
  4334. // History: 05-Dec-2000 JeffJon Created
  4335. //
  4336. //---------------------------------------------------------------------------
  4337. HRESULT SetComputerAccountType(PCWSTR pszDN,
  4338. const CDSCmdBasePathsInfo& /*refBasePathsInfo*/,
  4339. const CDSCmdCredentialObject& /*refCredentialObject*/,
  4340. const PDSOBJECTTABLEENTRY pObjectEntry,
  4341. const ARG_RECORD& /*argRecord*/,
  4342. DWORD dwAttributeIdx,
  4343. PADS_ATTR_INFO* ppAttr)
  4344. {
  4345. ENTER_FUNCTION_HR(LEVEL3_LOGGING, SetComputerAccountType, hr);
  4346. do // false loop
  4347. {
  4348. //
  4349. // Verify parameters
  4350. //
  4351. if (!pszDN ||
  4352. !pObjectEntry ||
  4353. !ppAttr)
  4354. {
  4355. ASSERT(pszDN);
  4356. ASSERT(pObjectEntry);
  4357. ASSERT(ppAttr);
  4358. hr = E_INVALIDARG;
  4359. break;
  4360. }
  4361. long lUserAccountControl = 0;
  4362. //
  4363. // If the userAccountControl hasn't already been read, do so now
  4364. //
  4365. if (0 == (DS_ATTRIBUTE_READ & pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags))
  4366. {
  4367. //
  4368. // Mark the table entry as read
  4369. //
  4370. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags |= DS_ATTRIBUTE_READ;
  4371. *ppAttr = &(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo);
  4372. (*ppAttr)->pADsValues = new ADSVALUE;
  4373. if (!(*ppAttr)->pADsValues)
  4374. {
  4375. hr = E_OUTOFMEMORY;
  4376. break;
  4377. }
  4378. (*ppAttr)->pADsValues->dwType = (*ppAttr)->dwADsType;
  4379. (*ppAttr)->dwNumValues = 1;
  4380. }
  4381. else
  4382. {
  4383. if (!pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues)
  4384. {
  4385. ASSERT(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues);
  4386. hr = E_INVALIDARG;
  4387. break;
  4388. }
  4389. lUserAccountControl = pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues->Integer;
  4390. //
  4391. // Don't create a new entry in the ADS_ATTR_INFO array
  4392. //
  4393. *ppAttr = NULL;
  4394. }
  4395. ASSERT(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues);
  4396. //
  4397. // Add in the required workstation flags
  4398. //
  4399. lUserAccountControl |= UF_WORKSTATION_TRUST_ACCOUNT | UF_ACCOUNTDISABLE | UF_PASSWD_NOTREQD;
  4400. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues->Integer = lUserAccountControl;
  4401. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags |= DS_ATTRIBUTE_DIRTY;
  4402. } while (false);
  4403. return hr;
  4404. }
  4405. //+--------------------------------------------------------------------------
  4406. //
  4407. // Function: GetErrorMessage
  4408. //
  4409. // Synopsis: Retrieves the error message associated with the HRESULT by
  4410. // using FormatMessage
  4411. //
  4412. // Arguments: [hr - IN] : HRESULT for which the error
  4413. // message is to be retrieved
  4414. // [sbstrErrorMessage - OUT] : Receives the error message
  4415. //
  4416. // Returns: bool : true if the message was formatted properly
  4417. // false otherwise
  4418. //
  4419. // History: 11-Sep-2000 JeffJon Created
  4420. //
  4421. //---------------------------------------------------------------------------
  4422. bool GetErrorMessage(HRESULT hr, CComBSTR& sbstrErrorMessage)
  4423. {
  4424. ENTER_FUNCTION(MINIMAL_LOGGING, GetErrorMessage);
  4425. HRESULT hrGetLast = S_OK;
  4426. DWORD status = 0;
  4427. PTSTR ptzSysMsg = NULL;
  4428. //
  4429. // first check if we have extended ADs errors
  4430. //
  4431. if (hr != S_OK)
  4432. {
  4433. WCHAR Buf1[256], Buf2[256];
  4434. hrGetLast = ::ADsGetLastError(&status, Buf1, 256, Buf2, 256);
  4435. if ((status != ERROR_INVALID_DATA) && (status != 0))
  4436. {
  4437. hr = status;
  4438. DEBUG_OUTPUT(MINIMAL_LOGGING,
  4439. L"ADsGetLastError returned hr = 0x%x",
  4440. hr);
  4441. if (HRESULT_CODE(hr) == ERROR_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER)
  4442. {
  4443. DEBUG_OUTPUT(MINIMAL_LOGGING,
  4444. L"Displaying special error message for ERROR_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER");
  4445. bool bLoadedMessage = sbstrErrorMessage.LoadString(::GetModuleHandle(NULL),
  4446. IDS_ERRMSG_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER);
  4447. if (bLoadedMessage)
  4448. {
  4449. return true;
  4450. }
  4451. }
  4452. }
  4453. }
  4454. //
  4455. // try the system first
  4456. //
  4457. int nChars = ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
  4458. | FORMAT_MESSAGE_FROM_SYSTEM,
  4459. NULL,
  4460. hr,
  4461. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  4462. (PTSTR)&ptzSysMsg,
  4463. 0,
  4464. NULL);
  4465. if (nChars == 0)
  4466. {
  4467. //
  4468. //try ads errors
  4469. //
  4470. static HMODULE g_adsMod = 0;
  4471. if (0 == g_adsMod)
  4472. {
  4473. g_adsMod = GetModuleHandle (L"activeds.dll");
  4474. }
  4475. nChars = ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
  4476. | FORMAT_MESSAGE_FROM_HMODULE,
  4477. g_adsMod,
  4478. hr,
  4479. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  4480. (PTSTR)&ptzSysMsg,
  4481. 0,
  4482. NULL);
  4483. }
  4484. if (nChars > 0)
  4485. {
  4486. //
  4487. // Strip off the newline if there is one
  4488. //
  4489. PTSTR ptzTemp = ptzSysMsg;
  4490. while (ptzTemp && *ptzTemp != _T('\0'))
  4491. {
  4492. if (*ptzTemp == _T('\n') || *ptzTemp == _T('\r'))
  4493. {
  4494. *ptzTemp = _T('\0');
  4495. }
  4496. ptzTemp++;
  4497. }
  4498. sbstrErrorMessage = ptzSysMsg;
  4499. ::LocalFree(ptzSysMsg);
  4500. }
  4501. return (nChars > 0);
  4502. }
  4503. //+--------------------------------------------------------------------------
  4504. //
  4505. // Function: DisplayErrorMessage
  4506. //
  4507. // Synopsis: Displays the error message retrieved from GetErrorMessage
  4508. // to stderr. If GetErrorMessage fails, it displays the error
  4509. // code of the HRESULT
  4510. //
  4511. // Arguments: [pszCommand - IN]: the name of the command line executable
  4512. // [pszName - IN] : the name passed in as the target of the operation
  4513. // [hr - IN] : HRESULT for which the error
  4514. // message is to be retrieved
  4515. // [pszMessage - IN]: string of an additional message to be displayed
  4516. // at the end
  4517. //
  4518. // Returns: bool : true if the message was formatted and displayed properly
  4519. // false otherwise
  4520. //
  4521. // History: 11-Sep-2000 JeffJon Created
  4522. // 10-May-2001 JonN 256583 output DSCMD-escaped DN
  4523. //
  4524. //---------------------------------------------------------------------------
  4525. bool DisplayErrorMessage(PCWSTR pszCommand,
  4526. PCWSTR pszName,
  4527. HRESULT hr,
  4528. PCWSTR pszMessage)
  4529. {
  4530. bool bRet = true;
  4531. CComBSTR sbstrError;
  4532. CComBSTR sbstrFailed;
  4533. bool bGetError = false;
  4534. if (FAILED(hr))
  4535. {
  4536. bGetError = GetErrorMessage(hr, sbstrError);
  4537. }
  4538. bool bLoadFailed = sbstrFailed.LoadString(::GetModuleHandle(NULL), IDS_FAILED);
  4539. // JonN 5/10/01 256583 output DSCMD-escaped DN
  4540. CComBSTR sbstrOutputDN;
  4541. if (pszName && *pszName)
  4542. {
  4543. HRESULT hr = GetOutputDN( &sbstrOutputDN, pszName );
  4544. if (FAILED(hr))
  4545. {
  4546. ASSERT(FALSE);
  4547. }
  4548. else
  4549. {
  4550. pszName = sbstrOutputDN;
  4551. }
  4552. }
  4553. if (bGetError && bLoadFailed && pszName && pszMessage)
  4554. {
  4555. WriteStandardError(L"%s %s:%s:%s:%s\n",
  4556. pszCommand,
  4557. sbstrFailed,
  4558. pszName,
  4559. sbstrError,
  4560. pszMessage);
  4561. }
  4562. else if (bGetError && bLoadFailed && pszName && !pszMessage)
  4563. {
  4564. WriteStandardError(L"%s %s:%s:%s\n",
  4565. pszCommand,
  4566. sbstrFailed,
  4567. pszName,
  4568. sbstrError);
  4569. }
  4570. else if (bGetError && bLoadFailed && !pszName && pszMessage)
  4571. {
  4572. WriteStandardError(L"%s %s:%s:%s\n",
  4573. pszCommand,
  4574. sbstrFailed,
  4575. sbstrError,
  4576. pszMessage);
  4577. }
  4578. else if (bGetError && bLoadFailed && !pszName && !pszMessage)
  4579. {
  4580. WriteStandardError(L"%s %s:%s\n",
  4581. pszCommand,
  4582. sbstrFailed,
  4583. sbstrError);
  4584. }
  4585. else if (!bGetError && bLoadFailed && !pszName && pszMessage)
  4586. {
  4587. WriteStandardError(L"%s %s:%s\n",
  4588. pszCommand,
  4589. sbstrFailed,
  4590. pszMessage);
  4591. }
  4592. else if (!bGetError && bLoadFailed && pszName && pszMessage)
  4593. {
  4594. WriteStandardError(L"%s %s:%s:%s\n",
  4595. pszCommand,
  4596. sbstrFailed,
  4597. pszName,
  4598. pszMessage);
  4599. }
  4600. else
  4601. {
  4602. WriteStandardError(L"Error code = 0x%x\n", hr);
  4603. bRet = FALSE;
  4604. }
  4605. return bRet;
  4606. }
  4607. //+--------------------------------------------------------------------------
  4608. //
  4609. // Function: DisplayErrorMessage
  4610. //
  4611. // Synopsis: Displays the error message retrieved from GetErrorMessage
  4612. // to stderr. If GetErrorMessage fails, it displays the error
  4613. // code of the HRESULT
  4614. //
  4615. // Arguments: [pszCommand - IN]: the name of the command line executable
  4616. // [pszName - IN] : the name passed in as the target of the operation
  4617. // [hr - IN] : HRESULT for which the error
  4618. // message is to be retrieved
  4619. // [nStringID - IN] : Resource ID an additional message to be displayed
  4620. // at the end
  4621. //
  4622. // Returns: bool : true if the message was formatted and displayed properly
  4623. // false otherwise
  4624. //
  4625. // History: 11-Sep-2000 JeffJon Created
  4626. //
  4627. //---------------------------------------------------------------------------
  4628. bool DisplayErrorMessage(PCWSTR pszCommand,
  4629. PCWSTR pszName,
  4630. HRESULT hr,
  4631. UINT nStringID)
  4632. {
  4633. CComBSTR sbstrMessage;
  4634. bool bLoadString = sbstrMessage.LoadString(::GetModuleHandle(NULL), nStringID);
  4635. if (bLoadString)
  4636. {
  4637. return DisplayErrorMessage(pszCommand, pszName, hr, sbstrMessage);
  4638. }
  4639. return DisplayErrorMessage(pszCommand, pszName, hr);
  4640. }
  4641. //+--------------------------------------------------------------------------
  4642. //
  4643. // Function: DisplaySuccessMessage
  4644. //
  4645. // Synopsis: Displays a success message for the command
  4646. //
  4647. // Arguments: [pszCommand - IN]: the name of the command line executable
  4648. // [pszName - IN] : the name passed in as the target of the operation
  4649. //
  4650. // Returns: bool : true if the message was formatted and displayed properly
  4651. // false otherwise
  4652. //
  4653. // History: 11-Sep-2000 JeffJon Created
  4654. // 10-May-2001 JonN 256583 output DSCMD-escaped DN
  4655. //
  4656. //---------------------------------------------------------------------------
  4657. bool DisplaySuccessMessage(PCWSTR pszCommand,
  4658. PCWSTR pszName)
  4659. {
  4660. //
  4661. // Verify parameters
  4662. //
  4663. if (!pszCommand)
  4664. {
  4665. ASSERT(pszCommand);
  4666. return false;
  4667. }
  4668. CComBSTR sbstrSuccess;
  4669. if (!sbstrSuccess.LoadString(::GetModuleHandle(NULL), IDS_SUCCESS))
  4670. {
  4671. return false;
  4672. }
  4673. CComBSTR sbstrOutputDN;
  4674. if (!pszName)
  4675. {
  4676. WriteStandardOut(L"%s %s\n", pszCommand, sbstrSuccess);
  4677. }
  4678. else
  4679. {
  4680. // JonN 5/10/01 256583 output DSCMD-escaped DN
  4681. if (*pszName)
  4682. {
  4683. HRESULT hr = GetOutputDN( &sbstrOutputDN, pszName );
  4684. if (FAILED(hr))
  4685. {
  4686. ASSERT(FALSE);
  4687. }
  4688. else
  4689. {
  4690. pszName = sbstrOutputDN;
  4691. }
  4692. }
  4693. WriteStandardOut(L"%s %s:%s\n", pszCommand, sbstrSuccess, pszName);
  4694. }
  4695. return true;
  4696. }
  4697. //+--------------------------------------------------------------------------
  4698. //
  4699. // Function: WriteStringIDToStandardOut
  4700. //
  4701. // Synopsis: Loads the String Resource and displays on Standardout
  4702. //
  4703. // Arguments: nStringID : Resource ID
  4704. // Returns: bool : true if the message was formatted and displayed properly
  4705. // false otherwise
  4706. //
  4707. // History: 11-Sep-2000 hiteshr Created
  4708. //
  4709. //---------------------------------------------------------------------------
  4710. bool WriteStringIDToStandardOut(UINT nStringID)
  4711. {
  4712. CComBSTR sbstrSuccess;
  4713. if (!sbstrSuccess.LoadString(::GetModuleHandle(NULL), nStringID))
  4714. {
  4715. return false;
  4716. }
  4717. WriteStandardOut(sbstrSuccess);
  4718. return true;
  4719. }
  4720. //+---------------------------------------------------------------------------
  4721. //
  4722. // Function: ExpandUsername
  4723. //
  4724. // Synopsis: If the value in pwzValue contains %username% it gets expanded
  4725. // to be the sAMAccountName
  4726. //
  4727. // Arguments: [pwzValue IN/OUT] : string that may contain %username%
  4728. // [pwzSamName IN] : the SAM name to substitute
  4729. // [fExpanded OUT] : whether the value needed to be expanded or not
  4730. //
  4731. // Return: bool : true if the function succeeded, false otherwise
  4732. //
  4733. // History 27-Oct-2000 JeffJon Created
  4734. //----------------------------------------------------------------------------
  4735. bool ExpandUsername(PWSTR& pwzValue, PWSTR pwzSamName, bool& fExpanded)
  4736. {
  4737. ENTER_FUNCTION(LEVEL5_LOGGING, ExpandUsername);
  4738. PCWSTR pszUserToken = L"$username$";
  4739. unsigned int TokenLength = static_cast<unsigned int>(wcslen(pszUserToken));
  4740. bool bRet = false;
  4741. do // false loop
  4742. {
  4743. if (!pwzValue)
  4744. {
  4745. ASSERT(pwzValue);
  4746. break;
  4747. }
  4748. //
  4749. // This determines if expansion is needed
  4750. //
  4751. PWSTR pwzTokenStart = wcschr(pwzValue, pszUserToken[0]);
  4752. if (pwzTokenStart)
  4753. {
  4754. if ((wcslen(pwzTokenStart) >= TokenLength) &&
  4755. (_wcsnicmp(pwzTokenStart, pszUserToken, TokenLength) == 0))
  4756. {
  4757. fExpanded = true;
  4758. }
  4759. else
  4760. {
  4761. fExpanded = false;
  4762. bRet = true;
  4763. break;
  4764. }
  4765. }
  4766. else
  4767. {
  4768. fExpanded = false;
  4769. bRet = true;
  4770. break;
  4771. }
  4772. //
  4773. // If the samName isn't given return without doing anything
  4774. // This is useful to just determine if expansion is needed or not
  4775. //
  4776. if (!pwzSamName)
  4777. {
  4778. bRet = false;
  4779. break;
  4780. }
  4781. CComBSTR sbstrValue;
  4782. CComBSTR sbstrAfterToken;
  4783. while (pwzTokenStart)
  4784. {
  4785. *pwzTokenStart = L'\0';
  4786. sbstrValue = pwzValue;
  4787. if ((L'\0' != *pwzValue) && !sbstrValue.Length())
  4788. {
  4789. bRet = false;
  4790. break;
  4791. }
  4792. PWSTR pwzAfterToken = pwzTokenStart + TokenLength;
  4793. sbstrAfterToken = pwzAfterToken;
  4794. if ((L'\0' != *pwzAfterToken) && !sbstrAfterToken.Length())
  4795. {
  4796. bRet = false;
  4797. break;
  4798. }
  4799. delete pwzValue;
  4800. sbstrValue += pwzSamName;
  4801. if (!sbstrValue.Length())
  4802. {
  4803. bRet = false;
  4804. break;
  4805. }
  4806. sbstrValue += sbstrAfterToken;
  4807. if (!sbstrValue.Length())
  4808. {
  4809. bRet = false;
  4810. break;
  4811. }
  4812. pwzValue = new WCHAR[sbstrValue.Length() + 1];
  4813. if (!pwzValue)
  4814. {
  4815. bRet = false;
  4816. break;
  4817. }
  4818. wcscpy(pwzValue, sbstrValue);
  4819. pwzTokenStart = wcschr(pwzValue, pszUserToken[0]);
  4820. if (!(pwzTokenStart &&
  4821. (wcslen(pwzTokenStart) >= TokenLength) &&
  4822. (_wcsnicmp(pwzTokenStart, pszUserToken, TokenLength) == 0)))
  4823. {
  4824. bRet = true;
  4825. break;
  4826. }
  4827. } // while
  4828. } while (false);
  4829. return bRet;
  4830. }
  4831. //+--------------------------------------------------------------------------
  4832. //
  4833. // Function: FillAttrInfoFromObjectEntryExpandUsername
  4834. //
  4835. // Synopsis: Fills the ADS_ATTR_INFO from the attribute table associated
  4836. // with the object entry and expands values containing %username%
  4837. //
  4838. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  4839. // to the object being modified
  4840. // [refBasePathsInfo - IN] : reference to an instance of the
  4841. // CDSCmdBasePathsInfo class
  4842. // [refCredentialObject - IN] : reference to an instance of the
  4843. // CDSCmdCredentialObject class
  4844. // [pObjectEntry - IN] : pointer to an entry in the object table
  4845. // that defines the object we are modifying
  4846. // [argRecord - IN] : the argument record structure from the
  4847. // parser table that corresponds to this
  4848. // attribute
  4849. // [dwAttributeIdx - IN] : index into the attribute table for the
  4850. // object in which we are setting
  4851. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  4852. // which this function will fill in
  4853. //
  4854. // Returns: HRESULT : S_OK if everything succeeded
  4855. // E_OUTOFMEMORY if we failed to allocate space for the value
  4856. // E_FAIL if we failed to format the value properly
  4857. //
  4858. // History: 27-Oct-2000 JeffJon Created
  4859. //
  4860. //---------------------------------------------------------------------------
  4861. HRESULT FillAttrInfoFromObjectEntryExpandUsername(PCWSTR pszDN,
  4862. const CDSCmdBasePathsInfo& refBasePathsInfo,
  4863. const CDSCmdCredentialObject& refCredentialObject,
  4864. const PDSOBJECTTABLEENTRY pObjectEntry,
  4865. const ARG_RECORD& argRecord,
  4866. DWORD dwAttributeIdx,
  4867. PADS_ATTR_INFO* ppAttr)
  4868. {
  4869. ENTER_FUNCTION_HR(LEVEL3_LOGGING, FillAttrInfoFromObjectEntryExpandUsername, hr);
  4870. do // false loop
  4871. {
  4872. //
  4873. // Verify Parameters
  4874. //
  4875. if (!pObjectEntry ||
  4876. !ppAttr ||
  4877. !pszDN)
  4878. {
  4879. ASSERT(pObjectEntry);
  4880. ASSERT(ppAttr);
  4881. ASSERT(pszDN);
  4882. hr = E_INVALIDARG;
  4883. break;
  4884. }
  4885. if (argRecord.strValue && argRecord.strValue[0] != L'\0')
  4886. {
  4887. //
  4888. // REVIEW_JEFFJON : this is being leaked!!!
  4889. //
  4890. PWSTR pszValue = new WCHAR[wcslen(argRecord.strValue) + 1];
  4891. if (!pszValue)
  4892. {
  4893. hr = E_OUTOFMEMORY;
  4894. break;
  4895. }
  4896. wcscpy(pszValue, argRecord.strValue);
  4897. //
  4898. // First check to see if we need to expand %username%
  4899. //
  4900. CComBSTR sbstrSamName;
  4901. bool bExpandNeeded = false;
  4902. ExpandUsername(pszValue, NULL, bExpandNeeded);
  4903. if (bExpandNeeded)
  4904. {
  4905. DEBUG_OUTPUT(LEVEL5_LOGGING, L"%username% expansion required. Retrieving sAMAccountName...");
  4906. //
  4907. // Retrieve the sAMAccountName of the object and then expand the %username%
  4908. //
  4909. CComBSTR sbstrPath;
  4910. refBasePathsInfo.ComposePathFromDN(pszDN, sbstrPath);
  4911. CComPtr<IADs> spADs;
  4912. hr = DSCmdOpenObject(refCredentialObject,
  4913. sbstrPath,
  4914. IID_IADs,
  4915. (void**)&spADs,
  4916. true);
  4917. if (FAILED(hr))
  4918. {
  4919. break;
  4920. }
  4921. CComVariant var;
  4922. hr = spADs->Get(L"sAMAccountName", &var);
  4923. if (FAILED(hr))
  4924. {
  4925. DEBUG_OUTPUT(MINIMAL_LOGGING,
  4926. L"Failed to get sAMAccountName: hr = 0x%x",
  4927. hr);
  4928. break;
  4929. }
  4930. ASSERT(var.vt == VT_BSTR);
  4931. sbstrSamName = var.bstrVal;
  4932. DEBUG_OUTPUT(LEVEL5_LOGGING,
  4933. L"sAMAccountName = %w",
  4934. sbstrSamName);
  4935. //
  4936. // Now expand the username to the sAMAccountName
  4937. //
  4938. if (!ExpandUsername(pszValue, sbstrSamName, bExpandNeeded))
  4939. {
  4940. DEBUG_OUTPUT(MINIMAL_LOGGING, L"Failed to expand %username%");
  4941. hr = E_OUTOFMEMORY;
  4942. break;
  4943. }
  4944. }
  4945. switch (argRecord.fType)
  4946. {
  4947. case ARG_TYPE_STR :
  4948. DEBUG_OUTPUT(LEVEL3_LOGGING, L"argRecord.fType = ARG_TYPE_STR");
  4949. *ppAttr = &(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo);
  4950. //
  4951. // REVIEW_JEFFJON : this is being leaked!
  4952. //
  4953. (*ppAttr)->pADsValues = new ADSVALUE[1];
  4954. if ((*ppAttr)->pADsValues)
  4955. {
  4956. (*ppAttr)->dwNumValues = 1;
  4957. (*ppAttr)->pADsValues->dwType = (*ppAttr)->dwADsType;
  4958. switch ((*ppAttr)->dwADsType)
  4959. {
  4960. case ADSTYPE_DN_STRING :
  4961. {
  4962. //
  4963. // Lets bind to be sure the object exists
  4964. //
  4965. CComBSTR sbstrObjPath;
  4966. refBasePathsInfo.ComposePathFromDN(pszValue, sbstrObjPath);
  4967. CComPtr<IADs> spIADs;
  4968. hr = DSCmdOpenObject(refCredentialObject,
  4969. sbstrObjPath,
  4970. IID_IADs,
  4971. (void**)&spIADs,
  4972. true);
  4973. if (FAILED(hr))
  4974. {
  4975. DEBUG_OUTPUT(LEVEL3_LOGGING, L"DN object doesn't exist. %s", pszValue);
  4976. break;
  4977. }
  4978. (*ppAttr)->pADsValues->DNString = pszValue;
  4979. DEBUG_OUTPUT(LEVEL3_LOGGING, L"ADSTYPE_DN_STRING = %s", pszValue);
  4980. }
  4981. break;
  4982. case ADSTYPE_CASE_EXACT_STRING :
  4983. (*ppAttr)->pADsValues->CaseExactString = pszValue;
  4984. DEBUG_OUTPUT(LEVEL3_LOGGING, L"ADSTYPE_CASE_EXACT_STRING = %s", pszValue);
  4985. break;
  4986. case ADSTYPE_CASE_IGNORE_STRING :
  4987. (*ppAttr)->pADsValues->CaseIgnoreString = pszValue;
  4988. DEBUG_OUTPUT(LEVEL3_LOGGING, L"ADSTYPE_CASE_IGNORE_STRING = %s", pszValue);
  4989. break;
  4990. case ADSTYPE_PRINTABLE_STRING :
  4991. (*ppAttr)->pADsValues->PrintableString = pszValue;
  4992. DEBUG_OUTPUT(LEVEL3_LOGGING, L"ADSTYPE_PRINTABLE_STRING = %s", pszValue);
  4993. break;
  4994. default :
  4995. hr = E_INVALIDARG;
  4996. break;
  4997. }
  4998. //
  4999. // Set the attribute dirty
  5000. //
  5001. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags |= DS_ATTRIBUTE_DIRTY;
  5002. }
  5003. break;
  5004. default:
  5005. hr = E_INVALIDARG;
  5006. break;
  5007. }
  5008. }
  5009. else
  5010. {
  5011. DEBUG_OUTPUT(LEVEL3_LOGGING, L"No value present, changing control code to ADS_ATTR_CLEAR");
  5012. //
  5013. // Clear the attribute
  5014. //
  5015. (*ppAttr)->dwControlCode = ADS_ATTR_CLEAR;
  5016. (*ppAttr)->dwNumValues = 0;
  5017. //
  5018. // Set the attribute dirty
  5019. //
  5020. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags |= DS_ATTRIBUTE_DIRTY;
  5021. }
  5022. } while (false);
  5023. return hr;
  5024. }
  5025. //+--------------------------------------------------------------------------
  5026. //
  5027. // Function: BindToFSMOHolder
  5028. //
  5029. // Synopsis: Binds to the appropriate object which can be used to find a
  5030. // particular FSMO owner
  5031. //
  5032. // Arguments: [refBasePathsInfo - IN] : reference to the base paths info object
  5033. // [refCredObject - IN] : reference to the credential management object
  5034. // [fsmoType - IN] : type of the FSMO we are searching for
  5035. // [refspIADs - OUT] : interface to the object that will be
  5036. // used to start a search for the FSMO owner
  5037. //
  5038. // Returns: HRESULT : S_OK if everything succeeded
  5039. // E_INVALIDARG if an invalid FSMO type was passed
  5040. // Otherwise an ADSI failure code
  5041. //
  5042. // History: 13-Dec-2000 JeffJon Created
  5043. //
  5044. //---------------------------------------------------------------------------
  5045. HRESULT BindToFSMOHolder(IN const CDSCmdBasePathsInfo& refBasePathsInfo,
  5046. IN const CDSCmdCredentialObject& refCredObject,
  5047. IN FSMO_TYPE fsmoType,
  5048. OUT CComPtr<IADs>& refspIADs)
  5049. {
  5050. ENTER_FUNCTION_HR(LEVEL3_LOGGING, BindToFSMOHolder, hr);
  5051. refspIADs = 0;
  5052. CComBSTR sbstrDN;
  5053. switch (fsmoType)
  5054. {
  5055. case SCHEMA_FSMO:
  5056. {
  5057. DEBUG_OUTPUT(LEVEL3_LOGGING,
  5058. L"FSMO_TYPE = SCHEMA_FSMO");
  5059. sbstrDN = refBasePathsInfo.GetSchemaNamingContext();
  5060. break;
  5061. }
  5062. case RID_POOL_FSMO:
  5063. {
  5064. DEBUG_OUTPUT(LEVEL3_LOGGING,
  5065. L"FSMO_TYPE = RID_POOL_FSMO");
  5066. sbstrDN = refBasePathsInfo.GetDefaultNamingContext();
  5067. CComBSTR sbstrPath;
  5068. refBasePathsInfo.ComposePathFromDN(sbstrDN, sbstrPath);
  5069. CComPtr<IADs> spIADsDefault;
  5070. hr = DSCmdOpenObject(refCredObject,
  5071. sbstrPath,
  5072. IID_IADs,
  5073. (void**)&spIADsDefault,
  5074. true);
  5075. if (FAILED(hr))
  5076. {
  5077. break;
  5078. }
  5079. CComVariant var;
  5080. hr = spIADsDefault->Get(g_bstrIDManagerReference, &var);
  5081. if (FAILED(hr))
  5082. {
  5083. break;
  5084. }
  5085. ASSERT(var.vt == VT_BSTR);
  5086. sbstrDN = var.bstrVal;
  5087. DEBUG_OUTPUT(LEVEL3_LOGGING,
  5088. L"rIDManagerReference = %s",
  5089. sbstrDN);
  5090. break;
  5091. }
  5092. case PDC_FSMO:
  5093. {
  5094. DEBUG_OUTPUT(LEVEL3_LOGGING,
  5095. L"FSMO_TYPE = PDC_FSMO");
  5096. sbstrDN = refBasePathsInfo.GetDefaultNamingContext();
  5097. break;
  5098. }
  5099. case INFRASTUCTURE_FSMO:
  5100. {
  5101. DEBUG_OUTPUT(LEVEL3_LOGGING,
  5102. L"FSMO_TYPE = INFRASTUCTURE_FSMO");
  5103. sbstrDN = refBasePathsInfo.GetDefaultNamingContext();
  5104. break;
  5105. }
  5106. case DOMAIN_NAMING_FSMO:
  5107. {
  5108. DEBUG_OUTPUT(LEVEL3_LOGGING,
  5109. L"FSMO_TYPE = DOMAIN_NAMING_FSMO");
  5110. sbstrDN = L"CN=Partitions,";
  5111. sbstrDN += refBasePathsInfo.GetConfigurationNamingContext();
  5112. break;
  5113. }
  5114. default:
  5115. ASSERT(FALSE);
  5116. hr = E_INVALIDARG;
  5117. break;
  5118. }
  5119. if (SUCCEEDED(hr))
  5120. {
  5121. CComBSTR sbstrPath;
  5122. refBasePathsInfo.ComposePathFromDN(sbstrDN, sbstrPath);
  5123. hr = DSCmdOpenObject(refCredObject,
  5124. sbstrPath,
  5125. IID_IADs,
  5126. (void**)&refspIADs,
  5127. true);
  5128. }
  5129. return hr;
  5130. }
  5131. //+--------------------------------------------------------------------------
  5132. //
  5133. // Function: FindFSMOOwner
  5134. //
  5135. // Synopsis:
  5136. //
  5137. // Arguments: [refBasePathsInfo - IN] : reference to the base paths info object
  5138. // [refCredObject - IN] : reference to the credential management object
  5139. // [fsmoType - IN] : type of the FSMO we are searching for
  5140. // [refspIADs - OUT] : interface to the object that will be
  5141. // used to start a search for the FSMO owner
  5142. //
  5143. // Returns: HRESULT : S_OK if everything succeeded
  5144. // Otherwise an ADSI failure code
  5145. //
  5146. // History: 13-Dec-2000 JeffJon Created
  5147. //
  5148. //---------------------------------------------------------------------------
  5149. HRESULT FindFSMOOwner(IN const CDSCmdBasePathsInfo& refBasePathsInfo,
  5150. IN const CDSCmdCredentialObject& refCredObject,
  5151. IN FSMO_TYPE fsmoType,
  5152. OUT CComBSTR& refsbstrServerDN)
  5153. {
  5154. ENTER_FUNCTION_HR(LEVEL3_LOGGING, FindFSMOOwner, hr);
  5155. refsbstrServerDN.Empty();
  5156. static const int nMaxReferrals = 10;
  5157. int nIterations = 0;
  5158. //
  5159. // We will start the search with the current server
  5160. //
  5161. CComBSTR sbstrNextServer;
  5162. sbstrNextServer = refBasePathsInfo.GetServerName();
  5163. do
  5164. {
  5165. //
  5166. // Initialize a new base paths info object on each iteration
  5167. //
  5168. CDSCmdBasePathsInfo nextPathsInfo;
  5169. hr = nextPathsInfo.InitializeFromName(refCredObject,
  5170. sbstrNextServer,
  5171. true);
  5172. if (FAILED(hr))
  5173. {
  5174. DEBUG_OUTPUT(LEVEL3_LOGGING,
  5175. L"Failed to initialize the base paths info for %s: hr = 0x%x",
  5176. sbstrNextServer,
  5177. hr);
  5178. break;
  5179. }
  5180. //
  5181. // Now bind to the fsmo holder for that server
  5182. //
  5183. CComPtr<IADs> spIADs;
  5184. hr = BindToFSMOHolder(nextPathsInfo,
  5185. refCredObject,
  5186. fsmoType,
  5187. spIADs);
  5188. if (FAILED(hr))
  5189. {
  5190. break;
  5191. }
  5192. //
  5193. // Get the fSMORoleOwner property
  5194. //
  5195. CComVariant fsmoRoleOwnerProperty;
  5196. hr = spIADs->Get(g_bstrFSMORoleOwner, &fsmoRoleOwnerProperty);
  5197. if (FAILED(hr))
  5198. {
  5199. DEBUG_OUTPUT(LEVEL3_LOGGING,
  5200. L"Failed to get the fSMORoleOwner: hr = 0x%x",
  5201. hr);
  5202. break;
  5203. }
  5204. //
  5205. // The result here is in the form, "CN=NTDS Settings,CN=Machine,CN=..."
  5206. // we need to just have "CN=Machine,CN=..."
  5207. //
  5208. CComBSTR sbstrMachineOwner;
  5209. hr = CPathCracker::GetParentDN(fsmoRoleOwnerProperty.bstrVal, sbstrMachineOwner);
  5210. if (FAILED(hr))
  5211. {
  5212. DEBUG_OUTPUT(LEVEL3_LOGGING,
  5213. L"Failed to get the parent DN of the FSMORoleOwner: hr = 0x%x",
  5214. hr);
  5215. break;
  5216. }
  5217. CComBSTR sbstrMachinePath;
  5218. nextPathsInfo.ComposePathFromDN(sbstrMachineOwner, sbstrMachinePath);
  5219. //
  5220. // Bind to the server object so we can get the dnsHostName to compare to the server name
  5221. //
  5222. CComPtr<IADs> spIADsServer;
  5223. hr = DSCmdOpenObject(refCredObject,
  5224. sbstrMachinePath,
  5225. IID_IADs,
  5226. (void**)&spIADsServer,
  5227. true);
  5228. if (FAILED(hr))
  5229. {
  5230. DEBUG_OUTPUT(LEVEL3_LOGGING,
  5231. L"Failed to bind to server object: hr = 0x%x",
  5232. hr);
  5233. break;
  5234. }
  5235. //
  5236. // Get the DNS host name
  5237. //
  5238. CComVariant varServerName;
  5239. hr = spIADsServer->Get(g_bstrDNSHostName, &varServerName);
  5240. if (FAILED(hr))
  5241. {
  5242. DEBUG_OUTPUT(LEVEL3_LOGGING,
  5243. L"Failed to get the dNSHostName: hr = 0x%x",
  5244. hr);
  5245. break;
  5246. }
  5247. ASSERT(varServerName.vt == VT_BSTR);
  5248. sbstrNextServer = varServerName.bstrVal;
  5249. //
  5250. // If the server name in the dNSHostName attribute matches the current
  5251. // base paths info, then we found the owner
  5252. //
  5253. if (0 == _wcsicmp(sbstrNextServer, nextPathsInfo.GetServerName()))
  5254. {
  5255. //
  5256. // We found it
  5257. //
  5258. DEBUG_OUTPUT(LEVEL3_LOGGING,
  5259. L"The role owner is %s",
  5260. sbstrNextServer);
  5261. refsbstrServerDN = sbstrMachineOwner;
  5262. break;
  5263. }
  5264. ++nIterations;
  5265. } while (nIterations < nMaxReferrals);
  5266. return hr;
  5267. }
  5268. //+--------------------------------------------------------------------------
  5269. //
  5270. // Function: ValidateAndModifySAMName
  5271. //
  5272. // Synopsis: Looks for any illegal characters in the SamAccountName and
  5273. // converts them to the replacementChar
  5274. //
  5275. // Arguments: [pszSAMName - IN/OUT] : pointer to a string that contains the SamAccountName
  5276. // illegal characters will be replaced
  5277. // [pszInvalidChars - IN] : string containing the illegal characters
  5278. //
  5279. // Returns: HRESULT : S_OK if the name was valid and no characters had to be replaced
  5280. // S_FALSE if the name contained invalid characters that were replaced
  5281. // E_INVALIDARG
  5282. //
  5283. // History: 21-Feb-2001 JeffJon Created
  5284. //
  5285. //---------------------------------------------------------------------------
  5286. HRESULT ValidateAndModifySAMName(PWSTR pszSAMName,
  5287. PCWSTR pszInvalidChars)
  5288. {
  5289. ENTER_FUNCTION_HR(LEVEL3_LOGGING, ValidateAndModifySAMName, hr);
  5290. static const WCHAR replacementChar = L'_';
  5291. do
  5292. {
  5293. if (!pszSAMName ||
  5294. !pszInvalidChars)
  5295. {
  5296. ASSERT(pszSAMName);
  5297. ASSERT(pszInvalidChars);
  5298. hr = E_INVALIDARG;
  5299. break;
  5300. }
  5301. DEBUG_OUTPUT(LEVEL3_LOGGING,
  5302. L"SAM name before: %s",
  5303. pszSAMName);
  5304. for (size_t idx = 0; idx < wcslen(pszInvalidChars); ++idx)
  5305. {
  5306. WCHAR* illegalChar = 0;
  5307. do
  5308. {
  5309. illegalChar = wcschr(pszSAMName, pszInvalidChars[idx]);
  5310. if (illegalChar)
  5311. {
  5312. *illegalChar = replacementChar;
  5313. hr = S_FALSE;
  5314. }
  5315. } while (illegalChar);
  5316. }
  5317. } while (false);
  5318. DEBUG_OUTPUT(LEVEL3_LOGGING,
  5319. L"SAM name after: %s",
  5320. pszSAMName);
  5321. return hr;
  5322. }
  5323. //+--------------------------------------------------------------------------
  5324. //
  5325. // Class: GetEscapedElement
  5326. //
  5327. // Purpose: Calls IADsPathname::GetEscapedElement. Uses LocalAlloc.
  5328. //
  5329. // History: 28-Apr-2001 JonN Created
  5330. //
  5331. //---------------------------------------------------------------------------
  5332. HRESULT GetEscapedElement( OUT PWSTR* ppszOut, IN PCWSTR pszIn )
  5333. {
  5334. ENTER_FUNCTION_HR(LEVEL3_LOGGING, GetEscapedElement, hr);
  5335. CPathCracker pathCracker;
  5336. CComBSTR sbstrIn = pszIn;
  5337. CComBSTR sbstrEscaped;
  5338. if (sbstrIn.Length() > 0) // handle empty path component
  5339. {
  5340. hr = pathCracker.GetEscapedElement(0,
  5341. sbstrIn,
  5342. &sbstrEscaped);
  5343. if (FAILED(hr))
  5344. return hr;
  5345. else if (!sbstrEscaped)
  5346. return E_FAIL;
  5347. }
  5348. *ppszOut = (LPWSTR)LocalAlloc(LPTR, (sbstrEscaped.Length()+1) * sizeof(WCHAR) );
  5349. if (NULL == *ppszOut)
  5350. return E_OUTOFMEMORY;
  5351. if (sbstrIn.Length() > 0) // handle empty path component
  5352. wcscpy( *ppszOut, sbstrEscaped );
  5353. return hr;
  5354. } // GetEscapedElement
  5355. //+--------------------------------------------------------------------------
  5356. //
  5357. // Class: GetOutputDN
  5358. //
  5359. // Purpose: Converts an ADSI-escaped DN to one with DSCMD input escaping.
  5360. // This way, the output DN can be piped as input to another
  5361. // DSCMD command.
  5362. //
  5363. // History: 08-May-2001 JonN Created
  5364. //
  5365. //---------------------------------------------------------------------------
  5366. HRESULT GetOutputDN( OUT BSTR* pbstrOut, IN PCWSTR pszIn )
  5367. {
  5368. ENTER_FUNCTION_HR(LEVEL3_LOGGING, GetOutputDN, hr);
  5369. if (NULL == pszIn || L'\0' == *pszIn)
  5370. {
  5371. *pbstrOut = SysAllocString(L"");
  5372. return (NULL == *pbstrOut) ? E_OUTOFMEMORY : S_OK;
  5373. }
  5374. CPathCracker pathCracker;
  5375. CComBSTR sbstrIn = pszIn;
  5376. hr = pathCracker.Set(sbstrIn, ADS_SETTYPE_DN);
  5377. if (FAILED(hr))
  5378. {
  5379. ASSERT(FALSE);
  5380. return hr;
  5381. }
  5382. long lnNumPathElements = 0;
  5383. hr = pathCracker.GetNumElements( &lnNumPathElements );
  5384. if (FAILED(hr))
  5385. {
  5386. ASSERT(FALSE);
  5387. return hr;
  5388. }
  5389. else if (0 >= lnNumPathElements)
  5390. {
  5391. ASSERT(FALSE);
  5392. return E_FAIL;
  5393. }
  5394. hr = pathCracker.put_EscapedMode( ADS_ESCAPEDMODE_OFF_EX );
  5395. if (FAILED(hr))
  5396. {
  5397. ASSERT(FALSE);
  5398. return hr;
  5399. }
  5400. CComBSTR sbstrOut;
  5401. CComBSTR sbstrComma( L"," );
  5402. for (long lnPathElement = 0;
  5403. lnPathElement < lnNumPathElements;
  5404. lnPathElement++)
  5405. {
  5406. CComBSTR sbstrElement;
  5407. hr = pathCracker.GetElement( lnPathElement, &sbstrElement );
  5408. if (FAILED(hr))
  5409. {
  5410. ASSERT(FALSE);
  5411. return hr;
  5412. }
  5413. // re-escape sbstrElement
  5414. CComBSTR sbstrEscapedElement( (sbstrElement.Length()+1) * 2 );
  5415. ::ZeroMemory( (BSTR)sbstrEscapedElement,
  5416. (sbstrElement.Length()+1) * 2 * sizeof(WCHAR) );
  5417. LPWSTR pszEscapedElement = sbstrEscapedElement;
  5418. for (LPWSTR pszElement = sbstrElement;
  5419. L'\0' != *pszElement;
  5420. pszElement++)
  5421. {
  5422. switch (*pszElement)
  5423. {
  5424. case ',':
  5425. case '\\':
  5426. *(pszEscapedElement++) = L'\\';
  5427. // fall through
  5428. default:
  5429. *(pszEscapedElement++) = *pszElement;
  5430. break;
  5431. }
  5432. }
  5433. if (!!sbstrOut)
  5434. sbstrOut += sbstrComma;
  5435. // cast to avoid CComBSTR::operator+= "bug"
  5436. sbstrOut += (BSTR)sbstrEscapedElement;
  5437. }
  5438. *pbstrOut = sbstrOut.Detach();
  5439. return hr;
  5440. } // GetOutputDN